A comprehensive guide to Mapping in Lua

By asynchronously, are you referring to using math.random inside LocalPlayer-blocks? Even simple table operations could lead to desyncs in this scenario (which aren't mentioned either), but I can add the info if you think it would help :)
When do table operations lead to desyncs if used in a LocalPlayer-block? I'm kinda trying to do that excessively right now. 🫣
 
When do table operations lead to desyncs if used in a LocalPlayer-block? I'm kinda trying to do that excessively right now. 🫣

No worries, I was just referring to obvious misuse like this:

Lua:
T = {1}
if GetLocalPlayer() = Player(0) then
    T[1] = 2
end
CreateNUnitsAtLoc(T[1], FourCC('hfoo'), ...)

GetLocalPlayer() is the most easy way to get desyncs, which holds for Jass and Lua alike.

But I think I get what you mean. Desyncs from the above example are to be expected, while people might not expect the same for math.random.
You convinced me, I'll add it to the post.

Edit: Done.
Also added known desync from CreateTimerBJ, StartTimerBJ and GetLastCreatedTimerBJ.
 
Last edited:
Level 5
Joined
Mar 8, 2012
Messages
50
Unstable sort means that the order of equal elements can change, which doesn't imply that the result is random. Unstable sorting can still always guarantee the same output provided the same input, and we should assume that is the case unless tested otherwise.
I tested sorting your table in different Lua interpreters including Wc3 1000 times each and always got the same result.

If you have seen other results, could you maybe provide a test scenario?
I don't really have a test case, nor can I say it with 100% certainty, but during almost a year spent debugging desyncs in Spectrum TD it did lead to what I felt was a noticeable reduction in the number of desyncs, beyond what I feel could've been observer bias. However, it unfortunately took several more months to get rid of the final source of desyncs, so I cannot say for certain whether it did or did not cause desyncs.
Just like you, I indeed tested it as well at the time and could not find any problems when sorting tables in singleplayer test cases, but the experienced reduction in desyncs still leads me to believe that in a multiplayer setting it can cause desyncs.

I guess technically it might be the case that it was just aggravating the true desync cause or something, hard to rule out. I personally don't really want to bother testing it in actual multiplayer settings since it's kind of a hassle to do so, and avoiding the potential fault is low effort anyway (in C#, at least).
 
That's fair, especially after seeing where you're coming from. I still prefer to assume that table.sort is safe to use as long as we don't have other evidence, especially after I was not able to reproduce any randomness in different interpreters. All sorting algorithms I know are fully deterministic (except for parallelized environments), unless specifically designed for randomness with a use case in mind.

I'll still keep this in mind and will add a warning to the main post once someone provides further evidence :)
 
Level 8
Joined
Sep 16, 2016
Messages
227
Hello, how come I don't see all the "natives"? I'm for example missing CreateUnit here.
1732564237621.png
 
Last edited:
Good question. I've been able to reproduce this with my setup, but not to find a solution. I also don't remember to have encountered this issue before and CreateUnit is the only function so far that I found to be affected (of the few I have tried).

VSCode still shows proper parameter information though once you have typed the opening bracket (CreateUnit( ).

Let me know, if you stumble upon a solution. But it's probably not worth much effort ;)
 
Level 8
Joined
Sep 16, 2016
Messages
227
Good question. I've been able to reproduce this with my setup, but not to find a solution. I also don't remember to have encountered this issue before and CreateUnit is the only function so far that I found to be affected (of the few I have tried).

VSCode still shows proper parameter information though once you have typed the opening bracket (CreateUnit( ).

Let me know, if you stumble upon a solution. But it's probably not worth much effort ;)
Yep, from what I've seen, it seem to be the only missing one :p

Btw, do you recommend damage engines? Is there any particular good one you would recommend for lua? (@Wrda do you use one?)

Perhaps this: DamageEngine/script.lua at master · BribeFromTheHive/DamageEngine
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
Btw, do you recommend damage engines? Is there any particular good one you would recommend for lua? (@Wrda do you use one?)
I don't use one. But If I ever need one, I think i'll just make my own because of my own, because it's easy to make in Lua.
Bribe's damage engine is specially good if you're aiming for complex stuff, so I recommend it.
 
Level 3
Joined
Nov 4, 2008
Messages
22
Hey @Eikonium,
First of all, I would like you express my gratitude on the time and effort you invested to create this detailed and very well written guide.
I started reading this guide about a month ago with very limited coding background, and now I have completely refactored a small map I created back in 2018 in GUI.
Currently my code works, but it's far from perfect. Every day I think about previous things I wrote that I could easily improve, which I will slowly do.

Well, small correction about "completely refactored". I have two things I have left that I still did not do yet, which are Ability/Spell and items concepts.
I would like to refactor the basic system I currently have, to abilities/items completely based on Lua code.
What I currently have is: Shops which you can buy simple abilities/items created/adjusted in Object Editor. Heroes are using these abilities/items (again, created via Object Editor).
I found a bit of information that shows the general basic logic (mainly via JASS, but it doesn't really matter because I can simply convert the logic)...
E.g; Create dummy unit->create special effects/sounds->make dummy unit cast custom spells->clean up any potential leaks.
What I also read is that this method is obsolete. So my question is, are you perhaps familiar with any extended advanced guide that goes over the logic and good practices that is relevant to the latest patch? I would appreciate some guidelines on this topic.
*I have seen a few resources/packs with premade spells to use. I did not check that, as I prefer to build my own system/spells (mainly for educational purposes).

Thanks a lot! 🙏
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
I found a bit of information that shows the general basic logic (mainly via JASS, but it doesn't really matter because I can simply convert the logic)...
E.g; Create dummy unit->create special effects/sounds->make dummy unit cast custom spells->clean up any potential leaks.
What I also read is that this method is obsolete.
Dummy units casting spells is not obsolete and likely never will be. But it all depends on your map. It's possible to avoid using them entirely but I feel like that'd be a waste of time when you can just learn how to use them effectively. There's definitely an old school way of using them which is obsolete, though.

For example:

You can calculate Movement Speed yourself, OR you can use the Slow ability with modified ability fields and let the game do the rest of the math for you. Slow comes with some baggage like a Buff and the 1 frame delay/overhead of using a Dummy unit, but that's the price for convenience. It's up to you to decide which is better.

So my question is, are you perhaps familiar with any extended advanced guide that goes over the logic and good practices that is relevant to the latest patch? I would appreciate some guidelines on this topic.
Useful features of the latest patch (1.31+):
  • Use the Ability Field natives to modify an abilities Object Editor data at runtime. The New Bonus system I linked does this and it's extremely powerful.
  • Use the new natives for modifying Armor, Attack Interval (cooldown), etc.
  • Use the new natives for Disabling/Enabling abilities. This could be used to make a custom Silence system and allow you to make abilities "silence immune".
  • Use the new BlzPauseUnitEx() native to Stun/Unstun units without the need of a Dummy unit. Can also be used to make a unit "stun immune".
These two systems could probably carry the majority of your custom spells:

You can use a Missile system, Knockback system, Damage system, Buff system, etc. to make your life easier.
These are all pretty straightforward, a Missile system is just a way to move Special Effects using Timers + run code in response to Impact.
A Knockback system is just a Unit being moved periodically with Timers while using tricks like Item pathing checks to avoid units getting stuck in terrain.
A Damage system uses the existing "a unit takes damage" natives more effectively, ideally avoiding infinite damage loops: On damage taken -> Deal damage.
A Buff is just a container of ability effects which is removed when it's Timer (duration) expires, which is fairly easy to recreate yourself.
 
Last edited:
Level 3
Joined
Nov 4, 2008
Messages
22
@Uncle Thank you for the response!
The main reason I am learning Lua and working on a map is for educational purposes, hence it is currently important to me to learn from the bottom up, instead of downloading and using the suggested systems.
Probably when I will get to more advance stuff, I will start utilizing the great systems people created (like what you suggested), but until then, I prefer to learn the basics.
Could you recommend a detailed guide for the "old and simple" dummy way?


On a different topic, I actually encountered some issues with a few natives that I would love to get help on. If it's not the right place to talk about it, let me know and I will edit the message and create a dedicated post.
I would like to create an item system while reducing the need to rely on Object Editor for editing/manipulating items/shops as much as possible, but I encountered a few issues that I don't quite get.
For my testing, I created a dummy item (from "Ring of Protection +2", "rde1") and dummy shop (from "Arcane Vault", "hvlt") in Object Editor just to have an itemId and unitId to work with. I intentionally did not remove the abilities (stats) the item already has and the items the shop already has.
I created the dummy shop (spawned, in the map itself) and tried to remove a predefined item from it, but it doesn't seem to do anything.
Lua:
function test()
    local shop = CreateUnit(Player(0), FourCC("h000"), -550, -850, 0) --works
    RemoveItemFromAllStock(FourCC("sreg")) -- doesn't seem to do anything
end
OnInit.final(function()
    test()
end)
The shop is created, but the scroll is still there.
I tried the same with the second native function, but that didn't seem to do anything either:
Lua:
function test()
    local shop = CreateUnit(Player(0), FourCC("h000"), -550, -850, 0) --works
    RemoveItemFromStock(shop, FourCC("sreg")) -- doesn't seem to do anything
end
OnInit.final(function()
    test()
end)

I tried adding an item to it, but again that didn't seem to do anything as well:
Lua:
function test()
    local shop = CreateUnit(Player(0), FourCC("h000"), -550, -850, 0) --works
    AddItemToStock(shop, FourCC("I000"), 1, 1) -- doesn't seem to do anything
end
OnInit.final(function()
    test()
end)
What am I doing wrong?

Regarding manipulating the item itself, I was able to do some stuff, but some isn't consistent. I will explain.
Lua:
function test2()
    local item = CreateItem(FourCC("I000"), -550, -850)
    BlzSetItemName(item, "new name") --works
    BlzSetItemIconPath(item, "Objects\\InventoryItems\\TreasureChest\\treasurechest.mdl") --Breaks the icon. The item becomes invisible in the inventory.
    BlzSetItemDescription(item, "new description") --works
    BlzItemRemoveAbility(item, FourCC("AId2")) --works
    BlzItemAddAbility(item, FourCC("AIt6")) --doesn't seem to do anything
end
OnInit.final(function()
    test2()
end)
So it seems like manipulating the icon breaks it, and adding an ability just doesn't do anything.
I did some more testing, and I figure out that if I add a delay before changing the icon path and adding ability, and during the delay I pick the item from the ground with a hero, the natives behave differently. The icon path native doesn't seem to do anything (doesn't break the icon either), but adding an ability suddenly actually adds the ability. Dropping the item and picking it up again doesn't seem to effect this. So the item now has the new ability (+ damage) forever. And the icon path function still didn't do anything.
Lua:
function test2()
    local item = CreateItem(FourCC("I000"), -550, -850)
    BlzSetItemName(item, "new name") --works
    BlzSetItemDescription(item, "new description") --works
    BlzItemRemoveAbility(item, FourCC("AId2")) --works
   
    TimerStart(CreateTimer(), 5, false, function ()
        BlzSetItemIconPath(item, "Objects\\InventoryItems\\TreasureChest\\treasurechest.mdl") --doesn't seem to do anything
        BlzItemAddAbility(item, FourCC("AIt6")) --now works
        DestroyTimer(GetExpiredTimer()) --doesn't matter here, but to show I am a decent student :D
    end)
end
OnInit.final(function()
    test2()
end)

Since I am very new to all of this, I could just be missing something obvious or doing something very stupid.
I would really appreciate anyone's help on this matter.
If there won't be a solution for this, I will have to result to manipulating hero stats and having items do nothing instead, which is less ideal for me, since I want to be able to do as much as possible via Lua code instead of relying on Object Editor GUI.

Thank you! :)

EDIT:
While writing this, I got an idea to give the item to an invisible unit, manipulate it and then give that item to someone else. Not sure if that would work, but either way I guess it doesn't fix everything and it's quite an overhead..
 
Last edited:

Uncle

Warcraft Moderator
Level 73
Joined
Aug 10, 2018
Messages
7,866
A lot of the new stuff is glitchy and requires tricks to get working, but that's been the case for Reforged since release and nothing has changed and likely never will. You just have to roll with the punches and hope a new patch doesn't make things worse (or lock yourself to say 1.31).

1) Anyway, you're trying to use a Model as the Icon path, you're supposed to use .blp / .dds files for Icons:
Lua:
BlzSetItemIconPath(item, "Objects\\InventoryItems\\TreasureChest\\treasurechest.mdl")
2) Shops can only have their Stock modified if the Items were added manually through code. Also, the Shop needs the Sell Items ability.

3) I've heard that adding abilities to Items is glitchy and the solution was something like what you did. The overhead is not that big of a deal, you could easily have a system that hides all of that from the user, it's not like it'll affect game performance.


A Dummy unit is simply a unit that casts an ability. This hasn't really changed much:

1) Base the Dummy unit on the Locust, with the following changes: Model = None, Shadow = None, Attacks = None, Speed Base = 0, Movement = None.

2) Create a new Ability that the Dummy will cast. Ensure that it has no requirements that will prevent it from casting: 99999 cast range, 0 mana, 0 cd, etc.

3) In your triggers: Create Dummy unit -> Add ability to it -> Set level of ability (optional) -> Add expiration timer to it (the duration depends, the Dummy should not expire before the effects of the ability expire, at least in a lot of cases) -> Order it to cast the ability.

Ways to improve Dummy usage:
1) Dummy recycling, try to reuse Dummy units instead of constantly creating new ones. This is a concept in game development calling Object Pooling.

2) Link Dummy units to their "creator" and redirect Damage Events so that the "creator" deals the damage. Mapmakers seem to not care that these Dummy units are getting damage/kill credit but that often messes with a bunch of other triggers.

3) Use the new Ability Field natives to modify your Dummy abilities after adding them. This avoids needing to have Levels on the Dummy ability and gives you much more control. For example, a Dummy could cast Bloodlust on a group of Units, and the bonus Attack Speed % could scale based on the casting Hero's Agility.

You should probably make your own thread about this!
 
Last edited:
Level 3
Joined
Nov 4, 2008
Messages
22
@Uncle Thanks for the detailed answer!
1) Anyway, you're trying to use a Model as the Icon path, you're supposed to use .blp / .dds files for Icons:
That was quite silly of me. Thanks. Changing icon works fine now.

2) Shops can only have their Stock modified if the Items were added manually through code. Also, the Shop needs the Sell Items ability.
Since it was already a "working shop" and it had "Shop Sharing" and "Shop Purchase Item" abilities, I assumed nothing else is needed. Weird.
1737537084950.png

I added "Sell Items" to "Abilities - Normal", although I wasn't sure where I should add it as it was available in all 4 of the Abilities fields.
Also, as you said, after removing the predefined items I was able add/remove new items through code.

3) I've heard that adding abilities to Items is glitchy and the solution was something like what you did. The overhead is not that big of a deal, you could easily have a system that hides all of that from the user, it's not like it'll affect game performance.
So the cleanest, most seamless and consistent solution I could think of, is using a "TriggerRegisterAnyUnitEvent" trigger with "EVENT_PLAYER_UNIT_PICKUP_ITEM" event, and simply manipulate the item after it was picked up. Everything seems to work consistently this way, aside from manipulating the ability of the item, which isn't directly related to the item itself and I will create a dedicated thread per your suggestion.

A Dummy unit is simply a unit that casts an ability. This hasn't really changed much:

1) Base the Dummy unit on the Locust, with the following changes: Model = None, Shadow = None, Attacks = None, Speed Base = 0, Movement = None.

2) Create a new Ability that the Dummy will cast. Ensure that it has no requirements that will prevent it from casting: 99999 cast range, 0 mana, 0 cd, etc.

3) In your triggers: Create Dummy unit -> Add ability to it -> Set level of ability (optional) -> Add expiration timer to it (the duration depends, the Dummy should not expire before the effects of the ability expire, at least in a lot of cases) -> Order it to cast the ability.

Ways to improve Dummy usage:
1) Dummy recycling, try to reuse Dummy units instead of constantly creating new ones. This is a concept in game development calling Object Pooling.

2) Link Dummy units to their "creator" and redirect Damage Events so that the "creator" deals the damage. Mapmakers seem to not care that these Dummy units are getting damage/kill credit but that often messes with a bunch of other triggers.

3) Use the new Ability Field natives to modify your Dummy abilities after adding them. This avoids needing to have Levels on the Dummy ability and gives you much more control. For example, a Dummy could cast Bloodlust on a group of Units, and the bonus Attack Speed % could scale based on the casting Hero's Agility.
This is what I needed. I couldn't find a clear all in one place instruction, and people were writing slightly different things in different places.
I will try that. Actually the issue I mentioned above is regarding your point (3), on changing ability fields. As I said, I will create a dedicated thread for it.

Thanks again for taking the time to read and reply to my questions!
 
Top