From b01486d767efefa71b44a3a802d4298b916f4350 Mon Sep 17 00:00:00 2001 From: Mitch Freeman <65987027+neckkola@users.noreply.github.com> Date: Thu, 13 Jul 2023 00:04:50 -0300 Subject: [PATCH] [Feature] Update raid features (#3443) * [RAID] Add Raid Features [RAID] Add Raid Features - Add delegate main assist - Add delegate main marker - Add target ring for main assisters. Uses MA1, then MA2, then MA3 - Add /assist raid respecting /assist on and /assist off - Add Raid Notes. Functions across zones - Add Raid XTarget functional - Raid Leader can mark without being delegated Main Marker. Must have the appropriate AA * Update to new db routines * Updated several formatting issues based on review * Update to pp->tribute_time_remaining to avoid edge case. Unrelated to raid updates. * Updates to resolve comments/review. Added a few edge case updates as well. * Refactored to use database repositories for raid_details and raid_members. Other updates as noted in review. * Updated database manifest and fixed potential leak within Client::Handle_OP_AssistGroup * Update for remaining review items * Refactor SendAssistTarget to use struct/vector loop * Have IsAssister use range based for loop and return bool * General cleanup * Simplify SendRaidAssistTarget to use struct / vector * Formatting in Handle_OP_RaidDelegateAbility * Format SendRemoveRaidXTargets and clean up error statements * Format SendRemoveAllRaidXTargets * Formatting * Default return FindNextRaidDelegateSlot to -1 * Change fields to marked_npc_1/2/3 (missing last underscore) --------- Co-authored-by: Akkadius --- common/database/database_update_manifest.cpp | 18 + common/emu_oplist.h | 3 + common/eq_packet_structs.h | 10 +- common/patches/rof2.cpp | 15 +- .../base/base_raid_details_repository.h | 66 +- .../base/base_raid_members_repository.h | 30 + common/repositories/raid_details_repository.h | 17 + common/repositories/raid_members_repository.h | 54 +- common/servertalk.h | 5 + common/version.h | 2 +- utils/patches/patch_RoF2.conf | 5 +- world/zoneserver.cpp | 8 + zone/client.cpp | 35 +- zone/client.h | 2 +- zone/client_packet.cpp | 222 ++++- zone/client_packet.h | 4 +- zone/entity.cpp | 12 + zone/entity.h | 1 + zone/mob.cpp | 8 + zone/raids.cpp | 806 +++++++++++++++++- zone/raids.h | 70 +- zone/string_ids.h | 5 + zone/worldserver.cpp | 10 + zone/zonedb.cpp | 2 +- 24 files changed, 1322 insertions(+), 88 deletions(-) diff --git a/common/database/database_update_manifest.cpp b/common/database/database_update_manifest.cpp index cbfa25af9..b4e29c583 100644 --- a/common/database/database_update_manifest.cpp +++ b/common/database/database_update_manifest.cpp @@ -4760,6 +4760,24 @@ UNIQUE KEY `name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; )" }, + ManifestEntry{ + .version = 9230, + .description = "2023_06_23_raid_feature_updates", + .check = "SHOW COLUMNS FROM `raid_members` LIKE 'is_assister'", + .condition = "empty", + .match = "", + .sql = R"( + ALTER TABLE `raid_members` + ADD COLUMN `is_marker` TINYINT UNSIGNED DEFAULT(0) NOT NULL AFTER `islooter`, + ADD COLUMN `is_assister` TINYINT UNSIGNED DEFAULT(0) NOT NULL AFTER `is_marker`, + ADD COLUMN `note` VARCHAR(64) DEFAULT("") NOT NULL AFTER `is_assister`; + + ALTER TABLE `raid_details` + ADD COLUMN `marked_npc_1` SMALLINT UNSIGNED DEFAULT(0) NOT NULL AFTER `motd`, + ADD COLUMN `marked_npc_2` SMALLINT UNSIGNED DEFAULT(0) NOT NULL AFTER `marked_npc_1`, + ADD COLUMN `marked_npc_3` SMALLINT UNSIGNED DEFAULT(0) NOT NULL AFTER `marked_npc_2`; + )", + }, // -- template; copy/paste this when you need to create a new entry // ManifestEntry{ diff --git a/common/emu_oplist.h b/common/emu_oplist.h index d8798c594..6e30333aa 100644 --- a/common/emu_oplist.h +++ b/common/emu_oplist.h @@ -316,6 +316,7 @@ N(OP_LootRequest), N(OP_ManaChange), N(OP_ManaUpdate), N(OP_MarkNPC), +N(OP_MarkRaidNPC), N(OP_Marquee), N(OP_MemorizeSpell), N(OP_Mend), @@ -398,6 +399,8 @@ N(OP_PVPLeaderBoardRequest), N(OP_PVPStats), N(OP_QueryResponseThing), N(OP_QueryUCSServerStatus), +N(OP_RaidDelegateAbility), +N(OP_RaidClearNPCMarks), N(OP_RaidInvite), N(OP_RaidJoin), N(OP_RaidUpdate), diff --git a/common/eq_packet_structs.h b/common/eq_packet_structs.h index 3e8f12694..d503f56bc 100644 --- a/common/eq_packet_structs.h +++ b/common/eq_packet_structs.h @@ -4105,7 +4105,9 @@ struct UpdateLeadershipAA_Struct { enum { - GroupLeadershipAbility_MarkNPC = 0 + GroupLeadershipAbility_MarkNPC = 0, + RaidLeadershipAbility_MarkNPC = 16, + RaidLeadershipAbility_MainAssist = 19 }; struct DoGroupLeadershipAbility_Struct @@ -4149,8 +4151,10 @@ struct InspectBuffs_Struct { struct RaidGeneral_Struct { /*00*/ uint32 action; //=10 /*04*/ char player_name[64]; //should both be the player's name -/*64*/ char leader_name[64]; -/*132*/ uint32 parameter; +/*68*/ uint32 unknown1; +/*72*/ char leader_name[64]; +/*136*/ uint32 parameter; +/*200*/ char note[64]; }; struct RaidAddMember_Struct { diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 30b6ad70a..a9ee64c94 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -35,6 +35,7 @@ #include "../path_manager.h" #include "../classes.h" #include "../races.h" +#include "../../zone/raids.h" #include #include @@ -2737,7 +2738,7 @@ namespace RoF2 { RaidLeadershipUpdate_Struct *inlaa = (RaidLeadershipUpdate_Struct *)__emu_buffer; auto outapp = - new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); + new EQApplicationPacket(OP_RaidUpdate, sizeof(structs::RaidLeadershipUpdate_Struct)); structs::RaidLeadershipUpdate_Struct *outlaa = (structs::RaidLeadershipUpdate_Struct *)outapp->pBuffer; outlaa->action = inlaa->action; @@ -2746,6 +2747,18 @@ namespace RoF2 memcpy(&outlaa->raid, &inlaa->raid, sizeof(RaidLeadershipAA_Struct)); dest->FastQueuePacket(&outapp); } + else if (raid_gen->action == raidSetNote) + { + auto in_note = (RaidGeneral_Struct*)__emu_buffer; + auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + auto note = (RaidGeneral_Struct*)outapp->pBuffer; + note->action = raidSetNote; + strn0cpy(note->leader_name, in_note->leader_name, sizeof(note->leader_name)); + strn0cpy(note->player_name, in_note->player_name, sizeof(note->leader_name)); + strn0cpy(note->note, in_note->note, sizeof(note->note)); + dest->QueuePacket(outapp); + safe_delete(outapp); + } else { RaidGeneral_Struct* in_raid_general = (RaidGeneral_Struct*)__emu_buffer; diff --git a/common/repositories/base/base_raid_details_repository.h b/common/repositories/base/base_raid_details_repository.h index 7ce468c60..5479595bd 100644 --- a/common/repositories/base/base_raid_details_repository.h +++ b/common/repositories/base/base_raid_details_repository.h @@ -16,6 +16,7 @@ #include "../../strings.h" #include + class BaseRaidDetailsRepository { public: struct RaidDetails { @@ -23,6 +24,9 @@ public: int32_t loottype; int8_t locked; std::string motd; + uint16_t marked_npc_1; + uint16_t marked_npc_2; + uint16_t marked_npc_3; }; static std::string PrimaryKey() @@ -37,6 +41,9 @@ public: "loottype", "locked", "motd", + "marked_npc_1", + "marked_npc_2", + "marked_npc_3", }; } @@ -47,6 +54,9 @@ public: "loottype", "locked", "motd", + "marked_npc_1", + "marked_npc_2", + "marked_npc_3", }; } @@ -87,10 +97,13 @@ public: { RaidDetails e{}; - e.raidid = 0; - e.loottype = 0; - e.locked = 0; - e.motd = ""; + e.raidid = 0; + e.loottype = 0; + e.locked = 0; + e.motd = ""; + e.marked_npc_1 = 0; + e.marked_npc_2 = 0; + e.marked_npc_3 = 0; return e; } @@ -116,8 +129,9 @@ public: { auto results = db.QueryDatabase( fmt::format( - "{} WHERE id = {} LIMIT 1", + "{} WHERE {} = {} LIMIT 1", BaseSelect(), + PrimaryKey(), raid_details_id ) ); @@ -126,10 +140,13 @@ public: if (results.RowCount() == 1) { RaidDetails e{}; - e.raidid = static_cast(atoi(row[0])); - e.loottype = static_cast(atoi(row[1])); - e.locked = static_cast(atoi(row[2])); - e.motd = row[3] ? row[3] : ""; + e.raidid = static_cast(atoi(row[0])); + e.loottype = static_cast(atoi(row[1])); + e.locked = static_cast(atoi(row[2])); + e.motd = row[3] ? row[3] : ""; + e.marked_npc_1 = static_cast(strtoul(row[4], nullptr, 10)); + e.marked_npc_2 = static_cast(strtoul(row[5], nullptr, 10)); + e.marked_npc_3 = static_cast(strtoul(row[6], nullptr, 10)); return e; } @@ -167,6 +184,9 @@ public: v.push_back(columns[1] + " = " + std::to_string(e.loottype)); v.push_back(columns[2] + " = " + std::to_string(e.locked)); v.push_back(columns[3] + " = '" + Strings::Escape(e.motd) + "'"); + v.push_back(columns[4] + " = " + std::to_string(e.marked_npc_1)); + v.push_back(columns[5] + " = " + std::to_string(e.marked_npc_2)); + v.push_back(columns[6] + " = " + std::to_string(e.marked_npc_3)); auto results = db.QueryDatabase( fmt::format( @@ -192,6 +212,9 @@ public: v.push_back(std::to_string(e.loottype)); v.push_back(std::to_string(e.locked)); v.push_back("'" + Strings::Escape(e.motd) + "'"); + v.push_back(std::to_string(e.marked_npc_1)); + v.push_back(std::to_string(e.marked_npc_2)); + v.push_back(std::to_string(e.marked_npc_3)); auto results = db.QueryDatabase( fmt::format( @@ -225,6 +248,9 @@ public: v.push_back(std::to_string(e.loottype)); v.push_back(std::to_string(e.locked)); v.push_back("'" + Strings::Escape(e.motd) + "'"); + v.push_back(std::to_string(e.marked_npc_1)); + v.push_back(std::to_string(e.marked_npc_2)); + v.push_back(std::to_string(e.marked_npc_3)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -258,10 +284,13 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { RaidDetails e{}; - e.raidid = static_cast(atoi(row[0])); - e.loottype = static_cast(atoi(row[1])); - e.locked = static_cast(atoi(row[2])); - e.motd = row[3] ? row[3] : ""; + e.raidid = static_cast(atoi(row[0])); + e.loottype = static_cast(atoi(row[1])); + e.locked = static_cast(atoi(row[2])); + e.motd = row[3] ? row[3] : ""; + e.marked_npc_1 = static_cast(strtoul(row[4], nullptr, 10)); + e.marked_npc_2 = static_cast(strtoul(row[5], nullptr, 10)); + e.marked_npc_3 = static_cast(strtoul(row[6], nullptr, 10)); all_entries.push_back(e); } @@ -286,10 +315,13 @@ public: for (auto row = results.begin(); row != results.end(); ++row) { RaidDetails e{}; - e.raidid = static_cast(atoi(row[0])); - e.loottype = static_cast(atoi(row[1])); - e.locked = static_cast(atoi(row[2])); - e.motd = row[3] ? row[3] : ""; + e.raidid = static_cast(atoi(row[0])); + e.loottype = static_cast(atoi(row[1])); + e.locked = static_cast(atoi(row[2])); + e.motd = row[3] ? row[3] : ""; + e.marked_npc_1 = static_cast(strtoul(row[4], nullptr, 10)); + e.marked_npc_2 = static_cast(strtoul(row[5], nullptr, 10)); + e.marked_npc_3 = static_cast(strtoul(row[6], nullptr, 10)); all_entries.push_back(e); } diff --git a/common/repositories/base/base_raid_members_repository.h b/common/repositories/base/base_raid_members_repository.h index 3a0d34dcb..295325a9d 100644 --- a/common/repositories/base/base_raid_members_repository.h +++ b/common/repositories/base/base_raid_members_repository.h @@ -31,6 +31,9 @@ public: int8_t isgroupleader; int8_t israidleader; int8_t islooter; + uint8_t is_marker; + uint8_t is_assister; + std::string note; }; static std::string PrimaryKey() @@ -52,6 +55,9 @@ public: "isgroupleader", "israidleader", "islooter", + "is_marker", + "is_assister", + "note", }; } @@ -69,6 +75,9 @@ public: "isgroupleader", "israidleader", "islooter", + "is_marker", + "is_assister", + "note", }; } @@ -120,6 +129,9 @@ public: e.isgroupleader = 0; e.israidleader = 0; e.islooter = 0; + e.is_marker = 0; + e.is_assister = 0; + e.note = ""; return e; } @@ -167,6 +179,9 @@ public: e.isgroupleader = static_cast(atoi(row[8])); e.israidleader = static_cast(atoi(row[9])); e.islooter = static_cast(atoi(row[10])); + e.is_marker = static_cast(strtoul(row[11], nullptr, 10)); + e.is_assister = static_cast(strtoul(row[12], nullptr, 10)); + e.note = row[13] ? row[13] : ""; return e; } @@ -210,6 +225,9 @@ public: v.push_back(columns[8] + " = " + std::to_string(e.isgroupleader)); v.push_back(columns[9] + " = " + std::to_string(e.israidleader)); v.push_back(columns[10] + " = " + std::to_string(e.islooter)); + v.push_back(columns[11] + " = " + std::to_string(e.is_marker)); + v.push_back(columns[12] + " = " + std::to_string(e.is_assister)); + v.push_back(columns[13] + " = '" + Strings::Escape(e.note) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -242,6 +260,9 @@ public: v.push_back(std::to_string(e.isgroupleader)); v.push_back(std::to_string(e.israidleader)); v.push_back(std::to_string(e.islooter)); + v.push_back(std::to_string(e.is_marker)); + v.push_back(std::to_string(e.is_assister)); + v.push_back("'" + Strings::Escape(e.note) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -282,6 +303,9 @@ public: v.push_back(std::to_string(e.isgroupleader)); v.push_back(std::to_string(e.israidleader)); v.push_back(std::to_string(e.islooter)); + v.push_back(std::to_string(e.is_marker)); + v.push_back(std::to_string(e.is_assister)); + v.push_back("'" + Strings::Escape(e.note) + "'"); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -326,6 +350,9 @@ public: e.isgroupleader = static_cast(atoi(row[8])); e.israidleader = static_cast(atoi(row[9])); e.islooter = static_cast(atoi(row[10])); + e.is_marker = static_cast(strtoul(row[11], nullptr, 10)); + e.is_assister = static_cast(strtoul(row[12], nullptr, 10)); + e.note = row[13] ? row[13] : ""; all_entries.push_back(e); } @@ -361,6 +388,9 @@ public: e.isgroupleader = static_cast(atoi(row[8])); e.israidleader = static_cast(atoi(row[9])); e.islooter = static_cast(atoi(row[10])); + e.is_marker = static_cast(strtoul(row[11], nullptr, 10)); + e.is_assister = static_cast(strtoul(row[12], nullptr, 10)); + e.note = row[13] ? row[13] : ""; all_entries.push_back(e); } diff --git a/common/repositories/raid_details_repository.h b/common/repositories/raid_details_repository.h index 524903cf2..61030c2fd 100644 --- a/common/repositories/raid_details_repository.h +++ b/common/repositories/raid_details_repository.h @@ -44,7 +44,24 @@ public: */ // Custom extended repository methods here + static int UpdateRaidMarkedNPC( + Database& db, + int32_t raid_id, + uint8_t marked_npc_number, + uint8_t value + ) { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE `{}` SET `marked_npc_{}` = '{}' WHERE raidid = '{}';", + TableName(), + marked_npc_number, + value, + raid_id + ) + ); + return results.Success() ? results.RowsAffected() : 0; + } }; #endif //EQEMU_RAID_DETAILS_REPOSITORY_H diff --git a/common/repositories/raid_members_repository.h b/common/repositories/raid_members_repository.h index e8f5ddb82..2c2aae1de 100644 --- a/common/repositories/raid_members_repository.h +++ b/common/repositories/raid_members_repository.h @@ -44,7 +44,59 @@ public: */ // Custom extended repository methods here + static int UpdateRaidNote( + Database& db, + int32_t raid_id, + const std::string& note, + const std::string& character_name + ) { + auto results = db.QueryDatabase( + fmt::format("UPDATE `{}` SET `note` = '{}' WHERE raidid = '{}' AND name = '{}';", + TableName(), + Strings::Escape(note), + raid_id, + Strings::Escape(character_name) + ) + ); + return results.Success() ? results.RowsAffected() : 0; + } + + static int UpdateRaidAssister( + Database& db, + int32_t raid_id, + const std::string& character_name, + uint8_t value + ) { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE `{}` SET `is_assister` = '{}' WHERE raidid = '{}' AND `name` = '{}';", + TableName(), + value, + raid_id, + Strings::Escape(character_name) + ) + ); + return results.Success() ? results.RowsAffected() : 0; + } + + static int UpdateRaidMarker( + Database& db, + int32_t raid_id, + const std::string& character_name, + uint8_t value + ) { + auto results = db.QueryDatabase( + fmt::format( + "UPDATE `{}` SET `is_marker` = '{}' WHERE raidid = '{}' AND `name` = '{}';", + TableName(), + value, + raid_id, + Strings::Escape(character_name) + ) + ); + + return results.Success() ? results.RowsAffected() : 0; + } }; - #endif //EQEMU_RAID_MEMBERS_REPOSITORY_H diff --git a/common/servertalk.h b/common/servertalk.h index f9740752f..901e2c35b 100644 --- a/common/servertalk.h +++ b/common/servertalk.h @@ -113,6 +113,7 @@ #define ServerOP_GroupFollowAck 0x0111 #define ServerOP_GroupCancelInvite 0x0112 #define ServerOP_RaidMOTD 0x0113 +#define ServerOP_RaidNote 0x0114 #define ServerOP_InstanceUpdateTime 0x014F #define ServerOP_AdventureRequest 0x0150 @@ -1075,6 +1076,10 @@ struct ServerRaidMOTD_Struct { char motd[0]; }; +struct ServerRaidNote_Struct { + uint32 rid; +}; + struct ServerLFGMatchesRequest_Struct { uint32 FromID; uint8 QuerierLevel; diff --git a/common/version.h b/common/version.h index 2e19f9c55..56eedc114 100644 --- a/common/version.h +++ b/common/version.h @@ -42,7 +42,7 @@ * Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt */ -#define CURRENT_BINARY_DATABASE_VERSION 9229 +#define CURRENT_BINARY_DATABASE_VERSION 9230 #define CURRENT_BINARY_BOTS_DATABASE_VERSION 9039 diff --git a/utils/patches/patch_RoF2.conf b/utils/patches/patch_RoF2.conf index 08e02b93c..ec63f7913 100644 --- a/utils/patches/patch_RoF2.conf +++ b/utils/patches/patch_RoF2.conf @@ -301,9 +301,7 @@ OP_LeadershipExpUpdate=0x2797 OP_PurchaseLeadershipAA=0x6c55 OP_UpdateLeadershipAA=0x0026 OP_MarkNPC=0x1fb5 -OP_MarkRaidNPC=0x5a58 #unimplemented OP_ClearNPCMarks=0x2003 -OP_ClearRaidNPCMarks=0x20d3 #unimplemented OP_DelegateAbility=0x76b8 OP_SetGroupTarget=0x2814 OP_Charm=0x5d92 @@ -544,6 +542,9 @@ OP_LFGResponse=0x0000 OP_RaidInvite=0x55ac OP_RaidUpdate=0x3973 OP_RaidJoin=0x0000 +OP_RaidDelegateAbility=0x2b33 +OP_MarkRaidNPC=0x5a58 +OP_RaidClearNPCMarks=0x20d3 # Button-push commands OP_Taunt=0x2703 diff --git a/world/zoneserver.cpp b/world/zoneserver.cpp index c79debaaf..0136aa450 100644 --- a/world/zoneserver.cpp +++ b/world/zoneserver.cpp @@ -397,6 +397,14 @@ void ZoneServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) { zoneserver_list.SendPacket(pack); break; } + case ServerOP_RaidNote: { + if (pack->size < sizeof(ServerRaidNote_Struct)) { + break; + } + + zoneserver_list.SendPacket(pack); + break; + } case ServerOP_SpawnCondition: { if (pack->size != sizeof(ServerSpawnCondition_Struct)) { break; diff --git a/zone/client.cpp b/zone/client.cpp index a74a922df..3e4b3790e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -6546,24 +6546,30 @@ void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) XTargets[i].dirty = true; } } + auto r = GetRaid(); + if (r) { + r->UpdateRaidXTargets(); + } } void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) { - if(!XTargettingAvailable()) + if (!XTargettingAvailable()) { return; + } - for(int i = 0; i < GetMaxXTargets(); ++i) - { - if(XTargets[i].Type == Type) - { - if(m) + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].Type == Type) { + if (m) { XTargets[i].ID = m->GetID(); - else + } + else { XTargets[i].ID = 0; + } - if(Name) + if (Name) { strncpy(XTargets[i].Name, Name, 64); + } SendXTargetPacket(i, m); } @@ -6597,10 +6603,7 @@ void Client::SendXTargetPacket(uint32 Slot, Mob *m) if (strlen(XTargets[Slot].Name) && ((XTargets[Slot].Type == CurrentTargetPC) || (XTargets[Slot].Type == GroupTank) || (XTargets[Slot].Type == GroupAssist) || - (XTargets[Slot].Type == Puller) || - (XTargets[Slot].Type == RaidAssist1) || - (XTargets[Slot].Type == RaidAssist2) || - (XTargets[Slot].Type == RaidAssist3))) + (XTargets[Slot].Type == Puller))) { outapp->WriteUInt8(2); } @@ -6664,13 +6667,7 @@ void Client::RemoveGroupXTargets() { if ((XTargets[i].Type == GroupTank) || (XTargets[i].Type == GroupAssist) || - (XTargets[i].Type == Puller) || - (XTargets[i].Type == RaidAssist1) || - (XTargets[i].Type == RaidAssist2) || - (XTargets[i].Type == RaidAssist3) || - (XTargets[i].Type == GroupMarkTarget1) || - (XTargets[i].Type == GroupMarkTarget2) || - (XTargets[i].Type == GroupMarkTarget3)) + (XTargets[i].Type == Puller)) { XTargets[i].ID = 0; XTargets[i].Name[0] = 0; diff --git a/zone/client.h b/zone/client.h index c4ef78ca3..22f5f1f17 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1924,6 +1924,7 @@ private: public: void SetSharedTaskId(int64 shared_task_id); int64 GetSharedTaskId() const; + struct XTarget_Struct XTargets[XTARGET_HARDCAP]; private: bool m_exp_enabled; @@ -1976,7 +1977,6 @@ private: bool XTargetAutoAddHaters; bool m_dirtyautohaters; - struct XTarget_Struct XTargets[XTARGET_HARDCAP]; XTargetAutoHaters m_autohatermgr; XTargetAutoHaters *m_activeautohatermgr; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index d52b26cb4..0a0dea12a 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -328,6 +328,8 @@ void MapOpcodes() ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest; ConnectedOpcodes[OP_QueryUCSServerStatus] = &Client::Handle_OP_QueryUCSServerStatus; ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand; + ConnectedOpcodes[OP_RaidDelegateAbility] = &Client::Handle_OP_RaidDelegateAbility; + ConnectedOpcodes[OP_RaidClearNPCMarks] = &Client::Handle_OP_RaidClearNPCMarks; ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq; ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook; ConnectedOpcodes[OP_RecipeAutoCombine] = &Client::Handle_OP_RecipeAutoCombine; @@ -607,7 +609,6 @@ void Client::CompleteConnect() but not important for now. */ raid->SendRaidCreate(this); - raid->SendMakeLeaderPacketTo(raid->leadername, this); raid->SendRaidAdd(GetName(), this); raid->SendBulkRaid(this); raid->SendGroupUpdate(this); @@ -616,6 +617,7 @@ void Client::CompleteConnect() raid->UpdateRaidAAs(); raid->SendAllRaidLeadershipAA(); } + raid->SendMakeLeaderPacketTo(raid->leadername, this); uint32 grpID = raid->GetGroup(GetName()); if (grpID < 12) { raid->SendRaidGroupRemove(GetName(), grpID); @@ -636,6 +638,8 @@ void Client::CompleteConnect() raid->SendRaidLockTo(this); raid->SendHPManaEndPacketsTo(this); + raid->SendAssistTarget(this); + raid->SendMarkTargets(this); } } else { @@ -3043,8 +3047,22 @@ void Client::Handle_OP_AssistGroup(const EQApplicationPacket *app) LogDebug("Size mismatch in OP_AssistGroup expected [{}] got [{}]", sizeof(EntityId_Struct), app->size); return; } - QueuePacket(app); - return; + + EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer; + Entity* entity = entity_list.GetID(eid->entity_id); + + if (entity && entity->IsMob()) { + Mob* new_target = entity->CastToMob(); + if (new_target && (GetGM() || + Distance(m_Position, new_target->GetPosition()) <= TARGETING_RANGE)) { + cheat_manager.SetExemptStatus(Assist, true); + EQApplicationPacket* outapp = new EQApplicationPacket(OP_Assist, sizeof(EntityId_Struct)); + eid = (EntityId_Struct*)outapp->pBuffer; + eid->entity_id = new_target->GetID(); + FastQueuePacket(&outapp); + safe_delete(outapp); + } + } } void Client::Handle_OP_AugmentInfo(const EQApplicationPacket *app) @@ -5850,6 +5868,31 @@ void Client::Handle_OP_DoGroupLeadershipAbility(const EQApplicationPacket *app) break; } + case RaidLeadershipAbility_MainAssist: + { + //This is not needed as it executes from opcode 0x2b33 which is sent + //with this opcode. + //if (GetTarget()) + //{ + // Raid* r = GetRaid(); + // if (r) + // { + // r->DelegateAbility(GetTarget()->CastToClient()->GetName()); + // } + //} + break; + } + case RaidLeadershipAbility_MarkNPC: + { + if (GetTarget() && GetTarget()->IsMob()) { + Raid* r = GetRaid(); + if (r) { + r->RaidMarkNPC(this, dglas->Parameter); + } + } + break; + + } default: LogDebug("Got unhandled OP_DoGroupLeadershipAbility Ability: [{}] Parameter: [{}]", dglas->Ability, dglas->Parameter); break; @@ -11934,6 +11977,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } } group->JoinRaidXTarget(raid); @@ -11948,6 +11993,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(this); } + raid->SendAssistTarget(this); + raid->SendMarkTargets(this); } } else @@ -11988,6 +12035,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } else { Client* c = nullptr; @@ -12004,6 +12053,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } } } @@ -12043,6 +12094,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } else { @@ -12060,6 +12113,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } } } @@ -12100,6 +12155,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } else { @@ -12116,6 +12173,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(c); } + raid->SendAssistTarget(c); + raid->SendMarkTargets(c); } } } @@ -12129,6 +12188,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(this); } + raid->SendAssistTarget(this); + raid->SendMarkTargets(this); } else { // neither has a group raid = new Raid(player_sending_invite); @@ -12143,6 +12204,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (raid->IsLocked()) { raid->SendRaidLockTo(this); } + raid->SendAssistTarget(this); + raid->SendMarkTargets(this); } } } @@ -12165,6 +12228,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) //Does not camp the Bots, just removes from the raid if (c_to_disband) { uint32 i = raid->GetPlayerIndex(raid_command_packet->leader_name); + raid->RemoveRaidDelegates(raid_command_packet->leader_name); + raid->SendRemoveAllRaidXTargets(raid_command_packet->leader_name); raid->SetNewRaidLeader(i); raid->HandleBotGroupDisband(c_to_disband->CharacterID()); raid->HandleOfflineBots(c_to_disband->CharacterID()); @@ -12180,11 +12245,13 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (gid < 12 && (raid->IsGroupLeader(b_to_disband->GetName()) || raid->GroupCount(gid) < 2)) { uint32 owner_id = b_to_disband->CastToBot()->GetOwner()->CastToClient()->CharacterID(); + raid->RemoveRaidDelegates(raid_command_packet->leader_name); + raid->UpdateRaidXTargets(); raid->HandleBotGroupDisband(owner_id, gid); - } else if (b_to_disband && raid->IsRaidMember(b_to_disband->GetName())) { + raid->RemoveRaidDelegates(raid_command_packet->leader_name); + raid->UpdateRaidXTargets(); Bot::RemoveBotFromRaid(b_to_disband); - } else if (gid < 12 && raid->GetGroupLeader(gid) && raid->GetGroupLeader(gid)->IsBot()) { c_doing_disband->Message( Chat::Yellow, @@ -12215,6 +12282,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) } } raid->SetNewRaidLeader(i); + raid->RemoveRaidDelegates(raid_command_packet->leader_name); + raid->UpdateRaidXTargets(); raid->RemoveMember(raid_command_packet->leader_name); Client* c = entity_list.GetClientByName(raid_command_packet->leader_name); if (c) { @@ -12268,9 +12337,9 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) if (client_to_update) { raid->SendRaidRemove(raid->members[x].member_name, client_to_update); raid->SendRaidCreate(client_to_update); - raid->SendMakeLeaderPacketTo(raid->leadername, client_to_update); raid->SendRaidAdd(raid->members[x].member_name, client_to_update); raid->SendBulkRaid(client_to_update); + raid->SendMakeLeaderPacketTo(raid->leadername, client_to_update); if (raid->IsLocked()) { raid->SendRaidLockTo(client_to_update); } @@ -12318,9 +12387,9 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) raid->GroupUpdate(raid_command_packet->parameter); /* If our old was a group send update there too */ - if (old_group < 12) + if (old_group < 12) { raid->GroupUpdate(old_group); - + } } } /* Move player to ungrouped bank */ @@ -12381,7 +12450,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) } raid->GroupUpdate(oldgrp); - } +} } Client* client_moved = entity_list.GetClientByName(raid_command_packet->leader_name); @@ -12479,7 +12548,18 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket* app) raid->SendRaidMOTDToWorld(); break; } + case RaidCommandSetNote: + { + Raid* raid = entity_list.GetRaidByClient(this); + if (!raid) { + break; + } + + raid->SaveRaidNote(raid_command_packet->leader_name, raid_command_packet->note); + raid->SendRaidNotesToWorld(); + break; + } default: { Message(Chat::Red, "Raid command (%d) NYI", raid_command_packet->action); break; @@ -15799,14 +15879,87 @@ void Client::Handle_OP_XTargetRequest(const EQApplicationPacket *app) case RaidAssist1: case RaidAssist2: case RaidAssist3: + { + struct AssistType { + XTargetType type; + int32 assist_slot; + }; + + std::vector assist_types = { + { RaidAssist1, MAIN_ASSIST_1_SLOT }, + { RaidAssist2, MAIN_ASSIST_2_SLOT }, + { RaidAssist3, MAIN_ASSIST_3_SLOT } + }; + + for (auto& t : assist_types) { + if (t.type == Type) { + Raid* r = GetRaid(); + if (r) { + Client* ma = entity_list.GetClientByName(r->main_assister_pcs[t.assist_slot]); + if (ma) { + UpdateXTargetType(t.type, ma, ma->GetName()); + } + } + } + } + break; + } + case RaidAssist1Target: case RaidAssist2Target: case RaidAssist3Target: + { + struct AssistType { + XTargetType type; + int32 assist_slot; + }; + + std::vector assist_types = { + { RaidAssist1Target, MAIN_ASSIST_1_SLOT }, + { RaidAssist2Target, MAIN_ASSIST_2_SLOT }, + { RaidAssist3Target, MAIN_ASSIST_3_SLOT } + }; + + for (auto& t : assist_types) { + if (t.type == Type) { + Raid* r = GetRaid(); + if (r) { + Client* ma = entity_list.GetClientByName(r->main_assister_pcs[t.assist_slot]); + if (ma && ma->GetTarget()) { + UpdateXTargetType(t.type, ma->GetTarget(), ma->GetTarget()->GetName()); + } + } + } + } + break; + } + case RaidMarkTarget1: case RaidMarkTarget2: case RaidMarkTarget3: { - // Not implemented yet. + struct AssistType { + XTargetType type; + int32 assist_slot; + }; + + std::vector assist_types = { + { RaidMarkTarget1, MAIN_MARKER_1_SLOT }, + { RaidMarkTarget2, MAIN_MARKER_2_SLOT }, + { RaidMarkTarget3, MAIN_MARKER_3_SLOT } + }; + + for (auto& t : assist_types) { + if (t.type == Type) { + Raid* r = GetRaid(); + if (r) { + auto mm = entity_list.GetNPCByID(r->marked_npcs[t.assist_slot]); + if (mm) { + UpdateXTargetType(t.type, mm->CastToMob(), mm->CastToMob()->GetName()); + } + } + } + } break; } @@ -16190,3 +16343,52 @@ void Client::RecordKilledNPCEvent(NPC *n) } } } + +void Client::Handle_OP_RaidDelegateAbility(const EQApplicationPacket *app) +{ + if (app->size != sizeof(DelegateAbility_Struct)) { + LogDebug( + "Size mismatch in OP_RaidDelegateAbility expected [{}] got [{}]", + sizeof(DelegateAbility_Struct), + app->size + ); + DumpPacket(app); + return; + } + + DelegateAbility_Struct *das = (DelegateAbility_Struct *) app->pBuffer; + + switch (das->DelegateAbility) { + case RaidDelegateMainAssist: { + auto r = GetRaid(); + if (r) { + r->DelegateAbilityAssist(this, das->Name); + } + break; + } + case RaidDelegateMainMarker: { + auto r = GetRaid(); + if (r) { + r->DelegateAbilityMark(this, das->Name); + } + break; + } + default: + LogDebug("RaidDelegateAbility default case"); + break; + } +} + +void Client::Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app) +{ + if (app->size != 0) { + LogDebug("Size mismatch in OP_RaidClearNPCMark expected [{}] got [{}]", 0, app->size); + DumpPacket(app); + return; + } + + auto r = GetRaid(); + if (r) { + r->RaidClearNPCMarks(this); + } +} diff --git a/zone/client_packet.h b/zone/client_packet.h index 887372cde..f779974ba 100644 --- a/zone/client_packet.h +++ b/zone/client_packet.h @@ -241,7 +241,9 @@ void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app); void Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app); void Handle_OP_RaidCommand(const EQApplicationPacket *app); - void Handle_OP_RandomReq(const EQApplicationPacket *app); + void Handle_OP_RaidDelegateAbility(const EQApplicationPacket* app); + void Handle_OP_RaidClearNPCMarks(const EQApplicationPacket* app); + void Handle_OP_RandomReq(const EQApplicationPacket* app); void Handle_OP_ReadBook(const EQApplicationPacket *app); void Handle_OP_RecipeAutoCombine(const EQApplicationPacket *app); void Handle_OP_RecipeDetails(const EQApplicationPacket *app); diff --git a/zone/entity.cpp b/zone/entity.cpp index 052b0afc7..94582eb79 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2244,6 +2244,18 @@ Raid* EntityList::GetRaidByBot(const Bot* bot) return nullptr; } +Raid* EntityList::GetRaidByName(const char* name) +{ + for (const auto& r : raid_list) { + for (const auto& m : r->members) { + if (Strings::EqualFold(m.member_name, name)) { + return r; + } + } + } + return nullptr; +} + Client *EntityList::GetClientByAccID(uint32 accid) { auto it = client_list.begin(); diff --git a/zone/entity.h b/zone/entity.h index 3bf128fc7..672a4f31f 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -198,6 +198,7 @@ public: Raid *GetRaidByID(uint32 id); Raid* GetRaidByBotName(const char* name); Raid* GetRaidByBot(const Bot* bot); + Raid* GetRaidByName(const char* name); Corpse *GetCorpseByOwner(Client* client); Corpse *GetCorpseByOwnerWithinRange(Client* client, Mob* center, int range); diff --git a/zone/mob.cpp b/zone/mob.cpp index 8aa0a71a7..cde8f1f34 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -5425,6 +5425,14 @@ void Mob::SetTarget(Mob *mob) if (IsClient() && GetTarget()) { GetTarget()->SendHPUpdate(true); } + + if (IsOfClientBot()) { + Raid* r = GetRaid(); + if (r) { + r->UpdateRaidXTargets(); + r->SendRaidAssistTarget(); + } + } } // For when we want a Ground Z at a location we are not at yet diff --git a/zone/raids.cpp b/zone/raids.cpp index 869bb867f..c0385167d 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -18,6 +18,8 @@ #include "../common/strings.h" #include "../common/events/player_event_logs.h" +#include "../common/repositories/raid_details_repository.h" +#include "../common/repositories/raid_members_repository.h" #include "client.h" #include "entity.h" @@ -49,6 +51,12 @@ Raid::Raid(uint32 raidID) LootType = 4; m_autohatermgr.SetOwner(nullptr, nullptr, this); + + for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) { + memset(main_assister_pcs[i], 0, 64); + memset(main_marker_pcs[i], 0, 64); + marked_npcs[i] = 0; + } } Raid::Raid(Client* nLeader) @@ -68,6 +76,12 @@ Raid::Raid(Client* nLeader) LootType = 4; m_autohatermgr.SetOwner(nullptr, nullptr, this); + + for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) { + memset(main_assister_pcs[i], 0, 64); + memset(main_marker_pcs[i], 0, 64); + marked_npcs[i] = 0; + } } Raid::~Raid() @@ -186,6 +200,9 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo rga->instance_id = zone->GetInstanceID(); worldserver.SendPacket(pack); safe_delete(pack); + + SendAssistTarget(c); + } void Raid::AddBot(Bot* b, uint32 group, bool raid_leader, bool group_leader, bool looter) @@ -311,7 +328,7 @@ void Raid::MoveMember(const char *name, uint32 newGroup) LearnMembers(); VerifyRaid(); SendRaidMoveAll(name); - + auto pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct)); auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer; strn0cpy(rga->playername, name, sizeof(rga->playername)); @@ -1133,6 +1150,15 @@ void Raid::SendRaidAdd(const char *who, Client *to) ram->isGroupLeader = m.is_group_leader; to->QueuePacket(outapp); safe_delete(outapp); + + if (IsAssister(m.member_name)) { + SendRaidAssisterTo(m.member_name, to); + } + + if (IsMarker(m.member_name)) { + SendRaidMarkerTo(m.member_name, to); + } + return; } } @@ -1156,6 +1182,15 @@ void Raid::SendRaidAddAll(const char *who) QueuePacket(outapp); safe_delete(outapp); + + if (IsAssister(m.member_name)) { + SendRaidAssister(m.member_name); + } + + if (IsMarker(m.member_name)) { + SendRaidMarker(m.member_name); + } + return; } } @@ -1280,6 +1315,7 @@ void Raid::SendBulkRaid(Client *to) SendRaidAdd(m.member_name, to); } } + SendRaidNotes(); } void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req) @@ -1634,28 +1670,17 @@ void Raid::SetRaidDetails() void Raid::GetRaidDetails() { - std::string query = StringFormat("SELECT locked, loottype, motd FROM raid_details WHERE raidid = %lu", - (unsigned long)GetID()); - auto results = database.QueryDatabase(query); - - if (!results.Success()) { + auto raid_details = RaidDetailsRepository::FindOne(database, GetID()); + if (raid_details.raidid == 0) { return; } - if (results.RowCount() == 0) { - LogError( - "Error getting raid details for raid [{}]: [{}]", - (unsigned long) GetID(), - results.ErrorMessage().c_str() - ); - return; - } - - auto row = results.begin(); - - locked = Strings::ToInt(row[0]); - LootType = Strings::ToInt(row[1]); - motd = std::string(row[2]); + locked = raid_details.locked; + LootType = raid_details.loottype; + motd = raid_details.motd; + marked_npcs[0] = raid_details.marked_npc_1; + marked_npcs[1] = raid_details.marked_npc_2; + marked_npcs[2] = raid_details.marked_npc_3; } void Raid::SaveRaidMOTD() @@ -1672,7 +1697,7 @@ bool Raid::LearnMembers() const auto query = fmt::format( "SELECT name, groupid, _class, level, " - "isgroupleader, israidleader, islooter, bot_id " + "isgroupleader, israidleader, islooter, is_marker, is_assister, bot_id, note " "FROM raid_members WHERE raidid = {} ORDER BY groupid", GetID() ); @@ -1695,6 +1720,7 @@ bool Raid::LearnMembers() members[i].member = nullptr; strn0cpy(members[i].member_name, row[0], sizeof(members[i].member_name)); + strn0cpy(members[i].note, row[10], sizeof(members[i].note)); uint32 group_id = Strings::ToUnsignedInt(row[1]); if (group_id >= MAX_RAID_GROUPS) { @@ -1704,15 +1730,16 @@ bool Raid::LearnMembers() members[i].group_number = group_id; } - members[i]._class = Strings::ToUnsignedInt(row[2]); - members[i].level = Strings::ToUnsignedInt(row[3]); + members[i]._class = Strings::ToUnsignedInt(row[2]); + members[i].level = Strings::ToUnsignedInt(row[3]); members[i].is_group_leader = Strings::ToBool(row[4]); members[i].is_raid_leader = Strings::ToBool(row[5]); - members[i].is_looter = Strings::ToBool(row[6]); - members[i].is_bot = Strings::ToBool(row[7]) > 0; + members[i].is_looter = Strings::ToBool(row[6]); + members[i].main_marker = Strings::ToUnsignedInt(row[7]); + members[i].main_assister = Strings::ToUnsignedInt(row[8]); + members[i].is_bot = Strings::ToBool(row[9]) > 0; ++i; } - return true; } @@ -1741,6 +1768,18 @@ void Raid::VerifyRaid() else { m.member = nullptr; } + + for (int i = 0; i < MAX_NO_RAID_MAIN_MARKERS; i++) { + if (m.main_marker == i + 1) { + strcpy(main_marker_pcs[i], m.member_name); + } + } + + for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) { + if (m.main_assister == i + 1) { + strcpy(main_assister_pcs[i], m.member_name); + } + } } if (m.is_raid_leader) { @@ -2048,7 +2087,7 @@ void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_re if (m.is_bot) { continue; } - + if (m.member->IsClient()) { continue; } @@ -2118,9 +2157,12 @@ bool Raid::DoesAnyMemberHaveExpeditionLockout(const std::string& expedition_name Mob* Raid::GetRaidMainAssistOne() { - for (const auto& m : GetMembers()) { - if (m.is_raid_main_assist_one) { - return m.member->CastToMob(); + for (int i = MAIN_ASSIST_1_SLOT; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) { + if (strlen(main_assister_pcs[i]) > 0) { + auto ma = entity_list.GetMob(main_assister_pcs[i]); + if (ma) { + return ma; + } } } return nullptr; @@ -2203,3 +2245,707 @@ void Raid::SetNewRaidLeader(uint32 i) } } } + +void Raid::SaveRaidNote(std::string who, std::string note) +{ + if (who.empty() || note.empty()) { + return; + } + + auto result = RaidMembersRepository::UpdateRaidNote(database, GetID(), note, who); + if (!result) { + LogError("Unable to update the raid note for player [{}] in guild [{}].", + who, + GetID() + ); + } +} + +std::vector Raid::GetMembersWithNotes() +{ + std::vector raid_members; + for (const auto& m : members) { + if (strlen(m.note) != 0) { + raid_members.emplace_back(m); + } + } + return raid_members; +} + +void Raid::SendRaidNotes() +{ + LearnMembers(); + VerifyRaid(); + + for (const auto& c : GetMembersWithNotes()) { + auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidGeneral_Struct)); + auto note = (RaidGeneral_Struct*)outapp->pBuffer; + note->action = raidSetNote; + strn0cpy(note->leader_name, c.member_name, 64); + strn0cpy(note->player_name, GetLeaderName().c_str(), 64); + strn0cpy(note->note, c.note, 64); + QueuePacket(outapp); + safe_delete(outapp); + } +} +void Raid::SendRaidNotesToWorld() +{ + auto pack = new ServerPacket(ServerOP_RaidNote, sizeof(ServerRaidNote_Struct)); + auto snote = (ServerRaidNote_Struct*)pack->pBuffer; + snote->rid = GetID(); + worldserver.SendPacket(pack); + safe_delete(pack); +} + +void Raid::DelegateAbilityAssist(Mob* delegator, const char* delegatee) +{ + auto raid_delegatee = entity_list.GetRaidByName(delegatee); + if (!raid_delegatee) { + delegator->CastToClient()->MessageString(Chat::Cyan, NOT_IN_YOUR_RAID, delegatee); + return; + } + uint32 raid_delegatee_id = raid_delegatee->GetID(); + uint32 raid_delegator_id = GetID(); + if (raid_delegatee_id != raid_delegator_id) { + delegator->CastToClient()->MessageString(Chat::Cyan, NOT_IN_YOUR_RAID, delegatee); + return; + } + + auto rm = &members[GetPlayerIndex(delegatee)]; + if (!rm) { + return; + } + auto c = rm->member; + if (!c) { + return; + } + + auto slot = FindNextRaidDelegateSlot(FindNextAssisterSlot); + auto ma = rm->main_assister; + if (slot == -1 && !ma) { + delegator->CastToClient()->MessageString(Chat::Cyan, MAX_MAIN_RAID_ASSISTERS); + return; + } + + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + if (ma) { + das->Action = ClearDelegate; + memset(main_assister_pcs[ma - 1], 0, 64); + rm->main_assister = DELEGATE_OFF; + auto result = RaidMembersRepository::UpdateRaidAssister( + database, + GetID(), + delegatee, + DELEGATE_OFF + ); + if (!result) { + LogError("Unable to clear raid main assister for player: [{}].", + delegatee + ); + } + } + else { + if (slot >= MAIN_ASSIST_1_SLOT) { + strcpy(main_assister_pcs[slot], delegatee); + rm->main_assister = slot + 1; + das->Action = SetDelegate; + auto result = RaidMembersRepository::UpdateRaidAssister( + database, + GetID(), + delegatee, + slot + 1 + ); + if (!result) { + LogError("Unable to set raid main assister for player: [{}] to [{}].", + delegatee, + slot + 1 + ); + } + } + } + das->DelegateAbility = RaidDelegateMainAssist; + das->MemberNumber = slot + 1; + das->EntityID = c->GetID(); + strcpy(das->Name, delegatee); + QueuePacket(outapp); + safe_delete(outapp); + UpdateRaidXTargets(); +} + +void Raid::UpdateRaidXTargets() +{ + struct AssistUpdate { + XTargetType assist_type; + XTargetType assist_target_type; + int32 slot; + }; + + std::vector assist_updates = { + AssistUpdate{.assist_type = RaidAssist1, .assist_target_type = RaidAssist1Target, .slot = MAIN_ASSIST_1_SLOT}, + AssistUpdate{.assist_type = RaidAssist2, .assist_target_type = RaidAssist2Target, .slot = MAIN_ASSIST_2_SLOT}, + AssistUpdate{.assist_type = RaidAssist3, .assist_target_type = RaidAssist3Target, .slot = MAIN_ASSIST_3_SLOT}, + }; + + for (const auto& u : assist_updates) { + if (strlen(main_assister_pcs[u.slot]) > 0) { + auto m = entity_list.GetMob(main_assister_pcs[u.slot]); + if (m) { + UpdateXTargetType(u.assist_type, m, m->GetName()); + auto n = m->GetTarget(); + if (n && n->GetHP() > 0) { + UpdateXTargetType(u.assist_target_type, n, n->GetName()); + } + else { + UpdateXTargetType(u.assist_target_type, nullptr); + } + } + } + else { + UpdateXTargetType(u.assist_type, nullptr); + UpdateXTargetType(u.assist_target_type, nullptr); + } + } + + struct MarkedUpdate { + XTargetType mark_target; + int32 slot; + }; + + std::vector marked_updates = { + MarkedUpdate{.mark_target = RaidMarkTarget1, .slot = MAIN_MARKER_1_SLOT}, + MarkedUpdate{.mark_target = RaidMarkTarget2, .slot = MAIN_MARKER_2_SLOT}, + MarkedUpdate{.mark_target = RaidMarkTarget3, .slot = MAIN_MARKER_3_SLOT}, + }; + + for (auto& u : marked_updates) { + if (marked_npcs[u.slot]) { + auto m = entity_list.GetMob(marked_npcs[u.slot]); + if (m && m->GetHP() > 0) { + UpdateXTargetType(u.mark_target, m, m->GetName()); + } + else { + UpdateXTargetType(u.mark_target, nullptr); + } + } + else { + UpdateXTargetType(u.mark_target, nullptr); + } + } +} + +void Raid::DelegateAbilityMark(Mob* delegator, const char* delegatee) +{ + auto raid_delegatee = entity_list.GetRaidByName(delegatee); + if (!raid_delegatee) { + delegator->CastToClient()->MessageString(Chat::Cyan, NOT_IN_YOUR_RAID, delegatee); + return; + } + uint32 raid_delegatee_id = raid_delegatee->GetID(); + uint32 raid_delegator_id = GetID(); + if (raid_delegatee_id != raid_delegator_id) { + delegator->CastToClient()->MessageString(Chat::Cyan, NOT_IN_YOUR_RAID, delegatee); + return; + } + + auto rm = &members[GetPlayerIndex(delegatee)]; + if (!rm) { + return; + } + auto c = rm->member; + if (!c) { + return; + } + + auto slot = FindNextRaidDelegateSlot(FindNextMarkerSlot); + auto mm = rm->main_marker; + + if (slot == -1 && !mm) { + delegator->CastToClient()->MessageString(Chat::Cyan, MAX_MAIN_RAID_MARKERS); + return; + } + + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + if (mm) { + das->Action = ClearDelegate; + memset(main_marker_pcs[mm - 1], 0, 64); + rm->main_marker = DELEGATE_OFF; + auto result = RaidMembersRepository::UpdateRaidMarker( + database, + GetID(), + delegatee, + DELEGATE_OFF + ); + if (!result) { + LogError("Unable to clear rain main marker for player: [{}].", delegatee); + } + } + else { + if (slot >= 0) { + strcpy(main_marker_pcs[slot], c->GetName()); + rm->main_marker = slot + 1; + das->Action = SetDelegate; + auto result = RaidMembersRepository::UpdateRaidMarker( + database, + GetID(), + delegatee, + slot + 1 + ); + if (!result) { + LogError("Unable to set raid main marker for player: [{}] to [{}].", delegatee, slot + 1); + } + } + } + das->DelegateAbility = RaidDelegateMainMarker; + das->MemberNumber = 0; + das->EntityID = c->GetID(); + strcpy(das->Name, delegatee); + QueuePacket(outapp); + safe_delete(outapp); +} + +int Raid::FindNextRaidDelegateSlot(int option) +{ + if (option == FindNextRaidMainMarkerSlot) { + for (int i = 0; i < MAX_NO_RAID_MAIN_MARKERS; i++) { + if (strlen(main_marker_pcs[i]) == 0) { + return i; + } + } + } + else if (option == FindNextRaidMainAssisterSlot) { + for (int i = 0; i < MAX_NO_RAID_MAIN_ASSISTERS; i++) { + if (strlen(main_assister_pcs[i]) == 0) { + return i; + } + } + } + + return -1; +} + +void Raid::UpdateXTargetType(XTargetType Type, Mob *m, const char *name) +{ + for (const auto &rm: members) { + if (!rm.member || rm.is_bot || !rm.member->XTargettingAvailable()) { + continue; + } + + for (int i = 0; i < rm.member->GetMaxXTargets(); ++i) { + if (rm.member->XTargets[i].Type == Type) { + if (m) { + rm.member->XTargets[i].ID = m->GetID(); + } + else { + rm.member->XTargets[i].ID = 0; + } + + if (name) { + strncpy(rm.member->XTargets[i].Name, name, 64); + } + + rm.member->SendXTargetPacket(i, m); + } + } + } +} + +void Raid::RaidMarkNPC(Mob* mob, uint32 parameter) +{ + Client* c = mob->CastToClient(); + if (!c || !c->GetTarget() || parameter < 1 || parameter > 3) { + LogDebug("RaidMarkNPC Failed sanity checks."); + return; + } + + for (int i = 0; i < MAX_NO_RAID_MAIN_MARKERS; i++) { + auto cname = c->GetCleanName(); + if (strcasecmp(main_marker_pcs[i], cname) == 0 || strcasecmp(leadername, cname) == 0) { + marked_npcs[parameter - 1] = c->GetTarget()->GetID(); + auto result = RaidDetailsRepository::UpdateRaidMarkedNPC( + database, + GetID(), + parameter, + marked_npcs[parameter - 1] + ); + if (!result) { + LogError("Unable to set MarkedNPC{} from slot: [{}] for guild [{}].", + parameter, + parameter - 1, + GetID() + ); + } + + auto outapp = new EQApplicationPacket(OP_MarkRaidNPC, sizeof(MarkNPC_Struct)); + MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer; + mnpcs->TargetID = marked_npcs[parameter - 1]; + mnpcs->Number = parameter; + strcpy(mnpcs->Name, c->GetTarget()->GetCleanName()); + QueuePacket(outapp); + safe_delete(outapp); + UpdateXtargetMarkedNPC(); + return; + } + } + //client is not delegated the mark ability + c->MessageString(Chat::Cyan, NOT_DELEGATED_MARKER); + return; +} + +void Raid::UpdateXtargetMarkedNPC() +{ + for (int i = 0; i < MAX_MARKED_NPCS; i++) { + auto mm = entity_list.GetNPCByID(marked_npcs[i]); + if (mm) { + UpdateXTargetType(static_cast(RaidMarkTarget1 + i), mm->CastToMob(), mm->CastToMob()->GetName()); + } + else { + UpdateXTargetType(static_cast(RaidMarkTarget1 + i), nullptr); + } + } +} + +void Raid::RaidClearNPCMarks(Client* c) +{ + auto mob_id = c->GetID(); + + if (Strings::EqualFold(main_marker_pcs[MAIN_MARKER_1_SLOT], c->GetCleanName()) || + Strings::EqualFold(main_marker_pcs[MAIN_MARKER_2_SLOT], c->GetCleanName()) || + Strings::EqualFold(main_marker_pcs[MAIN_MARKER_3_SLOT], c->GetCleanName())) { + for (int i = 0; i < MAX_MARKED_NPCS; i++) { + if (marked_npcs[i]) { + auto npc_name = entity_list.GetNPCByID(marked_npcs[i])->GetCleanName(); + RaidMessageString(nullptr, Chat::Cyan, RAID_NO_LONGER_MARKED, npc_name); + } + marked_npcs[i] = 0; + auto result = RaidDetailsRepository::UpdateRaidMarkedNPC( + database, + GetID(), + i + 1, + 0 + ); + if (!result) { + LogError("Unable to clear MarkedNPC{} from slot: [{}] for guild [{}].", i + 1, i, GetID()); + } + } + + auto outapp = new EQApplicationPacket(OP_RaidClearNPCMarks, sizeof(MarkNPC_Struct)); + MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer; + mnpcs->TargetID = 0; + mnpcs->Number = 0; + QueuePacket(outapp); + safe_delete(outapp); + UpdateXtargetMarkedNPC(); + } + else { + c->MessageString(Chat::Cyan, NOT_DELEGATED_MARKER); + } +} + +void Raid::RemoveRaidDelegates(const char* delegatee) +{ + auto ma = members[GetPlayerIndex(delegatee)].main_assister; + auto mm = members[GetPlayerIndex(delegatee)].main_marker; + + if (ma) { + SendRemoveRaidXTargets(static_cast(RaidAssist1 + ma - 1)); + SendRemoveRaidXTargets(static_cast(RaidAssist1Target + ma - 1)); + DelegateAbilityAssist(leader->CastToMob(), delegatee); + } + + if (mm) { + SendRemoveRaidXTargets(static_cast(RaidMarkTarget1 + mm - 1)); + DelegateAbilityMark(leader->CastToMob(), delegatee); + } +} + +void Raid::SendRemoveAllRaidXTargets(const char* client_name) +{ + + auto c = entity_list.GetClientByName(client_name); + + for (int i = 0; i < c->GetMaxXTargets(); ++i) + { + if ((c->XTargets[i].Type == RaidAssist1) || + (c->XTargets[i].Type == RaidAssist2) || + (c->XTargets[i].Type == RaidAssist3) || + (c->XTargets[i].Type == RaidAssist1Target) || + (c->XTargets[i].Type == RaidAssist2Target) || + (c->XTargets[i].Type == RaidAssist3Target) || + (c->XTargets[i].Type == RaidMarkTarget1) || + (c->XTargets[i].Type == RaidMarkTarget2) || + (c->XTargets[i].Type == RaidMarkTarget3)) + { + c->XTargets[i].ID = 0; + c->XTargets[i].Name[0] = 0; + c->SendXTargetPacket(i, nullptr); + } + } +} + +void Raid::SendRemoveRaidXTargets(XTargetType Type) +{ + for (const auto &m: members) { + if (m.member && !m.is_bot) { + for (int i = 0; i < m.member->GetMaxXTargets(); ++i) { + if (m.member->XTargets[i].Type == Type) { + m.member->XTargets[i].ID = 0; + m.member->XTargets[i].Name[0] = 0; + m.member->SendXTargetPacket(i, nullptr); + } + } + } + } +} + +void Raid::SendRemoveAllRaidXTargets() +{ + for (const auto &m: members) { + if (m.member && !m.is_bot) { + for (int i = 0; i < m.member->GetMaxXTargets(); ++i) { + if ((m.member->XTargets[i].Type == RaidAssist1) || + (m.member->XTargets[i].Type == RaidAssist2) || + (m.member->XTargets[i].Type == RaidAssist3) || + (m.member->XTargets[i].Type == RaidAssist1Target) || + (m.member->XTargets[i].Type == RaidAssist2Target) || + (m.member->XTargets[i].Type == RaidAssist3Target) || + (m.member->XTargets[i].Type == RaidMarkTarget1) || + (m.member->XTargets[i].Type == RaidMarkTarget2) || + (m.member->XTargets[i].Type == RaidMarkTarget3)) { + m.member->XTargets[i].ID = 0; + m.member->XTargets[i].Name[0] = 0; + m.member->SendXTargetPacket(i, nullptr); + } + } + } + } +} + +// Send a packet to the entire raid notifying them of the group target selected by the Main Assist. +void Raid::SendRaidAssistTarget() +{ + uint16 assist_target_id = 0; + uint16 number = 0; + Mob* target = nullptr; + + struct AssistTypes { + MainAssistType main_assist_type_slot; + MainAssistType main_assist_number; + }; + + std::vector assist_types = { + {.main_assist_type_slot = MAIN_ASSIST_1_SLOT, .main_assist_number = MAIN_ASSIST_1}, + {.main_assist_type_slot = MAIN_ASSIST_2_SLOT, .main_assist_number = MAIN_ASSIST_2}, + {.main_assist_type_slot = MAIN_ASSIST_3_SLOT, .main_assist_number = MAIN_ASSIST_3} + }; + + for (auto &a: assist_types) { + if (strlen(main_assister_pcs[a.main_assist_type_slot]) > 0) { + auto player = entity_list.GetMob(main_assister_pcs[a.main_assist_type_slot]); + if (player) { + target = player->GetTarget(); + if (target) { + assist_target_id = target->GetID(); + number = a.main_assist_number; + break; + } + } + } + } + + if (assist_target_id) { + auto outapp = new EQApplicationPacket(OP_SetGroupTarget, sizeof(MarkNPC_Struct)); + MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer; + mnpcs->TargetID = assist_target_id; + mnpcs->Number = number; + + for (const auto& m : members) { + if (m.member && !m.is_bot) { + m.member->QueuePacket(outapp); + } + } + safe_delete(outapp); + } +} + +void Raid::SendAssistTarget(Client *c) +{ + if (!c || c->IsBot()) { + return; + } + + uint16 assist_target_id = 0; + uint16 number = 0; + Mob *target = nullptr; + + struct AssistTypes { + MainAssistType main_assist_type_slot; + MainAssistType main_assist_number; + }; + + std::vector assist_types = { + {.main_assist_type_slot = MAIN_ASSIST_1_SLOT, .main_assist_number = MAIN_ASSIST_1}, + {.main_assist_type_slot = MAIN_ASSIST_2_SLOT, .main_assist_number = MAIN_ASSIST_2}, + {.main_assist_type_slot = MAIN_ASSIST_3_SLOT, .main_assist_number = MAIN_ASSIST_3} + }; + + for (auto &a: assist_types) { + if (strlen(main_assister_pcs[a.main_assist_type_slot]) > 0) { + auto player = entity_list.GetMob(main_assister_pcs[a.main_assist_type_slot]); + if (player) { + target = player->GetTarget(); + if (target) { + assist_target_id = target->GetID(); + number = a.main_assist_number; + break; + } + } + } + } + + if (assist_target_id) { + auto outapp = new EQApplicationPacket(OP_SetGroupTarget, sizeof(MarkNPC_Struct)); + MarkNPC_Struct *mnpcs = (MarkNPC_Struct *) outapp->pBuffer; + mnpcs->TargetID = assist_target_id; + mnpcs->Number = number; + c->QueuePacket(outapp); + safe_delete(outapp); + } +} + +bool Raid::IsAssister(const char* who) +{ + for (auto & main_assister_pc : main_assister_pcs) { + if (strcasecmp(main_assister_pc, who) == 0) { + return true; + } + } + + return false; +} + +void Raid::SendRaidAssisterTo(const char* assister, Client* to) +{ + if (strlen(assister) == 0 || !to || to->IsBot()) { + return; + } + + auto mob = entity_list.GetMob(assister); + if (mob) { + auto m_id = mob->GetID(); + if (m_id) { + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + das->Action = SetDelegate; + das->DelegateAbility = RaidDelegateMainAssist; + das->MemberNumber = 0; + das->EntityID = m_id; + strcpy(das->Name, assister); + to->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void Raid::SendRaidAssister(const char* assister) +{ + if (strlen(assister) == 0) { + return; + } + + auto mob = entity_list.GetMob(assister); + + if (mob) { + auto m_id = mob->GetID(); + if (m_id) { + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + das->Action = SetDelegate; + das->DelegateAbility = RaidDelegateMainAssist; + das->MemberNumber = 0; + das->EntityID = m_id; + strcpy(das->Name, assister); + QueuePacket(outapp); + safe_delete(outapp); + } + } +} +bool Raid::IsMarker(const char* who) +{ + for (int i = 0; i < MAX_NO_RAID_MAIN_MARKERS; i++) { + if (Strings::EqualFold(main_marker_pcs[i], who)) { + return 1; + } + } + return 0; +} + +void Raid::SendRaidMarkerTo(const char* marker, Client* to) +{ + if (strlen(marker) == 0 || !to || to->IsBot()) { + return; + } + + auto mob = entity_list.GetMob(marker); + if (mob) { + auto m_id = mob->GetID(); + if (m_id) { + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + das->Action = SetDelegate; + das->DelegateAbility = RaidDelegateMainMarker; + das->MemberNumber = 0; + das->EntityID = m_id; + strcpy(das->Name, marker); + to->QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void Raid::SendRaidMarker(const char* marker) +{ + if (strlen(marker) == 0) { + return; + } + + auto mob = entity_list.GetMob(marker); + if (mob) { + auto m_id = mob->GetID(); + if (m_id) { + auto outapp = new EQApplicationPacket(OP_RaidDelegateAbility, sizeof(DelegateAbility_Struct)); + DelegateAbility_Struct* das = (DelegateAbility_Struct*)outapp->pBuffer; + das->Action = SetDelegate; + das->DelegateAbility = RaidDelegateMainMarker; + das->MemberNumber = 0; + das->EntityID = m_id; + strcpy(das->Name, marker); + QueuePacket(outapp); + safe_delete(outapp); + } + } +} + +void Raid::SendMarkTargets(Client* c) +{ + if (!c || c->IsBot()) { + return; + } + + for (int i = 0; i < MAX_MARKED_NPCS; i++) { + if (marked_npcs[i] > 0) { + auto marked_mob = entity_list.GetMob(marked_npcs[i]); + if (marked_mob) { + auto outapp = new EQApplicationPacket(OP_MarkRaidNPC, sizeof(MarkNPC_Struct)); + MarkNPC_Struct* mnpcs = (MarkNPC_Struct*)outapp->pBuffer; + mnpcs->TargetID = marked_npcs[i]; + mnpcs->Number = i + 1; + strcpy(mnpcs->Name, marked_mob->GetCleanName()); + QueuePacket(outapp); + safe_delete(outapp); + } + } + } + UpdateXtargetMarkedNPC(); +} diff --git a/zone/raids.h b/zone/raids.h index 5b73ce063..551b1fcaa 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -21,6 +21,7 @@ #include "../common/types.h" #include "groups.h" #include "xtargetautohaters.h" +#include "client.h" class Client; class EQApplicationPacket; @@ -75,9 +76,46 @@ enum { //raid command types RaidCommandSetNote = 36, }; +enum { + FindNextMarkerSlot = 1, + FindNextAssisterSlot = 2, + RaidDelegateMainAssist = 3, + RaidDelegateMainMarker = 4 +}; + +typedef enum { + MAIN_ASSIST_1_SLOT = 0, + MAIN_ASSIST_2_SLOT = 1, + MAIN_ASSIST_3_SLOT = 2, + MAIN_ASSIST_1 = 1, + MAIN_ASSIST_2 = 2, + MAIN_ASSIST_3 = 3, +} MainAssistType; + +typedef enum { + MAIN_MARKER_1_SLOT = 0, + MAIN_MARKER_2_SLOT = 1, + MAIN_MARKER_3_SLOT = 2, + MAIN_MARKER_1 = 1, + MAIN_MARKER_2 = 2, + MAIN_MARKER_3 = 3, +} MainMarkerType; + +enum { + ClearDelegate = 1, + SetDelegate = 0, + FindNextRaidMainMarkerSlot = 1, + FindNextRaidMainAssisterSlot = 2, + DELEGATE_OFF = 0, + DELEGATE_ON = 1 +}; + + constexpr uint8_t MAX_RAID_GROUPS = 12; constexpr uint8_t MAX_RAID_MEMBERS = 72; const uint32 RAID_GROUPLESS = 0xFFFFFFFF; +#define MAX_NO_RAID_MAIN_ASSISTERS 3 +#define MAX_NO_RAID_MAIN_MARKERS 3 struct RaidMember{ char member_name[64]; @@ -85,9 +123,12 @@ struct RaidMember{ uint32 group_number; uint8 _class; uint8 level; + char note[64]; bool is_group_leader; bool is_raid_leader; bool is_looter; + uint8 main_marker; + uint8 main_assister; bool is_bot = false; bool is_raid_main_assist_one = false; }; @@ -131,6 +172,8 @@ public: bool IsRaidMember(Client *c); void UpdateLevel(const char *name, int newLevel); void SetNewRaidLeader(uint32 i); + bool IsAssister(const char* who); + bool IsMarker(const char* who); uint32 GetFreeGroup(); uint8 GroupCount(uint32 gid); @@ -188,6 +231,17 @@ public: void SendEndurancePacketFrom(Mob *mob); void RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill); void RaidGroupSay(const char *msg, Client *c, uint8 language, uint8 lang_skill); + void SaveRaidNote(std::string who, std::string note); + std::vector GetMembersWithNotes(); + void DelegateAbilityAssist(Mob* mob, const char* who); + void DelegateAbilityMark(Mob* mob, const char* who); + void RaidMarkNPC(Mob* mob, uint32 parameter); + void UpdateXTargetType(XTargetType Type, Mob* m, const char* name = (const char*)nullptr); + int FindNextRaidDelegateSlot(int option); + void UpdateXtargetMarkedNPC(); + void RaidClearNPCMarks(Client* c); + void RemoveRaidDelegates(const char* delegatee); + void UpdateRaidXTargets(); //Packet Functions void SendRaidCreate(Client *to); @@ -200,7 +254,13 @@ public: void SendRaidMove(const char* who, Client *to); void SendRaidMoveAll(const char* who); void SendBulkRaid(Client *to); - + void SendRaidNotes(); + void SendRaidNotesToWorld(); + void SendRemoveRaidXTargets(XTargetType Type); + void SendRemoveAllRaidXTargets(); + void SendRemoveAllRaidXTargets(const char* client_name); + void SendRaidAssistTarget(); + void SendAssistTarget(Client* c); void GroupUpdate(uint32 gid, bool initial = true); void SendGroupUpdate(Client *to); void SendGroupDisband(Client *to); @@ -218,6 +278,11 @@ public: void SendRaidMOTD(Client *c); void SendRaidMOTD(); void SendRaidMOTDToWorld(); + void SendRaidAssisterTo(const char* assister, Client* to); + void SendRaidAssister(const char* assister); + void SendRaidMarkerTo(const char* marker, Client* to); + void SendRaidMarker(const char* marker); + void SendMarkTargets(Client* c); void QueuePacket(const EQApplicationPacket *app, bool ack_req = true); @@ -259,6 +324,9 @@ public: RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; + char main_assister_pcs[MAX_NO_RAID_MAIN_ASSISTERS][64]; + char main_marker_pcs[MAX_NO_RAID_MAIN_MARKERS][64]; + uint32 marked_npcs[MAX_MARKED_NPCS]; protected: Client *leader; bool locked; diff --git a/zone/string_ids.h b/zone/string_ids.h index 208fc030b..1ab51ab40 100644 --- a/zone/string_ids.h +++ b/zone/string_ids.h @@ -368,6 +368,7 @@ #define PETITION_DELETED 5054 //Your petition was successfully deleted. #define ALREADY_IN_RAID 5060 //%1 is already in a raid. #define ALREADY_IN_YOUR_RAID 5077 //%1 is already in your raid. +#define NOT_IN_YOUR_RAID 5082 //%1 is not in your raid. #define GAIN_RAIDEXP 5085 //You gained raid experience! #define DUNGEON_SEALED 5141 //The gateway to the dungeon is sealed off to you. Perhaps you would be able to enter if you needed to adventure there. #define ADVENTURE_COMPLETE 5147 //You received %1 points for successfully completing the adventure. @@ -433,9 +434,13 @@ #define LEADERSHIP_EXP_ON 8653 // #define LEADERSHIP_EXP_OFF 8654 // #define CURRENT_SPELL_EFFECTS 8757 //%1's current spell effects: +#define MAX_MAIN_RAID_ASSISTERS 8782 //Max number of main assists reached (3) +#define MAX_MAIN_RAID_MARKERS 8783 //Max number of main markers reached (3) +#define NOT_DELEGATED_MARKER 8794 //You have not been delegated Raid Mark #define GAIN_GROUP_LEADERSHIP_EXP 8788 // #define GAIN_RAID_LEADERSHIP_EXP 8789 // #define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining) +#define RAID_NO_LONGER_MARKED 8801 //%1 is no longer marked #define YOU_HAVE_BEEN_GIVEN 8994 //You have been given: %1 #define NO_MORE_TRAPS 9002 //You have already placed your maximum number of traps. #define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell. diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 09ab02a74..8af0eb378 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -1548,6 +1548,16 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p) r->SendRaidMOTD(); break; } + case ServerOP_RaidNote: { + auto snote = (ServerRaidNote_Struct*)pack->pBuffer; + if (snote->rid > 0) { + Raid* r = entity_list.GetRaidByID(snote->rid); + if (r) { + r->SendRaidNotes(); + } + } + break; + } case ServerOP_SpawnPlayerCorpse: { SpawnPlayerCorpse_Struct* s = (SpawnPlayerCorpse_Struct*)pack->pBuffer; Corpse* NewCorpse = database.LoadCharacterCorpse(s->player_corpse_id); diff --git a/zone/zonedb.cpp b/zone/zonedb.cpp index f9cb3a310..30becb61b 100755 --- a/zone/zonedb.cpp +++ b/zone/zonedb.cpp @@ -687,7 +687,7 @@ bool ZoneDatabase::LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp->ldon_points_ruj = Strings::ToInt(row[r]); r++; // "ldon_points_ruj, " pp->ldon_points_tak = Strings::ToInt(row[r]); r++; // "ldon_points_tak, " pp->ldon_points_available = Strings::ToInt(row[r]); r++; // "ldon_points_available, " - pp->tribute_time_remaining = Strings::ToInt(row[r]); r++; // "tribute_time_remaining, " + pp->tribute_time_remaining = Strings::ToUnsignedInt(row[r]); r++; // "tribute_time_remaining, " pp->showhelm = Strings::ToInt(row[r]); r++; // "show_helm, " pp->career_tribute_points = Strings::ToInt(row[r]); r++; // "career_tribute_points, " pp->tribute_points = Strings::ToInt(row[r]); r++; // "tribute_points, "