Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3369,6 +3369,26 @@ bool Player::canDoAction() const {
return nextAction <= OTSYS_TIME();
}

void Player::setNextNecklaceAction(int64_t time) {
if (time > nextNecklaceAction) {
nextNecklaceAction = time;
}
}

void Player::setNextRingAction(int64_t time) {
if (time > nextRingAction) {
nextRingAction = time;
}
}

bool Player::canEquipNecklace() const {
return OTSYS_TIME() >= nextNecklaceAction;
}

bool Player::canEquipRing() const {
return OTSYS_TIME() >= nextRingAction;
}

void Player::setNextPotionAction(int64_t time) {
if (time > nextPotionAction) {
nextPotionAction = time;
Expand Down
8 changes: 8 additions & 0 deletions src/creatures/players/player.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1115,6 +1115,12 @@ class Player final : public Creature, public Cylinder, public Bankable {
void setNextPotionAction(int64_t time);
bool canDoPotionAction() const;

void setNextNecklaceAction(int64_t time);
bool canEquipNecklace() const;

void setNextRingAction(int64_t time);
bool canEquipRing() const;

void setNextExAction(int64_t time);
bool canDoExAction() const;

Expand Down Expand Up @@ -1680,6 +1686,8 @@ class Player final : public Creature, public Cylinder, public Bankable {
int64_t nextExAction = 0;
int64_t nextImbuementAction = 0;
int64_t nextPotionAction = 0;
int64_t nextNecklaceAction = 0;
int64_t nextRingAction = 0;
int64_t nextMarketAction = 0;
int64_t lastQuickLootNotification = 0;
int64_t lastWalking = 0;
Expand Down
124 changes: 60 additions & 64 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3468,6 +3468,10 @@ uint64_t Game::getItemMarketPrice(const std::map<uint16_t, uint64_t> &itemMap, b
}

std::shared_ptr<Item> searchForItem(const std::shared_ptr<Container> &container, uint16_t itemId, bool hasTier /* = false*/, uint8_t tier /* = 0*/) {
if (!container) {
return nullptr;
}

for (ContainerIterator it = container->iterator(); it.hasNext(); it.advance()) {
if ((*it)->getID() == itemId && (!hasTier || (*it)->getTier() == tier)) {
return *it;
Expand Down Expand Up @@ -3510,6 +3514,32 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* =
return;
}

const ItemType &it = Item::items[itemId];
Slots_t slot = getSlotType(it);

if (slot == CONST_SLOT_NECKLACE) {
if (!player->canEquipNecklace()) {
return;
}
} else if (slot == CONST_SLOT_RING) {
if (!player->canEquipRing()) {
return;
}
} else if (!player->canDoAction()) {
uint32_t delay = player->getNextActionTime();
if (delay > 0) {
const auto &task = createPlayerTask(
delay,
[this, playerId, itemId, hasTier, tier] {
playerEquipItem(playerId, itemId, hasTier, tier);
},
__FUNCTION__
);
player->setNextActionTask(task);
}
return;
}

if (player->hasCondition(CONDITION_FEARED)) {
/*
* When player is feared the player can´t equip any items.
Expand All @@ -3519,32 +3549,22 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* =
}

const auto &item = player->getInventoryItem(CONST_SLOT_BACKPACK);
if (!item) {
return;
}

const std::shared_ptr<Container> &backpack = item->getContainer();
if (!backpack) {
return;
}

if (player->getFreeBackpackSlots() == 0) {
player->sendCancelMessage(RETURNVALUE_NOTENOUGHROOM);
return;
}

const ItemType &it = Item::items[itemId];
Slots_t slot = getSlotType(it);
const auto &backpack = item ? item->getContainer() : nullptr;

const auto &slotItem = player->getInventoryItem(slot);
const auto &equipItem = searchForItem(backpack, it.id, hasTier, tier);
auto equipItem = searchForItem(backpack, it.id, hasTier, tier);
if (!equipItem) {
const auto &lootPouch = player->getLootPouch();
equipItem = searchForItem(lootPouch, it.id, hasTier, tier);
}
ReturnValue ret = RETURNVALUE_NOERROR;
if (slotItem && slotItem->getID() == it.id && (!it.stackable || slotItem->getItemCount() == slotItem->getStackSize() || !equipItem)) {
ret = internalMoveItem(slotItem->getParent(), player, CONST_SLOT_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr);
g_logger().debug("Item {} was unequipped", slotItem->getName());

if (slotItem && slotItem->getID() == it.id && (!hasTier || slotItem->getTier() == tier) && !equipItem) {
ret = internalCollectManagedItems(player, slotItem, getObjectCategory(slotItem), false);
} else if (equipItem) {
// Shield slot item
const auto &rightItem = player->getInventoryItem(CONST_SLOT_RIGHT);

// Check Ammo item
if (it.weaponType == WEAPON_AMMO) {
if (rightItem && rightItem->isQuiver()) {
Expand All @@ -3554,62 +3574,39 @@ void Game::playerEquipItem(uint32_t playerId, uint16_t itemId, bool hasTier /* =
const auto &leftItem = player->getInventoryItem(CONST_SLOT_LEFT);

const int32_t &slotPosition = equipItem->getSlotPosition();

// Checks if a two-handed item is being equipped in the left slot when the right slot is already occupied and move to backpack
if (
(slotPosition & SLOTP_LEFT)
&& (slotPosition & SLOTP_TWO_HAND)
&& rightItem
&& !(it.weaponType == WEAPON_DISTANCE)
&& !rightItem->isQuiver()
&& (!leftItem || leftItem->getWeaponType() != WEAPON_DISTANCE)
) {
ret = internalCollectManagedItems(player, rightItem, getObjectCategory(rightItem), false);
}

/* FIX: Auto-unequip shield when equipping two-handed bow */
if (slot == CONST_SLOT_LEFT && (slotPosition & SLOTP_TWO_HAND) && equipItem->getWeaponType() == WEAPON_DISTANCE) {
if (rightItem && !rightItem->isQuiver()) {
// Unequip item from right slot (shield or other item)
ret = internalMoveItem(rightItem->getParent(), player, INDEX_WHEREEVER, rightItem, rightItem->getItemCount(), nullptr);
if (ret == RETURNVALUE_NOERROR) {
g_logger().debug("Item {} was unequipped to equip two-handed bow", rightItem->getName());
} else {
player->sendCancelMessage(ret);
return;
}
}
}

// Check if trying to equip a shield while a two-handed weapon is equipped in the left slot
if (slot == CONST_SLOT_RIGHT && leftItem && leftItem->getSlotPosition() & SLOTP_TWO_HAND) {
// FIX: Don't unequip bow if quiver is equipped */
if (!it.isQuiver() || leftItem->getWeaponType() != WEAPON_DISTANCE) {
// Unequip the two-handed weapon from the left slot
ret = internalMoveItem(leftItem->getParent(), player, INDEX_WHEREEVER, leftItem, leftItem->getItemCount(), nullptr);
if (ret == RETURNVALUE_NOERROR) {
g_logger().debug("Two-handed weapon {} was unequipped to equip shield", leftItem->getName());
} else {
player->sendCancelMessage(ret);
return;
}
if (slot == CONST_SLOT_RIGHT && !it.isQuiver() && leftItem && leftItem->getSlotPosition() & SLOTP_TWO_HAND) {
ret = internalMoveItem(leftItem->getParent(), player, INDEX_WHEREEVER, leftItem, leftItem->getItemCount(), nullptr);
if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
return;
}
}

if (slotItem) {
ret = internalMoveItem(slotItem->getParent(), player, INDEX_WHEREEVER, slotItem, slotItem->getItemCount(), nullptr);
g_logger().debug("Item {} was moved back to player", slotItem->getName());
}

ret = internalMoveItem(equipItem->getParent(), player, slot, equipItem, equipItem->getItemCount(), nullptr);
if (ret == RETURNVALUE_NOERROR) {
g_logger().debug("Item {} was equipped", equipItem->getName());
}
}
}

if (ret != RETURNVALUE_NOERROR) {
player->sendCancelMessage(ret);
return;
}
if (slot == CONST_SLOT_NECKLACE) {
player->setNextNecklaceAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL));
} else if (slot == CONST_SLOT_RING) {
player->setNextRingAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL));
} else {
player->setNextAction(OTSYS_TIME() + g_configManager().getNumber(ACTIONS_DELAY_INTERVAL));
}
}

Expand Down Expand Up @@ -4135,14 +4132,13 @@ void Game::playerUseWithCreature(uint32_t playerId, const Position &fromPos, uin
return;
}

if (g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS)) {
if (std::shared_ptr<HouseTile> houseTile = std::dynamic_pointer_cast<HouseTile>(item->getTile())) {
const auto &house = houseTile->getHouse();
if (house && item->getRealParent() && item->getRealParent() != player && (!house->isInvited(player) || house->getHouseAccessLevel(player) == HOUSE_GUEST)) {
player->sendCancelMessage(RETURNVALUE_CANNOTUSETHISOBJECT);
return;
}
}
bool canUseHouseItem = !g_configManager().getBoolean(ONLY_INVITED_CAN_MOVE_HOUSE_ITEMS) || InternalGame::playerCanUseItemOnHouseTile(player, item);
if (!canUseHouseItem && item->hasOwner() && !item->isOwner(player)) {
player->sendCancelMessage(RETURNVALUE_ITEMISNOTYOURS);
return;
} else if (!canUseHouseItem) {
player->sendCancelMessage(RETURNVALUE_ITEMCANNOTBEMOVEDTHERE);
return;
}

const ItemType &it = Item::items[item->getID()];
Expand Down