/*
* Ascent MMORPG Server
* Copyright (C) 2005-2007 Ascent Team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*
*/
#include "StdAfx.h"
enum PartyUpdateFlags
{
GROUP_UPDATE_FLAG_NONE = 0, // 0x00000000
GROUP_UPDATE_FLAG_ONLINE = 1, // 0x00000001 uint8
GROUP_UPDATE_FLAG_HEALTH = 2, // 0x00000002 uint16
GROUP_UPDATE_FLAG_MAXHEALTH = 4, // 0x00000004 uint16
GROUP_UPDATE_FLAG_POWER_TYPE = 8, // 0x00000008 uint16
GROUP_UPDATE_FLAG_POWER = 16, // 0x00000010 uint16
GROUP_UPDATE_FLAG_MAXPOWER = 32, // 0x00000020 uint16
GROUP_UPDATE_FLAG_LEVEL = 64, // 0x00000040 uint16
GROUP_UPDATE_FLAG_ZONEID = 128, // 0x00000080 uint16
GROUP_UPDATE_FLAG_POSITION = 256, // 0x00000100 uint16, uint16
GROUP_UPDATE_FLAG_UNK_STRANGE = 512, // 0x00000200 uint64, uint16 for each uint64
GROUP_UPDATE_FLAG_UNK_1 = 1024, // 0x00000400 uint64
GROUP_UPDATE_FLAG_PET_NAME = 2048, // 0x00000800 string
GROUP_UPDATE_FLAG_PET_UNK_1 = 4096, // 0x00001000 uint16
GROUP_UPDATE_FLAG_PET_UNK_2 = 8192, // 0x00002000 uint16
GROUP_UPDATE_FLAG_PET_UNK_3 = 16384, // 0x00004000 uint16
GROUP_UPDATE_FLAG_PET_UNK_4 = 32768, // 0x00008000 uint8
GROUP_UPDATE_FLAG_UNK_3 = 65535, // 0x00010000 uint16
GROUP_UPDATE_FLAG_UNK_4 = 131070, // 0x00020000 uint16
GROUP_UPDATE_FLAG_UNK_5 = 262144, // 0x00040000 uint64, uint16 for each uint64
};
enum PartyUpdateFlagGroups
{
GROUP_UPDATE_TYPE_FULL_CREATE = GROUP_UPDATE_FLAG_ONLINE | GROUP_UPDATE_FLAG_HEALTH | GROUP_UPDATE_FLAG_MAXHEALTH |
GROUP_UPDATE_FLAG_POWER | GROUP_UPDATE_FLAG_LEVEL |
GROUP_UPDATE_FLAG_ZONEID | GROUP_UPDATE_FLAG_MAXPOWER | GROUP_UPDATE_FLAG_POSITION,
GROUP_UPDATE_TYPE_FULL_REQUEST_REPLY = 0x7FFC1800,
};
Group::Group()
{
m_GroupType = GROUP_TYPE_PARTY; // Always init as party
// Create initial subgroup
m_SubGroups[0] = new SubGroup(this, 0);
m_Leader = NULL;
m_Looter = NULL;
m_LootMethod = PARTY_LOOT_GROUP;
m_LootThreshold = 2;
m_SubGroupCount = 1;
m_MemberCount = 0;
//EventMgr::getSingleton().AddEvent(this, &Group::UpdatePositions, EVENT_GROUP_POSITION_UPDATE, 5000, 0);
m_Id = objmgr.GenerateGroupId();
ObjectMgr::getSingleton().AddGroup(this);
lastRRlooter = NULL;
m_dirty=false;
m_updateblock=false;
m_disbandOnNoMembers = true;
memset(m_targetIcons, 0, sizeof(uint64) * 8);
}
Group::~Group()
{
// SAFE
//EventMgr::getSingleton().RemoveEvents(this);
ObjectMgr::getSingleton().RemoveGroup(this);
}
SubGroup::~SubGroup()
{
GroupMembersSet::iterator itr = m_GroupMembers.begin();
for(; itr != m_GroupMembers.end(); ++itr)
{
if(itr->player)
itr->player->SetSubGroup(0);
}
m_GroupMembers.clear();
}
bool SubGroup::HasMember(uint64 guid)
{
for(GroupMembersSet::iterator itr = m_GroupMembers.begin(); itr != m_GroupMembers.end(); ++itr)
if(itr->player_info->guid==guid) return true;
return false;
}
void SubGroup::RemovePlayer(PlayerInfo * info, Player *pPlayer, bool forced_remove)
{
GroupMembersSet::iterator itr = m_GroupMembers.begin();
for(; itr != m_GroupMembers.end(); ++itr)
{
if(itr->player_info == info)
{
if(pPlayer == NULL && !forced_remove)
{
/* player temporary remove (set to offline) */
if(itr->player != NULL)
itr->player->RemoveReference(&itr->player);
info->subGroup=GetID();
}
else
{
/* full remove (player is removed from party) */
if(itr->player != NULL)
itr->player->RemoveReference(&itr->player);
m_GroupMembers.erase(itr);
m_Parent->m_MemberCount--;
info->subGroup=0;
if(pPlayer)
pPlayer->SetSubGroup(0);
m_Parent->m_dirty=true;
}
break;
}
}
}
bool SubGroup::AddPlayer(PlayerInfo * info, Player *pPlayer)
{
if(IsFull())
return false;
if(pPlayer)
{
sLog.outDebug("GROUP: Adding player %s to subgroup %d", pPlayer->GetName(), m_Id);
pPlayer->SetSubGroup(GetID());
}
info->subGroup=GetID();
/* check if we already exist */
for(GroupMembersSet::iterator itr = m_GroupMembers.begin(); itr != m_GroupMembers.end(); ++itr)
{
if(itr->player_info == info)
{
/* we're simply updating */
if(itr->player != NULL)
itr->player->RemoveReference(&itr->player);
if(pPlayer)
pPlayer->AddReference(&itr->player);
return true;
}
}
GroupMember mbr;
mbr.player = NULL;
mbr.player_info = info;
m_GroupMembers.push_back(mbr);
m_Parent->m_MemberCount++;
m_Parent->m_dirty=true;
GroupMembersSet::iterator itr = --m_GroupMembers.end();
ASSERT(itr->player_info == info);
if(pPlayer)
pPlayer->AddReference(&itr->player);
return true;
}
SubGroup * Group::FindFreeSubGroup()
{
for(uint32 i = 0; i < m_SubGroupCount; i++)
if(!m_SubGroups[i]->IsFull())
return m_SubGroups[i];
return NULL;
}
bool Group::AddMember(PlayerInfo * info, Player* pPlayer, int32 subgroupid)
{
m_groupLock.Acquire();
if(!IsFull())
{
SubGroup* subgroup = (subgroupid>0) ? m_SubGroups[subgroupid] : FindFreeSubGroup();
if(subgroup == NULL)
{
// weird shit..
if(pPlayer)
sLog.outDebug("GROUP: Tried to add member %s but FindFreeSubGroup returned NULL!", pPlayer->GetName());
return false;
}
if(subgroup->AddPlayer(info, pPlayer))
{
if(pPlayer)
{
pPlayer->SetGroup(this);
if(m_Leader == NULL)
{
// We're the only member? Set us to the leader.
pPlayer->AddReference(&m_Leader);
}
/* process any pending updates beforehand */
pPlayer->ProcessPendingUpdates();
UpdateAllOutOfRangePlayersFor(pPlayer);
}
Update(); // Send group update
info->m_Group=this;
m_groupLock.Release();
return true;
}
else
{
m_groupLock.Release();
info->m_Group=NULL;
pPlayer->SetGroup(NULL);
return false;
}
}
else
{
m_groupLock.Release();
return false;
}
}
void Group::SetLeader(Player* pPlayer, bool silent)
{
if(m_Leader != NULL)
m_Leader->RemoveReference(&m_Leader);
m_Leader = pPlayer;
m_dirty=true;
if(!silent)
{
WorldPacket *data = new WorldPacket;
data->Initialize(SMSG_GROUP_SET_LEADER);
*data << ((pPlayer) ? pPlayer->GetName() : "NONE");
SendPacketToAll(data);
delete data;
}
Update();
}
void Group::Update(bool delayed)
{
if(m_updateblock)
return;
WorldPacket data(50 + (m_MemberCount * 20));
GroupMembersSet::iterator itr1, itr2;
uint32 i=0,j=0;
SubGroup *sg1=NULL;
SubGroup *sg2=NULL;
m_groupLock.Acquire();
for(i=0;iGetGroupMembersBegin(); itr1!=sg1->GetGroupMembersEnd(); ++itr1)
{
/* skip offline players */
if(!itr1->player) continue;
if(delayed) itr1->player->ProcessPendingUpdates();
/* Check for Soulstone Caster */
/* SoulStone effect removed if */
/* the giver and reciever are not in party / raid */
bool removeSoulStone = true;
uint32 SoulStoneGiver = itr1->player->SoulStoneReciever;
if(!SoulStoneGiver || itr1->player->GetGUID() == SoulStoneGiver) removeSoulStone = false;
data.Initialize(SMSG_GROUP_LIST);
data << uint8(m_GroupType); //0=party,1=raid
data << uint8(0); // unk
data << uint8(sg1->GetID());
data << uint8(0); // unk2
data << uint64(0); // unk3
data << uint32(m_MemberCount-1); // we don't include self
for(j=0;jGetGroupMembersBegin(); itr2 != sg2->GetGroupMembersEnd(); ++itr2)
{
if(itr1->player_info != itr2->player_info)
{
/*check the reciever id against all group members*/
if(itr2->player_info->guid == SoulStoneGiver) removeSoulStone = false;
data << itr2->player_info->name << itr2->player_info->guid;
if(itr2->player)
data << uint8(1);
else
data << uint8(0);
/*if(m_GroupType == GROUP_TYPE_RAID && (*itr2) == sg2->m_SubGroupLeader)
data << uint8(80 + sg2->GetID());
else*/ // how do we do this? 80 obviously doesn't work.
data << uint8(sg2->GetID());
data << uint8(0);
}
}
}
if(m_Leader != NULL) data << m_Leader->GetGUID();
else data << uint64(0);
data << uint8(m_LootMethod);
if(m_Looter != NULL) data << m_Looter->GetGUID();
else data << uint64(0);
data << uint16(m_LootThreshold);
/*data << uint64(0); // new in 2.0.3, dunno what it is
data << uint64(0); // new in 2.0.3, dunno what it is*/
itr1->player->GetSession()->SendPacket(&data);
/* remove Soulstone Aura if the caster and recipient */
/* are not in the same group any more */
if(removeSoulStone)
itr1->player->removeSoulStone();
}
}
if(m_dirty)
{
m_dirty=false;
SaveToDB();
}
m_groupLock.Release();
}
void Group::Disband()
{
m_groupLock.Acquire();
uint32 i = 0;
for(i = 0; i < m_SubGroupCount; i++)
{
SubGroup *sg = m_SubGroups[i];
sg->Disband(true);
}
m_groupLock.Release();
CharacterDatabase.Execute("DELETE FROM groups WHERE group_id = %u", m_Id);
delete this; // destroy ourselves, the destructor removes from eventmgr and objectmgr.
}
void SubGroup::Disband(bool bRemoveGroup)
{
WorldPacket data;
data.SetOpcode(SMSG_GROUP_DESTROYED);
WorldPacket data2;
data2.SetOpcode(SMSG_PARTY_COMMAND_RESULT);
data2 << uint32(2) << uint8(0) << uint32(0); // you leave the group
GroupMembersSet::iterator itr = m_GroupMembers.begin();
for(; itr != m_GroupMembers.end();)
{
if(itr->player)
{
itr->player->GetSession()->SendPacket(&data);
itr->player->GetSession()->SendPacket(&data2);
itr->player->SetSubGroup(0);
if(bRemoveGroup)
{
itr->player->SetGroup(NULL);
m_Parent->SendNullUpdate(itr->player);
}
/* remove SoulStones if caster and reciever are not the same player */
if(itr->player->SoulStoneReciever && itr->player->SoulStoneReciever!=itr->player->GetGUID())
itr->player->removeSoulStone();
itr->player->RemoveReference(&itr->player);
}
if(bRemoveGroup)
itr->player_info->m_Group=NULL;
m_Parent->m_MemberCount--;
itr = m_GroupMembers.erase(itr);
}
m_Parent->m_SubGroups[m_Id] = NULL;
delete this;
}
Player* Group::FindFirstPlayer()
{
// SubGroup *sg = NULL;
GroupMembersSet::iterator itr;
m_groupLock.Acquire();
uint32 i = 0;
for(; i < m_SubGroupCount; i++) {
for(itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player)
{
m_groupLock.Release();
return itr->player; // return first
}
}
}
m_groupLock.Release();
return NULL;
}
void Group::RemovePlayer(PlayerInfo * info, Player* pPlayer, bool forced_remove)
{
WorldPacket data;
m_groupLock.Acquire();
SubGroup *sg=NULL;/* = GetSubGroup(pPlayer->GetSubGroup());
ASSERT(sg); // something wrong here if that isn't right*/
if(!pPlayer)
{
/* great, we gotta search all subgroups for him */
for(int i = 0; i < m_SubGroupCount; ++i) {
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player_info == info)
{
sg = m_SubGroups[i];
break;
}
}
}
}
else
{
sg = GetSubGroup(pPlayer->GetSubGroup());
if(!sg->HasMember(pPlayer->GetGUID()))
{
sg=NULL;
for(int i = 0; i < m_SubGroupCount; ++i) {
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player_info == info)
{
sg = m_SubGroups[i];
break;
}
}
}
}
}
if(!sg)
{
m_groupLock.Release();
return;
}
sg->RemovePlayer(info, pPlayer,forced_remove);
if(pPlayer!=NULL || forced_remove)
{
info->m_Group=NULL;
info->subGroup=0;
}
if(pPlayer)
{
pPlayer->SetGroup(NULL);
if(pPlayer->GetSession() != NULL)
{
SendNullUpdate(pPlayer);
//pPlayer->RemoveAllAreaAuras();
data.SetOpcode(SMSG_GROUP_DESTROYED);
pPlayer->GetSession()->SendPacket(&data);
data.Initialize(SMSG_PARTY_COMMAND_RESULT);
data << uint32(2) << uint8(0) << uint32(0); // you leave the group
pPlayer->GetSession()->SendPacket(&data);
}
}
if(m_MemberCount < 2)
{
if(m_disbandOnNoMembers)
{
/* remove some left-over references */
if(m_Looter)
m_Looter->RemoveReference(&m_Looter);
if(m_Leader)
m_Leader->RemoveReference(&m_Leader);
m_groupLock.Release();
Disband();
return;
}
}
/* eek! ;P */
Player *newPlayer = FindFirstPlayer();
if(m_Looter && m_Looter->GetGUID() == info->guid)
{
m_Looter->RemoveReference(&m_Looter);
m_Looter = newPlayer;
m_dirty=true;
}
if(m_Leader && m_Leader->GetGUID() == info->guid)
{
if(pPlayer != NULL)
{
/* full remove - display message */
SetLeader(newPlayer, false);
}
else
{
/* partial remove - no message */
m_Leader->RemoveReference(&m_Leader);
m_Leader = newPlayer;
m_dirty=true;
Update(true);
}
}
else
Update(true);
m_groupLock.Release();
}
void Group::SendPacketToAll(WorldPacket *packet)
{
SendPacketToAllButOne(packet, NULL);
}
void Group::ExpandToRaid()
{
// Very simple ;)
uint32 i = 1;
m_groupLock.Acquire();
m_SubGroupCount = 8;
for(; i < m_SubGroupCount; i++)
m_SubGroups[i] = new SubGroup(this, i);
m_GroupType = GROUP_TYPE_RAID;
m_dirty=true;
Update();
m_groupLock.Release();
}
void Group::SetLooter(Player *pPlayer, uint8 method, uint16 threshold)
{
if(m_Looter)
m_Looter->RemoveReference(&m_Looter);
m_LootMethod = method;
m_Looter = pPlayer;
m_LootThreshold = threshold;
m_dirty=true;
Update();
}
//!! function is not fair. If new player pops in/out he might get a piece of the action to soon or let first player get more loots :)
Player* Group::GetnextRRlooter()
{
//another methode would be to directly store a node from list so we can jump directly to next one. But that is not safe :P
GroupMembersSet::iterator itr1;
m_groupLock.Acquire();
Player *firsthit=NULL,*prevp=NULL;
for(int i = 0; i < m_SubGroupCount; i++)
{
for(itr1 = m_SubGroups[i]->GetGroupMembersBegin(); itr1 != m_SubGroups[i]->GetGroupMembersEnd(); ++itr1)
{
if(!itr1->player) continue;
if(firsthit==NULL)
firsthit = itr1->player;
if(prevp==lastRRlooter)
{
m_groupLock.Release();
return itr1->player;
}
prevp = itr1->player;
}
}
//if we got here it means player eighter exited group or it was last one
m_groupLock.Release();
return firsthit;
}
void Group::SendPacketToAllButOne(WorldPacket *packet, Player *pSkipTarget)
{
GroupMembersSet::iterator itr;
uint32 i = 0;
m_groupLock.Acquire();
for(; i < m_SubGroupCount; i++)
for(itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr)
if(itr->player && itr->player != pSkipTarget) itr->player->GetSession()->SendPacket(packet);
m_groupLock.Release();
}
bool Group::HasMember(Player * pPlayer)
{
GroupMembersSet::iterator itr;
uint32 i = 0;
m_groupLock.Acquire();
for(; i < m_SubGroupCount; i++) {
for(itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player == pPlayer)
{
m_groupLock.Release();
return true;
}
}
}
m_groupLock.Release();
return false;
}
bool Group::HasMember(PlayerInfo * info)
{
GroupMembersSet::iterator itr;
uint32 i = 0;
m_groupLock.Acquire();
for(; i < m_SubGroupCount; i++) {
for(itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player_info == info)
{
m_groupLock.Release();
return true;
}
}
}
m_groupLock.Release();
return false;
}
void Group::MovePlayer(PlayerInfo *info, uint8 subgroup)
{
ASSERT(subgroup < m_SubGroupCount);
m_groupLock.Acquire();
SubGroup *sg=NULL;
Player * pl=NULL;
/* great, we gotta search all subgroups for him */
for(int i = 0; i < m_SubGroupCount; ++i) {
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr) {
if(itr->player_info == info)
{
sg = m_SubGroups[i];
pl = itr->player;
break;
}
}
}
if(!sg) return;
sg->RemovePlayer(info, NULL, true);
// Grab the new group, and insert
sg = m_SubGroups[subgroup];
sg->AddPlayer(info,pl);
info->subGroup=sg->GetID();
Update();
m_groupLock.Release();
}
void Group::SendNullUpdate(Player *pPlayer)
{
// this packet is 24 bytes long. // AS OF 2.1.0
uint8 buffer[24];
memset(buffer, 0, 24);
pPlayer->GetSession()->OutPacket(SMSG_GROUP_LIST, 24, buffer);
}
// player is object class becouse its called from unit class
void Group::SendPartyKillLog(Object * player, Object * Unit)
{
if (!player || !Unit || !HasMember(((Player*)player)))
return;
WorldPacket data(SMSG_PARTYKILLLOG, 16);
data << player->GetGUID();
data << Unit->GetGUID();
SendPacketToAll(&data);
}
void Group::LoadFromDB(Field *fields)
{
m_updateblock=true;
m_Id = fields[0].GetUInt32();
m_GroupType = fields[1].GetUInt8();
m_SubGroupCount = fields[2].GetUInt8();
m_LootMethod = fields[3].GetUInt8();
m_LootThreshold = fields[4].GetUInt8();
// create groups
for(int i = 1; i < m_SubGroupCount; ++i)
m_SubGroups[i] = new SubGroup(this, i);
// assign players into groups
for(int i = 0; i < m_SubGroupCount; ++i)
{
for(int j = 0; j < 5; ++j)
{
uint64 guid = fields[5 + (i*5) + j].GetUInt64();
PlayerInfo * inf = objmgr.GetPlayerInfo(guid);
if(inf == NULL) continue;
AddMember(inf, NULL, i);
m_dirty=false;
}
}
m_updateblock=false;
}
void Group::SaveToDB()
{
std::stringstream ss;
//uint32 i = 0;
uint32 fillers = 8 - m_SubGroupCount;
ss << "REPLACE INTO groups VALUES("
<< m_Id << ","
<< uint32(m_GroupType) << ","
<< uint32(m_SubGroupCount) << ","
<< uint32(m_LootMethod) << ","
<< uint32(m_LootThreshold) << ",";
for(uint32 i = 0; i < m_SubGroupCount; ++i)
{
uint32 j = 0;
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); j<5 && itr != m_SubGroups[i]->GetGroupMembersEnd(); ++j, ++itr)
{
ss << itr->player_info->guid << ",";
}
for(; j < 5; ++j)
ss << "0,";
}
for(uint32 i = 0; i < fillers; ++i)
ss << "0,0,0,0,0,";
ss << UNIXTIME << ")";
/*printf("==%s==\n", ss.str().c_str());*/
CharacterDatabase.Execute(ss.str().c_str());
}
void Group::UpdateOutOfRangePlayer(Player * pPlayer, uint32 Flags, bool Distribute, WorldPacket * Packet)
{
WorldPacket * data = Packet;
if(!Packet)
data = new WorldPacket(SMSG_PARTY_MEMBER_STATS, 500);
if(pPlayer->GetPowerType() != POWER_TYPE_MANA)
Flags |= GROUP_UPDATE_FLAG_POWER_TYPE;
/*Flags |= GROUP_UPDATE_FLAG_PET_NAME;
Flags |= GROUP_UPDATE_FLAG_PET_UNK_1;*/
data->Initialize(SMSG_PARTY_MEMBER_STATS);
*data << pPlayer->GetNewGUID();
*data << Flags;
if(Flags & GROUP_UPDATE_FLAG_ONLINE)
{
if(pPlayer->IsPvPFlagged())
*data << uint8(3);
else
*data << uint8(1);
}
if(Flags & GROUP_UPDATE_FLAG_HEALTH)
*data << uint16(pPlayer->GetUInt32Value(UNIT_FIELD_HEALTH));
if(Flags & GROUP_UPDATE_FLAG_MAXHEALTH)
*data << uint16(pPlayer->GetUInt32Value(UNIT_FIELD_MAXHEALTH));
if(Flags & GROUP_UPDATE_FLAG_POWER_TYPE)
*data << uint8(pPlayer->GetPowerType());
if(Flags & GROUP_UPDATE_FLAG_POWER)
*data << uint16(pPlayer->GetUInt32Value(UNIT_FIELD_POWER1 + pPlayer->GetPowerType()));
if(Flags & GROUP_UPDATE_FLAG_MAXPOWER)
*data << uint16(pPlayer->GetUInt32Value(UNIT_FIELD_MAXPOWER1 + pPlayer->GetPowerType()));
if(Flags & GROUP_UPDATE_FLAG_LEVEL)
*data << uint16(pPlayer->getLevel());
if(Flags & GROUP_UPDATE_FLAG_ZONEID)
*data << uint16(pPlayer->GetZoneId());
if(Flags & GROUP_UPDATE_FLAG_POSITION)
{
*data << int16(pPlayer->GetPositionX()) << int16(pPlayer->GetPositionY()); // wtf packed floats? O.o
pPlayer->m_last_group_position = pPlayer->GetPosition();
}
if(Flags & GROUP_UPDATE_TYPE_FULL_REQUEST_REPLY)
{
// Full update - we have to put some weird extra shit on the end :/
/*
{SERVER} Packet: (0x02F2) CMSG_PET_UNLEARN PacketSize = 46
07 EB BB 69 - guid
F7 1B FC 7F - mask - 1111111111111000001101111110111 - full
// known parts
0000000000000000000000000000001 - online
0000000000000000000000000000010 - health
0000000000000000000000000000100 - max health
0000000000000000000000000010000 - power
0000000000000000000000000100000 - max power
0000000000000000000000001000000 - level
0000000000000000000000010000000 - zoneid
0000000000000000000000100000000 - level
0000000000000000000001000000000 - position
// unknown parts
1111111111111000001101111110111 - full
0000000000001000000000000000000 - GROUP_UPDATE_FLAG_UNK_5 = 262144, // 0x00040000 uint64, uint16 for each uint64
1111111111111000000000000000000 -
03 - online
7D 02 - health
0E 04 - max health
37 05 - power
BA|07 - max power
1B 00 - level
28 00 - zoneid
39 D8 B9 03 - position
-- this is what we append on the end of a full update.. no idea what is is
01 00 00 00
00 00 00 FF
4A 78 00 58
26 00 00 00
00 00 00 00
FF
-------------------------------------------------------------------
*/
*data << uint32(0x00000001);
*data << uint32(0xFF000000);
*data << uint32(0x5800784A);
*data << uint32(0x00000026);
*data << uint32(0x00000000);
*data << uint8(0xFF);
}
if(Distribute)
{
Player * plr;
float dist = sWorld.m_UpdateDistance;
m_groupLock.Acquire();
for(uint32 i = 0; i < m_SubGroupCount; ++i)
{
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd();)
{
plr = itr->player;
++itr;
if(plr && plr != pPlayer && plr->GetMapMgr() == pPlayer->GetMapMgr())
{
if(plr->CalcDistance(pPlayer) > dist)
plr->GetSession()->SendPacket(data);
}
}
}
m_groupLock.Release();
}
if(!Packet)
delete data;
}
void Group::UpdateAllOutOfRangePlayersFor(Player * pPlayer)
{
WorldPacket data(150);
WorldPacket data2(150);
/* tell the other players about us */
UpdateOutOfRangePlayer(pPlayer, GROUP_UPDATE_TYPE_FULL_CREATE, true, &data2);
/* tell us any other players we don't know about */
Player * plr;
bool u1, u2;
UpdateMask myMask;
myMask.SetCount(PLAYER_END);
UpdateMask hisMask;
hisMask.SetCount(PLAYER_END);
m_groupLock.Acquire();
for(uint32 i = 0; i < m_SubGroupCount; ++i)
{
for(GroupMembersSet::iterator itr = m_SubGroups[i]->GetGroupMembersBegin(); itr != m_SubGroups[i]->GetGroupMembersEnd(); ++itr)
{
plr = itr->player;
if(!plr || plr == pPlayer) continue;
if(!plr->IsVisible(pPlayer))
{
UpdateOutOfRangePlayer(plr, GROUP_UPDATE_TYPE_FULL_CREATE, false, &data);
pPlayer->GetSession()->SendPacket(&data);
}
else
{
if(pPlayer->GetSubGroup() == plr->GetSubGroup())
{
/* distribute quest fields to other players */
hisMask.Clear();
myMask.Clear();
u1 = u2 = false;
for(uint32 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_01; ++i)
{
if(plr->GetUInt32Value(i))
{
hisMask.SetBit(i);
u1 = true;
}
if(pPlayer->GetUInt32Value(i))
{
u2 = true;
myMask.SetBit(i);
}
}
if(u1)
{
data.clear();
plr->BuildValuesUpdateBlockForPlayer(&data, &hisMask);
pPlayer->PushUpdateData(&data, 1);
}
if(u2)
{
data.clear();
pPlayer->BuildValuesUpdateBlockForPlayer(&data, &myMask);
plr->PushUpdateData(&data, 1);
}
}
}
}
}
m_groupLock.Release();
}
void Group::HandleUpdateFieldChange(uint32 Index, Player * pPlayer)
{
uint32 Flags = 0;
switch(Index)
{
case UNIT_FIELD_HEALTH:
Flags = GROUP_UPDATE_FLAG_HEALTH;
break;
case UNIT_FIELD_MAXHEALTH:
Flags = GROUP_UPDATE_FLAG_MAXHEALTH;
break;
case UNIT_FIELD_POWER1:
case UNIT_FIELD_POWER2:
case UNIT_FIELD_POWER3:
case UNIT_FIELD_POWER4:
Flags = GROUP_UPDATE_FLAG_POWER;
break;
case UNIT_FIELD_MAXPOWER1:
case UNIT_FIELD_MAXPOWER2:
case UNIT_FIELD_MAXPOWER3:
case UNIT_FIELD_MAXPOWER4:
Flags = GROUP_UPDATE_FLAG_MAXPOWER;
break;
case UNIT_FIELD_LEVEL:
Flags = GROUP_UPDATE_FLAG_LEVEL;
break;
}
if(Flags)
UpdateOutOfRangePlayer(pPlayer, Flags, true, 0);
}
void Group::HandlePartialChange(uint32 Type, Player * pPlayer)
{
uint32 Flags = 0;
switch(Type)
{
case PARTY_UPDATE_FLAG_POSITION:
Flags = GROUP_UPDATE_FLAG_POSITION;
break;
case PARTY_UPDATE_FLAG_ZONEID:
Flags = GROUP_UPDATE_FLAG_ZONEID;
break;
}
if(Flags)
UpdateOutOfRangePlayer(pPlayer, Flags, true, 0);
}
void WorldSession::HandlePartyMemberStatsOpcode(WorldPacket & recv_data)
{
if(!_player->IsInWorld())
return;
uint64 guid;
recv_data >> guid;
Player * plr = _player->GetMapMgr()->GetPlayer(guid);
if(!_player->GetGroup() || !plr)
return;
WorldPacket data(200);
if(!_player->GetGroup()->HasMember(plr))
return; // invalid player
if(_player->IsVisible(plr))
return;
_player->GetGroup()->UpdateOutOfRangePlayer(plr, GROUP_UPDATE_TYPE_FULL_CREATE | GROUP_UPDATE_TYPE_FULL_REQUEST_REPLY, false, &data);
data.SetOpcode(CMSG_PET_UNLEARN);
SendPacket(&data);
}
Group* Group::Create()
{
return new Group;
}