• 🏆 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!

Small Code Snippets

Here's a better version of it:

JASS:
function CreateUnitWithDifferentModel takes player p, integer unitId1, integer unitId2, real x, real y, real facing returns /*Desync if there is another different object editor setting other than model :O*/ unit
    if GetLocalPlayer() == p then
        set unitId1 = unitId2
    endif
    return CreateUnit(p, unitId1, x, y, facing)
endfunction

Parameters can be modified after all :p
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Because original name is

function CreateUnitWithDifferentModel takes .... returns desync :grin:

I will test it with different situations because what Magtheridon said scares me, nobody want his game to explode like :goblin_boom:
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
I bet Mag (can i call you like it Magtheridon96 ?) just wanted to say that you have to really know what you are doing, like when you are enumerating units, indeed player X computer will have 1 unit of type A and 0 of type B, while for player Y computer it will be the opposite.
Pretty hard for GUI users to figure what will desync or not and how to handle it, i think.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
Sorry for late response, but I think you are wrong.

hfoo = normal footman
h002 = footman with capotain model

I created two h002 in my pc, and two hfoo in his pc.One unit is mine and other one belongs to him.While they were fightning, I runned this trigger.

  • Show Data
    • Events
      • Player - Player 1 (Red) types a chat message containing -showdata as An exact match
      • Player - Player 2 (Blue) types a chat message containing -showdata as An exact match
      • Player - Player 1 (Red) types a chat message containing showdata as An exact match
      • Player - Player 2 (Blue) types a chat message containing showdata as An exact match
    • Conditions
    • Actions
      • Actions
    • -------- Displaying normal footman(hfoo) --------
    • Game - Display to (All players) the text: (There are + ((String((Number of units in (Units of type Footman)))) + normal footmans in game.))
    • -------- Displaying footman with different model(h002) --------
    • Game - Display to (All players) the text: (There are + ((String((Number of units in (Units of type Footman)))) + footman with captain model in game.))
      • Unit Group - Pick every unit in (Units in (Playable map area) matching (((Matching unit) is alive) Equal to True)) and do (Actions)
        • Loop - Actions
          • Game - Display to (All players) the text: (Id = + ((String((Unit-type of (Picked unit)))) + (|nLife = + (String((Life of (Picked unit)))))))
Message displayed to me:
There are 0 normal footmans in game.
There are 2 footman with captain model in game.
Id = custom_h002|nLife = 258.785
Id = custom_h002|nLife = 257.930

Message displayed to him:
There are 2 normal footmans in game.
There are 0 footman wtih captain model in game.
Id = footman|nLife = 258.785
Id = footman|nLife = 257.930

Then one of the units is dead and this trigger runned well, no desync.

JASS:
function Trig_Test_3_If_Actions takes nothing returns nothing
    local integer id = GetUnitTypeId(GetDyingUnit())
    if id == 'hfoo' then
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Normal footman dead")
    elseif id == 'h002' then
        call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Footman with captain model dead")
    endif
endfunction

Message displayed to him:
-Normal footman dead
Message displayed to me:
-Footman with captain model dead
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
Test it with a different amount of unit picks, so the height of loop-block runs won't be the same.

Well, why do you expect the death event to check the unit type?

Of course
JASS:
if (GetUnitTypeId(<asyncUnit>) == 'hfoo') then
    call BJDebugMsg("bla")
endif
does not immediately cause a split but Magtheridon96 probably meant that you can do more dangerous things. For example,

  • Events
    • Unit - A unit Dies
  • Conditions
    • (Unit-type of (Triggering unit)) Gleich Footman
  • Actions
    • Game - Display to (All players) the text: bla
should be bad due to having a different outcome in trigger conditions block, therefore the trigger actions get run selectively, which again asynchronously increases the execution/handle counter etc.
 
Level 12
Joined
Feb 22, 2010
Messages
1,115
@Waterknight

  • Test 3 Footman Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to Footman
    • Actions
      • Game - Display to (All players) the text: A footman is dead.
  • Test 3 Other Footman Dies
    • Events
      • Unit - A unit Dies
    • Conditions
      • (Unit-type of (Dying unit)) Equal to Footman
    • Actions
      • Game - Display to (All players) the text: A footman with captain model is dead.
He saw "A footman is dead" message when I saw the other one.Also they are same units just with different model, no need to seperate triggers - trigger conditions.

I also runned this trigger.

  • Test 3 Display
    • Events
      • Player - Player 1 (Red) skips a cinematic sequence
    • Conditions
    • Actions
      • Game - Display to (All players) the text: (String((Evaluation count of Test 3 Footman Dies <gen>)))
      • Game - Display to (All players) the text: (String((Execution count of Test 3 Footman Dies <gen>)))
      • Game - Display to (All players) the text: (String((Evaluation count of Test 3 Other Footman Dies <gen>)))
      • Game - Display to (All players) the text: (String((Execution count of Test 3 Other Footman Dies <gen>)))
Me: 1 0 1 1
Him: 1 1 1 0
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
JASS:
library SaveHeroStats /*
*************************************************************************************
*
*   Saves hero strength, agility, and intelligence
*
*************************************************************************************
*
*   */uses/*
*
*       */ NumberStack /*       hiveworkshop.com/forums/1993458-post521.html
*
************************************************************************************
*
*
*   SETTINGS
*/
globals
    /*************************************************************************************
    *
    *                                   ALL_STATS_SPENT
    *
    *   Every stat point is spent at every level
    *
    *   If every stat point is spent at every level, the strength can be calculated from
    *   the remaining stat points. If the stat points are not spent, the strength has
    *   to be stored in the code.
    *
    *   Stat points spent at every level means a smaller code.
    *
    *************************************************************************************/
    private constant boolean ALL_STATS_SPENT = true
    
    /*************************************************************************************
    *
    *                                   SPLIT_BONUSES
    *
    *   Makes bonus specific to each stat rather than shared among all stats.
    *
    *   If SPLIT_BONUSES is true and 1024 is passed in as a max bonus, 1024 will be the cap
    *   for each individual stat.
    *
    *   If SPLIT_BONUSES is false and 1024 is passed in as a max bonus, 1024 will be the cap
    *   for all three stats put together. If intelligence is 500, then 524 bonus stats
    *   will be left.
    *
    *************************************************************************************/
    private constant boolean SPLIT_BONUSES = false
endglobals
/*
************************************************************************************
*   function SaveHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
*   function LoadHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
*
************************************************************************************/
    function SaveHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
        local unit u
        
        static if SPLIT_BONUSES then
            local integer bonusStr
        elseif not ALL_STATS_SPENT then
            local integer bonusStr
        endif
        
        local integer bonusAgi
        local integer bonusInt
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        static if SPLIT_BONUSES then
            set bonusStr = GetHeroStr(whichUnit, false)-GetHeroStr(u, false)
        elseif not ALL_STATS_SPENT then
            set bonusStr = GetHeroStr(whichUnit, false)-GetHeroStr(u, false)
        endif
        set bonusAgi = GetHeroAgi(whichUnit, false)-GetHeroAgi(u, false)
        set bonusInt = GetHeroInt(whichUnit, false)-GetHeroInt(u, false)
        
        static if SPLIT_BONUSES then
            call stack.push(bonusStr, maxBonusForLevel)
            call stack.push(bonusAgi, maxBonusForLevel)
            call stack.push(bonusInt, maxBonusForLevel)
        else
            static if not ALL_STATS_SPENT then
                call stack.push(bonusStr, maxBonusForLevel-bonusAgi-bonusInt)
            endif
            //else
                //bonus strength is the remaining bonus points
            
            call stack.push(bonusAgi, maxBonusForLevel-bonusInt)
            call stack.push(bonusInt, maxBonusForLevel)
        endif
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
    
    function LoadHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
        local unit u
        
        local integer bonusStr
        local integer bonusAgi
        local integer bonusInt
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        static if SPLIT_BONUSES then
            set bonusInt = stack.pop(maxBonusForLevel)
            set bonusAgi = stack.pop(maxBonusForLevel)
            set bonusStr = stack.pop(maxBonusForLevel)
        else
            set bonusInt = stack.pop(maxBonusForLevel)
            set bonusAgi = stack.pop(maxBonusForLevel-bonusInt)
            
            static if ALL_STATS_SPENT then
                set bonusStr = maxBonusForLevel-bonusAgi-bonusInt
            else
                set bonusStr = stack.pop(maxBonusForLevel-bonusAgi-bonusInt)
            endif
        endif
        
        call SetHeroInt(whichUnit, bonusInt+GetHeroInt(u, false), true)
        call SetHeroAgi(whichUnit, bonusAgi+GetHeroAgi(u, false), true)
        call SetHeroStr(whichUnit, bonusStr+GetHeroStr(u, false), true)
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
endlibrary
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
JASS:
library SaveHeroAbilities /*
*************************************************************************************
*
*   Saves learnable hero abilities.
*
*************************************************************************************
*
*   */uses/*
*
*       */ NumberStack /*       hiveworkshop.com/forums/1993458-post521.html
*       */ Catalog /*           hiveworkshop.com/forums/jass-functions-413/snippet-catalog-192452/
*       */ Table /*             hiveworkshop.com/forums/jass-functions-413/snippet-new-table-188084/
*
************************************************************************************
*
*   function SaveHeroAbilities takes NumberStack stack, unit whichUnit, Catalog abilityCatalog returns nothing
*   function LoadHeroAbilities takes NumberStack stack, unit whichUnit, Catalog abilityCatalog returns nothing
*
*   abilityCatalog
*   --------------------------------
*
*       abilityCatalog should be specific to the hero and contain all of the hero's learnable abilities.
*
************************************************************************************/
    private function GetMaxAbilityLevel takes unit whichUnit, integer abilityId returns integer
        local integer maxLevel = 0
        
        loop
            call SelectHeroSkill(whichUnit, abilityId)
            exitwhen maxLevel == GetUnitAbilityLevel(whichUnit, abilityId)
            set maxLevel = GetUnitAbilityLevel(whichUnit, abilityId)
        endloop
        
        return maxLevel
    endfunction
    function SaveHeroAbilities takes NumberStack stack, unit whichUnit, Catalog abilityCatalog returns nothing
        local unit u
        local integer i
        local integer abilityId
        local integer maxLevel
        local integer array levels
        local integer array maxLevels
        local integer abilCount = 0
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        //create a dummy unit for retrieving maximum ability levels
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        //for each learnable ability of unit
        set i = abilityCatalog.count
        loop
            exitwhen 0 == i or 0 == GetHeroSkillPoints(u)
            
            set abilityId = abilityCatalog.raw(i)               //retrieve ability
            set maxLevel = GetMaxAbilityLevel(u, abilityId)     //retrieve max level
            
            //if the max level isn't 0, store it for saving
            if (0 < maxLevel) then
                //store for saving so that loader can load in same order as the saver saved (save
                //in reverse order so loader and load in normal order).
                set maxLevels[abilCount] = maxLevel
                set levels[abilCount] = GetUnitAbilityLevel(whichUnit, abilityId)
                
                //give skill points not spent by actual unit back to dummy unit
                call UnitModifySkillPoints(u, maxLevel-levels[abilCount])
                
                set abilCount = abilCount + 1
            endif
            
            set i = i - 1
        endloop
        
        //loop through all stored abilities *backwards* so that the loader
        //can loop in same order as first loop
        loop
            exitwhen 0 == abilCount
            
            set abilCount = abilCount - 1
            
            call stack.push(levels[abilCount], maxLevels[abilCount])
        endloop
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
    
    function LoadHeroAbilities takes NumberStack stack, unit whichUnit, Catalog abilityCatalog returns nothing
        local unit u
        local integer i
        local integer abilityId
        local integer level
        local integer maxLevel
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        set i = abilityCatalog.count
        loop
            exitwhen 0 == i or 0 == GetHeroSkillPoints(u)
            
            set abilityId = abilityCatalog.raw(i)
            set maxLevel = GetMaxAbilityLevel(u, abilityId)
            
            if (0 < maxLevel) then
                set level = stack.pop(maxLevel)
                
                call UnitModifySkillPoints(u, maxLevel-level)
                
                if (0 < level) then
                    call SelectHeroSkill(whichUnit, abilityId)
                    
                    if (1 < level) then
                        call SetUnitAbilityLevel(whichUnit, abilityId, level)
                        
                        call UnitModifySkillPoints(whichUnit, -level+1)
                    endif
                endif
            endif
            
            set i = i - 1
        endloop
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
endlibrary
 
Last edited:
JASS:
library TimerOne
    private module Init
        private static method onInit takes nothing returns nothing
            call TimerStart(CreateTimer(),1.0,true,function e)
        endmethod
    endmodule
    struct TimerOne extends array
        private static trigger t = CreateTrigger()
        private static method e takes nothing returns nothing
            call TriggerEvaluate(t)
        endmethod
        static method register takes boolexpr b returns nothing
            call TriggerAddCondition(t,b)
        endmethod
        implement Init
    endstruct
endlibrary

I wrote this because I found that the most common timer I'm using in any of my maps is one that expires every second.
I used this system to replace about 20 timer registerations in my map :p

It may seem useful if you have countless 1-second periodic timers :p
 
The point is to have less handles and less timer expirations at once.
Having 20 timers expire at once is much slower than having 20 timers expire at different times.
This was the point of merging timers in Nestharus' TimerTools.
1 timer expiration per second should be much faster than having 20 seperate timers expiring at once.
Thus, my snippet proves to be useful.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
The only problem with this "1 second timer struct" is that all instances run at the same interval. In the end the issues will be like this:

1. Timer reaches 1.0 second, evaluates.

2. 0.5 second later, I want to add something, this will be fired 0.5 second later (issue).
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Still WaterKnight caught the big point, all will be fired at the same time. Be happy that you haven't registered unit orders...

What (do you want to mean) ?!

Also i suppose the first timeout innacury doesn't matter in some cases, like multiboard/leaderboard update.
Even if in this case i would update it in real time, each time a value changes.
 
1 timer running + 1 evaluation vs 1 timer - FAIL
1 timer running + 1 evaluation vs 2 timers - FAIL
1 timer running + 1 evaluation vs 10 timers - WIN!
1 timer running + 1 evaluation vs 1000 timers - SO MUCH FUCKING WIN!

It all depends on how many timers you have.
If you have a very small amount of timers (1-5 maybe), don't use my snippet.
If you have many timers (something like 12 or maybe 15 or maybe even 30), my snippet could increase performance and reduce what most people know as "Lag Spikes"
 
Level 26
Joined
Aug 18, 2009
Messages
4,097
I'm using it for lots of stuff:
- Multiboard Update
- Leaderboard Update
- Game Time Update
- Streak System Multikills Update
- Spawn System
- AI script Data Buffer
- AI script Core
etc..

AI maybe, Spawn System?, else these are not instanceable and therefore extraneous.

Trust me, you'll have TONS of 1-second timeout timers in a map ;)

As said, at the moment only auras, board update (there is not an event for everything and it would be less efficient considering values change more than one time per second) and AI. Still would like to specify the update intervals individually.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
What (do you want to mean) ?!

All bexprs will fire at the same time regardless what time they were registered, thus making all calls at once instead of spread out. (Still I guess there will not be that many to create laggspikes but...)

Anyway I don't say it's a bad snippet, it definitely has its uses. Would be cool to replace all 1. intervals in spells with this although would look like a mess instead of a snippet (and I don't remember any RemoveTriggerConditon native only the clear native).
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
TriggerRemoveCondition exists, but removing a condition while it's evaluated stops the whole trigger evaluation.
For example if you have in this order : cond1 ; cond2 ; cond3 ; cond4.

You remove the triggercondition cond2 while it's evaluated, then cond3 and cond4 will not be evaluated.

I say that just to mean that you have to do more work than intended, in order to remove correctly a condition inside a trigger.

About the spread out it makes sense, since anyway it's unlikely two/X timers would expire at the same moment (or enough close).
So in fact in any real case the X timers solution would even be "lagless", just because it will spread out the tasks.
 
Level 7
Joined
Dec 3, 2006
Messages
339
JASS:
//Use Spinnakers (The one below doesn't work)
    function SetBuildingFacing takes unit u, real a returns nothing
        call UnitRemoveType(u, UNIT_TYPE_STRUCTURE)
        call SetUnitTurnSpeed(u,9999.0)
        call SetUnitFacing(u, a)
        call UnitAddType(u, UNIT_TYPE_STRUCTURE)
    endfunction

Works for setting an building's facing angle. All other methods are pretty much horrid that i've seen. Removing the unit type and then re-adding seems to work wonders though. Well, I guess you could also probably use the root ability but it'd be the same speed really.

Also note; you can make a unit be unturnable for its facing angle with triggers or anything if you add UNIT_TYPE_STRUCTURE for an duration then remove it.
 
Last edited:
Top