Advertisement
Guest User

Untitled

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