Advertisement
Guest User

Untitled

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