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