Advertisement
Guest User

player.cpp

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