1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
|
/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* 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 2 of the License, or (at your
* option) 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 <http://www.gnu.org/licenses/>.
*/
#include "KillRewarder.h"
#include "Creature.h"
#include "DB2Stores.h"
#include "FlatSet.h"
#include "Formulas.h"
#include "Group.h"
#include "Guild.h"
#include "GuildMgr.h"
#include "Pet.h"
#include "Player.h"
#include "Scenario.h"
#include "SpellAuraEffects.h"
#include <boost/container/small_vector.hpp>
// == KillRewarder ====================================================
// KillRewarder encapsulates 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. _sumLevel - sum of levels of alive group members within reward distance;
// 2.3. _maxLevel - maximum level of alive group member within reward distance;
// 2.4. _maxNotGrayMember - maximum level of alive group member within reward distance,
// for whom victim is not gray;
// 2.5. _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.
// 6. Update guild achievements.
// 7. Scenario credit
KillRewarder::KillRewarder(Trinity::IteratorPair<Player**> killers, Unit* victim, bool isBattleGround) :
// 1. Initialize internal variables to default values.
_killers(killers), _victim(victim),
_groupRate(1.0f), _maxNotGrayMember(nullptr), _count(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();
}
inline void KillRewarder::_InitGroupData(Player const* killer)
{
if (Group const* group = killer->GetGroup())
{
// 2. In case when player is in group, initialize variables necessary for group calculations:
for (GroupReference const& itr : group->GetMembers())
{
Player* member = itr.GetSource();
if (killer == member || (member->IsAtGroupRewardDistance(_victim) && member->IsAlive()))
{
const uint8 lvl = member->GetLevel();
// 2.1. _count - number of alive group members within reward distance;
++_count;
// 2.2. _sumLevel - sum of levels of alive group members within reward distance;
_sumLevel += 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 = Trinity::XP::GetGrayLevel(lvl);
if (_victim->GetLevelForTarget(member) > grayLevel && (!_maxNotGrayMember || _maxNotGrayMember->GetLevel() < lvl))
_maxNotGrayMember = member;
}
}
// 2.5. _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, Player const* killer)
{
// Get initial value of XP for kill.
// XP is given:
// * on battlegrounds;
// * otherwise, not in PvP;
// * not if killer is on vehicle.
if (_isBattleGround || (!_isPvP && !killer->GetVehicle()))
_xp = Trinity::XP::Gain(player, _victim, _isBattleGround);
}
inline void KillRewarder::_RewardHonor(Player* player)
{
// Rewarded player must be alive.
if (player->IsAlive())
player->RewardHonor(_victim, _count, -1, HonorGainSource::Kill);
}
inline void KillRewarder::_RewardXP(Player* player, float rate)
{
uint32 xp(_xp);
if (player->GetGroup())
{
// 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 and SPELL_AURA_MOD_XP_FROM_CREATURE_TYPE).
xp *= player->GetTotalAuraMultiplier(SPELL_AURA_MOD_XP_PCT);
xp *= player->GetTotalAuraMultiplierByMiscValue(SPELL_AURA_MOD_XP_FROM_CREATURE_TYPE, int32(_victim->GetCreatureType()));
// 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(player->GetGroup() ? 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 (!player->GetGroup() || player->IsAlive() || !player->GetCorpse())
{
if (Creature* target = _victim->ToCreature())
{
player->KilledMonster(target);
player->UpdateCriteria(CriteriaType::KillAnyCreature, target->GetCreatureType(), 1, 0, 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(_victim->GetGUID());
}
// Give XP only in PvE or in battlegrounds.
// Give reputation and kill credit only in PvE.
if (!_isPvP || _isBattleGround)
{
float const rate = player->GetGroup() ?
_groupRate * float(player->GetLevel()) / _sumLevel : // Group rate depends on summary level.
1.0f; // Personal rate is 100%.
if (_xp)
// 4.2. Give XP.
_RewardXP(player, rate);
if (!_isBattleGround)
{
// If killer is in dungeon then all members receive full reputation at kill.
_RewardReputation(player, isDungeon ? 1.0f : rate);
_RewardKillCredit(player);
}
}
}
void KillRewarder::_RewardGroup(Group const* group, Player const* killer)
{
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, 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)
{
bool const isDungeon = !_isPvP && sMapStore.LookupEntry(killer->GetMapId())->IsDungeon();
if (!_isBattleGround)
{
// 3.1.2. Alter group rate if group is in raid (not for battlegrounds).
bool const isRaid = !_isPvP && sMapStore.LookupEntry(killer->GetMapId())->IsRaid() && group->isRaidGroup();
_groupRate = Trinity::XP::xp_in_group_rate(_count, isRaid);
}
// 3.1.3. Reward each group member (even dead or corpse) within reward distance.
for (GroupReference const& itr : group->GetMembers())
{
Player* member = itr.GetSource();
// Killer may not be at reward distance, check directly
if (killer == member || member->IsAtGroupRewardDistance(_victim))
_RewardPlayer(member, isDungeon);
}
}
}
}
void KillRewarder::Reward()
{
Trinity::Containers::FlatSet<Group const*, std::less<>, boost::container::small_vector<Group const*, 3>> processedGroups;
for (Player* killer : _killers)
{
_InitGroupData(killer);
// 3. Reward killer (and group, if necessary).
if (Group* group = killer->GetGroup())
{
if (!processedGroups.insert(group).second)
continue;
// 3.1. If killer is in group, reward group.
_RewardGroup(group, killer);
}
else
{
// 3.2. Reward single killer (not group case).
// 3.2.1. Initialize initial XP amount based on killer's level.
_InitXP(killer, 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.
_RewardPlayer(killer, false);
}
}
// 5. Credit instance encounter.
// 6. Update guild achievements.
// 7. Credit scenario criterias
if (Creature* victim = _victim->ToCreature())
{
if (_killers.begin() != _killers.end())
{
if (ObjectGuid::LowType guildId = victim->GetMap()->GetOwnerGuildId())
if (Guild* guild = sGuildMgr->GetGuildById(guildId))
guild->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, *_killers.begin());
if (Scenario* scenario = victim->GetScenario())
scenario->UpdateCriteria(CriteriaType::KillCreature, victim->GetEntry(), 1, 0, victim, *_killers.begin());
}
}
}
|