Advertisement
Guest User

Untitled

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