Snippet UnitStatePercent

Nestharus

o-o
Reaction score
84
Requirements:
Installation Script
JASS:

//! externalblock extension=lua ObjectMerger $FILENAME$
    //! runtextmacro LUA_FILE_HEADER()
    //! i dofile("GetVarObject")
    
    //! i local id = getvarobject("Hvsh", "units", "UNITS_UNIT_STATE_PERCENT", true)
    
    //! i createobject("Hvsh", id)
    //! i makechange(current, "uabi", "Aloc,Avul")
    //! i makechange(current, "usid", "0")
    //! i makechange(current, "usin", "0")
    //! i makechange(current, "usca", "0")
    
    //! i updateobjects()
//! endexternalblock


JASS:

//========================================================
library UnitStatePercent /* v1.0.1.7
//                                                  by nes
//========================================================
//      UnitStatePercent allows one to deal with Unit States as if they were 
//      represented by percents instead of literal values.  This is very useful
//      for Save/Load.
//
//      */uses/*
        */ WorldBounds      /*  hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
        */ SetHeroLevelX    //  hiveworkshop.com/forums/1994340-post545.html
//
//      FUNCTIONS
//          function GetPercentHeroXP takes unit u returns integer
//          function AddPercentHeroXP takes unit u, integer p returns nothing
//
//          function GetPercentUnitLife takes unit u returns integer
//          function SetPercentUnitLife takes unit u, integer p returns nothing
//
//          function GetPercentUnitMana takes unit u returns integer
//          function SetPercentUnitMana takes unit u, integer p returns nothing
//
//          function GetPercentUnitX takes unit u returns integer
//          function PercentToX takes integer l returns real
//
//          function GetPercentUnitY takes unit u returns integer
//          function PercentToY takes integer l returns real
//
//          function GetPercentUnitFacing takes unit u returns integer
//          function PercentToFacing takes integer f returns real
//
//      CREDITS
//          tooltiperror -
//              For helping with documentation
//
//
    globals
        private real xl=0
        private real yl=0
        private unit h=null
    endglobals
    private module Init
        private static method onInit takes nothing returns nothing
            set xl=WorldBounds.maxX-WorldBounds.minX
            set yl=WorldBounds.maxY-WorldBounds.minY
            set h=CreateUnit(Player(15),UNITS_UNIT_STATE_PERCENT,WorldBounds.maxX,WorldBounds.maxY, 0)
            call SetUnitX(h,WorldBounds.maxX)
            call SetUnitY(h,WorldBounds.maxY)
            call PauseUnit(h,true)
        endmethod
    endmodule
    private struct Inits extends array
        implement Init
    endstruct
    //retrieves how far a hero is into its level as an integer %
    //u refers to the hero
    function GetPercentHeroXP takes unit u returns integer
        local integer l=GetHeroLevel(u)
        local integer m=0
        if (1<l) then
            call SetHeroLevelX(h,l)
            set m=GetHeroXP(h)
        endif
        call SetHeroLevelX(h,l+1)
        set m=(GetHeroXP(u)-m)*100/(GetHeroXP(h)-m)
        call UnitStripHeroLevel(h,l+1)
        return m
    endfunction
    //adds an integer percent xp to a hero
    //u refer to hero, p refers to percent
    //make sure the hero was its original level before adding
        //call SetHeroLevel(u, myLvl, false)
        //call AddPercentHeroXP(u, 30) //pushes the hero 30% into the level
    function AddPercentHeroXP takes unit u,integer p returns nothing
        local integer l
        local integer m=0
        if (0<p) then
            set l=GetHeroLevel(u)
            if (1<l) then
                call SetHeroLevelX(h,l)
                set m=GetHeroXP(h)
            endif
            call SetHeroLevelX(h,l+1)
            call AddHeroXP(u,R2I((GetHeroXP(h)-m)*p/100.),false)
            call UnitStripHeroLevel(h,l+1)
        endif
    endfunction
    //retrieves a unit's current life as a percent
    function GetPercentUnitLife takes unit u returns integer
        return R2I((GetWidgetLife(u)/GetUnitState(u,UNIT_STATE_MAX_LIFE))*100+.5)
    endfunction
    //sets a unit's life given a percent
    function SetPercentUnitLife takes unit u, integer p returns nothing
        call SetWidgetLife(u,p/100.*GetUnitState(u,UNIT_STATE_MAX_LIFE))
    endfunction
    //retrieves a unit's current mana as a percent
    function GetPercentUnitMana takes unit u returns integer
        local real m=GetUnitState(u,UNIT_STATE_MAX_MANA)
        if (0==m) then
            return 0
        endif
        return R2I(GetUnitState(u,UNIT_STATE_MANA)/m*100+.5)
    endfunction
    //sets a unit's mana given a percent
    function SetPercentUnitMana takes unit u, integer p returns nothing
        call SetUnitState(u,UNIT_STATE_MANA,p/100.*GetUnitState(u,UNIT_STATE_MAX_MANA))
    endfunction
    function GetPercentUnitX takes unit u returns integer
        return R2I((GetWidgetX(u)-WorldBounds.minX)/xl*100+.5)
    endfunction
    function PercentToX takes integer l returns real
        return l/100.*xl+WorldBounds.minX
    endfunction
    function GetPercentUnitY takes unit u returns integer
        return R2I((GetWidgetY(u)-WorldBounds.minY)/yl*100+.5)
    endfunction
    function PercentToY takes integer l returns real
        return l/100.*yl+WorldBounds.minY
    endfunction
    function GetPercentUnitFacing takes unit u returns integer
        local integer i=R2I(GetUnitFacing(u)/360.*100+.5)
        if (100==i) then
            return 0
        endif
        return i
    endfunction
    function PercentToFacing takes integer f returns real
        return f/100.*360
    endfunction
endlibrary


Demo
Run this in map to see approximations (very close to actual values!)
JASS:

struct tester extends array
    private static method test takes real x, real y, real facing, real life, real mana, integer level, integer xp returns nothing
        local unit u = CreateUnit(Player(0), 'Hpal', x, y, facing)
        local integer xps
        local integer mp
        local integer lp
        local integer fp
        
        if (level > 1) then
            call SetHeroLevel(u, level, false)
        endif
        call AddHeroXP(u, xp, false)
        call SetWidgetLife(u, life)
        call SetUnitState(u, UNIT_STATE_MANA, mana)
        
        //done above as setting levels will change the percents
        set lp = GetPercentUnitLife(u)
        set mp = GetPercentUnitMana(u)
        set fp = GetPercentUnitFacing(u)
        set xps = GetPercentHeroXP(u)
        set xp = GetHeroXP(u)
        
        call UnitStripHeroLevel(u, level)
        call SetHeroXP(u, 0, false)
        
        call SetUnitX(u, PercentToX(GetPercentUnitX(u)))
        call SetUnitY(u, PercentToY(GetPercentUnitY(u)))
        if (level > 1) then
            call UnitStripHeroLevel(u, 1)
            call SetHeroLevel(u, level, false)
        endif
        call AddPercentHeroXP(u, xps)
        call SetPercentUnitLife(u, lp)
        call SetPercentUnitMana(u, mp)
        call SetUnitFacing(u, PercentToFacing(fp))
        
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "x: " + R2S(x) + "==" + R2S(GetWidgetX(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "y: " + R2S(y) + "==" + R2S(GetWidgetY(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "facing: " + R2S(facing) + "==" + R2S(GetUnitFacing(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "life: " + R2S(life) + "==" + R2S(GetWidgetLife(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "mana: " + R2S(mana) + "==" + R2S(GetUnitState(u, UNIT_STATE_MANA)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "level: " + I2S(level) + "==" + I2S(GetHeroLevel(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "xp: " + I2S(xp) + "==" + I2S(GetHeroXP(u)))
        set u = null
    endmethod
    private static method onInit takes nothing returns nothing
        call test(2691,WorldBounds.minY,124,450,0,2,50)
    endmethod
endstruct
 

tooltiperror

Super Moderator
Reaction score
231
brb, writing a code documentation tutorial.
 

Nestharus

o-o
Reaction score
84
You go do that ;P.

As for me, I've worked out every single bug in this script. Was accidentally setting UNIT_STATE_MAX_MAX in this like a dummy for the setting unit mana.

Also fixed all of the divisions by 0 and I fixed level 1 bug for SetHeroLevel.

Yup, fixed it all >: ). Tested with my Encoder demo map ^)^.
 

Jesus4Lyf

Good Idea™
Reaction score
397
I've been restraining myself from commenting on this for a long time, but you got me:
wtf is that..?

Ahh, yes! A GetPercentUnitX function! Just what I always wanted! Now I uh.. can um..
.. uhh...

Edit:
By the way, you're missing SetUnitOwnerPercent. So we can set the unit's ownership to whatever player happens to be 50% of all players playing, and then another version including computers, and another version including passive players! You could combine this with a player manager system. You should also be able to return a unit as a BigInt, storing all its values in one long integer so you can load save it, then you could return a % of that string! Just like, GetUnitPercent to return a percentage of the unit... :thup:
 

tooltiperror

Super Moderator
Reaction score
231
Please, for the rest of us:

//========

A nice little snippet for dealing with unit states as percentages rather than direct coordinates/integers. Very accurate and useful for save/load systems and the like. Complete safety from dividing by zero, also.

Requirements:
- WorldBounds

JASS:

//========================================================
// ~~ UnitStatePercent ~~ 1.0.0 ~~ Nestharthus
//========================================================
//   _   _      _ _   ___ _        _       ___                    _   
//  | | | |_ _ (_) |_/ __| |_ __ _| |_ ___| _ \___ _ _ __ ___ _ _| |_ 
//  | |_| | ' \| |  _\__ \  _/ _` |  _/ -_)  _/ -_) '_/ _/ -_) ' \  _|
//  \___/|_||_|_|\__|___/\__\__,_|\__\___|_| \___|_| \__\___|_||_\__|
//
//    
//        ABOUT THIS
//      --------------
//        UnitStatePercent allows you to deal with Unit States as if they were 
//        represented by percents instead of literal values.  This is very useful
//        for Save and Load systems, as well as for the desired syntax.
//
//        REQUIREMENTS
//      ----------------
//         * WorldBounds
//
//        FUNCTIONS
//      -------------
//        function GetPercentHeroXP takes unit u returns integer
//        function AddPercentHeroXP takes unit u, integer p returns nothing
//    
//        function GetPercentUnitLife takes unit u returns integer
//        function SetPercentUnitLife takes unit u, integer p returns nothing
//    
//        function GetPercentUnitMana takes unit u returns integer
//        function SetPercentUnitMana takes unit u, integer p returns nothing
//    
//        function GetPercentUnitX takes unit u returns integer
//        function PercentToX takes integer l returns real
//    
//        function GetPercentUnitY takes unit u returns integer
//        function PercentToY takes integer l returns real
//    
//        function GetPercentUnitFacing takes unit u returns integer
//        function PercentToFacing takes integer f returns real
//
//        CREDITS
//      -----------
//        Myself - For being cool and writing this system
//        TTE - for being such a kind bloke and writing documentation
//
//
library UnitStatePercent uses WorldBounds

    globals
        private real xl = 0
        private real yl = 0
    endglobals
    
    private module Init
        private static method onInit takes nothing returns nothing
            set xl = WorldBounds.maxX-WorldBounds.minX
            set yl = WorldBounds.maxY-WorldBounds.minY
        endmethod
    endmodule
    
    private struct Inits extends array
        implement Init
    endstruct
    
    //retrieves how far a hero is into its level as an integer %
    //u refers to the hero
    function GetPercentHeroXP takes unit u returns integer
        local integer l = GetHeroLevel(u)
        local integer p = GetHeroXP(u)
        local integer xp = p
        local real li = GetWidgetLife(u)
        local real ma = GetUnitState(u, UNIT_STATE_MANA)
        if (l == 1) then
            call SetHeroLevel(u, 2, false)
            set p = R2I((p/(GetHeroXP(u)+0.))*100+.5)
            call UnitStripHeroLevel(u, 1)
            call SetHeroXP(u, xp, false)
        else
            call UnitStripHeroLevel(u, 1)
            call SetHeroLevel(u, l, false)
            set p = R2I((p/(GetHeroXP(u)+0.)-1)*100+.5)
            call SetHeroXP(u, xp, false)
        endif
        call SetWidgetLife(u, li)
        call SetUnitState(u, UNIT_STATE_MANA, ma)
        return p
    endfunction
    
    //adds an integer percent xp to a hero
    //u refer to hero, p refers to percent
    //make sure the hero was its original level before adding
        //call SetHeroLevel(u, myLvl, false)
        //call AddPercentHeroXP(u, 30) //pushes the hero 30% into the level
    function AddPercentHeroXP takes unit u, integer p returns nothing
        if (p > 0) then
            if (GetHeroLevel(u) == 1) then
                call SetHeroLevel(u, 2, false)
                set p = R2I(p/100.*GetHeroXP(u))
                call UnitStripHeroLevel(u, 1)
                call AddHeroXP(u, p, false)
            else
                call AddHeroXP(u, R2I(p/100.*GetHeroXP(u)), false)
            endif
        endif
    endfunction
    
    //retrieves a unit's current life as a percent
    function GetPercentUnitLife takes unit u returns integer
        return R2I((GetWidgetLife(u)/GetUnitState(u, UNIT_STATE_MAX_LIFE))*100+.5)
    endfunction
    
    //sets a unit's life given a percent
    function SetPercentUnitLife takes unit u, integer p returns nothing
        call SetWidgetLife(u,p/100.*GetUnitState(u, UNIT_STATE_MAX_LIFE))
    endfunction
    
    //retrieves a unit's current mana as a percent
    function GetPercentUnitMana takes unit u returns integer
        local real m = GetUnitState(u, UNIT_STATE_MAX_MANA)
        if (m == 0) then
            return 0
        endif
        return R2I(GetUnitState(u, UNIT_STATE_MANA)/m*100+.5)
    endfunction
    
    //sets a unit's mana given a percent
    function SetPercentUnitMana takes unit u, integer p returns nothing
        call SetUnitState(u, UNIT_STATE_MANA, p/100.*GetUnitState(u, UNIT_STATE_MAX_MANA))
    endfunction
    
    function GetPercentUnitX takes unit u returns integer
        return R2I((GetWidgetX(u)-WorldBounds.minX)/xl*100+.5)
    endfunction
    
    function PercentToX takes integer l returns real
        return l/100.*xl+WorldBounds.minX
    endfunction
    
    function GetPercentUnitY takes unit u returns integer
        return R2I((GetWidgetY(u)-WorldBounds.minY)/yl*100+.5)
    endfunction
    
    function PercentToY takes integer l returns real
        return l/100.*yl+WorldBounds.minY
    endfunction
    
    function GetPercentUnitFacing takes unit u returns integer
        return R2I(GetUnitFacing(u)/360.*100+.5)
    endfunction
    
    function PercentToFacing takes integer f returns real
        return f/100.*360
    endfunction
endlibrary


And, the demo script:
]Run this in map to see approximations (very close to actual values!)
JASS:

struct tester extends array
    private static method test takes real x, real y, real facing, real life, real mana, integer level, integer xp returns nothing
        local unit u = CreateUnit(Player(0), 'Hpal', x, y, facing)
        local integer xps
        local integer mp
        local integer lp
        local integer fp
        
        if (level > 1) then
            call SetHeroLevel(u, level, false)
        endif
        call AddHeroXP(u, xp, false)
        call SetWidgetLife(u, life)
        call SetUnitState(u, UNIT_STATE_MANA, mana)
        
        //done above as setting levels will change the percents
        set lp = GetPercentUnitLife(u)
        set mp = GetPercentUnitMana(u)
        set fp = GetPercentUnitFacing(u)
        set xps = GetPercentHeroXP(u)
        set xp = GetHeroXP(u)
        
        call UnitStripHeroLevel(u, level)
        call SetHeroXP(u, 0, false)
        
        call SetUnitX(u, PercentToX(GetPercentUnitX(u)))
        call SetUnitY(u, PercentToY(GetPercentUnitY(u)))
        if (level > 1) then
            call UnitStripHeroLevel(u, 1)
            call SetHeroLevel(u, level, false)
        endif
        call AddPercentHeroXP(u, xps)
        call SetPercentUnitLife(u, lp)
        call SetPercentUnitMana(u, mp)
        call SetUnitFacing(u, PercentToFacing(fp))
        
        call SetWidgetLife(u, GetWidgetLife(u))
        
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "x: " + R2S(x) + "==" + R2S(GetWidgetX(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "y: " + R2S(y) + "==" + R2S(GetWidgetY(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "facing: " + R2S(facing) + "==" + R2S(GetUnitFacing(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "life: " + R2S(life) + "==" + R2S(GetWidgetLife(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "mana: " + R2S(mana) + "==" + R2S(GetUnitState(u, UNIT_STATE_MANA)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "level: " + I2S(level) + "==" + I2S(GetHeroLevel(u)))
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "xp: " + I2S(xp) + "==" + I2S(GetHeroXP(u)))
        set u = null
    endmethod
    private static method onInit takes nothing returns nothing
        call test(2691,WorldBounds.minY,124,450,0,2,50)
    endmethod
endstruct
endlibrary
 

Nestharus

o-o
Reaction score
84
I've been restraining myself from commenting on this for a long time, but you got me:

That is a smiley. The ^ ^ are eyes and the ) is a nose. ^)^.

Ahh, yes! A GetPercentUnitX function! Just what I always wanted! Now I uh.. can um..
[/qutoe]

Storing 0-100 is much smaller than storing a huge coordinate like 3924823 ;P.

By the way, you're missing SetUnitOwnerPercent

er...
 

Nestharus

o-o
Reaction score
84
Ok, updated documentation as a variant on tooltiperror's stuff. I'll also add actual links into the post ; ).

Hopefully it's better. I like to link the requirements and have the lib name at the top. I also don't like the ascii art stuff ;P.
 

Jesus4Lyf

Good Idea™
Reaction score
397
Storing 0-100 is much smaller than storing a huge coordinate like 3924823 ;P.
Then achieve the same thing by dividing by x before storing? I guess it almost makes sense since you can multiply it by an int before saving to get a resolution, but ofc not storing a character's actual position can wreak havoc if it is misplaced.

You can always just not use save/load codes, I don't believe blizzard have fixed the exploit yet.. have they?
 

Nestharus

o-o
Reaction score
84
Well, for an update I plan on introducing a constant to determine how big the percents are for each property. One percent wanted 100% accurate xp, but I told them that that was impossible unless they wanted numbers going into the millions or billions ;D.

So, I figured doing 1000 for xp would be rather good as it could track like 99.9%. Any more than that and you lose accuracy because of how wonderful the reals are -.-.

As for the exploit, from the Preloader one to the local gamecache files one, I prefer to use neither ;\.

Enabling local files for gamecache makes it so I can't play on b.net.

Preloader will more than likely be fixed, so I'd prefer not to use it ;P.
 

Narks

Vastly intelligent whale-like being from the stars
Reaction score
90
oh, that one... I don't see how it can be used for file input anyway, but I guess you could always write to a specified location, and then ask users to copy the save code from there
 

Bribe

vJass errors are legion
Reaction score
67
Tip:

www.hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494

Could just be:

www.hiveworkshop.com/forums/showthread.php?t=180494
 

emjlr3

Change can be a good thing
Reaction score
395
what does
JASS:
GetPercentUnitMana
GetPercentUnitLife


do that this doesn't?

also, maybe its just me, but why would you change the generic WC3 naming convention from GetUnitXXX to GetXXXUnit??
 

Nestharus

o-o
Reaction score
84
Oh, didn't see those o-o. Don't know many of the BJs ;D.

WC3 naming convention from GetUnitXXX to GetXXXUnit??

GetUnitXPercent looks ugly with 2 uppercase next to each other was the reason =P.


edit
Ah, the get percents take reals and return reals. These one do integers and round. Can't do reals for save/load =P. I guess that's not much of a dif =).
 

Weep

Godspeed to the sound of the pounding
Reaction score
401
Oh, didn't see those o-o. Don't know many of the BJs ;D.
Yours use GetWidgetLife so should be faster. ;)

BTW, what do you have against image emoticons? You always go out of your way to avoid the parsing.

GetUnitXPercent looks ugly with 2 uppercase next to each other was the reason =P.
Perhaps, but it's Blizzard convention*, eg. bj_forLoopAIndex.
*Not like they really follow any fixed set of rules, lol.
 

Nestharus

o-o
Reaction score
84
BTW, what do you have against image emoticons? You always go out of your way to avoid the parsing.

I don't like them ; P. I like regular ascii smiles a lot more =D.

Perhaps, but it's Blizzard convention*, eg. bj_forLoopAIndex.
*Not like they really follow any fixed set of rules, lol.

Yea, and the function name is hard to read with 2 caps next to each other.
->bj_forLoopAIndex

Point proven. AIndex makes me want to type AIIndex :\, so not only is it harder to read, it's harder to type for people who have been coding camelcase for years on end. You never put 2 caps next to each other, so it's just not natural to type it, you end up typing an extra cap because you treat the first 2 as its own thing since it's weird to begin with ;P.
 

emjlr3

Change can be a good thing
Reaction score
395
second that,

its only that, for someone who has scripted in JASS for as long as I have, suddenly trying to re-arrange syntax that's basically been programmed in gets confusing

for instance - it took me for ever to start using
JASS:
GetTimerData
after always using
JASS:
TimerGetSomething
natives - the change in convention was difficult to circumvent
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      No members online now.

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top