• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[Lua] Manipulating Stock Items

Level 3
Joined
Nov 4, 2008
Messages
22
Hey,
So I am creating a simple item system, and my goal is to manage things via Lua code as much as possible, and use Object Editor as much as possible.
I am able to pretty much fully manipulate item objects (name, icon, abilities etc). This way I can have 1 single item created in Object Editor and pretty much "create" any item I want ingame.
Huge props to Uncle for helping out with the struggles.

The thing is, the unit will get the items from shops by purchasing them. So I created shops and then I need to add them to stock. I did that as follows:
Lua:
function Shops.AddBasicItemsToShop(shop)
    local basicItems = Items.data.ShopItems.BasicItems
    AddItemToStock(shop, basicItems[1].typeId, 0, 0)
    AddItemToStock(shop, basicItems[2].typeId, 0, 0)
    AddItemToStock(shop, basicItems[8].typeId, 0, 0)
end

Now, I want to be able to manipulate the items in shop (or before adding them to the shop). The issue is, this native function requires you to use typeId, so I can't create an item object and add it to the shop. Meaning I need to somehow reference the items after they are added to the shop.
I tried getting them though inventory function, but that was a fail (obviously).
I also tried assigning the AddItemToStock function to a variable and use that (e.g: local item1 = AddItemToStock(shop, basicItems[1].typeId, 0, 0), but it didn't work either.
I search over all the natives I thought could be related and couldn't find an answer.
Is there a way to either reference the items in shop (stock items), or change the item typeId (not item object), then add it to the shop, and change it again with hopes that it won't change the item instance already added to the shop?
If that's not a possibility, sadly I will have to create an item in Object Editor for each item in game, and that somewhat negates the usage of manipulating object items (yes, I will still be able to change it's stats and such, but I will have to get an item for every item name and item icon I want to be in the shop..).

Any suggestions/ideas will be much appreciated :)
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Edit:
Unfortunately, there's no way to manipulate the Items added to the Shops. Unless I'm missing something.

One alternative:
Use Abilities to act as Items. Use the Charge Gold and Lumber ability to simulate Gold/Lumber cost. Cooldown can represent Stock Replenish Time.

Another alternative is to rely on custom UI which gives you full control over everything:
With this you could even avoid needing to use Items in the first place since you can design your own system from scratch. Although, at that point you're sort of reinventing the wheel. Also, this custom UI stuff isn't as responsive (noticeable input delay), so it can be favorable to try and use as much "Blizzard stuff" as possible if you want your map to feel snappy.
 
Last edited:
Level 3
Joined
Nov 4, 2008
Messages
22
@Uncle I also tried to use "GetSoldItem()", but yeah that's just referencing to the item that was purchased (after the fact), not the item used to purchase (the one in the shop. I might not have sentenced it very well).

Another alternative is to rely on custom UI which gives you full control over everything:

With this you could even avoid needing to use Items in the first place since you can design your own system from scratch. Although, at that point you're sort of reinventing the wheel. Also, this custom UI stuff isn't as responsive (noticeable input delay), so it can be favorable to try and use as much "Blizzard stuff" as possible if you want your map to feel snappy.
I have played a few maps that uses custom UI, so I am familiar with the delay you're referring to. It is indeed noticeable and I believe will be annoying on a fast phase map. So I can't really do that sadly.

Use Abilities to act as Items. Use the Charge Gold and Lumber ability to simulate Gold/Lumber cost. Cooldown can represent Stock Replenish Time.
Not sure I understood your intention.
Do you mean to create a "regular" unit (make it immobile etc) to simulate a shop by giving that unit abilities to act as items and manipulate the abilities? And use triggers to give the unit item and take his gold upon purchase (but the trigger will be "ability used" instead of "item sold"?

EDIT:
I see now what you meant by Charge Gold and Lumber. So the trigger to take gold from player might not be necessary, but the other question still stand..

EDIT#2:
So I changed an ability icon, then I added the ability to the unit, it worked well, for the first one.
How do I approach the second one? As it is based on the same ability ID.. I tried changing the icon of the same ID and adding it again (with hopes that the first ability won't refresh/change, and the second ability will be added), but it didn't work.
Lua:
function Shops.AddBasicItemsToShop(shop)
    BlzSetAbilityIcon(FourCC("AAns"), "ReplaceableTextures\\CommandButtons\\BTNWallOfFire.blp") -- Works based on the next line.
    UnitAddAbility(shop, FourCC("AAns")) -- Works.
    BlzSetAbilityIcon(FourCC("AAns"), "ReplaceableTextures\\CommandButtons\\BTNThunderclap.blp") -- No indication that it worked.
    UnitAddAbility(shop, FourCC("AAns")) -- Second ability isn't added.
    IncUnitAbilityLevel(shop, FourCC("AAns")) -- This doesn't seem to help btw. I had to first change the icon and then add the ability, otherwise it wouldn't work.
    DecUnitAbilityLevel(shop, FourCC("AAns"))
  
end
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Here's what I came up with. It's unfinished and has some issues but it should get the concept across.

Notes:
  • Items are sent to the ability caster (Arcane Vault). You could probably use this to your advantage and modify the Item while it's on the Arcane Vault then transfer it to the appropriate Hero.
  • The Custom Shop is not MUI, but could easily be fixed with some proper code.
  • The Charge Gold/Lumber ability doesn't like to use it's Cooldown from the OE. You might need to manually start the Cooldown via code.
  • Ability Icon changes are handled globally and affect all players. GetLocalPlayer() may be necessary to solve this and it may be a major pain in the butt.
Oh and those Base Order ID's are how a Unit can differentiate between copies of the same Ability.

Units are issued Orders in Warcraft 3 and those Orders tell the Unit what to do. IE: Cast "holylight" on that target over there. If a unit has two or more Holy Light abilities then it'll be confused by this Order and you'll experience weird behavior/a lack of control, like the wrong ability being used.

Luckily, the Channel, Charge Gold/Lumber, and Spellbook abilities have a special data field called Base Order ID that resolves this issue by allowing you to change the Order yourself. No other abilities can do this, regardless of what the Object Editor may lead you to believe. You can see me doing this in my example map.
 

Attachments

  • Custom Item Types Lua 1.w3m
    21.7 KB · Views: 2
Last edited:

Remixer

Map Reviewer
Level 33
Joined
Feb 19, 2011
Messages
2,112
Sure.

So essentially with the Charge Gold and Lumber ability, you use it as a interaction interface for the players and you make it look like the player would be buying an item (where in fact they are casting an ability with the shop that costs gold.

For each item you want to mimic, you create an ability based on the Charge Gold and Lumber ability and modify the field I specified previously (Ans5) in the object editor for each ability you just created: One ability for Claws of Attack +3, another one for Claws of Attack +4 and so forth. In the object editor you can choose that +3 claws equal Avatar and +4 claws equal Avatar of Vengeance, that way you'll be able to detect individual unique 'items' and their trigger.
 
Level 3
Joined
Nov 4, 2008
Messages
22
@Uncle Respect that you took the time to do that, and that fast!
Even for a beginner like me, the code looks quite clear, organized and relatively easy to understand. I actually feel quite ashamed comparing this to my mess of a code.
Saying that, I played around with it and I couldn't understand something (my lack of experience/knowledge probably taking effect here).
1. I tried adding the following lines (in their respective spots) to add 1 more ability(item), but it didn't work.
Lua:
local shield2 = RegisterItemType(3, "Basic Shield2", GetBTN("BTNSnazzyScrollGreen"), "It's a shield.", 1, 1, {"Alsx"})
AddItemAbilityToShop(shop, 3)
During load, the 3rd ability data is printed, which means the CustomItemType:new constructor was able to create and store the new object in ItemTypeDatabase as expected, yet the ability isn't added to the custom shop. If I change the order of the lines (e.g AddItemAbilityToShop(shop, 3) before AddItemAbilityToShop(shop, SHIELD_ID), then the new ability is added but the previous (SHIELD_ID) is not.

2. It took me a while to understand that the strings ("Alsx", "Altn" and "Ald0") are rawcodes of predefined items (same rawcodes as new objects has), and can be seen using CTRL+D via the Object Editor (I tried searching for the codes but it didn't find anything, so I had to look for it one by one).
So are the rawcodes abilities defined in the new objects, simply placeholders to attach later for the items given to the unit?

3. (@Remixer Thank you for the explanation as well. Making sure I got it right) ;
You created 3 new abilities, and I noticed that you changed each of their base order value. I thought that each ability is count as it's own, but because you changed their base order (and Remixer explanation), it leads me to assume that because all 3 abilities were created from the same "template ability", they are still counted as the same ability even if their data is changed, and to avoid the issues you mentioned, you changed their base order values (randomly? does the value itself matters?).
Let me know if I am understanding this correctly..

4. In my eyes, your system seems a bit complex, but still very straightforward, smart, efficient and with many insurances to avoid pitfalls.
This could be really silly of me, but if I am anyway required create (X) new objects (abilities) in the Object Editor for the new system you created, isn't it essentially the same as creating (X) new object (items) and using that?

I appreciate the help. Apologizes if my lack of knowledge and experience makes my questions sound dumb, disrespectful or anything of that sort.
Since it's 2am here, I could also be missing obvious stuff, so I will definitely go over everything again tomorrow.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
Edited a few times now, sorry.

I fixed #1, it was an issue with how I was handling GetAvailableShopAbility(). I attached the fix below (it's still an unfinished system overall).

To answer your questions:
2. So are the rawcodes abilities defined in the new objects, simply placeholders to attach later for the items given to the unit?
Yes, those were the +10 damage and +10 armor abilities, used as an example to show the concept of defining and adding abilities to items. Note that there's no way, as far as I know, to modify the stats of an Item ability at runtime. You can only modify a UNIT's ability. However, you could probably use some tricks like Adding multiple copies of the same ability to avoid needing to create unique Object Editor data. IE: Add ten +10 damage abilities to represent +100 damage.

Alternatively, you could use a system like NewBonus (the one I linked before) to manage your bonus stats, thus avoiding the need to even add abilities to items in the first place. Of course, activated Item abilities would still need to exist on the item itself, but that seems perfectly fine.

3. Let me know if I am understanding this correctly..
Yes, you understood it correctly. Copying and pasting an ability is technically creating a different ability but it uses the same Order Id which is where the issue comes into play. The 3 abilities I mentioned have the option to change this Order Id, thus avoiding the issue.

4. isn't it essentially the same as creating (X) new object (items) and using that?
Assuming everything works, you will only need 12 Abilities to represent the 12 Items that a Shop can sell. So those 12 Abilities enable you to sell an infinite number of Items all designed through code. You often have to make some kind of compromise like this in Wc3 modding.
 

Attachments

  • Custom Item Types Lua 2.w3m
    21.7 KB · Views: 3
Last edited:
Level 3
Joined
Nov 4, 2008
Messages
22
I fixed #1, it was an issue with how I was handling GetAvailableShopAbility(). I attached the fix below (it's still an unfinished system overall).
I think I understand the issue. Using #ShopItemIdsAvailable for the loop iterations length, means that the loop stops upon (after?) reaching the first 'nil' entry of the table. Because the logic in the loop changes the used entry to 'nil', only 2 abilities can be created. Using table.insert cleans up the table properly and does not leave 'nil' entries (I am explaining for my own educational purposes).


Yes, those were the +10 damage and +10 armor abilities, used as an example to show the concept of defining and adding abilities to items. Note that there's no way, as far as I know, to modify the stats of an Item ability at runtime. You can only modify a UNIT's ability. However, you could probably use some tricks like Adding multiple copies of the same ability to avoid needing to create unique Object Editor data. IE: Add ten +10 damage abilities to represent +100 damage.

Alternatively, you could use a system like NewBonus (the one I linked before) to manage your bonus stats, thus avoiding the need to even add abilities to items in the first place. Of course, activated Item abilities would still need to exist on the item itself, but that seems perfectly fine.
Understood. I pretty sure I was able to change the values (stats) of the item ability, but only when the ability was originally created from a Unit ability. Original item ability seem to not work, like you said. So I simply make the Unit abilities to also work with item abilities and that seems to work.


Yes, you understood it correctly. Copying and pasting an ability is technically creating a different ability but it uses the same Order Id which is where the issue comes into play. The 3 abilities I mentioned have the option to change this Order Id, thus avoiding the issue.

Assuming everything works, you will only need 12 Abilities to represent the 12 Items that a Shop can sell. So those 12 Abilities enable you to sell an infinite number of Items all designed through code. You often have to make some kind of compromise like this in Wc3 modding.
Sounds perfect.
With your approval, I would be happy to use the system you created and adjust it to my map needs. This looks like the most dynamic system I could hope for considering the limitations.

I found a pretty big thread on the "MUI" concept that you mentioned, so I will also go over that.
 

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I think I understand the issue. Using #ShopItemIdsAvailable for the loop iterations length, means that the loop stops upon (after?) reaching the first 'nil' entry of the table. Because the logic in the loop changes the used entry to 'nil', only 2 abilities can be created. Using table.insert cleans up the table properly and does not leave 'nil' entries (I am explaining for my own educational purposes).
Yeah, I'm now using a proper list that adds/removes/sorts elements automatically.

Understood. I pretty sure I was able to change the values (stats) of the item ability, but only when the ability was originally created from a Unit ability. Original item ability seem to not work, like you said. So I simply make the Unit abilities to also work with item abilities and that seems to work.
I don't fully follow, did you Add the abilities to the Unit instead of the Item or just uncheck "Item" in the Object Editor?

Sounds perfect.
With your approval, I would be happy to use the system you created and adjust it to my map needs. This looks like the most dynamic system I could hope for considering the limitations.
I don't need approval, but thank you, that code was hastily thrown together + copied from ChatGPT (it works surprisingly well as long as you have a good understanding of the Warcraft 3 API). Also, I'd argue that custom UI is still the best option, since the delay isn't THAT bad and more and more popular maps are using it (ie: Desert Strike). But of course that's up to you to decide.

I found a pretty big thread on the "MUI" concept that you mentioned, so I will also go over that.
To clarify, for MUI I meant that you could have multiple Shops running at the same time with their own unique item stock. It's not that confusing in this case, you just need all of the Arrays (Tables) and other global variables that I used in that Custom Shop script to have an additional index []. This index is then used to create a relationship between the Shop unit and it's data. That way each Shop has it's own set of variables. How you do that can vary, I personally like to use Unit Indexing, which is a method that gives each Unit a unique number ID and then uses that number as the [index] in your variables. Although in Lua you can use a unit's handle directly but I've read that this isn't safe since Unit handles aren't synced between clients.

Here's a basic example of a Unit Indexer system as well as using it to store a unit's custom "Spell Power" stat:
Lua:
GlobalUnitTable = {}
GlobalUnitIndex = 0

-- This would need to be setup to run whenever a new unit is created.
-- Ideally, you could hook into the original CreateUnit function.
function OnUnitCreated()
    local u = GetTriggerUnit()
    GlobalUnitIndex = GlobalUnitIndex + 1
    GlobalUnitTable[GlobalUnitIndex] = u
    SetUnitUserData(u, GlobalUnitIndex)
end

-- Manually run this to modify a unit's spell power.
function SetUnitSpellPower(u, v)
    local ud = GetUnitUserData(u)
    MyTable[ud].SpellPower = v -- you would initialize this table elsewhere
end

-- This example shows how you could use this custom stat.
function OnCastingSpell()
    local u = GetTriggerUnit() -- the caster
    local ud = GetUnitUserData(u)
    local dmg = 50.0 + MyTable[ud].SpellPower -- spell damage is improved by this custom stat
    UnitDamageTarget(u, GetSpellTargetUnit(), dmg, ...)
end
(This is just an example and there's likely better ways to do this - I'm far from a Lua expert)

So with all of that in mind, I would personally use the CustomItemType constructor method for the Shops as well. The CustomItemTypes use an Integer ID inside of a Database table, where 1 = Sword, 2 = Shield, etc... So just do that again for the Shop data but rely on the Shop unit's UserData (like in the above example) to act as it's ID in the Database rather than having predefined constants.

The end goal is very simple: If you have access to a Shop unit then you can Get it's data from the Database.
 
Last edited:
Level 3
Joined
Nov 4, 2008
Messages
22
I don't fully follow, did you Add the abilities to the Unit instead of the Item or just uncheck "Item" in the Object Editor?
I will explain better.
In my previous post (Manipulating Abilities (item abilities)), I tried to manipulate an item ability (e.g "Item Hero Stat Bonus (+10)", "AIaz") during runtime, but it didn't work. I tried doing it before and after creating the item, and I tried refreshing the ability too, but it didn't help.
I tried to Uncheck the "Item Ability" and only keep "Hero Ability", but manuplating/refreshing the ability still didn't work.
You taught me that it is possible to manipulate a Unit Ability, like Attribute Bonus. So because I learned so far that many things are behaving weirdly in WC3, I thought this might be something related to the template (original item), and that there might be a difference between original unit abilities and original item abilities.
So, I tried doing the opposite - Making the original Attribute Bonus (unit ability) an item ability (checking "Item Ability" field), and then I was able to freely manipulate the ability and attach it to the item.


I don't need approval, but thank you, that code was hastily thrown together + copied from ChatGPT (it works surprisingly well as long as you have a good understanding of the Warcraft 3 API). Also, I'd argue that custom UI is still the best option, since the delay isn't THAT bad and more and more popular maps are using it (ie: Desert Strike). But of course that's up to you to decide.
Noted, I will definitely keep that in mind. Perhaps in my next big step in this project (or another one) I will give it a go.


To clarify, for MUI I meant that you could have multiple Shops running at the same time with their own unique item stock. It's not that confusing in this case, you just need all of the Arrays (Tables) and other global variables that I used in that Custom Shop script to have an additional index [], which is then used to create a relationship between the Shop unit and it's data. That way each Shop has it's own set of those variables. How you do that can vary, I often use Unit Indexing which is a way to give each Unit a unique number ID, and then use that number as your [index]. Although in Lua you can use a unit's handle directly but I've read that this isn't safe since Unit handles aren't synced. Here's a basic example of using this method to store a unit's custom Spell Power stat:
This is just an example and there's likely better ways to do this - I'm far from a Lua expert.

But I would personally reuse the CustomItemType constructor method for the Shops as well. The CustomItemTypes used an Integer ID inside of a Database table, where 1 = Sword, 2 = Shield, etc... So just do that again but for the Shop data. Although, this time you would rely on the Shop unit's UserData to act as it's ID rather than having it predefined. The end goal is very simple: If you have access to a Shop unit then you can Get it's data from the Database.
Understood. At the moment I am not planning on having unique shops for each player, but I will have a few unique shops for all players (if that makes sense).
Due to the refactor I am doing (GUI and manual work to Lua), I am already working on replacing all the units I can/want to be created through code instead of manually placed in the WE. Which means I already have access (through code) to some of the shop units and should have access to all of them in the end.
I will take your advice and reuse/adjust the functions and tables in your code as much as I can, and fit the shops logic I already have (which is mostly gone anyway thanks to your improved system) inside.
Will probably take me a little while, but I think the design is quite defined now so it should be fine.


Just to make sure it doesn't go unnoticed, thanks again for all the time and effort you invested to help 🙏
 
Top