From c6865c88546f6828493edcba1837860fd18e2083 Mon Sep 17 00:00:00 2001 From: gesior Date: Sun, 29 Jan 2023 18:23:17 +0100 Subject: [PATCH] #2641 optimized item decay algorithm, timestamp (offline) decay --- .gitignore | 2 + src/container.cpp | 9 ++++ src/container.h | 1 + src/cylinder.cpp | 5 ++ src/cylinder.h | 1 + src/enums.h | 8 ++- src/game.cpp | 125 +++++++++++++++++++++++----------------------- src/game.h | 12 ++--- src/item.cpp | 71 +++++++++++++++----------- src/item.h | 90 ++++++++++++++++++++++----------- src/items.cpp | 17 +++++++ src/items.h | 2 + src/luascript.cpp | 71 +++++++++++++++++++++++++- src/luascript.h | 3 ++ src/tools.cpp | 4 +- 15 files changed, 287 insertions(+), 134 deletions(-) diff --git a/.gitignore b/.gitignore index 04b373f71b..95d05c7c6a 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ *.suo *.user *.sln.docstates +.idea .vscode/ # Build results @@ -19,6 +20,7 @@ x64/ build/ [Bb]in/ [Oo]bj/ +cmake-build-*/ # MSTest test Results [Tt]est[Rr]esult*/ diff --git a/src/container.cpp b/src/container.cpp index 1fe81175a0..c8908c6da8 100644 --- a/src/container.cpp +++ b/src/container.cpp @@ -681,11 +681,20 @@ void Container::internalAddThing(uint32_t, Thing* thing) void Container::startDecaying() { + Item::startDecaying(); for (Item* item : itemlist) { item->startDecaying(); } } +void Container::stopDecaying() +{ + Item::stopDecaying(); + for (Item* item : itemlist) { + item->stopDecaying(); + } +} + ContainerIterator Container::iterator() const { ContainerIterator cit; diff --git a/src/container.h b/src/container.h index c4a683843c..45cab42548 100644 --- a/src/container.h +++ b/src/container.h @@ -147,6 +147,7 @@ class Container : public Item, public Cylinder void internalAddThing(Thing* thing) override final; void internalAddThing(uint32_t index, Thing* thing) override final; void startDecaying() override final; + void stopDecaying() override final; protected: ItemDeque itemlist; diff --git a/src/cylinder.cpp b/src/cylinder.cpp index 4032343bcd..da056e29e9 100644 --- a/src/cylinder.cpp +++ b/src/cylinder.cpp @@ -67,3 +67,8 @@ void Cylinder::startDecaying() { // } + +void Cylinder::stopDecaying() +{ + // +} diff --git a/src/cylinder.h b/src/cylinder.h index 63a794e85b..bb0187e902 100644 --- a/src/cylinder.h +++ b/src/cylinder.h @@ -203,6 +203,7 @@ class Cylinder : virtual public Thing virtual void internalAddThing(uint32_t index, Thing* thing); virtual void startDecaying(); + virtual void stopDecaying(); }; class VirtualCylinder final : public Cylinder diff --git a/src/enums.h b/src/enums.h index 037b4cd408..d4b3e18d7a 100644 --- a/src/enums.h +++ b/src/enums.h @@ -84,15 +84,21 @@ enum itemAttrTypes : uint32_t { ITEM_ATTRIBUTE_SHOOTRANGE = 1 << 15, ITEM_ATTRIBUTE_OWNER = 1 << 16, ITEM_ATTRIBUTE_DURATION = 1 << 17, - ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18, + //ITEM_ATTRIBUTE_DECAYSTATE = 1 << 18, ITEM_ATTRIBUTE_CORPSEOWNER = 1 << 19, ITEM_ATTRIBUTE_CHARGES = 1 << 20, ITEM_ATTRIBUTE_FLUIDTYPE = 1 << 21, ITEM_ATTRIBUTE_DOORID = 1 << 22, + ITEM_ATTRIBUTE_DECAY_TIMESTAMP = 1 << 23, ITEM_ATTRIBUTE_CUSTOM = 1U << 31 }; +enum ItemDecayType_t : uint8_t { + DECAY_TYPE_NORMAL = 0, + DECAY_TYPE_TIMESTAMP = 1, +}; + enum VipStatus_t : uint8_t { VIPSTATUS_OFFLINE = 0, VIPSTATUS_ONLINE = 1, diff --git a/src/game.cpp b/src/game.cpp index b33c28c3b2..21854bbab0 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -1212,12 +1212,8 @@ ReturnValue Game::internalMoveItem(Cylinder* fromCylinder, Cylinder* toCylinder, return retMaxCount; } - if (moveItem && moveItem->getDuration() > 0) { - if (moveItem->getDecaying() != DECAYING_TRUE) { - moveItem->incrementReferenceCounter(); - moveItem->setDecaying(DECAYING_TRUE); - g_game.toDecayItems.push_front(moveItem); - } + if (moveItem && moveItem->getDurationLeft() > 0) { + startDecay(moveItem); } return ret; @@ -1304,10 +1300,8 @@ ReturnValue Game::internalAddItem(Cylinder* toCylinder, Item* item, int32_t inde } } - if (item->getDuration() > 0) { - item->incrementReferenceCounter(); - item->setDecaying(DECAYING_TRUE); - g_game.toDecayItems.push_front(item); + if (item->getDurationLeft() > 0) { + startDecay(item); } return RETURNVALUE_NOERROR; @@ -1351,7 +1345,7 @@ ReturnValue Game::internalRemoveItem(Item* item, int32_t count /*= -1*/, bool te if (item->isRemoved()) { item->onRemoved(); if (item->canDecay()) { - decayItems->remove(item); + stopDecay(item); } ReleaseItem(item); } @@ -1682,12 +1676,9 @@ Item* Game::transformItem(Item* item, uint16_t newId, int32_t newCount /*= -1*/) cylinder->postRemoveNotification(item, cylinder, itemIndex); ReleaseItem(item); - if (newItem->getDuration() > 0) { - if (newItem->getDecaying() != DECAYING_TRUE) { - newItem->incrementReferenceCounter(); - newItem->setDecaying(DECAYING_TRUE); - g_game.toDecayItems.push_front(newItem); - } + stopDecay(item); + if (newItem->getDurationLeft() > 0) { + startDecay(newItem); } return newItem; @@ -4393,24 +4384,55 @@ void Game::addDistanceEffect(const SpectatorHashSet& spectators, const Position& } } -void Game::startDecay(Item* item) +bool Game::isDecaying(Item *item) +{ + if (!item) { + return false; + } + + return reverseItemDecayMap.find(item) != reverseItemDecayMap.end(); +} + +void Game::startDecay(Item *item) { if (!item || !item->canDecay()) { return; } - ItemDecayState_t decayState = item->getDecaying(); - if (decayState == DECAYING_TRUE) { + if (isDecaying(item)) { return; } - if (item->getDuration() > 0) { - item->incrementReferenceCounter(); - item->setDecaying(DECAYING_TRUE); - toDecayItems.push_front(item); - } else { - internalDecayItem(item); + int64_t decayToTimestamp = OTSYS_TIME() + item->getDurationLeft(); + + if (item->getDecayType() == DECAY_TYPE_NORMAL) { + item->setDecayTimestamp(decayToTimestamp); } + + decayMap[decayToTimestamp][item] = item; + reverseItemDecayMap[item] = decayToTimestamp; + item->incrementReferenceCounter(); +} + +void Game::stopDecay(Item *item) +{ + if (!item) { + return; + } + + auto it = reverseItemDecayMap.find(item); + if (it == reverseItemDecayMap.end()) { + return; + } + + if (item->getDecayType() == DECAY_TYPE_NORMAL) { + item->setDuration(item->getDurationLeft()); + item->removeAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP); + } + + reverseItemDecayMap.erase(it); + decayMap[it->second].erase(item); + ReleaseItem(item); } void Game::internalDecayItem(Item* item) @@ -4431,43 +4453,30 @@ void Game::checkDecay() { g_scheduler.addEvent(createSchedulerTask(EVENT_DECAYINTERVAL, std::bind(&Game::checkDecay, this))); - size_t bucket = (lastBucket + 1) % EVENT_DECAY_BUCKETS; + int64_t time = OTSYS_TIME(); - auto it = decayItems[bucket].begin(), end = decayItems[bucket].end(); + std::list itemsToDecay; + + auto it = decayMap.begin(), end = decayMap.end(); while (it != end) { - Item* item = *it; - if (!item->canDecay()) { - item->setDecaying(DECAYING_FALSE); - ReleaseItem(item); - it = decayItems[bucket].erase(it); - continue; + if (it->first > time) { + break; } - int32_t duration = item->getDuration(); - int32_t decreaseTime = std::min(EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS, duration); + for(auto it2 : it->second) { + itemsToDecay.push_back(it2.first); + } - duration -= decreaseTime; - item->decreaseDuration(decreaseTime); + it = decayMap.erase(it); + } - if (duration <= 0) { - it = decayItems[bucket].erase(it); + for(auto item : itemsToDecay) { + stopDecay(item); + if (item->canDecay()) { internalDecayItem(item); - ReleaseItem(item); - } else if (duration < EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) { - it = decayItems[bucket].erase(it); - size_t newBucket = (bucket + ((duration + EVENT_DECAYINTERVAL / 2) / 1000)) % EVENT_DECAY_BUCKETS; - if (newBucket == bucket) { - internalDecayItem(item); - ReleaseItem(item); - } else { - decayItems[newBucket].push_back(item); - } - } else { - ++it; } } - lastBucket = bucket; cleanup(); } @@ -4562,16 +4571,6 @@ void Game::cleanup() item->decrementReferenceCounter(); } ToReleaseItems.clear(); - - for (Item* item : toDecayItems) { - const uint32_t dur = item->getDuration(); - if (dur >= EVENT_DECAYINTERVAL * EVENT_DECAY_BUCKETS) { - decayItems[lastBucket].push_back(item); - } else { - decayItems[(lastBucket + 1 + dur / 1000) % EVENT_DECAY_BUCKETS].push_back(item); - } - } - toDecayItems.clear(); } void Game::ReleaseCreature(Creature* creature) diff --git a/src/game.h b/src/game.h index c2566c59c2..94796def56 100644 --- a/src/game.h +++ b/src/game.h @@ -71,8 +71,7 @@ enum LightState_t { }; static constexpr int32_t EVENT_LIGHTINTERVAL = 10000; -static constexpr int32_t EVENT_DECAYINTERVAL = 250; -static constexpr int32_t EVENT_DECAY_BUCKETS = 4; +static constexpr int32_t EVENT_DECAYINTERVAL = 50; /** * Main Game class. @@ -452,6 +451,8 @@ class Game static void addDistanceEffect(const SpectatorHashSet& spectators, const Position& fromPos, const Position& toPos, uint8_t effect); void startDecay(Item* item); + void stopDecay(Item* item); + bool isDecaying(Item* item); int32_t getLightHour() const { return lightHour; } @@ -504,8 +505,6 @@ class Game Raids raids; Quests quests; - std::forward_list toDecayItems; - private: bool playerSaySpell(Player* player, SpeakClasses type, const std::string& text); void playerWhisper(Player* player, const std::string& text); @@ -522,14 +521,13 @@ class Game std::unordered_map uniqueItems; std::map stages; - std::list decayItems[EVENT_DECAY_BUCKETS]; + std::map> decayMap; + std::map reverseItemDecayMap; std::list checkCreatureLists[EVENT_CREATURECOUNT]; std::vector ToReleaseCreatures; std::vector ToReleaseItems; - size_t lastBucket = 0; - WildcardTreeNode wildcardTree { false }; std::map npcs; diff --git a/src/item.cpp b/src/item.cpp index 07862ad7f6..52e05f893c 100644 --- a/src/item.cpp +++ b/src/item.cpp @@ -181,10 +181,8 @@ Item* Item::clone() const Item* item = Item::CreateItem(id, count); if (attributes) { item->attributes.reset(new ItemAttributes(*attributes)); - if (item->getDuration() > 0) { - item->incrementReferenceCounter(); - item->setDecaying(DECAYING_TRUE); - g_game.toDecayItems.push_front(item); + if (item->getDurationLeft() > 0) { + g_game.startDecay(item); } } return item; @@ -258,15 +256,27 @@ void Item::setID(uint16_t newid) uint32_t newDuration = it.decayTime * 1000; if (newDuration == 0 && !it.stopTime && it.decayTo < 0) { - removeAttribute(ITEM_ATTRIBUTE_DECAYSTATE); + g_game.stopDecay(this); removeAttribute(ITEM_ATTRIBUTE_DURATION); + removeAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP); } removeAttribute(ITEM_ATTRIBUTE_CORPSEOWNER); - if (newDuration > 0 && (!prevIt.stopTime || !hasAttribute(ITEM_ATTRIBUTE_DURATION))) { - setDecaying(DECAYING_FALSE); - setDuration(newDuration); + if (it.decayType == DECAY_TYPE_NORMAL) { + if (newDuration > 0 && (!prevIt.stopTime || !hasAttribute(ITEM_ATTRIBUTE_DURATION))) { + g_game.stopDecay(this); + setDuration(newDuration); + } else if (!canDecay()) { + g_game.stopDecay(this); + } + } +} + +void Item::setParent(Cylinder* cylinder) { + parent = cylinder; + if (parent == nullptr) { + g_game.stopDecay(this); } } @@ -462,18 +472,6 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } - case ATTR_DECAYING_STATE: { - uint8_t state; - if (!propStream.read(state)) { - return ATTR_READ_ERROR; - } - - if (state != DECAYING_FALSE) { - setDecaying(DECAYING_PENDING); - } - break; - } - case ATTR_NAME: { std::string name; if (!propStream.readString(name)) { @@ -574,6 +572,16 @@ Attr_ReadValue Item::readAttr(AttrTypes_t attr, PropStream& propStream) break; } + case ATTR_DECAY_TIMESTAMP: { + int64_t decayTimestamp; + if (!propStream.read(decayTimestamp)) { + return ATTR_READ_ERROR; + } + + setInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP, decayTimestamp); + break; + } + //these should be handled through derived classes //If these are called then something has changed in the items.xml since the map was saved //just read the values @@ -718,15 +726,13 @@ void Item::serializeAttr(PropWriteStream& propWriteStream) const propWriteStream.writeString(specialDesc); } + if (items[id].decayType == DECAY_TYPE_TIMESTAMP && hasAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP)) { + propWriteStream.write(ATTR_DECAY_TIMESTAMP); + propWriteStream.write(getInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP)); + } if (hasAttribute(ITEM_ATTRIBUTE_DURATION)) { propWriteStream.write(ATTR_DURATION); - propWriteStream.write(getIntAttr(ITEM_ATTRIBUTE_DURATION)); - } - - ItemDecayState_t decayState = getDecaying(); - if (decayState == DECAYING_TRUE || decayState == DECAYING_PENDING) { - propWriteStream.write(ATTR_DECAYING_STATE); - propWriteStream.write(decayState); + propWriteStream.write(getDurationLeft()); } if (hasAttribute(ITEM_ATTRIBUTE_NAME)) { @@ -1333,8 +1339,8 @@ std::string Item::getDescription(const ItemType& it, int32_t lookDistance, } if (it.showDuration) { - if (item && item->hasAttribute(ITEM_ATTRIBUTE_DURATION)) { - uint32_t duration = item->getDuration() / 1000; + if (item && (item->hasAttribute(ITEM_ATTRIBUTE_DURATION) || item->hasAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP))) { + uint32_t duration = item->getDurationLeft() / 1000; s << " that will expire in "; if (duration >= 86400) { @@ -1697,6 +1703,13 @@ void Item::startDecaying() g_game.startDecay(this); } +void Item::stopDecaying() +{ + if (items[id].decayType != DECAY_TYPE_TIMESTAMP) { + g_game.stopDecay(this); + } +} + bool Item::hasMarketAttributes() const { if (attributes == nullptr) { diff --git a/src/item.h b/src/item.h index 97795bf395..8b654f7f66 100644 --- a/src/item.h +++ b/src/item.h @@ -62,12 +62,6 @@ enum TradeEvents_t { ON_TRADE_CANCEL, }; -enum ItemDecayState_t : uint8_t { - DECAYING_FALSE = 0, - DECAYING_TRUE, - DECAYING_PENDING, -}; - enum AttrTypes_t { //ATTR_DESCRIPTION = 1, //ATTR_EXT_FILE = 2, @@ -85,7 +79,7 @@ enum AttrTypes_t { ATTR_HOUSEDOORID = 14, ATTR_COUNT = 15, ATTR_DURATION = 16, - ATTR_DECAYING_STATE = 17, + //ATTR_DECAYING_STATE = 17, ATTR_WRITTENDATE = 18, ATTR_WRITTENBY = 19, ATTR_SLEEPERGUID = 20, @@ -102,7 +96,8 @@ enum AttrTypes_t { ATTR_ARMOR = 31, ATTR_HITCHANCE = 32, ATTR_SHOOTRANGE = 33, - ATTR_CUSTOM_ATTRIBUTES = 34 + ATTR_CUSTOM_ATTRIBUTES = 34, + ATTR_DECAY_TIMESTAMP = 35, }; enum Attr_ReadValue { @@ -198,18 +193,15 @@ class ItemAttributes void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); } - void decreaseDuration(int32_t time) { - increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time); - } uint32_t getDuration() const { return getIntAttr(ITEM_ATTRIBUTE_DURATION); } - void setDecaying(ItemDecayState_t decayState) { - setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState); + void setDecayTimestamp(int64_t timestamp) { + setIntAttr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP, timestamp); } - ItemDecayState_t getDecaying() const { - return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); + int64_t getDecayTimestamp() const { + return getIntAttr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP); } struct CustomAttribute @@ -497,7 +489,7 @@ class ItemAttributes public: static bool isIntAttrType(itemAttrTypes type) { - return (type & 0x7FFE13) != 0; + return (type & 0xFFFE13) != 0; } static bool isStrAttrType(itemAttrTypes type) { return (type & 0x1EC) != 0; @@ -587,6 +579,16 @@ class Item : virtual public Thing getAttributes()->setStrAttr(type, value); } + int64_t getInt64Attr(itemAttrTypes type) const { + if (!attributes) { + return 0; + } + return attributes->getIntAttr(type); + } + void setInt64Attr(itemAttrTypes type, int64_t value) { + getAttributes()->setIntAttr(type, value); + } + int32_t getIntAttr(itemAttrTypes type) const { if (!attributes) { return 0; @@ -735,12 +737,14 @@ class Item : virtual public Thing return getIntAttr(ITEM_ATTRIBUTE_CORPSEOWNER); } + ItemDecayType_t getDecayType() + { + return items[id].decayType; + } + void setDuration(int32_t time) { setIntAttr(ITEM_ATTRIBUTE_DURATION, time); } - void decreaseDuration(int32_t time) { - increaseIntAttr(ITEM_ATTRIBUTE_DURATION, -time); - } uint32_t getDuration() const { if (!attributes) { return 0; @@ -748,14 +752,37 @@ class Item : virtual public Thing return getIntAttr(ITEM_ATTRIBUTE_DURATION); } - void setDecaying(ItemDecayState_t decayState) { - setIntAttr(ITEM_ATTRIBUTE_DECAYSTATE, decayState); + uint32_t getDurationLeft() const { + if (!attributes) { + return 0; + } + + if (hasAttribute(ITEM_ATTRIBUTE_DECAY_TIMESTAMP)) { + if (getInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP) > OTSYS_TIME()) { + return getInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP) - OTSYS_TIME(); + } else { + return 0; + } + } else { + return getIntAttr(ITEM_ATTRIBUTE_DURATION); + } } - ItemDecayState_t getDecaying() const { + void setDurationLeft(int32_t duration) { + if (items[id].decayType == DECAY_TYPE_NORMAL) { + setIntAttr(ITEM_ATTRIBUTE_DURATION, duration); + } else { + setInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP, OTSYS_TIME() + duration); + } + } + + void setDecayTimestamp(int64_t timestamp) { + setInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP, timestamp); + } + int64_t getDecayTimestamp() const { if (!attributes) { - return DECAYING_FALSE; + return 0; } - return static_cast(getIntAttr(ITEM_ATTRIBUTE_DECAYSTATE)); + return getInt64Attr(ITEM_ATTRIBUTE_DECAY_TIMESTAMP); } static std::string getDescription(const ItemType& it, int32_t lookDistance, const Item* item = nullptr, int32_t subType = -1, bool addArticle = true); @@ -926,9 +953,13 @@ class Item : virtual public Thing void setUniqueId(uint16_t n); void setDefaultDuration() { - uint32_t duration = getDefaultDuration(); - if (duration != 0) { - setDuration(duration); + if (items[id].decayType == DECAY_TYPE_NORMAL) { + uint32_t duration = getDefaultDuration(); + if (duration != 0) { + setDuration(duration); + } + } else if (items[id].decayType == DECAY_TYPE_TIMESTAMP) { + setDecayTimestamp(OTSYS_TIME() + getDefaultDuration()); } } uint32_t getDefaultDuration() const { @@ -946,6 +977,7 @@ class Item : virtual public Thing virtual void onTradeEvent(TradeEvents_t, Player*) {} virtual void startDecaying(); + virtual void stopDecaying(); bool isLoadedFromMap() const { return loadedFromMap; @@ -978,9 +1010,7 @@ class Item : virtual public Thing Cylinder* getParent() const override { return parent; } - void setParent(Cylinder* cylinder) override { - parent = cylinder; - } + void setParent(Cylinder* cylinder) override; Cylinder* getTopParent(); const Cylinder* getTopParent() const; Tile* getTile() override; diff --git a/src/items.cpp b/src/items.cpp index fb5cc3cf6b..024f4e2082 100644 --- a/src/items.cpp +++ b/src/items.cpp @@ -146,6 +146,7 @@ const std::unordered_map ItemParseAttributes {"walkstack", ITEM_PARSE_WALKSTACK}, {"blocking", ITEM_PARSE_BLOCKING}, {"allowdistread", ITEM_PARSE_ALLOWDISTREAD}, + {"decaytype", ITEM_PARSE_DECAY_TYPE}, }; const std::unordered_map ItemTypesMap = { @@ -211,6 +212,11 @@ const std::unordered_map FluidTypesMap = { {"mead", FLUID_MEAD}, }; +const std::unordered_map DecayTypesMap = { + {"normal", DECAY_TYPE_NORMAL}, + {"timestamp", DECAY_TYPE_TIMESTAMP}, +}; + Items::Items() { @@ -809,6 +815,17 @@ void Items::parseItemNode(const pugi::xml_node& itemNode, uint16_t id) break; } + case ITEM_PARSE_DECAY_TYPE: { + tmpStrValue = asLowerCaseString(valueAttribute.as_string()); + auto it2 = DecayTypesMap.find(tmpStrValue); + if (it2 != DecayTypesMap.end()) { + it.decayType = it2->second; + } else { + std::cout << "[Warning - Items::parseItemNode] Unknown decayType: " << valueAttribute.as_string() << std::endl; + } + break; + } + case ITEM_PARSE_TRANSFORMEQUIPTO: { it.transformEquipTo = pugi::cast(valueAttribute.value()); break; diff --git a/src/items.h b/src/items.h index 0bc3d139a5..e9ae7548da 100644 --- a/src/items.h +++ b/src/items.h @@ -165,6 +165,7 @@ enum ItemParseAttributes_t { ITEM_PARSE_WALKSTACK, ITEM_PARSE_BLOCKING, ITEM_PARSE_ALLOWDISTREAD, + ITEM_PARSE_DECAY_TYPE, }; struct Abilities { @@ -321,6 +322,7 @@ class ItemType uint16_t rotateTo = 0; int32_t runeMagLevel = 0; int32_t runeLevel = 0; + ItemDecayType_t decayType = DECAY_TYPE_NORMAL; CombatType_t combatType = COMBAT_NONE; diff --git a/src/luascript.cpp b/src/luascript.cpp index 6b5801beec..5445ef23e4 100644 --- a/src/luascript.cpp +++ b/src/luascript.cpp @@ -1484,7 +1484,6 @@ void LuaScriptInterface::registerFunctions() registerEnum(ITEM_ATTRIBUTE_SHOOTRANGE) registerEnum(ITEM_ATTRIBUTE_OWNER) registerEnum(ITEM_ATTRIBUTE_DURATION) - registerEnum(ITEM_ATTRIBUTE_DECAYSTATE) registerEnum(ITEM_ATTRIBUTE_CORPSEOWNER) registerEnum(ITEM_ATTRIBUTE_CHARGES) registerEnum(ITEM_ATTRIBUTE_FLUIDTYPE) @@ -2151,6 +2150,9 @@ void LuaScriptInterface::registerFunctions() registerMethod("Item", "moveTo", LuaScriptInterface::luaItemMoveTo); registerMethod("Item", "transform", LuaScriptInterface::luaItemTransform); registerMethod("Item", "decay", LuaScriptInterface::luaItemDecay); + registerMethod("Item", "stopDecay", LuaScriptInterface::luaItemStopDecay); + registerMethod("Item", "getDurationLeft", LuaScriptInterface::luaItemGetDurationLeft); + registerMethod("Item", "setDurationLeft", LuaScriptInterface::luaItemSetDurationLeft); registerMethod("Item", "getDescription", LuaScriptInterface::luaItemGetDescription); @@ -6352,6 +6354,12 @@ int LuaScriptInterface::luaItemGetAttribute(lua_State* L) attribute = ITEM_ATTRIBUTE_NONE; } + if (attribute == ITEM_ATTRIBUTE_DURATION || attribute == ITEM_ATTRIBUTE_DECAY_TIMESTAMP) { + reportErrorFunc("Use \"item:getDurationLeft()\""); + pushBoolean(L, false); + return 1; + } + if (ItemAttributes::isIntAttrType(attribute)) { lua_pushnumber(L, item->getIntAttr(attribute)); } else if (ItemAttributes::isStrAttrType(attribute)) { @@ -6387,6 +6395,12 @@ int LuaScriptInterface::luaItemSetAttribute(lua_State* L) return 1; } + if (attribute == ITEM_ATTRIBUTE_DURATION || attribute == ITEM_ATTRIBUTE_DECAY_TIMESTAMP) { + reportErrorFunc("Use \"item:setDurationLeft(value)\""); + pushBoolean(L, false); + return 1; + } + item->setIntAttr(attribute, getNumber(L, 3)); pushBoolean(L, true); } else if (ItemAttributes::isStrAttrType(attribute)) { @@ -6416,6 +6430,12 @@ int LuaScriptInterface::luaItemRemoveAttribute(lua_State* L) attribute = ITEM_ATTRIBUTE_NONE; } + if (attribute == ITEM_ATTRIBUTE_DURATION || attribute == ITEM_ATTRIBUTE_DECAY_TIMESTAMP) { + reportErrorFunc("You can't remove decaying attributes."); + pushBoolean(L, false); + return 1; + } + bool ret = attribute != ITEM_ATTRIBUTE_UNIQUEID; if (ret) { item->removeAttribute(attribute); @@ -6635,7 +6655,54 @@ int LuaScriptInterface::luaItemDecay(lua_State* L) it.decayTo = getNumber(L, 2); } - g_game.startDecay(item); + item->startDecaying(); + pushBoolean(L, true); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemStopDecay(lua_State* L) +{ + // item:stopDecay() + Item* item = getUserdata(L, 1); + if (item) { + item->stopDecaying(); + pushBoolean(L, true); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemGetDurationLeft(lua_State* L) +{ + // item:getDurationLeft() + Item* item = getUserdata(L, 1); + if (item) { + lua_pushnumber(L, item->getDurationLeft()); + } else { + lua_pushnil(L); + } + return 1; +} + +int LuaScriptInterface::luaItemSetDurationLeft(lua_State* L) +{ + // item:setDurationLeft() + Item* item = getUserdata(L, 1); + if (item) { + bool isDecaying = g_game.isDecaying(item); + if (isDecaying) { + g_game.stopDecay(item); + } + + item->setDurationLeft(getNumber(L, 2)); + + if (isDecaying) { + g_game.startDecay(item); + } pushBoolean(L, true); } else { lua_pushnil(L); diff --git a/src/luascript.h b/src/luascript.h index 1434eee702..c4852261cd 100644 --- a/src/luascript.h +++ b/src/luascript.h @@ -717,6 +717,9 @@ class LuaScriptInterface static int luaItemMoveTo(lua_State* L); static int luaItemTransform(lua_State* L); static int luaItemDecay(lua_State* L); + static int luaItemStopDecay(lua_State* L); + static int luaItemGetDurationLeft(lua_State* L); + static int luaItemSetDurationLeft(lua_State* L); static int luaItemGetDescription(lua_State* L); diff --git a/src/tools.cpp b/src/tools.cpp index 81a983421c..8159e3a5bc 100644 --- a/src/tools.cpp +++ b/src/tools.cpp @@ -985,8 +985,6 @@ itemAttrTypes stringToItemAttribute(const std::string& str) return ITEM_ATTRIBUTE_OWNER; } else if (str == "duration") { return ITEM_ATTRIBUTE_DURATION; - } else if (str == "decaystate") { - return ITEM_ATTRIBUTE_DECAYSTATE; } else if (str == "corpseowner") { return ITEM_ATTRIBUTE_CORPSEOWNER; } else if (str == "charges") { @@ -995,6 +993,8 @@ itemAttrTypes stringToItemAttribute(const std::string& str) return ITEM_ATTRIBUTE_FLUIDTYPE; } else if (str == "doorid") { return ITEM_ATTRIBUTE_DOORID; + } else if (str == "decaytimestamp") { + return ITEM_ATTRIBUTE_DECAY_TIMESTAMP; } return ITEM_ATTRIBUTE_NONE; }