• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] [Snippet] SetUnitDamage

Level 2
Joined
Aug 16, 2012
Messages
15
SetUnitDamage

This system allows you to add or subtract damage from a unit. No green numbers following a plus sign, no background code, just simple damage addition (or subtraction).

WARNING: Tomes leak. This snippet uses tomes, and there have been suggestions that this snippet may leak. To deal with this problem, please use ItemCleanup found at http://www.hiveworkshop.com/forums/jass-resources-412/system-item-cleanup-175663/

Usage Example:

You want to add 10 damage to a peasant.

If you use a bonus style mod you will get: Damage: 5 - 6 + 10
(Note: 2 of the 3 most common bonus systems require unit indexing systems and have functionality unrelated to damage.)

If you use this function you will get: Damage: 15 - 16

The functional difference is that other systems use abilities that can be added to units to increase their damage with a nice green plus and the damage amount. This library/function creates several items that function as tomes of damage and add directly to, or subtract from the unit's damage. You can also use other systems with this function. The above peasant could easily have 15 - 16 + 10 damage.

Lua:
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! i function createdamagemodifier (id, i)
        //! i setobjecttype ("abilities")
        //! i createobject ("AIaa", "adb" .. id)
        //! i makechange (current, "ansf", "+" .. i)
        //! i makechange (current, "anam", "Damage Modifier")
        //! i makechange (current, "Iaa1", 1, i)
        //! i makechange (current, "acat", "")
        //! i setobjecttype ("items")
        //! i createobject ("tdex", "idb" .. id)
        //! i makechange (current, "unsf", "+" .. i)
        //! i makechange (current, "unam", "Damage Modifier")
        //! i makechange (current, "iabi", "adb" .. id)
        //! i makechange (current, "ifil", "Doodads\Terrain\LOSBlocker\IntentionallyLeftBlank.mdl")
        //! i makechange (current, "ipow", 0)
    //! i end
    
    //! i local i = 0
    //! i while (i < 5) do
    //! i     createdamagemodifier (i, 10 ^ i)
    //! i     createdamagemodifier (i + 5, -1 * 10 ^ i)
    //! i     i = i + 1
    //! i end 
//! endexternalblock

Jass:
JASS:
 library SetUnitDamage uses /*
    */optional RegisterPlayerUnitEvent // [url]www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/[/url]
/*
    Version 1.0 by Contradictator
    
    Thanks to Magtheridon96 and PurgeandFire111 for providing
    compatability with RegisterPlayerUnitEvent.
    
    This library allows the damage of units to be manipulated 
    in-game without the use of specific upgrades, or green number 
    bonuses. Note: this can only change the base damage of a unit.
    
    For this system to work you must copy the provided lua script
    into your map, save the map, and close the map. Thereafter,
    it is suggested that you either delete the lua script or
    atleast disable the trigger containing it.
    
    WARNING: Unit damage can go negative, but will display as 0.
    This means that a unit that shows 0-0 damage may actually
    have -12 damage. This unit will deal 0 damage on attack. If
    you give that unit an item to do +10 damage, it would show
    in game as doing 0-0 +10, but would still do -2 and deal no
    damage on attack.
    
    ADDITIONAL WARNING: This system will fire events for units
    dropping and picking up items unless the optional library
    by Magtheridon96 is also used in which case it will not fire
    such events.
    
    For your use:
    
    function AddUnitDamage
        takes unit u, integer d 
            returns nothing
    
    function SetUnitDamage
        takes unit u, integer currentDamage, integer newDamage 
            returns nothing
*/    
    
    globals
        // Damage modifying items.
        private item array dm
    
        // The amount that each modifier adds to damage.  
        private integer array dmVal
    endglobals
   
    // returns the amount of damage added with each modifier used.
    private function UnitUseDamageModifier takes unit u, integer index, integer times returns integer
        local item i = dm[index]
        local integer amtAdded = dmVal[index] * times
        // make sure there is still a charge left afterward.
        call SetItemCharges (i, times + 1)
        static if LIBRARY_RegisterPlayerUnitEvent then
            call DisableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_PICKUP_ITEM))
        endif
        call UnitAddItem (u, i)
        static if LIBRARY_RegisterPlayerUnitEvent then
            call EnableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_PICKUP_ITEM))
        endif
        loop
            exitwhen times == 0
            call UnitUseItem (u, i)
            set times = times - 1
        endloop
        static if LIBRARY_RegisterPlayerUnitEvent then
            call DisableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_DROP_ITEM))
        endif
        call UnitRemoveItem (u, i)
        static if LIBRARY_RegisterPlayerUnitEvent then
            call EnableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_DROP_ITEM))
        endif
        call SetItemVisible (i, false)
        set i = null
        return amtAdded
    endfunction
    
    function AddUnitDamage takes unit u, integer d returns nothing 
        local integer i
        local integer iEnd
        local item it
        local boolean inv = false
        if UnitInventorySize (u) > 0 then
            // The unit has an inventory. Remove the item in the
            // first slot and hide it so we can put damage
            // modifying items in that slot.
            set inv = true
            set it = UnitItemInSlot (u, 0)
            static if LIBRARY_RegisterPlayerUnitEvent then
                call DisableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_DROP_ITEM))
            endif
            call UnitRemoveItem (u, it)
            static if LIBRARY_RegisterPlayerUnitEvent then
                call EnableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_DROP_ITEM))
            endif
            call SetItemVisible (it, false)
        else
            // The unit has no inventory, so we'll give it one now
            // and remove it later.
            call UnitAddAbility (u, 'AInv')
        endif
        if d < 0 then
            // The unit needs the negative damage modifiers which
            // are stored in indicies 9 - 5.
            set i = 9
            set iEnd = 4
        else
            // The unit needs the positive damage modifiers which
            // are stored in indicies 4 - 0.
            set i = 4
            set iEnd = -1
        endif
        loop
            exitwhen i == iEnd
            // Have the unit use the damage modifier the whole
            // number of times is needs to (R2I rounds down).
            set d = d - UnitUseDamageModifier (u, i, R2I (d / dmVal[i]))
            set i = i - 1
        endloop
        if inv then
            // The unit had an inventory, so give it it's item back,
            // and don't remove the inventory.
            static if LIBRARY_RegisterPlayerUnitEvent then
                call DisableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_PICKUP_ITEM))
            endif
            call UnitAddItem (u, it)
            static if LIBRARY_RegisterPlayerUnitEvent then
                call EnableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_PICKUP_ITEM))
            endif
        else
            // The unit had no inventory, so remove the one it was
            // given.
            call UnitRemoveAbility (u, 'AInv')
        endif
        set it = null
    endfunction
    
    function SetUnitDamage takes unit u, integer currentDamage, integer newDamage returns nothing
        call AddUnitDamage (u, newDamage - currentDamage)
    endfunction
    
    
    private module mod
        private static method onInit takes nothing returns nothing
            local integer i = 0
            local integer x = 1
            loop
                exitwhen i == 10
                set dm[i] = CreateItem ('idb0' + i, 0, 0)
                call SetItemVisible (dm[i], false)
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i == 5
                set dmVal[i] = x
                set dmVal[i + 5] = -x
                set i = i + 1
                set x = x * 10
            endloop
        endmethod
    endmodule
    
    private struct init
        implement mod
    endstruct
endlibrary

Thank you to PurgeandFire111 for his Lua tutorial here on THW.

Edit: Changed post to reflect functionality.
 
Last edited:
Creating one handle per damage bonus isn't the right way to go :/

There are other systems that add damage to a unit using abilities, support negative damage, AND they work for armor, hp, mana, mana regeneration, hp regeneration, attack speed, sight range, you name it.

These are the 3 best in my opinion:
  • Bonus by Nestharus - Excellent configuration, very efficient
  • BonusMod by Earth-Fury - Awesome configuration, incredibly decent
  • Status by Jesus4Lyf - Just as good as the above, but not as good in terms of configuration. It's still great because it comes with a lot of other features like Banishing units, phasing them, making them untouchable, etc..., but I do believe that all those extra features should be put into a separate lib, which is why I recoded his Status system and turned into 2 new systems, one called Bonus, the other called Status. Status handles things like phase, banish, invisibility, touchability, etc..., while Bonus handles damage, attack speed, armor, hp and mana.

Bah, I'm talking too much.

By the way:

JASS:
        local integer i = 0
        local integer id = 'idb0'
        loop
            exitwhen i == 10
            set dm[i] = CreateItem(id, 0, 0)
            call SetItemVisible (dm[i], false)
            set id = id + 1
            set i = i + 1
        endloop

;)
 
Level 2
Joined
Aug 16, 2012
Messages
15
This doesn't add a bonus, it adds right to (or subtracts from) the units damage and isn't designed to be a system. With those systems when you apply a damage bonus of to a peasant you get "5 - 6 + 10". For the units damage with the 10 being the green item bonus color. With this, which is more meant to be a SetUnitMaxState style function, you get "15 - 16" when adding a bonus. This also doesn't have to track the bonus. This also isn't meant to be configured. The mapper can add as much damage as they would like. Attempting to make a unit's damage < 0 just sets it to 0 as per wc3's actions. I updated the jass and the original post, I hope I clarified my purpose.

JASS:
local integer i = 0
loop
    exitwhen i == 10
    set dm[i] = CreateItem ('idb0' + i, 0, 0)
    call SetItemVisible (dm[i], false)
    set i = i + 1
endloop
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
This doesn't add a bonus, it adds right to (or subtracts from) the units damage and isn't designed to be a system. With those systems when you apply a damage bonus of to a peasant you get "5 - 6 + 10". For the units damage with the 10 being the green item bonus color. With this, which is more meant to be a SetUnitMaxState style function, you get "15 - 16" when adding a bonus. This also doesn't have to track the bonus. This also isn't meant to be configured. The mapper can add as much damage as they would like. Attempting to make a unit's damage < 0 just sets it to 0 as per wc3's actions. I updated the jass and the original post, I hope I clarified my purpose.

JASS:
local integer i = 0
loop
    exitwhen i == 10
    set dm[i] = CreateItem ('idb0' + i, 0, 0)
    call SetItemVisible (dm[i], false)
    set i = i + 1
endloop

This was clear to me when I saw the item abilities. At first, I thought it was going to be a crappy DDS attempt at my Damage Modifier lib, but I saw it's like a SetUnitDamage.

I'm not going to argue for binary in this case as it would require more items. I think that with some polish, this library will be a fine addition to the other myriad of resources on THW ^)^.

Some questions though, in case you didn't test it out without these lines.

Is this necessary?
call UnitAddItem (u, i)

If so, will the above work with units without inventories and non hero units?

The next problem is the Lua. The policy is that you either cnp from the map or you do dynamic object ids with no chance of collision using the Lua framework. Those are your options, pick one. You can look at Purge's sig to find info on using the Lua framework.

After this, it appears that you are using tomes? You need to make it more dynamic. With slight modifications, you could change the code to support a tome. As such, you should make the code work with any tome. Be sure to use a function so that if you end up missing a tome, a user can add it in later. Furthermore, if a user wants to add a custom tome for some unknown reason, they can also add it in.

Those are the only problems I see for the moment, but I haven't gotten into your actual code yet, just the design.


Also, don't use the name Damage Modifier, it's misleading. Do Tome Mod or something.


Now of course, if you find the above to be too much work, we can always have your resource rejected and wait for someone else to come along and do it correctly =). It saves time down the line when we end up having to replace this resource with a correct one.

edit
On the code side, you need to use a module initializer. Cohadar's jasshelper isn't acceptable at the moment as it has a fatal bug in it. The reason for this is because Vexorian's jasshelper does initialization in the incorrect order. It's good that you didn't use any for loops, or you would have had more code to fix ; P.

Nice first attempt though, first resource on here in a while that has potential and doesn't require a complete rewrite ^)^, only some augmentations.
 
Level 2
Joined
Aug 16, 2012
Messages
15
UnitAddItem is necessary. I tested it without and no damage was added.

As for the Lua, I will work on updating that framework. There should be few if any conflicts, but if I understand your policy on resource usage correctly, "few if any" is an unacceptable number of conflicts. Tomorrow I will tackle this issue.

I don't intend this to be a general tome mod. Tomes can increase exp, level, health, agi, int, str, and damage. All of those except damage can be changed via natives. This is strictly for changing a unit's damage without the use of bonus abilities/systems.

I can understand the naming conflict (prolly one of those "few if any" type things really ;)) the name ChangeUnitDamage doesn't follow convention and the name SetUnitDamage is reserved for the function, which is more important in terms of convention than the library. Tome Mod is too general, because it works only with damage, and doesn't focus on the item aspect. Do you have a more specific suggestion?

I didn't use Cohadar's jasshelper (submission rules allow it though). This compiled perfectly for me perfectly using JNGP and the last of Vexorian's jasshelper versions. If you'd care to point out how I can change it to a module initializer, I'd be happy too.

Thank you.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
UnitAddItem is necessary. I tested it without and no damage was added.

As for the Lua, I will work on updating that framework. There should be few if any conflicts, but if I understand your policy on resource usage correctly, "few if any" is an unacceptable number of conflicts. Tomorrow I will tackle this issue.

I don't intend this to be a general tome mod. Tomes can increase exp, level, health, agi, int, str, and damage. All of those except damage can be changed via natives. This is strictly for changing a unit's damage without the use of bonus abilities/systems.

I can understand the naming conflict (prolly one of those "few if any" type things reall ;)) the name ChangeUnitDamage doesn't follow convention and the name SetUnitDamage is reserved for the function, which is more important in terms of convention than the library. Tome Mod is too general, because it works only with damage, and doesn't focus on the item aspect.

I didn't use Cohadar's jasshelper (submission rules allow it). This compiled perfectly for me perfectly using JNGP and the last of Vexorian's jasshelper versions. If you'd care to point out how I can change it to a module initializer, I'd be happy too.

Thank you.

The submission rules are a little outdated.

Look at Unit Indexer to see example of a module initializer I guess (just module with private static method onInit in it that's implemented into a struct).

Health could be another possible tome >.>, but I suppose just using a bonus lib in that case would be smarter.

Library names don't conflict with function names. I've named libraries the same thing as functions before, lol.
 
Level 2
Joined
Aug 16, 2012
Messages
15
I found a new problem. When you set a unit's damage below 0, it displays 0 damage and does zero damage, but if you add an item to it, it actually tracks how much negative you are and will only do what damage the item puts you over 0.

Example: You subtract 10 damage from a peasant. His damage would show as 0-0 and he would do 0, but if you add an item that does +1 damage, he will show 0-0 +1 and still do zero because the game tracks his actual damage as -5 - -4 + 1 which is still less than one.

I'm not sure how to deal with this except by making the AddUnitDamage a private function, and putting in a check at SetUnitDamage to prevent it from going negative without the mapper feeding it false information.

Look at Unit Indexer to see example of a module initializer I guess (just module with private static method onInit in it that's implemented into a struct).

I don't use a struct anywhere and a module initializer sitting in a library doesn't seem to do anything. I may be missing something entirely here, but some explanation would be nice.

Health could be another possible tome >.>, but I suppose just using a bonus lib in that case would be smarter.

hmmm...mind sharing a native which modifies max health of unit?

It would be drastically slower and generate more handles/items to use this for altering health than simple SetUnitMaxState, which is plenty easy to use.

If he starts using tomes for this, then the resource should also require ItemCleanup as tomes are never removed without it.

I don't ever kill my items. They always stay the same. I give them one more charge than is used then have the unit drop them and hide them. There're no dead items to be cleaned up. I checked for leaks and got none.

The next problem is the Lua. The policy is that you either cnp from the map or you do dynamic object ids with no chance of collision using the Lua framework. Those are your options, pick one. You can look at Purge's sig to find info on using the Lua framework.

I noticed that in your Bonus system, you imported the varname variables produced by getvarobject as an array (with the cryptically short name "bd", naturally). I looked at the tutorial and found nothing about how to gen an array of variables, and I feel one would be appropriate here. If you could point me in the right direction, I'd be much obliged.

While we're on the subject of lua, is it possible that some mappers might find that massive chain of lua scripts a bit daunting and be discouraged from using them or any system that requires them? I feel like there may be value to be had in simple scripts that can theoretically collide, but that people are able to use easily with one cnp. I realize that the code will not be perfect or optimal, but it will at least be usable by someone other than you. If you can show me how to do the array thing, I can write this up using the lua framework, but I really think that people other than the frequent fliers of the HW Jass section should be able to find and use this code.
 
Level 2
Joined
Aug 16, 2012
Messages
15
Are you reffering to some library?

Yes. I thought it was well known enough for a simple reference. It's been around since blade.dk wrote it years ago. It's actually just a function and could perhaps do with an overhaul.

Tbh, I haven't checked which system would be faster, but I strongly suspect that SetUnitMaxState would blow mine away because all it does is add an ability, set it's level, and remove it.
 
Level 2
Joined
Aug 16, 2012
Messages
15
I updated it to use a module initializer after taking a peek at some other submissions (UnitIndexer is a tad complex to learn just a few lines of code from it :/). I also changed the library name to SetUnitDamage to decrease ambiguity/conflicts. If someone could point me in the direction of creating an array of variables using getvarobject for the lua object generation, I can get a full lua version up and running. I will work on a map for ability cnp later today and make the code work with that too so that the non-lua people may use this too.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
nice error youve got there
JASS:
    struct init
        implement mod
    enstruct //<----

also private static function onInit takes nothing returns nothing throws out syntax error, it has to be static method onInit takes nothing returns nothing
 
You can omit the Pow calls in your onInit static method.

JASS:
set i = 0
set x = 1
loop
    exitwhen i == 5
    set dmVal[i] = x
    set dmVal[i + 5] = -x
    set i = i + 1
    set x = x * 10
endloop

where x is an integer.
Pow is a pretty slow function, as it can handle stuff like Pow(13.37, 42.9001)

edit
This function could cause some very strange behaviors with a ton of systems since it fires Drop/Pickup item events.
The solution is to use RegisterPlayerUnitEvent.
Before you remove the item from the unit, call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM))
Then enable it afterwards.

Before you add the item to the unit, call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM))
Enable it afterwards as well.

For example, with an old recipe system of mine, this would trigger the item compiling for an recipe item that has been disassembled upon adding damage with this system's function, which is quite a strange behavior if you ask me o_O
 
Last edited:
I found a new problem. When you set a unit's damage below 0, it displays 0 damage and does zero damage, but if you add an item to it, it actually tracks how much negative you are and will only do what damage the item puts you over 0.

I don't think making SetUnitDamage have checks to prevent negatives is the best option. It will reduce the functionality a bit. In my opinion, add that info as a note in the documentation. Since there is no way to retrieve unit damage without databasing, you should just leave it as it is.

Contradictator said:
I don't use a struct anywhere and a module initializer sitting in a library doesn't seem to do anything. I may be missing something entirely here, but some explanation would be nice.

Module initializers are used because they are fired before all other initializers. (in vexorian's jasshelper anyway) It is to ensure that the functions work on initialization, because if you used a library initializer, there would be a chance that people using your system might have their initialization called before your system is even set up.

Contradictator said:
While we're on the subject of lua, is it possible that some mappers might find that massive chain of lua scripts a bit daunting and be discouraged from using them or any system that requires them? I feel like there may be value to be had in simple scripts that can theoretically collide, but that people are able to use easily with one cnp. I realize that the code will not be perfect or optimal, but it will at least be usable by someone other than you. If you can show me how to do the array thing, I can write this up using the lua framework, but I really think that people other than the frequent fliers of the HW Jass section should be able to find and use this code.

Eh, in my opinion you probably should leave it as it is. Only so many people used getvarobject, and having it as a requirement for a user basically means "go to the object editor and copy the object yourself". It makes something that was meant to be easier for implementation become a bit of a hassle.

It is definitely not bad, but it didn't catch on. Having it as a requirement means (1) people won't use the scripts that require them because (A) laziness (B) confusing (C) takes long, and (2) people won't use lua in their scripts at all.

If someone wants to use it, they can add that feature. However, I doubt it will be very beneficial, especially since cohadar updated jasshelper. If you use cohadar's jasshelper, having file header in your map will mean that you get a message that pops up every time you save telling you to close your map and reopen.

----

Overall this system is good. :) Make your module and struct private. Also, as Mag said, you should disable the trigger in register player unit event. If you don't necessarily want it as a requirement, then you can add optional support with static if LIBRARY_RegisterPlayerUnitEvent then or whatever the command is.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
If someone wants to use it, they can add that feature. However, I doubt it will be very beneficial, especially since cohadar updated jasshelper. If you use cohadar's jasshelper, having file header in your map will mean that you get a message that pops up every time you save telling you to close your map and reopen.

Ahem, cohadar's jasshelper isn't approved on THW atm, so it isn't accepted. The reason for this is due to a critical error.

As for what I said in the Lua, it's either using that framework or cnping the objects from a map. It only took me about 15 seconds to get all of the scripts on my comp and it was a 1 time deal.

For quick test maps, I typically just cnp the object from a map.


Most people do just cnp from the map, so when I submit a resource, I usually don't even bother with Lua unless there are multiple objects (notice that Bonus has no map to cnp the objects, it completely requires LUA_FILE_HEADER).
 
Level 2
Joined
Aug 16, 2012
Messages
15
As for what I said in the Lua, it's either using that framework or cnping the objects from a map.

As I said:
If someone could point me in the direction of creating an array of variables using getvarobject for the lua object generation, I can get a full lua version up and running.

I would like to keep a version with the current lua available atleast. I realized given the number of abilities and items that cnp from a map is going to be too much work for many people and the chance of conflict just isn't that high. Anyone installing so many systems that a conflict occurs would probably be wise to get the Lua framework. Anyone else can use it as is.

The full lua framework would give me the ability to change the base and number of items + abilities according to the mapper's needs/preferences. 10 is really just a standard base, not necessarily the best.

This function could cause some very strange behaviors with a ton of systems since it fires Drop/Pickup item events.
The solution is to use RegisterPlayerUnitEvent.
Before you remove the item from the unit, call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_DROP_ITEM))
Then enable it afterwards.

Before you add the item to the unit, call DisableTrigger(GetPlayerUnitEventTrigger(EVENT_PLAYER_UNIT_PICKUP_ITEM))
Enable it afterwards as well.

Done and tested. It works and the event doesn't fire.

You can omit the Pow calls in your onInit static method.

Also done. A nice bit of efficiency there. Thanks.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Putting the stuff into arrays variables is done pretty easily with the writejass function ;o.

function writejass(name, code)

The name refers to the filename.

Demonstration (from LUA_FILE_HEADER thread)
JASS:
//! externalblock extension=lua FileExporter $FILENAME$
    //run the header first
    //! runtextmacro LUA_FILE_HEADER()
    
    //writing an lua script to a follow
    //! i writelua("MyScript", [[
        //! i function Hello()
            //! i logf("hi")
        //! i end
    //! i ]])
    
    //using the lua script just written
    //! i dofile("MyScript")
    
    //calling a function inside of written lua script
    //! i Hello()
    
    //writing 3 jass scripts that are imported into the map automatically
    //-----------------------------------------------------------
        //! i writejass("MyScript", [[
            //! i struct Tester1 extends array
                //! i private static method onInit takes nothing returns nothing
                    //! i call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "hello world")
                //! i endmethod
            //! i endstruct
        //! i ]])
        
        //! i writejass("MyScript2", [[
            //! i struct Tester2 extends array
                //! i private static method onInit takes nothing returns nothing
                    //! i call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "hello world")
                //! i endmethod
            //! i endstruct
        //! i ]])
        
        //! i writejass("MyScript3", [[
            //! i struct Tester3 extends array
                //! i private static method onInit takes nothing returns nothing
                    //! i call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "hello world")
                //! i endmethod
            //! i endstruct
        //! i ]])
    //-----------------------------------------------------------
    
    //delete the second jass script
    //! i deletejass("MyScript2")
    
    //write jass script 1 and lua script to grimext logs
    //! i logf(readjass("MyScript"))
    //! i logf(readlua("MyScript"))
    
    //clear out so that you don't have to delete this demo from your directory : D
    //! i deletelua("MyScript")
    //! i deletejass("MyScript")
    //! i deletejass("MyScript3")
//! endexternalblock

The written JASS code is automatically included. I normally wrap the JASS code in a textmacro so that I can put it directly into the library =).

Demo from Bonus
JASS:
    //! i     jass = jass .. [[//! textmacro BONUS_SCRIPT]] .. "\n"
    //! i         jass = jass .. beginglobals
    //! i             jass = jass .. powermax
    //! i             jass = jass .. powerset
    //! i             jass = jass .. binarypower
    //! i             jass = jass .. objectid
    //! i             jass = jass .. isranged
    //! i             jass = jass .. currentbonus
    //! i             jass = jass .. abilityids
    //! i         jass = jass .. endglobals
    //! i
    //! i         jass = jass .. begininit
    //! i             jass = jass .. preloadif
    //! i                 jass = jass .. preload
    //! i             jass = jass .. preloadend
    //! i             jass = jass .. index
    //! i             jass = jass .. deindex
    //! i             jass = jass .. objectids
    //! i             jass = jass .. powermaxes
    //! i             jass = jass .. powersets
    //! i             jass = jass .. israngeds
    //! i         jass = jass .. endinit
    //! i
    //! i         jass = jass .. beginstruct
    //! i             jass = jass .. beginindex
    //! i                 jass = jass .. indexcode
    //! i             jass = jass .. endindex
    //! i             jass = jass .. begindeindex
    //! i                 jass = jass .. deindexcode
    //! i             jass = jass .. enddeindex
    //! i         jass = jass .. endstruct
    //! i     jass = jass .. [[//! endtextmacro]] .. "\n"

The above is pretty easy to read. I'm just using variables that I filled with code.


#1, you'll need to declare the array you need in a globals block
#2, you'll need to do an onInit that initializes that array

onInit from Bonus
JASS:
    //! i     local begininit = "private module I\nprivate static method onInit takes nothing returns nothing\n"
    //! i         local preloadif = "static if PRELOAD then\n"
    //! i         local preloadend = "endif\n"
    //! i         local preload = "local unit u\nset UnitIndexer.enabled=false\nset u = CreateUnit(Player(14),'hpea',0,0,0)\n" .. getpreload(abilities) .. "call RemoveUnit(u)\nset u = null\nset UnitIndexer.enabled=true\n"
    //! i         local index = "call RegisterUnitIndexEvent(Condition(function thistype.index), UnitIndexer.INDEX)\n"
    //! i         local deindex = "call RegisterUnitIndexEvent(Condition(function thistype.deindex), UnitIndexer.DEINDEX)\n"
    //! i         local objectids = getobjectids(abilities)   --> actual object ids of abilities for UnitAddAbility
    //! i         local powermaxes = getpowermax(abilities)   --> max power (from config)
    //! i         local powersets = getpowerset(abilities)    --> power set: 1, 2, 4, 8, -16 etc
    //! i         local israngeds = getisranged(abilities)    --> is ability ranged or not
    //! i     local endinit = "endmethod\nendmodule\n"

Struct containing above module
JASS:
    //! i     local beginstruct = "private struct O extends array\n"
    //! i         local beginindex = "private static method index takes nothing returns boolean\n"
    //! i             local indexcode =
    //! i             [[set cb[GetIndexedUnitId()] = Table.create()]] .. "\n"
    //! i         local endindex = "return false\nendmethod\n"
    //! i         local begindeindex = "private static method deindex takes nothing returns boolean\n"
    //! i             local deindexcode =
    //! i             [[call cb[GetIndexedUnitId()].destroy()]] .. "\n"
    //! i         local enddeindex = "return false\nendmethod\n"
    //! i     local endstruct = "implement I\nendstruct\n"

Specific line of code that implements the module
//! i local endstruct = "implement I\nendstruct\n"

And here are my globals from Bonus
JASS:
    //! i     local beginglobals = "globals\n"
    //! i         local powermax = "private integer array " .. POWER_MAX .. "\n"
    //! i         local powerset = "private integer array " .. POWER_SET .. "\n"
    //! i         local binarypower = "private integer array " .. BINARY_POWER .. "\n"
    //! i         local objectid = "private integer array " .. OBJECT_ID .. "\n"
    //! i         local isranged = "private boolean array " .. IS_RANGED .. "\n"
    //! i         local currentbonus = "private Table array " .. CURRENT_BONUS .. "\n"
    //! i         local abilityids = getabilityids(abilities)
    //! i     local endglobals = "endglobals\n"


The Lua script that runs all of the above (looks like a main function)
JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i dofile("GetVarObject")
//! i dofile("BonusAbility")
//! i dofile("BonusJASS")
//! runtextmacro BONUS_CREATE_BONUSES()
//! i local jass = BonusJASS.get(abilities)
//! i writejass("BONUS",jass)
//! i updateobjects()
//! endexternalblock

Where the JASS code is retrieved (calls the function that generates all of the JASS code I showed above)
//! i local jass = BonusJASS.get(abilities)

Where the JASS code is written
//! i writejass("BONUS",jass)

Updating the object table
//! i updateobjects()

and finally, notice here that I'm not importing the constants (last arg is whether to import as a constant or not). I am putting all of the ids into arrays, so no need for the constants.
getvarobject(abilityId, name, false)


The Lua code in Bonus is really excellent code. I actually did it in 3 different files and then ported it over to vjass by surrounding those files with writelua statements ; ).
 
This is quite a fine resource.

It would be cooler if it used powers of 2 instead of powers of 10 though.

This way, you could shorten that damage modifying function so that 'times' would always be only 1.

JASS:
local integer i = 30 // (2^(i+1) - 1) is the max bonus you want to support, and 2^31 - 1 is the max int
local integer a = 3252 // how much do you want to give
loop
    if a >= powersOfTwo[i] then
        call GiveBonus(unit, powersOfTwo[i])
        set a = a - powersOfTwo[i]
    endif
    exitwhen a == 0
    set i = i - 1
endloop

This, of course, is only taking into account positive bonuses.
Negative ones would be handled in a similar fashion. You would just need to use an array of negative powers of 2 =o
 
Last edited:
Level 2
Joined
Aug 16, 2012
Messages
15
The lua framework stuff is my project for tomorrow, sorry for the delay.

It would be cooler if it used powers of 2 instead of powers of 10 though.

Binary would mean more item abilities. I suspect (but have not confirmed) that the loop to add a bonus multiple times is also faster than the one to go to the next bonus and add it, meaning that having it add multiple times and not be binary is a benefit. I have a nice formula somewhere for balancing the amount of objects/ability levels with the maximum number of additions you want it to do given the highest value you intend to make a unit go. It's nice for this and my own SetUnitMaxState stuff.

does this also take into account that permanent damage bonus is lost on transformation abilities such as metamorphosis

It does not. Tomorrow I will do some testing and, if necessary, add a warning about that. Without the ability to track what a unit's actual damage is, I can't do much about it losing bonuses on metamorphosis without doing unit indexing. Dealing with metamorphosis messing with damage would then be the task of whoever is writing the unit indexer, but we'll see what happens.
 
Level 2
Joined
Aug 16, 2012
Messages
15
The likelihood that I will add a version using the Lua framework is dwindling rapidly. The framework is not well enough documented for me to work through it. Here is what I have:

JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$

    //! runtextmacro LUA_FILE_HEADER()
    //! i dofile("GetVarObject")
    
    //! i local jass = ""
    
    //! i local i = 0
    //! i local val = 0
    //! i while (i < 10) do
    
        //! i if (i < 5) then
            //! i val = 10 ^ i
        //! i end
        //! i if (i > 4) then
            //! i val = -1 * 10 ^ (i - 5)
        //! i end
        
        //! i local abilId = getvarobject ("AIaa", "abilities", "", false)
        //! i setobjecttype ("abilities")
        //! i createobject ("AIaa", abilId)
        //! i makechange (current, "ansf", "+" .. val)
        //! i makechange (current, "anam", "Damage Modifier")
        //! i makechange (current, "Iaa1", 1, val)
        //! i makechange (current, "acat", "")
        
        //! i local itemId = getvarobject ("tdex", "items", "", false)
        //! i setobjecttype ("items")
        //! i createobject ("tdex", itemId)
        //! i makechange (current, "unsf", "+" .. val)
        //! i makechange (current, "unam", "Damage Modifier")
        //! i makechange (current, "iabi", abilId)
        //! i makechange (current, "ifil", "Doodads\Terrain\LOSBlocker\IntentionallyLeftBlank.mdl")
        //! i makechange (current, "ipow", 0)
        
        //! i jass = jass .. "set dm[" .. i .. "] = CreateItem ('" .. itemId .. "', 0, 0)\n" 
        //! i i = i + 1
    //! i end
    
    //! textmacro SetUnitDamage
        //! i do
            //! i writejass ("SetUnitDamage", jass)
        //! i end
    //! endtextmacro
    //! i updateobjects ()
    
//! endexternalblock

This is the corresponding change in my jass:

JASS:
//! runtextmacro SetUnitDamage ()
loop
    exitwhen i == 10
    call SetItemVisible (dm[i], false)
    set i = i + 1
endloop

Here are my problems:
1. I don't know where the jass is naturally written to or how to edit it once it is written using the lua.

2. I seem to have written the jass into the header file but cannot access it in the editor to delete the jass (which includes many statements outside of a function).

3. Because I cannot delete the faulty jass, I cannot run external commands and cannot actually test whether not the above lua and code would actually work.

4. All of the generated items have the same id of 'A!!!' (or appear to in the generated jass) and I have no means of fixing this because of the above problems. (This problem is the most difficult to deal with because I'm reasonably sure that GetVarObject usually works.)

5. There are no wc3-based tutorials available on textmacros or how they should be used (if I'm incorrect in this, a link would be greatly appreciated), so even if I could get some lua working, I would likely face hours of trying to get that to work only to post another frustrated reply here.

I apologize if I am doing something abundantly silly and I appreciate the advice I have already been given, but without any further help with my above problems, this resource will not be updated to use the lua framework.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
Jass is written to your jass folder in wc3 directory
Lua is written to grimext/luadir in jassnewgenpack

You can overwrite jass with another writejass command. You can also delete it.


Also, see the Learn vJASS Easily with Video Tuts link in my post. It contains a video on textmacros.

edit
You aren't currently writing any jass.. you write a null variable and create an empty textmacro that contains the code to write the null variable, lol..

JASS:
    //! textmacro SetUnitDamage
        //! i do
            //! i writejass ("SetUnitDamage", jass)
        //! i end
    //! endtextmacro

So when you do this
//! runtextmacro SetUnitDamage()

It writes this
JASS:
        //! i do
            //! i writejass ("SetUnitDamage", jass)
        //! i end

Look at the demo in the LUA_FILE_HEADER thread. It actually writes jass ;p.

You can cnp the writejass command to your map and then run the map and see the hello world. You can then delete the jass.


If you look at my code in the above post, I am actually setting tons and tons of variables to strings, and those strings are vjass code. I then put all of those strings into 1 variable with concats. After that, I pass that 1 variable into writejass.

You are making a textmacro and passing a variable that you don't declare anywhere into writejass.

edit
Here is a very simple demo from LUA_FILE_HEADER thread

JASS:
        //! i writejass("MyScript", [[
            //! i struct Tester1 extends array
                //! i private static method onInit takes nothing returns nothing
                    //! i call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "hello world")
                //! i endmethod
            //! i endstruct
        //! i ]])

//! i deletelua("MyScript")


Another with your textmacro in it
JASS:
//! i writejass("MyScript", [[
//! i     //! textmacro SetUnitDamage
//! i     //! endtextmacro
//! i ]])
 
Level 2
Joined
Aug 16, 2012
Messages
15
The following does not say "hello world" for me when I start the map.:

JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
//! runtextmacro LUA_FILE_HEADER()
//! i writejass("MyScript", [[
    //! i struct Tester1 extends array
        //! i private static method onInit takes nothing returns nothing
            //! i call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "hello world")
        //! i endmethod
    //! i endstruct
//! i ]])
//! endexternalblock

I am again, probably doing something that doesn't work, but some help would be nice.


you write a null variable

I did set it to a value in the lua block.

JASS:
//! i jass = jass .. "set dm[" .. i .. "] = CreateItem ('" .. itemId .. "', 0, 0)\n"

Does the variable not transfer to the textmacro?


It writes this

JASS:
//! i do
    //! i writejass ("SetUnitDamage", jass)
//! i end


I tried this:

JASS:
   //! textmacro SetUnitDamage
        set dm[0] = CreateItem ('A!!!', 0, 0)
    //! endtextmacro

and it did not write anything to the onInit call. Does it write each time after compiling and remove it when opened in the editor (I saved, closed, and reopened the map just in case).

If perhaps I am being unclear, this is my desired result:

JASS:
 private module mod
        private static method onInit takes nothing returns nothing
            local integer i = 0
            local integer x = 1
            // This is where the textmacro was called.
            set dm[0] = CreateItem (/*generated id for the first object*/, 0, 0)
            set dm[1] = CreateItem (/*generated id for the second object*/, 0, 0)
            //......
            set dm[9] = CreateItem (/*generated id for the tenth object*/, 0, 0)
            loop
                exitwhen i == 10
                call SetItemVisible (dm[i], false)
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i == 5
                set dmVal[i] = x
                set dmVal[i + 5] = -x
                set i = i + 1
                set x = x * 10
            endloop
        endmethod
    endmodule

Currently, as you stated I do not write jass. I found the directory where the jass from your example was written but it is not in the map and does not seem to function there.

A fundamental problem still remains:

4. All of the generated items have the same id of 'A!!!' (or appear to in the generated jass) and I have no means of fixing this because of the above problems. (This problem is the most difficult to deal with because I'm reasonably sure that GetVarObject usually works.)

Thanks again for your help, but I'm still having trouble.
 
Level 2
Joined
Aug 16, 2012
Messages
15
I have successfully written the jass to a file and imported it. Because of load order, I decided to simply write the whole module initializer so that it could be called properly and it works. However, there must be a better way of doing it than writing the module initalizer and using a textmacro because like that, you have to put in a blank module initializer just to let it compile far enough to actually generate the jass with the real module initializer in it. Well, here's the lua:

JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$

    //! runtextmacro LUA_FILE_HEADER()
    //! i dofile("GetVarObject")
    
    //! i local jass = "module SetUnitDamageI\n"
    //! i jass = jass ..    "private static method onInit takes nothing returns nothing\n"
    //! i jass = jass ..        "local integer i = 0\n"
    //! i jass = jass ..        "local integer x = 1\n"
    
    //! i local i = 0
    //! i local val = 0
    //! i local abilId = ""
    //! i local itemId = ""
    //! i while (i < 10) do
    
        //! i if (i < 5) then
            //! i val = 10 ^ i
        //! i end
        //! i if (i > 4) then
            //! i val = -1 * 10 ^ (i - 5)
        //! i end
        
        //! i abilId = getobjectid ("AIaa", "abilities")
        //! i setobjecttype ("abilities")
        //! i createobject ("AIaa", abilId)
        //! i makechange (current, "ansf", "+" .. val)
        //! i makechange (current, "anam", "Damage Modifier")
        //! i makechange (current, "Iaa1", 1, val)
        //! i makechange (current, "acat", "")
        
        //! i itemId = getobjectid ("tdex", "items")
        //! i setobjecttype ("items")
        //! i createobject ("tdex", itemId)
        //! i makechange (current, "unsf", "+" .. val)
        //! i makechange (current, "unam", "Damage Modifier")
        //! i makechange (current, "iabi", abilId)
        //! i makechange (current, "ifil", "Doodads\Terrain\LOSBlocker\IntentionallyLeftBlank.mdl")
        //! i makechange (current, "ipow", 0)
        
        //! i jass = jass .. "set SetUnitDamage___dm[" .. i .. "] = CreateItem ('" .. itemId .. "', 0, 0)\n" 
        //! i i = i + 1
    //! i end
    
    //! i jass = jass ..            "loop\n"
    //! i jass = jass ..                "exitwhen i == 10\n"
    //! i jass = jass ..                "call SetItemVisible (SetUnitDamage___dm[i], false)\n"
    //! i jass = jass ..                "set i = i + 1\n"
    //! i jass = jass ..            "endloop\n"
    //! i jass = jass ..            "set i = 0\n"
    //! i jass = jass ..            "loop\n"
    //! i jass = jass ..                "exitwhen i == 5\n"
    //! i jass = jass ..                "set SetUnitDamage___dmVal[i] = x\n"
    //! i jass = jass ..                "set SetUnitDamage___dmVal[i + 5] = -x\n"
    //! i jass = jass ..                "set i = i + 1\n"
    //! i jass = jass ..                "set x = x * 10\n"
    //! i jass = jass ..            "endloop\n"
    //! i jass = jass ..        "endmethod\n"
    //! i jass = jass ..    "endmodule\n"
    
    //! i writejass ("SetUnitDamage", jass)
    
    //! i updateobjects ()
//! endexternalblock

//! textmacro SET_UNIT_DAMAGE_INIT
    implement SetUnitDamageI
//! endtextmacro

and here is the corresponding change in the jass:

JASS:
    private struct init
        //! runtextmacro SET_UNIT_DAMAGE_INIT ()
    endstruct

A tip on a better way to do this would be much appreciated. Thanks.

edit: actually, it does not like me hacking into the library by just adding the prefix, so I need a better way to do this, or I have to make the items public.

edit: I tested metamorphosis. It will remove any damage modifications when the unit undergoes metamorphosis and these damage modifications will remain gone even when the unit changes back again. My advice: never use metamorphosis. It's just a pain, it really is.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
For me it's mostly about the visual benefit. You can manipulate the
standard damage display without having the green +damage ( 5 - 9 +10) displayed. Instead it will be ( 15 - 19 ).

Sadly it can't copy any scenario from a DDS, as DDS allows you to manipulate minimum
and maximum damage separately if wanted, while this system manipulates both equally. ( +10 in my example )

Still a good resource in my opinion. It just required changes in what you already mentioned in your previous posts.
 
Level 2
Joined
Aug 16, 2012
Messages
15
Individual configurability will come at the cost of easy scalability. Previously simple loops now become blocks of code which use the IDs. I could have an array of ids and go back to using arrays, but 5 magnitudes of modifiers seems sufficient. I have added extra comments to help users understand what can be changed and what cannot. Unless anyone has serious misgivings about not making the number of magnitudes configurable, this is what I intend to update the main post reflecting. (I omitted unchanged areas, namely in the vJASS.)

JASS:
//! externalblock extension=lua ObjectMerger $FILENAME$
    //! i function createdamagemodifier (abilityId, itemId, i)
        //! i setobjecttype ("abilities")
        //! i createobject ("AIaa", abilityId)
        //! i makechange (current, "ansf", "+" .. i)
        //! i makechange (current, "anam", "Damage Modifier")
        //! i makechange (current, "Iaa1", 1, i)
        //! i makechange (current, "acat", "")
        //! i setobjecttype ("items")
        //! i createobject ("tdex", itemId)
        //! i makechange (current, "unsf", "+" .. i)
        //! i makechange (current, "unam", "Damage Modifier")
        //! i makechange (current, "iabi", abilityId)
        //! i makechange (current, "ifil", "Doodads\Terrain\LOSBlocker\IntentionallyLeftBlank.mdl")
        //! i makechange (current, "ipow", 0)
    //! i end

    // You may change the ids below (and may need to in case of conflict),
    // however they must match the initialized ids in the onInit vJASS code
    // as well. The values may not be changed.
    //! i createdamagemodifier("abd0", "idb0", 1)
    //! i createdamagemodifier("abd1", "idb1", 10)
    //! i createdamagemodifier("abd2", "idb2", 100)
    //! i createdamagemodifier("abd3", "idb3", 1000)
    //! i createdamagemodifier("abd4", "idb4", 10000)
    //! i createdamagemodifier("abd5", "idb5", -1)
    //! i createdamagemodifier("abd6", "idb6", -10)
    //! i createdamagemodifier("abd7", "idb7", -100)
    //! i createdamagemodifier("abd8", "idb8", -1000)
    //! i createdamagemodifier("abd9", "idb9", -10000)
    
//! endexternalblock

I also plan to change the relevant part of the vJASS to the following:

JASS:
    private module mod
        private static method onInit takes nothing returns nothing
            local integer i = 0
            local integer x = 1

            // NOTE: Item ids below must reflect the Lua ids, including order.
            set dm[0] = CreateItem ('idb0', 0, 0)
            set dm[1] = CreateItem ('idb1', 0, 0)
            set dm[2] = CreateItem ('idb2', 0, 0)
            set dm[3] = CreateItem ('idb3', 0, 0)
            set dm[4] = CreateItem ('idb4', 0, 0)
            set dm[5] = CreateItem ('idb5', 0, 0)
            set dm[6] = CreateItem ('idb6', 0, 0)
            set dm[7] = CreateItem ('idb7', 0, 0)
            set dm[8] = CreateItem ('idb8', 0, 0)
            set dm[9] = CreateItem ('idb9', 0, 0)

            loop
                exitwhen i == 5
                call SetItemVisible (dm[i], false)
                call SetItemVisible (dm[i + 5], false)
                set dmVal[i] = x
                set dmVal[i + 5] = -x
                set i = i + 1
                set x = x * 10
            endloop
        endmethod
    endmodule

This isn't as pretty as before, but it has these properties:
1. Those who wish to use it out of the box as before, may.
2. Those who wish to change ids to avoid conflict, may.

A quick question: will it change anything if I make the private struct extend array or not? I know it doesn't matter in terms of run time, but will vJASS make methods for it if I don't explicitly have it extend array?
 
Level 26
Joined
Mar 19, 2008
Messages
3,140
Another prime example why new RegisterPlayerUnitEvent should be used.
JASS:
static if LIBRARY_RegisterPlayerUnitEvent then
    call DisableTrigger (GetPlayerUnitEventTrigger (EVENT_PLAYER_UNIT_DROP_ITEM))
endif
Doesn't work - or meaby it's better to say: is inaccurate.

Even single usage of call RegisterPlayerUnitEventForPlayer(EVENT_PLAYER_UNIT_DROP_ITEM, <code>, Player(<id>)) anywhere inside map script will create holes (triggers) which won't be able to be retrieved, thus the whole idea of preventing specific events from firing goes to hell.
 
Top