· 8 years ago · Jul 02, 2018, 12:54 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 uint32_t attackerId = m_attackedCreature->getID(), playerId = getID();
3416 const Weapon* weapon = g_weapons->getWeapon(tool);
3417 if(weapon && weapon->useWeapon(playerId, tool, attackerId))
3418 {
3419 lastAttack = OTSYS_TIME();
3420 addExhaust(attackSpeed, EXHAUST_WEAPON, CONDITION_EXHAUST);
3421
3422 if(random_range(1, 100) <= std::max(0, getAllPlayerBonusType(ITEM_DOUBLE_HIT)))
3423 Scheduler::getInstance().addEvent(createSchedulerTask(250, boost::bind(&Weapon::useWeapon, weapon, playerId, tool, attackerId)));
3424 }
3425
3426 return;
3427 }
3428
3429 if(Weapon::useFist(this, attackedCreature))
3430 lastAttack = OTSYS_TIME();
3431}
3432
3433double Player::getGainedExperience(Creature* attacker) const
3434{
3435 if(!skillLoss)
3436 return 0;
3437
3438 double rate = g_config.getDouble(ConfigManager::RATE_PVP_EXPERIENCE);
3439 if(rate <= 0)
3440 return 0;
3441
3442 Player* attackerPlayer = attacker->getPlayer();
3443 if(!attackerPlayer || attackerPlayer == this)
3444 return 0;
3445
3446 double attackerLevel = (double)attackerPlayer->getLevel(), min = g_config.getDouble(
3447 ConfigManager::EFP_MIN_THRESHOLD), max = g_config.getDouble(ConfigManager::EFP_MAX_THRESHOLD);
3448 if((min > 0 && level < (uint32_t)std::floor(attackerLevel * min)) || (max > 0 &&
3449 level > (uint32_t)std::floor(attackerLevel * max)))
3450 return 0;
3451
3452 /*
3453 Formula
3454 a = attackers level * 0.9
3455 b = victims level
3456 c = victims experience
3457
3458 result = (1 - (a / b)) * 0.05 * c
3459 Not affected by special multipliers(!)
3460 */
3461 uint32_t a = (uint32_t)std::floor(attackerLevel * 0.9), b = level;
3462 uint64_t c = getExperience();
3463 return (double)std::max((uint64_t)0, (uint64_t)std::floor(getDamageRatio(attacker)
3464 * std::max((double)0, ((double)(1 - (((double)a / b))))) * 0.05 * c)) * rate;
3465}
3466
3467void Player::onFollowCreature(const Creature* creature)
3468{
3469 if(!creature)
3470 stopEventWalk();
3471}
3472
3473void Player::setChaseMode(chaseMode_t mode)
3474{
3475 chaseMode_t prevChaseMode = chaseMode;
3476 chaseMode = mode;
3477
3478 if(prevChaseMode == chaseMode)
3479 return;
3480
3481 if(chaseMode == CHASEMODE_FOLLOW)
3482 {
3483 if(!followCreature && attackedCreature) //chase opponent
3484 setFollowCreature(attackedCreature);
3485 }
3486 else if(attackedCreature)
3487 {
3488 setFollowCreature(NULL);
3489 stopEventWalk();
3490 }
3491}
3492
3493void Player::onWalkAborted()
3494{
3495 setNextWalkActionTask(NULL);
3496 sendCancelWalk();
3497}
3498
3499void Player::onWalkComplete()
3500{
3501 if(!walkTask)
3502 return;
3503
3504 walkTaskEvent = Scheduler::getInstance().addEvent(walkTask);
3505 walkTask = NULL;
3506}
3507
3508void Player::stopWalk()
3509{
3510 if(listWalkDir.empty())
3511 return;
3512
3513 stopEventWalk();
3514}
3515
3516void Player::getCreatureLight(LightInfo& light) const
3517{
3518 if(internalLight.level > itemsLight.level)
3519 light = internalLight;
3520 else
3521 light = itemsLight;
3522}
3523
3524void Player::updateItemsLight(bool internal /*=false*/)
3525{
3526 LightInfo maxLight;
3527 LightInfo curLight;
3528 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3529 {
3530 if(Item* item = getInventoryItem((slots_t)i))
3531 {
3532 item->getLight(curLight);
3533 if(curLight.level > maxLight.level)
3534 maxLight = curLight;
3535 }
3536 }
3537 if(itemsLight.level != maxLight.level || itemsLight.color != maxLight.color)
3538 {
3539 itemsLight = maxLight;
3540 if(!internal)
3541 g_game.changeLight(this);
3542 }
3543}
3544
3545void Player::onAddCondition(Condition* condition, bool hadCondition)
3546{
3547 ConditionType_t type = condition->getType();
3548 Creature::onAddCondition(condition, hadCondition);
3549 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
3550 sendIcons();
3551}
3552
3553void Player::onAddCombatCondition(ConditionType_t type, bool hadCondition)
3554{
3555 std::string tmp;
3556 switch(type)
3557 {
3558 //client hardcoded
3559 case CONDITION_FIRE:
3560 tmp = "burning";
3561 break;
3562 case CONDITION_POISON:
3563 tmp = "poisoned";
3564 break;
3565 case CONDITION_ENERGY:
3566 tmp = "electrified";
3567 break;
3568 case CONDITION_FREEZING:
3569 tmp = "freezing";
3570 break;
3571 case CONDITION_DAZZLED:
3572 tmp = "dazzled";
3573 break;
3574 case CONDITION_CURSED:
3575 tmp = "cursed";
3576 break;
3577 case CONDITION_DROWN:
3578 tmp = "drowning";
3579 break;
3580 case CONDITION_DRUNK:
3581 tmp = "drunk";
3582 break;
3583 case CONDITION_MANASHIELD:
3584 tmp = "protected by a magic shield";
3585 break;
3586 case CONDITION_PARALYZE:
3587 tmp = "paralyzed";
3588 break;
3589 case CONDITION_HASTE:
3590 tmp = "hasted";
3591 break;
3592 case CONDITION_ATTRIBUTES:
3593 tmp = "strengthened";
3594 break;
3595 default:
3596 break;
3597 }
3598
3599 if(!tmp.empty())
3600 sendTextMessage(MSG_STATUS_DEFAULT, "You are " + tmp + ".");
3601}
3602
3603void Player::onEndCondition(ConditionType_t type)
3604{
3605 Creature::onEndCondition(type);
3606 if(type == CONDITION_INFIGHT)
3607 {
3608 onIdleStatus();
3609 clearAttacked();
3610
3611 pzLocked = false;
3612 if(skull < SKULL_RED)
3613 setSkull(SKULL_NONE);
3614
3615 g_game.updateCreatureSkull(this);
3616 }
3617
3618 sendIcons();
3619}
3620
3621void Player::onCombatRemoveCondition(const Creature* attacker, Condition* condition)
3622{
3623 //Creature::onCombatRemoveCondition(attacker, condition);
3624 bool remove = true;
3625 if(condition->getId() > 0)
3626 {
3627 remove = false;
3628 //Means the condition is from an item, id == slot
3629 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED)
3630 {
3631 if(Item* item = getInventoryItem((slots_t)condition->getId()))
3632 {
3633 //25% chance to destroy the item
3634 if(25 >= random_range(0, 100))
3635 g_game.internalRemoveItem(NULL, item);
3636 }
3637 }
3638 }
3639
3640 if(remove)
3641 {
3642 if(!canDoAction())
3643 {
3644 uint32_t delay = getNextActionTime();
3645 delay -= (delay % EVENT_CREATURE_THINK_INTERVAL);
3646 if(delay < 0)
3647 removeCondition(condition);
3648 else
3649 condition->setTicks(delay);
3650 }
3651 else
3652 removeCondition(condition);
3653 }
3654}
3655
3656void Player::onTickCondition(ConditionType_t type, int32_t interval, bool& _remove)
3657{
3658 Creature::onTickCondition(type, interval, _remove);
3659 if(type == CONDITION_HUNTING)
3660 useStamina(-(interval * g_config.getNumber(ConfigManager::RATE_STAMINA_LOSS)));
3661}
3662
3663void Player::onAttackedCreature(Creature* target)
3664{
3665 Creature::onAttackedCreature(target);
3666 if(hasFlag(PlayerFlag_NotGainInFight) || !target)
3667 return;
3668
3669 addInFightTicks();
3670 Player* targetPlayer = target->getPlayer();
3671 if(!targetPlayer)
3672 return;
3673
3674 addAttacked(targetPlayer);
3675 if(targetPlayer == this && targetPlayer->getZone() != ZONE_PVP)
3676 {
3677 targetPlayer->sendCreatureSkull(this);
3678 return;
3679 }
3680
3681 if(Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer) || (g_config.getBool(
3682 ConfigManager::ALLOW_FIGHTBACK) && targetPlayer->hasAttacked(this)))
3683 return;
3684
3685 if(!pzLocked)
3686 {
3687 pzLocked = true;
3688 sendIcons();
3689 }
3690
3691 if(getZone() != target->getZone())
3692 return;
3693
3694 if(skull == SKULL_NONE)
3695 {
3696 if(targetPlayer->getSkull() != SKULL_NONE)
3697 targetPlayer->sendCreatureSkull(this);
3698 else if(!hasCustomFlag(PlayerCustomFlag_NotGainSkull))
3699 {
3700 setSkull(SKULL_WHITE);
3701 g_game.updateCreatureSkull(this);
3702 }
3703 }
3704}
3705
3706void Player::onSummonAttackedCreature(Creature* summon, Creature* target)
3707{
3708 Creature::onSummonAttackedCreature(summon, target);
3709 onAttackedCreature(target);
3710}
3711
3712void Player::onAttacked()
3713{
3714 Creature::onAttacked();
3715 addInFightTicks();
3716}
3717
3718bool Player::checkLoginDelay(uint32_t playerId) const
3719{
3720 return (!hasCustomFlag(PlayerCustomFlag_IgnoreLoginDelay) && OTSYS_TIME() <= (lastLoad + g_config.getNumber(
3721 ConfigManager::LOGIN_PROTECTION)) && !hasBeenAttacked(playerId));
3722}
3723
3724void Player::onIdleStatus()
3725{
3726 Creature::onIdleStatus();
3727 if(getParty())
3728 getParty()->clearPlayerPoints(this);
3729}
3730
3731void Player::onPlacedCreature()
3732{
3733 //scripting event - onLogin
3734 if(!g_creatureEvents->playerLogin(this))
3735 {
3736 kickPlayer(true, true);
3737 return;
3738 }
3739
3740 updateAchievement(ACHIEVEMENT_LOGIN_50_TIMES);
3741
3742 int32_t value = getCreatureIntStorage(TRANSFORM_STORAGE);
3743 Transforms* transform = Transforms::getInstance();
3744 if(transform && (transform->isTransformRelog(this) || transform->isPermanent(this, value)))
3745 transform->doTransform(this, std::max(0, value), true, true);
3746}
3747
3748void Player::getSetsAttributeMap(ItemAttributes_t type, int32_t* table)
3749{
3750 if(setsAttributesList.empty())
3751 return;
3752
3753 for(SetsAttributesList::iterator it = setsAttributesList.begin(); it != setsAttributesList.end(); ++it)
3754 {
3755 if(it->second.empty())
3756 continue;
3757
3758 for(AttributesListMap::iterator _it = it->second.begin(); _it != it->second.end(); ++_it)
3759 {
3760 if(_it->first != type || _it->second.empty())
3761 continue;
3762
3763 for(std::map<int32_t, int32_t>::iterator __it = _it->second.begin(); __it != _it->second.end(); ++__it)
3764 table[__it->first] += __it->second;
3765 }
3766 }
3767}
3768
3769void Player::onAttackedCreatureDrain(Creature* target, int32_t points)
3770{
3771 Creature::onAttackedCreatureDrain(target, points);
3772 if(party && target && (!target->getMaster() || !target->getMaster()->getPlayer())
3773 && target->getMonster() && target->getMonster()->isHostile()) //we have fulfilled a requirement for shared experience
3774 getParty()->addPlayerDamageMonster(this, points);
3775
3776 char buffer[100];
3777 sprintf(buffer, "You deal %d damage to %s.", points, target->getNameDescription().c_str());
3778 sendTextMessage(MSG_STATUS_DEFAULT, buffer);
3779}
3780
3781void Player::onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points)
3782{
3783 Creature::onSummonAttackedCreatureDrain(summon, target, points);
3784
3785 char buffer[100];
3786 sprintf(buffer, "Your %s deals %d damage to %s.", summon->getName().c_str(), points, target->getNameDescription().c_str());
3787 sendTextMessage(MSG_EVENT_DEFAULT, buffer);
3788}
3789
3790void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
3791{
3792 Creature::onTargetCreatureGainHealth(target, points);
3793 if(target && getParty())
3794 {
3795 Player* tmpPlayer = NULL;
3796 if(target->getPlayer())
3797 tmpPlayer = target->getPlayer();
3798 else if(target->getMaster() && target->getMaster()->getPlayer())
3799 tmpPlayer = target->getMaster()->getPlayer();
3800
3801 if(isPartner(tmpPlayer))
3802 getParty()->addPlayerHealedMember(this, points);
3803 }
3804}
3805
3806bool Player::onKilledCreature(Creature* target, uint32_t& flags)
3807{
3808 if(!Creature::onKilledCreature(target, flags))
3809 return false;
3810
3811 if(hasFlag(PlayerFlag_NotGenerateLoot))
3812 target->setDropLoot(LOOT_DROP_NONE);
3813
3814 Condition* condition = NULL;
3815 if(target->getMonster() && !target->isPlayerSummon() && !hasFlag(PlayerFlag_HasInfiniteStamina)
3816 && (condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_HUNTING,
3817 g_config.getNumber(ConfigManager::HUNTING_DURATION))))
3818 addCondition(condition);
3819
3820 if(hasFlag(PlayerFlag_NotGainInFight) || !hasBitSet((uint32_t)KILLFLAG_JUSTIFY, flags) || getZone() != target->getZone())
3821 return true;
3822
3823 Player* targetPlayer = target->getPlayer();
3824 if(!targetPlayer)
3825 return true;
3826
3827 setCreatureStorage(KILL_COUNT_STORAGE, std::max(0, getCreatureIntStorage(KILL_COUNT_STORAGE)) + 1);
3828 if(Combat::isInPvpZone(this, targetPlayer) || !hasCondition(CONDITION_INFIGHT) || isPartner(targetPlayer))
3829 return true;
3830
3831 if(!targetPlayer->hasAttacked(this) && target->getSkull() == SKULL_NONE && targetPlayer != this
3832 && ((g_config.getBool(ConfigManager::USE_FRAG_HANDLER) && addUnjustifiedKill(
3833 targetPlayer)) || hasBitSet((uint32_t)KILLFLAG_LASTHIT, flags)))
3834 flags |= (uint32_t)KILLFLAG_UNJUSTIFIED;
3835
3836 pzLocked = true;
3837 if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT,
3838 g_config.getNumber(ConfigManager::WHITE_SKULL_TIME))))
3839 addCondition(condition);
3840
3841 return true;
3842}
3843
3844bool Player::gainExperience(double& gainExp, bool fromMonster)
3845{
3846 if(!rateExperience(gainExp, fromMonster))
3847 return false;
3848
3849 //soul regeneration
3850 if(gainExp >= level)
3851 {
3852 if(Condition* condition = Condition::createCondition(
3853 CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000))
3854 {
3855 condition->setParam(CONDITIONPARAM_SOULGAIN,
3856 vocation->getGainAmount(GAIN_SOUL));
3857 condition->setParam(CONDITIONPARAM_SOULTICKS,
3858 (vocation->getGainTicks(GAIN_SOUL) * 1000));
3859 addCondition(condition);
3860 }
3861 }
3862
3863 addExperience((uint64_t)gainExp);
3864 return true;
3865}
3866
3867int32_t Player::getItemsMultipliers(ItemsMultipliers_t type)
3868{
3869 int32_t var = 0;
3870 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3871 {
3872 if(i == SLOT_AMMO)
3873 continue;
3874
3875 Item* item = getInventoryItem((slots_t)i);
3876 if(!item)
3877 continue;
3878
3879 const ItemType& it = Item::items[item->getID()];
3880 switch(type)
3881 {
3882 case MULTIPLIER_EXPERIENCE:
3883 var += it.abilities.experienceRate;
3884 break;
3885 case MULTIPLIER_SKILL:
3886 var += it.abilities.skillRate;
3887 break;
3888 case MULTIPLIER_DROP:
3889 var += it.abilities.dropRate;
3890 break;
3891
3892 default:
3893 break;
3894 }
3895 }
3896
3897 return var;
3898}
3899
3900bool Player::rateExperience(double& gainExp, bool fromMonster)
3901{
3902 if(hasFlag(PlayerFlag_NotGainExperience) || gainExp <= 0)
3903 return false;
3904
3905 if(!fromMonster)
3906 return true;
3907
3908 if(hasCondition(CONDITION_ATTRIBUTES, SUBID_EXPERIENCE))
3909 {
3910 int32_t var = getCreatureAttribute(SUBID_EXPERIENCE);
3911 if(var != 0)
3912 gainExp += (gainExp * var) / 100;
3913 }
3914
3915 gainExp += gainExp * getItemsMultipliers(MULTIPLIER_EXPERIENCE);
3916 gainExp *= rates[SKILL__LEVEL] * g_game.getExperienceStage(level,
3917 vocation->getExperienceMultiplier());
3918 if(!hasFlag(PlayerFlag_HasInfiniteStamina))
3919 {
3920 int32_t minutes = getStaminaMinutes();
3921 if(minutes >= g_config.getNumber(ConfigManager::STAMINA_LIMIT_TOP))
3922 {
3923 if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM))
3924 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3925 }
3926 else if(minutes < (g_config.getNumber(ConfigManager::STAMINA_LIMIT_BOTTOM)) && minutes > 0)
3927 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_UNDER);
3928 else if(minutes <= 0)
3929 gainExp = 0;
3930 }
3931 else if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM))
3932 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3933
3934 return true;
3935}
3936
3937void Player::onGainExperience(double& gainExp, bool fromMonster, bool multiplied)
3938{
3939 if(isPremium())
3940 gainExp *= 1.2;
3941
3942 if(party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive())
3943 {
3944 party->shareExperience(gainExp, fromMonster, multiplied);
3945 rateExperience(gainExp, fromMonster);
3946 return; //we will get a share of the experience through the sharing mechanism
3947 }
3948
3949 if(gainExperience(gainExp, fromMonster))
3950 Creature::onGainExperience(gainExp, fromMonster, true);
3951}
3952
3953void Player::onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied)
3954{
3955 if(gainExperience(gainExp, fromMonster))
3956 Creature::onGainSharedExperience(gainExp, fromMonster, true);
3957}
3958
3959bool Player::isImmune(CombatType_t type) const
3960{
3961 return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
3962}
3963
3964bool Player::isImmune(ConditionType_t type) const
3965{
3966 return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
3967}
3968
3969bool Player::isAttackable() const
3970{
3971 return (!hasFlag(PlayerFlag_CannotBeAttacked) && !isAccountManager());
3972}
3973
3974void Player::changeHealth(int32_t healthChange)
3975{
3976 Creature::changeHealth(healthChange);
3977 sendStats();
3978}
3979
3980void Player::changeMana(int32_t manaChange)
3981{
3982 if(!hasFlag(PlayerFlag_HasInfiniteMana))
3983 Creature::changeMana(manaChange);
3984
3985 sendStats();
3986}
3987
3988void Player::changeSoul(int32_t soulChange)
3989{
3990 if(!hasFlag(PlayerFlag_HasInfiniteSoul))
3991 soul = std::min((int32_t)soulMax, (int32_t)soul + soulChange);
3992
3993 sendStats();
3994}
3995
3996bool Player::canLogout(bool checkInfight)
3997{
3998 if(checkInfight && hasCondition(CONDITION_INFIGHT))
3999 return false;
4000
4001 return !isConnecting && !pzLocked && !getTile()->hasFlag(TILESTATE_NOLOGOUT);
4002}
4003
4004bool Player::changeOutfit(Outfit_t outfit, bool checkList)
4005{
4006 uint32_t outfitId = Outfits::getInstance()->getOutfitId(outfit.lookType);
4007 if(checkList && (!canWearOutfit(outfitId, outfit.lookAddons) || !requestedOutfit))
4008 return false;
4009
4010 requestedOutfit = false;
4011 if(outfitAttributes)
4012 {
4013 uint32_t oldId = Outfits::getInstance()->getOutfitId(defaultOutfit.lookType);
4014 outfitAttributes = !Outfits::getInstance()->removeAttributes(getID(), oldId, sex);
4015 }
4016
4017 defaultOutfit = outfit;
4018 outfitAttributes = Outfits::getInstance()->addAttributes(getID(), outfitId, sex, defaultOutfit.lookAddons);
4019 return true;
4020}
4021
4022bool Player::canWearOutfit(uint32_t outfitId, uint32_t addons)
4023{
4024 OutfitMap::iterator it = outfits.find(outfitId);
4025 if(it == outfits.end() || (it->second.isPremium && !isPremium()) || getAccess() < it->second.accessLevel
4026 || ((it->second.addons & addons) != addons && !hasCustomFlag(PlayerCustomFlag_CanWearAllAddons)))
4027 return false;
4028
4029 if(!it->second.storageId)
4030 return true;
4031
4032 std::string value;
4033 return getStorage(it->second.storageId, value) && value == it->second.storageValue;
4034}
4035
4036bool Player::addOutfit(uint32_t outfitId, uint32_t addons)
4037{
4038 Outfit outfit;
4039 if(!Outfits::getInstance()->getOutfit(outfitId, sex, outfit))
4040 return false;
4041
4042 OutfitMap::iterator it = outfits.find(outfitId);
4043 if(it != outfits.end())
4044 outfit.addons |= it->second.addons;
4045
4046 outfit.addons |= addons;
4047 outfits[outfitId] = outfit;
4048 return true;
4049}
4050
4051bool Player::removeOutfit(uint32_t outfitId, uint32_t addons)
4052{
4053 OutfitMap::iterator it = outfits.find(outfitId);
4054 if(it == outfits.end())
4055 return false;
4056
4057 if(addons == 0xFF) //remove outfit
4058 outfits.erase(it);
4059 else //remove addons
4060 outfits[outfitId].addons = it->second.addons & (~addons);
4061
4062 return true;
4063}
4064
4065void Player::generateReservedStorage()
4066{
4067 uint32_t baseKey = PSTRG_OUTFITSID_RANGE_START + 1;
4068 const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4069 for(OutfitMap::const_iterator it = outfits.begin(); it != outfits.end(); ++it)
4070 {
4071 OutfitMap::const_iterator dit = defaultOutfits.find(it->first);
4072 if(dit == defaultOutfits.end() || (dit->second.isDefault && (dit->second.addons
4073 & it->second.addons) == it->second.addons))
4074 continue;
4075
4076 std::stringstream ss;
4077 ss << ((it->first << 16) | (it->second.addons & 0xFF));
4078 storageMap[baseKey] = ss.str();
4079
4080 baseKey++;
4081 if(baseKey <= PSTRG_OUTFITSID_RANGE_START + PSTRG_OUTFITSID_RANGE_SIZE)
4082 continue;
4083
4084 std::cout << "[Warning - Player::genReservedStorageRange] Player " << getName() << " with more than 500 outfits!" << std::endl;
4085 break;
4086 }
4087}
4088
4089void Player::setSex(uint16_t newSex)
4090{
4091 sex = newSex;
4092 const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4093 for(OutfitMap::const_iterator it = defaultOutfits.begin(); it != defaultOutfits.end(); ++it)
4094 {
4095 if(it->second.isDefault)
4096 addOutfit(it->first, it->second.addons);
4097 }
4098}
4099
4100Skulls_t Player::getSkull() const
4101{
4102 if(hasFlag(PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4103 return SKULL_NONE;
4104
4105 return skull;
4106}
4107
4108Skulls_t Player::getSkullClient(const Creature* creature) const
4109{
4110 if(const Player* player = creature->getPlayer())
4111 {
4112 if(g_game.getWorldType() != WORLD_TYPE_PVP)
4113 return SKULL_NONE;
4114
4115 if((player == this || (skull != SKULL_NONE && player->getSkull() < SKULL_RED)) && player->hasAttacked(this))
4116 return SKULL_YELLOW;
4117
4118 if(player->getSkull() == SKULL_NONE && isPartner(player) && g_game.getWorldType() != WORLD_TYPE_NO_PVP)
4119 return SKULL_GREEN;
4120 }
4121
4122 return Creature::getSkullClient(creature);
4123}
4124
4125bool Player::hasAttacked(const Player* attacked) const
4126{
4127 return !hasFlag(PlayerFlag_NotGainInFight) && attacked &&
4128 attackedSet.find(attacked->getID()) != attackedSet.end();
4129}
4130
4131void Player::addAttacked(const Player* attacked)
4132{
4133 if(hasFlag(PlayerFlag_NotGainInFight) || !attacked)
4134 return;
4135
4136 uint32_t attackedId = attacked->getID();
4137 if(attackedSet.find(attackedId) == attackedSet.end())
4138 attackedSet.insert(attackedId);
4139}
4140
4141void Player::setSkullEnd(time_t _time, bool login, Skulls_t _skull)
4142{
4143 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED)
4144 return;
4145
4146 bool requireUpdate = false;
4147 if(_time > time(NULL))
4148 {
4149 requireUpdate = true;
4150 setSkull(_skull);
4151 }
4152 else if(skull == _skull)
4153 {
4154 requireUpdate = true;
4155 setSkull(SKULL_NONE);
4156 _time = 0;
4157 }
4158
4159 if(requireUpdate)
4160 {
4161 skullEnd = _time;
4162 if(!login)
4163 g_game.updateCreatureSkull(this);
4164 }
4165}
4166
4167bool Player::addUnjustifiedKill(const Player* attacked)
4168{
4169 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED || attacked == this || hasFlag(
4170 PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4171 return false;
4172
4173 if(client)
4174 {
4175 char buffer[90];
4176 sprintf(buffer, "Warning! The murder of %s was not justified.",
4177 attacked->getName().c_str());
4178 client->sendTextMessage(MSG_STATUS_WARNING, buffer);
4179 }
4180
4181 time_t now = time(NULL), today = (now - 84600), week = (now - (7 * 84600));
4182 std::vector<time_t> dateList;
4183 IOLoginData::getInstance()->getUnjustifiedDates(guid, dateList, now);
4184
4185 dateList.push_back(now);
4186 uint32_t tc = 0, wc = 0, mc = dateList.size();
4187 for(std::vector<time_t>::iterator it = dateList.begin(); it != dateList.end(); ++it)
4188 {
4189 if((*it) > week)
4190 wc++;
4191
4192 if((*it) > today)
4193 tc++;
4194 }
4195
4196 uint32_t d = g_config.getNumber(ConfigManager::RED_DAILY_LIMIT), w = g_config.getNumber(
4197 ConfigManager::RED_WEEKLY_LIMIT), m = g_config.getNumber(ConfigManager::RED_MONTHLY_LIMIT);
4198 if(skull < SKULL_RED && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4199 setSkullEnd(now + g_config.getNumber(ConfigManager::RED_SKULL_LENGTH), false, SKULL_RED);
4200
4201 if(!g_config.getBool(ConfigManager::USE_BLACK_SKULL))
4202 {
4203 d += g_config.getNumber(ConfigManager::BAN_DAILY_LIMIT);
4204 w += g_config.getNumber(ConfigManager::BAN_WEEKLY_LIMIT);
4205 m += g_config.getNumber(ConfigManager::BAN_MONTHLY_LIMIT);
4206 if((d <= 0 || tc < d) && (w <= 0 || wc < w) && (m <= 0 || mc < m))
4207 return true;
4208
4209 if(!IOBan::getInstance()->addAccountBanishment(accountId, (now + g_config.getNumber(
4210 ConfigManager::KILLS_BAN_LENGTH)), 20, ACTION_BANISHMENT, "Unjustified player killing.", 0, guid))
4211 return true;
4212
4213 sendTextMessage(MSG_INFO_DESCR, "You have been banished.");
4214 g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_GREEN);
4215 Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind(
4216 &Game::kickPlayer, &g_game, getID(), false)));
4217 }
4218 else
4219 {
4220 d += g_config.getNumber(ConfigManager::BLACK_DAILY_LIMIT);
4221 w += g_config.getNumber(ConfigManager::BLACK_WEEKLY_LIMIT);
4222 m += g_config.getNumber(ConfigManager::BLACK_MONTHLY_LIMIT);
4223 if(skull < SKULL_BLACK && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4224 {
4225 setSkullEnd(now + g_config.getNumber(ConfigManager::BLACK_SKULL_LENGTH), false, SKULL_BLACK);
4226 setAttackedCreature(NULL);
4227 destroySummons();
4228 }
4229 }
4230
4231 return true;
4232}
4233
4234void Player::setPromotionLevel(uint32_t pLevel)
4235{
4236 if(pLevel > promotionLevel)
4237 {
4238 uint32_t tmpLevel = 0, currentVoc = vocation_id;
4239 for(uint32_t i = promotionLevel; i < pLevel; ++i)
4240 {
4241 currentVoc = Vocations::getInstance()->getPromotedVocation(currentVoc);
4242 if(!currentVoc)
4243 break;
4244
4245 tmpLevel++;
4246 Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4247 if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4248 continue;
4249
4250 vocation_id = currentVoc;
4251 }
4252
4253 promotionLevel += tmpLevel;
4254 }
4255 else if(pLevel < promotionLevel)
4256 {
4257 uint32_t tmpLevel = 0, currentVoc = vocation_id;
4258 for(uint32_t i = pLevel; i < promotionLevel; ++i)
4259 {
4260 Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4261 if(voc->getFromVocation() == currentVoc)
4262 break;
4263
4264 tmpLevel++;
4265 currentVoc = voc->getFromVocation();
4266 if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4267 continue;
4268
4269 vocation_id = currentVoc;
4270 }
4271
4272 promotionLevel -= tmpLevel;
4273 }
4274
4275 setVocation(vocation_id);
4276}
4277
4278uint16_t Player::getBlessings() const
4279{
4280 if(!isPremium() && g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM))
4281 return 0;
4282
4283 uint16_t count = 0;
4284 for(int16_t i = 0; i < 16; ++i)
4285 {
4286 if(hasBlessing(i))
4287 count++;
4288 }
4289
4290 return count;
4291}
4292
4293uint64_t Player::getLostExperience() const
4294{
4295 if(!skillLoss)
4296 return 0;
4297
4298 double percent = (double)(lossPercent[LOSS_EXPERIENCE] - vocation->getLessLoss() - (getBlessings() * g_config.getNumber(
4299 ConfigManager::BLESS_REDUCTION))) / 100.;
4300 if(level <= 25)
4301 return (uint64_t)std::floor((double)(experience * percent) / 10.);
4302
4303 int32_t base = level;
4304 double levels = (double)(base + 50) / 100.;
4305
4306 uint64_t lost = 0;
4307 while(levels > 1.0f)
4308 {
4309 lost += (getExpForLevel(base) - getExpForLevel(base - 1));
4310 base--;
4311 levels -= 1.;
4312 }
4313
4314 if(levels > 0.)
4315 lost += (uint64_t)std::floor((double)(getExpForLevel(base) - getExpForLevel(base - 1)) * levels);
4316
4317 return (uint64_t)std::floor((double)(lost * percent)) / 3;
4318}
4319
4320uint32_t Player::getAttackSpeed()
4321{
4322 int32_t attackSpeed = 3000, buff = 0;
4323
4324 int32_t var = getAllPlayerBonusType(ITEM_ATTACK_SPEED, true);
4325 if(var > 0)
4326 buff += var;
4327
4328 buff += getSkill(SKILL_FIST, SKILL_LEVEL) * 15;
4329
4330 var = ((attackSpeed * (transformAttributes[TRANSFORM_ATTACK_SPEED] + getAchievementBonus(ACHIEVEMENT_AGILITY))) / 100);
4331 if(var > 0)
4332 buff += var;
4333
4334 if(buff + 100 > attackSpeed)
4335 return 100;
4336
4337 return std::max<uint32_t>(100, uint32_t(attackSpeed - buff));
4338}
4339
4340void Player::learnInstantSpell(const std::string& name)
4341{
4342 if(!hasLearnedInstantSpell(name))
4343 learnedInstantSpellList.push_back(name);
4344}
4345
4346void Player::unlearnInstantSpell(const std::string& name)
4347{
4348 if(!hasLearnedInstantSpell(name))
4349 return;
4350
4351 LearnedInstantSpellList::iterator it = std::find(learnedInstantSpellList.begin(), learnedInstantSpellList.end(), name);
4352 if(it != learnedInstantSpellList.end())
4353 learnedInstantSpellList.erase(it);
4354}
4355
4356bool Player::hasLearnedInstantSpell(const std::string& name) const
4357{
4358 if(hasFlag(PlayerFlag_CannotUseSpells))
4359 return false;
4360
4361 if(hasFlag(PlayerFlag_IgnoreSpellCheck))
4362 return true;
4363
4364 for(LearnedInstantSpellList::const_iterator it = learnedInstantSpellList.begin(); it != learnedInstantSpellList.end(); ++it)
4365 {
4366 if(!strcasecmp((*it).c_str(), name.c_str()))
4367 return true;
4368 }
4369
4370 return false;
4371}
4372
4373void Player::manageAccount(const std::string &text)
4374{
4375 std::stringstream msg;
4376 msg << "Account Manager: ";
4377
4378 bool noSwap = true;
4379 switch(accountManager)
4380 {
4381 case MANAGER_NAMELOCK:
4382 {
4383 if(!talkState[1])
4384 {
4385 managerString = text;
4386 trimString(managerString);
4387 if(managerString.length() < 4)
4388 msg << "Your name you want is too short, please select a longer name.";
4389 else if(managerString.length() > 20)
4390 msg << "The name you want is too long, please select a shorter name.";
4391 else if(!isValidName(managerString))
4392 msg << "That name seems to contain invalid symbols, please choose another name.";
4393 else if(IOLoginData::getInstance()->playerExists(managerString, true))
4394 msg << "A player with that name already exists, please choose another name.";
4395 else
4396 {
4397 std::string tmp = asLowerCaseString(managerString);
4398 if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4399 {
4400 talkState[1] = true;
4401 talkState[2] = true;
4402 msg << managerString << ", are you sure?";
4403 }
4404 else
4405 msg << "Your character is not a staff member, please tell me another name!";
4406 }
4407 }
4408 else if(checkText(text, "no") && talkState[2])
4409 {
4410 talkState[1] = talkState[2] = false;
4411 msg << "What else would you like to name your character?";
4412 }
4413 else if(checkText(text, "yes") && talkState[2])
4414 {
4415 if(!IOLoginData::getInstance()->playerExists(managerString, true))
4416 {
4417 uint32_t tmp;
4418 if(IOLoginData::getInstance()->getGuidByName(tmp, managerString2) &&
4419 IOLoginData::getInstance()->changeName(tmp, managerString, managerString2) &&
4420 IOBan::getInstance()->removePlayerBanishment(tmp, PLAYERBAN_LOCK))
4421 {
4422 if(House* house = Houses::getInstance()->getHouseByPlayerId(tmp))
4423 house->updateDoorDescription(managerString);
4424
4425 talkState[1] = true;
4426 talkState[2] = false;
4427 msg << "Your character has been successfully renamed, you should now be able to login at it without any problems.";
4428 }
4429 else
4430 {
4431 talkState[1] = talkState[2] = false;
4432 msg << "Failed to change your name, please try again.";
4433 }
4434 }
4435 else
4436 {
4437 talkState[1] = talkState[2] = false;
4438 msg << "A player with that name already exists, please choose another name.";
4439 }
4440 }
4441 else
4442 msg << "Sorry, but I can't understand you, please try to repeat that!";
4443
4444 break;
4445 }
4446 case MANAGER_ACCOUNT:
4447 {
4448 Account account = IOLoginData::getInstance()->loadAccount(managerNumber);
4449 if(checkText(text, "cancel") || (checkText(text, "account") && !talkState[1]))
4450 {
4451 talkState[1] = true;
4452 for(int8_t i = 2; i <= 12; i++)
4453 talkState[i] = false;
4454
4455 msg << "Do you want to change your 'password', request a 'recovery key', add a 'character', or 'delete' a character?";
4456 }
4457 else if(checkText(text, "delete") && talkState[1])
4458 {
4459 talkState[1] = false;
4460 talkState[2] = true;
4461 msg << "Which character would you like to delete?";
4462 }
4463 else if(talkState[2])
4464 {
4465 std::string tmp = text;
4466 trimString(tmp);
4467 if(!isValidName(tmp, false))
4468 msg << "That name contains invalid characters, try to say your name again, you might have typed it wrong.";
4469 else
4470 {
4471 talkState[2] = false;
4472 talkState[3] = true;
4473 managerString = tmp;
4474 msg << "Do you really want to delete the character named " << managerString << "?";
4475 }
4476 }
4477 else if(checkText(text, "yes") && talkState[3])
4478 {
4479 switch(IOLoginData::getInstance()->deleteCharacter(managerNumber, managerString))
4480 {
4481 case DELETE_INTERNAL:
4482 msg << "An error occured while deleting your character. Either the character does not belong to you or it doesn't exist.";
4483 break;
4484
4485 case DELETE_SUCCESS:
4486 msg << "Your character has been deleted.";
4487 break;
4488
4489 case DELETE_HOUSE:
4490 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.";
4491 break;
4492
4493 case DELETE_LEADER:
4494 msg << "Your character is the leader of a guild. You need to disband or pass the leadership someone else to delete your character.";
4495 break;
4496
4497 case DELETE_ONLINE:
4498 msg << "A character with that name is currently online, to delete a character it has to be offline.";
4499 break;
4500 }
4501
4502 talkState[1] = true;
4503 for(int8_t i = 2; i <= 12; i++)
4504 talkState[i] = false;
4505 }
4506 else if(checkText(text, "no") && talkState[3])
4507 {
4508 talkState[1] = true;
4509 talkState[3] = false;
4510 msg << "Tell me what character you want to delete.";
4511 }
4512 else if(checkText(text, "password") && talkState[1])
4513 {
4514 talkState[1] = false;
4515 talkState[4] = true;
4516 msg << "Tell me your new password please.";
4517 }
4518 else if(talkState[4])
4519 {
4520 std::string tmp = text;
4521 trimString(tmp);
4522 if(tmp.length() < 6)
4523 msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4524 else if(!isValidPassword(tmp))
4525 msg << "Your password contains invalid characters... please tell me another one.";
4526 else
4527 {
4528 talkState[4] = false;
4529 talkState[5] = true;
4530 managerString = tmp;
4531 msg << "Should '" << managerString << "' be your new password?";
4532 }
4533 }
4534 else if(checkText(text, "yes") && talkState[5])
4535 {
4536 talkState[1] = true;
4537 for(int8_t i = 2; i <= 12; i++)
4538 talkState[i] = false;
4539
4540 IOLoginData::getInstance()->setPassword(managerNumber, managerString);
4541 msg << "Your password has been changed.";
4542 }
4543 else if(checkText(text, "no") && talkState[5])
4544 {
4545 talkState[1] = true;
4546 for(int8_t i = 2; i <= 12; i++)
4547 talkState[i] = false;
4548
4549 msg << "Then not.";
4550 }
4551 else if(checkText(text, "character") && talkState[1])
4552 {
4553 if(account.charList.size() <= 15)
4554 {
4555 talkState[1] = false;
4556 talkState[6] = true;
4557 msg << "What would you like as your character name?";
4558 }
4559 else
4560 {
4561 talkState[1] = true;
4562 for(int8_t i = 2; i <= 12; i++)
4563 talkState[i] = false;
4564
4565 msg << "Your account reach the limit of 15 players, you can 'delete' a character if you want to create a new one.";
4566 }
4567 }
4568 else if(talkState[6])
4569 {
4570 managerString = text;
4571 trimString(managerString);
4572 if(managerString.length() < 4)
4573 msg << "Your name you want is too short, please select a longer name.";
4574 else if(managerString.length() > 20)
4575 msg << "The name you want is too long, please select a shorter name.";
4576 else if(!isValidName(managerString))
4577 msg << "That name seems to contain invalid symbols, please choose another name.";
4578 else if(IOLoginData::getInstance()->playerExists(managerString, true))
4579 msg << "A player with that name already exists, please choose another name.";
4580 else
4581 {
4582 std::string tmp = asLowerCaseString(managerString);
4583 if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4584 {
4585 talkState[6] = false;
4586 talkState[7] = true;
4587 msg << managerString << ", are you sure?";
4588 }
4589 else
4590 msg << "Your character is not a staff member, please tell me another name!";
4591 }
4592 }
4593 else if(checkText(text, "no") && talkState[7])
4594 {
4595 talkState[6] = true;
4596 talkState[7] = false;
4597 msg << "What else would you like to name your character?";
4598 }
4599 else if(checkText(text, "yes") && talkState[7])
4600 {
4601 talkState[7] = false;
4602 talkState[8] = true;
4603 msg << "Should your character be a 'male' or a 'female'.";
4604 }
4605 else if(talkState[8] && (checkText(text, "female") || checkText(text, "male")))
4606 {
4607 talkState[8] = false;
4608 talkState[9] = true;
4609 if(checkText(text, "female"))
4610 {
4611 msg << "A female, are you sure?";
4612 managerSex = PLAYERSEX_FEMALE;
4613 }
4614 else
4615 {
4616 msg << "A male, are you sure?";
4617 managerSex = PLAYERSEX_MALE;
4618 }
4619 }
4620 else if(checkText(text, "no") && talkState[9])
4621 {
4622 talkState[8] = true;
4623 talkState[9] = false;
4624 msg << "Tell me... would you like to be a 'male' or a 'female'?";
4625 }
4626 else if(checkText(text, "yes") && talkState[9])
4627 {
4628 if(g_config.getBool(ConfigManager::START_CHOOSEVOC))
4629 {
4630 talkState[9] = false;
4631 talkState[11] = true;
4632
4633 bool firstPart = true;
4634 for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4635 {
4636 if(it->first == it->second->getFromVocation() && it->first != 0)
4637 {
4638 if(firstPart)
4639 {
4640 msg << "What do you want to be... " << it->second->getDescription();
4641 firstPart = false;
4642 }
4643 else if(it->first - 1 != 0)
4644 msg << ", " << it->second->getDescription();
4645 else
4646 msg << " or " << it->second->getDescription() << ".";
4647 }
4648 }
4649 }
4650 else if(!IOLoginData::getInstance()->playerExists(managerString, true))
4651 {
4652 talkState[1] = true;
4653 for(int8_t i = 2; i <= 12; i++)
4654 talkState[i] = false;
4655
4656 if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4657 msg << "Your character has been created.";
4658 else
4659 msg << "Your character couldn't be created, please try again.";
4660 }
4661 else
4662 {
4663 talkState[6] = true;
4664 talkState[9] = false;
4665 msg << "A player with that name already exists, please choose another name.";
4666 }
4667 }
4668 else if(talkState[11])
4669 {
4670 for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4671 {
4672 std::string tmp = asLowerCaseString(it->second->getName());
4673 if(checkText(text, tmp) && it != Vocations::getInstance()->getLastVocation() && it->first == it->second->getFromVocation() && it->first != 0)
4674 {
4675 msg << "So you would like to be " << it->second->getDescription() << "... are you sure?";
4676 managerNumber2 = it->first;
4677 talkState[11] = false;
4678 talkState[12] = true;
4679 }
4680 }
4681
4682 if(msg.str().length() == 17)
4683 msg << "I don't understand what vocation you would like to be... could you please repeat it?";
4684 }
4685 else if(checkText(text, "yes") && talkState[12])
4686 {
4687 if(!IOLoginData::getInstance()->playerExists(managerString, true))
4688 {
4689 talkState[1] = true;
4690 for(int8_t i = 2; i <= 12; i++)
4691 talkState[i] = false;
4692
4693 if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4694 msg << "Your character has been created.";
4695 else
4696 msg << "Your character couldn't be created, please try again.";
4697 }
4698 else
4699 {
4700 talkState[6] = true;
4701 talkState[9] = false;
4702 msg << "A player with that name already exists, please choose another name.";
4703 }
4704 }
4705 else if(checkText(text, "no") && talkState[12])
4706 {
4707 talkState[11] = true;
4708 talkState[12] = false;
4709 msg << "No? Then what would you like to be?";
4710 }
4711 else if(checkText(text, "recovery key") && talkState[1])
4712 {
4713 talkState[1] = false;
4714 talkState[10] = true;
4715 msg << "Would you like a recovery key?";
4716 }
4717 else if(checkText(text, "yes") && talkState[10])
4718 {
4719 if(account.recoveryKey != "0")
4720 msg << "Sorry, you already have a recovery key, for security reasons I may not give you a new one.";
4721 else
4722 {
4723 managerString = generateRecoveryKey(4, 4);
4724 IOLoginData::getInstance()->setRecoveryKey(managerNumber, managerString);
4725 msg << "Your recovery key is: " << managerString << ".";
4726 }
4727
4728 talkState[1] = true;
4729 for(int8_t i = 2; i <= 12; i++)
4730 talkState[i] = false;
4731 }
4732 else if(checkText(text, "no") && talkState[10])
4733 {
4734 msg << "Then not.";
4735 talkState[1] = true;
4736 for(int8_t i = 2; i <= 12; i++)
4737 talkState[i] = false;
4738 }
4739 else
4740 msg << "Please read the latest message that I have specified, I don't understand the current requested action.";
4741
4742 break;
4743 }
4744 case MANAGER_NEW:
4745 {
4746 if(checkText(text, "account") && !talkState[1])
4747 {
4748 msg << "What would you like your password to be?";
4749 talkState[1] = true;
4750 talkState[2] = true;
4751 }
4752 else if(talkState[2])
4753 {
4754 std::string tmp = text;
4755 trimString(tmp);
4756 if(tmp.length() < 6)
4757 msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4758 else if(!isValidPassword(tmp))
4759 msg << "Your password contains invalid characters... please tell me another one.";
4760 else
4761 {
4762 talkState[3] = true;
4763 talkState[2] = false;
4764 managerString = tmp;
4765 msg << managerString << " is it? 'yes' or 'no'?";
4766 }
4767 }
4768 else if(checkText(text, "yes") && talkState[3])
4769 {
4770 if(g_config.getBool(ConfigManager::GENERATE_ACCOUNT_NUMBER))
4771 {
4772 do
4773 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));
4774 while(IOLoginData::getInstance()->accountNameExists(managerChar));
4775
4776 uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4777 if(id)
4778 {
4779 accountManager = MANAGER_ACCOUNT;
4780 managerNumber = id;
4781
4782 noSwap = talkState[1] = false;
4783 msg << "Your account has been created, you may manage it now, but remember your account name: '"
4784 << managerChar << "' and password: '" << managerString
4785 << "'! If the account name is too hard to remember, please note it somewhere.";
4786 }
4787 else
4788 msg << "Your account could not be created, please try again.";
4789
4790 for(int8_t i = 2; i <= 5; i++)
4791 talkState[i] = false;
4792 }
4793 else
4794 {
4795 msg << "What would you like your account name to be?";
4796 talkState[3] = false;
4797 talkState[4] = true;
4798 }
4799 }
4800 else if(checkText(text, "no") && talkState[3])
4801 {
4802 talkState[2] = true;
4803 talkState[3] = false;
4804 msg << "What would you like your password to be then?";
4805 }
4806 else if(talkState[4])
4807 {
4808 std::string tmp = text;
4809 trimString(tmp);
4810 if(tmp.length() < 3)
4811 msg << "That account name is too short, at least 3 digits are required. Please select a longer account name.";
4812 else if(tmp.length() > 25)
4813 msg << "That account name is too long, not more than 25 digits are required. Please select a shorter account name.";
4814 else if(!isValidAccountName(tmp))
4815 msg << "Your account name contains invalid characters, please choose another one.";
4816 else if(asLowerCaseString(tmp) == asLowerCaseString(managerString))
4817 msg << "Your account name cannot be same as password, please choose another one.";
4818 else
4819 {
4820 sprintf(managerChar, "%s", tmp.c_str());
4821 msg << managerChar << ", are you sure?";
4822 talkState[4] = false;
4823 talkState[5] = true;
4824 }
4825 }
4826 else if(checkText(text, "yes") && talkState[5])
4827 {
4828 if(!IOLoginData::getInstance()->accountNameExists(managerChar))
4829 {
4830 uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4831 if(id)
4832 {
4833 accountManager = MANAGER_ACCOUNT;
4834 managerNumber = id;
4835
4836 noSwap = talkState[1] = false;
4837 msg << "Your account has been created, you may manage it now, but remember your account name: '"
4838 << managerChar << "' and password: '" << managerString << "'!";
4839 }
4840 else
4841 msg << "Your account could not be created, please try again.";
4842
4843 for(int8_t i = 2; i <= 5; i++)
4844 talkState[i] = false;
4845 }
4846 else
4847 {
4848 msg << "An account with that name already exists, please try another account name.";
4849 talkState[4] = true;
4850 talkState[5] = false;
4851 }
4852 }
4853 else if(checkText(text, "no") && talkState[5])
4854 {
4855 talkState[5] = false;
4856 talkState[4] = true;
4857 msg << "What else would you like as your account name?";
4858 }
4859 else if(checkText(text, "recover") && !talkState[6])
4860 {
4861 talkState[6] = true;
4862 talkState[7] = true;
4863 msg << "What was your account name?";
4864 }
4865 else if(talkState[7])
4866 {
4867 managerString = text;
4868 if(IOLoginData::getInstance()->getAccountId(managerString, (uint32_t&)managerNumber))
4869 {
4870 talkState[7] = false;
4871 talkState[8] = true;
4872 msg << "What was your recovery key?";
4873 }
4874 else
4875 {
4876 msg << "Sorry, but account with such name doesn't exists.";
4877 talkState[6] = talkState[7] = false;
4878 }
4879 }
4880 else if(talkState[8])
4881 {
4882 managerString2 = text;
4883 if(IOLoginData::getInstance()->validRecoveryKey(managerNumber, managerString2) && managerString2 != "0")
4884 {
4885 sprintf(managerChar, "%s%d", g_config.getString(ConfigManager::SERVER_NAME).c_str(), random_range(100, 999));
4886 IOLoginData::getInstance()->setPassword(managerNumber, managerChar);
4887 msg << "Correct! Your new password is: " << managerChar << ".";
4888 }
4889 else
4890 msg << "Sorry, but this key doesn't match to account you gave me.";
4891
4892 talkState[7] = talkState[8] = false;
4893 }
4894 else
4895 msg << "Sorry, but I can't understand you, please try to repeat that.";
4896
4897 break;
4898 }
4899 default:
4900 return;
4901 break;
4902 }
4903
4904 sendTextMessage(MSG_STATUS_CONSOLE_BLUE, msg.str().c_str());
4905 if(!noSwap)
4906 sendTextMessage(MSG_STATUS_CONSOLE_ORANGE, "Hint: Type 'account' to manage your account and if you want to start over then type 'cancel'.");
4907}
4908
4909bool Player::isGuildInvited(uint32_t guildId) const
4910{
4911 for(InvitedToGuildsList::const_iterator it = invitedToGuildsList.begin(); it != invitedToGuildsList.end(); ++it)
4912 {
4913 if((*it) == guildId)
4914 return true;
4915 }
4916
4917 return false;
4918}
4919
4920void Player::leaveGuild()
4921{
4922 sendClosePrivate(CHANNEL_GUILD);
4923 guildLevel = GUILDLEVEL_NONE;
4924 guildId = rankId = 0;
4925 guildName = rankName = guildNick = "";
4926}
4927
4928bool Player::isPremium() const
4929{
4930 if(g_config.getBool(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium))
4931 return true;
4932
4933 return premiumDays;
4934}
4935
4936bool Player::setGuildLevel(GuildLevel_t newLevel, uint32_t rank/* = 0*/)
4937{
4938 std::string name;
4939 if(!IOGuild::getInstance()->getRankEx(rank, name, guildId, newLevel))
4940 return false;
4941
4942 guildLevel = newLevel;
4943 rankName = name;
4944 rankId = rank;
4945 return true;
4946}
4947
4948void Player::setGroupId(int32_t newId)
4949{
4950 if(Group* tmp = Groups::getInstance()->getGroup(newId))
4951 {
4952 groupId = newId;
4953 group = tmp;
4954 }
4955}
4956
4957void Player::setGroup(Group* newGroup)
4958{
4959 if(newGroup)
4960 {
4961 group = newGroup;
4962 groupId = group->getId();
4963 }
4964}
4965
4966PartyShields_t Player::getPartyShield(const Creature* creature) const
4967{
4968 const Player* player = creature->getPlayer();
4969 if(!player)
4970 return Creature::getPartyShield(creature);
4971
4972 if(Party* party = getParty())
4973 {
4974 if(party->getLeader() == player)
4975 {
4976 if(party->isSharedExperienceActive())
4977 {
4978 if(party->isSharedExperienceEnabled())
4979 return SHIELD_YELLOW_SHAREDEXP;
4980
4981 if(party->canUseSharedExperience(player))
4982 return SHIELD_YELLOW_NOSHAREDEXP;
4983
4984 return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
4985 }
4986
4987 return SHIELD_YELLOW;
4988 }
4989
4990 if(party->isPlayerMember(player))
4991 {
4992 if(party->isSharedExperienceActive())
4993 {
4994 if(party->isSharedExperienceEnabled())
4995 return SHIELD_BLUE_SHAREDEXP;
4996
4997 if(party->canUseSharedExperience(player))
4998 return SHIELD_BLUE_NOSHAREDEXP;
4999
5000 return SHIELD_BLUE_NOSHAREDEXP_BLINK;
5001 }
5002
5003 return SHIELD_BLUE;
5004 }
5005
5006 if(isInviting(player))
5007 return SHIELD_WHITEBLUE;
5008 }
5009
5010 if(player->isInviting(this))
5011 return SHIELD_WHITEYELLOW;
5012
5013 return SHIELD_NONE;
5014}
5015
5016bool Player::isInviting(const Player* player) const
5017{
5018 if(!player || !getParty() || getParty()->getLeader() != this)
5019 return false;
5020
5021 return getParty()->isPlayerInvited(player);
5022}
5023
5024bool Player::isPartner(const Player* player) const
5025{
5026 if(!player || !getParty() || !player->getParty())
5027 return false;
5028
5029 return (getParty() == player->getParty());
5030}
5031
5032void Player::sendPlayerPartyIcons(Player* player)
5033{
5034 sendCreatureShield(player);
5035 sendCreatureSkull(player);
5036}
5037
5038bool Player::addPartyInvitation(Party* party)
5039{
5040 if(!party)
5041 return false;
5042
5043 PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5044 if(it != invitePartyList.end())
5045 return false;
5046
5047 invitePartyList.push_back(party);
5048 return true;
5049}
5050
5051bool Player::removePartyInvitation(Party* party)
5052{
5053 if(!party)
5054 return false;
5055
5056 PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5057 if(it != invitePartyList.end())
5058 {
5059 invitePartyList.erase(it);
5060 return true;
5061 }
5062 return false;
5063}
5064
5065void Player::clearPartyInvitations()
5066{
5067 if(invitePartyList.empty())
5068 return;
5069
5070 PartyList list;
5071 for(PartyList::iterator it = invitePartyList.begin(); it != invitePartyList.end(); ++it)
5072 list.push_back(*it);
5073
5074 invitePartyList.clear();
5075 for(PartyList::iterator it = list.begin(); it != list.end(); ++it)
5076 (*it)->removeInvite(this);
5077}
5078
5079void Player::increaseCombatValues(int32_t& min, int32_t& max, bool useCharges, bool countWeapon)
5080{
5081 if(min > 0)
5082 min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_HEALING));
5083 else
5084 min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_MAGIC));
5085
5086 if(max > 0)
5087 max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_HEALING));
5088 else
5089 max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_MAGIC));
5090
5091 /*Item* weapon = NULL;
5092 if(!countWeapon)
5093 weapon = getWeapon();
5094
5095 Item* item = NULL;
5096 int32_t minValue = 0, maxValue = 0;
5097 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5098 {
5099 if(!(item = getInventoryItem((slots_t)i)) || (g_moveEvents->hasEquipEvent(item)
5100 && !isItemAbilityEnabled((slots_t)i)))
5101 continue;
5102
5103 const ItemType& it = Item::items[item->getID()];
5104 if(min > 0)
5105 {
5106 minValue += it.abilities.increment[HEALING_VALUE];
5107 if(it.abilities.increment[HEALING_PERCENT])
5108 min = (int32_t)std::ceil((double)(min * it.abilities.increment[HEALING_PERCENT]) / 100.);
5109 }
5110 else
5111 {
5112 minValue -= it.abilities.increment[MAGIC_VALUE];
5113 if(it.abilities.increment[MAGIC_PERCENT])
5114 min = (int32_t)std::ceil((double)(min * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5115 }
5116
5117 if(max > 0)
5118 {
5119 maxValue += it.abilities.increment[HEALING_VALUE];
5120 if(it.abilities.increment[HEALING_PERCENT])
5121 max = (int32_t)std::ceil((double)(max * it.abilities.increment[HEALING_PERCENT]) / 100.);
5122 }
5123 else
5124 {
5125 maxValue -= it.abilities.increment[MAGIC_VALUE];
5126 if(it.abilities.increment[MAGIC_PERCENT])
5127 max = (int32_t)std::ceil((double)(max * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5128 }
5129
5130 bool removeCharges = false;
5131 for(int32_t j = INCREMENT_FIRST; j <= INCREMENT_LAST; ++j)
5132 {
5133 if(!it.abilities.increment[(Increment_t)j])
5134 continue;
5135
5136 removeCharges = true;
5137 break;
5138 }
5139
5140 if(useCharges && removeCharges && item != weapon && item->hasCharges())
5141 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5142 }
5143
5144 min += minValue;
5145 max += maxValue;*/
5146}
5147
5148bool Player::transferMoneyTo(const std::string& name, uint64_t amount)
5149{
5150 if(!g_config.getBool(ConfigManager::BANK_SYSTEM) || amount > balance)
5151 return false;
5152
5153 Player* target = g_game.getPlayerByNameEx(name);
5154 if(!target)
5155 return false;
5156
5157 balance -= amount;
5158 target->balance += amount;
5159 if(target->isVirtual())
5160 {
5161 IOLoginData::getInstance()->savePlayer(target);
5162 delete target;
5163 }
5164
5165 return true;
5166}
5167
5168void Player::sendCritical() const
5169{
5170 if(g_config.getBool(ConfigManager::DISPLAY_CRITICAL_HIT))
5171 g_game.addAnimatedText(getPosition(), TEXTCOLOR_DARKRED, "CRITICAL!");
5172}
5173
5174void Player::setSetsAttributesList(uint32_t setId, ItemAttributes_t type, int32_t value, int32_t attrType)
5175{
5176 setsAttributesList[setId][type][attrType] += value;
5177}
5178
5179int32_t Player::getAllPlayerBonusType(ItemAttributes_t type, bool ignoreWeapons/*= false*/, bool useCharges/*= false*/) const
5180{
5181 int32_t bonusValue = getSetsAttribute(type);
5182 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5183 {
5184 if(i == SLOT_AMMO)
5185 continue;
5186
5187 Item* item = getInventoryItem((slots_t)i);
5188 if(item)
5189 {
5190 if(ignoreWeapons && item->isWeapon())
5191 continue;
5192
5193 const ItemType& it = Item::items[item->getID()];
5194 int32_t value = 0;
5195 if(item->isLegendary(it) && it.legendaryBonus.attributes[type])
5196 value += it.legendaryBonus.attributes[type];
5197
5198 if(value != 0 && useCharges && item->hasCharges() && type > ITEM_ATTACK_SPEED)
5199 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5200
5201 bonusValue += value;
5202 }
5203 }
5204 return bonusValue;
5205}
5206
5207int32_t Player::getSetsAttribute(ItemAttributes_t type, int32_t attrType) const
5208{
5209 int32_t value = 0;
5210 std::string key = getAttributeName(type);
5211 if(!key.empty())
5212 {
5213 key.clear();
5214 switch(type)
5215 {
5216 case ITEM_MONSTER_DEFENSE:
5217 case ITEM_MONSTER_DAMAGE:
5218 key = getMonsterClassName((MonsterClass_t)attrType);
5219 break;
5220 case ITEM_ABSORB:
5221 key = getCombatName((CombatType_t)attrType) + "Absorb";
5222 break;
5223 case ITEM_INCREMENT:
5224 key = getCombatName((CombatType_t)attrType) + "Increment";
5225 break;
5226 case ITEM_SKILL:
5227 key = getSkillName(attrType, false);
5228 break;
5229 case ITEM_REGENERATION:
5230 {
5231 if(attrType == STAT_MAXMANA)
5232 key = "mana";
5233 else if(attrType == STAT_MAXHEALTH)
5234 key = "health";
5235 }
5236 default:
5237 break;
5238 }
5239
5240 if(!key.empty())
5241 {
5242 key += "Upgrade";
5243
5244 for(uint16_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5245 {
5246 Item* item = getInventoryItem((slots_t)slot);
5247 if(!item)
5248 continue;
5249
5250 const int32_t* v = item->getIntegerAttribute(key);
5251 if(v && *v != 0)
5252 value += *v;
5253 }
5254 }
5255 }
5256
5257 /*if(setsAttributesList.empty())
5258 return value;
5259
5260 for(SetsAttributesList::const_iterator it = setsAttributesList.begin(); it != setsAttributesList.end(); ++it)
5261 {
5262 if(it->second.empty())
5263 continue;
5264
5265 for(AttributesListMap::const_iterator _it = it->second.begin(); _it != it->second.end(); ++_it)
5266 {
5267 if(_it->first != type)
5268 continue;
5269
5270 for(std::map<int32_t, int32_t>::const_iterator __it = _it->second.begin(); __it != _it->second.end(); ++__it)
5271 {
5272 if(__it->first != attrType)
5273 continue;
5274
5275 value += __it->second;
5276 }
5277 }
5278 }*/
5279
5280 return value;
5281}
5282
5283void Player::executeAbsorb(CombatType_t combatType, int32_t& damage)
5284{
5285 int32_t _value = getSetsAttribute(ITEM_ABSORB, combatType);
5286 if(_value != 0)
5287 damage -= (int32_t)std::ceil((double)(damage * _value) / 100.);
5288
5289 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5290 {
5291 Item* item = getInventoryItem((slots_t)slot);
5292 if(!item)
5293 continue;
5294
5295 const ItemType& it = Item::items[item->getID()];
5296 if(it.abilities.absorb[combatType])
5297 {
5298 damage -= (int32_t)std::ceil((double)(damage * it.abilities.absorb[combatType]) / 100.);
5299 if(item->hasCharges())
5300 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5301 }
5302
5303 if(item->isLegendary(it) && it.legendaryBonus.absorb[combatType] != 0)
5304 {
5305 int32_t absorbValue = it.legendaryBonus.absorb[combatType];
5306 if(absorbValue > 100)
5307 absorbValue = (absorbValue - 100) - ((absorbValue - 100) * 2);
5308
5309 damage -= (int32_t)std::ceil((double)(damage * absorbValue) / 100);
5310 }
5311 }
5312}
5313
5314int32_t Player::checkMonsterClassIncrement(std::string name) const
5315{
5316 MonsterClass_t monsterClass = getMonsterClass(name);
5317 int32_t bonusIncrease = getSetsAttribute(ITEM_MONSTER_DAMAGE, monsterClass);
5318 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5319 {
5320 if(slot == SLOT_AMMO)
5321 continue;
5322
5323 Item* item = getInventoryItem((slots_t)slot);
5324 if(!item)
5325 continue;
5326
5327 const ItemType& it = Item::items[item->getID()];
5328 int32_t value = 0;
5329 if(item->isLegendary(it) && it.legendaryBonus.monsterIncrement[monsterClass] != 0)
5330 value += it.legendaryBonus.monsterIncrement[monsterClass];
5331
5332 if(value != 0 && item->hasCharges())
5333 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5334
5335 if(value != 0)
5336 bonusIncrease += value;
5337 }
5338
5339 return std::max(-95, bonusIncrease);
5340}
5341
5342int32_t Player::checkMonsterClassDefense(std::string name) const
5343{
5344 MonsterClass_t monsterClass = getMonsterClass(name);
5345 int32_t bonusDefense = getSetsAttribute(ITEM_MONSTER_DEFENSE, monsterClass);
5346 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5347 {
5348 if(slot == SLOT_AMMO)
5349 continue;
5350
5351 Item* item = getInventoryItem((slots_t)slot);
5352 if(!item)
5353 continue;
5354
5355 const ItemType& it = Item::items[item->getID()];
5356 int32_t value = 0;
5357 if(item->isLegendary(it) && it.legendaryBonus.defenseMonsterAttack[monsterClass] != 0)
5358 value += it.legendaryBonus.defenseMonsterAttack[monsterClass];
5359
5360 if(value != 0 && item->hasCharges())
5361 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5362
5363 if(value != 0)
5364 bonusDefense += value;
5365 }
5366
5367 return std::max(-95, bonusDefense);
5368}
5369
5370int32_t Player::getBonusDropItem(uint16_t id) const
5371{
5372 int32_t bonusDrop = 0;
5373 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5374 {
5375 if(i == SLOT_AMMO)
5376 continue;
5377
5378 Item* item = getInventoryItem((slots_t)i);
5379 if(item)
5380 {
5381 const ItemType& it = Item::items[item->getID()];
5382 if(item->isLegendary(it))
5383 {
5384 StringVec vec = explodeString(it.legendaryBonus.listItems, ";");
5385 for(uint16_t i = 0; i < (vec.size() / 2); ++i)
5386 {
5387 if(vec[i * 2].empty())
5388 continue;
5389
5390 if(atoi(vec[i * 2].c_str()) == id)
5391 bonusDrop += atoi(vec[i * 2 + 1].c_str());
5392 }
5393 }
5394 }
5395 }
5396 return bonusDrop;
5397}
5398
5399void Player::checkDeathHit(Creature* target)
5400{
5401 if(random_range(1, 100000) > getAllPlayerBonusType(ITEM_DEATH_HIT) || !target || target->isImmune(COMBAT_DEATHDAMAGE))
5402 return;
5403
5404 CombatParams param;
5405 param.combatType = COMBAT_DEATHDAMAGE;
5406 int32_t health = -target->getHealth();
5407 Combat::doCombatHealth(this, target, health, health, param);
5408
5409 g_game.addMagicEffect(target->getPosition(), MAGIC_EFFECT_MORT_AREA);
5410}
5411
5412void Player::updateKillAchievement(Creature* target)
5413{
5414 if(target->getPlayer())
5415 {
5416 if(target == this)
5417 updateAchievement(ACHIEVEMENT_KILL_YOURSELF);
5418 else
5419 {
5420 updateAchievement(ACHIEVEMENT_KILL_1_PLAYER);
5421 updateAchievement(ACHIEVEMENT_KILL_25_PLAYER);
5422 updateAchievement(ACHIEVEMENT_KILL_100_PLAYER);
5423 updateAchievement(ACHIEVEMENT_KILL_1000_PLAYER);
5424 if(target->getSkull() == SKULL_WHITE)
5425 updateAchievement(ACHIEVEMENT_KILL_1_PLAYER_WITH_WHITE_SKULL);
5426 }
5427 }
5428 else if(target->getMonster())
5429 {
5430 updateAchievement(ACHIEVEMENT_KILL_100_MONSTER);
5431 updateAchievement(ACHIEVEMENT_KILL_10000_MONSTER);
5432 updateAchievement(ACHIEVEMENT_KILL_100000_MONSTER);
5433 const std::string name = target->getName();
5434 if(name == "Wolf")
5435 updateAchievement(ACHIEVEMENT_KILL_WOLF);
5436 else if(name == "Eventador")
5437 updateAchievement(ACHIEVEMENT_KILL_EVENTADOR);
5438 }
5439}
5440
5441void Player::addAchievementPoints(AchievementBonus_t key, uint16_t strength)
5442{
5443 if(achievementPoints < strength)
5444 {
5445 sendTextMessage(MSG_INFO_DESCR, "You don't have enough achievement points!");
5446 return;
5447 }
5448
5449 uint16_t max = 0;
5450 switch(key)
5451 {
5452 case ACHIEVEMENT_STRENGTH:
5453 case ACHIEVEMENT_POWER:
5454 case ACHIEVEMENT_WISDOM:
5455 max = 50;
5456 break;
5457
5458 case ACHIEVEMENT_DEFENSE:
5459 max = 20;
5460 break;
5461
5462 case ACHIEVEMENT_AGILITY:
5463 max = 30;
5464 break;
5465
5466 case ACHIEVEMENT_HEALTH:
5467 max = 100;
5468 break;
5469
5470 default:
5471 return;
5472 }
5473
5474 if(achievementBonus[key] + strength >= max)
5475 {
5476 sendTextMessage(MSG_INFO_DESCR, "You reached maximum points of this skill.");
5477 return;
5478 }
5479
5480 achievementPoints -= strength;
5481 achievementBonus[key] += strength;
5482 if(key == ACHIEVEMENT_HEALTH)
5483 sendStats();
5484
5485 std::string description = "Your ";
5486 switch(key)
5487 {
5488 case ACHIEVEMENT_STRENGTH:
5489 description += "strength";
5490 break;
5491
5492 case ACHIEVEMENT_POWER:
5493 description += "power";
5494 break;
5495
5496 case ACHIEVEMENT_WISDOM:
5497 description += "wisdom";
5498 break;
5499
5500 case ACHIEVEMENT_DEFENSE:
5501 description += "defense";
5502 break;
5503
5504 case ACHIEVEMENT_AGILITY:
5505 description += "agility";
5506 break;
5507
5508 case ACHIEVEMENT_HEALTH:
5509 description += "health";
5510 break;
5511
5512 default:
5513 return;
5514 }
5515
5516 std::ostringstream ss;
5517 ss << achievementBonus[key];
5518 description += " grow. Your current level is " + ss.str() + ".";
5519 sendTextMessage(MSG_INFO_DESCR, description);
5520}
5521
5522void Player::clearAchievementPoints()
5523{
5524 memset(achievementBonus, 0, sizeof(achievementBonus));
5525 sendStats();
5526
5527 uint16_t points = 0;
5528 for(uint16_t id = ACHIEVEMENT_FIRST; id <= ACHIEVEMENT_LAST; ++id)
5529 {
5530 if(achievements->gotAchievement((Achievement_t)id))
5531 points += achievements->getPoints((Achievement_t)id);
5532 }
5533
5534 std::ostringstream ss;
5535 ss << points;
5536 sendTextMessage(MSG_INFO_DESCR, "You reset your achievement points! Now you have " + ss.str() + " achievement points!");
5537}
5538
5539void Player::onSteal(Creature* creature, CombatType_t type, int32_t& damage)
5540{
5541 if(!creature)
5542 return;
5543
5544 if((type == COMBAT_LIFEDRAIN && getHealth() >= getMaxHealth()) || (type == COMBAT_MANADRAIN && getMana() >= getMaxMana()))
5545 return;
5546
5547 uint32_t count = 0;
5548 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5549 {
5550 if(i == SLOT_AMMO)
5551 continue;
5552
5553 Item* item = getInventoryItem((slots_t)i);
5554 if(!item)
5555 continue;
5556
5557 const ItemType& it = Item::items[item->getID()];
5558 if(!it.legendaryItem)
5559 continue;
5560
5561 if(type == COMBAT_LIFEDRAIN)
5562 {
5563 if(random_range(1, 100) <= it.legendaryBonus.attributes[ITEM_CHANCE_STOLEN_LIFE])
5564 {
5565 count += it.legendaryBonus.attributes[ITEM_COUNT_STOLEN_LIFE];
5566 count += it.legendaryBonus.attributes[ITEM_PERCENT_STOLEN_LIFE] * damage / 100;
5567 }
5568 }
5569 else
5570 {
5571 if(random_range(1, 100) <= it.legendaryBonus.attributes[ITEM_CHANCE_STOLEN_MANA])
5572 {
5573 count += it.legendaryBonus.attributes[ITEM_COUNT_STOLEN_MANA];
5574 count += it.legendaryBonus.attributes[ITEM_PERCENT_STOLEN_MANA] * damage / 100;
5575 }
5576 }
5577
5578 if(count != 0 && item->hasCharges())
5579 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5580 }
5581
5582 if(count == 0)
5583 return;
5584
5585 std::stringstream buffer;
5586 buffer << "You steal your opponent " << count << " point" << (count > 1 ? "s" : "");
5587
5588 if(type == COMBAT_LIFEDRAIN)
5589 {
5590 CombatParams params;
5591 params.combatType = COMBAT_HEALING;
5592 params.effects.impact = MAGIC_EFFECT_WRAPS_RED;
5593 params.effects.hit = MAGIC_EFFECT_WRAPS_RED;
5594
5595 Combat::doCombatHealth(NULL, this, count, count, params);
5596 buffer << " of life.";
5597 }
5598 else if(type == COMBAT_MANADRAIN)
5599 {
5600 CombatParams params;
5601 params.effects.impact = MAGIC_EFFECT_WRAPS_BLUE;
5602 params.effects.hit = MAGIC_EFFECT_WRAPS_BLUE;
5603
5604 Combat::doCombatMana(NULL, this, count, count, params);
5605 buffer << " of mana.";
5606 }
5607
5608 sendTextMessage(MSG_EVENT_DEFAULT, buffer.str());
5609}
5610
5611void Player::onParalyze(Creature* creature)
5612{
5613 if(!creature)
5614 return;
5615
5616 int32_t count = 0;
5617 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5618 {
5619 if(i == SLOT_AMMO)
5620 continue;
5621
5622 Item* item = getInventoryItem((slots_t)i);
5623 if(!item)
5624 continue;
5625
5626 const ItemType& it = Item::items[item->getID()];
5627 if(!it.legendaryItem)
5628 continue;
5629
5630 if(random_range(1, 100) <= it.legendaryBonus.attributes[ITEM_CHANCE_PARALYZE])
5631 count = std::max<uint32_t>(count, it.legendaryBonus.attributes[ITEM_COUNT_PARALYZE]);
5632 }
5633
5634 if(count == 0)
5635 return;
5636
5637 if(ConditionSpeed* condition = dynamic_cast<ConditionSpeed*>(Condition::createCondition(
5638 CONDITIONID_COMBAT, CONDITION_PARALYZE, 2000)))
5639 {
5640 condition->setFormulaVars((-count / 100.), 0, (-count / 100.), 0);
5641 creature->addCondition(condition);
5642 g_game.addMagicEffect(creature->getPosition(), MAGIC_EFFECT_BATS);
5643 }
5644}
5645
5646void Player::onStun(Creature* creature)
5647{
5648 if(!creature)
5649 return;
5650
5651 uint32_t duration = 0;
5652 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5653 {
5654 if(i == SLOT_AMMO)
5655 continue;
5656
5657 Item* item = getInventoryItem((slots_t)i);
5658 if(!item)
5659 continue;
5660
5661 const ItemType& it = Item::items[item->getID()];
5662 if(!it.legendaryItem)
5663 continue;
5664
5665 if(random_range(1, 100) <= it.legendaryBonus.attributes[ITEM_CHANCE_STUN])
5666 duration = std::max<uint32_t>(duration, it.legendaryBonus.attributes[ITEM_COUNT_STUN]);
5667 }
5668
5669 if(duration == 0)
5670 return;
5671
5672 if(ConditionAttributes* condition = dynamic_cast<ConditionAttributes*>(Condition::createCondition(
5673 CONDITIONID_COMBAT, CONDITION_ATTRIBUTES, duration, 0, false, SUBID_STUNNED)))
5674 {
5675 creature->addCondition(condition);
5676 g_game.addMagicEffect(creature->getPosition(), MAGIC_EFFECT_STUN);
5677 creature->setStunned(MAGIC_EFFECT_STUN, false);
5678 }
5679}