Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: item not decaying duration and related bugs #869

Merged
merged 12 commits into from
Feb 21, 2023
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