Skip to content

Commit

Permalink
[Feature] Depot search system (#411)
Browse files Browse the repository at this point in the history
Enabling and implementing client 12x feature named 'Depot search'. This system can be used to make it easier to find, organize and manipulate players depot/inbox/stash.

The systems already include support for a future implementation of item tier system. The tier system is being implemented on PR #366
  • Loading branch information
marcosvf132 committed Jul 19, 2022
1 parent 647b005 commit a02d5a5
Show file tree
Hide file tree
Showing 16 changed files with 627 additions and 63 deletions.
3 changes: 2 additions & 1 deletion data/scripts/movements/tiles.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ function tile.onStepIn(creature, item, position, fromPosition)
end
player:sendTextMessage(MESSAGE_FAILURE, "Your depot contains " .. depotItems .. " item" .. (depotItems > 1 and "s." or ".") .. "\
Your supply stash contains " .. player:getStashCount() .. " item" .. (player:getStashCount() > 1 and "s." or "."))
player:setSpecialContainersAvailable(true, true)
player:setSpecialContainersAvailable(true, true, true)
return true
end
end
Expand Down Expand Up @@ -71,6 +71,7 @@ function tile.onStepOut(creature, item, position, fromPosition)
end

item:transform(decreasing[item.itemid])
player:setSpecialContainersAvailable(false, false, false)
return true
end

Expand Down
10 changes: 10 additions & 0 deletions src/creatures/creatures_definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -716,6 +716,16 @@ using MarketOfferList = std::list<MarketOffer>;
using HistoryMarketOfferList = std::list<HistoryMarketOffer>;
using StashItemList = std::map<uint16_t, uint32_t>;

using ItemsTierCountList = std::map<uint16_t, std::map<uint8_t, uint32_t>>;
/*
> ItemsTierCountList structure:
|- [itemID]
|- [itemTier]
|- Count
| ...
| ...
*/

struct Familiar {
Familiar(std::string initName, uint16_t initLookType,
bool initPremium, bool initUnlocked, std::string initType) :
Expand Down
217 changes: 216 additions & 1 deletion src/creatures/players/player.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3600,11 +3600,15 @@ void Player::stashContainer(StashContainerList itemDict)

uint32_t totalStowed = 0;
std::ostringstream retString;
uint16_t refreshDepotSearchOnItem = 0;
for (auto stashIterator : itemDict) {
uint16_t iteratorCID = (stashIterator.first)->getID();
if (g_game().internalRemoveItem(stashIterator.first, stashIterator.second) == RETURNVALUE_NOERROR) {
addItemOnStash(iteratorCID, stashIterator.second);
totalStowed += stashIterator.second;
if (isDepotSearchOpenOnItem(iteratorCID)) {
refreshDepotSearchOnItem = iteratorCID;
}
}
}

Expand All @@ -3619,6 +3623,11 @@ void Player::stashContainer(StashContainerList itemDict)
movedItems = 0;
}
sendTextMessage(MESSAGE_STATUS, retString.str());

// Refresh depot search window if necessary
if (refreshDepotSearchOnItem != 0) {
requestDepotSearchItem(refreshDepotSearchOnItem, 0);
}
}

bool Player::canSellImbuedItem(Item *item, bool ignoreImbued)
Expand Down Expand Up @@ -5721,7 +5730,11 @@ bool Player::addItemFromStash(uint16_t itemId, uint32_t itemCount) {
}
}

sendOpenStash();
// This check is necessary because we need to block it when we retrieve an item from depot search.
if (!isDepotSearchOpenOnItem(itemId)) {
sendOpenStash();
}

return true;
}

Expand Down Expand Up @@ -5899,6 +5912,208 @@ bool Player::isCreatureUnlockedOnTaskHunting(const MonsterType* mtype) const {
return getBestiaryKillCount(mtype->info.raceid) >= mtype->info.bestiaryToUnlock;
}

/*******************************************************************************
* Depot search system
******************************************************************************/
void Player::requestDepotItems()
{
ItemsTierCountList itemMap;
uint16_t count = 0;
const DepotLocker* depotLocker = getDepotLocker(getLastDepotId());
if (!depotLocker) {
return;
}

for (Item* locker : depotLocker->getItemList()) {
const Container* c = locker->getContainer();
if (!c || c->empty()) {
continue;
}

for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) {
auto itemMap_it = itemMap.find((*it)->getID());

uint8_t itemTier = Item::items[(*it)->getID()].upgradeClassification > 0 ? 1 : 0;
// To-Do: When forge is complete, change this to '(*it)->getTier() + 1'
if (itemMap_it == itemMap.end()) {
std::map<uint8_t, uint32_t> itemTierMap;
itemTierMap[itemTier] = Item::countByType((*it), -1);
itemMap[(*it)->getID()] = itemTierMap;
count++;
} else if (auto itemTier_it = itemMap[(*it)->getID()].find(itemTier); itemTier_it == itemMap[(*it)->getID()].end()) {
itemMap[(*it)->getID()][itemTier] = Item::countByType((*it), -1);
count++;
} else {
itemMap[(*it)->getID()][itemTier] += Item::countByType((*it), -1);
}
}
}

for (const auto& [itemId, itemCount] : getStashItems()) {
auto itemMap_it = itemMap.find(itemId);

uint8_t itemTier = Item::items[itemId].upgradeClassification > 0 ? 1 : 0;
// To-Do: When forge is complete, change this to '(*it)->getTier() + 1'
if (itemMap_it == itemMap.end()) {
std::map<uint8_t, uint32_t> itemTierMap;
itemTierMap[itemTier] = itemCount;
itemMap[itemId] = itemTierMap;
count++;
} else if (auto itemTier_it = itemMap[itemId].find(itemTier); itemTier_it == itemMap[itemId].end()) {
itemMap[itemId][itemTier] = itemCount;
count++;
} else {
itemMap[itemId][itemTier] += itemCount;
}
}

setDepotSearchIsOpen(1, 0);
sendDepotItems(itemMap, count);
}

void Player::requestDepotSearchItem(uint16_t itemId, uint8_t tier)
{
ItemVector depotItems;
ItemVector inboxItems;
uint32_t depotCount = 0;
uint32_t inboxCount = 0;
uint32_t stashCount = 0;

if (const ItemType& iType = Item::items[itemId];
iType.stackable && iType.wareId > 0) {
stashCount = static_cast<uint32_t>(getStashItemCount(itemId));
}

const DepotLocker* depotLocker = getDepotLocker(getLastDepotId());
if (!depotLocker) {
return;
}

for (Item* locker : depotLocker->getItemList()) {
const Container* c = locker->getContainer();
if (!c || c->empty()) {
continue;
}

for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) {
Item* item = *it;
// To-Do: When forge is complete check for item tier here using 'depotSearchOnItem.second'.
if (!item || item->getID() != itemId) {
continue;
}

if (c->isInbox()) {
inboxItems.push_back(item);
inboxCount += Item::countByType(item, -1);
} else {
depotItems.push_back(item);
depotCount += Item::countByType(item, -1);
}
}
}

setDepotSearchIsOpen(itemId, tier);
sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount);
}

void Player::retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool isDepot)
{
const DepotLocker* depotLocker = getDepotLocker(getLastDepotId());
if (!depotLocker) {
return;
}

std::vector<Item*> itemsVector;
for (Item* locker : depotLocker->getItemList()) {
const Container* c = locker->getContainer();
if (!c || c->empty() ||
(c->isInbox() && isDepot) || // Retrieve from inbox.
(!c->isInbox() && !isDepot)) { // Retrieve from depot.
continue;
}

for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) {
// To-Do: When forge is complete check for item tier here using 'depotSearchOnItem.second'.
if (Item* item = *it; item && item->getID() == itemId) {
itemsVector.push_back(item);
}
}
}

ReturnValue ret = RETURNVALUE_NOERROR;
for (Item* item : itemsVector) {
// First lets try to retrieve the item to the stash retrieve container.
if (ret = g_game().internalQuickLootItem(this, item, OBJECTCATEGORY_STASHRETRIEVE); ret == RETURNVALUE_NOERROR) {
continue;
}

// If the retrieve fails to move the item to the stash retrieve container, let's add the item anywhere.
if (ret = g_game().internalMoveItem(item->getParent(), this, INDEX_WHEREEVER, item, item->getItemCount(), nullptr); ret == RETURNVALUE_NOERROR) {
continue;
}

sendCancelMessage(ret);
return;
}

requestDepotSearchItem(itemId, tier);
}

void Player::openContainerFromDepotSearch(const Position& pos)
{
if (!isDepotSearchOpen()) {
sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
}

const Item* item = getItemFromDepotSearch(depotSearchOnItem.first, pos);
if (!item) {
sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
}

Container* container = item->getParent() ? item->getParent()->getContainer() : nullptr;
if (!container) {
sendCancelMessage(RETURNVALUE_NOTPOSSIBLE);
return;
}

g_actions().useItem(this, pos, 0, container, false);
}

Item* Player::getItemFromDepotSearch(uint16_t itemId, const Position& pos)
{
const DepotLocker* depotLocker = getDepotLocker(getLastDepotId());
if (!depotLocker) {
return nullptr;
}

uint8_t index = 0;
for (Item* locker : depotLocker->getItemList()) {
const Container* c = locker->getContainer();
if (!c || c->empty() ||
(c->isInbox() && pos.y != 0x21) || // From inbox.
(!c->isInbox() && pos.y != 0x20)) { // From depot.
continue;
}

for (ContainerIterator it = c->iterator(); it.hasNext(); it.advance()) {
Item* item = *it;
// To-Do: When forge is complete check for item tier here using 'depotSearchOnItem.second'.
if (!item || item->getID() != itemId) {
continue;
}

if (pos.z == index) {
return item;
}
index++;
}
}

return nullptr;
}

/*******************************************************************************
* Interfaces
******************************************************************************/
Expand Down
60 changes: 57 additions & 3 deletions src/creatures/players/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -400,15 +400,36 @@ class Player final : public Creature, public Cylinder
bool isInMarket() const {
return inMarket;
}
void setSpecialMenuAvailable(bool supplyStashBool, bool marketMenuBool) {
void setSpecialMenuAvailable(bool supplyStashBool, bool marketMenuBool, bool depotSearchBool) {

// Closing depot search when player have special container disabled and it's still open.
if (isDepotSearchOpen() && !depotSearchBool && depotSearch) {
depotSearchOnItem = {0, 0};
sendCloseDepotSearch();
}

// Menu option 'stow, stow container ...'
// Menu option 'show in market'
// Menu option to open depot search
supplyStash = supplyStashBool;
marketMenu = marketMenuBool;
depotSearch = depotSearchBool;
if (client) {
client->sendSpecialContainersAvailable();
}
}
bool isDepotSearchOpen() const {
return depotSearchOnItem.first != 0;
}
bool isDepotSearchOpenOnItem(uint16_t itemId) const {
return depotSearchOnItem.first == itemId;
}
void setDepotSearchIsOpen(uint16_t itemId, uint8_t tier) {
depotSearchOnItem = {itemId, tier};
}
bool isDepotSearchAvailable() const {
return depotSearch;
}
bool isSupplyStashMenuAvailable() {
return supplyStash;
}
Expand Down Expand Up @@ -987,9 +1008,25 @@ class Player final : public Creature, public Cylinder
}

//inventory
void sendLockerItems(std::map<uint16_t, uint16_t> itemMap, uint16_t count) {
void sendDepotItems(const ItemsTierCountList &itemMap, uint16_t count) const {
if (client) {
client->sendDepotItems(itemMap, count);
}
}
void sendCloseDepotSearch() const {
if (client) {
client->sendCloseDepotSearch();
}
}
void sendDepotSearchResultDetail(uint16_t itemId,
uint8_t tier,
uint32_t depotCount,
const ItemVector &depotItems,
uint32_t inboxCount,
const ItemVector &inboxItems,
uint32_t stashCount) const {
if (client) {
client->sendLockerItems(itemMap, count);
client->sendDepotSearchResultDetail(itemId, tier, depotCount, depotItems, inboxCount, inboxItems, stashCount);
}
}
void sendCoinBalance() {
Expand Down Expand Up @@ -2036,6 +2073,18 @@ class Player final : public Creature, public Cylinder
return nullptr;
}

// Depot search system
void requestDepotItems();
void requestDepotSearchItem(uint16_t itemId, uint8_t tier);
void retrieveAllItemsFromDepotSearch(uint16_t itemId, uint8_t tier, bool isDepot);
void openContainerFromDepotSearch(const Position& pos);
Item* getItemFromDepotSearch(uint16_t itemId, const Position& pos);
bool isDepotSearchExhausted() const {
return (OTSYS_TIME() - lastDepotSearchInteraction < 1000);
}
void updateDepotSearchExhausted() {
lastDepotSearchInteraction = OTSYS_TIME();
}

private:
std::forward_list<Condition*> getMuteConditions() const;
Expand Down Expand Up @@ -2164,6 +2213,7 @@ class Player final : public Creature, public Cylinder
int64_t lastToggleMount = 0;
int64_t lastMarketInteraction = 0; // Market exhaust.
int64_t lastStashInteraction = 0;
int64_t lastDepotSearchInteraction = 0;
int64_t lastPing;
int64_t lastPong;
int64_t nextAction = 0;
Expand Down Expand Up @@ -2240,6 +2290,10 @@ class Player final : public Creature, public Cylinder
StashItemList stashItems; // [ItemID] = amount
uint32_t movedItems = 0;

// Depot search system
bool depotSearch = false;
std::pair<uint16_t, uint8_t> depotSearchOnItem;

// Bestiary
bool charmExpansion = false;
uint16_t charmRuneWound = 0;
Expand Down
Loading

0 comments on commit a02d5a5

Please sign in to comment.