Jump to content

[Patch] Item changes after duration expires


Guest cmaranec

Recommended Posts

What features does this patch add?

It implements feature, when item duration expires, new item will be added to inventory if it has record in item_duration_change table

For which revision?

9467 (but should work with newest)

Who has been writing this patch?

me

Patch:

diff --git a/sql/item_duration_changes.sql b/sql/item_duration_changes.sql
new file mode 100644
index 0000000..3c8c952
--- /dev/null
+++ b/sql/item_duration_changes.sql
@@ -0,0 +1,6 @@
+DROP TABLE IF EXISTS `item_duration_changes`;
+CREATE TABLE `item_duration_changes` (
+  `item` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Item id with set duration',
+  `changed_item` mediumint(8) unsigned NOT NULL default '0' COMMENT 'Item id to which should source item change',
+  PRIMARY KEY (`item`)
+) ENGINE=MyISAM DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED COMMENT='Item duration changes system';
diff --git a/src/game/Item.cpp b/src/game/Item.cpp
index 10eef36..5fc872b 100644
--- a/src/game/Item.cpp
+++ b/src/game/Item.cpp
@@ -273,6 +273,11 @@ bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner)
    return true;
}

+uint32 Item::GetDurationChange()
+{
+    return sObjectMgr.GetItemDurationChange(this->GetEntry()); 
+}
+
void Item::UpdateDuration(Player* owner, uint32 diff)
{
    if (!GetUInt32Value(ITEM_FIELD_DURATION))
@@ -283,6 +288,8 @@ void Item::UpdateDuration(Player* owner, uint32 diff)
    if (GetUInt32Value(ITEM_FIELD_DURATION)<=diff)
    {
        owner->DestroyItem(GetBagSlot(), GetSlot(), true);
+        if(uint32 ChangedItem = GetDurationChange())
+            owner->StoreNewItemInBestSlots(ChangedItem,1);
        return;
    }

diff --git a/src/game/Item.h b/src/game/Item.h
index 409f8ce..8981925 100644
--- a/src/game/Item.h
+++ b/src/game/Item.h
@@ -272,6 +272,7 @@ class MANGOS_DLL_SPEC Item : public Object
        void SetSlot(uint8 slot) {m_slot = slot;}
        uint16 GetPos() const { return uint16(GetBagSlot()) << 8 | GetSlot(); }
        void SetContainer(Bag *container) { m_container = container; }
+        uint32 GetDurationChange();

        bool IsInBag() const { return m_container != NULL; }
        bool IsEquipped() const;
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
index b887961..f9a900e 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -4523,6 +4523,49 @@ void ObjectMgr::LoadItemTexts()
    sLog.outString( ">> Loaded %u item texts", count );
}

+void ObjectMgr::LoadItemDurationChanges()
+{
+    QueryResult *result = WorldDatabase.Query("SELECT item, changed_item FROM item_duration_changes");
+
+    mItemDurationChanges.clear();
+
+    uint32 count = 0;
+
+    if( !result )
+    {
+        barGoLink bar( 1 );
+        bar.step();
+
+        sLog.outString();
+        sLog.outString( ">> Loaded %u item duration changes", count );
+        return;
+    }
+
+    barGoLink bar( result->GetRowCount() );
+
+    Field* fields;
+    do
+    {
+        bar.step();
+
+        fields = result->Fetch();
+
+        ItemDurationChanges temp;
+        temp.source_item = fields[0].GetUInt32();
+        temp.changed_item = fields[1].GetUInt32();
+
+        mItemDurationChanges.push_back(temp);
+
+        ++count;
+
+    } while ( result->NextRow() );
+
+    delete result;
+
+    sLog.outString();
+    sLog.outString( ">> Loaded %u item duration changes", count );
+}
+
void ObjectMgr::LoadPageTexts()
{
    sPageTextStore.Free();                                  // for reload case
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
index 4442f56..376ab38 100644
--- a/src/game/ObjectMgr.h
+++ b/src/game/ObjectMgr.h
@@ -366,6 +366,12 @@ struct MANGOS_DLL_SPEC LanguageDesc
    uint32   skill_id;
};

+struct ItemDurationChanges
+{
+    uint32 source_item;
+    uint32 changed_item;
+};
+
extern LanguageDesc lang_description[LANGUAGES_COUNT];
MANGOS_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);

@@ -591,6 +597,7 @@ class ObjectMgr
        void LoadGameobjects();
        void LoadGameobjectRespawnTimes();
        void LoadItemPrototypes();
+        void LoadItemDurationChanges();
        void LoadItemRequiredTarget();
        void LoadItemLocales();
        void LoadQuestLocales();
@@ -846,6 +853,17 @@ class ObjectMgr
            return &iter->second;
        }

+        uint32 GetItemDurationChange(uint32 item)
+        {
+            for(std::list<ItemDurationChanges>::const_iterator itr = mItemDurationChanges.begin();
+                itr != mItemDurationChanges.end(); ++itr)
+            {
+                if(itr->source_item == item)
+                    return itr->changed_item;
+            }
+            return NULL;
+        }
+
        VendorItemData const* GetNpcVendorItemList(uint32 entry) const
        {
            CacheVendorItemMap::const_iterator  iter = m_mCacheVendorItemMap.find(entry);
@@ -917,6 +935,7 @@ class ObjectMgr
        ArenaTeamMap        mArenaTeamMap;

        ItemTextMap         mItemTexts;
+        std::list<ItemDurationChanges> mItemDurationChanges;

        QuestAreaTriggerMap mQuestAreaTriggerMap;
        TavernAreaTriggerSet mTavernAreaTriggerSet;
diff --git a/src/game/World.cpp b/src/game/World.cpp
index b1605d5..5e317a6 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -975,6 +975,9 @@ void World::SetInitialWorldSettings()
    sLog.outString( "Loading Item Texts..." );
    sObjectMgr.LoadItemTexts();

+    sLog.outString( "Loading Item Duration Changes..." );
+    sObjectMgr.LoadItemDurationChanges();
+
    sLog.outString( "Loading Creature Model Based Info Data..." );
    sObjectMgr.LoadCreatureModelInfo();

Link to comment
Share on other sites

Storing the pairs in a std::list and doing linear search on it really hurts my eyes...

MaNGOS defines an UNORDERED_MAP type for such purposes, if i'm not mistaken.

Other than that, this looks like it should work :)

Naming could be a bit clearer though IMHO. Does not make me think of item replacement on duration expire.

Some sanity checks on loading wouldn't hurt either, like do the items even exist, do they have duration etc...

Link to comment
Share on other sites

When a function returns an integer, you shouldn't return NULL. This is usually reserved to pointers.

Also, a reload command would be nice.

Aside from that, it's pretty clean. FYI if you want it to get accepted faster, provide an example of an item on official servers that have this functionality.

Link to comment
Share on other sites

UPDATE:

New Patch: http://paste2.org/p/711632

Works with 9558

Improvements:

* changed return value if item with duration haven't got a duration change from NULL to 0

* implemented reload command

Example items:

INSERT INTO `item_duration_changes` VALUES
('39878', '39883');
INSERT INTO `item_duration_changes` VALUES
('44717', '44718');

EDIT:PS: You need set duration to items 39878 and 44717! For testing i've set 15 seconds

Link to comment
Share on other sites

May be implement just script-call after item expire?

You can't "just add" some script call, this will need special care, especially at load. Without reading the actual code, i see that author here already are aware of the issues that may occur at load:

* added checks in loading function

Sure, it's easy enough when item expire when you play, but not so easy if it expire at loading :)

Link to comment
Share on other sites

So? Would new column in item_template be better? These two items were only examples - i think there's more than two items, which changes when duration expires. It would be also useful for custom items (in case of funserver, custom workarounds and so on..)

What do you suggest?

EDIT:

For NoFantasy: i think duration update of any item is called when player loggs in, so it is ok.

Link to comment
Share on other sites

Hmmm.. Correct me if i'm wrong, but Vladimir said some time ago, that best way to implement it - is item scripts. So, you need to implement event like onItemDestroy, which will call relative script function when item expires. At now, separate DB table only for 2 items is wrong solution IMO.

Link to comment
Share on other sites

I think same, eNeRGy.

NoFantasy said, that might be some problems when expire at loading. But no one problem at loading here , cuz UpdateItemDuration called after add to world players and items. Anyway in corrent code should happen at loading if item expire (and all fine).

I don't say thas it bad solution (about this patch) =) , i try to find better solution, better way. ^^

Link to comment
Share on other sites

Shouldn't be there some chance? May be better to implement it in *_loot_template way?

Nope... You have some item, and it will be replaced with another when duration ends. With 100% chance. But what you'll loot from new item is another story, so finally there is some chance that you'll get protodrake from FHs.

Example: Frenzyheart's jar will be replaced with another one, which contains loot, defined in _loot_template :)

Frenzyheart Tribe === > http://www.wowhead.com/?item=44717 ===> 1 week ===> http://www.wowhead.com/?item=44718 ===MAYBE===> http://www.wowhead.com/?item=44719

Oracles ===> http://www.wowhead.com/?item=39878 ===> 1 week ===> http://www.wowhead.com/?item=39883 ===MAYBE===> http://www.wowhead.com/?item=44707

Anyway, there is only 2 items atm, so IMHO new script event on destroy will be the best solution.

Link to comment
Share on other sites

Tell me the best way to do it and i will do it :D

My suggestion is adding new column to item_template table, which refers to loot record in reference_loot_template and after duration expires, the new item will be generated by this loot_template.

Is not a good idea, at present such a design on the good, that is just a matter of more items

Link to comment
Share on other sites

So the best way to do it is implementing new script event OnItemDestroy? I think it's good idea, but would it be better to call every time when any item destroys, or only if it have assigned ScriptName?

+: better way than mine, maybe safer, maybe more universal

-: won't be reloadable, more complicated (needs to be scripted in C++ than only insert record to database ), won't check for item existing, has duration and so on..

Link to comment
Share on other sites

So the best way to do it is implementing new script event OnItemDestroy? I think it's good idea, but would it be better to call every time when any item destroys, or only if it have assigned ScriptName?

+: better way than mine, maybe safer, maybe more universal

-: won't be reloadable, more complicated (needs to be scripted in C++ than only insert record to database ), won't check for item existing, has duration and so on..

look forward to this patch... ...

Link to comment
Share on other sites

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue. Privacy Policy Terms of Use