diff --git a/changelog.txt b/changelog.txt index 88180df59..6e37c6513 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,5 +1,30 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- +== 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. diff --git a/common/database.cpp b/common/database.cpp index 5216469c8..b1677c803 100644 --- a/common/database.cpp +++ b/common/database.cpp @@ -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; } } -} \ No newline at end of file +} diff --git a/common/dbcore.cpp b/common/dbcore.cpp index 4f95fba3f..d256f7b42 100644 --- a/common/dbcore.cpp +++ b/common/dbcore.cpp @@ -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; } diff --git a/common/eq_packet.cpp b/common/eq_packet.cpp index 3486beca2..390084728 100644 --- a/common/eq_packet.cpp +++ b/common/eq_packet.cpp @@ -24,6 +24,7 @@ #include "platform.h" #include #include +#include #include #ifndef STATIC_OPCODE @@ -440,3 +441,8 @@ void DumpPacket(const EQApplicationPacket* app, bool iShowInfo) { DumpPacketHex(app->pBuffer, app->size); } +std::string DumpPacketToString(const EQApplicationPacket* app){ + std::ostringstream out; + out << DumpPacketHexToString(app->pBuffer, app->size); + return out.str(); +} diff --git a/common/eq_packet.h b/common/eq_packet.h index dcd3747a0..ed90d132a 100644 --- a/common/eq_packet.h +++ b/common/eq_packet.h @@ -20,6 +20,7 @@ #include "base_packet.h" #include "platform.h" +#include #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 diff --git a/common/eq_stream.cpp b/common/eq_stream.cpp index d4264e55f..16708e3da 100644 --- a/common/eq_stream.cpp +++ b/common/eq_stream.cpp @@ -23,6 +23,7 @@ #include "op_codes.h" #include "crc16.h" #include "platform.h" +#include "string_util.h" #include #include @@ -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 @@ EQApplicationPacket *EQStream::PopPacket() } 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); } } diff --git a/common/eqemu_logsys.cpp b/common/eqemu_logsys.cpp index 139e07dd2..72d274984 100644 --- a/common/eqemu_logsys.cpp +++ b/common/eqemu_logsys.cpp @@ -289,7 +289,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) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 81d370714..ac732a5b8 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -80,6 +80,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*/ }; @@ -90,7 +94,7 @@ namespace Logs { "AI", "Aggro", "Attack", - "Client Server Packet", + "Packet :: Client -> Server", "Combat", "Commands", "Crash", @@ -125,7 +129,11 @@ namespace Logs { "MySQLError", "MySQLQuery", "Mercenaries", - "QuestDebug" + "Quest Debug", + "Packet :: Server -> Client", + "Packet :: Client -> Server Unhandled", + "Packet :: Server -> Client (Dump)", + "Packet :: Client -> Server (Dump)", }; } @@ -164,6 +172,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. diff --git a/common/item.cpp b/common/item.cpp index 35de0e773..82da305f1 100644 --- a/common/item.cpp +++ b/common/item.cpp @@ -1011,7 +1011,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; } } @@ -1441,6 +1442,7 @@ ItemInst::ItemInst(const Item_Struct* item, int16 charges) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { @@ -1466,6 +1468,7 @@ ItemInst::ItemInst(SharedDatabase *db, uint32 item_id, int16 charges) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } ItemInst::ItemInst(ItemInstTypes use_type) { @@ -1486,6 +1489,7 @@ ItemInst::ItemInst(ItemInstTypes use_type) { m_ornamenticon = 0; m_ornamentidfile = 0; m_ornament_hero_model = 0; + m_recast_timestamp = 0; } // Make a copy of an ItemInst object @@ -1539,6 +1543,7 @@ ItemInst::ItemInst(const ItemInst& copy) m_ornamenticon = copy.m_ornamenticon; m_ornamentidfile = copy.m_ornamentidfile; m_ornament_hero_model = copy.m_ornament_hero_model; + m_recast_timestamp = copy.m_recast_timestamp; } // Clean up container contents diff --git a/common/item.h b/common/item.h index 0868e8c5f..fd30b5ea0 100644 --- a/common/item.h +++ b/common/item.h @@ -403,6 +403,8 @@ public: void SetOrnamentationIDFile(uint32 ornament_idfile) { m_ornamentidfile = ornament_idfile; } uint32 GetOrnamentHeroModel(int32 material_slot = -1) const; void SetOrnamentHeroModel(uint32 ornament_hero_model) { m_ornament_hero_model = ornament_hero_model; } + uint32 GetRecastTimestamp() const { return m_recast_timestamp; } + void SetRecastTimestamp(uint32 in) { m_recast_timestamp = in; } void Initialize(SharedDatabase *db = nullptr); void ScaleItem(); @@ -450,6 +452,7 @@ protected: uint32 m_ornamenticon; uint32 m_ornamentidfile; uint32 m_ornament_hero_model; + uint32 m_recast_timestamp; // // Items inside of this item (augs or contents); diff --git a/common/packet_dump.cpp b/common/packet_dump.cpp index 3c9dfab4f..4ab1fcbef 100644 --- a/common/packet_dump.cpp +++ b/common/packet_dump.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include @@ -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); diff --git a/common/packet_dump.h b/common/packet_dump.h index 7a57597f7..db6e485a4 100644 --- a/common/packet_dump.h +++ b/common/packet_dump.h @@ -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); diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index 9494c5048..772e60517 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -5000,7 +5000,7 @@ namespace RoF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index d4c619942..6947ded15 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -5069,7 +5069,7 @@ namespace RoF2 hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/rof2_structs.h b/common/patches/rof2_structs.h index dd9bfb587..9ef524f55 100644 --- a/common/patches/rof2_structs.h +++ b/common/patches/rof2_structs.h @@ -2865,7 +2865,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 +2877,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 diff --git a/common/patches/sod.cpp b/common/patches/sod.cpp index 2e96719be..98f7faccd 100644 --- a/common/patches/sod.cpp +++ b/common/patches/sod.cpp @@ -3548,7 +3548,7 @@ namespace SoD hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/sof.cpp b/common/patches/sof.cpp index 44a87d3cc..b8a84d993 100644 --- a/common/patches/sof.cpp +++ b/common/patches/sof.cpp @@ -2872,7 +2872,7 @@ namespace SoF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/patches/titanium.cpp b/common/patches/titanium.cpp index aa5d3d7fb..3be2d7d70 100644 --- a/common/patches/titanium.cpp +++ b/common/patches/titanium.cpp @@ -2004,7 +2004,7 @@ namespace Titanium inst->IsScaling() ? inst->GetExp() / 100 : 0, //merchant_slot, //instance ID, bullshit for now (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot, - 0, // item recast timer timestamp field (aka..last_cast_time field in SoF+ clients) + inst->GetRecastTimestamp(), (stackable ? ((inst->GetItem()->ItemType == ItemTypePotion) ? 1 : 0) : charges), inst->IsAttuned() ? 1 : 0, 0 diff --git a/common/patches/uf.cpp b/common/patches/uf.cpp index 3f297534d..f772509d0 100644 --- a/common/patches/uf.cpp +++ b/common/patches/uf.cpp @@ -3793,7 +3793,7 @@ namespace UF hdr.scaled_value = inst->IsScaling() ? inst->GetExp() / 100 : 0; hdr.instance_id = (merchant_slot == 0) ? inst->GetSerialNumber() : merchant_slot; hdr.unknown028 = 0; - hdr.last_cast_time = ((item->RecastDelay > 1) ? 1212693140 : 0); + hdr.last_cast_time = inst->GetRecastTimestamp(); hdr.charges = (stackable ? (item->MaxCharges ? 1 : 0) : charges); hdr.inst_nodrop = inst->IsAttuned() ? 1 : 0; hdr.unknown044 = 0; diff --git a/common/ptimer.h b/common/ptimer.h index 829a015b2..d12b6779d 100644 --- a/common/ptimer.h +++ b/common/ptimer.h @@ -79,6 +79,7 @@ public: inline const uint32 GetTimerTime() const { return timer_time; } inline const uint32 GetStartTime() const { return start_time; } inline const pTimerType GetType() const { return _type; } + inline const uint32 GetReadyTimestamp() const { return start_time + timer_time; } inline bool Enabled() { return enabled; } diff --git a/common/ruletypes.h b/common/ruletypes.h index 136f55026..ff88115e2 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -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 ) @@ -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 ) diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 159ce13fc..13ae065be 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -498,6 +498,8 @@ bool SharedDatabase::GetInventory(uint32 char_id, Inventory *inv) return false; } + auto timestamps = GetItemRecastTimestamps(char_id); + for (auto row = results.begin(); row != results.end(); ++row) { int16 slot_id = atoi(row[0]); uint32 item_id = atoi(row[1]); @@ -582,6 +584,13 @@ bool SharedDatabase::GetInventory(uint32 char_id, Inventory *inv) else inst->SetCharges(charges); + if (item->RecastDelay) { + if (timestamps.count(item->RecastType)) + inst->SetRecastTimestamp(timestamps.at(item->RecastType)); + else + inst->SetRecastTimestamp(0); + } + if (item->ItemClass == ItemClassCommon) { for (int i = AUG_BEGIN; i < EmuConstants::ITEM_COMMON_SIZE; i++) { if (aug[i]) @@ -725,6 +734,39 @@ bool SharedDatabase::GetInventory(uint32 account_id, char *name, Inventory *inv) return GetSharedBank(account_id, inv, false); } +std::map SharedDatabase::GetItemRecastTimestamps(uint32 char_id) +{ + std::map timers; + std::string query = StringFormat("SELECT recast_type,timestamp FROM character_item_recast WHERE id=%u", char_id); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return timers; + + for (auto row = results.begin(); row != results.end(); ++row) + timers[atoul(row[0])] = atoul(row[1]); + return timers; // RVO or move assigned +} + +uint32 SharedDatabase::GetItemRecastTimestamp(uint32 char_id, uint32 recast_type) +{ + std::string query = StringFormat("SELECT timestamp FROM character_item_recast WHERE id=%u AND recast_type=%u", + char_id, recast_type); + auto results = QueryDatabase(query); + if (!results.Success() || results.RowCount() == 0) + return 0; + + auto row = results.begin(); + return static_cast(atoul(row[0])); +} + +void SharedDatabase::ClearOldRecastTimestamps(uint32 char_id) +{ + // This actually isn't strictly live-like. Live your recast timestamps are forever + std::string query = + StringFormat("DELETE FROM character_item_recast WHERE id = %u and timestamp < UNIX_TIMESTAMP()", char_id); + QueryDatabase(query); +} + void SharedDatabase::GetItemsCount(int32 &item_count, uint32 &max_id) { item_count = -1; diff --git a/common/shareddb.h b/common/shareddb.h index 073c385e8..aef325380 100644 --- a/common/shareddb.h +++ b/common/shareddb.h @@ -11,6 +11,7 @@ #include "fixed_memory_variable_hash_set.h" #include +#include class EvolveInfo; class Inventory; @@ -69,6 +70,9 @@ class SharedDatabase : public Database bool SetSharedPlatinum(uint32 account_id, int32 amount_to_add); bool GetInventory(uint32 char_id, Inventory* inv); bool GetInventory(uint32 account_id, char* name, Inventory* inv); + std::map GetItemRecastTimestamps(uint32 char_id); + uint32 GetItemRecastTimestamp(uint32 char_id, uint32 recast_type); + void ClearOldRecastTimestamps(uint32 char_id); bool SetStartingItems(PlayerProfile_Struct* pp, Inventory* inv, uint32 si_race, uint32 si_class, uint32 si_deity, uint32 si_current_zone, char* si_name, int admin); diff --git a/common/string_util.cpp b/common/string_util.cpp index a7e5f2af1..e791eb7f6 100644 --- a/common/string_util.cpp +++ b/common/string_util.cpp @@ -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); + } +} diff --git a/common/string_util.h b/common/string_util.h index 70e888bdf..c69f01456 100644 --- a/common/string_util.h +++ b/common/string_util.h @@ -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 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 diff --git a/common/version.h b/common/version.h index 7044ba6e5..85dd86b46 100644 --- a/common/version.h +++ b/common/version.h @@ -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 9072 +#define CURRENT_BINARY_DATABASE_VERSION 9076 #define COMPILE_DATE __DATE__ #define COMPILE_TIME __TIME__ #ifndef WIN32 diff --git a/queryserv/database.cpp b/queryserv/database.cpp index 56f01e22c..0f86319e6 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -394,6 +394,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 diff --git a/ucs/database.cpp b/ucs/database.cpp index 7bfbe5444..e5fa02647 100644 --- a/ucs/database.cpp +++ b/ucs/database.cpp @@ -621,6 +621,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 diff --git a/utils/sql/db_update_manifest.txt b/utils/sql/db_update_manifest.txt index 820a79bd4..f23638a07 100644 --- a/utils/sql/db_update_manifest.txt +++ b/utils/sql/db_update_manifest.txt @@ -326,6 +326,10 @@ 9070|2015_01_28_quest_debug_log_category.sql|SELECT * FROM `logsys_categories` WHERE `log_category_description` LIKE 'Quest Debug'|empty| 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 # Upgrade conditions: # This won't be needed after this system is implemented, but it is used database that are not diff --git a/utils/sql/git/optional/2015_02_15_UpdatePetTypes.sql b/utils/sql/git/optional/2015_02_15_UpdatePetTypes.sql new file mode 100644 index 000000000..29ba3b03e --- /dev/null +++ b/utils/sql/git/optional/2015_02_15_UpdatePetTypes.sql @@ -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"; diff --git a/utils/sql/git/optional/2015_2_5_UseAdditiveFocusFromWornSlot.sql b/utils/sql/git/optional/2015_2_5_UseAdditiveFocusFromWornSlot.sql new file mode 100644 index 000000000..905dcc163 --- /dev/null +++ b/utils/sql/git/optional/2015_2_5_UseAdditiveFocusFromWornSlot.sql @@ -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.'); diff --git a/utils/sql/git/required/2015_01_31_character_item_recast.sql b/utils/sql/git/required/2015_01_31_character_item_recast.sql new file mode 100644 index 000000000..fbc222608 --- /dev/null +++ b/utils/sql/git/required/2015_01_31_character_item_recast.sql @@ -0,0 +1,7 @@ +CREATE TABLE `character_item_recast` ( + `id` int(11) UNSIGNED NOT NULL DEFAULT 0, + `recast_type` smallint(11) UNSIGNED NOT NULL DEFAULT 0, + `timestamp` int(11) UNSIGNED NOT NULL DEFAULT 0, + PRIMARY KEY(`id`, `recast_type`), + KEY `id` (`id`) +) ENGINE = InnoDB DEFAULT CHARSET = latin1; diff --git a/utils/sql/git/required/2015_02_01_logsys_packet_logs.sql b/utils/sql/git/required/2015_02_01_logsys_packet_logs.sql new file mode 100644 index 000000000..ba00ee607 --- /dev/null +++ b/utils/sql/git/required/2015_02_01_logsys_packet_logs.sql @@ -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'); \ No newline at end of file diff --git a/utils/sql/git/required/2015_02_02_logsys_packet_logs_with_dump.sql b/utils/sql/git/required/2015_02_02_logsys_packet_logs_with_dump.sql new file mode 100644 index 000000000..7da9af532 --- /dev/null +++ b/utils/sql/git/required/2015_02_02_logsys_packet_logs_with_dump.sql @@ -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'); \ No newline at end of file diff --git a/utils/sql/git/required/2015_02_04_average_coin.sql b/utils/sql/git/required/2015_02_04_average_coin.sql new file mode 100644 index 000000000..9ebff6d67 --- /dev/null +++ b/utils/sql/git/required/2015_02_04_average_coin.sql @@ -0,0 +1,2 @@ +ALTER TABLE `loottable` CHANGE COLUMN `avgcoin` `avgcoin` INT UNSIGNED NOT NULL DEFAULT '0' AFTER `maxcash`; +UPDATE `loottable` SET avgcoin = 0; diff --git a/zone/attack.cpp b/zone/attack.cpp index 25dc88688..bfd4a5cc2 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1584,10 +1584,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)) { diff --git a/zone/bonuses.cpp b/zone/bonuses.cpp index 8cceb5023..4aa13519b 100644 --- a/zone/bonuses.cpp +++ b/zone/bonuses.cpp @@ -411,11 +411,11 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu 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, true, true); } 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, true, false); } switch(item->BardType) @@ -639,7 +639,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(effect); continue; } @@ -1393,7 +1393,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, false, false, buffs[i].ticsremaining,i); if (buffs[i].numhits > 0) Numhits(true); @@ -1416,7 +1416,7 @@ 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, bool item_bonus, bool IsWornEffect, 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; @@ -1439,7 +1439,12 @@ 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 (!IsWornEffect) + new_bonus->FocusEffects[focus] = static_cast(spells[spell_id].effectid[i]); + + else if (RuleB(Spells, UseAdditiveFocusFromWornSlot)) + new_bonus->FocusEffectsWorn[focus] += spells[spell_id].base[i]; + continue; } diff --git a/zone/client.cpp b/zone/client.cpp index 1e4ba461a..5cc5ae309 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -7516,7 +7516,6 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui int32 npc_value[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; uint8 temp[MAX_NPC_FACTIONS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int32 current_value; - bool change = false; // Get the npc faction list if (!database.GetNPCFactionList(npc_id, faction_id, npc_value, temp)) @@ -7525,43 +7524,81 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui { int32 faction_before_hit; int32 faction_to_use_for_messaging; + FactionMods fm; + int32 this_faction_max; + int32 this_faction_min; if (faction_id[i] <= 0) continue; + // Find out starting faction for this faction + // It needs to be used to adj max and min personal + // The range is still the same, 1200-3000(4200), but adjusted for base + database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), + faction_id[i]); + + // Adjust the amount you can go up or down so the resulting range + // is PERSONAL_MAX - PERSONAL_MIN + // + // Adjust these values for cases where starting faction is below + // min or above max by not allowing any earn in those directions. + this_faction_min = MIN_PERSONAL_FACTION - fm.base; + this_faction_min = std::min(0, this_faction_min); + this_faction_max = MAX_PERSONAL_FACTION - fm.base; + this_faction_max = std::max(0, this_faction_max); + // Get the characters current value with that faction current_value = GetCharacterFactionLevel(faction_id[i]); faction_before_hit = current_value; - change = UpdatePersonalFaction(char_id, npc_value[i], faction_id[i], ¤t_value, temp[i]); + UpdatePersonalFaction(char_id, npc_value[i], faction_id[i], ¤t_value, temp[i], this_faction_min, this_faction_max); - if (change) - { - SendFactionMessage(npc_value[i], faction_id[i], faction_before_hit, current_value, temp[i]); - } + //Message(14, "Min(%d) Max(%d) Before(%d), After(%d)\n", this_faction_min, this_faction_max, faction_before_hit, current_value); + + SendFactionMessage(npc_value[i], faction_id[i], faction_before_hit, current_value, temp[i], this_faction_min, this_faction_max); } + return; } void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp) { int32 current_value; - bool change=false; //Get the npc faction list if(faction_id > 0 && value != 0) { int32 faction_before_hit; + FactionMods fm; + int32 this_faction_max; + int32 this_faction_min; + + // Find out starting faction for this faction + // It needs to be used to adj max and min personal + // The range is still the same, 1200-3000(4200), but adjusted for base + database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), + faction_id); + + // Adjust the amount you can go up or down so the resulting range + // is PERSONAL_MAX - PERSONAL_MIN + // + // Adjust these values for cases where starting faction is below + // min or above max by not allowing any earn/loss in those directions. + // At least one faction starts out way below min, so we don't want + // to allow loses in those cases, just massive gains. + this_faction_min = MIN_PERSONAL_FACTION - fm.base; + this_faction_min = std::min(0, this_faction_min); + this_faction_max = MAX_PERSONAL_FACTION - fm.base; + this_faction_max = std::max(0, this_faction_max); //Get the faction modifiers current_value = GetCharacterFactionLevel(faction_id); faction_before_hit = current_value; - change = UpdatePersonalFaction(char_id, value, faction_id, ¤t_value, temp); + UpdatePersonalFaction(char_id, value, faction_id, ¤t_value, temp, this_faction_min, this_faction_max); - if (change) - { - SendFactionMessage(value, faction_id, faction_before_hit, current_value, temp); - } + //Message(14, "Min(%d) Max(%d) Before(%d), After(%d)\n", this_faction_min, this_faction_max, faction_before_hit, current_value); + + SendFactionMessage(value, faction_id, faction_before_hit, current_value, temp, this_faction_min, this_faction_max); } return; @@ -7583,7 +7620,7 @@ int32 Client::GetCharacterFactionLevel(int32 faction_id) // Checks for bottom out and max faction and old faction db entries // Updates the faction if we are not minned, maxed or we need to repair -bool Client::UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp) +void Client::UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp, int32 this_faction_min, int32 this_faction_max) { bool repair = false; bool change = false; @@ -7601,33 +7638,38 @@ bool Client::UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction npc_value *= 2; } } + // Set flag when to update db - if (*current_value > MAX_PERSONAL_FACTION) + // Repair needed, as db changes could modify a base value for a faction + // and we need to auto correct when that happens. + if (*current_value > this_faction_max) { - *current_value = MAX_PERSONAL_FACTION; + *current_value = this_faction_max; repair = true; } - else if (*current_value < MIN_PERSONAL_FACTION) + else if (*current_value < this_faction_min) { - *current_value = MIN_PERSONAL_FACTION; + *current_value = this_faction_min; repair = true; } - else if ((m_pp.gm != 1) && (npc_value != 0) && ((*current_value != MAX_PERSONAL_FACTION) || (*current_value != MIN_PERSONAL_FACTION))) + else if ((m_pp.gm != 1) && (npc_value != 0) && + ((npc_value > 0 && *current_value != this_faction_max) || + ((npc_value < 0 && *current_value != this_faction_min)))) change = true; - *current_value += npc_value; - - if (*current_value > MAX_PERSONAL_FACTION) - *current_value = MAX_PERSONAL_FACTION; - else if (*current_value < MIN_PERSONAL_FACTION) - *current_value = MIN_PERSONAL_FACTION; - if (change || repair) { + *current_value += npc_value; + + if (*current_value > this_faction_max) + *current_value = this_faction_max; + else if (*current_value < this_faction_min) + *current_value = this_faction_min; + database.SetCharacterFactionLevel(char_id, faction_id, *current_value, temp, factionvalues); } -return (repair || change); +return; } // returns the character's faction level, adjusted for racial, class, and deity modifiers @@ -7708,21 +7750,21 @@ void Client::MerchantRejectMessage(Mob *merchant, int primaryfaction) //o-------------------------------------------------------------- //| Purpose: Send faction change message to client //o-------------------------------------------------------------- -void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp) +void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp, int32 this_faction_min, int32 this_faction_max) { char name[50]; int32 faction_value; - // If we're dropping from MAX or raising from MIN, we should - // base the message on the new updated value so we don't show + // If we're dropping from MAX or raising from MIN or repairing, + // we should base the message on the new updated value so we don't show // a min MAX message // // If we're changing any other place, we use the value before the // hit. For example, if we go from 1199 to 1200 which is the MAX // we still want to say faction got better this time around. - if ( (faction_before_hit == MAX_PERSONAL_FACTION) || - (faction_before_hit == MIN_PERSONAL_FACTION)) + if ( (faction_before_hit >= this_faction_max) || + (faction_before_hit <= this_faction_min)) faction_value = totalvalue; else faction_value = faction_before_hit; @@ -7733,13 +7775,13 @@ void Client::SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_ if (tmpvalue == 0 || temp == 1 || temp == 2) return; - else if (faction_value >= MAX_PERSONAL_FACTION) + else if (faction_value >= this_faction_max) Message_StringID(15, FACTION_BEST, name); - else if (faction_value <= MIN_PERSONAL_FACTION) + else if (faction_value <= this_faction_min) Message_StringID(15, FACTION_WORST, name); - else if (tmpvalue > 0 && faction_value < MAX_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) + else if (tmpvalue > 0 && faction_value < this_faction_max && !RuleB(Client, UseLiveFactionMessage)) Message_StringID(15, FACTION_BETTER, name); - else if (tmpvalue < 0 && faction_value > MIN_PERSONAL_FACTION && !RuleB(Client, UseLiveFactionMessage)) + else if (tmpvalue < 0 && faction_value > this_faction_min && !RuleB(Client, UseLiveFactionMessage)) Message_StringID(15, FACTION_WORSE, name); else if (RuleB(Client, UseLiveFactionMessage)) Message(15, "Your faction standing with %s has been adjusted by %i.", name, tmpvalue); //New Live faction message (14261) diff --git a/zone/client.h b/zone/client.h index fbdcfb676..9248fb742 100644 --- a/zone/client.h +++ b/zone/client.h @@ -607,9 +607,9 @@ public: int32 GetCharacterFactionLevel(int32 faction_id); int32 GetModCharacterFactionLevel(int32 faction_id); void MerchantRejectMessage(Mob *merchant, int primaryfaction); - void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp); + void SendFactionMessage(int32 tmpvalue, int32 faction_id, int32 faction_before_hit, int32 totalvalue, uint8 temp, int32 this_faction_min, int32 this_faction_max); - bool UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp); + void UpdatePersonalFaction(int32 char_id, int32 npc_value, int32 faction_id, int32 *current_value, int32 temp, int32 this_faction_min, int32 this_faction_max); void SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, uint8 char_race, uint8 char_deity); void SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class, uint8 char_race, uint8 char_deity, int32 value, uint8 temp); int32 GetRawItemAC(); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c4e449db5..95475f1ca 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -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 args; args.push_back(const_cast(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; } @@ -1392,6 +1391,7 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) if (RuleB(Character, SharedBankPlat)) m_pp.platinum_shared = database.GetSharedPlatinum(this->AccountID()); + database.ClearOldRecastTimestamps(cid); /* Clear out our old recast timestamps to keep the DB clean */ loaditems = database.GetInventory(cid, &m_inv); /* Load Character Inventory */ database.LoadCharacterBandolier(cid, &m_pp); /* Load Character Bandolier */ database.LoadCharacterBindPoint(cid, &m_pp); /* Load Character Bind */ @@ -5516,7 +5516,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; @@ -5526,11 +5526,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 */ @@ -9862,6 +9862,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; diff --git a/zone/command.cpp b/zone/command.cpp index 1fbfc81d3..35d1ab353 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -10591,6 +10591,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 { diff --git a/zone/common.h b/zone/common.h index 19237d99c..639eee91e 100644 --- a/zone/common.h +++ b/zone/common.h @@ -350,7 +350,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 +506,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 { diff --git a/zone/corpse.cpp b/zone/corpse.cpp index 36e585ea2..0dd4ed1f3 100644 --- a/zone/corpse.cpp +++ b/zone/corpse.cpp @@ -965,6 +965,7 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a Save(); } + auto timestamps = database.GetItemRecastTimestamps(client->CharacterID()); outapp->priority = 6; client->QueuePacket(outapp); safe_delete(outapp); @@ -973,6 +974,8 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a const Item_Struct* item = database.GetItem(pkitem); ItemInst* inst = database.CreateItem(item, item->MaxCharges); if(inst) { + if (item->RecastDelay) + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); client->SendItemPacket(EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); safe_delete(inst); } @@ -1004,6 +1007,8 @@ void Corpse::MakeLootRequestPackets(Client* client, const EQApplicationPacket* a if(client && item) { ItemInst* inst = database.CreateItem(item, item_data->charges, item_data->aug_1, item_data->aug_2, item_data->aug_3, item_data->aug_4, item_data->aug_5, item_data->aug_6, item_data->attuned); if(inst) { + if (item->RecastDelay) + inst->SetRecastTimestamp(timestamps.count(item->RecastType) ? timestamps.at(item->RecastType) : 0); // MainGeneral1 is the corpse inventory start offset for Ti(EMu) - CORPSE_END = MainGeneral1 + MainCursor client->SendItemPacket(i + EmuConstants::CORPSE_BEGIN, inst, ItemPacketLoot); safe_delete(inst); @@ -1460,4 +1465,4 @@ void Corpse::LoadPlayerCorpseDecayTime(uint32 corpse_db_id){ else { corpse_graveyard_timer.SetTimer(3000); } -} \ No newline at end of file +} diff --git a/zone/effects.cpp b/zone/effects.cpp index 802fce796..5cc024ce2 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -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. diff --git a/zone/groups.h b/zone/groups.h index 909a9dd53..71935bf81 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -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 leader->GetName(); }; void SendHPPacketsTo(Mob* newmember); void SendHPPacketsFrom(Mob* newmember); bool UpdatePlayer(Mob* update); diff --git a/zone/loottables.cpp b/zone/loottables.cpp index e004bbee5..0e269f13e 100644 --- a/zone/loottables.cpp +++ b/zone/loottables.cpp @@ -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; iNumEntries;) - { - //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]; diff --git a/zone/lua_general.cpp b/zone/lua_general.cpp index cdf42005d..50afdb6e9 100644 --- a/zone/lua_general.cpp +++ b/zone/lua_general.cpp @@ -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(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(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) ]; } diff --git a/zone/lua_parser.cpp b/zone/lua_parser.cpp index cf6d356dc..7f6412b0f 100644 --- a/zone/lua_parser.cpp +++ b/zone/lua_parser.cpp @@ -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", diff --git a/zone/mob.cpp b/zone/mob.cpp index a54881298..e9cee33e4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -300,6 +300,7 @@ Mob::Mob(const char* in_name, focused = false; _IsTempPet = false; pet_owner_client = false; + pet_targetlock_id = 0; attacked_count = 0; mezzed = false; @@ -1981,9 +1982,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)); @@ -5362,3 +5364,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; +} + diff --git a/zone/mob.h b/zone/mob.h index 9641a2ffb..46531b819 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -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, + bool item_bonus = false, bool IsWornEffect = false, 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); @@ -315,6 +315,7 @@ public: 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 +622,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 +676,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); } @@ -1244,6 +1248,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; diff --git a/zone/npc.cpp b/zone/npc.cpp index 9a142f128..5d180e994 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -2419,4 +2419,14 @@ void NPC::DepopSwarmPets() } } } + + if (IsPet() && GetPetType() == petTargetLock && GetPetTargetLockID()){ + + Mob *targMob = entity_list.GetMob(GetPetTargetLockID()); + + if(!targMob || (targMob && targMob->IsCorpse())){ + Kill(); + return; + } + } } diff --git a/zone/npc.h b/zone/npc.h index 6cb31ed87..ee3dedf62 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -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); } diff --git a/zone/object.cpp b/zone/object.cpp index 38f4c67c4..7dd97d355 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -467,16 +467,21 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) if (m_inst && sender) { // if there is a lore conflict, delete the offending item from the server inventory // the client updates itself and takes care of sending "duplicate lore item" messages - if(sender->CheckLoreConflict(m_inst->GetItem())) { - int16 loreslot = sender->GetInv().HasItem(m_inst->GetItem()->ID, 0, invWhereBank); + auto item = m_inst->GetItem(); + if(sender->CheckLoreConflict(item)) { + int16 loreslot = sender->GetInv().HasItem(item->ID, 0, invWhereBank); if (loreslot != INVALID_INDEX) // if the duplicate is in the bank, delete it. sender->DeleteItemInInventory(loreslot); else cursordelete = true; // otherwise, we delete the new one } + if (item->RecastDelay) + m_inst->SetRecastTimestamp( + database.GetItemRecastTimestamp(sender->CharacterID(), item->RecastType)); + char buf[10]; - snprintf(buf, 9, "%u", m_inst->GetItem()->ID); + snprintf(buf, 9, "%u", item->ID); buf[9] = '\0'; std::vector args; args.push_back(m_inst); diff --git a/zone/perl_groups.cpp b/zone/perl_groups.cpp index 6960d14df..cdbffdd1c 100644 --- a/zone/perl_groups.cpp +++ b/zone/perl_groups.cpp @@ -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")) { diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index f72e98e43..a28d51376 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -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; } diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index 2b7b91617..bbefef2ed 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -2076,7 +2076,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 +2089,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); } diff --git a/zone/pets.cpp b/zone/pets.cpp index 117b466a7..871e9bc6c 100644 --- a/zone/pets.cpp +++ b/zone/pets.cpp @@ -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 diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index a6725b296..24eac51ee 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -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) { diff --git a/zone/spells.cpp b/zone/spells.cpp index bf4da6688..849dacfbe 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1242,6 +1242,8 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, uint16 slot, { //Can we start the timer here? I don't see why not. CastToClient()->GetPTimers().Start((pTimerItemStart + recasttype), recastdelay); + database.UpdateItemRecastTimestamps(CastToClient()->CharacterID(), recasttype, + CastToClient()->GetPTimers().Get(pTimerItemStart + recasttype)->GetReadyTimestamp()); } } @@ -2023,6 +2025,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 // // Switch #2 - execute the spell // + switch(CastAction) { default: @@ -2146,6 +2149,15 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 // caster if they're not using TGB // NOTE: this will always hit the caster, plus the target's group so // it can affect up to 7 people if the targeted group is not our own + + // Allow pets who cast group spells to affect the group. + if (spell_target->IsPetOwnerClient()){ + Mob* owner = spell_target->GetOwner(); + + if (owner) + spell_target = owner; + } + if(spell_target->IsGrouped()) { Group *target_group = entity_list.GetGroupByMob(spell_target); @@ -2270,11 +2282,15 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, uint16 slot, uint16 { ItemInst *itm = CastToClient()->GetInv().GetItem(inventory_slot); if(itm && itm->GetItem()->RecastDelay > 0){ - CastToClient()->GetPTimers().Start((pTimerItemStart + itm->GetItem()->RecastType), itm->GetItem()->RecastDelay); + auto recast_type = itm->GetItem()->RecastType; + CastToClient()->GetPTimers().Start((pTimerItemStart + recast_type), itm->GetItem()->RecastDelay); + database.UpdateItemRecastTimestamps( + CastToClient()->CharacterID(), recast_type, + CastToClient()->GetPTimers().Get(pTimerItemStart + recast_type)->GetReadyTimestamp()); EQApplicationPacket *outapp = new EQApplicationPacket(OP_ItemRecastDelay, sizeof(ItemRecastDelay_Struct)); ItemRecastDelay_Struct *ird = (ItemRecastDelay_Struct *)outapp->pBuffer; ird->recast_delay = itm->GetItem()->RecastDelay; - ird->recast_type = itm->GetItem()->RecastType; + ird->recast_type = recast_type; CastToClient()->QueuePacket(outapp); safe_delete(outapp); } diff --git a/zone/tasks.cpp b/zone/tasks.cpp index 59c6f50eb..98a0ec4c5 100644 --- a/zone/tasks.cpp +++ b/zone/tasks.cpp @@ -2872,7 +2872,13 @@ int ClientTaskState::GetTaskActivityDoneCountFromTaskID(int TaskID, int Activity break; } } - return ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount; + + if (ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount){ + return ActiveTasks[ActiveTaskIndex].Activity[ActivityID].DoneCount; + } + else{ + return 0; + } } int ClientTaskState::GetTaskStartTime(int index) { diff --git a/zone/zone.h b/zone/zone.h index 3d1775718..80e0473f2 100644 --- a/zone/zone.h +++ b/zone/zone.h @@ -23,6 +23,7 @@ #include "../common/rulesys.h" #include "../common/types.h" #include "../common/random.h" +#include "../common/string_util.h" #include "qglobals.h" #include "spawn2.h" #include "spawngroup.h" @@ -67,16 +68,6 @@ struct item_tick_struct { std::string qglobal; }; -// static uint32 gmsay_log_message_colors[EQEmuLogSys::MaxLogID] = { -// 15, // "Status", - Yellow -// 15, // "Normal", - Yellow -// 3, // "Error", - Red -// 14, // "Debug", - Light Green -// 4, // "Quest", -// 5, // "Command", -// 3 // "Crash" -// }; - class Client; class Map; class Mob; @@ -266,7 +257,25 @@ public: // random object that provides random values for the zone EQEmu::Random random; - static void GMSayHookCallBackProcess(uint16 log_category, const std::string& message){ entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str()); } + static void GMSayHookCallBackProcess(uint16 log_category, std::string message){ + /* Cut messages down to 4000 max to prevent client crash */ + if (!message.empty()) + message = message.substr(0, 4000); + + /* Replace Occurrences of % or MessageStatus will crash */ + find_replace(message, std::string("%"), std::string(".")); + + if (message.find("\n") != std::string::npos){ + auto message_split = SplitString(message, '\n'); + entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message_split[0].c_str()); + for (size_t iter = 1; iter < message_split.size(); ++iter) { + entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "--- %s", message_split[iter].c_str()); + } + } + else{ + entity_list.MessageStatus(0, 80, Log.GetGMSayColorFromCategory(log_category), "%s", message.c_str()); + } + } //MODDING HOOKS void mod_init(); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index bb81a0fc4..07fe67679 100644 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -1118,11 +1118,9 @@ bool ZoneDatabase::LoadCharacterMaterialColor(uint32 character_id, PlayerProfile bool ZoneDatabase::LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp){ std::string query = StringFormat("SELECT `bandolier_id`, `bandolier_slot`, `item_id`, `icon`, `bandolier_name` FROM `character_bandolier` WHERE `id` = %u LIMIT 16", character_id); auto results = database.QueryDatabase(query); int i = 0; int r = 0; int si = 0; - for (i = 0; i <= EmuConstants::BANDOLIERS_COUNT; i++){ - for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++){ + for (i = 0; i < EmuConstants::BANDOLIERS_COUNT; i++) + for (int si = 0; si < EmuConstants::BANDOLIER_SIZE; si++) pp->bandoliers[i].items[si].icon = 0; - } - } for (auto row = results.begin(); row != results.end(); ++row) { r = 0; @@ -2999,6 +2997,14 @@ void ZoneDatabase::RemoveTempFactions(Client *client) { QueryDatabase(query); } +void ZoneDatabase::UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp) +{ + std::string query = + StringFormat("REPLACE INTO character_item_recast (id, recast_type, timestamp) VALUES (%u, %u, %u)", char_id, + recast_type, timestamp); + QueryDatabase(query); +} + void ZoneDatabase::LoadPetInfo(Client *client) { // Load current pet and suspended pet @@ -3204,16 +3210,7 @@ bool ZoneDatabase::GetNPCFactionList(uint32 npcfaction_id, int32* faction_id, in bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, int32 value, uint8 temp, faction_map &val_list) { - std::string query = StringFormat("DELETE FROM faction_values " - "WHERE char_id=%i AND faction_id = %i", - char_id, faction_id); - auto results = QueryDatabase(query); - if (!results.Success()) { - return false; - } - - if(value == 0) - return true; + std::string query; if(temp == 2) temp = 0; @@ -3221,17 +3218,18 @@ bool ZoneDatabase::SetCharacterFactionLevel(uint32 char_id, int32 faction_id, in if(temp == 3) temp = 1; - query = StringFormat("INSERT INTO faction_values (char_id, faction_id, current_value, temp) " - "VALUES (%i, %i, %i, %i)", char_id, faction_id, value, temp); - results = QueryDatabase(query); - if (!results.Success()) { + query = StringFormat("INSERT INTO `faction_values` " + "(`char_id`, `faction_id`, `current_value`, `temp`) " + "VALUES (%i, %i, %i, %i) " + "ON DUPLICATE KEY UPDATE `current_value`=%i,`temp`=%i", + char_id, faction_id, value, temp, value, temp); + auto results = QueryDatabase(query); + + if (!results.Success()) return false; - } + else + val_list[faction_id] = value; - if (results.RowsAffected() == 0) - return false; - - val_list[faction_id] = value; return true; } diff --git a/zone/zonedb.h b/zone/zonedb.h index 5c3ed3d0e..5e1a55248 100644 --- a/zone/zonedb.h +++ b/zone/zonedb.h @@ -256,6 +256,7 @@ public: void LoadPetInfo(Client *c); void SavePetInfo(Client *c); void RemoveTempFactions(Client *c); + void UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp); /* Character Data Loaders */ bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list);