View difference between Paste ID: R72WLAb5 and MACDS1Bi
SHOW: | | - or go back to the newest paste.
1
////////////////////////////////////////////////////////////////////////
2
// OpenTibia - an opensource roleplaying game
3
////////////////////////////////////////////////////////////////////////
4
// This program is free software: you can redistribute it and/or modify
5
// it under the terms of the GNU General Public License as published by
6
// the Free Software Foundation, either version 3 of the License, or
7
// (at your option) any later version.
8
//
9
// This program is distributed in the hope that it will be useful,
10
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
// GNU General Public License for more details.
13
//
14
// You should have received a copy of the GNU General Public License
15
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
16
////////////////////////////////////////////////////////////////////////
17
#include "otpch.h"
18
#include <iostream>
19
20
#include "player.h"
21
#include "manager.h"
22
23
#include "iologindata.h"
24
#include "ioban.h"
25
26
#include "town.h"
27
#include "house.h"
28
#include "beds.h"
29
30
#include "combat.h"
31
#include "movement.h"
32
#include "weapons.h"
33
#include "creatureevent.h"
34
35
#include "configmanager.h"
36
#include "game.h"
37
#include "chat.h"
38
#include "textlogger.h"
39
40
#if defined(WINDOWS) && !defined(__CONSOLE__)
41
#include "gui.h"
42
#endif
43
44
extern ConfigManager g_config;
45
extern Game g_game;
46
extern Chat g_chat;
47
extern MoveEvents* g_moveEvents;
48
extern Weapons* g_weapons;
49
extern CreatureEvents* g_creatureEvents;
50
51
AutoList<Player> Player::autoList;
52
#ifdef __ENABLE_SERVER_DIAGNOSTIC__
53
uint32_t Player::playerCount = 0;
54
#endif
55
MuteCountMap Player::muteCountMap;
56
57
Player::Player(const std::string& _name, ProtocolGame* p):
58
	Creature(), transferContainer(ITEM_LOCKER), name(_name), nameDescription(_name), client(p)
59
{
60
	if(client)
61
		client->setPlayer(this);
62
63
	pzLocked = isConnecting = addAttackSkillPoint = requestedOutfit = false;
64
	saving = true;
65
66
	lastAttackBlockType = BLOCK_NONE;
67
	chaseMode = CHASEMODE_STANDSTILL;
68
	fightMode = FIGHTMODE_ATTACK;
69
	tradeState = TRADE_NONE;
70
	accountManager = MANAGER_NONE;
71
	guildLevel = GUILDLEVEL_NONE;
72
73
	promotionLevel = walkTaskEvent = actionTaskEvent = nextStepEvent = bloodHitCount = shieldBlockCount = 0;
74
	lastAttack = mailAttempts = idleTime = marriage = blessings = balance = premiumDays = mana = manaMax = manaSpent = 0;
75
	soul = guildId = levelPercent = magLevelPercent = magLevel = experience = damageImmunities = rankId = 0;
76
	conditionImmunities = conditionSuppressions = groupId = vocationId = managerNumber2 = town = skullEnd = 0;
77
	lastLogin = lastLogout = lastIP = messageTicks = messageBuffer = nextAction = editListId = maxWriteLen = 0;
78
	windowTextId = 0;
79
80
	purchaseCallback = saleCallback = -1;
81
	level = shootRange = 1;
82
	rates[SKILL__MAGLEVEL] = rates[SKILL__LEVEL] = 1.0f;
83
	soulMax = 100;
84
	capacity = 400.00;
85
	stamina = STAMINA_MAX;
86
	lastLoad = lastPing = lastPong = lastMail = OTSYS_TIME();
87
88
	writeItem = NULL;
89
	group = NULL;
90
	editHouse = NULL;
91
	shopOwner = NULL;
92
	tradeItem = NULL;
93
	tradePartner = NULL;
94
	walkTask = NULL;
95
	weapon = NULL;
96
97
	setVocation(0);
98
	setParty(NULL);
99
100
	transferContainer.setParent(NULL);
101
	for(int32_t i = 0; i < 11; i++)
102
	{
103
		inventory[i] = NULL;
104
		inventoryAbilities[i] = false;
105
	}
106
107
	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
108
	{
109
		skills[i][SKILL_LEVEL] = 10;
110
		skills[i][SKILL_TRIES] = skills[i][SKILL_PERCENT] = 0;
111
		rates[i] = 1.0f;
112
	}
113
114
	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
115
		varSkills[i] = 0;
116
117
	for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i)
118
		varStats[i] = 0;
119
120
	for(int32_t i = LOSS_FIRST; i <= LOSS_LAST; ++i)
121
		lossPercent[i] = 100;
122
123
	for(int32_t i = 0; i <= 12; i++)
124
		talkState[i] = false;
125
#ifdef __ENABLE_SERVER_DIAGNOSTIC__
126
127
	playerCount++;
128
#endif
129
}
130
131
Player::~Player()
132
{
133
#ifdef __ENABLE_SERVER_DIAGNOSTIC__
134
	playerCount--;
135
#endif
136
	setWriteItem(NULL);
137
	for(int32_t i = 0; i < 11; i++)
138
	{
139
		if(inventory[i])
140
		{
141
			inventory[i]->setParent(NULL);
142
			inventory[i]->unRef();
143
144
			inventory[i] = NULL;
145
			inventoryAbilities[i] = false;
146
		}
147
	}
148
149
	setNextWalkActionTask(NULL);
150
	transferContainer.setParent(NULL);
151
	for(DepotMap::iterator it = depots.begin(); it != depots.end(); it++)
152
		it->second.first->unRef();
153
}
154
155
void Player::setVocation(uint32_t id)
156
{
157
	vocationId = id;
158
	vocation = Vocations::getInstance()->getVocation(id);
159
160
	soulMax = vocation->getGain(GAIN_SOUL);
161
	if(Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT))
162
	{
163
		condition->setParam(CONDITIONPARAM_HEALTHGAIN, vocation->getGainAmount(GAIN_HEALTH));
164
		condition->setParam(CONDITIONPARAM_HEALTHTICKS, (vocation->getGainTicks(GAIN_HEALTH) * 1000));
165
		condition->setParam(CONDITIONPARAM_MANAGAIN, vocation->getGainAmount(GAIN_MANA));
166
		condition->setParam(CONDITIONPARAM_MANATICKS, (vocation->getGainTicks(GAIN_MANA) * 1000));
167
	}
168
}
169
170
bool Player::isPushable() const
171
{
172
	return accountManager == MANAGER_NONE && !hasFlag(PlayerFlag_CannotBePushed) && Creature::isPushable();
173
}
174
175
std::string Player::getDescription(int32_t lookDistance) const
176
{
177
	std::stringstream s;
178
	if(lookDistance == -1)
179
	{
180
		s << "yourself.";
181
		if(hasFlag(PlayerFlag_ShowGroupNameInsteadOfVocation))
182
			s << " You are " << group->getName();
183
		else if(vocationId != 0)
184
			s << " You are " << vocation->getDescription();
185
		else
186
			s << " You have no vocation";
187
	}
188
	else
189
	{
190
		s << nameDescription;
191
		if(!hasCustomFlag(PlayerCustomFlag_HideLevel))
192
			s << " (Level " << level << ")";
193
194
		s << ". " << (sex % 2 ? "He" : "She");
195
		if(hasFlag(PlayerFlag_ShowGroupNameInsteadOfVocation))
196
			s << " is " << group->getName();
197
		else if(vocationId != 0)
198
			s << " is " << vocation->getDescription();
199
		else
200
			s << " has no vocation";
201
202
		s << getSpecialDescription();
203
	}
204
205
	std::string tmp;
206
	if(marriage && IOLoginData::getInstance()->getNameByGuid(marriage, tmp))
207
	{
208
		s << ", ";
209
		if(vocationId == 0)
210
		{
211
			if(lookDistance == -1)
212
				s << "and you are";
213
			else
214
				s << "and is";
215
216
			s << " ";
217
		}
218
219
		s << (sex % 2 ? "husband" : "wife") << " of " << tmp;
220
	}
221
222
	s << ".";
223
	if(guildId)
224
	{
225
		if(lookDistance == -1)
226
			s << " You are ";
227
		else
228
			s << " " << (sex % 2 ? "He" : "She") << " is ";
229
230
		s << (rankName.empty() ? "a member" : rankName)<< " of the " << guildName;
231
		if(!guildNick.empty())
232
			s << " (" << guildNick << ")";
233
234
		s << ".";
235
	}
236
237
	return s.str();
238
}
239
240
Item* Player::getInventoryItem(slots_t slot) const
241
{
242
	if(slot > SLOT_PRE_FIRST && slot < SLOT_LAST)
243
		return inventory[slot];
244
245
	if(slot == SLOT_HAND)
246
		return inventory[SLOT_LEFT] ? inventory[SLOT_LEFT] : inventory[SLOT_RIGHT];
247
248
	return NULL;
249
}
250
251
Item* Player::getEquippedItem(slots_t slot) const
252
{
253
	Item* item = getInventoryItem(slot);
254
	if(!item)
255
		return NULL;
256
257
	switch(slot)
258
	{
259
		case SLOT_LEFT:
260
		case SLOT_RIGHT:
261
			return item->getWieldPosition() == SLOT_HAND ? item : NULL;
262
263
		default:
264
			break;
265
	}
266
267
	return item->getWieldPosition() == slot ? item : NULL;
268
}
269
270
void Player::setConditionSuppressions(uint32_t conditions, bool remove)
271
{
272
	if(remove)
273
		conditionSuppressions &= ~conditions;
274
	else
275
		conditionSuppressions |= conditions;
276
}
277
278
Item* Player::getWeapon(bool ignoreAmmo)
279
{
280
	if(weapon)
281
		return weapon;
282
283
	Item* item = NULL;
284
	for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot)
285
	{
286
		if(!(item = getEquippedItem((slots_t)slot)) || item->getWeaponType() != WEAPON_DIST)
287
			continue;
288
289
		if(!ignoreAmmo && item->getAmmoType() != AMMO_NONE)
290
		{
291
			Item* ammoItem = getInventoryItem(SLOT_AMMO);
292
			if(ammoItem && ammoItem->getAmmoType() == item->getAmmoType())
293
			{
294
				if(g_weapons->getWeapon(ammoItem))
295
				{
296
					shootRange = item->getShootRange();
297
					return ammoItem;
298
				}
299
			}
300
		}
301
		else if(g_weapons->getWeapon(item))
302
		{
303
			shootRange = item->getShootRange();
304
			return item;
305
		}
306
	}
307
308
	return NULL;
309
}
310
311
ItemVector Player::getWeapons() const
312
{
313
	Item* item = NULL;
314
	ItemVector weapons;
315
	for(int32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot)
316
	{
317
		if(!(item = getEquippedItem((slots_t)slot)))
318
			continue;
319
320
		switch(item->getWeaponType())
321
		{
322
			case WEAPON_DIST:
323
				if(item->getAmmoType() != AMMO_NONE)
324
					break;
325
326
			case WEAPON_SWORD:
327
			case WEAPON_CLUB:
328
			case WEAPON_AXE:
329
			case WEAPON_FIST:
330
			case WEAPON_WAND:
331
			{
332
				if(g_weapons->getWeapon(item))
333
					weapons.push_back(item);
334
335
				break;
336
			}
337
338
			default:
339
				break;
340
		}
341
	}
342
343
	return weapons;
344
}
345
346
void Player::updateWeapon()
347
{
348
	ItemVector weapons = getWeapons();
349
	if(weapons.empty())
350
		weapon = NULL;
351
	else if(!weapon || weapons.size() == 1 || weapons[1] == weapon)
352
		weapon = weapons[0];
353
	else if(weapons[0] == weapon)
354
		weapon = weapons[1];
355
	else
356
		weapon = NULL;
357
358
	if(weapon)
359
		shootRange = weapon->getShootRange();
360
}
361
362
WeaponType_t Player::getWeaponType()
363
{
364
	if(Item* item = getWeapon(false))
365
		return item->getWeaponType();
366
367
	return WEAPON_NONE;
368
}
369
370
int32_t Player::getWeaponSkill(const Item* item) const
371
{
372
	if(!item)
373
		return getSkill(SKILL_FIST, SKILL_LEVEL);
374
375
	switch(item->getWeaponType())
376
	{
377
		case WEAPON_SWORD:
378
			return getSkill(SKILL_SWORD, SKILL_LEVEL);
379
380
		case WEAPON_CLUB:
381
			return getSkill(SKILL_CLUB, SKILL_LEVEL);
382
383
		case WEAPON_AXE:
384
			return getSkill(SKILL_AXE, SKILL_LEVEL);
385
386
		case WEAPON_FIST:
387
			return getSkill(SKILL_FIST, SKILL_LEVEL);
388
389
		case WEAPON_DIST:
390
			return getSkill(SKILL_DIST, SKILL_LEVEL);
391
392
		default:
393
			break;
394
	}
395
396
	return 0;
397
}
398
399
int32_t Player::getArmor() const
400
{
401
	int32_t i = SLOT_FIRST, armor = 0;
402
	for(; i < SLOT_LAST; ++i)
403
	{
404
		if(Item* item = getInventoryItem((slots_t)i))
405
			armor += item->getArmor();
406
	}
407
408
	if(vocation->getMultiplier(MULTIPLIER_ARMOR) != 1.0)
409
		return int32_t(armor * vocation->getMultiplier(MULTIPLIER_ARMOR));
410
411
	return armor;
412
}
413
414
void Player::getShieldAndWeapon(const Item* &_shield, const Item* &_weapon) const
415
{
416
	_shield = NULL;
417
	Item* item = NULL;
418
	for(uint32_t slot = SLOT_RIGHT; slot <= SLOT_LEFT; ++slot)
419
	{
420
		if(!(item = getInventoryItem((slots_t)slot)) || item->getWeaponType() != WEAPON_SHIELD)
421
			continue;
422
423
		if(!_shield || (_shield && item->getDefense() > _shield->getDefense()))
424
			_shield = item;
425
	}
426
427
	_weapon = weapon;
428
}
429
430
int32_t Player::getDefense() const
431
{
432
	int32_t baseDefense = 5, defenseValue = 0, defenseSkill = 0, extraDefense = 0;
433
	float defenseFactor = getDefenseFactor();
434
435
	const Item *_weapon = NULL, *_shield = NULL;
436
	getShieldAndWeapon(_shield, _weapon);
437
	if(_weapon)
438
	{
439
		extraDefense = _weapon->getExtraDefense();
440
		defenseValue = baseDefense + _weapon->getDefense();
441
		defenseSkill = getWeaponSkill(_weapon);
442
	}
443
444
	if(_shield && _shield->getDefense() > defenseValue)
445
	{
446
		if(_shield->getExtraDefense() > extraDefense)
447
			extraDefense = _shield->getExtraDefense();
448
449
		defenseValue = baseDefense + _shield->getDefense();
450
		defenseSkill = getSkill(SKILL_SHIELD, SKILL_LEVEL);
451
	}
452
453
	if(!defenseSkill)
454
		return 0;
455
456
	defenseValue += extraDefense;
457
	if(vocation->getMultiplier(MULTIPLIER_DEFENSE) != 1.0)
458
		defenseValue = int32_t(defenseValue * vocation->getMultiplier(MULTIPLIER_DEFENSE));
459
460
	return ((int32_t)std::ceil(((float)(defenseSkill * (defenseValue * 0.015)) + (defenseValue * 0.1)) * defenseFactor));
461
}
462
463
float Player::getAttackFactor() const
464
{
465
	switch(fightMode)
466
	{
467
		case FIGHTMODE_BALANCED:
468
			return 1.2f;
469
470
		case FIGHTMODE_DEFENSE:
471
			return 2.0f;
472
473
		case FIGHTMODE_ATTACK:
474
		default:
475
			break;
476
	}
477
478
	return 1.0f;
479
}
480
481
float Player::getDefenseFactor() const
482
{
483
	switch(fightMode)
484
	{
485
		case FIGHTMODE_BALANCED:
486
			return 1.2f;
487
488
		case FIGHTMODE_DEFENSE:
489
		{
490
			if((OTSYS_TIME() - lastAttack) < getAttackSpeed()) //attacking will cause us to get into normal defense
491
				return 1.0f;
492
493
			return 2.0f;
494
		}
495
496
		case FIGHTMODE_ATTACK:
497
		default:
498
			break;
499
	}
500
501
	return 1.0f;
502
}
503
504
void Player::sendIcons() const
505
{
506
	if(!client)
507
		return;
508
509
	uint32_t icons = 0;
510
	for(ConditionList::const_iterator it = conditions.begin(); it != conditions.end(); ++it)
511
	{
512
		if(!isSuppress((*it)->getType()))
513
			icons |= (*it)->getIcons();
514
	}
515
516
	if(getZone() == ZONE_PROTECTION)
517
		icons |= ICON_PROTECTIONZONE;
518
519
	if(pzLocked)
520
		icons |= ICON_PZ;
521
522
	client->sendIcons(icons);
523
}
524
525
void Player::updateInventoryWeight()
526
{
527
	inventoryWeight = 0.00;
528
	if(hasFlag(PlayerFlag_HasInfiniteCapacity))
529
		return;
530
531
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
532
	{
533
		if(Item* item = getInventoryItem((slots_t)i))
534
			inventoryWeight += item->getWeight();
535
	}
536
}
537
538
void Player::updateInventoryGoods(uint32_t itemId)
539
{
540
	if(Item::items[itemId].worth)
541
	{
542
		sendGoods();
543
		return;
544
	}
545
546
	for(ShopInfoList::iterator it = shopOffer.begin(); it != shopOffer.end(); ++it)
547
	{
548
		if(it->itemId != itemId)
549
			continue;
550
551
		sendGoods();
552
		break;
553
	}
554
}
555
556
int32_t Player::getPlayerInfo(playerinfo_t playerinfo) const
557
{
558
	switch(playerinfo)
559
	{
560
		case PLAYERINFO_LEVEL:
561
			return level;
562
		case PLAYERINFO_LEVELPERCENT:
563
			return levelPercent;
564
		case PLAYERINFO_MAGICLEVEL:
565
			return std::max((int32_t)0, ((int32_t)magLevel + varStats[STAT_MAGICLEVEL]));
566
		case PLAYERINFO_MAGICLEVELPERCENT:
567
			return magLevelPercent;
568
		case PLAYERINFO_HEALTH:
569
			return health;
570
		case PLAYERINFO_MAXHEALTH:
571
			return std::max((int32_t)1, ((int32_t)healthMax + varStats[STAT_MAXHEALTH]));
572
		case PLAYERINFO_MANA:
573
			return mana;
574
		case PLAYERINFO_MAXMANA:
575
			return std::max((int32_t)0, ((int32_t)manaMax + varStats[STAT_MAXMANA]));
576
		case PLAYERINFO_SOUL:
577
			return std::max((int32_t)0, ((int32_t)soul + varStats[STAT_SOUL]));
578
		default:
579
			break;
580
	}
581
582
	return 0;
583
}
584
585
int32_t Player::getSkill(skills_t skilltype, skillsid_t skillinfo) const
586
{
587
	int32_t ret = skills[skilltype][skillinfo];
588
	if(skillinfo == SKILL_LEVEL)
589
		ret += varSkills[skilltype];
590
591
	return std::max((int32_t)0, ret);
592
}
593
594
void Player::addSkillAdvance(skills_t skill, uint32_t count, bool useMultiplier/* = true*/)
595
{
596
	if(!count)
597
		return;
598
599
	//player has reached max skill
600
	uint32_t currReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL]),
601
		nextReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL] + 1);
602
	if(currReqTries > nextReqTries)
603
		return;
604
605
	if(useMultiplier)
606
		count = uint32_t((double)count * rates[skill] * g_config.getDouble(ConfigManager::RATE_SKILL));
607
608
	std::stringstream s;
609
	while(skills[skill][SKILL_TRIES] + count >= nextReqTries)
610
	{
611
		count -= nextReqTries - skills[skill][SKILL_TRIES];
612
	 	skills[skill][SKILL_TRIES] = skills[skill][SKILL_PERCENT] = 0;
613
		skills[skill][SKILL_LEVEL]++;
614
615
		s.str("");
616
		s << "You advanced in " << getSkillName(skill);
617
		if(g_config.getBool(ConfigManager::ADVANCING_SKILL_LEVEL))
618
			s << " [" << skills[skill][SKILL_LEVEL] << "]";
619
620
		s << ".";
621
		sendTextMessage(MSG_EVENT_ADVANCE, s.str().c_str());
622
623
		CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE);
624
		for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it)
625
			(*it)->executeAdvance(this, skill, (skills[skill][SKILL_LEVEL] - 1), skills[skill][SKILL_LEVEL]);
626
627
		currReqTries = nextReqTries;
628
		nextReqTries = vocation->getReqSkillTries(skill, skills[skill][SKILL_LEVEL] + 1);
629
		if(currReqTries > nextReqTries)
630
		{
631
			count = 0;
632
			break;
633
		}
634
	}
635
636
	if(count)
637
		skills[skill][SKILL_TRIES] += count;
638
639
	//update percent
640
	uint32_t newPercent = Player::getPercentLevel(skills[skill][SKILL_TRIES], nextReqTries);
641
	if(skills[skill][SKILL_PERCENT] != newPercent)
642
	{
643
		skills[skill][SKILL_PERCENT] = newPercent;
644
		sendSkills();
645
	}
646
	else if(!s.str().empty())
647
		sendSkills();
648
}
649
650
void Player::setVarStats(stats_t stat, int32_t modifier)
651
{
652
	varStats[stat] += modifier;
653
	switch(stat)
654
	{
655
		case STAT_MAXHEALTH:
656
		{
657
			if(getHealth() > getMaxHealth())
658
				Creature::changeHealth(getMaxHealth() - getHealth());
659
			else
660
				g_game.addCreatureHealth(this);
661
662
			break;
663
		}
664
665
		case STAT_MAXMANA:
666
		{
667
			if(getMana() > getMaxMana())
668
				Creature::changeMana(getMaxMana() - getMana());
669
670
			break;
671
		}
672
673
		default:
674
			break;
675
	}
676
}
677
678
int32_t Player::getDefaultStats(stats_t stat)
679
{
680
	switch(stat)
681
	{
682
		case STAT_MAGICLEVEL:
683
			return getMagicLevel() - getVarStats(STAT_MAGICLEVEL);
684
		case STAT_MAXHEALTH:
685
			return getMaxHealth() - getVarStats(STAT_MAXHEALTH);
686
		case STAT_MAXMANA:
687
			return getMaxMana() - getVarStats(STAT_MAXMANA);
688
		case STAT_SOUL:
689
			return getSoul() - getVarStats(STAT_SOUL);
690
		default:
691
			break;
692
	}
693
694
	return 0;
695
}
696
697
Container* Player::getContainer(uint32_t cid)
698
{
699
	for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it)
700
	{
701
		if(it->first == cid)
702
			return it->second;
703
	}
704
705
	return NULL;
706
}
707
708
int32_t Player::getContainerID(const Container* container) const
709
{
710
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
711
	{
712
		if(cl->second == container)
713
			return cl->first;
714
	}
715
716
	return -1;
717
}
718
719
void Player::addContainer(uint32_t cid, Container* container)
720
{
721
#ifdef __DEBUG__
722
	std::clog << getName() << ", addContainer: " << (int32_t)cid << std::endl;
723
#endif
724
	if(cid > 0xF)
725
		return;
726
727
	for(ContainerVector::iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
728
	{
729
		if(cl->first == cid)
730
		{
731
			cl->second = container;
732
			return;
733
		}
734
	}
735
736
	containerVec.push_back(std::make_pair(cid, container));
737
}
738
739
void Player::closeContainer(uint32_t cid)
740
{
741
	for(ContainerVector::iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
742
	{
743
		if(cl->first == cid)
744
		{
745
			containerVec.erase(cl);
746
			break;
747
		}
748
	}
749
#ifdef __DEBUG__
750
751
	std::clog << getName() << ", closeContainer: " << (int32_t)cid << std::endl;
752
#endif
753
}
754
755
bool Player::canOpenCorpse(uint32_t ownerId)
756
{
757
	return guid == ownerId || (party && party->canOpenCorpse(ownerId)) || hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges);
758
}
759
760
uint16_t Player::getLookCorpse() const
761
{
762
	if(sex % 2)
763
		return ITEM_MALE_CORPSE;
764
765
	return ITEM_FEMALE_CORPSE;
766
}
767
768
void Player::dropLoot(Container* corpse)
769
{
770
	if(!corpse || lootDrop != LOOT_DROP_FULL)
771
		return;
772
773
	uint32_t loss = lossPercent[LOSS_CONTAINERS];
774
	if(g_config.getBool(ConfigManager::BLESSINGS))
775
	{
776
		uint32_t start = g_config.getNumber(ConfigManager::BLESS_REDUCTION_BASE), bless = getBlessings();
777
		while(bless > 0 && loss > 0)
778
		{
779
			loss -= start;
780
			start -= g_config.getNumber(ConfigManager::BLESS_REDUCTION_DECREMENT);
781
			bless--;
782
		}
783
	}
784
785
	uint32_t itemLoss = (uint32_t)std::floor((5. + loss) * lossPercent[LOSS_ITEMS] / 1000.);
786
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
787
	{
788
		Item* item = inventory[i];
789
		if(!item)
790
			continue;
791
792
		uint32_t tmp = random_range(1, 100);
793
		if(skull > SKULL_BLACK || (item->getContainer() && tmp < loss) || (!item->getContainer() && tmp < itemLoss))
794
        {
795
	        g_game.internalMoveItem(NULL, this, corpse, INDEX_WHEREEVER, item, item->getItemCount(), 0);
796
	        sendRemoveInventoryItem((slots_t)i, inventory[(slots_t)i]);
797
        }
798
	}
799
}
800
801
bool Player::setStorage(const std::string& key, const std::string& value)
802
{
803
	uint32_t numericKey = atol(key.c_str());
804
	if(!IS_IN_KEYRANGE(numericKey, RESERVED_RANGE))
805
		return Creature::setStorage(key, value);
806
807
	if(IS_IN_KEYRANGE(numericKey, OUTFITS_RANGE))
808
	{
809
		uint32_t lookType = atoi(value.c_str()) >> 16, addons = atoi(value.c_str()) & 0xFF;
810
		if(addons < 4)
811
		{
812
			Outfit outfit;
813
			if(Outfits::getInstance()->getOutfit(lookType, outfit))
814
				return addOutfit(outfit.outfitId, addons);
815
		}
816
		else
817
			std::clog << "[Warning - Player::setStorage] Invalid addons value key: " << key
818
				<< ", value: " << value << " for player: " << getName() << std::endl;
819
	}
820
	else if(IS_IN_KEYRANGE(numericKey, OUTFITSID_RANGE))
821
	{
822
		uint32_t outfitId = atoi(value.c_str()) >> 16, addons = atoi(value.c_str()) & 0xFF;
823
		if(addons < 4)
824
			return addOutfit(outfitId, addons);
825
		else
826
			std::clog << "[Warning - Player::setStorage] Invalid addons value key: " << key
827
				<< ", value: " << value << " for player: " << getName() << std::endl;
828
	}
829
	else
830
		std::clog << "[Warning - Player::setStorage] Unknown reserved key: " << key << " for player: " << getName() << std::endl;
831
832
	return false;
833
}
834
835
void Player::eraseStorage(const std::string& key)
836
{
837
	Creature::eraseStorage(key);
838
	if(IS_IN_KEYRANGE(atol(key.c_str()), RESERVED_RANGE))
839
		std::clog << "[Warning - Player::eraseStorage] Unknown reserved key: " << key << " for player: " << name << std::endl;
840
}
841
842
bool Player::canSee(const Position& pos) const
843
{
844
	if(client)
845
		return client->canSee(pos);
846
847
	return false;
848
}
849
850
bool Player::canSeeCreature(const Creature* creature) const
851
{
852
	if(creature == this)
853
		return true;
854
855
	if(const Player* player = creature->getPlayer())
856
		return !player->isGhost() || getGhostAccess() >= player->getGhostAccess();
857
858
	return !creature->isInvisible() || canSeeInvisibility();
859
}
860
861
bool Player::canWalkthrough(const Creature* creature) const
862
{
863
	if(creature == this || hasFlag(PlayerFlag_CanPassThroughAllCreatures) || creature->isWalkable() ||
864
		std::find(forceWalkthrough.begin(), forceWalkthrough.end(), creature->getID()) != forceWalkthrough.end()
865
			|| (creature->getMaster() && creature->getMaster() != this && canWalkthrough(creature->getMaster())))
866
		return true;
867
868
	const Player* player = creature->getPlayer();
869
	if(!player)
870
		return false;
871
872
	if((((g_game.getWorldType() == WORLDTYPE_OPTIONAL &&
873
		!player->isEnemy(this, true) &&
874
		player->getVocation()->isAttackable()) || player->getTile()->hasFlag(TILESTATE_PROTECTIONZONE) || (player->getVocation()->isAttackable()
875
			&& player->getLevel() < (uint32_t)g_config.getNumber(ConfigManager::PROTECTION_LEVEL))) && player->getTile()->ground &&
876
		Item::items[player->getTile()->ground->getID()].walkStack) && (!player->hasCustomFlag(PlayerCustomFlag_GamemasterPrivileges)
877
		|| player->getAccess() <= getAccess()))
878
		return true;
879
880
	return (player->isGhost() && getGhostAccess() < player->getGhostAccess())
881
		|| (isGhost() && getGhostAccess() > player->getGhostAccess());
882
}
883
884
void Player::setWalkthrough(const Creature* creature, bool walkthrough)
885
{
886
	std::vector<uint32_t>::iterator it = std::find(forceWalkthrough.begin(),
887
		forceWalkthrough.end(), creature->getID());
888
	bool update = false;
889
	if(walkthrough && it == forceWalkthrough.end())
890
	{
891
		forceWalkthrough.push_back(creature->getID());
892
		update = true;
893
	}
894
	else if(!walkthrough && it != forceWalkthrough.end())
895
	{
896
		forceWalkthrough.erase(it);
897
		update = true;
898
	}
899
900
	if(update)
901
		sendCreatureWalkthrough(creature, !walkthrough ? canWalkthrough(creature) : walkthrough);
902
}
903
904
Depot* Player::getDepot(uint32_t depotId, bool autoCreateDepot)
905
{
906
	DepotMap::iterator it = depots.find(depotId);
907
	if(it != depots.end())
908
		return it->second.first;
909
910
	//create a new depot?
911
	if(autoCreateDepot)
912
	{
913
		Item* locker = Item::CreateItem(ITEM_LOCKER);
914
		if(Container* container = locker->getContainer())
915
		{
916
			if(Depot* depot = container->getDepot())
917
			{
918
				container->__internalAddThing(Item::CreateItem(ITEM_DEPOT));
919
				addDepot(depot, depotId);
920
				return depot;
921
			}
922
		}
923
924
		g_game.freeThing(locker);
925
		std::clog << "Failure: Creating a new depot with id: " << depotId <<
926
			", for player: " << getName() << std::endl;
927
	}
928
929
	return NULL;
930
}
931
932
bool Player::addDepot(Depot* depot, uint32_t depotId)
933
{
934
	if(getDepot(depotId, false))
935
		return false;
936
937
	depots[depotId] = std::make_pair(depot, false);
938
	depot->setMaxDepotLimit((group != NULL ? group->getDepotLimit(isPremium()) : 1000));
939
	return true;
940
}
941
942
void Player::useDepot(uint32_t depotId, bool value)
943
{
944
	DepotMap::iterator it = depots.find(depotId);
945
	if(it != depots.end())
946
		depots[depotId] = std::make_pair(it->second.first, value);
947
}
948
949
void Player::sendCancelMessage(ReturnValue message) const
950
{
951
	switch(message)
952
	{
953
		case RET_DESTINATIONOUTOFREACH:
954
			sendCancel("Destination is out of reach.");
955
			break;
956
957
		case RET_NOTMOVABLE:
958
			sendCancel("You cannot move this object.");
959
			break;
960
961
		case RET_DROPTWOHANDEDITEM:
962
			sendCancel("Drop the double-handed object first.");
963
			break;
964
965
		case RET_BOTHHANDSNEEDTOBEFREE:
966
			sendCancel("Both hands need to be free.");
967
			break;
968
969
		case RET_CANNOTBEDRESSED:
970
			sendCancel("You cannot dress this object there.");
971
			break;
972
973
		case RET_PUTTHISOBJECTINYOURHAND:
974
			sendCancel("Put this object in your hand.");
975
			break;
976
977
		case RET_PUTTHISOBJECTINBOTHHANDS:
978
			sendCancel("Put this object in both hands.");
979
			break;
980
981
		case RET_CANONLYUSEONEWEAPON:
982
			sendCancel("You may use only one weapon.");
983
			break;
984
985
		case RET_TOOFARAWAY:
986
			sendCancel("Too far away.");
987
			break;
988
989
		case RET_FIRSTGODOWNSTAIRS:
990
			sendCancel("First go downstairs.");
991
			break;
992
993
		case RET_FIRSTGOUPSTAIRS:
994
			sendCancel("First go upstairs.");
995
			break;
996
997
		case RET_NOTENOUGHCAPACITY:
998
			sendCancel("This object is too heavy for you to carry.");
999
			break;
1000
1001
		case RET_CONTAINERNOTENOUGHROOM:
1002
			sendCancel("You cannot put more objects in this container.");
1003
			break;
1004
1005
		case RET_NEEDEXCHANGE:
1006
		case RET_NOTENOUGHROOM:
1007
			sendCancel("There is not enough room.");
1008
			break;
1009
1010
		case RET_CANNOTPICKUP:
1011
			sendCancel("You cannot take this object.");
1012
			break;
1013
1014
		case RET_CANNOTTHROW:
1015
			sendCancel("You cannot throw there.");
1016
			break;
1017
1018
		case RET_THEREISNOWAY:
1019
			sendCancel("There is no way.");
1020
			break;
1021
1022
		case RET_THISISIMPOSSIBLE:
1023
			sendCancel("This is impossible.");
1024
			break;
1025
1026
		case RET_PLAYERISPZLOCKED:
1027
			sendCancel("You cannot enter a protection zone after attacking another player.");
1028
			break;
1029
1030
		case RET_PLAYERISNOTINVITED:
1031
			sendCancel("You are not invited.");
1032
			break;
1033
1034
		case RET_CREATUREDOESNOTEXIST:
1035
			sendCancel("Creature does not exist.");
1036
			break;
1037
1038
		case RET_DEPOTISFULL:
1039
			sendCancel("You cannot put more items in this depot.");
1040
			break;
1041
1042
		case RET_CANNOTUSETHISOBJECT:
1043
			sendCancel("You cannot use this object.");
1044
			break;
1045
1046
		case RET_PLAYERWITHTHISNAMEISNOTONLINE:
1047
			sendCancel("A player with this name is not online.");
1048
			break;
1049
1050
		case RET_NOTREQUIREDLEVELTOUSERUNE:
1051
			sendCancel("You do not have the required magic level to use this rune.");
1052
			break;
1053
1054
		case RET_YOUAREALREADYTRADING:
1055
			sendCancel("You are already trading.");
1056
			break;
1057
1058
		case RET_THISPLAYERISALREADYTRADING:
1059
			sendCancel("This player is already trading.");
1060
			break;
1061
1062
		case RET_YOUMAYNOTLOGOUTDURINGAFIGHT:
1063
			sendCancel("You may not logout during or immediately after a fight!");
1064
			break;
1065
1066
		case RET_DIRECTPLAYERSHOOT:
1067
			sendCancel("You are not allowed to shoot directly on players.");
1068
			break;
1069
1070
		case RET_NOTENOUGHLEVEL:
1071
			sendCancel("You do not have enough level.");
1072
			break;
1073
1074
		case RET_NOTENOUGHMAGICLEVEL:
1075
			sendCancel("You do not have enough magic level.");
1076
			break;
1077
1078
		case RET_NOTENOUGHMANA:
1079
			sendCancel("You do not have enough mana.");
1080
			break;
1081
1082
		case RET_NOTENOUGHSOUL:
1083
			sendCancel("You do not have enough soul.");
1084
			break;
1085
1086
		case RET_YOUAREEXHAUSTED:
1087
			sendCancel("You are exhausted.");
1088
			break;
1089
1090
		case RET_CANONLYUSETHISRUNEONCREATURES:
1091
			sendCancel("You can only use this rune on creatures.");
1092
			break;
1093
1094
		case RET_PLAYERISNOTREACHABLE:
1095
			sendCancel("Player is not reachable.");
1096
			break;
1097
1098
		case RET_CREATUREISNOTREACHABLE:
1099
			sendCancel("Creature is not reachable.");
1100
			break;
1101
1102
		case RET_ACTIONNOTPERMITTEDINPROTECTIONZONE:
1103
			sendCancel("This action is not permitted in a protection zone.");
1104
			break;
1105
1106
		case RET_YOUMAYNOTATTACKTHISPLAYER:
1107
			sendCancel("You may not attack this player.");
1108
			break;
1109
1110
		case RET_YOUMAYNOTATTACKTHISCREATURE:
1111
			sendCancel("You may not attack this creature.");
1112
			break;
1113
1114
		case RET_YOUMAYNOTATTACKAPERSONINPROTECTIONZONE:
1115
			sendCancel("You may not attack a person in a protection zone.");
1116
			break;
1117
1118
		case RET_YOUMAYNOTATTACKAPERSONWHILEINPROTECTIONZONE:
1119
			sendCancel("You may not attack a person while you are in a protection zone.");
1120
			break;
1121
1122
		case RET_YOUCANONLYUSEITONCREATURES:
1123
			sendCancel("You can only use it on creatures.");
1124
			break;
1125
1126
		case RET_TURNSECUREMODETOATTACKUNMARKEDPLAYERS:
1127
			sendCancel("Turn secure mode off if you really want to attack unmarked players.");
1128
			break;
1129
1130
		case RET_YOUNEEDPREMIUMACCOUNT:
1131
			sendCancel("You need a premium account.");
1132
			break;
1133
1134
		case RET_YOUNEEDTOLEARNTHISSPELL:
1135
			sendCancel("You need to learn this spell first.");
1136
			break;
1137
1138
		case RET_YOURVOCATIONCANNOTUSETHISSPELL:
1139
			sendCancel("Your vocation cannot use this spell.");
1140
			break;
1141
1142
		case RET_YOUNEEDAWEAPONTOUSETHISSPELL:
1143
			sendCancel("You need to equip a weapon to use this spell.");
1144
			break;
1145
1146
		case RET_PLAYERISPZLOCKEDLEAVEPVPZONE:
1147
			sendCancel("You cannot leave a pvp zone after attacking another player.");
1148
			break;
1149
1150
		case RET_PLAYERISPZLOCKEDENTERPVPZONE:
1151
			sendCancel("You cannot enter a pvp zone after attacking another player.");
1152
			break;
1153
1154
		case RET_ACTIONNOTPERMITTEDINANOPVPZONE:
1155
			sendCancel("This action is not permitted in a safe zone.");
1156
			break;
1157
1158
		case RET_YOUCANNOTLOGOUTHERE:
1159
			sendCancel("You cannot logout here.");
1160
			break;
1161
1162
		case RET_YOUNEEDAMAGICITEMTOCASTSPELL:
1163
			sendCancel("You need a magic item to cast this spell.");
1164
			break;
1165
1166
		case RET_CANNOTCONJUREITEMHERE:
1167
			sendCancel("You cannot conjure items here.");
1168
			break;
1169
1170
		case RET_NAMEISTOOAMBIGUOUS:
1171
			sendCancel("Name is too ambiguous.");
1172
			break;
1173
1174
		case RET_CANONLYUSEONESHIELD:
1175
			sendCancel("You may use only one shield.");
1176
			break;
1177
1178
		case RET_YOUARENOTTHEOWNER:
1179
			sendCancel("You are not the owner.");
1180
			break;
1181
1182
		case RET_YOUMAYNOTCASTAREAONBLACKSKULL:
1183
			sendCancel("You may not cast area spells while you have a black skull.");
1184
			break;
1185
1186
		case RET_TILEISFULL:
1187
			sendCancel("You cannot add more items on this tile.");
1188
			break;
1189
1190
		case RET_NOTENOUGHSKILL:
1191
			sendCancel("You do not have enough skill.");
1192
			break;
1193
1194
		case RET_DONTSHOWMESSAGE:
1195
			break;
1196
1197
		case RET_NOTPOSSIBLE:
1198
		default:
1199
			sendCancel("Sorry, not possible.");
1200
			break;
1201
	}
1202
}
1203
1204
Item* Player::getWriteItem(uint32_t& _windowTextId, uint16_t& _maxWriteLen)
1205
{
1206
	_windowTextId = windowTextId;
1207
	_maxWriteLen = maxWriteLen;
1208
	return writeItem;
1209
}
1210
1211
void Player::setWriteItem(Item* item, uint16_t _maxWriteLen/* = 0*/)
1212
{
1213
	windowTextId++;
1214
	if(writeItem)
1215
		writeItem->unRef();
1216
1217
	if(item)
1218
	{
1219
		writeItem = item;
1220
		maxWriteLen = _maxWriteLen;
1221
		writeItem->addRef();
1222
	}
1223
	else
1224
	{
1225
		writeItem = NULL;
1226
		maxWriteLen = 0;
1227
	}
1228
}
1229
1230
House* Player::getEditHouse(uint32_t& _windowTextId, uint32_t& _listId)
1231
{
1232
	_windowTextId = windowTextId;
1233
	_listId = editListId;
1234
	return editHouse;
1235
}
1236
1237
void Player::setEditHouse(House* house, uint32_t listId/* = 0*/)
1238
{
1239
	windowTextId++;
1240
	editHouse = house;
1241
	editListId = listId;
1242
}
1243
1244
void Player::sendHouseWindow(House* house, uint32_t listId) const
1245
{
1246
	if(!client)
1247
		return;
1248
1249
	std::string text;
1250
	if(house->getAccessList(listId, text))
1251
		client->sendHouseWindow(windowTextId, house, listId, text);
1252
}
1253
1254
void Player::sendCreatureChangeVisible(const Creature* creature, Visible_t visible)
1255
{
1256
	if(!client)
1257
		return;
1258
1259
	const Player* player = creature->getPlayer();
1260
	if(player == this || (player && (visible < VISIBLE_GHOST_APPEAR || getGhostAccess() >= player->getGhostAccess()))
1261
		|| (!player && canSeeInvisibility()))
1262
		sendCreatureChangeOutfit(creature, creature->getCurrentOutfit());
1263
	else if(visible == VISIBLE_DISAPPEAR || visible == VISIBLE_GHOST_DISAPPEAR)
1264
		sendCreatureDisappear(creature, creature->getTile()->getClientIndexOfThing(this, creature));
1265
	else
1266
		sendCreatureAppear(creature);
1267
}
1268
1269
void Player::sendAddContainerItem(const Container* container, const Item* item)
1270
{
1271
	if(!client)
1272
		return;
1273
1274
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
1275
	{
1276
		if(cl->second == container)
1277
			client->sendAddContainerItem(cl->first, item);
1278
	}
1279
}
1280
1281
void Player::sendUpdateContainerItem(const Container* container, uint8_t slot, const Item*, const Item* newItem)
1282
{
1283
	if(!client)
1284
		return;
1285
1286
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
1287
	{
1288
		if(cl->second == container)
1289
			client->sendUpdateContainerItem(cl->first, slot, newItem);
1290
	}
1291
}
1292
1293
void Player::sendRemoveContainerItem(const Container* container, uint8_t slot, const Item*)
1294
{
1295
	if(!client)
1296
		return;
1297
1298
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
1299
	{
1300
		if(cl->second == container)
1301
			client->sendRemoveContainerItem(cl->first, slot);
1302
	}
1303
}
1304
1305
void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
1306
	const ItemType& oldType, const Item* newItem, const ItemType& newType)
1307
{
1308
	Creature::onUpdateTileItem(tile, pos, oldItem, oldType, newItem, newType);
1309
	if(oldItem != newItem)
1310
		onRemoveTileItem(tile, pos, oldType, oldItem);
1311
1312
	if(tradeState != TRADE_TRANSFER && tradeItem && oldItem == tradeItem)
1313
		g_game.internalCloseTrade(this);
1314
}
1315
1316
void Player::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType, const Item* item)
1317
{
1318
	Creature::onRemoveTileItem(tile, pos, iType, item);
1319
	if(tradeState == TRADE_TRANSFER)
1320
		return;
1321
1322
	checkTradeState(item);
1323
	if(tradeItem)
1324
	{
1325
		const Container* container = item->getContainer();
1326
		if(container && container->isHoldingItem(tradeItem))
1327
			g_game.internalCloseTrade(this);
1328
	}
1329
}
1330
1331
void Player::onCreatureAppear(const Creature* creature)
1332
{
1333
	Creature::onCreatureAppear(creature);
1334
	if(creature != this)
1335
		return;
1336
1337
	Item* item = NULL;
1338
	for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
1339
	{
1340
		if(!(item = getInventoryItem((slots_t)slot)))
1341
			continue;
1342
1343
		item->__startDecaying();
1344
		g_moveEvents->onPlayerEquip(this, item, (slots_t)slot, false);
1345
	}
1346
1347
	updateWeapon();
1348
	if(BedItem* bed = Beds::getInstance()->getBedBySleeper(guid))
1349
		bed->wakeUp();
1350
1351
	Outfit outfit;
1352
	if(Outfits::getInstance()->getOutfit(defaultOutfit.lookType, outfit))
1353
		outfitAttributes = Outfits::getInstance()->addAttributes(getID(), outfit.outfitId, sex, defaultOutfit.lookAddons);
1354
1355
	if(lastLogout && stamina < STAMINA_MAX)
1356
	{
1357
		int64_t ticks = (int64_t)time(NULL) - lastLogout - 600;
1358
		if(ticks > 0)
1359
		{
1360
			ticks = (int64_t)((double)(ticks * 1000) / g_config.getDouble(ConfigManager::RATE_STAMINA_GAIN));
1361
			int64_t premium = g_config.getNumber(ConfigManager::STAMINA_LIMIT_TOP) * STAMINA_MULTIPLIER, period = ticks;
1362
			if((int64_t)stamina <= premium)
1363
			{
1364
				period += stamina;
1365
				if(period > premium)
1366
					period -= premium;
1367
				else
1368
					period = 0;
1369
1370
				useStamina(ticks - period);
1371
			}
1372
1373
			if(period > 0)
1374
			{
1375
				ticks = (int64_t)((g_config.getDouble(ConfigManager::RATE_STAMINA_GAIN) * period)
1376
					/ g_config.getDouble(ConfigManager::RATE_STAMINA_THRESHOLD));
1377
				if(stamina + ticks > STAMINA_MAX)
1378
					ticks = STAMINA_MAX - stamina;
1379
1380
				useStamina(ticks);
1381
			}
1382
1383
			sendStats();
1384
		}
1385
	}
1386
1387
	g_game.checkPlayersRecord(this);
1388
	if(!isGhost())
1389
		IOLoginData::getInstance()->updateOnlineStatus(guid, true);
1390
1391
	#if defined(WINDOWS) && !defined(__CONSOLE__)
1392
	GUI::getInstance()->m_pBox.addPlayer(this);
1393
	#endif
1394
	if(g_config.getBool(ConfigManager::DISPLAY_LOGGING))
1395
		std::clog << name << " has logged in." << std::endl;
1396
}
1397
1398
void Player::onAttackedCreatureDisappear(bool isLogout)
1399
{
1400
	sendCancelTarget();
1401
	if(!isLogout)
1402
		sendTextMessage(MSG_STATUS_SMALL, "Target lost.");
1403
}
1404
1405
void Player::onFollowCreatureDisappear(bool isLogout)
1406
{
1407
	sendCancelTarget();
1408
	if(!isLogout)
1409
		sendTextMessage(MSG_STATUS_SMALL, "Target lost.");
1410
}
1411
1412
void Player::onChangeZone(ZoneType_t zone)
1413
{
1414
	if(zone == ZONE_PROTECTION)
1415
	{
1416
		if(attackedCreature && !hasFlag(PlayerFlag_IgnoreProtectionZone))
1417
		{
1418
			setAttackedCreature(NULL);
1419
			onAttackedCreatureDisappear(false);
1420
		}
1421
	}
1422
1423
	g_game.updateCreatureWalkthrough(this);
1424
	sendIcons();
1425
}
1426
1427
void Player::onAttackedCreatureChangeZone(ZoneType_t zone)
1428
{
1429
	if(zone == ZONE_PROTECTION && !hasFlag(PlayerFlag_IgnoreProtectionZone))
1430
	{
1431
		setAttackedCreature(NULL);
1432
		onAttackedCreatureDisappear(false);
1433
	}
1434
	else if(zone == ZONE_OPTIONAL && attackedCreature->getPlayer() && !hasFlag(PlayerFlag_IgnoreProtectionZone))
1435
	{
1436
		setAttackedCreature(NULL);
1437
		onAttackedCreatureDisappear(false);
1438
	}
1439
	else if(zone == ZONE_OPEN && g_game.getWorldType() == WORLDTYPE_OPTIONAL && attackedCreature->getPlayer()
1440
		&& !attackedCreature->getPlayer()->isEnemy(this, true))
1441
	{
1442
		//attackedCreature can leave a pvp zone if not pzlocked
1443
		setAttackedCreature(NULL);
1444
		onAttackedCreatureDisappear(false);
1445
	}
1446
}
1447
1448
void Player::onCreatureDisappear(const Creature* creature, bool isLogout)
1449
{
1450
	Creature::onCreatureDisappear(creature, isLogout);
1451
	if(creature != this)
1452
		return;
1453
1454
	if(isLogout)
1455
	{
1456
		loginPosition = getPosition();
1457
		lastLogout = time(NULL);
1458
	}
1459
1460
	if(eventWalk)
1461
		setFollowCreature(NULL);
1462
1463
	closeShopWindow();
1464
	if(tradePartner)
1465
		g_game.internalCloseTrade(this);
1466
1467
	clearPartyInvitations();
1468
	if(party)
1469
		party->leave(this);
1470
1471
	g_game.cancelRuleViolation(this);
1472
	if(hasFlag(PlayerFlag_CanAnswerRuleViolations))
1473
	{
1474
		PlayerVector closeReportList;
1475
		for(RuleViolationsMap::const_iterator it = g_game.getRuleViolations().begin(); it != g_game.getRuleViolations().end(); ++it)
1476
		{
1477
			if(it->second->gamemaster == this)
1478
				closeReportList.push_back(it->second->reporter);
1479
		}
1480
1481
		for(PlayerVector::iterator it = closeReportList.begin(); it != closeReportList.end(); ++it)
1482
			g_game.closeRuleViolation(*it);
1483
	}
1484
1485
	g_chat.removeUserFromAllChannels(this);
1486
	if(!isGhost())
1487
		IOLoginData::getInstance()->updateOnlineStatus(guid, false);
1488
1489
	#if defined(WINDOWS) && !defined(__CONSOLE__)
1490
	GUI::getInstance()->m_pBox.removePlayer(this);
1491
	#endif
1492
	if(g_config.getBool(ConfigManager::DISPLAY_LOGGING))
1493
		std::clog << getName() << " has logged out." << std::endl;
1494
1495
	bool saved = false;
1496
	for(uint32_t tries = 0; !saved && tries < 3; ++tries)
1497
	{
1498
		if(IOLoginData::getInstance()->savePlayer(this))
1499
			saved = true;
1500
#ifdef __DEBUG__
1501
		else
1502
			std::clog << "Error while saving player: " << getName() << ", strike " << tries << "." << std::endl;
1503
#endif
1504
	}
1505
1506
	if(!saved)
1507
#ifndef __DEBUG__
1508
		std::clog << "Error while saving player: " << getName() << "." << std::endl;
1509
#else
1510
		std::clog << "Player " << getName() << " couldn't be saved." << std::endl;
1511
#endif
1512
}
1513
1514
void Player::openShopWindow()
1515
{
1516
	sendShop();
1517
	sendGoods();
1518
}
1519
1520
void Player::closeShopWindow(bool send/* = true*/)
1521
{
1522
	int32_t onBuy = -1, onSell = -1;
1523
	if(Npc* npc = getShopOwner(onBuy, onSell))
1524
		npc->onPlayerEndTrade(this, onBuy, onSell);
1525
1526
	if(shopOwner)
1527
	{
1528
		shopOwner = NULL;
1529
		if(send)
1530
			sendCloseShop();
1531
	}
1532
1533
	purchaseCallback = saleCallback = -1;
1534
	shopOffer.clear();
1535
}
1536
1537
bool Player::canShopItem(uint16_t itemId, uint8_t subType, ShopEvent_t event)
1538
{
1539
	for(ShopInfoList::iterator sit = shopOffer.begin(); sit != shopOffer.end(); ++sit)
1540
	{
1541
		if(sit->itemId != itemId || ((event != SHOPEVENT_BUY || sit->buyPrice < 0)
1542
			&& (event != SHOPEVENT_SELL || sit->sellPrice < 0)))
1543
			continue;
1544
1545
		if(event == SHOPEVENT_SELL)
1546
			return true;
1547
1548
		const ItemType& it = Item::items[id];
1549
		if(it.isFluidContainer() || it.isSplash())
1550
			return sit->subType == subType;
1551
1552
		return true;
1553
	}
1554
1555
	return false;
1556
}
1557
1558
void Player::onWalk(Direction& dir)
1559
{
1560
	Creature::onWalk(dir);
1561
	setNextActionTask(NULL);
1562
	setNextAction(OTSYS_TIME() + getStepDuration(dir));
1563
}
1564
1565
void Player::onCreatureMove(const Creature* creature, const Tile* newTile, const Position& newPos,
1566
	const Tile* oldTile, const Position& oldPos, bool teleport)
1567
{
1568
	Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport);
1569
	if(creature != this)
1570
		return;
1571
1572
	if(party)
1573
		party->updateSharedExperience();
1574
1575
	//check if we should close trade
1576
	if(tradeState != TRADE_TRANSFER && ((tradeItem && !Position::areInRange<1,1,0>(tradeItem->getPosition(), getPosition()))
1577
		|| (tradePartner && !Position::areInRange<2,2,0>(tradePartner->getPosition(), getPosition()))))
1578
		g_game.internalCloseTrade(this);
1579
1580
	if((teleport || oldPos.z != newPos.z) && !hasCustomFlag(PlayerCustomFlag_CanStairhop))
1581
	{
1582
		int32_t ticks = g_config.getNumber(ConfigManager::STAIRHOP_DELAY);
1583
		if(ticks > 0)
1584
		{
1585
			addExhaust(ticks, EXHAUST_HEALING);
1586
			addExhaust(ticks, EXHAUST_COMBAT);
1587
			if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks))
1588
				addCondition(condition);
1589
		}
1590
	}
1591
}
1592
1593
void Player::onAddContainerItem(const Container*, const Item* item)
1594
{
1595
	checkTradeState(item);
1596
}
1597
1598
void Player::onUpdateContainerItem(const Container* container, uint8_t slot,
1599
	const Item* oldItem, const ItemType&, const Item* newItem, const ItemType&)
1600
{
1601
	if(oldItem != newItem)
1602
		onRemoveContainerItem(container, slot, oldItem);
1603
1604
	if(tradeState != TRADE_TRANSFER)
1605
		checkTradeState(oldItem);
1606
}
1607
1608
void Player::onRemoveContainerItem(const Container* container, uint8_t, const Item* item)
1609
{
1610
	if(tradeState == TRADE_TRANSFER)
1611
		return;
1612
1613
	checkTradeState(item);
1614
	if(tradeItem)
1615
	{
1616
		if(tradeItem->getParent() != container && container->isHoldingItem(tradeItem))
1617
			g_game.internalCloseTrade(this);
1618
	}
1619
}
1620
1621
void Player::onCloseContainer(const Container* container)
1622
{
1623
	if(!client)
1624
		return;
1625
1626
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
1627
	{
1628
		if(cl->second == container)
1629
			client->sendCloseContainer(cl->first);
1630
	}
1631
}
1632
1633
void Player::onSendContainer(const Container* container)
1634
{
1635
	if(!client)
1636
		return;
1637
1638
	bool hasParent = dynamic_cast<const Container*>(container->getParent()) != NULL;
1639
	for(ContainerVector::const_iterator cl = containerVec.begin(); cl != containerVec.end(); ++cl)
1640
	{
1641
		if(cl->second == container)
1642
			client->sendContainer(cl->first, container, hasParent);
1643
	}
1644
}
1645
1646
void Player::onUpdateInventoryItem(slots_t slot, Item* oldItem, const ItemType& ,
1647
	Item* newItem, const ItemType&)
1648
{
1649
	if(oldItem != newItem)
1650
		onRemoveInventoryItem(slot, oldItem);
1651
1652
	if(tradeState != TRADE_TRANSFER)
1653
		checkTradeState(oldItem);
1654
}
1655
1656
void Player::onRemoveInventoryItem(slots_t, Item* item)
1657
{
1658
	if(tradeState == TRADE_TRANSFER)
1659
		return;
1660
1661
	checkTradeState(item);
1662
	if(tradeItem)
1663
	{
1664
		const Container* container = item->getContainer();
1665
		if(container && container->isHoldingItem(tradeItem))
1666
			g_game.internalCloseTrade(this);
1667
	}
1668
}
1669
1670
void Player::checkTradeState(const Item* item)
1671
{
1672
	if(!tradeItem || tradeState == TRADE_TRANSFER)
1673
		return;
1674
1675
	if(tradeItem != item)
1676
	{
1677
		const Container* container = dynamic_cast<const Container*>(item->getParent());
1678
		while(container != NULL)
1679
		{
1680
			if(container == tradeItem)
1681
			{
1682
				g_game.internalCloseTrade(this);
1683
				break;
1684
			}
1685
1686
			container = dynamic_cast<const Container*>(container->getParent());
1687
		}
1688
	}
1689
	else
1690
		g_game.internalCloseTrade(this);
1691
}
1692
1693
void Player::setNextWalkActionTask(SchedulerTask* task)
1694
{
1695
	if(walkTaskEvent)
1696
	{
1697
		Scheduler::getInstance().stopEvent(walkTaskEvent);
1698
		walkTaskEvent = 0;
1699
	}
1700
1701
	delete walkTask;
1702
	walkTask = task;
1703
	setIdleTime(0);
1704
}
1705
1706
void Player::setNextWalkTask(SchedulerTask* task)
1707
{
1708
	if(nextStepEvent)
1709
	{
1710
		Scheduler::getInstance().stopEvent(nextStepEvent);
1711
		nextStepEvent = 0;
1712
	}
1713
1714
	if(task)
1715
	{
1716
		nextStepEvent = Scheduler::getInstance().addEvent(task);
1717
		setIdleTime(0);
1718
	}
1719
}
1720
1721
void Player::setNextActionTask(SchedulerTask* task)
1722
{
1723
	if(actionTaskEvent)
1724
	{
1725
		Scheduler::getInstance().stopEvent(actionTaskEvent);
1726
		actionTaskEvent = 0;
1727
	}
1728
1729
	if(task)
1730
	{
1731
		actionTaskEvent = Scheduler::getInstance().addEvent(task);
1732
		setIdleTime(0);
1733
	}
1734
}
1735
1736
uint32_t Player::getNextActionTime() const
1737
{
1738
	return (uint32_t)std::max((int64_t)SCHEDULER_MINTICKS, ((int64_t)nextAction - OTSYS_TIME()));
1739
}
1740
1741
void Player::onThink(uint32_t interval)
1742
{
1743
	Creature::onThink(interval);
1744
	int64_t timeNow = OTSYS_TIME();
1745
	if(timeNow - lastPing >= 5000)
1746
	{
1747
		lastPing = timeNow;
1748
		if(client)
1749
			client->sendPing();
1750
		else if(g_config.getBool(ConfigManager::STOP_ATTACK_AT_EXIT))
1751
			setAttackedCreature(NULL);
1752
	}
1753
1754
	if((timeNow - lastPong) >= 60000 && !getTile()->hasFlag(TILESTATE_NOLOGOUT)
1755
		&& !isConnecting && !pzLocked && !hasCondition(CONDITION_INFIGHT))
1756
	{
1757
		if(client)
1758
			client->logout(true, true);
1759
		else if(g_creatureEvents->playerLogout(this, false))
1760
			g_game.removeCreature(this, true);
1761
	}
1762
1763
	messageTicks += interval;
1764
	if(messageTicks >= 1500)
1765
	{
1766
		messageTicks = 0;
1767
		addMessageBuffer();
1768
	}
1769
1770
	if(lastMail && lastMail < (uint64_t)(OTSYS_TIME() + g_config.getNumber(ConfigManager::MAIL_ATTEMPTS_FADE)))
1771
		mailAttempts = lastMail = 0;
1772
}
1773
1774
bool Player::isMuted(uint16_t channelId, SpeakClasses type, int32_t& time)
1775
{
1776
	time = 0;
1777
	if(hasFlag(PlayerFlag_CannotBeMuted))
1778
		return false;
1779
1780
	int32_t muteTicks = 0;
1781
	for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ++it)
1782
	{
1783
		if((*it)->getType() == CONDITION_MUTED && (*it)->getSubId() == 0)
1784
		{
1785
			if((*it)->getTicks() == -1)
1786
			{
1787
				time = -1;
1788
				break;
1789
			}
1790
1791
			if((*it)->getTicks() > muteTicks)
1792
				muteTicks = (*it)->getTicks();
1793
		}
1794
	}
1795
1796
	if(muteTicks)
1797
		time = (uint32_t)muteTicks / 1000;
1798
1799
	return type != SPEAK_PRIVATE_PN && (type != SPEAK_CHANNEL_Y || (channelId != CHANNEL_GUILD && !g_chat.isPrivateChannel(channelId)));
1800
}
1801
1802
void Player::addMessageBuffer()
1803
{
1804
	if(!hasFlag(PlayerFlag_CannotBeMuted) && g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER) && messageBuffer)
1805
		messageBuffer--;
1806
}
1807
1808
void Player::removeMessageBuffer()
1809
{
1810
	if(hasFlag(PlayerFlag_CannotBeMuted))
1811
		return;
1812
1813
	int32_t maxBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER);
1814
	if(!maxBuffer || messageBuffer > maxBuffer + 1 || ++messageBuffer <= maxBuffer)
1815
		return;
1816
1817
	uint32_t muteCount = 1;
1818
	MuteCountMap::iterator it = muteCountMap.find(guid);
1819
	if(it != muteCountMap.end())
1820
		muteCount = it->second;
1821
1822
	uint32_t muteTime = 5 * muteCount * muteCount;
1823
	muteCountMap[guid] = muteCount + 1;
1824
	if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000))
1825
		addCondition(condition);
1826
1827
	char buffer[50];
1828
	sprintf(buffer, "You are muted for %d seconds.", muteTime);
1829
	sendTextMessage(MSG_STATUS_SMALL, buffer);
1830
}
1831
1832
void Player::drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage)
1833
{
1834
	Creature::drainHealth(attacker, combatType, damage);
1835
	char buffer[150];
1836
	if(attacker)
1837
		sprintf(buffer, "You lose %d hitpoint%s due to an attack by %s.", damage, (damage != 1 ? "s" : ""), attacker->getNameDescription().c_str());
1838
	else
1839
		sprintf(buffer, "You lose %d hitpoint%s.", damage, (damage != 1 ? "s" : ""));
1840
1841
	sendStats();
1842
	sendTextMessage(MSG_EVENT_DEFAULT, buffer);
1843
}
1844
1845
void Player::drainMana(Creature* attacker, CombatType_t combatType, int32_t damage)
1846
{
1847
	Creature::drainMana(attacker, combatType, damage);
1848
	char buffer[150];
1849
	if(attacker)
1850
		sprintf(buffer, "You lose %d mana blocking an attack by %s.", damage, attacker->getNameDescription().c_str());
1851
	else
1852
		sprintf(buffer, "You lose %d mana.", damage);
1853
1854
	sendStats();
1855
	sendTextMessage(MSG_EVENT_DEFAULT, buffer);
1856
}
1857
1858
void Player::addManaSpent(uint64_t amount, bool useMultiplier/* = true*/)
1859
{
1860
	if(!amount)
1861
		return;
1862
1863
	uint64_t currReqMana = vocation->getReqMana(magLevel), nextReqMana = vocation->getReqMana(magLevel + 1);
1864
	if(currReqMana > nextReqMana) //player has reached max magic level
1865
		return;
1866
1867
	if(useMultiplier)
1868
		amount = uint64_t((double)amount * rates[SKILL__MAGLEVEL] * g_config.getDouble(ConfigManager::RATE_MAGIC));
1869
1870
	bool advance = false;
1871
	while(manaSpent + amount >= nextReqMana)
1872
	{
1873
		amount -= nextReqMana - manaSpent;
1874
		manaSpent = 0;
1875
		magLevel++;
1876
1877
		char advMsg[50];
1878
		sprintf(advMsg, "You advanced to magic level %d.", magLevel);
1879
		sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
1880
1881
		advance = true;
1882
		CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE);
1883
		for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it)
1884
			(*it)->executeAdvance(this, SKILL__MAGLEVEL, (magLevel - 1), magLevel);
1885
1886
		currReqMana = nextReqMana;
1887
		nextReqMana = vocation->getReqMana(magLevel + 1);
1888
		if(currReqMana > nextReqMana)
1889
		{
1890
			amount = 0;
1891
			break;
1892
		}
1893
	}
1894
1895
	if(amount)
1896
		manaSpent += amount;
1897
1898
	uint32_t newPercent = Player::getPercentLevel(manaSpent, nextReqMana);
1899
	if(magLevelPercent != newPercent)
1900
	{
1901
		magLevelPercent = newPercent;
1902
		sendStats();
1903
	}
1904
	else if(advance)
1905
		sendStats();
1906
}
1907
1908
void Player::addExperience(uint64_t exp)
1909
{
1910
	uint32_t prevLevel = level;
1911
	uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
1912
	if(Player::getExpForLevel(level) > nextLevelExp)
1913
	{
1914
		//player has reached max level
1915
		levelPercent = 0;
1916
		sendStats();
1917
		return;
1918
	}
1919
1920
	experience += exp;
1921
	while(experience >= nextLevelExp)
1922
	{
1923
		healthMax += vocation->getGain(GAIN_HEALTH);
1924
		health += vocation->getGain(GAIN_HEALTH);
1925
		manaMax += vocation->getGain(GAIN_MANA);
1926
		mana += vocation->getGain(GAIN_MANA);
1927
		capacity += vocation->getGainCap();
1928
1929
		++level;
1930
		nextLevelExp = Player::getExpForLevel(level + 1);
1931
		if(Player::getExpForLevel(level) > nextLevelExp) //player has reached max level
1932
			break;
1933
	}
1934
1935
	if(prevLevel != level)
1936
	{
1937
		updateBaseSpeed();
1938
		g_game.changeSpeed(this, 0);
1939
		g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_BIGCLOUDS);
1940
		if(g_config.getBool(ConfigManager::HEAL_PLAYER_ON_LEVEL))
1941
		{
1942
			health = healthMax;
1943
			mana = manaMax;
1944
		}
1945
1946
		g_game.addCreatureHealth(this);
1947
		if(party)
1948
			party->updateSharedExperience();
1949
1950
		char advMsg[60];
1951
		sprintf(advMsg, "You advanced from Level %d to Level %d.", prevLevel, level);
1952
		sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
1953
1954
		CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE);
1955
		for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it)
1956
			(*it)->executeAdvance(this, SKILL__LEVEL, prevLevel, level);
1957
	}
1958
1959
	uint64_t currLevelExp = Player::getExpForLevel(level);
1960
	nextLevelExp = Player::getExpForLevel(level + 1);
1961
	levelPercent = 0;
1962
	if(nextLevelExp > currLevelExp)
1963
		levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
1964
1965
	sendStats();
1966
}
1967
1968
void Player::removeExperience(uint64_t exp, bool updateStats/* = true*/)
1969
{
1970
	uint32_t prevLevel = level;
1971
	experience -= std::min(exp, experience);
1972
	while(level > 1 && experience < Player::getExpForLevel(level))
1973
	{
1974
		level--;
1975
		healthMax = std::max((int32_t)0, (healthMax - (int32_t)vocation->getGain(GAIN_HEALTH)));
1976
		manaMax = std::max((int32_t)0, (manaMax - (int32_t)vocation->getGain(GAIN_MANA)));
1977
		capacity = std::max((double)0, (capacity - (double)vocation->getGainCap()));
1978
	}
1979
1980
	if(prevLevel != level)
1981
	{
1982
		if(updateStats)
1983
		{
1984
			updateBaseSpeed();
1985
			g_game.changeSpeed(this, 0);
1986
			g_game.addCreatureHealth(this);
1987
		}
1988
1989
		char advMsg[90];
1990
		sprintf(advMsg, "You were downgraded from Level %d to Level %d.", prevLevel, level);
1991
		sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
1992
	}
1993
1994
	uint64_t currLevelExp = Player::getExpForLevel(level),
1995
		nextLevelExp = Player::getExpForLevel(level + 1);
1996
	if(nextLevelExp > currLevelExp)
1997
		levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
1998
	else
1999
		levelPercent = 0;
2000
2001
	if(updateStats)
2002
		sendStats();
2003
}
2004
2005
uint32_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount)
2006
{
2007
	if(nextLevelCount > 0)
2008
		return std::min((uint32_t)100, std::max((uint32_t)0, uint32_t(count * 100 / nextLevelCount)));
2009
2010
	return 0;
2011
}
2012
2013
void Player::onBlockHit(BlockType_t)
2014
{
2015
	if(shieldBlockCount > 0)
2016
	{
2017
		--shieldBlockCount;
2018
		if(hasShield())
2019
			addSkillAdvance(SKILL_SHIELD, 1);
2020
	}
2021
}
2022
2023
void Player::onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType)
2024
{
2025
	Creature::onAttackedCreatureBlockHit(target, blockType);
2026
	lastAttackBlockType = blockType;
2027
	switch(blockType)
2028
	{
2029
		case BLOCK_NONE:
2030
		{
2031
			bloodHitCount = shieldBlockCount = 30;
2032
			addAttackSkillPoint = true;
2033
			break;
2034
		}
2035
2036
		case BLOCK_DEFENSE:
2037
		case BLOCK_ARMOR:
2038
		{
2039
			//need to draw blood every 30 hits
2040
			if(bloodHitCount > 0)
2041
			{
2042
				addAttackSkillPoint = true;
2043
				--bloodHitCount;
2044
			}
2045
			else
2046
				addAttackSkillPoint = false;
2047
2048
			break;
2049
		}
2050
2051
		default:
2052
		{
2053
			addAttackSkillPoint = false;
2054
			break;
2055
		}
2056
	}
2057
}
2058
2059
bool Player::hasShield() const
2060
{
2061
	Item* item = getInventoryItem(SLOT_LEFT);
2062
	return (item && item->getWeaponType() == WEAPON_SHIELD) || ((item = getInventoryItem(SLOT_RIGHT)) && item->getWeaponType() == WEAPON_SHIELD);
2063
}
2064
2065
BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
2066
	bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool reflect/* = true*/, bool field/* = false*/)
2067
{
2068
	BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, reflect, field);
2069
	if(attacker)
2070
	{
2071
		int16_t color = g_config.getNumber(ConfigManager::SQUARE_COLOR);
2072
		if(color < 0)
2073
			color = random_range(0, 254);
2074
2075
		sendCreatureSquare(attacker, color);
2076
	}
2077
2078
	if(blockType != BLOCK_NONE)
2079
		return blockType;
2080
2081
	if(vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE) != 1.0 && combatType != COMBAT_PHYSICALDAMAGE &&
2082
		combatType != COMBAT_NONE && combatType != COMBAT_UNDEFINEDDAMAGE && combatType != COMBAT_DROWNDAMAGE)
2083
		damage -= (int32_t)std::ceil((double)(damage * vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE)) / 100.);
2084
2085
	if(damage <= 0)
2086
		return blockType;
2087
2088
	int32_t blocked = 0, reflected = 0;
2089
	if(reflect)
2090
		reflect = attacker && !attacker->isRemoved() && attacker->getHealth() > 0;
2091
2092
	Item* item = NULL;
2093
	for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
2094
	{
2095
		if(!(item = getInventoryItem((slots_t)slot)) || item->isRemoved() ||
2096
				(g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)slot)))
2097
			continue;
2098
2099
		const ItemType& it = Item::items[item->getID()];
2100
		if(!field)
2101
		{
2102
			if(it.abilities.absorb[combatType])
2103
			{
2104
				blocked += (int32_t)std::ceil((double)(damage * it.abilities.absorb[combatType]) / 100.);
2105
				if(item->hasCharges())
2106
					g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
2107
			}
2108
		}
2109
		else if(it.abilities.fieldAbsorb[combatType])
2110
		{
2111
			blocked += (int32_t)std::ceil((double)(damage * it.abilities.fieldAbsorb[combatType]) / 100.);
2112
			if(item->hasCharges())
2113
				g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
2114
		}
2115
2116
		if(!reflect)
2117
			continue;
2118
2119
		if(it.abilities.reflect[REFLECT_PERCENT][combatType] && random_range(1, 100) < it.abilities.reflect[REFLECT_CHANCE][combatType])
2120
		{
2121
			reflected += (int32_t)std::ceil((double)(damage * it.abilities.reflect[REFLECT_PERCENT][combatType]) / 100.);
2122
			if(item->hasCharges() && !it.abilities.absorb[combatType])
2123
				g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
2124
		}
2125
	}
2126
2127
	if(outfitAttributes)
2128
	{
2129
		uint32_t tmp = Outfits::getInstance()->getOutfitAbsorb(defaultOutfit.lookType, sex, combatType);
2130
		if(tmp)
2131
			blocked += (int32_t)std::ceil((double)(damage * tmp) / 100.);
2132
2133
		if(reflect)
2134
		{
2135
			tmp = Outfits::getInstance()->getOutfitReflect(defaultOutfit.lookType, sex, combatType);
2136
			if(tmp)
2137
				reflected += (int32_t)std::ceil((double)(damage * tmp) / 100.);
2138
		}
2139
	}
2140
2141
	if(vocation->getAbsorb(combatType))
2142
		blocked += (int32_t)std::ceil((double)(damage * vocation->getAbsorb(combatType)) / 100.);
2143
2144
	if(reflect && vocation->getReflect(combatType))
2145
		reflected += (int32_t)std::ceil((double)(damage * vocation->getReflect(combatType)) / 100.);
2146
2147
	damage -= blocked;
2148
	if(damage <= 0)
2149
	{
2150
		damage = 0;
2151
		blockType = BLOCK_DEFENSE;
2152
	}
2153
2154
	if(reflected)
2155
	{
2156
		if(combatType != COMBAT_HEALING)
2157
			reflected = -reflected;
2158
2159
		if(!g_game.combatBlockHit(combatType, this, attacker, reflected, false, false, false))
2160
			g_game.combatChangeHealth(combatType, NULL, attacker, reflected);
2161
	}
2162
2163
	return blockType;
2164
}
2165
2166
uint32_t Player::getIP() const
2167
{
2168
	if(client)
2169
		return client->getIP();
2170
2171
	return lastIP;
2172
}
2173
2174
bool Player::onDeath()
2175
{
2176
	Item *preventLoss = NULL, *preventDrop = NULL;
2177
	if(getZone() == ZONE_HARDCORE)
2178
	{
2179
		setDropLoot(LOOT_DROP_NONE);
2180
		setLossSkill(false);
2181
	}
2182
	else if(skull < SKULL_RED)
2183
	{
2184
		Item* item = NULL;
2185
		for(int32_t i = SLOT_FIRST; ((skillLoss || lootDrop == LOOT_DROP_FULL) && i < SLOT_LAST); ++i)
2186
		{
2187
			if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() ||
2188
				(g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i)))
2189
				continue;
2190
2191
			const ItemType& it = Item::items[item->getID()];
2192
			if(lootDrop == LOOT_DROP_FULL && it.abilities.preventDrop)
2193
			{
2194
				setDropLoot(LOOT_DROP_PREVENT);
2195
				preventDrop = item;
2196
			}
2197
2198
			if(skillLoss && !preventLoss && it.abilities.preventLoss)
2199
				preventLoss = item;
2200
		}
2201
	}
2202
2203
	if(!Creature::onDeath())
2204
	{
2205
		if(preventDrop)
2206
			setDropLoot(LOOT_DROP_FULL);
2207
2208
		return false;
2209
	}
2210
2211
	if(preventLoss)
2212
	{
2213
		setLossSkill(false);
2214
		if(preventLoss->getCharges() > 1) //weird, but transform failed to remove for some hosters
2215
			g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, ((int32_t)preventLoss->getCharges() - 1)));
2216
		else
2217
			g_game.internalRemoveItem(NULL, preventDrop);
2218
	}
2219
2220
	if(preventDrop && preventDrop != preventLoss)
2221
	{
2222
		if(preventDrop->getCharges() > 1) //weird, but transform failed to remove for some hosters
2223
			g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, ((int32_t)preventDrop->getCharges() - 1)));
2224
		else
2225
			g_game.internalRemoveItem(NULL, preventDrop);
2226
	}
2227
2228
	removeConditions(CONDITIONEND_DEATH);
2229
	if(skillLoss)
2230
	{
2231
		uint64_t lossExperience = getLostExperience();
2232
		removeExperience(lossExperience, false);
2233
		double percent = 1. - ((double)(experience - lossExperience) / experience);
2234
2235
		//Magic level loss
2236
		uint32_t sumMana = 0;
2237
		uint64_t lostMana = 0;
2238
		for(uint32_t i = 1; i <= magLevel; ++i)
2239
			sumMana += vocation->getReqMana(i);
2240
2241
		sumMana += manaSpent;
2242
		lostMana = (uint64_t)std::ceil(sumMana * ((double)(percent * lossPercent[LOSS_MANA]) / 100.));
2243
		while(lostMana > manaSpent && magLevel > 0)
2244
		{
2245
			lostMana -= manaSpent;
2246
			manaSpent = vocation->getReqMana(magLevel);
2247
			magLevel--;
2248
		}
2249
2250
		manaSpent -= std::max((int32_t)0, (int32_t)lostMana);
2251
		uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
2252
		if(nextReqMana > vocation->getReqMana(magLevel))
2253
			magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
2254
		else
2255
			magLevelPercent = 0;
2256
2257
		//Skill loss
2258
		uint32_t lostSkillTries, sumSkillTries;
2259
		for(int16_t i = 0; i < 7; ++i) //for each skill
2260
		{
2261
			lostSkillTries = sumSkillTries = 0;
2262
			for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) //sum up all required tries for all skill levels
2263
				sumSkillTries += vocation->getReqSkillTries(i, c);
2264
2265
			sumSkillTries += skills[i][SKILL_TRIES];
2266
			lostSkillTries = (uint32_t)std::ceil(sumSkillTries * ((double)(percent * lossPercent[LOSS_SKILLS]) / 100.));
2267
			while(lostSkillTries > skills[i][SKILL_TRIES])
2268
			{
2269
				lostSkillTries -= skills[i][SKILL_TRIES];
2270
				skills[i][SKILL_TRIES] = vocation->getReqSkillTries(i, skills[i][SKILL_LEVEL]);
2271
				if(skills[i][SKILL_LEVEL] < 11)
2272
				{
2273
					skills[i][SKILL_LEVEL] = 10;
2274
					skills[i][SKILL_TRIES] = lostSkillTries = 0;
2275
					break;
2276
				}
2277
				else
2278
					skills[i][SKILL_LEVEL]--;
2279
			}
2280
2281
			skills[i][SKILL_TRIES] = std::max((int32_t)0, (int32_t)(skills[i][SKILL_TRIES] - lostSkillTries));
2282
		}
2283
2284
		blessings = 0;
2285
		loginPosition = masterPosition;
2286
			if(g_config.getBool(ConfigManager::ROOK_SYSTEM) && level <= (uint32_t)g_config.getNumber(ConfigManager::ROOK_LEVELTO) && vocationId != 0)
2287
				{
2288
						if(Town* _town = Towns::getInstance()->getTown(g_config.getNumber(ConfigManager::ROOK_TOWN)))
2289
						{
2290
								level = 1;
2291
								soulMax = 100;
2292
								capacity = 555;
2293
								stamina = STAMINA_MAX;
2294
								health = healthMax = 155;
2295
								mana = manaMax = 55;
2296
								loginPosition = masterPosition = _town->getPosition();
2297
								experience = magLevel = manaSpent = soul = balance = marriage = promotionLevel = 0;
2298
								setTown(_town->getID());
2299
								setVocation(0);
2300
								leaveGuild();
2301
								storageMap.clear();
2302
2303
								for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
2304
								{
2305
										skills[i][SKILL_LEVEL] = 10;
2306
										skills[i][SKILL_TRIES] = 0;
2307
								}
2308
2309
								for(uint32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
2310
								{
2311
										if(inventory[i])
2312
												g_game.internalRemoveItem(NULL, inventory[i]);
2313
								}
2314
						}
2315
				}
2316
				else if(!inventory[SLOT_BACKPACK])
2317
			__internalAddThing(SLOT_BACKPACK, Item::CreateItem(g_config.getNumber(ConfigManager::DEATH_CONTAINER)));
2318
2319
		sendIcons();
2320
		sendStats();
2321
		sendSkills();
2322
2323
		g_creatureEvents->playerLogout(this, true);
2324
		g_game.removeCreature(this, false);
2325
		sendReLoginWindow();
2326
	}
2327
	else
2328
	{
2329
		setLossSkill(true);
2330
		if(preventLoss)
2331
		{
2332
			loginPosition = masterPosition;
2333
			g_creatureEvents->playerLogout(this, true);
2334
2335
			g_game.removeCreature(this, false);
2336
			sendReLoginWindow();
2337
		}
2338
	}
2339
2340
	return true;
2341
}
2342
2343
void Player::dropCorpse(DeathList deathList)
2344
{
2345
	if(lootDrop == LOOT_DROP_NONE)
2346
	{
2347
		pzLocked = false;
2348
		if(health <= 0)
2349
		{
2350
			health = healthMax;
2351
			if(getZone() != ZONE_HARDCORE || g_config.getBool(ConfigManager::PVPZONE_ADDMANASPENT))
2352
			mana = manaMax;
2353
		}
2354
2355
		setDropLoot(LOOT_DROP_FULL);
2356
		sendStats();
2357
		sendIcons();
2358
2359
		onIdleStatus();
2360
		g_game.addCreatureHealth(this);
2361
		g_game.internalTeleport(this, masterPosition, false);
2362
	}
2363
	else
2364
	{
2365
		Creature::dropCorpse(deathList);
2366
		if(g_config.getBool(ConfigManager::DEATH_LIST))
2367
			IOLoginData::getInstance()->playerDeath(this, deathList);
2368
	}
2369
}
2370
2371
Item* Player::createCorpse(DeathList deathList)
2372
{
2373
	Item* corpse = Creature::createCorpse(deathList);
2374
	if(!corpse)
2375
		return NULL;
2376
2377
	std::stringstream ss;
2378
	ss << "You recognize " << getNameDescription() << ". " << (sex % 2 ? "He" : "She") << " was killed by ";
2379
	if(deathList[0].isCreatureKill())
2380
	{
2381
		ss << deathList[0].getKillerCreature()->getNameDescription();
2382
		if(deathList[0].getKillerCreature()->getMaster())
2383
			ss << " summoned by " << deathList[0].getKillerCreature()->getMaster()->getNameDescription();
2384
	}
2385
	else
2386
		ss << deathList[0].getKillerName();
2387
2388
	if(deathList.size() > 1)
2389
	{
2390
		if(deathList[0].getKillerType() != deathList[1].getKillerType())
2391
		{
2392
			if(deathList[1].isCreatureKill())
2393
			{
2394
				ss << " and by " << deathList[1].getKillerCreature()->getNameDescription();
2395
				if(deathList[1].getKillerCreature()->getMaster())
2396
					ss << " summoned by " << deathList[1].getKillerCreature()->getMaster()->getNameDescription();
2397
			}
2398
			else
2399
				ss << " and by " << deathList[1].getKillerName();
2400
		}
2401
		else if(deathList[1].isCreatureKill())
2402
		{
2403
			if(deathList[0].getKillerCreature()->getName() != deathList[1].getKillerCreature()->getName())
2404
			{
2405
				ss << " and by " << deathList[1].getKillerCreature()->getNameDescription();
2406
				if(deathList[1].getKillerCreature()->getMaster())
2407
					ss << " summoned by " << deathList[1].getKillerCreature()->getMaster()->getNameDescription();
2408
			}
2409
		}
2410
		else if(asLowerCaseString(deathList[0].getKillerName()) != asLowerCaseString(deathList[1].getKillerName()))
2411
			ss << " and by " << deathList[1].getKillerName();
2412
	}
2413
2414
	ss << ".";
2415
	corpse->setSpecialDescription(ss.str().c_str());
2416
	return corpse;
2417
}
2418
2419
void Player::addExhaust(uint32_t ticks, Exhaust_t type)
2420
{
2421
	if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT,
2422
		CONDITION_EXHAUST, ticks, 0, false, (uint32_t)type))
2423
		addCondition(condition);
2424
}
2425
2426
void Player::addInFightTicks(bool pzLock, int32_t ticks/* = 0*/)
2427
{
2428
	if(hasFlag(PlayerFlag_NotGainInFight))
2429
		return;
2430
2431
	if(!ticks)
2432
		ticks = g_config.getNumber(ConfigManager::PZ_LOCKED);
2433
	else
2434
		ticks = std::max(-1, ticks);
2435
2436
	if(pzLock)
2437
		pzLocked = true;
2438
2439
	if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT,
2440
		CONDITION_INFIGHT, ticks))
2441
		addCondition(condition);
2442
}
2443
2444
void Player::addDefaultRegeneration(uint32_t addTicks)
2445
{
2446
	Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
2447
	if(condition)
2448
		condition->setTicks(condition->getTicks() + addTicks);
2449
	else if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_REGENERATION, addTicks)))
2450
	{
2451
		condition->setParam(CONDITIONPARAM_HEALTHGAIN, vocation->getGainAmount(GAIN_HEALTH));
2452
		condition->setParam(CONDITIONPARAM_HEALTHTICKS, vocation->getGainTicks(GAIN_HEALTH) * 1000);
2453
		condition->setParam(CONDITIONPARAM_MANAGAIN, vocation->getGainAmount(GAIN_MANA));
2454
		condition->setParam(CONDITIONPARAM_MANATICKS, vocation->getGainTicks(GAIN_MANA) * 1000);
2455
		addCondition(condition);
2456
	}
2457
}
2458
2459
void Player::removeList()
2460
{
2461
	Manager::getInstance()->removeUser(id);
2462
	autoList.erase(id);
2463
	if(!isGhost())
2464
	{
2465
		for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2466
			it->second->notifyLogOut(this);
2467
	}
2468
	else
2469
	{
2470
		for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2471
		{
2472
			if(it->second->canSeeCreature(this))
2473
				it->second->notifyLogOut(this);
2474
		}
2475
	}
2476
}
2477
2478
void Player::addList()
2479
{
2480
	if(!isGhost())
2481
	{
2482
		for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2483
			it->second->notifyLogIn(this);
2484
	}
2485
	else
2486
	{
2487
		for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2488
		{
2489
			if(it->second->canSeeCreature(this))
2490
				it->second->notifyLogIn(this);
2491
		}
2492
	}
2493
2494
	autoList[id] = this;
2495
	Manager::getInstance()->addUser(this);
2496
}
2497
2498
void Player::kick(bool displayEffect, bool forceLogout)
2499
{
2500
	if(!client)
2501
	{
2502
		if(g_creatureEvents->playerLogout(this, forceLogout))
2503
			g_game.removeCreature(this);
2504
	}
2505
	else
2506
		client->logout(displayEffect, forceLogout);
2507
}
2508
2509
void Player::notifyLogIn(Player* loginPlayer)
2510
{
2511
	if(!client)
2512
		return;
2513
2514
	VIPSet::iterator it = VIPList.find(loginPlayer->getGUID());
2515
	if(it != VIPList.end())
2516
		client->sendVIPLogIn(loginPlayer->getGUID());
2517
}
2518
2519
void Player::notifyLogOut(Player* logoutPlayer)
2520
{
2521
	if(!client)
2522
		return;
2523
2524
	VIPSet::iterator it = VIPList.find(logoutPlayer->getGUID());
2525
	if(it != VIPList.end())
2526
		client->sendVIPLogOut(logoutPlayer->getGUID());
2527
}
2528
2529
bool Player::removeVIP(uint32_t _guid)
2530
{
2531
	VIPSet::iterator it = VIPList.find(_guid);
2532
	if(it == VIPList.end())
2533
		return false;
2534
2535
	VIPList.erase(it);
2536
	return true;
2537
}
2538
2539
bool Player::addVIP(uint32_t _guid, std::string& name, bool isOnline, bool internal/* = false*/)
2540
{
2541
	if(guid == _guid)
2542
	{
2543
		if(!internal)
2544
			sendTextMessage(MSG_STATUS_SMALL, "You cannot add yourself.");
2545
2546
		return false;
2547
	}
2548
2549
	if(VIPList.size() > (group ? group->getMaxVips(isPremium()) : g_config.getNumber(ConfigManager::VIPLIST_DEFAULT_LIMIT)))
2550
	{
2551
		if(!internal)
2552
			sendTextMessage(MSG_STATUS_SMALL, "You cannot add more buddies.");
2553
2554
		return false;
2555
	}
2556
2557
	VIPSet::iterator it = VIPList.find(_guid);
2558
	if(it != VIPList.end())
2559
	{
2560
		if(!internal)
2561
			sendTextMessage(MSG_STATUS_SMALL, "This player is already in your list.");
2562
2563
		return false;
2564
	}
2565
2566
	VIPList.insert(_guid);
2567
	if(client && !internal)
2568
		client->sendVIP(_guid, name, isOnline);
2569
2570
	return true;
2571
}
2572
2573
//close container and its child containers
2574
void Player::autoCloseContainers(const Container* container)
2575
{
2576
	typedef std::vector<uint32_t> CloseList;
2577
	CloseList closeList;
2578
	for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it)
2579
	{
2580
		Container* tmp = it->second;
2581
		while(tmp != NULL)
2582
		{
2583
			if(tmp->isRemoved() || tmp == container)
2584
			{
2585
				closeList.push_back(it->first);
2586
				break;
2587
			}
2588
2589
			tmp = dynamic_cast<Container*>(tmp->getParent());
2590
		}
2591
	}
2592
2593
	for(CloseList::iterator it = closeList.begin(); it != closeList.end(); ++it)
2594
	{
2595
		closeContainer(*it);
2596
		if(client)
2597
			client->sendCloseContainer(*it);
2598
	}
2599
}
2600
2601
bool Player::hasCapacity(const Item* item, uint32_t count) const
2602
{
2603
	if(hasFlag(PlayerFlag_CannotPickupItem))
2604
		return false;
2605
2606
	if(hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this)
2607
		return true;
2608
2609
	double itemWeight = 0;
2610
	if(item->isStackable())
2611
		itemWeight = Item::items[item->getID()].weight * count;
2612
	else
2613
		itemWeight = item->getWeight();
2614
2615
	return (itemWeight < getFreeCapacity());
2616
}
2617
2618
ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags, Creature*) const
2619
{
2620
	const Item* item = thing->getItem();
2621
	if(!item)
2622
		return RET_NOTPOSSIBLE;
2623
2624
	bool childOwner = ((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER), skipLimit = ((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT);
2625
	if(childOwner)
2626
	{
2627
		//a child container is querying the player, just check if enough capacity
2628
		if(skipLimit || hasCapacity(item, count))
2629
			return RET_NOERROR;
2630
2631
		return RET_NOTENOUGHCAPACITY;
2632
	}
2633
2634
	if(!item->isPickupable())
2635
		return RET_CANNOTPICKUP;
2636
2637
	ReturnValue ret = RET_NOERROR;
2638
	if((item->getSlotPosition() & SLOTP_HEAD) || (item->getSlotPosition() & SLOTP_NECKLACE) ||
2639
		(item->getSlotPosition() & SLOTP_BACKPACK) || (item->getSlotPosition() & SLOTP_ARMOR) ||
2640
		(item->getSlotPosition() & SLOTP_LEGS) || (item->getSlotPosition() & SLOTP_FEET) ||
2641
		(item->getSlotPosition() & SLOTP_RING) || (!g_config.getBool(ConfigManager::TIBIA_SLOTS) &&
2642
		(item->getSlotPosition() & SLOTP_AMMO)))
2643
		ret = RET_CANNOTBEDRESSED;
2644
	else if(item->getSlotPosition() & SLOTP_TWO_HAND)
2645
		ret = RET_PUTTHISOBJECTINBOTHHANDS;
2646
	else if((item->getSlotPosition() & SLOTP_RIGHT) || (item->getSlotPosition() & SLOTP_LEFT))
2647
		ret = RET_PUTTHISOBJECTINYOURHAND;
2648
2649
	switch(index)
2650
	{
2651
		case SLOT_HEAD:
2652
			if(item->getSlotPosition() & SLOTP_HEAD)
2653
				ret = RET_NOERROR;
2654
			break;
2655
		case SLOT_NECKLACE:
2656
			if(item->getSlotPosition() & SLOTP_NECKLACE)
2657
				ret = RET_NOERROR;
2658
			break;
2659
		case SLOT_BACKPACK:
2660
			if(item->getSlotPosition() & SLOTP_BACKPACK)
2661
				ret = RET_NOERROR;
2662
			break;
2663
		case SLOT_ARMOR:
2664
			if(item->getSlotPosition() & SLOTP_ARMOR)
2665
				ret = RET_NOERROR;
2666
			break;
2667
		case SLOT_RIGHT:
2668
			if(item->getSlotPosition() & SLOTP_RIGHT)
2669
			{
2670
				//check if we already carry an item in the other hand
2671
				if(!g_config.getBool(ConfigManager::TIBIA_SLOTS))
2672
				{
2673
					if(!item->isWeapon() || (item->getWeaponType() != WEAPON_SHIELD && !item->isDualWield()))
2674
						ret = RET_NOTPOSSIBLE;
2675
					else if(inventory[SLOT_LEFT] && inventory[SLOT_LEFT]->getSlotPosition() & SLOTP_TWO_HAND)
2676
						ret = RET_DROPTWOHANDEDITEM;
2677
					else
2678
						ret = RET_NOERROR;
2679
				}
2680
				else if(item->getSlotPosition() & SLOTP_TWO_HAND)
2681
				{
2682
					if(inventory[SLOT_LEFT] && inventory[SLOT_LEFT] != item)
2683
						ret = RET_BOTHHANDSNEEDTOBEFREE;
2684
					else
2685
						ret = RET_NOERROR;
2686
				}
2687
				else if(inventory[SLOT_LEFT])
2688
				{
2689
					const Item* leftItem = inventory[SLOT_LEFT];
2690
					WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();
2691
					if(leftItem->getSlotPosition() & SLOTP_TWO_HAND)
2692
						ret = RET_DROPTWOHANDEDITEM;
2693
					else if(item == leftItem && item->getItemCount() == count)
2694
						ret = RET_NOERROR;
2695
					else if(leftType == WEAPON_SHIELD && type == WEAPON_SHIELD)
2696
						ret = RET_CANONLYUSEONESHIELD;
2697
					else if(!leftItem->isWeapon() || !item->isWeapon() ||
2698
						leftType == WEAPON_AMMO || type == WEAPON_AMMO ||
2699
						leftType == WEAPON_SHIELD || type == WEAPON_SHIELD ||
2700
						(leftItem->isDualWield() && item->isDualWield()))
2701
						ret = RET_NOERROR;
2702
					else
2703
						ret = RET_CANONLYUSEONEWEAPON;
2704
				}
2705
				else
2706
					ret = RET_NOERROR;
2707
			}
2708
			break;
2709
		case SLOT_LEFT:
2710
			if(item->getSlotPosition() & SLOTP_LEFT)
2711
			{
2712
				if(!g_config.getBool(ConfigManager::TIBIA_SLOTS))
2713
				{
2714
					if(!item->isWeapon() || item->getWeaponType() == WEAPON_SHIELD)
2715
						ret = RET_NOTPOSSIBLE;
2716
					else if(inventory[SLOT_RIGHT] && item->getSlotPosition() & SLOTP_TWO_HAND)
2717
						ret = RET_BOTHHANDSNEEDTOBEFREE;
2718
					else
2719
						ret = RET_NOERROR;
2720
				}
2721
				else if(item->getSlotPosition() & SLOTP_TWO_HAND)
2722
				{
2723
					if(inventory[SLOT_RIGHT] && inventory[SLOT_RIGHT] != item)
2724
						ret = RET_BOTHHANDSNEEDTOBEFREE;
2725
					else
2726
						ret = RET_NOERROR;
2727
				}
2728
				else if(inventory[SLOT_RIGHT])
2729
				{
2730
					const Item* rightItem = inventory[SLOT_RIGHT];
2731
					WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();
2732
					if(rightItem->getSlotPosition() & SLOTP_TWO_HAND)
2733
						ret = RET_DROPTWOHANDEDITEM;
2734
					else if(item == rightItem && item->getItemCount() == count)
2735
						ret = RET_NOERROR;
2736
					else if(rightType == WEAPON_SHIELD && type == WEAPON_SHIELD)
2737
						ret = RET_CANONLYUSEONESHIELD;
2738
					else if(!rightItem->isWeapon() || !item->isWeapon() ||
2739
						rightType == WEAPON_AMMO || type == WEAPON_AMMO ||
2740
						rightType == WEAPON_SHIELD || type == WEAPON_SHIELD ||
2741
						(rightItem->isDualWield() && item->isDualWield()))
2742
						ret = RET_NOERROR;
2743
					else
2744
						ret = RET_CANONLYUSEONEWEAPON;
2745
				}
2746
				else
2747
					ret = RET_NOERROR;
2748
			}
2749
			break;
2750
		case SLOT_LEGS:
2751
			if(item->getSlotPosition() & SLOTP_LEGS)
2752
				ret = RET_NOERROR;
2753
			break;
2754
		case SLOT_FEET:
2755
			if(item->getSlotPosition() & SLOTP_FEET)
2756
				ret = RET_NOERROR;
2757
			break;
2758
		case SLOT_RING:
2759
			if(item->getSlotPosition() & SLOTP_RING)
2760
				ret = RET_NOERROR;
2761
			break;
2762
		case SLOT_AMMO:
2763
			if(item->getSlotPosition() & SLOTP_AMMO)
2764
				ret = RET_NOERROR;
2765
			break;
2766
		case SLOT_WHEREEVER:
2767
		case -1:
2768
			ret = RET_NOTENOUGHROOM;
2769
			break;
2770
		default:
2771
			ret = RET_NOTPOSSIBLE;
2772
			break;
2773
	}
2774
2775
	if(ret == RET_NOERROR || ret == RET_NOTENOUGHROOM)
2776
	{
2777
		//need an exchange with source?
2778
		if(getInventoryItem((slots_t)index) != NULL && (!getInventoryItem((slots_t)index)->isStackable()
2779
			|| getInventoryItem((slots_t)index)->getID() != item->getID()))
2780
			return RET_NEEDEXCHANGE;
2781
2782
		if(!g_moveEvents->onPlayerEquip(const_cast<Player*>(this), const_cast<Item*>(item), (slots_t)index, true))
2783
			return RET_CANNOTBEDRESSED;
2784
2785
		//check if enough capacity
2786
		if(!hasCapacity(item, count))
2787
			return RET_NOTENOUGHCAPACITY;
2788
	}
2789
2790
	return ret;
2791
}
2792
2793
ReturnValue Player::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount,
2794
	uint32_t flags) const
2795
{
2796
	const Item* item = thing->getItem();
2797
	if(!item)
2798
	{
2799
		maxQueryCount = 0;
2800
		return RET_NOTPOSSIBLE;
2801
	}
2802
2803
	if(index == INDEX_WHEREEVER)
2804
	{
2805
		uint32_t n = 0;
2806
		for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
2807
		{
2808
			if(Item* inventoryItem = inventory[i])
2809
			{
2810
				if(Container* subContainer = inventoryItem->getContainer())
2811
				{
2812
					uint32_t queryCount = 0;
2813
					subContainer->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), queryCount, flags);
2814
2815
					//iterate through all items, including sub-containers (deep search)
2816
					n += queryCount;
2817
					for(ContainerIterator cit = subContainer->begin(); cit != subContainer->end(); ++cit)
2818
					{
2819
						if(Container* tmpContainer  = (*cit)->getContainer())
2820
						{
2821
							queryCount = 0;
2822
							tmpContainer->__queryMaxCount(INDEX_WHEREEVER, item, item->getItemCount(), queryCount, flags);
2823
							n += queryCount;
2824
						}
2825
					}
2826
				}
2827
				else if(inventoryItem->isStackable() && item->getID() == inventoryItem->getID() && inventoryItem->getItemCount() < 100)
2828
				{
2829
					uint32_t remainder = (100 - inventoryItem->getItemCount());
2830
					if(__queryAdd(i, item, remainder, flags) == RET_NOERROR)
2831
						n += remainder;
2832
				}
2833
			}
2834
			else if(__queryAdd(i, item, item->getItemCount(), flags) == RET_NOERROR)
2835
			{
2836
				if(item->isStackable())
2837
					n += 100;
2838
				else
2839
					n += 1;
2840
			}
2841
		}
2842
2843
		maxQueryCount = n;
2844
	}
2845
	else
2846
	{
2847
		const Thing* destThing = __getThing(index);
2848
		const Item* destItem = NULL;
2849
		if(destThing)
2850
			destItem = destThing->getItem();
2851
2852
		if(destItem)
2853
		{
2854
			if(destItem->isStackable() && item->getID() == destItem->getID() && destItem->getItemCount() < 100)
2855
				maxQueryCount = 100 - destItem->getItemCount();
2856
			else
2857
				maxQueryCount = 0;
2858
		}
2859
		else if(__queryAdd(index, item, count, flags) == RET_NOERROR)
2860
		{
2861
			if(item->isStackable())
2862
				maxQueryCount = 100;
2863
			else
2864
				maxQueryCount = 1;
2865
2866
			return RET_NOERROR;
2867
		}
2868
	}
2869
2870
	if(maxQueryCount < count)
2871
		return RET_NOTENOUGHROOM;
2872
2873
	return RET_NOERROR;
2874
}
2875
2876
ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags, Creature*) const
2877
{
2878
	int32_t index = __getIndexOfThing(thing);
2879
	if(index == -1)
2880
		return RET_NOTPOSSIBLE;
2881
2882
	const Item* item = thing->getItem();
2883
	if(!item)
2884
		return RET_NOTPOSSIBLE;
2885
2886
	if(!count || (item->isStackable() && count > item->getItemCount()))
2887
		return RET_NOTPOSSIBLE;
2888
2889
	 if(!item->isMovable() && !hasBitSet(FLAG_IGNORENOTMOVABLE, flags))
2890
		return RET_NOTMOVABLE;
2891
2892
	return RET_NOERROR;
2893
}
2894
2895
Cylinder* Player::__queryDestination(int32_t& index, const Thing* thing, Item** destItem,
2896
	uint32_t& flags)
2897
{
2898
	if(!index /*drop to capacity window*/ || index == INDEX_WHEREEVER)
2899
	{
2900
		*destItem = NULL;
2901
		const Item* item = thing->getItem();
2902
		if(!item)
2903
			return this;
2904
2905
		std::list<std::pair<Container*, int32_t> > containers;
2906
		std::list<std::pair<Cylinder*, int32_t> > freeSlots;
2907
2908
		bool autoStack = !((flags & FLAG_IGNOREAUTOSTACK) == FLAG_IGNOREAUTOSTACK);
2909
		for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
2910
		{
2911
			if(Item* invItem = inventory[i])
2912
			{
2913
				if(invItem == item || invItem == tradeItem)
2914
					continue;
2915
2916
				if(autoStack && item->isStackable() && __queryAdd(i, item, item->getItemCount(), 0)
2917
					== RET_NOERROR && invItem->getID() == item->getID() && invItem->getItemCount() < 100)
2918
				{
2919
					*destItem = invItem;
2920
					index = i;
2921
					return this;
2922
				}
2923
2924
				if(Container* container = invItem->getContainer())
2925
				{
2926
					if(!autoStack && container->__queryAdd(INDEX_WHEREEVER,
2927
						item, item->getItemCount(), flags) == RET_NOERROR)
2928
					{
2929
						index = INDEX_WHEREEVER;
2930
						return container;
2931
					}
2932
2933
					containers.push_back(std::make_pair(container, 0));
2934
				}
2935
			}
2936
			else if(!autoStack)
2937
			{
2938
				if(__queryAdd(i, item, item->getItemCount(), 0) == RET_NOERROR)
2939
				{
2940
					index = i;
2941
					return this;
2942
				}
2943
			}
2944
			else
2945
				freeSlots.push_back(std::make_pair(this, i));
2946
		}
2947
2948
		int32_t deepness = g_config.getNumber(ConfigManager::PLAYER_DEEPNESS);
2949
		while(!containers.empty())
2950
		{
2951
			Container* tmpContainer = containers.front().first;
2952
			int32_t level = containers.front().second;
2953
2954
			containers.pop_front();
2955
			if(!tmpContainer)
2956
				continue;
2957
2958
			for(uint32_t n = 0; n < tmpContainer->capacity(); ++n)
2959
			{
2960
				if(Item* tmpItem = tmpContainer->getItem(n))
2961
				{
2962
					if(tmpItem == item || tmpItem == tradeItem)
2963
						continue;
2964
2965
					if(autoStack && item->isStackable() && tmpContainer->__queryAdd(n, item, item->getItemCount(),
2966
						0) == RET_NOERROR && tmpItem->getID() == item->getID() && tmpItem->getItemCount() < 100)
2967
					{
2968
						index = n;
2969
						*destItem = tmpItem;
2970
						return tmpContainer;
2971
					}
2972
2973
					if(Container* container = tmpItem->getContainer())
2974
					{
2975
						if(!autoStack && container->__queryAdd(INDEX_WHEREEVER,
2976
							item, item->getItemCount(), flags) == RET_NOERROR)
2977
						{
2978
							index = INDEX_WHEREEVER;
2979
							return container;
2980
						}
2981
2982
						if(deepness < 0 || level < deepness)
2983
							containers.push_back(std::make_pair(container, level + 1));
2984
					}
2985
				}
2986
				else
2987
				{
2988
					if(!autoStack)
2989
					{
2990
						if(tmpContainer->__queryAdd(n, item, item->getItemCount(), 0) == RET_NOERROR)
2991
						{
2992
							index = n;
2993
							return tmpContainer;
2994
						}
2995
					}
2996
					else
2997
						freeSlots.push_back(std::make_pair(tmpContainer, n));
2998
2999
					break; // one slot to check is definitely enough.
3000
				}
3001
			}
3002
		}
3003
3004
		if(autoStack)
3005
		{
3006
			while(!freeSlots.empty())
3007
			{
3008
				Cylinder* tmpCylinder = freeSlots.front().first;
3009
				int32_t i = freeSlots.front().second;
3010
3011
				freeSlots.pop_front();
3012
				if(!tmpCylinder)
3013
					continue;
3014
3015
				if(tmpCylinder->__queryAdd(i, item, item->getItemCount(), flags) == RET_NOERROR)
3016
				{
3017
					index = i;
3018
					return tmpCylinder;
3019
				}
3020
			}
3021
		}
3022
3023
		return this;
3024
	}
3025
3026
	Thing* destThing = __getThing(index);
3027
	if(destThing)
3028
		*destItem = destThing->getItem();
3029
3030
	if(Cylinder* subCylinder = dynamic_cast<Cylinder*>(destThing))
3031
	{
3032
		index = INDEX_WHEREEVER;
3033
		*destItem = NULL;
3034
		return subCylinder;
3035
	}
3036
3037
	return this;
3038
}
3039
3040
void Player::__addThing(Creature* actor, Thing* thing)
3041
{
3042
	__addThing(actor, 0, thing);
3043
}
3044
3045
void Player::__addThing(Creature*, int32_t index, Thing* thing)
3046
{
3047
	if(index < 0 || index > 11)
3048
	{
3049
#ifdef __DEBUG_MOVESYS__
3050
		std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl;
3051
#endif
3052
		return /*RET_NOTPOSSIBLE*/;
3053
	}
3054
3055
	if(!index)
3056
	{
3057
#ifdef __DEBUG_MOVESYS__
3058
		std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", index == 0" << std::endl;
3059
#endif
3060
		return /*RET_NOTENOUGHROOM*/;
3061
	}
3062
3063
	Item* item = thing->getItem();
3064
	if(!item)
3065
	{
3066
#ifdef __DEBUG_MOVESYS__
3067
		std::clog << "Failure: [Player::__addThing], " << "player: " << getName() << ", item == NULL" << std::endl;
3068
#endif
3069
		return /*RET_NOTPOSSIBLE*/;
3070
	}
3071
3072
	item->setParent(this);
3073
	inventory[index] = item;
3074
3075
	//send to client
3076
	sendAddInventoryItem((slots_t)index, item);
3077
	//event methods
3078
	onAddInventoryItem((slots_t)index, item);
3079
}
3080
3081
void Player::__updateThing(Thing* thing, uint16_t itemId, uint32_t count)
3082
{
3083
	int32_t index = __getIndexOfThing(thing);
3084
	if(index == -1)
3085
	{
3086
#ifdef __DEBUG_MOVESYS__
3087
		std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", index == -1" << std::endl;
3088
#endif
3089
		return /*RET_NOTPOSSIBLE*/;
3090
	}
3091
3092
	Item* item = thing->getItem();
3093
	if(!item)
3094
	{
3095
#ifdef __DEBUG_MOVESYS__
3096
		std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl;
3097
#endif
3098
		return /*RET_NOTPOSSIBLE*/;
3099
	}
3100
3101
	const ItemType& oldType = Item::items[item->getID()];
3102
	const ItemType& newType = Item::items[itemId];
3103
3104
	item->setID(itemId);
3105
	item->setSubType(count);
3106
3107
	//send to client
3108
	sendUpdateInventoryItem((slots_t)index, item, item);
3109
	//event methods
3110
	onUpdateInventoryItem((slots_t)index, item, oldType, item, newType);
3111
}
3112
3113
void Player::__replaceThing(uint32_t index, Thing* thing)
3114
{
3115
	if(index > 11)
3116
	{
3117
#ifdef __DEBUG_MOVESYS__
3118
		std::clog << "Failure: [Player::__replaceThing], " << "player: " << getName() << ", index: " << index << ", index < 0 || index > 11" << std::endl;
3119
#endif
3120
		return /*RET_NOTPOSSIBLE*/;
3121
	}
3122
3123
	Item* oldItem = getInventoryItem((slots_t)index);
3124
	if(!oldItem)
3125
	{
3126
#ifdef __DEBUG_MOVESYS__
3127
		std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", oldItem == NULL" << std::endl;
3128
#endif
3129
		return /*RET_NOTPOSSIBLE*/;
3130
	}
3131
3132
	Item* item = thing->getItem();
3133
	if(!item)
3134
	{
3135
#ifdef __DEBUG_MOVESYS__
3136
		std::clog << "Failure: [Player::__updateThing], " << "player: " << getName() << ", item == NULL" << std::endl;
3137
#endif
3138
		return /*RET_NOTPOSSIBLE*/;
3139
	}
3140
3141
	const ItemType& oldType = Item::items[oldItem->getID()];
3142
	const ItemType& newType = Item::items[item->getID()];
3143
3144
	//send to client
3145
	sendUpdateInventoryItem((slots_t)index, oldItem, item);
3146
	//event methods
3147
	onUpdateInventoryItem((slots_t)index, oldItem, oldType, item, newType);
3148
3149
	item->setParent(this);
3150
	inventory[index] = item;
3151
}
3152
3153
void Player::__removeThing(Thing* thing, uint32_t count)
3154
{
3155
	Item* item = thing->getItem();
3156
	if(!item)
3157
	{
3158
#ifdef __DEBUG_MOVESYS__
3159
		std::clog << "Failure: [Player::__removeThing], " << "player: " << getName() << ", item == NULL" << std::endl;
3160
#endif
3161
		return /*RET_NOTPOSSIBLE*/;
3162
	}
3163
3164
	int32_t index = __getIndexOfThing(thing);
3165
	if(index == -1)
3166
	{
3167
#ifdef __DEBUG_MOVESYS__
3168
		std::clog << "Failure: [Player::__removeThing], " << "player: " << getName() << ", index == -1" << std::endl;
3169
#endif
3170
		return /*RET_NOTPOSSIBLE*/;
3171
	}
3172
3173
	if(item->isStackable())
3174
	{
3175
		if(count == item->getItemCount())
3176
		{
3177
			//send change to client
3178
			sendRemoveInventoryItem((slots_t)index, item);
3179
			//event methods
3180
			onRemoveInventoryItem((slots_t)index, item);
3181
3182
			item->setParent(NULL);
3183
			inventory[index] = NULL;
3184
		}
3185
		else
3186
		{
3187
			item->setItemCount(std::max(0, (int32_t)(item->getItemCount() - count)));
3188
			const ItemType& it = Item::items[item->getID()];
3189
3190
			//send change to client
3191
			sendUpdateInventoryItem((slots_t)index, item, item);
3192
			//event methods
3193
			onUpdateInventoryItem((slots_t)index, item, it, item, it);
3194
		}
3195
	}
3196
	else
3197
	{
3198
		//send change to client
3199
		sendRemoveInventoryItem((slots_t)index, item);
3200
		//event methods
3201
		onRemoveInventoryItem((slots_t)index, item);
3202
3203
		item->setParent(NULL);
3204
		inventory[index] = NULL;
3205
	}
3206
}
3207
3208
Thing* Player::__getThing(uint32_t index) const
3209
{
3210
	if(index > SLOT_PRE_FIRST && index < SLOT_LAST)
3211
		return inventory[index];
3212
3213
	return NULL;
3214
}
3215
3216
int32_t Player::__getIndexOfThing(const Thing* thing) const
3217
{
3218
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3219
	{
3220
		if(inventory[i] == thing)
3221
			return i;
3222
	}
3223
3224
	return -1;
3225
}
3226
3227
int32_t Player::__getFirstIndex() const
3228
{
3229
	return SLOT_FIRST;
3230
}
3231
3232
int32_t Player::__getLastIndex() const
3233
{
3234
	return SLOT_LAST;
3235
}
3236
3237
uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
3238
{
3239
	Item* item = NULL;
3240
	Container* container = NULL;
3241
3242
	uint32_t count = 0;
3243
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3244
	{
3245
		if(!(item = inventory[i]))
3246
			continue;
3247
3248
		if(item->getID() != itemId)
3249
		{
3250
			if(!(container = item->getContainer()))
3251
				continue;
3252
3253
			for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it)
3254
			{
3255
				if((*it)->getID() == itemId)
3256
					count += Item::countByType(*it, subType);
3257
			}
3258
		}
3259
		else
3260
			count += Item::countByType(item, subType);
3261
	}
3262
3263
	return count;
3264
3265
}
3266
3267
std::map<uint32_t, uint32_t>& Player::__getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const
3268
{
3269
	Item* item = NULL;
3270
	Container* container = NULL;
3271
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3272
	{
3273
		if(!(item = inventory[i]))
3274
			continue;
3275
3276
		countMap[item->getID()] += Item::countByType(item, -1);
3277
		if(!(container = item->getContainer()))
3278
			continue;
3279
3280
		for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it)
3281
			countMap[(*it)->getID()] += Item::countByType(*it, -1);
3282
	}
3283
3284
	return countMap;
3285
}
3286
3287
void Player::postAddNotification(Creature*, Thing* thing, const Cylinder* oldParent,
3288
	int32_t index, CylinderLink_t link /*= LINK_OWNER*/)
3289
{
3290
	if(link == LINK_OWNER) //calling movement scripts
3291
		g_moveEvents->onPlayerEquip(this, thing->getItem(), (slots_t)index, false);
3292
3293
	bool requireListUpdate = true;
3294
	if(link == LINK_OWNER || link == LINK_TOPPARENT)
3295
	{
3296
		if(const Item* item = (oldParent ? oldParent->getItem() : NULL))
3297
		{
3298
			assert(item->getContainer() != NULL);
3299
			requireListUpdate = item->getContainer()->getHoldingPlayer() != this;
3300
		}
3301
		else
3302
			requireListUpdate = oldParent != this;
3303
3304
		updateInventoryWeight();
3305
		updateItemsLight();
3306
		updateWeapon();
3307
		sendStats();
3308
	}
3309
3310
	if(const Item* item = thing->getItem())
3311
	{
3312
		if(const Container* container = item->getContainer())
3313
			onSendContainer(container);
3314
3315
		if(shopOwner && requireListUpdate)
3316
			updateInventoryGoods(item->getID());
3317
	}
3318
	else if(const Creature* creature = thing->getCreature())
3319
	{
3320
		if(creature != this)
3321
			return;
3322
3323
		std::vector<Container*> containers;
3324
		for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it)
3325
		{
3326
			if(!Position::areInRange<1,1,0>(it->second->getPosition(), getPosition()))
3327
				containers.push_back(it->second);
3328
		}
3329
3330
		for(std::vector<Container*>::const_iterator it = containers.begin(); it != containers.end(); ++it)
3331
			autoCloseContainers(*it);
3332
	}
3333
}
3334
3335
void Player::postRemoveNotification(Creature*, Thing* thing, const Cylinder* newParent,
3336
	int32_t index, bool isCompleteRemoval, CylinderLink_t link /*= LINK_OWNER*/)
3337
{
3338
	if(link == LINK_OWNER) //calling movement scripts
3339
		g_moveEvents->onPlayerDeEquip(this, thing->getItem(), (slots_t)index, isCompleteRemoval);
3340
3341
	bool requireListUpdate = true;
3342
	if(link == LINK_OWNER || link == LINK_TOPPARENT)
3343
	{
3344
		if(const Item* item = (newParent ? newParent->getItem() : NULL))
3345
		{
3346
			assert(item->getContainer() != NULL);
3347
			requireListUpdate = item->getContainer()->getHoldingPlayer() != this;
3348
		}
3349
		else
3350
			requireListUpdate = newParent != this;
3351
3352
		updateInventoryWeight();
3353
		updateItemsLight();
3354
		updateWeapon();
3355
		sendStats();
3356
	}
3357
3358
	if(const Item* item = thing->getItem())
3359
	{
3360
		if(const Container* container = item->getContainer())
3361
		{
3362
			if(container->isRemoved() || !Position::areInRange<1,1,0>(getPosition(), container->getPosition()))
3363
				autoCloseContainers(container);
3364
			else if(container->getTopParent() == this)
3365
				onSendContainer(container);
3366
			else if(const Container* topContainer = dynamic_cast<const Container*>(container->getTopParent()))
3367
			{
3368
				if(const Depot* depot = dynamic_cast<const Depot*>(topContainer))
3369
				{
3370
					bool isOwner = false;
3371
					for(DepotMap::iterator it = depots.begin(); it != depots.end(); ++it)
3372
					{
3373
						if(it->second.first != depot)
3374
							continue;
3375
3376
						isOwner = true;
3377
						onSendContainer(container);
3378
					}
3379
3380
					if(!isOwner)
3381
						autoCloseContainers(container);
3382
				}
3383
				else
3384
					onSendContainer(container);
3385
			}
3386
			else
3387
				autoCloseContainers(container);
3388
		}
3389
3390
		if(shopOwner && requireListUpdate)
3391
			updateInventoryGoods(item->getID());
3392
	}
3393
}
3394
3395
void Player::__internalAddThing(Thing* thing)
3396
{
3397
	__internalAddThing(0, thing);
3398
}
3399
3400
void Player::__internalAddThing(uint32_t index, Thing* thing)
3401
{
3402
#ifdef __DEBUG_MOVESYS__
3403
	std::clog << "[Player::__internalAddThing] index: " << index << std::endl;
3404
3405
#endif
3406
	if(!index || index > 11)
3407
	{
3408
#ifdef __DEBUG_MOVESYS__
3409
		std::clog << "Failure: [Player::__internalAddThing] index == 0 || index > 11" << std::endl;
3410
#endif
3411
		return;
3412
	}
3413
3414
	if(inventory[index])
3415
	{
3416
#ifdef __DEBUG_MOVESYS__
3417
		std::clog << "Warning: [Player::__internalAddThing], player: " << getName() << ", items[index] is not empty." << std::endl;
3418
#endif
3419
		return;
3420
	}
3421
3422
	Item* item = thing->getItem();
3423
	if(!item)
3424
	{
3425
#ifdef __DEBUG_MOVESYS__
3426
		std::clog << "Failure: [Player::__internalAddThing] item == NULL" << std::endl;
3427
#endif
3428
		return;
3429
	}
3430
3431
	inventory[index] = item;
3432
	item->setParent(this);
3433
}
3434
3435
bool Player::setFollowCreature(Creature* creature, bool fullPathSearch /*= false*/)
3436
{
3437
	bool deny = false;
3438
	CreatureEventList followEvents = getCreatureEvents(CREATURE_EVENT_FOLLOW);
3439
	for(CreatureEventList::iterator it = followEvents.begin(); it != followEvents.end(); ++it)
3440
	{
3441
		if(creature && !(*it)->executeFollow(this, creature))
3442
			deny = true;
3443
	}
3444
3445
	if(!deny && Creature::setFollowCreature(creature, fullPathSearch))
3446
		return true;
3447
3448
	setFollowCreature(NULL);
3449
	setAttackedCreature(NULL);
3450
	if(!deny)
3451
		sendCancelMessage(RET_THEREISNOWAY);
3452
3453
	sendCancelTarget();
3454
	cancelNextWalk = true;
3455
	return false;
3456
}
3457
3458
bool Player::setAttackedCreature(Creature* creature)
3459
{
3460
	if(!Creature::setAttackedCreature(creature))
3461
	{
3462
		sendCancelTarget();
3463
		return false;
3464
	}
3465
3466
	if(chaseMode == CHASEMODE_FOLLOW && creature)
3467
	{
3468
		if(followCreature != creature) //chase opponent
3469
			setFollowCreature(creature);
3470
	}
3471
	else
3472
		setFollowCreature(NULL);
3473
3474
	if(creature)
3475
		Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::checkCreatureAttack, &g_game, getID())));
3476
3477
	return true;
3478
}
3479
3480
void Player::goToFollowCreature()
3481
{
3482
	if(!walkTask)
3483
		Creature::goToFollowCreature();
3484
}
3485
3486
void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
3487
{
3488
	Creature::getPathSearchParams(creature, fpp);
3489
	fpp.fullPathSearch = true;
3490
}
3491
3492
void Player::doAttacking(uint32_t)
3493
{
3494
	if(!lastAttack)
3495
		lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
3496
	else if((OTSYS_TIME() - lastAttack) < getAttackSpeed())
3497
		return;
3498
3499
	if(hasCondition(CONDITION_PACIFIED) && !hasCustomFlag(PlayerCustomFlag_IgnorePacification))
3500
	{
3501
		lastAttack = OTSYS_TIME();
3502
		return;
3503
	}
3504
3505
	Item* item = getWeapon(false);
3506
	if(const Weapon* _weapon = g_weapons->getWeapon(item))
3507
	{
3508
		if(_weapon->interruptSwing() && !canDoAction())
3509
		{
3510
			SchedulerTask* task = createSchedulerTask(getNextActionTime(),
3511
				boost::bind(&Game::checkCreatureAttack, &g_game, getID()));
3512
			setNextActionTask(task);
3513
		}
3514
		else
3515
		{
3516
			if((!_weapon->hasExhaustion() || !hasCondition(CONDITION_EXHAUST, EXHAUST_COMBAT)) && _weapon->useWeapon(this, item, attackedCreature))
3517
				lastAttack = OTSYS_TIME();
3518
3519
			updateWeapon();
3520
		}
3521
	}
3522
	else if(Weapon::useFist(this, attackedCreature))
3523
		lastAttack = OTSYS_TIME();
3524
}
3525
3526
double Player::getGainedExperience(Creature* attacker) const
3527
{
3528
	if(!skillLoss)
3529
		return 0;
3530
3531
	double rate = g_config.getDouble(ConfigManager::RATE_PVP_EXPERIENCE);
3532
	if(rate <= 0)
3533
		return 0;
3534
3535
	Player* attackerPlayer = attacker->getPlayer();
3536
	if(!attackerPlayer || attackerPlayer == this)
3537
		return 0;
3538
3539
	double attackerLevel = (double)attackerPlayer->getLevel(), min = g_config.getDouble(
3540
		ConfigManager::EFP_MIN_THRESHOLD), max = g_config.getDouble(ConfigManager::EFP_MAX_THRESHOLD);
3541
	if((min > 0 && level < (uint32_t)std::floor(attackerLevel * min)) || (max > 0 &&
3542
		level > (uint32_t)std::floor(attackerLevel * max)))
3543
		return 0;
3544
3545
	/*
3546
		Formula
3547
		a = attackers level * 0.9
3548
		b = victims level
3549
		c = victims experience
3550
3551
		result = (1 - (a / b)) * 0.05 * c
3552
		Not affected by special multipliers(!)
3553
	*/
3554
	uint32_t a = (uint32_t)std::floor(attackerLevel * 0.9), b = level;
3555
	uint64_t c = getExperience();
3556
	return (double)std::max((uint64_t)0, (uint64_t)std::floor(getDamageRatio(attacker)
3557
		* std::max((double)0, ((double)(1 - (((double)a / b))))) * 0.05 * c)) * rate;
3558
}
3559
3560
void Player::onFollowCreature(const Creature* creature)
3561
{
3562
	if(!creature)
3563
		cancelNextWalk = true;
3564
}
3565
3566
void Player::setChaseMode(chaseMode_t mode)
3567
{
3568
	chaseMode_t prevChaseMode = chaseMode;
3569
	chaseMode = mode;
3570
3571
	if(prevChaseMode == chaseMode)
3572
		return;
3573
3574
	if(chaseMode == CHASEMODE_FOLLOW)
3575
	{
3576
		if(!followCreature && attackedCreature) //chase opponent
3577
			setFollowCreature(attackedCreature);
3578
	}
3579
	else if(attackedCreature)
3580
	{
3581
		setFollowCreature(NULL);
3582
		cancelNextWalk = true;
3583
	}
3584
}
3585
3586
void Player::onWalkAborted()
3587
{
3588
	setNextWalkActionTask(NULL);
3589
	sendCancelWalk();
3590
}
3591
3592
void Player::onWalkComplete()
3593
{
3594
	if(!walkTask)
3595
		return;
3596
3597
	walkTaskEvent = Scheduler::getInstance().addEvent(walkTask);
3598
	walkTask = NULL;
3599
}
3600
3601
void Player::getCreatureLight(LightInfo& light) const
3602
{
3603
	if(internalLight.level > itemsLight.level)
3604
		light = internalLight;
3605
	else
3606
		light = itemsLight;
3607
}
3608
3609
void Player::updateItemsLight(bool internal/* = false*/)
3610
{
3611
	LightInfo maxLight, curLight;
3612
	Item* item = NULL;
3613
	for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3614
	{
3615
		if(!(item = getInventoryItem((slots_t)i)))
3616
			continue;
3617
3618
		item->getLight(curLight);
3619
		if(curLight.level > maxLight.level)
3620
			maxLight = curLight;
3621
	}
3622
3623
	if(maxLight.level > itemsLight.level || (maxLight.level == itemsLight.level && maxLight.color != itemsLight.color))
3624
	{
3625
		itemsLight = maxLight;
3626
		if(!internal)
3627
			g_game.changeLight(this);
3628
	}
3629
}
3630
3631
void Player::onAddCondition(ConditionType_t type, bool hadCondition)
3632
{
3633
	Creature::onAddCondition(type, hadCondition);
3634
	if(type == CONDITION_GAMEMASTER)
3635
		return;
3636
3637
	if(getLastPosition().x)
3638
		sendIcons();
3639
}
3640
3641
void Player::onAddCombatCondition(ConditionType_t type, bool)
3642
{
3643
	std::string tmp;
3644
	switch(type)
3645
	{
3646
		//client hardcoded
3647
		case CONDITION_FIRE:
3648
			tmp = "burning";
3649
			break;
3650
		case CONDITION_POISON:
3651
			tmp = "poisoned";
3652
			break;
3653
		case CONDITION_ENERGY:
3654
			tmp = "electrified";
3655
			break;
3656
		case CONDITION_FREEZING:
3657
			tmp = "freezing";
3658
			break;
3659
		case CONDITION_DAZZLED:
3660
			tmp = "dazzled";
3661
			break;
3662
		case CONDITION_CURSED:
3663
			tmp = "cursed";
3664
			break;
3665
		case CONDITION_DROWN:
3666
			tmp = "drowning";
3667
			break;
3668
		case CONDITION_DRUNK:
3669
			tmp = "drunk";
3670
			break;
3671
		case CONDITION_PARALYZE:
3672
			tmp = "paralyzed";
3673
			break;
3674
		/*case CONDITION_MANASHIELD:
3675
			tmp = "protected by a magic shield";
3676
			break;
3677
		case CONDITION_HASTE:
3678
			tmp = "hasted";
3679
			break;
3680
		case CONDITION_ATTRIBUTES:
3681
			tmp = "strengthened";
3682
			break;*/
3683
		default:
3684
			break;
3685
	}
3686
3687
	if(!tmp.empty())
3688
		sendTextMessage(MSG_STATUS_DEFAULT, "You are " + tmp + ".");
3689
}
3690
3691
void Player::onEndCondition(ConditionType_t type)
3692
{
3693
	Creature::onEndCondition(type);
3694
	if(type == CONDITION_INFIGHT)
3695
	{
3696
		onIdleStatus();
3697
		clearAttacked();
3698
3699
		pzLocked = false;
3700
		if(skull < SKULL_RED)
3701-
			setSkull(SKULL_NONE);
3701+
3702
		g_game.updateCreatureSkull(this);
3703
	}
3704
3705
	sendIcons();
3706
}
3707
3708
void Player::onCombatRemoveCondition(const Creature*, Condition* condition)
3709
{
3710
	//Creature::onCombatRemoveCondition(attacker, condition);
3711
	bool remove = true;
3712
	if(condition->getId() > 0)
3713
	{
3714
		remove = false;
3715
		//Means the condition is from an item, id == slot
3716
		if(g_game.getWorldType() == WORLDTYPE_HARDCORE)
3717
		{
3718
			if(Item* item = getInventoryItem((slots_t)condition->getId()))
3719
			{
3720
				//25% chance to destroy the item
3721
				if(random_range(1, 100) < 26)
3722
					g_game.internalRemoveItem(NULL, item);
3723
			}
3724
		}
3725
	}
3726
3727
	if(remove)
3728
	{
3729
		if(!canDoAction())
3730
		{
3731
			int32_t delay = getNextActionTime();
3732
			delay -= (delay % EVENT_CREATURE_THINK_INTERVAL);
3733
			if(delay < 0)
3734
				removeCondition(condition);
3735
			else
3736
				condition->setTicks(delay);
3737
		}
3738
		else
3739
			removeCondition(condition);
3740
	}
3741
}
3742
3743
void Player::onTickCondition(ConditionType_t type, int32_t interval, bool& _remove)
3744
{
3745
	Creature::onTickCondition(type, interval, _remove);
3746
	if(type == CONDITION_HUNTING)
3747
		useStamina(-(interval * g_config.getNumber(ConfigManager::RATE_STAMINA_LOSS)));
3748
}
3749
3750
void Player::onAttackedCreature(Creature* target)
3751
{
3752
	Creature::onAttackedCreature(target);
3753
	if(hasFlag(PlayerFlag_NotGainInFight))
3754
		return;
3755
3756
	addInFightTicks(false);
3757
	Player* targetPlayer = target->getPlayer();
3758
	if(!targetPlayer)
3759
		return;
3760
3761
	addAttacked(targetPlayer);
3762
	if(Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer) ||
3763
		isAlly(targetPlayer) ||
3764
		(g_config.getBool(ConfigManager::ALLOW_FIGHTBACK) && targetPlayer->hasAttacked(this)
3765
			&& !targetPlayer->isEnemy(this, false)))
3766
		return;
3767
3768
	if(!pzLocked)
3769
	{
3770
		pzLocked = true;
3771
		sendIcons();
3772
	}
3773
3774
	if(getZone() != target->getZone() || skull != SKULL_NONE
3775
		|| targetPlayer->isEnemy(this, true))
3776
		return;
3777
}
3778
3779
void Player::onSummonAttackedCreature(Creature* summon, Creature* target)
3780
{
3781
	Creature::onSummonAttackedCreature(summon, target);
3782
	onAttackedCreature(target);
3783
}
3784
3785
void Player::onAttacked()
3786
{
3787
	Creature::onAttacked();
3788
	addInFightTicks(false);
3789
}
3790
3791
bool Player::checkLoginDelay(uint32_t playerId) const
3792
{
3793
	return (!hasCustomFlag(PlayerCustomFlag_IgnoreLoginDelay) && OTSYS_TIME() <= (lastLoad + g_config.getNumber(
3794
		ConfigManager::LOGIN_PROTECTION)) && !hasBeenAttacked(playerId));
3795
}
3796
3797
void Player::onIdleStatus()
3798
{
3799
	Creature::onIdleStatus();
3800
	if(getParty())
3801
		getParty()->clearPlayerPoints(this);
3802
}
3803
3804
void Player::onPlacedCreature()
3805
{
3806
	//scripting event - onLogin
3807
	if(!g_creatureEvents->playerLogin(this))
3808
		kick(true, true);
3809
}
3810
3811
void Player::onAttackedCreatureDrain(Creature* target, int32_t points)
3812
{
3813
	Creature::onAttackedCreatureDrain(target, points);
3814
	if(party && target && (!target->getMaster() || !target->getMaster()->getPlayer())
3815
		&& target->getMonster() && target->getMonster()->isHostile()) //we have fulfilled a requirement for shared experience
3816
		getParty()->addPlayerDamageMonster(this, points);
3817
3818
	char buffer[100];
3819
	sprintf(buffer, "You deal %d damage to %s.", points, target->getNameDescription().c_str());
3820
	sendTextMessage(MSG_STATUS_DEFAULT, buffer);
3821
}
3822
3823
void Player::onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points)
3824
{
3825
	Creature::onSummonAttackedCreatureDrain(summon, target, points);
3826
3827
	char buffer[100];
3828
	sprintf(buffer, "Your %s deals %d damage to %s.", summon->getName().c_str(), points, target->getNameDescription().c_str());
3829
	sendTextMessage(MSG_EVENT_DEFAULT, buffer);
3830
}
3831
3832
void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
3833
{
3834
	Creature::onTargetCreatureGainHealth(target, points);
3835
	if(target && getParty())
3836
	{
3837
		Player* tmpPlayer = NULL;
3838
		if(target->getPlayer())
3839
			tmpPlayer = target->getPlayer();
3840
		else if(target->getMaster() && target->getMaster()->getPlayer())
3841
			tmpPlayer = target->getMaster()->getPlayer();
3842
3843
		if(isPartner(tmpPlayer))
3844
			party->addPlayerHealedMember(this, points);
3845
	}
3846
}
3847
3848
GuildEmblems_t Player::getGuildEmblem(const Creature* creature) const
3849
{
3850
	const Player* player = creature->getPlayer();
3851
	if(!player || !player->hasEnemy())
3852
		return Creature::getGuildEmblem(creature);
3853
3854
	if(player->isEnemy(this, false))
3855
		return EMBLEM_RED;
3856
3857
	return player->getGuildId() == guildId ? EMBLEM_GREEN : EMBLEM_BLUE;
3858
}
3859
3860
bool Player::getEnemy(const Player* player, War_t& data) const
3861
{
3862
	if(!guildId || !player || player->isRemoved())
3863
		return false;
3864
3865
	uint32_t guild = player->getGuildId();
3866
	if(!guild)
3867
		return false;
3868
3869
	WarMap::const_iterator it = warMap.find(guild);
3870
	if(it == warMap.end())
3871
		return false;
3872
3873
	data = it->second;
3874
	return true;
3875
}
3876
3877
bool Player::isEnemy(const Player* player, bool allies) const
3878
{
3879
	if(!guildId || !player || player->isRemoved())
3880
		return false;
3881
3882
	uint32_t guild = player->getGuildId();
3883
	if(!guild)
3884
		return false;
3885
3886
	return !warMap.empty() && (((g_game.getWorldType() != WORLDTYPE_OPTIONAL || g_config.getBool(
3887
		ConfigManager::OPTIONAL_WAR_ATTACK_ALLY)) && allies && guildId == guild) ||
3888
		warMap.find(guild) != warMap.end());
3889
}
3890
3891
bool Player::isAlly(const Player* player) const
3892
{
3893
	return !warMap.empty() && player && player->getGuildId() == guildId;
3894
}
3895
3896
bool Player::onKilledCreature(Creature* target, DeathEntry& entry)
3897
{
3898
	if(!Creature::onKilledCreature(target, entry))
3899
		return false;
3900
3901
	if(hasFlag(PlayerFlag_NotGenerateLoot))
3902
		target->setDropLoot(LOOT_DROP_NONE);
3903
3904
	Condition* condition = NULL;
3905
	if(target->getMonster() && !target->isPlayerSummon() && !hasFlag(PlayerFlag_HasInfiniteStamina)
3906
		&& (condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_HUNTING,
3907
		g_config.getNumber(ConfigManager::HUNTING_DURATION))))
3908
		addCondition(condition);
3909
3910
	if(hasFlag(PlayerFlag_NotGainInFight) || getZone() != target->getZone())
3911
		return true;
3912
3913
	Player* targetPlayer = target->getPlayer();
3914
	if(!targetPlayer || Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer)
3915
		|| isAlly(targetPlayer))
3916
		return true;
3917
3918
	War_t enemy;
3919
	if(targetPlayer->getEnemy(this, enemy) && (!entry.isLast() || IOGuild::getInstance()->updateWar(enemy)))
3920
		entry.setWar(enemy);
3921
3922
	if(!entry.isJustify() || !hasCondition(CONDITION_INFIGHT))
3923
		return true;
3924
3925
	if(!targetPlayer->hasAttacked(this) && target->getSkull() == SKULL_NONE && targetPlayer != this
3926
		&& (addUnjustifiedKill(targetPlayer, !enemy.war) || entry.isLast()))
3927
		entry.setUnjustified();
3928
3929
	addInFightTicks(true, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME));
3930
	return true;
3931
}
3932
3933
bool Player::gainExperience(double& gainExp, Creature* target)
3934
{
3935
	if(!rateExperience(gainExp, target))
3936
		return false;
3937
3938
	//soul regeneration
3939
	if(gainExp >= level)
3940
	{
3941
		if(Condition* condition = Condition::createCondition(
3942
			CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000))
3943
		{
3944
			condition->setParam(CONDITIONPARAM_SOULGAIN,
3945
				vocation->getGainAmount(GAIN_SOUL));
3946
			condition->setParam(CONDITIONPARAM_SOULTICKS,
3947
				(vocation->getGainTicks(GAIN_SOUL) * 1000));
3948
			addCondition(condition);
3949
		}
3950
	}
3951
3952
	addExperience((uint64_t)gainExp);
3953
	return true;
3954
}
3955
3956
bool Player::rateExperience(double& gainExp, Creature* target)
3957
{
3958
	if(hasFlag(PlayerFlag_NotGainExperience) || gainExp <= 0)
3959
		return false;
3960
3961
	if(target->getPlayer())
3962
		return true;
3963
3964
	gainExp *= rates[SKILL__LEVEL] * g_game.getExperienceStage(level,
3965
		vocation->getExperienceMultiplier());
3966
	if(!hasFlag(PlayerFlag_HasInfiniteStamina))
3967
	{
3968
		int32_t minutes = getStaminaMinutes();
3969
		if(minutes >= g_config.getNumber(ConfigManager::STAMINA_LIMIT_TOP))
3970
		{
3971
			if(isPremium() || !g_config.getBool(ConfigManager::STAMINA_BONUS_PREMIUM))
3972
				gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3973
		}
3974
		else if(minutes < (g_config.getNumber(ConfigManager::STAMINA_LIMIT_BOTTOM)) && minutes > 0)
3975
			gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_UNDER);
3976
		else if(minutes <= 0)
3977
			gainExp = 0;
3978
	}
3979
	else if(isPremium() || !g_config.getBool(ConfigManager::STAMINA_BONUS_PREMIUM))
3980
		gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3981
3982
	return true;
3983
}
3984
3985
void Player::onGainExperience(double& gainExp, Creature* target, bool multiplied)
3986
{
3987
	if(party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive())
3988
	{
3989
		party->shareExperience(gainExp, target, multiplied);
3990
		rateExperience(gainExp, target);
3991
		return; //we will get a share of the experience through the sharing mechanism
3992
	}
3993
3994
	if(gainExperience(gainExp, target))
3995
		Creature::onGainExperience(gainExp, target, true);
3996
}
3997
3998
void Player::onGainSharedExperience(double& gainExp, Creature* target, bool)
3999
{
4000
	if(gainExperience(gainExp, target))
4001
		Creature::onGainSharedExperience(gainExp, target, true);
4002
}
4003
4004
bool Player::isImmune(CombatType_t type) const
4005
{
4006
	return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
4007
}
4008
4009
bool Player::isImmune(ConditionType_t type) const
4010
{
4011
	return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
4012
}
4013
4014
bool Player::isAttackable() const
4015
{
4016
	return (!hasFlag(PlayerFlag_CannotBeAttacked) && !isAccountManager());
4017
}
4018
4019
void Player::changeHealth(int32_t healthChange)
4020
{
4021
	Creature::changeHealth(healthChange);
4022
	sendStats();
4023
}
4024
4025
void Player::changeMana(int32_t manaChange)
4026
{
4027
	if(!hasFlag(PlayerFlag_HasInfiniteMana))
4028
		Creature::changeMana(manaChange);
4029
4030
	sendStats();
4031
}
4032
4033
void Player::changeSoul(int32_t soulChange)
4034
{
4035
	if(!hasFlag(PlayerFlag_HasInfiniteSoul))
4036
		soul = std::min((int32_t)soulMax, (int32_t)soul + soulChange);
4037
4038
	sendStats();
4039
}
4040
4041
bool Player::changeOutfit(Outfit_t outfit, bool checkList)
4042
{
4043
	uint32_t outfitId = Outfits::getInstance()->getOutfitId(outfit.lookType);
4044
	if(checkList && (!canWearOutfit(outfitId, outfit.lookAddons) || !requestedOutfit))
4045
		return false;
4046
4047
	requestedOutfit = false;
4048
	if(outfitAttributes)
4049
	{
4050
		uint32_t oldId = Outfits::getInstance()->getOutfitId(defaultOutfit.lookType);
4051
		outfitAttributes = !Outfits::getInstance()->removeAttributes(getID(), oldId, sex);
4052
	}
4053
4054
	defaultOutfit = outfit;
4055
	outfitAttributes = Outfits::getInstance()->addAttributes(getID(), outfitId, sex, defaultOutfit.lookAddons);
4056
	return true;
4057
}
4058
4059
bool Player::canWearOutfit(uint32_t outfitId, uint32_t addons)
4060
{
4061
	OutfitMap::iterator it = outfits.find(outfitId);
4062
	if(it == outfits.end() || (it->second.isPremium && !isPremium()) || getAccess() < it->second.accessLevel
4063
		|| ((it->second.addons & addons) != addons && !hasCustomFlag(PlayerCustomFlag_CanWearAllAddons)))
4064
		return false;
4065
4066
	if(it->second.storageId.empty())
4067
		return true;
4068
4069
	std::string value;
4070
	getStorage(it->second.storageId, value);
4071
4072
	bool ret = value == it->second.storageValue;
4073
	if(ret)
4074
		return ret;
4075
4076
	int32_t tmp = atoi(value.c_str());
4077
	if(!tmp && value != "0")
4078
		return ret;
4079
4080
	tmp = atoi(it->second.storageValue.c_str());
4081
	if(!tmp && it->second.storageValue != "0")
4082
		return ret;
4083
4084
	return atoi(value.c_str()) >= tmp;
4085
}
4086
4087
bool Player::addOutfit(uint32_t outfitId, uint32_t addons)
4088
{
4089
	Outfit outfit;
4090
	if(!Outfits::getInstance()->getOutfit(outfitId, sex, outfit))
4091
		return false;
4092
4093
	OutfitMap::iterator it = outfits.find(outfitId);
4094
	if(it != outfits.end())
4095
		outfit.addons |= it->second.addons;
4096
4097
	outfit.addons |= addons;
4098
	outfits[outfitId] = outfit;
4099
	return true;
4100
}
4101
4102
bool Player::removeOutfit(uint32_t outfitId, uint32_t addons)
4103
{
4104
	OutfitMap::iterator it = outfits.find(outfitId);
4105
	if(it == outfits.end())
4106
		return false;
4107
4108
	if(addons == 0xFF) //remove outfit
4109
		outfits.erase(it);
4110
	else //remove addons
4111
		outfits[outfitId].addons = it->second.addons & (~addons);
4112
4113
	return true;
4114
}
4115
4116
void Player::generateReservedStorage()
4117
{
4118
	uint32_t key = PSTRG_OUTFITSID_RANGE_START + 1;
4119
	const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4120
	for(OutfitMap::const_iterator it = outfits.begin(); it != outfits.end(); ++it)
4121
	{
4122
		OutfitMap::const_iterator dit = defaultOutfits.find(it->first);
4123
		if(dit == defaultOutfits.end() || (dit->second.isDefault && (dit->second.addons
4124
			& it->second.addons) == it->second.addons))
4125
			continue;
4126
4127
		std::stringstream k, v;
4128
		k << key++; // this may not work as intended, revalidate it
4129
		v << ((it->first << 16) | (it->second.addons & 0xFF));
4130
4131
		storageMap[k.str()] = v.str();
4132
		if(key <= PSTRG_OUTFITSID_RANGE_START + PSTRG_OUTFITSID_RANGE_SIZE)
4133
			continue;
4134
4135
		std::clog << "[Warning - Player::genReservedStorageRange] Player " << getName() << " with more than 500 outfits!" << std::endl;
4136
		break;
4137
	}
4138
}
4139
4140
void Player::setSex(uint16_t newSex)
4141
{
4142
	sex = newSex;
4143
	const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4144
	for(OutfitMap::const_iterator it = defaultOutfits.begin(); it != defaultOutfits.end(); ++it)
4145
	{
4146
		if(it->second.isDefault)
4147
			addOutfit(it->first, it->second.addons);
4148
	}
4149
}
4150
4151
Skulls_t Player::getSkull() const
4152
{
4153
	if(hasFlag(PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4154
		return SKULL_NONE;
4155
4156
	return skull;
4157
}
4158
4159
Skulls_t Player::getSkullType(const Creature* creature) const
4160
{
4161
	const Player* player = creature->getPlayer();
4162
	if(player && player->getSkull() == SKULL_NONE)
4163
	{
4164
	}
4165
4166
	return Creature::getSkullType(creature);
4167
}
4168
4169
bool Player::hasAttacked(const Player* attacked) const
4170
{
4171
	return !hasFlag(PlayerFlag_NotGainInFight) && attacked &&
4172
		attackedSet.find(attacked->getID()) != attackedSet.end();
4173
}
4174
4175
void Player::addAttacked(const Player* attacked)
4176
{
4177
	if(hasFlag(PlayerFlag_NotGainInFight) || !attacked)
4178
		return;
4179
4180
	uint32_t attackedId = attacked->getID();
4181
	if(attackedSet.find(attackedId) == attackedSet.end())
4182
		attackedSet.insert(attackedId);
4183
}
4184
4185
void Player::setSkullEnd(time_t _time, bool login, Skulls_t _skull)
4186
{
4187
	if(g_game.getWorldType() != WORLDTYPE_OPEN || hasFlag(PlayerFlag_NotGainInFight) ||
4188
			hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4189
		return;
4190
4191
	bool requireUpdate = false;
4192
	if(_time > time(NULL))
4193
	{
4194
		requireUpdate = true;
4195
		setSkull(_skull);
4196
	}
4197
	else if(skull == _skull)
4198
	{
4199
		requireUpdate = true;
4200
		_time = 0;
4201-
		setSkull(SKULL_NONE);
4201+
4202
4203
	if(requireUpdate)
4204
	{
4205
		skullEnd = _time;
4206
		if(!login)
4207
			g_game.updateCreatureSkull(this);
4208
	}
4209
}
4210
4211
bool Player::addUnjustifiedKill(const Player* attacked, bool countNow)
4212
{
4213
	if(!g_config.getBool(ConfigManager::USE_FRAG_HANDLER) || hasFlag(
4214
		PlayerFlag_NotGainInFight) || g_game.getWorldType() != WORLDTYPE_OPEN
4215
		|| hasCustomFlag(PlayerCustomFlag_NotGainUnjustified) || hasCustomFlag(
4216
		PlayerCustomFlag_NotGainSkull) || attacked == this)
4217
		return false;
4218
4219
	if(countNow)
4220
	{
4221
		char buffer[90];
4222
		sprintf(buffer, "Warning! The murder of %s was not justified.", attacked->getName().c_str());
4223
		sendTextMessage(MSG_STATUS_WARNING, buffer);
4224
	}
4225
4226
	time_t now = time(NULL), today = (now - 84600), week = (now - (7 * 84600));
4227
	std::vector<time_t> dateList;
4228
4229
	IOLoginData::getInstance()->getUnjustifiedDates(guid, dateList, now);
4230
	if(countNow)
4231
		dateList.push_back(now);
4232
4233
	uint32_t tc = 0, wc = 0, mc = dateList.size();
4234
	for(std::vector<time_t>::iterator it = dateList.begin(); it != dateList.end(); ++it)
4235
	{
4236
		if((*it) > week)
4237
			wc++;
4238
4239
		if((*it) > today)
4240
			tc++;
4241
	}
4242
4243
	uint32_t d = g_config.getNumber(ConfigManager::RED_DAILY_LIMIT), w = g_config.getNumber(
4244
		ConfigManager::RED_WEEKLY_LIMIT), m = g_config.getNumber(ConfigManager::RED_MONTHLY_LIMIT);
4245
	if(skull < SKULL_RED && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4246
		setSkullEnd(now + g_config.getNumber(ConfigManager::RED_SKULL_LENGTH), false, SKULL_RED);
4247
4248
	if(!g_config.getBool(ConfigManager::USE_BLACK_SKULL))
4249
	{
4250
		d += g_config.getNumber(ConfigManager::BAN_DAILY_LIMIT);
4251
		w += g_config.getNumber(ConfigManager::BAN_WEEKLY_LIMIT);
4252
		m += g_config.getNumber(ConfigManager::BAN_MONTHLY_LIMIT);
4253
		if((d <= 0 || tc < d) && (w <= 0 || wc < w) && (m <= 0 || mc < m))
4254
			return true;
4255
4256
		if(!IOBan::getInstance()->addAccountBanishment(accountId, (now + g_config.getNumber(
4257
			ConfigManager::KILLS_BAN_LENGTH)), 20, ACTION_BANISHMENT, "Unjustified player killing.", 0, guid))
4258
			return true;
4259
4260
		sendTextMessage(MSG_INFO_DESCR, "You have been banished.");
4261
		g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_GREEN);
4262
		Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind(
4263
			&Game::kickPlayer, &g_game, getID(), false)));
4264
	}
4265
	else
4266
	{
4267
		d += g_config.getNumber(ConfigManager::BLACK_DAILY_LIMIT);
4268
		w += g_config.getNumber(ConfigManager::BLACK_WEEKLY_LIMIT);
4269
		m += g_config.getNumber(ConfigManager::BLACK_MONTHLY_LIMIT);
4270
		if(skull < SKULL_BLACK && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4271
		{
4272
			setSkullEnd(now + g_config.getNumber(ConfigManager::BLACK_SKULL_LENGTH), false, SKULL_BLACK);
4273
			setAttackedCreature(NULL);
4274
			destroySummons();
4275
		}
4276
	}
4277
4278
	return true;
4279
}
4280
4281
void Player::setPromotionLevel(uint32_t pLevel)
4282
{
4283
	if(pLevel > promotionLevel)
4284
	{
4285
		uint32_t tmpLevel = 0, currentVoc = vocationId;
4286
		for(uint32_t i = promotionLevel; i < pLevel; ++i)
4287
		{
4288
			currentVoc = Vocations::getInstance()->getPromotedVocation(currentVoc);
4289
			if(!currentVoc)
4290
				break;
4291
4292
			tmpLevel++;
4293
			Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4294
			if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4295
				continue;
4296
4297
			vocationId = currentVoc;
4298
		}
4299
4300
		promotionLevel += tmpLevel;
4301
	}
4302
	else if(pLevel < promotionLevel)
4303
	{
4304
		uint32_t tmpLevel = 0, currentVoc = vocationId;
4305
		for(uint32_t i = pLevel; i < promotionLevel; ++i)
4306
		{
4307
			Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4308
			if(voc->getFromVocation() == currentVoc)
4309
				break;
4310
4311
			tmpLevel++;
4312
			currentVoc = voc->getFromVocation();
4313
			if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4314
				continue;
4315
4316
			vocationId = currentVoc;
4317
		}
4318
4319
		promotionLevel -= tmpLevel;
4320
	}
4321
4322
	setVocation(vocationId);
4323
}
4324
4325
uint16_t Player::getBlessings() const
4326
{
4327
	if(!g_config.getBool(ConfigManager::BLESSINGS) || (!isPremium() &&
4328
		g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM)))
4329
		return 0;
4330
4331
	uint16_t count = 0;
4332
	for(int16_t i = 0; i < 16; ++i)
4333
	{
4334
		if(hasBlessing(i))
4335
			count++;
4336
	}
4337
4338
	return count;
4339
}
4340
4341
uint64_t Player::getLostExperience() const
4342
{
4343
	if(!skillLoss)
4344
		return 0;
4345
4346
	double percent = (double)(lossPercent[LOSS_EXPERIENCE] - vocation->getLessLoss() - (getBlessings() * g_config.getNumber(
4347
		ConfigManager::BLESS_REDUCTION))) / 100.;
4348
	if(level <= 25)
4349
		return (uint64_t)std::floor((double)(experience * percent) / 10.);
4350
4351
	int32_t base = level;
4352
	double levels = (double)(base + 50) / 100.;
4353
4354
	uint64_t lost = 0;
4355
	while(levels > 1.0f)
4356
	{
4357
		lost += (getExpForLevel(base) - getExpForLevel(base - 1));
4358
		base--;
4359
		levels -= 1.;
4360
	}
4361
4362
	if(levels > 0.)
4363
		lost += (uint64_t)std::floor((double)(getExpForLevel(base) - getExpForLevel(base - 1)) * levels);
4364
4365
	return (uint64_t)std::floor((double)(lost * percent));
4366
}
4367
4368
uint32_t Player::getAttackSpeed() const
4369
{
4370
	return ((weapon && weapon->getAttackSpeed() != 0) ? weapon->getAttackSpeed() : (vocation->getAttackSpeed() / std::max((size_t)1, getWeapons().size())));
4371
}
4372
4373
void Player::learnInstantSpell(const std::string& name)
4374
{
4375
	if(!hasLearnedInstantSpell(name))
4376
		learnedInstantSpellList.push_back(name);
4377
}
4378
4379
void Player::unlearnInstantSpell(const std::string& name)
4380
{
4381
	if(!hasLearnedInstantSpell(name))
4382
		return;
4383
4384
	LearnedInstantSpellList::iterator it = std::find(learnedInstantSpellList.begin(), learnedInstantSpellList.end(), name);
4385
	if(it != learnedInstantSpellList.end())
4386
		learnedInstantSpellList.erase(it);
4387
}
4388
4389
bool Player::hasLearnedInstantSpell(const std::string& name) const
4390
{
4391
	if(hasFlag(PlayerFlag_CannotUseSpells))
4392
		return false;
4393
4394
	if(hasFlag(PlayerFlag_IgnoreSpellCheck))
4395
		return true;
4396
4397
	for(LearnedInstantSpellList::const_iterator it = learnedInstantSpellList.begin(); it != learnedInstantSpellList.end(); ++it)
4398
	{
4399
		if(boost::algorithm::iequals(*it, name))
4400
			return true;
4401
	}
4402
4403
	return false;
4404
}
4405
4406
void Player::manageAccount(const std::string &text)
4407
{
4408
	std::stringstream msg;
4409
	msg << "Account Manager: ";
4410
4411
	bool noSwap = true;
4412
	switch(accountManager)
4413
	{
4414
		case MANAGER_NAMELOCK:
4415
		{
4416
			if(!talkState[1])
4417
			{
4418
				managerString = text;
4419
				trimString(managerString);
4420
				if(managerString.length() < 4)
4421
					msg << "Your name you want is too short, please select a longer name.";
4422
				else if(managerString.length() > 20)
4423
					msg << "The name you want is too long, please select a shorter name.";
4424
				else if(!isValidName(managerString))
4425
					msg << "That name seems to contain invalid symbols, please choose another name.";
4426
				else if(IOLoginData::getInstance()->playerExists(managerString, true))
4427
					msg << "A player with that name already exists, please choose another name.";
4428
				else
4429
				{
4430
					std::string tmp = asLowerCaseString(managerString);
4431
					if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4432
					{
4433
						talkState[1] = true;
4434
						talkState[2] = true;
4435
						msg << managerString << ", are you sure?";
4436
					}
4437
					else
4438
						msg << "Your character is not a staff member, please tell me another name!";
4439
				}
4440
			}
4441
			else if(checkText(text, "no") && talkState[2])
4442
			{
4443
				talkState[1] = talkState[2] = false;
4444
				msg << "What else would you like to name your character?";
4445
			}
4446
			else if(checkText(text, "yes") && talkState[2])
4447
			{
4448
				if(!IOLoginData::getInstance()->playerExists(managerString, true))
4449
				{
4450
					uint32_t tmp;
4451
					if(IOLoginData::getInstance()->getGuidByName(tmp, managerString2) &&
4452
						IOLoginData::getInstance()->changeName(tmp, managerString, managerString2) &&
4453
						IOBan::getInstance()->removePlayerBanishment(tmp, PLAYERBAN_LOCK))
4454
					{
4455
						if(House* house = Houses::getInstance()->getHouseByPlayerId(tmp))
4456
							house->updateDoorDescription(managerString);
4457
4458
						talkState[1] = true;
4459
						talkState[2] = false;
4460
						msg << "Your character has been successfully renamed, you should now be able to login at it without any problems.";
4461
					}
4462
					else
4463
					{
4464
						talkState[1] = talkState[2] = false;
4465
						msg << "Failed to change your name, please try again.";
4466
					}
4467
				}
4468
				else
4469
				{
4470
					talkState[1] = talkState[2] = false;
4471
					msg << "A player with that name already exists, please choose another name.";
4472
				}
4473
			}
4474
			else
4475
				msg << "Sorry, but I can't understand you, please try to repeat that!";
4476
4477
			break;
4478
		}
4479
		case MANAGER_ACCOUNT:
4480
		{
4481
			Account account = IOLoginData::getInstance()->loadAccount(managerNumber);
4482
			if(checkText(text, "cancel") || (checkText(text, "account") && !talkState[1]))
4483
			{
4484
				talkState[1] = true;
4485
				for(int8_t i = 2; i <= 12; i++)
4486
					talkState[i] = false;
4487
4488
				msg << "Do you want to change your 'password', request a 'recovery key', add a 'character', or 'delete' a character?";
4489
			}
4490
			else if(checkText(text, "delete") && talkState[1])
4491
			{
4492
				talkState[1] = false;
4493
				talkState[2] = true;
4494
				msg << "Which character would you like to delete?";
4495
			}
4496
			else if(talkState[2])
4497
			{
4498
				std::string tmp = text;
4499
				trimString(tmp);
4500
				if(!isValidName(tmp, false))
4501
					msg << "That name contains invalid characters, try to say your name again, you might have typed it wrong.";
4502
				else
4503
				{
4504
					talkState[2] = false;
4505
					talkState[3] = true;
4506
					managerString = tmp;
4507
					msg << "Do you really want to delete the character named " << managerString << "?";
4508
				}
4509
			}
4510
			else if(checkText(text, "yes") && talkState[3])
4511
			{
4512
				switch(IOLoginData::getInstance()->deleteCharacter(managerNumber, managerString))
4513
				{
4514
					case DELETE_INTERNAL:
4515
						msg << "An error occured while deleting your character. Either the character does not belong to you or it doesn't exist.";
4516
						break;
4517
4518
					case DELETE_SUCCESS:
4519
						msg << "Your character has been deleted.";
4520
						break;
4521
4522
					case DELETE_HOUSE:
4523
						msg << "Your character owns a house. To make sure you really want to lose your house by deleting your character, you have to login and leave the house or pass it to someone else first.";
4524
						break;
4525
4526
					case DELETE_LEADER:
4527
						msg << "Your character is the leader of a guild. You need to disband or pass the leadership someone else to delete your character.";
4528
						break;
4529
4530
					case DELETE_ONLINE:
4531
						msg << "A character with that name is currently online, to delete a character it has to be offline.";
4532
						break;
4533
				}
4534
4535
				talkState[1] = true;
4536
				for(int8_t i = 2; i <= 12; i++)
4537
					talkState[i] = false;
4538
			}
4539
			else if(checkText(text, "no") && talkState[3])
4540
			{
4541
				talkState[1] = true;
4542
				talkState[3] = false;
4543
				msg << "Tell me what character you want to delete.";
4544
			}
4545
			else if(checkText(text, "password") && talkState[1])
4546
			{
4547
				talkState[1] = false;
4548
				talkState[4] = true;
4549
				msg << "Tell me your new password please.";
4550
			}
4551
			else if(talkState[4])
4552
			{
4553
				std::string tmp = text;
4554
				trimString(tmp);
4555
				if(tmp.length() < 6)
4556
					msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4557
				else if(!isValidPassword(tmp))
4558
					msg << "Your password contains invalid characters... please tell me another one.";
4559
				else
4560
				{
4561
					talkState[4] = false;
4562
					talkState[5] = true;
4563
					managerString = tmp;
4564
					msg << "Should '" << managerString << "' be your new password?";
4565
				}
4566
			}
4567
			else if(checkText(text, "yes") && talkState[5])
4568
			{
4569
				talkState[1] = true;
4570
				for(int8_t i = 2; i <= 12; i++)
4571
					talkState[i] = false;
4572
4573
				IOLoginData::getInstance()->setPassword(managerNumber, managerString);
4574
				msg << "Your password has been changed.";
4575
			}
4576
			else if(checkText(text, "no") && talkState[5])
4577
			{
4578
				talkState[1] = true;
4579
				for(int8_t i = 2; i <= 12; i++)
4580
					talkState[i] = false;
4581
4582
				msg << "Then not.";
4583
			}
4584
			else if(checkText(text, "character") && talkState[1])
4585
			{
4586
				if(account.charList.size() <= 15)
4587
				{
4588
					talkState[1] = false;
4589
					talkState[6] = true;
4590
					msg << "What would you like as your character name?";
4591
				}
4592
				else
4593
				{
4594
					talkState[1] = true;
4595
					for(int8_t i = 2; i <= 12; i++)
4596
						talkState[i] = false;
4597
4598
					msg << "Your account reach the limit of 15 players, you can 'delete' a character if you want to create a new one.";
4599
				}
4600
			}
4601
			else if(talkState[6])
4602
			{
4603
				managerString = text;
4604
				trimString(managerString);
4605
				if(managerString.length() < 4)
4606
					msg << "Your name you want is too short, please select a longer name.";
4607
				else if(managerString.length() > 20)
4608
					msg << "The name you want is too long, please select a shorter name.";
4609
				else if(!isValidName(managerString))
4610
					msg << "That name seems to contain invalid symbols, please choose another name.";
4611
				else if(IOLoginData::getInstance()->playerExists(managerString, true))
4612
					msg << "A player with that name already exists, please choose another name.";
4613
				else
4614
				{
4615
					std::string tmp = asLowerCaseString(managerString);
4616
					if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4617
					{
4618
						talkState[6] = false;
4619
						talkState[7] = true;
4620
						msg << managerString << ", are you sure?";
4621
					}
4622
					else
4623
						msg << "Your character is not a staff member, please tell me another name!";
4624
				}
4625
			}
4626
			else if(checkText(text, "no") && talkState[7])
4627
			{
4628
				talkState[6] = true;
4629
				talkState[7] = false;
4630
				msg << "What else would you like to name your character?";
4631
			}
4632
			else if(checkText(text, "yes") && talkState[7])
4633
			{
4634
				talkState[7] = false;
4635
				talkState[8] = true;
4636
				msg << "Should your character be a 'male' or a 'female'.";
4637
			}
4638
			else if(talkState[8] && (checkText(text, "female") || checkText(text, "male")))
4639
			{
4640
				talkState[8] = false;
4641
				talkState[9] = true;
4642
				if(checkText(text, "female"))
4643
				{
4644
					msg << "A female, are you sure?";
4645
					managerSex = PLAYERSEX_FEMALE;
4646
				}
4647
				else
4648
				{
4649
					msg << "A male, are you sure?";
4650
					managerSex = PLAYERSEX_MALE;
4651
				}
4652
			}
4653
			else if(checkText(text, "no") && talkState[9])
4654
			{
4655
				talkState[8] = true;
4656
				talkState[9] = false;
4657
				msg << "Tell me... would you like to be a 'male' or a 'female'?";
4658
			}
4659
			else if(checkText(text, "yes") && talkState[9])
4660
			{
4661
				if(g_config.getBool(ConfigManager::START_CHOOSEVOC))
4662
				{
4663
					talkState[9] = false;
4664
					talkState[11] = true;
4665
4666
					bool firstPart = true;
4667
					for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4668
					{
4669
						if(it->first == it->second->getFromVocation() && it->first != 0)
4670
						{
4671
							if(firstPart)
4672
							{
4673
								msg << "What do you want to be... " << it->second->getDescription();
4674
								firstPart = false;
4675
							}
4676
							else if(it->first - 1 != 0)
4677
								msg << ", " << it->second->getDescription();
4678
							else
4679
								msg << " or " << it->second->getDescription() << ".";
4680
						}
4681
					}
4682
				}
4683
				else if(!IOLoginData::getInstance()->playerExists(managerString, true))
4684
				{
4685
					talkState[1] = true;
4686
					for(int8_t i = 2; i <= 12; i++)
4687
						talkState[i] = false;
4688
4689
					if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4690
						msg << "Your character has been created.";
4691
					else
4692
						msg << "Your character couldn't be created, please try again.";
4693
				}
4694
				else
4695
				{
4696
					talkState[6] = true;
4697
					talkState[9] = false;
4698
					msg << "A player with that name already exists, please choose another name.";
4699
				}
4700
			}
4701
			else if(talkState[11])
4702
			{
4703
				for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4704
				{
4705
					std::string tmp = asLowerCaseString(it->second->getName());
4706
					if(checkText(text, tmp) && it != Vocations::getInstance()->getLastVocation() && it->first == it->second->getFromVocation() && it->first != 0)
4707
					{
4708
						msg << "So you would like to be " << it->second->getDescription() << "... are you sure?";
4709
						managerNumber2 = it->first;
4710
						talkState[11] = false;
4711
						talkState[12] = true;
4712
					}
4713
				}
4714
4715
				if(msg.str().length() == 17)
4716
					msg << "I don't understand what vocation you would like to be... could you please repeat it?";
4717
			}
4718
			else if(checkText(text, "yes") && talkState[12])
4719
			{
4720
				if(!IOLoginData::getInstance()->playerExists(managerString, true))
4721
				{
4722
					talkState[1] = true;
4723
					for(int8_t i = 2; i <= 12; i++)
4724
						talkState[i] = false;
4725
4726
					if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4727
						msg << "Your character has been created.";
4728
					else
4729
						msg << "Your character couldn't be created, please try again.";
4730
				}
4731
				else
4732
				{
4733
					talkState[6] = true;
4734
					talkState[9] = false;
4735
					msg << "A player with that name already exists, please choose another name.";
4736
				}
4737
			}
4738
			else if(checkText(text, "no") && talkState[12])
4739
			{
4740
				talkState[11] = true;
4741
				talkState[12] = false;
4742
				msg << "No? Then what would you like to be?";
4743
			}
4744
			else if(checkText(text, "recovery key") && talkState[1])
4745
			{
4746
				talkState[1] = false;
4747
				talkState[10] = true;
4748
				msg << "Would you like a recovery key?";
4749
			}
4750
			else if(checkText(text, "yes") && talkState[10])
4751
			{
4752
				if(account.recoveryKey != "0")
4753
					msg << "Sorry, you already have a recovery key, for security reasons I may not give you a new one.";
4754
				else
4755
				{
4756
					managerString = generateRecoveryKey(4, 4);
4757
					IOLoginData::getInstance()->setRecoveryKey(managerNumber, managerString);
4758
					msg << "Your recovery key is: " << managerString << ".";
4759
				}
4760
4761
				talkState[1] = true;
4762
				for(int8_t i = 2; i <= 12; i++)
4763
					talkState[i] = false;
4764
			}
4765
			else if(checkText(text, "no") && talkState[10])
4766
			{
4767
				msg << "Then not.";
4768
				talkState[1] = true;
4769
				for(int8_t i = 2; i <= 12; i++)
4770
					talkState[i] = false;
4771
			}
4772
			else
4773
				msg << "Please read the latest message that I have specified, I don't understand the current requested action.";
4774
4775
			break;
4776
		}
4777
		case MANAGER_NEW:
4778
		{
4779
			if(checkText(text, "account") && !talkState[1])
4780
			{
4781
				msg << "What would you like your password to be?";
4782
				talkState[1] = true;
4783
				talkState[2] = true;
4784
			}
4785
			else if(talkState[2])
4786
			{
4787
				std::string tmp = text;
4788
				trimString(tmp);
4789
				if(tmp.length() < 6)
4790
					msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4791
				else if(!isValidPassword(tmp))
4792
					msg << "Your password contains invalid characters... please tell me another one.";
4793
				else
4794
				{
4795
					talkState[3] = true;
4796
					talkState[2] = false;
4797
					managerString = tmp;
4798
					msg << managerString << " is it? 'yes' or 'no'?";
4799
				}
4800
			}
4801
			else if(checkText(text, "yes") && talkState[3])
4802
			{
4803
				if(g_config.getBool(ConfigManager::GENERATE_ACCOUNT_NUMBER))
4804
				{
4805
					do
4806
						sprintf(managerChar, "%d%d%d%d%d%d%d", random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9), random_range(2, 9));
4807
					while(IOLoginData::getInstance()->accountNameExists(managerChar));
4808
4809
					uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4810
					if(id)
4811
					{
4812
						accountManager = MANAGER_ACCOUNT;
4813
						managerNumber = id;
4814
4815
						noSwap = talkState[1] = false;
4816
						msg << "Your account has been created, you may manage it now, but remember your account name: '"
4817
							<< managerChar << "' and password: '" << managerString
4818
							<< "'! If the account name is too hard to remember, please note it somewhere.";
4819
					}
4820
					else
4821
						msg << "Your account could not be created, please try again.";
4822
4823
					for(int8_t i = 2; i <= 5; i++)
4824
						talkState[i] = false;
4825
				}
4826
				else
4827
				{
4828
					msg << "What would you like your account name to be?";
4829
					talkState[3] = false;
4830
					talkState[4] = true;
4831
				}
4832
			}
4833
			else if(checkText(text, "no") && talkState[3])
4834
			{
4835
				talkState[2] = true;
4836
				talkState[3] = false;
4837
				msg << "What would you like your password to be then?";
4838
			}
4839
			else if(talkState[4])
4840
			{
4841
				std::string tmp = text;
4842
				trimString(tmp);
4843
				if(tmp.length() < 3)
4844
					msg << "That account name is too short, at least 3 digits are required. Please select a longer account name.";
4845
				else if(tmp.length() > 25)
4846
					msg << "That account name is too long, not more than 25 digits are required. Please select a shorter account name.";
4847
				else if(!isValidAccountName(tmp))
4848
					msg << "Your account name contains invalid characters, please choose another one.";
4849
				else if(asLowerCaseString(tmp) == asLowerCaseString(managerString))
4850
					msg << "Your account name cannot be same as password, please choose another one.";
4851
				else
4852
				{
4853
					sprintf(managerChar, "%s", tmp.c_str());
4854
					msg << managerChar << ", are you sure?";
4855
					talkState[4] = false;
4856
					talkState[5] = true;
4857
				}
4858
			}
4859
			else if(checkText(text, "yes") && talkState[5])
4860
			{
4861
				if(!IOLoginData::getInstance()->accountNameExists(managerChar))
4862
				{
4863
					uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4864
					if(id)
4865
					{
4866
						accountManager = MANAGER_ACCOUNT;
4867
						managerNumber = id;
4868
4869
						noSwap = talkState[1] = false;
4870
						msg << "Your account has been created, you may manage it now, but remember your account name: '"
4871
							<< managerChar << "' and password: '" << managerString << "'!";
4872
					}
4873
					else
4874
						msg << "Your account could not be created, please try again.";
4875
4876
					for(int8_t i = 2; i <= 5; i++)
4877
						talkState[i] = false;
4878
				}
4879
				else
4880
				{
4881
					msg << "An account with that name already exists, please try another account name.";
4882
					talkState[4] = true;
4883
					talkState[5] = false;
4884
				}
4885
			}
4886
			else if(checkText(text, "no") && talkState[5])
4887
			{
4888
				talkState[5] = false;
4889
				talkState[4] = true;
4890
				msg << "What else would you like as your account name?";
4891
			}
4892
			else if(checkText(text, "recover") && !talkState[6])
4893
			{
4894
				talkState[6] = true;
4895
				talkState[7] = true;
4896
				msg << "What was your account name?";
4897
			}
4898
			else if(talkState[7])
4899
			{
4900
				managerString = text;
4901
				if(IOLoginData::getInstance()->getAccountId(managerString, (uint32_t&)managerNumber))
4902
				{
4903
					talkState[7] = false;
4904
					talkState[8] = true;
4905
					msg << "What was your recovery key?";
4906
				}
4907
				else
4908
				{
4909
					msg << "Sorry, but account with such name doesn't exists.";
4910
					talkState[6] = talkState[7] = false;
4911
				}
4912
			}
4913
			else if(talkState[8])
4914
			{
4915
				managerString2 = text;
4916
				if(IOLoginData::getInstance()->validRecoveryKey(managerNumber, managerString2) && managerString2 != "0")
4917
				{
4918
					sprintf(managerChar, "%s%d", g_config.getString(ConfigManager::SERVER_NAME).c_str(), random_range(100, 999));
4919
					IOLoginData::getInstance()->setPassword(managerNumber, managerChar);
4920
					msg << "Correct! Your new password is: " << managerChar << ".";
4921
				}
4922
				else
4923
					msg << "Sorry, but this key doesn't match to account you gave me.";
4924
4925
				talkState[7] = talkState[8] = false;
4926
			}
4927
			else
4928
				msg << "Sorry, but I can't understand you, please try to repeat that.";
4929
4930
			break;
4931
		}
4932
		default:
4933
			return;
4934
			break;
4935
	}
4936
4937
	sendTextMessage(MSG_STATUS_CONSOLE_BLUE, msg.str().c_str());
4938
	if(!noSwap)
4939
		sendTextMessage(MSG_STATUS_CONSOLE_ORANGE, "Hint: Type 'account' to manage your account and if you want to start over then type 'cancel'.");
4940
}
4941
4942
bool Player::isGuildInvited(uint32_t guildId) const
4943
{
4944
	for(InvitedToGuildsList::const_iterator it = invitedToGuildsList.begin(); it != invitedToGuildsList.end(); ++it)
4945
	{
4946
		if((*it) == guildId)
4947
			return true;
4948
	}
4949
4950
	return false;
4951
}
4952
4953
void Player::leaveGuild()
4954
{
4955
	sendClosePrivate(CHANNEL_GUILD);
4956
	warMap.clear();
4957
	g_game.updateCreatureEmblem(this);
4958
4959
	guildLevel = GUILDLEVEL_NONE;
4960
	guildId = rankId = 0;
4961
	guildName = rankName = guildNick = std::string();
4962
}
4963
4964
bool Player::isPremium() const
4965
{
4966
	if(g_config.getBool(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium))
4967
		return true;
4968
4969
	return premiumDays;
4970
}
4971
4972
bool Player::setGuildLevel(GuildLevel_t newLevel, uint32_t rank/* = 0*/)
4973
{
4974
	std::string name;
4975
	if(!IOGuild::getInstance()->getRankEx(rank, name, guildId, newLevel))
4976
		return false;
4977
4978
	guildLevel = newLevel;
4979
	rankName = name;
4980
	rankId = rank;
4981
	return true;
4982
}
4983
4984
void Player::setGroupId(int32_t newId)
4985
{
4986
	if(Group* tmp = Groups::getInstance()->getGroup(newId))
4987
	{
4988
		groupId = newId;
4989
		group = tmp;
4990
	}
4991
}
4992
4993
void Player::setGroup(Group* newGroup)
4994
{
4995
	if(!newGroup)
4996
		return;
4997
4998
	group = newGroup;
4999
	groupId = group->getId();
5000
}
5001
5002
PartyShields_t Player::getPartyShield(const Creature* creature) const
5003
{
5004
	const Player* player = creature->getPlayer();
5005
	if(!player)
5006
		return Creature::getPartyShield(creature);
5007
5008
	if(Party* party = getParty())
5009
	{
5010
		if(party->getLeader() == player)
5011
		{
5012
			if(!party->isSharedExperienceActive())
5013
				return SHIELD_YELLOW;
5014
5015
			if(party->isSharedExperienceEnabled())
5016
				return SHIELD_YELLOW_SHAREDEXP;
5017
5018
			if(party->canUseSharedExperience(player))
5019
				return SHIELD_YELLOW_NOSHAREDEXP;
5020
5021
			return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
5022
		}
5023
5024
		if(party->isPlayerMember(player))
5025
		{
5026
			if(!party->isSharedExperienceActive())
5027
				return SHIELD_BLUE;
5028
5029
			if(party->isSharedExperienceEnabled())
5030
				return SHIELD_BLUE_SHAREDEXP;
5031
5032
			if(party->canUseSharedExperience(player))
5033
				return SHIELD_BLUE_NOSHAREDEXP;
5034
5035
			return SHIELD_BLUE_NOSHAREDEXP_BLINK;
5036
		}
5037
5038
		if(isInviting(player))
5039
			return SHIELD_WHITEBLUE;
5040
	}
5041
5042
	if(player->isInviting(this))
5043
		return SHIELD_WHITEYELLOW;
5044
5045
	return SHIELD_NONE;
5046
}
5047
5048
bool Player::isInviting(const Player* player) const
5049
{
5050
	if(!player || player->isRemoved() || !party || party->getLeader() != this)
5051
		return false;
5052
5053
	return party->isPlayerInvited(player);
5054
}
5055
5056
bool Player::isPartner(const Player* player) const
5057
{
5058
	return player && player->getParty() && player->getParty() == party;
5059
}
5060
5061
bool Player::getHideHealth() const
5062
{
5063
	if(hasFlag(PlayerFlag_HideHealth))
5064
		return true;
5065
5066
	return hideHealth;
5067
}
5068
5069
void Player::sendPlayerIcons(Player* player)
5070
{
5071
	sendCreatureShield(player);
5072
	sendCreatureSkull(player);
5073
}
5074
5075
bool Player::addPartyInvitation(Party* party)
5076
{
5077
	if(!party)
5078
		return false;
5079
5080
	PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5081
	if(it != invitePartyList.end())
5082
		return false;
5083
5084
	invitePartyList.push_back(party);
5085
	return true;
5086
}
5087
5088
bool Player::removePartyInvitation(Party* party)
5089
{
5090
	if(!party)
5091
		return false;
5092
5093
	PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5094
	if(it != invitePartyList.end())
5095
	{
5096
		invitePartyList.erase(it);
5097
		return true;
5098
	}
5099
	return false;
5100
}
5101
5102
void Player::clearPartyInvitations()
5103
{
5104
	if(invitePartyList.empty())
5105
		return;
5106
5107
	PartyList list;
5108
	for(PartyList::iterator it = invitePartyList.begin(); it != invitePartyList.end(); ++it)
5109
		list.push_back(*it);
5110
5111
	invitePartyList.clear();
5112
	for(PartyList::iterator it = list.begin(); it != list.end(); ++it)
5113
		(*it)->removeInvite(this);
5114
}
5115
5116
void Player::increaseCombatValues(int32_t& min, int32_t& max, bool useCharges, bool countWeapon)
5117
{
5118
	if(min > 0)
5119
		min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_HEALING));
5120
	else
5121
		min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_MAGIC));
5122
5123
	if(max > 0)
5124
		max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_HEALING));
5125
	else
5126
		max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_MAGIC));
5127
5128
	Item* item = NULL;
5129
	int32_t minValue = 0, maxValue = 0, i = SLOT_FIRST;
5130
	for(; i < SLOT_LAST; ++i)
5131
	{
5132
		if(!(item = getInventoryItem((slots_t)i)) || item->isRemoved() ||
5133
				(g_moveEvents->hasEquipEvent(item) && !isItemAbilityEnabled((slots_t)i)))
5134
			continue;
5135
5136
		const ItemType& it = Item::items[item->getID()];
5137
		if(min > 0)
5138
		{
5139
			minValue += it.abilities.increment[HEALING_VALUE];
5140
			if(it.abilities.increment[HEALING_PERCENT])
5141
				min = (int32_t)std::ceil((double)(min * it.abilities.increment[HEALING_PERCENT]) / 100.);
5142
		}
5143
		else
5144
		{
5145
			minValue -= it.abilities.increment[MAGIC_VALUE];
5146
			if(it.abilities.increment[MAGIC_PERCENT])
5147
				min = (int32_t)std::ceil((double)(min * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5148
		}
5149
5150
		if(max > 0)
5151
		{
5152
			maxValue += it.abilities.increment[HEALING_VALUE];
5153
			if(it.abilities.increment[HEALING_PERCENT])
5154
				max = (int32_t)std::ceil((double)(max * it.abilities.increment[HEALING_PERCENT]) / 100.);
5155
		}
5156
		else
5157
		{
5158
			maxValue -= it.abilities.increment[MAGIC_VALUE];
5159
			if(it.abilities.increment[MAGIC_PERCENT])
5160
				max = (int32_t)std::ceil((double)(max * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5161
		}
5162
5163
		bool removeCharges = false;
5164
		for(int32_t j = INCREMENT_FIRST; j <= INCREMENT_LAST; ++j)
5165
		{
5166
			if(!it.abilities.increment[(Increment_t)j])
5167
				continue;
5168
5169
			removeCharges = true;
5170
			break;
5171
		}
5172
5173
		if(useCharges && removeCharges && (countWeapon || item != weapon) && item->hasCharges())
5174
			g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5175
	}
5176
5177
	min += minValue;
5178
	max += maxValue;
5179
}
5180
5181
bool Player::transferMoneyTo(const std::string& name, uint64_t amount)
5182
{
5183
	if(!g_config.getBool(ConfigManager::BANK_SYSTEM) || amount > balance)
5184
		return false;
5185
5186
	Player* target = g_game.getPlayerByNameEx(name);
5187
	if(!target)
5188
		return false;
5189
5190
	balance -= amount;
5191
	target->balance += amount;
5192
	if(target->isVirtual())
5193
	{
5194
		IOLoginData::getInstance()->savePlayer(target);
5195
		delete target;
5196
	}
5197
5198
	return true;
5199
}
5200
5201
void Player::sendCritical() const
5202
{
5203
	if(g_config.getBool(ConfigManager::DISPLAY_CRITICAL_HIT))
5204
		g_game.addAnimatedText(getPosition(), COLOR_DARKRED, "CRITICAL!");
5205
}