Advertisement
Guest User

Untitled

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