Advertisement
Guest User

player.cpp

a guest
Feb 8th, 2018
128
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 117.05 KB | None | 0 0
  1. /**
  2. * The Forgotten Server - a free and open-source MMORPG server emulator
  3. * Copyright (C) 2017 Mark Samman <mark.samman@gmail.com>
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  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 along
  16. * with this program; if not, write to the Free Software Foundation, Inc.,
  17. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  18. */
  19.  
  20. #include "otpch.h"
  21.  
  22. #include <bitset>
  23.  
  24. #include "bed.h"
  25. #include "chat.h"
  26. #include "combat.h"
  27. #include "configmanager.h"
  28. #include "creatureevent.h"
  29. #include "events.h"
  30. #include "game.h"
  31. #include "iologindata.h"
  32. #include "monster.h"
  33. #include "movement.h"
  34. #include "scheduler.h"
  35. #include "weapons.h"
  36.  
  37. extern ConfigManager g_config;
  38. extern Game g_game;
  39. extern Chat* g_chat;
  40. extern Vocations g_vocations;
  41. extern MoveEvents* g_moveEvents;
  42. extern Weapons* g_weapons;
  43. extern CreatureEvents* g_creatureEvents;
  44. extern Events* g_events;
  45.  
  46. MuteCountMap Player::muteCountMap;
  47.  
  48. uint32_t Player::playerAutoID = 0x10000000;
  49.  
  50. void Player::addAutoLootItem(uint16_t itemId)
  51. {
  52. autoLootList.insert(itemId);
  53. }
  54.  
  55. void Player::removeAutoLootItem(uint16_t itemId)
  56. {
  57. autoLootList.erase(itemId);
  58. }
  59.  
  60. bool Player::getAutoLootItem(const uint16_t itemId)
  61. {
  62. return autoLootList.find(itemId) != autoLootList.end();
  63. }
  64.  
  65.  
  66. Player::Player(ProtocolGame_ptr p) :
  67. Creature(), lastPing(OTSYS_TIME()), lastPong(lastPing), inbox(new Inbox(ITEM_INBOX)), client(std::move(p))
  68. {
  69. inbox->incrementReferenceCounter();
  70. }
  71.  
  72. Player::~Player()
  73. {
  74. for (Item* item : inventory) {
  75. if (item) {
  76. item->setParent(nullptr);
  77. item->decrementReferenceCounter();
  78. }
  79. }
  80.  
  81. for (const auto& it : depotLockerMap) {
  82. it.second->removeInbox(inbox);
  83. it.second->decrementReferenceCounter();
  84. }
  85.  
  86. for (const auto& it : rewardMap) {
  87. it.second->decrementReferenceCounter();
  88. }
  89.  
  90. inbox->decrementReferenceCounter();
  91.  
  92. setWriteItem(nullptr);
  93. setEditHouse(nullptr);
  94. }
  95.  
  96. bool Player::setVocation(uint16_t vocId)
  97. {
  98. Vocation* voc = g_vocations.getVocation(vocId);
  99. if (!voc) {
  100. return false;
  101. }
  102. vocation = voc;
  103.  
  104. Condition* condition = getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT);
  105. if (condition) {
  106. condition->setParam(CONDITION_PARAM_HEALTHGAIN, vocation->getHealthGainAmount());
  107. condition->setParam(CONDITION_PARAM_HEALTHTICKS, vocation->getHealthGainTicks() * 1000);
  108. condition->setParam(CONDITION_PARAM_MANAGAIN, vocation->getManaGainAmount());
  109. condition->setParam(CONDITION_PARAM_MANATICKS, vocation->getManaGainTicks() * 1000);
  110. }
  111. return true;
  112. }
  113.  
  114. bool Player::isPushable() const
  115. {
  116. if (hasFlag(PlayerFlag_CannotBePushed)) {
  117. return false;
  118. }
  119. return Creature::isPushable();
  120. }
  121.  
  122. std::string Player::getDescription(int32_t lookDistance) const
  123. {
  124. std::ostringstream s;
  125.  
  126. if (lookDistance == -1) {
  127. s << "yourself.";
  128.  
  129. if (group->access) {
  130. s << " You are " << group->name << '.';
  131. } else if (vocation->getId() != VOCATION_NONE) {
  132. s << " You are " << vocation->getVocDescription() << '.';
  133. } else {
  134. s << " You have no vocation.";
  135. }
  136. } else {
  137. s << name;
  138. if (!group->access) {
  139. s << " (Level " << level << ')';
  140. }
  141. s << '.';
  142.  
  143. if (sex == PLAYERSEX_FEMALE) {
  144. s << " She";
  145. } else {
  146. s << " He";
  147. }
  148.  
  149. if (group->access) {
  150. s << " is " << group->name << '.';
  151. } else if (vocation->getId() != VOCATION_NONE) {
  152. s << " is " << vocation->getVocDescription() << '.';
  153. } else {
  154. s << " has no vocation.";
  155. }
  156. }
  157.  
  158. if (party) {
  159. if (lookDistance == -1) {
  160. s << " Your party has ";
  161. } else if (sex == PLAYERSEX_FEMALE) {
  162. s << " She is in a party with ";
  163. } else {
  164. s << " He is in a party with ";
  165. }
  166.  
  167. size_t memberCount = party->getMemberCount() + 1;
  168. if (memberCount == 1) {
  169. s << "1 member and ";
  170. } else {
  171. s << memberCount << " members and ";
  172. }
  173.  
  174. size_t invitationCount = party->getInvitationCount();
  175. if (invitationCount == 1) {
  176. s << "1 pending invitation.";
  177. } else {
  178. s << invitationCount << " pending invitations.";
  179. }
  180. }
  181.  
  182. if (guild && guildRank) {
  183. if (lookDistance == -1) {
  184. s << " You are ";
  185. } else if (sex == PLAYERSEX_FEMALE) {
  186. s << " She is ";
  187. } else {
  188. s << " He is ";
  189. }
  190.  
  191. s << guildRank->name << " of the " << guild->getName();
  192. if (!guildNick.empty()) {
  193. s << " (" << guildNick << ')';
  194. }
  195.  
  196. size_t memberCount = guild->getMemberCount();
  197. if (memberCount == 1) {
  198. s << ", which has 1 member, " << guild->getMembersOnline().size() << " of them online.";
  199. } else {
  200. s << ", which has " << memberCount << " members, " << guild->getMembersOnline().size() << " of them online.";
  201. }
  202. }
  203. return s.str();
  204. }
  205.  
  206. Item* Player::getInventoryItem(slots_t slot) const
  207. {
  208. if (slot < CONST_SLOT_FIRST || slot > CONST_SLOT_LAST) {
  209. return nullptr;
  210. }
  211. return inventory[slot];
  212. }
  213.  
  214. void Player::addConditionSuppressions(uint32_t conditions)
  215. {
  216. conditionSuppressions |= conditions;
  217. }
  218.  
  219. void Player::removeConditionSuppressions(uint32_t conditions)
  220. {
  221. conditionSuppressions &= ~conditions;
  222. }
  223.  
  224. Item* Player::getWeapon(slots_t slot, bool ignoreAmmo) const
  225. {
  226. Item* item = inventory[slot];
  227. if (!item) {
  228. return nullptr;
  229. }
  230.  
  231. WeaponType_t weaponType = item->getWeaponType();
  232. if (weaponType == WEAPON_NONE || weaponType == WEAPON_SHIELD || weaponType == WEAPON_AMMO) {
  233. return nullptr;
  234. }
  235.  
  236. if (!ignoreAmmo && weaponType == WEAPON_DISTANCE) {
  237. const ItemType& it = Item::items[item->getID()];
  238. if (it.ammoType != AMMO_NONE) {
  239. Item* ammoItem = inventory[CONST_SLOT_AMMO];
  240. if (!ammoItem || ammoItem->getAmmoType() != it.ammoType) {
  241. return nullptr;
  242. }
  243. item = ammoItem;
  244. }
  245. }
  246. return item;
  247. }
  248.  
  249. Item* Player::getWeapon(bool ignoreAmmo/* = false*/) const
  250. {
  251. Item* item = getWeapon(CONST_SLOT_LEFT, ignoreAmmo);
  252. if (item) {
  253. return item;
  254. }
  255.  
  256. item = getWeapon(CONST_SLOT_RIGHT, ignoreAmmo);
  257. if (item) {
  258. return item;
  259. }
  260. return nullptr;
  261. }
  262.  
  263. WeaponType_t Player::getWeaponType() const
  264. {
  265. Item* item = getWeapon();
  266. if (!item) {
  267. return WEAPON_NONE;
  268. }
  269. return item->getWeaponType();
  270. }
  271.  
  272. int32_t Player::getWeaponSkill(const Item* item) const
  273. {
  274. if (!item) {
  275. return getSkillLevel(SKILL_FIST);
  276. }
  277.  
  278. int32_t attackSkill;
  279.  
  280. WeaponType_t weaponType = item->getWeaponType();
  281. switch (weaponType) {
  282. case WEAPON_SWORD: {
  283. attackSkill = getSkillLevel(SKILL_SWORD);
  284. break;
  285. }
  286.  
  287. case WEAPON_CLUB: {
  288. attackSkill = getSkillLevel(SKILL_CLUB);
  289. break;
  290. }
  291.  
  292. case WEAPON_AXE: {
  293. attackSkill = getSkillLevel(SKILL_AXE);
  294. break;
  295. }
  296.  
  297. case WEAPON_DISTANCE: {
  298. attackSkill = getSkillLevel(SKILL_DISTANCE);
  299. break;
  300. }
  301.  
  302. default: {
  303. attackSkill = 0;
  304. break;
  305. }
  306. }
  307. return attackSkill;
  308. }
  309.  
  310. int32_t Player::getArmor() const
  311. {
  312. int32_t armor = 0;
  313.  
  314. static const slots_t armorSlots[] = {CONST_SLOT_HEAD, CONST_SLOT_NECKLACE, CONST_SLOT_ARMOR, CONST_SLOT_LEGS, CONST_SLOT_FEET, CONST_SLOT_RING};
  315. for (slots_t slot : armorSlots) {
  316. Item* inventoryItem = inventory[slot];
  317. if (inventoryItem) {
  318. armor += inventoryItem->getArmor();
  319. }
  320. }
  321. return static_cast<int32_t>(armor * vocation->armorMultiplier);
  322. }
  323.  
  324. void Player::getShieldAndWeapon(const Item*& shield, const Item*& weapon) const
  325. {
  326. shield = nullptr;
  327. weapon = nullptr;
  328.  
  329. for (uint32_t slot = CONST_SLOT_RIGHT; slot <= CONST_SLOT_LEFT; slot++) {
  330. Item* item = inventory[slot];
  331. if (!item) {
  332. continue;
  333. }
  334.  
  335. switch (item->getWeaponType()) {
  336. case WEAPON_NONE:
  337. break;
  338.  
  339. case WEAPON_SHIELD: {
  340. if (!shield || (shield && item->getDefense() > shield->getDefense())) {
  341. shield = item;
  342. }
  343. break;
  344. }
  345.  
  346. default: { // weapons that are not shields
  347. weapon = item;
  348. break;
  349. }
  350. }
  351. }
  352. }
  353.  
  354. int32_t Player::getDefense() const
  355. {
  356. int32_t defenseSkill = getSkillLevel(SKILL_FIST);
  357. int32_t defenseValue = 7;
  358. const Item* weapon;
  359. const Item* shield;
  360. getShieldAndWeapon(shield, weapon);
  361.  
  362. if (weapon) {
  363. defenseValue = weapon->getDefense() + weapon->getExtraDefense();
  364. defenseSkill = getWeaponSkill(weapon);
  365. }
  366.  
  367. if (shield) {
  368. defenseValue = weapon != nullptr ? shield->getDefense() + weapon->getExtraDefense() : shield->getDefense();
  369. defenseSkill = getSkillLevel(SKILL_SHIELD);
  370. }
  371.  
  372. if (defenseSkill == 0) {
  373. switch (fightMode) {
  374. case FIGHTMODE_ATTACK:
  375. case FIGHTMODE_BALANCED:
  376. return 1;
  377.  
  378. case FIGHTMODE_DEFENSE:
  379. return 2;
  380. }
  381. }
  382.  
  383. return (defenseSkill / 4. + 2.23) * defenseValue * 0.15 * getDefenseFactor() * vocation->defenseMultiplier;
  384. }
  385.  
  386. float Player::getAttackFactor() const
  387. {
  388. switch (fightMode) {
  389. case FIGHTMODE_ATTACK: return 1.0f;
  390. case FIGHTMODE_BALANCED: return 1.2f;
  391. case FIGHTMODE_DEFENSE: return 2.0f;
  392. default: return 1.0f;
  393. }
  394. }
  395.  
  396. float Player::getDefenseFactor() const
  397. {
  398. switch (fightMode) {
  399. case FIGHTMODE_ATTACK: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.5f : 1.0f;
  400. case FIGHTMODE_BALANCED: return (OTSYS_TIME() - lastAttack) < getAttackSpeed() ? 0.75f : 1.0f;
  401. case FIGHTMODE_DEFENSE: return 1.0f;
  402. default: return 1.0f;
  403. }
  404. }
  405.  
  406. uint16_t Player::getClientIcons() const
  407. {
  408. uint16_t icons = 0;
  409. for (Condition* condition : conditions) {
  410. if (!isSuppress(condition->getType())) {
  411. icons |= condition->getIcons();
  412. }
  413. }
  414.  
  415. if (pzLocked) {
  416. icons |= ICON_REDSWORDS;
  417. }
  418.  
  419. if (tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
  420. icons |= ICON_PIGEON;
  421.  
  422. // Don't show ICON_SWORDS if player is in protection zone.
  423. if (hasBitSet(ICON_SWORDS, icons)) {
  424. icons &= ~ICON_SWORDS;
  425. }
  426. }
  427.  
  428. // Game client debugs with 10 or more icons
  429. // so let's prevent that from happening.
  430. std::bitset<20> icon_bitset(static_cast<uint64_t>(icons));
  431. for (size_t pos = 0, bits_set = icon_bitset.count(); bits_set >= 10; ++pos) {
  432. if (icon_bitset[pos]) {
  433. icon_bitset.reset(pos);
  434. --bits_set;
  435. }
  436. }
  437. return icon_bitset.to_ulong();
  438. }
  439.  
  440. void Player::updateInventoryWeight()
  441. {
  442. if (hasFlag(PlayerFlag_HasInfiniteCapacity)) {
  443. return;
  444. }
  445.  
  446. inventoryWeight = 0;
  447. for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  448. const Item* item = inventory[i];
  449. if (item) {
  450. inventoryWeight += item->getWeight();
  451. }
  452. }
  453. }
  454.  
  455. void Player::addSkillAdvance(skills_t skill, uint64_t count)
  456. {
  457. uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
  458. uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  459. if (currReqTries >= nextReqTries) {
  460. //player has reached max skill
  461. return;
  462. }
  463.  
  464. g_events->eventPlayerOnGainSkillTries(this, skill, count);
  465. if (count == 0) {
  466. return;
  467. }
  468.  
  469. bool sendUpdateSkills = false;
  470. while ((skills[skill].tries + count) >= nextReqTries) {
  471. count -= nextReqTries - skills[skill].tries;
  472. skills[skill].level++;
  473. skills[skill].tries = 0;
  474. skills[skill].percent = 0;
  475.  
  476. std::ostringstream ss;
  477. ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
  478. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  479.  
  480. g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);
  481.  
  482. sendUpdateSkills = true;
  483. currReqTries = nextReqTries;
  484. nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  485. if (currReqTries >= nextReqTries) {
  486. count = 0;
  487. break;
  488. }
  489. }
  490.  
  491. skills[skill].tries += count;
  492.  
  493. uint32_t newPercent;
  494. if (nextReqTries > currReqTries) {
  495. newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
  496. } else {
  497. newPercent = 0;
  498. }
  499.  
  500. if (skills[skill].percent != newPercent) {
  501. skills[skill].percent = newPercent;
  502. sendUpdateSkills = true;
  503. }
  504.  
  505. if (sendUpdateSkills) {
  506. sendSkills();
  507. }
  508. }
  509.  
  510. void Player::setVarStats(stats_t stat, int32_t modifier)
  511. {
  512. varStats[stat] += modifier;
  513.  
  514. switch (stat) {
  515. case STAT_MAXHITPOINTS: {
  516. if (getHealth() > getMaxHealth()) {
  517. Creature::changeHealth(getMaxHealth() - getHealth());
  518. } else {
  519. g_game.addCreatureHealth(this);
  520. }
  521. break;
  522. }
  523.  
  524. case STAT_MAXMANAPOINTS: {
  525. if (getMana() > getMaxMana()) {
  526. Creature::changeMana(getMaxMana() - getMana());
  527. }
  528. break;
  529. }
  530.  
  531. default: {
  532. break;
  533. }
  534. }
  535. }
  536.  
  537. int32_t Player::getDefaultStats(stats_t stat) const
  538. {
  539. switch (stat) {
  540. case STAT_MAXHITPOINTS: return healthMax;
  541. case STAT_MAXMANAPOINTS: return manaMax;
  542. case STAT_MAGICPOINTS: return getBaseMagicLevel();
  543. default: return 0;
  544. }
  545. }
  546.  
  547. void Player::addContainer(uint8_t cid, Container* container)
  548. {
  549. if (cid > 0xF) {
  550. return;
  551. }
  552.  
  553. if (container->getID() == ITEM_BROWSEFIELD) {
  554. container->incrementReferenceCounter();
  555. }
  556.  
  557. auto it = openContainers.find(cid);
  558. if (it != openContainers.end()) {
  559. OpenContainer& openContainer = it->second;
  560. Container* oldContainer = openContainer.container;
  561. if (oldContainer->getID() == ITEM_BROWSEFIELD) {
  562. oldContainer->decrementReferenceCounter();
  563. }
  564.  
  565. openContainer.container = container;
  566. openContainer.index = 0;
  567. } else {
  568. OpenContainer openContainer;
  569. openContainer.container = container;
  570. openContainer.index = 0;
  571. openContainers[cid] = openContainer;
  572. }
  573. }
  574.  
  575. void Player::closeContainer(uint8_t cid)
  576. {
  577. auto it = openContainers.find(cid);
  578. if (it == openContainers.end()) {
  579. return;
  580. }
  581.  
  582. OpenContainer openContainer = it->second;
  583. Container* container = openContainer.container;
  584. openContainers.erase(it);
  585.  
  586. if (container && container->getID() == ITEM_BROWSEFIELD) {
  587. container->decrementReferenceCounter();
  588. }
  589. }
  590.  
  591. void Player::setContainerIndex(uint8_t cid, uint16_t index)
  592. {
  593. auto it = openContainers.find(cid);
  594. if (it == openContainers.end()) {
  595. return;
  596. }
  597. it->second.index = index;
  598. }
  599.  
  600. Container* Player::getContainerByID(uint8_t cid)
  601. {
  602. auto it = openContainers.find(cid);
  603. if (it == openContainers.end()) {
  604. return nullptr;
  605. }
  606. return it->second.container;
  607. }
  608.  
  609. int8_t Player::getContainerID(const Container* container) const
  610. {
  611. for (const auto& it : openContainers) {
  612. if (it.second.container == container) {
  613. return it.first;
  614. }
  615. }
  616. return -1;
  617. }
  618.  
  619. uint16_t Player::getContainerIndex(uint8_t cid) const
  620. {
  621. auto it = openContainers.find(cid);
  622. if (it == openContainers.end()) {
  623. return 0;
  624. }
  625. return it->second.index;
  626. }
  627.  
  628. bool Player::canOpenCorpse(uint32_t ownerId) const
  629. {
  630. return getID() == ownerId || (party && party->canOpenCorpse(ownerId));
  631. }
  632.  
  633. uint16_t Player::getLookCorpse() const
  634. {
  635. if (sex == PLAYERSEX_FEMALE) {
  636. return ITEM_FEMALE_CORPSE;
  637. } else {
  638. return ITEM_MALE_CORPSE;
  639. }
  640. }
  641.  
  642. void Player::addStorageValue(const uint32_t key, const int32_t value, const bool isLogin/* = false*/)
  643. {
  644. if (IS_IN_KEYRANGE(key, RESERVED_RANGE)) {
  645. if (IS_IN_KEYRANGE(key, OUTFITS_RANGE)) {
  646. outfits.emplace_back(
  647. value >> 16,
  648. value & 0xFF
  649. );
  650. return;
  651. } else if (IS_IN_KEYRANGE(key, MOUNTS_RANGE)) {
  652. // do nothing
  653. } else {
  654. std::cout << "Warning: unknown reserved key: " << key << " player: " << getName() << std::endl;
  655. return;
  656. }
  657. }
  658.  
  659. if (value != -1) {
  660. int32_t oldValue;
  661. getStorageValue(key, oldValue);
  662.  
  663. storageMap[key] = value;
  664.  
  665. if (!isLogin) {
  666. auto currentFrameTime = g_dispatcher.getDispatcherCycle();
  667. if (lastQuestlogUpdate != currentFrameTime && g_game.quests.isQuestStorage(key, value, oldValue)) {
  668. lastQuestlogUpdate = currentFrameTime;
  669. sendTextMessage(MESSAGE_EVENT_ADVANCE, "Your questlog has been updated.");
  670. }
  671. }
  672. } else {
  673. storageMap.erase(key);
  674. }
  675. }
  676.  
  677. bool Player::getStorageValue(const uint32_t key, int32_t& value) const
  678. {
  679. auto it = storageMap.find(key);
  680. if (it == storageMap.end()) {
  681. value = -1;
  682. return false;
  683. }
  684.  
  685. value = it->second;
  686. return true;
  687. }
  688.  
  689. bool Player::canSee(const Position& pos) const
  690. {
  691. if (!client) {
  692. return false;
  693. }
  694. return client->canSee(pos);
  695. }
  696.  
  697. bool Player::canSeeCreature(const Creature* creature) const
  698. {
  699. if (creature == this) {
  700. return true;
  701. }
  702.  
  703. if (creature->isInGhostMode() && !group->access) {
  704. return false;
  705. }
  706.  
  707. if (!creature->getPlayer() && !canSeeInvisibility() && creature->isInvisible()) {
  708. return false;
  709. }
  710. return true;
  711. }
  712.  
  713. bool Player::canWalkthrough(const Creature* creature) const
  714. {
  715. if (group->access || creature->isInGhostMode()) {
  716. return true;
  717. }
  718.  
  719. const Player* player = creature->getPlayer();
  720. if (!player) {
  721. return false;
  722. }
  723.  
  724. const Tile* playerTile = player->getTile();
  725. if (!playerTile || !playerTile->hasFlag(TILESTATE_PROTECTIONZONE)) {
  726. return false;
  727. }
  728.  
  729. const Item* playerTileGround = playerTile->getGround();
  730. if (!playerTileGround || !playerTileGround->hasWalkStack()) {
  731. return false;
  732. }
  733.  
  734. Player* thisPlayer = const_cast<Player*>(this);
  735. if ((OTSYS_TIME() - lastWalkthroughAttempt) > 2000) {
  736. thisPlayer->setLastWalkthroughAttempt(OTSYS_TIME());
  737. return false;
  738. }
  739.  
  740. if (creature->getPosition() != lastWalkthroughPosition) {
  741. thisPlayer->setLastWalkthroughPosition(creature->getPosition());
  742. return false;
  743. }
  744.  
  745. thisPlayer->setLastWalkthroughPosition(creature->getPosition());
  746. return true;
  747. }
  748.  
  749. bool Player::canWalkthroughEx(const Creature* creature) const
  750. {
  751. if (group->access) {
  752. return true;
  753. }
  754.  
  755. const Player* player = creature->getPlayer();
  756. if (!player) {
  757. return false;
  758. }
  759.  
  760. const Tile* playerTile = player->getTile();
  761. return playerTile && playerTile->hasFlag(TILESTATE_PROTECTIONZONE);
  762. }
  763.  
  764. void Player::onReceiveMail() const
  765. {
  766. if (isNearDepotBox()) {
  767. sendTextMessage(MESSAGE_EVENT_ADVANCE, "New mail has arrived.");
  768. }
  769. }
  770.  
  771. bool Player::isNearDepotBox() const
  772. {
  773. const Position& pos = getPosition();
  774. for (int32_t cx = -1; cx <= 1; ++cx) {
  775. for (int32_t cy = -1; cy <= 1; ++cy) {
  776. Tile* tile = g_game.map.getTile(pos.x + cx, pos.y + cy, pos.z);
  777. if (!tile) {
  778. continue;
  779. }
  780.  
  781. if (tile->hasFlag(TILESTATE_DEPOT)) {
  782. return true;
  783. }
  784. }
  785. }
  786. return false;
  787. }
  788.  
  789. DepotChest* Player::getDepotBox()
  790. {
  791. DepotChest* depotBoxs = new DepotChest(ITEM_DEPOT);
  792. depotBoxs->incrementReferenceCounter();
  793. depotBoxs->setMaxDepotItems(getMaxDepotItems()); //check each depotID, if depot limit is 1000, so all depots have 17k items max, causes crash?? I think not
  794. for (uint32_t index = 1; index <= 17; ++index) {
  795. depotBoxs->internalAddThing(getDepotChest(18 - index, true));
  796. }
  797. return depotBoxs;
  798. }
  799.  
  800. DepotChest* Player::getDepotChest(uint32_t depotId, bool autoCreate)
  801. {
  802. auto it = depotChests.find(depotId);
  803. if (it != depotChests.end()) {
  804. return it->second;
  805. }
  806.  
  807. if (!autoCreate) {
  808. return nullptr;
  809. }
  810.  
  811. DepotChest* depotChest;
  812. if (depotId > 0 && depotId < 18) {
  813. depotChest = new DepotChest(ITEM_DEPOT_NULL + depotId);
  814. }
  815. else {
  816. depotChest = new DepotChest(ITEM_DEPOT);
  817. }
  818.  
  819. depotChest->incrementReferenceCounter();
  820. //depotChest->setMaxDepotItems(getMaxDepotItems()); why ?? my depot commit don't have this code, is possible add more items in depot with this
  821. depotChests[depotId] = depotChest;
  822. return depotChest;
  823. }
  824.  
  825. DepotLocker* Player::getDepotLocker(uint32_t depotId)
  826. {
  827. auto it = depotLockerMap.find(depotId);
  828. if (it != depotLockerMap.end()) {
  829. inbox->setParent(it->second);
  830. for (uint8_t i = g_config.getNumber(ConfigManager::DEPOT_BOXES); i > 0; i--) {
  831. if (DepotChest* depotBox = getDepotChest(i, false)) {
  832. depotBox->setParent(it->second->getItemByIndex(0)->getContainer());
  833. }
  834. }
  835. return it->second;
  836. }
  837.  
  838. DepotLocker* depotLocker = new DepotLocker(ITEM_LOCKER1);
  839. depotLocker->setDepotId(depotId);
  840. depotLocker->internalAddThing(Item::CreateItem(ITEM_MARKET));
  841. depotLocker->internalAddThing(inbox);
  842. Container* depotChest = Item::CreateItemAsContainer(ITEM_DEPOT, g_config.getNumber(ConfigManager::DEPOT_BOXES));
  843. for (uint8_t i = g_config.getNumber(ConfigManager::DEPOT_BOXES); i > 0; i--) {
  844. DepotChest* depotBox = getDepotChest(i, true);
  845. depotChest->internalAddThing(depotBox);
  846. depotBox->setParent(depotChest);
  847. }
  848. depotLocker->internalAddThing(depotChest);
  849. depotLockerMap[depotId] = depotLocker;
  850. return depotLocker;
  851. }
  852.  
  853. RewardChest* Player::getRewardChest()
  854. {
  855. if (rewardChest != nullptr) {
  856. return rewardChest;
  857. }
  858.  
  859. rewardChest = new RewardChest(ITEM_REWARD_CHEST);
  860. return rewardChest;
  861. }
  862.  
  863. Reward* Player::getReward(uint32_t rewardId, bool autoCreate)
  864. {
  865. auto it = rewardMap.find(rewardId);
  866. if (it != rewardMap.end()) {
  867. return it->second;
  868. }
  869.  
  870. if (!autoCreate) {
  871. return nullptr;
  872. }
  873.  
  874. Reward* reward = new Reward();
  875. reward->incrementReferenceCounter();
  876. reward->setIntAttr(ITEM_ATTRIBUTE_DATE, rewardId);
  877. rewardMap[rewardId] = reward;
  878.  
  879. g_game.internalAddItem(getRewardChest(), reward, INDEX_WHEREEVER, FLAG_NOLIMIT);
  880.  
  881. return reward;
  882. }
  883.  
  884. void Player::removeReward(uint32_t rewardId) {
  885. rewardMap.erase(rewardId);
  886. }
  887.  
  888. void Player::getRewardList(std::vector<uint32_t>& rewards) {
  889. rewards.reserve(rewardMap.size());
  890. for (auto& it : rewardMap) {
  891. rewards.push_back(it.first);
  892. }
  893. }
  894.  
  895. void Player::sendCancelMessage(ReturnValue message) const
  896. {
  897. sendCancelMessage(getReturnMessage(message));
  898. }
  899.  
  900. void Player::sendStats()
  901. {
  902. if (client) {
  903. client->sendStats();
  904. lastStatsTrainingTime = getOfflineTrainingTime() / 60 / 1000;
  905. }
  906. }
  907.  
  908. void Player::sendPing()
  909. {
  910. int64_t timeNow = OTSYS_TIME();
  911.  
  912. bool hasLostConnection = false;
  913. if ((timeNow - lastPing) >= 5000) {
  914. lastPing = timeNow;
  915. if (client) {
  916. client->sendPing();
  917. } else {
  918. hasLostConnection = true;
  919. }
  920. }
  921.  
  922. int64_t noPongTime = timeNow - lastPong;
  923. if ((hasLostConnection || noPongTime >= 7000) && attackedCreature && attackedCreature->getPlayer()) {
  924. setAttackedCreature(nullptr);
  925. }
  926.  
  927. if (noPongTime >= 60000 && canLogout()) {
  928. if (g_creatureEvents->playerLogout(this)) {
  929. if (client) {
  930. client->logout(true, true);
  931. } else {
  932. g_game.removeCreature(this, true);
  933. }
  934. }
  935. }
  936. }
  937.  
  938. Item* Player::getWriteItem(uint32_t& windowTextId, uint16_t& maxWriteLen)
  939. {
  940. windowTextId = this->windowTextId;
  941. maxWriteLen = this->maxWriteLen;
  942. return writeItem;
  943. }
  944.  
  945. void Player::setWriteItem(Item* item, uint16_t maxWriteLen /*= 0*/)
  946. {
  947. windowTextId++;
  948.  
  949. if (writeItem) {
  950. writeItem->decrementReferenceCounter();
  951. }
  952.  
  953. if (item) {
  954. writeItem = item;
  955. this->maxWriteLen = maxWriteLen;
  956. writeItem->incrementReferenceCounter();
  957. } else {
  958. writeItem = nullptr;
  959. this->maxWriteLen = 0;
  960. }
  961. }
  962.  
  963. House* Player::getEditHouse(uint32_t& windowTextId, uint32_t& listId)
  964. {
  965. windowTextId = this->windowTextId;
  966. listId = this->editListId;
  967. return editHouse;
  968. }
  969.  
  970. void Player::setEditHouse(House* house, uint32_t listId /*= 0*/)
  971. {
  972. windowTextId++;
  973. editHouse = house;
  974. editListId = listId;
  975. }
  976.  
  977. void Player::sendHouseWindow(House* house, uint32_t listId) const
  978. {
  979. if (!client) {
  980. return;
  981. }
  982.  
  983. std::string text;
  984. if (house->getAccessList(listId, text)) {
  985. client->sendHouseWindow(windowTextId, text);
  986. }
  987. }
  988.  
  989. //container
  990. void Player::sendAddContainerItem(const Container* container, const Item* item)
  991. {
  992. if (!client) {
  993. return;
  994. }
  995.  
  996. for (const auto& it : openContainers) {
  997. const OpenContainer& openContainer = it.second;
  998. if (openContainer.container != container) {
  999. continue;
  1000. }
  1001.  
  1002. uint16_t slot = openContainer.index;
  1003. if (container->getID() == ITEM_BROWSEFIELD) {
  1004. uint16_t containerSize = container->size() - 1;
  1005. uint16_t pageEnd = openContainer.index + container->capacity() - 1;
  1006. if (containerSize > pageEnd) {
  1007. slot = pageEnd;
  1008. item = container->getItemByIndex(pageEnd);
  1009. } else {
  1010. slot = containerSize;
  1011. }
  1012. } else if (openContainer.index >= container->capacity()) {
  1013. item = container->getItemByIndex(openContainer.index - 1);
  1014. }
  1015. client->sendAddContainerItem(it.first, slot, item);
  1016. }
  1017. }
  1018.  
  1019. void Player::sendUpdateContainerItem(const Container* container, uint16_t slot, const Item* newItem)
  1020. {
  1021. if (!client) {
  1022. return;
  1023. }
  1024.  
  1025. for (const auto& it : openContainers) {
  1026. const OpenContainer& openContainer = it.second;
  1027. if (openContainer.container != container) {
  1028. continue;
  1029. }
  1030.  
  1031. if (slot < openContainer.index) {
  1032. continue;
  1033. }
  1034.  
  1035. uint16_t pageEnd = openContainer.index + container->capacity();
  1036. if (slot >= pageEnd) {
  1037. continue;
  1038. }
  1039.  
  1040. client->sendUpdateContainerItem(it.first, slot, newItem);
  1041. }
  1042. }
  1043.  
  1044. void Player::sendRemoveContainerItem(const Container* container, uint16_t slot)
  1045. {
  1046. if (!client) {
  1047. return;
  1048. }
  1049.  
  1050. for (auto& it : openContainers) {
  1051. OpenContainer& openContainer = it.second;
  1052. if (openContainer.container != container) {
  1053. continue;
  1054. }
  1055.  
  1056. uint16_t& firstIndex = openContainer.index;
  1057. if (firstIndex > 0 && firstIndex >= container->size() - 1) {
  1058. firstIndex -= container->capacity();
  1059. sendContainer(it.first, container, false, firstIndex);
  1060. }
  1061.  
  1062. client->sendRemoveContainerItem(it.first, std::max<uint16_t>(slot, firstIndex), container->getItemByIndex(container->capacity() + firstIndex));
  1063. }
  1064. }
  1065.  
  1066. void Player::onUpdateTileItem(const Tile* tile, const Position& pos, const Item* oldItem,
  1067. const ItemType& oldType, const Item* newItem, const ItemType& newType)
  1068. {
  1069. Creature::onUpdateTileItem(tile, pos, oldItem, oldType, newItem, newType);
  1070.  
  1071. if (oldItem != newItem) {
  1072. onRemoveTileItem(tile, pos, oldType, oldItem);
  1073. }
  1074.  
  1075. if (tradeState != TRADE_TRANSFER) {
  1076. if (tradeItem && oldItem == tradeItem) {
  1077. g_game.internalCloseTrade(this);
  1078. }
  1079. }
  1080. }
  1081.  
  1082. void Player::onRemoveTileItem(const Tile* tile, const Position& pos, const ItemType& iType,
  1083. const Item* item)
  1084. {
  1085. Creature::onRemoveTileItem(tile, pos, iType, item);
  1086.  
  1087. if (tradeState != TRADE_TRANSFER) {
  1088. checkTradeState(item);
  1089.  
  1090. if (tradeItem) {
  1091. const Container* container = item->getContainer();
  1092. if (container && container->isHoldingItem(tradeItem)) {
  1093. g_game.internalCloseTrade(this);
  1094. }
  1095. }
  1096. }
  1097. }
  1098.  
  1099. void Player::onCreatureAppear(Creature* creature, bool isLogin)
  1100. {
  1101. Creature::onCreatureAppear(creature, isLogin);
  1102.  
  1103. if (isLogin && creature == this) {
  1104. for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
  1105. Item* item = inventory[slot];
  1106. if (item) {
  1107. item->startDecaying();
  1108. g_moveEvents->onPlayerEquip(this, item, static_cast<slots_t>(slot), false);
  1109. }
  1110. }
  1111.  
  1112. for (Condition* condition : storedConditionList) {
  1113. addCondition(condition);
  1114. }
  1115. storedConditionList.clear();
  1116.  
  1117. BedItem* bed = g_game.getBedBySleeper(guid);
  1118. if (bed) {
  1119. bed->wakeUp(this);
  1120. }
  1121.  
  1122. std::cout << name << " has logged in." << std::endl;
  1123.  
  1124. if (guild) {
  1125. guild->addMember(this);
  1126. }
  1127.  
  1128. int32_t offlineTime;
  1129. if (getLastLogout() != 0) {
  1130. // Not counting more than 21 days to prevent overflow when multiplying with 1000 (for milliseconds).
  1131. offlineTime = std::min<int32_t>(time(nullptr) - getLastLogout(), 86400 * 21);
  1132. } else {
  1133. offlineTime = 0;
  1134. }
  1135.  
  1136. for (Condition* condition : getMuteConditions()) {
  1137. condition->setTicks(condition->getTicks() - (offlineTime * 1000));
  1138. if (condition->getTicks() <= 0) {
  1139. removeCondition(condition);
  1140. }
  1141. }
  1142.  
  1143. g_game.checkPlayersRecord();
  1144. IOLoginData::updateOnlineStatus(guid, true);
  1145. }
  1146. }
  1147.  
  1148. void Player::onAttackedCreatureDisappear(bool isLogout)
  1149. {
  1150. sendCancelTarget();
  1151.  
  1152. if (!isLogout) {
  1153. sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
  1154. }
  1155. }
  1156.  
  1157. void Player::onFollowCreatureDisappear(bool isLogout)
  1158. {
  1159. sendCancelTarget();
  1160.  
  1161. if (!isLogout) {
  1162. sendTextMessage(MESSAGE_STATUS_SMALL, "Target lost.");
  1163. }
  1164. }
  1165.  
  1166. void Player::onChangeZone(ZoneType_t zone)
  1167. {
  1168. if (zone == ZONE_PROTECTION) {
  1169. if (attackedCreature && !hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1170. setAttackedCreature(nullptr);
  1171. onAttackedCreatureDisappear(false);
  1172. }
  1173.  
  1174. if (!group->access && isMounted()) {
  1175. dismount();
  1176. g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  1177. wasMounted = true;
  1178. }
  1179. } else {
  1180. if (wasMounted) {
  1181. toggleMount(true);
  1182. wasMounted = false;
  1183. }
  1184. }
  1185.  
  1186. g_game.updateCreatureWalkthrough(this);
  1187. sendIcons();
  1188. }
  1189.  
  1190. void Player::onAttackedCreatureChangeZone(ZoneType_t zone)
  1191. {
  1192. if (zone == ZONE_PROTECTION) {
  1193. if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1194. setAttackedCreature(nullptr);
  1195. onAttackedCreatureDisappear(false);
  1196. }
  1197. } else if (zone == ZONE_NOPVP) {
  1198. if (attackedCreature->getPlayer()) {
  1199. if (!hasFlag(PlayerFlag_IgnoreProtectionZone)) {
  1200. setAttackedCreature(nullptr);
  1201. onAttackedCreatureDisappear(false);
  1202. }
  1203. }
  1204. } else if (zone == ZONE_NORMAL) {
  1205. //attackedCreature can leave a pvp zone if not pzlocked
  1206. if (g_game.getWorldType() == WORLD_TYPE_NO_PVP) {
  1207. if (attackedCreature->getPlayer()) {
  1208. setAttackedCreature(nullptr);
  1209. onAttackedCreatureDisappear(false);
  1210. }
  1211. }
  1212. }
  1213. }
  1214.  
  1215. void Player::onRemoveCreature(Creature* creature, bool isLogout)
  1216. {
  1217. Creature::onRemoveCreature(creature, isLogout);
  1218.  
  1219. if (creature == this) {
  1220. if (isLogout) {
  1221. loginPosition = getPosition();
  1222. }
  1223.  
  1224. lastLogout = time(nullptr);
  1225.  
  1226. if (eventWalk != 0) {
  1227. setFollowCreature(nullptr);
  1228. }
  1229.  
  1230. if (tradePartner) {
  1231. g_game.internalCloseTrade(this);
  1232. }
  1233.  
  1234. closeShopWindow();
  1235.  
  1236. clearPartyInvitations();
  1237.  
  1238. if (party) {
  1239. party->leaveParty(this);
  1240. }
  1241.  
  1242. g_chat->removeUserFromAllChannels(*this);
  1243.  
  1244. std::cout << getName() << " has logged out." << std::endl;
  1245.  
  1246. if (guild) {
  1247. guild->removeMember(this);
  1248. }
  1249.  
  1250. IOLoginData::updateOnlineStatus(guid, false);
  1251.  
  1252. bool saved = false;
  1253. for (uint32_t tries = 0; tries < 3; ++tries) {
  1254. if (IOLoginData::savePlayer(this)) {
  1255. saved = true;
  1256. break;
  1257. }
  1258. }
  1259.  
  1260. if (!saved) {
  1261. std::cout << "Error while saving player: " << getName() << std::endl;
  1262. }
  1263. }
  1264. }
  1265.  
  1266. void Player::openShopWindow(Npc* npc, const std::list<ShopInfo>& shop)
  1267. {
  1268. shopItemList = shop;
  1269. sendShop(npc);
  1270. sendSaleItemList();
  1271. }
  1272.  
  1273. bool Player::closeShopWindow(bool sendCloseShopWindow /*= true*/)
  1274. {
  1275. //unreference callbacks
  1276. int32_t onBuy;
  1277. int32_t onSell;
  1278.  
  1279. Npc* npc = getShopOwner(onBuy, onSell);
  1280. if (!npc) {
  1281. shopItemList.clear();
  1282. return false;
  1283. }
  1284.  
  1285. setShopOwner(nullptr, -1, -1);
  1286. npc->onPlayerEndTrade(this, onBuy, onSell);
  1287.  
  1288. if (sendCloseShopWindow) {
  1289. sendCloseShop();
  1290. }
  1291.  
  1292. shopItemList.clear();
  1293. return true;
  1294. }
  1295.  
  1296. void Player::onWalk(Direction& dir)
  1297. {
  1298. Creature::onWalk(dir);
  1299. setNextActionTask(nullptr);
  1300. setNextAction(OTSYS_TIME() + getStepDuration(dir));
  1301. }
  1302.  
  1303. void Player::onCreatureMove(Creature* creature, const Tile* newTile, const Position& newPos,
  1304. const Tile* oldTile, const Position& oldPos, bool teleport)
  1305. {
  1306. Creature::onCreatureMove(creature, newTile, newPos, oldTile, oldPos, teleport);
  1307.  
  1308. if (hasFollowPath && (creature == followCreature || (creature == this && followCreature))) {
  1309. isUpdatingPath = false;
  1310. g_dispatcher.addTask(createTask(std::bind(&Game::updateCreatureWalk, &g_game, getID())));
  1311. }
  1312.  
  1313. if (creature != this) {
  1314. return;
  1315. }
  1316.  
  1317. if (tradeState != TRADE_TRANSFER) {
  1318. //check if we should close trade
  1319. if (tradeItem && !Position::areInRange<1, 1, 0>(tradeItem->getPosition(), getPosition())) {
  1320. g_game.internalCloseTrade(this);
  1321. }
  1322.  
  1323. if (tradePartner && !Position::areInRange<2, 2, 0>(tradePartner->getPosition(), getPosition())) {
  1324. g_game.internalCloseTrade(this);
  1325. }
  1326. }
  1327.  
  1328. // close modal windows
  1329. if (!modalWindows.empty()) {
  1330. // TODO: This shouldn't be hardcoded
  1331. for (uint32_t modalWindowId : modalWindows) {
  1332. if (modalWindowId == std::numeric_limits<uint32_t>::max()) {
  1333. sendTextMessage(MESSAGE_EVENT_ADVANCE, "Offline training aborted.");
  1334. break;
  1335. }
  1336. }
  1337. modalWindows.clear();
  1338. }
  1339.  
  1340. // leave market
  1341. if (inMarket) {
  1342. inMarket = false;
  1343. }
  1344.  
  1345. if (party) {
  1346. party->updateSharedExperience();
  1347. }
  1348.  
  1349. if (teleport || oldPos.z != newPos.z) {
  1350. int32_t ticks = g_config.getNumber(ConfigManager::STAIRHOP_DELAY);
  1351. if (ticks > 0) {
  1352. if (Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_PACIFIED, ticks, 0)) {
  1353. addCondition(condition);
  1354. }
  1355. }
  1356. }
  1357. }
  1358.  
  1359. //container
  1360. void Player::onAddContainerItem(const Item* item)
  1361. {
  1362. checkTradeState(item);
  1363. }
  1364.  
  1365. void Player::onUpdateContainerItem(const Container* container, const Item* oldItem, const Item* newItem)
  1366. {
  1367. if (oldItem != newItem) {
  1368. onRemoveContainerItem(container, oldItem);
  1369. }
  1370.  
  1371. if (tradeState != TRADE_TRANSFER) {
  1372. checkTradeState(oldItem);
  1373. }
  1374. }
  1375.  
  1376. void Player::onRemoveContainerItem(const Container* container, const Item* item)
  1377. {
  1378. if (tradeState != TRADE_TRANSFER) {
  1379. checkTradeState(item);
  1380.  
  1381. if (tradeItem) {
  1382. if (tradeItem->getParent() != container && container->isHoldingItem(tradeItem)) {
  1383. g_game.internalCloseTrade(this);
  1384. }
  1385. }
  1386. }
  1387. }
  1388.  
  1389. void Player::onCloseContainer(const Container* container)
  1390. {
  1391. if (!client) {
  1392. return;
  1393. }
  1394.  
  1395. for (const auto& it : openContainers) {
  1396. if (it.second.container == container) {
  1397. client->sendCloseContainer(it.first);
  1398. }
  1399. }
  1400. }
  1401.  
  1402. void Player::onSendContainer(const Container* container)
  1403. {
  1404. if (!client) {
  1405. return;
  1406. }
  1407.  
  1408. bool hasParent = container->hasParent();
  1409. for (const auto& it : openContainers) {
  1410. const OpenContainer& openContainer = it.second;
  1411. if (openContainer.container == container) {
  1412. client->sendContainer(it.first, container, hasParent, openContainer.index);
  1413. }
  1414. }
  1415. }
  1416.  
  1417. //inventory
  1418. void Player::onUpdateInventoryItem(Item* oldItem, Item* newItem)
  1419. {
  1420. if (oldItem != newItem) {
  1421. onRemoveInventoryItem(oldItem);
  1422. }
  1423.  
  1424. if (tradeState != TRADE_TRANSFER) {
  1425. checkTradeState(oldItem);
  1426. }
  1427. }
  1428.  
  1429. void Player::onRemoveInventoryItem(Item* item)
  1430. {
  1431. if (tradeState != TRADE_TRANSFER) {
  1432. checkTradeState(item);
  1433.  
  1434. if (tradeItem) {
  1435. const Container* container = item->getContainer();
  1436. if (container && container->isHoldingItem(tradeItem)) {
  1437. g_game.internalCloseTrade(this);
  1438. }
  1439. }
  1440. }
  1441. }
  1442.  
  1443. void Player::checkTradeState(const Item* item)
  1444. {
  1445. if (!tradeItem || tradeState == TRADE_TRANSFER) {
  1446. return;
  1447. }
  1448.  
  1449. if (tradeItem == item) {
  1450. g_game.internalCloseTrade(this);
  1451. } else {
  1452. const Container* container = dynamic_cast<const Container*>(item->getParent());
  1453. while (container) {
  1454. if (container == tradeItem) {
  1455. g_game.internalCloseTrade(this);
  1456. break;
  1457. }
  1458.  
  1459. container = dynamic_cast<const Container*>(container->getParent());
  1460. }
  1461. }
  1462. }
  1463.  
  1464. void Player::setNextWalkActionTask(SchedulerTask* task)
  1465. {
  1466. if (walkTaskEvent != 0) {
  1467. g_scheduler.stopEvent(walkTaskEvent);
  1468. walkTaskEvent = 0;
  1469. }
  1470.  
  1471. delete walkTask;
  1472. walkTask = task;
  1473. }
  1474.  
  1475. void Player::setNextWalkTask(SchedulerTask* task)
  1476. {
  1477. if (nextStepEvent != 0) {
  1478. g_scheduler.stopEvent(nextStepEvent);
  1479. nextStepEvent = 0;
  1480. }
  1481.  
  1482. if (task) {
  1483. nextStepEvent = g_scheduler.addEvent(task);
  1484. resetIdleTime();
  1485. }
  1486. }
  1487.  
  1488. void Player::setNextActionTask(SchedulerTask* task)
  1489. {
  1490. if (actionTaskEvent != 0) {
  1491. g_scheduler.stopEvent(actionTaskEvent);
  1492. actionTaskEvent = 0;
  1493. }
  1494.  
  1495. if (task) {
  1496. actionTaskEvent = g_scheduler.addEvent(task);
  1497. resetIdleTime();
  1498. }
  1499. }
  1500.  
  1501. uint32_t Player::getNextActionTime() const
  1502. {
  1503. return std::max<int64_t>(SCHEDULER_MINTICKS, nextAction - OTSYS_TIME());
  1504. }
  1505.  
  1506. void Player::onThink(uint32_t interval)
  1507. {
  1508. Creature::onThink(interval);
  1509.  
  1510. sendPing();
  1511.  
  1512. MessageBufferTicks += interval;
  1513. if (MessageBufferTicks >= 1500) {
  1514. MessageBufferTicks = 0;
  1515. addMessageBuffer();
  1516. }
  1517.  
  1518. if (!getTile()->hasFlag(TILESTATE_NOLOGOUT) && !isAccessPlayer()) {
  1519. idleTime += interval;
  1520. const int32_t kickAfterMinutes = g_config.getNumber(ConfigManager::KICK_AFTER_MINUTES);
  1521. if (idleTime > (kickAfterMinutes * 60000) + 60000) {
  1522. kickPlayer(true);
  1523. } else if (client && idleTime == 60000 * kickAfterMinutes) {
  1524. std::ostringstream ss;
  1525. ss << "You have been idle for " << kickAfterMinutes << " minutes. You will be disconnected in one minute if you are still idle then.";
  1526. client->sendTextMessage(TextMessage(MESSAGE_STATUS_WARNING, ss.str()));
  1527. }
  1528. }
  1529.  
  1530. if (g_game.getWorldType() != WORLD_TYPE_PVP_ENFORCED) {
  1531. checkSkullTicks(interval);
  1532. }
  1533.  
  1534. addOfflineTrainingTime(interval);
  1535. if (lastStatsTrainingTime != getOfflineTrainingTime() / 60 / 1000) {
  1536. sendStats();
  1537. }
  1538. }
  1539.  
  1540. uint32_t Player::isMuted() const
  1541. {
  1542. if (hasFlag(PlayerFlag_CannotBeMuted)) {
  1543. return 0;
  1544. }
  1545.  
  1546. int32_t muteTicks = 0;
  1547. for (Condition* condition : conditions) {
  1548. if (condition->getType() == CONDITION_MUTED && condition->getTicks() > muteTicks) {
  1549. muteTicks = condition->getTicks();
  1550. }
  1551. }
  1552. return static_cast<uint32_t>(muteTicks) / 1000;
  1553. }
  1554.  
  1555. void Player::addMessageBuffer()
  1556. {
  1557. if (MessageBufferCount > 0 && g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER) != 0 && !hasFlag(PlayerFlag_CannotBeMuted)) {
  1558. --MessageBufferCount;
  1559. }
  1560. }
  1561.  
  1562. void Player::removeMessageBuffer()
  1563. {
  1564. if (hasFlag(PlayerFlag_CannotBeMuted)) {
  1565. return;
  1566. }
  1567.  
  1568. const int32_t maxMessageBuffer = g_config.getNumber(ConfigManager::MAX_MESSAGEBUFFER);
  1569. if (maxMessageBuffer != 0 && MessageBufferCount <= maxMessageBuffer + 1) {
  1570. if (++MessageBufferCount > maxMessageBuffer) {
  1571. uint32_t muteCount = 1;
  1572. auto it = muteCountMap.find(guid);
  1573. if (it != muteCountMap.end()) {
  1574. muteCount = it->second;
  1575. }
  1576.  
  1577. uint32_t muteTime = 5 * muteCount * muteCount;
  1578. muteCountMap[guid] = muteCount + 1;
  1579. Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_MUTED, muteTime * 1000, 0);
  1580. addCondition(condition);
  1581.  
  1582. std::ostringstream ss;
  1583. ss << "You are muted for " << muteTime << " seconds.";
  1584. sendTextMessage(MESSAGE_STATUS_SMALL, ss.str());
  1585. }
  1586. }
  1587. }
  1588.  
  1589. void Player::drainHealth(Creature* attacker, int32_t damage)
  1590. {
  1591. Creature::drainHealth(attacker, damage);
  1592. sendStats();
  1593. }
  1594.  
  1595. void Player::drainMana(Creature* attacker, int32_t manaLoss)
  1596. {
  1597. Creature::drainMana(attacker, manaLoss);
  1598. sendStats();
  1599. }
  1600.  
  1601. void Player::addManaSpent(uint64_t amount)
  1602. {
  1603. if (hasFlag(PlayerFlag_NotGainMana)) {
  1604. return;
  1605. }
  1606.  
  1607. uint64_t currReqMana = vocation->getReqMana(magLevel);
  1608. uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  1609. if (currReqMana >= nextReqMana) {
  1610. //player has reached max magic level
  1611. return;
  1612. }
  1613.  
  1614. g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, amount);
  1615. if (amount == 0) {
  1616. return;
  1617. }
  1618.  
  1619. bool sendUpdateStats = false;
  1620. while ((manaSpent + amount) >= nextReqMana) {
  1621. amount -= nextReqMana - manaSpent;
  1622.  
  1623. magLevel++;
  1624. manaSpent = 0;
  1625.  
  1626. std::ostringstream ss;
  1627. ss << "You advanced to magic level " << magLevel << '.';
  1628. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1629.  
  1630. g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);
  1631.  
  1632. sendUpdateStats = true;
  1633. currReqMana = nextReqMana;
  1634. nextReqMana = vocation->getReqMana(magLevel + 1);
  1635. if (currReqMana >= nextReqMana) {
  1636. return;
  1637. }
  1638. }
  1639.  
  1640. manaSpent += amount;
  1641.  
  1642. uint8_t oldPercent = magLevelPercent;
  1643. if (nextReqMana > currReqMana) {
  1644. magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  1645. } else {
  1646. magLevelPercent = 0;
  1647. }
  1648.  
  1649. if (oldPercent != magLevelPercent) {
  1650. sendUpdateStats = true;
  1651. }
  1652.  
  1653. if (sendUpdateStats) {
  1654. sendStats();
  1655. }
  1656. }
  1657.  
  1658. void Player::addExperience(Creature* source, uint64_t exp, bool sendText/* = false*/)
  1659. {
  1660. uint64_t currLevelExp = Player::getExpForLevel(level);
  1661. uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  1662. uint64_t rawExp = exp;
  1663. if (currLevelExp >= nextLevelExp) {
  1664. //player has reached max level
  1665. levelPercent = 0;
  1666. sendStats();
  1667. return;
  1668. }
  1669.  
  1670. g_events->eventPlayerOnGainExperience(this, source, exp, rawExp);
  1671. if (exp == 0) {
  1672. return;
  1673. }
  1674.  
  1675. experience += exp;
  1676.  
  1677. if (sendText) {
  1678. std::string expString = std::to_string(exp) + (exp != 1 ? " experience points." : " experience point.");
  1679.  
  1680. TextMessage message(MESSAGE_EXPERIENCE, "You gained " + expString);
  1681. message.position = position;
  1682. message.primary.value = exp;
  1683. message.primary.color = TEXTCOLOR_WHITE_EXP;
  1684. sendTextMessage(message);
  1685.  
  1686. SpectatorHashSet spectators;
  1687. g_game.map.getSpectators(spectators, position, false, true);
  1688. spectators.erase(this);
  1689. if (!spectators.empty()) {
  1690. message.type = MESSAGE_EXPERIENCE_OTHERS;
  1691. message.text = getName() + " gained " + expString;
  1692. for (Creature* spectator : spectators) {
  1693. spectator->getPlayer()->sendTextMessage(message);
  1694. }
  1695. }
  1696. }
  1697.  
  1698. uint32_t prevLevel = level;
  1699. while (experience >= nextLevelExp) {
  1700. ++level;
  1701. healthMax += vocation->getHPGain();
  1702. health += vocation->getHPGain();
  1703. manaMax += vocation->getManaGain();
  1704. mana += vocation->getManaGain();
  1705. capacity += vocation->getCapGain();
  1706.  
  1707. currLevelExp = nextLevelExp;
  1708. nextLevelExp = Player::getExpForLevel(level + 1);
  1709. if (currLevelExp >= nextLevelExp) {
  1710. //player has reached max level
  1711. break;
  1712. }
  1713. }
  1714.  
  1715. if (prevLevel != level) {
  1716. health = healthMax;
  1717. mana = manaMax;
  1718.  
  1719. updateBaseSpeed();
  1720. setBaseSpeed(getBaseSpeed());
  1721. setBaseXpGain(g_game.getExperienceStage(level)*100);
  1722. g_game.changeSpeed(this, 0);
  1723. g_game.addCreatureHealth(this);
  1724.  
  1725. if (party) {
  1726. party->updateSharedExperience();
  1727. }
  1728.  
  1729. g_creatureEvents->playerAdvance(this, SKILL_LEVEL, prevLevel, level);
  1730.  
  1731. std::ostringstream ss;
  1732. ss << "You advanced from Level " << prevLevel << " to Level " << level << '.';
  1733. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1734. }
  1735.  
  1736. if (nextLevelExp > currLevelExp) {
  1737. levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  1738. } else {
  1739. levelPercent = 0;
  1740. }
  1741. sendStats();
  1742. }
  1743.  
  1744. void Player::removeExperience(uint64_t exp, bool sendText/* = false*/)
  1745. {
  1746. if (experience == 0 || exp == 0) {
  1747. return;
  1748. }
  1749.  
  1750. g_events->eventPlayerOnLoseExperience(this, exp);
  1751. if (exp == 0) {
  1752. return;
  1753. }
  1754.  
  1755. uint64_t lostExp = experience;
  1756. experience = std::max<int64_t>(0, experience - exp);
  1757.  
  1758. if (sendText) {
  1759. lostExp -= experience;
  1760.  
  1761. std::string expString = std::to_string(lostExp) + (lostExp != 1 ? " experience points." : " experience point.");
  1762.  
  1763. TextMessage message(MESSAGE_EXPERIENCE, "You lost " + expString);
  1764. message.position = position;
  1765. message.primary.value = lostExp;
  1766. message.primary.color = TEXTCOLOR_RED;
  1767. sendTextMessage(message);
  1768.  
  1769. SpectatorHashSet spectators;
  1770. g_game.map.getSpectators(spectators, position, false, true);
  1771. spectators.erase(this);
  1772. if (!spectators.empty()) {
  1773. message.type = MESSAGE_EXPERIENCE_OTHERS;
  1774. message.text = getName() + " lost " + expString;
  1775. for (Creature* spectator : spectators) {
  1776. spectator->getPlayer()->sendTextMessage(message);
  1777. }
  1778. }
  1779. }
  1780.  
  1781. uint32_t oldLevel = level;
  1782. uint64_t currLevelExp = Player::getExpForLevel(level);
  1783.  
  1784. while (level > 1 && experience < currLevelExp) {
  1785. --level;
  1786. healthMax = std::max<int32_t>(0, healthMax - vocation->getHPGain());
  1787. manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
  1788. capacity = std::max<int32_t>(0, capacity - vocation->getCapGain());
  1789. currLevelExp = Player::getExpForLevel(level);
  1790. }
  1791.  
  1792. if (oldLevel != level) {
  1793. health = healthMax;
  1794. mana = manaMax;
  1795.  
  1796. updateBaseSpeed();
  1797. setBaseSpeed(getBaseSpeed());
  1798.  
  1799. g_game.changeSpeed(this, 0);
  1800. g_game.addCreatureHealth(this);
  1801.  
  1802. if (party) {
  1803. party->updateSharedExperience();
  1804. }
  1805.  
  1806. std::ostringstream ss;
  1807. ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
  1808. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  1809. }
  1810.  
  1811. uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  1812. if (nextLevelExp > currLevelExp) {
  1813. levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  1814. } else {
  1815. levelPercent = 0;
  1816. }
  1817. sendStats();
  1818. }
  1819.  
  1820. uint8_t Player::getPercentLevel(uint64_t count, uint64_t nextLevelCount)
  1821. {
  1822. if (nextLevelCount == 0) {
  1823. return 0;
  1824. }
  1825.  
  1826. uint8_t result = (count * 100) / nextLevelCount;
  1827. if (result > 100) {
  1828. return 0;
  1829. }
  1830. return result;
  1831. }
  1832.  
  1833. void Player::onBlockHit()
  1834. {
  1835. if (shieldBlockCount > 0) {
  1836. --shieldBlockCount;
  1837.  
  1838. if (hasShield()) {
  1839. addSkillAdvance(SKILL_SHIELD, 1);
  1840. }
  1841. }
  1842. }
  1843.  
  1844. void Player::onAttackedCreatureBlockHit(BlockType_t blockType)
  1845. {
  1846. lastAttackBlockType = blockType;
  1847.  
  1848. switch (blockType) {
  1849. case BLOCK_NONE: {
  1850. addAttackSkillPoint = true;
  1851. bloodHitCount = 30;
  1852. shieldBlockCount = 30;
  1853. break;
  1854. }
  1855.  
  1856. case BLOCK_DEFENSE:
  1857. case BLOCK_ARMOR: {
  1858. //need to draw blood every 30 hits
  1859. if (bloodHitCount > 0) {
  1860. addAttackSkillPoint = true;
  1861. --bloodHitCount;
  1862. } else {
  1863. addAttackSkillPoint = false;
  1864. }
  1865. break;
  1866. }
  1867.  
  1868. default: {
  1869. addAttackSkillPoint = false;
  1870. break;
  1871. }
  1872. }
  1873. }
  1874.  
  1875. bool Player::hasShield() const
  1876. {
  1877. Item* item = inventory[CONST_SLOT_LEFT];
  1878. if (item && item->getWeaponType() == WEAPON_SHIELD) {
  1879. return true;
  1880. }
  1881.  
  1882. item = inventory[CONST_SLOT_RIGHT];
  1883. if (item && item->getWeaponType() == WEAPON_SHIELD) {
  1884. return true;
  1885. }
  1886. return false;
  1887. }
  1888.  
  1889. BlockType_t Player::blockHit(Creature* attacker, CombatType_t combatType, int32_t& damage,
  1890. bool checkDefense /* = false*/, bool checkArmor /* = false*/, bool field /* = false*/)
  1891. {
  1892. BlockType_t blockType = Creature::blockHit(attacker, combatType, damage, checkDefense, checkArmor, field);
  1893.  
  1894. if (attacker) {
  1895. sendCreatureSquare(attacker, SQ_COLOR_BLACK);
  1896. }
  1897.  
  1898. if (blockType != BLOCK_NONE) {
  1899. return blockType;
  1900. }
  1901.  
  1902. if (damage > 0) {
  1903. for (int32_t slot = CONST_SLOT_FIRST; slot <= CONST_SLOT_LAST; ++slot) {
  1904. if (!isItemAbilityEnabled(static_cast<slots_t>(slot))) {
  1905. continue;
  1906. }
  1907.  
  1908. Item* item = inventory[slot];
  1909. if (!item) {
  1910. continue;
  1911. }
  1912.  
  1913. const ItemType& it = Item::items[item->getID()];
  1914. if (it.abilities) {
  1915. const int16_t& absorbPercent = it.abilities->absorbPercent[combatTypeToIndex(combatType)];
  1916. if (absorbPercent != 0) {
  1917. damage -= std::round(damage * (absorbPercent / 100.));
  1918.  
  1919. uint16_t charges = item->getCharges();
  1920. if (charges != 0) {
  1921. g_game.transformItem(item, item->getID(), charges - 1);
  1922. }
  1923. }
  1924.  
  1925. if (field) {
  1926. const int16_t& fieldAbsorbPercent = it.abilities->fieldAbsorbPercent[combatTypeToIndex(combatType)];
  1927. if (fieldAbsorbPercent != 0) {
  1928. damage -= std::round(damage * (fieldAbsorbPercent / 100.));
  1929.  
  1930. uint16_t charges = item->getCharges();
  1931. if (charges != 0) {
  1932. g_game.transformItem(item, item->getID(), charges - 1);
  1933. }
  1934. }
  1935. }
  1936. if (attacker) {
  1937. const int16_t& reflectPercent = it.abilities->reflectPercent[combatTypeToIndex(combatType)];
  1938. if (reflectPercent != 0) {
  1939. CombatParams params;
  1940. params.combatType = combatType;
  1941. params.impactEffect = CONST_ME_MAGIC_BLUE;
  1942.  
  1943. CombatDamage reflectDamage;
  1944. reflectDamage.origin = ORIGIN_SPELL;
  1945. reflectDamage.primary.type = combatType;
  1946. reflectDamage.primary.value = std::round(-damage * (reflectPercent / 100.));
  1947.  
  1948. Combat::doCombatHealth(this, attacker, reflectDamage, params);
  1949. }
  1950. }
  1951. }
  1952. }
  1953.  
  1954. if (damage <= 0) {
  1955. damage = 0;
  1956. blockType = BLOCK_ARMOR;
  1957. }
  1958. }
  1959. return blockType;
  1960. }
  1961.  
  1962. uint32_t Player::getIP() const
  1963. {
  1964. if (client) {
  1965. return client->getIP();
  1966. }
  1967.  
  1968. return 0;
  1969. }
  1970.  
  1971. void Player::death(Creature* lastHitCreature)
  1972. {
  1973. loginPosition = town->getTemplePosition();
  1974.  
  1975. if (skillLoss) {
  1976. uint8_t unfairFightReduction = 100;
  1977. bool lastHitPlayer = Player::lastHitIsPlayer(lastHitCreature);
  1978.  
  1979. if (lastHitPlayer) {
  1980. uint32_t sumLevels = 0;
  1981. uint32_t inFightTicks = g_config.getNumber(ConfigManager::PZ_LOCKED);
  1982. for (const auto& it : damageMap) {
  1983. CountBlock_t cb = it.second;
  1984. if ((OTSYS_TIME() - cb.ticks) <= inFightTicks) {
  1985. Player* damageDealer = g_game.getPlayerByID(it.first);
  1986. if (damageDealer) {
  1987. sumLevels += damageDealer->getLevel();
  1988. }
  1989. }
  1990. }
  1991.  
  1992. if (sumLevels > level) {
  1993. double reduce = level / static_cast<double>(sumLevels);
  1994. unfairFightReduction = std::max<uint8_t>(20, std::floor((reduce * 100) + 0.5));
  1995. }
  1996. }
  1997.  
  1998. //Magic level loss
  1999. uint64_t sumMana = 0;
  2000. uint64_t lostMana = 0;
  2001.  
  2002. //sum up all the mana
  2003. for (uint32_t i = 1; i <= magLevel; ++i) {
  2004. sumMana += vocation->getReqMana(i);
  2005. }
  2006.  
  2007. sumMana += manaSpent;
  2008.  
  2009. double deathLossPercent = getLostPercent() * (unfairFightReduction / 100.);
  2010.  
  2011. lostMana = static_cast<uint64_t>(sumMana * deathLossPercent);
  2012.  
  2013. while (lostMana > manaSpent && magLevel > 0) {
  2014. lostMana -= manaSpent;
  2015. manaSpent = vocation->getReqMana(magLevel);
  2016. magLevel--;
  2017. }
  2018.  
  2019. manaSpent -= lostMana;
  2020.  
  2021. uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  2022. if (nextReqMana > vocation->getReqMana(magLevel)) {
  2023. magLevelPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  2024. } else {
  2025. magLevelPercent = 0;
  2026. }
  2027.  
  2028. //Skill loss
  2029. for (uint8_t i = SKILL_FIRST; i <= SKILL_LAST; ++i) { //for each skill
  2030. uint64_t sumSkillTries = 0;
  2031. for (uint16_t c = 11; c <= skills[i].level; ++c) { //sum up all required tries for all skill levels
  2032. sumSkillTries += vocation->getReqSkillTries(i, c);
  2033. }
  2034.  
  2035. sumSkillTries += skills[i].tries;
  2036.  
  2037. uint32_t lostSkillTries = static_cast<uint32_t>(sumSkillTries * deathLossPercent);
  2038. while (lostSkillTries > skills[i].tries) {
  2039. lostSkillTries -= skills[i].tries;
  2040.  
  2041. if (skills[i].level <= 10) {
  2042. skills[i].level = 10;
  2043. skills[i].tries = 0;
  2044. lostSkillTries = 0;
  2045. break;
  2046. }
  2047.  
  2048. skills[i].tries = vocation->getReqSkillTries(i, skills[i].level);
  2049. skills[i].level--;
  2050. }
  2051.  
  2052. skills[i].tries = std::max<int32_t>(0, skills[i].tries - lostSkillTries);
  2053. skills[i].percent = Player::getPercentLevel(skills[i].tries, vocation->getReqSkillTries(i, skills[i].level));
  2054. }
  2055.  
  2056. //Level loss
  2057. uint64_t expLoss = static_cast<uint64_t>(experience * deathLossPercent);
  2058. g_events->eventPlayerOnLoseExperience(this, expLoss);
  2059.  
  2060. if (expLoss != 0) {
  2061. uint32_t oldLevel = level;
  2062.  
  2063. if (vocation->getId() == VOCATION_NONE || level > 7) {
  2064. experience -= expLoss;
  2065. }
  2066.  
  2067. while (level > 1 && experience < Player::getExpForLevel(level)) {
  2068. --level;
  2069. healthMax = std::max<int32_t>(0, healthMax - vocation->getHPGain());
  2070. manaMax = std::max<int32_t>(0, manaMax - vocation->getManaGain());
  2071. capacity = std::max<int32_t>(0, capacity - vocation->getCapGain());
  2072. }
  2073.  
  2074. if (oldLevel != level) {
  2075. std::ostringstream ss;
  2076. ss << "You were downgraded from Level " << oldLevel << " to Level " << level << '.';
  2077. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  2078. }
  2079.  
  2080. uint64_t currLevelExp = Player::getExpForLevel(level);
  2081. uint64_t nextLevelExp = Player::getExpForLevel(level + 1);
  2082. if (nextLevelExp > currLevelExp) {
  2083. levelPercent = Player::getPercentLevel(experience - currLevelExp, nextLevelExp - currLevelExp);
  2084. } else {
  2085. levelPercent = 0;
  2086. }
  2087. }
  2088.  
  2089. if (hasBlessing(8)) {
  2090. if (lastHitPlayer && hasBlessing(1)) {
  2091. removeBlessing(1, 1);
  2092. } else {
  2093. for (int i = 2; i <= 8; i++) {
  2094. removeBlessing(i, 1);
  2095. }
  2096. }
  2097. } else {
  2098. uint8_t maxBlessing = (operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6;
  2099. for (int i = 1; i <= maxBlessing; i++) {
  2100. removeBlessing(i, 1);
  2101. }
  2102. }
  2103.  
  2104. sendStats();
  2105. sendSkills();
  2106. sendReLoginWindow(unfairFightReduction);
  2107.  
  2108. if (getSkull() == SKULL_BLACK) {
  2109. health = 40;
  2110. mana = 0;
  2111. } else {
  2112. health = healthMax;
  2113. mana = manaMax;
  2114. }
  2115.  
  2116. auto it = conditions.begin(), end = conditions.end();
  2117. while (it != end) {
  2118. Condition* condition = *it;
  2119. if (condition->isPersistent()) {
  2120. it = conditions.erase(it);
  2121.  
  2122. condition->endCondition(this);
  2123. onEndCondition(condition->getType());
  2124. delete condition;
  2125. } else {
  2126. ++it;
  2127. }
  2128. }
  2129. } else {
  2130. setSkillLoss(true);
  2131.  
  2132. auto it = conditions.begin(), end = conditions.end();
  2133. while (it != end) {
  2134. Condition* condition = *it;
  2135. if (condition->isPersistent()) {
  2136. it = conditions.erase(it);
  2137.  
  2138. condition->endCondition(this);
  2139. onEndCondition(condition->getType());
  2140. delete condition;
  2141. } else {
  2142. ++it;
  2143. }
  2144. }
  2145.  
  2146. health = healthMax;
  2147. g_game.internalTeleport(this, getTemplePosition(), true);
  2148. g_game.addCreatureHealth(this);
  2149. onThink(EVENT_CREATURE_THINK_INTERVAL);
  2150. onIdleStatus();
  2151. sendStats();
  2152. }
  2153. }
  2154.  
  2155. bool Player::dropCorpse(Creature* lastHitCreature, Creature* mostDamageCreature, bool lastHitUnjustified, bool mostDamageUnjustified)
  2156. {
  2157. if (getZone() != ZONE_PVP || !Player::lastHitIsPlayer(lastHitCreature)) {
  2158. return Creature::dropCorpse(lastHitCreature, mostDamageCreature, lastHitUnjustified, mostDamageUnjustified);
  2159. }
  2160.  
  2161. setDropLoot(true);
  2162. return false;
  2163. }
  2164.  
  2165. Item* Player::getCorpse(Creature* lastHitCreature, Creature* mostDamageCreature)
  2166. {
  2167. Item* corpse = Creature::getCorpse(lastHitCreature, mostDamageCreature);
  2168. if (corpse && corpse->getContainer()) {
  2169. std::ostringstream ss;
  2170. if (lastHitCreature) {
  2171. ss << "You recognize " << getNameDescription() << ". " << (getSex() == PLAYERSEX_FEMALE ? "She" : "He") << " was killed by " << lastHitCreature->getNameDescription() << '.';
  2172. } else {
  2173. ss << "You recognize " << getNameDescription() << '.';
  2174. }
  2175.  
  2176. corpse->setSpecialDescription(ss.str());
  2177. }
  2178. return corpse;
  2179. }
  2180.  
  2181. void Player::addInFightTicks(bool pzlock /*= false*/)
  2182. {
  2183. if (hasFlag(PlayerFlag_NotGainInFight)) {
  2184. return;
  2185. }
  2186.  
  2187. if (pzlock) {
  2188. pzLocked = true;
  2189. sendIcons();
  2190. }
  2191.  
  2192. Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::PZ_LOCKED), 0);
  2193. addCondition(condition);
  2194. }
  2195.  
  2196. void Player::removeList()
  2197. {
  2198. g_game.removePlayer(this);
  2199.  
  2200. for (const auto& it : g_game.getPlayers()) {
  2201. it.second->notifyStatusChange(this, VIPSTATUS_OFFLINE);
  2202. }
  2203. }
  2204.  
  2205. void Player::addList()
  2206. {
  2207. for (const auto& it : g_game.getPlayers()) {
  2208. it.second->notifyStatusChange(this, VIPSTATUS_ONLINE);
  2209. }
  2210.  
  2211. g_game.addPlayer(this);
  2212. }
  2213.  
  2214. void Player::kickPlayer(bool displayEffect)
  2215. {
  2216. g_creatureEvents->playerLogout(this);
  2217. if (client) {
  2218. client->logout(displayEffect, true);
  2219. } else {
  2220. g_game.removeCreature(this);
  2221. }
  2222. }
  2223.  
  2224. void Player::notifyStatusChange(Player* loginPlayer, VipStatus_t status)
  2225. {
  2226. if (!client) {
  2227. return;
  2228. }
  2229.  
  2230. auto it = VIPList.find(loginPlayer->guid);
  2231. if (it == VIPList.end()) {
  2232. return;
  2233. }
  2234.  
  2235. client->sendUpdatedVIPStatus(loginPlayer->guid, status);
  2236.  
  2237. if (status == VIPSTATUS_ONLINE) {
  2238. client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged in."));
  2239. } else if (status == VIPSTATUS_OFFLINE) {
  2240. client->sendTextMessage(TextMessage(MESSAGE_STATUS_SMALL, loginPlayer->getName() + " has logged out."));
  2241. }
  2242. }
  2243.  
  2244. bool Player::removeVIP(uint32_t vipGuid)
  2245. {
  2246. if (VIPList.erase(vipGuid) == 0) {
  2247. return false;
  2248. }
  2249.  
  2250. IOLoginData::removeVIPEntry(accountNumber, vipGuid);
  2251. return true;
  2252. }
  2253.  
  2254. bool Player::addVIP(uint32_t vipGuid, const std::string& vipName, VipStatus_t status)
  2255. {
  2256. if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
  2257. sendTextMessage(MESSAGE_STATUS_SMALL, "You cannot add more buddies.");
  2258. return false;
  2259. }
  2260.  
  2261. auto result = VIPList.insert(vipGuid);
  2262. if (!result.second) {
  2263. sendTextMessage(MESSAGE_STATUS_SMALL, "This player is already in your list.");
  2264. return false;
  2265. }
  2266.  
  2267. IOLoginData::addVIPEntry(accountNumber, vipGuid, "", 0, false);
  2268. if (client) {
  2269. client->sendVIP(vipGuid, vipName, "", 0, false, status);
  2270. }
  2271. return true;
  2272. }
  2273.  
  2274. bool Player::addVIPInternal(uint32_t vipGuid)
  2275. {
  2276. if (VIPList.size() >= getMaxVIPEntries() || VIPList.size() == 200) { // max number of buddies is 200 in 9.53
  2277. return false;
  2278. }
  2279.  
  2280. return VIPList.insert(vipGuid).second;
  2281. }
  2282.  
  2283. bool Player::editVIP(uint32_t vipGuid, const std::string& description, uint32_t icon, bool notify)
  2284. {
  2285. auto it = VIPList.find(vipGuid);
  2286. if (it == VIPList.end()) {
  2287. return false; // player is not in VIP
  2288. }
  2289.  
  2290. IOLoginData::editVIPEntry(accountNumber, vipGuid, description, icon, notify);
  2291. return true;
  2292. }
  2293.  
  2294. //close container and its child containers
  2295. void Player::autoCloseContainers(const Container* container)
  2296. {
  2297. std::vector<uint32_t> closeList;
  2298. for (const auto& it : openContainers) {
  2299. Container* tmpContainer = it.second.container;
  2300. while (tmpContainer) {
  2301. if (tmpContainer->isRemoved() || tmpContainer == container) {
  2302. closeList.push_back(it.first);
  2303. break;
  2304. }
  2305.  
  2306. tmpContainer = dynamic_cast<Container*>(tmpContainer->getParent());
  2307. }
  2308. }
  2309.  
  2310. for (uint32_t containerId : closeList) {
  2311. closeContainer(containerId);
  2312. if (client) {
  2313. client->sendCloseContainer(containerId);
  2314. }
  2315. }
  2316. }
  2317.  
  2318. bool Player::hasCapacity(const Item* item, uint32_t count) const
  2319. {
  2320. if (hasFlag(PlayerFlag_CannotPickupItem)) {
  2321. return false;
  2322. }
  2323.  
  2324. if (hasFlag(PlayerFlag_HasInfiniteCapacity) || item->getTopParent() == this) {
  2325. return true;
  2326. }
  2327.  
  2328. uint32_t itemWeight = item->getContainer() != nullptr ? item->getWeight() : item->getBaseWeight();
  2329. if (item->isStackable()) {
  2330. itemWeight *= count;
  2331. }
  2332. return itemWeight <= getFreeCapacity();
  2333. }
  2334.  
  2335. ReturnValue Player::queryAdd(int32_t index, const Thing& thing, uint32_t count, uint32_t flags, Creature*) const
  2336. {
  2337. const Item* item = thing.getItem();
  2338. if (item == nullptr) {
  2339. return RETURNVALUE_NOTPOSSIBLE;
  2340. }
  2341.  
  2342. bool childIsOwner = hasBitSet(FLAG_CHILDISOWNER, flags);
  2343. if (childIsOwner) {
  2344. //a child container is querying the player, just check if enough capacity
  2345. bool skipLimit = hasBitSet(FLAG_NOLIMIT, flags);
  2346. if (skipLimit || hasCapacity(item, count)) {
  2347. return RETURNVALUE_NOERROR;
  2348. }
  2349. return RETURNVALUE_NOTENOUGHCAPACITY;
  2350. }
  2351.  
  2352. if (!item->isPickupable()) {
  2353. return RETURNVALUE_CANNOTPICKUP;
  2354. }
  2355.  
  2356. ReturnValue ret = RETURNVALUE_NOERROR;
  2357.  
  2358. const int32_t& slotPosition = item->getSlotPosition();
  2359. if ((slotPosition & SLOTP_HEAD) || (slotPosition & SLOTP_NECKLACE) ||
  2360. (slotPosition & SLOTP_BACKPACK) || (slotPosition & SLOTP_ARMOR) ||
  2361. (slotPosition & SLOTP_LEGS) || (slotPosition & SLOTP_FEET) ||
  2362. (slotPosition & SLOTP_RING)) {
  2363. ret = RETURNVALUE_CANNOTBEDRESSED;
  2364. } else if (slotPosition & SLOTP_TWO_HAND) {
  2365. ret = RETURNVALUE_PUTTHISOBJECTINBOTHHANDS;
  2366. } else if ((slotPosition & SLOTP_RIGHT) || (slotPosition & SLOTP_LEFT)) {
  2367. if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2368. ret = RETURNVALUE_CANNOTBEDRESSED;
  2369. } else {
  2370. ret = RETURNVALUE_PUTTHISOBJECTINYOURHAND;
  2371. }
  2372. }
  2373.  
  2374. switch (index) {
  2375. case CONST_SLOT_HEAD: {
  2376. if (slotPosition & SLOTP_HEAD) {
  2377. ret = RETURNVALUE_NOERROR;
  2378. }
  2379. break;
  2380. }
  2381.  
  2382. case CONST_SLOT_NECKLACE: {
  2383. if (slotPosition & SLOTP_NECKLACE) {
  2384. ret = RETURNVALUE_NOERROR;
  2385. }
  2386. break;
  2387. }
  2388.  
  2389. case CONST_SLOT_BACKPACK: {
  2390. if (slotPosition & SLOTP_BACKPACK) {
  2391. ret = RETURNVALUE_NOERROR;
  2392. }
  2393. break;
  2394. }
  2395.  
  2396. case CONST_SLOT_ARMOR: {
  2397. if (slotPosition & SLOTP_ARMOR) {
  2398. ret = RETURNVALUE_NOERROR;
  2399. }
  2400. break;
  2401. }
  2402.  
  2403. case CONST_SLOT_RIGHT: {
  2404. if (slotPosition & SLOTP_RIGHT) {
  2405. if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2406. if (item->getWeaponType() != WEAPON_SHIELD) {
  2407. ret = RETURNVALUE_CANNOTBEDRESSED;
  2408. } else {
  2409. const Item* leftItem = inventory[CONST_SLOT_LEFT];
  2410. if (leftItem) {
  2411. if ((leftItem->getSlotPosition() | slotPosition) & SLOTP_TWO_HAND) {
  2412. ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2413. } else {
  2414. ret = RETURNVALUE_NOERROR;
  2415. }
  2416. } else {
  2417. ret = RETURNVALUE_NOERROR;
  2418. }
  2419. }
  2420. } else if (slotPosition & SLOTP_TWO_HAND) {
  2421. if (inventory[CONST_SLOT_LEFT] && inventory[CONST_SLOT_LEFT] != item) {
  2422. ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2423. } else {
  2424. ret = RETURNVALUE_NOERROR;
  2425. }
  2426. } else if (inventory[CONST_SLOT_LEFT]) {
  2427. const Item* leftItem = inventory[CONST_SLOT_LEFT];
  2428. WeaponType_t type = item->getWeaponType(), leftType = leftItem->getWeaponType();
  2429.  
  2430. if (leftItem->getSlotPosition() & SLOTP_TWO_HAND) {
  2431. ret = RETURNVALUE_DROPTWOHANDEDITEM;
  2432. } else if (item == leftItem && count == item->getItemCount()) {
  2433. ret = RETURNVALUE_NOERROR;
  2434. } else if (leftType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
  2435. ret = RETURNVALUE_CANONLYUSEONESHIELD;
  2436. } else if (leftType == WEAPON_NONE || type == WEAPON_NONE ||
  2437. leftType == WEAPON_SHIELD || leftType == WEAPON_AMMO
  2438. || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
  2439. ret = RETURNVALUE_NOERROR;
  2440. } else {
  2441. ret = RETURNVALUE_CANONLYUSEONEWEAPON;
  2442. }
  2443. } else {
  2444. ret = RETURNVALUE_NOERROR;
  2445. }
  2446. }
  2447. break;
  2448. }
  2449.  
  2450. case CONST_SLOT_LEFT: {
  2451. if (slotPosition & SLOTP_LEFT) {
  2452. if (!g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2453. WeaponType_t type = item->getWeaponType();
  2454. if (type == WEAPON_NONE || type == WEAPON_SHIELD) {
  2455. ret = RETURNVALUE_CANNOTBEDRESSED;
  2456. } else if (inventory[CONST_SLOT_RIGHT] && (slotPosition & SLOTP_TWO_HAND)) {
  2457. ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2458. } else {
  2459. ret = RETURNVALUE_NOERROR;
  2460. }
  2461. } else if (slotPosition & SLOTP_TWO_HAND) {
  2462. if (inventory[CONST_SLOT_RIGHT] && inventory[CONST_SLOT_RIGHT] != item) {
  2463. ret = RETURNVALUE_BOTHHANDSNEEDTOBEFREE;
  2464. } else {
  2465. ret = RETURNVALUE_NOERROR;
  2466. }
  2467. } else if (inventory[CONST_SLOT_RIGHT]) {
  2468. const Item* rightItem = inventory[CONST_SLOT_RIGHT];
  2469. WeaponType_t type = item->getWeaponType(), rightType = rightItem->getWeaponType();
  2470.  
  2471. if (rightItem->getSlotPosition() & SLOTP_TWO_HAND) {
  2472. ret = RETURNVALUE_DROPTWOHANDEDITEM;
  2473. } else if (item == rightItem && count == item->getItemCount()) {
  2474. ret = RETURNVALUE_NOERROR;
  2475. } else if (rightType == WEAPON_SHIELD && type == WEAPON_SHIELD) {
  2476. ret = RETURNVALUE_CANONLYUSEONESHIELD;
  2477. } else if (rightType == WEAPON_NONE || type == WEAPON_NONE ||
  2478. rightType == WEAPON_SHIELD || rightType == WEAPON_AMMO
  2479. || type == WEAPON_SHIELD || type == WEAPON_AMMO) {
  2480. ret = RETURNVALUE_NOERROR;
  2481. } else {
  2482. ret = RETURNVALUE_CANONLYUSEONEWEAPON;
  2483. }
  2484. } else {
  2485. ret = RETURNVALUE_NOERROR;
  2486. }
  2487. }
  2488. break;
  2489. }
  2490.  
  2491. case CONST_SLOT_LEGS: {
  2492. if (slotPosition & SLOTP_LEGS) {
  2493. ret = RETURNVALUE_NOERROR;
  2494. }
  2495. break;
  2496. }
  2497.  
  2498. case CONST_SLOT_FEET: {
  2499. if (slotPosition & SLOTP_FEET) {
  2500. ret = RETURNVALUE_NOERROR;
  2501. }
  2502. break;
  2503. }
  2504.  
  2505. case CONST_SLOT_RING: {
  2506. if (slotPosition & SLOTP_RING) {
  2507. ret = RETURNVALUE_NOERROR;
  2508. }
  2509. break;
  2510. }
  2511.  
  2512. case CONST_SLOT_AMMO: {
  2513. if ((slotPosition & SLOTP_AMMO) || g_config.getBoolean(ConfigManager::CLASSIC_EQUIPMENT_SLOTS)) {
  2514. ret = RETURNVALUE_NOERROR;
  2515. }
  2516. break;
  2517. }
  2518.  
  2519. case CONST_SLOT_WHEREEVER:
  2520. case -1:
  2521. ret = RETURNVALUE_NOTENOUGHROOM;
  2522. break;
  2523.  
  2524. default:
  2525. ret = RETURNVALUE_NOTPOSSIBLE;
  2526. break;
  2527. }
  2528.  
  2529. if (ret == RETURNVALUE_NOERROR || ret == RETURNVALUE_NOTENOUGHROOM) {
  2530. //need an exchange with source?
  2531. const Item* inventoryItem = getInventoryItem(static_cast<slots_t>(index));
  2532. if (inventoryItem && (!inventoryItem->isStackable() || inventoryItem->getID() != item->getID())) {
  2533. return RETURNVALUE_NEEDEXCHANGE;
  2534. }
  2535.  
  2536. //check if enough capacity
  2537. if (!hasCapacity(item, count)) {
  2538. return RETURNVALUE_NOTENOUGHCAPACITY;
  2539. }
  2540.  
  2541. if (!g_moveEvents->onPlayerEquip(const_cast<Player*>(this), const_cast<Item*>(item), static_cast<slots_t>(index), true)) {
  2542. return RETURNVALUE_CANNOTBEDRESSED;
  2543. }
  2544. }
  2545. return ret;
  2546. }
  2547.  
  2548. ReturnValue Player::queryMaxCount(int32_t index, const Thing& thing, uint32_t count, uint32_t& maxQueryCount,
  2549. uint32_t flags) const
  2550. {
  2551. const Item* item = thing.getItem();
  2552. if (item == nullptr) {
  2553. maxQueryCount = 0;
  2554. return RETURNVALUE_NOTPOSSIBLE;
  2555. }
  2556.  
  2557. if (index == INDEX_WHEREEVER) {
  2558. uint32_t n = 0;
  2559. for (int32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_LAST; ++slotIndex) {
  2560. Item* inventoryItem = inventory[slotIndex];
  2561. if (inventoryItem) {
  2562. if (Container* subContainer = inventoryItem->getContainer()) {
  2563. uint32_t queryCount = 0;
  2564. subContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
  2565. n += queryCount;
  2566.  
  2567. //iterate through all items, including sub-containers (deep search)
  2568. for (ContainerIterator it = subContainer->iterator(); it.hasNext(); it.advance()) {
  2569. if (Container* tmpContainer = (*it)->getContainer()) {
  2570. queryCount = 0;
  2571. tmpContainer->queryMaxCount(INDEX_WHEREEVER, *item, item->getItemCount(), queryCount, flags);
  2572. n += queryCount;
  2573. }
  2574. }
  2575. } else if (inventoryItem->isStackable() && item->equals(inventoryItem) && inventoryItem->getItemCount() < 100) {
  2576. uint32_t remainder = (100 - inventoryItem->getItemCount());
  2577.  
  2578. if (queryAdd(slotIndex, *item, remainder, flags) == RETURNVALUE_NOERROR) {
  2579. n += remainder;
  2580. }
  2581. }
  2582. } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
  2583. if (item->isStackable()) {
  2584. n += 100;
  2585. } else {
  2586. ++n;
  2587. }
  2588. }
  2589. }
  2590.  
  2591. maxQueryCount = n;
  2592. } else {
  2593. const Item* destItem = nullptr;
  2594.  
  2595. const Thing* destThing = getThing(index);
  2596. if (destThing) {
  2597. destItem = destThing->getItem();
  2598. }
  2599.  
  2600. if (destItem) {
  2601. if (destItem->isStackable() && item->equals(destItem) && destItem->getItemCount() < 100) {
  2602. maxQueryCount = 100 - destItem->getItemCount();
  2603. } else {
  2604. maxQueryCount = 0;
  2605. }
  2606. } else if (queryAdd(index, *item, count, flags) == RETURNVALUE_NOERROR) { //empty slot
  2607. if (item->isStackable()) {
  2608. maxQueryCount = 100;
  2609. } else {
  2610. maxQueryCount = 1;
  2611. }
  2612.  
  2613. return RETURNVALUE_NOERROR;
  2614. }
  2615. }
  2616.  
  2617. if (maxQueryCount < count) {
  2618. return RETURNVALUE_NOTENOUGHROOM;
  2619. } else {
  2620. return RETURNVALUE_NOERROR;
  2621. }
  2622. }
  2623.  
  2624. ReturnValue Player::queryRemove(const Thing& thing, uint32_t count, uint32_t flags) const
  2625. {
  2626. int32_t index = getThingIndex(&thing);
  2627. if (index == -1) {
  2628. return RETURNVALUE_NOTPOSSIBLE;
  2629. }
  2630.  
  2631. const Item* item = thing.getItem();
  2632. if (item == nullptr) {
  2633. return RETURNVALUE_NOTPOSSIBLE;
  2634. }
  2635.  
  2636. if (count == 0 || (item->isStackable() && count > item->getItemCount())) {
  2637. return RETURNVALUE_NOTPOSSIBLE;
  2638. }
  2639.  
  2640. if (!item->isMoveable() && !hasBitSet(FLAG_IGNORENOTMOVEABLE, flags)) {
  2641. return RETURNVALUE_NOTMOVEABLE;
  2642. }
  2643.  
  2644. return RETURNVALUE_NOERROR;
  2645. }
  2646.  
  2647. Cylinder* Player::queryDestination(int32_t& index, const Thing& thing, Item** destItem,
  2648. uint32_t& flags)
  2649. {
  2650. if (index == 0 /*drop to capacity window*/ || index == INDEX_WHEREEVER) {
  2651. *destItem = nullptr;
  2652.  
  2653. const Item* item = thing.getItem();
  2654. if (item == nullptr) {
  2655. return this;
  2656. }
  2657.  
  2658. bool autoStack = !((flags & FLAG_IGNOREAUTOSTACK) == FLAG_IGNOREAUTOSTACK);
  2659. bool isStackable = item->isStackable();
  2660.  
  2661. std::vector<Container*> containers;
  2662.  
  2663. for (uint32_t slotIndex = CONST_SLOT_FIRST; slotIndex <= CONST_SLOT_AMMO; ++slotIndex) {
  2664. Item* inventoryItem = inventory[slotIndex];
  2665. if (inventoryItem) {
  2666. if (inventoryItem == tradeItem) {
  2667. continue;
  2668. }
  2669.  
  2670. if (inventoryItem == item) {
  2671. continue;
  2672. }
  2673.  
  2674. if (autoStack && isStackable) {
  2675. //try find an already existing item to stack with
  2676. if (queryAdd(slotIndex, *item, item->getItemCount(), 0) == RETURNVALUE_NOERROR) {
  2677. if (inventoryItem->equals(item) && inventoryItem->getItemCount() < 100) {
  2678. index = slotIndex;
  2679. *destItem = inventoryItem;
  2680. return this;
  2681. }
  2682. }
  2683.  
  2684. if (Container* subContainer = inventoryItem->getContainer()) {
  2685. containers.push_back(subContainer);
  2686. }
  2687. } else if (Container* subContainer = inventoryItem->getContainer()) {
  2688. containers.push_back(subContainer);
  2689. }
  2690. } else if (queryAdd(slotIndex, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) { //empty slot
  2691. index = slotIndex;
  2692. *destItem = nullptr;
  2693. return this;
  2694. }
  2695. }
  2696.  
  2697. size_t i = 0;
  2698. while (i < containers.size()) {
  2699. Container* tmpContainer = containers[i++];
  2700. if (!autoStack || !isStackable) {
  2701. //we need to find first empty container as fast as we can for non-stackable items
  2702. uint32_t n = tmpContainer->capacity() - tmpContainer->size();
  2703. while (n) {
  2704. if (tmpContainer->queryAdd(tmpContainer->capacity() - n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  2705. index = tmpContainer->capacity() - n;
  2706. *destItem = nullptr;
  2707. return tmpContainer;
  2708. }
  2709.  
  2710. n--;
  2711. }
  2712.  
  2713. for (Item* tmpContainerItem : tmpContainer->getItemList()) {
  2714. if (Container* subContainer = tmpContainerItem->getContainer()) {
  2715. containers.push_back(subContainer);
  2716. }
  2717. }
  2718.  
  2719. continue;
  2720. }
  2721.  
  2722. uint32_t n = 0;
  2723.  
  2724. for (Item* tmpItem : tmpContainer->getItemList()) {
  2725. if (tmpItem == tradeItem) {
  2726. continue;
  2727. }
  2728.  
  2729. if (tmpItem == item) {
  2730. continue;
  2731. }
  2732.  
  2733. //try find an already existing item to stack with
  2734. if (tmpItem->equals(item) && tmpItem->getItemCount() < 100) {
  2735. index = n;
  2736. *destItem = tmpItem;
  2737. return tmpContainer;
  2738. }
  2739.  
  2740. if (Container* subContainer = tmpItem->getContainer()) {
  2741. containers.push_back(subContainer);
  2742. }
  2743.  
  2744. n++;
  2745. }
  2746.  
  2747. if (n < tmpContainer->capacity() && tmpContainer->queryAdd(n, *item, item->getItemCount(), flags) == RETURNVALUE_NOERROR) {
  2748. index = n;
  2749. *destItem = nullptr;
  2750. return tmpContainer;
  2751. }
  2752. }
  2753.  
  2754. return this;
  2755. }
  2756.  
  2757. Thing* destThing = getThing(index);
  2758. if (destThing) {
  2759. *destItem = destThing->getItem();
  2760. }
  2761.  
  2762. Cylinder* subCylinder = dynamic_cast<Cylinder*>(destThing);
  2763. if (subCylinder) {
  2764. index = INDEX_WHEREEVER;
  2765. *destItem = nullptr;
  2766. return subCylinder;
  2767. } else {
  2768. return this;
  2769. }
  2770. }
  2771.  
  2772. void Player::addThing(int32_t index, Thing* thing)
  2773. {
  2774. if (index < CONST_SLOT_FIRST || index > CONST_SLOT_LAST) {
  2775. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2776. }
  2777.  
  2778. Item* item = thing->getItem();
  2779. if (!item) {
  2780. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2781. }
  2782.  
  2783. item->setParent(this);
  2784. inventory[index] = item;
  2785.  
  2786. //send to client
  2787. sendInventoryItem(static_cast<slots_t>(index), item);
  2788. }
  2789.  
  2790. void Player::updateThing(Thing* thing, uint16_t itemId, uint32_t count)
  2791. {
  2792. int32_t index = getThingIndex(thing);
  2793. if (index == -1) {
  2794. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2795. }
  2796.  
  2797. Item* item = thing->getItem();
  2798. if (!item) {
  2799. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2800. }
  2801.  
  2802. item->setID(itemId);
  2803. item->setSubType(count);
  2804.  
  2805. //send to client
  2806. sendInventoryItem(static_cast<slots_t>(index), item);
  2807.  
  2808. //event methods
  2809. onUpdateInventoryItem(item, item);
  2810. }
  2811.  
  2812. void Player::replaceThing(uint32_t index, Thing* thing)
  2813. {
  2814. if (index > CONST_SLOT_LAST) {
  2815. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2816. }
  2817.  
  2818. Item* oldItem = getInventoryItem(static_cast<slots_t>(index));
  2819. if (!oldItem) {
  2820. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2821. }
  2822.  
  2823. Item* item = thing->getItem();
  2824. if (!item) {
  2825. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2826. }
  2827.  
  2828. //send to client
  2829. sendInventoryItem(static_cast<slots_t>(index), item);
  2830.  
  2831. //event methods
  2832. onUpdateInventoryItem(oldItem, item);
  2833.  
  2834. item->setParent(this);
  2835.  
  2836. inventory[index] = item;
  2837. }
  2838.  
  2839. void Player::removeThing(Thing* thing, uint32_t count)
  2840. {
  2841. Item* item = thing->getItem();
  2842. if (!item) {
  2843. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2844. }
  2845.  
  2846. int32_t index = getThingIndex(thing);
  2847. if (index == -1) {
  2848. return /*RETURNVALUE_NOTPOSSIBLE*/;
  2849. }
  2850.  
  2851. if (item->isStackable()) {
  2852. if (count == item->getItemCount()) {
  2853. //send change to client
  2854. sendInventoryItem(static_cast<slots_t>(index), nullptr);
  2855.  
  2856. //event methods
  2857. onRemoveInventoryItem(item);
  2858.  
  2859. item->setParent(nullptr);
  2860. inventory[index] = nullptr;
  2861. } else {
  2862. uint8_t newCount = static_cast<uint8_t>(std::max<int32_t>(0, item->getItemCount() - count));
  2863. item->setItemCount(newCount);
  2864.  
  2865. //send change to client
  2866. sendInventoryItem(static_cast<slots_t>(index), item);
  2867.  
  2868. //event methods
  2869. onUpdateInventoryItem(item, item);
  2870. }
  2871. } else {
  2872. //send change to client
  2873. sendInventoryItem(static_cast<slots_t>(index), nullptr);
  2874.  
  2875. //event methods
  2876. onRemoveInventoryItem(item);
  2877.  
  2878. item->setParent(nullptr);
  2879. inventory[index] = nullptr;
  2880. }
  2881. }
  2882.  
  2883. int32_t Player::getThingIndex(const Thing* thing) const
  2884. {
  2885. for (int i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  2886. if (inventory[i] == thing) {
  2887. return i;
  2888. }
  2889. }
  2890. return -1;
  2891. }
  2892.  
  2893. size_t Player::getFirstIndex() const
  2894. {
  2895. return CONST_SLOT_FIRST;
  2896. }
  2897.  
  2898. size_t Player::getLastIndex() const
  2899. {
  2900. return CONST_SLOT_LAST + 1;
  2901. }
  2902.  
  2903. uint32_t Player::getItemTypeCount(uint16_t itemId, int32_t subType /*= -1*/) const
  2904. {
  2905. uint32_t count = 0;
  2906. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  2907. Item* item = inventory[i];
  2908. if (!item) {
  2909. continue;
  2910. }
  2911.  
  2912. if (item->getID() == itemId) {
  2913. count += Item::countByType(item, subType);
  2914. }
  2915.  
  2916. if (Container* container = item->getContainer()) {
  2917. for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  2918. if ((*it)->getID() == itemId) {
  2919. count += Item::countByType(*it, subType);
  2920. }
  2921. }
  2922. }
  2923. }
  2924. return count;
  2925. }
  2926.  
  2927. bool Player::removeItemOfType(uint16_t itemId, uint32_t amount, int32_t subType, bool ignoreEquipped/* = false*/) const
  2928. {
  2929. if (amount == 0) {
  2930. return true;
  2931. }
  2932.  
  2933. std::vector<Item*> itemList;
  2934.  
  2935. uint32_t count = 0;
  2936. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  2937. Item* item = inventory[i];
  2938. if (!item) {
  2939. continue;
  2940. }
  2941.  
  2942. if (!ignoreEquipped && item->getID() == itemId) {
  2943. uint32_t itemCount = Item::countByType(item, subType);
  2944. if (itemCount == 0) {
  2945. continue;
  2946. }
  2947.  
  2948. itemList.push_back(item);
  2949.  
  2950. count += itemCount;
  2951. if (count >= amount) {
  2952. g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
  2953. return true;
  2954. }
  2955. } else if (Container* container = item->getContainer()) {
  2956. for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  2957. Item* containerItem = *it;
  2958. if (containerItem->getID() == itemId) {
  2959. uint32_t itemCount = Item::countByType(containerItem, subType);
  2960. if (itemCount == 0) {
  2961. continue;
  2962. }
  2963.  
  2964. itemList.push_back(containerItem);
  2965.  
  2966. count += itemCount;
  2967. if (count >= amount) {
  2968. g_game.internalRemoveItems(std::move(itemList), amount, Item::items[itemId].stackable);
  2969. return true;
  2970. }
  2971. }
  2972. }
  2973. }
  2974. }
  2975. return false;
  2976. }
  2977.  
  2978. std::map<uint32_t, uint32_t>& Player::getAllItemTypeCount(std::map<uint32_t, uint32_t>& countMap) const
  2979. {
  2980. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  2981. Item* item = inventory[i];
  2982. if (!item) {
  2983. continue;
  2984. }
  2985.  
  2986. countMap[item->getID()] += Item::countByType(item, -1);
  2987.  
  2988. if (Container* container = item->getContainer()) {
  2989. for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  2990. countMap[(*it)->getID()] += Item::countByType(*it, -1);
  2991. }
  2992. }
  2993. }
  2994. return countMap;
  2995. }
  2996.  
  2997. Item* Player::getItemByClientId(uint16_t clientId) const
  2998. {
  2999. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  3000. Item* item = inventory[i];
  3001. if (!item) {
  3002. continue;
  3003. }
  3004.  
  3005. if (item->getClientID() == clientId) {
  3006. return item;
  3007. }
  3008.  
  3009. if (Container* container = item->getContainer()) {
  3010. for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  3011. if ((*it)->getClientID() == clientId) {
  3012. return (*it);
  3013. }
  3014. }
  3015. }
  3016. }
  3017. return nullptr;
  3018. }
  3019.  
  3020. std::map<uint16_t, uint16_t> Player::getInventoryClientIds() const
  3021. {
  3022. std::map<uint16_t, uint16_t> itemMap;
  3023. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; i++) {
  3024. Item* item = inventory[i];
  3025. if (!item) {
  3026. continue;
  3027. }
  3028.  
  3029. itemMap.emplace(item->getClientID(), Item::countByType(item, -1));
  3030.  
  3031. const ItemType& itemType = Item::items[item->getID()];
  3032. if (itemType.transformEquipTo) {
  3033. itemMap.emplace(Item::items[itemType.transformEquipTo].clientId, 1);
  3034. }
  3035.  
  3036. if (itemType.transformDeEquipTo) {
  3037. itemMap.emplace(Item::items[itemType.transformDeEquipTo].clientId, 1);
  3038. }
  3039.  
  3040. if (Container* container = item->getContainer()) {
  3041. for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
  3042. itemMap.emplace((*it)->getClientID(), Item::countByType(*it, -1));
  3043. const ItemType& itItemType = Item::items[(*it)->getID()];
  3044. if (itItemType.transformEquipTo) {
  3045. itemMap.emplace(Item::items[itItemType.transformEquipTo].clientId, 1);
  3046. }
  3047.  
  3048. if (itItemType.transformDeEquipTo) {
  3049. itemMap.emplace(Item::items[itItemType.transformDeEquipTo].clientId, 1);
  3050. }
  3051. }
  3052. }
  3053. }
  3054. return itemMap;
  3055. }
  3056.  
  3057. Thing* Player::getThing(size_t index) const
  3058. {
  3059. if (index >= CONST_SLOT_FIRST && index <= CONST_SLOT_LAST) {
  3060. return inventory[index];
  3061. }
  3062. return nullptr;
  3063. }
  3064.  
  3065. void Player::postAddNotification(Thing* thing, const Cylinder* oldParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
  3066. {
  3067. if (link == LINK_OWNER) {
  3068. //calling movement scripts
  3069. g_moveEvents->onPlayerEquip(this, thing->getItem(), static_cast<slots_t>(index), false);
  3070. }
  3071.  
  3072. bool requireListUpdate = true;
  3073.  
  3074. if (link == LINK_OWNER || link == LINK_TOPPARENT) {
  3075. const Item* i = (oldParent ? oldParent->getItem() : nullptr);
  3076.  
  3077. // Check if we owned the old container too, so we don't need to do anything,
  3078. // as the list was updated in postRemoveNotification
  3079. assert(i ? i->getContainer() != nullptr : true);
  3080.  
  3081. if (i) {
  3082. requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
  3083. } else {
  3084. requireListUpdate = oldParent != this;
  3085. }
  3086.  
  3087. updateInventoryWeight();
  3088. updateItemsLight();
  3089. sendInventoryClientIds();
  3090. sendStats();
  3091. }
  3092.  
  3093. if (const Item* item = thing->getItem()) {
  3094. if (const Container* container = item->getContainer()) {
  3095. onSendContainer(container);
  3096. }
  3097.  
  3098. if (shopOwner && requireListUpdate) {
  3099. updateSaleShopList(item);
  3100. }
  3101. } else if (const Creature* creature = thing->getCreature()) {
  3102. if (creature == this) {
  3103. //check containers
  3104. std::vector<Container*> containers;
  3105.  
  3106. for (const auto& it : openContainers) {
  3107. Container* container = it.second.container;
  3108. if (!Position::areInRange<1, 1, 0>(container->getPosition(), getPosition())) {
  3109. containers.push_back(container);
  3110. }
  3111. }
  3112.  
  3113. for (const Container* container : containers) {
  3114. autoCloseContainers(container);
  3115. }
  3116. }
  3117. }
  3118. }
  3119.  
  3120. void Player::postRemoveNotification(Thing* thing, const Cylinder* newParent, int32_t index, cylinderlink_t link /*= LINK_OWNER*/)
  3121. {
  3122. if (link == LINK_OWNER) {
  3123. //calling movement scripts
  3124. g_moveEvents->onPlayerDeEquip(this, thing->getItem(), static_cast<slots_t>(index));
  3125. }
  3126.  
  3127. bool requireListUpdate = true;
  3128.  
  3129. if (link == LINK_OWNER || link == LINK_TOPPARENT) {
  3130. const Item* i = (newParent ? newParent->getItem() : nullptr);
  3131.  
  3132. // Check if we owned the old container too, so we don't need to do anything,
  3133. // as the list was updated in postRemoveNotification
  3134. assert(i ? i->getContainer() != nullptr : true);
  3135.  
  3136. if (i) {
  3137. requireListUpdate = i->getContainer()->getHoldingPlayer() != this;
  3138. } else {
  3139. requireListUpdate = newParent != this;
  3140. }
  3141.  
  3142. updateInventoryWeight();
  3143. updateItemsLight();
  3144. sendInventoryClientIds();
  3145. sendStats();
  3146. }
  3147.  
  3148. if (const Item* item = thing->getItem()) {
  3149. if (const Container* container = item->getContainer()) {
  3150. if (container->isRemoved() || !Position::areInRange<1, 1, 0>(getPosition(), container->getPosition())) {
  3151. autoCloseContainers(container);
  3152. } else if (container->getTopParent() == this) {
  3153. onSendContainer(container);
  3154. } else if (const Container* topContainer = dynamic_cast<const Container*>(container->getTopParent())) {
  3155. if (const DepotChest* depotChest = dynamic_cast<const DepotChest*>(topContainer)) {
  3156. bool isOwner = false;
  3157.  
  3158. for (const auto& it : depotChests) {
  3159. if (it.second == depotChest) {
  3160. isOwner = true;
  3161. onSendContainer(container);
  3162. }
  3163. }
  3164.  
  3165. if (!isOwner) {
  3166. autoCloseContainers(container);
  3167. }
  3168. } else {
  3169. onSendContainer(container);
  3170. }
  3171. } else {
  3172. autoCloseContainers(container);
  3173. }
  3174. }
  3175.  
  3176. if (shopOwner && requireListUpdate) {
  3177. updateSaleShopList(item);
  3178. }
  3179. }
  3180. }
  3181.  
  3182. bool Player::updateSaleShopList(const Item* item)
  3183. {
  3184. uint16_t itemId = item->getID();
  3185. if (itemId != ITEM_GOLD_COIN && itemId != ITEM_PLATINUM_COIN && itemId != ITEM_CRYSTAL_COIN) {
  3186. auto it = std::find_if(shopItemList.begin(), shopItemList.end(), [itemId](const ShopInfo& shopInfo) { return shopInfo.itemId == itemId && shopInfo.sellPrice != 0; });
  3187. if (it == shopItemList.end()) {
  3188. const Container* container = item->getContainer();
  3189. if (!container) {
  3190. return false;
  3191. }
  3192.  
  3193. const auto& items = container->getItemList();
  3194. return std::any_of(items.begin(), items.end(), [this](const Item* containerItem) {
  3195. return updateSaleShopList(containerItem);
  3196. });
  3197. }
  3198. }
  3199.  
  3200. if (client) {
  3201. client->sendSaleItemList(shopItemList);
  3202. }
  3203. return true;
  3204. }
  3205.  
  3206. bool Player::hasShopItemForSale(uint32_t itemId, uint8_t subType) const
  3207. {
  3208. const ItemType& itemType = Item::items[itemId];
  3209. return std::any_of(shopItemList.begin(), shopItemList.end(), [&](const ShopInfo& shopInfo) {
  3210. return shopInfo.itemId == itemId && shopInfo.buyPrice != 0 && (!itemType.isFluidContainer() || shopInfo.subType == subType);
  3211. });
  3212. }
  3213.  
  3214. void Player::internalAddThing(Thing* thing)
  3215. {
  3216. internalAddThing(0, thing);
  3217. }
  3218.  
  3219. void Player::internalAddThing(uint32_t index, Thing* thing)
  3220. {
  3221. Item* item = thing->getItem();
  3222. if (!item) {
  3223. return;
  3224. }
  3225.  
  3226. //index == 0 means we should equip this item at the most appropiate slot (no action required here)
  3227. if (index > 0 && index < 12) {
  3228. if (inventory[index]) {
  3229. return;
  3230. }
  3231.  
  3232. inventory[index] = item;
  3233. item->setParent(this);
  3234. }
  3235. }
  3236.  
  3237. bool Player::setFollowCreature(Creature* creature)
  3238. {
  3239. if (!Creature::setFollowCreature(creature)) {
  3240. setFollowCreature(nullptr);
  3241. setAttackedCreature(nullptr);
  3242.  
  3243. sendCancelMessage(RETURNVALUE_THEREISNOWAY);
  3244. sendCancelTarget();
  3245. stopWalk();
  3246. return false;
  3247. }
  3248. return true;
  3249. }
  3250.  
  3251. bool Player::setAttackedCreature(Creature* creature)
  3252. {
  3253. if (!Creature::setAttackedCreature(creature)) {
  3254. sendCancelTarget();
  3255. return false;
  3256. }
  3257.  
  3258. if (chaseMode && creature) {
  3259. if (followCreature != creature) {
  3260. //chase opponent
  3261. setFollowCreature(creature);
  3262. }
  3263. } else if (followCreature) {
  3264. setFollowCreature(nullptr);
  3265. }
  3266.  
  3267. if (creature) {
  3268. g_dispatcher.addTask(createTask(std::bind(&Game::checkCreatureAttack, &g_game, getID())));
  3269. }
  3270. return true;
  3271. }
  3272.  
  3273. void Player::goToFollowCreature()
  3274. {
  3275. if (!walkTask) {
  3276. if ((OTSYS_TIME() - lastFailedFollow) < 2000) {
  3277. return;
  3278. }
  3279.  
  3280. Creature::goToFollowCreature();
  3281.  
  3282. if (followCreature && !hasFollowPath) {
  3283. lastFailedFollow = OTSYS_TIME();
  3284. }
  3285. }
  3286. }
  3287.  
  3288. void Player::getPathSearchParams(const Creature* creature, FindPathParams& fpp) const
  3289. {
  3290. Creature::getPathSearchParams(creature, fpp);
  3291. fpp.fullPathSearch = true;
  3292. }
  3293.  
  3294. void Player::doAttacking(uint32_t)
  3295. {
  3296. if (lastAttack == 0) {
  3297. lastAttack = OTSYS_TIME() - getAttackSpeed() - 1;
  3298. }
  3299.  
  3300. if (hasCondition(CONDITION_PACIFIED)) {
  3301. return;
  3302. }
  3303.  
  3304. if ((OTSYS_TIME() - lastAttack) >= getAttackSpeed()) {
  3305. bool result = false;
  3306.  
  3307. Item* tool = getWeapon();
  3308. const Weapon* weapon = g_weapons->getWeapon(tool);
  3309. uint32_t delay = getAttackSpeed();
  3310. bool classicSpeed = g_config.getBoolean(ConfigManager::CLASSIC_ATTACK_SPEED);
  3311.  
  3312. if (weapon) {
  3313. if (!weapon->interruptSwing()) {
  3314. result = weapon->useWeapon(this, tool, attackedCreature);
  3315. } else if (!classicSpeed && !canDoAction()) {
  3316. delay = getNextActionTime();
  3317. } else {
  3318. result = weapon->useWeapon(this, tool, attackedCreature);
  3319. }
  3320. } else {
  3321. result = Weapon::useFist(this, attackedCreature);
  3322. }
  3323.  
  3324. SchedulerTask* task = createSchedulerTask(std::max<uint32_t>(SCHEDULER_MINTICKS, delay), std::bind(&Game::checkCreatureAttack, &g_game, getID()));
  3325. if (!classicSpeed) {
  3326. setNextActionTask(task);
  3327. } else {
  3328. g_scheduler.addEvent(task);
  3329. }
  3330.  
  3331. if (result) {
  3332. lastAttack = OTSYS_TIME();
  3333. }
  3334. }
  3335. }
  3336.  
  3337. uint64_t Player::getGainedExperience(Creature* attacker) const
  3338. {
  3339. if (g_config.getBoolean(ConfigManager::EXPERIENCE_FROM_PLAYERS)) {
  3340. Player* attackerPlayer = attacker->getPlayer();
  3341. if (attackerPlayer && attackerPlayer != this && skillLoss && std::abs(static_cast<int32_t>(attackerPlayer->getLevel() - level)) <= g_config.getNumber(ConfigManager::EXP_FROM_PLAYERS_LEVEL_RANGE)) {
  3342. return std::max<uint64_t>(0, std::floor(getLostExperience() * getDamageRatio(attacker) * 0.75));
  3343. }
  3344. }
  3345. return 0;
  3346. }
  3347.  
  3348. void Player::onFollowCreature(const Creature* creature)
  3349. {
  3350. if (!creature) {
  3351. stopWalk();
  3352. }
  3353. }
  3354.  
  3355. void Player::setChaseMode(bool mode)
  3356. {
  3357. bool prevChaseMode = chaseMode;
  3358. chaseMode = mode;
  3359.  
  3360. if (prevChaseMode != chaseMode) {
  3361. if (chaseMode) {
  3362. if (!followCreature && attackedCreature) {
  3363. //chase opponent
  3364. setFollowCreature(attackedCreature);
  3365. }
  3366. } else if (attackedCreature) {
  3367. setFollowCreature(nullptr);
  3368. cancelNextWalk = true;
  3369. }
  3370. }
  3371. }
  3372.  
  3373. void Player::onWalkAborted()
  3374. {
  3375. setNextWalkActionTask(nullptr);
  3376. sendCancelWalk();
  3377. }
  3378.  
  3379. void Player::onWalkComplete()
  3380. {
  3381. if (walkTask) {
  3382. walkTaskEvent = g_scheduler.addEvent(walkTask);
  3383. walkTask = nullptr;
  3384. }
  3385. }
  3386.  
  3387. void Player::stopWalk()
  3388. {
  3389. cancelNextWalk = true;
  3390. }
  3391.  
  3392. void Player::getCreatureLight(LightInfo& light) const
  3393. {
  3394. if (internalLight.level > itemsLight.level) {
  3395. light = internalLight;
  3396. } else {
  3397. light = itemsLight;
  3398. }
  3399. }
  3400.  
  3401. void Player::updateItemsLight(bool internal /*=false*/)
  3402. {
  3403. LightInfo maxLight;
  3404. LightInfo curLight;
  3405.  
  3406. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  3407. Item* item = inventory[i];
  3408. if (item) {
  3409. item->getLight(curLight);
  3410.  
  3411. if (curLight.level > maxLight.level) {
  3412. maxLight = curLight;
  3413. }
  3414. }
  3415. }
  3416.  
  3417. if (itemsLight.level != maxLight.level || itemsLight.color != maxLight.color) {
  3418. itemsLight = maxLight;
  3419.  
  3420. if (!internal) {
  3421. g_game.changeLight(this);
  3422. }
  3423. }
  3424. }
  3425.  
  3426. void Player::onAddCondition(ConditionType_t type)
  3427. {
  3428. Creature::onAddCondition(type);
  3429.  
  3430. if (type == CONDITION_OUTFIT && isMounted()) {
  3431. dismount();
  3432. }
  3433.  
  3434. sendIcons();
  3435. }
  3436.  
  3437. void Player::onAddCombatCondition(ConditionType_t type)
  3438. {
  3439. switch (type) {
  3440. case CONDITION_POISON:
  3441. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are poisoned.");
  3442. break;
  3443.  
  3444. case CONDITION_DROWN:
  3445. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drowning.");
  3446. break;
  3447.  
  3448. case CONDITION_PARALYZE:
  3449. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are paralyzed.");
  3450. break;
  3451.  
  3452. case CONDITION_DRUNK:
  3453. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are drunk.");
  3454. break;
  3455.  
  3456. case CONDITION_CURSED:
  3457. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are cursed.");
  3458. break;
  3459.  
  3460. case CONDITION_FREEZING:
  3461. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are freezing.");
  3462. break;
  3463.  
  3464. case CONDITION_DAZZLED:
  3465. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are dazzled.");
  3466. break;
  3467.  
  3468. case CONDITION_BLEEDING:
  3469. sendTextMessage(MESSAGE_STATUS_DEFAULT, "You are bleeding.");
  3470. break;
  3471.  
  3472. default:
  3473. break;
  3474. }
  3475. }
  3476.  
  3477. void Player::onEndCondition(ConditionType_t type)
  3478. {
  3479. Creature::onEndCondition(type);
  3480.  
  3481. if (type == CONDITION_INFIGHT) {
  3482. onIdleStatus();
  3483. pzLocked = false;
  3484. clearAttacked();
  3485.  
  3486. if (getSkull() != SKULL_RED && getSkull() != SKULL_BLACK) {
  3487. setSkull(SKULL_NONE);
  3488. }
  3489. }
  3490.  
  3491. sendIcons();
  3492. }
  3493.  
  3494. void Player::onCombatRemoveCondition(Condition* condition)
  3495. {
  3496. //Creature::onCombatRemoveCondition(condition);
  3497. if (condition->getId() > 0) {
  3498. //Means the condition is from an item, id == slot
  3499. if (g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3500. Item* item = getInventoryItem(static_cast<slots_t>(condition->getId()));
  3501. if (item) {
  3502. //25% chance to destroy the item
  3503. if (25 >= uniform_random(1, 100)) {
  3504. g_game.internalRemoveItem(item);
  3505. }
  3506. }
  3507. }
  3508. } else {
  3509. if (!canDoAction()) {
  3510. const uint32_t delay = getNextActionTime();
  3511. const int32_t ticks = delay - (delay % EVENT_CREATURE_THINK_INTERVAL);
  3512. if (ticks < 0) {
  3513. removeCondition(condition);
  3514. } else {
  3515. condition->setTicks(ticks);
  3516. }
  3517. } else {
  3518. removeCondition(condition);
  3519. }
  3520. }
  3521. }
  3522.  
  3523. void Player::onAttackedCreature(Creature* target)
  3524. {
  3525. Creature::onAttackedCreature(target);
  3526.  
  3527. if (target->getZone() == ZONE_PVP) {
  3528. return;
  3529. }
  3530.  
  3531. if (target == this) {
  3532. addInFightTicks();
  3533. return;
  3534. }
  3535.  
  3536. if (hasFlag(PlayerFlag_NotGainInFight)) {
  3537. return;
  3538. }
  3539.  
  3540. Player* targetPlayer = target->getPlayer();
  3541. if (targetPlayer && !isPartner(targetPlayer) && !isGuildMate(targetPlayer)) {
  3542. if (!pzLocked && g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3543. pzLocked = true;
  3544. sendIcons();
  3545. }
  3546.  
  3547. if (getSkull() == SKULL_NONE && getSkullClient(targetPlayer) == SKULL_YELLOW) {
  3548. addAttacked(targetPlayer);
  3549. targetPlayer->sendCreatureSkull(this);
  3550. } else if (!targetPlayer->hasAttacked(this)) {
  3551. if (!pzLocked) {
  3552. pzLocked = true;
  3553. sendIcons();
  3554. }
  3555.  
  3556. if (!Combat::isInPvpZone(this, targetPlayer) && !isInWar(targetPlayer)) {
  3557. addAttacked(targetPlayer);
  3558.  
  3559. if (targetPlayer->getSkull() == SKULL_NONE && getSkull() == SKULL_NONE && !targetPlayer->hasKilled(this)) {
  3560. setSkull(SKULL_WHITE);
  3561. }
  3562.  
  3563. if (getSkull() == SKULL_NONE) {
  3564. targetPlayer->sendCreatureSkull(this);
  3565. }
  3566. }
  3567. }
  3568. }
  3569.  
  3570. addInFightTicks();
  3571. }
  3572.  
  3573. void Player::onAttacked()
  3574. {
  3575. Creature::onAttacked();
  3576.  
  3577. addInFightTicks();
  3578. }
  3579.  
  3580. void Player::onIdleStatus()
  3581. {
  3582. Creature::onIdleStatus();
  3583.  
  3584. if (party) {
  3585. party->clearPlayerPoints(this);
  3586. }
  3587. }
  3588.  
  3589. void Player::onPlacedCreature()
  3590. {
  3591. //scripting event - onLogin
  3592. if (!g_creatureEvents->playerLogin(this)) {
  3593. kickPlayer(true);
  3594. }
  3595.  
  3596. sendUnjustifiedPoints();
  3597. }
  3598.  
  3599. void Player::onAttackedCreatureDrainHealth(Creature* target, int32_t points)
  3600. {
  3601. Creature::onAttackedCreatureDrainHealth(target, points);
  3602.  
  3603. if (target) {
  3604. if (party && !Combat::isPlayerCombat(target)) {
  3605. Monster* tmpMonster = target->getMonster();
  3606. if (tmpMonster && tmpMonster->isHostile()) {
  3607. //We have fulfilled a requirement for shared experience
  3608. party->updatePlayerTicks(this, points);
  3609. }
  3610. }
  3611. }
  3612. }
  3613.  
  3614. void Player::onTargetCreatureGainHealth(Creature* target, int32_t points)
  3615. {
  3616. if (target && party) {
  3617. Player* tmpPlayer = nullptr;
  3618.  
  3619. if (isPartner(tmpPlayer) && (tmpPlayer != this)) {
  3620. tmpPlayer = target->getPlayer();
  3621. } else if (Creature* targetMaster = target->getMaster()) {
  3622. if (Player* targetMasterPlayer = targetMaster->getPlayer()) {
  3623. tmpPlayer = targetMasterPlayer;
  3624. }
  3625. }
  3626.  
  3627. if (isPartner(tmpPlayer)) {
  3628. party->updatePlayerTicks(this, points);
  3629. }
  3630. }
  3631. }
  3632.  
  3633. bool Player::onKilledCreature(Creature* target, bool lastHit/* = true*/)
  3634. {
  3635. bool unjustified = false;
  3636.  
  3637. if (hasFlag(PlayerFlag_NotGenerateLoot)) {
  3638. target->setDropLoot(false);
  3639. }
  3640.  
  3641. Creature::onKilledCreature(target, lastHit);
  3642.  
  3643. if (Player* targetPlayer = target->getPlayer()) {
  3644. if (targetPlayer && targetPlayer->getZone() == ZONE_PVP) {
  3645. targetPlayer->setDropLoot(false);
  3646. targetPlayer->setSkillLoss(false);
  3647. } else if (!hasFlag(PlayerFlag_NotGainInFight) && !isPartner(targetPlayer)) {
  3648. if (!Combat::isInPvpZone(this, targetPlayer) && hasAttacked(targetPlayer) && !targetPlayer->hasAttacked(this) && !isGuildMate(targetPlayer) && targetPlayer != this) {
  3649. if (targetPlayer->hasKilled(this)) {
  3650. for (auto& kill : targetPlayer->unjustifiedKills) {
  3651. if (kill.target == getGUID() && kill.unavenged) {
  3652. kill.unavenged = false;
  3653. auto it = attackedSet.find(targetPlayer->guid);
  3654. attackedSet.erase(it);
  3655. break;
  3656. }
  3657. }
  3658. } else if (targetPlayer->getSkull() == SKULL_NONE && !isInWar(targetPlayer)) {
  3659. unjustified = true;
  3660. addUnjustifiedDead(targetPlayer);
  3661. }
  3662.  
  3663. if (lastHit && hasCondition(CONDITION_INFIGHT)) {
  3664. pzLocked = true;
  3665. Condition* condition = Condition::createCondition(CONDITIONID_DEFAULT, CONDITION_INFIGHT, g_config.getNumber(ConfigManager::WHITE_SKULL_TIME), 0);
  3666. addCondition(condition);
  3667. }
  3668. }
  3669. }
  3670. }
  3671.  
  3672. return unjustified;
  3673. }
  3674.  
  3675. void Player::gainExperience(uint64_t gainExp, Creature* source)
  3676. {
  3677. if (hasFlag(PlayerFlag_NotGainExperience) || gainExp == 0 || staminaMinutes == 0) {
  3678. return;
  3679. }
  3680.  
  3681. addExperience(source, gainExp, true);
  3682. }
  3683.  
  3684. void Player::onGainExperience(uint64_t gainExp, Creature* target)
  3685. {
  3686. if (hasFlag(PlayerFlag_NotGainExperience)) {
  3687. return;
  3688. }
  3689.  
  3690. if (target && !target->getPlayer() && party && party->isSharedExperienceActive() && party->isSharedExperienceEnabled()) {
  3691. party->shareExperience(gainExp, target);
  3692. //We will get a share of the experience through the sharing mechanism
  3693. return;
  3694. }
  3695.  
  3696. Creature::onGainExperience(gainExp, target);
  3697. gainExperience(gainExp, target);
  3698. }
  3699.  
  3700. void Player::onGainSharedExperience(uint64_t gainExp, Creature* source)
  3701. {
  3702. gainExperience(gainExp, source);
  3703. }
  3704.  
  3705. bool Player::isImmune(CombatType_t type) const
  3706. {
  3707. if (hasFlag(PlayerFlag_CannotBeAttacked)) {
  3708. return true;
  3709. }
  3710. return Creature::isImmune(type);
  3711. }
  3712.  
  3713. bool Player::isImmune(ConditionType_t type) const
  3714. {
  3715. if (hasFlag(PlayerFlag_CannotBeAttacked)) {
  3716. return true;
  3717. }
  3718. return Creature::isImmune(type);
  3719. }
  3720.  
  3721. bool Player::isAttackable() const
  3722. {
  3723. return !hasFlag(PlayerFlag_CannotBeAttacked);
  3724. }
  3725.  
  3726. bool Player::lastHitIsPlayer(Creature* lastHitCreature)
  3727. {
  3728. if (!lastHitCreature) {
  3729. return false;
  3730. }
  3731.  
  3732. if (lastHitCreature->getPlayer()) {
  3733. return true;
  3734. }
  3735.  
  3736. Creature* lastHitMaster = lastHitCreature->getMaster();
  3737. return lastHitMaster && lastHitMaster->getPlayer();
  3738. }
  3739.  
  3740. void Player::changeHealth(int32_t healthChange, bool sendHealthChange/* = true*/)
  3741. {
  3742. Creature::changeHealth(healthChange, sendHealthChange);
  3743. sendStats();
  3744. }
  3745.  
  3746. void Player::changeMana(int32_t manaChange)
  3747. {
  3748. if (!hasFlag(PlayerFlag_HasInfiniteMana)) {
  3749. Creature::changeMana(manaChange);
  3750. }
  3751.  
  3752. sendStats();
  3753. }
  3754.  
  3755. void Player::changeSoul(int32_t soulChange)
  3756. {
  3757. if (soulChange > 0) {
  3758. soul += std::min<int32_t>(soulChange, vocation->getSoulMax() - soul);
  3759. } else {
  3760. soul = std::max<int32_t>(0, soul + soulChange);
  3761. }
  3762.  
  3763. sendStats();
  3764. }
  3765.  
  3766. bool Player::canWear(uint32_t lookType, uint8_t addons) const
  3767. {
  3768. if (group->access) {
  3769. return true;
  3770. }
  3771.  
  3772. const Outfit* outfit = Outfits::getInstance().getOutfitByLookType(sex, lookType);
  3773. if (!outfit) {
  3774. return false;
  3775. }
  3776.  
  3777. if (outfit->premium && !isPremium()) {
  3778. return false;
  3779. }
  3780.  
  3781. if (outfit->unlocked && addons == 0) {
  3782. return true;
  3783. }
  3784.  
  3785. for (const OutfitEntry& outfitEntry : outfits) {
  3786. if (outfitEntry.lookType != lookType) {
  3787. continue;
  3788. }
  3789. return (outfitEntry.addons & addons) == addons;
  3790. }
  3791. return false;
  3792. }
  3793.  
  3794. bool Player::canLogout()
  3795. {
  3796. if (isConnecting) {
  3797. return false;
  3798. }
  3799.  
  3800. if (getTile()->hasFlag(TILESTATE_NOLOGOUT)) {
  3801. return false;
  3802. }
  3803.  
  3804. if (getTile()->hasFlag(TILESTATE_PROTECTIONZONE)) {
  3805. return true;
  3806. }
  3807.  
  3808. return !isPzLocked() && !hasCondition(CONDITION_INFIGHT);
  3809. }
  3810.  
  3811. void Player::genReservedStorageRange()
  3812. {
  3813. //generate outfits range
  3814. uint32_t base_key = PSTRG_OUTFITS_RANGE_START;
  3815. for (const OutfitEntry& entry : outfits) {
  3816. storageMap[++base_key] = (entry.lookType << 16) | entry.addons;
  3817. }
  3818. }
  3819.  
  3820. void Player::addOutfit(uint16_t lookType, uint8_t addons)
  3821. {
  3822. for (OutfitEntry& outfitEntry : outfits) {
  3823. if (outfitEntry.lookType == lookType) {
  3824. outfitEntry.addons |= addons;
  3825. return;
  3826. }
  3827. }
  3828. outfits.emplace_back(lookType, addons);
  3829. }
  3830.  
  3831. bool Player::removeOutfit(uint16_t lookType)
  3832. {
  3833. for (auto it = outfits.begin(), end = outfits.end(); it != end; ++it) {
  3834. OutfitEntry& entry = *it;
  3835. if (entry.lookType == lookType) {
  3836. outfits.erase(it);
  3837. return true;
  3838. }
  3839. }
  3840. return false;
  3841. }
  3842.  
  3843. bool Player::removeOutfitAddon(uint16_t lookType, uint8_t addons)
  3844. {
  3845. for (OutfitEntry& outfitEntry : outfits) {
  3846. if (outfitEntry.lookType == lookType) {
  3847. outfitEntry.addons &= ~addons;
  3848. return true;
  3849. }
  3850. }
  3851. return false;
  3852. }
  3853.  
  3854. bool Player::getOutfitAddons(const Outfit& outfit, uint8_t& addons) const
  3855. {
  3856. if (group->access) {
  3857. addons = 3;
  3858. return true;
  3859. }
  3860.  
  3861. if (outfit.premium && !isPremium()) {
  3862. return false;
  3863. }
  3864.  
  3865. for (const OutfitEntry& outfitEntry : outfits) {
  3866. if (outfitEntry.lookType != outfit.lookType) {
  3867. continue;
  3868. }
  3869.  
  3870. addons = outfitEntry.addons;
  3871. return true;
  3872. }
  3873.  
  3874. if (!outfit.unlocked) {
  3875. return false;
  3876. }
  3877.  
  3878. addons = 0;
  3879. return true;
  3880. }
  3881.  
  3882. void Player::setSex(PlayerSex_t newSex)
  3883. {
  3884. sex = newSex;
  3885. }
  3886.  
  3887. Skulls_t Player::getSkull() const
  3888. {
  3889. if (hasFlag(PlayerFlag_NotGainInFight)) {
  3890. return SKULL_NONE;
  3891. }
  3892. return skull;
  3893. }
  3894.  
  3895. Skulls_t Player::getSkullClient(const Creature* creature) const
  3896. {
  3897. if (!creature || g_game.getWorldType() != WORLD_TYPE_PVP) {
  3898. return SKULL_NONE;
  3899. }
  3900.  
  3901. const Player* player = creature->getPlayer();
  3902. if (player && player->getSkull() == SKULL_NONE) {
  3903. if (player == this) {
  3904. for (const auto& kill : unjustifiedKills) {
  3905. if (kill.unavenged && (time(nullptr) - kill.time) < g_config.getNumber(ConfigManager::ORANGE_SKULL_DURATION) * 24 * 60 * 60) {
  3906. return SKULL_ORANGE;
  3907. }
  3908. }
  3909. }
  3910.  
  3911. if (isInWar(player)) {
  3912. return SKULL_GREEN;
  3913. }
  3914.  
  3915. if (!player->getGuildWarVector().empty() && guild == player->getGuild()) {
  3916. return SKULL_GREEN;
  3917. }
  3918.  
  3919. if (player->hasKilled(this)) {
  3920. return SKULL_ORANGE;
  3921. }
  3922.  
  3923. if (player->hasAttacked(this)) {
  3924. return SKULL_YELLOW;
  3925. }
  3926.  
  3927. if (isPartner(player)) {
  3928. return SKULL_GREEN;
  3929. }
  3930. }
  3931. return Creature::getSkullClient(creature);
  3932. }
  3933.  
  3934. bool Player::hasKilled(const Player* player) const
  3935. {
  3936. for (const auto& kill : unjustifiedKills) {
  3937. if (kill.target == player->getGUID() && (time(nullptr) - kill.time) < g_config.getNumber(ConfigManager::ORANGE_SKULL_DURATION) * 24 * 60 * 60 && kill.unavenged) {
  3938. return true;
  3939. }
  3940. }
  3941.  
  3942. return false;
  3943. }
  3944.  
  3945. bool Player::hasAttacked(const Player* attacked) const
  3946. {
  3947. if (hasFlag(PlayerFlag_NotGainInFight) || !attacked) {
  3948. return false;
  3949. }
  3950.  
  3951. return attackedSet.find(attacked->guid) != attackedSet.end();
  3952. }
  3953.  
  3954. void Player::addAttacked(const Player* attacked)
  3955. {
  3956. if (hasFlag(PlayerFlag_NotGainInFight) || !attacked || attacked == this) {
  3957. return;
  3958. }
  3959.  
  3960. attackedSet.insert(attacked->guid);
  3961. }
  3962.  
  3963. void Player::removeAttacked(const Player* attacked)
  3964. {
  3965. if (!attacked || attacked == this) {
  3966. return;
  3967. }
  3968.  
  3969. auto it = attackedSet.find(attacked->guid);
  3970. if (it != attackedSet.end()) {
  3971. attackedSet.erase(it);
  3972. }
  3973. }
  3974.  
  3975. void Player::clearAttacked()
  3976. {
  3977. attackedSet.clear();
  3978. }
  3979.  
  3980. void Player::addUnjustifiedDead(const Player* attacked)
  3981. {
  3982. if (hasFlag(PlayerFlag_NotGainInFight) || attacked == this || g_game.getWorldType() == WORLD_TYPE_PVP_ENFORCED) {
  3983. return;
  3984. }
  3985.  
  3986. sendTextMessage(MESSAGE_EVENT_ADVANCE, "Warning! The murder of " + attacked->getName() + " was not justified.");
  3987.  
  3988. unjustifiedKills.emplace_back(attacked->getGUID(), time(nullptr), true);
  3989.  
  3990. uint8_t dayKills = 0;
  3991. uint8_t weekKills = 0;
  3992. uint8_t monthKills = 0;
  3993.  
  3994. for (const auto& kill : unjustifiedKills) {
  3995. const auto diff = time(nullptr) - kill.time;
  3996. if (diff <= 24 * 60 * 60) {
  3997. dayKills += 1;
  3998. }
  3999. if (diff <= 7 * 24 * 60 * 60) {
  4000. weekKills += 1;
  4001. }
  4002. if (diff <= 30 * 24 * 60 * 60) {
  4003. monthKills += 1;
  4004. }
  4005. }
  4006.  
  4007. if (getSkull() != SKULL_BLACK) {
  4008. if (dayKills >= 2 * g_config.getNumber(ConfigManager::DAY_KILLS_TO_RED) || weekKills >= 2 * g_config.getNumber(ConfigManager::WEEK_KILLS_TO_RED) || monthKills >= 2 * g_config.getNumber(ConfigManager::MONTH_KILLS_TO_RED)) {
  4009. setSkull(SKULL_BLACK);
  4010. //start black skull time
  4011. skullTicks = static_cast<int64_t>(g_config.getNumber(ConfigManager::BLACK_SKULL_DURATION)) * 24 * 60 * 60 * 1000;
  4012. } else if (dayKills >= g_config.getNumber(ConfigManager::DAY_KILLS_TO_RED) || weekKills >= g_config.getNumber(ConfigManager::WEEK_KILLS_TO_RED) || monthKills >= g_config.getNumber(ConfigManager::MONTH_KILLS_TO_RED)) {
  4013. setSkull(SKULL_RED);
  4014. //reset red skull time
  4015. skullTicks = static_cast<int64_t>(g_config.getNumber(ConfigManager::RED_SKULL_DURATION)) * 24 * 60 * 60 * 1000;
  4016. }
  4017. }
  4018.  
  4019. sendUnjustifiedPoints();
  4020. }
  4021.  
  4022. void Player::checkSkullTicks(int64_t ticks)
  4023. {
  4024. int64_t newTicks = skullTicks - ticks;
  4025. if (newTicks < 0) {
  4026. skullTicks = 0;
  4027. } else {
  4028. skullTicks = newTicks;
  4029. }
  4030.  
  4031. if ((skull == SKULL_RED || skull == SKULL_BLACK) && skullTicks < 1000 && !hasCondition(CONDITION_INFIGHT)) {
  4032. setSkull(SKULL_NONE);
  4033. }
  4034. }
  4035.  
  4036. bool Player::isPromoted() const
  4037. {
  4038. uint16_t promotedVocation = g_vocations.getPromotedVocation(vocation->getId());
  4039. return promotedVocation == VOCATION_NONE && vocation->getId() != promotedVocation;
  4040. }
  4041.  
  4042. double Player::getLostPercent() const
  4043. {
  4044. int32_t blessingCount = 0;
  4045. uint8_t maxBlessing = (operatingSystem == CLIENTOS_NEW_WINDOWS) ? 8 : 6;
  4046. for (int i = 1; i <= maxBlessing; i++) {
  4047. if (hasBlessing(i)) {
  4048. blessingCount++;
  4049. }
  4050. }
  4051.  
  4052. int32_t deathLosePercent = g_config.getNumber(ConfigManager::DEATH_LOSE_PERCENT);
  4053. if (deathLosePercent != -1) {
  4054. if (isPromoted()) {
  4055. deathLosePercent -= 3;
  4056. }
  4057.  
  4058. deathLosePercent -= blessingCount;
  4059. return std::max<int32_t>(0, deathLosePercent) / 100.;
  4060. }
  4061.  
  4062. double lossPercent;
  4063. if (level >= 25) {
  4064. double tmpLevel = level + (levelPercent / 100.);
  4065. lossPercent = static_cast<double>((tmpLevel + 50) * 50 * ((tmpLevel * tmpLevel) - (5 * tmpLevel) + 8)) / experience;
  4066. } else {
  4067. lossPercent = 10;
  4068. }
  4069.  
  4070. if (isPromoted()) {
  4071. lossPercent *= 0.7;
  4072. }
  4073.  
  4074. return lossPercent * pow(0.92, blessingCount) / 100;
  4075. }
  4076.  
  4077. void Player::learnInstantSpell(const std::string& spellName)
  4078. {
  4079. if (!hasLearnedInstantSpell(spellName)) {
  4080. learnedInstantSpellList.push_front(spellName);
  4081. }
  4082. }
  4083.  
  4084. void Player::forgetInstantSpell(const std::string& spellName)
  4085. {
  4086. learnedInstantSpellList.remove(spellName);
  4087. }
  4088.  
  4089. bool Player::hasLearnedInstantSpell(const std::string& spellName) const
  4090. {
  4091. if (hasFlag(PlayerFlag_CannotUseSpells)) {
  4092. return false;
  4093. }
  4094.  
  4095. if (hasFlag(PlayerFlag_IgnoreSpellCheck)) {
  4096. return true;
  4097. }
  4098.  
  4099. for (const auto& learnedSpellName : learnedInstantSpellList) {
  4100. if (strcasecmp(learnedSpellName.c_str(), spellName.c_str()) == 0) {
  4101. return true;
  4102. }
  4103. }
  4104. return false;
  4105. }
  4106.  
  4107. bool Player::isInWar(const Player* player) const
  4108. {
  4109. if (!player || !guild) {
  4110. return false;
  4111. }
  4112.  
  4113. const Guild* playerGuild = player->getGuild();
  4114. if (!playerGuild) {
  4115. return false;
  4116. }
  4117.  
  4118. return isInWarList(playerGuild->getId()) && player->isInWarList(guild->getId());
  4119. }
  4120.  
  4121. bool Player::isInWarList(uint32_t guildId) const
  4122. {
  4123. return std::find(guildWarVector.begin(), guildWarVector.end(), guildId) != guildWarVector.end();
  4124. }
  4125.  
  4126. bool Player::isPremium() const
  4127. {
  4128. if (g_config.getBoolean(ConfigManager::FREE_PREMIUM) || hasFlag(PlayerFlag_IsAlwaysPremium)) {
  4129. return true;
  4130. }
  4131.  
  4132. return premiumDays > 0;
  4133. }
  4134.  
  4135. void Player::setPremiumDays(int32_t v)
  4136. {
  4137. premiumDays = v;
  4138. sendBasicData();
  4139. }
  4140.  
  4141. void Player::setTibiaCoins(int32_t v)
  4142. {
  4143. tibiaCoins = v;
  4144. }
  4145.  
  4146. PartyShields_t Player::getPartyShield(const Player* player) const
  4147. {
  4148. if (!player) {
  4149. return SHIELD_NONE;
  4150. }
  4151.  
  4152. if (party) {
  4153. if (party->getLeader() == player) {
  4154. if (party->isSharedExperienceActive()) {
  4155. if (party->isSharedExperienceEnabled()) {
  4156. return SHIELD_YELLOW_SHAREDEXP;
  4157. }
  4158.  
  4159. if (party->canUseSharedExperience(player)) {
  4160. return SHIELD_YELLOW_NOSHAREDEXP;
  4161. }
  4162.  
  4163. return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
  4164. }
  4165.  
  4166. return SHIELD_YELLOW;
  4167. }
  4168.  
  4169. if (player->party == party) {
  4170. if (party->isSharedExperienceActive()) {
  4171. if (party->isSharedExperienceEnabled()) {
  4172. return SHIELD_BLUE_SHAREDEXP;
  4173. }
  4174.  
  4175. if (party->canUseSharedExperience(player)) {
  4176. return SHIELD_BLUE_NOSHAREDEXP;
  4177. }
  4178.  
  4179. return SHIELD_BLUE_NOSHAREDEXP_BLINK;
  4180. }
  4181.  
  4182. return SHIELD_BLUE;
  4183. }
  4184.  
  4185. if (isInviting(player)) {
  4186. return SHIELD_WHITEBLUE;
  4187. }
  4188. }
  4189.  
  4190. if (player->isInviting(this)) {
  4191. return SHIELD_WHITEYELLOW;
  4192. }
  4193.  
  4194. if (player->party) {
  4195. return SHIELD_GRAY;
  4196. }
  4197.  
  4198. return SHIELD_NONE;
  4199. }
  4200.  
  4201. bool Player::isInviting(const Player* player) const
  4202. {
  4203. if (!player || !party || party->getLeader() != this) {
  4204. return false;
  4205. }
  4206. return party->isPlayerInvited(player);
  4207. }
  4208.  
  4209. bool Player::isPartner(const Player* player) const
  4210. {
  4211. if (!player || !party) {
  4212. return false;
  4213. }
  4214. return party == player->party;
  4215. }
  4216.  
  4217. bool Player::isGuildMate(const Player* player) const
  4218. {
  4219. if (!player || !guild) {
  4220. return false;
  4221. }
  4222. return guild == player->guild;
  4223. }
  4224.  
  4225. void Player::sendPlayerPartyIcons(Player* player)
  4226. {
  4227. sendCreatureShield(player);
  4228. sendCreatureSkull(player);
  4229. }
  4230.  
  4231. bool Player::addPartyInvitation(Party* party)
  4232. {
  4233. auto it = std::find(invitePartyList.begin(), invitePartyList.end(), party);
  4234. if (it != invitePartyList.end()) {
  4235. return false;
  4236. }
  4237.  
  4238. invitePartyList.push_front(party);
  4239. return true;
  4240. }
  4241.  
  4242. void Player::removePartyInvitation(Party* party)
  4243. {
  4244. invitePartyList.remove(party);
  4245. }
  4246.  
  4247. void Player::clearPartyInvitations()
  4248. {
  4249. for (Party* invitingParty : invitePartyList) {
  4250. invitingParty->removeInvite(*this, false);
  4251. }
  4252. invitePartyList.clear();
  4253. }
  4254.  
  4255. GuildEmblems_t Player::getGuildEmblem(const Player* player) const
  4256. {
  4257. if (!player) {
  4258. return GUILDEMBLEM_NONE;
  4259. }
  4260.  
  4261. const Guild* playerGuild = player->getGuild();
  4262. if (!playerGuild) {
  4263. return GUILDEMBLEM_NONE;
  4264. }
  4265.  
  4266. if (player->getGuildWarVector().empty()) {
  4267. if (guild == playerGuild) {
  4268. return GUILDEMBLEM_MEMBER;
  4269. } else {
  4270. return GUILDEMBLEM_OTHER;
  4271. }
  4272. } else if (guild == playerGuild) {
  4273. return GUILDEMBLEM_ALLY;
  4274. } else if (isInWar(player)) {
  4275. return GUILDEMBLEM_ENEMY;
  4276. }
  4277.  
  4278. return GUILDEMBLEM_NEUTRAL;
  4279. }
  4280.  
  4281. void Player::sendUnjustifiedPoints()
  4282. {
  4283. if (client) {
  4284. double dayKills = 0;
  4285. double weekKills = 0;
  4286. double monthKills = 0;
  4287.  
  4288. for (const auto& kill : unjustifiedKills) {
  4289. const auto diff = time(nullptr) - kill.time;
  4290. if (diff <= 24 * 60 * 60) {
  4291. dayKills += 1;
  4292. }
  4293. if (diff <= 7 * 24 * 60 * 60) {
  4294. weekKills += 1;
  4295. }
  4296. if (diff <= 30 * 24 * 60 * 60) {
  4297. monthKills += 1;
  4298. }
  4299. }
  4300.  
  4301. bool isRed = getSkull() == SKULL_RED;
  4302.  
  4303. auto dayMax = ((isRed ? 2 : 1) * g_config.getNumber(ConfigManager::DAY_KILLS_TO_RED));
  4304. auto weekMax = ((isRed ? 2 : 1) * g_config.getNumber(ConfigManager::WEEK_KILLS_TO_RED));
  4305. auto monthMax = ((isRed ? 2 : 1) * g_config.getNumber(ConfigManager::MONTH_KILLS_TO_RED));
  4306.  
  4307. uint8_t dayProgress = std::min(std::round(dayKills / dayMax * 100), 100.0);
  4308. uint8_t weekProgress = std::min(std::round(weekKills / weekMax * 100), 100.0);
  4309. uint8_t monthProgress = std::min(std::round(monthKills / monthMax * 100), 100.0);
  4310. uint8_t skullDuration = 0;
  4311. if (skullTicks != 0) {
  4312. skullDuration = std::floor<uint8_t>(skullTicks / (24 * 60 * 60 * 1000));
  4313. }
  4314. client->sendUnjustifiedPoints(dayProgress, std::max(dayMax - dayKills, 0.0), weekProgress, std::max(weekMax - weekKills, 0.0), monthProgress, std::max(monthMax - monthKills, 0.0), skullDuration);
  4315. }
  4316. }
  4317.  
  4318. uint8_t Player::getCurrentMount() const
  4319. {
  4320. int32_t value;
  4321. if (getStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, value)) {
  4322. return value;
  4323. }
  4324. return 0;
  4325. }
  4326.  
  4327. void Player::setCurrentMount(uint8_t mount)
  4328. {
  4329. addStorageValue(PSTRG_MOUNTS_CURRENTMOUNT, mount);
  4330. }
  4331.  
  4332. bool Player::toggleMount(bool mount)
  4333. {
  4334. if ((OTSYS_TIME() - lastToggleMount) < 3000 && !wasMounted) {
  4335. sendCancelMessage(RETURNVALUE_YOUAREEXHAUSTED);
  4336. return false;
  4337. }
  4338.  
  4339. if (mount) {
  4340. if (isMounted()) {
  4341. return false;
  4342. }
  4343.  
  4344. if (!group->access && tile->hasFlag(TILESTATE_PROTECTIONZONE)) {
  4345. sendCancelMessage(RETURNVALUE_ACTIONNOTPERMITTEDINPROTECTIONZONE);
  4346. return false;
  4347. }
  4348.  
  4349. const Outfit* playerOutfit = Outfits::getInstance().getOutfitByLookType(getSex(), defaultOutfit.lookType);
  4350. if (!playerOutfit) {
  4351. return false;
  4352. }
  4353.  
  4354. uint8_t currentMountId = getCurrentMount();
  4355. if (currentMountId == 0) {
  4356. sendOutfitWindow();
  4357. return false;
  4358. }
  4359.  
  4360. Mount* currentMount = g_game.mounts.getMountByID(currentMountId);
  4361. if (!currentMount) {
  4362. return false;
  4363. }
  4364.  
  4365. if (!hasMount(currentMount)) {
  4366. setCurrentMount(0);
  4367. sendOutfitWindow();
  4368. return false;
  4369. }
  4370.  
  4371. if (currentMount->premium && !isPremium()) {
  4372. sendCancelMessage(RETURNVALUE_YOUNEEDPREMIUMACCOUNT);
  4373. return false;
  4374. }
  4375.  
  4376. if (hasCondition(CONDITION_OUTFIT)) {
  4377. sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
  4378. return false;
  4379. }
  4380.  
  4381. defaultOutfit.lookMount = currentMount->clientId;
  4382.  
  4383. if (currentMount->speed != 0) {
  4384. g_game.changeSpeed(this, currentMount->speed);
  4385. }
  4386. } else {
  4387. if (!isMounted()) {
  4388. return false;
  4389. }
  4390.  
  4391. dismount();
  4392. }
  4393.  
  4394. g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  4395. lastToggleMount = OTSYS_TIME();
  4396. return true;
  4397. }
  4398.  
  4399. bool Player::tameMount(uint8_t mountId)
  4400. {
  4401. if (!g_game.mounts.getMountByID(mountId)) {
  4402. return false;
  4403. }
  4404.  
  4405. const uint8_t tmpMountId = mountId - 1;
  4406. const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
  4407.  
  4408. int32_t value;
  4409. if (getStorageValue(key, value)) {
  4410. value |= (1 << (tmpMountId % 31));
  4411. } else {
  4412. value = (1 << (tmpMountId % 31));
  4413. }
  4414.  
  4415. addStorageValue(key, value);
  4416. return true;
  4417. }
  4418.  
  4419. bool Player::untameMount(uint8_t mountId)
  4420. {
  4421. if (!g_game.mounts.getMountByID(mountId)) {
  4422. return false;
  4423. }
  4424.  
  4425. const uint8_t tmpMountId = mountId - 1;
  4426. const uint32_t key = PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31);
  4427.  
  4428. int32_t value;
  4429. if (!getStorageValue(key, value)) {
  4430. return true;
  4431. }
  4432.  
  4433. value &= ~(1 << (tmpMountId % 31));
  4434. addStorageValue(key, value);
  4435.  
  4436. if (getCurrentMount() == mountId) {
  4437. if (isMounted()) {
  4438. dismount();
  4439. g_game.internalCreatureChangeOutfit(this, defaultOutfit);
  4440. }
  4441.  
  4442. setCurrentMount(0);
  4443. }
  4444.  
  4445. return true;
  4446. }
  4447.  
  4448. bool Player::hasMount(const Mount* mount) const
  4449. {
  4450. if (isAccessPlayer()) {
  4451. return true;
  4452. }
  4453.  
  4454. if (mount->premium && !isPremium()) {
  4455. return false;
  4456. }
  4457.  
  4458. const uint8_t tmpMountId = mount->id - 1;
  4459.  
  4460. int32_t value;
  4461. if (!getStorageValue(PSTRG_MOUNTS_RANGE_START + (tmpMountId / 31), value)) {
  4462. return false;
  4463. }
  4464.  
  4465. return ((1 << (tmpMountId % 31)) & value) != 0;
  4466. }
  4467.  
  4468. void Player::dismount()
  4469. {
  4470. Mount* mount = g_game.mounts.getMountByID(getCurrentMount());
  4471. if (mount && mount->speed > 0) {
  4472. g_game.changeSpeed(this, -mount->speed);
  4473. }
  4474.  
  4475. defaultOutfit.lookMount = 0;
  4476. }
  4477.  
  4478. bool Player::addOfflineTrainingTries(skills_t skill, uint64_t tries)
  4479. {
  4480. if (tries == 0 || skill == SKILL_LEVEL) {
  4481. return false;
  4482. }
  4483.  
  4484. bool sendUpdate = false;
  4485. uint32_t oldSkillValue, newSkillValue;
  4486. long double oldPercentToNextLevel, newPercentToNextLevel;
  4487.  
  4488. if (skill == SKILL_MAGLEVEL) {
  4489. uint64_t currReqMana = vocation->getReqMana(magLevel);
  4490. uint64_t nextReqMana = vocation->getReqMana(magLevel + 1);
  4491.  
  4492. if (currReqMana >= nextReqMana) {
  4493. return false;
  4494. }
  4495.  
  4496. oldSkillValue = magLevel;
  4497. oldPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
  4498.  
  4499. g_events->eventPlayerOnGainSkillTries(this, SKILL_MAGLEVEL, tries);
  4500. uint32_t currMagLevel = magLevel;
  4501.  
  4502. while ((manaSpent + tries) >= nextReqMana) {
  4503. tries -= nextReqMana - manaSpent;
  4504.  
  4505. magLevel++;
  4506. manaSpent = 0;
  4507.  
  4508. g_creatureEvents->playerAdvance(this, SKILL_MAGLEVEL, magLevel - 1, magLevel);
  4509.  
  4510. sendUpdate = true;
  4511. currReqMana = nextReqMana;
  4512. nextReqMana = vocation->getReqMana(magLevel + 1);
  4513.  
  4514. if (currReqMana >= nextReqMana) {
  4515. tries = 0;
  4516. break;
  4517. }
  4518. }
  4519.  
  4520. manaSpent += tries;
  4521.  
  4522. if (magLevel != currMagLevel) {
  4523. std::ostringstream ss;
  4524. ss << "You advanced to magic level " << magLevel << '.';
  4525. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4526. }
  4527.  
  4528. uint8_t newPercent;
  4529. if (nextReqMana > currReqMana) {
  4530. newPercent = Player::getPercentLevel(manaSpent, nextReqMana);
  4531. newPercentToNextLevel = static_cast<long double>(manaSpent * 100) / nextReqMana;
  4532. } else {
  4533. newPercent = 0;
  4534. newPercentToNextLevel = 0;
  4535. }
  4536.  
  4537. if (newPercent != magLevelPercent) {
  4538. magLevelPercent = newPercent;
  4539. sendUpdate = true;
  4540. }
  4541.  
  4542. newSkillValue = magLevel;
  4543. } else {
  4544. uint64_t currReqTries = vocation->getReqSkillTries(skill, skills[skill].level);
  4545. uint64_t nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  4546. if (currReqTries >= nextReqTries) {
  4547. return false;
  4548. }
  4549.  
  4550. oldSkillValue = skills[skill].level;
  4551. oldPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
  4552.  
  4553. g_events->eventPlayerOnGainSkillTries(this, skill, tries);
  4554. uint32_t currSkillLevel = skills[skill].level;
  4555.  
  4556. while ((skills[skill].tries + tries) >= nextReqTries) {
  4557. tries -= nextReqTries - skills[skill].tries;
  4558.  
  4559. skills[skill].level++;
  4560. skills[skill].tries = 0;
  4561. skills[skill].percent = 0;
  4562.  
  4563. g_creatureEvents->playerAdvance(this, skill, (skills[skill].level - 1), skills[skill].level);
  4564.  
  4565. sendUpdate = true;
  4566. currReqTries = nextReqTries;
  4567. nextReqTries = vocation->getReqSkillTries(skill, skills[skill].level + 1);
  4568.  
  4569. if (currReqTries >= nextReqTries) {
  4570. tries = 0;
  4571. break;
  4572. }
  4573. }
  4574.  
  4575. skills[skill].tries += tries;
  4576.  
  4577. if (currSkillLevel != skills[skill].level) {
  4578. std::ostringstream ss;
  4579. ss << "You advanced to " << getSkillName(skill) << " level " << skills[skill].level << '.';
  4580. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4581. }
  4582.  
  4583. uint8_t newPercent;
  4584. if (nextReqTries > currReqTries) {
  4585. newPercent = Player::getPercentLevel(skills[skill].tries, nextReqTries);
  4586. newPercentToNextLevel = static_cast<long double>(skills[skill].tries * 100) / nextReqTries;
  4587. } else {
  4588. newPercent = 0;
  4589. newPercentToNextLevel = 0;
  4590. }
  4591.  
  4592. if (skills[skill].percent != newPercent) {
  4593. skills[skill].percent = newPercent;
  4594. sendUpdate = true;
  4595. }
  4596.  
  4597. newSkillValue = skills[skill].level;
  4598. }
  4599.  
  4600. if (sendUpdate) {
  4601. sendSkills();
  4602. }
  4603.  
  4604. std::ostringstream ss;
  4605. ss << std::fixed << std::setprecision(2) << "Your " << ucwords(getSkillName(skill)) << " skill changed from level " << oldSkillValue << " (with " << oldPercentToNextLevel << "% progress towards level " << (oldSkillValue + 1) << ") to level " << newSkillValue << " (with " << newPercentToNextLevel << "% progress towards level " << (newSkillValue + 1) << ')';
  4606. sendTextMessage(MESSAGE_EVENT_ADVANCE, ss.str());
  4607. return sendUpdate;
  4608. }
  4609.  
  4610. bool Player::hasModalWindowOpen(uint32_t modalWindowId) const
  4611. {
  4612. return find(modalWindows.begin(), modalWindows.end(), modalWindowId) != modalWindows.end();
  4613. }
  4614.  
  4615. void Player::onModalWindowHandled(uint32_t modalWindowId)
  4616. {
  4617. modalWindows.remove(modalWindowId);
  4618. }
  4619.  
  4620. void Player::sendModalWindow(const ModalWindow& modalWindow)
  4621. {
  4622. if (!client) {
  4623. return;
  4624. }
  4625.  
  4626. modalWindows.push_front(modalWindow.id);
  4627. client->sendModalWindow(modalWindow);
  4628. }
  4629.  
  4630. void Player::clearModalWindows()
  4631. {
  4632. modalWindows.clear();
  4633. }
  4634.  
  4635. uint16_t Player::getHelpers() const
  4636. {
  4637. uint16_t helpers;
  4638.  
  4639. if (guild && party) {
  4640. std::unordered_set<Player*> helperSet;
  4641.  
  4642. const auto& guildMembers = guild->getMembersOnline();
  4643. helperSet.insert(guildMembers.begin(), guildMembers.end());
  4644.  
  4645. const auto& partyMembers = party->getMembers();
  4646. helperSet.insert(partyMembers.begin(), partyMembers.end());
  4647.  
  4648. const auto& partyInvitees = party->getInvitees();
  4649. helperSet.insert(partyInvitees.begin(), partyInvitees.end());
  4650.  
  4651. helperSet.insert(party->getLeader());
  4652.  
  4653. helpers = helperSet.size();
  4654. } else if (guild) {
  4655. helpers = guild->getMembersOnline().size();
  4656. } else if (party) {
  4657. helpers = party->getMemberCount() + party->getInvitationCount() + 1;
  4658. } else {
  4659. helpers = 0;
  4660. }
  4661.  
  4662. return helpers;
  4663. }
  4664.  
  4665. void Player::sendClosePrivate(uint16_t channelId)
  4666. {
  4667. if (channelId == CHANNEL_GUILD || channelId == CHANNEL_PARTY) {
  4668. g_chat->removeUserFromChannel(*this, channelId);
  4669. }
  4670.  
  4671. if (client) {
  4672. client->sendClosePrivate(channelId);
  4673. }
  4674. }
  4675.  
  4676. uint64_t Player::getMoney() const
  4677. {
  4678. std::vector<const Container*> containers;
  4679. uint64_t moneyCount = 0;
  4680.  
  4681. for (int32_t i = CONST_SLOT_FIRST; i <= CONST_SLOT_LAST; ++i) {
  4682. Item* item = inventory[i];
  4683. if (!item) {
  4684. continue;
  4685. }
  4686.  
  4687. const Container* container = item->getContainer();
  4688. if (container) {
  4689. containers.push_back(container);
  4690. } else {
  4691. moneyCount += item->getWorth();
  4692. }
  4693. }
  4694.  
  4695. size_t i = 0;
  4696. while (i < containers.size()) {
  4697. const Container* container = containers[i++];
  4698. for (const Item* item : container->getItemList()) {
  4699. const Container* tmpContainer = item->getContainer();
  4700. if (tmpContainer) {
  4701. containers.push_back(tmpContainer);
  4702. } else {
  4703. moneyCount += item->getWorth();
  4704. }
  4705. }
  4706. }
  4707. return moneyCount;
  4708. }
  4709.  
  4710. size_t Player::getMaxVIPEntries() const
  4711. {
  4712. if (group->maxVipEntries != 0) {
  4713. return group->maxVipEntries;
  4714. } else if (isPremium()) {
  4715. return 100;
  4716. }
  4717. return 20;
  4718. }
  4719.  
  4720. size_t Player::getMaxDepotItems() const
  4721. {
  4722. if (group->maxDepotItems != 0) {
  4723. return group->maxDepotItems;
  4724. } else if (isPremium()) {
  4725. return 3000; // its better add limit for max 3000 ea depotId
  4726. }
  4727. return 2000;
  4728. }
  4729.  
  4730. std::forward_list<Condition*> Player::getMuteConditions() const
  4731. {
  4732. std::forward_list<Condition*> muteConditions;
  4733. for (Condition* condition : conditions) {
  4734. if (condition->getTicks() <= 0) {
  4735. continue;
  4736. }
  4737.  
  4738. ConditionType_t type = condition->getType();
  4739. if (type != CONDITION_MUTED && type != CONDITION_CHANNELMUTEDTICKS && type != CONDITION_YELLTICKS) {
  4740. continue;
  4741. }
  4742.  
  4743. muteConditions.push_front(condition);
  4744. }
  4745. return muteConditions;
  4746. }
  4747.  
  4748. void Player::setGuild(Guild* guild)
  4749. {
  4750. if (guild == this->guild) {
  4751. return;
  4752. }
  4753.  
  4754. Guild* oldGuild = this->guild;
  4755.  
  4756. this->guildNick.clear();
  4757. this->guild = nullptr;
  4758. this->guildRank = nullptr;
  4759.  
  4760. if (guild) {
  4761. const GuildRank* rank = guild->getRankByLevel(1);
  4762. if (!rank) {
  4763. return;
  4764. }
  4765.  
  4766. this->guild = guild;
  4767. this->guildRank = rank;
  4768. guild->addMember(this);
  4769. }
  4770.  
  4771. if (oldGuild) {
  4772. oldGuild->removeMember(this);
  4773. }
  4774. }
  4775.  
  4776. void Player::doCriticalDamage(CombatDamage& damage) const
  4777. {
  4778. int32_t criticalChance = getSkillLevel(SKILL_CRITICAL_HIT_CHANCE);
  4779. if (uniform_random(1, 100) <= criticalChance) {
  4780. float multiplier = 1 + ((float) getSkillLevel(SKILL_CRITICAL_HIT_DAMAGE) / 100);
  4781.  
  4782. damage.primary.value = (int32_t) (multiplier * damage.primary.value);
  4783. damage.secondary.value = (int32_t) (multiplier * damage.secondary.value);
  4784. damage.critical = true;
  4785. }
  4786. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement