Guest User

Untitled

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