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