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

[System] Heal

This system efficiently handles healing units instantly and over time.

JASS:
/************************************************************
*
*   Heal
*   v4.0.0.3
*   By Magtheridon96
*
*   - Heals units instantly or over time.
*   - Can also allow Damage over time with negative heal amount.
*   - Comes with heal events:
*
*       - Heal.ANY
*           - Fires on any healing event.
*           - This would be used for systems like:
*               - Accurate Unit Regeneration Logs
*               - Accurate Damage Logs
*               - Heal Block/Spell Effect Block
*
*       - Heal.INSTANT
*           - Fires on instant heal events.
*           - This would be used for systems like:
*               - Texttag Displays
*
*       - Heal.TIMED
*           - Fires 32x a second during a timed heal.
*           - This was only added for a sense of 
*             completeness. It may be useful.
*
*   - Requires:
*
*       - UnitEvent by Nestharus
*           - hiveworkshop.com/forums/jass-resources-412/extension-unit-event-172365/
*       - UnitIndexer by Nestharus
*           - hiveworkshop.com/forums/jass-resources-412/system-unit-indexer-172090/
*       - Event by Nestharus
*           - hiveworkshop.com/forums/jass-resources-412/snippet-event-186555/
*       - CTL by Nestharus
*           - hiveworkshop.com/forums/jass-resources-412/snippet-constant-timer-loop-32-a-201381/
*
*   - API:
*     ----
*
*       - struct Heal extends array
*           - readonly static Event ANY
*           - readonly static Event TIMED
*           - readonly static Event INSTANT
*               - Heal events
*
*           - static boolean enabled
*               - Used to enable/disable the system
*
*           - static method healUnit takes unit source, unit target, real amount returns nothing
*           - static method healUnitOverTime takes unit source, unit target, real amount, real duration returns nothing
*               - Heal a unit instantly or over time
*
*       - function RegisterInstantHealEvent takes code c returns nothing
*       - function RegisterTimedHealEvent takes code c returns nothing
*       - function RegisterAnyHealEvent takes code c returns nothing
*           - These functions register specific heal events that fire differently.
*
*       - function GetHealingUnit takes nothing returns unit
*       - function GetHealingUnitId takes nothing returns integer
*           - These functions are used to get the healing unit or its id.
*
*       - function GetHealedUnit takes nothing returns unit
*       - function GetHealedUnitId takes nothing returns integer
*           - These functions are used to get the healed unit or its id.
*
*       - function GetHealedAmount takes nothing returns real
*       - function GetEffectiveHealedAmount takes nothing returns real
*       - function GetOriginalLife takes nothing returns real
*           - These functions are used to get:
*               - Amount of HP intended to be healed.
*               - Amount of HP actually healed.
*               - Original HP before healing.
*
*       - function HealUnit takes unit source, unit target, real amount returns nothing
*       - function HealUnitOverTime takes unit source, unit target, real amount, real duration returns nothing
*           - These are the wrappers used for healing units.
*
************************************************************/
library Heal requires UnitIndexer, UnitEvent, Event, CTL

    private module Init
        private static method onInit takes nothing returns nothing
            set INSTANT = CreateEvent()
            set TIMED = CreateEvent()
            set ANY = CreateEvent()
        endmethod
    endmodule
    
    struct Heal extends array
    
        readonly static Event INSTANT
        readonly static Event TIMED
        readonly static Event ANY
        
        static boolean enabled = true
        
        static UnitIndex sourceId = 0
        static UnitIndex targetId = 0
        
        static real amount = 0.0
        static real original = 0.0
        static real effective = 0.0
        
        private static unit array source
        private static unit array target
        private static real array regen
        private static real array duration
        
        private static method lock takes nothing returns nothing
            if sourceId != 0 then
                call sourceId.lock()
            endif
            if targetId != 0 then
                call targetId.lock()
            endif
        endmethod
        
        private static method unlock takes nothing returns nothing
            if sourceId != 0 then
                call sourceId.unlock()
            endif
            if targetId != 0 then
                call targetId.unlock()
            endif
        endmethod
        
        implement CTL
        
            local real r
            local integer u
            
        implement CTLExpire
        
            set r = GetWidgetLife(target[this])
            set u = GetUnitUserData(target[this])
            
            if not IsUnitDead(u) and duration[this] <= 0. then
                set duration[this] = duration[this] - 0.03125
                call SetWidgetLife(target[this], r + regen[this])
                
                if enabled then
                
                    set sourceId = GetUnitUserData(source[this])
                    set targetId = u
                    set amount = regen[this]
                    set original = r
                    set effective = GetWidgetLife(target[this]) - r
                    
                    call lock()
                    
                    call TIMED.fire()
                    call ANY.fire()
                    
                    call unlock()
                    
                endif
            else
                set source[this] = null
                set target[this] = null
                
                call this.destroy()
            endif
            
        implement CTLNull
        implement CTLEnd
        
        static method healUnit takes unit source, unit target, real howMuch returns nothing
            local real r = GetWidgetLife(target)
            local integer u = GetUnitUserData(target)
            
            if not IsUnitDead(u) then
                call SetWidgetLife(target, r + howMuch)
                
                if enabled then
                
                    set sourceId = GetUnitUserData(source)
                    set targetId = u
                    set amount = howMuch
                    set original = r
                    set effective = GetWidgetLife(target) - r
                    
                    call lock()
                    
                    call INSTANT.fire()
                    call ANY.fire()
                    
                    call unlock()
                    
                endif
            endif
        endmethod
        
        static method healUnitOverTime takes unit src, unit trgt, real howMuch, real dur returns nothing
            local thistype this = create()
            
            set source[this] = src
            set target[this] = trgt
            set regen[this] = 0.03125 * howMuch / dur
            
            set duration[this] = dur
        endmethod
        
        implement Init
    endstruct
    
    function RegisterInstantHealEvent takes code c returns nothing
        call Heal.INSTANT.register(Filter(c))
        return
    endfunction
    
    function RegisterTimedHealEvent takes code c returns nothing
        call Heal.TIMED.register(Filter(c))
        return
    endfunction
    
    function RegisterAnyHealEvent takes code c returns nothing
        call Heal.ANY.register(Filter(c))
        return
    endfunction
    
    function GetHealingUnit takes nothing returns unit
        return GetUnitById(Heal.sourceId)
    endfunction
    
    function GetHealingUnitId takes nothing returns integer
        return Heal.sourceId
    endfunction
    
    function GetHealedUnit takes nothing returns unit
        return GetUnitById(Heal.targetId)
    endfunction
    
    function GetHealedUnitId takes nothing returns integer
        return Heal.targetId
    endfunction
    
    function GetHealedAmount takes nothing returns real
        return Heal.amount
    endfunction
    
    function GetEffectiveHealedAmount takes nothing returns real
        return Heal.effective
    endfunction
    
    function GetOriginalLife takes nothing returns real
        return Heal.original
    endfunction
    
    function HealUnit takes unit source, unit target, real amount returns nothing
        call Heal.healUnit(source,target,amount)
    endfunction
    
    function HealUnitOverTime takes unit source, unit target, real amount, real duration returns nothing
        call Heal.healUnitOverTime(source,target,amount,duration)
    endfunction
    
endlibrary

Demo Code

JASS:
struct Tester extends array

    private static unit fighters1
    private static unit fighters2

    private static unit regen

    private static method instant takes nothing returns nothing
        call HealUnit(null, fighters1, 150)
        call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, "Healed unit for 150 HP")
    endmethod

    private static method onInit takes nothing returns nothing
        
        set fighters1 = CreateUnit(Player(0), 'Hpal', 128, 0, 180)
        set fighters2 = CreateUnit(Player(1), 'Hpal', -128, 0, 0)
        call TimerStart(CreateTimer(), 10, true, function thistype.instant)
        
        set regen = CreateUnit(Player(0), 'Hpal', 0, 1024, 270)
        call CreateUnit(Player(1), 'Hpal', 0, 1024, 270)
        call SetWidgetLife(regen, GetUnitState(regen, UNIT_STATE_MAX_LIFE) / 2)
        
        // This will regenerate 500000 HP over 50000 seconds meaning 10 HP/sec
        call HealUnitOverTime(null, regen, 500000, 50000)
        
        call PauseUnit(regen, true)
    endmethod

endstruct

Feel free to comment..
 
Last edited:
Level 14
Joined
Nov 18, 2007
Messages
1,084
JASS:
            if r>=0.405 then
                call SetWidgetLife(.target,r+.amount)
                set Heal.sourceId = GetUnitUserData(.source)
                set Heal.targetId = GetUnitUserData(.target)
                set Heal.amount = .amount
                set Heal.original = r
                set Heal.effective = GetWidgetLife(.target)-r
                call Heal.onHeal.fire()
            else
                call .stopPeriodic()
                call .destroy()
                return
            endif
            set .counter = .counter + T32_PERIOD
            if .counter>=.duration then
                call .stopPeriodic()
                call .destroy()
            endif
->
JASS:
            if r>=0.405 and .counter < .duration then
                set .counter = .counter + T32_PERIOD
                call SetWidgetLife(.target,r+.amount)
                set Heal.sourceId = GetUnitUserData(.source)
                set Heal.targetId = GetUnitUserData(.target)
                set Heal.amount = .amount
                set Heal.original = r
                set Heal.effective = GetWidgetLife(.target)-r
                call Heal.onHeal.fire()
            else
                call .stopPeriodic()
                call .destroy()
            endif
You should probably add a function to test the effectiveness of the healing seeing that you already have a effective member.

You should write better documentation.

Does the system support healing inside a healing event?

I suggest that healing also consider the ethereal heal bonus. You could also make it easier for the user to specify healing modifiers, which would be similar to damage modifiers.
 
Does the system support healing inside a healing event?

Got it.
static boolean enabled = true
:p I'll add that in.

You should write better documentation

Yeah, I'll probably just list the API :p

I suggest that healing also consider the ethereal heal bonus. You could also make it easier for the user to specify healing modifiers, which would be similar to damage modifiers

Nah ;p

You should probably add a function to test the effectiveness of the healing seeing that you already have a effective member.

Oh sorry about that :p
I added those members and modified the code inside the post ^)^


edit
Updated.
 
Last edited:
Level 5
Joined
Jun 30, 2004
Messages
170
So, I have questions:

First of all, you really need better documentation. If you want people to use a system, you better tell them how to use it. It's easy for you to understand because you made it, but you put it here for other people to use. It makes little sense if notes for using the system are poor, but you put it here for other people to use.

Heal.onTimedHeal has an incomplete note. "Fires 32x a second with overtime heals (I would use this for *BLANK*)". Nothing? Okay, then why is it there? >_>

What is the difference between GetHealAmount and GetEffectiveHealAmount? I didn't find any random value in any healing factor. Healing values don't register as damage anyway, since you're using GetWidgetLife.

Also, are you using this for something of your own? I'm gonna be evil here: What's the point in this system when it's easy to replicate what you have with a library with very simple function calls? I don't see the point.
 
Level 4
Joined
Mar 27, 2008
Messages
112
GetHealAmount = the healing that you inputed and GetEffectiveHealAmount is the actual healing done.
Ex.
Unit has 150/180 hp
You heal him for 100
then GetHealAmount returns 100
and GetEffectiveHealAmount returns 30 (180-150)
 
First of all, you really need better documentation. If you want people to use a system, you better tell them how to use it. It's easy for you to understand because you made it, but you put it here for other people to use. It makes little sense if notes for using the system are poor, but you put it here for other people to use.

The API has been listed and I put a small description for the functions in the API.

Heal.onTimedHeal has an incomplete note. "Fires 32x a second with overtime heals (I would use this for *BLANK*)". Nothing? Okay, then why is it there? >_>

In case you wanted to reverse the effects of some healing, you'd want the event to fire everytime the unit is given bonus life.

I'm using the event Heal.ANY for my Is Unit Regenerating snippet so that I can filter out any given HP and get real regeneration.

What is the difference between GetHealAmount and GetEffectiveHealAmount ? I didn't find any random value in any healing factor. Healing values don't register as damage anyway, since you're using GetWidgetLife.

dudeim explained it above this comment :p

What's the point in this system when it's easy to replicate what you have with a library with very simple function calls? I don't see the point.

There are 2 major systems involving "Healing"
One is found at wc3c.net, but it's absolutely terrible.
The other is at TheHelper, it's decent, but it needs a bit of work.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Oh I see. When I released my dummy lib people complained about the keyword thingie although the readability would be silly without it. I still wonder why as I cannot find that thread as it got rejected (due to no update and purge). Yeah seems pretty simple with the modules implemention but still I don't need the index looping :p
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
Because Zinc Is Not C and don't have pointers, so this fugly keyword was available :p

I suppose he tried to get rid of the jass verbosity but still follow its convention (takes ... returns ...)
And he was also in a rush (because of cJass ?), his decisions couldn't be all good.

But anyway honestly because it's pretty much an alpha preprocessor and isn't under development anymore, it shouldn't be used at all.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
He could have least left a thought on making it easier as he did with declaring and function calling. > screws it all up as I have to shift + < to make it and I don't wanna change my keyboard.

vJASS isn't under dev as well :p Vex left us all to rot with the stupid initializer orders and stupid inlining.
 
Level 5
Joined
Jun 30, 2004
Messages
170
The API has been listed and I put a small description for the functions in the API.

Yeah, I know... but I still don't think it's sufficient. I look at look at other libraries and then I look at yours. Yours is so empty. xD

In case you wanted to reverse the effects of some healing, you'd want the event to fire everytime the unit is given bonus life.
Seriously. Put this in the comment instead of "you might need this".

I'm using the event Heal.ANY for my Is Unit Regenerating snippet so that I can filter out any given HP and get real regeneration.

Hmm...

dudeim explained it above this comment :p

Yes. When I read GetEffectiveHealAmount I was looking for a random factor... not the amount that is needed to fully recover the health of a unit. You should write that down, too.

There are 2 major systems involving "Healing"
One is found at wc3c.net, but it's absolutely terrible.
The other is at TheHelper, it's decent, but it needs a bit of work.
I know of the one at wc3c.net. I never used it for that reason. >_>

I can't use yours either, though. I'm too far in development to add another system. Maybe in another map...
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Yes but bugs are both known and not that hard, more we have workarounds (except maybe the pure OOP part), but still vJass >>>> jass.

But blame Bribe, he left the luck project :p

I'd say there should be more bugs that we don't know of yet because we didn't find the use of them.

Yes of course vJASS but still Zinc was a good option while it was developed...

Yeah I know, I was a great supporter of the luck language :) YOU SEE THIS BRIBE?! (Yes I know the reasons but still)
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I stopped the Luck project because everyone kept disagreeing with the API, and I am not going to invest so much work into a language that so many people are going to cry about.

The form really became too ugly for me to work with, based on the recommendations I was getting, and there was no practical way to live the dream I intended with so much compromise.

I also wanted to write the parser using Python, because that would have saved me the most development time, but people were crying that I wouldn't use their precious C or Flex Bison.

So it was a lose/lose situation, why should I waste my time?
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
vJASS was never perfect when it got out as well you know :p and you should not really care what others think about which language you code in, Vex used Gold (something) for goodness sake, it's sooo slow. Anyway when you'll get into other thoughts, we'll be there for ya.
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Oh crap then I might join the opposition to your chosing of language if the Luck parser was supposed to compile slower than JH ;)

Maybe Vex's followed it from the start or earlier phases. As with me, I'm more familiar with Ruby than C++ as I followed the development of Ruby a lot longer than with C++ (nearly haven't followed it at all).
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
I enjoyed the syntax of Python and found the syntax of C redundant and verbose in comparison.

See, so it is not such a bad idea that the project was not finished.

Nestharus, for example, flipped his lid when I told him the language I was going to compile it with ;)

You realise that Goldparser is not a language but a grammar parser (quite like flex/bison) which can be used in different languages ?!

Jasshelper was made with Pascal, and as far i know i wouldn't say this language is fast.

I typed the wrong thing, I meant Pascal. I should not be writing in complete sentences here at work.
 

BBQ

BBQ

Level 4
Joined
Jun 7, 2011
Messages
97
Well, originally Pascal was made for an educational purpose, it's still used, but not by me :p
http://en.wikipedia.org/wiki/Pascal_(programming_language
JassHelper is not written in Pascal. It is written in Embarcadero Delphi. Yes, Delphi is a dialect of Object Pascal, which itself is an object-oriented derivative of Pascal, but let me quote Wikipedia:
"Delphi, in contrast to traditional Pascal, was not primarily designed for education purposes."​
It is, as a matter of fact, a quite "powerful" language, as long as you're accommodated to its syntax.


EDIT: And about that Luck thing or whatever it was going to be called. There's absolutely no point in writing a new parser. It'd take an immense amount of time to implement all of JassHelper's features. By that time, nobody would be giving a shit about WC3 modding.

I know that you guys here hate Vexorian and have your own "standards" of what good code is and ways of fixing your imaginary code overheads, but really, that's just ridiculous. Why not use plain JASS2? That way, you'd have absolute control over everything. Short names, fuck yeah.

So yes, either quit your bitching and use JASS2, or update JassHelper. It's open source and everything. The only thing I'm afraid of is your actual inability to write or maintain any "real" code, if I may call it like that.
 
Last edited:
Level 17
Joined
Apr 27, 2008
Messages
2,455
@BBQ : Good to know, i said pascal just because of the ".pas", i should have guess that wasn't pascal, just because it's Vexorian.
I apologize about it.

Also i for one love Vexorian's work, sure it's not perfect, but like i've already said i wouldn't imagine how would be the wc3 modding without him.
Actually i've not the knowledge to write or maintain a "real" code, but i'm in learning process.

I will probably make my own preprocessor from crash, yeah i know it will take time and nobody will use it but i don't care since i will enjoy to make it.

@Bribe : It must be a joke, the third party tool is not an issue and really improve wc3 scripting, unless you don't use windows, but jasshelper by itself is enough (if you can use it in a mac, at least you can on linux with wine)

Scripting in jass directly inside the official editor is a pain in the ass.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,456
The third party tool is a matter of debate, considering antivirus hates it and getting it working properly is not always without bugs. Jasshelper has some strange crashes when compiling and also uses poor grammar, it is altogether unprofessional.

For me I prefer to do more with less, which is more of a challenge. This is why I don't want to use SC2 editor because there are too many options, which ironically reduces creativity, because at the end of the day you can't say someone else could do it just as good, because it was such a challenge to make it work in the first place.

Does this make sense? It is a really important concept that most people miss. I don't like programming in "real" languages either for the same reasons.
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
For third party tools it's not so much a big deal to make them working correctly.
Well, Jasshelper has a "poor" grammar but that doesn't matter that much once you've used it for a while.

As long you enjoy it's fine but personnaly i enjoy vJass/cJass/whateverJass not jass by itself, i can't go backward now that i know them and their possibilities.

For "real" languages i don't see how your points are valid :
- you don't have to use external libraries if you like reinventing the wheel
- ofc third party tools are needed but i wouldn't script directly in a simple text editor ...
 
Level 22
Joined
Nov 14, 2008
Messages
3,256
Bribe, you remind me of Paladon - "What you can do in JASS, I can do in GUI." (yeah I know they're the same but you'll get my point and vJASS was just uprising at that time).

But
I don't like programming in "real" languages either for the same reasons.
I don't agree with. Yet I do create more in WC3 than I do in i.e. Ruby (and we get rid off the part that I know a lot less about Ruby than I know about JASS when you look at the whole knowledge compared to what exists).
 
The op-limit is scarier than you think >:p
And the speed doesn't help at all ^^
Try doing this in any language (I did it with C++):

Code:
#include <iostream>
using namespace std;

int main() {
    int i=0;
    cout << "Starting...";
    while (i<300000000) {
        i++;
    }
    cout << "\nDone: i = " << i;
    return 0;
}

This only took about 1 second o.o

While in Jass:

JASS:
local integer i = 0
loop
    exitwhen i>300000000
    set i=i+1
endloop
This would never finish >:p

That's why it's so damn fun to code in Jass ^^
 
Level 17
Joined
Apr 27, 2008
Messages
2,455
No i've understood but to be honest it's way too strange for me.

I don't see any fun in a script without several common things like some POO concepts and from other paradigms. (you've said you don't like vJass or i've missunderstood)
Reiventing the wheel for such common things are really boring for me.

About the very limited thingie, i understand your point even if i can't see how it can't become boring.

Anyway End of off-topic for me, good luck Mag.
 
Last edited:
Top