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