[Bots] Add Basic Bot Raiding Functionality (#2782)

* Fix for GENERIC_9_STRINGS

* Add Bot Heal Message Display

Creates a new rule to display Bot heal messages to the Bot Owner

* 2021-03-25 11L04pm

Spell and Heal Rule added to allow for Bot spell and heal damage to be sent to the Bot Owner's Group.  Also added a check to remove duplicate message for #damage on self.

* Update .gitignore

* BOT work

Added BOT logging damage/heals to owner
Added BOT message to owner for harmony fails
Made var Critical global to remove duplicate crit messages
Added a NULL check to Mob:GetCleanname()

* Bot Group Work

Fixed botid=charid spawn on zone issue
Added a group_list update on zone to refresh from database to fix a dangling pointer to a Bot object that was camped but was previously in a group within the zone being entered.
Modified Bot::ProcessBotGroupInvite to use the client of the bot when doing the Bot initialization so that a leader can invite another owner's Bot

* Jan 4

Basic structure in place for Raid::AddBot though not working

* Basement Jan 5

* End of day Jan 5
Working Raid Invite to a Bot.

* Update to Client::QueuePacket to not attempt to send a packet to a BoT.  Not clean, but a broad solution.

* Updated Raid::VerifyRaid

* Some Bot Raid working

* Before VS Crash

* Use Case 1, 2, 3,4,7 working.
Need to fix 5, 6, 8

* Work on usecase 5

* A few more use cases working

* New work on Raid invite with a invitor having a group

* Bot Raid inviting working for all use cases

* A few changes

* end of day jan 10

* Jan 11

* end of day Jan 11

* Bot Invite/Accept cleanup

* Start of moving raid bot functions to their own methods

* More bot raid changes

* More raid spell work

* end of day Jan 16

* spawn work

* Spawn on login working

* End of Day Jan 18

* Raid leader and mana/hp updates fixed

* Spell Tracking

* Issue with Bot Death in raid when casted upon.  1741 raid.cpp

* Bot Death fixed and few other crashes

* Working on botgroup removal

* Bot Disbanding Work 90%

* Looks like BOTs are working

* Fixed a bot crash

* bug tracing on entity list mismatch

* safe_delete resoves problem.  No to track down leak

* seems to be working

* Memory corruption found - sending packets to BoTs using Client class

* added Raid::IsRaidMemberBot()

* Update p_raid_instance

* g3

* Final - Bot Raid Working

* Fixed IsRaidMemberBot to remove memory leak
Fixed altcombat crash though RaidMainAssist (428) needs fixing

* add RaidMember.IsBot

* Repaired IsBot function to be more preformant.  Now works on standard performance machine

* Fixed Bard AE Target Spells
Removed assert for buffs

* updated based on Feb 2022 master updates

* Added bot_db_updates and version increment

* Cleanup of bot raid work and inclusion of bot_raid in cmake

* Delete .gitignore

* Revert "Delete .gitignore"

This reverts commit 8523658d3b.

* Fixed a packet issue

* Merged upstream/master

Merged upstream/master and removed ifdef BOTS as per recent dev approach for BOTS.  Functionality is there, compiles and tests ok.  A few problems to be resolved though this is a good baseline.

* Added sql update for raid_members to add isbot

* Updated Bot Follow Function

Bot will now follow the Group Leader if IsClient, otherwise follows the Bot Owner

* Updates to Bot Raid System

When camping a client, remove them from the raid.  If they are leader, place leadership to the next client.
Update a few crash checks in bot_raid.cpp

* [BOTS] Added RuleB Enabled checks and updated base repo for raid_members

Updated several RuleB(Bots, Enabled) checks
Updated the base repo to be autogenerated.
Raid functionality should work with a non-bots enabled database.

* Few quick updates

* Updates

Corrected a number of comments.  Compiled and tested against bot and non-bot database though requires the isbot column in raid_members for both.
Moved the db update out of the bot stream to make bot check code easier.

* Formatting and other small updates

* A few more RuleB(Bots, Enabled) additions

* Fix issue with conflict of bot ID versus character ID.

* Delete CMakeSettings.json

* Comment Updates and other

Several updates including
- fixed comments from PR
- added id to raid_members and unique index on name to avoid botid and charid conflicts
- updated a few raid functions for iterators
- reordered several raid operations to ensure send leader packet to be the last item to ensure proper updating on the client
- update sql to use Replace instead of Insert for botid conflicting with charid

* Exploit fix for Raid Bots

Added item from @Nite to disallow spawning or camping bots if Raid is engaged.  Avoids abusive situations.

* Initial Commit

* fix Raid Window after zoning

The raid window was not fully updating for clients not in the zone.

* Cleanup

* Update

Fixed comments

* Resolve crash for MOTD

Fixed a crash situation sending a raid MOTD to BOTS.

* Update ruletypes.h

* Updated to resolve a few recent comments

Fixed some comments within attack.cpp

* fix sql query

* update repository

* prevent duplicate entries in raid after group invite, and cleanup

* fixes for botgroups not following, and add already in raid messages.

* fix messagestring

* fixes

* Cleanup, and resolving issues with disbanding

* refactoring

* more cleanup/fixing.

* fixes for removing from ground in raid

* Refactoring/fixing multiple clients

* fix for compiling

* Bugs from refactoring fixed

* Testing completed, cleaning up unwanted items/duplicate code.

* Cleaned up AICastSpell

* fix typos

* Refactoring

* Adding Raid checks to AI_Process/cleanup

* Fix a typo

Was getting a SQL error on BOT spawn.  Fixed typo.

* fix for crash

* Fixed crash when inviting player, more refactoring

* AI_Process Refactoring work

* More Refactoring/fixes for follow

* Finish Refactoring AI_Process

* cleanup

* cleanup

* cleanup

* cleanup

* fix melee attack loop

* fix for leashowner.

* fix for leashowner.

* Bots persist in raid after client death/LD/Camp

* Fix Bot Groups when zoning after death.

* Fix Bots in group following after client death

* remove unnecessary query

* Allow Raid members to invite Bots if owner is in raid. cleanup

* optimization of raid verification

* remove this

* Code Cleanup

* formatting

* formatting

* formatting

* fix for macro

* add return for TryClassAttacks

* fix query

* fix for crash

* restrict camping/spawn in combat.

* Fix other crash issue.

* update learnmembers to use Strings::To, cleanup magic numbers

* fix for merge.

---------

Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
Co-authored-by: Alex King <89047260+Kinglykrab@users.noreply.github.com>
Co-authored-by: Aeadoin <109764533+Aeadoin@users.noreply.github.com>
This commit is contained in:
Mitch Freeman
2023-03-17 12:19:59 -03:00
committed by GitHub
parent e778041198
commit 45da8cab61
35 changed files with 5429 additions and 5050 deletions
+375 -153
View File
@@ -26,6 +26,7 @@
#include "mob.h"
#include "raids.h"
#include "string_ids.h"
#include "bot.h"
#include "worldserver.h"
@@ -94,41 +95,57 @@ bool Raid::Process()
}
void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bool looter){
if(!c)
if (!c) {
return;
}
std::string query = StringFormat("INSERT INTO raid_members SET raidid = %lu, charid = %lu, "
"groupid = %lu, _class = %d, level = %d, name = '%s', "
"isgroupleader = %d, israidleader = %d, islooter = %d",
(unsigned long)GetID(), (unsigned long)c->CharacterID(),
(unsigned long)group, c->GetClass(), c->GetLevel(),
c->GetName(), groupleader, rleader, looter);
auto results = database.QueryDatabase(query);
const auto query = fmt::format(
"REPLACE INTO raid_members SET raidid = {}, charid = {}, bot_id = 0, "
"groupid = {}, _class = {}, level = {}, name = '{}', "
"isgroupleader = {}, israidleader = {}, islooter = {}",
GetID(),
c->CharacterID(),
group,
c->GetClass(),
c->GetLevel(),
c->GetName(),
groupleader,
rleader,
looter
);
auto results = database.QueryDatabase(query);
if(!results.Success()) {
LogError("Error inserting into raid members: [{}]", results.ErrorMessage().c_str());
}
LearnMembers();
VerifyRaid();
if (rleader) {
database.SetRaidGroupLeaderInfo(group, GetID());
UpdateRaidAAs();
} else if (rleader) {
database.SetRaidGroupLeaderInfo(RAID_GROUPLESS, GetID());
UpdateRaidAAs();
}
if (group != RAID_GROUPLESS && groupleader) {
database.SetRaidGroupLeaderInfo(group, GetID());
UpdateGroupAAs(group);
}
if(group < 12)
if (group < MAX_RAID_GROUPS) {
GroupUpdate(group);
else // get raid AAs, GroupUpdate will handles it otherwise
} else { // get raid AAs, GroupUpdate will handles it otherwise
SendGroupLeadershipAA(c, RAID_GROUPLESS);
}
SendRaidAddAll(c->GetName());
c->SetRaidGrouped(true);
SendRaidMOTD(c);
// xtarget shit ..........
if (group == RAID_GROUPLESS) {
if (rleader) {
GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr());
@@ -148,54 +165,115 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo
}
}
Raid *raid_update = nullptr;
raid_update = c->GetRaid();
auto* raid_update = c->GetRaid();
if (raid_update) {
raid_update->SendHPManaEndPacketsTo(c);
raid_update->SendHPManaEndPacketsFrom(c);
}
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
strn0cpy(rga->playername, c->GetName(), 64);
rga->zoneid = zone->GetZoneID();
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
strn0cpy(rga->playername, c->GetName(), sizeof(rga->playername));
rga->rid = GetID();
rga->zoneid = zone->GetZoneID();
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::AddBot(Bot* b, uint32 group, bool rleader, bool groupleader, bool looter) {
if (!b) {
return;
}
const auto query = fmt::format(
"REPLACE INTO raid_members SET raidid = {}, "
"charid = 0, bot_id = {}, groupid = {}, _class = {}, level = {}, name = '{}', "
"isgroupleader = {}, israidleader = {}, islooter = {}",
GetID(),
b->GetBotID(),
group,
b->GetClass(),
b->GetLevel(),
b->GetName(),
groupleader,
rleader,
looter
);
auto results = database.QueryDatabase(query);
LearnMembers();
VerifyRaid();
if (group < MAX_RAID_GROUPS) {
GroupUpdate(group);
} else { // get raid AAs, GroupUpdate will handle it otherwise
SendGroupLeadershipAA(b->GetOwner()->CastToClient(), RAID_GROUPLESS);
}
SendRaidAddAll(b->GetName());
b->SetRaidGrouped(true);
b->p_raid_instance = this;
auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct));
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
strn0cpy(rga->playername, b->GetName(), sizeof(rga->playername));
rga->rid = GetID();
rga->zoneid = zone->GetZoneID();
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::RemoveMember(const char *characterName)
{
std::string query = StringFormat("DELETE FROM raid_members where name='%s'", characterName);
auto results = database.QueryDatabase(query);
Client *client = entity_list.GetClientByName(characterName);
auto* b = entity_list.GetBotByBotName(characterName);
auto* c = entity_list.GetClientByName(characterName);
if (RuleB(Bots, Enabled) && b) {
b = entity_list.GetBotByBotName(characterName);
b->SetFollowID(b->GetOwner()->CastToClient()->GetID());
b->SetTarget(nullptr);
b->SetRaidGrouped(false);
}
disbandCheck = true;
SendRaidRemoveAll(characterName);
SendRaidDisband(client);
SendRaidDisband(c);
LearnMembers();
VerifyRaid();
if(client) {
client->SetRaidGrouped(false);
client->LeaveRaidXTargets(this);
client->p_raid_instance = nullptr;
if (c) {
c->SetRaidGrouped(false);
c->LeaveRaidXTargets(this);
c->p_raid_instance = nullptr;
}
auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->instance_id = zone->GetInstanceID();
strn0cpy(rga->playername, characterName, 64);
rga->zoneid = zone->GetZoneID();
rga->zoneid = zone->GetZoneID();
strn0cpy(rga->playername, characterName, sizeof(rga->playername));
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::DisbandRaid()
{
std::string query = StringFormat("DELETE FROM raid_members WHERE raidid = %lu", (unsigned long)GetID());
const auto query = fmt::format(
"DELETE FROM raid_members WHERE raidid = {}",
GetID()
);
auto results = database.QueryDatabase(query);
LearnMembers();
@@ -203,11 +281,11 @@ void Raid::DisbandRaid()
SendRaidDisbandAll();
auto pack = new ServerPacket(ServerOP_RaidDisband, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
strn0cpy(rga->playername, " ", 64);
rga->zoneid = zone->GetZoneID();
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->zoneid = zone->GetZoneID();
rga->instance_id = zone->GetInstanceID();
strn0cpy(rga->playername, " ", sizeof(rga->playername));
worldserver.SendPacket(pack);
safe_delete(pack);
@@ -216,19 +294,23 @@ void Raid::DisbandRaid()
void Raid::MoveMember(const char *name, uint32 newGroup)
{
std::string query = StringFormat("UPDATE raid_members SET groupid = %lu WHERE name = '%s'",
(unsigned long)newGroup, name);
auto results = database.QueryDatabase(query);
const auto query = fmt::format(
"UPDATE raid_members SET groupid = {} WHERE name = '{}'",
newGroup,
name
);
auto results = database.QueryDatabase(query);
LearnMembers();
VerifyRaid();
SendRaidMoveAll(name);
auto pack = new ServerPacket(ServerOP_RaidChangeGroup, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
strn0cpy(rga->playername, name, 64);
rga->zoneid = zone->GetZoneID();
auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer;
strn0cpy(rga->playername, name, sizeof(rga->playername));
rga->rid = GetID();
rga->zoneid = zone->GetZoneID();
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
@@ -236,18 +318,21 @@ void Raid::MoveMember(const char *name, uint32 newGroup)
void Raid::SetGroupLeader(const char *who, bool glFlag)
{
std::string query = StringFormat("UPDATE raid_members SET isgroupleader = %lu WHERE name = '%s'",
(unsigned long)glFlag, who);
auto results = database.QueryDatabase(query);
const auto query = fmt::format(
"UPDATE raid_members SET isgroupleader = {} WHERE name = '{}'",
glFlag,
who
);
auto results = database.QueryDatabase(query);
LearnMembers();
VerifyRaid();
auto pack = new ServerPacket(ServerOP_RaidGroupLeader, sizeof(ServerRaidGeneralAction_Struct));
ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
rga->rid = GetID();
strn0cpy(rga->playername, who, 64);
rga->zoneid = zone->GetZoneID();
auto* rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer;
strn0cpy(rga->playername, who, sizeof(rga->playername));
rga->rid = GetID();
rga->zoneid = zone->GetZoneID();
rga->instance_id = zone->GetInstanceID();
worldserver.SendPacket(pack);
safe_delete(pack);
@@ -406,13 +491,13 @@ uint32 Raid::GetFreeGroup()
return x;
}
//if we get to here then there were no free groups so we added the group as free floating members.
return 0xFFFFFFFF;
return RAID_GROUPLESS;
}
uint8 Raid::GroupCount(uint32 gid)
{
uint8 count = 0;
if(gid < 12)
if(gid < MAX_RAID_GROUPS)
{
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
@@ -453,7 +538,7 @@ uint32 Raid::GetGroup(const char *name)
if(strcmp(members[x].membername, name) == 0)
return members[x].GroupNumber;
}
return 0xFFFFFFFF;
return RAID_GROUPLESS;
}
uint32 Raid::GetGroup(Client *c)
@@ -463,7 +548,7 @@ uint32 Raid::GetGroup(Client *c)
if(members[x].member == c)
return members[x].GroupNumber;
}
return 0xFFFFFFFF;
return RAID_GROUPLESS;
}
void Raid::RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill)
@@ -474,7 +559,7 @@ void Raid::RaidSay(const char *msg, Client *c, uint8 language, uint8 lang_skill)
auto pack = new ServerPacket(ServerOP_RaidSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
ServerRaidMessage_Struct *rga = (ServerRaidMessage_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->gid = 0xFFFFFFFF;
rga->gid = RAID_GROUPLESS;
rga->language = language;
rga->lang_skill = lang_skill;
strn0cpy(rga->from, c->GetName(), 64);
@@ -992,19 +1077,21 @@ void Raid::SendRaidAdd(const char *who, Client *to)
if(!to)
return;
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
std::vector<RaidMember> rm = GetMembers();
for (const auto& m : rm)
{
if(strcmp(members[x].membername, who) == 0)
if (strcmp(m.membername, who) == 0)
{
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct));
RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer;
RaidAddMember_Struct* ram = (RaidAddMember_Struct*)outapp->pBuffer;
ram->raidGen.action = raidAdd;
ram->raidGen.parameter = members[x].GroupNumber;
strn0cpy(ram->raidGen.leader_name, members[x].membername, 64);
strn0cpy(ram->raidGen.player_name, members[x].membername, 64);
ram->_class = members[x]._class;
ram->level = members[x].level;
ram->isGroupLeader = members[x].IsGroupLeader;
ram->raidGen.parameter = m.GroupNumber;
strn0cpy(ram->raidGen.leader_name, m.membername, 64);
strn0cpy(ram->raidGen.player_name, m.membername, 64);
ram->_class = m._class;
ram->level = m.level;
ram->isGroupLeader = m.IsGroupLeader;
to->QueuePacket(outapp);
safe_delete(outapp);
return;
@@ -1014,19 +1101,21 @@ void Raid::SendRaidAdd(const char *who, Client *to)
void Raid::SendRaidAddAll(const char *who)
{
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(strcmp(members[x].membername, who) == 0)
std::vector<RaidMember> rm = GetMembers();
for (const auto& m : rm) {
if (strcmp(m.membername, who) == 0)
{
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidAddMember_Struct));
RaidAddMember_Struct *ram = (RaidAddMember_Struct*)outapp->pBuffer;
RaidAddMember_Struct* ram = (RaidAddMember_Struct*)outapp->pBuffer;
ram->raidGen.action = raidAdd;
ram->raidGen.parameter = members[x].GroupNumber;
strcpy(ram->raidGen.leader_name, members[x].membername);
strcpy(ram->raidGen.player_name, members[x].membername);
ram->_class = members[x]._class;
ram->level = members[x].level;
ram->isGroupLeader = members[x].IsGroupLeader;
ram->raidGen.parameter = m.GroupNumber;
strcpy(ram->raidGen.leader_name, m.membername);
strcpy(ram->raidGen.player_name, m.membername);
ram->isGroupLeader = m.IsGroupLeader;
ram->_class = m._class;
ram->level = m.level;
QueuePacket(outapp);
safe_delete(outapp);
return;
@@ -1129,12 +1218,13 @@ void Raid::SendRaidMoveAll(const char* who)
SendRaidRemoveAll(who);
if(c)
SendRaidCreate(c);
SendMakeLeaderPacket(leadername);
SendRaidAddAll(who);
if(c){
SendBulkRaid(c);
if(IsLocked()) { SendRaidLockTo(c); }
}
SendRaidAddAll(who);
SendMakeLeaderPacket(leadername);
}
void Raid::SendBulkRaid(Client *to)
@@ -1144,7 +1234,7 @@ void Raid::SendBulkRaid(Client *to)
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(strlen(members[x].membername) > 0 && (strcmp(members[x].membername, to->GetName()) != 0)) //don't send ourself
if (strlen(members[x].membername) > 0 && (strcmp(members[x].membername, to->GetName()) != 0)) //don't send ourself
{
SendRaidAdd(members[x].membername, to);
}
@@ -1155,7 +1245,7 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req)
{
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(members[x].member)
if(members[x].member && !members[x].IsBot)
{
members[x].member->QueuePacket(app, ack_req);
}
@@ -1164,6 +1254,11 @@ void Raid::QueuePacket(const EQApplicationPacket *app, bool ack_req)
void Raid::SendMakeLeaderPacket(const char *who) //30
{
if (RuleB(Bots, Enabled) && entity_list.GetBotByBotName(who) && members[GetPlayerIndex(who)].IsBot) {
return;
}
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
rg->action = raidMakeLeader;
@@ -1176,8 +1271,13 @@ void Raid::SendMakeLeaderPacket(const char *who) //30
void Raid::SendMakeLeaderPacketTo(const char *who, Client *to)
{
if(!to)
if (!to) {
return;
}
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(who)].IsBot) {
return;
}
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rg = (RaidLeadershipUpdate_Struct*)outapp->pBuffer;
@@ -1206,8 +1306,13 @@ void Raid::SendMakeGroupLeaderPacketTo(const char *who, Client *to)
void Raid::SendGroupUpdate(Client *to)
{
if(!to)
if (!to) {
return;
}
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(to)].IsBot) {
return;
}
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate2_Struct));
GroupUpdate2_Struct* gu = (GroupUpdate2_Struct*)outapp->pBuffer;
@@ -1249,13 +1354,14 @@ void Raid::SendGroupUpdate(Client *to)
void Raid::GroupUpdate(uint32 gid, bool initial)
{
if(gid > 11) //ungrouped member doesn't need grouping.
if(gid > 11) {//ungrouped member doesn't need grouping.
return;
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
}
for (int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(strlen(members[x].membername) > 0){
if(members[x].GroupNumber == gid){
if(members[x].member) {
if (members[x].member) {
SendGroupUpdate(members[x].member);
SendGroupLeadershipAA(members[x].member, gid);
}
@@ -1362,6 +1468,10 @@ void Raid::SendRaidMOTD(Client *c)
if (!c || motd.empty())
return;
if (members[GetPlayerIndex(c)].IsBot) {
return;
}
size_t size = motd.size() + 1;
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidMOTD_Struct) + size);
RaidMOTD_Struct *rmotd = (RaidMOTD_Struct *)outapp->pBuffer;
@@ -1397,6 +1507,10 @@ void Raid::SendRaidMOTDToWorld()
void Raid::SendGroupLeadershipAA(Client *c, uint32 gid)
{
if (RuleB(Bots, Enabled) && members[GetPlayerIndex(c)].IsBot) {
return;
}
auto outapp = new EQApplicationPacket(OP_RaidUpdate, sizeof(RaidLeadershipUpdate_Struct));
RaidLeadershipUpdate_Struct *rlaa = (RaidLeadershipUpdate_Struct *)outapp->pBuffer;
rlaa->action = raidSetLeaderAbilities;
@@ -1411,18 +1525,23 @@ void Raid::SendGroupLeadershipAA(Client *c, uint32 gid)
void Raid::SendGroupLeadershipAA(uint32 gid)
{
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member && members[i].GroupNumber == gid)
SendGroupLeadershipAA(members[i].member, gid);
for (const auto& m : members) {
if (m.member && m.GroupNumber == gid && !m.IsBot) {
SendGroupLeadershipAA(m.member, gid);
}
}
}
void Raid::SendAllRaidLeadershipAA()
{
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++)
if (members[i].member)
SendGroupLeadershipAA(members[i].member, members[i].GroupNumber);
for (const auto& m : members) {
if (m.member && !m.IsBot) {
SendGroupLeadershipAA(m.member, m.GroupNumber);
}
}
}
void Raid::LockRaid(bool lockFlag)
{
std::string query = StringFormat("UPDATE raid_details SET locked = %d WHERE raidid = %lu",
@@ -1486,70 +1605,84 @@ void Raid::SaveRaidMOTD()
bool Raid::LearnMembers()
{
memset(members, 0, (sizeof(RaidMember)*MAX_RAID_MEMBERS));
memset(members, 0, (sizeof(RaidMember) * MAX_RAID_MEMBERS));
std::string query = StringFormat("SELECT name, groupid, _class, level, "
"isgroupleader, israidleader, islooter "
"FROM raid_members WHERE raidid = %lu",
(unsigned long)GetID());
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
const auto query = fmt::format(
"SELECT name, groupid, _class, level, "
"isgroupleader, israidleader, islooter, bot_id "
"FROM raid_members WHERE raidid = {} ORDER BY groupid",
GetID()
);
if(results.RowCount() == 0) {
LogError("Error getting raid members for raid [{}]: [{}]", (unsigned long)GetID(), results.ErrorMessage().c_str());
disbandCheck = true;
return false;
}
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
int index = 0;
for(auto row = results.begin(); row != results.end(); ++row) {
if(!row[0])
continue;
if (!results.RowCount()) {
LogError("Error getting raid members for raid [{}]: [{}]", GetID(), results.ErrorMessage());
disbandCheck = true;
return false;
}
members[index].member = nullptr;
strn0cpy(members[index].membername, row[0], 64);
uint32 groupNum = Strings::ToUnsignedInt(row[1]);
if(groupNum > 11)
members[index].GroupNumber = 0xFFFFFFFF;
else
members[index].GroupNumber = groupNum;
int index = 0;
for (auto row: results) {
if (!row[0]) {
continue;
}
members[index]._class = Strings::ToInt(row[2]);
members[index].level = Strings::ToInt(row[3]);
members[index].IsGroupLeader = Strings::ToInt(row[4]);
members[index].IsRaidLeader = Strings::ToInt(row[5]);
members[index].IsLooter = Strings::ToInt(row[6]);
++index;
}
members[index].member = nullptr;
strn0cpy(members[index].membername, row[0], sizeof(members[index].membername));
uint32 group_id = Strings::ToUnsignedInt(row[1]);
if (group_id > 11) {
members[index].GroupNumber = RAID_GROUPLESS;
} else {
members[index].GroupNumber = group_id;
}
members[index]._class = Strings::ToUnsignedInt(row[2]);
members[index].level = Strings::ToUnsignedInt(row[3]);
members[index].IsGroupLeader = Strings::ToBool(row[4]);
members[index].IsRaidLeader = Strings::ToBool(row[5]);
members[index].IsLooter = Strings::ToBool(row[6]);
members[index].IsBot = Strings::ToBool(row[7]) > 0;
++index;
}
return true;
}
void Raid::VerifyRaid()
{
for(int x = 0; x < MAX_RAID_MEMBERS; x++)
{
if(strlen(members[x].membername) == 0){
members[x].member = nullptr;
}
else{
Client *c = entity_list.GetClientByName(members[x].membername);
if(c){
members[x].member = c;
}
else{
members[x].member = nullptr;
for (auto& m : members) {
if(strlen(m.membername) == 0){
m.member = nullptr;
} else {
auto* c = entity_list.GetClientByName(m.membername);
auto* b = entity_list.GetBotByBotName(m.membername);
if (c) {
m.member = c;
m.IsBot = false;
} else if(RuleB(Bots, Enabled) && b){
//Raid requires client* we are forcing it here to be a BOT. Care is needed here as any client function that
//does not exist within the Bot Class will likely corrupt memory for the member object. Good practice to test the IsBot
//attribute before calling a client function or casting to client.
b = entity_list.GetBotByBotName(m.membername);
m.member = b->CastToClient();
m.IsBot = true; //Used to identify those members who are Bots
} else {
m.member = nullptr;
m.IsBot = false;
}
}
if(members[x].IsRaidLeader){
if(strlen(members[x].membername) > 0){
SetLeader(members[x].member);
strn0cpy(leadername, members[x].membername, 64);
}
else
{
//should never happen, but maybe it is?
if (m.IsRaidLeader) {
if (strlen(m.membername) > 0){
SetLeader(m.member);
strn0cpy(leadername, m.membername, sizeof(leadername));
} else {
SetLeader(nullptr);
}
}
@@ -1577,7 +1710,7 @@ void Raid::MemberZoned(Client *c)
}
}
if (gid < 12 && group_mentor[gid].mentoree == c)
if (gid < MAX_RAID_GROUPS && group_mentor[gid].mentoree == c)
group_mentor[gid].mentoree = nullptr;
}
@@ -1592,7 +1725,7 @@ void Raid::SendHPManaEndPacketsTo(Client *client)
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
for(int x = 0; x < MAX_RAID_MEMBERS; x++) {
if(members[x].member) {
if(members[x].member && !members[x].IsBot) {
if((members[x].member != client) && (members[x].GroupNumber == group_id)) {
members[x].member->CreateHPPacket(&hp_packet);
@@ -1634,7 +1767,7 @@ void Raid::SendHPManaEndPacketsFrom(Mob *mob)
mob->CreateHPPacket(&hpapp);
for(int x = 0; x < MAX_RAID_MEMBERS; x++) {
if(members[x].member) {
if(members[x].member && !members[x].IsBot) {
if(!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
members[x].member->QueuePacket(&hpapp, false);
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
@@ -1667,7 +1800,7 @@ void Raid::SendManaPacketFrom(Mob *mob)
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
if (members[x].member) {
if (members[x].member && !members[x].IsBot) {
if (!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
outapp.SetOpcode(OP_MobManaUpdate);
@@ -1694,7 +1827,7 @@ void Raid::SendEndurancePacketFrom(Mob *mob)
EQApplicationPacket outapp(OP_MobManaUpdate, sizeof(MobManaUpdate_Struct));
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
if (members[x].member) {
if (members[x].member && !members[x].IsBot) {
if (!mob->IsClient() || ((members[x].member != mob->CastToClient()) && (members[x].GroupNumber == group_id))) {
if (members[x].member->ClientVersion() >= EQ::versions::ClientVersion::SoD) {
outapp.SetOpcode(OP_MobEnduranceUpdate);
@@ -1801,9 +1934,14 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c)
void Raid::SetDirtyAutoHaters()
{
for (int i = 0; i < MAX_RAID_MEMBERS; ++i)
if (members[i].member)
if (members[i].member && members[i].IsBot)
{
members[i].member->CastToBot()->SetDirtyAutoHaters();
}
else if (members[i].member && !members[i].IsBot)
{
members[i].member->SetDirtyAutoHaters();
}
}
void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_required /*= true*/, bool ignore_sender /*= true*/, float distance /*= 0*/, bool group_only /*= true*/) {
@@ -1812,22 +1950,25 @@ void Raid::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_re
uint32 group_id = GetGroup(sender->CastToClient());
/* If this is a group only packet and we're not in a group -- return */
if (group_id == 0xFFFFFFFF && group_only)
if (group_id == RAID_GROUPLESS && group_only)
return;
for (uint32 i = 0; i < MAX_RAID_MEMBERS; i++) {
if (!members[i].member)
if (!members[i].member) {
continue;
if (!members[i].member->IsClient())
}
if (!members[i].member->IsClient()) {
continue;
if (ignore_sender && members[i].member == sender)
}
if (members[i].IsBot) {
continue;
if (group_only && members[i].GroupNumber != group_id)
}
if (ignore_sender && members[i].member == sender) {
continue;
}
if (group_only && members[i].GroupNumber != group_id) {
continue;
}
/* If we don't have a distance requirement - send to all members */
if (distance == 0) {
members[i].member->CastToClient()->QueuePacket(app, ack_required);
@@ -1884,3 +2025,84 @@ bool Raid::DoesAnyMemberHaveExpeditionLockout(
return Expedition::HasLockoutByCharacterName(raid_member.membername, expedition_name, event_name);
});
}
Mob* Raid::GetRaidMainAssistOneByName(const char* name)
{
Raid* raid = entity_list.GetRaidByBotName(name);
std::vector<RaidMember> raid_members = raid->GetMembers();
for (RaidMember iter : raid_members)
{
if (iter.IsRaidMainAssistOne) {
return iter.member->CastToMob();
}
}
return nullptr;
}
bool Raid::IsEngaged() {
std::vector<RaidMember> rm = GetMembers();
for (const auto& m : rm) {
if (m.member && !m.IsBot && (m.member->IsEngaged() || m.member->GetAggroCount() > 0)) {
return 1;
}
}
return 0;
}
void Raid::RaidGroupSay(const char* msg, const char* from, uint8 language, uint8 lang_skill)
{
if (!from)
return;
uint32 groupToUse = GetGroup(from);
if (groupToUse > 11)
return;
auto pack = new ServerPacket(ServerOP_RaidGroupSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
ServerRaidMessage_Struct* rga = (ServerRaidMessage_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->gid = groupToUse;
rga->language = language;
rga->lang_skill = lang_skill;
strn0cpy(rga->from, from, 64);
strcpy(rga->message, msg); // this is safe because we are allocating enough space for the entire msg above
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::RaidSay(const char* msg, const char* from, uint8 language, uint8 lang_skill)
{
if (!from)
return;
auto pack = new ServerPacket(ServerOP_RaidSay, sizeof(ServerRaidMessage_Struct) + strlen(msg) + 1);
ServerRaidMessage_Struct* rga = (ServerRaidMessage_Struct*)pack->pBuffer;
rga->rid = GetID();
rga->gid = RAID_GROUPLESS;
rga->language = language;
rga->lang_skill = lang_skill;
strn0cpy(rga->from, from, 64);
strcpy(rga->message, msg);
worldserver.SendPacket(pack);
safe_delete(pack);
}
void Raid::SetNewRaidLeader(uint32 i)
{
if (members[i].IsRaidLeader) {
for (int x = 0; x < MAX_RAID_MEMBERS; x++) {
if (!members[x].IsBot) {
if (strlen(members[x].membername) > 0 && strcmp(members[x].membername, members[i].membername) != 0) {
SetRaidLeader(members[i].membername, members[x].membername);
UpdateRaidAAs();
SendAllRaidLeadershipAA();
break;
}
}
}
}
}