[9918] Fixed player's tapped creature loot access by group in diff cases

* If player tap creature in group and leave then group will have access to creature loot if not disbanded
* If player tap creature and after join to group then creature loot will accesable only by player
* Also RewardPlayerAndGroupAtKill divided to simgle player and group reward versions used for group tap
  and single player tap cases.
This commit is contained in:
VladimirMangos 2010-05-17 08:58:54 +04:00
parent 96d50bf55a
commit 696a4b6db0
13 changed files with 223 additions and 135 deletions

View file

@ -111,7 +111,7 @@ bool ForcedDespawnDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
Creature::Creature(CreatureSubtype subtype) :
Unit(), i_AI(NULL),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), m_groupLootId(0),
m_lootMoney(0), m_lootRecipient(0),
m_lootMoney(0), m_lootGroupRecipientId(0),
m_deathTimer(0), m_respawnTime(0), m_respawnDelay(25), m_corpseDelay(60), m_respawnradius(5.0f),
m_subtype(subtype), m_defaultMovementType(IDLE_MOTION_TYPE), m_DBTableGuid(0), m_equipmentId(0),
m_AlreadyCallAssistance(false), m_AlreadySearchedAssistance(false),
@ -810,13 +810,59 @@ void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, Splin
SendMonsterMove(x, y, z, type, flags, time);
}
Player *Creature::GetLootRecipient() const
/**
* Return original player who tap creature, it can be different from player/group allowed to loot so not use it for loot code
*/
Player* Creature::GetOriginalLootRecipient() const
{
if (!m_lootRecipient)
return NULL;
else return ObjectAccessor::FindPlayer(m_lootRecipient);
return !m_lootRecipientGuid.IsEmpty() ? ObjectAccessor::FindPlayer(m_lootRecipientGuid) : NULL;
}
/**
* Return group if player tap creature as group member, independent is player after leave group or stil be group member
*/
Group* Creature::GetGroupLootRecipient() const
{
// original recipient group if set and not disbanded
return m_lootGroupRecipientId ? sObjectMgr.GetGroupById(m_lootGroupRecipientId) : NULL;
}
/**
* Return player who can loot tapped creature (member of group or single player)
*
* In case when original player tap creature as group member then group tap prefered.
* This is for example important if player after tap leave group.
* If group not exist or disbanded or player tap creature not as group member return player
*/
Player* Creature::GetLootRecipient() const
{
// original recipient group if set and not disbanded
Group* group = GetGroupLootRecipient();
// original recipient player if online
Player* player = GetOriginalLootRecipient();
// if group not set or disbanded return original recipient player if any
if (!group)
return player;
// group case
// return player if it still be in original recipient group
if (player && player->GetGroup() == group)
return player;
// find any in group
for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
if (Player *p = itr->getSource())
return p;
return NULL;
}
/**
* Set player and group (if player group member) who tap creature
*/
void Creature::SetLootRecipient(Unit *unit)
{
// set the player whose group should receive the right
@ -825,7 +871,8 @@ void Creature::SetLootRecipient(Unit *unit)
if (!unit)
{
m_lootRecipient = 0;
m_lootRecipientGuid.Clear();
m_lootGroupRecipientId = 0;
RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED);
return;
}
@ -834,7 +881,13 @@ void Creature::SetLootRecipient(Unit *unit)
if(!player) // normal creature, no player involved
return;
m_lootRecipient = player->GetGUID();
// set player for non group case or if group will disbanded
m_lootRecipientGuid = player->GetObjectGuid();
// set group for group existed case including if player will leave group at loot time
if (Group* group = player->GetGroup())
m_lootGroupRecipientId = group->GetId();
SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TAPPED);
}

View file

@ -531,11 +531,16 @@ class MANGOS_DLL_SPEC Creature : public Unit
Loot loot;
bool lootForPickPocketed;
bool lootForBody;
Player *GetLootRecipient() const;
bool hasLootRecipient() const { return m_lootRecipient!=0; }
void SetLootRecipient (Unit* unit);
ObjectGuid GetLootRecipientGuid() const { return m_lootRecipientGuid; }
uint32 GetLootGroupRecipientId() const { return m_lootGroupRecipientId; }
Player* GetLootRecipient() const; // use group cases as prefered
Group* GetGroupLootRecipient() const;
bool HasLootRecipient() const { return m_lootGroupRecipientId || !m_lootRecipientGuid.IsEmpty(); }
bool IsGroupLootRecipient() const { return m_lootGroupRecipientId; }
void SetLootRecipient(Unit* unit);
void AllLootRemovedFromCorpse();
Player* GetOriginalLootRecipient() const; // ignore group changes/etc, not for looting
SpellEntry const *reachWithSpellAttack(Unit *pVictim);
SpellEntry const *reachWithSpellCure(Unit *pVictim);
@ -641,7 +646,8 @@ class MANGOS_DLL_SPEC Creature : public Unit
static float _GetDamageMod(int32 Rank);
uint32 m_lootMoney;
uint64 m_lootRecipient;
ObjectGuid m_lootRecipientGuid; // player who will have rights for looting if m_lootGroupRecipient==0 or group disbanded
uint32 m_lootGroupRecipientId; // group who will have rights for looting if set and exist
/// Timers
uint32 m_deathTimer; // (msecs)timer for death or corpse disappearance

View file

@ -698,7 +698,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
break;
}
case ACTION_T_KILLED_MONSTER:
//first attempt player who tapped creature
//first attempt player/group who tapped creature
if (Player* pPlayer = m_creature->GetLootRecipient())
pPlayer->RewardPlayerAndGroupAtEvent(action.killed_monster.creatureId, m_creature);
else

View file

@ -1691,3 +1691,75 @@ void Group::_homebindIfInstance(Player *player)
player->m_InstanceValid = false;
}
}
void Group::RewardGroupAtKill(Unit* pVictim)
{
// for creature case use tapped group (for avoid use group if not set and use if player switch group after tap
//Group* pGroup = pVictim->GetTypeId() == TYPEID_UNIT ? ((Creature*)pVictim)->GetGroupLootRecipient() : GetGroup();
bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
// prepare data for near group iteration (PvP and !PvP cases)
uint32 xp = 0;
uint32 count = 0;
uint32 sum_level = 0;
Player* member_with_max_level = NULL;
Player* not_gray_member_with_max_level = NULL;
GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level,not_gray_member_with_max_level);
if(member_with_max_level)
{
/// not get Xp in PvP or no not gray players in group
xp = (PvP || !not_gray_member_with_max_level) ? 0 : MaNGOS::XP::Gain(not_gray_member_with_max_level, pVictim);
/// skip in check PvP case (for speed, not used)
bool is_raid = PvP ? false : sMapStore.LookupEntry(pVictim->GetMapId())->IsRaid() && isRaidGroup();
bool is_dungeon = PvP ? false : sMapStore.LookupEntry(pVictim->GetMapId())->IsDungeon();
float group_rate = MaNGOS::XP::xp_in_group_rate(count,is_raid);
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* pGroupGuy = itr->getSource();
if(!pGroupGuy)
continue;
if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
continue; // member (alive or dead) or his corpse at req. distance
// honor can be in PvP and !PvP (racial leader) cases (for alive)
if (pGroupGuy->isAlive())
pGroupGuy->RewardHonor(pVictim,count);
// xp and reputation only in !PvP case
if(!PvP)
{
float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
// if is in dungeon then all receive full reputation at kill
// rewarded any alive/dead/near_corpse group member
pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
// XP updated only for alive group member
if(pGroupGuy->isAlive() && not_gray_member_with_max_level &&
pGroupGuy->getLevel() <= not_gray_member_with_max_level->getLevel())
{
uint32 itr_xp = (member_with_max_level == not_gray_member_with_max_level) ? uint32(xp*rate) : uint32((xp*rate/2)+1);
pGroupGuy->GiveXP(itr_xp, pVictim);
if(Pet* pet = pGroupGuy->GetPet())
pet->GivePetXP(itr_xp/2);
}
// quest objectives updated only for alive group member or dead but with not released body
if(pGroupGuy->isAlive()|| !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
{
// normal creature (not pet/etc) can be only in !PvP case
if(pVictim->GetTypeId()==TYPEID_UNIT)
pGroupGuy->KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetObjectGuid());
}
}
}
}
}

View file

@ -323,6 +323,8 @@ class MANGOS_DLL_SPEC Group
void BroadcastReadyCheck(WorldPacket *packet);
void OfflineReadyCheck();
void RewardGroupAtKill(Unit* pVictim);
/*********************************************************/
/*** LOOT SYSTEM ***/
/*********************************************************/

View file

@ -436,10 +436,9 @@ void WorldSession::DoLootRelease(ObjectGuid lguid)
loot = &pCreature->loot;
// update next looter
if(Player *recipient = pCreature->GetLootRecipient())
if(Group* group = recipient->GetGroup())
if (group->GetLooterGuid() == player->GetGUID())
group->UpdateLooterGuid(pCreature);
if(Group* group = pCreature->GetGroupLootRecipient())
if (group->GetLooterGuid() == player->GetGUID())
group->UpdateLooterGuid(pCreature);
if (loot->isLooted())
{

View file

@ -130,6 +130,7 @@ class MANGOS_DLL_SPEC ObjectGuid
PackedGuidReader ReadAsPacked() { return PackedGuidReader(*this); }
void Set(uint64 const& guid) { m_guid = guid; }
void Clear() { m_guid = 0; }
// Possible removed in future for more strict control type conversions
void operator= (uint64 const& guid) { m_guid = guid; }

View file

@ -7861,7 +7861,7 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
if (Group* group = recipient->GetGroup())
if (Group* group = creature->GetGroupLootRecipient())
{
group->UpdateLooterGuid(creature,true);
@ -7892,9 +7892,9 @@ void Player::SendLoot(ObjectGuid guid, LootType loot_type)
// set group rights only for loot_type != LOOT_SKINNING
else
{
if(Group* group = GetGroup())
if(Group* group = creature->GetGroupLootRecipient())
{
if (group == recipient->GetGroup())
if (group == GetGroup())
{
if (group->GetLootMethod() == FREE_FOR_ALL)
permission = ALL_PERMISSION;
@ -15452,18 +15452,20 @@ bool Player::isAllowedToLoot(Creature* creature)
{
if (recipient == this)
return true;
if( Group* otherGroup = recipient->GetGroup())
if (Group* otherGroup = recipient->GetGroup())
{
Group* thisGroup = GetGroup();
if(!thisGroup)
if (!thisGroup)
return false;
return thisGroup == otherGroup;
}
return false;
}
else
// prevent other players from looting if the recipient got disconnected
return !creature->hasLootRecipient();
return !creature->HasLootRecipient();
}
void Player::_LoadActions(QueryResult *result)
@ -20099,99 +20101,28 @@ bool Player::isHonorOrXPTarget(Unit* pVictim) const
return true;
}
bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
bool Player::RewardSinglePlayerAtKill(Unit* pVictim)
{
bool PvP = pVictim->isCharmedOwnedByPlayerOrPlayer();
uint32 xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
// prepare data for near group iteration (PvP and !PvP cases)
uint32 xp = 0;
bool honored_kill = false;
// honor can be in PvP and !PvP (racial leader) cases
bool honored_kill = RewardHonor(pVictim,1);
if(Group *pGroup = GetGroup())
// xp and reputation only in !PvP case
if(!PvP)
{
uint32 count = 0;
uint32 sum_level = 0;
Player* member_with_max_level = NULL;
Player* not_gray_member_with_max_level = NULL;
RewardReputation(pVictim,1);
GiveXP(xp, pVictim);
pGroup->GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level,not_gray_member_with_max_level);
if(Pet* pet = GetPet())
pet->GivePetXP(xp);
if(member_with_max_level)
{
/// not get Xp in PvP or no not gray players in group
xp = (PvP || !not_gray_member_with_max_level) ? 0 : MaNGOS::XP::Gain(not_gray_member_with_max_level, pVictim);
/// skip in check PvP case (for speed, not used)
bool is_raid = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsRaid() && pGroup->isRaidGroup();
bool is_dungeon = PvP ? false : sMapStore.LookupEntry(GetMapId())->IsDungeon();
float group_rate = MaNGOS::XP::xp_in_group_rate(count,is_raid);
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* pGroupGuy = itr->getSource();
if(!pGroupGuy)
continue;
if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
continue; // member (alive or dead) or his corpse at req. distance
// honor can be in PvP and !PvP (racial leader) cases (for alive)
if(pGroupGuy->isAlive() && pGroupGuy->RewardHonor(pVictim,count) && pGroupGuy==this)
honored_kill = true;
// xp and reputation only in !PvP case
if(!PvP)
{
float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
// if is in dungeon then all receive full reputation at kill
// rewarded any alive/dead/near_corpse group member
pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
// XP updated only for alive group member
if(pGroupGuy->isAlive() && not_gray_member_with_max_level &&
pGroupGuy->getLevel() <= not_gray_member_with_max_level->getLevel())
{
uint32 itr_xp = (member_with_max_level == not_gray_member_with_max_level) ? uint32(xp*rate) : uint32((xp*rate/2)+1);
pGroupGuy->GiveXP(itr_xp, pVictim);
if(Pet* pet = pGroupGuy->GetPet())
pet->GivePetXP(itr_xp/2);
}
// quest objectives updated only for alive group member or dead but with not released body
if(pGroupGuy->isAlive()|| !pGroupGuy->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST))
{
// normal creature (not pet/etc) can be only in !PvP case
if(pVictim->GetTypeId()==TYPEID_UNIT)
pGroupGuy->KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetObjectGuid());
}
}
}
}
// normal creature (not pet/etc) can be only in !PvP case
if(pVictim->GetTypeId()==TYPEID_UNIT)
KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetObjectGuid());
}
else // if (!pGroup)
{
xp = PvP ? 0 : MaNGOS::XP::Gain(this, pVictim);
// honor can be in PvP and !PvP (racial leader) cases
if(RewardHonor(pVictim,1))
honored_kill = true;
// xp and reputation only in !PvP case
if(!PvP)
{
RewardReputation(pVictim,1);
GiveXP(xp, pVictim);
if(Pet* pet = GetPet())
pet->GivePetXP(xp);
// normal creature (not pet/etc) can be only in !PvP case
if(pVictim->GetTypeId()==TYPEID_UNIT)
KilledMonster(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetObjectGuid());
}
}
return xp || honored_kill;
}

View file

@ -1904,7 +1904,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void InitDisplayIds();
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
bool RewardPlayerAndGroupAtKill(Unit* pVictim);
bool RewardSinglePlayerAtKill(Unit* pVictim);
void RewardPlayerAndGroupAtEvent(uint32 creature_id,WorldObject* pRewardSource);
bool isHonorOrXPTarget(Unit* pVictim) const;

View file

@ -5675,7 +5675,7 @@ void Spell::EffectScriptEffect(SpellEffectIndex eff_idx)
if (m_caster->GetTypeId() != TYPEID_UNIT)
return;
Player* pPlayer = ((Creature*)m_caster)->GetLootRecipient();
Player* pPlayer = ((Creature*)m_caster)->GetOriginalLootRecipient();
if (!pPlayer)
return;

View file

@ -658,7 +658,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
if (pVictim->GetTypeId() == TYPEID_PLAYER)
((Player*)pVictim)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED, damage);
if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->hasLootRecipient())
if (pVictim->GetTypeId() == TYPEID_UNIT && !((Creature*)pVictim)->isPet() && !((Creature*)pVictim)->HasLootRecipient())
((Creature*)pVictim)->SetLootRecipient(this);
if (health <= damage)
@ -666,35 +666,50 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
DEBUG_LOG("DealDamage: victim just died");
// find player: owner of controlled `this` or `this` itself maybe
Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
// for loot will be sued only if group_tap==NULL
Player *player_tap = GetCharmerOrOwnerPlayerOrPlayerItself();
Group *group_tap = NULL;
// find owner of pVictim, used for creature cases, AI calls
Unit* pOwner = pVictim->GetCharmerOrOwner();
if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient())
player = ((Creature*)pVictim)->GetLootRecipient();
if (pVictim->GetTypeId() == TYPEID_UNIT)
{
group_tap = ((Creature*)pVictim)->GetGroupLootRecipient();
if (Player* recipient = ((Creature*)pVictim)->GetOriginalLootRecipient())
player_tap = recipient;
}
if (pVictim->GetTypeId() == TYPEID_PLAYER)
{
((Player*)pVictim)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, health);
if (player)
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL,1,0,pVictim);
if (player_tap)
player_tap->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL,1,0,pVictim);
}
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
if(player_tap && player_tap != pVictim)
{
player_tap->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0);
WorldPacket data(SMSG_PARTYKILLLOG, (8+8)); //send event PARTY_KILL
data << player_tap->GetObjectGuid(); //player with killing blow
data << pVictim->GetObjectGuid(); //victim
if (group_tap)
group_tap->BroadcastPacket(&data, false, group_tap->GetMemberGroup(player_tap->GetGUID()),player_tap->GetGUID());
player_tap->SendDirectMessage(&data);
}
// Reward player, his pets, and group/raid members
// call kill spell proc event (before real die and combat stop to triggering auras removed at death/combat stop)
if(player && player!=pVictim)
if (player_tap != pVictim)
{
player->RewardPlayerAndGroupAtKill(pVictim);
player->ProcDamageAndSpell(pVictim, PROC_FLAG_KILL, PROC_FLAG_KILLED, PROC_EX_NONE, 0);
WorldPacket data(SMSG_PARTYKILLLOG, (8+8)); //send event PARTY_KILL
data << uint64(player->GetGUID()); //player with killing blow
data << uint64(pVictim->GetGUID()); //victim
if (Group *group = player->GetGroup())
group->BroadcastPacket(&data, group->GetMemberGroup(player->GetGUID()));
else
player->SendDirectMessage(&data);
if (group_tap)
group_tap->RewardGroupAtKill(pVictim);
else if (player_tap)
player_tap->RewardSinglePlayerAtKill(pVictim);
}
DEBUG_LOG("DealDamageAttackStop");
@ -749,7 +764,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
// remember victim PvP death for corpse type and corpse reclaim delay
// at original death (not at SpiritOfRedemtionTalent timeout)
if( pVictim->GetTypeId()==TYPEID_PLAYER && !damageFromSpiritOfRedemtionTalent )
((Player*)pVictim)->SetPvPDeath(player!=NULL);
((Player*)pVictim)->SetPvPDeath(player_tap != NULL);
// Call KilledUnit for creatures
if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->AI())
@ -769,7 +784,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
if (pVictim->GetTypeId() == TYPEID_PLAYER)
{
// only if not player and not controlled by player pet. And not at BG
if (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround())
if (durabilityLoss && !player_tap && !((Player*)pVictim)->InBattleGround())
{
DEBUG_LOG("We are dead, loosing 10 percents durability");
((Player*)pVictim)->DurabilityLossAll(0.10f,false);
@ -855,14 +870,14 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
{
Player *killed = ((Player*)pVictim);
if(BattleGround *bg = killed->GetBattleGround())
if(player)
bg->HandleKillPlayer(killed, player);
if(player_tap)
bg->HandleKillPlayer(killed, player_tap);
}
else if(pVictim->GetTypeId() == TYPEID_UNIT)
{
if (player)
if (BattleGround *bg = player->GetBattleGround())
bg->HandleKillUnit((Creature*)pVictim, player);
if (player_tap)
if (BattleGround *bg = player_tap->GetBattleGround())
bg->HandleKillUnit((Creature*)pVictim, player_tap);
}
}
else // if (health <= damage)

View file

@ -344,7 +344,16 @@ bool ChatHandler::HandleDebugGetLootRecipientCommand(const char* /*args*/)
if (!target)
return false;
PSendSysMessage("loot recipient: %s", target->hasLootRecipient()?(target->GetLootRecipient()?target->GetLootRecipient()->GetName():"offline"):"no loot recipient");
if (!target->HasLootRecipient())
SendSysMessage("loot recipient: no loot recipient");
else if(Player* recipient = target->GetLootRecipient())
PSendSysMessage("loot recipient: %s with raw data %s from group %u",
recipient->GetObjectGuid().GetString().c_str(),
target->GetLootRecipientGuid().GetString().c_str(),
target->GetLootGroupRecipientId());
else
SendSysMessage("loot recipient: offline ");
return true;
}

View file

@ -1,4 +1,4 @@
#ifndef __REVISION_NR_H__
#define __REVISION_NR_H__
#define REVISION_NR "9917"
#define REVISION_NR "9918"
#endif // __REVISION_NR_H__