diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 532cc86cd..7bc96c3db 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -122,6 +122,7 @@ SET(zone_sources water_map_v2.cpp waypoints.cpp worldserver.cpp + xtargetautohaters.cpp zone.cpp zone_config.cpp zonedb.cpp @@ -215,6 +216,7 @@ SET(zone_headers water_map_v1.h water_map_v2.h worldserver.h + xtargetautohaters.h zone.h zone_config.h zonedb.h diff --git a/zone/attack.cpp b/zone/attack.cpp index 826ae713b..b7cd49384 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2432,9 +2432,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b Mob* mypet = this->GetPet(); Mob* myowner = this->GetOwner(); Mob* targetmob = this->GetTarget(); + bool on_hatelist = CheckAggro(other); if(other){ - bool on_hatelist = CheckAggro(other); AddRampage(other); if (on_hatelist) { // odd reason, if you're not on the hate list, subtlety etc don't apply! // Spell Casting Subtlety etc @@ -2510,7 +2510,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic); - if(other->IsClient()) + if(other->IsClient() && !on_hatelist) other->CastToClient()->AddAutoXTarget(this); #ifdef BOTS @@ -2549,7 +2549,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b if(!owner->GetSpecialAbility(IMMUNE_AGGRO)) { hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic); - if(owner->IsClient()) + if(owner->IsClient() && !CheckAggro(owner)) owner->CastToClient()->AddAutoXTarget(this); } } diff --git a/zone/client.cpp b/zone/client.cpp index 1a5c02f1e..a3239eeb1 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -310,6 +310,8 @@ Client::Client(EQStreamInterface* ieqs) } MaxXTargets = 5; XTargetAutoAddHaters = true; + m_autohatermgr.SetOwner(this, nullptr, nullptr); + m_activeautohatermgr = &m_autohatermgr; LoadAccountFlags(); initial_respawn_selection = 0; @@ -4159,6 +4161,18 @@ bool Client::GroupFollow(Client* inviter) { } if (raid->RaidCount() < MAX_RAID_MEMBERS) { + // okay, so we now have a single client (this) joining a group in a raid + // And they're not already in the raid (which is above and doesn't need xtarget shit) + if (!GetXTargetAutoMgr()->empty()) { + raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + GetXTargetAutoMgr()->clear(); + RemoveAutoXTargets(); + } + + SetXTargetAutoMgr(GetXTargetAutoMgr()); + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); + if (raid->GroupCount(groupToUse) < 6) { raid->SendRaidCreate(this); @@ -4234,7 +4248,9 @@ bool Client::GroupFollow(Client* inviter) { inviter->SendGroupLeaderChangePacket(inviter->GetName()); inviter->SendGroupJoinAcknowledge(); } - + group->GetXTargetAutoMgr()->merge(*inviter->GetXTargetAutoMgr()); + inviter->GetXTargetAutoMgr()->clear(); + inviter->SetXTargetAutoMgr(group->GetXTargetAutoMgr()); } if (!group) @@ -7171,12 +7187,12 @@ void Client::UpdateClientXTarget(Client *c) } } +// IT IS NOT SAFE TO CALL THIS IF IT'S NOT INITIAL AGGRO void Client::AddAutoXTarget(Mob *m, bool send) { - if(!XTargettingAvailable() || !XTargetAutoAddHaters) - return; + m_activeautohatermgr->increment_count(m); - if(IsXTarget(m)) + if (!XTargettingAvailable() || !XTargetAutoAddHaters || IsXTarget(m)) return; for(int i = 0; i < GetMaxXTargets(); ++i) @@ -7195,60 +7211,15 @@ void Client::AddAutoXTarget(Mob *m, bool send) void Client::RemoveXTarget(Mob *m, bool OnlyAutoSlots) { - if (!XTargettingAvailable()) - return; - - bool HadFreeAutoSlotsBefore = false; - - int FreedAutoSlots = 0; - - if (m->GetID() == 0) - return; - + m_activeautohatermgr->decrement_count(m); + // now we may need to clean up our CurrentTargetNPC entries for (int i = 0; i < GetMaxXTargets(); ++i) { - if (OnlyAutoSlots && XTargets[i].Type != Auto) - continue; - - if (XTargets[i].ID == m->GetID()) { - if (XTargets[i].Type == CurrentTargetNPC) - XTargets[i].Type = Auto; - - if (XTargets[i].Type == Auto) - ++FreedAutoSlots; - + if (XTargets[i].Type == CurrentTargetNPC && XTargets[i].ID == m->GetID()) { + XTargets[i].Type = Auto; XTargets[i].ID = 0; XTargets[i].dirty = true; - } else { - if (XTargets[i].Type == Auto && XTargets[i].ID == 0) - HadFreeAutoSlotsBefore = true; } } - - // move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto - // and we need to potentially fill it - std::queue empty_slots; - for (int i = 0; i < GetMaxXTargets(); ++i) { - if (XTargets[i].Type != Auto) - continue; - - if (XTargets[i].ID == 0) { - empty_slots.push(i); - continue; - } - - if (XTargets[i].ID != 0 && !empty_slots.empty()) { - int temp = empty_slots.front(); - std::swap(XTargets[i], XTargets[temp]); - XTargets[i].dirty = XTargets[temp].dirty = true; - empty_slots.pop(); - empty_slots.push(i); - } - } - // If there are more mobs aggro on us than we had auto-hate slots, add one of those haters into the slot(s) we - // just freed up. - if (!HadFreeAutoSlotsBefore && FreedAutoSlots) - entity_list.RefreshAutoXTargets(this); - SendXTargetUpdates(); } void Client::UpdateXTargetType(XTargetType Type, Mob *m, const char *Name) @@ -7405,6 +7376,123 @@ void Client::ShowXTargets(Client *c) for(int i = 0; i < GetMaxXTargets(); ++i) c->Message(0, "Xtarget Slot: %i, Type: %2i, ID: %4i, Name: %s", i, XTargets[i].Type, XTargets[i].ID, XTargets[i].Name); + auto &list = GetXTargetAutoMgr()->get_list(); + // yeah, I kept having to do something for debugging to tell if managers were the same object or not :P + // so lets use the address as an "ID" + c->Message(0, "XTargetAutoMgr ID %p size %d", GetXTargetAutoMgr(), list.size()); + int count = 0; + for (auto &e : list) { + c->Message(0, "spawn id %d count %d", e.spawn_id, e.count); + count++; + if (count == 20) { // lets not spam too many ... + c->Message(0, " ... "); + break; + } + } +} + +void Client::ProcessXTargetAutoHaters() +{ + if (!XTargettingAvailable()) + return; + + // move shit up! If the removed NPC was in a CurrentTargetNPC slot it becomes Auto + // and we need to potentially fill it + std::queue empty_slots; + for (int i = 0; i < GetMaxXTargets(); ++i) { + if (XTargets[i].Type != Auto) + continue; + + if (XTargets[i].ID != 0 && !GetXTargetAutoMgr()->contains_mob(XTargets[i].ID)) { + XTargets[i].ID = 0; + XTargets[i].dirty = true; + } + + if (XTargets[i].ID == 0) { + empty_slots.push(i); + continue; + } + + if (XTargets[i].ID != 0 && !empty_slots.empty()) { + int temp = empty_slots.front(); + std::swap(XTargets[i], XTargets[temp]); + XTargets[i].dirty = XTargets[temp].dirty = true; + empty_slots.pop(); + empty_slots.push(i); + } + } + // okay, now we need to check if we have any empty slots and if we have aggro + // We make the assumption that if we shuffled the NPCs up that they're still on the aggro + // list in the same order. We could probably do this better and try to calc if + // there are new NPCs for our empty slots on the manager, but ahhh fuck it. + if (!empty_slots.empty() && !GetXTargetAutoMgr()->empty() && XTargetAutoAddHaters) { + auto &haters = GetXTargetAutoMgr()->get_list(); + for (auto &e : haters) { + auto *mob = entity_list.GetMob(e.spawn_id); + if (!IsXTarget(mob)) { + auto slot = empty_slots.front(); + empty_slots.pop(); + XTargets[slot].dirty = true; + XTargets[slot].ID = mob->GetID(); + strn0cpy(XTargets[slot].Name, mob->GetCleanName(), 64); + } + if (empty_slots.empty()) + break; + } + } + m_dirtyautohaters = false; + SendXTargetUpdates(); +} + +// This function is called when a client is added to a group +// Group leader joining isn't handled by this function +void Client::JoinGroupXTargets(Group *g) +{ + if (!g) + return; + + if (!GetXTargetAutoMgr()->empty()) { + g->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + GetXTargetAutoMgr()->clear(); + RemoveAutoXTargets(); + } + + SetXTargetAutoMgr(g->GetXTargetAutoMgr()); + + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); +} + +// This function is called when a client leaves a group +void Client::LeaveGroupXTargets(Group *g) +{ + if (!g) + return; + + SetXTargetAutoMgr(nullptr); // this will set it back to our manager + RemoveAutoXTargets(); + entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever + // We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups + if (!GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->demerge(*g->GetXTargetAutoMgr()); // this will remove entries where we only had aggro + SetDirtyAutoHaters(); + } +} + +// This function is called when a client leaves a group +void Client::LeaveRaidXTargets(Raid *r) +{ + if (!r) + return; + + SetXTargetAutoMgr(nullptr); // this will set it back to our manager + RemoveAutoXTargets(); + entity_list.RefreshAutoXTargets(this); // this will probably break the temporal ordering, but whatever + // We now have a rebuilt, valid auto hater manager, so we need to demerge from the groups + if (!GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->demerge(*r->GetXTargetAutoMgr()); // this will remove entries where we only had aggro + SetDirtyAutoHaters(); + } } void Client::SetMaxXTargets(uint8 NewMax) diff --git a/zone/client.h b/zone/client.h index 69fdddffa..ae6c2ef30 100644 --- a/zone/client.h +++ b/zone/client.h @@ -48,6 +48,7 @@ namespace EQEmu #include "../common/inventory_profile.h" #include "../common/guilds.h" //#include "../common/item_data.h" +#include "xtargetautohaters.h" #include "common.h" #include "merc.h" @@ -1122,6 +1123,13 @@ public: void RemoveGroupXTargets(); void RemoveAutoXTargets(); void ShowXTargets(Client *c); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return m_activeautohatermgr; } // will be either raid or group or self + inline void SetXTargetAutoMgr(XTargetAutoHaters *in) { if (in) m_activeautohatermgr = in; else m_activeautohatermgr = &m_autohatermgr; } + inline void SetDirtyAutoHaters() { m_dirtyautohaters = true; } + void ProcessXTargetAutoHaters(); // fixes up our auto haters + void JoinGroupXTargets(Group *g); + void LeaveGroupXTargets(Group *g); + void LeaveRaidXTargets(Raid *r); bool GroupFollow(Client* inviter); inline bool GetRunMode() const { return runmode; } @@ -1546,8 +1554,11 @@ private: uint8 MaxXTargets; bool XTargetAutoAddHaters; + bool m_dirtyautohaters; struct XTarget_Struct XTargets[XTARGET_HARDCAP]; + XTargetAutoHaters m_autohatermgr; + XTargetAutoHaters *m_activeautohatermgr; Timer ItemTickTimer; Timer ItemQuestTimer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f2e78adf7..721421b9e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -575,6 +575,11 @@ void Client::CompleteConnect() } } raid->SendGroupLeadershipAA(this, grpID); // this may get sent an extra time ... + + SetXTargetAutoMgr(raid->GetXTargetAutoMgr()); + if (!GetXTargetAutoMgr()->empty()) + SetDirtyAutoHaters(); + if (raid->IsLocked()) raid->SendRaidLockTo(this); } @@ -1548,8 +1553,8 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) // we purchased a new one while out-of-zone. if (group->IsLeader(this)) group->SendLeadershipAAUpdate(); - } + JoinGroupXTargets(group); group->UpdatePlayer(this); LFG = false; } @@ -10806,7 +10811,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - g->DisbandGroup(); + g->JoinRaidXTarget(r); + g->DisbandGroup(true); r->GroupUpdate(freeGroup); } else{ @@ -10871,7 +10877,8 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - ig->DisbandGroup(); + ig->JoinRaidXTarget(r, true); + ig->DisbandGroup(true); r->GroupUpdate(groupFree); groupFree = r->GetFreeGroup(); } @@ -10924,10 +10931,11 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) } } } - g->DisbandGroup(); + g->JoinRaidXTarget(r); + g->DisbandGroup(true); r->GroupUpdate(groupFree); } - else + else // target does not have a group { if (ig){ r = new Raid(i); @@ -10981,14 +10989,15 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app) r->SendRaidCreate(this); r->SendMakeLeaderPacketTo(r->leadername, this); r->SendBulkRaid(this); + ig->JoinRaidXTarget(r, true); r->AddMember(this); - ig->DisbandGroup(); + ig->DisbandGroup(true); r->GroupUpdate(0); if (r->IsLocked()) { r->SendRaidLockTo(this); } } - else{ + else{ // neither has a group r = new Raid(i); entity_list.AddRaid(r); r->SetRaidDetails(); @@ -14087,6 +14096,7 @@ void Client::Handle_OP_XTargetAutoAddHaters(const EQApplicationPacket *app) } XTargetAutoAddHaters = app->ReadUInt8(0); + SetDirtyAutoHaters(); } void Client::Handle_OP_XTargetOpen(const EQApplicationPacket *app) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index bb2db4777..b97f7b22e 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -681,6 +681,12 @@ bool Client::Process() { Message(0, "Your enemies have forgotten you!"); } + if (client_state == CLIENT_CONNECTED) { + if (m_dirtyautohaters) + ProcessXTargetAutoHaters(); + // aggro meter stuff should live here + } + return ret; } diff --git a/zone/entity.cpp b/zone/entity.cpp index 2ff83980e..9f23f63a4 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1404,15 +1404,15 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets) if (!m) continue; - m->RemoveFromHateList(mob); - if (RemoveFromXTargets) { - if (m->IsClient()) + if (m->IsClient() && mob->CheckAggro(m)) m->CastToClient()->RemoveXTarget(mob, false); // FadingMemories calls this function passing the client. - else if (mob->IsClient()) + else if (mob->IsClient() && m->CheckAggro(mob)) mob->CastToClient()->RemoveXTarget(m, false); } + + m->RemoveFromHateList(mob); } } @@ -2557,6 +2557,8 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone) it->second->RemoveFromHateList(mob); else it->second->SetHateAmountOnEnt(mob, 1); + if (mob->IsClient()) + mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping } ++it; } diff --git a/zone/groups.cpp b/zone/groups.cpp index 2162e735e..24d31e74c 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -64,6 +64,8 @@ Group::Group(uint32 gid) MarkedNPCs[i] = 0; NPCMarkerID = 0; + + m_autohatermgr.SetOwner(nullptr, this, nullptr); } //creating a new group @@ -94,6 +96,7 @@ Group::Group(Mob* leader) MarkedNPCs[i] = 0; NPCMarkerID = 0; + m_autohatermgr.SetOwner(nullptr, this, nullptr); } Group::~Group() @@ -346,6 +349,9 @@ bool Group::AddMember(Mob* newmember, const char *NewMemberName, uint32 Characte database.SetGroupID(NewMemberName, GetID(), CharacterID, ismerc); } + if (newmember && newmember->IsClient()) + newmember->CastToClient()->JoinGroupXTargets(this); + safe_delete(outapp); return true; @@ -718,8 +724,10 @@ bool Group::DelMember(Mob* oldmember, bool ignoresender) if (oldmember->GetName() == mentoree_name) ClearGroupMentor(); - if(oldmember->IsClient()) + if(oldmember->IsClient()) { SendMarkedNPCsToMember(oldmember->CastToClient(), true); + oldmember->CastToClient()->LeaveGroupXTargets(this); + } if(GroupCount() < 3) { @@ -872,7 +880,7 @@ uint32 Group::GetTotalGroupDamage(Mob* other) { return total; } -void Group::DisbandGroup() { +void Group::DisbandGroup(bool joinraid) { auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupUpdate_Struct)); GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer; @@ -899,6 +907,8 @@ void Group::DisbandGroup() { database.SetGroupID(members[i]->GetCleanName(), 0, members[i]->CastToClient()->CharacterID(), false); members[i]->CastToClient()->QueuePacket(outapp); SendMarkedNPCsToMember(members[i]->CastToClient(), true); + if (!joinraid) + members[i]->CastToClient()->LeaveGroupXTargets(this); } if (members[i]->IsMerc()) @@ -2292,6 +2302,30 @@ void Group::UpdateXTargetMarkedNPC(uint32 Number, Mob *m) } +void Group::SetDirtyAutoHaters() +{ + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) + if (members[i] && members[i]->IsClient()) + members[i]->CastToClient()->SetDirtyAutoHaters(); +} + +void Group::JoinRaidXTarget(Raid *raid, bool first) +{ + if (!GetXTargetAutoMgr()->empty()) + raid->GetXTargetAutoMgr()->merge(*GetXTargetAutoMgr()); + + for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { + if (members[i] && members[i]->IsClient()) { + auto *client = members[i]->CastToClient(); + if (!first) + client->RemoveAutoXTargets(); + client->SetXTargetAutoMgr(raid->GetXTargetAutoMgr()); + if (!client->GetXTargetAutoMgr()->empty()) + client->SetDirtyAutoHaters(); + } + } +} + void Group::SetMainTank(const char *NewMainTankName) { MainTankName = NewMainTankName; diff --git a/zone/groups.h b/zone/groups.h index 3da2aae54..6de364d93 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -22,6 +22,7 @@ #include "../common/types.h" #include "mob.h" +#include "xtargetautohaters.h" class Client; class EQApplicationPacket; @@ -58,7 +59,7 @@ public: void SendWorldGroup(uint32 zone_id,Mob* zoningmember); bool DelMemberOOZ(const char *Name); bool DelMember(Mob* oldmember,bool ignoresender = false); - void DisbandGroup(); + void DisbandGroup(bool joinraid = false); void GetMemberList(std::list& member_list, bool clear_list = true); void GetClientList(std::list& client_list, bool clear_list = true); #ifdef BOTS @@ -140,6 +141,9 @@ public: void ChangeLeader(Mob* newleader); const char *GetClientNameByIndex(uint8 index); void UpdateXTargetMarkedNPC(uint32 Number, Mob *m); + void SetDirtyAutoHaters(); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; } + void JoinRaidXTarget(Raid *raid, bool first = false); void SetGroupMentor(int percent, char *name); void ClearGroupMentor(); @@ -168,6 +172,8 @@ private: std::string mentoree_name; Client *mentoree; int mentor_percent; + + XTargetAutoHaters m_autohatermgr; }; #endif diff --git a/zone/raids.cpp b/zone/raids.cpp index ff9e84578..46dd4424e 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -43,6 +43,8 @@ Raid::Raid(uint32 raidID) memset(leadername, 0, 64); locked = false; LootType = 4; + + m_autohatermgr.SetOwner(nullptr, nullptr, this); } Raid::Raid(Client* nLeader) @@ -60,6 +62,8 @@ Raid::Raid(Client* nLeader) strn0cpy(leadername, nLeader->GetName(), 64); locked = false; LootType = 4; + + m_autohatermgr.SetOwner(nullptr, nullptr, this); } Raid::~Raid() @@ -121,6 +125,26 @@ void Raid::AddMember(Client *c, uint32 group, bool rleader, bool groupleader, bo c->SetRaidGrouped(true); SendRaidMOTD(c); + // xtarget shit .......... + if (group == RAID_GROUPLESS) { + if (rleader) { + GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr()); + c->GetXTargetAutoMgr()->clear(); + c->SetXTargetAutoMgr(GetXTargetAutoMgr()); + } else { + if (!c->GetXTargetAutoMgr()->empty()) { + GetXTargetAutoMgr()->merge(*c->GetXTargetAutoMgr()); + c->GetXTargetAutoMgr()->clear(); + c->RemoveAutoXTargets(); + } + + c->SetXTargetAutoMgr(GetXTargetAutoMgr()); + + if (!c->GetXTargetAutoMgr()->empty()) + c->SetDirtyAutoHaters(); + } + } + auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; rga->rid = GetID(); @@ -143,8 +167,10 @@ void Raid::RemoveMember(const char *characterName) LearnMembers(); VerifyRaid(); - if(client) + if(client) { client->SetRaidGrouped(false); + client->LeaveRaidXTargets(this); + } auto pack = new ServerPacket(ServerOP_RaidRemove, sizeof(ServerRaidGeneralAction_Struct)); ServerRaidGeneralAction_Struct *rga = (ServerRaidGeneralAction_Struct*)pack->pBuffer; @@ -1672,3 +1698,11 @@ void Raid::CheckGroupMentor(uint32 group_id, Client *c) group_mentor[group_id].mentoree = c; } +void Raid::SetDirtyAutoHaters() +{ + for (int i = 0; i < MAX_RAID_MEMBERS; ++i) + if (members[i].member) + members[i].member->SetDirtyAutoHaters(); + +} + diff --git a/zone/raids.h b/zone/raids.h index ca5d0ff9b..9e681153c 100644 --- a/zone/raids.h +++ b/zone/raids.h @@ -20,6 +20,7 @@ #include "../common/types.h" #include "groups.h" +#include "xtargetautohaters.h" class Client; class EQApplicationPacket; @@ -230,6 +231,9 @@ public: inline int GetMentorPercent(uint32 group_id) { return group_mentor[group_id].mentor_percent; } inline Client *GetMentoree(uint32 group_id) { return group_mentor[group_id].mentoree; } + void SetDirtyAutoHaters(); + inline XTargetAutoHaters *GetXTargetAutoMgr() { return &m_autohatermgr; } + RaidMember members[MAX_RAID_MEMBERS]; char leadername[64]; protected: @@ -244,6 +248,8 @@ protected: GroupLeadershipAA_Struct group_aa[MAX_RAID_GROUPS]; GroupMentor group_mentor[MAX_RAID_GROUPS]; + + XTargetAutoHaters m_autohatermgr; }; diff --git a/zone/worldserver.cpp b/zone/worldserver.cpp index 84d4cb399..3251250ce 100644 --- a/zone/worldserver.cpp +++ b/zone/worldserver.cpp @@ -897,6 +897,9 @@ void WorldServer::Process() { Inviter->CastToClient()->SendGroupLeaderChangePacket(Inviter->GetName()); Inviter->CastToClient()->SendGroupJoinAcknowledge(); } + group->GetXTargetAutoMgr()->merge(*Inviter->CastToClient()->GetXTargetAutoMgr()); + Inviter->CastToClient()->GetXTargetAutoMgr()->clear(); + Inviter->CastToClient()->SetXTargetAutoMgr(group->GetXTargetAutoMgr()); } if(!group) @@ -1011,6 +1014,7 @@ void WorldServer::Process() { group->SetGroupMentor(mentor_percent, mentoree_name); } + client->JoinGroupXTargets(group); } else if (client->GetMerc()) { @@ -1109,6 +1113,7 @@ void WorldServer::Process() { r->SendRaidRemoveAll(rga->playername); Client *rem = entity_list.GetClientByName(rga->playername); if(rem){ + rem->LeaveRaidXTargets(r); r->SendRaidDisband(rem); } r->LearnMembers(); diff --git a/zone/xtargetautohaters.cpp b/zone/xtargetautohaters.cpp new file mode 100644 index 000000000..ab010d9db --- /dev/null +++ b/zone/xtargetautohaters.cpp @@ -0,0 +1,112 @@ +#include "xtargetautohaters.h" +#include "mob.h" +#include "client.h" +#include "raids.h" +#include "groups.h" + +#include + +void XTargetAutoHaters::increment_count(Mob *in) +{ + assert(in != nullptr); + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&in](const HatersCount &c) { return c.spawn_id == in->GetID(); }); + + // we are on the list, we just need to increment the count + if (it != m_haters.end()) { + it->count++; + return; + } + // We are not on the list + HatersCount c; + c.spawn_id = in->GetID(); + c.count = 1; + + m_haters.push_back(c); + // trigger event on owner + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); +} + +void XTargetAutoHaters::decrement_count(Mob *in) +{ + assert(in != nullptr); + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&in](const HatersCount &c) { return c.spawn_id == in->GetID(); }); + + // we are not on the list ... shouldn't happen + if (it == m_haters.end()) + return; + it->count--; + if (it->count == 0) { + m_haters.erase(it); + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); + } +} + +void XTargetAutoHaters::merge(XTargetAutoHaters &other) +{ + bool trigger = false; + for (auto &e : other.m_haters) { + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; }); + if (it != m_haters.end()) { + it->count += e.count; + continue; + } + m_haters.push_back(e); + trigger = true; + } + + if (trigger) { + if (m_client) + m_client->SetDirtyAutoHaters(); + else if (m_group) + m_group->SetDirtyAutoHaters(); + else if (m_raid) + m_raid->SetDirtyAutoHaters(); + } +} + +// demerge this from other. other belongs to group/raid you just left +void XTargetAutoHaters::demerge(XTargetAutoHaters &other) +{ + bool trigger = false; + for (auto &e : m_haters) { + auto it = std::find_if(other.m_haters.begin(), other.m_haters.end(), + [&e](const HatersCount &c) { return e.spawn_id == c.spawn_id; }); + if (it != other.m_haters.end()) { + it->count -= e.count; + if (it->count == 0) { + trigger = true; + other.m_haters.erase(it); + } + } + } + + if (trigger) { + if (other.m_client) + other.m_client->SetDirtyAutoHaters(); + else if (other.m_group) + other.m_group->SetDirtyAutoHaters(); + else if (other.m_raid) + other.m_raid->SetDirtyAutoHaters(); + } +} + +bool XTargetAutoHaters::contains_mob(int spawn_id) +{ + auto it = std::find_if(m_haters.begin(), m_haters.end(), + [spawn_id](const HatersCount &c) { return c.spawn_id == spawn_id; }); + return it != m_haters.end(); +} + diff --git a/zone/xtargetautohaters.h b/zone/xtargetautohaters.h new file mode 100644 index 000000000..fd8ee4f21 --- /dev/null +++ b/zone/xtargetautohaters.h @@ -0,0 +1,48 @@ +#ifndef XTARGETAUTOHATERS_H +#define XTARGETAUTOHATERS_H + +#include + +class Mob; +class Client; +class Group; +class Raid; + +class XTargetAutoHaters { +struct HatersCount { + int spawn_id; + int count; +}; +public: + XTargetAutoHaters() : m_client(nullptr), m_group(nullptr), m_raid(nullptr) {} + XTargetAutoHaters(Client *co, Group *go, Raid *ro) : m_client(co), m_group(go), m_raid(ro) {} + ~XTargetAutoHaters() {} + + void merge(XTargetAutoHaters &other); + void demerge(XTargetAutoHaters &other); + void increment_count(Mob *in); + void decrement_count(Mob *in); + + bool contains_mob(int spawn_id); + + inline const std::vector &get_list() { return m_haters; } + inline void SetOwner(Client *c, Group *g, Raid *r) {m_client = c; m_group = g; m_raid = r; } + inline void clear() { m_haters.clear(); } + inline bool empty() { return m_haters.empty(); } + +private: + /* This will contain all of the mobs that are possible to fill in an autohater + * slot. This keeps track of ALL MOBS for a client or group or raid + * This list needs to be merged when you join group/raid/etc + */ + std::vector m_haters; + + // So this is the object that owns us ... only 1 shouldn't be null + Client *m_client; + Group *m_group; + Raid *m_raid; +}; + + +#endif /* !XTARGETAUTOHATERS_H */ +