Merge remote-tracking branch 'upstream/master'

Conflicts:
	changelog.txt
	world/worlddb.cpp
This commit is contained in:
Paul Coene 2015-02-23 08:13:09 -05:00
commit 19fc02c284
109 changed files with 4914 additions and 3112 deletions

View File

@ -1,12 +1,89 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 02/21/2015 ==
Noudess: Starting erudites that were supposed to start in paineel were landing in erudin on Titanium. Fixed to be paineel.
== 02/20/2015 ==
demonstar55: Implement claims for RoF/RoF2 (should no longer crash the client!)
- fixed bugs related to claims for the rest of the clients (woo infinite loops)
== 02/18/2015 ==
Noudess: Starting erudites that were supposed to start in paineel were landing
in erudin on Titanium. Fixed to be paineel.
Trevius: Fix for potential recursive loops if using RemoveFromHateList() within EVENT_HATE_LIST.
== 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 ==
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
== 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.
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 ==
Akkadius: Implement zone based gravity, required SQL DB change
- To test `zone` table `gravity` values, change the value and use #zheader <zoneshortname> to test
== 02/11/2015 ==
Trevius: (RoF+) Bandolier no longer displays a Treasure Chest Icon when no Bandoliers are set.
== 02/09/2015 ==
Noudess: Placing an aug tagged as magical onto a non-magical weapon now allows
the resulting weapon to hit creatures requiring magical creatures. Seems like
the correct thing, as the weapon is marked as magical.
Trevius: (RoF+) Setting Alt flag on characters in the Guild Management Window is now saved and functional for filtering.
Trevius: (RoF+) Guild Invites between RoF+ and previous Clients is now functional.
== 02/08/2015 ==
Kayen: Implemented npc specialability (44) COUNTER_AVOID_DAMAGE which when applied to the ATTACKING NPC will make their attacks more difficult to be avoided by riposte/dodge/parry/block.
Parama0: Negative modifer value that affects ALL avoid damage types dodge/parry/riposte/block) chance on defender. Ie (44,50 = 50 pct reduction to ALL)
Parama1: Negative modifer value that affects RIPOSTE chance on defender. Ie (44,1,0,50 = 50 pct reduction to riposte chance)
Parama2: Negative modifer value that affects PARRY chance on defender. Ie (44,1,0,0,50 = 50 pct reduction to parry chance)
Parama3: Negative modifer value that affects BLOCK chance on defender. Ie (44,1,0,0,0,50 = 50 pct reduction to block chance)
Parama4: Negative modifer value that affects DODGE chance on defender. e (44,1,0,0,0,0,50 = 50 pct reduction to dodge chance)
Example of usage: Player has Improved Dodge V (+50 pct dodge chance), you want to negate this bonus you would set 44,1,0,0,0,0,50 on your NPC.
== 02/07/2015 ==
Akkadius: Reduced #repop time dramatically by taking down hundreds of individual SELECT/DELETE/INSERT queries in routines and bringing it down to very few
See: https://www.youtube.com/watch?v=9kSFbyTBuAk
== 02/06/2015 ==
Uleat: Updated returns for Inventory and ItemInst const iterators. (const == const)
Uleat: Replaced 'iter_inst' and 'iter_contents' typedefs with their stl definitions
Uleat: Removed 'limbo' from the 'HasItem' series of checks - including lore checks. The client excludes this range and it causes issues when performing item searches - dupe lore checks were added to account for this.
Uleat: Updated command #iteminfo to show light source information and a few other things
== 02/05/2015 ==
Trevius: Fixed Environmental Damage for RoF2.
== 02/03/2015 ==
Trevius: Crashfix for TempName() when numbers are passed at the end of the name.
Uleat: Tweaking of item type exclusions to alleviate strobing conditions with light sources
== 02/02/2015 ==
Akkadius: Implement Packet logs with dumps
- Category: 41: Packet: Server -> Client With Dump
- Category: 42: Packet: Server -> Client With Dump
See: http://wiki.eqemulator.org/p?Logging_System_Overhaul#packet-logging-levels
== 02/01/2015 ==
demonstar55: Add quest debugging to lua
eq.debug("Test debug level implicit 1")
eq.debug("Test debug level explicit 1", 1)
eq.debug("Test debug level explicit 2", 2)
eq.debug("Test debug level explicit 3", 3)
Akkadius: Add Packet Logging Categories
- 5 - Packet: Client -> Server - Logs::Client_Server_Packet
- 39 - Packet: Server -> Client - Logs::Server_Client_Packet
- 40 - Packet: Client -> Server Unhandled - Logs::Client_Server_Packet_Unhandled
See: http://wiki.eqemulator.org/p?Logging_System_Overhaul#packet-logging
== 01/31/2015 ==
Trevius: Fixed FindGroundZ() and GetGroundZ() to once again utilize the X and Y arguments that are passed to them.

View File

@ -76,6 +76,24 @@ int GetSpellColumns(SharedDatabase *db) {
return results.RowCount();
}
bool IsStringField(int i) {
switch(i)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
return true;
break;
default:
return false;
}
}
void ImportSpells(SharedDatabase *db) {
Log.Out(Logs::General, Logs::Status, "Importing Spells...");
FILE *f = fopen("import/spells_us.txt", "r");
@ -113,7 +131,12 @@ void ImportSpells(SharedDatabase *db) {
sql += "'";
}
sql += split[i];
if(split[i].compare("") == 0 && !IsStringField(i)) {
sql += "0";
}
else {
sql += split[i];
}
sql += "'";
}
@ -128,7 +151,12 @@ void ImportSpells(SharedDatabase *db) {
sql += "'";
}
sql += split[i];
if(split[i].compare("") == 0 && !IsStringField(i)) {
sql += "0";
} else {
sql += split[i];
}
sql += "'";
}

View File

@ -63,6 +63,7 @@ public:
void WriteFloat(float value) { *(float *)(pBuffer + _wpos) = value; _wpos += sizeof(float); }
void WriteDouble(double value) { *(double *)(pBuffer + _wpos) = value; _wpos += sizeof(double); }
void WriteString(const char * str) { uint32 len = static_cast<uint32>(strlen(str)) + 1; memcpy(pBuffer + _wpos, str, len); _wpos += len; }
void WriteData(const void *ptr, size_t n) { memcpy(pBuffer + _wpos, ptr, n); _wpos += n; }
uint8 ReadUInt8() { uint8 value = *(uint8 *)(pBuffer + _rpos); _rpos += sizeof(uint8); return value; }
uint8 ReadUInt8(uint32 Offset) const { uint8 value = *(uint8 *)(pBuffer + Offset); return value; }

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
#define CLIENTVERSIONS_H
@ -46,32 +67,87 @@ static const char* ClientVersionName(ClientVersion version)
switch (version)
{
case ClientVersion::Unknown:
return "ClientVersion::Unknown";
return "Unknown";
case ClientVersion::Client62:
return "ClientVersion::Client62";
return "Client62";
case ClientVersion::Titanium:
return "ClientVersion::Titanium";
return "Titanium";
case ClientVersion::SoF:
return "ClientVersion::SoF";
return "SoF";
case ClientVersion::SoD:
return "ClientVersion::SoD";
return "SoD";
case ClientVersion::UF:
return "ClientVersion::UF";
return "UF";
case ClientVersion::RoF:
return "ClientVersion::RoF";
return "RoF";
case ClientVersion::RoF2:
return "ClientVersion::RoF2";
return "RoF2";
case ClientVersion::MobNPC:
return "ClientVersion::MobNPC";
return "MobNPC";
case ClientVersion::MobMerc:
return "ClientVersion::MobMerc";
return "MobMerc";
case ClientVersion::MobBot:
return "ClientVersion::MobBot";
return "MobBot";
case ClientVersion::MobPet:
return "ClientVersion::MobPet";
return "MobPet";
default:
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 */

View File

@ -1984,7 +1984,7 @@ void Database::GetRaidLeadershipInfo(uint32 rid, char *maintank,
void Database::SetRaidGroupLeaderInfo(uint32 gid, uint32 rid)
{
std::string query = StringFormat("UPDATE raid_leaders SET leadershipaa = '', WHERE gid = %lu AND rid = %lu",
std::string query = StringFormat("UPDATE raid_leaders SET leadershipaa = '' WHERE gid = %lu AND rid = %lu",
(unsigned long)gid, (unsigned long)rid);
auto results = QueryDatabase(query);
@ -2154,6 +2154,16 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings)
log_settings[log_category].log_to_file = atoi(row[3]);
log_settings[log_category].log_to_gmsay = atoi(row[4]);
/* Determine if any output method is enabled for the category
and set it to 1 so it can used to check if category is enabled */
const bool log_to_console = log_settings[log_category].log_to_console > 0;
const bool log_to_file = log_settings[log_category].log_to_file > 0;
const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay;
if (is_category_enabled)
log_settings[log_category].is_category_enabled = 1;
/*
This determines whether or not the process needs to actually file log anything.
If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open
@ -2162,4 +2172,4 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings)
Log.file_logs_enabled = true;
}
}
}
}

View File

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

@ -110,7 +110,8 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo
/* Implement Logging at the Root */
if (mysql_errno(&mysql) > 0 && strlen(query) > 0){
Log.Out(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query);
if (Log.log_settings[Logs::MySQLError].is_category_enabled == 1)
Log.Out(Logs::General, Logs::MySQLError, "%i: %s \n %s", mysql_errno(&mysql), mysql_error(&mysql), query);
}
return MySQLRequestResult(nullptr, 0, 0, 0, 0, mysql_errno(&mysql),errorBuffer);
@ -125,8 +126,9 @@ MySQLRequestResult DBcore::QueryDatabase(const char* query, uint32 querylen, boo
rowCount = (uint32)mysql_num_rows(res);
MySQLRequestResult requestResult(res, (uint32)mysql_affected_rows(&mysql), rowCount, (uint32)mysql_field_count(&mysql), (uint32)mysql_insert_id(&mysql));
Log.Out(Logs::General, Logs::MySQLQuery, "%s (%u rows returned)", query, rowCount, requestResult.RowCount());
if (Log.log_settings[Logs::MySQLQuery].is_category_enabled == 1)
Log.Out(Logs::General, Logs::MySQLQuery, "%s (%u rows returned)", query, rowCount, requestResult.RowCount());
return requestResult;
}

View File

@ -1,7 +1,7 @@
/*
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
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
//
uint16 EmuConstants::InventoryMapSize(int16 indexMap) {
switch (indexMap) {
uint16 EmuConstants::InventoryMapSize(int16 indexMap)
{
switch (indexMap)
{
case MapPossessions:
return MAP_POSSESSIONS_SIZE;
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...
std::string ret_str;
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) {
switch (indexMap) {
std::string EmuConstants::InventoryMapName(int16 indexMap)
{
switch (indexMap)
{
case INVALID_INDEX:
return "Invalid Map";
case MapPossessions:
@ -100,7 +105,7 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapBank:
return "Bank";
case MapSharedBank:
return "Shared Bank";
return "SharedBank";
case MapTrade:
return "Trade";
case MapWorld:
@ -110,9 +115,9 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapTribute:
return "Tribute";
case MapTrophyTribute:
return "Trophy Tribute";
return "TrophyTribute";
case MapGuildTribute:
return "Guild Tribute";
return "GuildTribute";
case MapMerchant:
return "Merchant";
case MapDeleted:
@ -124,23 +129,23 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
case MapInspect:
return "Inspect";
case MapRealEstate:
return "Real Estate";
return "RealEstate";
case MapViewMODPC:
return "View MOD PC";
return "ViewMODPC";
case MapViewMODBank:
return "View MOD Bank";
return "ViewMODBank";
case MapViewMODSharedBank:
return "View MOD Shared Bank";
return "ViewMODSharedBank";
case MapViewMODLimbo:
return "View MOD Limbo";
return "ViewMODLimbo";
case MapAltStorage:
return "Alt Storage";
return "AltStorage";
case MapArchived:
return "Archived";
case MapMail:
return "Mail";
case MapGuildTrophyTribute:
return "Guild Trophy Tribute";
return "GuildTrophyTribute";
case MapKrono:
return "Krono";
case MapOther:
@ -150,20 +155,22 @@ std::string EmuConstants::InventoryMapName(int16 indexMap) {
}
}
std::string EmuConstants::InventoryMainName(int16 indexMain) {
switch (indexMain) {
std::string EmuConstants::InventoryMainName(int16 indexMain)
{
switch (indexMain)
{
case INVALID_INDEX:
return "Invalid Main";
case MainCharm:
return "Charm";
case MainEar1:
return "Ear 1";
return "Ear1";
case MainHead:
return "Head";
case MainFace:
return "Face";
case MainEar2:
return "Ear 2";
return "Ear2";
case MainNeck:
return "Neck";
case MainShoulders:
@ -173,9 +180,9 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainBack:
return "Back";
case MainWrist1:
return "Wrist 1";
return "Wrist1";
case MainWrist2:
return "Wrist 2";
return "Wrist2";
case MainRange:
return "Range";
case MainHands:
@ -185,9 +192,9 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainSecondary:
return "Secondary";
case MainFinger1:
return "Finger 1";
return "Finger1";
case MainFinger2:
return "Finger 2";
return "Finger2";
case MainChest:
return "Chest";
case MainLegs:
@ -197,30 +204,30 @@ std::string EmuConstants::InventoryMainName(int16 indexMain) {
case MainWaist:
return "Waist";
case MainPowerSource:
return "Power Source";
return "PowerSource";
case MainAmmo:
return "Ammo";
case MainGeneral1:
return "General 1";
return "General1";
case MainGeneral2:
return "General 2";
return "General2";
case MainGeneral3:
return "General 3";
return "General3";
case MainGeneral4:
return "General 4";
return "General4";
case MainGeneral5:
return "General 5";
return "General5";
case MainGeneral6:
return "General 6";
return "General6";
case MainGeneral7:
return "General 7";
return "General7";
case MainGeneral8:
return "General 8";
return "General8";
/*
case MainGeneral9:
return "General 9";
return "General9";
case MainGeneral10:
return "General 10";
return "General10";
*/
case MainCursor:
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)
return "Invalid Sub";
@ -237,12 +245,13 @@ std::string EmuConstants::InventorySubName(int16 indexSub) {
return "Unknown Sub";
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;
}
std::string EmuConstants::InventoryAugName(int16 indexAug) {
std::string EmuConstants::InventoryAugName(int16 indexAug)
{
if (indexAug == INVALID_INDEX)
return "Invalid Aug";
@ -250,7 +259,7 @@ std::string EmuConstants::InventoryAugName(int16 indexAug) {
return "Unknown Aug";
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;
}
@ -260,14 +269,16 @@ std::string EmuConstants::InventoryAugName(int16 indexAug) {
// class EQLimits
//
// client validation
bool EQLimits::IsValidPCClientVersion(ClientVersion clientVersion) {
bool EQLimits::IsValidPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT)
return true;
return false;
}
ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion) {
ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_PC_CLIENT)
return clientVersion;
@ -275,14 +286,16 @@ ClientVersion EQLimits::ValidatePCClientVersion(ClientVersion clientVersion) {
}
// npc validation
bool EQLimits::IsValidNPCClientVersion(ClientVersion clientVersion) {
bool EQLimits::IsValidNPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT)
return true;
return false;
}
ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion) {
ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion)
{
if (clientVersion > LAST_PC_CLIENT && clientVersion <= LAST_NPC_CLIENT)
return clientVersion;
@ -290,22 +303,47 @@ ClientVersion EQLimits::ValidateNPCClientVersion(ClientVersion clientVersion) {
}
// mob validation
bool EQLimits::IsValidMobClientVersion(ClientVersion clientVersion) {
bool EQLimits::IsValidMobClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT)
return true;
return false;
}
ClientVersion EQLimits::ValidateMobClientVersion(ClientVersion clientVersion) {
ClientVersion EQLimits::ValidateMobClientVersion(ClientVersion clientVersion)
{
if (clientVersion > ClientVersion::Unknown && clientVersion <= LAST_NPC_CLIENT)
return clientVersion;
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
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...)
// 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
@ -704,7 +742,8 @@ uint16 EQLimits::InventoryMapSize(int16 indexMap, ClientVersion clientVersion) {
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...
// 0x0000000000200000 is SlotPowerSource (SoF+)
// 0x0000000080000000 is SlotGeneral9 (RoF+)
@ -730,7 +769,8 @@ uint64 EQLimits::PossessionsBitmask(ClientVersion 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] = {
/*Unknown*/ NOT_USED,
/*62*/ 0x00000000005FFFFF,
@ -751,7 +791,8 @@ uint64 EQLimits::EquipmentBitmask(ClientVersion 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] = {
/*Unknown*/ NOT_USED,
/*62*/ 0x000000007F800000,
@ -772,7 +813,8 @@ uint64 EQLimits::GeneralBitmask(ClientVersion 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] = {
/*Unknown*/ NOT_USED,
/*62*/ 0x0000000200000000,
@ -793,7 +835,8 @@ uint64 EQLimits::CursorBitmask(ClientVersion 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] = {
/*Unknown*/ false,
/*62*/ false,
@ -814,7 +857,8 @@ bool EQLimits::AllowsEmptyBagInBag(ClientVersion 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] = {
/*Unknown*/ false,
/*62*/ false,
@ -835,7 +879,8 @@ bool EQLimits::AllowsClickCastFromBag(ClientVersion clientVersion) {
}
// items
uint16 EQLimits::ItemCommonSize(ClientVersion clientVersion) {
uint16 EQLimits::ItemCommonSize(ClientVersion clientVersion)
{
static const uint16 local[CLIENT_VERSION_COUNT] = {
/*Unknown*/ NOT_USED,
/*62*/ EmuConstants::ITEM_COMMON_SIZE,
@ -855,7 +900,8 @@ uint16 EQLimits::ItemCommonSize(ClientVersion 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] = {
/*Unknown*/ NOT_USED,
/*62*/ EmuConstants::ITEM_CONTAINER_SIZE,
@ -875,7 +921,8 @@ uint16 EQLimits::ItemContainerSize(ClientVersion 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] = {
/*Unknown*/ true,
/*62*/ true,
@ -894,63 +941,3 @@ bool EQLimits::CoinHasWeight(ClientVersion 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
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
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 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
public:
// database
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
static uint16 InventoryMapSize(int16 indexMap);
//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_CONTAINER_SIZE = Titanium::consts::ITEM_CONTAINER_SIZE;
// player profile
//static const uint32 CLASS_BITMASK = 0; // needs value
//static const uint32 RACE_BITMASK = 0; // needs value
// BANDOLIERS_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?)
static const size_t BANDOLIERS_SIZE = RoF2::consts::BANDOLIERS_SIZE; // number of bandolier instances
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
static const uint32 BANDOLIERS_COUNT = Titanium::consts::BANDOLIERS_COUNT; // count = number of bandolier instances
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;
// POTION_BELT_SIZE sets maximum limit..active limit will need to be handled by the appropriate AA or spell (or item?)
static const size_t POTION_BELT_ITEM_COUNT = RoF2::consts::POTION_BELT_ITEM_COUNT;
static const size_t TEXT_LINK_BODY_LENGTH = 56;
// legacy-related functions
//static int ServerToPerlSlot(int slot); // encode
//static int PerlToServerSlot(int slot); // decode
static const size_t TEXT_LINK_BODY_LENGTH = RoF2::consts::TEXT_LINK_BODY_LENGTH;
};
class EQLimits {
class EQLimits
{
// 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...)
@ -174,6 +172,9 @@ public:
static bool IsValidMobClientVersion(ClientVersion clientVersion);
static ClientVersion ValidateMobClientVersion(ClientVersion clientVersion);
// database
static size_t CharacterCreationLimit(ClientVersion clientVersion);
// inventory
static uint16 InventoryMapSize(int16 indexMap, ClientVersion clientVersion);
static uint64 PossessionsBitmask(ClientVersion clientVersion);
@ -190,11 +191,6 @@ public:
// player profile
static bool CoinHasWeight(ClientVersion clientVersion);
static uint32 BandoliersCount(ClientVersion clientVersion);
static uint32 BandolierSize(ClientVersion clientVersion);
static uint32 PotionBeltSize(ClientVersion clientVersion);
};
#endif /* EQ_DICTIONARY_H */

View File

@ -24,6 +24,7 @@
#include "platform.h"
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdio.h>
#ifndef STATIC_OPCODE
@ -510,3 +511,8 @@ void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) {
// DumpPacketAscii(app->pBuffer, app->size);
}
std::string DumpPacketToString(const EQApplicationPacket* app){
std::ostringstream out;
out << DumpPacketHexToString(app->pBuffer, app->size);
return out.str();
}

View File

@ -20,6 +20,7 @@
#include "base_packet.h"
#include "platform.h"
#include <iostream>
#ifdef STATIC_OPCODE
typedef unsigned short EmuOpcode;
@ -146,6 +147,6 @@ protected:
};
extern void DumpPacket(const EQApplicationPacket* app, bool iShowInfo = false);
extern std::string DumpPacketToString(const EQApplicationPacket* app);
#endif

View File

@ -15,6 +15,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef EQ_PACKET_STRUCTS_H
#define EQ_PACKET_STRUCTS_H
@ -123,83 +124,80 @@ struct LDoNTrapTemplate
///////////////////////////////////////////////////////////////////////////////
/*
** Color_Struct
** Size: 4 bytes
** Used for convenience
** Merth: Gave struct a name so gcc 2.96 would compile
**
*/
// All clients translate the character select information to some degree
struct Color_Struct
{
union
{
struct
{
uint8 blue;
uint8 green;
uint8 red;
uint8 use_tint; // if there's a tint this is FF
} rgb;
uint32 color;
union {
struct {
uint8 Blue;
uint8 Green;
uint8 Red;
uint8 UseTint; // if there's a tint this is FF
} RGB;
uint32 Color;
};
};
/*
* 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 EquipStruct
{
uint32 Material;
uint32 Unknown1;
uint32 EliteMaterial;
uint32 HeroForgeModel;
uint32 Material2; // Same as material?
};
struct CharSelectEquip {
uint32 material;
uint32 unknown1;
uint32 elitematerial;
uint32 heroforgemodel;
uint32 material2;
Color_Struct color;
struct CharSelectEquip
{
uint32 Material;
uint32 Unknown1;
uint32 EliteMaterial;
uint32 HeroForgeModel;
uint32 Material2;
Color_Struct Color;
};
/*
** Character Selection Struct
** Length: 1704 Bytes
**
*/
struct CharacterSelect_Struct {
/*0000*/ uint32 race[10]; // Characters Race
/*0040*/ //Color_Struct cs_colors[10][9]; // Characters Equipment Colors
/*0400*/ uint8 beardcolor[10]; // Characters beard Color
/*0410*/ uint8 hairstyle[10]; // Characters hair style
/*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)
/*0000*/ CharSelectEquip equip[10][9];
/*0780*/ uint32 secondary[10]; // Characters secondary IDFile number
/*0820*/ uint32 drakkin_heritage[10]; // added for SoF
/*0860*/ uint32 drakkin_tattoo[10]; // added for SoF
/*0900*/ uint32 drakkin_details[10]; // added for SoF
/*0940*/ uint32 deity[10]; // Characters Deity
/*0980*/ uint8 gohome[10]; // 1=Go Home available, 0=not
/*0990*/ uint8 tutorial[10]; // 1=Tutorial available, 0=not
/*1000*/ uint8 beard[10]; // Characters Beard Type
/*1010*/ uint8 unknown902[10]; // 10x ff
/*1020*/ uint32 primary[10]; // Characters primary IDFile number
/*1060*/ uint8 haircolor[10]; // Characters Hair Color
/*1070*/ uint8 unknown0962[2]; // 2x 00
/*1072*/ uint32 zone[10]; // Characters Current Zone
/*1112*/ uint8 class_[10]; // Characters Classes
/*1022*/ uint8 face[10]; // Characters Face Type
/*1032*/ char name[10][64]; // Characters Names
/*1672*/ uint8 gender[10]; // Characters Gender
/*1682*/ uint8 eyecolor1[10]; // Characters Eye Color
/*1692*/ uint8 eyecolor2[10]; // Characters Eye 2 Color
/*1702*/ uint8 level[10]; // Characters Levels
/*1712*/
struct CharacterSelectEntry_Struct
{
char Name[64];
uint8 Class;
uint32 Race;
uint8 Level;
uint8 ShroudClass;
uint32 ShroudRace;
uint16 Zone;
uint16 Instance;
uint8 Gender;
uint8 Face;
CharSelectEquip Equip[9];
uint8 Unknown15; // Seen FF
uint8 Unknown19; // Seen FF
uint32 DrakkinTattoo;
uint32 DrakkinDetails;
uint32 Deity;
uint32 PrimaryIDFile;
uint32 SecondaryIDFile;
uint8 HairColor;
uint8 BeardColor;
uint8 EyeColor1;
uint8 EyeColor2;
uint8 HairStyle;
uint8 Beard;
uint8 Enabled;
uint8 Tutorial; // Seen 1 for new char or 0 for existing
uint32 DrakkinHeritage;
uint8 Unknown1; // Seen 0
uint8 GoHome; // Seen 0 for new char and 1 for existing
uint32 LastLogin;
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;
};
//len = 72
struct BandolierItem_Struct {
uint32 item_id;
uint32 icon;
char item_name[64];
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
// Bandolier item positions
enum
{
bandolierPrimary = 0,
bandolierSecondary,
bandolierRange,
bandolierAmmo
};
struct Bandolier_Struct {
char name[32];
BandolierItem_Struct items[EmuConstants::BANDOLIER_SIZE];
};
struct PotionBelt_Struct {
BandolierItem_Struct items[EmuConstants::POTION_BELT_SIZE];
//len = 72
struct BandolierItem_Struct
{
uint32 ID;
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 SlotNumber;
uint32 ItemID;
@ -1103,7 +1118,7 @@ struct PlayerProfile_Struct
/*12800*/ uint32 expAA;
/*12804*/ uint32 aapoints; //avaliable, unspent
/*12808*/ uint8 unknown12844[36];
/*12844*/ Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_COUNT];
/*12844*/ Bandolier_Struct bandoliers[EmuConstants::BANDOLIERS_SIZE];
/*14124*/ uint8 unknown14160[4506];
/*18630*/ SuspendedMinion_Struct SuspendedMinion; // No longer in use
/*19240*/ uint32 timeentitledonaccount;
@ -2761,7 +2776,8 @@ struct BazaarWelcome_Struct {
BazaarWindowStart_Struct Beginning;
uint32 Traders;
uint32 Items;
uint8 Unknown012[8];
uint32 Unknown012;
uint32 Unknown016;
};
struct BazaarSearch_Struct {
@ -3146,6 +3162,7 @@ struct Trader_ShowItems_Struct{
/*000*/ uint32 Code;
/*004*/ uint32 TraderID;
/*008*/ uint32 Unknown08[3];
/*020*/
};
struct TraderBuy_Struct{
@ -3191,9 +3208,10 @@ struct TraderDelItem_Struct{
struct TraderClick_Struct{
/*000*/ uint32 TraderID;
/*004*/ uint32 Unknown004;
/*004*/ uint32 Code;
/*008*/ uint32 Unknown008;
/*012*/ uint32 Approval;
/*016*/
};
struct FormattedMessage_Struct{
@ -4104,30 +4122,35 @@ struct DynamicWall_Struct {
/*80*/
};
enum { //bandolier actions
BandolierCreate = 0,
BandolierRemove = 1,
BandolierSet = 2
// Bandolier actions
enum
{
bandolierCreate = 0,
bandolierRemove,
bandolierSet
};
struct BandolierCreate_Struct {
/*00*/ uint32 action; //0 for create
/*04*/ uint8 number;
/*05*/ char name[32];
/*37*/ uint16 unknown37; //seen 0x93FD
/*39*/ uint8 unknown39; //0
struct BandolierCreate_Struct
{
/*00*/ uint32 Action; //0 for create
/*04*/ uint8 Number;
/*05*/ char Name[32];
/*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
};
struct BandolierDelete_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierDelete_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct BandolierSet_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierSet_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct Arrow_Struct {
@ -4530,19 +4553,12 @@ struct InternalVeteranReward
/*012*/ InternalVeteranRewardItem items[8];
};
struct VeteranClaimReply
struct VeteranClaim
{
/*000*/ char name[64];
/*064*/ uint32 claim_id;
/*068*/ uint32 reject_field;
/*072*/ uint32 unknown072;
};
struct VeteranClaimRequest
{
/*000*/ char name_data[64]; //name + other data
/*000*/ char name[64]; //name + other data
/*064*/ uint32 claim_id;
/*068*/ uint32 unknown068;
/*072*/ uint32 action;
};
struct GMSearchCorpse_Struct

View File

@ -23,6 +23,7 @@
#include "op_codes.h"
#include "crc16.h"
#include "platform.h"
#include "string_util.h"
#include <string>
#include <iomanip>
@ -555,9 +556,21 @@ void EQStream::FastQueuePacket(EQApplicationPacket **p, bool ack_req)
void EQStream::SendPacket(uint16 opcode, EQApplicationPacket *p)
{
uint32 chunksize,used;
uint32 chunksize, used;
uint32 length;
if (Log.log_settings[Logs::Server_Client_Packet].is_category_enabled == 1){
if (p->GetOpcode() != OP_SpecialMesg){
Log.Out(Logs::General, Logs::Server_Client_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size());
}
}
if (Log.log_settings[Logs::Server_Client_Packet_With_Dump].is_category_enabled == 1){
if (p->GetOpcode() != OP_SpecialMesg){
Log.Out(Logs::General, Logs::Server_Client_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->GetOpcode(), p->Size(), DumpPacketToString(p).c_str());
}
}
// Convert the EQApplicationPacket to 1 or more EQProtocolPackets
if (p->size>(MaxLen-8)) { // proto-op(2), seq(2), app-op(2) ... data ... crc(2)
Log.Out(Logs::Detail, Logs::Netcode, _L "Making oversized packet, len %d" __L, p->size);
@ -951,14 +964,12 @@ EQRawApplicationPacket *p=nullptr;
}
MInboundQueue.unlock();
//resolve the opcode if we can.
if(p) {
if(OpMgr != nullptr && *OpMgr != nullptr) {
if (p) {
if (OpMgr != nullptr && *OpMgr != nullptr) {
EmuOpcode emu_op = (*OpMgr)->EQToEmu(p->opcode);
if (emu_op == OP_Unknown) {
Log.Out(Logs::General, Logs::Netcode, "[ERROR] Unable to convert EQ opcode 0x%.4x to an Application opcode.", p->opcode);
}
// Log.Out(Logs::General, Logs::Client_Server_Packet_Unhandled, "Unknown :: [%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(p->GetOpcode()), p->opcode, p->Size(), DumpPacketToString(p).c_str());
}
p->SetOpcode(emu_op);
}
}

View File

@ -288,7 +288,7 @@ void EQEmuLogSys::Out(Logs::DebugLevel debug_level, uint16 log_category, std::st
if (log_to_console) EQEmuLogSys::ProcessConsoleMessage(debug_level, log_category, output_debug_message);
if (log_to_gmsay) EQEmuLogSys::ProcessGMSay(debug_level, log_category, output_debug_message);
if (log_to_file) EQEmuLogSys::ProcessLogWrite(debug_level, log_category, output_message);
if (log_to_file) EQEmuLogSys::ProcessLogWrite(debug_level, log_category, output_debug_message);
}
void EQEmuLogSys::SetCurrentTimeStamp(char* time_stamp)

View File

@ -78,6 +78,10 @@ namespace Logs {
MySQLQuery,
Mercenaries,
QuestDebug,
Server_Client_Packet,
Client_Server_Packet_Unhandled,
Server_Client_Packet_With_Dump,
Client_Server_Packet_With_Dump,
MaxCategoryID /* Don't Remove this*/
};
@ -88,7 +92,7 @@ namespace Logs {
"AI",
"Aggro",
"Attack",
"Client Server Packet",
"Packet :: Client -> Server",
"Combat",
"Commands",
"Crash",
@ -121,7 +125,11 @@ namespace Logs {
"MySQL Error",
"MySQL Query",
"Mercenaries",
"Quest Debug"
"Quest Debug",
"Packet :: Server -> Client",
"Packet :: Client -> Server Unhandled",
"Packet :: Server -> Client (Dump)",
"Packet :: Client -> Server (Dump)",
};
}
@ -160,6 +168,7 @@ public:
uint8 log_to_file;
uint8 log_to_console;
uint8 log_to_gmsay;
uint8 is_category_enabled; /* When any log output in a category > 0, set this to 1 as (Enabled) */
};
/* Internally used memory reference for all log settings per category.

View File

@ -506,6 +506,7 @@ int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where)
return slot_id;
}
// Behavioral change - Limbo is no longer checked due to improper handling of return value
if (where & invWhereCursor) {
// Check cursor queue
slot_id = _HasItem(m_cursor, item_id, quantity);
@ -552,6 +553,7 @@ int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where)
return slot_id;
}
// Behavioral change - Limbo is no longer checked due to improper handling of return value
if (where & invWhereCursor) {
// Check cursor queue
slot_id = _HasItemByUse(m_cursor, use, quantity);
@ -597,6 +599,7 @@ int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where)
return slot_id;
}
// Behavioral change - Limbo is no longer checked due to improper handling of return value
if (where & invWhereCursor) {
// Check cursor queue
slot_id = _HasItemByLoreGroup(m_cursor, loregroup);
@ -1011,7 +1014,8 @@ uint8 Inventory::FindHighestLightValue()
if (inst == nullptr) { continue; }
auto item = inst->GetItem();
if (item == nullptr) { continue; }
if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight) { continue; }
// 'Gloomingdeep lantern' is ItemTypeArmor in the database..there may be others instances and/or types that need to be handled
if (item->ItemType != ItemTypeMisc && item->ItemType != ItemTypeLight && item->ItemType != ItemTypeArmor) { continue; }
if (item->Light & 0xF0) { continue; }
if (item->Light > light_value) { light_value = item->Light; }
}
@ -1059,7 +1063,7 @@ int Inventory::GetSlotByItemInstCollection(const std::map<int16, ItemInst*> &col
}
if (t_inst && !t_inst->IsType(ItemClassContainer)) {
for (auto b_iter = t_inst->_begin(); b_iter != t_inst->_end(); ++b_iter) {
for (auto b_iter = t_inst->_cbegin(); b_iter != t_inst->_cend(); ++b_iter) {
if (b_iter->second == inst) {
return Inventory::CalcSlotId(iter->first, b_iter->first);
}
@ -1070,13 +1074,10 @@ int Inventory::GetSlotByItemInstCollection(const std::map<int16, ItemInst*> &col
return -1;
}
void Inventory::dumpItemCollection(const std::map<int16, ItemInst*> &collection) {
iter_inst it;
iter_contents itb;
ItemInst* inst = nullptr;
for (it = collection.begin(); it != collection.end(); ++it) {
inst = it->second;
void Inventory::dumpItemCollection(const std::map<int16, ItemInst*> &collection)
{
for (auto it = collection.cbegin(); it != collection.cend(); ++it) {
auto inst = it->second;
if (!inst || !inst->GetItem())
continue;
@ -1087,14 +1088,13 @@ void Inventory::dumpItemCollection(const std::map<int16, ItemInst*> &collection)
}
}
void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) {
iter_contents itb;
void Inventory::dumpBagContents(ItemInst *inst, std::map<int16, ItemInst*>::const_iterator *it)
{
if (!inst || !inst->IsType(ItemClassContainer))
return;
// Go through bag, if bag
for (itb = inst->_begin(); itb != inst->_end(); ++itb) {
for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) {
ItemInst* baginst = itb->second;
if (!baginst || !baginst->GetItem())
continue;
@ -1109,7 +1109,7 @@ void Inventory::dumpBagContents(ItemInst *inst, iter_inst *it) {
// Internal Method: Retrieves item within an inventory bucket
ItemInst* Inventory::_GetItem(const std::map<int16, ItemInst*>& bucket, int16 slot_id) const
{
iter_inst it = bucket.find(slot_id);
auto it = bucket.find(slot_id);
if (it != bucket.end()) {
return it->second;
}
@ -1122,6 +1122,8 @@ ItemInst* Inventory::_GetItem(const std::map<int16, ItemInst*>& bucket, int16 sl
// Assumes item has already been allocated
int16 Inventory::_PutItem(int16 slot_id, ItemInst* inst)
{
// What happens here when we _PutItem(MainCursor)? Bad things..really bad things...
//
// If putting a nullptr into slot, we need to remove slot without memory delete
if (inst == nullptr) {
//Why do we not delete the poped item here????
@ -1203,7 +1205,7 @@ int16 Inventory::_HasItem(std::map<int16, ItemInst*>& bucket, uint32 item_id, ui
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1234,7 +1236,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity)
uint8 quantity_found = 0;
for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) {
for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) {
auto inst = *iter;
if (inst == nullptr) { continue; }
@ -1251,7 +1253,7 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity)
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1266,6 +1268,9 @@ int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity)
return legacy::SLOT_AUGMENT;
}
}
// We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo)
break;
}
return INVALID_INDEX;
@ -1288,7 +1293,7 @@ int16 Inventory::_HasItemByUse(std::map<int16, ItemInst*>& bucket, uint8 use, ui
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1308,7 +1313,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity)
{
uint8 quantity_found = 0;
for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) {
for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) {
auto inst = *iter;
if (inst == nullptr) { continue; }
@ -1320,7 +1325,7 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity)
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1330,6 +1335,9 @@ int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity)
return Inventory::CalcSlotId(MainCursor, bag_iter->first);
}
}
// We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo)
break;
}
return INVALID_INDEX;
@ -1354,7 +1362,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map<int16, ItemInst*>& bucket, uint32
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1377,7 +1385,7 @@ int16 Inventory::_HasItemByLoreGroup(std::map<int16, ItemInst*>& bucket, uint32
// Internal Method: Checks an inventory queue type bucket for a particular item
int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup)
{
for (auto iter = iqueue.begin(); iter != iqueue.end(); ++iter) {
for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) {
auto inst = *iter;
if (inst == nullptr) { continue; }
@ -1394,7 +1402,7 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup)
if (!inst->IsType(ItemClassContainer)) { continue; }
for (auto bag_iter = inst->_begin(); bag_iter != inst->_end(); ++bag_iter) {
for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) {
auto bag_inst = bag_iter->second;
if (bag_inst == nullptr) { continue; }
@ -1409,6 +1417,9 @@ int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup)
return legacy::SLOT_AUGMENT;
}
}
// We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo)
break;
}
return INVALID_INDEX;
@ -1504,8 +1515,7 @@ ItemInst::ItemInst(const ItemInst& copy)
m_attuned=copy.m_attuned;
m_merchantcount=copy.m_merchantcount;
// Copy container contents
iter_contents it;
for (it=copy.m_contents.begin(); it!=copy.m_contents.end(); ++it) {
for (auto it = copy.m_contents.begin(); it != copy.m_contents.end(); ++it) {
ItemInst* inst_old = it->second;
ItemInst* inst_new = nullptr;
@ -1675,7 +1685,7 @@ bool ItemInst::IsAugmentSlotAvailable(int32 augtype, uint8 slot) const
// Retrieve item inside container
ItemInst* ItemInst::GetItem(uint8 index) const
{
iter_contents it = m_contents.find(index);
auto it = m_contents.find(index);
if (it != m_contents.end()) {
return it->second;
}
@ -1738,7 +1748,7 @@ void ItemInst::ClearByFlags(byFlagSetting is_nodrop, byFlagSetting is_norent)
// TODO: This needs work...
// Destroy container contents
iter_contents cur, end, del;
std::map<uint8, ItemInst*>::const_iterator cur, end, del;
cur = m_contents.begin();
end = m_contents.end();
for (; cur != end;) {

View File

@ -33,9 +33,6 @@ class EvolveInfo; // Stores information about an evolving item family
#include <list>
#include <map>
// Helper typedefs
typedef std::map<int16, ItemInst*>::const_iterator iter_inst;
typedef std::map<uint8, ItemInst*>::const_iterator iter_contents;
namespace ItemField
{
@ -86,8 +83,8 @@ public:
// Public Methods
/////////////////////////
inline std::list<ItemInst*>::const_iterator begin() { return m_list.begin(); }
inline std::list<ItemInst*>::const_iterator end() { return m_list.end(); }
inline std::list<ItemInst*>::const_iterator cbegin() { return m_list.cbegin(); }
inline std::list<ItemInst*>::const_iterator cend() { return m_list.cend(); }
inline int size() { return static_cast<int>(m_list.size()); } // TODO: change to size_t
inline bool empty() { return m_list.empty(); }
@ -140,8 +137,8 @@ public:
ItemInst* GetItem(int16 slot_id) const;
ItemInst* GetItem(int16 slot_id, uint8 bagidx) const;
inline std::list<ItemInst*>::const_iterator cursor_begin() { return m_cursor.begin(); }
inline std::list<ItemInst*>::const_iterator cursor_end() { return m_cursor.end(); }
inline std::list<ItemInst*>::const_iterator cursor_cbegin() { return m_cursor.cbegin(); }
inline std::list<ItemInst*>::const_iterator cursor_cend() { return m_cursor.cend(); }
inline int CursorSize() { return m_cursor.size(); }
inline bool CursorEmpty() { return m_cursor.empty(); }
@ -227,7 +224,7 @@ protected:
int GetSlotByItemInstCollection(const std::map<int16, ItemInst*> &collection, ItemInst *inst);
void dumpItemCollection(const std::map<int16, ItemInst*> &collection);
void dumpBagContents(ItemInst *inst, iter_inst *it);
void dumpBagContents(ItemInst *inst, std::map<int16, ItemInst*>::const_iterator *it);
// Retrieves item within an inventory bucket
ItemInst* _GetItem(const std::map<int16, ItemInst*>& bucket, int16 slot_id) const;
@ -425,8 +422,8 @@ protected:
//////////////////////////
// Protected Members
//////////////////////////
iter_contents _begin() { return m_contents.begin(); }
iter_contents _end() { return m_contents.end(); }
std::map<uint8, ItemInst*>::const_iterator _cbegin() { return m_contents.cbegin(); }
std::map<uint8, ItemInst*>::const_iterator _cend() { return m_contents.cend(); }
friend class Inventory;

View File

@ -180,7 +180,7 @@ IN(OP_GMLastName, GMLastName_Struct);
IN(OP_GMToggle, GMToggle_Struct);
IN(OP_LFGCommand, LFG_Struct);
IN(OP_GMGoto, GMSummon_Struct);
IN(OP_TraderShop, TraderClick_Struct);
INv(OP_TraderShop, TraderClick_Struct);
IN(OP_ShopRequest, Merchant_Click_Struct);
IN(OP_Bazaar, BazaarSearch_Struct);
//alt:IN(OP_Bazaar, BazaarWelcome_Struct); //alternate structure for OP_Bazaar
@ -399,7 +399,7 @@ OUT(OP_Weather, Weather_Struct);
OUT(OP_ZoneChange, ZoneChange_Struct);
OUT(OP_ZoneInUnknown, ZoneInUnknown_Struct);
//this is the set of opcodes which are allready listed
//this is the set of opcodes which are already listed
//in the IN section above, but are also sent OUT
#ifdef DISJOINT_DIRECTIONS
OUTz(OP_ClientReady); //follows OP_SetServerFilter
@ -449,7 +449,7 @@ OUT(OP_Trader, TraderBuy_Struct); //3 possible lengths
//alt:OUT(OP_Trader, Trader_ShowItems_Struct);
//alt:OUT(OP_Trader, Trader_Struct);
OUT(OP_TraderBuy, TraderBuy_Struct);
OUT(OP_TraderShop, TraderClick_Struct);
OUTv(OP_TraderShop, TraderClick_Struct);
OUT(OP_WearChange, WearChange_Struct);
OUT(OP_ZoneEntry, ServerZoneEntry_Struct);
#endif

View File

@ -17,6 +17,7 @@
*/
#include <iostream>
#include <sstream>
#include <iomanip>
#include <stdio.h>
@ -89,6 +90,54 @@ void DumpPacketHex(const uchar* buf, uint32 size, uint32 cols, uint32 skip) {
safe_delete_array(ascii);
}
std::string DumpPacketHexToString(const uchar* buf, uint32 size, uint32 cols, uint32 skip) {
std::ostringstream out;
if (size == 0 || size > 39565)
return "";
out << "\n";
// Output as HEX
char output[4];
int j = 0;
char* ascii = new char[cols + 1];
memset(ascii, 0, cols + 1);
uint32 i;
for (i = skip; i < size; i++)
{
if ((i - skip) % cols == 0) {
if (i != skip)
out << " | " << ascii << std::endl;
out << std::setw(4) << std::setfill(' ') << i - skip << ": ";
memset(ascii, 0, cols + 1);
j = 0;
}
else if ((i - skip) % (cols / 2) == 0) {
out << "- ";
}
sprintf(output, "%02X ", (unsigned char)buf[i]);
out << output;
if (buf[i] >= 32 && buf[i] < 127) {
ascii[j++] = buf[i];
}
else {
ascii[j++] = '.';
}
// std::cout << std::setfill(0) << std::setw(2) << std::hex << (int)buf[i] << " "; // unknown intent [CODEBUG]
}
uint32 k = ((i - skip) - 1) % cols;
if (k < 8)
out << " ";
for (uint32 h = k + 1; h < cols; h++) {
out << " ";
}
out << " | " << ascii << std::endl;
safe_delete_array(ascii);
return out.str();
}
void DumpPacket(const uchar* buf, uint32 size)
{
DumpPacketHex(buf, size);

View File

@ -24,6 +24,7 @@ class ServerPacket;
void DumpPacketAscii(const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0);
void DumpPacketHex(const uchar* buf, uint32 size, uint32 cols=16, uint32 skip=0);
std::string DumpPacketHexToString(const uchar* buf, uint32 size, uint32 cols = 16, uint32 skip = 0);
void DumpPacketBin(const void* data, uint32 len);
void DumpPacket(const uchar* buf, uint32 size);
void DumpPacket(const ServerPacket* pack, bool iShowInfo = false);

View File

@ -15,6 +15,8 @@
#include <iostream>
#include <sstream>
#include <numeric>
#include <cassert>
namespace RoF
{
@ -2043,7 +2045,7 @@ namespace RoF
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
outapp->WriteUInt32(0);
@ -2053,7 +2055,7 @@ namespace RoF
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
outapp->WriteUInt32(0);
@ -2286,46 +2288,52 @@ namespace RoF
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++)
{
outapp->WriteString(emu->bandoliers[r].name);
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j)
{
outapp->WriteString(emu->bandoliers[r].items[j].item_name);
outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id);
outapp->WriteUInt32(emu->bandoliers[r].items[j].icon);
// 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);
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
outapp->WriteString(emu->bandoliers[r].Items[j].Name);
outapp->WriteUInt32(emu->bandoliers[r].Items[j].ID);
if (emu->bandoliers[r].Items[j].Icon) {
outapp->WriteSInt32(emu->bandoliers[r].Items[j].Icon);
}
else {
// If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteSInt32(-1);
}
}
}
for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++)
{
// 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 < EmuConstants::BANDOLIER_SIZE; ++j)
{
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
outapp->WriteString("");
outapp->WriteUInt32(0);
outapp->WriteUInt32(0);
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++)
{
outapp->WriteString(emu->potionbelt.items[r].item_name);
outapp->WriteUInt32(emu->potionbelt.items[r].item_id);
outapp->WriteUInt32(emu->potionbelt.items[r].icon);
// 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].Name);
outapp->WriteUInt32(emu->potionbelt.Items[r].ID);
if (emu->potionbelt.Items[r].Icon) {
outapp->WriteSInt32(emu->potionbelt.Items[r].Icon);
}
else {
// If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteSInt32(-1);
}
}
for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; r++)
{
// Nullify potion belt where server and client indexes diverge, with a client bias
for (uint32 r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
outapp->WriteString("");
outapp->WriteUInt32(0);
outapp->WriteUInt32(0);
outapp->WriteSInt32(-1);
}
outapp->WriteSInt32(-1); // Unknown;
@ -2418,7 +2426,7 @@ namespace RoF
outapp->WriteUInt32(emu->silver_bank);
outapp->WriteUInt32(emu->copper_bank);
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(emu->platinum_shared);
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown
@ -2891,85 +2899,99 @@ namespace RoF
ENCODE(OP_SendCharInfo)
{
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p;
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
// Zero-character count shunt
if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
int char_count;
int namelen = 0;
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]);
FINISH_ENCODE();
return;
}
int total_length = sizeof(structs::CharacterSelect_Struct)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen;
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);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_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];
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
eq->CharCount = character_count;
//eq->TotalChars = emu->TotalChars;
eq->char_count = char_count;
//eq->total_chars = 10;
//if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
// eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries;
int r;
for (r = 0; r < char_count; r++) {
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
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]);
{ //post-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->class_ = emu->class_[r];
eq2->race = emu->race[r];
eq2->level = emu->level[r];
eq2->class_2 = emu->class_[r];
eq2->race2 = emu->race[r];
eq2->zone = emu->zone[r];
eq2->instance = 0;
eq2->gender = emu->gender[r];
eq2->face = emu->face[r];
int k;
for (k = 0; k < _MaterialCount; k++) {
eq2->equip[k].material = emu->equip[r][k].material;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].heroforgemodel = emu->equip[r][k].heroforgemodel;
eq2->equip[k].material2 = emu->equip[r][k].material2;
eq2->equip[k].color.color = emu->equip[r][k].color.color;
}
eq2->u15 = 0xff;
eq2->u19 = 0xFF;
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);
eq_cse->Unknown15 = emu_cse->Unknown15;
eq_cse->Unknown19 = emu_cse->Unknown19;
eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq_cse->Deity = emu_cse->Deity;
eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq_cse->HairColor = emu_cse->HairColor;
eq_cse->BeardColor = emu_cse->BeardColor;
eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq_cse->HairStyle = emu_cse->HairStyle;
eq_cse->Beard = emu_cse->Beard;
eq_cse->Enabled = emu_cse->Enabled;
eq_cse->Tutorial = emu_cse->Tutorial;
eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq_cse->Unknown1 = emu_cse->Unknown1;
eq_cse->GoHome = emu_cse->GoHome;
eq_cse->LastLogin = emu_cse->LastLogin;
eq_cse->Unknown2 = emu_cse->Unknown2;
emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
}
FINISH_ENCODE();
@ -3588,37 +3610,71 @@ namespace RoF
FINISH_ENCODE();
}
ENCODE(OP_VetClaimReply)
{
ENCODE_LENGTH_EXACT(VeteranClaim);
SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim);
memcpy(eq->name, emu->name, sizeof(emu->name));
OUT(claim_id);
OUT(action);
FINISH_ENCODE();
}
ENCODE(OP_VetRewardsAvaliable)
{
EQApplicationPacket *inapp = *p;
unsigned char * __emu_buffer = inapp->pBuffer;
auto __emu_buffer = inapp->pBuffer;
uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward));
*p = nullptr;
EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count));
uchar *old_data = __emu_buffer;
uchar *data = outapp_create->pBuffer;
for (unsigned int i = 0; i < count; ++i)
{
structs::VeteranReward *vr = (structs::VeteranReward*)data;
InternalVeteranReward *ivr = (InternalVeteranReward*)old_data;
// calculate size of names, note the packet DOES NOT have null termed c-strings
std::vector<uint32> name_lengths;
for (int i = 0; i < count; ++i) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
vr->claim_count = ivr->claim_count;
vr->claim_id = ivr->claim_id;
vr->number_available = ivr->number_available;
for (int x = 0; x < 8; ++x)
{
vr->items[x].item_id = ivr->items[x].item_id;
strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name));
vr->items[x].charges = ivr->items[x].charges;
for (int i = 0; i < ivr->claim_count; i++) {
uint32 length = strnlen(ivr->items[i].item_name, 63);
if (length)
name_lengths.push_back(length);
}
old_data += sizeof(InternalVeteranReward);
data += sizeof(structs::VeteranReward);
__emu_buffer += sizeof(InternalVeteranReward);
}
dest->FastQueuePacket(&outapp_create);
uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) +
sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) +
// size of name_lengths is the same as item count
(sizeof(structs::VeteranRewardItem) * name_lengths.size());
// build packet now!
auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size);
__emu_buffer = inapp->pBuffer;
outapp->WriteUInt32(count);
auto name_itr = name_lengths.begin();
for (int i = 0; i < count; i++) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
outapp->WriteUInt32(ivr->claim_id);
outapp->WriteUInt32(ivr->number_available);
outapp->WriteUInt32(ivr->claim_count);
outapp->WriteUInt8(1); // enabled
for (int j = 0; j < ivr->claim_count; j++) {
assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert
outapp->WriteUInt32(*name_itr);
outapp->WriteData(ivr->items[j].item_name, *name_itr);
outapp->WriteUInt32(ivr->items[j].item_id);
outapp->WriteUInt32(ivr->items[j].charges);
++name_itr;
}
__emu_buffer += sizeof(InternalVeteranReward);
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
@ -3633,7 +3689,7 @@ namespace RoF
OUT(elite_material);
OUT(hero_forge_model);
OUT(unknown18);
OUT(color.color);
OUT(color.Color);
OUT(wear_slot_id);
FINISH_ENCODE();
@ -3724,42 +3780,23 @@ namespace RoF
ENCODE(OP_ZonePlayerToBind)
{
ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct);
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer;
__packet->SetWritePosition(0);
__packet->WriteUInt16(emu->bind_zone_id);
__packet->WriteUInt16(emu->bind_instance_id);
__packet->WriteFloat(emu->x);
__packet->WriteFloat(emu->y);
__packet->WriteFloat(emu->z);
__packet->WriteFloat(emu->heading);
__packet->WriteString(emu->zone_name);
__packet->WriteUInt8(1); // save items
__packet->WriteUInt32(0); // hp
__packet->WriteUInt32(0); // mana
__packet->WriteUInt32(0); // endurance
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)];
structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1;
unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)];
structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2;
zph->x = zps->x;
zph->y = zps->y;
zph->z = zps->z;
zph->heading = zps->heading;
zph->bind_zone_id = 0;
zph->bind_instance_id = zps->bind_instance_id;
strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name));
zpf->unknown021 = 1;
zpf->unknown022 = 0;
zpf->unknown023 = 0;
zpf->unknown024 = 0;
ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)));
ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct));
delete[] buffer1;
delete[] buffer2;
delete[](*p)->pBuffer;
(*p)->pBuffer = new unsigned char[ss.str().size()];
(*p)->size = ss.str().size();
memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size());
dest->FastQueuePacket(&(*p));
FINISH_ENCODE();
}
ENCODE(OP_ZoneServerInfo)
@ -3963,18 +4000,18 @@ namespace RoF
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;
for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material;
Equipment[k].unknown1 = emu->equipment[k].unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial;
Equipment[k].heroforgemodel = emu->equipment[k].heroforgemodel;
Equipment[k].material2 = emu->equipment[k].material2;
Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
Equipment[k].HeroForgeModel = emu->equipment[k].HeroForgeModel;
Equipment[k].Material2 = emu->equipment[k].Material2;
}
Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3987,13 +4024,13 @@ namespace RoF
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);
@ -4923,6 +4960,16 @@ namespace RoF
FINISH_DIRECT_DECODE();
}
DECODE(OP_VetClaimRequest)
{
DECODE_LENGTH_EXACT(structs::VeteranClaim);
SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim);
IN(claim_id);
FINISH_DIRECT_DECODE();
}
DECODE(OP_ZoneChange)
{
DECODE_LENGTH_EXACT(structs::ZoneChange_Struct);
@ -4983,7 +5030,7 @@ namespace RoF
//sprintf(hdr.unknown000, "06e0002Y1W00");
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID);
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID);
hdr.stacksize = stackable ? charges : 1;
hdr.unknown004 = 0;

View File

@ -15,6 +15,8 @@
#include <iostream>
#include <sstream>
#include <numeric>
#include <cassert>
namespace RoF2
{
@ -104,7 +106,7 @@ namespace RoF2
{
//all opcodes default to passthrough.
#include "ss_register.h"
#include "rof_ops.h"
#include "rof2_ops.h"
}
std::string Strategy::Describe() const
@ -2118,7 +2120,7 @@ namespace RoF2
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
outapp->WriteUInt32(0);
@ -2128,7 +2130,7 @@ namespace RoF2
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
outapp->WriteUInt32(0);
@ -2360,46 +2362,52 @@ namespace RoF2
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++)
{
outapp->WriteString(emu->bandoliers[r].name);
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j)
{
outapp->WriteString(emu->bandoliers[r].items[j].item_name);
outapp->WriteUInt32(emu->bandoliers[r].items[j].item_id);
outapp->WriteUInt32(emu->bandoliers[r].items[j].icon);
}
}
for (uint32 r = 0; r < structs::MAX_PLAYER_BANDOLIER - EmuConstants::BANDOLIERS_COUNT; r++)
{
outapp->WriteString("");
for (uint32 j = 0; j < EmuConstants::BANDOLIER_SIZE; ++j)
{
outapp->WriteString("");
outapp->WriteUInt32(0);
outapp->WriteUInt32(0);
}
}
outapp->WriteUInt32(structs::MAX_POTIONS_IN_BELT);
for (uint32 r = 0; r < EmuConstants::POTION_BELT_SIZE; r++)
{
outapp->WriteString(emu->potionbelt.items[r].item_name);
outapp->WriteUInt32(emu->potionbelt.items[r].item_id);
outapp->WriteUInt32(emu->potionbelt.items[r].icon);
}
for (uint32 r = 0; r < structs::MAX_POTIONS_IN_BELT - EmuConstants::POTION_BELT_SIZE; 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);
for (uint32 j = 0; j < consts::BANDOLIER_ITEM_COUNT; ++j) { // Will need adjusting if 'server != client' is ever true
outapp->WriteString(emu->bandoliers[r].Items[j].Name);
outapp->WriteUInt32(emu->bandoliers[r].Items[j].ID);
if (emu->bandoliers[r].Items[j].Icon) {
outapp->WriteSInt32(emu->bandoliers[r].Items[j].Icon);
}
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("");
outapp->WriteUInt32(0);
outapp->WriteSInt32(-1);
}
}
outapp->WriteUInt32(consts::POTION_BELT_ITEM_COUNT);
// 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].Name);
outapp->WriteUInt32(emu->potionbelt.Items[r].ID);
if (emu->potionbelt.Items[r].Icon) {
outapp->WriteSInt32(emu->potionbelt.Items[r].Icon);
}
else {
// If no icon, it must send -1 or Treasure Chest Icon (836) is displayed
outapp->WriteSInt32(-1);
}
}
// Nullify potion belt where server and client indexes diverge, with a client bias
for (uint32 r = EmuConstants::POTION_BELT_ITEM_COUNT; r < consts::POTION_BELT_ITEM_COUNT; ++r) {
outapp->WriteString("");
outapp->WriteUInt32(0);
outapp->WriteUInt32(0);
outapp->WriteSInt32(-1);
}
outapp->WriteSInt32(-1); // Unknown;
@ -2493,7 +2501,7 @@ namespace RoF2
outapp->WriteUInt32(emu->silver_bank);
outapp->WriteUInt32(emu->copper_bank);
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(emu->platinum_shared);
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown
outapp->WriteUInt32(0); // Unknown
@ -2975,85 +2983,99 @@ namespace RoF2
ENCODE(OP_SendCharInfo)
{
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p;
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
// Zero-character count shunt
if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
int char_count;
int namelen = 0;
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]);
FINISH_ENCODE();
return;
}
int total_length = sizeof(structs::CharacterSelect_Struct)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen;
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);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_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];
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
eq->CharCount = character_count;
//eq->TotalChars = emu->TotalChars;
eq->char_count = char_count;
//eq->total_chars = 10;
//if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
// eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries;
int r;
for (r = 0; r < char_count; r++) {
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
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]);
{ //post-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->class_ = emu->class_[r];
eq2->race = emu->race[r];
eq2->level = emu->level[r];
eq2->class_2 = emu->class_[r];
eq2->race2 = emu->race[r];
eq2->zone = emu->zone[r];
eq2->instance = 0;
eq2->gender = emu->gender[r];
eq2->face = emu->face[r];
int k;
for (k = 0; k < _MaterialCount; k++) {
eq2->equip[k].material = emu->equip[r][k].material;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].heroforgemodel = emu->equip[r][k].heroforgemodel;
eq2->equip[k].material2 = emu->equip[r][k].material2;
eq2->equip[k].color.color = emu->equip[r][k].color.color;
}
eq2->u15 = 0xff;
eq2->u19 = 0xFF;
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);
eq_cse->Unknown15 = emu_cse->Unknown15;
eq_cse->Unknown19 = emu_cse->Unknown19;
eq_cse->DrakkinTattoo = emu_cse->DrakkinTattoo;
eq_cse->DrakkinDetails = emu_cse->DrakkinDetails;
eq_cse->Deity = emu_cse->Deity;
eq_cse->PrimaryIDFile = emu_cse->PrimaryIDFile;
eq_cse->SecondaryIDFile = emu_cse->SecondaryIDFile;
eq_cse->HairColor = emu_cse->HairColor;
eq_cse->BeardColor = emu_cse->BeardColor;
eq_cse->EyeColor1 = emu_cse->EyeColor1;
eq_cse->EyeColor2 = emu_cse->EyeColor2;
eq_cse->HairStyle = emu_cse->HairStyle;
eq_cse->Beard = emu_cse->Beard;
eq_cse->Enabled = emu_cse->Enabled;
eq_cse->Tutorial = emu_cse->Tutorial;
eq_cse->DrakkinHeritage = emu_cse->DrakkinHeritage;
eq_cse->Unknown1 = emu_cse->Unknown1;
eq_cse->GoHome = emu_cse->GoHome;
eq_cse->LastLogin = emu_cse->LastLogin;
eq_cse->Unknown2 = emu_cse->Unknown2;
emu_ptr += sizeof(CharacterSelectEntry_Struct);
eq_ptr += sizeof(structs::CharacterSelectEntry_Struct);
}
FINISH_ENCODE();
@ -3563,12 +3585,13 @@ namespace RoF2
// Live actually has 200 items now, but 80 is the most our internal struct supports
for (uint32 i = 0; i < 200; i++)
{
strncpy(eq->items[i].SerialNumber, "0000000000000000", sizeof(eq->items[i].SerialNumber));
eq->items[i].Unknown18 = 0;
if (i < 80) {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", emu->SerialNumber[i]);
eq->ItemCost[i] = emu->ItemCost[i];
}
else {
snprintf(eq->items[i].SerialNumber, sizeof(eq->items[i].SerialNumber), "%016d", 0);
eq->ItemCost[i] = 0;
}
}
@ -3581,10 +3604,11 @@ namespace RoF2
SETUP_DIRECT_ENCODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
eq->Code = emu->Code;
strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber));
//strncpy(eq->SerialNumber, "0000000000000000", sizeof(eq->SerialNumber));
//snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", 0);
eq->TraderID = emu->TraderID;
eq->Stacksize = 0;
eq->Price = 0;
//eq->Stacksize = 0;
//eq->Price = 0;
FINISH_ENCODE();
}
@ -3619,6 +3643,78 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_TraderDelItem)
{
ENCODE_LENGTH_EXACT(TraderDelItem_Struct);
SETUP_DIRECT_ENCODE(TraderDelItem_Struct, structs::TraderDelItem_Struct);
OUT(TraderID);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderDelItem): TraderID %d, SerialNumber: %d", emu->TraderID, emu->ItemID);
FINISH_ENCODE();
}
ENCODE(OP_TraderShop)
{
uint32 psize = (*p)->size;
if (psize == sizeof(TraderClick_Struct))
{
ENCODE_LENGTH_EXACT(TraderClick_Struct);
SETUP_DIRECT_ENCODE(TraderClick_Struct, structs::TraderClick_Struct);
eq->Code = 28; // Seen on Live
OUT(TraderID);
OUT(Approval);
FINISH_ENCODE();
}
else if (psize == sizeof(BazaarWelcome_Struct))
{
ENCODE_LENGTH_EXACT(BazaarWelcome_Struct);
SETUP_DIRECT_ENCODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
eq->Code = emu->Beginning.Action;
eq->EntityID = emu->Unknown012;
OUT(Traders);
OUT(Items);
eq->Traders2 = emu->Traders;
eq->Items2 = emu->Items;
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d",
eq->Code, eq->Traders, eq->Items);
FINISH_ENCODE();
}
else if (psize == sizeof(TraderBuy_Struct))
{
ENCODE_LENGTH_EXACT(TraderBuy_Struct);
SETUP_DIRECT_ENCODE(TraderBuy_Struct, structs::TraderBuy_Struct);
OUT(Action);
OUT(TraderID);
//memcpy(eq->BuyerName, emu->BuyerName, sizeof(eq->BuyerName));
//memcpy(eq->SellerName, emu->SellerName, sizeof(eq->SellerName));
memcpy(eq->ItemName, emu->ItemName, sizeof(eq->ItemName));
OUT(ItemID);
OUT(AlreadySold);
OUT(Price);
OUT(Quantity);
snprintf(eq->SerialNumber, sizeof(eq->SerialNumber), "%016d", emu->ItemID);
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, emu->ItemName);
FINISH_ENCODE();
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "ENCODE(OP_TraderShop): Encode Size Unknown (%d)", psize);
}
}
ENCODE(OP_TributeInfo)
{
ENCODE_LENGTH_ATLEAST(TributeAbility_Struct);
@ -3655,37 +3751,71 @@ namespace RoF2
FINISH_ENCODE();
}
ENCODE(OP_VetClaimReply)
{
ENCODE_LENGTH_EXACT(VeteranClaim);
SETUP_DIRECT_ENCODE(VeteranClaim, structs::VeteranClaim);
memcpy(eq->name, emu->name, sizeof(emu->name));
OUT(claim_id);
OUT(action);
FINISH_ENCODE();
}
ENCODE(OP_VetRewardsAvaliable)
{
EQApplicationPacket *inapp = *p;
unsigned char * __emu_buffer = inapp->pBuffer;
auto __emu_buffer = inapp->pBuffer;
uint32 count = ((*p)->Size() / sizeof(InternalVeteranReward));
*p = nullptr;
EQApplicationPacket *outapp_create = new EQApplicationPacket(OP_VetRewardsAvaliable, (sizeof(structs::VeteranReward)*count));
uchar *old_data = __emu_buffer;
uchar *data = outapp_create->pBuffer;
for (unsigned int i = 0; i < count; ++i)
{
structs::VeteranReward *vr = (structs::VeteranReward*)data;
InternalVeteranReward *ivr = (InternalVeteranReward*)old_data;
// calculate size of names, note the packet DOES NOT have null termed c-strings
std::vector<uint32> name_lengths;
for (int i = 0; i < count; ++i) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
vr->claim_count = ivr->claim_count;
vr->claim_id = ivr->claim_id;
vr->number_available = ivr->number_available;
for (int x = 0; x < 8; ++x)
{
vr->items[x].item_id = ivr->items[x].item_id;
strncpy(vr->items[x].item_name, ivr->items[x].item_name, sizeof(vr->items[x].item_name));
vr->items[x].charges = ivr->items[x].charges;
for (int i = 0; i < ivr->claim_count; i++) {
uint32 length = strnlen(ivr->items[i].item_name, 63);
if (length)
name_lengths.push_back(length);
}
old_data += sizeof(InternalVeteranReward);
data += sizeof(structs::VeteranReward);
__emu_buffer += sizeof(InternalVeteranReward);
}
dest->FastQueuePacket(&outapp_create);
uint32 packet_size = std::accumulate(name_lengths.begin(), name_lengths.end(), 0) +
sizeof(structs::VeteranReward) + (sizeof(structs::VeteranRewardEntry) * count) +
// size of name_lengths is the same as item count
(sizeof(structs::VeteranRewardItem) * name_lengths.size());
// build packet now!
auto outapp = new EQApplicationPacket(OP_VetRewardsAvaliable, packet_size);
__emu_buffer = inapp->pBuffer;
outapp->WriteUInt32(count);
auto name_itr = name_lengths.begin();
for (int i = 0; i < count; i++) {
InternalVeteranReward *ivr = (InternalVeteranReward *)__emu_buffer;
outapp->WriteUInt32(ivr->claim_id);
outapp->WriteUInt32(ivr->number_available);
outapp->WriteUInt32(ivr->claim_count);
outapp->WriteUInt8(1); // enabled
for (int j = 0; j < ivr->claim_count; j++) {
assert(name_itr != name_lengths.end()); // the way it's written, it should never happen, so just assert
outapp->WriteUInt32(*name_itr);
outapp->WriteData(ivr->items[j].item_name, *name_itr);
outapp->WriteUInt32(ivr->items[j].item_id);
outapp->WriteUInt32(ivr->items[j].charges);
++name_itr;
}
__emu_buffer += sizeof(InternalVeteranReward);
}
dest->FastQueuePacket(&outapp);
delete inapp;
}
@ -3700,7 +3830,7 @@ namespace RoF2
OUT(elite_material);
OUT(hero_forge_model);
OUT(unknown18);
OUT(color.color);
OUT(color.Color);
OUT(wear_slot_id);
FINISH_ENCODE();
@ -3791,42 +3921,23 @@ namespace RoF2
ENCODE(OP_ZonePlayerToBind)
{
ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct);
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer;
__packet->SetWritePosition(0);
__packet->WriteUInt16(emu->bind_zone_id);
__packet->WriteUInt16(emu->bind_instance_id);
__packet->WriteFloat(emu->x);
__packet->WriteFloat(emu->y);
__packet->WriteFloat(emu->z);
__packet->WriteFloat(emu->heading);
__packet->WriteString(emu->zone_name);
__packet->WriteUInt8(1); // save items
__packet->WriteUInt32(0); // hp
__packet->WriteUInt32(0); // mana
__packet->WriteUInt32(0); // endurance
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)];
structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1;
unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)];
structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2;
zph->x = zps->x;
zph->y = zps->y;
zph->z = zps->z;
zph->heading = zps->heading;
zph->bind_zone_id = 0;
zph->bind_instance_id = zps->bind_instance_id;
strncpy(zph->zone_name, zps->zone_name, sizeof(zph->zone_name));
zpf->unknown021 = 1;
zpf->unknown022 = 0;
zpf->unknown023 = 0;
zpf->unknown024 = 0;
ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)));
ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct));
delete[] buffer1;
delete[] buffer2;
delete[](*p)->pBuffer;
(*p)->pBuffer = new unsigned char[ss.str().size()];
(*p)->size = ss.str().size();
memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size());
dest->FastQueuePacket(&(*p));
FINISH_ENCODE();
}
ENCODE(OP_ZoneServerInfo)
@ -4034,18 +4145,18 @@ namespace RoF2
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;
for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material;
Equipment[k].unknown1 = emu->equipment[k].unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial;
Equipment[k].heroforgemodel = emu->equipment[k].heroforgemodel;
Equipment[k].material2 = emu->equipment[k].material2;
Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
Equipment[k].HeroForgeModel = emu->equipment[k].HeroForgeModel;
Equipment[k].Material2 = emu->equipment[k].Material2;
}
Buffer += (sizeof(structs::EquipStruct) * 9);
@ -4058,13 +4169,13 @@ namespace RoF2
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);
@ -4200,7 +4311,6 @@ namespace RoF2
return;
SETUP_DIRECT_DECODE(NewBazaarInspect_Struct, structs::NewBazaarInspect_Struct);
MEMSET_IN(structs::NewBazaarInspect_Struct);
IN(Beginning.Action);
memcpy(emu->Name, eq->Name, sizeof(emu->Name));
@ -4897,7 +5007,6 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::ClickTrader_Struct);
SETUP_DIRECT_DECODE(ClickTrader_Struct, structs::ClickTrader_Struct);
MEMSET_IN(ClickTrader_Struct);
emu->Code = eq->Code;
// Live actually has 200 items now, but 80 is the most our internal struct supports
@ -4913,7 +5022,6 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::Trader_ShowItems_Struct);
SETUP_DIRECT_DECODE(Trader_ShowItems_Struct, structs::Trader_ShowItems_Struct);
MEMSET_IN(Trader_ShowItems_Struct);
emu->Code = eq->Code;
emu->TraderID = eq->TraderID;
@ -4924,9 +5032,8 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::TraderStatus_Struct);
SETUP_DIRECT_DECODE(TraderStatus_Struct, structs::TraderStatus_Struct);
MEMSET_IN(TraderStatus_Struct);
emu->Code = eq->Code;
emu->Code = eq->Code; // 11 = Start Trader, 2 = End Trader, 22 = ? - Guessing
FINISH_DIRECT_DECODE();
}
@ -4936,7 +5043,6 @@ namespace RoF2
{
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
MEMSET_IN(TraderBuy_Struct);
IN(Action);
IN(Price);
@ -4948,6 +5054,65 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_TraderShop)
{
uint32 psize = __packet->size;
if (psize == sizeof(structs::TraderClick_Struct))
{
DECODE_LENGTH_EXACT(structs::TraderClick_Struct);
SETUP_DIRECT_DECODE(TraderClick_Struct, structs::TraderClick_Struct);
IN(Code);
IN(TraderID);
IN(Approval);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderClick_Struct Code %d, TraderID %d, Approval %d",
eq->Code, eq->TraderID, eq->Approval);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::BazaarWelcome_Struct))
{
// Don't think this size gets used in RoF+ - Leaving for now...
DECODE_LENGTH_EXACT(structs::BazaarWelcome_Struct);
SETUP_DIRECT_DECODE(BazaarWelcome_Struct, structs::BazaarWelcome_Struct);
emu->Beginning.Action = eq->Code;
IN(Traders);
IN(Items);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): BazaarWelcome_Struct Code %d, Traders %d, Items %d",
eq->Code, eq->Traders, eq->Items);
FINISH_DIRECT_DECODE();
}
else if (psize == sizeof(structs::TraderBuy_Struct))
{
DECODE_LENGTH_EXACT(structs::TraderBuy_Struct);
SETUP_DIRECT_DECODE(TraderBuy_Struct, structs::TraderBuy_Struct);
IN(Action);
IN(Price);
IN(TraderID);
memcpy(emu->ItemName, eq->ItemName, sizeof(emu->ItemName));
IN(ItemID);
IN(Quantity);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct (Unknowns) Unknown004 %d, Unknown008 %d, Unknown012 %d, Unknown076 %d, Unknown276 %d",
eq->Unknown004, eq->Unknown008, eq->Unknown012, eq->Unknown076, eq->Unknown276);
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): TraderBuy_Struct Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
eq->Action, eq->Price, eq->TraderID, eq->ItemID, eq->Quantity, eq->ItemName);
FINISH_DIRECT_DECODE();
}
else if (psize == 4)
{
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Forwarding packet as-is with size 4");
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "DECODE(OP_TraderShop): Decode Size Unknown (%d)", psize);
}
}
DECODE(OP_TradeSkillCombine)
{
DECODE_LENGTH_EXACT(structs::NewCombine_Struct);
@ -4993,6 +5158,16 @@ namespace RoF2
FINISH_DIRECT_DECODE();
}
DECODE(OP_VetClaimRequest)
{
DECODE_LENGTH_EXACT(structs::VeteranClaim);
SETUP_DIRECT_DECODE(VeteranClaim, structs::VeteranClaim);
IN(claim_id);
FINISH_DIRECT_DECODE();
}
DECODE(OP_ZoneChange)
{
DECODE_LENGTH_EXACT(structs::ZoneChange_Struct);
@ -5053,7 +5228,7 @@ namespace RoF2
//sprintf(hdr.unknown000, "06e0002Y1W00");
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%012d", item->ID);
snprintf(hdr.unknown000, sizeof(hdr.unknown000), "%016d", item->ID);
hdr.stacksize = stackable ? charges : 1;
hdr.unknown004 = 0;
@ -5464,10 +5639,10 @@ namespace RoF2
RoF2::structs::WornEffectStruct ibes;
memset(&ibes, 0, sizeof(RoF2::structs::WornEffectStruct));
ibes.effect = 0xffffffff;
ibes.level2 = 0;
ibes.type = 0;
ibes.level = 0;
ibes.effect = item->Bard.Effect;
ibes.level2 = item->Bard.Level2;
ibes.type = item->Bard.Type;
ibes.level = item->Bard.Level;
//ibes.unknown6 = 0xffffffff;
ss.write((const char*)&ibes, sizeof(RoF2::structs::WornEffectStruct));

View File

@ -103,6 +103,8 @@ namespace RoF2 {
}
namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24;
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_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 uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 56;
}

View File

@ -2,10 +2,14 @@
// Begin RoF2 Encodes
E(OP_SendMembershipDetails)
E(OP_TraderShop)
E(OP_TraderDelItem)
// incoming packets that require a DECODE translation:
// Begin RoF2 Decodes
D(OP_TraderShop)
// End RoF2 Encodes/Decodes
// These require Encodes/Decodes for RoF, so they do for RoF2 as well
@ -107,6 +111,7 @@ E(OP_Trader)
E(OP_TraderBuy)
E(OP_TributeInfo)
E(OP_TributeItem)
E(OP_VetClaimReply)
E(OP_VetRewardsAvaliable)
E(OP_WearChange)
E(OP_WhoAllResponse)
@ -170,6 +175,7 @@ D(OP_Trader)
D(OP_TraderBuy)
D(OP_TradeSkillCombine)
D(OP_TributeItem)
D(OP_VetClaimRequest)
D(OP_WhoAllRequest)
D(OP_ZoneChange)
D(OP_ZoneEntry)

View File

@ -97,11 +97,6 @@ static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_TRIBUTE_TIERS = 10;
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_RAID_LEADERSHIP_AA_ARRAY = 16;
static const uint32 MAX_LEADERSHIP_AA_ARRAY = (MAX_GROUP_LEADERSHIP_AA_ARRAY+MAX_RAID_LEADERSHIP_AA_ARRAY);
@ -114,7 +109,7 @@ static const uint32 MAX_PP_SPELLBOOK = 720; // was 480
static const uint32 MAX_PP_MEMSPELL = 16; // was 12
static const uint32 MAX_PP_SKILL = PACKET_SKILL_ARRAY_SIZE; // 100 - actual skills buffer size
static const uint32 MAX_PP_AA_ARRAY = 300;
static const uint32 MAX_PP_DISCIPLINES = 200; // was 100
static const uint32 MAX_PP_DISCIPLINES = 300; // was 200
static const uint32 MAX_GROUP_MEMBERS = 6;
static const uint32 MAX_RECAST_TYPES = 20;
@ -147,84 +142,87 @@ struct AdventureInfo {
*/
struct Color_Struct
{
union
{
struct
{
uint8 blue;
uint8 green;
uint8 red;
uint8 use_tint; // if there's a tint this is FF
} rgb;
uint32 color;
union {
struct {
uint8 Blue;
uint8 Green;
uint8 Red;
uint8 UseTint; // if there's a tint this is FF
} RGB;
uint32 Color;
};
};
/*
* 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 CharSelectEquip
{
uint32 Material;
uint32 Unknown1;
uint32 EliteMaterial;
uint32 HeroForgeModel;
uint32 Material2;
Color_Struct Color;
};
struct CharSelectEquip {
uint32 material;
uint32 unknown1;
uint32 elitematerial;
uint32 heroforgemodel;
uint32 material2;
Color_Struct color;
};
struct CharacterSelectEntry_Struct {
/*0000*/ char name[1]; // Name null terminated
/*0000*/ uint8 class_;
/*0000*/ uint32 race;
/*0000*/ uint8 level;
/*0000*/ uint8 class_2;
/*0000*/ uint32 race2;
/*0000*/ uint16 zone;
/*0000*/ uint16 instance;
/*0000*/ uint8 gender;
/*0000*/ uint8 face;
/*0000*/ CharSelectEquip equip[9];
/*0000*/ uint8 u15; // Seen FF
/*0000*/ uint8 u19; // Seen FF
/*0000*/ uint32 drakkin_tattoo;
/*0000*/ uint32 drakkin_details;
/*0000*/ uint32 deity;
/*0000*/ uint32 primary;
/*0000*/ uint32 secondary;
/*0000*/ uint8 haircolor;
/*0000*/ uint8 beardcolor;
/*0000*/ uint8 eyecolor1;
/*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
struct CharacterSelectEntry_Struct
{
/*0000*/ char Name[1]; // Name null terminated
/*0000*/ uint8 Class;
/*0000*/ uint32 Race;
/*0000*/ uint8 Level;
/*0000*/ uint8 ShroudClass;
/*0000*/ uint32 ShroudRace;
/*0000*/ uint16 Zone;
/*0000*/ uint16 Instance;
/*0000*/ uint8 Gender;
/*0000*/ uint8 Face;
/*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint8 Unknown15; // Seen FF
/*0000*/ uint8 Unknown19; // Seen FF
/*0000*/ uint32 DrakkinTattoo;
/*0000*/ uint32 DrakkinDetails;
/*0000*/ uint32 Deity;
/*0000*/ uint32 PrimaryIDFile;
/*0000*/ uint32 SecondaryIDFile;
/*0000*/ uint8 HairColor;
/*0000*/ uint8 BeardColor;
/*0000*/ uint8 EyeColor1;
/*0000*/ uint8 EyeColor2;
/*0000*/ uint8 HairStyle;
/*0000*/ uint8 Beard;
/*0000*/ uint8 Enabled;
/*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing
/*0000*/ uint32 DrakkinHeritage;
/*0000*/ uint8 Unknown1; // Seen 0
/*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing
/*0000*/ uint32 LastLogin;
/*0000*/ uint8 unknown2; // Seen 0
/*0000*/ uint8 Unknown2; // Seen 0
};
/*
** Character Selection Struct
**
*/
struct CharacterSelect_Struct {
/*000*/ uint32 char_count; //number of chars in this packet
/*004*/ CharacterSelectEntry_Struct entries[0];
struct CharacterSelect_Struct
{
/*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
{
/*000*/ uint32 purchase_id; // Seen 1, then increments 90287 to 90300
@ -896,38 +894,66 @@ struct Tribute_Struct {
uint32 tier;
};
struct BandolierItem_Struct {
char item_name[1]; // Variable Length
uint32 item_id;
uint32 icon;
};
//len = 72
struct BandolierItem_Struct_Old {
uint32 item_id;
uint32 icon;
char item_name[64];
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
// Bandolier item positions
enum
{
bandolierPrimary = 0,
bandolierSecondary,
bandolierRange,
bandolierAmmo
};
struct Bandolier_Struct {
char name[1]; // Variable Length
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS];
struct BandolierItem_Struct
{
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
};
struct Bandolier_Struct_Old {
char name[32];
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS];
//len = 72
struct BandolierItem_Struct_Old
{
uint32 ID;
uint32 Icon;
char Name[64];
};
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_POTIONS_IN_BELT];
//len = 320
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 {
@ -1137,7 +1163,7 @@ union
/*12949*/ uint32 aapoints; // Unspent AA points - Seen 1
/*12953*/ uint16 unknown_rof20; //
/*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
/*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes)
/*13748*/ int32 unknown_rof21; // Seen -1
@ -2865,7 +2891,7 @@ struct SetRunMode_Struct {
};
// EnvDamage is EnvDamage2 without a few bytes at the end.
// Size: 37 bytes
// Size: 39 bytes
struct EnvDamage2_Struct {
/*0000*/ uint32 id;
/*0004*/ uint16 unknown4;
@ -2877,7 +2903,8 @@ struct EnvDamage2_Struct {
/*0031*/ uint16 unknown31; // New to Underfoot - Seen 66
/*0033*/ uint16 constant; // Always FFFF
/*0035*/ uint16 unknown35;
/*0037*/
/*0037*/ uint16 unknown37;
/*0039*/
};
//Bazaar Stuff
@ -2913,10 +2940,12 @@ struct BazaarWindowStart_Struct {
struct BazaarWelcome_Struct {
BazaarWindowStart_Struct Beginning;
uint32 Traders;
uint32 Items;
uint8 Unknown012[8];
uint32 Code;
uint32 EntityID;
uint32 Traders;
uint32 Items;
uint32 Traders2;
uint32 Items2;
};
struct BazaarSearch_Struct {
@ -3199,6 +3228,13 @@ struct BecomeTrader_Struct {
};
struct Trader_ShowItems_Struct {
/*000*/ uint32 Code;
/*004*/ uint16 TraderID;
/*008*/ uint32 Unknown08;
/*012*/
};
struct Trader_ShowItems_Struct_WIP {
/*000*/ uint32 Code;
/*004*/ char SerialNumber[17];
/*021*/ uint8 Unknown21;
@ -3216,6 +3252,26 @@ struct TraderStatus_Struct {
};
struct TraderBuy_Struct {
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Unknown008;
/*012*/ uint32 Unknown012;
/*016*/ uint32 TraderID;
/*020*/ char BuyerName[64];
/*084*/ char SellerName[64];
/*148*/ char Unknown148[32];
/*180*/ char ItemName[64];
/*244*/ char SerialNumber[16];
/*260*/ uint32 Unknown076;
/*264*/ uint32 ItemID;
/*268*/ uint32 Price;
/*272*/ uint32 AlreadySold;
/*276*/ uint32 Unknown276;
/*280*/ uint32 Quantity;
/*284*/
};
struct TraderBuy_Struct_OLD {
/*000*/ uint32 Action;
/*004*/ uint32 Unknown004;
/*008*/ uint32 Price;
@ -3245,25 +3301,19 @@ struct MoneyUpdate_Struct{
int32 copper;
};
//struct MoneyUpdate_Struct
//{
//*0000*/ uint32 spawn_id; // ***Placeholder
//*0004*/ uint32 cointype; // Coin Type
//*0008*/ uint32 amount; // Amount
//*0012*/
//};
struct TraderDelItem_Struct{
uint32 slotid;
uint32 quantity;
uint32 unknown;
/*000*/ uint32 Unknown000;
/*004*/ uint32 TraderID;
/*008*/ char SerialNumber[16];
/*024*/ uint32 Unknown012;
/*028*/
};
struct TraderClick_Struct{
uint32 traderid;
uint32 unknown4[2];
uint32 approval;
/*000*/ uint32 Code;
/*004*/ uint32 TraderID;
/*008*/ uint32 Approval;
/*012*/
};
struct FormattedMessage_Struct{
@ -4087,30 +4137,35 @@ struct DynamicWall_Struct {
/*80*/
};
enum { //bandolier actions
BandolierCreate = 0,
BandolierRemove = 1,
BandolierSet = 2
// Bandolier actions
enum
{
bandolierCreate = 0,
bandolierRemove,
bandolierSet
};
struct BandolierCreate_Struct {
/*00*/ uint32 action; //0 for create
/*04*/ uint8 number;
/*05*/ char name[32];
/*37*/ uint16 unknown37; //seen 0x93FD
/*39*/ uint8 unknown39; //0
struct BandolierCreate_Struct
{
/*00*/ uint32 Action; //0 for create
/*04*/ uint8 Number;
/*05*/ char Name[32];
/*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
};
struct BandolierDelete_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierDelete_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct BandolierSet_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierSet_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct Arrow_Struct {
@ -4352,7 +4407,7 @@ struct RoF2SlotStruct
struct ItemSerializationHeader
{
/*000*/ char unknown000[13]; // New for HoT. Looks like a string.
/*000*/ char unknown000[17]; // New for HoT. Looks like a string.
/*017*/ uint32 stacksize;
/*021*/ uint32 unknown004;
/*025*/ uint8 slot_type; // 0 = normal, 1 = bank, 2 = shared bank, 9 = merchant, 20 = ?
@ -4672,17 +4727,33 @@ struct AugmentInfo_Struct
struct VeteranRewardItem
{
/*000*/ uint32 item_id;
/*004*/ uint32 charges;
/*008*/ char item_name[64];
/*000*/ uint32 name_length;
/*004*/ //char item_name[0]; // THIS IS NOT NULL TERMED
/*???*/ uint32 item_id;
/*???*/ uint32 charges;
};
struct VeteranRewardEntry
{
/*000*/ uint32 claim_id; // guessed
/*004*/ uint32 avaliable_count;
/*008*/ uint32 claim_count;
/*012*/ char enabled;
/*013*/ //VeteranRewardItem items[0];
};
struct VeteranReward
{
/*000*/ uint32 claim_id;
/*004*/ uint32 number_available;
/*008*/ uint32 claim_count;
/*012*/ VeteranRewardItem items[8];
/*000*/ uint32 claim_count;
/*004*/ //VeteranRewardEntry entries[0];
};
struct VeteranClaim
{
/*000*/ char name[68]; //name + other data
/*068*/ uint32 claim_id;
/*072*/ uint32 unknown072;
/*076*/ uint32 action;
};
struct ExpeditionEntryHeader_Struct

View File

@ -102,6 +102,8 @@ namespace RoF {
}
namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24;
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_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 uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 55;
}

View File

@ -96,6 +96,7 @@ E(OP_Trader)
E(OP_TraderBuy)
E(OP_TributeInfo)
E(OP_TributeItem)
E(OP_VetClaimReply)
E(OP_VetRewardsAvaliable)
E(OP_WearChange)
E(OP_WhoAllResponse)
@ -159,6 +160,7 @@ D(OP_Trader)
D(OP_TraderBuy)
D(OP_TradeSkillCombine)
D(OP_TributeItem)
D(OP_VetClaimRequest)
D(OP_WhoAllRequest)
D(OP_ZoneChange)
D(OP_ZoneEntry)

View File

@ -97,11 +97,6 @@ static const uint32 MAX_PLAYER_TRIBUTES = 5;
static const uint32 MAX_TRIBUTE_TIERS = 10;
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_RAID_LEADERSHIP_AA_ARRAY = 16;
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
{
union
{
struct
{
uint8 blue;
uint8 green;
uint8 red;
uint8 use_tint; // if there's a tint this is FF
} rgb;
uint32 color;
union {
struct {
uint8 Blue;
uint8 Green;
uint8 Red;
uint8 UseTint; // if there's a tint this is FF
} RGB;
uint32 Color;
};
};
struct CharSelectEquip {
uint32 material;
uint32 unknown1;
uint32 elitematerial;
uint32 heroforgemodel;
uint32 material2;
Color_Struct color;
struct CharSelectEquip
{
uint32 Material;
uint32 Unknown1;
uint32 EliteMaterial;
uint32 HeroForgeModel;
uint32 Material2;
Color_Struct Color;
};
struct CharacterSelectEntry_Struct {
/*0000*/ char name[1]; // Name null terminated
/*0000*/ uint8 class_;
/*0000*/ uint32 race;
/*0000*/ uint8 level;
/*0000*/ uint8 class_2;
/*0000*/ uint32 race2;
/*0000*/ uint16 zone;
/*0000*/ uint16 instance;
/*0000*/ uint8 gender;
/*0000*/ uint8 face;
/*0000*/ CharSelectEquip equip[9];
/*0000*/ uint8 u15; // Seen FF
/*0000*/ uint8 u19; // Seen FF
/*0000*/ uint32 drakkin_tattoo;
/*0000*/ uint32 drakkin_details;
/*0000*/ uint32 deity;
/*0000*/ uint32 primary;
/*0000*/ uint32 secondary;
/*0000*/ uint8 haircolor;
/*0000*/ uint8 beardcolor;
/*0000*/ uint8 eyecolor1;
/*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
struct CharacterSelectEntry_Struct
{
/*0000*/ char Name[1]; // Name null terminated
/*0000*/ uint8 Class;
/*0000*/ uint32 Race;
/*0000*/ uint8 Level;
/*0000*/ uint8 ShroudClass;
/*0000*/ uint32 ShroudRace;
/*0000*/ uint16 Zone;
/*0000*/ uint16 Instance;
/*0000*/ uint8 Gender;
/*0000*/ uint8 Face;
/*0000*/ CharSelectEquip Equip[9];
/*0000*/ uint8 Unknown15; // Seen FF
/*0000*/ uint8 Unknown19; // Seen FF
/*0000*/ uint32 DrakkinTattoo;
/*0000*/ uint32 DrakkinDetails;
/*0000*/ uint32 Deity;
/*0000*/ uint32 PrimaryIDFile;
/*0000*/ uint32 SecondaryIDFile;
/*0000*/ uint8 HairColor;
/*0000*/ uint8 BeardColor;
/*0000*/ uint8 EyeColor1;
/*0000*/ uint8 EyeColor2;
/*0000*/ uint8 HairStyle;
/*0000*/ uint8 Beard;
/*0000*/ uint8 Enabled;
/*0000*/ uint8 Tutorial; // Seen 1 for new char or 0 for existing
/*0000*/ uint32 DrakkinHeritage;
/*0000*/ uint8 Unknown1; // Seen 0
/*0000*/ uint8 GoHome; // Seen 0 for new char and 1 for existing
/*0000*/ uint32 LastLogin;
/*0000*/ uint8 unknown2; // Seen 0
/*0000*/ uint8 Unknown2; // Seen 0
};
/*
** Character Selection Struct
**
*/
struct CharacterSelect_Struct {
/*000*/ uint32 char_count; //number of chars in this packet
/*004*/ CharacterSelectEntry_Struct entries[0];
struct CharacterSelect_Struct
{
/*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
{
/*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
** Length: 897 Octets
@ -880,38 +877,66 @@ struct Tribute_Struct {
uint32 tier;
};
struct BandolierItem_Struct {
char item_name[1]; // Variable Length
uint32 item_id;
uint32 icon;
};
//len = 72
struct BandolierItem_Struct_Old {
uint32 item_id;
uint32 icon;
char item_name[64];
};
//len = 320
enum { //bandolier item positions
bandolierMainHand = 0,
bandolierOffHand,
// Bandolier item positions
enum
{
bandolierPrimary = 0,
bandolierSecondary,
bandolierRange,
bandolierAmmo
};
struct Bandolier_Struct {
char name[1]; // Variable Length
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS];
struct BandolierItem_Struct
{
char Name[1]; // Variable Length
uint32 ID;
uint32 Icon;
};
struct Bandolier_Struct_Old {
char name[32];
BandolierItem_Struct items[MAX_PLAYER_BANDOLIER_ITEMS];
//len = 72
struct BandolierItem_Struct_Old
{
uint32 ID;
uint32 Icon;
char Name[64];
};
struct PotionBelt_Struct {
BandolierItem_Struct items[MAX_POTIONS_IN_BELT];
//len = 320
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 {
@ -1121,7 +1146,7 @@ union
/*12949*/ uint32 aapoints; // Unspent AA points - Seen 1
/*12953*/ uint16 unknown_rof20; //
/*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
/*13703*/ PotionBelt_Struct potionbelt; // [5] 45 bytes potion belt - (Variable Name Sizes)
/*13748*/ int32 unknown_rof21; // Seen -1
@ -4113,30 +4138,35 @@ struct DynamicWall_Struct {
/*80*/
};
enum { //bandolier actions
BandolierCreate = 0,
BandolierRemove = 1,
BandolierSet = 2
// Bandolier actions
enum
{
bandolierCreate = 0,
bandolierRemove,
bandolierSet
};
struct BandolierCreate_Struct {
/*00*/ uint32 action; //0 for create
/*04*/ uint8 number;
/*05*/ char name[32];
/*37*/ uint16 unknown37; //seen 0x93FD
/*39*/ uint8 unknown39; //0
struct BandolierCreate_Struct
{
/*00*/ uint32 Action; //0 for create
/*04*/ uint8 Number;
/*05*/ char Name[32];
/*37*/ uint16 Unknown37; //seen 0x93FD
/*39*/ uint8 Unknown39; //0
};
struct BandolierDelete_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierDelete_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct BandolierSet_Struct {
/*00*/ uint32 action;
/*04*/ uint8 number;
/*05*/ uint8 unknown05[35];
struct BandolierSet_Struct
{
/*00*/ uint32 Action;
/*04*/ uint8 Number;
/*05*/ uint8 Unknown05[35];
};
struct Arrow_Struct {
@ -4378,7 +4408,7 @@ struct RoFSlotStruct
struct ItemSerializationHeader
{
/*000*/ char unknown000[13]; // New for HoT. Looks like a string.
/*000*/ char unknown000[17]; // New for HoT. Looks like a string.
/*017*/ uint32 stacksize;
/*021*/ uint32 unknown004;
/*025*/ uint8 slot_type; // 0 = normal, 1 = bank, 2 = shared bank, 9 = merchant, 20 = ?
@ -4688,17 +4718,33 @@ struct AugmentInfo_Struct
struct VeteranRewardItem
{
/*000*/ uint32 item_id;
/*004*/ uint32 charges;
/*008*/ char item_name[64];
/*000*/ uint32 name_length;
/*004*/ //char item_name[0]; // THIS IS NOT NULL TERMED
/*???*/ uint32 item_id;
/*???*/ uint32 charges;
};
struct VeteranRewardEntry
{
/*000*/ uint32 claim_id; // guessed
/*004*/ uint32 avaliable_count;
/*008*/ uint32 claim_count;
/*012*/ char enabled;
/*013*/ //VeteranRewardItem items[0];
};
struct VeteranReward
{
/*000*/ uint32 claim_id;
/*004*/ uint32 number_available;
/*008*/ uint32 claim_count;
/*012*/ VeteranRewardItem items[8];
/*000*/ uint32 claim_count;
/*004*/ //VeteranRewardEntry entries[0];
};
struct VeteranClaim
{
/*000*/ char name[68]; //name + other data
/*068*/ uint32 claim_id;
/*072*/ uint32 unknown072;
/*076*/ uint32 action;
};
struct ExpeditionEntryHeader_Struct

View File

@ -1542,13 +1542,13 @@ namespace SoD
OUT(beard);
// OUT(unknown00178[10]);
for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r];
eq->equipment[r].unknown1 = 0;
eq->equipment[r].elitematerial = 0;
eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].Unknown1 = 0;
eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color;
}
for (r = 0; r < 7; r++) {
OUT(item_tint[r].color);
OUT(item_tint[r].Color);
}
// OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP
@ -1606,26 +1606,46 @@ namespace SoD
OUT(endurance);
OUT(aapoints_spent);
OUT(aapoints);
// OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4..
for (r = 0; r < 4; r++) {
OUT_str(bandoliers[r].name);
uint32 k;
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) {
OUT(bandoliers[r].items[k].item_id);
OUT(bandoliers[r].items[k].icon);
OUT_str(bandoliers[r].items[k].item_name);
// Copy bandoliers where server and client indexes converge
for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].Name);
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].Items[k].Icon);
OUT_str(bandoliers[r].Items[k].Name);
}
}
// OUT(unknown07444[5120]);
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) {
OUT(potionbelt.items[r].item_id);
OUT(potionbelt.items[r].icon);
OUT_str(potionbelt.items[r].item_name);
// Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
eq->bandoliers[r].Name[0] = '\0';
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
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(unknown12864[76]);
OUT_str(name);
OUT_str(last_name);
OUT(guild_id);
@ -1891,76 +1911,95 @@ namespace SoD
ENCODE(OP_SendCharInfo)
{
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p;
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
// Zero-character count shunt
if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = emu->TotalChars;
int char_count;
int namelen = 0;
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]);
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
FINISH_ENCODE();
return;
}
int total_length = sizeof(structs::CharacterSelect_Struct)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen;
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);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_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];
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
eq->CharCount = character_count;
eq->TotalChars = emu->TotalChars;
eq->char_count = char_count;
eq->total_chars = 10;
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries;
int r;
for (r = 0; r < char_count; r++) {
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->level = emu->level[r];
eq2->hairstyle = emu->hairstyle[r];
eq2->gender = emu->gender[r];
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
for (int counter = 0; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
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]);
{ //post-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->beard = emu->beard[r];
eq2->haircolor = emu->haircolor[r];
eq2->face = emu->face[r];
int k;
for (k = 0; k < _MaterialCount; k++) {
eq2->equip[k].material = emu->equip[r][k].material;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].color.color = emu->equip[r][k].color.color;
}
eq2->primary = emu->primary[r];
eq2->secondary = emu->secondary[r];
eq2->tutorial = emu->tutorial[r]; // was u15
eq2->u15 = 0xff;
eq2->deity = emu->deity[r];
eq2->zone = emu->zone[r];
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);
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();
@ -2355,7 +2394,7 @@ namespace SoD
OUT(material);
OUT(unknown06);
OUT(elite_material);
OUT(color.color);
OUT(color.Color);
OUT(wear_slot_id);
FINISH_ENCODE();
@ -2427,42 +2466,23 @@ namespace SoD
ENCODE(OP_ZonePlayerToBind)
{
ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct);
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer;
__packet->SetWritePosition(0);
__packet->WriteUInt16(emu->bind_zone_id);
__packet->WriteUInt16(emu->bind_instance_id);
__packet->WriteFloat(emu->x);
__packet->WriteFloat(emu->y);
__packet->WriteFloat(emu->z);
__packet->WriteFloat(emu->heading);
__packet->WriteString(emu->zone_name);
__packet->WriteUInt8(1); // save items
__packet->WriteUInt32(0); // hp
__packet->WriteUInt32(0); // mana
__packet->WriteUInt32(0); // endurance
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)];
structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1;
unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)];
structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2;
zph->x = zps->x;
zph->y = zps->y;
zph->z = zps->z;
zph->heading = zps->heading;
zph->bind_zone_id = zps->bind_zone_id;
zph->bind_instance_id = zps->bind_instance_id;
strcpy(zph->zone_name, zps->zone_name);
zpf->unknown021 = 1;
zpf->unknown022 = 0;
zpf->unknown023 = 0;
zpf->unknown024 = 0;
ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)));
ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct));
delete[] buffer1;
delete[] buffer2;
delete[](*p)->pBuffer;
(*p)->pBuffer = new unsigned char[ss.str().size()];
(*p)->size = ss.str().size();
memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size());
dest->FastQueuePacket(&(*p));
FINISH_ENCODE();
}
ENCODE(OP_ZoneServerInfo)
@ -2742,7 +2762,7 @@ namespace SoD
for (k = 0; k < 9; ++k)
{
{
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
}
}
}
@ -2752,11 +2772,11 @@ namespace SoD
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, 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);
}
@ -2767,9 +2787,9 @@ namespace SoD
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material;
Equipment[k].unknown1 = emu->equipment[k].unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial;
Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
}
Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3484,7 +3504,7 @@ namespace SoD
IN(material);
IN(unknown06);
IN(elite_material);
IN(color.color);
IN(color.Color);
IN(wear_slot_id);
emu->hero_forge_model = 0;
emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace SoD {
}
namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24;
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_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50;
}

View File

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

View File

@ -1200,13 +1200,13 @@ namespace SoF
OUT(beard);
// OUT(unknown00178[10]);
for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r];
eq->equipment[r].unknown1 = 0;
eq->equipment[r].elitematerial = 0;
eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].Unknown1 = 0;
eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color;
}
for (r = 0; r < 7; r++) {
OUT(item_tint[r].color);
OUT(item_tint[r].Color);
}
// OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP
@ -1264,26 +1264,46 @@ namespace SoF
OUT(endurance);
OUT(aapoints_spent);
OUT(aapoints);
// OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4..
for (r = 0; r < 4; r++) {
OUT_str(bandoliers[r].name);
uint32 k;
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) {
OUT(bandoliers[r].items[k].item_id);
OUT(bandoliers[r].items[k].icon);
OUT_str(bandoliers[r].items[k].item_name);
// Copy bandoliers where server and client indexes converge
for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].Name);
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].Items[k].Icon);
OUT_str(bandoliers[r].Items[k].Name);
}
}
// OUT(unknown07444[5120]);
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) {
OUT(potionbelt.items[r].item_id);
OUT(potionbelt.items[r].icon);
OUT_str(potionbelt.items[r].item_name);
// Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
eq->bandoliers[r].Name[0] = '\0';
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
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(unknown12864[76]);
OUT_str(name);
OUT_str(last_name);
OUT(guild_id);
@ -1550,76 +1570,95 @@ namespace SoF
ENCODE(OP_SendCharInfo)
{
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p;
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
// Zero-character count shunt
if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = emu->TotalChars;
int char_count;
int namelen = 0;
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]);
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
FINISH_ENCODE();
return;
}
int total_length = sizeof(structs::CharacterSelect_Struct)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen;
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);
}
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_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];
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
eq->CharCount = character_count;
eq->TotalChars = emu->TotalChars;
eq->char_count = char_count;
eq->total_chars = 10;
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries;
int r;
for (r = 0; r < char_count; r++) {
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->level = emu->level[r];
eq2->hairstyle = emu->hairstyle[r];
eq2->gender = emu->gender[r];
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
for (int counter = 0; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
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]);
{ //post-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->beard = emu->beard[r];
eq2->haircolor = emu->haircolor[r];
eq2->face = emu->face[r];
int k;
for (k = 0; k < _MaterialCount; k++) {
eq2->equip[k].material = emu->equip[r][k].material;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].color.color = emu->equip[r][k].color.color;
}
eq2->primary = emu->primary[r];
eq2->secondary = emu->secondary[r];
eq2->tutorial = emu->tutorial[r]; // was u15
eq2->u15 = 0xff;
eq2->deity = emu->deity[r];
eq2->zone = emu->zone[r];
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);
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();
@ -1941,7 +1980,7 @@ namespace SoF
OUT(material);
OUT(unknown06);
OUT(elite_material);
OUT(color.color);
OUT(color.Color);
OUT(wear_slot_id);
FINISH_ENCODE();
@ -1951,42 +1990,23 @@ namespace SoF
ENCODE(OP_ZonePlayerToBind)
{
ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct);
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer;
__packet->SetWritePosition(0);
__packet->WriteUInt16(emu->bind_zone_id);
__packet->WriteUInt16(emu->bind_instance_id);
__packet->WriteFloat(emu->x);
__packet->WriteFloat(emu->y);
__packet->WriteFloat(emu->z);
__packet->WriteFloat(emu->heading);
__packet->WriteString(emu->zone_name);
__packet->WriteUInt8(1); // save items
__packet->WriteUInt32(0); // hp
__packet->WriteUInt32(0); // mana
__packet->WriteUInt32(0); // endurance
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)];
structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1;
unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)];
structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2;
zph->x = zps->x;
zph->y = zps->y;
zph->z = zps->z;
zph->heading = zps->heading;
zph->bind_zone_id = zps->bind_zone_id;
zph->bind_instance_id = zps->bind_instance_id;
strcpy(zph->zone_name, zps->zone_name);
zpf->unknown021 = 1;
zpf->unknown022 = 0;
zpf->unknown023 = 0;
zpf->unknown024 = 0;
ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)));
ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct));
delete[] buffer1;
delete[] buffer2;
delete[](*p)->pBuffer;
(*p)->pBuffer = new unsigned char[ss.str().size()];
(*p)->size = ss.str().size();
memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size());
dest->FastQueuePacket(&(*p));
FINISH_ENCODE();
}
ENCODE(OP_ZoneServerInfo)
@ -2044,10 +2064,10 @@ namespace SoF
eq->drakkin_heritage = emu->drakkin_heritage;
eq->gender = emu->gender;
for (k = 0; k < 9; k++) {
eq->equipment[k].material = emu->equipment[k].material;
eq->equipment[k].unknown1 = emu->equipment[k].unknown1;
eq->equipment[k].elitematerial = emu->equipment[k].elitematerial;
eq->colors[k].color = emu->colors[k].color;
eq->equipment[k].Material = emu->equipment[k].Material;
eq->equipment[k].Unknown1 = emu->equipment[k].Unknown1;
eq->equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
eq->colors[k].Color = emu->colors[k].Color;
}
eq->StandState = emu->StandState;
eq->guildID = emu->guildID;
@ -2109,7 +2129,7 @@ namespace SoF
eq->petOwnerId = emu->petOwnerId;
eq->pvp = 0; // 0 = non-pvp colored name, 1 = red pvp name
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->face = emu->face;
@ -2808,7 +2828,7 @@ namespace SoF
IN(material);
IN(unknown06);
IN(elite_material);
IN(color.color);
IN(color.Color);
IN(wear_slot_id);
emu->hero_forge_model = 0;
emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace SoF {
}
namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24;
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_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50;
}

View File

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

View File

@ -41,6 +41,11 @@
memset(__packet->pBuffer, 0, len); \
eq_struct *eq = (eq_struct *) __packet->pBuffer; \
#define ALLOC_LEN_ENCODE(len) \
__packet->pBuffer = new unsigned char[len]; \
__packet->size = len; \
memset(__packet->pBuffer, 0, len); \
//a shorter assignment for direct mode
#undef OUT
#define OUT(x) eq->x = emu->x;
@ -124,14 +129,14 @@
//check length of packet before decoding. Call before setup.
#define DECODE_LENGTH_EXACT(struct_) \
if(__packet->size != sizeof(struct_)) { \
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \
Log.Out(Logs::Detail, Logs::Netcode, "Wrong size on incoming %s (" #struct_ "): Got %d, expected %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \
return; \
}
#define DECODE_LENGTH_ATLEAST(struct_) \
if(__packet->size < sizeof(struct_)) { \
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \
Log.Out(Logs::Detail, Logs::Netcode, "Wrong size on incoming %s (" #struct_ "): Got %d, expected at least %d", opcodes->EmuToName(__packet->GetOpcode()), __packet->size, sizeof(struct_)); \
__packet->SetOpcode(OP_Unknown); /* invalidate the packet */ \
return; \
}

View File

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

View File

@ -100,6 +100,8 @@ namespace Titanium {
}
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_BANK_SIZE = 16;
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_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 4; // count = number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 4;
static const size_t BANDOLIERS_SIZE = 4; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 4;
static const size_t TEXT_LINK_BODY_LENGTH = 45;
}

View File

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

View File

@ -1791,13 +1791,13 @@ namespace UF
OUT(beard);
// OUT(unknown00178[10]);
for (r = 0; r < 9; r++) {
eq->equipment[r].material = emu->item_material[r];
eq->equipment[r].unknown1 = 0;
eq->equipment[r].elitematerial = 0;
eq->equipment[r].Material = emu->item_material[r];
eq->equipment[r].Unknown1 = 0;
eq->equipment[r].EliteMaterial = 0;
//eq->colors[r].color = emu->colors[r].color;
}
for (r = 0; r < 7; r++) {
OUT(item_tint[r].color);
OUT(item_tint[r].Color);
}
// OUT(unknown00224[48]);
//NOTE: new client supports 300 AAs, our internal rep/PP
@ -1868,26 +1868,46 @@ namespace UF
OUT(endurance);
OUT(aapoints_spent);
OUT(aapoints);
// OUT(unknown06160[4]);
//NOTE: new client supports 20 bandoliers, our internal rep
//only supports 4..
for (r = 0; r < 4; r++) {
OUT_str(bandoliers[r].name);
uint32 k;
for (k = 0; k < structs::MAX_PLAYER_BANDOLIER_ITEMS; k++) {
OUT(bandoliers[r].items[k].item_id);
OUT(bandoliers[r].items[k].icon);
OUT_str(bandoliers[r].items[k].item_name);
// Copy bandoliers where server and client indexes converge
for (r = 0; r < EmuConstants::BANDOLIERS_SIZE && r < consts::BANDOLIERS_SIZE; ++r) {
OUT_str(bandoliers[r].Name);
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
OUT(bandoliers[r].Items[k].ID);
OUT(bandoliers[r].Items[k].Icon);
OUT_str(bandoliers[r].Items[k].Name);
}
}
// OUT(unknown07444[5120]);
for (r = 0; r < structs::MAX_POTIONS_IN_BELT; r++) {
OUT(potionbelt.items[r].item_id);
OUT(potionbelt.items[r].icon);
OUT_str(potionbelt.items[r].item_name);
// Nullify bandoliers where server and client indexes diverge, with a client bias
for (r = EmuConstants::BANDOLIERS_SIZE; r < consts::BANDOLIERS_SIZE; ++r) {
eq->bandoliers[r].Name[0] = '\0';
for (uint32 k = 0; k < consts::BANDOLIER_ITEM_COUNT; ++k) { // Will need adjusting if 'server != client' is ever true
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(unknown12864[76]);
OUT_str(name);
OUT_str(last_name);
OUT(guild_id);
@ -2174,77 +2194,103 @@ namespace UF
ENCODE(OP_SendCharInfo)
{
ENCODE_LENGTH_EXACT(CharacterSelect_Struct);
ENCODE_LENGTH_ATLEAST(CharacterSelect_Struct);
SETUP_VAR_ENCODE(CharacterSelect_Struct);
//EQApplicationPacket *packet = *p;
//const CharacterSelect_Struct *emu = (CharacterSelect_Struct *) packet->pBuffer;
// Zero-character count shunt
if (emu->CharCount == 0) {
ALLOC_VAR_ENCODE(structs::CharacterSelect_Struct, sizeof(structs::CharacterSelect_Struct));
eq->CharCount = emu->CharCount;
eq->TotalChars = emu->TotalChars;
int char_count;
int namelen = 0;
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]);
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
// Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars'
uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field
eq->TotalChars = adjusted_total;
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)
+ char_count * sizeof(structs::CharacterSelectEntry_Struct)
+ namelen;
size_t total_length = sizeof(structs::CharacterSelect_Struct)
+ character_count * sizeof(structs::CharacterSelectEntry_Struct)
+ names_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];
//structs::CharacterSelect_Struct *eq_head = (structs::CharacterSelect_Struct *) eq_buffer;
eq->CharCount = character_count;
eq->TotalChars = emu->TotalChars;
eq->char_count = char_count;
eq->total_chars = 10;
if (eq->TotalChars > consts::CHARACTER_CREATION_LIMIT)
eq->TotalChars = consts::CHARACTER_CREATION_LIMIT;
unsigned char *bufptr = (unsigned char *)eq->entries;
int r;
for (r = 0; r < char_count; r++) {
{ //pre-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->level = emu->level[r];
eq2->hairstyle = emu->hairstyle[r];
eq2->gender = emu->gender[r];
memcpy(eq2->name, emu->name[r], strlen(emu->name[r]) + 1);
}
//adjust for name.
bufptr += strlen(emu->name[r]);
{ //post-name section...
structs::CharacterSelectEntry_Struct *eq2 = (structs::CharacterSelectEntry_Struct *) bufptr;
eq2->beard = emu->beard[r];
eq2->haircolor = emu->haircolor[r];
eq2->face = emu->face[r];
int k;
for (k = 0; k < _MaterialCount; k++) {
eq2->equip[k].material = emu->equip[r][k].material;
eq2->equip[k].unknown1 = emu->equip[r][k].unknown1;
eq2->equip[k].elitematerial = emu->equip[r][k].elitematerial;
eq2->equip[k].color.color = emu->equip[r][k].color.color;
}
eq2->primary = emu->primary[r];
eq2->secondary = emu->secondary[r];
eq2->tutorial = emu->tutorial[r]; // was u15
eq2->u15 = 0xff;
eq2->deity = emu->deity[r];
eq2->zone = emu->zone[r];
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];
// Special Underfoot adjustment - field should really be 'AdditionalChars' or 'BonusChars' in this client
uint32 adjusted_total = eq->TotalChars - 8; // Yes, it rolls under for '< 8' - probably an int32 field
eq->TotalChars = adjusted_total;
emu_ptr = __emu_buffer;
emu_ptr += sizeof(CharacterSelect_Struct);
unsigned char *eq_ptr = __packet->pBuffer;
eq_ptr += sizeof(structs::CharacterSelect_Struct);
for (int counter = 0; counter < character_count; ++counter) {
emu_cse = (CharacterSelectEntry_Struct *)emu_ptr;
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;
}
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();
@ -2621,7 +2667,7 @@ namespace UF
OUT(material);
OUT(unknown06);
OUT(elite_material);
OUT(color.color);
OUT(color.Color);
OUT(wear_slot_id);
FINISH_ENCODE();
@ -2693,42 +2739,23 @@ namespace UF
ENCODE(OP_ZonePlayerToBind)
{
ENCODE_LENGTH_ATLEAST(ZonePlayerToBind_Struct);
SETUP_VAR_ENCODE(ZonePlayerToBind_Struct);
ALLOC_LEN_ENCODE(sizeof(structs::ZonePlayerToBind_Struct) + strlen(emu->zone_name));
ZonePlayerToBind_Struct *zps = (ZonePlayerToBind_Struct*)(*p)->pBuffer;
__packet->SetWritePosition(0);
__packet->WriteUInt16(emu->bind_zone_id);
__packet->WriteUInt16(emu->bind_instance_id);
__packet->WriteFloat(emu->x);
__packet->WriteFloat(emu->y);
__packet->WriteFloat(emu->z);
__packet->WriteFloat(emu->heading);
__packet->WriteString(emu->zone_name);
__packet->WriteUInt8(1); // save items
__packet->WriteUInt32(0); // hp
__packet->WriteUInt32(0); // mana
__packet->WriteUInt32(0); // endurance
std::stringstream ss(std::stringstream::in | std::stringstream::out | std::stringstream::binary);
unsigned char *buffer1 = new unsigned char[sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)];
structs::ZonePlayerToBindHeader_Struct *zph = (structs::ZonePlayerToBindHeader_Struct*)buffer1;
unsigned char *buffer2 = new unsigned char[sizeof(structs::ZonePlayerToBindFooter_Struct)];
structs::ZonePlayerToBindFooter_Struct *zpf = (structs::ZonePlayerToBindFooter_Struct*)buffer2;
zph->x = zps->x;
zph->y = zps->y;
zph->z = zps->z;
zph->heading = zps->heading;
zph->bind_zone_id = zps->bind_zone_id;
zph->bind_instance_id = zps->bind_instance_id;
strcpy(zph->zone_name, zps->zone_name);
zpf->unknown021 = 1;
zpf->unknown022 = 0;
zpf->unknown023 = 0;
zpf->unknown024 = 0;
ss.write((const char*)buffer1, (sizeof(structs::ZonePlayerToBindHeader_Struct) + strlen(zps->zone_name)));
ss.write((const char*)buffer2, sizeof(structs::ZonePlayerToBindFooter_Struct));
delete[] buffer1;
delete[] buffer2;
delete[](*p)->pBuffer;
(*p)->pBuffer = new unsigned char[ss.str().size()];
(*p)->size = ss.str().size();
memcpy((*p)->pBuffer, ss.str().c_str(), ss.str().size());
dest->FastQueuePacket(&(*p));
FINISH_ENCODE();
}
ENCODE(OP_ZoneServerInfo)
@ -3002,7 +3029,7 @@ namespace UF
for (k = 0; k < 9; ++k)
{
{
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].color);
VARSTRUCT_ENCODE_TYPE(uint32, Buffer, emu->colors[k].Color);
}
}
}
@ -3012,11 +3039,11 @@ namespace UF
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, 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);
}
@ -3026,9 +3053,9 @@ namespace UF
structs::EquipStruct *Equipment = (structs::EquipStruct *)Buffer;
for (k = 0; k < 9; k++) {
Equipment[k].material = emu->equipment[k].material;
Equipment[k].unknown1 = emu->equipment[k].unknown1;
Equipment[k].elitematerial = emu->equipment[k].elitematerial;
Equipment[k].Material = emu->equipment[k].Material;
Equipment[k].Unknown1 = emu->equipment[k].Unknown1;
Equipment[k].EliteMaterial = emu->equipment[k].EliteMaterial;
}
Buffer += (sizeof(structs::EquipStruct) * 9);
@ -3728,7 +3755,7 @@ namespace UF
IN(material);
IN(unknown06);
IN(elite_material);
IN(color.color);
IN(color.Color);
IN(wear_slot_id);
emu->hero_forge_model = 0;
emu->unknown18 = 0;

View File

@ -101,6 +101,8 @@ namespace UF {
}
namespace consts {
static const size_t CHARACTER_CREATION_LIMIT = 12;
static const uint16 MAP_POSSESSIONS_SIZE = slots::_MainCount;
static const uint16 MAP_BANK_SIZE = 24;
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_CONTAINER_SIZE = 10;
static const uint32 BANDOLIERS_COUNT = 20; // count = number of bandolier instances
static const uint32 BANDOLIER_SIZE = 4; // size = number of equipment slots in bandolier instance
static const uint32 POTION_BELT_SIZE = 5;
static const size_t BANDOLIERS_SIZE = 20; // number of bandolier instances
static const size_t BANDOLIER_ITEM_COUNT = 4; // number of equipment slots in bandolier instance
static const size_t POTION_BELT_ITEM_COUNT = 5;
static const size_t TEXT_LINK_BODY_LENGTH = 50;
}

View File

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

View File

@ -102,6 +102,7 @@ RULE_INT ( Character, BaseInstrumentSoftCap, 36) // Softcap for instrument mods,
RULE_INT ( Character, BaseRunSpeedCap, 158) // Base Run Speed Cap, on live it's 158% which will give you a runspeed of 1.580 hard capped to 225.
RULE_INT ( Character, OrnamentationAugmentType, 20) //Ornamentation Augment Type
RULE_REAL(Character, EnvironmentDamageMulipliter, 1)
RULE_BOOL(Character, UnmemSpellsOnDeath, true)
RULE_CATEGORY_END()
RULE_CATEGORY( Mercs )
@ -299,7 +300,7 @@ RULE_INT ( Spells, MaxCastTimeReduction, 50) //Max percent your spell cast time
RULE_INT ( Spells, RootBreakFromSpells, 55) //Chance for root to break when cast on.
RULE_INT ( Spells, DeathSaveCharismaMod, 3) //Determines how much charisma effects chance of death save firing.
RULE_INT ( Spells, DivineInterventionHeal, 8000) //Divine intervention heal amount.
RULE_BOOL ( Spells, AdditiveBonusValues, false) //Allow certain bonuses to be calculated by adding together the value from each item, instead of taking the highest value. (ie Add together all Cleave Effects)
RULE_INT ( Spells, AdditiveBonusWornType, 0) //Calc worn bonuses to add together (instead of taking highest) if set to THIS worn type. (2=Will covert live items automatically)
RULE_BOOL ( Spells, UseCHAScribeHack, false) //ScribeSpells and TrainDiscs quest functions will ignore entries where field 12 is CHA. What's the best way to do this?
RULE_BOOL ( Spells, BuffLevelRestrictions, true) //Buffs will not land on low level toons like live
RULE_INT ( Spells, RootBreakCheckChance, 70) //Determines chance for a root break check to occur each buff tick.
@ -328,6 +329,7 @@ RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true f
RULE_BOOL ( Spells, SwarmPetTargetLock, false) // Use old method of swarm pets target locking till target dies then despawning.
RULE_BOOL ( Spells, NPC_UseFocusFromSpells, true) // Allow npcs to use most spell derived focus effects.
RULE_BOOL ( Spells, NPC_UseFocusFromItems, false) // Allow npcs to use most item derived focus effects.
RULE_BOOL ( Spells, UseAdditiveFocusFromWornSlot, false) // Allows an additive focus effect to be calculated from worn slot.
RULE_CATEGORY_END()
RULE_CATEGORY( Combat )

View File

@ -1158,7 +1158,7 @@ void SharedDatabase::GetFactionListInfo(uint32 &list_count, uint32 &max_lists) {
auto row = results.begin();
list_count = static_cast<uint32>(atoul(row[0]));
max_lists = static_cast<uint32>(atoul(row[1]));
max_lists = static_cast<uint32>(atoul(row[1] ? row[1] : "0"));
}
const NPCFactionList* SharedDatabase::GetNPCFactionEntry(uint32 id) {
@ -1235,9 +1235,6 @@ bool SharedDatabase::LoadNPCFactionLists() {
uint32 list_count = 0;
uint32 max_lists = 0;
GetFactionListInfo(list_count, max_lists);
if(list_count == 0) {
EQ_EXCEPT("SharedDatabase", "Database returned no result");
}
uint32 size = static_cast<uint32>(EQEmu::FixedMemoryHashSet<NPCFactionList>::estimated_size(
list_count, max_lists));
@ -1837,7 +1834,7 @@ void SharedDatabase::GetLootTableInfo(uint32 &loot_table_count, uint32 &max_loot
auto row = results.begin();
loot_table_count = static_cast<uint32>(atoul(row[0]));
max_loot_table = static_cast<uint32>(atoul(row[1]));
max_loot_table = static_cast<uint32>(atoul(row[1] ? row[1] : "0"));
loot_table_entries = static_cast<uint32>(atoul(row[2]));
}
@ -1858,7 +1855,7 @@ void SharedDatabase::GetLootDropInfo(uint32 &loot_drop_count, uint32 &max_loot_d
auto row =results.begin();
loot_drop_count = static_cast<uint32>(atoul(row[0]));
max_loot_drop = static_cast<uint32>(atoul(row[1]));
max_loot_drop = static_cast<uint32>(atoul(row[1] ? row[1] : "0"));
loot_drop_entries = static_cast<uint32>(atoul(row[2]));
}

View File

@ -71,7 +71,6 @@ const std::string StringFormat(const char* format, ...)
return output;
}
// normal strncpy doesnt put a null term on copied strings, this one does
// ref: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wcecrt/htm/_wcecrt_strncpy_wcsncpy.asp
char* strn0cpy(char* dest, const char* source, uint32 size) {
@ -408,3 +407,11 @@ bool isAlphaNumeric(const char *text)
return true;
}
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string) {
auto index = string_subject.find_first_of(search_string);
while (index != std::string::npos) {
string_subject.replace(index, index + 1, replace_string);
index = string_subject.find_first_of(search_string);
}
}

View File

@ -23,37 +23,34 @@
#include "types.h"
const std::string vStringFormat(const char* format, va_list args);
const std::string StringFormat(const char* format, ...);
std::string EscapeString(const std::string &s);
std::string EscapeString(const char *src, size_t sz);
const char *MakeLowerString(const char *source);
void MakeLowerString(const char *source, char *target);
int MakeAnyLenString(char** ret, const char* format, ...);
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...);
uint32 hextoi(const char* num);
uint64 hextoi64(const char* num);
bool atobool(const char* iBool);
char* strn0cpy(char* dest, const char* source, uint32 size);
// return value =true if entire string(source) fit, false if it was truncated
bool isAlphaNumeric(const char *text);
bool strn0cpyt(char* dest, const char* source, uint32 size);
char *CleanMobName(const char *in, char *out);
char *RemoveApostrophes(const char *s);
char* strn0cpy(char* dest, const char* source, uint32 size);
const char *ConvertArray(int input, char *returnchar);
const char *ConvertArrayF(float input, char *returnchar);
const char *MakeLowerString(const char *source);
const std::string StringFormat(const char* format, ...);
const std::string vStringFormat(const char* format, va_list args);
void RemoveApostrophes(std::string &s);
char *RemoveApostrophes(const char *s);
int MakeAnyLenString(char** ret, const char* format, ...);
std::string EscapeString(const char *src, size_t sz);
std::string EscapeString(const std::string &s);
std::vector<std::string> SplitString(const std::string &s, char delim);
bool isAlphaNumeric(const char *text);
uint32 AppendAnyLenString(char** ret, uint32* bufsize, uint32* strlen, const char* format, ...);
uint32 hextoi(const char* num);
uint64 hextoi64(const char* num);
void MakeLowerString(const char *source, char *target);
void RemoveApostrophes(std::string &s);
void find_replace(std::string& string_subject, const std::string& search_string, const std::string& replace_string);
#endif

View File

@ -22,7 +22,7 @@
#define LOGIN_VERSION "0.8.0"
#define EQEMU_PROTOCOL_VERSION "0.3.10"
#define CURRENT_VERSION "1.0.0"
#define CURRENT_VERSION "1.1.3"
/*
Everytime a Database SQL is added to Github,
@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9073
#define CURRENT_BINARY_DATABASE_VERSION 9077
#define COMPILE_DATE __DATE__
#define COMPILE_TIME __TIME__
#ifndef WIN32

View File

@ -386,6 +386,16 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings){
log_settings[log_category].log_to_file = atoi(row[3]);
log_settings[log_category].log_to_gmsay = atoi(row[4]);
/* Determine if any output method is enabled for the category
and set it to 1 so it can used to check if category is enabled */
const bool log_to_console = log_settings[log_category].log_to_console > 0;
const bool log_to_file = log_settings[log_category].log_to_file > 0;
const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay;
if (is_category_enabled)
log_settings[log_category].is_category_enabled = 1;
/*
This determines whether or not the process needs to actually file log anything.
If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open

View File

@ -31,9 +31,6 @@ void LoadFactions(SharedDatabase *database) {
uint32 lists = 0;
uint32 max_list = 0;
database->GetFactionListInfo(lists, max_list);
if(lists == 0) {
EQ_EXCEPT("Shared Memory", "Unable to get any factions from the database.");
}
uint32 size = static_cast<uint32>(EQEmu::FixedMemoryHashSet<NPCFactionList>::estimated_size(lists, max_list));
EQEmu::MemoryMappedFile mmf("shared/faction", size);

View File

@ -600,6 +600,16 @@ void Database::LoadLogSettings(EQEmuLogSys::LogSettings* log_settings){
log_settings[log_category].log_to_file = atoi(row[3]);
log_settings[log_category].log_to_gmsay = atoi(row[4]);
/* Determine if any output method is enabled for the category
and set it to 1 so it can used to check if category is enabled */
const bool log_to_console = log_settings[log_category].log_to_console > 0;
const bool log_to_file = log_settings[log_category].log_to_file > 0;
const bool log_to_gmsay = log_settings[log_category].log_to_gmsay > 0;
const bool is_category_enabled = log_to_console || log_to_file || log_to_gmsay;
if (is_category_enabled)
log_settings[log_category].is_category_enabled = 1;
/*
This determines whether or not the process needs to actually file log anything.
If we go through this whole loop and nothing is set to any debug level, there is no point to create a file or keep anything open

View File

@ -129,7 +129,7 @@ OP_GuildInviteAccept=0x78a5
OP_GuildDemote=0x3100
OP_GuildPromote=0x2945
OP_GuildPublicNote=0x3c2c
OP_GuildManageBanker=0x096d # Was 0x0737
OP_GuildManageBanker=0x389c # Was 0x096d
OP_GuildBank=0x2ab0 # Was 0x10c3
OP_SetGuildRank=0x3599
OP_GuildUpdateURLAndChannel=0x7851

View File

@ -128,7 +128,7 @@ OP_GuildInviteAccept=0x7053
OP_GuildDemote=0x2d4e
OP_GuildPromote=0x6a98
OP_GuildPublicNote=0x5053
OP_GuildManageBanker=0x748f
OP_GuildManageBanker=0x3f35
OP_GuildBank=0x5134
OP_SetGuildRank=0x0b9c
OP_GuildUpdateURLAndChannel=0x2958
@ -353,21 +353,22 @@ OP_Marquee=0x502e
OP_ItemRecastDelay=0x15a9
#OP_OpenInventory=0x0000 # Likely does not exist in RoF -U
OP_DzQuit=0x205f
OP_DzListTimers=0x0398
OP_DzAddPlayer=0x59ca
OP_DzRemovePlayer=0x4701
OP_DzSwapPlayer=0x1abc
OP_DzMakeLeader=0x405b
OP_DzPlayerList=0x543d
OP_DzJoinExpeditionConfirm=0x14c6
OP_DzJoinExpeditionReply=0x7f4b
OP_DzExpeditionInfo=0x4f7e
OP_DzExpeditionList=0x9119
OP_DzMemberStatus=0xb2e3
OP_DzLeaderStatus=0x32f0
# Expeditions
OP_DzAddPlayer=0x4701
OP_DzRemovePlayer=0x1abc
OP_DzSwapPlayer=0x405b
OP_DzMakeLeader=0x543d
OP_DzPlayerList=0x14c6
OP_DzJoinExpeditionConfirm=0x7f4b
OP_DzJoinExpeditionReply=0x1950
OP_DzListTimers=0x7b68
OP_DzExpeditionInfo=0x9119
OP_DzExpeditionList=0x205f
OP_DzQuit=0xb2e3
OP_DzMemberStatus=0x32f0
OP_DzLeaderStatus=0x3de9
OP_DzMemberList=0x5ae4
OP_DzExpeditionEndsWarning=0x383c
OP_DzMemberList=0x3de9
OP_DzCompass=0x3e0e
OP_DzChooseZone=0x0b7d
@ -401,12 +402,12 @@ OP_LootComplete=0x55c4
# bazaar trader stuff:
OP_BazaarSearch=0x39d6
OP_TraderDelItem=0x0000
OP_TraderDelItem=0x5829
OP_BecomeTrader=0x61b3
OP_TraderShop=0x31df
OP_Trader=0x4ef5
OP_TraderBuy=0x0000
OP_Barter=0x243a
OP_TraderBuy=0x0000
OP_ShopItem=0x0000
OP_BazaarInspect=0x0000
OP_Bazaar=0x0000

View File

@ -525,7 +525,7 @@ OP_KnowledgeBase=0x0000
OP_SlashAdventure=0x571a # /adventure
OP_VetRewardsAvaliable=0x0557
OP_VetClaimRequest=0x6ba0
OP_VetClaimReply=0x0000
OP_VetClaimReply=0x407e
OP_BecomePVPPrompt=0x36B2 #guessed from ASM
OP_PVPStats=0x5cc0
OP_PVPLeaderBoardRequest=0x61d2

View File

@ -327,6 +327,10 @@
9071|2015_01_29_merc_stats_table_update.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'statscale'|empty|
9072|2015_01_30_merc_attack_delay.sql|SHOW COLUMNS FROM `merc_stats` LIKE 'attack_delay'|empty|
9073|2015_01_31_character_item_recast.sql|SHOW TABLES LIKE 'character_item_recast'|empty|
9074|2015_02_01_logsys_packet_logs.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client'|empty|
9075|2015_02_02_logsys_packet_logs_with_dump.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Packet: Server -> Client With Dump'|empty|
9076|2015_02_04_average_coin.sql|SHOW COLUMNS FROM `loottable` WHERE Field = 'avgcoin'|contains|smallint
9077|2015_02_12_zone_gravity.sql|SHOW COLUMNS FROM `zone` LIKE 'gravity'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,50 @@
-- Updates live pets who should be type (5) - Pet locks onto target until dead.
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword99Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword99Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword99Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword94Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword94Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword94Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword89Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword89Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "WizSwarmSword89Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_79_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_79_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_79_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_74_";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_67_";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SumSword";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SUMHammer4";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SUMHammer3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SUMHammer2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SUMHammer1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SumHammer";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "Burnout";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SumSword";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "SumHammer";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_67_";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_73_";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_73_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_73_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_73_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_78_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_78_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_78_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_83_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_83_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_83_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "wizard_sword_84_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_88_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_88_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_88_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_94_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_94_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_94_Rk3";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_99_Rk1";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_99_Rk2";
UPDATE pets SET petcontrol = 5 WHERE type LIKE "cleric_hammer_99_Rk3";

View File

@ -0,0 +1 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:UseAdditiveFocusFromWornSlot', 'false', '[Not live like] If a focus effect is placed in a worn slot the base value will calculated as an additive bonus to regular focus effects.');

View File

@ -0,0 +1,4 @@
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AdditiveBonusWornType', '0', 'Calcs worn bonuses to add together (instead of taking highest) if item set to THIS worn type. Will stack with regular worn bonuses. (2=Will cause all live items to use this behavior)');
-- This is no longer used - Set the above value equal to 2 to achieve the same effect.
DELETE FROM `rule_values` WHERE rule_name LIKE "Spells:AdditiveBonusValues";

View File

@ -0,0 +1,3 @@
REPLACE INTO `logsys_categories` (`log_category_id`, `log_category_description`) VALUES ('39', 'Packet: Server -> Client');
REPLACE INTO `logsys_categories` (`log_category_id`, `log_category_description`) VALUES ('5', 'Packet: Client -> Server');
REPLACE INTO `logsys_categories` (`log_category_id`, `log_category_description`) VALUES ('40', 'Packet: Client -> Server Unhandled');

View File

@ -0,0 +1,2 @@
REPLACE INTO `logsys_categories` (`log_category_id`, `log_category_description`) VALUES ('41', 'Packet: Server -> Client With Dump');
REPLACE INTO `logsys_categories` (`log_category_id`, `log_category_description`) VALUES ('42', 'Packet: Server -> Client With Dump');

View File

@ -0,0 +1,2 @@
ALTER TABLE `loottable` CHANGE COLUMN `avgcoin` `avgcoin` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `maxcash`;
UPDATE `loottable` SET avgcoin = 0;

View File

@ -0,0 +1,2 @@
ALTER TABLE `zone`
ADD COLUMN `gravity` float NOT NULL DEFAULT .4 AFTER `snow_duration4`;

View File

@ -16,6 +16,7 @@
#include "../common/string_util.h"
#include "../common/clientversions.h"
#include "../common/random.h"
#include "../common/shareddb.h"
#include "client.h"
#include "worlddb.h"
@ -86,11 +87,11 @@ Client::Client(EQStreamInterface* ieqs)
charid = 0;
pwaitingforbootup = 0;
StartInTutorial = false;
ClientVersionBit = 0;
numclients++;
if (eqs->GetClientVersion() != ClientVersion::Unknown)
ClientVersionBit = 1 << (static_cast<unsigned int>(eqs->GetClientVersion()) - 1);
m_ClientVersion = eqs->GetClientVersion();
m_ClientVersionBit = ClientBitFromVersion(m_ClientVersion);
numclients++;
}
Client::~Client() {
@ -161,32 +162,34 @@ void Client::SendCharInfo() {
cle->SetOnline(CLE_Status_CharSelect);
}
if (ClientVersionBit & BIT_RoFAndLater)
{
// Can make max char per account into a rule - New to VoA
SendMaxCharCreate(10);
if (m_ClientVersionBit & BIT_RoFAndLater) {
SendMaxCharCreate();
SendMembership();
SendMembershipSettings();
}
seencharsel = true;
// Send OP_SendCharInfo
auto outapp = new EQApplicationPacket(OP_SendCharInfo, sizeof(CharacterSelect_Struct));
CharacterSelect_Struct* cs = (CharacterSelect_Struct*)outapp->pBuffer;
EQApplicationPacket *outapp = nullptr;
database.GetCharSelectInfo(GetAccountID(), &outapp, m_ClientVersionBit);
database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit);
QueuePacket(outapp);
if (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);
}
void Client::SendMaxCharCreate(int max_chars) {
void Client::SendMaxCharCreate() {
auto outapp = new EQApplicationPacket(OP_SendMaxCharacters, sizeof(MaxCharacters_Struct));
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);
safe_delete(outapp);
@ -671,7 +674,7 @@ bool Client::HandleCharacterCreatePacket(const EQApplicationPacket *app) {
}
else
{
if(ClientVersionBit & BIT_TitaniumAndEarlier)
if (m_ClientVersionBit & BIT_TitaniumAndEarlier)
StartInTutorial = true;
SendCharInfo();
}
@ -716,66 +719,71 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
return true;
}
if(!pZoning && ew->return_home && !ew->tutorial) {
auto cs = new CharacterSelect_Struct;
memset(cs, 0, sizeof(CharacterSelect_Struct));
database.GetCharSelectInfo(GetAccountID(), cs, ClientVersionBit);
bool home_enabled = false;
// This can probably be moved outside and have another method return requested info (don't forget to remove the #include "../common/shareddb.h" above)
if (!pZoning) {
size_t character_limit = EQLimits::CharacterCreationLimit(eqs->GetClientVersion());
if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT) { character_limit = EmuConstants::CHARACTER_CREATION_LIMIT; }
if (eqs->GetClientVersion() == ClientVersion::Titanium) { character_limit = 8; }
for(int x = 0; x < 10; ++x)
{
if(strcasecmp(cs->name[x], char_name) == 0)
{
if(cs->gohome[x] == 1)
{
home_enabled = true;
break;
std::string tgh_query = StringFormat(
"SELECT "
"`id`, "
"name, "
"`level`, "
"last_login "
"FROM "
"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, EnableReturnHomeButton)) {
int now = time(nullptr);
if ((now - atoi(row[3])) >= RuleI(World, MinOfflineTimeToReturnHome)) {
home_enabled = true;
break;
}
}
}
}
}
safe_delete(cs);
if(home_enabled) {
zoneID = database.MoveCharacterToBind(charid,4);
}
else {
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.");
eqs->Close();
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;
}
if (home_enabled) {
zoneID = database.MoveCharacterToBind(charid, 4);
}
else {
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.");
eqs->Close();
return true;
}
}
safe_delete(cs);
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;
/* Check Tutorial*/
if (RuleB(World, EnableTutorialButton) && (ew->tutorial || StartInTutorial)) {
bool tutorial_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))) {
tutorial_enabled = true;
break;
}
}
}
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;
if(ClientVersionBit & BIT_UFAndLater)
if (m_ClientVersionBit & BIT_UFAndLater)
ConnectionType = 'U';
else if(ClientVersionBit & BIT_SoFAndLater)
else if (m_ClientVersionBit & BIT_SoFAndLater)
ConnectionType = 'S';
else
ConnectionType = 'C';
@ -872,7 +880,7 @@ bool Client::HandleEnterWorldPacket(const EQApplicationPacket *app) {
outapp2 = new EQApplicationPacket(OP_SetChatServer2);
if(ClientVersionBit & BIT_TitaniumAndEarlier)
if (m_ClientVersionBit & BIT_TitaniumAndEarlier)
ConnectionType = 'M';
sprintf(buffer,"%s,%i,%s.%s,%c%08X",
@ -906,7 +914,7 @@ bool Client::HandleDeleteCharacterPacket(const EQApplicationPacket *app) {
bool Client::HandleZoneChangePacket(const EQApplicationPacket *app) {
// HoT sends this to world while zoning and wants it echoed back.
if(ClientVersionBit & BIT_RoFAndLater)
if (m_ClientVersionBit & BIT_RoFAndLater)
{
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);
/* Validate the char creation struct */
if (ClientVersionBit & BIT_SoFAndLater) {
if (m_ClientVersionBit & BIT_SoFAndLater) {
if (!CheckCharCreateInfoSoF(cc)) {
Log.Out(Logs::Detail, Logs::World_Server,"CheckCharCreateInfo did not validate the request (bad race/class/stats)");
return false;
@ -1438,7 +1446,7 @@ bool Client::OPCharCreate(char *name, CharCreate_Struct *cc)
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 (ClientVersionBit & BIT_SoFAndLater) {
if (m_ClientVersionBit & BIT_SoFAndLater) {
Log.Out(Logs::Detail, Logs::World_Server,"Found 'SoFStartZoneID' rule setting: %i", RuleI(World, SoFStartZoneID));
if (RuleI(World, SoFStartZoneID) > 0) {
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.*/
bool ValidStartZone = database.GetStartZone(&pp, cc, ClientVersionBit & BIT_TitaniumAndEarlier);
bool ValidStartZone = database.GetStartZone(&pp, cc, m_ClientVersionBit & BIT_TitaniumAndEarlier);
if (!ValidStartZone){
return false;

View File

@ -41,7 +41,7 @@ public:
bool Process();
void ReceiveData(uchar* buf, int len);
void SendCharInfo();
void SendMaxCharCreate(int max_chars);
void SendMaxCharCreate();
void SendMembership();
void SendMembershipSettings();
void EnterWorld(bool TryBootup = true);
@ -84,7 +84,8 @@ private:
uint32 pwaitingforbootup;
bool StartInTutorial;
uint32 ClientVersionBit;
ClientVersion m_ClientVersion;
uint32 m_ClientVersionBit;
bool OPCharCreate(char *name, CharCreate_Struct *cc);
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
void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct* cs, uint32 ClientVersion) {
Inventory *inv;
uint8 has_home = 0;
uint8 has_bind = 0;
/* Initialize Variables */
for (int i=0; i<10; i++) {
strcpy(cs->name[i], "<none>");
cs->zone[i] = 0;
cs->level[i] = 0;
cs->tutorial[i] = 0;
cs->gohome[i] = 0;
}
void WorldDatabase::GetCharSelectInfo(uint32 accountID, EQApplicationPacket **outApp, uint32 clientVersionBit)
{
/* Set Character Creation Limit */
ClientVersion client_version = ClientVersionFromBit(clientVersionBit);
size_t character_limit = EQLimits::CharacterCreationLimit(client_version);
// Validate against absolute server max
if (character_limit > EmuConstants::CHARACTER_CREATION_LIMIT)
character_limit = EmuConstants::CHARACTER_CREATION_LIMIT;
// Force Titanium clients to use '8'
if (client_version == ClientVersion::Titanium)
character_limit = 8;
/* Get Character Info */
std::string cquery = StringFormat(
"SELECT "
@ -72,52 +72,102 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*
"zone_id " // 19
"FROM "
"character_data "
"WHERE `account_id` = %i ORDER BY `name` LIMIT 10 ", account_id);
auto results = database.QueryDatabase(cquery); int char_num = 0;
"WHERE `account_id` = %i ORDER BY `name` LIMIT %u", accountID, character_limit);
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) {
CharacterSelectEntry_Struct *cse = (CharacterSelectEntry_Struct *)buff_ptr;
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));
/* 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]);
uint32 character_id = atoi(row[0]);
strcpy(cs->name[char_num], row[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]);
for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) { // Processed below
cse->Equip[matslot].Material = 0;
cse->Equip[matslot].Unknown1 = 0;
cse->Equip[matslot].EliteMaterial = 0;
cse->Equip[matslot].HeroForgeModel = 0;
cse->Equip[matslot].Material2 = 0;
cse->Equip[matslot].Color.Color = 0;
}
if (RuleB(World, EnableTutorialButton) && (lvl <= RuleI(World, MaxLevelForTutorial)))
cs->tutorial[char_num] = 1;
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 */
if (RuleB(World, EnableTutorialButton) && (cse->Level <= RuleI(World, MaxLevelForTutorial))) {
cse->Tutorial = 1;
}
if (RuleB(World, EnableReturnHomeButton)) {
int now = time(nullptr);
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 */
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) {
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 (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",
cs->class_[char_num], cs->deity[char_num], cs->race[char_num]);
cse->Class, cse->Deity, cse->Race);
auto results_bind = database.QueryDatabase(cquery);
for (auto row_d = results_bind.begin(); row_d != results_bind.end(); ++row_d) {
/* If a bind_id is specified, make them start there */
@ -133,23 +183,22 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*
float z = atof(row_d[4]);
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[0] = pp.binds[4];
/* 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)"
" 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);
auto results_bset = QueryDatabase(query);
auto results_bset = QueryDatabase(query);
}
/* 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)"
" 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);
auto results_bset = QueryDatabase(query);
auto results_bset = QueryDatabase(query);
}
}
/* Bind End */
@ -157,97 +206,78 @@ void WorldDatabase::GetCharSelectInfo(uint32 account_id, CharacterSelect_Struct*
/* 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)
{
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]);
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]);
}
/* Character Material Data End */
/* Load Inventory */
inv = new Inventory;
if (GetInventory(account_id, cs->name[char_num], inv))
{
// If we ensure that the material data is updated appropriately, we can do away with inventory loads
if (GetInventory(accountID, cse->Name, &inv)) {
const Item_Struct* item = nullptr;
const ItemInst* inst = nullptr;
int16 invslot = 0;
for (uint32 matslot = 0; matslot < _MaterialCount; matslot++)
{
for (uint32 matslot = 0; matslot < _MaterialCount; matslot++) {
invslot = Inventory::CalcSlotFromMaterial(matslot);
if (invslot == INVALID_INDEX) { continue; }
inst = inv->GetItem(invslot);
inst = inv.GetItem(invslot);
if (inst == nullptr) { continue; }
item = inst->GetItem();
if (item == nullptr) { continue; }
if (matslot > 6)
{
if (matslot > 6) {
uint32 idfile = 0;
// Weapon Models
if (inst->GetOrnamentationIDFile() != 0)
{
if (inst->GetOrnamentationIDFile() != 0) {
idfile = inst->GetOrnamentationIDFile();
cs->equip[char_num][matslot].material = idfile;
cse->Equip[matslot].Material = idfile;
}
else
{
if (strlen(item->IDFile) > 2)
{
else {
if (strlen(item->IDFile) > 2) {
idfile = atoi(&item->IDFile[2]);
cs->equip[char_num][matslot].material = idfile;
cse->Equip[matslot].Material = idfile;
}
}
if (matslot == MaterialPrimary)
{
cs->primary[char_num] = idfile;
if (matslot == MaterialPrimary) {
cse->PrimaryIDFile = idfile;
}
else
{
cs->secondary[char_num] = idfile;
else {
cse->SecondaryIDFile = idfile;
}
}
else
{
else {
uint32 color = 0;
if (pp.item_tint[matslot].rgb.use_tint)
{
color = pp.item_tint[matslot].color;
if (pp.item_tint[matslot].RGB.UseTint) {
color = pp.item_tint[matslot].Color;
}
else
{
else {
color = inst->GetColor();
}
// Armor Materials/Models
cs->equip[char_num][matslot].material = item->Material;
cs->equip[char_num][matslot].elitematerial = item->EliteMaterial;
cs->equip[char_num][matslot].heroforgemodel = inst->GetOrnamentHeroModel(matslot);
cs->equip[char_num][matslot].color.color = color;
cse->Equip[matslot].Material = item->Material;
cse->Equip[matslot].EliteMaterial = item->EliteMaterial;
cse->Equip[matslot].HeroForgeModel = inst->GetOrnamentHeroModel(matslot);
cse->Equip[matslot].Color.Color = color;
}
}
}
else
{
printf("Error loading inventory for %s\n", cs->name[char_num]);
else {
printf("Error loading inventory for %s\n", cse->Name);
}
/* Load Inventory End */
safe_delete(inv);
if (++char_num > 10)
{
break;
}
buff_ptr += sizeof(CharacterSelectEntry_Struct);
}
return;
}
int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum) {
int WorldDatabase::MoveCharacterToBind(int CharID, uint8 bindnum)
{
/* if an invalid bind point is specified, use the primary bind */
if (bindnum > 4)
{
@ -330,6 +360,7 @@ bool WorldDatabase::GetStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct*
return true;
}
void WorldDatabase::SetSoFDefaultStartZone(PlayerProfile_Struct* in_pp, CharCreate_Struct* in_cc){
if (in_cc->start_zone == RuleI(World, TutorialZoneID)) {
in_pp->zone_id = in_cc->start_zone;
@ -348,15 +379,15 @@ void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, Cha
{
case 0:
{
if (in_cc->deity == 203) // Cazie erudites go to paineel
if (in_cc->deity == 203) // Cazic-Thule Erudites go to Paineel
{
in_pp->zone_id = 75; // paineel
in_pp->binds[0].zoneId = 75; // paineel
in_pp->zone_id = 75; // paineel
in_pp->binds[0].zoneId = 75;
}
else
{
in_pp->zone_id = 24; // erudnext
in_pp->binds[0].zoneId = 38; // tox
in_pp->zone_id = 24; // erudnext
in_pp->binds[0].zoneId = 38; // tox
}
break;
}
@ -440,6 +471,7 @@ void WorldDatabase::SetTitaniumDefaultStartZone(PlayerProfile_Struct* in_pp, Cha
}
}
}
void WorldDatabase::GetLauncherList(std::vector<std::string> &rl) {
rl.clear();
@ -455,8 +487,8 @@ void WorldDatabase::GetLauncherList(std::vector<std::string> &rl) {
}
void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey) {
void WorldDatabase::SetMailKey(int CharID, int IPAddress, int MailKey)
{
char MailKeyString[17];
if(RuleB(Chat, EnableMailKeyIPVerification) == true)
@ -490,7 +522,8 @@ bool WorldDatabase::GetCharacterLevel(const char *name, int &level)
return true;
}
bool WorldDatabase::LoadCharacterCreateAllocations() {
bool WorldDatabase::LoadCharacterCreateAllocations()
{
character_create_allocations.clear();
std::string query = "SELECT * FROM char_create_point_allocations ORDER BY id";
@ -522,7 +555,8 @@ bool WorldDatabase::LoadCharacterCreateAllocations() {
return true;
}
bool WorldDatabase::LoadCharacterCreateCombos() {
bool WorldDatabase::LoadCharacterCreateCombos()
{
character_create_race_class_combos.clear();
std::string query = "SELECT * FROM char_create_combinations ORDER BY race, class, deity, start_zone";
@ -544,4 +578,3 @@ bool WorldDatabase::LoadCharacterCreateCombos() {
return true;
}

View File

@ -20,6 +20,7 @@
#include "../common/shareddb.h"
#include "../common/zone_numbers.h"
#include "../common/eq_packet.h"
struct PlayerProfile_Struct;
struct CharCreate_Struct;
@ -29,7 +30,7 @@ struct CharacterSelect_Struct;
class WorldDatabase : public SharedDatabase {
public:
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);
void GetLauncherList(std::vector<std::string> &result);

View File

@ -525,7 +525,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(pet.npc_id);
const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id);
if(npc_type == nullptr) {
//log write
Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet spell id: %d", spell_id);
@ -622,7 +622,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(typesid);
const NPCType *npc_type = database.LoadNPCTypesData(typesid);
if(npc_type == nullptr) {
//log write
Log.Out(Logs::General, Logs::Error, "Unknown npc type for swarm pet type id: %d", typesid);
@ -715,7 +715,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
return;
//assuming we have pets in our table; we take the first pet as a base type.
const NPCType *base_type = database.GetNPCType(500);
const NPCType *base_type = database.LoadNPCTypesData(500);
NPCType *make_npc = new NPCType;
memcpy(make_npc, base_type, sizeof(NPCType));

View File

@ -362,13 +362,40 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
//garunteed hit
bool ghit = false;
if((attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500)
if((attacker->aabonuses.MeleeSkillCheck + attacker->spellbonuses.MeleeSkillCheck + attacker->itembonuses.MeleeSkillCheck) > 500)
ghit = true;
bool InFront = false;
if (attacker->InFrontMob(this, attacker->GetX(), attacker->GetY()))
InFront = true;
/*
This special ability adds a negative modifer to the defenders riposte/block/parry/chance
therefore reducing the defenders chance to successfully avoid the melee attack. At present
time this is the only way to fine tune counter these mods on players. This may
ultimately end up being more useful as fields in npc_types.
*/
int counter_all = 0;
int counter_riposte = 0;
int counter_block = 0;
int counter_parry = 0;
int counter_dodge = 0;
if (attacker->GetSpecialAbility(COUNTER_AVOID_DAMAGE)){
counter_all = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 0);
counter_riposte = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE,1);
counter_block = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 2);
counter_parry = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 3);
counter_dodge = attacker->GetSpecialAbilityParam(COUNTER_AVOID_DAMAGE, 4);
}
//////////////////////////////////////////////////////////
// make enrage same as riposte
/////////////////////////////////////////////////////////
if (IsEnraged() && other->InFrontMob(this, other->GetX(), other->GetY())) {
if (IsEnraged() && InFront) {
damage = -3;
Log.Out(Logs::Detail, Logs::Combat, "I am enraged, riposting frontal attack.");
}
@ -377,9 +404,10 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
// riposte
/////////////////////////////////////////////////////////
float riposte_chance = 0.0f;
if (CanRiposte && damage > 0 && CanThisClassRiposte() && other->InFrontMob(this, other->GetX(), other->GetY()))
if (CanRiposte && damage > 0 && CanThisClassRiposte() && InFront)
{
riposte_chance = (100.0f + (float)defender->aabonuses.RiposteChance + (float)defender->spellbonuses.RiposteChance + (float)defender->itembonuses.RiposteChance) / 100.0f;
riposte_chance = (100.0f + static_cast<float>(aabonuses.RiposteChance + spellbonuses.RiposteChance +
itembonuses.RiposteChance - counter_riposte - counter_all)) / 100.0f;
skill = GetSkill(SkillRiposte);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillRiposte, other, -10);
@ -398,28 +426,19 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
///////////////////////////////////////////////////////
bool bBlockFromRear = false;
bool bShieldBlockFromRear = false;
if (this->IsClient()) {
int aaChance = 0;
// a successful roll on this does not mean a successful block is forthcoming. only that a chance to block
// from a direction other than the rear is granted.
// a successful roll on this does not mean a successful block is forthcoming. only that a chance to block
// from a direction other than the rear is granted.
int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind;
//Live AA - HightenedAwareness
int BlockBehindChance = aabonuses.BlockBehind + spellbonuses.BlockBehind + itembonuses.BlockBehind;
if (BlockBehindChance && zone->random.Roll(BlockBehindChance)) {
bBlockFromRear = true;
if (spellbonuses.BlockBehind || itembonuses.BlockBehind)
bShieldBlockFromRear = true; //This bonus should allow a chance to Shield Block from behind.
}
}
if (BlockBehindChance && zone->random.Roll(BlockBehindChance))
bBlockFromRear = true;
float block_chance = 0.0f;
if (damage > 0 && CanThisClassBlock() && (other->InFrontMob(this, other->GetX(), other->GetY()) || bBlockFromRear)) {
block_chance = (100.0f + (float)spellbonuses.IncreaseBlockChance + (float)itembonuses.IncreaseBlockChance) / 100.0f;
if (damage > 0 && CanThisClassBlock() && (InFront || bBlockFromRear)) {
block_chance = (100.0f + static_cast<float>(aabonuses.IncreaseBlockChance + spellbonuses.IncreaseBlockChance +
itembonuses.IncreaseBlockChance - counter_block - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillBlock);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillBlock, other, -10);
@ -435,32 +454,20 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
RollTable[1] = RollTable[0];
}
if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock)
&& (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) {
float bonusShieldBlock = 0.0f;
bonusShieldBlock = static_cast<float>(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock);
RollTable[1] += bonusShieldBlock;
}
if(IsClient() && damage > 0 && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock)
&& (other->InFrontMob(this, other->GetX(), other->GetY()) || bShieldBlockFromRear)) {
if(CastToClient()->m_inv.GetItem(MainPrimary)) {
float bonusStaffBlock = 0.0f;
if (CastToClient()->m_inv.GetItem(MainPrimary)->GetItem()->ItemType == ItemType2HBlunt){
bonusStaffBlock = static_cast<float>(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock);
RollTable[1] += bonusStaffBlock;
}
}
}
//Try Shield Block OR TwoHandBluntBlockCheck
if(damage > 0 && HasShieldEquiped() && (aabonuses.ShieldBlock || spellbonuses.ShieldBlock || itembonuses.ShieldBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.ShieldBlock + spellbonuses.ShieldBlock + itembonuses.ShieldBlock - counter_block - counter_all);
else if(damage > 0 && HasTwoHandBluntEquiped() && (aabonuses.TwoHandBluntBlock || spellbonuses.TwoHandBluntBlock || itembonuses.TwoHandBluntBlock) && (InFront || bBlockFromRear))
RollTable[1] += static_cast<float>(aabonuses.TwoHandBluntBlock + spellbonuses.TwoHandBluntBlock + itembonuses.TwoHandBluntBlock - counter_block - counter_all);
//////////////////////////////////////////////////////
// parry
//////////////////////////////////////////////////////
float parry_chance = 0.0f;
if (damage > 0 && CanThisClassParry() && other->InFrontMob(this, other->GetX(), other->GetY()))
{
parry_chance = (100.0f + (float)defender->spellbonuses.ParryChance + (float)defender->itembonuses.ParryChance) / 100.0f;
if (damage > 0 && CanThisClassParry() && InFront){
parry_chance = (100.0f + static_cast<float>(aabonuses.ParryChance + itembonuses.ParryChance +
itembonuses.ParryChance - counter_parry - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillParry);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillParry, other, -10);
@ -481,9 +488,11 @@ bool Mob::AvoidDamage(Mob* other, int32 &damage, bool CanRiposte)
// dodge
////////////////////////////////////////////////////////
float dodge_chance = 0.0f;
if (damage > 0 && CanThisClassDodge() && other->InFrontMob(this, other->GetX(), other->GetY()))
{
dodge_chance = (100.0f + (float)defender->spellbonuses.DodgeChance + (float)defender->itembonuses.DodgeChance) / 100.0f;
if (damage > 0 && CanThisClassDodge() && InFront){
dodge_chance = (100.0f + static_cast<float>(aabonuses.DodgeChance + spellbonuses.DodgeChance +
itembonuses.DodgeChance - counter_dodge - counter_all)) / 100.0f;
skill = CastToClient()->GetSkill(SkillDodge);
if (IsClient()) {
CastToClient()->CheckIncreaseSkill(SkillDodge, other, -10);
@ -1595,10 +1604,12 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes att
//this generates a lot of 'updates' to the client that the client does not need
BuffFadeNonPersistDeath();
if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover))
UnmemSpellAll(true);
else
UnmemSpellAll(false);
if (RuleB(Character, UnmemSpellsOnDeath)) {
if((GetClientVersionBit() & BIT_SoFAndLater) && RuleB(Character, RespawnFromHover))
UnmemSpellAll(true);
else
UnmemSpellAll(false);
}
if((RuleB(Character, LeaveCorpses) && GetLevel() >= RuleI(Character, DeathItemLossLevel)) || RuleB(Character, LeaveNakedCorpses))
{
@ -2404,8 +2415,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, bool iYellForHelp /*= true*/, bool bFrenzy /*= false*/, bool iBuffTic /*= false*/)
{
assert(other != nullptr);
if(!other)
return;
if (other == this)
return;
@ -3530,6 +3541,10 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
Log.Out(Logs::Detail, Logs::Combat, "Melee Damage reduced to %d", damage);
damage = ReduceAllDamage(damage);
TryTriggerThreshHold(damage, SE_TriggerMeleeThreshold, attacker);
if (skill_used)
CheckNumHitsRemaining(NumHit::IncomingHitSuccess);
} else {
int32 origdmg = damage;
damage = AffectMagicalDamage(damage, spell_id, iBuffTic, attacker);
@ -3545,9 +3560,6 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
TryTriggerThreshHold(damage, SE_TriggerSpellThreshold, attacker);
}
if (skill_used)
CheckNumHitsRemaining(NumHit::IncomingHitSuccess);
if(IsClient() && CastToClient()->sneaking){
CastToClient()->sneaking = false;
SendAppearancePacket(AT_Sneak, 0);

View File

@ -139,7 +139,8 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
// Clear item faction mods
ClearItemFactionBonuses();
ShieldEquiped(false);
SetShieldEquiped(false);
SetTwoHandBluntEquiped(false);
unsigned int i;
//should not include 21 (SLOT_AMMO)
@ -149,9 +150,12 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
continue;
AddItemBonuses(inst, newbon);
//Check if item is secondary slot is a 'shield'. Required for multiple spelll effects.
if (i == MainSecondary && (m_inv.GetItem(MainSecondary)->GetItem()->ItemType == ItemTypeShield))
ShieldEquiped(true);
//These are given special flags due to how often they are checked for various spell effects.
const Item_Struct *item = inst->GetItem();
if (i == MainSecondary && (item && item->ItemType == ItemTypeShield))
SetShieldEquiped(true);
else if (i == MainPrimary && (item && item->ItemType == ItemType2HBlunt))
SetTwoHandBluntEquiped(true);
}
//Power Source Slot
@ -169,6 +173,17 @@ void Client::CalcItemBonuses(StatBonuses* newbon) {
continue;
AddItemBonuses(inst, newbon, false, true);
}
//Optional ability to have worn effects calculate as an addititive bonus instead of highest value
if (RuleI(Spells, AdditiveBonusWornType) && RuleI(Spells, AdditiveBonusWornType) != ET_WornEffect){
for (i = MainCharm; i < MainAmmo; i++) {
const ItemInst* inst = m_inv[i];
if(inst == 0)
continue;
AdditiveWornBonuses(inst, newbon);
}
}
// Caps
if(newbon->HPRegen > CalcHPRegenCap())
newbon->HPRegen = CalcHPRegenCap();
@ -410,12 +425,12 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
else
newbon->DSMitigation += item->DSMitigation;
}
if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true);
if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0);
}
switch(item->BardType)
@ -537,6 +552,45 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
}
void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug) {
/*
Powerful Non-live like option allows developers to add worn effects on items that
can stack with other worn effects of the same spell effect type, instead of only taking the highest value.
Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus.
To enable use RuleI(Spells, AdditiveBonusWornType)
Setting value = 2 Will force all live items to automatically be calculated additivily
Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation
which will also stack with regular (worntype 2) effects. [Ie set rule = 3 and item worntype = 3]
*/
if(!inst || !inst->IsType(ItemClassCommon))
return;
if(inst->GetAugmentType()==0 && isAug == true)
return;
const Item_Struct *item = inst->GetItem();
if(!inst->IsEquipable(GetBaseRace(),GetClass()))
return;
if(GetLevel() < item->ReqLevel)
return;
if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType))
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects
if (!isAug)
{
int i;
for (i = 0; i < EmuConstants::ITEM_COMMON_SIZE; i++) {
AdditiveWornBonuses(inst->GetAugment(i),newbon,true);
}
}
}
void Client::CalcEdibleBonuses(StatBonuses* newbon) {
uint32 i;
@ -639,7 +693,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
uint8 focus = IsFocusEffect(0, 0, true,effect);
if (focus)
{
newbon->FocusEffects[focus] = effect;
newbon->FocusEffects[focus] = static_cast<uint8>(effect);
continue;
}
@ -1393,7 +1447,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN){
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, false, buffs[i].ticsremaining,i);
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i);
if (buffs[i].numhits > 0)
Numhits(true);
@ -1416,10 +1470,11 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
}
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot,
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{
int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false;
Mob *caster = nullptr;
if(!IsAISpellEffect && !IsValidSpell(spell_id))
@ -1439,10 +1494,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
uint8 focus = IsFocusEffect(spell_id, i);
if (focus)
{
new_bonus->FocusEffects[focus] = spells[spell_id].effectid[i];
if (WornType){
if (RuleB(Spells, UseAdditiveFocusFromWornSlot))
new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i];
}
else
new_bonus->FocusEffects[focus] = static_cast<uint8>(spells[spell_id].effectid[i]);
continue;
}
if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType))
AdditiveWornBonus = true;
effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
@ -1808,7 +1872,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CriticalHitChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
if (AdditiveWornBonus) {
if(base2 == -1)
new_bonus->CriticalHitChance[HIGHEST_SKILL+1] += effect_value;
else
@ -1834,7 +1898,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_CrippBlowChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->CrippBlowChance += effect_value;
else if((effect_value < 0) && (new_bonus->CrippBlowChance > effect_value))
@ -1848,7 +1912,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AvoidMeleeChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->AvoidMeleeChanceEffect += effect_value;
else if((effect_value < 0) && (new_bonus->AvoidMeleeChanceEffect > effect_value))
@ -1861,7 +1925,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_RiposteChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->RiposteChance += effect_value;
else if((effect_value < 0) && (new_bonus->RiposteChance > effect_value))
@ -1874,7 +1938,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DodgeChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DodgeChance += effect_value;
else if((effect_value < 0) && (new_bonus->DodgeChance > effect_value))
@ -1887,7 +1951,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ParryChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->ParryChance += effect_value;
else if((effect_value < 0) && (new_bonus->ParryChance > effect_value))
@ -1900,7 +1964,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DualWieldChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DualWieldChance += effect_value;
else if((effect_value < 0) && (new_bonus->DualWieldChance > effect_value))
@ -1914,7 +1978,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DoubleAttackChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->DoubleAttackChance += effect_value;
else if((effect_value < 0) && (new_bonus->DoubleAttackChance > effect_value))
@ -1928,7 +1992,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_TripleAttackChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->TripleAttackChance += effect_value;
else if((effect_value < 0) && (new_bonus->TripleAttackChance > effect_value))
@ -1941,7 +2005,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_MeleeLifetap:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->MeleeLifetap += spells[spell_id].base[i];
else if((effect_value < 0) && (new_bonus->MeleeLifetap > effect_value))
@ -1990,7 +2054,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_HundredHands:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->HundredHands += effect_value;
if (effect_value > 0 && effect_value > new_bonus->HundredHands)
@ -2012,7 +2076,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_HitChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus){
if (AdditiveWornBonus){
if(base2 == -1)
new_bonus->HitChanceEffect[HIGHEST_SKILL+1] += effect_value;
else
@ -2079,7 +2143,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_ProcChance:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
if (AdditiveWornBonus)
new_bonus->ProcChanceSPA += effect_value;
else if((effect_value < 0) && (new_bonus->ProcChanceSPA > effect_value))
@ -2117,7 +2181,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_DivineSave:
{
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
if (AdditiveWornBonus) {
new_bonus->DivineSaveChance[0] += effect_value;
new_bonus->DivineSaveChance[1] = 0;
}
@ -2126,7 +2190,6 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
{
new_bonus->DivineSaveChance[0] = effect_value;
new_bonus->DivineSaveChance[1] = base2;
//SetDeathSaveChance(true);
}
break;
}
@ -3046,12 +3109,12 @@ void NPC::CalcItemBonuses(StatBonuses *newbon)
newbon->ProcChance += cur->CombatEffects;
}
if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon);
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type);
}
if (RuleB(Spells, NPC_UseFocusFromItems)){
if (cur->Focus.Effect>0 && (cur->Focus.Type == ET_Focus)){ // focus effects
ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(cur->Focus.Effect, cur->Focus.Level, newbon);
}
}

View File

@ -4395,24 +4395,24 @@ void Bot::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
item = inst->GetItem();
if (item != 0)
{
ns->spawn.equipment[i].material = item->Material;
ns->spawn.equipment[i].elitematerial = item->EliteMaterial;
ns->spawn.equipment[i].heroforgemodel = item->HerosForgeModel;
ns->spawn.equipment[i].Material = item->Material;
ns->spawn.equipment[i].EliteMaterial = item->EliteMaterial;
ns->spawn.equipment[i].HeroForgeModel = item->HerosForgeModel;
if (armor_tint[i])
{
ns->spawn.colors[i].color = armor_tint[i];
ns->spawn.colors[i].Color = armor_tint[i];
}
else
{
ns->spawn.colors[i].color = item->Color;
ns->spawn.colors[i].Color = item->Color;
}
}
else
{
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)
{
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)
{
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->material = material;
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@ -10959,7 +10959,7 @@ void Bot::CalcItemBonuses()
}
}
if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses);
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type);
}
}
}
@ -11043,7 +11043,7 @@ void Bot::CalcItemBonuses()
}
}
if ((itemtmp->Worn.Effect != 0) && (itemtmp->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses);
ApplySpellsBonuses(itemtmp->Worn.Effect, itemtmp->Worn.Level, &itembonuses,0,itemtmp->Worn.Type);
}
}
}

View File

@ -141,6 +141,10 @@ Client::Client(EQStreamInterface* ieqs)
merc_timer(RuleI(Mercs, UpkeepIntervalMS)),
ItemTickTimer(10000),
ItemQuestTimer(500),
anon_toggle_timer(250),
afk_toggle_timer(250),
helm_toggle_timer(250),
light_update_timer(600),
m_Proximity(FLT_MAX, FLT_MAX, FLT_MAX), //arbitrary large number
m_ZoneSummonLocation(-2.0f,-2.0f,-2.0f),
m_AutoAttackPosition(0.0f, 0.0f, 0.0f, 0.0f),
@ -161,6 +165,7 @@ Client::Client(EQStreamInterface* ieqs)
Trader=false;
Buyer = false;
CustomerID = 0;
TraderID = 0;
TrackingID = 0;
WID = 0;
account_id = 0;
@ -245,7 +250,7 @@ Client::Client(EQStreamInterface* ieqs)
AttemptedMessages = 0;
TotalKarma = 0;
m_ClientVersion = ClientVersion::Unknown;
ClientVersionBit = 0;
m_ClientVersionBit = 0;
AggroCount = 0;
RestRegenHP = 0;
RestRegenMana = 0;
@ -409,6 +414,69 @@ Client::~Client() {
UninitializeBuffSlots();
}
void Client::SendZoneInPackets()
{
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
}
safe_delete(outapp);
SendAATimers();
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
}
void Client::SendLogoutPackets() {
EQApplicationPacket* outapp = new EQApplicationPacket(OP_CancelTrade, sizeof(CancelTrade_Struct));
@ -2471,11 +2539,13 @@ void Client::LogMerchant(Client* player, Mob* merchant, uint32 quantity, uint32
bool Client::BindWound(Mob* bindmob, bool start, bool fail){
EQApplicationPacket* outapp = 0;
if(!fail) {
if(!fail)
{
outapp = new EQApplicationPacket(OP_Bind_Wound, sizeof(BindWound_Struct));
BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer;
// Start bind
if(!bindwound_timer.Enabled()) {
if(!bindwound_timer.Enabled())
{
//make sure we actually have a bandage... and consume it.
int16 bslot = m_inv.HasItemByUse(ItemTypeBandage, 1, invWhereWorn|invWherePersonal);
if (bslot == INVALID_INDEX) {
@ -2521,7 +2591,9 @@ bool Client::BindWound(Mob* bindmob, bool start, bool fail){
; // Binding self
}
}
} else {
}
else if (bindwound_timer.Check()) // Did the timer finish?
{
// finish bind
// disable complete timer
bindwound_timer.Disable();
@ -2967,7 +3039,7 @@ void Client::Tell_StringID(uint32 string_id, const char *who, const char *messag
void Client::SetTint(int16 in_slot, uint32 color) {
Color_Struct new_color;
new_color.color = color;
new_color.Color = color;
SetTint(in_slot, new_color);
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color);
}
@ -2978,8 +3050,8 @@ void Client::SetTint(int16 in_slot, Color_Struct& color) {
uint8 matslot = Inventory::CalcMaterialFromSlot(in_slot);
if (matslot != _MaterialInvalid)
{
m_pp.item_tint[matslot].color = color.color;
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.color);
m_pp.item_tint[matslot].Color = color.Color;
database.SaveCharacterMaterialColor(this->CharacterID(), in_slot, color.Color);
}
}
@ -5290,35 +5362,35 @@ void Client::SendRewards()
FastQueuePacket(&vetapp);
}
bool Client::TryReward(uint32 claim_id) {
//Make sure we have an open spot
//Make sure we have it in our acct and count > 0
//Make sure the entry was found
//If we meet all the criteria:
//Decrement our count by 1 if it > 1 delete if it == 1
//Create our item in bag if necessary at the free inv slot
//save
bool Client::TryReward(uint32 claim_id)
{
// Make sure we have an open spot
// Make sure we have it in our acct and count > 0
// Make sure the entry was found
// If we meet all the criteria:
// Decrement our count by 1 if it > 1 delete if it == 1
// Create our item in bag if necessary at the free inv slot
// save
uint32 free_slot = 0xFFFFFFFF;
for(int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) {
for (int i = EmuConstants::GENERAL_BEGIN; i <= EmuConstants::GENERAL_END; ++i) {
ItemInst *item = GetInv().GetItem(i);
if(!item) {
if (!item) {
free_slot = i;
break;
}
}
if(free_slot == 0xFFFFFFFF)
if (free_slot == 0xFFFFFFFF)
return false;
char errbuf[MYSQL_ERRMSG_SIZE];
std::string query = StringFormat("SELECT amount FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
if (!results.Success())
return false;
}
if (results.RowCount() == 0)
return false;
@ -5326,58 +5398,57 @@ bool Client::TryReward(uint32 claim_id) {
auto row = results.begin();
uint32 amt = atoi(row[0]);
if(amt == 0)
if (amt == 0)
return false;
std::list<InternalVeteranReward>::iterator iter = zone->VeteranRewards.begin();
for (; iter != zone->VeteranRewards.end(); ++row)
if((*iter).claim_id == claim_id)
break;
auto iter = std::find_if(zone->VeteranRewards.begin(), zone->VeteranRewards.end(),
[claim_id](const InternalVeteranReward &a) { return a.claim_id == claim_id; });
if(iter == zone->VeteranRewards.end())
if (iter == zone->VeteranRewards.end())
return false;
if(amt == 1) {
if (amt == 1) {
query = StringFormat("DELETE FROM account_rewards "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
else {
} else {
query = StringFormat("UPDATE account_rewards SET amount = (amount-1) "
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
"WHERE account_id = %i AND reward_id = %i",
AccountID(), claim_id);
auto results = database.QueryDatabase(query);
}
InternalVeteranReward ivr = (*iter);
auto &ivr = (*iter);
ItemInst *claim = database.CreateItem(ivr.items[0].item_id, ivr.items[0].charges);
if(!claim) {
if (!claim) {
Save();
return true;
}
bool lore_conflict = CheckLoreConflict(claim->GetItem());
for(int y = 1; y < 8; y++)
if(ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) {
for (int y = 1; y < 8; y++)
if (ivr.items[y].item_id && claim->GetItem()->ItemClass == 1) {
ItemInst *item_temp = database.CreateItem(ivr.items[y].item_id, ivr.items[y].charges);
if(item_temp) {
if(CheckLoreConflict(item_temp->GetItem())) {
if (item_temp) {
if (CheckLoreConflict(item_temp->GetItem())) {
lore_conflict = true;
DuplicateLoreMessage(ivr.items[y].item_id);
}
claim->PutItem(y-1, *item_temp);
claim->PutItem(y - 1, *item_temp);
safe_delete(item_temp);
}
}
if(lore_conflict) {
if (lore_conflict) {
safe_delete(claim);
return true;
}
PutItemInInventory(free_slot, *claim);
SendItemPacket(free_slot, claim, ItemPacketTrade);
safe_delete(claim);
Save();
return true;
@ -6173,7 +6244,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid
NPCType *made_npc = nullptr;
const NPCType *npc_type = database.GetNPCType(pet.npc_id);
const NPCType *npc_type = database.LoadNPCTypesData(pet.npc_id);
if(npc_type == nullptr) {
Log.Out(Logs::General, Logs::Error, "Unknown npc type for doppelganger spell id: %d", spell_id);
Message(0,"Unable to find pet!");
@ -7396,7 +7467,7 @@ void Client::SendClearMercInfo()
void Client::DuplicateLoreMessage(uint32 ItemID)
{
if(!(ClientVersionBit & BIT_RoFAndLater))
if (!(m_ClientVersionBit & BIT_RoFAndLater))
{
Message_StringID(0, PICK_LORE);
return;

View File

@ -266,10 +266,11 @@ public:
void SendBazaarResults(uint32 trader_id,uint32 class_,uint32 race,uint32 stat,uint32 slot,uint32 type,char name[64],uint32 minprice,uint32 maxprice);
void SendTraderItem(uint32 item_id,uint16 quantity);
uint16 FindTraderItem(int32 SerialNumber,uint16 Quantity);
uint32 FindTraderItemSerialNumber(int32 ItemID);
ItemInst* FindTraderItemBySerialNumber(int32 SerialNumber);
void FindAndNukeTraderItem(int32 item_id,uint16 quantity,Client* customer,uint16 traderslot);
void NukeTraderItem(uint16 slot,int16 charges,uint16 quantity,Client* customer,uint16 traderslot, int uniqueid);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges);
void NukeTraderItem(uint16 slot, int16 charges, uint16 quantity, Client* customer, uint16 traderslot, int32 uniqueid, int32 itemid = 0);
void ReturnTraderReq(const EQApplicationPacket* app,int16 traderitemcharges, uint32 itemid = 0);
void TradeRequestFailed(const EQApplicationPacket* app);
void BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const EQApplicationPacket* app);
void TraderUpdate(uint16 slot_id,uint32 trader_id);
@ -1021,7 +1022,7 @@ public:
inline int CompletedTasksInSet(int TaskSet) { return (taskstate ? taskstate->CompletedTasksInSet(TaskSet) :0); }
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; }
/** Adventure Stuff **/
@ -1139,7 +1140,7 @@ public:
void HandleLFGuildResponse(ServerPacket *pack);
void SendLFGuildStatus();
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; }
void SetMaxXTargets(uint8 NewMax);
bool IsXTarget(const Mob *m) const;
@ -1257,6 +1258,7 @@ protected:
friend class Mob;
void CalcItemBonuses(StatBonuses* newbon);
void AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false, bool isTribute = false);
void AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAug = false);
int CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat);
void CalcEdibleBonuses(StatBonuses* newbon);
void CalcAABonuses(StatBonuses* newbon);
@ -1387,6 +1389,7 @@ private:
uint16 BoatID;
uint16 TrackingID;
uint16 CustomerID;
uint16 TraderID;
uint32 account_creation;
uint8 firstlogon;
uint32 mercid; // current merc
@ -1414,6 +1417,7 @@ private:
bool CanBeInZone();
void SendLogoutPackets();
void SendZoneInPackets();
bool AddPacket(const EQApplicationPacket *, bool);
bool AddPacket(EQApplicationPacket**, bool);
bool SendAllPackets();
@ -1464,6 +1468,10 @@ private:
Timer TrackingTimer;
Timer RespawnFromHoverTimer;
Timer merc_timer;
Timer anon_toggle_timer;
Timer afk_toggle_timer;
Timer helm_toggle_timer;
Timer light_update_timer;
glm::vec3 m_Proximity;
@ -1509,7 +1517,7 @@ private:
uint32 AttemptedMessages;
ClientVersion m_ClientVersion;
uint32 ClientVersionBit;
uint32 m_ClientVersionBit;
int XPRate;

View File

@ -398,12 +398,18 @@ void ClearMappedOpcode(EmuOpcode op)
// client methods
int Client::HandlePacket(const EQApplicationPacket *app)
{
if(Log.log_settings[Logs::LogCategory::Netcode].log_to_console > 0) {
if (Log.log_settings[Logs::LogCategory::Netcode].is_category_enabled == 1) {
char buffer[64];
app->build_header_dump(buffer);
Log.Out(Logs::Detail, Logs::Client_Server_Packet, "Dispatch opcode: %s", buffer);
}
if (Log.log_settings[Logs::Client_Server_Packet].is_category_enabled == 1)
Log.Out(Logs::General, Logs::Client_Server_Packet, "[%s - 0x%04x] [Size: %u]", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size());
if (Log.log_settings[Logs::Client_Server_Packet_With_Dump].is_category_enabled == 1)
Log.Out(Logs::General, Logs::Client_Server_Packet_With_Dump, "[%s - 0x%04x] [Size: %u] %s", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode(), app->Size(), DumpPacketToString(app).c_str());
EmuOpcode opcode = app->GetOpcode();
if (opcode == OP_AckPacket) {
return true;
@ -445,23 +451,16 @@ int Client::HandlePacket(const EQApplicationPacket *app)
case CLIENT_CONNECTED: {
ClientPacketProc p;
p = ConnectedOpcodes[opcode];
if(p == nullptr) {
if(p == nullptr) {
std::vector<EQEmu::Any> args;
args.push_back(const_cast<EQApplicationPacket*>(app));
parse->EventPlayer(EVENT_UNHANDLED_OPCODE, this, "", 0, &args);
char buffer[64];
Log.Out(Logs::Detail, Logs::Client_Server_Packet, "Unhandled incoming opcode: %s - 0x%04x", OpcodeManager::EmuToName(app->GetOpcode()), app->GetOpcode());
if (Log.log_settings[Logs::Client_Server_Packet].log_to_console > 0){
if (Log.log_settings[Logs::Client_Server_Packet_Unhandled].is_category_enabled == 1){
char buffer[64];
app->build_header_dump(buffer);
if (app->size < 1000)
DumpPacket(app, app->size);
else{
std::cout << "Dump limited to 1000 characters:\n";
DumpPacket(app, 1000);
}
Log.Out(Logs::General, Logs::Client_Server_Packet_Unhandled, "%s %s", buffer, DumpPacketToString(app).c_str());
}
break;
}
@ -1090,76 +1089,15 @@ void Client::Handle_Connect_OP_SendAATable(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_SendExpZonein(const EQApplicationPacket *app)
{
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SendExpZonein, 0);
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
// SoF+ Gets Zone-In packets after sending OP_WorldObjectsSent
if (GetClientVersion() < ClientVersion::SoF)
{
SendZoneInPackets();
}
safe_delete(outapp);
SendAATimers();
outapp = new EQApplicationPacket(OP_SendExpZonein, 0);
QueuePacket(outapp);
safe_delete(outapp);
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
/* this is actually the guild MOTD
outapp = new EQApplicationPacket(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2));
ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer;
strcpy(zonesendname2->name,m_pp.name);
QueuePacket(outapp);
safe_delete(outapp);*/
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
return;
}
@ -1217,72 +1155,13 @@ void Client::Handle_Connect_OP_WearChange(const EQApplicationPacket *app)
void Client::Handle_Connect_OP_WorldObjectsSent(const EQApplicationPacket *app)
{
//This is a copy of SendExpZonein created for SoF+ due to packet order change
//////////////////////////////////////////////////////
// Spawn Appearance Packet
EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
sa->type = AT_SpawnID; // Is 0x10 used to set the player id?
sa->parameter = GetID(); // Four bytes for this parameter...
outapp->priority = 6;
// New for SoF+
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0);
QueuePacket(outapp);
safe_delete(outapp);
// Inform the world about the client
outapp = new EQApplicationPacket();
CreateSpawnPacket(outapp);
outapp->priority = 6;
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
safe_delete(outapp);
if (GetPVP()) //force a PVP update until we fix the spawn struct
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
//Send AA Exp packet:
if (GetLevel() >= 51)
SendAAStats();
// Send exp packets
outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
uint32 tmpxp1 = GetEXPForLevel(GetLevel() + 1);
uint32 tmpxp2 = GetEXPForLevel(GetLevel());
// Crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
float tmpxp = (float)((float)m_pp.exp - tmpxp2) / ((float)tmpxp1 - tmpxp2);
eu->exp = (uint32)(330.0f * tmpxp);
outapp->priority = 6;
QueuePacket(outapp);
}
safe_delete(outapp);
SendAATimers();
// New for Secrets of Faydwer - Used in Place of OP_SendExpZonein
outapp = new EQApplicationPacket(OP_WorldObjectsSent, 0);
QueuePacket(outapp);
safe_delete(outapp);
outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(ZoneInSendName_Struct));
ZoneInSendName_Struct* zonesendname = (ZoneInSendName_Struct*)outapp->pBuffer;
strcpy(zonesendname->name, m_pp.name);
strcpy(zonesendname->name2, m_pp.name);
zonesendname->unknown0 = 0x0A;
QueuePacket(outapp);
safe_delete(outapp);
if (IsInAGuild()) {
SendGuildMembers();
SendGuildURL();
SendGuildChannel();
SendGuildLFGuildStatus();
}
SendLFGuildStatus();
//No idea why live sends this if even were not in a guild
SendGuildMOTD();
// Packet order changed for SoF+, so below is sent here instead of OP_SendExpLogin
SendZoneInPackets();
if (RuleB(Mercs, AllowMercs))
{
@ -1312,8 +1191,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
conn_state = ReceivedZoneEntry;
SetClientVersion(Connection()->GetClientVersion());
if (m_ClientVersion != ClientVersion::Unknown)
ClientVersionBit = 1 << (static_cast<unsigned int>(m_ClientVersion) - 1);
m_ClientVersionBit = ClientBitFromVersion(Connection()->GetClientVersion());
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);
@ -1428,9 +1306,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
/* Set item material tint */
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;
}
}
@ -1839,9 +1717,9 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
if (loaditems) { /* Dont load if a length error occurs */
BulkSendInventoryItems();
/* Send stuff on the cursor which isnt sent in bulk */
for (auto iter = m_inv.cursor_begin(); iter != m_inv.cursor_end(); ++iter) {
for (auto iter = m_inv.cursor_cbegin(); iter != m_inv.cursor_cend(); ++iter) {
/* First item cursor is sent in bulk inventory packet */
if (iter == m_inv.cursor_begin())
if (iter == m_inv.cursor_cbegin())
continue;
const ItemInst *inst = *iter;
SendItemPacket(MainCursor, inst, ItemPacketSummonItem);
@ -1857,7 +1735,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
safe_delete(outapp);
}
if (ClientVersionBit & BIT_UFAndLater) {
if (m_ClientVersionBit & BIT_UFAndLater) {
outapp = new EQApplicationPacket(OP_XTargetResponse, 8);
outapp->WriteUInt32(GetMaxXTargets());
outapp->WriteUInt32(0);
@ -3292,7 +3170,6 @@ void Client::Handle_OP_AutoFire(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.
//
if (app->size != sizeof(BandolierCreate_Struct)) {
@ -3304,19 +3181,20 @@ void Client::Handle_OP_Bandolier(const EQApplicationPacket *app)
BandolierCreate_Struct *bs = (BandolierCreate_Struct*)app->pBuffer;
switch (bs->action) {
case BandolierCreate:
switch (bs->Action)
{
case bandolierCreate:
CreateBandolier(app);
break;
case BandolierRemove:
case bandolierRemove:
RemoveBandolier(app);
break;
case BandolierSet:
case bandolierSet:
SetBandolier(app);
break;
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;
}
}
@ -5517,7 +5395,7 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
if (damage < 0)
damage = 31337;
if (admin >= minStatusToAvoidFalling && GetGM()){
if (admin >= minStatusToAvoidFalling && GetGM()) {
Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype);
SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return;
@ -5527,11 +5405,11 @@ void Client::Handle_OP_EnvDamage(const EQApplicationPacket *app)
SetHP(GetHP() - 1);//needed or else the client wont acknowledge
return;
}
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184){
else if (zone->GetZoneID() == 183 || zone->GetZoneID() == 184) {
// Hard coded tutorial and load zones for no fall damage
return;
}
else{
else {
SetHP(GetHP() - (damage * RuleR(Character, EnvironmentDamageMulipliter)));
/* EVENT_ENVIRONMENTAL_DAMAGE */
@ -7361,6 +7239,16 @@ void Client::Handle_OP_GuildInvite(const EQApplicationPacket *app)
if (gc->guildeqid == 0)
gc->guildeqid = GuildID();
// Convert Membership Level between RoF and previous clients.
if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF)
{
gc->officer = 0;
}
if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF)
{
gc->officer = 8;
}
Log.Out(Logs::Detail, Logs::Guilds, "Sending OP_GuildInvite for invite to %s, length %d", client->GetName(), app->size);
client->SetPendingGuildInvitation(true);
client->QueuePacket(app);
@ -7395,6 +7283,8 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
GuildInviteAccept_Struct* gj = (GuildInviteAccept_Struct*)app->pBuffer;
uint32 guildrank = gj->response;
if (GetClientVersion() >= ClientVersion::RoF)
{
if (gj->response > 9)
@ -7423,9 +7313,25 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
Log.Out(Logs::Detail, Logs::Guilds, "Guild Invite Accept: guild %d, response %d, inviter %s, person %s",
gj->guildeqid, gj->response, gj->inviter, gj->newmember);
//ok, the invite is also used for changing rank as well.
Mob* inviter = entity_list.GetMob(gj->inviter);
if (inviter && inviter->IsClient())
{
Client* client = inviter->CastToClient();
// Convert Membership Level between RoF and previous clients.
if (client->GetClientVersion() < ClientVersion::RoF && GetClientVersion() >= ClientVersion::RoF)
{
guildrank = 0;
}
if (client->GetClientVersion() >= ClientVersion::RoF && GetClientVersion() < ClientVersion::RoF)
{
guildrank = 8;
}
}
//we dont really care a lot about what this packet means, as long as
//it has been authorized with the guild manager
if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, gj->response)) {
if (!guild_mgr.VerifyAndClearInvite(CharacterID(), gj->guildeqid, guildrank)) {
worldserver.SendEmoteMessage(gj->inviter, 0, 0, "%s has sent an invalid response to your invite!", GetName());
Message(13, "Invalid invite response packet!");
return;
@ -7453,7 +7359,7 @@ void Client::Handle_OP_GuildInviteAccept(const EQApplicationPacket *app)
//change guild and rank
uint32 guildrank = gj->response;
guildrank = gj->response;
if (GetClientVersion() >= ClientVersion::RoF)
{
@ -9863,6 +9769,9 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
return;
}
if (mypet->GetPetType() == petTargetLock && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST))
return;
if (mypet->GetPetType() == petAnimation && (pet->command != PET_HEALTHREPORT && pet->command != PET_GETLOST) && !GetAA(aaAnimationEmpathy))
return;
@ -10532,16 +10441,16 @@ void Client::Handle_OP_PotionBelt(const EQApplicationPacket *app)
if (mptbs->Action == 0) {
const Item_Struct *BaseItem = database.GetItem(mptbs->ItemID);
if (BaseItem) {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = BaseItem->ID;
m_pp.potionbelt.items[mptbs->SlotNumber].icon = BaseItem->Icon;
strn0cpy(m_pp.potionbelt.items[mptbs->SlotNumber].item_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);
m_pp.potionbelt.Items[mptbs->SlotNumber].ID = BaseItem->ID;
m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = BaseItem->Icon;
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].ID, m_pp.potionbelt.Items[mptbs->SlotNumber].Icon);
}
}
else {
m_pp.potionbelt.items[mptbs->SlotNumber].item_id = 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].ID = 0;
m_pp.potionbelt.Items[mptbs->SlotNumber].Icon = 0;
m_pp.potionbelt.Items[mptbs->SlotNumber].Name[0] = '\0';
}
}
@ -12042,12 +11951,6 @@ void Client::Handle_OP_ShopEnd(const EQApplicationPacket *app)
{
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
//EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopEndConfirm, 2);
//outapp->pBuffer[0] = 0x0a;
//outapp->pBuffer[1] = 0x66;
//QueuePacket(outapp);
//safe_delete(outapp);
//Save();
return;
}
@ -12143,6 +12046,10 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
mp->quantity = prevcharges;
}
// Item's stackable, but the quantity they want to buy exceeds the max stackable quantity.
if (item->Stackable && mp->quantity > item->StackSize)
mp->quantity = item->StackSize;
EQApplicationPacket* outapp = new EQApplicationPacket(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
Merchant_Sell_Struct* mpo = (Merchant_Sell_Struct*)outapp->pBuffer;
mpo->quantity = mp->quantity;
@ -12169,6 +12076,7 @@ void Client::Handle_OP_ShopPlayerBuy(const EQApplicationPacket *app)
mpo->price = SinglePrice;
else
mpo->price = SinglePrice * mp->quantity;
if (mpo->price < 0)
{
safe_delete(outapp);
@ -12638,6 +12546,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
else if (sa->type == AT_Anim) {
if (IsAIControlled())
return;
if (sa->parameter == ANIM_STAND) {
SetAppearance(eaStanding);
playeraction = 0;
@ -12671,15 +12580,6 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
SetFeigned(false);
}
// This is from old code
// I have no clue what it's for
/*
else if (sa->parameter == 0x05) {
// Illusion
std::cout << "Illusion packet recv'd:" << std::endl;
DumpPacket(app);
}
*/
else {
std::cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << std::endl;
return;
@ -12688,6 +12588,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Anon) {
if(!anon_toggle_timer.Check()) {
return;
}
// For Anon/Roleplay
if (sa->parameter == 1) { // Anon
m_pp.anon = 1;
@ -12709,13 +12613,18 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
return;
}
else if (sa->type == AT_AFK) {
this->AFK = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
if(afk_toggle_timer.Check()) {
AFK = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else if (sa->type == AT_Split) {
m_pp.autosplit = (sa->parameter == 1);
}
else if (sa->type == AT_Sneak) {
if(sneaking == 0)
return;
if (sa->parameter != 0)
{
if (!HasSkill(SkillSneak))
@ -12727,7 +12636,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
return;
}
this->sneaking = 0;
sneaking = 0;
entity_list.QueueClients(this, app, true);
}
else if (sa->type == AT_Size)
@ -12739,7 +12648,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
else if (sa->type == AT_Light) // client emitting light (lightstone, shiny shield)
{
entity_list.QueueClients(this, app, false);
//don't do anything with this
}
else if (sa->type == AT_Levitate)
{
@ -12748,8 +12657,10 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
}
else if (sa->type == AT_ShowHelm)
{
m_pp.showhelm = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
if(helm_toggle_timer.Check()) {
m_pp.showhelm = (sa->parameter == 1);
entity_list.QueueClients(this, app, true);
}
}
else {
std::cout << "Unknown SpawnAppearance type: 0x" << std::hex << std::setw(4) << std::setfill('0') << sa->type << std::dec
@ -13360,7 +13271,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
/*
if (GetClientVersion() >= EQClientRoF)
max_items = 200;
max_items = 200;
*/
//Show Items
@ -13372,20 +13283,25 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
{
case BazaarTrader_EndTraderMode: {
Trader_EndTrader();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session");
break;
}
case BazaarTrader_EndTransaction: {
Client* c = entity_list.GetClientByID(sis->TraderID);
if (c)
{
c->WithCustomer(0);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction");
}
else
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer");
break;
}
case BazaarTrader_ShowItems: {
Trader_ShowItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items");
break;
}
default: {
@ -13408,6 +13324,7 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
{
GetItems_Struct* gis = GetTraderItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Start Trader Mode");
// Verify there are no NODROP or items with a zero price
bool TradeItemsValid = true;
@ -13454,7 +13371,8 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
safe_delete(gis);
this->Trader_StartTrader();
// This refreshes the Trader window to display the End Trader button
if (GetClientVersion() >= ClientVersion::RoF)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Trader, sizeof(TraderStatus_Struct));
@ -13464,15 +13382,6 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
safe_delete(outapp);
}
}
else if (app->size == sizeof(TraderStatus_Struct))
{
TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer;
if (tss->Code == BazaarTrader_ShowItems)
{
Trader_ShowItems();
}
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unknown TraderStruct code of: %i\n",
ints->Code);
@ -13480,9 +13389,35 @@ void Client::Handle_OP_Trader(const EQApplicationPacket *app)
Log.Out(Logs::General, Logs::Error, "Unknown TraderStruct code of: %i\n", ints->Code);
}
}
else if (app->size == sizeof(TraderStatus_Struct))
{
TraderStatus_Struct* tss = (TraderStatus_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Status Code: %d", tss->Code);
switch (tss->Code)
{
case BazaarTrader_EndTraderMode: {
Trader_EndTrader();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Trader Session");
break;
}
case BazaarTrader_ShowItems: {
Trader_ShowItems();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Show Trader Items");
break;
}
default: {
Log.Out(Logs::Detail, Logs::Trading, "Unhandled action code in OP_Trader ShowItems_Struct");
break;
}
}
}
else if (app->size == sizeof(TraderPriceUpdate_Struct))
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Trader Price Update");
HandleTraderPriceUpdate(app);
}
else {
@ -13501,23 +13436,22 @@ void Client::Handle_OP_TraderBuy(const EQApplicationPacket *app)
//
// Client has elected to buy an item from a Trader
//
if (app->size != sizeof(TraderBuy_Struct)) {
Log.Out(Logs::General, Logs::Error, "Wrong size: OP_TraderBuy, size=%i, expected %i", app->size, sizeof(TraderBuy_Struct));
return;
}
if (app->size == sizeof(TraderBuy_Struct)){
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID)){
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Buy Trader Item ");
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Struct size mismatch");
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderBuy: Null Client Pointer");
}
return;
}
@ -13579,51 +13513,124 @@ void Client::Handle_OP_TradeRequestAck(const EQApplicationPacket *app)
void Client::Handle_OP_TraderShop(const EQApplicationPacket *app)
{
// Bazaar Trader:
//
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
//
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
if (app->size == sizeof(TraderClick_Struct))
{
TraderClick_Struct* tcs = (TraderClick_Struct*)app->pBuffer;
if (app->size != sizeof(TraderClick_Struct)) {
Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: TraderClick_Struct TraderID %d, Code %d, Unknown008 %d, Approval %d",
tcs->TraderID, tcs->Code, tcs->Unknown008, tcs->Approval);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Returning due to struct size mismatch");
if (tcs->Code == BazaarWelcome)
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
SendBazaarWelcome();
}
else
{
// This is when a potential purchaser right clicks on this client who is in Trader mode to
// browse their goods.
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Trader = entity_list.GetClientByID(tcs->TraderID);
if (Trader)
{
outtcs->Approval = Trader->WithCustomer(GetID());
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Shop Request (%s) to (%s) with Approval: %d", GetCleanName(), Trader->GetCleanName(), outtcs->Approval);
}
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
return;
}
outtcs->TraderID = tcs->TraderID;
outtcs->Unknown008 = 0x3f800000;
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Trader->CharacterID());
Trader->Trader_CustomerBrowsing(this);
TraderID = tcs->TraderID;
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Inventory Sent");
}
else
{
Message_StringID(clientMessageYellow, TRADER_BUSY);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Trader Busy");
}
safe_delete(outapp);
return;
}
return;
}
EQApplicationPacket* outapp = new EQApplicationPacket(OP_TraderShop, sizeof(TraderClick_Struct));
TraderClick_Struct* outtcs = (TraderClick_Struct*)outapp->pBuffer;
Client* Customer = entity_list.GetClientByID(tcs->TraderID);
if (Customer)
outtcs->Approval = Customer->WithCustomer(GetID());
else {
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: entity_list.GetClientByID(tcs->traderid)"
" returned a nullptr pointer");
return;
else if (app->size == sizeof(BazaarWelcome_Struct))
{
// RoF+
// Client requested Bazaar Welcome Info (Trader and Item Total Counts)
SendBazaarWelcome();
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_TraderShop: Sent Bazaar Welcome Info");
}
else if (app->size == sizeof(TraderBuy_Struct))
{
// RoF+
// Customer has purchased an item from the Trader
outtcs->TraderID = tcs->TraderID;
TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
outtcs->Unknown008 = 0x3f800000;
if (Client* Trader = entity_list.GetClientByID(tbs->TraderID))
{
BuyTraderItem(tbs, Trader, app);
Log.Out(Logs::Detail, Logs::Trading, "Handle_OP_TraderShop: Buy Action %d, Price %d, Trader %d, ItemID %d, Quantity %d, ItemName, %s",
tbs->Action, tbs->Price, tbs->TraderID, tbs->ItemID, tbs->Quantity, tbs->ItemName);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "OP_TraderShop: Null Client Pointer");
}
}
else if (app->size == 4)
{
// RoF+
// Customer has closed the trade window
uint32 Command = *((uint32 *)app->pBuffer);
QueuePacket(outapp);
if (outtcs->Approval) {
this->BulkSendTraderInventory(Customer->CharacterID());
Customer->Trader_CustomerBrowsing(this);
if (Command == 4)
{
Client* c = entity_list.GetClientByID(TraderID);
TraderID = 0;
if (c)
{
c->WithCustomer(0);
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: End Transaction - Code %d", Command);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Null Client Pointer for Trader - Code %d", Command);
}
EQApplicationPacket empty(OP_ShopEndConfirm);
QueuePacket(&empty);
}
else
{
Log.Out(Logs::Detail, Logs::Trading, "Client::Handle_OP_Trader: Unhandled Code %d", Command);
}
}
else
Message_StringID(clientMessageYellow, TRADER_BUSY);
safe_delete(outapp);
return;
{
Log.Out(Logs::Detail, Logs::Trading, "Unknown size for OP_TraderShop: %i\n", app->size);
Log.Out(Logs::General, Logs::Error, "Unknown size for OP_TraderShop: %i\n", app->size);
DumpPacket(app);
return;
}
}
void Client::Handle_OP_TradeSkillCombine(const EQApplicationPacket *app)
@ -13786,41 +13793,32 @@ void Client::Handle_OP_TributeUpdate(const EQApplicationPacket *app)
void Client::Handle_OP_VetClaimRequest(const EQApplicationPacket *app)
{
if (app->size < sizeof(VeteranClaimRequest))
{
Log.Out(Logs::General, Logs::None, "OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size, sizeof(VeteranClaimRequest));
if (app->size < sizeof(VeteranClaim)) {
Log.Out(Logs::General, Logs::None,
"OP_VetClaimRequest size lower than expected: got %u expected at least %u", app->size,
sizeof(VeteranClaim));
DumpPacket(app);
return;
}
VeteranClaimRequest *vcr = (VeteranClaimRequest*)app->pBuffer;
VeteranClaim *vcr = (VeteranClaim *)app->pBuffer;
if (vcr->claim_id == 0xFFFFFFFF) //request update packet
{
if (vcr->claim_id == 0xFFFFFFFF) { // request update packet
SendRewards();
return;
}
else //try to claim something!
{
if (!TryReward(vcr->claim_id))
{
Message(13, "Your claim has been rejected.");
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply));
VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
cr->reject_field = -1;
FastQueuePacket(&vetapp);
}
else
{
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaimReply));
VeteranClaimReply * cr = (VeteranClaimReply*)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
cr->reject_field = 0;
FastQueuePacket(&vetapp);
}
}
// try to claim something!
EQApplicationPacket *vetapp = new EQApplicationPacket(OP_VetClaimReply, sizeof(VeteranClaim));
VeteranClaim *cr = (VeteranClaim *)vetapp->pBuffer;
strcpy(cr->name, GetName());
cr->claim_id = vcr->claim_id;
if (!TryReward(vcr->claim_id))
cr->action = 1;
else
cr->action = 0;
FastQueuePacket(&vetapp);
}
void Client::Handle_OP_VoiceMacroIn(const EQApplicationPacket *app)

View File

@ -239,7 +239,8 @@ bool Client::Process() {
if(IsAIControlled())
AI_Process();
if (bindwound_timer.Check() && bindwound_target != 0) {
// Don't reset the bindwound timer so we can check it in BindWound as well.
if (bindwound_timer.Check(false) && bindwound_target != 0) {
BindWound(bindwound_target, false);
}
@ -260,6 +261,13 @@ bool Client::Process() {
}
}
if(light_update_timer.Check()) {
UpdateEquipLightValue();
if(UpdateActiveLightValue()) {
SendAppearancePacket(AT_Light, GetActiveLightValue());
}
}
bool may_use_attacks = false;
/*
Things which prevent us from attacking:
@ -960,7 +968,7 @@ void Client::BulkSendInventoryItems()
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
const Item_Struct* handyitem = nullptr;
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;
}
const Item_Struct *item;
@ -1886,7 +1894,7 @@ void Client::DoHPRegen() {
}
void Client::DoManaRegen() {
if (GetMana() >= max_mana)
if (GetMana() >= max_mana && spellbonuses.ManaRegen >= 0)
return;
SetMana(GetMana() + CalcManaRegen() + RestRegenMana);

View File

@ -2461,7 +2461,7 @@ void command_npctypespawn(Client *c, const Seperator *sep)
{
if (sep->IsNumber(1)) {
const NPCType* tmp = 0;
if ((tmp = database.GetNPCType(atoi(sep->arg[1])))) {
if ((tmp = database.LoadNPCTypesData(atoi(sep->arg[1])))) {
//tmp->fixedZ = 1;
NPC* npc = new NPC(tmp, 0, c->GetPosition(), FlyMode3);
if (npc && sep->IsNumber(2))
@ -2618,7 +2618,7 @@ void command_peekinv(Client *c, const Seperator *sep)
}
else {
int cursorDepth = 0;
for (auto it = targetClient->GetInv().cursor_begin(); (it != targetClient->GetInv().cursor_end()); ++it, ++cursorDepth) {
for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++cursorDepth) {
inst_main = *it;
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
linker.SetItemInst(inst_main);
@ -2923,7 +2923,7 @@ void command_viewnpctype(Client *c, const Seperator *sep)
else
{
uint32 npctypeid=atoi(sep->arg[1]);
const NPCType* npct = database.GetNPCType(npctypeid);
const NPCType* npct = database.LoadNPCTypesData(npctypeid);
if (npct) {
c->Message(0, " NPCType Info, ");
c->Message(0, " NPCTypeID: %u", npct->npc_id);
@ -4284,31 +4284,49 @@ void command_goto(Client *c, const Seperator *sep)
void command_iteminfo(Client *c, const Seperator *sep)
{
const ItemInst* inst = c->GetInv()[MainCursor];
if (!inst)
c->Message(13, "Error: You need an item on your cursor for this command");
else {
const Item_Struct* item = inst->GetItem();
c->Message(0, "ID: %i Name: %s", item->ID, item->Name);
c->Message(0, " Lore: %s ND: %i NS: %i Type: %i", (item->LoreFlag) ? "true":"false", item->NoDrop, item->NoRent, item->ItemClass);
c->Message(0, " IDF: %s Size: %i Weight: %i icon_id: %i Price: %i", item->IDFile, item->Size, item->Weight, item->Icon, item->Price);
if (c->Admin() >= 200)
c->Message(0, "MinStatus: %i", item->MinStatus);
if (item->ItemClass==ItemClassBook)
c->Message(0, " This item is a Book: %s", item->Filename);
else if (item->ItemClass==ItemClassContainer)
c->Message(0, " This item is a container with %i slots", item->BagSlots);
else {
c->Message(0, " equipableSlots: %u equipable Classes: %u", item->Slots, item->Classes);
c->Message(0, " Magic: %i SpellID: %i Proc Level: %i DBCharges: %i CurCharges: %i", item->Magic, item->Click.Effect, item->Click.Level, item->MaxCharges, inst->GetCharges());
c->Message(0, " EffectType: 0x%02x CastTime: %.2f", (uint8) item->Click.Type, (double) item->CastTime/1000);
c->Message(0, " Material: 0x%02x Color: 0x%08x Skill: %i", item->Material, item->Color, item->ItemType);
c->Message(0, " Required level: %i Required skill: %i Recommended level:%i", item->ReqLevel, item->RecSkill, item->RecLevel);
c->Message(0, " Skill mod: %i percent: %i", item->SkillModType, item->SkillModValue);
c->Message(0, " BaneRace: %i BaneBody: %i BaneDMG: %i", item->BaneDmgRace, item->BaneDmgBody, item->BaneDmgAmt);
}
auto inst = c->GetInv()[MainCursor];
if (!inst) { c->Message(13, "Error: You need an item on your cursor for this command"); }
auto item = inst->GetItem();
if (!item) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Command #iteminfo processed an item with no data pointer");
c->Message(13, "Error: This item has no data reference");
}
Client::TextLink linker;
linker.SetLinkType(linker.linkItemInst);
linker.SetItemInst(inst);
auto item_link = linker.GenerateLink();
c->Message(0, "*** Item Info for [%s] ***", item_link.c_str());
c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass);
c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon);
c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice);
c->Message(0, ">> Material: 0x%02X, Color: 0x%08X, Tint: 0x%08X, Light: 0x%02X", item->Material, item->Color, inst->GetColor(), item->Light);
c->Message(0, ">> IsLore: %s, LoreGroup: %u, Lore: '%s'", (item->LoreFlag ? "TRUE" : "FALSE"), item->LoreGroup, item->Lore);
c->Message(0, ">> NoDrop: %u, NoRent: %u, NoPet: %u, NoTransfer: %u, FVNoDrop: %u",
item->NoDrop, item->NoRent, (uint8)item->NoPet, (uint8)item->NoTransfer, item->FVNoDrop);
if (item->ItemClass == ItemClassBook) {
c->Message(0, "*** This item is a Book (filename:'%s') ***", item->Filename);
}
else if (item->ItemClass == ItemClassContainer) {
c->Message(0, "*** This item is a Container (%u slots) ***", item->BagSlots);
}
else {
c->Message(0, "*** This item is Common ***");
c->Message(0, ">> Classes: %u, Races: %u, Slots: %u", item->Classes, item->Races, item->Slots);
c->Message(0, ">> ReqSkill: %u, ReqLevel: %u, RecLevel: %u", item->RecSkill, item->ReqLevel, item->RecLevel);
c->Message(0, ">> SkillModType: %u, SkillModValue: %i", item->SkillModType, item->SkillModValue);
c->Message(0, ">> BaneRaceType: %u, BaneRaceDamage: %u, BaneBodyType: %u, BaneBodyDamage: %i",
item->BaneDmgRace, item->BaneDmgRaceAmt, item->BaneDmgBody, item->BaneDmgAmt);
c->Message(0, ">> Magic: %s, SpellID: %i, ProcLevel: %u, Charges: %u, MaxCharges: %u",
(item->Magic ? "TRUE" : "FALSE"), item->Click.Effect, item->Click.Level, inst->GetCharges(), item->MaxCharges);
c->Message(0, ">> EffectType: 0x%02X, CastTime: %.2f", (uint8)item->Click.Type, ((double)item->CastTime / 1000));
}
if (c->Admin() >= 200)
c->Message(0, ">> MinStatus: %u", item->MinStatus);
}
void command_uptime(Client *c, const Seperator *sep)
@ -6669,7 +6687,7 @@ void command_qglobal(Client *c, const Seperator *sep) {
}
if(!strcasecmp(sep->arg[1], "view")) {
const NPCType *type = database.GetNPCType(target->GetNPCTypeID());
const NPCType *type = database.LoadNPCTypesData(target->GetNPCTypeID());
if(!type)
c->Message(15, "Invalid NPC type.");
else if(type->qglobal)
@ -7002,7 +7020,7 @@ void Client::Undye() {
database.SaveInventory(CharacterID(), inst, slot2);
}
m_pp.item_tint[cur_slot].color = 0;
m_pp.item_tint[cur_slot].Color = 0;
SendWearChange(cur_slot);
}
@ -10605,6 +10623,15 @@ void command_logs(Client *c, const Seperator *sep){
c->Message(15, "Your Log Settings have been applied");
c->Message(15, "Output Method: %s :: Debug Level: %i - Category: %s", sep->arg[2], atoi(sep->arg[4]), Logs::LogCategoryName[atoi(sep->arg[3])]);
}
/* We use a general 'is_category_enabled' now, let's update when we update any output settings
This is used in hot places of code to check if its enabled in any way before triggering logs
*/
if (sep->arg[4] > 0){
Log.log_settings[atoi(sep->arg[3])].is_category_enabled = 1;
}
else{
Log.log_settings[atoi(sep->arg[3])].is_category_enabled = 0;
}
}
}
else {

View File

@ -136,7 +136,8 @@ enum {
ALLOW_TO_TANK = 41,
IGNORE_ROOT_AGGRO_RULES = 42,
CASTING_RESIST_DIFF = 43,
MAX_SPECIAL_ATTACK = 44
COUNTER_AVOID_DAMAGE = 44,
MAX_SPECIAL_ATTACK = 45
};
typedef enum { //fear states
@ -350,7 +351,8 @@ struct StatBonuses {
int32 CharmBreakChance; // chance to break charm
int32 SongRange; // increases range of beneficial bard songs
uint32 HPToManaConvert; // Uses HP to cast spells at specific conversion
uint32 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
uint8 FocusEffects[HIGHEST_FOCUS+1]; // Stores the focus effectid for each focustype you have.
int16 FocusEffectsWorn[HIGHEST_FOCUS+1]; // Optional to allow focus effects to be applied additively from worn slot
bool NegateEffects; // Check if you contain a buff with negate effect. (only spellbonuses)
int32 SkillDamageAmount2[HIGHEST_SKILL+2]; // Adds skill specific damage
uint32 NegateAttacks[3]; // 0 = bool HasEffect 1 = Buff Slot 2 = Max damage absorbed per hit
@ -505,7 +507,7 @@ typedef enum {
petOther,
petCharmed,
petNPCFollow,
petHatelist //remain active as long something is on the hatelist. Don't listen to any commands
petTargetLock //remain active as long something is on the hatelist. Don't listen to any commands
} PetType;
typedef enum {

View File

@ -113,15 +113,15 @@ Corpse* Corpse::LoadCharacterCorpseEntity(uint32 in_dbid, uint32 in_charid, std:
pc->Lock();
/* Load Item Tints */
pc->item_tint[0].color = pcs->item_tint[0].color;
pc->item_tint[1].color = pcs->item_tint[1].color;
pc->item_tint[2].color = pcs->item_tint[2].color;
pc->item_tint[3].color = pcs->item_tint[3].color;
pc->item_tint[4].color = pcs->item_tint[4].color;
pc->item_tint[5].color = pcs->item_tint[5].color;
pc->item_tint[6].color = pcs->item_tint[6].color;
pc->item_tint[7].color = pcs->item_tint[7].color;
pc->item_tint[8].color = pcs->item_tint[8].color;
pc->item_tint[0].Color = pcs->item_tint[0].Color;
pc->item_tint[1].Color = pcs->item_tint[1].Color;
pc->item_tint[2].Color = pcs->item_tint[2].Color;
pc->item_tint[3].Color = pcs->item_tint[3].Color;
pc->item_tint[4].Color = pcs->item_tint[4].Color;
pc->item_tint[5].Color = pcs->item_tint[5].Color;
pc->item_tint[6].Color = pcs->item_tint[6].Color;
pc->item_tint[7].Color = pcs->item_tint[7].Color;
pc->item_tint[8].Color = pcs->item_tint[8].Color;
/* Load Physical Appearance */
pc->haircolor = pcs->haircolor;
@ -361,8 +361,8 @@ Corpse::Corpse(Client* client, int32 in_rezexp) : Mob (
database.QueryDatabase(ss.str().c_str());
}
auto start = client->GetInv().cursor_begin();
auto finish = client->GetInv().cursor_end();
auto start = client->GetInv().cursor_cbegin();
auto finish = client->GetInv().cursor_cend();
database.SaveCursor(client->CharacterID(), start, finish);
client->CalcBonuses();
@ -1271,11 +1271,7 @@ void Corpse::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) {
Mob::FillSpawnStruct(ns, ForWho);
ns->spawn.max_hp = 120;
if (IsPlayerCorpse())
ns->spawn.NPC = 3;
else
ns->spawn.NPC = 2;
ns->spawn.NPC = 2;
UpdateActiveLightValue();
ns->spawn.light = active_light;
@ -1417,8 +1413,8 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const {
item = database.GetItem(GetEquipment(material_slot));
if(item != NO_ITEM) {
return item_tint[material_slot].rgb.use_tint ?
item_tint[material_slot].color :
return item_tint[material_slot].RGB.UseTint ?
item_tint[material_slot].Color :
item->Color;
}

View File

@ -41,14 +41,14 @@ extern EntityList entity_list;
extern WorldServer worldserver;
Doors::Doors(const Door* door) :
close_timer(5000),
m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading),
m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading)
close_timer(5000),
m_Position(door->pos_x, door->pos_y, door->pos_z, door->heading),
m_Destination(door->dest_x, door->dest_y, door->dest_z, door->dest_heading)
{
db_id = door->db_id;
door_id = door->door_id;
strn0cpy(zone_name,door->zone_name,32);
strn0cpy(door_name,door->door_name,32);
strn0cpy(zone_name, door->zone_name, 32);
strn0cpy(door_name, door->door_name, 32);
incline = door->incline;
opentype = door->opentype;
guild_id = door->guild_id;
@ -57,7 +57,7 @@ Doors::Doors(const Door* door) :
nokeyring = door->nokeyring;
trigger_door = door->trigger_door;
trigger_type = door->trigger_type;
triggered=false;
triggered = false;
door_param = door->door_param;
size = door->size;
invert_state = door->invert_state;
@ -65,7 +65,7 @@ Doors::Doors(const Door* door) :
close_timer.Disable();
strn0cpy(dest_zone,door->dest_zone,16);
strn0cpy(dest_zone, door->dest_zone, 16);
dest_instance_id = door->dest_instance_id;
is_ldon_door = door->is_ldon_door;
@ -73,14 +73,14 @@ Doors::Doors(const Door* door) :
}
Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uint16 dsize) :
close_timer(5000),
m_Position(position),
m_Destination(glm::vec4())
close_timer(5000),
m_Position(position),
m_Destination(glm::vec4())
{
db_id = database.GetDoorsCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion());
door_id = database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion());
strn0cpy(zone_name,zone->GetShortName(),32);
strn0cpy(door_name,dmodel,32);
strn0cpy(zone_name, zone->GetShortName(), 32);
strn0cpy(door_name, dmodel, 32);
incline = 0;
opentype = dopentype;
guild_id = 0;
@ -89,7 +89,7 @@ Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uin
nokeyring = 0;
trigger_door = 0;
trigger_type = 0;
triggered=false;
triggered = false;
door_param = 0;
size = dsize;
invert_state = 0;
@ -97,7 +97,7 @@ Doors::Doors(const char *dmodel, const glm::vec4& position, uint8 dopentype, uin
close_timer.Disable();
strn0cpy(dest_zone,"NONE",32);
strn0cpy(dest_zone, "NONE", 32);
dest_instance_id = 0;
is_ldon_door = 0;
@ -655,6 +655,8 @@ bool ZoneDatabase::LoadDoors(int32 iDoorCount, Door *into, const char *zone_name
into[rowIndex].db_id = atoi(row[0]);
into[rowIndex].door_id = atoi(row[1]);
Log.Out(Logs::Detail, Logs::Doors, "Door Load: db id: %u, door_id %u", into[rowIndex].db_id, into[rowIndex].door_id);
strn0cpy(into[rowIndex].zone_name,row[2],32);
strn0cpy(into[rowIndex].door_name,row[3],32);

View File

@ -749,9 +749,9 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
if (bad) {
if (!caster->IsAttackAllowed(curmob, true))
continue;
if (center && !center->CheckLosFN(curmob))
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
continue;
if (!center && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// This does not check faction for beneficial AE buffs..only agro and attackable.

View File

@ -270,7 +270,7 @@ void Client::GoFish()
//check for add NPC
if(npc_chance > 0 && npc_id) {
if(npc_chance < zone->random.Int(0, 99)) {
const NPCType* tmp = database.GetNPCType(npc_id);
const NPCType* tmp = database.LoadNPCTypesData(npc_id);
if(tmp != nullptr) {
auto positionNPC = GetPosition();
positionNPC.x = positionNPC.x + 3;

View File

@ -72,8 +72,8 @@ public:
uint32 GetTotalGroupDamage(Mob* other);
void SplitMoney(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, Client *splitter = nullptr);
inline void SetLeader(Mob* newleader){ leader=newleader; };
inline Mob* GetLeader(){ return leader; };
char* GetLeaderName() { return membername[0]; };
inline Mob* GetLeader() { return leader; };
const char* GetLeaderName() { return membername[0]; };
void SendHPPacketsTo(Mob* newmember);
void SendHPPacketsFrom(Mob* newmember);
bool UpdatePlayer(Mob* update);

View File

@ -219,17 +219,17 @@ bool HateList::RemoveEntFromHateList(Mob *in_entity)
{
if ((*iterator)->entity_on_hatelist == in_entity)
{
if (in_entity)
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0);
is_found = true;
if (in_entity && in_entity->IsClient())
in_entity->CastToClient()->DecrementAggroCount();
delete (*iterator);
iterator = list.erase(iterator);
if (in_entity)
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "0", 0);
}
else
++iterator;
@ -282,14 +282,14 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
return nullptr;
Mob* top_hate = nullptr;
int32 hate = -1;
int64 hate = -1;
if (center == nullptr)
return nullptr;
if (RuleB(Aggro, SmartAggroList)){
Mob* top_client_type_in_range = nullptr;
int32 hate_client_type_in_range = -1;
int64 hate_client_type_in_range = -1;
int skipped_count = 0;
auto iterator = list.begin();
@ -337,7 +337,7 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
continue;
}
int32 current_hate = cur->stored_hate_amount;
int64 current_hate = cur->stored_hate_amount;
if (cur->entity_on_hatelist->IsClient()){
@ -459,13 +459,13 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center)
Mob *HateList::GetEntWithMostHateOnList(){
Mob* top = nullptr;
int32 hate = -1;
int64 hate = -1;
auto iterator = list.begin();
while (iterator != list.end())
{
struct_HateList *cur = (*iterator);
if (cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
if (cur && cur->entity_on_hatelist != nullptr && (cur->stored_hate_amount > hate))
{
top = cur->entity_on_hatelist;
hate = cur->stored_hate_amount;

View File

@ -177,16 +177,16 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
}
bool Client::CheckLoreConflict(const Item_Struct* item) {
if (!item)
return false;
if (!(item->LoreFlag))
return false;
bool Client::CheckLoreConflict(const Item_Struct* item)
{
if (!item) { return false; }
if (!item->LoreFlag) { return false; }
if (item->LoreGroup == 0) { return false; }
if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result
if (item->LoreGroup == 0xFFFFFFFF) // Standard lore items; look everywhere except the shared bank, return the result
return (m_inv.HasItem(item->ID, 0, ~invWhereSharedBank) != INVALID_INDEX);
//If the item has a lore group, we check for other items with the same group and return the result
// If the item has a lore group, we check for other items with the same group and return the result
return (m_inv.HasItemByLoreGroup(item->LoreGroup, ~invWhereSharedBank) != INVALID_INDEX);
}
@ -619,7 +619,7 @@ void Client::DropItem(int16 slot_id)
// Save client inventory change to database
if (slot_id == MainCursor) {
SendCursorBuffer();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(CharacterID(), s, e);
} else {
database.SaveInventory(CharacterID(), nullptr, slot_id);
@ -680,20 +680,37 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) {
return INVALID_ID;
}
void Client::SendCursorBuffer() {
void Client::SendCursorBuffer()
{
// Temporary work-around for the RoF+ Client Buffer
// Instead of dealing with client moving items in cursor buffer,
// we can just send the next item in the cursor buffer to the cursor.
if (GetClientVersion() >= ClientVersion::RoF)
{
if (!GetInv().CursorEmpty())
{
const ItemInst* inst = GetInv().GetCursorItem();
if (inst)
{
SendItemPacket(MainCursor, inst, ItemPacketSummonItem);
}
}
if (GetClientVersion() < ClientVersion::RoF) { return; }
if (GetInv().CursorEmpty()) { return; }
auto test_inst = GetInv().GetCursorItem();
if (test_inst == nullptr) { return; }
auto test_item = test_inst->GetItem();
if (test_item == nullptr) { return; }
bool lore_pass = true;
if (test_item->LoreGroup == 0xFFFFFFFF) {
lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
else if (test_item->LoreGroup != 0) {
lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
if (!lore_pass) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor",
GetName(), test_item->Name, test_item->ID);
Message_StringID(MT_LootMessages, 290);
parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0);
DeleteItemInInventory(MainCursor);
SendCursorBuffer();
}
else {
SendItemPacket(MainCursor, test_inst, ItemPacketSummonItem);
}
}
@ -772,7 +789,7 @@ void Client::DeleteItemInInventory(int16 slot_id, int8 quantity, bool client_upd
const ItemInst* inst = nullptr;
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
if(update_db)
database.SaveCursor(character_id, s, e);
}
@ -826,7 +843,7 @@ bool Client::PushItemOnCursor(const ItemInst& inst, bool client_update)
SendItemPacket(MainCursor, &inst, ItemPacketSummonItem);
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
return database.SaveCursor(CharacterID(), s, e);
}
@ -851,7 +868,7 @@ bool Client::PutItemInInventory(int16 slot_id, const ItemInst& inst, bool client
}
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
return database.SaveCursor(this->CharacterID(), s, e);
}
else {
@ -870,7 +887,7 @@ void Client::PutLootInInventory(int16 slot_id, const ItemInst &inst, ServerLootI
SendLootItemInPacket(&inst, slot_id);
if (slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
else {
@ -1009,7 +1026,7 @@ void Client::MoveItemCharges(ItemInst &from, int16 to_slot, uint8 type)
from.SetCharges(from.GetCharges() - charges_to_move);
SendLootItemInPacket(tmp_inst, to_slot);
if (to_slot == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
else {
@ -1320,10 +1337,33 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
return false;
}
// This could be expounded upon at some point to let the server know that
// the client has moved a buffered cursor item onto the active cursor -U
if (move_in->from_slot == move_in->to_slot) { // Item summon, no further processing needed
if(RuleB(QueryServ, PlayerLogMoves)) { QSSwapItemAuditor(move_in); } // QS Audit
if (GetClientVersion() >= ClientVersion::RoF) { return true; } // Can't do RoF+
if (move_in->to_slot == MainCursor) {
auto test_inst = m_inv.GetItem(MainCursor);
if (test_inst == nullptr) { return true; }
auto test_item = test_inst->GetItem();
if (test_item == nullptr) { return true; }
if (!test_item->LoreFlag) { return true; }
bool lore_pass = true;
if (test_item->LoreGroup == 0xFFFFFFFF) {
lore_pass = (m_inv.HasItem(test_item->ID, 0, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
else if (test_item->LoreGroup != 0) {
lore_pass = (m_inv.HasItemByLoreGroup(test_item->LoreGroup, ~(invWhereSharedBank | invWhereCursor)) == INVALID_INDEX);
}
if (!lore_pass) {
Log.Out(Logs::General, Logs::Inventory, "(%s) Duplicate lore items are not allowed - destroying item %s(id:%u) on cursor",
GetName(), test_item->Name, test_item->ID);
Message_StringID(MT_LootMessages, 290);
parse->EventItem(EVENT_DESTROY_ITEM, this, test_inst, nullptr, "", 0);
DeleteItemInInventory(MainCursor, 0, true);
}
}
return true;
}
@ -1567,7 +1607,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
{
SendCursorBuffer();
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else
@ -1726,7 +1766,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
{
SendCursorBuffer();
}
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else {
@ -1734,7 +1774,7 @@ bool Client::SwapItem(MoveItem_Struct* move_in) {
}
if (dst_slot_id == MainCursor) {
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(character_id, s, e);
}
else {
@ -1944,9 +1984,9 @@ void Client::QSSwapItemAuditor(MoveItem_Struct* move_in, bool postaction_call) {
void Client::DyeArmor(DyeStruct* dye){
int16 slot=0;
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 ||
m_pp.item_tint[i].rgb.red != dye->dye[i].rgb.red ||
m_pp.item_tint[i].rgb.green != dye->dye[i].rgb.green
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.Green != dye->dye[i].RGB.Green
) {
slot = m_inv.HasItem(32557, 1, invWherePersonal);
if (slot != INVALID_INDEX){
@ -1954,18 +1994,18 @@ void Client::DyeArmor(DyeStruct* dye){
uint8 slot2=SlotConvert(i);
ItemInst* inst = this->m_inv.GetItem(slot2);
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);
database.SaveCharacterMaterialColor(this->CharacterID(), i, armor_color);
database.SaveInventory(CharacterID(),inst,slot2);
if(dye->dye[i].rgb.use_tint)
m_pp.item_tint[i].rgb.use_tint = 0xFF;
if(dye->dye[i].RGB.UseTint)
m_pp.item_tint[i].RGB.UseTint = 0xFF;
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.red=dye->dye[i].rgb.red;
m_pp.item_tint[i].rgb.green=dye->dye[i].rgb.green;
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.Green=dye->dye[i].RGB.Green;
SendWearChange(i);
}
else{
@ -2170,7 +2210,7 @@ void Client::RemoveNoRent(bool client_update)
}
local.clear();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
}
@ -2298,7 +2338,7 @@ void Client::RemoveDuplicateLore(bool client_update)
}
local_2.clear();
auto s = m_inv.cursor_begin(), e = m_inv.cursor_end();
auto s = m_inv.cursor_cbegin(), e = m_inv.cursor_cend();
database.SaveCursor(this->CharacterID(), s, e);
}
}
@ -2380,7 +2420,7 @@ uint32 Client::GetEquipmentColor(uint8 material_slot) const
const Item_Struct *item = database.GetItem(GetEquipment(material_slot));
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;
}
@ -2436,97 +2476,99 @@ EQApplicationPacket* Client::ReturnItemPacket(int16 slot_id, const ItemInst* ins
return outapp;
}
static int16 BandolierSlotToWeaponSlot(int BandolierSlot) {
switch(BandolierSlot) {
case bandolierMainHand:
return MainPrimary;
case bandolierOffHand:
return MainSecondary;
case bandolierRange:
return MainRange;
default:
return MainAmmo;
static int16 BandolierSlotToWeaponSlot(int BandolierSlot)
{
switch (BandolierSlot)
{
case bandolierPrimary:
return MainPrimary;
case bandolierSecondary:
return MainSecondary;
case bandolierRange:
return MainRange;
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
// in the players weapon slots.
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);
strcpy(m_pp.bandoliers[bs->number].name, 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);
const ItemInst* InvItem = 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);
InvItem = GetInv()[WeaponSlot];
if(InvItem) {
BaseItem = InvItem->GetItem();
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].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);
m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = BaseItem->ID;
m_pp.bandoliers[bs->Number].Items[BandolierSlot].Icon = BaseItem->Icon;
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 {
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].icon = 0;
m_pp.bandoliers[bs->Number].Items[BandolierSlot].ID = 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;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->number);
memset(m_pp.bandoliers[bds->number].name, 0, 32);
for(int i = bandolierMainHand; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->number].items[i].item_id = 0;
m_pp.bandoliers[bds->number].items[i].icon = 0;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s removing set", GetName(), bds->Number);
memset(m_pp.bandoliers[bds->Number].Name, 0, 32);
for(int i = bandolierPrimary; i <= bandolierAmmo; i++) {
m_pp.bandoliers[bds->Number].Items[i].ID = 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
// any items currently in the weapon slots to inventory.
BandolierSet_Struct *bss = (BandolierSet_Struct*)app->pBuffer;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->number);
int16 slot;
int16 WeaponSlot;
Log.Out(Logs::Detail, Logs::Inventory, "Char: %s activating set %i", GetName(), bss->Number);
int16 slot = 0;
int16 WeaponSlot = 0;
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
// 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(m_pp.bandoliers[bss->number].items[BandolierSlot].item_id) {
if(m_pp.bandoliers[bss->Number].Items[BandolierSlot].ID) {
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// 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);
// removed 'invWhereCursor' argument from above and implemented slots 30, 331-340 checks here
if (slot == INVALID_INDEX) {
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
slot = MainCursor;
}
else if (m_inv.GetItem(MainCursor)->GetItem()->ItemClass == 1) {
for(int16 CursorBagSlot = EmuConstants::CURSOR_BAG_BEGIN; CursorBagSlot <= EmuConstants::CURSOR_BAG_END; 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
slot = CursorBagSlot;
break;
@ -2590,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
// 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
WeaponSlot = BandolierSlotToWeaponSlot(BandolierSlot);
// 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(BandolierItems[BandolierSlot]) {
// Pull the item that we are going to replace
@ -2826,9 +2868,9 @@ bool Client::InterrogateInventory(Client* requester, bool log, bool silent, bool
}
int limbo = 0;
for (auto cursor_itr = m_inv.cursor_begin(); cursor_itr != m_inv.cursor_end(); ++cursor_itr, ++limbo) {
for (auto cursor_itr = m_inv.cursor_cbegin(); cursor_itr != m_inv.cursor_cend(); ++cursor_itr, ++limbo) {
// m_inv.cursor_begin() is referenced as MainCursor in MapPossessions above
if (cursor_itr == m_inv.cursor_begin())
if (cursor_itr == m_inv.cursor_cbegin())
continue;
instmap[8000 + limbo] = *cursor_itr;

View File

@ -19,6 +19,7 @@
#include "../common/global_define.h"
#include "../common/loottable.h"
#include "../common/misc_functions.h"
#include "../common/data_verification.h"
#include "client.h"
#include "entity.h"
@ -35,7 +36,7 @@
// Queries the loottable: adds item & coin to the npc
void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) {
const LootTable_Struct* lts = 0;
const LootTable_Struct* lts = nullptr;
*copper = 0;
*silver = 0;
*gold = 0;
@ -45,44 +46,39 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
if (!lts)
return;
// do coin
if (lts->mincash > lts->maxcash) {
std::cerr << "Error in loottable #" << loottable_id << ": mincash > maxcash" << std::endl;
uint32 min_cash = lts->mincash;
uint32 max_cash = lts->maxcash;
if(min_cash > max_cash) {
uint32 t = min_cash;
min_cash = max_cash;
max_cash = t;
}
else if (lts->maxcash != 0) {
uint32 cash = 0;
if (lts->mincash == lts->maxcash)
cash = lts->mincash;
else
cash = zone->random.Int(lts->mincash, lts->maxcash);
if (cash != 0) {
if (lts->avgcoin != 0) {
//this is some crazy ass stuff... and makes very little sense... dont use it, k?
uint32 mincoin = (uint32) (lts->avgcoin * 0.75 + 1);
uint32 maxcoin = (uint32) (lts->avgcoin * 1.25 + 1);
*copper = zone->random.Int(mincoin, maxcoin);
*silver = zone->random.Int(mincoin, maxcoin);
*gold = zone->random.Int(mincoin, maxcoin);
if(*copper > cash) { *copper = cash; }
cash -= *copper;
if(*silver>(cash/10)) { *silver = (cash/10); }
cash -= *silver*10;
if(*gold > (cash/100)) { *gold = (cash/100); }
cash -= *gold*100;
}
if (cash < 0) {
cash = 0;
}
*plat = cash / 1000;
cash -= *plat * 1000;
uint32 gold2 = cash / 100;
cash -= gold2 * 100;
uint32 silver2 = cash / 10;
cash -= silver2 * 10;
*gold += gold2;
*silver += silver2;
*copper += cash;
uint32 cash = 0;
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
if(avg_cash_roll < upper_chance) {
cash = zone->random.Int(lts->avgcoin, max_cash);
} else {
cash = zone->random.Int(min_cash, lts->avgcoin);
}
} else {
cash = zone->random.Int(min_cash, max_cash);
}
if(cash != 0) {
*plat = cash / 1000;
cash -= *plat * 1000;
*gold = cash / 100;
cash -= *gold * 100;
*silver = cash / 10;
cash -= *silver * 10;
*copper = cash;
}
// Do items
@ -97,11 +93,11 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
float drop_chance = 0.0f;
if(ltchance > 0.0 && ltchance < 100.0) {
drop_chance = zone->random.Real(0.0, 100.0);
drop_chance = (float)zone->random.Real(0.0, 100.0);
}
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance < ltchance)) {
AddLootDropToNPC(npc,lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
if (ltchance != 0.0 && (ltchance == 100.0 || drop_chance <= ltchance)) {
AddLootDropToNPC(npc, lts->Entries[i].lootdrop_id, itemlist, droplimit, mindrop);
}
}
}
@ -114,63 +110,76 @@ void ZoneDatabase::AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* iteml
if (!lds) {
return;
}
if(lds->NumEntries == 0) //nothing possible to add
if(lds->NumEntries == 0)
return;
// Too long a list needs to be limited.
if(lds->NumEntries > 99 && droplimit < 1)
droplimit = lds->NumEntries/100;
uint8 limit = 0;
// Start at a random point in itemlist.
uint32 item = zone->random.Int(0, lds->NumEntries-1);
// Main loop.
for (uint32 i=0; i<lds->NumEntries;)
{
//Force the itemlist back to beginning.
if (item > (lds->NumEntries-1))
item = 0;
uint8 charges = lds->Entries[item].multiplier;
uint8 pickedcharges = 0;
// Loop to check multipliers.
for (uint32 x=1; x<=charges; x++)
{
// Actual roll.
float thischance = 0.0;
thischance = lds->Entries[item].chance;
float drop_chance = 0.0;
if(thischance != 100.0)
drop_chance = zone->random.Real(0.0, 100.0);
#if EQDEBUG>=11
Log.Out(Logs::General, Logs::None, "Drop chance for npc: %s, this chance:%f, drop roll:%f", npc->GetName(), thischance, drop_chance);
#endif
if (thischance == 100.0 || drop_chance < thischance)
{
uint32 itemid = lds->Entries[item].item_id;
const Item_Struct* dbitem = GetItem(itemid);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[item].item_charges, lds->Entries[item].minlevel, lds->Entries[item].maxlevel, lds->Entries[item].equip_item, false);
pickedcharges++;
if(droplimit == 0 && mindrop == 0) {
for(uint32 i = 0; i < lds->NumEntries; ++i) {
int charges = lds->Entries[i].multiplier;
for(int j = 0; j < charges; ++j) {
if(zone->random.Real(0.0, 100.0) <= lds->Entries[i].chance) {
const Item_Struct* dbitem = GetItem(lds->Entries[i].item_id);
npc->AddLootDrop(dbitem, itemlist, lds->Entries[i].item_charges, lds->Entries[i].minlevel,
lds->Entries[i].maxlevel, lds->Entries[i].equip_item > 0 ? true : false, false);
}
}
}
// Items with multipliers only count as 1 towards the limit.
if(pickedcharges > 0)
limit++;
return;
}
// If true, limit reached.
if(limit >= droplimit && droplimit > 0)
break;
if(lds->NumEntries > 100 && droplimit == 0) {
droplimit = 10;
}
item++;
i++;
if(droplimit < mindrop) {
droplimit = mindrop;
}
// We didn't reach our minimium, run loop again.
if(i == lds->NumEntries){
if(limit < mindrop){
i = 0;
float roll_t = 0.0f;
bool active_item_list = false;
for(uint32 i = 0; i < lds->NumEntries; ++i) {
const Item_Struct* db_item = GetItem(lds->Entries[i].item_id);
if(db_item) {
roll_t += lds->Entries[i].chance;
active_item_list = true;
}
}
roll_t = EQEmu::ClampLower(roll_t, 100.0f);
if(!active_item_list) {
return;
}
mindrop = EQEmu::ClampLower(mindrop, (uint8)1);
int item_count = zone->random.Int(mindrop, droplimit);
for(int i = 0; i < item_count; ++i) {
float roll = (float)zone->random.Real(0.0, roll_t);
for(uint32 j = 0; j < lds->NumEntries; ++j) {
const Item_Struct* db_item = GetItem(lds->Entries[j].item_id);
if(db_item) {
if(roll < lds->Entries[j].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
int charges = (int)lds->Entries[i].multiplier;
charges = EQEmu::ClampLower(charges, 1);
for(int k = 1; k < charges; ++k) {
float c_roll = (float)zone->random.Real(0.0, 100.0);
if(c_roll <= lds->Entries[i].chance) {
npc->AddLootDrop(db_item, itemlist, lds->Entries[j].item_charges, lds->Entries[j].minlevel,
lds->Entries[j].maxlevel, lds->Entries[j].equip_item > 0 ? true : false, false);
}
}
j = lds->NumEntries;
break;
}
else {
roll -= lds->Entries[j].chance;
}
}
}
} // We either ran out of items or reached our limit.
@ -215,6 +224,7 @@ void NPC::AddLootDrop(const Item_Struct *item2, ItemList* itemlist, int16 charge
item->attuned = 0;
item->min_level = minlevel;
item->max_level = maxlevel;
if (equipit) {
uint8 eslot = 0xFF;
char newid[20];

View File

@ -17,6 +17,7 @@
#include "questmgr.h"
#include "qglobals.h"
#include "../common/timer.h"
#include "../common/eqemu_logsys.h"
struct Events { };
struct Factions { };
@ -1221,7 +1222,6 @@ std::string lua_get_encounter() {
return quest_manager.GetEncounter();
}
void lua_map_opcodes() {
MapOpcodes();
}
@ -1249,6 +1249,17 @@ double lua_clock() {
return static_cast<double>(t) / 1000.0;
}
void lua_debug(std::string message) {
Log.Out(Logs::General, Logs::QuestDebug, message);
}
void lua_debug(std::string message, int level) {
if (level < Logs::General || level > Logs::Detail)
return;
Log.Out(static_cast<Logs::DebugLevel>(level), Logs::QuestDebug, message);
}
#define LuaCreateNPCParse(name, c_type, default_value) do { \
cur = table[#name]; \
if(luabind::type(cur) != LUA_TNIL) { \
@ -1582,7 +1593,9 @@ luabind::scope lua_register_general() {
luabind::def("disable_recipe", &lua_disable_recipe),
luabind::def("clear_npctype_cache", &lua_clear_npctype_cache),
luabind::def("clock", &lua_clock),
luabind::def("create_npc", &lua_create_npc)
luabind::def("create_npc", &lua_create_npc),
luabind::def("debug", (void(*)(std::string))&lua_debug),
luabind::def("debug", (void(*)(std::string, int))&lua_debug)
];
}

View File

@ -35,14 +35,13 @@
#include "zone.h"
#include "lua_parser.h"
const char *LuaEvents[_LargestEventID] = {
const char *LuaEvents[_LargestEventID] = {
"event_say",
"event_trade",
"event_death",
"event_spawn",
"event_attack",
"event_combat",
"event_environmental_damage",
"event_aggro",
"event_slay",
"event_npc_slay",
@ -68,6 +67,7 @@ const char *LuaEvents[_LargestEventID] = {
"event_aggro_say",
"event_player_pickup",
"event_popup_response",
"event_environmental_damage",
"event_proximity_say",
"event_cast",
"event_cast_begin",

View File

@ -449,11 +449,11 @@ void Merc::AddItemBonuses(const Item_Struct *item, StatBonuses* newbon) {
newbon->DSMitigation += item->DSMitigation;
}
if (item->Worn.Effect>0 && (item->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, true);
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0, true);
ApplySpellsBonuses(item->Focus.Effect, item->Focus.Level, newbon, 0);
}
switch(item->BardType)

View File

@ -170,6 +170,7 @@ Mob::Mob(const char* in_name,
findable = false;
trackable = true;
has_shieldequiped = false;
has_twohandbluntequiped = false;
has_numhits = false;
has_MGB = false;
has_ProjectIllusion = false;
@ -300,6 +301,7 @@ Mob::Mob(const char* in_name,
focused = false;
_IsTempPet = false;
pet_owner_client = false;
pet_targetlock_id = 0;
attacked_count = 0;
mezzed = false;
@ -959,10 +961,10 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
// Only Player Races Wear Armor
if (Mob::IsPlayerRace(race) || i > 6)
{
ns->spawn.equipment[i].material = GetEquipmentMaterial(i);
ns->spawn.equipment[i].elitematerial = IsEliteMaterialItem(i);
ns->spawn.equipment[i].heroforgemodel = GetHerosForgeModel(i);
ns->spawn.colors[i].color = GetEquipmentColor(i);
ns->spawn.equipment[i].Material = GetEquipmentMaterial(i);
ns->spawn.equipment[i].EliteMaterial = IsEliteMaterialItem(i);
ns->spawn.equipment[i].HeroForgeModel = GetHerosForgeModel(i);
ns->spawn.colors[i].Color = GetEquipmentColor(i);
}
}
@ -1981,9 +1983,10 @@ void Mob::TempName(const char *newname)
strn0cpy(temp_name, GetCleanName(), 64);
}
// Remove Numbers before making name unique
EntityList::RemoveNumbers(temp_name);
// Make the new name unique and set it
strn0cpy(temp_name, entity_list.MakeNameUnique(temp_name), 64);
entity_list.MakeNameUnique(temp_name);
// Send the new name to all clients
EQApplicationPacket* outapp = new EQApplicationPacket(OP_MobRename, sizeof(MobRename_Struct));
@ -2559,7 +2562,7 @@ void Mob::SendWearChange(uint8 material_slot)
wc->material = GetEquipmentMaterial(material_slot);
wc->elite_material = IsEliteMaterialItem(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;
entity_list.QueueClients(this, outapp);
@ -2574,9 +2577,9 @@ void Mob::SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model, uin
wc->spawn_id = this->GetID();
wc->material = texture;
if (this->IsClient())
wc->color.color = GetEquipmentColor(slot);
wc->color.Color = GetEquipmentColor(slot);
else
wc->color.color = this->GetArmorTint(slot);
wc->color.Color = this->GetArmorTint(slot);
wc->wear_slot_id = slot;
wc->unknown06 = unknown06;
@ -2604,7 +2607,7 @@ void Mob::SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uin
wc->spawn_id = this->GetID();
wc->material = GetEquipmentMaterial(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@ -2621,7 +2624,7 @@ void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 h
wc->spawn_id = this->GetID();
wc->material = texture;
wc->hero_forge_model = hero_forge_model;
wc->color.color = color;
wc->color.Color = color;
wc->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
@ -5362,3 +5365,26 @@ int32 Mob::GetSpellStat(uint32 spell_id, const char *identifier, uint8 slot)
return stat;
}
bool Mob::CanClassEquipItem(uint32 item_id)
{
const Item_Struct* itm = nullptr;
itm = database.GetItem(item_id);
if (!itm)
return false;
if(itm->Classes == 65535 )
return true;
if (GetClass() > 16)
return false;
int bitmask = 1;
bitmask = bitmask << (GetClass() - 1);
if(!(itm->Classes & bitmask))
return false;
else
return true;
}

View File

@ -194,7 +194,7 @@ public:
bool IsBeneficialAllowed(Mob *target);
virtual int GetCasterLevel(uint16 spell_id);
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1,
uint8 WornType = 0, uint32 ticsremaining = 0, int buffslot = -1,
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
void NegateSpellsBonuses(uint16 spell_id);
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false);
@ -308,13 +308,16 @@ public:
void SetTargetable(bool on);
bool IsTargetable() const { return m_targetable; }
bool HasShieldEquiped() const { return has_shieldequiped; }
inline void ShieldEquiped(bool val) { has_shieldequiped = val; }
inline void SetShieldEquiped(bool val) { has_shieldequiped = val; }
bool HasTwoHandBluntEquiped() const { return has_twohandbluntequiped; }
inline void SetTwoHandBluntEquiped(bool val) { has_twohandbluntequiped = val; }
virtual uint16 GetSkill(SkillUseTypes skill_num) const { return 0; }
virtual uint32 GetEquipment(uint8 material_slot) const { return(0); }
virtual int32 GetEquipmentMaterial(uint8 material_slot) const;
virtual int32 GetHerosForgeModel(uint8 material_slot) const;
virtual uint32 GetEquipmentColor(uint8 material_slot) const;
virtual uint32 IsEliteMaterialItem(uint8 material_slot) const;
bool CanClassEquipItem(uint32 item_id);
bool AffectedBySpellExcludingSlot(int slot, int effect);
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, SkillUseTypes attack_skill) = 0;
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, SkillUseTypes attack_skill,
@ -621,7 +624,7 @@ public:
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
bool ImprovedTaunt();
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
int16 GetSlowMitigation() const {return slow_mitigation;}
float GetSlowMitigation() const { return slow_mitigation; }
void CalcSpellPowerDistanceMod(uint16 spell_id, float range, Mob* caster = nullptr);
inline int16 GetSpellPowerDistanceMod() const { return SpellPowerDistanceMod; };
inline void SetSpellPowerDistanceMod(int16 value) { SpellPowerDistanceMod = value; };
@ -675,6 +678,9 @@ public:
bool IsFamiliar() const { return(typeofpet == petFamiliar); }
bool IsAnimation() const { return(typeofpet == petAnimation); }
bool IsCharmed() const { return(typeofpet == petCharmed); }
bool IsTargetLockPet() const { return(typeofpet == petTargetLock); }
inline uint32 GetPetTargetLockID() { return pet_targetlock_id; };
inline void SetPetTargetLockID(uint32 value) { pet_targetlock_id = value; };
void SetOwnerID(uint16 NewOwnerID);
inline uint16 GetOwnerID() const { return ownerid; }
inline virtual bool HasOwner() { if(GetOwnerID()==0){return false;} return( entity_list.GetMob(GetOwnerID()) != 0); }
@ -1146,6 +1152,7 @@ protected:
uint16 viral_spells[MAX_SPELL_TRIGGER*2]; // Stores the spell ids of the viruses on target and caster ids
bool offhand;
bool has_shieldequiped;
bool has_twohandbluntequiped;
bool has_numhits;
bool has_MGB;
bool has_ProjectIllusion;
@ -1244,6 +1251,7 @@ protected:
bool _IsTempPet;
int16 count_TempPet;
bool pet_owner_client; //Flags regular and pets as belonging to a client
uint32 pet_targetlock_id;
EGNode *_egnode; //the EG node we are in
glm::vec3 m_TargetLocation;

View File

@ -2559,7 +2559,7 @@ void NPC::ApplyAISpellEffects(StatBonuses* newbon)
for(int i=0; i < AIspellsEffects.size(); i++)
{
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
ApplySpellsBonuses(0, 0, newbon, 0, 0, 0,-1,
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
}

View File

@ -2394,6 +2394,29 @@ void NPC::DoQuestPause(Mob *other) {
}
void NPC::ChangeLastName(const char* in_lastname)
{
EQApplicationPacket* outapp = new EQApplicationPacket(OP_GMLastName, sizeof(GMLastName_Struct));
GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer;
strcpy(gmn->name, GetName());
strcpy(gmn->gmname, GetName());
strcpy(gmn->lastname, in_lastname);
gmn->unknown[0]=1;
gmn->unknown[1]=1;
gmn->unknown[2]=1;
gmn->unknown[3]=1;
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
}
void NPC::ClearLastName()
{
std::string WT;
WT = '\0'; //Clear Last Name
ChangeLastName( WT.c_str());
}
void NPC::DepopSwarmPets()
{
if (GetSwarmInfo()) {
@ -2419,4 +2442,14 @@ void NPC::DepopSwarmPets()
}
}
}
if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){
Mob *targMob = entity_list.GetMob(GetPetTargetLockID());
if(!targMob || (targMob && targMob->IsCorpse())){
Kill();
return;
}
}
}

View File

@ -253,7 +253,7 @@ public:
uint32 GetMaxDMG() const {return max_dmg;}
uint32 GetMinDMG() const {return min_dmg;}
int16 GetSlowMitigation() const {return slow_mitigation;}
float GetSlowMitigation() const { return slow_mitigation; }
float GetAttackSpeed() const {return attack_speed;}
uint8 GetAttackDelay() const {return attack_delay;}
bool IsAnimal() const { return(bodytype == BT_Animal); }
@ -361,6 +361,9 @@ public:
const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; }
const char* GetRawNPCTypeName() const { return NPCTypedata->name; }
void ChangeLastName(const char* in_lastname);
void ClearLastName();
bool GetDepop() { return p_depop; }
void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots

View File

@ -342,8 +342,8 @@ XS(XS_Group_GetLeaderName)
if (items != 1)
Perl_croak(aTHX_ "Usage: Group::GetLeaderName(THIS)");
{
Group * THIS;
char * RETVAL;
Group * THIS;
const char * RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "Group")) {

View File

@ -8367,6 +8367,33 @@ XS(XS_Mob_ProcessSpecialAbilities)
XSRETURN_EMPTY;
}
XS(XS_Mob_CanClassEquipItem); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CanClassEquipItem)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::CanClassEquipItem(THIS, item_id)");
{
Mob * THIS;
bool RETVAL;
uint32 item_id = (uint32)SvUV(ST(1));
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Mob");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->CanClassEquipItem(item_id);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
}
XSRETURN(1);
}
#ifdef __cplusplus
extern "C"
#endif
@ -8675,6 +8702,7 @@ XS(boot_Mob)
newXSproto(strcpy(buf, "SetSpecialAbilityParam"), XS_Mob_SetSpecialAbilityParam, file, "$$$$");
newXSproto(strcpy(buf, "ClearSpecialAbilities"), XS_Mob_ClearSpecialAbilities, file, "$");
newXSproto(strcpy(buf, "ProcessSpecialAbilities"), XS_Mob_ProcessSpecialAbilities, file, "$$");
newXSproto(strcpy(buf, "CanClassEquipItem"), XS_Mob_CanClassEquipItem, file, "$$");
XSRETURN_YES;
}

View File

@ -795,6 +795,42 @@ XS(XS_NPC_IsOnHatelist)
XSRETURN(1);
}
XS(XS_NPC_RemoveFromHateList); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_RemoveFromHateList)
{
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: NPC::RemoveFromHateList(THIS, ent)");
{
NPC * THIS;
Mob* ent;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV*)SvRV(ST(1)));
ent = INT2PTR(Mob *,tmp);
}
else
Perl_croak(aTHX_ "ent is not of type Mob");
if(ent == nullptr)
Perl_croak(aTHX_ "ent is nullptr, avoiding crash.");
THIS->RemoveFromHateList(ent);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_SetNPCFactionID); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_SetNPCFactionID)
{
@ -2076,7 +2112,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
{
NPC * THIS;
int16 RETVAL;
float RETVAL;
dXSTARG;
if (sv_derived_from(ST(0), "NPC")) {
@ -2089,7 +2125,7 @@ XS(XS_NPC_GetSlowMitigation)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetSlowMitigation();
XSprePUSH; PUSHn((UV)RETVAL);
XSprePUSH; PUSHn((double)RETVAL);
}
XSRETURN(1);
}
@ -2343,7 +2379,7 @@ XS(XS_NPC_AddRangedProc) {
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->AddDefensiveProc(spell_id,chance);
THIS->AddRangedProc(spell_id,chance);
}
XSRETURN_EMPTY;
}
@ -2368,7 +2404,56 @@ XS(XS_NPC_AddDefensiveProc) {
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
THIS->AddProcToWeapon(spell_id, true, chance);
THIS->AddDefensiveProc(spell_id,chance);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_ChangeLastName); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_ChangeLastName)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: Mob::ChangeLastName(THIS, name)");
{
NPC * THIS;
char * name = nullptr;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items > 1) { name = (char *)SvPV_nolen(ST(1)); }
THIS->ChangeLastName(name);
}
XSRETURN_EMPTY;
}
XS(XS_NPC_ClearLastName); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_ClearLastName)
{
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::ClearLastName(THIS)");
{
NPC * THIS;
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(NPC *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type NPC");
if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
THIS->ClearLastName();
}
XSRETURN_EMPTY;
}
@ -2420,6 +2505,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "GetPrimaryFaction"), XS_NPC_GetPrimaryFaction, file, "$");
newXSproto(strcpy(buf, "GetNPCHate"), XS_NPC_GetNPCHate, file, "$$");
newXSproto(strcpy(buf, "IsOnHatelist"), XS_NPC_IsOnHatelist, file, "$$");
newXSproto(strcpy(buf, "RemoveFromHateList"), XS_NPC_RemoveFromHateList, file, "$$");
newXSproto(strcpy(buf, "SetNPCFactionID"), XS_NPC_SetNPCFactionID, file, "$$");
newXSproto(strcpy(buf, "GetMaxDMG"), XS_NPC_GetMaxDMG, file, "$");
newXSproto(strcpy(buf, "GetMinDMG"), XS_NPC_GetMinDMG, file, "$");
@ -2480,6 +2566,8 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$");
newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$");
newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$");
newXSproto(strcpy(buf, "ChangeLastName"), XS_NPC_ChangeLastName, file, "$:$");
newXSproto(strcpy(buf, "ClearLastName"), XS_NPC_ClearLastName, file, "$");
XSRETURN_YES;
}

View File

@ -248,7 +248,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
}
//find the NPC data for the specified NPC type
const NPCType *base = database.GetNPCType(record.npc_type);
const NPCType *base = database.LoadNPCTypesData(record.npc_type);
if(base == nullptr) {
Message(13, "Unable to load NPC data for pet %s", pettype);
Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
@ -384,7 +384,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
monsterid = 567;
// give the summoned pet the attributes of the monster we found
const NPCType* monster = database.GetNPCType(monsterid);
const NPCType* monster = database.LoadNPCTypesData(monsterid);
if(monster) {
npc_type->race = monster->race;
npc_type->size = monster->size;
@ -426,6 +426,20 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
entity_list.AddNPC(npc, true, true);
SetPetID(npc->GetID());
// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet
if (record.petcontrol == petTargetLock)
{
Mob* target = GetTarget();
if (target){
npc->AddToHateList(target, 1);
npc->SetPetTargetLockID(target->GetID());
npc->SetSpecialAbility(IMMUNE_AGGRO, 1);
}
else
npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP)
}
}
/* This is why the pets ghost - pets were being spawned too far away from its npc owner and some
into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I

View File

@ -483,7 +483,7 @@ QuestInterface *QuestParserCollection::GetQIByNPCQuest(uint32 npcid, std::string
}
//second look for /quests/zone/npcname.ext (precedence)
const NPCType *npc_type = database.GetNPCType(npcid);
const NPCType *npc_type = database.LoadNPCTypesData(npcid);
if(!npc_type) {
return nullptr;
}

View File

@ -203,7 +203,7 @@ void QuestManager::write(const char *file, const char *str) {
Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& position) {
const NPCType* tmp = 0;
if (tmp = database.GetNPCType(npc_type))
if (tmp = database.LoadNPCTypesData(npc_type))
{
NPC* npc = new NPC(tmp, nullptr, position, FlyMode3);
npc->AddLootTable();
@ -225,7 +225,7 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
}
const NPCType* tmp = 0;
if (tmp = database.GetNPCType(npc_type))
if (tmp = database.LoadNPCTypesData(npc_type))
{
NPC* npc = new NPC(tmp, nullptr, position, FlyMode3);
npc->AddLootTable();
@ -275,7 +275,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
return nullptr;
}
const NPCType* tmp = database.GetNPCType(npcid);
const NPCType* tmp = database.LoadNPCTypesData(npcid);
if(!tmp)
{
return nullptr;
@ -297,7 +297,7 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
}
}
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0);
database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), 0);
found_spawn->SetCurrentNPCID(npcid);
auto position = glm::vec4(found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), found_spawn->GetHeading());
@ -1574,7 +1574,7 @@ void QuestManager::respawn(int npcTypeID, int grid) {
quests_running_.push(e);
const NPCType* npcType = nullptr;
if ((npcType = database.GetNPCType(npcTypeID)))
if ((npcType = database.LoadNPCTypesData(npcTypeID)))
{
owner = new NPC(npcType, nullptr, owner->GetPosition(), FlyMode3);
owner->CastToNPC()->AddLootTable();
@ -2388,7 +2388,7 @@ void QuestManager::UpdateSpawnTimer(uint32 id, uint32 newTime)
{
bool found = false;
database.UpdateSpawn2Timeleft(id, 0, (newTime/1000));
database.UpdateRespawnTime(id, 0, (newTime/1000));
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
while (iterator.MoreElements())

View File

@ -183,7 +183,7 @@ bool Spawn2::Process() {
}
//try to find our NPC type.
const NPCType* tmp = database.GetNPCType(npcid);
const NPCType* tmp = database.LoadNPCTypesData(npcid);
if (tmp == nullptr) {
Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn group %d yeilded an invalid NPC type %d", spawn2_id, spawngroup_id_, npcid);
Reset(); //try again later
@ -214,9 +214,6 @@ bool Spawn2::Process() {
if(IsDespawned)
return true;
if(spawn2_id)
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), 0);
currentnpcid = npcid;
NPC* npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
@ -348,7 +345,7 @@ void Spawn2::DeathReset(bool realdeath)
//if we have a valid spawn id
if(spawn2_id)
{
database.UpdateSpawn2Timeleft(spawn2_id, zone->GetInstanceID(), (cur/1000));
database.UpdateRespawnTime(spawn2_id, zone->GetInstanceID(), (cur/1000));
Log.Out(Logs::Detail, Logs::Spawns, "Spawn2 %d: Spawn reset by death, repop in %d ms", spawn2_id, timer.GetRemainingTime());
//store it to database too
}
@ -356,28 +353,95 @@ void Spawn2::DeathReset(bool realdeath)
bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spawn2_list, int16 version, uint32 repopdelay) {
std::unordered_map<uint32, uint32> spawn_times;
timeval tv;
gettimeofday(&tv, nullptr);
/* Bulk Load NPC Types Data into the cache */
database.LoadNPCTypesData(0, true);
std::string spawn_query = StringFormat(
"SELECT "
"respawn_times.id, "
"respawn_times.`start`, "
"respawn_times.duration "
"FROM "
"respawn_times "
"WHERE instance_id = %u",
zone->GetInstanceID()
);
auto results = QueryDatabase(spawn_query);
for (auto row = results.begin(); row != results.end(); ++row) {
uint32 start_duration = atoi(row[1]) > 0 ? atoi(row[1]) : 0;
uint32 end_duration = atoi(row[2]) > 0 ? atoi(row[2]) : 0;
/* Our current time was expired */
if ((start_duration + end_duration) <= tv.tv_sec) {
spawn_times[atoi(row[0])] = 0;
}
/* We still have time left on this timer */
else {
spawn_times[atoi(row[0])] = ((start_duration + end_duration) - tv.tv_sec) * 1000;
}
}
const char *zone_name = database.GetZoneName(zoneid);
std::string query = StringFormat("SELECT id, spawngroupID, x, y, z, heading, "
"respawntime, variance, pathgrid, _condition, "
"cond_value, enabled, animation FROM spawn2 "
"WHERE zone = '%s' AND version = %u",
zone_name, version);
auto results = QueryDatabase(query);
if (!results.Success()) {
std::string query = StringFormat(
"SELECT "
"id, "
"spawngroupID, "
"x, "
"y, "
"z, "
"heading, "
"respawntime, "
"variance, "
"pathgrid, "
"_condition, "
"cond_value, "
"enabled, "
"animation "
"FROM "
"spawn2 "
"WHERE zone = '%s' AND version = %u",
zone_name,
version
);
results = QueryDatabase(query);
if (!results.Success()) {
return false;
}
}
for (auto row = results.begin(); row != results.end(); ++row) {
Spawn2* newSpawn = 0;
for (auto row = results.begin(); row != results.end(); ++row) {
bool perl_enabled = atoi(row[11]) == 1? true: false;
uint32 spawnLeft = (GetSpawnTimeLeft(atoi(row[0]), zone->GetInstanceID()) * 1000);
newSpawn = new Spawn2(atoi(row[0]), atoi(row[1]), atof(row[2]), atof(row[3]), atof(row[4]),
atof(row[5]), atoi(row[6]), atoi(row[7]), spawnLeft, atoi(row[8]),
atoi(row[9]), atoi(row[10]), perl_enabled, (EmuAppearance)atoi(row[12]));
uint32 spawn_time_left = 0;
Spawn2* new_spawn = 0;
bool perl_enabled = atoi(row[11]) == 1 ? true : false;
spawn2_list.Insert(newSpawn);
}
if (spawn_times.count(atoi(row[0])) != 0)
spawn_time_left = spawn_times[atoi(row[0])];
new_spawn = new Spawn2( //
atoi(row[0]), // uint32 in_spawn2_id
atoi(row[1]), // uint32 spawngroup_id
atof(row[2]), // float in_x
atof(row[3]), // float in_y
atof(row[4]), // float in_z
atof(row[5]), // float in_heading
atoi(row[6]), // uint32 respawn
atoi(row[7]), // uint32 variance
spawn_time_left, // uint32 timeleft
atoi(row[8]), // uint32 grid
atoi(row[9]), // uint16 in_cond_id
atoi(row[10]), // int16 in_min_value
perl_enabled, // bool in_enabled
(EmuAppearance)atoi(row[12]) // EmuAppearance anim
);
spawn2_list.Insert(new_spawn);
}
return true;
}
@ -427,8 +491,6 @@ bool ZoneDatabase::CreateSpawn2(Client *client, uint32 spawngroup, const char* z
if (results.RowsAffected() != 1)
return false;
if(client)
return true;
}

View File

@ -1186,7 +1186,6 @@ float Mob::GetRangeDistTargetSizeMod(Mob* other)
void NPC::RangedAttack(Mob* other)
{
if (!other)
return;
//make sure the attack and ranged timers are up
@ -1306,7 +1305,7 @@ void NPC::DoRangedAttackDmg(Mob* other, bool Launch, int16 damage_mod, int16 cha
if (TotalDmg > 0)
CommonOutgoingHitSuccess(other, TotalDmg, skillInUse);
else
else if (TotalDmg < -4)
TotalDmg = -5;
if (TotalDmg > 0)

View File

@ -5527,7 +5527,12 @@ int16 Client::GetFocusEffect(focusType type, uint16 spell_id) {
//Summon Spells that require reagents are typically imbue type spells, enchant metal, sacrifice and shouldn't be affected
//by reagent conservation for obvious reasons.
return realTotal + realTotal2 + realTotal3;
//Non-Live like feature to allow for an additive focus bonus to be applied from foci that are placed in worn slot. (No limit checks)
int16 worneffect_bonus = 0;
if (RuleB(Spells, UseAdditiveFocusFromWornSlot))
worneffect_bonus = itembonuses.FocusEffectsWorn[type];
return realTotal + realTotal2 + realTotal3 + worneffect_bonus;
}
int16 NPC::GetFocusEffect(focusType type, uint16 spell_id) {

View File

@ -3742,7 +3742,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect, bool use_r
return false;
}
if (IsValidSpell(spells[spell_id].RecourseLink))
if (IsValidSpell(spells[spell_id].RecourseLink) && spells[spell_id].RecourseLink != spell_id)
SpellFinished(spells[spell_id].RecourseLink, this, 10, 0, -1, spells[spells[spell_id].RecourseLink].ResistDiff);
if (IsDetrimentalSpell(spell_id)) {

Some files were not shown because too many files have changed in this diff Show More