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