Skip to content

Commit

Permalink
fix: item not decaying duration and related bugs (opentibiabr#869)
Browse files Browse the repository at this point in the history
• Fixed item decay with duration (both equip/deEquip and onUse),
• Fixed the look of items with duration, it did not show the duration of some items (example rings, when equipped),
• Functions created in the Item class to avoid code duplication and facilitate the maintenance readability of some systems
• Removed old code from imbuement, which is no longer used
• Removed the decay of items created by loading the ".otbm" map from decay std::map, thus decreasing by about three thousand five hundred items in the decay check.
  • Loading branch information
dudantas authored and beats-dh committed Feb 26, 2023
1 parent 5773e46 commit 28085b2
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 118 deletions.
2 changes: 1 addition & 1 deletion src/enums/item_attribute.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ enum ItemAttribute_t : uint64_t {
OPENCONTAINER = 1 << 25,
QUICKLOOTCONTAINER = 1 << 26,
DURATION_TIMESTAMP = 1 << 27,
IMBUEMENT_TYPE = 1 << 28,
IMBUEMENT_TYPE = 1 << 28, // Deprecated, can be override
TIER = 1 << 29,

CUSTOM = 1U << 31
Expand Down
30 changes: 4 additions & 26 deletions src/game/game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1967,20 +1967,12 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
return nullptr;
} else if (newItemId != newId) {
// Replacing the the old item with the new while maintaining the old position
Item* newItem = Item::CreateItem(newItemId, 1);
auto newItem = item->transform(newItemId);
if (newItem == nullptr) {
SPDLOG_ERROR("[{}] new item with id {} is nullptr, (ERROR CODE: 01)", __FUNCTION__, newItemId);
return nullptr;
}

cylinder->replaceThing(itemIndex, newItem);
cylinder->postAddNotification(newItem, cylinder, itemIndex);

item->setParent(nullptr);
cylinder->postRemoveNotification(item, cylinder, itemIndex);
item->stopDecaying();
ReleaseItem(item);
newItem->startDecaying();

return newItem;
} else {
return transformItem(item, newItemId);
Expand Down Expand Up @@ -2026,26 +2018,12 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/)
}

// Replacing the the old item with the new while maintaining the old position
Item* newItem;
if (newCount == -1) {
newItem = Item::CreateItem(newId);
} else {
newItem = Item::CreateItem(newId, newCount);
}

auto newItem = item->transform(newId, newCount);
if (newItem == nullptr) {
SPDLOG_ERROR("[{}] new item with id {} is nullptr (ERROR CODE: 02)", __FUNCTION__, newId);
return nullptr;
}

cylinder->replaceThing(itemIndex, newItem);
cylinder->postAddNotification(newItem, cylinder, itemIndex);

item->setParent(nullptr);
cylinder->postRemoveNotification(item, cylinder, itemIndex);
item->stopDecaying();
ReleaseItem(item);
newItem->startDecaying();

return newItem;
}

Expand Down
4 changes: 2 additions & 2 deletions src/items/decay/decay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@
#include "game/scheduling/scheduler.h"

void Decay::startDecay(Item* item) {
if (!item) {
if (!item || item->getLoadedFromMap()) {
return;
}

auto decayState = item->getAttribute<ItemDecayState_t>(ItemAttribute_t::DECAYSTATE);
auto decayState = item->getDecaying();
if (decayState == DECAYING_STOPPING || (!item->canDecay() && decayState == DECAYING_TRUE)) {
stopDecay(item);
return;
Expand Down
195 changes: 111 additions & 84 deletions src/items/item.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ Item::Item(const uint16_t itemId, uint16_t itemCount /*= 0*/) :

Item::Item(const Item &i) :
Thing(), id(i.id), count(i.count), loadedFromMap(i.loadedFromMap) {
if (i.initAttributePtr()) {
initAttributePtr().reset(new ItemAttribute());
if (i.attributePtr) {
attributePtr.reset(new ItemAttribute());
}
}

Expand All @@ -223,8 +223,8 @@ Item* Item::clone() const {
return nullptr;
}

if (initAttributePtr()) {
item->initAttributePtr().reset(new ItemAttribute());
if (attributePtr) {
item->attributePtr.reset(new ItemAttribute());
}

return item;
Expand All @@ -239,7 +239,7 @@ bool Item::equals(const Item* compareItem) const {
return false;
}

for (const auto &attribute : initAttributePtr()->getAttributeVector()) {
for (const auto &attribute : getAttributeVector()) {
for (const auto &compareAttribute : compareItem->getAttributeVector()) {
if (attribute.getAttributeType() != compareAttribute.getAttributeType()) {
continue;
Expand Down Expand Up @@ -730,16 +730,6 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream &propStream) {
break;
}

case ATTR_IMBUEMENT_TYPE: {
std::string imbuementType;
if (!propStream.readString(imbuementType)) {
return ATTR_READ_ERROR;
}

setAttribute(ItemAttribute_t::IMBUEMENT_TYPE, imbuementType);
break;
}

case ATTR_TIER: {
uint8_t tier;
if (!propStream.read<uint8_t>(tier)) {
Expand Down Expand Up @@ -848,7 +838,7 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const {
propWriteStream.write<int32_t>(getDuration());
}

if (auto decayState = getAttribute<ItemDecayState_t>(ItemAttribute_t::DECAYSTATE);
if (auto decayState = getDecaying();
decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) {
propWriteStream.write<uint8_t>(ATTR_DECAYING_STATE);
propWriteStream.write<uint8_t>(decayState);
Expand Down Expand Up @@ -924,11 +914,6 @@ void Item::serializeAttr(PropWriteStream &propWriteStream) const {
propWriteStream.write<uint32_t>(getAttribute<uint32_t>(ItemAttribute_t::QUICKLOOTCONTAINER));
}

if (hasAttribute(ItemAttribute_t::IMBUEMENT_TYPE)) {
propWriteStream.write<uint8_t>(ATTR_IMBUEMENT_TYPE);
propWriteStream.writeString(getString(ItemAttribute_t::IMBUEMENT_TYPE));
}

if (hasAttribute(ItemAttribute_t::TIER)) {
propWriteStream.write<uint8_t>(ATTR_TIER);
propWriteStream.write<uint8_t>(getTier());
Expand Down Expand Up @@ -1657,6 +1642,62 @@ std::string Item::parseClassificationDescription(const Item* item) {
return string.str();
}

std::string Item::parseShowDurationSpeed(int32_t speed, bool &begin) {
std::ostringstream description;
if (begin) {
begin = false;
description << " (";
} else {
description << ", ";
}

description << fmt::format("speed {:+}", speed);
return description.str();
}

std::string Item::parseShowDuration(const Item* item) {
if (!item) {
return {};
}

std::ostringstream description;
uint32_t duration = item->getDuration() / 1000;
if (item && item->hasAttribute(ItemAttribute_t::DURATION) && duration > 0) {
description << " that will expire in ";
if (duration >= 86400) {
uint16_t days = duration / 86400;
uint16_t hours = (duration % 86400) / 3600;
description << days << " day" << (days != 1 ? "s" : "");

if (hours > 0) {
description << " and " << hours << " hour" << (hours != 1 ? "s" : "");
}
} else if (duration >= 3600) {
uint16_t hours = duration / 3600;
uint16_t minutes = (duration % 3600) / 60;
description << hours << " hour" << (hours != 1 ? "s" : "");

if (minutes > 0) {
description << " and " << minutes << " minute" << (minutes != 1 ? "s" : "");
}
} else if (duration >= 60) {
uint16_t minutes = duration / 60;
description << minutes << " minute" << (minutes != 1 ? "s" : "");
uint16_t seconds = duration % 60;

if (seconds > 0) {
description << " and " << seconds << " second" << (seconds != 1 ? "s" : "");
}
} else {
description << duration << " second" << (duration != 1 ? "s" : "");
}
} else {
description << " that is brand-new";
}

return description.str();
}

std::string Item::parseShowAttributesDescription(const Item* item, const uint16_t itemId) {
std::ostringstream itemDescription;
const ItemType &itemType = Item::items[itemId];
Expand Down Expand Up @@ -1809,14 +1850,7 @@ std::string Item::parseShowAttributesDescription(const Item* item, const uint16_
}

if (itemType.abilities->speed) {
if (begin) {
begin = false;
itemDescription << " (";
} else {
itemDescription << ", ";
}

itemDescription << fmt::format("speed {:+}", itemType.abilities->speed);
itemDescription << parseShowDurationSpeed(itemType.abilities->speed, begin);
}
}

Expand Down Expand Up @@ -2057,14 +2091,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, const
}

if (it.abilities->speed) {
if (begin) {
begin = false;
s << " (";
} else {
s << ", ";
}

s << fmt::format("speed {:+}", it.abilities->speed);
s << parseShowDurationSpeed(it.abilities->speed, begin);
}
}

Expand Down Expand Up @@ -2250,14 +2277,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, const
}

if (it.abilities->speed) {
if (begin) {
begin = false;
s << " (";
} else {
s << ", ";
}

s << fmt::format("speed {:+}", it.abilities->speed);
s << parseShowDurationSpeed(it.abilities->speed, begin);
}
}

Expand All @@ -2283,7 +2303,8 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, const

if (it.abilities && it.slotPosition & SLOTP_RING) {
if (it.abilities->speed > 0) {
s << fmt::format(" (speed {:+})", it.abilities->speed);
bool begin = true;
s << parseShowDurationSpeed(it.abilities->speed, begin) << ")" << parseShowDuration(item);
} else if (hasBitSet(CONDITION_DRUNK, it.abilities->conditionSuppressions)) {
s << " (hard drinking)";
} else if (it.abilities->invisible) {
Expand Down Expand Up @@ -2370,40 +2391,7 @@ std::string Item::getDescription(const ItemType &it, int32_t lookDistance, const
}

if (it.showDuration) {
if (item && item->hasAttribute(ItemAttribute_t::DURATION)) {
uint32_t duration = item->getDuration() / 1000;
s << " that will expire in ";

if (duration >= 86400) {
uint16_t days = duration / 86400;
uint16_t hours = (duration % 86400) / 3600;
s << days << " day" << (days != 1 ? "s" : "");

if (hours > 0) {
s << " and " << hours << " hour" << (hours != 1 ? "s" : "");
}
} else if (duration >= 3600) {
uint16_t hours = duration / 3600;
uint16_t minutes = (duration % 3600) / 60;
s << hours << " hour" << (hours != 1 ? "s" : "");

if (minutes > 0) {
s << " and " << minutes << " minute" << (minutes != 1 ? "s" : "");
}
} else if (duration >= 60) {
uint16_t minutes = duration / 60;
s << minutes << " minute" << (minutes != 1 ? "s" : "");
uint16_t seconds = duration % 60;

if (seconds > 0) {
s << " and " << seconds << " second" << (seconds != 1 ? "s" : "");
}
} else {
s << duration << " second" << (duration != 1 ? "s" : "");
}
} else {
s << " that is brand-new";
}
s << parseShowDuration(item);
}

if (!it.allowDistRead || (it.id >= 7369 && it.id <= 7371)) {
Expand Down Expand Up @@ -2641,6 +2629,45 @@ void Item::stopDecaying() {
g_decay().stopDecay(this);
}

Item* Item::transform(uint16_t itemId, uint16_t itemCount /*= -1*/) {
Cylinder* cylinder = getParent();
if (cylinder == nullptr) {
SPDLOG_INFO("[{}] failed to transform item {}, cylinder is nullptr", __FUNCTION__, getID());
return nullptr;
}

Tile* fromTile = cylinder->getTile();
if (fromTile) {
auto it = g_game().browseFields.find(fromTile);
if (it != g_game().browseFields.end() && it->second == cylinder) {
cylinder = fromTile;
}
}

Item* newItem;
if (itemCount == -1) {
newItem = Item::CreateItem(itemId, 1);
} else {
newItem = Item::CreateItem(itemId, itemCount);
}

int32_t itemIndex = cylinder->getThingIndex(this);
auto duration = getDuration();
if (duration > 0) {
newItem->setDuration(duration);
}

cylinder->replaceThing(itemIndex, newItem);
cylinder->postAddNotification(newItem, cylinder, itemIndex);

setParent(nullptr);
cylinder->postRemoveNotification(this, cylinder, itemIndex);
stopDecaying();
g_game().ReleaseItem(this);
newItem->startDecaying();
return newItem;
}

bool Item::hasMarketAttributes() const {
if (!isInitializedAttributePtr()) {
return true;
Expand All @@ -2655,15 +2682,15 @@ bool Item::hasMarketAttributes() const {
return false;
}

if (attribute.getAttributeType() == ItemAttribute_t::IMBUEMENT_TYPE && !hasImbuements()) {
return false;
}

if (attribute.getAttributeType() == ItemAttribute_t::TIER && static_cast<uint8_t>(attribute.getInteger()) != getTier()) {
return false;
}
}

if (hasImbuements()) {
return false;
}

return true;
}

Expand Down
Loading

0 comments on commit 28085b2

Please sign in to comment.