[vJASS] Regeneration

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
This is made since wc3 doesn't allow precise hp regeneration (below 1) and only provides mp regeneration by percent. Can be used along with CSS or BonusMod. I don't know you will like the design or not. Any suggestion is welcome. :)

JASS:
library Regeneration uses TimerUtils, UnitIndexer
    
    globals
        private constant real REGENERATION_RATE = 0.2
    endglobals
    
    /*
        Regeneration v1.0
        by: Quilnez
        
        Allows you to apply hp and mp regeneration on units.
        
        Requires:
            - UnitIndexer by Nestharus  | hiveworkshop.com/forums/spells-569/unit-indexer-v5-3-0-1-a-260859/
            - TimerUtils  by Vexorian   | wc3c.net/showthread.php?t=101322
            
        API
            struct Regeneration
                1. Apply/add regeneration to a unit
                    static method apply takes unit whichUnit, boolean hp, real regenVal returns Regeneration
                    static method add   takes unit whichUnit, boolean hp, real regenVal returns Regeneration
                        - boolean hp    => false to apply to mp regeneration
                        - real regenVal => regenerated hp/hp per second
                
                2. Get unit regeneration rate (per second)
                    static method get   takes unit whichUnit, boolean hp returns real
                
                3. Stop any regeneration on a unit
                    static method stop  takes unit whichUnit returns nothing
                
                4. Set/get unit regenerations (per second)
                    method operator hpRegen= takes real regenVal returns nothing
                    method operator mpRegen= takes real regenVal returns nothing
                    method operator hpRegen takes nothing returns real
                    method operator mpRegen takes nothing returns real
             
            function SetUnitHPRegeneration takes unit whichUnit, real regenVal returns Regeneration
            function SetUnitMPRegeneration takes unit whichUnit, real regenVal returns Regeneration
            function AddUnitHPRegeneration takes unit whichUnit, real regenVal returns Regeneration
            function AddUnitMPRegeneration takes unit whichUnit, real regenVal returns Regeneration
            function GetUnitHPRegeneration takes unit whichUnit, real regenVal returns real
            function GetUnitMPRegeneration takes unit whichUnit, real regenVal returns real
    */
    
    native UnitAlive takes unit id returns boolean
    
    struct Regeneration
        
        private unit target
        private timer time
        
        private real hpInc
        private real mpInc
        
        private static thistype array Index
        
        method destroy takes nothing returns nothing
            
            set Index[GetUnitUserData(target)] = 0
            call ReleaseTimer(time)
            call deallocate()
            set target = null
            set time   = null
            
        endmethod
        
        method operator hpRegen= takes real regenVal returns nothing
            set hpInc = REGENERATION_RATE/1.*regenVal
        endmethod
        
        method operator mpRegen= takes real regenVal returns nothing
            set mpInc = REGENERATION_RATE/1.*regenVal
        endmethod
        
        method operator hpRegen takes nothing returns real
            return 1./REGENERATION_RATE*hpInc
        endmethod
        
        method operator mpRegen takes nothing returns real
            return 1./REGENERATION_RATE*mpInc
        endmethod
        
        private static method onPeriodic takes nothing returns nothing
            
            local thistype this = GetTimerData(GetExpiredTimer())
            
            if UnitAlive(target) then
                call SetWidgetLife(target, GetWidgetLife(target)+hpInc)
                call SetUnitState(target, UNIT_STATE_MANA, GetUnitState(target, UNIT_STATE_MANA)+mpInc)
            endif
            
        endmethod
        
        static method stop takes unit whichUnit returns nothing
            call Index[GetUnitUserData(whichUnit)].destroy()
        endmethod
        
        static method get takes unit whichUnit, boolean hp returns real
            if hp then
                return Index[GetUnitUserData(whichUnit)].hpRegen
            else
                return Index[GetUnitUserData(whichUnit)].mpRegen
            endif
        endmethod
        
        static method add takes unit whichUnit, boolean hp, real regenVal returns thistype
            return apply(whichUnit, hp, get(whichUnit, hp)+regenVal)
        endmethod
        
        static method apply takes unit whichUnit, boolean hp, real regenVal returns thistype
            
            local thistype this = Index[GetUnitUserData(whichUnit)]
            
            if this == 0 then
                set this = allocate()
                set Index[GetUnitUserData(whichUnit)] = this
                set target = whichUnit
                set time   = NewTimerEx(this)
                call TimerStart(time, REGENERATION_RATE, true, function thistype.onPeriodic)
            endif
            
            if hp then
                set hpRegen = regenVal
            else
                set mpRegen = regenVal
            endif
            
            return this
        endmethod
        
        private static method onDeindex takes nothing returns boolean
        
            local thistype this = Index[GetUnitUserData(GetIndexedUnit())]
            
            if this != 0 then
                call destroy()
            endif
            
            return false
        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterUnitIndexEvent(Condition(function thistype.onDeindex), UnitIndexer.DEINDEX)
        endmethod
        
    endstruct
    
    function SetUnitHPRegeneration takes unit whichUnit, real regenVal returns Regeneration
        return Regeneration.apply(whichUnit, true, regenVal)
    endfunction
    
    function SetUnitMPRegeneration takes unit whichUnit, real regenVal returns Regeneration
        return Regeneration.apply(whichUnit, false, regenVal)
    endfunction
    
    function AddUnitHPRegeneration takes unit whichUnit, real regenVal returns Regeneration
        return Regeneration.add(whichUnit, true, regenVal)
    endfunction
    
    function AddUnitMPRegeneration takes unit whichUnit, real regenVal returns Regeneration
        return Regeneration.add(whichUnit, false, regenVal)
    endfunction
    
    function GetUnitHPRegeneration takes unit whichUnit, real regenVal returns real
        return Regeneration.get(whichUnit, true)
    endfunction
    
    function GetUnitMPRegeneration takes unit whichUnit, real regenVal returns real
        return Regeneration.get(whichUnit, false)
    endfunction
    
endlibrary
 
Level 10
Joined
Sep 19, 2011
Messages
527
have been reading a few articles about "why not oop"... kinda like i'm seeing their points now.

for something like this i would go for a simpler/clear approach (didn't test it but you get the point):

JASS:
globals
  constant real REG_RATE = 0.2
  integer units_count = 0
endglobals

struct regeneration extends array
  unit u
  real hp
  real mp
endstruct

function get_reg takes unit u, boolean autocreate returns regeneration
  local integer i = units_count

  loop
    set i = i - 1
    exitwhen i == 0 or regeneration[i].u == u
  endloop

  if (i == 0 and autocreate) then
    set units_count = units_count+1
    set i = units_count
    set regeneration[i].u = u
  endif

  return regeneration[i]
endfunction

function remove_reg takes unit u returns nothing
  local regeneration reg = get_reg(u, false)

  if (reg == 0) then
    return
  endif

  set regeneration[reg] = regeneration[units_count]
  set units_count = units_count-1
endfunction

function set_reg takes unit u, real reg, boolean hp returns regeneration
  local regeneration reg = get_reg(u, true)

  if (hp) then
    set reg.hp = reg
  else
    set reg.mp = reg
  endif

  return reg
endfunction

function set_mp takes unit u, real mp, boolean add returns regeneration
  if (add) then
    set mp = mp + get_reg(u, true).mp
  endif

  return set_reg(u, REG_RATE/1*mp, false)
endfunction

function set_hp takes unit u, real hp, boolean add returns regeneration
  if (add) then
    set hp = hp + get_reg(u, true).hp
  endif

  return set_reg(u, REG_RATE/1*hp, true)
endfunction

function reg_tick takes nothing returns nothing
  local integer i = units_count
  local regeneration reg

  loop
    set reg = regeneration[i]

    call SetWidgetLife(reg, GetWidgetLife(reg.u)+reg.hp)
    call SetUnitState(reg.u, UNIT_STATE_MANA, GetUnitState(reg.u, UNIT_STATE_MANA)+reg.mp)

    set i = i - 1
    exitwhen i == 0
  endloop
endfunction

function init takes nothing returns nothing
  call TimerStart(CreateTimer(), REG_RATE, true, function reg_tick)
endfunction
 
Not needing Nestharus' indexer would be a plus, though this is very similar to Magtheridon96's Regeneration resource sans the MP aspect.

@Flux, I like not having requirements but it subsequently removes the auto deindex portion. This would be the most efficient if it used Unit Event to remove units from the periodic loop when they started reincarnating, died or were removed.
 
Level 10
Joined
Sep 19, 2011
Messages
527
if that is the behavior wanted (resurrection/reanimated) yes, it "will fail". don't know, i think it may be better to leave the user handle it since it wouldn't be that hard.

edit: by the way @Quilnez, you don't need the Index property, you can simply use Regeneration[unit handle id]. you should also make your struct extend from array.
 

Kazeon

Hosted Project: EC
Level 34
Joined
Oct 12, 2011
Messages
3,449
Not needing Nestharus' indexer would be a plus, though this is very similar to Magtheridon96's Regeneration resource sans the MP aspect.
Sooo... Do we need to create our own vJass UnitIndexer? Since Nesth's is the only vJass indexer we have. :p

For another approach, I think the result won't be that much better. Using lot of timers is not costy at all. I've proved this in my maps.

And using unit's index will be slightly faster as well, since using handle id means we need hashtable, and hashtable is slower they said. No problem tho about the speed, it just doesn't worth it.
 
Maybe I can see about making more of the libs optional in Unit Indexer like I did with Alloc. Strip a bunch of features if the libraries aren't present. With this, if you want to go for only the classic unit indexer, which is what most people want, then it won't have any other dependencies (I would think) ^_-.

This is a good approach, there is just a lot of code... and it'd take a lot of time >.<.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you can actually catch the life regen as precisely as the game loop runs.

http://www.hiveworkshop.com/forums/pastebin.php?id=9bs1fo

this will still print different values every iteration for me, all you have to do is substract the amount from previous iteration's value and you get the regen.

Note that this may be computer-based, depending on how muscular your cpu is, but it will work even if you put lower time in, but it will start printing same values on iterations(but X - X is 0, so you can treat it as no regen)
 
Can be useful.

Though I would prefer public constant variables like HP/MP over true/false for the user to give as parameter for the create function.
Or actually the boolean could be scewed and the function can just take "real hpregen, real mpregen" next to the unit argument. Then everything is covered also.
If you still look for an other unit indexer: http://www.hiveworkshop.com/threads/system-unitdex-unit-indexer.248209/
 
I know you are busy these days, but I still would want to have it changes first.

Also, the setters operation onPeriodic are actually more expensive than an if statement I believe, especially the SetUnitState native.
So some if statement would be probably good, if I also imagine MP is not used too often, or in general if only one regen is used.

JASS:
if hpInc != 0
    call SetWidgetLife(target, GetWidgetLife(target)+hpInc)
if mpInc != 0
    call SetUnitState(target, UNIT_STATE_MANA, GetUnitState(target, UNIT_STATE_MANA)+mpInc)

in the description a small typo:
// - real regenVal => regenerated hp/hp per second
2x "hp" is written ^^ :)

For now I move it to Graveyard. Just make some notice when needed and it can be approved I guess.
 
Top