diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..03f782494 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,23 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true + +# Matches multiple files with brace expansion notation +# Set default charset +[*.{js,py}] +charset = utf-8 + +[*.cpp] +indent_style = tab +[*.h] +indent_style = tab + +# Tab indentation (no size specified) +[Makefile] +indent_style = tab \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 817af542c..9f3694698 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,9 @@ #EQEMU_MAP_DIR CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +IF(POLICY CMP0074) + cmake_policy(SET CMP0074 NEW) +ENDIF() #FindMySQL is located here so lets make it so CMake can find it SET(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/" ${CMAKE_MODULE_PATH}) diff --git a/common/ruletypes.h b/common/ruletypes.h index 1796c9e4d..3e6965150 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -702,6 +702,7 @@ RULE_CATEGORY_END() RULE_CATEGORY(QueryServ) RULE_BOOL(QueryServ, PlayerLogChat, false) // Logs Player Chat RULE_BOOL(QueryServ, PlayerLogTrades, false) // Logs Player Trades +RULE_BOOL(QueryServ, PlayerDropItems, false) // Logs Player dropping items RULE_BOOL(QueryServ, PlayerLogHandins, false) // Logs Player Handins RULE_BOOL(QueryServ, PlayerLogNPCKills, false) // Logs Player NPC Kills RULE_BOOL(QueryServ, PlayerLogDeletes, false) // Logs Player Deletes diff --git a/common/servertalk.h b/common/servertalk.h index eb357a180..ce390bcb6 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -210,6 +210,7 @@ #define ServerOP_CZSignalNPC 0x5017 #define ServerOP_CZSetEntityVariableByNPCTypeID 0x5018 #define ServerOP_WWMarquee 0x5019 +#define ServerOP_QSPlayerDropItem 0x5020 /* Query Serv Generic Packet Flag/Type Enumeration */ enum { QSG_LFGuild = 0 }; @@ -1160,6 +1161,27 @@ struct QSPlayerLogTrade_Struct { QSTradeItems_Struct items[0]; }; +struct QSDropItems_Struct { + uint32 item_id; + uint16 charges; + uint32 aug_1; + uint32 aug_2; + uint32 aug_3; + uint32 aug_4; + uint32 aug_5; +}; + +struct QSPlayerDropItem_Struct { + uint32 char_id; + bool pickup; // 0 drop, 1 pickup + uint32 zone_id; + int x; + int y; + int z; + uint16 _detail_count; + QSDropItems_Struct items[0]; +}; + struct QSHandinItems_Struct { char action_type[7]; // handin, return or reward uint16 char_slot; diff --git a/queryserv/database.cpp b/queryserv/database.cpp index c4e185360..fcda884e4 100644 --- a/queryserv/database.cpp +++ b/queryserv/database.cpp @@ -123,6 +123,39 @@ void Database::AddSpeech(const char* from, const char* to, const char* message, } +void Database::LogPlayerDropItem(QSPlayerDropItem_Struct* QS) { + + std::string query = StringFormat("INSERT INTO `qs_player_drop_record` SET `time` = NOW(), " + "`char_id` = '%i', `pickup` = '%i', " + "`zone_id` = '%i', `x` = '%i', `y` = '%i', `z` = '%i' ", + QS->char_id, QS->pickup, QS->zone_id, QS->x, QS->y, QS->z); + + auto results = QueryDatabase(query); + if (!results.Success()) { + Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Insert: %s", results.ErrorMessage().c_str()); + Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + } + + if (QS->_detail_count == 0) + return; + + int lastIndex = results.LastInsertedID(); + + for (int i = 0; i < QS->_detail_count; i++) { + query = StringFormat("INSERT INTO `qs_player_drop_record_entries` SET `event_id` = '%i', " + "`item_id` = '%i', `charges` = '%i', `aug_1` = '%i', `aug_2` = '%i', " + "`aug_3` = '%i', `aug_4` = '%i', `aug_5` = '%i'", + lastIndex, QS->items[i].item_id, + QS->items[i].charges, QS->items[i].aug_1, QS->items[i].aug_2, + QS->items[i].aug_3, QS->items[i].aug_4, QS->items[i].aug_5); + results = QueryDatabase(query); + if (!results.Success()) { + Log(Logs::Detail, Logs::QS_Server, "Failed Drop Record Entry Insert: %s", results.ErrorMessage().c_str()); + Log(Logs::Detail, Logs::QS_Server, "%s", query.c_str()); + } + } +} + void Database::LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 detailCount) { std::string query = StringFormat("INSERT INTO `qs_player_trade_record` SET `time` = NOW(), " diff --git a/queryserv/database.h b/queryserv/database.h index b2d32341b..13815c575 100644 --- a/queryserv/database.h +++ b/queryserv/database.h @@ -45,6 +45,7 @@ public: void AddSpeech(const char* from, const char* to, const char* message, uint16 minstatus, uint32 guilddbid, uint8 type); void LogPlayerTrade(QSPlayerLogTrade_Struct* QS, uint32 DetailCount); + void LogPlayerDropItem(QSPlayerDropItem_Struct* QS); void LogPlayerHandin(QSPlayerLogHandin_Struct* QS, uint32 DetailCount); void LogPlayerNPCKill(QSPlayerLogNPCKill_Struct* QS, uint32 Members); void LogPlayerDelete(QSPlayerLogDelete_Struct* QS, uint32 Items); diff --git a/queryserv/worldserver.cpp b/queryserv/worldserver.cpp index 686c92326..a03d23b1e 100644 --- a/queryserv/worldserver.cpp +++ b/queryserv/worldserver.cpp @@ -98,6 +98,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) database.LogPlayerTrade(QS, QS->_detail_count); break; } + case ServerOP_QSPlayerDropItem: { + QSPlayerDropItem_Struct *QS = (QSPlayerDropItem_Struct *) p.Data(); + database.LogPlayerDropItem(QS); + break; + } case ServerOP_QSPlayerLogHandins: { QSPlayerLogHandin_Struct *QS = (QSPlayerLogHandin_Struct*)p.Data(); database.LogPlayerHandin(QS, QS->_detail_count); diff --git a/world/clientlist.cpp b/world/clientlist.cpp index 3f1c3f50c..eb821b22f 100644 --- a/world/clientlist.cpp +++ b/world/clientlist.cpp @@ -530,6 +530,28 @@ void ClientList::SendWhoAll(uint32 fromid,const char* to, int16 admin, Who_All_S //uint32 x = 0; int whomlen = 0; if (whom) { + // fixes for client converting some queries into a race query instead of zone + if (whom->wrace == 221) { + whom->wrace = 0xFFFF; + strcpy(whom->whom, "scarlet"); + } + if (whom->wrace == 327) { + whom->wrace = 0xFFFF; + strcpy(whom->whom, "crystal"); + } + if (whom->wrace == 103) { + whom->wrace = 0xFFFF; + strcpy(whom->whom, "kedge"); + } + if (whom->wrace == 230) { + whom->wrace = 0xFFFF; + strcpy(whom->whom, "akheva"); + } + if (whom->wrace == 229) { + whom->wrace = 0xFFFF; + strcpy(whom->whom, "netherbian"); + } + whomlen = strlen(whom->whom); if(whom->wrace == 0x001A) // 0x001A is the old Froglok race number and is sent by the client for /who all froglok whom->wrace = FROGLOK; // This is what EQEmu uses for the Froglok Race number. diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index a99a99e1b..e3d43669e 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -1297,6 +1297,7 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { case ServerOP_QSPlayerLogDeletes: case ServerOP_QSPlayerLogMoves: case ServerOP_QSPlayerLogMerchantTransactions: + case ServerOP_QSPlayerDropItem: { QSLink.SendPacket(pack); break; diff --git a/zone/client.h b/zone/client.h index e0183cd4c..7adba5214 100644 --- a/zone/client.h +++ b/zone/client.h @@ -885,6 +885,7 @@ public: void SetStats(uint8 type,int16 set_val); void IncStats(uint8 type,int16 increase_val); void DropItem(int16 slot_id, bool recurse = true); + void DropItemQS(EQEmu::ItemInstance* inst, bool pickup); int GetItemLinkHash(const EQEmu::ItemInstance* inst); // move to ItemData..or make use of the pre-calculated database field diff --git a/zone/fearpath.cpp b/zone/fearpath.cpp index 168dd2ce6..c73ec448e 100644 --- a/zone/fearpath.cpp +++ b/zone/fearpath.cpp @@ -155,42 +155,43 @@ void Mob::ProcessFlee() } } -void Mob::CalculateNewFearpoint() -{ - if(RuleB(Pathing, Fear) && zone->pathing) - { +void Mob::CalculateNewFearpoint() { + if (RuleB(Pathing, Fear) && zone->pathing) { auto Node = zone->pathing->GetRandomLocation(); if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) { ++Node.z; - m_FearWalkTarget = Node; + m_FearWalkTarget = Node; + currently_fleeing = true; + return; } - Log(Logs::Detail, Logs::None, "No path found to selected node. Falling through to old fear point selection."); + Log(Logs::Detail, + Logs::Pathing, + "No path found to selected node. Falling through to old fear point selection."); } - int loop = 0; + int loop = 0; float ranx, rany, ranz; currently_fleeing = true; while (loop < 100) //Max 100 tries { - int ran = 250 - (loop*2); + int ran = 250 - (loop * 2); loop++; - ranx = GetX()+zone->random.Int(0, ran-1)-zone->random.Int(0, ran-1); - rany = GetY()+zone->random.Int(0, ran-1)-zone->random.Int(0, ran-1); - ranz = FindGroundZ(ranx,rany); + ranx = GetX() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1); + rany = GetY() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1); + ranz = FindGroundZ(ranx, rany); if (ranz == BEST_Z_INVALID) continue; float fdist = ranz - GetZ(); - if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(),GetY(),GetZ(),ranx,rany,ranz)) - { + if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(), GetY(), GetZ(), ranx, rany, ranz)) { break; } } if (currently_fleeing) - m_FearWalkTarget = glm::vec3(ranx, rany, ranz); + m_FearWalkTarget = glm::vec3(ranx, rany, ranz); } diff --git a/zone/inventory.cpp b/zone/inventory.cpp index d375d94b6..902268e2c 100644 --- a/zone/inventory.cpp +++ b/zone/inventory.cpp @@ -688,10 +688,79 @@ void Client::DropItem(int16 slot_id, bool recurse) object->StartDecay(); Log(Logs::General, Logs::Inventory, "Item drop handled ut assolet"); + DropItemQS(inst, false); safe_delete(inst); } +void Client::DropItemQS(EQEmu::ItemInstance* inst, bool pickup) { + if (RuleB(QueryServ, PlayerDropItems)) { + QSPlayerDropItem_Struct qs_audit; + std::list event_details; + memset(&qs_audit, 0, sizeof(QSPlayerDropItem_Struct)); + + qs_audit.char_id = this->character_id; + qs_audit.pickup = pickup; + qs_audit.zone_id = this->GetZoneID(); + qs_audit.x = (int) this->GetX(); + qs_audit.y = (int) this->GetY(); + qs_audit.z = (int) this->GetZ(); + + if (inst) { + auto detail = new QSDropItems_Struct; + detail->item_id = inst->GetID(); + detail->charges = inst->IsClassBag() ? 1 : inst->GetCharges(); + detail->aug_1 = inst->GetAugmentItemID(1); + detail->aug_2 = inst->GetAugmentItemID(2); + detail->aug_3 = inst->GetAugmentItemID(3); + detail->aug_4 = inst->GetAugmentItemID(4); + detail->aug_5 = inst->GetAugmentItemID(5); + event_details.push_back(detail); + + if (inst->IsClassBag()) { + for (uint8 sub_slot = EQEmu::invbag::SLOT_BEGIN; (sub_slot <= EQEmu::invbag::SLOT_END); ++sub_slot) { // this is to catch ALL items + const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot); + if (bag_inst) { + detail = new QSDropItems_Struct; + detail->item_id = bag_inst->GetID(); + detail->charges = (!bag_inst->IsStackable() ? 1 : bag_inst->GetCharges()); + detail->aug_1 = bag_inst->GetAugmentItemID(1); + detail->aug_2 = bag_inst->GetAugmentItemID(2); + detail->aug_3 = bag_inst->GetAugmentItemID(3); + detail->aug_4 = bag_inst->GetAugmentItemID(4); + detail->aug_5 = bag_inst->GetAugmentItemID(5); + event_details.push_back(detail); + } + } + } + } + qs_audit._detail_count = event_details.size(); + + auto qs_pack = new ServerPacket( + ServerOP_QSPlayerDropItem, + sizeof(QSPlayerDropItem_Struct) + + (sizeof(QSDropItems_Struct) * qs_audit._detail_count)); + QSPlayerDropItem_Struct* qs_buf = (QSPlayerDropItem_Struct*) qs_pack->pBuffer; + + memcpy(qs_buf, &qs_audit, sizeof(QSPlayerDropItem_Struct)); + + int offset = 0; + + for (auto iter = event_details.begin(); iter != event_details.end(); ++iter, ++offset) { + QSDropItems_Struct* detail = reinterpret_cast(*iter); + qs_buf->items[offset] = *detail; + safe_delete(detail); + } + + event_details.clear(); + + if (worldserver.Connected()) + worldserver.SendPacket(qs_pack); + + safe_delete(qs_pack); + } +} + // Drop inst void Client::DropInst(const EQEmu::ItemInstance* inst) { diff --git a/zone/mob.h b/zone/mob.h index 763fa61b7..4dafe2bf9 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -988,7 +988,7 @@ public: void SendToFixZ(float new_x, float new_y, float new_z); float GetZOffset() const; float GetDefaultRaceSize() const; - void FixZ(int32 z_find_offset = 5); + void FixZ(int32 z_find_offset = 5, bool fix_client_z = false); float GetFixedZ(glm::vec3 destination, int32 z_find_offset = 5); void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 405ff3643..bf09feb73 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -466,6 +466,7 @@ void NPC::AI_Init() roambox_distance = 0; roambox_destination_x = 0; roambox_destination_y = 0; + roambox_destination_z = 0; roambox_min_delay = 2500; roambox_delay = 2500; } @@ -779,44 +780,50 @@ void Client::AI_Process() } } - if(RuleB(Combat, EnableFearPathing)){ - if(currently_fleeing) { + if (RuleB(Combat, EnableFearPathing)) { + if (currently_fleeing) { - if (fix_z_timer_engaged.Check()) - this->FixZ(); + if (fix_z_timer.Check()) + this->FixZ(5, true); - if(IsRooted()) { + if (IsRooted()) { //make sure everybody knows were not moving, for appearance sake - if(IsMoving()) - { - if(GetTarget()) + if (IsMoving()) { + if (GetTarget()) SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetCurrentSpeed(0); } //continue on to attack code, ensuring that we execute the engaged code engaged = true; - } else { - if(AI_movement_timer->Check()) { + } + else { + if (AI_movement_timer->Check()) { int speed = GetFearSpeed(); animation = speed; speed *= 2; SetCurrentSpeed(speed); // Check if we have reached the last fear point if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) && - (std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) { + (std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) { // Calculate a new point to run to CalculateNewFearpoint(); } - if(!RuleB(Pathing, Fear) || !zone->pathing) + + if (!RuleB(Pathing, Fear) || !zone->pathing) CalculateNewPosition(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed, true); - else - { - bool WaypointChanged, NodeReached; + else { + bool waypoint_changed, node_reached; - glm::vec3 Goal = UpdatePath(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, - speed, WaypointChanged, NodeReached); + glm::vec3 Goal = UpdatePath( + m_FearWalkTarget.x, + m_FearWalkTarget.y, + m_FearWalkTarget.z, + speed, + waypoint_changed, + node_reached + ); - if(WaypointChanged) + if (waypoint_changed) tar_ndx = 20; CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed); @@ -1136,8 +1143,12 @@ void Mob::AI_Process() { bool WaypointChanged, NodeReached; glm::vec3 Goal = UpdatePath( - m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, - GetFearSpeed(), WaypointChanged, NodeReached + m_FearWalkTarget.x, + m_FearWalkTarget.y, + m_FearWalkTarget.z, + GetFearSpeed(), + WaypointChanged, + NodeReached ); if (WaypointChanged) @@ -1696,6 +1707,14 @@ void NPC::AI_DoMovement() { (m_Position.z - 15) ); + /** + * If someone brought us into water when we naturally wouldn't path there, return to spawn + */ + if (zone->watermap->InLiquid(position) && zone->watermap->InLiquid(m_Position)) { + roambox_destination_x = m_SpawnPoint.x; + roambox_destination_y = m_SpawnPoint.y; + } + if (zone->watermap->InLiquid(position)) { Log(Logs::Detail, Logs::NPCRoamBox, "%s | My destination is in water and I don't belong there!", @@ -1706,6 +1725,12 @@ void NPC::AI_DoMovement() { } } + glm::vec3 destination; + destination.x = roambox_destination_x; + destination.y = roambox_destination_y; + destination.z = m_Position.z; + roambox_destination_z = GetFixedZ(destination) + this->GetZOffset(); + Log(Logs::Detail, Logs::NPCRoamBox, "Calculate | NPC: %s distance %.3f | min_x %.3f | max_x %.3f | final_x %.3f | min_y %.3f | max_y %.3f | final_y %.3f", @@ -1719,11 +1744,20 @@ void NPC::AI_DoMovement() { roambox_destination_y); } - if (fix_z_timer.Check()) { - this->FixZ(); - } + bool waypoint_changed, node_reached; - if (!CalculateNewPosition(roambox_destination_x, roambox_destination_y, m_Position.z, move_speed, true)) { + glm::vec3 Goal = UpdatePath( + roambox_destination_x, + roambox_destination_y, + roambox_destination_z, + move_speed, + waypoint_changed, + node_reached + ); + + CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed, true); + + if (m_Position.x == roambox_destination_x && m_Position.y == roambox_destination_y) { time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay); SetMoving(false); this->FixZ(); diff --git a/zone/npc.h b/zone/npc.h index 52c8b2171..ec2676f60 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -540,6 +540,7 @@ protected: float roambox_distance; float roambox_destination_x; float roambox_destination_y; + float roambox_destination_z; uint32 roambox_delay; uint32 roambox_min_delay; diff --git a/zone/object.cpp b/zone/object.cpp index 3ad4a6a9c..4306c6642 100644 --- a/zone/object.cpp +++ b/zone/object.cpp @@ -528,6 +528,8 @@ bool Object::HandleClick(Client* sender, const ClickObject_Struct* click_object) if(cursordelete) // delete the item if it's a duplicate lore. We have to do this because the client expects the item packet sender->DeleteItemInInventory(EQEmu::invslot::slotCursor); + sender->DropItemQS(m_inst, true); + if(!m_ground_spawn) safe_delete(m_inst); diff --git a/zone/waypoints.cpp b/zone/waypoints.cpp index 5e1f0dd82..36c996ce8 100644 --- a/zone/waypoints.cpp +++ b/zone/waypoints.cpp @@ -793,26 +793,34 @@ float Mob::GetFixedZ(glm::vec3 destination, int32 z_find_offset) { return new_z; } -void Mob::FixZ(int32 z_find_offset /*= 5*/) { +void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) { glm::vec3 current_loc(m_Position); - float new_z = GetFixedZ(current_loc, z_find_offset); - if (!IsClient() && new_z != m_Position.z) { - if ((new_z > -2000) && new_z != BEST_Z_INVALID) { - if (RuleB(Map, MobZVisualDebug)) { - this->SendAppearanceEffect(78, 0, 0, 0, 0); - } + if (IsClient() && !fix_client_z) + return; - m_Position.z = new_z; + float new_z = GetFixedZ(current_loc, z_find_offset); + + if (new_z == m_Position.z) + return; + + if ((new_z > -2000) && new_z != BEST_Z_INVALID) { + if (RuleB(Map, MobZVisualDebug)) { + this->SendAppearanceEffect(78, 0, 0, 0, 0); } - else { - if (RuleB(Map, MobZVisualDebug)) { - this->SendAppearanceEffect(103, 0, 0, 0, 0); - } - Log(Logs::General, Logs::FixZ, "%s is failing to find Z %f", - this->GetCleanName(), std::abs(m_Position.z - new_z)); + m_Position.z = new_z; + } + else { + if (RuleB(Map, MobZVisualDebug)) { + this->SendAppearanceEffect(103, 0, 0, 0, 0); } + + Log(Logs::General, + Logs::FixZ, + "%s is failing to find Z %f", + this->GetCleanName(), + std::abs(m_Position.z - new_z)); } } diff --git a/zone/zone.cpp b/zone/zone.cpp index d43527c4f..3b9e5e513 100644 --- a/zone/zone.cpp +++ b/zone/zone.cpp @@ -904,10 +904,10 @@ bool Zone::Init(bool iStaticZone) { RuleManager::Instance()->LoadRules(&database, r_name.c_str()); } } - - zone->zonemap = Map::LoadMapFile(zone->map_name); + + zone->zonemap = Map::LoadMapFile(zone->map_name); zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name); - zone->pathing = IPathfinder::Load(zone->map_name); + zone->pathing = IPathfinder::Load(zone->map_name); Log(Logs::General, Logs::Status, "Loading spawn conditions..."); if(!spawn_conditions.LoadSpawnConditions(short_name, instanceid)) {