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