· 8 years ago · Jul 26, 2018, 12:18 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 if(timeNow - lastPing >= 5000)
1777 {
1778 lastPing = timeNow;
1779 if(client)
1780 client->sendPing();
1781 else if(g_config.getBool(ConfigManager::STOP_ATTACK_AT_EXIT))
1782 setAttackedCreature(NULL);
1783 }
1784
1785 if((timeNow - lastPong) >= 60000 && canLogout(true))
1786 {
1787 if(client)
1788 client->logout(true, true);
1789 else if(g_creatureEvents->playerLogout(this, true))
1790 g_game.removeCreature(this, true);
1791 }
1792
1793 messageTicks += interval;
1794 if(messageTicks >= 1500)
1795 {
1796 messageTicks = 0;
1797 addMessageBuffer();
1798 }
1799}
1800
1801bool Player::isMuted(uint16_t channelId, SpeakClasses type, uint32_t& time)
1802{
1803 time = 0;
1804 if(hasFlag(PlayerFlag_CannotBeMuted))
1805 return false;
1806
1807 /*int32_t muteTicks = 0;
1808 for(ConditionList::iterator it = conditions.begin(); it != conditions.end(); ++it)
1809 {
1810 if((*it)->getType() == CONDITION_MUTED && (*it)->getSubId() == 0 && (*it)->getTicks() > muteTicks)
1811 muteTicks = (*it)->getTicks();
1812 }
1813
1814 time = (uint32_t)muteTicks / 1000;
1815 return time > 0 && type != SPEAK_PRIVATE_PN && (type != SPEAK_CHANNEL_Y || (channelId != CHANNEL_GUILD && !g_chat.isPrivateChannel(channelId)));*/
1816 return false;
1817}
1818
1819void Player::addMessageBuffer()
1820{
1821 if(!hasFlag(PlayerFlag_CannotBeMuted) && g_config.getNumber(
1822 ConfigManager::MAX_MESSAGEBUFFER) != 0 && messageBuffer > 0)
1823 messageBuffer--;
1824}
1825
1826void Player::removeMessageBuffer()
1827{
1828 int32_t maxBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER);
1829 if(!hasFlag(PlayerFlag_CannotBeMuted) && maxBuffer != 0 && messageBuffer <= maxBuffer + 1)
1830 {
1831 if(++messageBuffer > maxBuffer)
1832 {
1833 uint32_t muteCount = 1;
1834 MuteCountMap::iterator it = muteCountMap.find(guid);
1835 if(it != muteCountMap.end())
1836 muteCount = it->second;
1837
1838 uint32_t muteTime = 5 * muteCount * muteCount;
1839 muteCountMap[guid] = muteCount + 1;
1840 if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000))
1841 addCondition(condition);
1842
1843 char buffer[50];
1844 sprintf(buffer, "You are muted for %d seconds.", muteTime);
1845 sendTextMessage(MSG_STATUS_SMALL, buffer);
1846 }
1847 }
1848}
1849
1850void Player::drainHealth(Creature* attacker, CombatType_t combatType, int32_t damage)
1851{
1852 Creature::drainHealth(attacker, combatType, damage);
1853 char buffer[150];
1854 if(attacker)
1855 sprintf(buffer, "You lose %d hitpoint%s due to an attack by %s.", damage, (damage != 1 ? "s" : ""), attacker->getNameDescription().c_str());
1856 else
1857 sprintf(buffer, "You lose %d hitpoint%s.", damage, (damage != 1 ? "s" : ""));
1858
1859 sendStats();
1860 sendTextMessage(MSG_EVENT_DEFAULT, buffer);
1861 updateAchievement(ACHIEVEMENT_OBTAIN_10000_DAMAGE, damage);
1862 updateAchievement(ACHIEVEMENT_OBTAIN_200000_DAMAGE, damage);
1863 updateAchievement(ACHIEVEMENT_OBTAIN_5000000_DAMAGE, damage);
1864}
1865
1866void Player::drainMana(Creature* attacker, CombatType_t combatType, int32_t damage)
1867{
1868 Creature::drainMana(attacker, combatType, damage);
1869 char buffer[150];
1870 if(attacker)
1871 sprintf(buffer, "You lose %d mana blocking an attack by %s.", damage, attacker->getNameDescription().c_str());
1872 else
1873 sprintf(buffer, "You lose %d mana.", damage);
1874
1875 sendStats();
1876 sendTextMessage(MSG_EVENT_DEFAULT, buffer);
1877}
1878
1879void Player::addManaSpent(uint64_t amount, bool useMultiplier/* = true*/)
1880{
1881 if(!amount)
1882 return;
1883
1884 uint64_t currReqMana = vocation->getReqMana(magLevel), nextReqMana = vocation->getReqMana(magLevel + 1);
1885 if(currReqMana > nextReqMana || nextReqMana == 0) //player has reached max magic level
1886 return;
1887
1888 if(useMultiplier)
1889 amount = uint64_t((double)amount * rates[SKILL__MAGLEVEL] * g_config.getDouble(ConfigManager::RATE_MAGIC));
1890
1891 bool advance = false;
1892 while(manaSpent + amount >= nextReqMana)
1893 {
1894 amount -= nextReqMana - manaSpent;
1895 manaSpent = 0;
1896 magLevel++;
1897
1898 char advMsg[50];
1899 sprintf(advMsg, "You advanced to Ninjutsu %d.", magLevel);
1900 sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
1901
1902 advance = true;
1903 CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE);
1904 for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it)
1905 (*it)->executeAdvance(this, SKILL__MAGLEVEL, (magLevel - 1), magLevel);
1906
1907 currReqMana = nextReqMana;
1908 nextReqMana = vocation->getReqMana(magLevel + 1);
1909 if(currReqMana > nextReqMana)
1910 {
1911 amount = 0;
1912 break;
1913 }
1914 }
1915
1916 if(amount)
1917 manaSpent += amount;
1918
1919 uint32_t newPercent = Player::getPercentLevel(manaSpent, nextReqMana);
1920 if(magLevelPercent != newPercent)
1921 {
1922 magLevelPercent = newPercent;
1923 sendStats();
1924 }
1925 else if(advance)
1926 sendStats();
1927
1928 updateAchievement(ACHIEVEMENT_USE_100000_MANA, amount);
1929 updateAchievement(ACHIEVEMENT_USE_500000_MANA, amount);
1930 updateAchievement(ACHIEVEMENT_USE_1000000_MANA, amount);
1931}
1932
1933void Player::addExperience(uint64_t exp)
1934{
1935 uint32_t prevLevel = level;
1936 uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
1937 if(Player::getExpForLevel(level) > nextLevelExp)
1938 {
1939 //player has reached max level
1940 levelPercent = 0;
1941 sendStats();
1942 return;
1943 }
1944
1945 experience += exp;
1946 while(experience >= nextLevelExp)
1947 {
1948 healthMax += vocation->getGain(GAIN_HEALTH);
1949 health += vocation->getGain(GAIN_HEALTH);
1950 manaMax += vocation->getGain(GAIN_MANA);
1951 mana += vocation->getGain(GAIN_MANA);
1952 capacity += vocation->getGainCap();
1953
1954 ++level;
1955 nextLevelExp = Player::getExpForLevel(level + 1);
1956 if(Player::getExpForLevel(level) > nextLevelExp) //player has reached max level
1957 break;
1958 }
1959
1960 if(prevLevel != level)
1961 {
1962 updateBaseSpeed();
1963 setBaseSpeed(getBaseSpeed());
1964
1965 g_game.changeSpeed(this, 0);
1966 g_game.addCreatureHealth(this);
1967 if(getParty())
1968 getParty()->updateSharedExperience();
1969
1970 char advMsg[60];
1971 sprintf(advMsg, "You advanced from Level %d to Level %d.", prevLevel, level);
1972 sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
1973
1974 CreatureEventList advanceEvents = getCreatureEvents(CREATURE_EVENT_ADVANCE);
1975 for(CreatureEventList::iterator it = advanceEvents.begin(); it != advanceEvents.end(); ++it)
1976 (*it)->executeAdvance(this, SKILL__LEVEL, prevLevel, level);
1977
1978 updateAchievement(ACHIEVEMENT_GOT_100_LEVEL, 0, level);
1979 updateAchievement(ACHIEVEMENT_GOT_500_LEVEL, 0, level);
1980 updateAchievement(ACHIEVEMENT_GOT_1000_LEVEL, 0, level);
1981 updateAchievement(ACHIEVEMENT_GOT_2000_LEVEL, 0, level);
1982
1983 if(level % 100 == 0 && (uint32_t)std::max(0, getCreatureIntStorage(LEVEL_STORAGE)) < level)
1984 {
1985 std::stringstream ss;
1986 ss << getName() << " advanced from Level " << prevLevel << " to Level " << level << ".";
1987 g_game.broadcastMessage(ss.str(), MSG_STATUS_WARNING);
1988
1989 setCreatureStorage(LEVEL_STORAGE, level);
1990 }
1991 }
1992
1993 uint64_t currLevelExp = Player::getExpForLevel(level);
1994 nextLevelExp = Player::getExpForLevel(level + 1);
1995 levelPercent = 0;
1996 if(nextLevelExp > currLevelExp)
1997 levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
1998
1999 sendStats();
2000}
2001
2002void Player::removeExperience(uint64_t exp, bool updateStats/* = true*/)
2003{
2004 uint32_t prevLevel = level;
2005 experience -= std::min(exp, experience);
2006 while(level > 1 && experience < Player::getExpForLevel(level))
2007 {
2008 level--;
2009 healthMax = std::max((int32_t)0, (healthMax - (int32_t)vocation->getGain(GAIN_HEALTH)));
2010 manaMax = std::max((int32_t)0, (manaMax - (int32_t)vocation->getGain(GAIN_MANA)));
2011 capacity = std::max((double)0, (capacity - (double)vocation->getGainCap()));
2012 }
2013
2014 if(prevLevel != level)
2015 {
2016 if(updateStats)
2017 {
2018 updateBaseSpeed();
2019 setBaseSpeed(getBaseSpeed());
2020
2021 g_game.changeSpeed(this, 0);
2022 g_game.addCreatureHealth(this);
2023 }
2024
2025 char advMsg[90];
2026 sprintf(advMsg, "You were downgraded from Level %d to Level %d.", prevLevel, level);
2027 sendTextMessage(MSG_EVENT_ADVANCE, advMsg);
2028 }
2029
2030 uint64_t currLevelExp = Player::getExpForLevel(level);
2031 uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
2032 if(nextLevelExp > currLevelExp)
2033 levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
2034 else
2035 levelPercent = 0;
2036
2037 if(updateStats)
2038 sendStats();
2039}
2040
2041uint32_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount)
2042{
2043 if(nextLevelCount > 0)
2044 return std::min((uint32_t)100, std::max((uint32_t)0, uint32_t(count * 100 / nextLevelCount)));
2045
2046 return 0;
2047}
2048
2049void Player::onBlockHit(BlockType_t blockType)
2050{
2051 if(shieldBlockCount > 0)
2052 {
2053 --shieldBlockCount;
2054 if(hasShield())
2055 addSkillAdvance(SKILL_SHIELD, 1);
2056 }
2057}
2058
2059void Player::onAttackedCreatureBlockHit(Creature* target, BlockType_t blockType)
2060{
2061 Creature::onAttackedCreatureBlockHit(target, blockType);
2062 lastAttackBlockType = blockType;
2063 switch(blockType)
2064 {
2065 case BLOCK_NONE:
2066 {
2067 addAttackSkillPoint = true;
2068 bloodHitCount = 30;
2069 shieldBlockCount = 30;
2070 break;
2071 }
2072
2073 case BLOCK_DEFENSE:
2074 case BLOCK_ARMOR:
2075 {
2076 //need to draw blood every 30 hits
2077 if(bloodHitCount > 0)
2078 {
2079 addAttackSkillPoint = true;
2080 --bloodHitCount;
2081 }
2082 else
2083 addAttackSkillPoint = false;
2084
2085 break;
2086 }
2087
2088 default:
2089 {
2090 addAttackSkillPoint = false;
2091 break;
2092 }
2093 }
2094}
2095
2096bool Player::hasShield() const
2097{
2098 bool result = false;
2099 Item* item = getInventoryItem(SLOT_LEFT);
2100 if(item && item->getWeaponType() == WEAPON_SHIELD)
2101 result = true;
2102
2103 item = getInventoryItem(SLOT_RIGHT);
2104 if(item && item->getWeaponType() == WEAPON_SHIELD)
2105 result = true;
2106
2107 return result;
2108}
2109
2110BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
2111 bool checkDefense/* = false*/, bool checkArmor/* = false*/, bool isWeapon/*= false*/)
2112{
2113 damage -= (int32_t)((float)damage * 0.25f);
2114 if(attacker)
2115 {
2116 Player* player = attacker->getPlayer();
2117 if(player && player->getParty() && player->getParty() == getParty())
2118 return BLOCK_IMMUNITY;
2119
2120 Monster* monster = attacker->getMonster();
2121 if(monster)
2122 {
2123 if(monster->isPlayerSummon() && this == monster->getPlayerMaster())
2124 {
2125 damage = 0;
2126 return BLOCK_IMMUNITY;
2127 }
2128 }
2129 }
2130
2131 BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, isWeapon);
2132 if(attacker)
2133 {
2134 int16_t color = g_config.getNumber(ConfigManager::SQUARE_COLOR);
2135 if(color < 0)
2136 color = random_range(0, 255);
2137
2138 sendCreatureSquare(attacker, (SquareColor_t)color);
2139 }
2140
2141 if(blockType != BLOCK_NONE)
2142 return blockType;
2143
2144 if(attacker)
2145 {
2146 Monster* monster = attacker->getMonster();
2147 if(monster)
2148 {
2149 int32_t restOfAbsorb = checkMonsterClassDefense(monster->getMonsterTypes());
2150 if(restOfAbsorb != 0)
2151 damage -= (int32_t)(damage * restOfAbsorb / 100.f);
2152 }
2153 else if(Player* player = attacker->getPlayer())
2154 {
2155 int32_t value = player->getSetsAttribute(ITEM_INCREMENT, combatType);
2156 if(value != 0)
2157 damage += (int32_t)std::ceil((double)(damage * value) / 100.);
2158 }
2159 }
2160
2161 uint32_t defenseMultiplier = transformAttributes[TRANSFORM_DEFENSE] + getAchievementBonus(ACHIEVEMENT_DEFENSE);
2162 if (defenseMultiplier > 0)
2163 damage -= (damage * defenseMultiplier) / 100;
2164
2165 executeAbsorb(combatType, damage);
2166
2167 int32_t _value = getSetsAttribute(ITEM_ABSORB, combatType);
2168 if(_value != 0)
2169 damage -= (int32_t)std::ceil((double)(damage * _value) / 100.);
2170
2171 if(vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE) != 1.0 && combatType != COMBAT_NONE &&
2172 combatType != COMBAT_PHYSICALDAMAGE && combatType != COMBAT_UNDEFINEDDAMAGE &&
2173 combatType != COMBAT_DROWNDAMAGE)
2174 {
2175 uint16_t absorbSpell = getSkill(SKILL_FISH, SKILL_LEVEL) / 20;
2176 if(random_range(1, 100) <= absorbSpell)
2177 {
2178 damage = 0;
2179 return BLOCK_DEFENSE;
2180 }
2181
2182 damage -= (int32_t)std::ceil((double)(damage * vocation->getMultiplier(MULTIPLIER_MAGICDEFENSE)) / 300.);
2183 }
2184
2185 if(damage > 0)
2186 {
2187 uint32_t reduce = getSkill(SKILL_SHIELD, SKILL_LEVEL) / 6;
2188 if(reduce > 0 && combatType == COMBAT_PHYSICALDAMAGE)
2189 {
2190 reduce *= damage / 100;
2191 if(reduce > 0)
2192 {
2193 char buffer[150];
2194 sprintf(buffer, "You absorbed %d by %s.", reduce, (attacker ? attacker->getNameDescription().c_str() : "Unkown"));
2195 sendTextMessage(MSG_STATUS_DEFAULT, buffer);
2196
2197 damage -= (int32_t)reduce;
2198 }
2199 }
2200
2201 Item* item = NULL;
2202 int32_t blocked = 0, reflected = 0;
2203 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
2204 {
2205 if(!(item = getInventoryItem((slots_t)slot)) || (g_moveEvents->hasEquipEvent(item)
2206 && !isItemAbilityEnabled((slots_t)slot)))
2207 continue;
2208
2209 const ItemType& it = Item::items[item->getID()];
2210 if(it.abilities.reflect[REFLECT_PERCENT][combatType] && it.abilities.reflect[REFLECT_CHANCE][combatType] < random_range(0, 100))
2211 {
2212 reflected += (int32_t)std::ceil((double)(damage * it.abilities.reflect[REFLECT_PERCENT][combatType]) / 100.);
2213 if(item->hasCharges() && !it.abilities.absorb[combatType])
2214 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
2215 }
2216 }
2217
2218 if(outfitAttributes)
2219 {
2220 uint32_t tmp = Outfits::getInstance()->getOutfitAbsorb(defaultOutfit.lookType, sex, combatType);
2221 if(tmp)
2222 blocked += (int32_t)std::ceil((double)(damage * tmp) / 100.);
2223
2224 tmp = Outfits::getInstance()->getOutfitReflect(defaultOutfit.lookType, sex, combatType);
2225 if(tmp)
2226 reflected += (int32_t)std::ceil((double)(damage * tmp) / 100.);
2227 }
2228
2229 if(vocation->getAbsorb(combatType))
2230 blocked += (int32_t)std::ceil((double)(damage * vocation->getAbsorb(combatType)) / 100.);
2231
2232 if(vocation->getReflect(combatType))
2233 reflected += (int32_t)std::ceil((double)(damage * vocation->getReflect(combatType)) / 100.);
2234
2235 damage -= blocked;
2236 if(damage <= 0)
2237 {
2238 damage = 0;
2239 blockType = BLOCK_DEFENSE;
2240 }
2241
2242 if(reflected)
2243 {
2244 CombatType_t reflectType = combatType;
2245 if(reflected <= 0)
2246 reflectType = COMBAT_HEALING;
2247
2248 g_game.combatChangeHealth(reflectType, NULL, attacker, -reflected, false);
2249 }
2250 }
2251
2252 return blockType;
2253}
2254
2255uint32_t Player::getIP() const
2256{
2257 if(client)
2258 return client->getIP();
2259
2260 return lastIP;
2261}
2262
2263bool Player::onDeath()
2264{
2265 Item* preventLoss = NULL;
2266 Item* preventDrop = NULL;
2267 if(getZone() == ZONE_PVP)
2268 {
2269 setDropLoot(LOOT_DROP_NONE);
2270 setLossSkill(false);
2271 }
2272 else if(skull < SKULL_RED && g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED)
2273 {
2274 Item* item = NULL;
2275 for(int32_t i = SLOT_FIRST; ((skillLoss || lootDrop == LOOT_DROP_FULL) && i < SLOT_LAST); ++i)
2276 {
2277 item = getInventoryItem((slots_t)i);
2278 if(!item)
2279 continue;
2280
2281 const ItemType& it = Item::items[item->getID()];
2282 if(lootDrop == LOOT_DROP_FULL && it.abilities.preventDrop)
2283 {
2284 setDropLoot(LOOT_DROP_PREVENT);
2285 preventDrop = item;
2286 }
2287
2288 if(skillLoss && !preventLoss && it.abilities.preventLoss)
2289 preventLoss = item;
2290 }
2291 }
2292
2293 // death effect
2294 MagicEffect_t effect = (MagicEffect_t)g_config.getNumber(ConfigManager::DEATH_EFFECT);
2295 if(effect != MAGIC_EFFECT_NONE)
2296 g_game.addMagicEffect(getPosition(), effect);
2297
2298 if(!Creature::onDeath())
2299 {
2300 if(preventDrop)
2301 setDropLoot(LOOT_DROP_FULL);
2302
2303 return false;
2304 }
2305
2306 updateAchievement(ACHIEVEMENT_DIE_1_TIMES);
2307 updateAchievement(ACHIEVEMENT_DIE_10_TIMES);
2308
2309 Transforms* transform = Transforms::getInstance();
2310 if(transform)
2311 {
2312 TransformDeath_t deathAction = transform->getDeathAction(this);
2313 if(deathAction != DEATH_NO_REVERT)
2314 setCreatureStorage(TRANSFORM_STORAGE, (deathAction == DEATH_REVERT_TO_BASE ? 0 : std::max(0, (int32_t)transformId - 1)));
2315 }
2316
2317 if(preventLoss)
2318 {
2319 setLossSkill(false);
2320 if(preventLoss->getCharges() > 1) //weird, but transform failed to remove for some hosters
2321 g_game.transformItem(preventLoss, preventLoss->getID(), std::max(0, ((int32_t)preventLoss->getCharges() - 1)));
2322 else
2323 g_game.internalRemoveItem(NULL, preventDrop);
2324 }
2325
2326 if(preventDrop && preventDrop != preventLoss)
2327 {
2328 if(preventDrop->getCharges() > 1) //weird, but transform failed to remove for some hosters
2329 g_game.transformItem(preventDrop, preventDrop->getID(), std::max(0, ((int32_t)preventDrop->getCharges() - 1)));
2330 else
2331 g_game.internalRemoveItem(NULL, preventDrop);
2332 }
2333
2334 removeConditions(CONDITIONEND_DEATH);
2335 if(lossExperienceStatus && skillLoss)
2336 {
2337 uint64_t lossExperience = getLostExperience();
2338 removeExperience(lossExperience, false);
2339 double percent = 1. - ((double)(experience - lossExperience) / experience);
2340
2341 //Magic level loss
2342 uint32_t sumMana = 0;
2343 uint64_t lostMana = 0;
2344 for(uint32_t i = 1; i <= magLevel; ++i)
2345 sumMana += vocation->getReqMana(i);
2346
2347 sumMana += manaSpent;
2348 lostMana = (uint64_t)std::ceil(sumMana * ((double)(percent * lossPercent[LOSS_MANA]) / 100.));
2349 while(lostMana > manaSpent && magLevel > 0)
2350 {
2351 lostMana -= manaSpent;
2352 manaSpent = vocation->getReqMana(magLevel);
2353 magLevel--;
2354 }
2355
2356 manaSpent -= std::max((int32_t)0, (int32_t)lostMana);
2357 uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
2358 if(nextReqMana > vocation->getReqMana(magLevel))
2359 magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
2360 else
2361 magLevelPercent = 0;
2362
2363 //Skill loss
2364 uint32_t lostSkillTries, sumSkillTries;
2365 for(int16_t i = 0; i < 7; ++i) //for each skill
2366 {
2367 lostSkillTries = sumSkillTries = 0;
2368 for(uint32_t c = 11; c <= skills[i][SKILL_LEVEL]; ++c) //sum up all required tries for all skill levels
2369 sumSkillTries += vocation->getReqSkillTries(i, c);
2370
2371 sumSkillTries += skills[i][SKILL_TRIES];
2372 lostSkillTries = (uint32_t)std::ceil(sumSkillTries * ((double)(percent * lossPercent[LOSS_SKILLS]) / 100.));
2373 while(lostSkillTries > skills[i][SKILL_TRIES])
2374 {
2375 lostSkillTries -= skills[i][SKILL_TRIES];
2376 skills[i][SKILL_TRIES] = vocation->getReqSkillTries(i, skills[i][SKILL_LEVEL]);
2377 if(skills[i][SKILL_LEVEL] < 11)
2378 {
2379 skills[i][SKILL_LEVEL] = 10;
2380 skills[i][SKILL_TRIES] = lostSkillTries = 0;
2381 break;
2382 }
2383 else
2384 skills[i][SKILL_LEVEL]--;
2385 }
2386
2387 skills[i][SKILL_TRIES] = std::max((int32_t)0, (int32_t)(skills[i][SKILL_TRIES] - lostSkillTries));
2388 }
2389
2390 blessings = 0;
2391 loginPosition = masterPosition;
2392 if(!inventory[SLOT_BACKPACK])
2393 __internalAddThing(SLOT_BACKPACK, Item::CreateItem(g_config.getNumber(ConfigManager::DEATH_CONTAINER)));
2394
2395 sendIcons();
2396 sendStats();
2397 sendSkills();
2398
2399 sendReLoginWindow();
2400 g_game.removeCreature(this, false);
2401 }
2402 else
2403 {
2404 setLossSkill(true);
2405 if(preventLoss)
2406 {
2407 loginPosition = masterPosition;
2408 sendReLoginWindow();
2409 g_game.removeCreature(this, false);
2410 }
2411 }
2412
2413 return true;
2414}
2415
2416void Player::dropCorpse(DeathList deathList)
2417{
2418 if(lootDrop == LOOT_DROP_NONE)
2419 {
2420 pzLocked = false;
2421 if(health <= 0)
2422 {
2423 health = healthMax;
2424 mana = manaMax;
2425 }
2426
2427 setDropLoot(LOOT_DROP_FULL);
2428 sendStats();
2429 sendIcons();
2430
2431 onIdleStatus();
2432 g_game.addCreatureHealth(this);
2433 g_game.internalTeleport(this, masterPosition, true);
2434 }
2435 else
2436 {
2437 Creature::dropCorpse(deathList);
2438 if(g_config.getBool(ConfigManager::DEATH_LIST))
2439 IOLoginData::getInstance()->playerDeath(this, deathList);
2440 }
2441}
2442
2443Item* Player::createCorpse(DeathList deathList)
2444{
2445 Item* corpse = Creature::createCorpse(deathList);
2446 if(!corpse)
2447 return NULL;
2448
2449 std::stringstream ss;
2450 ss << "You recognize " << getNameDescription() << ". " << (sex % 2 ? "He" : "She") << " was killed by ";
2451 if(!deathList.empty())
2452 {
2453 if(deathList[0].isCreatureKill())
2454 {
2455 ss << deathList[0].getKillerCreature()->getNameDescription();
2456 if(deathList[0].getKillerCreature()->getMaster())
2457 ss << " summoned by " << deathList[0].getKillerCreature()->getMaster()->getNameDescription();
2458 }
2459 else
2460 ss << deathList[0].getKillerName();
2461 }
2462
2463 if(deathList.size() > 1)
2464 {
2465 if(deathList[0].getKillerType() != deathList[1].getKillerType())
2466 {
2467 if(deathList[1].isCreatureKill())
2468 {
2469 ss << " and by " << deathList[1].getKillerCreature()->getNameDescription();
2470 if(deathList[1].getKillerCreature()->getMaster())
2471 ss << " summoned by " << deathList[1].getKillerCreature()->getMaster()->getNameDescription();
2472 }
2473 else
2474 ss << " and by " << deathList[1].getKillerName();
2475 }
2476 else if(deathList[1].isCreatureKill())
2477 {
2478 if(deathList[0].getKillerCreature()->getName() != deathList[1].getKillerCreature()->getName())
2479 {
2480 ss << " and by " << deathList[1].getKillerCreature()->getNameDescription();
2481 if(deathList[1].getKillerCreature()->getMaster())
2482 ss << " summoned by " << deathList[1].getKillerCreature()->getMaster()->getNameDescription();
2483 }
2484 }
2485 else if(asLowerCaseString(deathList[0].getKillerName()) != asLowerCaseString(deathList[1].getKillerName()))
2486 ss << " and by " << deathList[1].getKillerName();
2487 }
2488
2489 ss << ".";
2490 corpse->setSpecialDescription(ss.str().c_str());
2491 return corpse;
2492}
2493
2494void Player::addExhaust(uint32_t ticks, int32_t type, ConditionType_t conditionType)
2495{
2496 if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, conditionType, ticks, 0, false, type))
2497 addCondition(condition);
2498}
2499
2500void Player::addInFightTicks(bool pzLock/* = false*/)
2501{
2502 if(hasFlag(PlayerFlag_NotGainInFight))
2503 return;
2504
2505 if(pzLock)
2506 pzLocked = true;
2507
2508 if(Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT,
2509 CONDITION_INFIGHT, g_config.getNumber(ConfigManager::PZ_LOCKED)))
2510 addCondition(condition);
2511}
2512
2513void Player::addDefaultRegeneration(uint32_t addTicks)
2514{
2515 Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
2516 if(condition)
2517 condition->setTicks(condition->getTicks() + addTicks);
2518 else if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_REGENERATION, addTicks)))
2519 {
2520 condition->setParam(CONDITIONPARAM_HEALTHGAIN, vocation->getGainAmount(GAIN_HEALTH));
2521 condition->setParam(CONDITIONPARAM_HEALTHTICKS, vocation->getGainTicks(GAIN_HEALTH) * 1000);
2522 condition->setParam(CONDITIONPARAM_MANAGAIN, vocation->getGainAmount(GAIN_MANA));
2523 condition->setParam(CONDITIONPARAM_MANATICKS, vocation->getGainTicks(GAIN_MANA) * 1000);
2524 addCondition(condition);
2525 }
2526}
2527
2528void Player::removeList()
2529{
2530 autoList.erase(id);
2531 if(!isGhost())
2532 {
2533 for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2534 it->second->notifyLogOut(this);
2535 }
2536 else
2537 {
2538 for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2539 {
2540 if(it->second->canSeeCreature(this))
2541 it->second->notifyLogOut(this);
2542 }
2543 }
2544}
2545
2546void Player::addList()
2547{
2548 if(!isGhost())
2549 {
2550 for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2551 it->second->notifyLogIn(this);
2552 }
2553 else
2554 {
2555 for(AutoList<Player>::iterator it = autoList.begin(); it != autoList.end(); ++it)
2556 {
2557 if(it->second->canSeeCreature(this))
2558 it->second->notifyLogIn(this);
2559 }
2560 }
2561
2562 autoList[id] = this;
2563}
2564
2565void Player::kickPlayer(bool displayEffect, bool forceLogout)
2566{
2567 if(!client)
2568 {
2569 if(g_creatureEvents->playerLogout(this, forceLogout))
2570 g_game.removeCreature(this);
2571 }
2572 else
2573 client->logout(displayEffect, forceLogout);
2574}
2575
2576void Player::notifyLogIn(Player* loginPlayer)
2577{
2578 if(!client)
2579 return;
2580
2581 VIPListSet::iterator it = VIPList.find(loginPlayer->getGUID());
2582 if(it != VIPList.end())
2583 client->sendVIPLogIn(loginPlayer->getGUID());
2584}
2585
2586void Player::notifyLogOut(Player* logoutPlayer)
2587{
2588 if(!client)
2589 return;
2590
2591 VIPListSet::iterator it = VIPList.find(logoutPlayer->getGUID());
2592 if(it != VIPList.end())
2593 client->sendVIPLogOut(logoutPlayer->getGUID());
2594}
2595
2596bool Player::removeVIP(uint32_t _guid)
2597{
2598 VIPListSet::iterator it = VIPList.find(_guid);
2599 if(it == VIPList.end())
2600 return false;
2601
2602 VIPList.erase(it);
2603 return true;
2604}
2605
2606bool Player::addVIP(uint32_t _guid, std::string& name, bool isOnline, bool internal/* = false*/)
2607{
2608 if(guid == _guid)
2609 {
2610 if(!internal)
2611 sendTextMessage(MSG_STATUS_SMALL, "You cannot add yourself.");
2612
2613 return false;
2614 }
2615
2616 if(VIPList.size() > (group ? group->getMaxVips(isPremium()) : 20))
2617 {
2618 if(!internal)
2619 sendTextMessage(MSG_STATUS_SMALL, "You cannot add more buddies.");
2620
2621 return false;
2622 }
2623
2624 VIPListSet::iterator it = VIPList.find(_guid);
2625 if(it != VIPList.end())
2626 {
2627 if(!internal)
2628 sendTextMessage(MSG_STATUS_SMALL, "This player is already in your list.");
2629
2630 return false;
2631 }
2632
2633 VIPList.insert(_guid);
2634 if(client && !internal)
2635 client->sendVIP(_guid, name, isOnline);
2636
2637 if(!internal)
2638 {
2639 updateAchievement(ACHIEVEMENT_ADD_1_FRIEND);
2640 updateAchievement(ACHIEVEMENT_ADD_30_FRIEND);
2641 }
2642
2643 return true;
2644}
2645
2646//close container and its child containers
2647void Player::autoCloseContainers(const Container* container)
2648{
2649 typedef std::vector<uint32_t> CloseList;
2650 CloseList closeList;
2651 for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it)
2652 {
2653 Container* tmp = it->second;
2654 while(tmp != NULL)
2655 {
2656 if(tmp->isRemoved() || tmp == container)
2657 {
2658 closeList.push_back(it->first);
2659 break;
2660 }
2661
2662 tmp = dynamic_cast<Container*>(tmp->getParent());
2663 }
2664 }
2665
2666 for(CloseList::iterator it = closeList.begin(); it != closeList.end(); ++it)
2667 {
2668 closeContainer(*it);
2669 if(client)
2670 client->sendCloseContainer(*it);
2671 }
2672}
2673
2674bool Player::hasCapacity(const Item* item, uint32_t count) const
2675{
2676 if(hasFlag(PlayerFlag_CannotPickupItem))
2677 return false;
2678
2679 if(hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this)
2680 return true;
2681
2682 double itemWeight = 0;
2683 if(item->isStackable())
2684 itemWeight = Item::items[item->getID()].weight * count;
2685 else
2686 itemWeight = item->getWeight();
2687
2688 return (itemWeight < getFreeCapacity());
2689}
2690
2691ReturnValue Player::__queryAdd(int32_t index, const Thing* thing, uint32_t count, uint32_t flags) const
2692{
2693 const Item* item = thing->getItem();
2694 if(!item)
2695 return RET_NOTPOSSIBLE;
2696
2697 bool childIsOwner = ((flags & FLAG_CHILDISOWNER) == FLAG_CHILDISOWNER), skipLimit = ((flags & FLAG_NOLIMIT) == FLAG_NOLIMIT);
2698 if(childIsOwner)
2699 {
2700 //a child container is querying the player, just check if enough capacity
2701 if(skipLimit || hasCapacity(item, count))
2702 return RET_NOERROR;
2703
2704 return RET_NOTENOUGHCAPACITY;
2705 }
2706
2707 if(!item->isPickupable())
2708 return RET_CANNOTPICKUP;
2709
2710 ReturnValue ret = RET_NOERROR;
2711 if((item->getSlotPosition() & SLOTP_HEAD) || (item->getSlotPosition() & SLOTP_NECKLACE) ||
2712 (item->getSlotPosition() & SLOTP_BACKPACK) || (item->getSlotPosition() & SLOTP_ARMOR) ||
2713 (item->getSlotPosition() & SLOTP_LEGS) || (item->getSlotPosition() & SLOTP_FEET) ||
2714 (item->getSlotPosition() & SLOTP_RING))
2715 ret = RET_CANNOTBEDRESSED;
2716 else if(item->getSlotPosition() & SLOTP_TWO_HAND)
2717 ret = RET_PUTTHISOBJECTINBOTHHANDS;
2718 else if((item->getSlotPosition() & SLOTP_RIGHT) || (item->getSlotPosition() & SLOTP_LEFT))
2719 ret = RET_PUTTHISOBJECTINYOURHAND;
2720
2721 switch(index)
2722 {
2723 case SLOT_HEAD:
2724 if(item->getSlotPosition() & SLOTP_HEAD)
2725 ret = RET_NOERROR;
2726 break;
2727 case SLOT_NECKLACE:
2728 if(item->getSlotPosition() & SLOTP_NECKLACE)
2729 ret = RET_NOERROR;
2730 break;
2731 case SLOT_BACKPACK:
2732 if(item->getSlotPosition() & SLOTP_BACKPACK)
2733 ret = RET_NOERROR;
2734 break;
2735 case SLOT_ARMOR:
2736 if(item->getSlotPosition() & SLOTP_ARMOR)
2737 ret = RET_NOERROR;
2738 break;
2739 case SLOT_RIGHT:
2740 if(item->getSlotPosition() & SLOTP_RIGHT)
2741 {
2742 //check if we already carry an item in the other hand
2743 if(item->getSlotPosition() & SLOTP_TWO_HAND)
2744 {
2745 if(inventory[SLOT_LEFT] && inventory[SLOT_LEFT] != item)
2746 ret = RET_BOTHHANDSNEEDTOBEFREE;
2747 else
2748 ret = RET_NOERROR;
2749 }
2750 else if(inventory[SLOT_LEFT])
2751 {
2752 const Item* leftItem = inventory[SLOT_LEFT];
2753 WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();
2754 if(type == WEAPON_NONE)
2755 ret = RET_CANONLYUSEONEWEAPON;
2756 else if(leftItem->getSlotPosition() & SLOTP_TWO_HAND)
2757 ret = RET_DROPTWOHANDEDITEM;
2758 else if(item == leftItem && count == item->getItemCount())
2759 ret = RET_NOERROR;
2760 else if(leftType == WEAPON_SHIELD && type == WEAPON_SHIELD)
2761 ret = RET_CANONLYUSEONESHIELD;
2762 else if(!leftItem->isWeapon() || !item->isWeapon() ||
2763 leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO
2764 || type == WEAPON_SHIELD || type == WEAPON_AMMO)
2765 ret = RET_NOERROR;
2766 else
2767 ret = RET_CANONLYUSEONEWEAPON;
2768 }
2769 else if(item->getWeaponType() == WEAPON_NONE)
2770 ret = RET_CANONLYUSEONEWEAPONORSHIELD;
2771 else
2772 ret = RET_NOERROR;
2773 }
2774 break;
2775 case SLOT_LEFT:
2776 if(item->getSlotPosition() & SLOTP_LEFT)
2777 {
2778 //check if we already carry an item in the other hand
2779 if(item->getSlotPosition() & SLOTP_TWO_HAND)
2780 {
2781 if(inventory[SLOT_RIGHT] && inventory[SLOT_RIGHT] != item)
2782 ret = RET_BOTHHANDSNEEDTOBEFREE;
2783 else
2784 ret = RET_NOERROR;
2785 }
2786 else if(inventory[SLOT_RIGHT])
2787 {
2788 const Item* rightItem = inventory[SLOT_RIGHT];
2789 WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();
2790 if(type == WEAPON_NONE)
2791 ret = RET_CANONLYUSEONEWEAPON;
2792 else if(rightItem->getSlotPosition() & SLOTP_TWO_HAND)
2793 ret = RET_DROPTWOHANDEDITEM;
2794 else if(item == rightItem && count == item->getItemCount())
2795 ret = RET_NOERROR;
2796 else if(rightType == WEAPON_SHIELD && type == WEAPON_SHIELD)
2797 ret = RET_CANONLYUSEONESHIELD;
2798 else if(!rightItem->isWeapon() || !item->isWeapon() ||
2799 rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO
2800 || type == WEAPON_SHIELD || type == WEAPON_AMMO)
2801 ret = RET_NOERROR;
2802 else
2803 ret = RET_CANONLYUSEONEWEAPON;
2804 }
2805 else if(item->getWeaponType() == WEAPON_NONE)
2806 ret = RET_CANONLYUSEONEWEAPONORSHIELD;
2807 else
2808 ret = RET_NOERROR;
2809 }
2810 break;
2811 case SLOT_LEGS:
2812 if(item->getSlotPosition() & SLOTP_LEGS)
2813 ret = RET_NOERROR;
2814 break;
2815 case SLOT_FEET:
2816 if(item->getSlotPosition() & SLOTP_FEET)
2817 ret = RET_NOERROR;
2818 break;
2819 case SLOT_RING:
2820 if(item->getSlotPosition() & SLOTP_RING)
2821 ret = RET_NOERROR;
2822 break;
2823 case SLOT_AMMO:
2824 {
2825 if(item->getWeaponType() != WEAPON_NONE)
2826 ret = RET_NOERROR;
2827 break;
2828 }
2829 case SLOT_WHEREEVER:
2830 case -1:
2831 ret = RET_NOTENOUGHROOM;
2832 break;
2833 default:
2834 ret = RET_NOTPOSSIBLE;
2835 break;
2836 }
2837
2838 if(ret == RET_NOERROR || ret == RET_NOTENOUGHROOM)
2839 {
2840 //need an exchange with source?
2841 if(getInventoryItem((slots_t)index) != NULL && (!getInventoryItem((slots_t)index)->isStackable()
2842 || getInventoryItem((slots_t)index)->getID() != item->getID()))
2843 return RET_NEEDEXCHANGE;
2844
2845 if(!g_moveEvents->onPlayerEquip(const_cast<Player*>(this), const_cast<Item*>(item), (slots_t)index, true))
2846 return RET_CANNOTBEDRESSED;
2847
2848 //check if enough capacity
2849 if(!hasCapacity(item, count))
2850 return RET_NOTENOUGHCAPACITY;
2851 }
2852
2853 return ret;
2854}
2855
2856ReturnValue Player::__queryMaxCount(int32_t index, const Thing* thing, uint32_t count, uint32_t& maxQueryCount,
2857 uint32_t flags) const
2858{
2859 const Item* item = thing->getItem();
2860 if(!item)
2861 {
2862 maxQueryCount = 0;
2863 return RET_NOTPOSSIBLE;
2864 }
2865
2866 const Thing* destThing = __getThing(index);
2867 const Item* destItem = NULL;
2868 if(destThing)
2869 destItem = destThing->getItem();
2870
2871 if(destItem)
2872 {
2873 if(destItem->isStackable() && item->getID() == destItem->getID())
2874 maxQueryCount = 100 - destItem->getItemCount();
2875 else
2876 maxQueryCount = 0;
2877 }
2878 else
2879 {
2880 if(item->isStackable())
2881 maxQueryCount = 100;
2882 else
2883 maxQueryCount = 1;
2884
2885 return RET_NOERROR;
2886 }
2887
2888 if(maxQueryCount < count)
2889 return RET_NOTENOUGHROOM;
2890
2891 return RET_NOERROR;
2892}
2893
2894ReturnValue Player::__queryRemove(const Thing* thing, uint32_t count, uint32_t flags) const
2895{
2896 int32_t index = __getIndexOfThing(thing);
2897 if(index == -1)
2898 return RET_NOTPOSSIBLE;
2899
2900 const Item* item = thing->getItem();
2901 if(!item)
2902 return RET_NOTPOSSIBLE;
2903
2904 if(count == 0 || (item->isStackable() && count > item->getItemCount()))
2905 return RET_NOTPOSSIBLE;
2906
2907 if(item->isNotMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags))
2908 return RET_NOTMOVEABLE;
2909
2910 return RET_NOERROR;
2911}
2912
2913Cylinder* Player::__queryDestination(int32_t& index, const Thing* thing, Item** destItem,
2914 uint32_t& flags)
2915{
2916 if(index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER)
2917 {
2918 *destItem = NULL;
2919 const Item* item = thing->getItem();
2920 if(!item)
2921 return this;
2922
2923 //find a appropiate slot
2924 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
2925 {
2926 if(!inventory[i] && __queryAdd(i, item, item->getItemCount(), 0) == RET_NOERROR)
2927 {
2928 index = i;
2929 return this;
2930 }
2931 }
2932
2933 //try containers
2934 std::list<std::pair<Container*, int32_t> > deepList;
2935 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
2936 {
2937 if(inventory[i] == tradeItem)
2938 continue;
2939
2940 if(Container* container = dynamic_cast<Container*>(inventory[i]))
2941 {
2942 if(container->__queryAdd(-1, item, item->getItemCount(), 0) == RET_NOERROR)
2943 {
2944 index = INDEX_WHEREEVER;
2945 *destItem = NULL;
2946 return container;
2947 }
2948
2949 deepList.push_back(std::make_pair(container, 0));
2950 }
2951 }
2952
2953 //check deeper in the containers
2954 int32_t deepness = g_config.getNumber(ConfigManager::PLAYER_DEEPNESS);
2955 for(std::list<std::pair<Container*, int32_t> >::iterator dit = deepList.begin(); dit != deepList.end(); ++dit)
2956 {
2957 Container* c = (*dit).first;
2958 if(!c || c->empty())
2959 continue;
2960
2961 int32_t level = (*dit).second;
2962 for(ItemList::const_iterator it = c->getItems(); it != c->getEnd(); ++it)
2963 {
2964 if((*it) == tradeItem)
2965 continue;
2966
2967 if(Container* subContainer = dynamic_cast<Container*>(*it))
2968 {
2969 if(subContainer->__queryAdd(-1, item, item->getItemCount(), 0) == RET_NOERROR)
2970 {
2971 index = INDEX_WHEREEVER;
2972 *destItem = NULL;
2973 return subContainer;
2974 }
2975
2976 if(deepness < 0 || level < deepness)
2977 deepList.push_back(std::make_pair(subContainer, (level + 1)));
2978 }
2979 }
2980 }
2981
2982 return this;
2983 }
2984
2985 Thing* destThing = __getThing(index);
2986 if(destThing)
2987 *destItem = destThing->getItem();
2988
2989 if(Cylinder* subCylinder = dynamic_cast<Cylinder*>(destThing))
2990 {
2991 index = INDEX_WHEREEVER;
2992 *destItem = NULL;
2993 return subCylinder;
2994 }
2995
2996 return this;
2997}
2998
2999void Player::__addThing(Creature* actor, Thing* thing)
3000{
3001 __addThing(actor, 0, thing);
3002}
3003
3004void Player::__addThing(Creature* actor, int32_t index, Thing* thing)
3005{
3006 if(index < 0 || index > 11)
3007 return /*RET_NOTPOSSIBLE*/;
3008
3009 if(index == 0)
3010 return /*RET_NOTENOUGHROOM*/;
3011
3012 Item* item = thing->getItem();
3013 if(!item)
3014 return /*RET_NOTPOSSIBLE*/;
3015
3016 item->setParent(this);
3017 inventory[index] = item;
3018
3019 //send to client
3020 sendAddInventoryItem((slots_t)index, item);
3021
3022 //event methods
3023 onAddInventoryItem((slots_t)index, item);
3024}
3025
3026void Player::__updateThing(Thing* thing, uint16_t itemId, uint32_t count)
3027{
3028 int32_t index = __getIndexOfThing(thing);
3029 if(index == -1)
3030 return /*RET_NOTPOSSIBLE*/;
3031
3032 Item* item = thing->getItem();
3033 if(!item)
3034 return /*RET_NOTPOSSIBLE*/;
3035
3036 const ItemType& oldType = Item::items[item->getID()];
3037 const ItemType& newType = Item::items[itemId];
3038
3039 item->setID(itemId);
3040 item->setSubType(count);
3041
3042 //send to client
3043 sendUpdateInventoryItem((slots_t)index, item, item);
3044 //event methods
3045 onUpdateInventoryItem((slots_t)index, item, oldType, item, newType);
3046}
3047
3048void Player::__replaceThing(uint32_t index, Thing* thing)
3049{
3050 if(index < 0 || index > 11)
3051 return /*RET_NOTPOSSIBLE*/;
3052
3053 Item* oldItem = getInventoryItem((slots_t)index);
3054 if(!oldItem)
3055 return /*RET_NOTPOSSIBLE*/;
3056
3057 Item* item = thing->getItem();
3058 if(!item)
3059 return /*RET_NOTPOSSIBLE*/;
3060
3061 const ItemType& oldType = Item::items[oldItem->getID()];
3062 const ItemType& newType = Item::items[item->getID()];
3063
3064 //send to client
3065 sendUpdateInventoryItem((slots_t)index, oldItem, item);
3066 //event methods
3067 onUpdateInventoryItem((slots_t)index, oldItem, oldType, item, newType);
3068
3069 item->setParent(this);
3070 inventory[index] = item;
3071}
3072
3073void Player::__removeThing(Thing* thing, uint32_t count)
3074{
3075 Item* item = thing->getItem();
3076 if(!item)
3077 return /*RET_NOTPOSSIBLE*/;
3078
3079 int32_t index = __getIndexOfThing(thing);
3080 if(index == -1)
3081 return /*RET_NOTPOSSIBLE*/;
3082
3083 if(item->isStackable())
3084 {
3085 if(count == item->getItemCount())
3086 {
3087 //send change to client
3088 sendRemoveInventoryItem((slots_t)index, item);
3089 //event methods
3090 onRemoveInventoryItem((slots_t)index, item);
3091
3092 item->setParent(NULL);
3093 inventory[index] = NULL;
3094 }
3095 else
3096 {
3097 item->setItemCount(std::max(0, (int32_t)(item->getItemCount() - count)));
3098 const ItemType& it = Item::items[item->getID()];
3099
3100 //send change to client
3101 sendUpdateInventoryItem((slots_t)index, item, item);
3102 //event methods
3103 onUpdateInventoryItem((slots_t)index, item, it, item, it);
3104 }
3105 }
3106 else
3107 {
3108 //send change to client
3109 sendRemoveInventoryItem((slots_t)index, item);
3110 //event methods
3111 onRemoveInventoryItem((slots_t)index, item);
3112
3113 item->setParent(NULL);
3114 inventory[index] = NULL;
3115 }
3116}
3117
3118Thing* Player::__getThing(uint32_t index) const
3119{
3120 if(index > SLOT_PRE_FIRST && index < SLOT_LAST)
3121 return inventory[index];
3122
3123 return NULL;
3124}
3125
3126int32_t Player::__getIndexOfThing(const Thing* thing) const
3127{
3128 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3129 {
3130 if(inventory[i] == thing)
3131 return i;
3132 }
3133
3134 return -1;
3135}
3136
3137int32_t Player::__getFirstIndex() const
3138{
3139 return SLOT_FIRST;
3140}
3141
3142int32_t Player::__getLastIndex() const
3143{
3144 return SLOT_LAST;
3145}
3146
3147uint32_t Player::__getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/, bool itemCount /*= true*/, bool use /*= false*/) const
3148{
3149 uint32_t count = 0;
3150 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3151 {
3152 Item* item = inventory[i];
3153 if(!item)
3154 continue;
3155
3156 if(item->getID() == itemId)
3157 {
3158 if(use)
3159 {
3160 g_game.playerUseActionItem(this, item, i);
3161 return 1;
3162 }
3163
3164 count += Item::countByType(item, subType, itemCount);
3165 }
3166
3167 Container* container = item->getContainer();
3168 if(!container)
3169 continue;
3170
3171 for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it)
3172 {
3173 assert(*it);
3174 if((*it)->getID() == itemId)
3175 {
3176 if(use)
3177 {
3178 g_game.playerUseActionItem(this, *it, container->__getIndexOfThing(*it));
3179 return 1;
3180 }
3181
3182 count += Item::countByType(*it, subType, itemCount);
3183 }
3184 }
3185 }
3186
3187 return count;
3188}
3189
3190std::map<uint32_t, uint32_t>& Player::__getAllItemTypeCount(std::map<uint32_t,
3191 uint32_t>& countMap, bool itemCount/* = true*/) const
3192{
3193 Item* item = NULL;
3194 Container* container = NULL;
3195 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3196 {
3197 if(!(item = inventory[i]))
3198 continue;
3199
3200 countMap[item->getID()] += Item::countByType(item, -1, itemCount);
3201 if(!(container = item->getContainer()))
3202 continue;
3203
3204 for(ContainerIterator it = container->begin(), end = container->end(); it != end; ++it)
3205 countMap[(*it)->getID()] += Item::countByType(*it, -1, itemCount);
3206 }
3207
3208 return countMap;
3209}
3210
3211void Player::postAddNotification(Creature* actor, Thing* thing, const Cylinder* oldParent,
3212 int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
3213{
3214 if(link == LINK_OWNER) //calling movement scripts
3215 g_moveEvents->onPlayerEquip(this, thing->getItem(), (slots_t)index, false);
3216
3217 bool requireListUpdate = true;
3218 if(link == LINK_OWNER || link == LINK_TOPPARENT)
3219 {
3220 if(const Item* item = (oldParent ? oldParent->getItem() : NULL))
3221 {
3222 assert(item->getContainer() != NULL);
3223 requireListUpdate = item->getContainer()->getHoldingPlayer() != this;
3224 }
3225 else
3226 requireListUpdate = oldParent != this;
3227
3228 updateInventoryWeight();
3229 updateItemsLight();
3230 sendStats();
3231 }
3232
3233 if(const Item* item = thing->getItem())
3234 {
3235 if(const Container* container = item->getContainer())
3236 onSendContainer(container);
3237
3238 if(shopOwner && requireListUpdate)
3239 updateInventoryGoods(item->getID());
3240 }
3241 else if(const Creature* creature = thing->getCreature())
3242 {
3243 if(creature != this)
3244 return;
3245
3246 typedef std::vector<Container*> Containers;
3247 Containers containers;
3248 for(ContainerVector::iterator it = containerVec.begin(); it != containerVec.end(); ++it)
3249 {
3250 if(!Position::areInRange<1,1,0>(it->second->getPosition(), getPosition()))
3251 containers.push_back(it->second);
3252 }
3253
3254 for(Containers::const_iterator it = containers.begin(); it != containers.end(); ++it)
3255 autoCloseContainers(*it);
3256 }
3257}
3258
3259void Player::postRemoveNotification(Creature* actor, Thing* thing, const Cylinder* newParent,
3260 int32_t index, bool isCompleteRemoval, cylinderlink_t link /*= LINK_OWNER*/)
3261{
3262 if(link == LINK_OWNER) //calling movement scripts
3263 g_moveEvents->onPlayerDeEquip(this, thing->getItem(), (slots_t)index, isCompleteRemoval);
3264
3265 bool requireListUpdate = true;
3266 if(link == LINK_OWNER || link == LINK_TOPPARENT)
3267 {
3268 if(const Item* item = (newParent ? newParent->getItem() : NULL))
3269 {
3270 assert(item->getContainer() != NULL);
3271 requireListUpdate = item->getContainer()->getHoldingPlayer() != this;
3272 }
3273 else
3274 requireListUpdate = newParent != this;
3275
3276 updateInventoryWeight();
3277 updateItemsLight();
3278 sendStats();
3279 }
3280
3281 if(const Item* item = thing->getItem())
3282 {
3283 if(const Container* container = item->getContainer())
3284 {
3285 if(container->isRemoved() || !Position::areInRange<1,1,0>(getPosition(), container->getPosition()))
3286 autoCloseContainers(container);
3287 else if(container->getTopParent() == this)
3288 onSendContainer(container);
3289 else if(const Container* topContainer = dynamic_cast<const Container*>(container->getTopParent()))
3290 {
3291 if(const Depot* depot = dynamic_cast<const Depot*>(topContainer))
3292 {
3293 bool isOwner = false;
3294 for(DepotMap::iterator it = depots.begin(); it != depots.end(); ++it)
3295 {
3296 if(it->second.first != depot)
3297 continue;
3298
3299 isOwner = true;
3300 onSendContainer(container);
3301 }
3302
3303 if(!isOwner)
3304 autoCloseContainers(container);
3305 }
3306 else
3307 onSendContainer(container);
3308 }
3309 else
3310 autoCloseContainers(container);
3311 }
3312
3313 if(shopOwner && requireListUpdate)
3314 updateInventoryGoods(item->getID());
3315 }
3316}
3317
3318void Player::__internalAddThing(Thing* thing)
3319{
3320 __internalAddThing(0, thing);
3321}
3322
3323void Player::__internalAddThing(uint32_t index, Thing* thing)
3324{
3325 Item* item = thing->getItem();
3326 if(!item)
3327 return;
3328
3329 //index == 0 means we should equip this item at the most appropiate slot
3330 if(index == 0)
3331 return;
3332
3333 if(index > 0 && index < 11)
3334 {
3335 if(inventory[index])
3336 return;
3337
3338 inventory[index] = item;
3339 item->setParent(this);
3340 }
3341}
3342
3343bool Player::setFollowCreature(Creature* creature, bool fullPathSearch /*= false*/)
3344{
3345 bool deny = false;
3346 CreatureEventList followEvents = getCreatureEvents(CREATURE_EVENT_FOLLOW);
3347 for(CreatureEventList::iterator it = followEvents.begin(); it != followEvents.end(); ++it)
3348 {
3349 if(creature && !(*it)->executeFollow(this, creature))
3350 deny = true;
3351 }
3352
3353 if(deny || !Creature::setFollowCreature(creature, fullPathSearch))
3354 {
3355 setFollowCreature(NULL);
3356 setAttackedCreature(NULL);
3357 if(!deny)
3358 sendCancelMessage(RET_THEREISNOWAY);
3359
3360 sendCancelTarget();
3361 stopEventWalk();
3362 return false;
3363 }
3364
3365 return true;
3366}
3367
3368bool Player::setAttackedCreature(Creature* creature)
3369{
3370 if(!Creature::setAttackedCreature(creature))
3371 {
3372 sendCancelTarget();
3373 return false;
3374 }
3375
3376 if(chaseMode == CHASEMODE_FOLLOW && creature)
3377 {
3378 if(followCreature != creature) //chase opponent
3379 setFollowCreature(creature);
3380 }
3381 else
3382 setFollowCreature(NULL);
3383
3384 //if(creature)
3385 // Dispatcher::getInstance().addTask(createTask(boost::bind(&Game::checkCreatureAttack, &g_game, getID())));
3386
3387 return true;
3388}
3389
3390void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
3391{
3392 Creature::getPathSearchParams(creature, fpp);
3393 fpp.fullPathSearch = true;
3394}
3395
3396void Player::doAttacking(uint32_t interval)
3397{
3398 if(hasCondition(CONDITION_EXHAUST, EXHAUST_WEAPON))
3399 return;
3400
3401 uint32_t attackSpeed = getAttackSpeed();
3402 if(lastAttack == 0)
3403 lastAttack = OTSYS_TIME() - attackSpeed - 1;
3404 else if((OTSYS_TIME() - lastAttack) < attackSpeed || hasCondition(CONDITION_PACIFIED))
3405 return;
3406
3407 Item* tool = getWeapon();
3408 if(tool)
3409 {
3410 const Weapon* weapon = g_weapons->getWeapon(tool);
3411 if(weapon && weapon->useWeapon(this, tool, attackedCreature->getID()))
3412 {
3413 lastAttack = OTSYS_TIME();
3414 addExhaust(attackSpeed, EXHAUST_WEAPON, CONDITION_EXHAUST);
3415
3416 if(random_range(1, 100) <= std::max(0, getAllPlayerBonusType(ITEM_DOUBLE_HIT)))
3417 Scheduler::getInstance().addEvent(createSchedulerTask(250, boost::bind(&Weapon::useWeapon, weapon, this, tool, attackedCreature->getID())));
3418 }
3419
3420 return;
3421 }
3422
3423 if(Weapon::useFist(this, attackedCreature))
3424 lastAttack = OTSYS_TIME();
3425}
3426
3427double Player::getGainedExperience(Creature* attacker) const
3428{
3429 if(!skillLoss)
3430 return 0;
3431
3432 double rate = g_config.getDouble(ConfigManager::RATE_PVP_EXPERIENCE);
3433 if(rate <= 0)
3434 return 0;
3435
3436 Player* attackerPlayer = attacker->getPlayer();
3437 if(!attackerPlayer || attackerPlayer == this)
3438 return 0;
3439
3440 double attackerLevel = (double)attackerPlayer->getLevel(), min = g_config.getDouble(
3441 ConfigManager::EFP_MIN_THRESHOLD), max = g_config.getDouble(ConfigManager::EFP_MAX_THRESHOLD);
3442 if((min > 0 && level < (uint32_t)std::floor(attackerLevel * min)) || (max > 0 &&
3443 level > (uint32_t)std::floor(attackerLevel * max)))
3444 return 0;
3445
3446 /*
3447 Formula
3448 a = attackers level * 0.9
3449 b = victims level
3450 c = victims experience
3451
3452 result = (1 - (a / b)) * 0.05 * c
3453 Not affected by special multipliers(!)
3454 */
3455 uint32_t a = (uint32_t)std::floor(attackerLevel * 0.9), b = level;
3456 uint64_t c = getExperience();
3457 return (double)std::max((uint64_t)0, (uint64_t)std::floor(getDamageRatio(attacker)
3458 * std::max((double)0, ((double)(1 - (((double)a / b))))) * 0.05 * c)) * rate;
3459}
3460
3461void Player::onFollowCreature(const Creature* creature)
3462{
3463 if(!creature)
3464 stopEventWalk();
3465}
3466
3467void Player::setChaseMode(chaseMode_t mode)
3468{
3469 chaseMode_t prevChaseMode = chaseMode;
3470 chaseMode = mode;
3471
3472 if(prevChaseMode == chaseMode)
3473 return;
3474
3475 if(chaseMode == CHASEMODE_FOLLOW)
3476 {
3477 if(!followCreature && attackedCreature) //chase opponent
3478 setFollowCreature(attackedCreature);
3479 }
3480 else if(attackedCreature)
3481 {
3482 setFollowCreature(NULL);
3483 stopEventWalk();
3484 }
3485}
3486
3487void Player::onWalkAborted()
3488{
3489 setNextWalkActionTask(NULL);
3490 sendCancelWalk();
3491}
3492
3493void Player::onWalkComplete()
3494{
3495 if(!walkTask)
3496 return;
3497
3498 walkTaskEvent = Scheduler::getInstance().addEvent(walkTask);
3499 walkTask = NULL;
3500}
3501
3502void Player::stopWalk()
3503{
3504 if(listWalkDir.empty())
3505 return;
3506
3507 stopEventWalk();
3508}
3509
3510void Player::getCreatureLight(LightInfo& light) const
3511{
3512 if(internalLight.level > itemsLight.level)
3513 light = internalLight;
3514 else
3515 light = itemsLight;
3516}
3517
3518void Player::updateItemsLight(bool internal /*=false*/)
3519{
3520 LightInfo maxLight;
3521 LightInfo curLight;
3522 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3523 {
3524 if(Item* item = getInventoryItem((slots_t)i))
3525 {
3526 item->getLight(curLight);
3527 if(curLight.level > maxLight.level)
3528 maxLight = curLight;
3529 }
3530 }
3531 if(itemsLight.level != maxLight.level || itemsLight.color != maxLight.color)
3532 {
3533 itemsLight = maxLight;
3534 if(!internal)
3535 g_game.changeLight(this);
3536 }
3537}
3538
3539void Player::onAddCondition(ConditionType_t type, bool hadCondition)
3540{
3541 Creature::onAddCondition(type, hadCondition);
3542 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
3543 sendIcons();
3544}
3545
3546void Player::onAddCombatCondition(ConditionType_t type, bool hadCondition)
3547{
3548 std::string tmp;
3549 switch(type)
3550 {
3551 //client hardcoded
3552 case CONDITION_FIRE:
3553 tmp = "burning";
3554 break;
3555 case CONDITION_POISON:
3556 tmp = "poisoned";
3557 break;
3558 case CONDITION_ENERGY:
3559 tmp = "electrified";
3560 break;
3561 case CONDITION_FREEZING:
3562 tmp = "freezing";
3563 break;
3564 case CONDITION_DAZZLED:
3565 tmp = "dazzled";
3566 break;
3567 case CONDITION_CURSED:
3568 tmp = "cursed";
3569 break;
3570 case CONDITION_DROWN:
3571 tmp = "drowning";
3572 break;
3573 case CONDITION_DRUNK:
3574 tmp = "drunk";
3575 break;
3576 case CONDITION_MANASHIELD:
3577 tmp = "protected by a magic shield";
3578 break;
3579 case CONDITION_PARALYZE:
3580 tmp = "paralyzed";
3581 break;
3582 case CONDITION_HASTE:
3583 tmp = "hasted";
3584 break;
3585 case CONDITION_ATTRIBUTES:
3586 tmp = "strengthened";
3587 break;
3588 default:
3589 break;
3590 }
3591
3592 if(!tmp.empty())
3593 sendTextMessage(MSG_STATUS_DEFAULT, "You are " + tmp + ".");
3594}
3595
3596void Player::onEndCondition(ConditionType_t type)
3597{
3598 Creature::onEndCondition(type);
3599 if(type == CONDITION_INFIGHT)
3600 {
3601 onIdleStatus();
3602 clearAttacked();
3603
3604 pzLocked = false;
3605 if(skull < SKULL_RED)
3606 setSkull(SKULL_NONE);
3607
3608 g_game.updateCreatureSkull(this);
3609 }
3610
3611 sendIcons();
3612}
3613
3614void Player::onCombatRemoveCondition(const Creature* attacker, Condition* condition)
3615{
3616 //Creature::onCombatRemoveCondition(attacker, condition);
3617 bool remove = true;
3618 if(condition->getId() > 0)
3619 {
3620 remove = false;
3621 //Means the condition is from an item, id == slot
3622 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED)
3623 {
3624 if(Item* item = getInventoryItem((slots_t)condition->getId()))
3625 {
3626 //25% chance to destroy the item
3627 if(25 >= random_range(0, 100))
3628 g_game.internalRemoveItem(NULL, item);
3629 }
3630 }
3631 }
3632
3633 if(remove)
3634 {
3635 if(!canDoAction())
3636 {
3637 uint32_t delay = getNextActionTime();
3638 delay -= (delay % EVENT_CREATURE_THINK_INTERVAL);
3639 if(delay < 0)
3640 removeCondition(condition);
3641 else
3642 condition->setTicks(delay);
3643 }
3644 else
3645 removeCondition(condition);
3646 }
3647}
3648
3649void Player::onTickCondition(ConditionType_t type, int32_t interval, bool& _remove)
3650{
3651 Creature::onTickCondition(type, interval, _remove);
3652 if(type == CONDITION_HUNTING)
3653 useStamina(-(interval * g_config.getNumber(ConfigManager::RATE_STAMINA_LOSS)));
3654}
3655
3656void Player::onAttackedCreature(Creature* target)
3657{
3658 Creature::onAttackedCreature(target);
3659 if(hasFlag(PlayerFlag_NotGainInFight) || !target)
3660 return;
3661
3662 addInFightTicks();
3663 Player* targetPlayer = target->getPlayer();
3664 if(!targetPlayer)
3665 return;
3666
3667 addAttacked(targetPlayer);
3668 if(targetPlayer == this && targetPlayer->getZone() != ZONE_PVP)
3669 {
3670 targetPlayer->sendCreatureSkull(this);
3671 return;
3672 }
3673
3674 if(Combat::isInPvpZone(this, targetPlayer) || isPartner(targetPlayer) || (g_config.getBool(
3675 ConfigManager::ALLOW_FIGHTBACK) && targetPlayer->hasAttacked(this)))
3676 return;
3677
3678 if(!pzLocked)
3679 {
3680 pzLocked = true;
3681 sendIcons();
3682 }
3683
3684 if(getZone() != target->getZone())
3685 return;
3686
3687 if(skull == SKULL_NONE)
3688 {
3689 if(targetPlayer->getSkull() != SKULL_NONE)
3690 targetPlayer->sendCreatureSkull(this);
3691 else if(!hasCustomFlag(PlayerCustomFlag_NotGainSkull))
3692 {
3693 setSkull(SKULL_WHITE);
3694 g_game.updateCreatureSkull(this);
3695 }
3696 }
3697}
3698
3699void Player::onSummonAttackedCreature(Creature* summon, Creature* target)
3700{
3701 Creature::onSummonAttackedCreature(summon, target);
3702 onAttackedCreature(target);
3703}
3704
3705void Player::onAttacked()
3706{
3707 Creature::onAttacked();
3708 addInFightTicks();
3709}
3710
3711bool Player::checkLoginDelay(uint32_t playerId) const
3712{
3713 return (!hasCustomFlag(PlayerCustomFlag_IgnoreLoginDelay) && OTSYS_TIME() <= (lastLoad + g_config.getNumber(
3714 ConfigManager::LOGIN_PROTECTION)) && !hasBeenAttacked(playerId));
3715}
3716
3717void Player::onIdleStatus()
3718{
3719 Creature::onIdleStatus();
3720 if(getParty())
3721 getParty()->clearPlayerPoints(this);
3722}
3723
3724void Player::onPlacedCreature()
3725{
3726 //scripting event - onLogin
3727 if(!g_creatureEvents->playerLogin(this))
3728 {
3729 kickPlayer(true, true);
3730 return;
3731 }
3732
3733 updateAchievement(ACHIEVEMENT_LOGIN_50_TIMES);
3734
3735 int32_t value = getCreatureIntStorage(TRANSFORM_STORAGE);
3736 Transforms* transform = Transforms::getInstance();
3737 if(transform && (transform->isTransformRelog(this) || transform->isPermanent(this, value)))
3738 transform->doTransform(this, std::max(0, value), true, true);
3739}
3740
3741void Player::getSetsAttributeMap(ItemAttributes_t type, int16_t* table)
3742{
3743 if(setsAttributesList.empty())
3744 return;
3745
3746 for(SetsAttributesList::iterator it = setsAttributesList.begin(); it != setsAttributesList.end(); ++it)
3747 {
3748 if(it->second.empty())
3749 continue;
3750
3751 for(AttributesListMap::iterator _it = it->second.begin(); _it != it->second.end(); ++_it)
3752 {
3753 if(_it->first != type || _it->second.empty())
3754 continue;
3755
3756 for(std::map<int32_t, int32_t>::iterator __it = _it->second.begin(); __it != _it->second.end(); ++__it)
3757 table[__it->first] += __it->second;
3758 }
3759 }
3760}
3761
3762void Player::onAttackedCreatureDrain(Creature* target, int32_t points)
3763{
3764 Creature::onAttackedCreatureDrain(target, points);
3765 if(party && target && (!target->getMaster() || !target->getMaster()->getPlayer())
3766 && target->getMonster() && target->getMonster()->isHostile()) //we have fulfilled a requirement for shared experience
3767 getParty()->addPlayerDamageMonster(this, points);
3768
3769 char buffer[100];
3770 sprintf(buffer, "You deal %d damage to %s.", points, target->getNameDescription().c_str());
3771 sendTextMessage(MSG_STATUS_DEFAULT, buffer);
3772}
3773
3774void Player::onSummonAttackedCreatureDrain(Creature* summon, Creature* target, int32_t points)
3775{
3776 Creature::onSummonAttackedCreatureDrain(summon, target, points);
3777
3778 char buffer[100];
3779 sprintf(buffer, "Your %s deals %d damage to %s.", summon->getName().c_str(), points, target->getNameDescription().c_str());
3780 sendTextMessage(MSG_EVENT_DEFAULT, buffer);
3781}
3782
3783void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
3784{
3785 Creature::onTargetCreatureGainHealth(target, points);
3786 if(target && getParty())
3787 {
3788 Player* tmpPlayer = NULL;
3789 if(target->getPlayer())
3790 tmpPlayer = target->getPlayer();
3791 else if(target->getMaster() && target->getMaster()->getPlayer())
3792 tmpPlayer = target->getMaster()->getPlayer();
3793
3794 if(isPartner(tmpPlayer))
3795 getParty()->addPlayerHealedMember(this, points);
3796 }
3797}
3798
3799bool Player::onKilledCreature(Creature* target, uint32_t& flags)
3800{
3801 if(!Creature::onKilledCreature(target, flags))
3802 return false;
3803
3804 if(hasFlag(PlayerFlag_NotGenerateLoot))
3805 target->setDropLoot(LOOT_DROP_NONE);
3806
3807 Condition* condition = NULL;
3808 if(target->getMonster() && !target->isPlayerSummon() && !hasFlag(PlayerFlag_HasInfiniteStamina)
3809 && (condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_HUNTING,
3810 g_config.getNumber(ConfigManager::HUNTING_DURATION))))
3811 addCondition(condition);
3812
3813 if(hasFlag(PlayerFlag_NotGainInFight) || !hasBitSet((uint32_t)KILLFLAG_JUSTIFY, flags) || getZone() != target->getZone())
3814 return true;
3815
3816 Player* targetPlayer = target->getPlayer();
3817 if(!targetPlayer)
3818 return true;
3819
3820 setCreatureStorage(KILL_COUNT_STORAGE, std::max(0, getCreatureIntStorage(KILL_COUNT_STORAGE)) + 1);
3821 if(Combat::isInPvpZone(this, targetPlayer) || !hasCondition(CONDITION_INFIGHT) || isPartner(targetPlayer))
3822 return true;
3823
3824 if(!targetPlayer->hasAttacked(this) && target->getSkull() == SKULL_NONE && targetPlayer != this
3825 && ((g_config.getBool(ConfigManager::USE_FRAG_HANDLER) && addUnjustifiedKill(
3826 targetPlayer)) || hasBitSet((uint32_t)KILLFLAG_LASTHIT, flags)))
3827 flags |= (uint32_t)KILLFLAG_UNJUSTIFIED;
3828
3829 pzLocked = true;
3830 if((condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT,
3831 g_config.getNumber(ConfigManager::WHITE_SKULL_TIME))))
3832 addCondition(condition);
3833
3834 return true;
3835}
3836
3837bool Player::gainExperience(double& gainExp, bool fromMonster)
3838{
3839 if(!rateExperience(gainExp, fromMonster))
3840 return false;
3841
3842 //soul regeneration
3843 if(gainExp >= level)
3844 {
3845 if(Condition* condition = Condition::createCondition(
3846 CONDITIONID_DEFAULT, CONDITION_SOUL, 4 * 60 * 1000))
3847 {
3848 condition->setParam(CONDITIONPARAM_SOULGAIN,
3849 vocation->getGainAmount(GAIN_SOUL));
3850 condition->setParam(CONDITIONPARAM_SOULTICKS,
3851 (vocation->getGainTicks(GAIN_SOUL) * 1000));
3852 addCondition(condition);
3853 }
3854 }
3855
3856 addExperience((uint64_t)gainExp);
3857 return true;
3858}
3859
3860int32_t Player::getItemsMultipliers(ItemsMultipliers_t type)
3861{
3862 int32_t var = 0;
3863 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
3864 {
3865 Item* item = getInventoryItem((slots_t)i);
3866 if(!item)
3867 continue;
3868
3869 const ItemType& it = Item::items[item->getID()];
3870 switch(type)
3871 {
3872 case MULTIPLIER_EXPERIENCE:
3873 var += it.abilities.experienceRate;
3874 break;
3875 case MULTIPLIER_SKILL:
3876 var += it.abilities.skillRate;
3877 break;
3878 case MULTIPLIER_DROP:
3879 var += it.abilities.dropRate;
3880 break;
3881
3882 default:
3883 break;
3884 }
3885 }
3886
3887 return var;
3888}
3889
3890bool Player::rateExperience(double& gainExp, bool fromMonster)
3891{
3892 if(hasFlag(PlayerFlag_NotGainExperience) || gainExp <= 0)
3893 return false;
3894
3895 if(!fromMonster)
3896 return true;
3897
3898 if(hasCondition(CONDITION_ATTRIBUTES, SUBID_EXPERIENCE))
3899 {
3900 int32_t var = getCreatureAttribute(SUBID_EXPERIENCE);
3901 if(var != 0)
3902 gainExp += (gainExp * var) / 100;
3903 }
3904
3905 gainExp += gainExp * getItemsMultipliers(MULTIPLIER_EXPERIENCE);
3906 gainExp *= rates[SKILL__LEVEL] * g_game.getExperienceStage(level,
3907 vocation->getExperienceMultiplier());
3908 if(!hasFlag(PlayerFlag_HasInfiniteStamina))
3909 {
3910 int32_t minutes = getStaminaMinutes();
3911 if(minutes >= g_config.getNumber(ConfigManager::STAMINA_LIMIT_TOP))
3912 {
3913 if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM))
3914 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3915 }
3916 else if(minutes < (g_config.getNumber(ConfigManager::STAMINA_LIMIT_BOTTOM)) && minutes > 0)
3917 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_UNDER);
3918 else if(minutes <= 0)
3919 gainExp = 0;
3920 }
3921 else if(isPremium() || !g_config.getNumber(ConfigManager::STAMINA_BONUS_PREMIUM))
3922 gainExp *= g_config.getDouble(ConfigManager::RATE_STAMINA_ABOVE);
3923
3924 return true;
3925}
3926
3927void Player::onGainExperience(double& gainExp, bool fromMonster, bool multiplied)
3928{
3929 if(isPremium())
3930 gainExp *= 1.2;
3931
3932 if(party && party->isSharedExperienceEnabled() && party->isSharedExperienceActive())
3933 {
3934 party->shareExperience(gainExp, fromMonster, multiplied);
3935 rateExperience(gainExp, fromMonster);
3936 return; //we will get a share of the experience through the sharing mechanism
3937 }
3938
3939 if(gainExperience(gainExp, fromMonster))
3940 Creature::onGainExperience(gainExp, fromMonster, true);
3941}
3942
3943void Player::onGainSharedExperience(double& gainExp, bool fromMonster, bool multiplied)
3944{
3945 if(gainExperience(gainExp, fromMonster))
3946 Creature::onGainSharedExperience(gainExp, fromMonster, true);
3947}
3948
3949bool Player::isImmune(CombatType_t type) const
3950{
3951 return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
3952}
3953
3954bool Player::isImmune(ConditionType_t type) const
3955{
3956 return hasCustomFlag(PlayerCustomFlag_IsImmune) || Creature::isImmune(type);
3957}
3958
3959bool Player::isAttackable() const
3960{
3961 return (!hasFlag(PlayerFlag_CannotBeAttacked) && !isAccountManager());
3962}
3963
3964void Player::changeHealth(int32_t healthChange)
3965{
3966 Creature::changeHealth(healthChange);
3967 sendStats();
3968}
3969
3970void Player::changeMana(int32_t manaChange)
3971{
3972 if(!hasFlag(PlayerFlag_HasInfiniteMana))
3973 Creature::changeMana(manaChange);
3974
3975 sendStats();
3976}
3977
3978void Player::changeSoul(int32_t soulChange)
3979{
3980 if(!hasFlag(PlayerFlag_HasInfiniteSoul))
3981 soul = std::min((int32_t)soulMax, (int32_t)soul + soulChange);
3982
3983 sendStats();
3984}
3985
3986bool Player::canLogout(bool checkInfight)
3987{
3988 if(checkInfight && hasCondition(CONDITION_INFIGHT))
3989 return false;
3990
3991 return !isConnecting && !pzLocked && !getTile()->hasFlag(TILESTATE_NOLOGOUT);
3992}
3993
3994bool Player::changeOutfit(Outfit_t outfit, bool checkList)
3995{
3996 uint32_t outfitId = Outfits::getInstance()->getOutfitId(outfit.lookType);
3997 if(checkList && (!canWearOutfit(outfitId, outfit.lookAddons) || !requestedOutfit))
3998 return false;
3999
4000 requestedOutfit = false;
4001 if(outfitAttributes)
4002 {
4003 uint32_t oldId = Outfits::getInstance()->getOutfitId(defaultOutfit.lookType);
4004 outfitAttributes = !Outfits::getInstance()->removeAttributes(getID(), oldId, sex);
4005 }
4006
4007 defaultOutfit = outfit;
4008 outfitAttributes = Outfits::getInstance()->addAttributes(getID(), outfitId, sex, defaultOutfit.lookAddons);
4009 return true;
4010}
4011
4012bool Player::canWearOutfit(uint32_t outfitId, uint32_t addons)
4013{
4014 OutfitMap::iterator it = outfits.find(outfitId);
4015 if(it == outfits.end() || (it->second.isPremium && !isPremium()) || getAccess() < it->second.accessLevel
4016 || ((it->second.addons & addons) != addons && !hasCustomFlag(PlayerCustomFlag_CanWearAllAddons)))
4017 return false;
4018
4019 if(!it->second.storageId)
4020 return true;
4021
4022 std::string value;
4023 return getStorage(it->second.storageId, value) && value == it->second.storageValue;
4024}
4025
4026bool Player::addOutfit(uint32_t outfitId, uint32_t addons)
4027{
4028 Outfit outfit;
4029 if(!Outfits::getInstance()->getOutfit(outfitId, sex, outfit))
4030 return false;
4031
4032 OutfitMap::iterator it = outfits.find(outfitId);
4033 if(it != outfits.end())
4034 outfit.addons |= it->second.addons;
4035
4036 outfit.addons |= addons;
4037 outfits[outfitId] = outfit;
4038 return true;
4039}
4040
4041bool Player::removeOutfit(uint32_t outfitId, uint32_t addons)
4042{
4043 OutfitMap::iterator it = outfits.find(outfitId);
4044 if(it == outfits.end())
4045 return false;
4046
4047 if(addons == 0xFF) //remove outfit
4048 outfits.erase(it);
4049 else //remove addons
4050 outfits[outfitId].addons = it->second.addons & (~addons);
4051
4052 return true;
4053}
4054
4055void Player::generateReservedStorage()
4056{
4057 uint32_t baseKey = PSTRG_OUTFITSID_RANGE_START + 1;
4058 const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4059 for(OutfitMap::const_iterator it = outfits.begin(); it != outfits.end(); ++it)
4060 {
4061 OutfitMap::const_iterator dit = defaultOutfits.find(it->first);
4062 if(dit == defaultOutfits.end() || (dit->second.isDefault && (dit->second.addons
4063 & it->second.addons) == it->second.addons))
4064 continue;
4065
4066 std::stringstream ss;
4067 ss << ((it->first << 16) | (it->second.addons & 0xFF));
4068 storageMap[baseKey] = ss.str();
4069
4070 baseKey++;
4071 if(baseKey <= PSTRG_OUTFITSID_RANGE_START + PSTRG_OUTFITSID_RANGE_SIZE)
4072 continue;
4073
4074 std::cout << "[Warning - Player::genReservedStorageRange] Player " << getName() << " with more than 500 outfits!" << std::endl;
4075 break;
4076 }
4077}
4078
4079void Player::setSex(uint16_t newSex)
4080{
4081 sex = newSex;
4082 const OutfitMap& defaultOutfits = Outfits::getInstance()->getOutfits(sex);
4083 for(OutfitMap::const_iterator it = defaultOutfits.begin(); it != defaultOutfits.end(); ++it)
4084 {
4085 if(it->second.isDefault)
4086 addOutfit(it->first, it->second.addons);
4087 }
4088}
4089
4090Skulls_t Player::getSkull() const
4091{
4092 if(hasFlag(PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4093 return SKULL_NONE;
4094
4095 return skull;
4096}
4097
4098Skulls_t Player::getSkullClient(const Creature* creature) const
4099{
4100 if(const Player* player = creature->getPlayer())
4101 {
4102 if(g_game.getWorldType() != WORLD_TYPE_PVP)
4103 return SKULL_NONE;
4104
4105 if((player == this || (skull != SKULL_NONE && player->getSkull() < SKULL_RED)) && player->hasAttacked(this))
4106 return SKULL_YELLOW;
4107
4108 if(player->getSkull() == SKULL_NONE && isPartner(player) && g_game.getWorldType() != WORLD_TYPE_NO_PVP)
4109 return SKULL_GREEN;
4110 }
4111
4112 return Creature::getSkullClient(creature);
4113}
4114
4115bool Player::hasAttacked(const Player* attacked) const
4116{
4117 return !hasFlag(PlayerFlag_NotGainInFight) && attacked &&
4118 attackedSet.find(attacked->getID()) != attackedSet.end();
4119}
4120
4121void Player::addAttacked(const Player* attacked)
4122{
4123 if(hasFlag(PlayerFlag_NotGainInFight) || !attacked)
4124 return;
4125
4126 uint32_t attackedId = attacked->getID();
4127 if(attackedSet.find(attackedId) == attackedSet.end())
4128 attackedSet.insert(attackedId);
4129}
4130
4131void Player::setSkullEnd(time_t _time, bool login, Skulls_t _skull)
4132{
4133 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED)
4134 return;
4135
4136 bool requireUpdate = false;
4137 if(_time > time(NULL))
4138 {
4139 requireUpdate = true;
4140 setSkull(_skull);
4141 }
4142 else if(skull == _skull)
4143 {
4144 requireUpdate = true;
4145 setSkull(SKULL_NONE);
4146 _time = 0;
4147 }
4148
4149 if(requireUpdate)
4150 {
4151 skullEnd = _time;
4152 if(!login)
4153 g_game.updateCreatureSkull(this);
4154 }
4155}
4156
4157bool Player::addUnjustifiedKill(const Player* attacked)
4158{
4159 if(g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED || attacked == this || hasFlag(
4160 PlayerFlag_NotGainInFight) || hasCustomFlag(PlayerCustomFlag_NotGainSkull))
4161 return false;
4162
4163 if(client)
4164 {
4165 char buffer[90];
4166 sprintf(buffer, "Warning! The murder of %s was not justified.",
4167 attacked->getName().c_str());
4168 client->sendTextMessage(MSG_STATUS_WARNING, buffer);
4169 }
4170
4171 time_t now = time(NULL), today = (now - 84600), week = (now - (7 * 84600));
4172 std::vector<time_t> dateList;
4173 IOLoginData::getInstance()->getUnjustifiedDates(guid, dateList, now);
4174
4175 dateList.push_back(now);
4176 uint32_t tc = 0, wc = 0, mc = dateList.size();
4177 for(std::vector<time_t>::iterator it = dateList.begin(); it != dateList.end(); ++it)
4178 {
4179 if((*it) > week)
4180 wc++;
4181
4182 if((*it) > today)
4183 tc++;
4184 }
4185
4186 uint32_t d = g_config.getNumber(ConfigManager::RED_DAILY_LIMIT), w = g_config.getNumber(
4187 ConfigManager::RED_WEEKLY_LIMIT), m = g_config.getNumber(ConfigManager::RED_MONTHLY_LIMIT);
4188 if(skull < SKULL_RED && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4189 setSkullEnd(now + g_config.getNumber(ConfigManager::RED_SKULL_LENGTH), false, SKULL_RED);
4190
4191 if(!g_config.getBool(ConfigManager::USE_BLACK_SKULL))
4192 {
4193 d += g_config.getNumber(ConfigManager::BAN_DAILY_LIMIT);
4194 w += g_config.getNumber(ConfigManager::BAN_WEEKLY_LIMIT);
4195 m += g_config.getNumber(ConfigManager::BAN_MONTHLY_LIMIT);
4196 if((d <= 0 || tc < d) && (w <= 0 || wc < w) && (m <= 0 || mc < m))
4197 return true;
4198
4199 if(!IOBan::getInstance()->addAccountBanishment(accountId, (now + g_config.getNumber(
4200 ConfigManager::KILLS_BAN_LENGTH)), 20, ACTION_BANISHMENT, "Unjustified player killing.", 0, guid))
4201 return true;
4202
4203 sendTextMessage(MSG_INFO_DESCR, "You have been banished.");
4204 g_game.addMagicEffect(getPosition(), MAGIC_EFFECT_WRAPS_GREEN);
4205 Scheduler::getInstance().addEvent(createSchedulerTask(1000, boost::bind(
4206 &Game::kickPlayer, &g_game, getID(), false)));
4207 }
4208 else
4209 {
4210 d += g_config.getNumber(ConfigManager::BLACK_DAILY_LIMIT);
4211 w += g_config.getNumber(ConfigManager::BLACK_WEEKLY_LIMIT);
4212 m += g_config.getNumber(ConfigManager::BLACK_MONTHLY_LIMIT);
4213 if(skull < SKULL_BLACK && ((d > 0 && tc >= d) || (w > 0 && wc >= w) || (m > 0 && mc >= m)))
4214 {
4215 setSkullEnd(now + g_config.getNumber(ConfigManager::BLACK_SKULL_LENGTH), false, SKULL_BLACK);
4216 setAttackedCreature(NULL);
4217 destroySummons();
4218 }
4219 }
4220
4221 return true;
4222}
4223
4224void Player::setPromotionLevel(uint32_t pLevel)
4225{
4226 if(pLevel > promotionLevel)
4227 {
4228 uint32_t tmpLevel = 0, currentVoc = vocation_id;
4229 for(uint32_t i = promotionLevel; i < pLevel; ++i)
4230 {
4231 currentVoc = Vocations::getInstance()->getPromotedVocation(currentVoc);
4232 if(!currentVoc)
4233 break;
4234
4235 tmpLevel++;
4236 Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4237 if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4238 continue;
4239
4240 vocation_id = currentVoc;
4241 }
4242
4243 promotionLevel += tmpLevel;
4244 }
4245 else if(pLevel < promotionLevel)
4246 {
4247 uint32_t tmpLevel = 0, currentVoc = vocation_id;
4248 for(uint32_t i = pLevel; i < promotionLevel; ++i)
4249 {
4250 Vocation* voc = Vocations::getInstance()->getVocation(currentVoc);
4251 if(voc->getFromVocation() == currentVoc)
4252 break;
4253
4254 tmpLevel++;
4255 currentVoc = voc->getFromVocation();
4256 if(voc->isPremiumNeeded() && !isPremium() && g_config.getBool(ConfigManager::PREMIUM_FOR_PROMOTION))
4257 continue;
4258
4259 vocation_id = currentVoc;
4260 }
4261
4262 promotionLevel -= tmpLevel;
4263 }
4264
4265 setVocation(vocation_id);
4266}
4267
4268uint16_t Player::getBlessings() const
4269{
4270 if(!isPremium() && g_config.getBool(ConfigManager::BLESSING_ONLY_PREMIUM))
4271 return 0;
4272
4273 uint16_t count = 0;
4274 for(int16_t i = 0; i < 16; ++i)
4275 {
4276 if(hasBlessing(i))
4277 count++;
4278 }
4279
4280 return count;
4281}
4282
4283uint64_t Player::getLostExperience() const
4284{
4285 if(!skillLoss)
4286 return 0;
4287
4288 double percent = (double)(lossPercent[LOSS_EXPERIENCE] - vocation->getLessLoss() - (getBlessings() * g_config.getNumber(
4289 ConfigManager::BLESS_REDUCTION))) / 100.;
4290 if(level <= 25)
4291 return (uint64_t)std::floor((double)(experience * percent) / 10.);
4292
4293 int32_t base = level;
4294 double levels = (double)(base + 50) / 100.;
4295
4296 uint64_t lost = 0;
4297 while(levels > 1.0f)
4298 {
4299 lost += (getExpForLevel(base) - getExpForLevel(base - 1));
4300 base--;
4301 levels -= 1.;
4302 }
4303
4304 if(levels > 0.)
4305 lost += (uint64_t)std::floor((double)(getExpForLevel(base) - getExpForLevel(base - 1)) * levels);
4306
4307 return (uint64_t)std::floor((double)(lost * percent)) / 3;
4308}
4309
4310uint32_t Player::getAttackSpeed()
4311{
4312 int32_t attackSpeed = 3000, buff = 0;
4313
4314 int32_t var = getAllPlayerBonusType(ITEM_ATTACK_SPEED, true);
4315 if(var > 0)
4316 buff += var;
4317
4318 buff += getSkill(SKILL_FIST, SKILL_LEVEL) * 15;
4319
4320 var = ((attackSpeed * (transformAttributes[TRANSFORM_ATTACK_SPEED] + getAchievementBonus(ACHIEVEMENT_AGILITY))) / 100);
4321 if(var > 0)
4322 buff += var;
4323
4324 if(buff + 100 > attackSpeed)
4325 return 100;
4326
4327 return std::max<uint32_t>(100, uint32_t(attackSpeed - buff));
4328}
4329
4330void Player::learnInstantSpell(const std::string& name)
4331{
4332 if(!hasLearnedInstantSpell(name))
4333 learnedInstantSpellList.push_back(name);
4334}
4335
4336void Player::unlearnInstantSpell(const std::string& name)
4337{
4338 if(!hasLearnedInstantSpell(name))
4339 return;
4340
4341 LearnedInstantSpellList::iterator it = std::find(learnedInstantSpellList.begin(), learnedInstantSpellList.end(), name);
4342 if(it != learnedInstantSpellList.end())
4343 learnedInstantSpellList.erase(it);
4344}
4345
4346bool Player::hasLearnedInstantSpell(const std::string& name) const
4347{
4348 if(hasFlag(PlayerFlag_CannotUseSpells))
4349 return false;
4350
4351 if(hasFlag(PlayerFlag_IgnoreSpellCheck))
4352 return true;
4353
4354 for(LearnedInstantSpellList::const_iterator it = learnedInstantSpellList.begin(); it != learnedInstantSpellList.end(); ++it)
4355 {
4356 if(!strcasecmp((*it).c_str(), name.c_str()))
4357 return true;
4358 }
4359
4360 return false;
4361}
4362
4363void Player::manageAccount(const std::string &text)
4364{
4365 std::stringstream msg;
4366 msg << "Account Manager: ";
4367
4368 bool noSwap = true;
4369 switch(accountManager)
4370 {
4371 case MANAGER_NAMELOCK:
4372 {
4373 if(!talkState[1])
4374 {
4375 managerString = text;
4376 trimString(managerString);
4377 if(managerString.length() < 4)
4378 msg << "Your name you want is too short, please select a longer name.";
4379 else if(managerString.length() > 20)
4380 msg << "The name you want is too long, please select a shorter name.";
4381 else if(!isValidName(managerString))
4382 msg << "That name seems to contain invalid symbols, please choose another name.";
4383 else if(IOLoginData::getInstance()->playerExists(managerString, true))
4384 msg << "A player with that name already exists, please choose another name.";
4385 else
4386 {
4387 std::string tmp = asLowerCaseString(managerString);
4388 if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4389 {
4390 talkState[1] = true;
4391 talkState[2] = true;
4392 msg << managerString << ", are you sure?";
4393 }
4394 else
4395 msg << "Your character is not a staff member, please tell me another name!";
4396 }
4397 }
4398 else if(checkText(text, "no") && talkState[2])
4399 {
4400 talkState[1] = talkState[2] = false;
4401 msg << "What else would you like to name your character?";
4402 }
4403 else if(checkText(text, "yes") && talkState[2])
4404 {
4405 if(!IOLoginData::getInstance()->playerExists(managerString, true))
4406 {
4407 uint32_t tmp;
4408 if(IOLoginData::getInstance()->getGuidByName(tmp, managerString2) &&
4409 IOLoginData::getInstance()->changeName(tmp, managerString, managerString2) &&
4410 IOBan::getInstance()->removePlayerBanishment(tmp, PLAYERBAN_LOCK))
4411 {
4412 if(House* house = Houses::getInstance()->getHouseByPlayerId(tmp))
4413 house->updateDoorDescription(managerString);
4414
4415 talkState[1] = true;
4416 talkState[2] = false;
4417 msg << "Your character has been successfully renamed, you should now be able to login at it without any problems.";
4418 }
4419 else
4420 {
4421 talkState[1] = talkState[2] = false;
4422 msg << "Failed to change your name, please try again.";
4423 }
4424 }
4425 else
4426 {
4427 talkState[1] = talkState[2] = false;
4428 msg << "A player with that name already exists, please choose another name.";
4429 }
4430 }
4431 else
4432 msg << "Sorry, but I can't understand you, please try to repeat that!";
4433
4434 break;
4435 }
4436 case MANAGER_ACCOUNT:
4437 {
4438 Account account = IOLoginData::getInstance()->loadAccount(managerNumber);
4439 if(checkText(text, "cancel") || (checkText(text, "account") && !talkState[1]))
4440 {
4441 talkState[1] = true;
4442 for(int8_t i = 2; i <= 12; i++)
4443 talkState[i] = false;
4444
4445 msg << "Do you want to change your 'password', request a 'recovery key', add a 'character', or 'delete' a character?";
4446 }
4447 else if(checkText(text, "delete") && talkState[1])
4448 {
4449 talkState[1] = false;
4450 talkState[2] = true;
4451 msg << "Which character would you like to delete?";
4452 }
4453 else if(talkState[2])
4454 {
4455 std::string tmp = text;
4456 trimString(tmp);
4457 if(!isValidName(tmp, false))
4458 msg << "That name contains invalid characters, try to say your name again, you might have typed it wrong.";
4459 else
4460 {
4461 talkState[2] = false;
4462 talkState[3] = true;
4463 managerString = tmp;
4464 msg << "Do you really want to delete the character named " << managerString << "?";
4465 }
4466 }
4467 else if(checkText(text, "yes") && talkState[3])
4468 {
4469 switch(IOLoginData::getInstance()->deleteCharacter(managerNumber, managerString))
4470 {
4471 case DELETE_INTERNAL:
4472 msg << "An error occured while deleting your character. Either the character does not belong to you or it doesn't exist.";
4473 break;
4474
4475 case DELETE_SUCCESS:
4476 msg << "Your character has been deleted.";
4477 break;
4478
4479 case DELETE_HOUSE:
4480 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.";
4481 break;
4482
4483 case DELETE_LEADER:
4484 msg << "Your character is the leader of a guild. You need to disband or pass the leadership someone else to delete your character.";
4485 break;
4486
4487 case DELETE_ONLINE:
4488 msg << "A character with that name is currently online, to delete a character it has to be offline.";
4489 break;
4490 }
4491
4492 talkState[1] = true;
4493 for(int8_t i = 2; i <= 12; i++)
4494 talkState[i] = false;
4495 }
4496 else if(checkText(text, "no") && talkState[3])
4497 {
4498 talkState[1] = true;
4499 talkState[3] = false;
4500 msg << "Tell me what character you want to delete.";
4501 }
4502 else if(checkText(text, "password") && talkState[1])
4503 {
4504 talkState[1] = false;
4505 talkState[4] = true;
4506 msg << "Tell me your new password please.";
4507 }
4508 else if(talkState[4])
4509 {
4510 std::string tmp = text;
4511 trimString(tmp);
4512 if(tmp.length() < 6)
4513 msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4514 else if(!isValidPassword(tmp))
4515 msg << "Your password contains invalid characters... please tell me another one.";
4516 else
4517 {
4518 talkState[4] = false;
4519 talkState[5] = true;
4520 managerString = tmp;
4521 msg << "Should '" << managerString << "' be your new password?";
4522 }
4523 }
4524 else if(checkText(text, "yes") && talkState[5])
4525 {
4526 talkState[1] = true;
4527 for(int8_t i = 2; i <= 12; i++)
4528 talkState[i] = false;
4529
4530 IOLoginData::getInstance()->setPassword(managerNumber, managerString);
4531 msg << "Your password has been changed.";
4532 }
4533 else if(checkText(text, "no") && talkState[5])
4534 {
4535 talkState[1] = true;
4536 for(int8_t i = 2; i <= 12; i++)
4537 talkState[i] = false;
4538
4539 msg << "Then not.";
4540 }
4541 else if(checkText(text, "character") && talkState[1])
4542 {
4543 if(account.charList.size() <= 15)
4544 {
4545 talkState[1] = false;
4546 talkState[6] = true;
4547 msg << "What would you like as your character name?";
4548 }
4549 else
4550 {
4551 talkState[1] = true;
4552 for(int8_t i = 2; i <= 12; i++)
4553 talkState[i] = false;
4554
4555 msg << "Your account reach the limit of 15 players, you can 'delete' a character if you want to create a new one.";
4556 }
4557 }
4558 else if(talkState[6])
4559 {
4560 managerString = text;
4561 trimString(managerString);
4562 if(managerString.length() < 4)
4563 msg << "Your name you want is too short, please select a longer name.";
4564 else if(managerString.length() > 20)
4565 msg << "The name you want is too long, please select a shorter name.";
4566 else if(!isValidName(managerString))
4567 msg << "That name seems to contain invalid symbols, please choose another name.";
4568 else if(IOLoginData::getInstance()->playerExists(managerString, true))
4569 msg << "A player with that name already exists, please choose another name.";
4570 else
4571 {
4572 std::string tmp = asLowerCaseString(managerString);
4573 if(tmp.substr(0, 4) != "god " && tmp.substr(0, 3) != "cm " && tmp.substr(0, 3) != "gm ")
4574 {
4575 talkState[6] = false;
4576 talkState[7] = true;
4577 msg << managerString << ", are you sure?";
4578 }
4579 else
4580 msg << "Your character is not a staff member, please tell me another name!";
4581 }
4582 }
4583 else if(checkText(text, "no") && talkState[7])
4584 {
4585 talkState[6] = true;
4586 talkState[7] = false;
4587 msg << "What else would you like to name your character?";
4588 }
4589 else if(checkText(text, "yes") && talkState[7])
4590 {
4591 talkState[7] = false;
4592 talkState[8] = true;
4593 msg << "Should your character be a 'male' or a 'female'.";
4594 }
4595 else if(talkState[8] && (checkText(text, "female") || checkText(text, "male")))
4596 {
4597 talkState[8] = false;
4598 talkState[9] = true;
4599 if(checkText(text, "female"))
4600 {
4601 msg << "A female, are you sure?";
4602 managerSex = PLAYERSEX_FEMALE;
4603 }
4604 else
4605 {
4606 msg << "A male, are you sure?";
4607 managerSex = PLAYERSEX_MALE;
4608 }
4609 }
4610 else if(checkText(text, "no") && talkState[9])
4611 {
4612 talkState[8] = true;
4613 talkState[9] = false;
4614 msg << "Tell me... would you like to be a 'male' or a 'female'?";
4615 }
4616 else if(checkText(text, "yes") && talkState[9])
4617 {
4618 if(g_config.getBool(ConfigManager::START_CHOOSEVOC))
4619 {
4620 talkState[9] = false;
4621 talkState[11] = true;
4622
4623 bool firstPart = true;
4624 for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4625 {
4626 if(it->first == it->second->getFromVocation() && it->first != 0)
4627 {
4628 if(firstPart)
4629 {
4630 msg << "What do you want to be... " << it->second->getDescription();
4631 firstPart = false;
4632 }
4633 else if(it->first - 1 != 0)
4634 msg << ", " << it->second->getDescription();
4635 else
4636 msg << " or " << it->second->getDescription() << ".";
4637 }
4638 }
4639 }
4640 else if(!IOLoginData::getInstance()->playerExists(managerString, true))
4641 {
4642 talkState[1] = true;
4643 for(int8_t i = 2; i <= 12; i++)
4644 talkState[i] = false;
4645
4646 if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4647 msg << "Your character has been created.";
4648 else
4649 msg << "Your character couldn't be created, please try again.";
4650 }
4651 else
4652 {
4653 talkState[6] = true;
4654 talkState[9] = false;
4655 msg << "A player with that name already exists, please choose another name.";
4656 }
4657 }
4658 else if(talkState[11])
4659 {
4660 for(VocationsMap::iterator it = Vocations::getInstance()->getFirstVocation(); it != Vocations::getInstance()->getLastVocation(); ++it)
4661 {
4662 std::string tmp = asLowerCaseString(it->second->getName());
4663 if(checkText(text, tmp) && it != Vocations::getInstance()->getLastVocation() && it->first == it->second->getFromVocation() && it->first != 0)
4664 {
4665 msg << "So you would like to be " << it->second->getDescription() << "... are you sure?";
4666 managerNumber2 = it->first;
4667 talkState[11] = false;
4668 talkState[12] = true;
4669 }
4670 }
4671
4672 if(msg.str().length() == 17)
4673 msg << "I don't understand what vocation you would like to be... could you please repeat it?";
4674 }
4675 else if(checkText(text, "yes") && talkState[12])
4676 {
4677 if(!IOLoginData::getInstance()->playerExists(managerString, true))
4678 {
4679 talkState[1] = true;
4680 for(int8_t i = 2; i <= 12; i++)
4681 talkState[i] = false;
4682
4683 if(IOLoginData::getInstance()->createCharacter(managerNumber, managerString, managerNumber2, (uint16_t)managerSex))
4684 msg << "Your character has been created.";
4685 else
4686 msg << "Your character couldn't be created, please try again.";
4687 }
4688 else
4689 {
4690 talkState[6] = true;
4691 talkState[9] = false;
4692 msg << "A player with that name already exists, please choose another name.";
4693 }
4694 }
4695 else if(checkText(text, "no") && talkState[12])
4696 {
4697 talkState[11] = true;
4698 talkState[12] = false;
4699 msg << "No? Then what would you like to be?";
4700 }
4701 else if(checkText(text, "recovery key") && talkState[1])
4702 {
4703 talkState[1] = false;
4704 talkState[10] = true;
4705 msg << "Would you like a recovery key?";
4706 }
4707 else if(checkText(text, "yes") && talkState[10])
4708 {
4709 if(account.recoveryKey != "0")
4710 msg << "Sorry, you already have a recovery key, for security reasons I may not give you a new one.";
4711 else
4712 {
4713 managerString = generateRecoveryKey(4, 4);
4714 IOLoginData::getInstance()->setRecoveryKey(managerNumber, managerString);
4715 msg << "Your recovery key is: " << managerString << ".";
4716 }
4717
4718 talkState[1] = true;
4719 for(int8_t i = 2; i <= 12; i++)
4720 talkState[i] = false;
4721 }
4722 else if(checkText(text, "no") && talkState[10])
4723 {
4724 msg << "Then not.";
4725 talkState[1] = true;
4726 for(int8_t i = 2; i <= 12; i++)
4727 talkState[i] = false;
4728 }
4729 else
4730 msg << "Please read the latest message that I have specified, I don't understand the current requested action.";
4731
4732 break;
4733 }
4734 case MANAGER_NEW:
4735 {
4736 if(checkText(text, "account") && !talkState[1])
4737 {
4738 msg << "What would you like your password to be?";
4739 talkState[1] = true;
4740 talkState[2] = true;
4741 }
4742 else if(talkState[2])
4743 {
4744 std::string tmp = text;
4745 trimString(tmp);
4746 if(tmp.length() < 6)
4747 msg << "That password is too short, at least 6 digits are required. Please select a longer password.";
4748 else if(!isValidPassword(tmp))
4749 msg << "Your password contains invalid characters... please tell me another one.";
4750 else
4751 {
4752 talkState[3] = true;
4753 talkState[2] = false;
4754 managerString = tmp;
4755 msg << managerString << " is it? 'yes' or 'no'?";
4756 }
4757 }
4758 else if(checkText(text, "yes") && talkState[3])
4759 {
4760 if(g_config.getBool(ConfigManager::GENERATE_ACCOUNT_NUMBER))
4761 {
4762 do
4763 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));
4764 while(IOLoginData::getInstance()->accountNameExists(managerChar));
4765
4766 uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4767 if(id)
4768 {
4769 accountManager = MANAGER_ACCOUNT;
4770 managerNumber = id;
4771
4772 noSwap = talkState[1] = false;
4773 msg << "Your account has been created, you may manage it now, but remember your account name: '"
4774 << managerChar << "' and password: '" << managerString
4775 << "'! If the account name is too hard to remember, please note it somewhere.";
4776 }
4777 else
4778 msg << "Your account could not be created, please try again.";
4779
4780 for(int8_t i = 2; i <= 5; i++)
4781 talkState[i] = false;
4782 }
4783 else
4784 {
4785 msg << "What would you like your account name to be?";
4786 talkState[3] = false;
4787 talkState[4] = true;
4788 }
4789 }
4790 else if(checkText(text, "no") && talkState[3])
4791 {
4792 talkState[2] = true;
4793 talkState[3] = false;
4794 msg << "What would you like your password to be then?";
4795 }
4796 else if(talkState[4])
4797 {
4798 std::string tmp = text;
4799 trimString(tmp);
4800 if(tmp.length() < 3)
4801 msg << "That account name is too short, at least 3 digits are required. Please select a longer account name.";
4802 else if(tmp.length() > 25)
4803 msg << "That account name is too long, not more than 25 digits are required. Please select a shorter account name.";
4804 else if(!isValidAccountName(tmp))
4805 msg << "Your account name contains invalid characters, please choose another one.";
4806 else if(asLowerCaseString(tmp) == asLowerCaseString(managerString))
4807 msg << "Your account name cannot be same as password, please choose another one.";
4808 else
4809 {
4810 sprintf(managerChar, "%s", tmp.c_str());
4811 msg << managerChar << ", are you sure?";
4812 talkState[4] = false;
4813 talkState[5] = true;
4814 }
4815 }
4816 else if(checkText(text, "yes") && talkState[5])
4817 {
4818 if(!IOLoginData::getInstance()->accountNameExists(managerChar))
4819 {
4820 uint32_t id = (uint32_t)IOLoginData::getInstance()->createAccount(managerChar, managerString);
4821 if(id)
4822 {
4823 accountManager = MANAGER_ACCOUNT;
4824 managerNumber = id;
4825
4826 noSwap = talkState[1] = false;
4827 msg << "Your account has been created, you may manage it now, but remember your account name: '"
4828 << managerChar << "' and password: '" << managerString << "'!";
4829 }
4830 else
4831 msg << "Your account could not be created, please try again.";
4832
4833 for(int8_t i = 2; i <= 5; i++)
4834 talkState[i] = false;
4835 }
4836 else
4837 {
4838 msg << "An account with that name already exists, please try another account name.";
4839 talkState[4] = true;
4840 talkState[5] = false;
4841 }
4842 }
4843 else if(checkText(text, "no") && talkState[5])
4844 {
4845 talkState[5] = false;
4846 talkState[4] = true;
4847 msg << "What else would you like as your account name?";
4848 }
4849 else if(checkText(text, "recover") && !talkState[6])
4850 {
4851 talkState[6] = true;
4852 talkState[7] = true;
4853 msg << "What was your account name?";
4854 }
4855 else if(talkState[7])
4856 {
4857 managerString = text;
4858 if(IOLoginData::getInstance()->getAccountId(managerString, (uint32_t&)managerNumber))
4859 {
4860 talkState[7] = false;
4861 talkState[8] = true;
4862 msg << "What was your recovery key?";
4863 }
4864 else
4865 {
4866 msg << "Sorry, but account with such name doesn't exists.";
4867 talkState[6] = talkState[7] = false;
4868 }
4869 }
4870 else if(talkState[8])
4871 {
4872 managerString2 = text;
4873 if(IOLoginData::getInstance()->validRecoveryKey(managerNumber, managerString2) && managerString2 != "0")
4874 {
4875 sprintf(managerChar, "%s%d", g_config.getString(ConfigManager::SERVER_NAME).c_str(), random_range(100, 999));
4876 IOLoginData::getInstance()->setPassword(managerNumber, managerChar);
4877 msg << "Correct! Your new password is: " << managerChar << ".";
4878 }
4879 else
4880 msg << "Sorry, but this key doesn't match to account you gave me.";
4881
4882 talkState[7] = talkState[8] = false;
4883 }
4884 else
4885 msg << "Sorry, but I can't understand you, please try to repeat that.";
4886
4887 break;
4888 }
4889 default:
4890 return;
4891 break;
4892 }
4893
4894 sendTextMessage(MSG_STATUS_CONSOLE_BLUE, msg.str().c_str());
4895 if(!noSwap)
4896 sendTextMessage(MSG_STATUS_CONSOLE_ORANGE, "Hint: Type 'account' to manage your account and if you want to start over then type 'cancel'.");
4897}
4898
4899bool Player::isGuildInvited(uint32_t guildId) const
4900{
4901 for(InvitedToGuildsList::const_iterator it = invitedToGuildsList.begin(); it != invitedToGuildsList.end(); ++it)
4902 {
4903 if((*it) == guildId)
4904 return true;
4905 }
4906
4907 return false;
4908}
4909
4910void Player::leaveGuild()
4911{
4912 sendClosePrivate(CHANNEL_GUILD);
4913 guildLevel = GUILDLEVEL_NONE;
4914 guildId = rankId = 0;
4915 guildName = rankName = guildNick = "";
4916}
4917
4918bool Player::isPremium() const
4919{
4920 if(g_config.getBool(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium))
4921 return true;
4922
4923 return premiumDays;
4924}
4925
4926bool Player::setGuildLevel(GuildLevel_t newLevel, uint32_t rank/* = 0*/)
4927{
4928 std::string name;
4929 if(!IOGuild::getInstance()->getRankEx(rank, name, guildId, newLevel))
4930 return false;
4931
4932 guildLevel = newLevel;
4933 rankName = name;
4934 rankId = rank;
4935 return true;
4936}
4937
4938void Player::setGroupId(int32_t newId)
4939{
4940 if(Group* tmp = Groups::getInstance()->getGroup(newId))
4941 {
4942 groupId = newId;
4943 group = tmp;
4944 }
4945}
4946
4947void Player::setGroup(Group* newGroup)
4948{
4949 if(newGroup)
4950 {
4951 group = newGroup;
4952 groupId = group->getId();
4953 }
4954}
4955
4956PartyShields_t Player::getPartyShield(const Creature* creature) const
4957{
4958 const Player* player = creature->getPlayer();
4959 if(!player)
4960 return Creature::getPartyShield(creature);
4961
4962 if(Party* party = getParty())
4963 {
4964 if(party->getLeader() == player)
4965 {
4966 if(party->isSharedExperienceActive())
4967 {
4968 if(party->isSharedExperienceEnabled())
4969 return SHIELD_YELLOW_SHAREDEXP;
4970
4971 if(party->canUseSharedExperience(player))
4972 return SHIELD_YELLOW_NOSHAREDEXP;
4973
4974 return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
4975 }
4976
4977 return SHIELD_YELLOW;
4978 }
4979
4980 if(party->isPlayerMember(player))
4981 {
4982 if(party->isSharedExperienceActive())
4983 {
4984 if(party->isSharedExperienceEnabled())
4985 return SHIELD_BLUE_SHAREDEXP;
4986
4987 if(party->canUseSharedExperience(player))
4988 return SHIELD_BLUE_NOSHAREDEXP;
4989
4990 return SHIELD_BLUE_NOSHAREDEXP_BLINK;
4991 }
4992
4993 return SHIELD_BLUE;
4994 }
4995
4996 if(isInviting(player))
4997 return SHIELD_WHITEBLUE;
4998 }
4999
5000 if(player->isInviting(this))
5001 return SHIELD_WHITEYELLOW;
5002
5003 return SHIELD_NONE;
5004}
5005
5006bool Player::isInviting(const Player* player) const
5007{
5008 if(!player || !getParty() || getParty()->getLeader() != this)
5009 return false;
5010
5011 return getParty()->isPlayerInvited(player);
5012}
5013
5014bool Player::isPartner(const Player* player) const
5015{
5016 if(!player || !getParty() || !player->getParty())
5017 return false;
5018
5019 return (getParty() == player->getParty());
5020}
5021
5022void Player::sendPlayerPartyIcons(Player* player)
5023{
5024 sendCreatureShield(player);
5025 sendCreatureSkull(player);
5026}
5027
5028bool Player::addPartyInvitation(Party* party)
5029{
5030 if(!party)
5031 return false;
5032
5033 PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5034 if(it != invitePartyList.end())
5035 return false;
5036
5037 invitePartyList.push_back(party);
5038 return true;
5039}
5040
5041bool Player::removePartyInvitation(Party* party)
5042{
5043 if(!party)
5044 return false;
5045
5046 PartyList::iterator it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
5047 if(it != invitePartyList.end())
5048 {
5049 invitePartyList.erase(it);
5050 return true;
5051 }
5052 return false;
5053}
5054
5055void Player::clearPartyInvitations()
5056{
5057 if(invitePartyList.empty())
5058 return;
5059
5060 PartyList list;
5061 for(PartyList::iterator it = invitePartyList.begin(); it != invitePartyList.end(); ++it)
5062 list.push_back(*it);
5063
5064 invitePartyList.clear();
5065 for(PartyList::iterator it = list.begin(); it != list.end(); ++it)
5066 (*it)->removeInvite(this);
5067}
5068
5069void Player::increaseCombatValues(int32_t& min, int32_t& max, bool useCharges, bool countWeapon)
5070{
5071 if(min > 0)
5072 min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_HEALING));
5073 else
5074 min = (int32_t)(min * vocation->getMultiplier(MULTIPLIER_MAGIC));
5075
5076 if(max > 0)
5077 max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_HEALING));
5078 else
5079 max = (int32_t)(max * vocation->getMultiplier(MULTIPLIER_MAGIC));
5080
5081 Item* weapon = NULL;
5082 if(!countWeapon)
5083 weapon = getWeapon();
5084
5085 Item* item = NULL;
5086 int32_t minValue = 0, maxValue = 0;
5087 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5088 {
5089 if(!(item = getInventoryItem((slots_t)i)) || (g_moveEvents->hasEquipEvent(item)
5090 && !isItemAbilityEnabled((slots_t)i)))
5091 continue;
5092
5093 const ItemType& it = Item::items[item->getID()];
5094 if(min > 0)
5095 {
5096 minValue += it.abilities.increment[HEALING_VALUE];
5097 if(it.abilities.increment[HEALING_PERCENT])
5098 min = (int32_t)std::ceil((double)(min * it.abilities.increment[HEALING_PERCENT]) / 100.);
5099 }
5100 else
5101 {
5102 minValue -= it.abilities.increment[MAGIC_VALUE];
5103 if(it.abilities.increment[MAGIC_PERCENT])
5104 min = (int32_t)std::ceil((double)(min * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5105 }
5106
5107 if(max > 0)
5108 {
5109 maxValue += it.abilities.increment[HEALING_VALUE];
5110 if(it.abilities.increment[HEALING_PERCENT])
5111 max = (int32_t)std::ceil((double)(max * it.abilities.increment[HEALING_PERCENT]) / 100.);
5112 }
5113 else
5114 {
5115 maxValue -= it.abilities.increment[MAGIC_VALUE];
5116 if(it.abilities.increment[MAGIC_PERCENT])
5117 max = (int32_t)std::ceil((double)(max * it.abilities.increment[MAGIC_PERCENT]) / 100.);
5118 }
5119
5120 bool removeCharges = false;
5121 for(int32_t j = INCREMENT_FIRST; j <= INCREMENT_LAST; ++j)
5122 {
5123 if(!it.abilities.increment[(Increment_t)j])
5124 continue;
5125
5126 removeCharges = true;
5127 break;
5128 }
5129
5130 if(useCharges && removeCharges && item != weapon && item->hasCharges())
5131 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5132 }
5133
5134 min += minValue;
5135 max += maxValue;
5136}
5137
5138bool Player::transferMoneyTo(const std::string& name, uint64_t amount)
5139{
5140 if(!g_config.getBool(ConfigManager::BANK_SYSTEM) || amount > balance)
5141 return false;
5142
5143 Player* target = g_game.getPlayerByNameEx(name);
5144 if(!target)
5145 return false;
5146
5147 balance -= amount;
5148 target->balance += amount;
5149 if(target->isVirtual())
5150 {
5151 IOLoginData::getInstance()->savePlayer(target);
5152 delete target;
5153 }
5154
5155 return true;
5156}
5157
5158void Player::sendCritical() const
5159{
5160 if(g_config.getBool(ConfigManager::DISPLAY_CRITICAL_HIT))
5161 g_game.addAnimatedText(getPosition(), TEXTCOLOR_DARKRED, "CRITICAL!");
5162}
5163
5164void Player::setSetsAttributesList(uint32_t setId, ItemAttributes_t type, int32_t value, int32_t attrType)
5165{
5166 setsAttributesList[setId][type][attrType] += value;
5167}
5168
5169int32_t Player::getAllPlayerBonusType(ItemAttributes_t type, bool ignoreWeapons/*= false*/, bool useCharges/*= false*/) const
5170{
5171 int32_t bonusValue = getSetsAttribute(type);
5172 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5173 {
5174 Item* item = getInventoryItem((slots_t)i);
5175 if(item)
5176 {
5177 if(ignoreWeapons && item->isWeapon())
5178 continue;
5179
5180 const ItemType& it = Item::items[item->getID()];
5181 int32_t value = 0;
5182 if(item->isLegendary(it) && it.legendaryBonus.attributes[type])
5183 value += it.legendaryBonus.attributes[type];
5184
5185 if(value != 0 && useCharges && item->hasCharges() && type > ITEM_ATTACK_SPEED)
5186 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5187
5188 bonusValue += value;
5189 }
5190 }
5191 return bonusValue;
5192}
5193
5194int32_t Player::getSetsAttribute(ItemAttributes_t type, int32_t attrType) const
5195{
5196 int32_t value = 0;
5197 std::string key = getAttributeName(type);
5198 if(!key.empty())
5199 {
5200 key.clear();
5201 switch(type)
5202 {
5203 case ITEM_MONSTER_DEFENSE:
5204 case ITEM_MONSTER_DAMAGE:
5205 key = getMonsterClassName((MonsterClass_t)attrType);
5206 break;
5207 case ITEM_ABSORB:
5208 key = getCombatName((CombatType_t)attrType) + "Absorb";
5209 break;
5210 case ITEM_INCREMENT:
5211 key = getCombatName((CombatType_t)attrType) + "Increment";
5212 break;
5213 case ITEM_SKILL:
5214 key = getSkillName(attrType, false);
5215 break;
5216 case ITEM_REGENERATION:
5217 {
5218 if(attrType == STAT_MAXMANA)
5219 key = "mana";
5220 else if(attrType == STAT_MAXHEALTH)
5221 key = "health";
5222 }
5223 default:
5224 break;
5225 }
5226
5227 if(!key.empty())
5228 {
5229 key += "Upgrade";
5230
5231 for(uint16_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5232 {
5233 Item* item = getInventoryItem((slots_t)slot);
5234 if(!item)
5235 continue;
5236
5237 const int32_t* v = item->getIntegerAttribute(key);
5238 if(v && *v != 0)
5239 value += *v;
5240 }
5241 }
5242 }
5243
5244 /*if(setsAttributesList.empty())
5245 return value;
5246
5247 for(SetsAttributesList::const_iterator it = setsAttributesList.begin(); it != setsAttributesList.end(); ++it)
5248 {
5249 if(it->second.empty())
5250 continue;
5251
5252 for(AttributesListMap::const_iterator _it = it->second.begin(); _it != it->second.end(); ++_it)
5253 {
5254 if(_it->first != type)
5255 continue;
5256
5257 for(std::map<int32_t, int32_t>::const_iterator __it = _it->second.begin(); __it != _it->second.end(); ++__it)
5258 {
5259 if(__it->first != attrType)
5260 continue;
5261
5262 value += __it->second;
5263 }
5264 }
5265 }*/
5266
5267 return value;
5268}
5269
5270void Player::executeAbsorb(CombatType_t combatType, int32_t& damage)
5271{
5272 int32_t _value = getSetsAttribute(ITEM_ABSORB, combatType);
5273 if(_value != 0)
5274 damage -= (int32_t)std::ceil((double)(damage * _value) / 100.);
5275
5276 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5277 {
5278 Item* item = getInventoryItem((slots_t)slot);
5279 if(!item)
5280 continue;
5281
5282 const ItemType& it = Item::items[item->getID()];
5283 if(it.abilities.absorb[combatType])
5284 {
5285 damage -= (int32_t)std::ceil((double)(damage * it.abilities.absorb[combatType]) / 100.);
5286 if(item->hasCharges())
5287 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5288 }
5289
5290 if(it.legendaryItem && item->isLegendary(it))
5291 std::cout << "Item with name: " << it.name << " are a legendary item and has " << it.legendaryBonus.absorb[combatType] << " absorb of " << getCombatName(combatType) << "." << std::endl;
5292
5293 if(item->isLegendary(it) && it.legendaryBonus.absorb[combatType] != 0)
5294 {
5295 int32_t absorbValue = it.legendaryBonus.absorb[combatType];
5296 if(absorbValue > 100)
5297 absorbValue = (absorbValue - 100) - ((absorbValue - 100) * 2);
5298
5299 damage -= (int32_t)std::ceil((double)(damage * absorbValue) / 100);
5300 }
5301 }
5302}
5303
5304int32_t Player::checkMonsterClassIncrement(std::string name) const
5305{
5306 MonsterClass_t monsterClass = getMonsterClass(name);
5307 int32_t bonusIncrease = getSetsAttribute(ITEM_MONSTER_DAMAGE, monsterClass);
5308 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5309 {
5310 Item* item = getInventoryItem((slots_t)slot);
5311 if(!item)
5312 continue;
5313
5314 const ItemType& it = Item::items[item->getID()];
5315 int32_t value = 0;
5316 if(item->isLegendary(it) && it.legendaryBonus.monsterIncrement[monsterClass] != 0)
5317 value += it.legendaryBonus.monsterIncrement[monsterClass];
5318
5319 if(value != 0 && item->hasCharges())
5320 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5321
5322 if(value != 0)
5323 bonusIncrease += value;
5324 }
5325
5326 return std::max(-95, bonusIncrease);
5327}
5328
5329int32_t Player::checkMonsterClassDefense(std::string name) const
5330{
5331 MonsterClass_t monsterClass = getMonsterClass(name);
5332 int32_t bonusDefense = getSetsAttribute(ITEM_MONSTER_DEFENSE, monsterClass);
5333 for(int32_t slot = SLOT_FIRST; slot < SLOT_LAST; ++slot)
5334 {
5335 Item* item = getInventoryItem((slots_t)slot);
5336 if(!item)
5337 continue;
5338
5339 const ItemType& it = Item::items[item->getID()];
5340 int32_t value = 0;
5341 if(item->isLegendary(it) && it.legendaryBonus.defenseMonsterAttack[monsterClass] != 0)
5342 value += it.legendaryBonus.defenseMonsterAttack[monsterClass];
5343
5344 if(value != 0 && item->hasCharges())
5345 g_game.transformItem(item, item->getID(), std::max((int32_t)0, (int32_t)item->getCharges() - 1));
5346
5347 if(value != 0)
5348 bonusDefense += value;
5349 }
5350
5351 return std::max(-95, bonusDefense);
5352}
5353
5354int32_t Player::getBonusDropItem(uint16_t id) const
5355{
5356 int32_t bonusDrop = 0;
5357 for(int32_t i = SLOT_FIRST; i < SLOT_LAST; ++i)
5358 {
5359 Item* item = getInventoryItem((slots_t)i);
5360 if(item)
5361 {
5362 const ItemType& it = Item::items[item->getID()];
5363 if(item->isLegendary(it))
5364 {
5365 StringVec vec = explodeString(it.legendaryBonus.listItems, ";");
5366 for(uint16_t i = 0; i < (vec.size() / 2); ++i)
5367 {
5368 if(vec[i * 2].empty())
5369 continue;
5370
5371 if(atoi(vec[i * 2].c_str()) == id)
5372 bonusDrop += atoi(vec[i * 2 + 1].c_str());
5373 }
5374 }
5375 }
5376 }
5377 return bonusDrop;
5378}
5379
5380void Player::checkDeathHit(Creature* target)
5381{
5382 if(random_range(1, 100000) > getAllPlayerBonusType(ITEM_DEATH_HIT) || !target || target->isImmune(COMBAT_DEATHDAMAGE))
5383 return;
5384
5385 CombatParams param;
5386 param.combatType = COMBAT_DEATHDAMAGE;
5387 int32_t health = -target->getHealth();
5388 Combat::doCombatHealth(this, target, health, health, param);
5389
5390 g_game.addMagicEffect(target->getPosition(), MAGIC_EFFECT_MORT_AREA);
5391}
5392
5393void Player::updateKillAchievement(Creature* target)
5394{
5395 if(target->getPlayer())
5396 {
5397 if(target == this)
5398 updateAchievement(ACHIEVEMENT_KILL_YOURSELF);
5399 else
5400 {
5401 updateAchievement(ACHIEVEMENT_KILL_1_PLAYER);
5402 updateAchievement(ACHIEVEMENT_KILL_25_PLAYER);
5403 updateAchievement(ACHIEVEMENT_KILL_100_PLAYER);
5404 updateAchievement(ACHIEVEMENT_KILL_1000_PLAYER);
5405 if(target->getSkull() == SKULL_WHITE)
5406 updateAchievement(ACHIEVEMENT_KILL_1_PLAYER_WITH_WHITE_SKULL);
5407 }
5408 }
5409 else if(target->getMonster())
5410 {
5411 updateAchievement(ACHIEVEMENT_KILL_100_MONSTER);
5412 updateAchievement(ACHIEVEMENT_KILL_10000_MONSTER);
5413 updateAchievement(ACHIEVEMENT_KILL_100000_MONSTER);
5414 const std::string name = target->getName();
5415 if(name == "Wolf")
5416 updateAchievement(ACHIEVEMENT_KILL_WOLF);
5417 else if(name == "Eventador")
5418 updateAchievement(ACHIEVEMENT_KILL_EVENTADOR);
5419 }
5420}
5421
5422void Player::addAchievementPoints(AchievementBonus_t key, uint16_t strength)
5423{
5424 if(achievementPoints < strength)
5425 {
5426 sendTextMessage(MSG_INFO_DESCR, "You don't have enough achievement points!");
5427 return;
5428 }
5429
5430 uint16_t max = 0;
5431 switch(key)
5432 {
5433 case ACHIEVEMENT_STRENGTH:
5434 case ACHIEVEMENT_POWER:
5435 case ACHIEVEMENT_WISDOM:
5436 max = 50;
5437 break;
5438
5439 case ACHIEVEMENT_DEFENSE:
5440 max = 20;
5441 break;
5442
5443 case ACHIEVEMENT_AGILITY:
5444 max = 30;
5445 break;
5446
5447 case ACHIEVEMENT_HEALTH:
5448 max = 100;
5449 break;
5450
5451 default:
5452 return;
5453 }
5454
5455 if(achievementBonus[key] + strength >= max)
5456 {
5457 sendTextMessage(MSG_INFO_DESCR, "You reached maximum points of this skill.");
5458 return;
5459 }
5460
5461 achievementPoints -= strength;
5462 achievementBonus[key] += strength;
5463 if(key == ACHIEVEMENT_HEALTH)
5464 sendStats();
5465
5466 std::string description = "Your ";
5467 switch(key)
5468 {
5469 case ACHIEVEMENT_STRENGTH:
5470 description += "strength";
5471 break;
5472
5473 case ACHIEVEMENT_POWER:
5474 description += "power";
5475 break;
5476
5477 case ACHIEVEMENT_WISDOM:
5478 description += "wisdom";
5479 break;
5480
5481 case ACHIEVEMENT_DEFENSE:
5482 description += "defense";
5483 break;
5484
5485 case ACHIEVEMENT_AGILITY:
5486 description += "agility";
5487 break;
5488
5489 case ACHIEVEMENT_HEALTH:
5490 description += "health";
5491 break;
5492
5493 default:
5494 return;
5495 }
5496
5497 std::ostringstream ss;
5498 ss << achievementBonus[key];
5499 description += " grow. Your current level is " + ss.str() + ".";
5500 sendTextMessage(MSG_INFO_DESCR, description);
5501}
5502
5503void Player::clearAchievementPoints()
5504{
5505 memset(achievementBonus, 0, sizeof(achievementBonus));
5506 sendStats();
5507
5508 uint16_t points = 0;
5509 for(uint16_t id = ACHIEVEMENT_FIRST; id <= ACHIEVEMENT_LAST; ++id)
5510 {
5511 if(achievements->gotAchievement((Achievement_t)id))
5512 points += achievements->getPoints((Achievement_t)id);
5513 }
5514
5515 std::ostringstream ss;
5516 ss << points;
5517 sendTextMessage(MSG_INFO_DESCR, "You reset your achievement points! Now you have " + ss.str() + " achievement points!");
5518}