Guest User

Untitled

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