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