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