mirror of
https://github.com/mod-playerbots/azerothcore-wotlk.git
synced 2026-01-22 05:06:24 +00:00
refactor(Core/Player): extract KillRewarder, TradeData and PlayerTaxi (#6804)
This commit is contained in:
277
src/server/game/Entities/Player/KillerRewarder.cpp
Normal file
277
src/server/game/Entities/Player/KillerRewarder.cpp
Normal file
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
#include "Formulas.h"
|
||||
#include "Group.h"
|
||||
#include "Pet.h"
|
||||
#include "Player.h"
|
||||
#include "SpellAuraEffects.h"
|
||||
|
||||
// KillRewarder incapsulates logic of rewarding player upon kill with:
|
||||
// * XP;
|
||||
// * honor;
|
||||
// * reputation;
|
||||
// * kill credit (for quest objectives).
|
||||
// Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
|
||||
// and on battlegrounds in Battleground::RewardXPAtKill().
|
||||
//
|
||||
// Rewarding algorithm is:
|
||||
// 1. Initialize internal variables to default values.
|
||||
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
||||
// 2.1. _count - number of alive group members within reward distance;
|
||||
// 2.2. _aliveSumLevel - sum of levels of alive group members within reward distance;
|
||||
// 2.3. _sumLevel - sum of levels of group members within reward distance;
|
||||
// 2.4. _maxLevel - maximum level of alive group member within reward distance;
|
||||
// 2.5. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
||||
// for whom victim is not gray;
|
||||
// 2.6. _isFullXP - flag identifying that for all group members victim is not gray,
|
||||
// so 100% XP will be rewarded (50% otherwise).
|
||||
// 3. Reward killer (and group, if necessary).
|
||||
// 3.1. If killer is in group, reward group.
|
||||
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
||||
// for whom victim is not gray.
|
||||
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
|
||||
// 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
|
||||
// 3.2. Reward single killer (not group case).
|
||||
// 3.2.1. Initialize initial XP amount based on killer's level.
|
||||
// 3.2.2. Reward killer (see 4. for more details).
|
||||
// 4. Reward player.
|
||||
// 4.1. Give honor (player must be alive and not on BG).
|
||||
// 4.2. Give XP.
|
||||
// 4.2.1. If player is in group, adjust XP:
|
||||
// * set to 0 if player's level is more than maximum level of not gray member;
|
||||
// * cut XP in half if _isFullXP is false.
|
||||
// 4.2.2. Apply auras modifying rewarded XP.
|
||||
// 4.2.3. Give XP to player.
|
||||
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
||||
// 4.3. Give reputation (player must not be on BG).
|
||||
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
||||
// 5. Credit instance encounter.
|
||||
KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
|
||||
// 1. Initialize internal variables to default values.
|
||||
_killer(killer), _victim(victim), _group(killer->GetGroup()),
|
||||
_groupRate(1.0f), _maxNotGrayMember(nullptr), _count(0), _aliveSumLevel(0), _sumLevel(0), _xp(0),
|
||||
_isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
|
||||
{
|
||||
// mark the credit as pvp if victim is player
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER)
|
||||
_isPvP = true;
|
||||
// or if its owned by player and its not a vehicle
|
||||
else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
|
||||
_isPvP = !victim->IsVehicle();
|
||||
|
||||
_InitGroupData();
|
||||
}
|
||||
|
||||
inline void KillRewarder::_InitGroupData()
|
||||
{
|
||||
if (_group)
|
||||
{
|
||||
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
||||
for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
if (Player* member = itr->GetSource())
|
||||
if ((_killer == member || member->IsAtGroupRewardDistance(_victim)))
|
||||
{
|
||||
const uint8 lvl = member->getLevel();
|
||||
if (member->IsAlive())
|
||||
{
|
||||
// 2.1. _count - number of alive group members within reward distance;
|
||||
++_count;
|
||||
// 2.2. _aliveSumLevel - sum of levels of alive group members within reward distance;
|
||||
_aliveSumLevel += lvl;
|
||||
// 2.3. _maxLevel - maximum level of alive group member within reward distance;
|
||||
if (_maxLevel < lvl)
|
||||
{
|
||||
_maxLevel = lvl;
|
||||
}
|
||||
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
||||
// for whom victim is not gray;
|
||||
uint32 grayLevel = Acore::XP::GetGrayLevel(lvl);
|
||||
if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
|
||||
{
|
||||
_maxNotGrayMember = member;
|
||||
}
|
||||
}
|
||||
// 2.5. _sumLevel - sum of levels of group members within reward distance;
|
||||
_sumLevel += lvl;
|
||||
}
|
||||
// 2.6. _isFullXP - flag identifying that for all group members victim is not gray,
|
||||
// so 100% XP will be rewarded (50% otherwise).
|
||||
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
|
||||
}
|
||||
else
|
||||
_count = 1;
|
||||
}
|
||||
|
||||
inline void KillRewarder::_InitXP(Player* player)
|
||||
{
|
||||
// Get initial value of XP for kill.
|
||||
// XP is given:
|
||||
// * on battlegrounds;
|
||||
// * otherwise, not in PvP;
|
||||
// * not if killer is on vehicle.
|
||||
if (_victim && (_isBattleGround || (!_isPvP && !_killer->GetVehicle())))
|
||||
_xp = Acore::XP::Gain(player, _victim, _isBattleGround);
|
||||
|
||||
if (_xp && !_isBattleGround && _victim) // pussywizard: npcs with relatively low hp give lower exp
|
||||
if (_victim->GetTypeId() == TYPEID_UNIT)
|
||||
if (const CreatureTemplate* ct = _victim->ToCreature()->GetCreatureTemplate())
|
||||
if (ct->ModHealth <= 0.75f && ct->ModHealth >= 0.0f)
|
||||
_xp = uint32(_xp * ct->ModHealth);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardHonor(Player* player)
|
||||
{
|
||||
// Rewarded player must be alive.
|
||||
if (player->IsAlive())
|
||||
player->RewardHonor(_victim, _count, -1);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardXP(Player* player, float rate)
|
||||
{
|
||||
uint32 xp(_xp);
|
||||
if (_group)
|
||||
{
|
||||
// 4.2.1. If player is in group, adjust XP:
|
||||
// * set to 0 if player's level is more than maximum level of not gray member;
|
||||
// * cut XP in half if _isFullXP is false.
|
||||
if (_maxNotGrayMember && player->IsAlive() &&
|
||||
_maxNotGrayMember->getLevel() >= player->getLevel())
|
||||
xp = _isFullXP ?
|
||||
uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
|
||||
uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
|
||||
else
|
||||
xp = 0;
|
||||
}
|
||||
if (xp)
|
||||
{
|
||||
// 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
|
||||
Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
|
||||
for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
|
||||
AddPct(xp, (*i)->GetAmount());
|
||||
|
||||
// 4.2.3. Give XP to player.
|
||||
player->GiveXP(xp, _victim, _groupRate);
|
||||
if (Pet* pet = player->GetPet())
|
||||
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
||||
pet->GivePetXP(_group ? xp / 2 : xp);
|
||||
}
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardReputation(Player* player, float rate)
|
||||
{
|
||||
// 4.3. Give reputation (player must not be on BG).
|
||||
// Even dead players and corpses are rewarded.
|
||||
player->RewardReputation(_victim, rate);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardKillCredit(Player* player)
|
||||
{
|
||||
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
||||
if (!_group || player->IsAlive() || !player->GetCorpse())
|
||||
if (Creature* target = _victim->ToCreature())
|
||||
{
|
||||
player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, target);
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
|
||||
{
|
||||
// 4. Reward player.
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// 4.1. Give honor (player must be alive and not on BG).
|
||||
_RewardHonor(player);
|
||||
// 4.1.1 Send player killcredit for quests with PlayerSlain
|
||||
if (_victim->GetTypeId() == TYPEID_PLAYER)
|
||||
player->KilledPlayerCredit();
|
||||
}
|
||||
// Give XP only in PvE or in battlegrounds.
|
||||
// Give reputation and kill credit only in PvE.
|
||||
if (!_isPvP || _isBattleGround)
|
||||
{
|
||||
float xpRate = _group ? _groupRate * float(player->getLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
|
||||
float reputationRate = _group ? _groupRate * float(player->getLevel()) / _sumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
|
||||
sScriptMgr->OnRewardKillRewarder(player, isDungeon, xpRate); // Personal rate is 100%.
|
||||
|
||||
if (_xp)
|
||||
{
|
||||
// 4.2. Give XP.
|
||||
_RewardXP(player, xpRate);
|
||||
}
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// If killer is in dungeon then all members receive full reputation at kill.
|
||||
_RewardReputation(player, isDungeon ? 1.0f : reputationRate);
|
||||
_RewardKillCredit(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::_RewardGroup()
|
||||
{
|
||||
if (_maxLevel)
|
||||
{
|
||||
if (_maxNotGrayMember)
|
||||
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
||||
// for whom victim is not gray.
|
||||
_InitXP(_maxNotGrayMember);
|
||||
// To avoid unnecessary calculations and calls,
|
||||
// proceed only if XP is not ZERO or player is not on battleground
|
||||
// (battleground rewards only XP, that's why).
|
||||
if (!_isBattleGround || _xp)
|
||||
{
|
||||
const bool isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
|
||||
const bool isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
|
||||
_groupRate = Acore::XP::xp_in_group_rate(_count, isRaid);
|
||||
}
|
||||
|
||||
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
|
||||
for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
if (Player* member = itr->GetSource())
|
||||
{
|
||||
if (_killer == member || member->IsAtGroupRewardDistance(_victim))
|
||||
{
|
||||
_RewardPlayer(member, isDungeon);
|
||||
// Xinef: only count players
|
||||
//if (_victim->GetTypeId() == TYPEID_PLAYER)
|
||||
// member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, _victim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::Reward()
|
||||
{
|
||||
// 3. Reward killer (and group, if necessary).
|
||||
if (_group)
|
||||
// 3.1. If killer is in group, reward group.
|
||||
_RewardGroup();
|
||||
else
|
||||
{
|
||||
// 3.2. Reward single killer (not group case).
|
||||
// 3.2.1. Initialize initial XP amount based on killer's level.
|
||||
_InitXP(_killer);
|
||||
// To avoid unnecessary calculations and calls,
|
||||
// proceed only if XP is not ZERO or player is not on battleground
|
||||
// (battleground rewards only XP, that's why).
|
||||
if (!_isBattleGround || _xp)
|
||||
// 3.2.2. Reward killer.
|
||||
if (_killer->IsInMap(_victim)) // pussywizard: killer may be on other map (crashfix), when killing in a group same map is required, so its not a problem
|
||||
_RewardPlayer(_killer, false);
|
||||
}
|
||||
|
||||
// 5. Credit instance encounter.
|
||||
if (Creature* victim = _victim->ToCreature())
|
||||
if (victim->IsDungeonBoss())
|
||||
if (Map* map = _victim->FindMap())
|
||||
map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
|
||||
}
|
||||
36
src/server/game/Entities/Player/KillerRewarder.h
Normal file
36
src/server/game/Entities/Player/KillerRewarder.h
Normal file
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
class KillRewarder
|
||||
{
|
||||
public:
|
||||
KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
|
||||
|
||||
void Reward();
|
||||
|
||||
private:
|
||||
void _InitXP(Player* player);
|
||||
void _InitGroupData();
|
||||
|
||||
void _RewardHonor(Player* player);
|
||||
void _RewardXP(Player* player, float rate);
|
||||
void _RewardReputation(Player* player, float rate);
|
||||
void _RewardKillCredit(Player* player);
|
||||
void _RewardPlayer(Player* player, bool isDungeon);
|
||||
void _RewardGroup();
|
||||
|
||||
Player* _killer;
|
||||
Unit* _victim;
|
||||
Group* _group;
|
||||
float _groupRate;
|
||||
Player* _maxNotGrayMember;
|
||||
uint32 _count;
|
||||
uint32 _aliveSumLevel;
|
||||
uint32 _sumLevel;
|
||||
uint32 _xp;
|
||||
bool _isFullXP;
|
||||
uint8 _maxLevel;
|
||||
bool _isBattleGround;
|
||||
bool _isPvP;
|
||||
};
|
||||
@@ -154,562 +154,6 @@ enum CharacterCustomizeFlags
|
||||
|
||||
static uint32 copseReclaimDelay[MAX_DEATH_COUNT] = { 30, 60, 120 };
|
||||
|
||||
// == PlayerTaxi ================================================
|
||||
|
||||
PlayerTaxi::PlayerTaxi() : _taxiSegment(0)
|
||||
{
|
||||
memset(m_taximask, 0, sizeof(m_taximask));
|
||||
}
|
||||
|
||||
void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
|
||||
{
|
||||
// class specific initial known nodes
|
||||
switch (chrClass)
|
||||
{
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; ++i)
|
||||
m_taximask[i] |= sOldContinentsNodesMask[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// race specific initial known nodes: capital and taxi hub masks
|
||||
switch (race)
|
||||
{
|
||||
case RACE_HUMAN:
|
||||
SetTaximaskNode(2);
|
||||
break; // Human
|
||||
case RACE_ORC:
|
||||
SetTaximaskNode(23);
|
||||
break; // Orc
|
||||
case RACE_DWARF:
|
||||
SetTaximaskNode(6);
|
||||
break; // Dwarf
|
||||
case RACE_NIGHTELF:
|
||||
SetTaximaskNode(26);
|
||||
SetTaximaskNode(27);
|
||||
break; // Night Elf
|
||||
case RACE_UNDEAD_PLAYER:
|
||||
SetTaximaskNode(11);
|
||||
break;// Undead
|
||||
case RACE_TAUREN:
|
||||
SetTaximaskNode(22);
|
||||
break; // Tauren
|
||||
case RACE_GNOME:
|
||||
SetTaximaskNode(6);
|
||||
break; // Gnome
|
||||
case RACE_TROLL:
|
||||
SetTaximaskNode(23);
|
||||
break; // Troll
|
||||
case RACE_BLOODELF:
|
||||
SetTaximaskNode(82);
|
||||
break; // Blood Elf
|
||||
case RACE_DRAENEI:
|
||||
SetTaximaskNode(94);
|
||||
break; // Draenei
|
||||
}
|
||||
|
||||
// new continent starting masks (It will be accessible only at new map)
|
||||
switch (Player::TeamIdForRace(race))
|
||||
{
|
||||
case TEAM_ALLIANCE:
|
||||
SetTaximaskNode(100);
|
||||
break;
|
||||
case TEAM_HORDE:
|
||||
SetTaximaskNode(99);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// level dependent taxi hubs
|
||||
if (level >= 68)
|
||||
SetTaximaskNode(213); //Shattered Sun Staging Area
|
||||
}
|
||||
|
||||
void PlayerTaxi::LoadTaxiMask(std::string const& data)
|
||||
{
|
||||
Tokenizer tokens(data, ' ');
|
||||
|
||||
uint8 index;
|
||||
Tokenizer::const_iterator iter;
|
||||
for (iter = tokens.begin(), index = 0;
|
||||
(index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
|
||||
{
|
||||
// load and set bits only for existed taxi nodes
|
||||
m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
|
||||
{
|
||||
if (all)
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; i++)
|
||||
data << uint32(sTaxiNodesMask[i]); // all existed nodes
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; i++)
|
||||
data << uint32(m_taximask[i]); // known nodes
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamId teamId)
|
||||
{
|
||||
ClearTaxiDestinations();
|
||||
|
||||
Tokenizer tokens(values, ' ');
|
||||
|
||||
for (Tokenizer::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
|
||||
{
|
||||
uint32 node = uint32(atol(*iter));
|
||||
AddTaxiDestination(node);
|
||||
}
|
||||
|
||||
// Check integrity
|
||||
if (m_TaxiDestinations.size() < 3)
|
||||
return false;
|
||||
|
||||
// xinef: current segment is saved as last destination in db
|
||||
_taxiSegment = m_TaxiDestinations[m_TaxiDestinations.size() - 1];
|
||||
m_TaxiDestinations.pop_back();
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size() - 1; ++i)
|
||||
{
|
||||
uint32 cost;
|
||||
uint32 path;
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[i], m_TaxiDestinations[i + 1], path, cost);
|
||||
if (!path)
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't load taxi path without mount set (quest taxi path?)
|
||||
if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), teamId, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PlayerTaxi::SaveTaxiDestinationsToString()
|
||||
{
|
||||
if (m_TaxiDestinations.empty())
|
||||
return "";
|
||||
|
||||
std::ostringstream ss;
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
|
||||
ss << m_TaxiDestinations[i] << ' ';
|
||||
|
||||
ss << _taxiSegment << ' ';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
uint32 PlayerTaxi::GetCurrentTaxiPath() const
|
||||
{
|
||||
if (m_TaxiDestinations.size() < 2 || m_TaxiDestinations.size() <= _taxiSegment + 1)
|
||||
return 0;
|
||||
|
||||
uint32 path;
|
||||
uint32 cost;
|
||||
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[_taxiSegment], m_TaxiDestinations[_taxiSegment + 1], path, cost);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; ++i)
|
||||
ss << taxi.m_taximask[i] << ' ';
|
||||
return ss;
|
||||
}
|
||||
|
||||
//== TradeData =================================================
|
||||
|
||||
TradeData* TradeData::GetTraderData() const
|
||||
{
|
||||
return m_trader->GetTradeData();
|
||||
}
|
||||
|
||||
Item* TradeData::GetItem(TradeSlots slot) const
|
||||
{
|
||||
return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : nullptr;
|
||||
}
|
||||
|
||||
bool TradeData::HasItem(ObjectGuid itemGuid) const
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||
if (m_items[i] == itemGuid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||
if (m_items[i] == itemGuid)
|
||||
return TradeSlots(i);
|
||||
|
||||
return TRADE_SLOT_INVALID;
|
||||
}
|
||||
|
||||
Item* TradeData::GetSpellCastItem() const
|
||||
{
|
||||
return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : nullptr;
|
||||
}
|
||||
|
||||
void TradeData::SetItem(TradeSlots slot, Item* item)
|
||||
{
|
||||
ObjectGuid itemGuid = item ? item->GetGUID() : ObjectGuid::Empty;
|
||||
|
||||
if (m_items[slot] == itemGuid)
|
||||
return;
|
||||
|
||||
m_items[slot] = itemGuid;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update();
|
||||
|
||||
// need remove possible trader spell applied to changed item
|
||||
if (slot == TRADE_SLOT_NONTRADED)
|
||||
GetTraderData()->SetSpell(0);
|
||||
|
||||
// need remove possible player spell applied (possible move reagent)
|
||||
SetSpell(0);
|
||||
}
|
||||
|
||||
void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= nullptr*/)
|
||||
{
|
||||
ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
|
||||
|
||||
if (m_spell == spell_id && m_spellCastItem == itemGuid)
|
||||
return;
|
||||
|
||||
m_spell = spell_id;
|
||||
m_spellCastItem = itemGuid;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update(true); // send spell info to item owner
|
||||
Update(false); // send spell info to caster self
|
||||
}
|
||||
|
||||
void TradeData::SetMoney(uint32 money)
|
||||
{
|
||||
if (m_money == money)
|
||||
return;
|
||||
|
||||
if (!m_player->HasEnoughMoney(money))
|
||||
{
|
||||
m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
m_money = money;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update(true);
|
||||
}
|
||||
|
||||
void TradeData::Update(bool forTarget /*= true*/)
|
||||
{
|
||||
if (forTarget)
|
||||
m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
|
||||
else
|
||||
m_player->GetSession()->SendUpdateTrade(false); // player state for player
|
||||
}
|
||||
|
||||
void TradeData::SetAccepted(bool state, bool crosssend /*= false*/)
|
||||
{
|
||||
m_accepted = state;
|
||||
|
||||
if (!state)
|
||||
{
|
||||
if (crosssend)
|
||||
m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
|
||||
else
|
||||
m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
|
||||
}
|
||||
}
|
||||
|
||||
// == KillRewarder ====================================================
|
||||
// KillRewarder incapsulates logic of rewarding player upon kill with:
|
||||
// * XP;
|
||||
// * honor;
|
||||
// * reputation;
|
||||
// * kill credit (for quest objectives).
|
||||
// Rewarding is initiated in two cases: when player kills unit in Unit::Kill()
|
||||
// and on battlegrounds in Battleground::RewardXPAtKill().
|
||||
//
|
||||
// Rewarding algorithm is:
|
||||
// 1. Initialize internal variables to default values.
|
||||
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
||||
// 2.1. _count - number of alive group members within reward distance;
|
||||
// 2.2. _aliveSumLevel - sum of levels of alive group members within reward distance;
|
||||
// 2.3. _sumLevel - sum of levels of group members within reward distance;
|
||||
// 2.4. _maxLevel - maximum level of alive group member within reward distance;
|
||||
// 2.5. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
||||
// for whom victim is not gray;
|
||||
// 2.6. _isFullXP - flag identifying that for all group members victim is not gray,
|
||||
// so 100% XP will be rewarded (50% otherwise).
|
||||
// 3. Reward killer (and group, if necessary).
|
||||
// 3.1. If killer is in group, reward group.
|
||||
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
||||
// for whom victim is not gray.
|
||||
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
|
||||
// 3.1.3. Reward each group member (even dead) within reward distance (see 4. for more details).
|
||||
// 3.2. Reward single killer (not group case).
|
||||
// 3.2.1. Initialize initial XP amount based on killer's level.
|
||||
// 3.2.2. Reward killer (see 4. for more details).
|
||||
// 4. Reward player.
|
||||
// 4.1. Give honor (player must be alive and not on BG).
|
||||
// 4.2. Give XP.
|
||||
// 4.2.1. If player is in group, adjust XP:
|
||||
// * set to 0 if player's level is more than maximum level of not gray member;
|
||||
// * cut XP in half if _isFullXP is false.
|
||||
// 4.2.2. Apply auras modifying rewarded XP.
|
||||
// 4.2.3. Give XP to player.
|
||||
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
||||
// 4.3. Give reputation (player must not be on BG).
|
||||
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
||||
// 5. Credit instance encounter.
|
||||
KillRewarder::KillRewarder(Player* killer, Unit* victim, bool isBattleGround) :
|
||||
// 1. Initialize internal variables to default values.
|
||||
_killer(killer), _victim(victim), _group(killer->GetGroup()),
|
||||
_groupRate(1.0f), _maxNotGrayMember(nullptr), _count(0), _aliveSumLevel(0), _sumLevel(0), _xp(0),
|
||||
_isFullXP(false), _maxLevel(0), _isBattleGround(isBattleGround), _isPvP(false)
|
||||
{
|
||||
// mark the credit as pvp if victim is player
|
||||
if (victim->GetTypeId() == TYPEID_PLAYER)
|
||||
_isPvP = true;
|
||||
// or if its owned by player and its not a vehicle
|
||||
else if (victim->GetCharmerOrOwnerGUID().IsPlayer())
|
||||
_isPvP = !victim->IsVehicle();
|
||||
|
||||
_InitGroupData();
|
||||
}
|
||||
|
||||
inline void KillRewarder::_InitGroupData()
|
||||
{
|
||||
if (_group)
|
||||
{
|
||||
// 2. In case when player is in group, initialize variables necessary for group calculations:
|
||||
for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
if (Player* member = itr->GetSource())
|
||||
if ((_killer == member || member->IsAtGroupRewardDistance(_victim)))
|
||||
{
|
||||
const uint8 lvl = member->getLevel();
|
||||
if (member->IsAlive())
|
||||
{
|
||||
// 2.1. _count - number of alive group members within reward distance;
|
||||
++_count;
|
||||
// 2.2. _aliveSumLevel - sum of levels of alive group members within reward distance;
|
||||
_aliveSumLevel += lvl;
|
||||
// 2.3. _maxLevel - maximum level of alive group member within reward distance;
|
||||
if (_maxLevel < lvl)
|
||||
{
|
||||
_maxLevel = lvl;
|
||||
}
|
||||
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
|
||||
// for whom victim is not gray;
|
||||
uint32 grayLevel = Acore::XP::GetGrayLevel(lvl);
|
||||
if (_victim->getLevel() > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->getLevel() < lvl))
|
||||
{
|
||||
_maxNotGrayMember = member;
|
||||
}
|
||||
}
|
||||
// 2.5. _sumLevel - sum of levels of group members within reward distance;
|
||||
_sumLevel += lvl;
|
||||
}
|
||||
// 2.6. _isFullXP - flag identifying that for all group members victim is not gray,
|
||||
// so 100% XP will be rewarded (50% otherwise).
|
||||
_isFullXP = _maxNotGrayMember && (_maxLevel == _maxNotGrayMember->getLevel());
|
||||
}
|
||||
else
|
||||
_count = 1;
|
||||
}
|
||||
|
||||
inline void KillRewarder::_InitXP(Player* player)
|
||||
{
|
||||
// Get initial value of XP for kill.
|
||||
// XP is given:
|
||||
// * on battlegrounds;
|
||||
// * otherwise, not in PvP;
|
||||
// * not if killer is on vehicle.
|
||||
if (_victim && (_isBattleGround || (!_isPvP && !_killer->GetVehicle())))
|
||||
_xp = Acore::XP::Gain(player, _victim, _isBattleGround);
|
||||
|
||||
if (_xp && !_isBattleGround && _victim) // pussywizard: npcs with relatively low hp give lower exp
|
||||
if (_victim->GetTypeId() == TYPEID_UNIT)
|
||||
if (const CreatureTemplate* ct = _victim->ToCreature()->GetCreatureTemplate())
|
||||
if (ct->ModHealth <= 0.75f && ct->ModHealth >= 0.0f)
|
||||
_xp = uint32(_xp * ct->ModHealth);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardHonor(Player* player)
|
||||
{
|
||||
// Rewarded player must be alive.
|
||||
if (player->IsAlive())
|
||||
player->RewardHonor(_victim, _count, -1);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardXP(Player* player, float rate)
|
||||
{
|
||||
uint32 xp(_xp);
|
||||
if (_group)
|
||||
{
|
||||
// 4.2.1. If player is in group, adjust XP:
|
||||
// * set to 0 if player's level is more than maximum level of not gray member;
|
||||
// * cut XP in half if _isFullXP is false.
|
||||
if (_maxNotGrayMember && player->IsAlive() &&
|
||||
_maxNotGrayMember->getLevel() >= player->getLevel())
|
||||
xp = _isFullXP ?
|
||||
uint32(xp * rate) : // Reward FULL XP if all group members are not gray.
|
||||
uint32(xp * rate / 2) + 1; // Reward only HALF of XP if some of group members are gray.
|
||||
else
|
||||
xp = 0;
|
||||
}
|
||||
if (xp)
|
||||
{
|
||||
// 4.2.2. Apply auras modifying rewarded XP (SPELL_AURA_MOD_XP_PCT).
|
||||
Unit::AuraEffectList const& auras = player->GetAuraEffectsByType(SPELL_AURA_MOD_XP_PCT);
|
||||
for (Unit::AuraEffectList::const_iterator i = auras.begin(); i != auras.end(); ++i)
|
||||
AddPct(xp, (*i)->GetAmount());
|
||||
|
||||
// 4.2.3. Give XP to player.
|
||||
player->GiveXP(xp, _victim, _groupRate);
|
||||
if (Pet* pet = player->GetPet())
|
||||
// 4.2.4. If player has pet, reward pet with XP (100% for single player, 50% for group case).
|
||||
pet->GivePetXP(_group ? xp / 2 : xp);
|
||||
}
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardReputation(Player* player, float rate)
|
||||
{
|
||||
// 4.3. Give reputation (player must not be on BG).
|
||||
// Even dead players and corpses are rewarded.
|
||||
player->RewardReputation(_victim, rate);
|
||||
}
|
||||
|
||||
inline void KillRewarder::_RewardKillCredit(Player* player)
|
||||
{
|
||||
// 4.4. Give kill credit (player must not be in group, or he must be alive or without corpse).
|
||||
if (!_group || player->IsAlive() || !player->GetCorpse())
|
||||
if (Creature* target = _victim->ToCreature())
|
||||
{
|
||||
player->KilledMonster(target->GetCreatureTemplate(), target->GetGUID());
|
||||
player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE_TYPE, target->GetCreatureType(), 1, target);
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::_RewardPlayer(Player* player, bool isDungeon)
|
||||
{
|
||||
// 4. Reward player.
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// 4.1. Give honor (player must be alive and not on BG).
|
||||
_RewardHonor(player);
|
||||
// 4.1.1 Send player killcredit for quests with PlayerSlain
|
||||
if (_victim->GetTypeId() == TYPEID_PLAYER)
|
||||
player->KilledPlayerCredit();
|
||||
}
|
||||
// Give XP only in PvE or in battlegrounds.
|
||||
// Give reputation and kill credit only in PvE.
|
||||
if (!_isPvP || _isBattleGround)
|
||||
{
|
||||
float xpRate = _group ? _groupRate * float(player->getLevel()) / _aliveSumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
|
||||
float reputationRate = _group ? _groupRate * float(player->getLevel()) / _sumLevel : /*Personal rate is 100%.*/ 1.0f; // Group rate depends on the sum of levels.
|
||||
sScriptMgr->OnRewardKillRewarder(player, isDungeon, xpRate); // Personal rate is 100%.
|
||||
|
||||
if (_xp)
|
||||
{
|
||||
// 4.2. Give XP.
|
||||
_RewardXP(player, xpRate);
|
||||
}
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// If killer is in dungeon then all members receive full reputation at kill.
|
||||
_RewardReputation(player, isDungeon ? 1.0f : reputationRate);
|
||||
_RewardKillCredit(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::_RewardGroup()
|
||||
{
|
||||
if (_maxLevel)
|
||||
{
|
||||
if (_maxNotGrayMember)
|
||||
// 3.1.1. Initialize initial XP amount based on maximum level of group member,
|
||||
// for whom victim is not gray.
|
||||
_InitXP(_maxNotGrayMember);
|
||||
// To avoid unnecessary calculations and calls,
|
||||
// proceed only if XP is not ZERO or player is not on battleground
|
||||
// (battleground rewards only XP, that's why).
|
||||
if (!_isBattleGround || _xp)
|
||||
{
|
||||
const bool isDungeon = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsDungeon();
|
||||
if (!_isBattleGround)
|
||||
{
|
||||
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
|
||||
const bool isRaid = !_isPvP && sMapStore.LookupEntry(_killer->GetMapId())->IsRaid() && _group->isRaidGroup();
|
||||
_groupRate = Acore::XP::xp_in_group_rate(_count, isRaid);
|
||||
}
|
||||
|
||||
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
|
||||
for (GroupReference* itr = _group->GetFirstMember(); itr != nullptr; itr = itr->next())
|
||||
{
|
||||
if (Player* member = itr->GetSource())
|
||||
{
|
||||
if (_killer == member || member->IsAtGroupRewardDistance(_victim))
|
||||
{
|
||||
_RewardPlayer(member, isDungeon);
|
||||
// Xinef: only count players
|
||||
//if (_victim->GetTypeId() == TYPEID_PLAYER)
|
||||
// member->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL, 1, 0, _victim);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void KillRewarder::Reward()
|
||||
{
|
||||
// 3. Reward killer (and group, if necessary).
|
||||
if (_group)
|
||||
// 3.1. If killer is in group, reward group.
|
||||
_RewardGroup();
|
||||
else
|
||||
{
|
||||
// 3.2. Reward single killer (not group case).
|
||||
// 3.2.1. Initialize initial XP amount based on killer's level.
|
||||
_InitXP(_killer);
|
||||
// To avoid unnecessary calculations and calls,
|
||||
// proceed only if XP is not ZERO or player is not on battleground
|
||||
// (battleground rewards only XP, that's why).
|
||||
if (!_isBattleGround || _xp)
|
||||
// 3.2.2. Reward killer.
|
||||
if (_killer->IsInMap(_victim)) // pussywizard: killer may be on other map (crashfix), when killing in a group same map is required, so its not a problem
|
||||
_RewardPlayer(_killer, false);
|
||||
}
|
||||
|
||||
// 5. Credit instance encounter.
|
||||
if (Creature* victim = _victim->ToCreature())
|
||||
if (victim->IsDungeonBoss())
|
||||
if (Map* map = _victim->FindMap())
|
||||
map->UpdateEncounterState(ENCOUNTER_CREDIT_KILL_CREATURE, _victim->GetEntry(), _victim);
|
||||
}
|
||||
|
||||
// == Player ====================================================
|
||||
|
||||
// we can disable this warning for this since it only
|
||||
// causes undefined behavior when passed to the base class constructor
|
||||
#ifdef _MSC_VER
|
||||
|
||||
@@ -7,18 +7,21 @@
|
||||
#ifndef _PLAYER_H
|
||||
#define _PLAYER_H
|
||||
|
||||
#include "ArenaTeam.h"
|
||||
#include "Battleground.h"
|
||||
#include "DatabaseEnvFwd.h"
|
||||
#include "DBCStores.h"
|
||||
#include "GroupReference.h"
|
||||
#include "InstanceSaveMgr.h"
|
||||
#include "ArenaTeam.h"
|
||||
#include "Item.h"
|
||||
#include "KillerRewarder.h"
|
||||
#include "MapReference.h"
|
||||
#include "ObjectMgr.h"
|
||||
#include "PetDefines.h"
|
||||
#include "PlayerTaxi.h"
|
||||
#include "QuestDef.h"
|
||||
#include "SpellMgr.h"
|
||||
#include "TradeData.h"
|
||||
#include "Unit.h"
|
||||
#include "WorldSession.h"
|
||||
#include <string>
|
||||
@@ -676,14 +679,6 @@ struct ItemPosCount
|
||||
};
|
||||
typedef std::vector<ItemPosCount> ItemPosCountVec;
|
||||
|
||||
enum TradeSlots
|
||||
{
|
||||
TRADE_SLOT_COUNT = 7,
|
||||
TRADE_SLOT_TRADED_COUNT = 6,
|
||||
TRADE_SLOT_NONTRADED = 6,
|
||||
TRADE_SLOT_INVALID = -1,
|
||||
};
|
||||
|
||||
enum TransferAbortReason
|
||||
{
|
||||
TRANSFER_ABORT_NONE = 0x00,
|
||||
@@ -917,64 +912,6 @@ enum EmoteBroadcastTextID
|
||||
EMOTE_BROADCAST_TEXT_ID_STRANGE_GESTURES = 91243
|
||||
};
|
||||
|
||||
class PlayerTaxi
|
||||
{
|
||||
public:
|
||||
PlayerTaxi();
|
||||
~PlayerTaxi() = default;
|
||||
// Nodes
|
||||
void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
|
||||
void LoadTaxiMask(std::string const& data);
|
||||
|
||||
[[nodiscard]] bool IsTaximaskNodeKnown(uint32 nodeidx) const
|
||||
{
|
||||
uint8 field = uint8((nodeidx - 1) / 32);
|
||||
uint32 submask = 1 << ((nodeidx - 1) % 32);
|
||||
return (m_taximask[field] & submask) == submask;
|
||||
}
|
||||
bool SetTaximaskNode(uint32 nodeidx)
|
||||
{
|
||||
uint8 field = uint8((nodeidx - 1) / 32);
|
||||
uint32 submask = 1 << ((nodeidx - 1) % 32);
|
||||
if ((m_taximask[field] & submask) != submask)
|
||||
{
|
||||
m_taximask[field] |= submask;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void AppendTaximaskTo(ByteBuffer& data, bool all);
|
||||
|
||||
// Destinations
|
||||
bool LoadTaxiDestinationsFromString(std::string const& values, TeamId teamId);
|
||||
std::string SaveTaxiDestinationsToString();
|
||||
|
||||
void ClearTaxiDestinations() { m_TaxiDestinations.clear(); _taxiSegment = 0; }
|
||||
void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
|
||||
[[nodiscard]] uint32 GetTaxiSource() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment]; }
|
||||
[[nodiscard]] uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment + 1]; }
|
||||
[[nodiscard]] uint32 GetCurrentTaxiPath() const;
|
||||
uint32 NextTaxiDestination()
|
||||
{
|
||||
++_taxiSegment;
|
||||
return GetTaxiDestination();
|
||||
}
|
||||
|
||||
// xinef:
|
||||
void SetTaxiSegment(uint32 segment) { _taxiSegment = segment; }
|
||||
[[nodiscard]] uint32 GetTaxiSegment() const { return _taxiSegment; }
|
||||
|
||||
[[nodiscard]] std::vector<uint32> const& GetPath() const { return m_TaxiDestinations; }
|
||||
[[nodiscard]] bool empty() const { return m_TaxiDestinations.empty(); }
|
||||
|
||||
friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
private:
|
||||
TaxiMask m_taximask;
|
||||
std::vector<uint32> m_TaxiDestinations;
|
||||
uint32 _taxiSegment;
|
||||
};
|
||||
|
||||
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
|
||||
class Player;
|
||||
@@ -1012,87 +949,6 @@ struct EntryPointData
|
||||
[[nodiscard]] bool HasTaxiPath() const { return !taxiPath.empty(); }
|
||||
};
|
||||
|
||||
class TradeData
|
||||
{
|
||||
public: // constructors
|
||||
TradeData(Player* player, Player* trader) : m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false), m_money(0), m_spell(0)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] Player* GetTrader() const { return m_trader; }
|
||||
[[nodiscard]] TradeData* GetTraderData() const;
|
||||
|
||||
[[nodiscard]] Item* GetItem(TradeSlots slot) const;
|
||||
[[nodiscard]] bool HasItem(ObjectGuid itemGuid) const;
|
||||
[[nodiscard]] TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid) const;
|
||||
void SetItem(TradeSlots slot, Item* item);
|
||||
|
||||
[[nodiscard]] uint32 GetSpell() const { return m_spell; }
|
||||
void SetSpell(uint32 spell_id, Item* castItem = nullptr);
|
||||
|
||||
[[nodiscard]] Item* GetSpellCastItem() const;
|
||||
[[nodiscard]] bool HasSpellCastItem() const { return m_spellCastItem; }
|
||||
|
||||
[[nodiscard]] uint32 GetMoney() const { return m_money; }
|
||||
void SetMoney(uint32 money);
|
||||
|
||||
[[nodiscard]] bool IsAccepted() const { return m_accepted; }
|
||||
void SetAccepted(bool state, bool crosssend = false);
|
||||
|
||||
[[nodiscard]] bool IsInAcceptProcess() const { return m_acceptProccess; }
|
||||
void SetInAcceptProcess(bool state) { m_acceptProccess = state; }
|
||||
|
||||
private: // internal functions
|
||||
void Update(bool for_trader = true);
|
||||
|
||||
private: // fields
|
||||
Player* m_player; // Player who own of this TradeData
|
||||
Player* m_trader; // Player who trade with m_player
|
||||
|
||||
bool m_accepted; // m_player press accept for trade list
|
||||
bool m_acceptProccess; // one from player/trader press accept and this processed
|
||||
|
||||
uint32 m_money; // m_player place money to trade
|
||||
|
||||
uint32 m_spell; // m_player apply spell to non-traded slot item
|
||||
ObjectGuid m_spellCastItem; // applied spell casted by item use
|
||||
|
||||
ObjectGuid m_items[TRADE_SLOT_COUNT]; // traded itmes from m_player side including non-traded slot
|
||||
};
|
||||
|
||||
class KillRewarder
|
||||
{
|
||||
public:
|
||||
KillRewarder(Player* killer, Unit* victim, bool isBattleGround);
|
||||
|
||||
void Reward();
|
||||
|
||||
private:
|
||||
void _InitXP(Player* player);
|
||||
void _InitGroupData();
|
||||
|
||||
void _RewardHonor(Player* player);
|
||||
void _RewardXP(Player* player, float rate);
|
||||
void _RewardReputation(Player* player, float rate);
|
||||
void _RewardKillCredit(Player* player);
|
||||
void _RewardPlayer(Player* player, bool isDungeon);
|
||||
void _RewardGroup();
|
||||
|
||||
Player* _killer;
|
||||
Unit* _victim;
|
||||
Group* _group;
|
||||
float _groupRate;
|
||||
Player* _maxNotGrayMember;
|
||||
uint32 _count;
|
||||
uint32 _aliveSumLevel;
|
||||
uint32 _sumLevel;
|
||||
uint32 _xp;
|
||||
bool _isFullXP;
|
||||
uint8 _maxLevel;
|
||||
bool _isBattleGround;
|
||||
bool _isPvP;
|
||||
};
|
||||
|
||||
class Player : public Unit, public GridObject<Player>
|
||||
{
|
||||
friend class WorldSession;
|
||||
|
||||
174
src/server/game/Entities/Player/PlayerTaxi.cpp
Normal file
174
src/server/game/Entities/Player/PlayerTaxi.cpp
Normal file
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
#include "Player.h"
|
||||
|
||||
PlayerTaxi::PlayerTaxi() : _taxiSegment(0)
|
||||
{
|
||||
memset(m_taximask, 0, sizeof(m_taximask));
|
||||
}
|
||||
|
||||
void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level)
|
||||
{
|
||||
// class specific initial known nodes
|
||||
switch (chrClass)
|
||||
{
|
||||
case CLASS_DEATH_KNIGHT:
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; ++i)
|
||||
m_taximask[i] |= sOldContinentsNodesMask[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// race specific initial known nodes: capital and taxi hub masks
|
||||
switch (race)
|
||||
{
|
||||
case RACE_HUMAN:
|
||||
SetTaximaskNode(2);
|
||||
break; // Human
|
||||
case RACE_ORC:
|
||||
SetTaximaskNode(23);
|
||||
break; // Orc
|
||||
case RACE_DWARF:
|
||||
SetTaximaskNode(6);
|
||||
break; // Dwarf
|
||||
case RACE_NIGHTELF:
|
||||
SetTaximaskNode(26);
|
||||
SetTaximaskNode(27);
|
||||
break; // Night Elf
|
||||
case RACE_UNDEAD_PLAYER:
|
||||
SetTaximaskNode(11);
|
||||
break;// Undead
|
||||
case RACE_TAUREN:
|
||||
SetTaximaskNode(22);
|
||||
break; // Tauren
|
||||
case RACE_GNOME:
|
||||
SetTaximaskNode(6);
|
||||
break; // Gnome
|
||||
case RACE_TROLL:
|
||||
SetTaximaskNode(23);
|
||||
break; // Troll
|
||||
case RACE_BLOODELF:
|
||||
SetTaximaskNode(82);
|
||||
break; // Blood Elf
|
||||
case RACE_DRAENEI:
|
||||
SetTaximaskNode(94);
|
||||
break; // Draenei
|
||||
}
|
||||
|
||||
// new continent starting masks (It will be accessible only at new map)
|
||||
switch (Player::TeamIdForRace(race))
|
||||
{
|
||||
case TEAM_ALLIANCE:
|
||||
SetTaximaskNode(100);
|
||||
break;
|
||||
case TEAM_HORDE:
|
||||
SetTaximaskNode(99);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
// level dependent taxi hubs
|
||||
if (level >= 68)
|
||||
SetTaximaskNode(213); //Shattered Sun Staging Area
|
||||
}
|
||||
|
||||
void PlayerTaxi::LoadTaxiMask(std::string const& data)
|
||||
{
|
||||
Tokenizer tokens(data, ' ');
|
||||
|
||||
uint8 index;
|
||||
Tokenizer::const_iterator iter;
|
||||
for (iter = tokens.begin(), index = 0;
|
||||
(index < TaxiMaskSize) && (iter != tokens.end()); ++iter, ++index)
|
||||
{
|
||||
// load and set bits only for existed taxi nodes
|
||||
m_taximask[index] = sTaxiNodesMask[index] & uint32(atol(*iter));
|
||||
}
|
||||
}
|
||||
|
||||
void PlayerTaxi::AppendTaximaskTo(ByteBuffer& data, bool all)
|
||||
{
|
||||
if (all)
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; i++)
|
||||
data << uint32(sTaxiNodesMask[i]); // all existed nodes
|
||||
}
|
||||
else
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; i++)
|
||||
data << uint32(m_taximask[i]); // known nodes
|
||||
}
|
||||
}
|
||||
|
||||
bool PlayerTaxi::LoadTaxiDestinationsFromString(const std::string& values, TeamId teamId)
|
||||
{
|
||||
ClearTaxiDestinations();
|
||||
|
||||
Tokenizer tokens(values, ' ');
|
||||
|
||||
for (Tokenizer::const_iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
|
||||
{
|
||||
uint32 node = uint32(atol(*iter));
|
||||
AddTaxiDestination(node);
|
||||
}
|
||||
|
||||
// Check integrity
|
||||
if (m_TaxiDestinations.size() < 3)
|
||||
return false;
|
||||
|
||||
// xinef: current segment is saved as last destination in db
|
||||
_taxiSegment = m_TaxiDestinations[m_TaxiDestinations.size() - 1];
|
||||
m_TaxiDestinations.pop_back();
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size() - 1; ++i)
|
||||
{
|
||||
uint32 cost;
|
||||
uint32 path;
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[i], m_TaxiDestinations[i + 1], path, cost);
|
||||
if (!path)
|
||||
return false;
|
||||
}
|
||||
|
||||
// can't load taxi path without mount set (quest taxi path?)
|
||||
if (!sObjectMgr->GetTaxiMountDisplayId(GetTaxiSource(), teamId, true))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string PlayerTaxi::SaveTaxiDestinationsToString()
|
||||
{
|
||||
if (m_TaxiDestinations.empty())
|
||||
return "";
|
||||
|
||||
std::ostringstream ss;
|
||||
|
||||
for (size_t i = 0; i < m_TaxiDestinations.size(); ++i)
|
||||
ss << m_TaxiDestinations[i] << ' ';
|
||||
|
||||
ss << _taxiSegment << ' ';
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
uint32 PlayerTaxi::GetCurrentTaxiPath() const
|
||||
{
|
||||
if (m_TaxiDestinations.size() < 2 || m_TaxiDestinations.size() <= _taxiSegment + 1)
|
||||
return 0;
|
||||
|
||||
uint32 path;
|
||||
uint32 cost;
|
||||
|
||||
sObjectMgr->GetTaxiPath(m_TaxiDestinations[_taxiSegment], m_TaxiDestinations[_taxiSegment + 1], path, cost);
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
|
||||
{
|
||||
for (uint8 i = 0; i < TaxiMaskSize; ++i)
|
||||
ss << taxi.m_taximask[i] << ' ';
|
||||
return ss;
|
||||
}
|
||||
61
src/server/game/Entities/Player/PlayerTaxi.h
Normal file
61
src/server/game/Entities/Player/PlayerTaxi.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
class PlayerTaxi
|
||||
{
|
||||
public:
|
||||
PlayerTaxi();
|
||||
~PlayerTaxi() = default;
|
||||
// Nodes
|
||||
void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint8 level);
|
||||
void LoadTaxiMask(std::string const& data);
|
||||
|
||||
[[nodiscard]] bool IsTaximaskNodeKnown(uint32 nodeidx) const
|
||||
{
|
||||
uint8 field = uint8((nodeidx - 1) / 32);
|
||||
uint32 submask = 1 << ((nodeidx - 1) % 32);
|
||||
return (m_taximask[field] & submask) == submask;
|
||||
}
|
||||
bool SetTaximaskNode(uint32 nodeidx)
|
||||
{
|
||||
uint8 field = uint8((nodeidx - 1) / 32);
|
||||
uint32 submask = 1 << ((nodeidx - 1) % 32);
|
||||
if ((m_taximask[field] & submask) != submask)
|
||||
{
|
||||
m_taximask[field] |= submask;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
}
|
||||
void AppendTaximaskTo(ByteBuffer& data, bool all);
|
||||
|
||||
// Destinations
|
||||
bool LoadTaxiDestinationsFromString(std::string const& values, TeamId teamId);
|
||||
std::string SaveTaxiDestinationsToString();
|
||||
|
||||
void ClearTaxiDestinations() { m_TaxiDestinations.clear(); _taxiSegment = 0; }
|
||||
void AddTaxiDestination(uint32 dest) { m_TaxiDestinations.push_back(dest); }
|
||||
[[nodiscard]] uint32 GetTaxiSource() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment]; }
|
||||
[[nodiscard]] uint32 GetTaxiDestination() const { return m_TaxiDestinations.size() <= _taxiSegment + 1 ? 0 : m_TaxiDestinations[_taxiSegment + 1]; }
|
||||
[[nodiscard]] uint32 GetCurrentTaxiPath() const;
|
||||
uint32 NextTaxiDestination()
|
||||
{
|
||||
++_taxiSegment;
|
||||
return GetTaxiDestination();
|
||||
}
|
||||
|
||||
// xinef:
|
||||
void SetTaxiSegment(uint32 segment) { _taxiSegment = segment; }
|
||||
[[nodiscard]] uint32 GetTaxiSegment() const { return _taxiSegment; }
|
||||
|
||||
[[nodiscard]] std::vector<uint32> const& GetPath() const { return m_TaxiDestinations; }
|
||||
[[nodiscard]] bool empty() const { return m_TaxiDestinations.empty(); }
|
||||
|
||||
friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
|
||||
private:
|
||||
TaxiMask m_taximask;
|
||||
std::vector<uint32> m_TaxiDestinations;
|
||||
uint32 _taxiSegment;
|
||||
};
|
||||
117
src/server/game/Entities/Player/TradeData.cpp
Normal file
117
src/server/game/Entities/Player/TradeData.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
#include "Player.h"
|
||||
|
||||
TradeData* TradeData::GetTraderData() const
|
||||
{
|
||||
return m_trader->GetTradeData();
|
||||
}
|
||||
|
||||
Item* TradeData::GetItem(TradeSlots slot) const
|
||||
{
|
||||
return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : nullptr;
|
||||
}
|
||||
|
||||
bool TradeData::HasItem(ObjectGuid itemGuid) const
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||
if (m_items[i] == itemGuid)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const
|
||||
{
|
||||
for (uint8 i = 0; i < TRADE_SLOT_COUNT; ++i)
|
||||
if (m_items[i] == itemGuid)
|
||||
return TradeSlots(i);
|
||||
|
||||
return TRADE_SLOT_INVALID;
|
||||
}
|
||||
|
||||
Item* TradeData::GetSpellCastItem() const
|
||||
{
|
||||
return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : nullptr;
|
||||
}
|
||||
|
||||
void TradeData::SetItem(TradeSlots slot, Item* item)
|
||||
{
|
||||
ObjectGuid itemGuid = item ? item->GetGUID() : ObjectGuid::Empty;
|
||||
|
||||
if (m_items[slot] == itemGuid)
|
||||
return;
|
||||
|
||||
m_items[slot] = itemGuid;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update();
|
||||
|
||||
// need remove possible trader spell applied to changed item
|
||||
if (slot == TRADE_SLOT_NONTRADED)
|
||||
GetTraderData()->SetSpell(0);
|
||||
|
||||
// need remove possible player spell applied (possible move reagent)
|
||||
SetSpell(0);
|
||||
}
|
||||
|
||||
void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= nullptr*/)
|
||||
{
|
||||
ObjectGuid itemGuid = castItem ? castItem->GetGUID() : ObjectGuid::Empty;
|
||||
|
||||
if (m_spell == spell_id && m_spellCastItem == itemGuid)
|
||||
return;
|
||||
|
||||
m_spell = spell_id;
|
||||
m_spellCastItem = itemGuid;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update(true); // send spell info to item owner
|
||||
Update(false); // send spell info to caster self
|
||||
}
|
||||
|
||||
void TradeData::SetMoney(uint32 money)
|
||||
{
|
||||
if (m_money == money)
|
||||
return;
|
||||
|
||||
if (!m_player->HasEnoughMoney(money))
|
||||
{
|
||||
m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BUSY);
|
||||
return;
|
||||
}
|
||||
|
||||
m_money = money;
|
||||
|
||||
SetAccepted(false);
|
||||
GetTraderData()->SetAccepted(false);
|
||||
|
||||
Update(true);
|
||||
}
|
||||
|
||||
void TradeData::Update(bool forTarget /*= true*/)
|
||||
{
|
||||
if (forTarget)
|
||||
m_trader->GetSession()->SendUpdateTrade(true); // player state for trader
|
||||
else
|
||||
m_player->GetSession()->SendUpdateTrade(false); // player state for player
|
||||
}
|
||||
|
||||
void TradeData::SetAccepted(bool state, bool crosssend /*= false*/)
|
||||
{
|
||||
m_accepted = state;
|
||||
|
||||
if (!state)
|
||||
{
|
||||
if (crosssend)
|
||||
m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
|
||||
else
|
||||
m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
|
||||
}
|
||||
}
|
||||
59
src/server/game/Entities/Player/TradeData.h
Normal file
59
src/server/game/Entities/Player/TradeData.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU AGPL v3 license: https://github.com/azerothcore/azerothcore-wotlk/blob/master/LICENSE-AGPL3
|
||||
*/
|
||||
|
||||
enum TradeSlots
|
||||
{
|
||||
TRADE_SLOT_COUNT = 7,
|
||||
TRADE_SLOT_TRADED_COUNT = 6,
|
||||
TRADE_SLOT_NONTRADED = 6,
|
||||
TRADE_SLOT_INVALID = -1,
|
||||
};
|
||||
|
||||
class TradeData
|
||||
{
|
||||
public: // constructors
|
||||
TradeData(Player* player, Player* trader) : m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false), m_money(0), m_spell(0)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] Player* GetTrader() const { return m_trader; }
|
||||
[[nodiscard]] TradeData* GetTraderData() const;
|
||||
|
||||
[[nodiscard]] Item* GetItem(TradeSlots slot) const;
|
||||
[[nodiscard]] bool HasItem(ObjectGuid itemGuid) const;
|
||||
[[nodiscard]] TradeSlots GetTradeSlotForItem(ObjectGuid itemGuid) const;
|
||||
void SetItem(TradeSlots slot, Item* item);
|
||||
|
||||
[[nodiscard]] uint32 GetSpell() const { return m_spell; }
|
||||
void SetSpell(uint32 spell_id, Item* castItem = nullptr);
|
||||
|
||||
[[nodiscard]] Item* GetSpellCastItem() const;
|
||||
[[nodiscard]] bool HasSpellCastItem() const { return m_spellCastItem; }
|
||||
|
||||
[[nodiscard]] uint32 GetMoney() const { return m_money; }
|
||||
void SetMoney(uint32 money);
|
||||
|
||||
[[nodiscard]] bool IsAccepted() const { return m_accepted; }
|
||||
void SetAccepted(bool state, bool crosssend = false);
|
||||
|
||||
[[nodiscard]] bool IsInAcceptProcess() const { return m_acceptProccess; }
|
||||
void SetInAcceptProcess(bool state) { m_acceptProccess = state; }
|
||||
|
||||
private: // internal functions
|
||||
void Update(bool for_trader = true);
|
||||
|
||||
private: // fields
|
||||
Player* m_player; // Player who own of this TradeData
|
||||
Player* m_trader; // Player who trade with m_player
|
||||
|
||||
bool m_accepted; // m_player press accept for trade list
|
||||
bool m_acceptProccess; // one from player/trader press accept and this processed
|
||||
|
||||
uint32 m_money; // m_player place money to trade
|
||||
|
||||
uint32 m_spell; // m_player apply spell to non-traded slot item
|
||||
ObjectGuid m_spellCastItem; // applied spell casted by item use
|
||||
|
||||
ObjectGuid m_items[TRADE_SLOT_COUNT]; // traded itmes from m_player side including non-traded slot
|
||||
};
|
||||
Reference in New Issue
Block a user