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

Heals, Summons, and Missiles

Status
Not open for further replies.
Level 10
Joined
Jun 10, 2007
Messages
557
Heya... it's me... again. With these issues fixed I should be able to unveil my map, and people who help will get +rep, as always.

1. aznricepuff/wd40bomber7 gave me a working attribute-based damage over time spell code, with which I've made several spells. Okay, great. But one of my heroes has, essentially, rejuvination. I want the amount healed per second to be based on attributes as well, to be specific, Intelligence. I have figured out a way in GUI to make a one-shot instantaneous heal, but to do that with heal-over-time spells requires the casting unit to be referred to after Wait functions, which I think doesn't work. Also, if possible, I'd like to know how to do this for a cone effect, like Breath of Fire in reverse (heals damage and heals more over time), and for a reverse Blizzard (heals over time in waves). And, if it wouldn't be too much trouble, healing wave.

2. I'd like to know how to give summoned units more health and damage based on their summoner's attributes. Like, for every point of Intelligence, they have 5 extra health and deal 1 extra damage, or something along those lines.

3. One-shot attribute-based spells that have missile effects deal their damage instantly after casting, not when the missile impacts. Is there a way to time it so that it only works after impact?

I'm kinda JASS retarded, but I'm open to possibilities. Thanks in advance.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
1. I don't know what they gave you to make damage over time, but you can just use the same spells with -X damage instead of +X.

2. Jass.

3. you can either use waits based on Missle_Speed/Distance, or use a buff checker, or use one of those fancy (and probably useful :p) JASS systems people made.
 
Level 10
Joined
Jun 10, 2007
Messages
557
JASS:
function Trig_NaturesgifthealJASS_Actions takes nothing returns nothing
local unit caster = GetTriggerUnit()
local unit target = GetSpellTargetUnit()
local real damage = GetHeroInt( caster, true ) x 2
local integer counter = 0
loop
    exitwhen ( counter > 7 or GetUnitState( target, UNIT_STATE_LIFE ) < 0.405 )
    call UnitDamageTarget( caster, target, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS )
    call TriggerSleepAction( 1.00 )
    set counter = counter + 1
endloop
set caster = null
set target = null
endfunction

And... um, what do you mean by -X? WorldEdit didn't like -x 2, or x -2, or anything else I tried. You're being kinda vague.
 
Level 29
Joined
Jul 29, 2007
Messages
5,174
I mean that if for example you damage the units for 2*Int of the hero, to heal you'll damage -2*Int of your hero.

So his life is set to Life--(2*Int) and we all know that [- * - = +].

In your case, you'll do "-damage".
If it isn't working then you can always do:

local real damage = GetHeroInt( caster, true ) x 2
damage = damage*-1

(not sure if this is the way to write it in JASS)

Or is it just "local real damage = ((GetHeroInt( caster, true ) x 2) * (-1))" ?
 
Level 11
Joined
Aug 25, 2006
Messages
971
Your misunderstanding him Ghostwolf, he doesn't want to do damage, he wants to heal damage. In otherwords he wants to heal. Hes not interested in a reverse heal, hes interested in a sort of reverse breath of fire. (From what I understand)
 
Level 3
Joined
Mar 24, 2007
Messages
51
2: u can use auras for that easily, make an aura that only affects summoned units of ur own, then when the summon is casted use triggers or JASS to add abilities that increase health to him, and then to increase damage just make ur aura then make the game check if someone is with that aura then add attack and armor to it ^^ (the aura is just a dummy buffing skill)

edit: just thought about 1
1: use that heal and wait stuff, but with local variables to refer to the targeted unit by spell

edit2: about the cone effect for 1
1: just use the unit has buff condition, then set it to the same thing as above, heal an amount based on int, then wait 1 sec, then heal the same amount, if the duration is not changed with int too of course, if it is, then u'd have to make a loop, with an integer value that goes reducing with each time the loop is runned, then when it reaches to 0 it exit (JASS coding necessary, can't think about a way of doing it for GUI Friendly to u)
 
Level 10
Joined
Jun 10, 2007
Messages
557
Yeah, negative damage, effectively a heal. Here's the list of what I need/want but can't code: rejuvination, healing breath of fire, healing blizzard, and healing wave. With the JASS code given to me I don't know how to make it so that I can deal negative damage.

The reason attribute-based spells are so critical is because heroes don't level up in the map, but they improve through stats given by items and tomes, while the enemies gradually do improve.

EDIT: @^ Good ideas there... but for the last one, would that have a periodic event? If so, how would you refer to the casting unit's stats if it's a separate trigger?
 
Level 11
Joined
Aug 25, 2006
Messages
971
To do negative damage from a spell just do the following:
Say you want to make breath of fire do negative damage. Hold shift and double click the damage field. This will bring up the box where you can modify the damage. Put in a negative number and hit ok.
 
Level 11
Joined
Feb 22, 2006
Messages
752
Redeemer59 said:
reverse Blizzard (heals over time in waves

Just use healing spray.

Redeemer59 said:
And, if it wouldn't be too much trouble, healing wave.

I'm not sure what you mean. Can't you use take the healing wave spell and modify that? If you want "reverse" healing wave (does dmg instead of heal), just use chain lightning.

Redeemer59 said:
I'd like to know how to give summoned units more health and damage based on their summoner's attributes. Like, for every point of Intelligence, they have 5 extra health and deal 1 extra damage, or something along those lines

Use Weaddar's (I'm sure i just butchered his name) BonusMod system. Download it from Vexorian's InvX 2.0 here:

http://www.wc3campaigns.net/showthread.php?t=87132&highlight=invx

If you're wondering why I'm telling you to get it from InvX, it's cuz Vexorian optimized it, and the standalone doesn't include the optimizations. It basically works the same way though. If you want the standalone:

http://www.wc3jass.com/viewtopic.php?t=202

The setup is kind of a pain cuz you have to make like 40 different abilities. But once you've got it set up you can give units basically (almost) any bonus you want - to their attack, armor, mana, hero attributes. So just implement BonusMod, then have a trigger detect when a unit is summoned, get the attributes of the casting unit and use those attributes to apply the necessary bonuses to the summoned unit.
 
Level 10
Joined
Jun 10, 2007
Messages
557
I don't think we're on the same page; I want the listed abilities to heal an amount based on the primary attribute of the casting hero. The higher your Intelligence, the more is healed, etc. The problem with healing wave is the bouncing, the problem with the healing-breath of fire is the cone shaped area of effect, the problem with the healing-blizzard is the fact that it deals/heals damage in successive bursts (waves), and I can't coordinate said healing with the visual effects.

And azn, I'm sorry, but I dl'd that, and I can't make any sense out of it at all. o_O;
 
Level 3
Joined
Mar 24, 2007
Messages
51
a periodic event would do what i said, but that's just a way of doing it, there are others, but this is the most simple of'em, if u're doing a periodic event remember to adjust the spell duration to duration u want + periodic intervals time + 0.01, so if the periodic starts the heal won't come 1 periodic lesser and put the "delay" between the healing times higher then the periodic timer, these tips are important to correct minor bugs of periodic events that may happen, depending on how u're gonna do it

edit: another way would be making a dummy unit with a dummy aura buff, so u can area heal everyone, for this i'd recommend using: "unit starts the effect of an ability" (that's how it's called in GUI i guess), then just making the actions to happen, using locals to become MUI
 
Level 11
Joined
Feb 22, 2006
Messages
752
Redeemer59 said:
I don't think we're on the same page; I want the listed abilities to heal an amount based on the primary attribute of the casting hero.

Oh, OK, now I understand.

For the cone effect, it's actually quite simple (from my point of view anyway):

JASS:
function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real //A remake of Blizzard's AngleBetweenPoints() function only with coordinates instead of locations and more efficient
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction

function Heal_Over_Time takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local group g = GetHandleGroup( t, "g2" ) //save "g2" (the group with units in it) to local variable g
    local group g2 = GetHandleGroup( t, "g" ) //save "g" (empty group) to local variable g2
    //yes, I know the names are confusing, but it will make sense later
    local unit u
    local real tohealsmall = GetHandleReal( t, "tohealsmall" )
    local real life
    local real maxlife
    //once again, we loop through every unit in group g (remember, it was saved as g2 in the timer so this is the group with units in it)
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set life = GetUnitState( u, UNIT_STATE_LIFE )
        set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
        if ( life >= 0.405 ) then //make sure the unit is alive; no sense in healing dead units
            if ( life + tohealsmall >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + tohealsmall )
            endif
            call GroupAddUnit( g2 ) //save the unit to g2 (empty group), so that the trigger may use it later
        endif
        call GroupRemoveUnit( g )
     endloop
     //this is why i swapped the variable names, so that it makes more sense here when I swap the saved handles (why do I have to swap them? because the function assumes the group saved as "g2" is the one with units in it - take a look at the local var declarations at the beginning)
     call SetHandleHandle( t, "g2", g2 )
     call SetHandleHandle( t, "g", g )
     //cleanup
     set t = null
     set g = null
     set g2 = null
endfunction

function Cone_Of_Healing_Actions takes nothing returns nothing
    local timer t = CreateTimer()
    local group g = CreateGroup()
    local group g2 = CreateGroup()
    local unit caster = GetTriggerUnit()
    local unit u
    local player owner = GetOwningPlayer( caster )
    local real casterx = GetUnitX( caster )
    local real castery = GetUnitY( caster )
    local real facing = GetUnitFacing( caster )
    local real angle
    local real life
    local real maxlife
    local real toheal = GetHeroInt( caster, true ) * 50.00 //this is how much the initial heal is
    local real tohealsmall = GetHeroInt( caster, true ) * 5.00 //this is how much it heals every interval for the healing over time
    local real distance = 250.00 //How far you want the "cone" to project from the caster
    call GroupEnumUnitsInRange( g, casterx, castery, distance, null ) //We select all units around the caster within a radius equal to the distance specified
    //Now we loop through every unit in the group
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set angle = GetAngleBetweenPoints( casterx, castery, GetUnitX( u ), GetUnitY( u ) ) //We get the angle between the selected unit and the caster
        if ( IsUnitAlly( u, owner ) and ( angle - facing ) <= 45.00 and ( angle - facing ) >= -45.00 ) then //Weed out enemies (you don't want to heal those surely) and also any units whose angle they make with the caster do not fall within a certain interval (here its -45.00 to 45.00 degrees with the caster's facing angle as 0.00 ). In essence, we weed out any units not within a cone defined by two straight lines projecting out from the caster's position - one x degrees to the right of the caster's facing angle and one x degrees to the left - and also the border of the circle with center at the caster and radius equal to the distance specified earlier.
            set life = GetUnitState( u, UNIT_STATE_LIFE )
            set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
            if ( life + toheal >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + toheal )
            endif
            call GroupAddUnit( g2, u ) //we add all units that pass our test to a second unit group to be used later for the healing over time
        endif
        call GroupRemoveUnit( g, u )
    endloop
    call TimerStart( t, 1.00, true, function Heal_Over_Time ) //start the timer that controls when the healing over time actions occur (you could use loops and TriggerSleepAction() and that would be more efficient, but timers are more accurate and they allow for waits less than 0.27 seconds)
    call SetHandleHandle( t, "g", g ) //save the group g (now empty) to the timer...why am i saving an empty group? Look at the timer's callback function and you will see.
    call SetHandleHandle( t, "g2", g2 ) //save the group g2 (now with all the units to be healed over time) to the timer
    call SetHandleReal( t, "tohealsmall", tohealsmall ) //save the amount of healing to be done on each interval to the timer
    call TriggerSleepAction( 5.10 ) //How long you want the healing over time to last (it's best to add 0.10 seconds to make sure the timer fires correctly the last time)
    //cleanup
    call DestroyGroup( g )
    call DestroyGroup( g2 )
    call FlushHandleLocals( t )
    call DestroyTimer( t )
    set t = null
    set g = null
    set g2 = null
    set caster = null
endfunction

You need a trigger that detects when your cone of healing ability is cast and that has the function Cone_Of_Healing_Actions as its triggeraction.
 
Level 3
Joined
Mar 24, 2007
Messages
51
hum... quite big at the first view but when u start reading that JASS code it's really simple, good job ^^, and whithout leaks too
 
Level 11
Joined
Feb 22, 2006
Messages
752
Redeemer59 said:
And azn, I'm sorry, but I dl'd that, and I can't make any sense out of it at all. o_O;

The system is quite simple. Without going into how the script itself works, basically you take a bunch of passive abilities that give bonuses (for attack, hp, mana, etc.) and make it so that each of the types of custom bonus abilities have bonuses that are powers of 2.

So for example, you would have abilities that increase damage by 1 (2^0), 2 (2^1), 4 (2^2), 8 (2^3)...and so on up until a certain point. You would also do the same for armor, hp and mana (only for hp and mana you multiply by 10 so its 10, 20, 40, 80...).

Why multiples of two? Well because if I were to give you just powers of 2 up to a certain number x, you could make ANY integer from 1 to 2x-1 by adding together certain combinations of the numbers I gave you, using no number more than once. (don't believe me? Try to make any number from 1-15 by adding together any numbers from the set: 1, 2, 4, 8). Because of this cool property, with just 10 custom abilities for, let's say attack (+1, +2, +4, all the way to +512), you can give a unit ANY integer attack bonus from 1 all the way to 1023. Think about it; only 10 abilities instead of 1023: that's pretty nice. And, if you change the last ability from +512 to -512 (like it does in the BonusMod system), you can also now have NEGATIVE bonuses all the way down to -512 (though you do also decrease your max bonus from 1023 to 511).

So, once you have those abilities, you can now make bonuses for attack and armor and hero attributes of a large range of integer values and bonuses for hp and mana for a large range of integer multiples of 10. And all you have to do, is call a function already set up by the BonusMod system where you tell it what unit to apply the bonus to and how much for each type of bonus (attack, armor, etc.).

If you're having trouble setting up the abilities, just open up that InvX map I linked you to, go into Object Editor, and go to Abilities. In the custom abilities tree, you will find a section (sry, forgot which race exactly) full of custom unit abilities made for BonusMod. Just look at how it's set up and do the exact same thing on your map. You can even just copy-paste the abilities from InvX into yours.
 
Level 11
Joined
Aug 25, 2006
Messages
971
Personally I think you overused gamecache. Learn how to use vJass, structs really kick @$$.
Yea it took me a bit to understand BonusMod. Its so much simpler if you think of everything as binary.
1 = 00001
2 = 00010
4 = 00100
8 = 01000
16 = 10000
Notice how you you can make any combination of ones and zeros because each number there is a one in a different position. So you can easily add them together to get any number.
 
Level 10
Joined
Jun 10, 2007
Messages
557
Oh, OK, now I understand.

For the cone effect, it's actually quite simple (from my point of view anyway):

JASS:
code

You need a trigger that detects when your cone of healing ability is cast and that has the function Cone_Of_Healing_Actions as its triggeraction.

Okay, I did everything said here, and I got an error... deleting all of the commentary (//text) reduced the amount of 'compilation errors', but still.

Do I need to create variables "g" "g2" "u" "t" etc.?

Sorry for bad image quality but I was stupid and saved it as a .gif instead of .jpg
 

Attachments

  • jasserror.GIF
    jasserror.GIF
    21.2 KB · Views: 130
Level 11
Joined
Aug 25, 2006
Messages
971
Ok... Behold!
I fixed 2 errors where he specified a group but not a unit (In GroupRemoveUnit() and GroupAddUnit())
I also included kattana's system that he used (And I don't think he told you about). This one should work:
JASS:
// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

// ===========================
function LocalVars takes nothing returns gamecache
    // Replace InitGameCache("jasslocalvars.w3v") with a global variable!!
    return InitGameCache("jasslocalvars.w3v")
endfunction

function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleGroup takes handle subject, string name returns group
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleLightning takes handle subject, string name returns lightning
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleWidget takes handle subject, string name returns widget
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real //A remake of Blizzard's AngleBetweenPoints() function only with coordinates instead of locations and more efficient
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction

function Heal_Over_Time takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local group g = GetHandleGroup( t, "g2" ) //save "g2" (the group with units in it) to local variable g
    local group g2 = GetHandleGroup( t, "g" ) //save "g" (empty group) to local variable g2
    //yes, I know the names are confusing, but it will make sense later
    local unit u
    local real tohealsmall = GetHandleReal( t, "tohealsmall" )
    local real life
    local real maxlife
    //once again, we loop through every unit in group g (remember, it was saved as g2 in the timer so this is the group with units in it)
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set life = GetUnitState( u, UNIT_STATE_LIFE )
        set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
        if ( life >= 0.405 ) then //make sure the unit is alive; no sense in healing dead units
            if ( life + tohealsmall >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + tohealsmall )
            endif
            call GroupAddUnit( g2, u ) //save the unit to g2 (empty group), so that the trigger may use it later
        endif
        call GroupRemoveUnit( g, u )
     endloop
     //this is why i swapped the variable names, so that it makes more sense here when I swap the saved handles (why do I have to swap them? because the function assumes the group saved as "g2" is the one with units in it - take a look at the local var declarations at the beginning)
     call SetHandleHandle( t, "g2", g2 )
     call SetHandleHandle( t, "g", g )
     //cleanup
     set t = null
     set g = null
     set g2 = null
endfunction

function Cone_Of_Healing_Actions takes nothing returns nothing
    local timer t = CreateTimer()
    local group g = CreateGroup()
    local group g2 = CreateGroup()
    local unit caster = GetTriggerUnit()
    local unit u
    local player owner = GetOwningPlayer( caster )
    local real casterx = GetUnitX( caster )
    local real castery = GetUnitY( caster )
    local real facing = GetUnitFacing( caster )
    local real angle
    local real life
    local real maxlife
    local real toheal = GetHeroInt( caster, true ) * 50.00 //this is how much the initial heal is
    local real tohealsmall = GetHeroInt( caster, true ) * 5.00 //this is how much it heals every interval for the healing over time
    local real distance = 250.00 //How far you want the "cone" to project from the caster
    call GroupEnumUnitsInRange( g, casterx, castery, distance, null ) //We select all units around the caster within a radius equal to the distance specified
    //Now we loop through every unit in the group
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set angle = GetAngleBetweenPoints( casterx, castery, GetUnitX( u ), GetUnitY( u ) ) //We get the angle between the selected unit and the caster
        if ( IsUnitAlly( u, owner ) and ( angle - facing ) <= 45.00 and ( angle - facing ) >= -45.00 ) then //Weed out enemies (you don't want to heal those surely) and also any units whose angle they make with the caster do not fall within a certain interval (here its -45.00 to 45.00 degrees with the caster's facing angle as 0.00 ). In essence, we weed out any units not within a cone defined by two straight lines projecting out from the caster's position - one x degrees to the right of the caster's facing angle and one x degrees to the left - and also the border of the circle with center at the caster and radius equal to the distance specified earlier.
            set life = GetUnitState( u, UNIT_STATE_LIFE )
            set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
            if ( life + toheal >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + toheal )
            endif
            call GroupAddUnit( g2, u ) //we add all units that pass our test to a second unit group to be used later for the healing over time
        endif
        call GroupRemoveUnit( g, u )
    endloop
    call TimerStart( t, 1.00, true, function Heal_Over_Time ) //start the timer that controls when the healing over time actions occur (you could use loops and TriggerSleepAction() and that would be more efficient, but timers are more accurate and they allow for waits less than 0.27 seconds)
    call SetHandleHandle( t, "g", g ) //save the group g (now empty) to the timer...why am i saving an empty group? Look at the timer's callback function and you will see.
    call SetHandleHandle( t, "g2", g2 ) //save the group g2 (now with all the units to be healed over time) to the timer
    call SetHandleReal( t, "tohealsmall", tohealsmall ) //save the amount of healing to be done on each interval to the timer
    call TriggerSleepAction( 5.10 ) //How long you want the healing over time to last (it's best to add 0.10 seconds to make sure the timer fires correctly the last time)
    //cleanup
    call DestroyGroup( g )
    call DestroyGroup( g2 )
    call FlushHandleLocals( t )
    call DestroyTimer( t )
    set t = null
    set g = null
    set g2 = null
    set caster = null
endfunction

BTW: If it gives errors, the first error is the problem, everything else is just the syntax checker going crazy. So don't delete the comments, its really a waste of your time.

Follow his original instructions of use..

If you still have problems, just post the map and I (or if I'm not online, someone else) will put it in the map for you...
 
Level 11
Joined
Aug 25, 2006
Messages
971
First make a trigger named
"Cone Of Healing"
EXACTLY (Without double quotes)
Then convert to custom text.
Then replace everything you see with the code above.
 
Level 11
Joined
Aug 25, 2006
Messages
971
Ok I added some more code he should have included, and even fixed the code for a messed up function name.
JASS:
constant function ReturnAbilId takes nothing returns integer
    return 'A000' //THIS NEEDS TO BE CHANGED to the rawcode of your spell!
endfunction

// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction

// ===========================
function LocalVars takes nothing returns gamecache
    // Replace InitGameCache("jasslocalvars.w3v") with a global variable!!
    return InitGameCache("jasslocalvars.w3v")
endfunction

function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction

function SetHandleInt takes handle subject, string name, integer value returns nothing
    if value==0 then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleBoolean takes handle subject, string name, boolean value returns nothing
    if value==false then
        call FlushStoredBoolean(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreBoolean(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function SetHandleString takes handle subject, string name, string value returns nothing
    if value==null then
        call FlushStoredString(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreString(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction

function GetHandleHandle takes handle subject, string name returns handle
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleInt takes handle subject, string name returns integer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleBoolean takes handle subject, string name returns boolean
    return GetStoredBoolean(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction
function GetHandleString takes handle subject, string name returns string
    return GetStoredString(LocalVars(), I2S(H2I(subject)), name)
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTimer takes handle subject, string name returns timer
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleTrigger takes handle subject, string name returns trigger
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleEffect takes handle subject, string name returns effect
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleGroup takes handle subject, string name returns group
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleLightning takes handle subject, string name returns lightning
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction
function GetHandleWidget takes handle subject, string name returns widget
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction

function FlushHandleLocals takes handle subject returns nothing
    call FlushStoredMission(LocalVars(), I2S(H2I(subject)) )
endfunction

function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real //A remake of Blizzard's AngleBetweenPoints() function only with coordinates instead of locations and more efficient
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction

function Heal_Over_Time takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local group g = GetHandleGroup( t, "g2" ) //save "g2" (the group with units in it) to local variable g
    local group g2 = GetHandleGroup( t, "g" ) //save "g" (empty group) to local variable g2
    //yes, I know the names are confusing, but it will make sense later
    local unit u
    local real tohealsmall = GetHandleReal( t, "tohealsmall" )
    local real life
    local real maxlife
    //once again, we loop through every unit in group g (remember, it was saved as g2 in the timer so this is the group with units in it)
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set life = GetUnitState( u, UNIT_STATE_LIFE )
        set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
        if ( life >= 0.405 ) then //make sure the unit is alive; no sense in healing dead units
            if ( life + tohealsmall >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + tohealsmall )
            endif
            call GroupAddUnit( g2, u ) //save the unit to g2 (empty group), so that the trigger may use it later
        endif
        call GroupRemoveUnit( g, u )
     endloop
     //this is why i swapped the variable names, so that it makes more sense here when I swap the saved handles (why do I have to swap them? because the function assumes the group saved as "g2" is the one with units in it - take a look at the local var declarations at the beginning)
     call SetHandleHandle( t, "g2", g2 )
     call SetHandleHandle( t, "g", g )
     //cleanup
     set t = null
     set g = null
     set g2 = null
endfunction

function Cone_Of_Healing_Actions takes nothing returns nothing
    local timer t = CreateTimer()
    local group g = CreateGroup()
    local group g2 = CreateGroup()
    local unit caster = GetTriggerUnit()
    local unit u
    local player owner = GetOwningPlayer( caster )
    local real casterx = GetUnitX( caster )
    local real castery = GetUnitY( caster )
    local real facing = GetUnitFacing( caster )
    local real angle
    local real life
    local real maxlife
    local real toheal = GetHeroInt( caster, true ) * 50.00 //this is how much the initial heal is
    local real tohealsmall = GetHeroInt( caster, true ) * 5.00 //this is how much it heals every interval for the healing over time
    local real distance = 250.00 //How far you want the "cone" to project from the caster
    call GroupEnumUnitsInRange( g, casterx, castery, distance, null ) //We select all units around the caster within a radius equal to the distance specified
    //Now we loop through every unit in the group
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set angle = GetAngleBetweenPoints( casterx, castery, GetUnitX( u ), GetUnitY( u ) ) //We get the angle between the selected unit and the caster
        if ( IsUnitAlly( u, owner ) and ( angle - facing ) <= 45.00 and ( angle - facing ) >= -45.00 ) then //Weed out enemies (you don't want to heal those surely) and also any units whose angle they make with the caster do not fall within a certain interval (here its -45.00 to 45.00 degrees with the caster's facing angle as 0.00 ). In essence, we weed out any units not within a cone defined by two straight lines projecting out from the caster's position - one x degrees to the right of the caster's facing angle and one x degrees to the left - and also the border of the circle with center at the caster and radius equal to the distance specified earlier.
            set life = GetUnitState( u, UNIT_STATE_LIFE )
            set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
            if ( life + toheal >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + toheal )
            endif
            call GroupAddUnit( g2, u ) //we add all units that pass our test to a second unit group to be used later for the healing over time
        endif
        call GroupRemoveUnit( g, u )
    endloop
    call TimerStart( t, 1.00, true, function Heal_Over_Time ) //start the timer that controls when the healing over time actions occur (you could use loops and TriggerSleepAction() and that would be more efficient, but timers are more accurate and they allow for waits less than 0.27 seconds)
    call SetHandleHandle( t, "g", g ) //save the group g (now empty) to the timer...why am i saving an empty group? Look at the timer's callback function and you will see.
    call SetHandleHandle( t, "g2", g2 ) //save the group g2 (now with all the units to be healed over time) to the timer
    call SetHandleReal( t, "tohealsmall", tohealsmall ) //save the amount of healing to be done on each interval to the timer
    call TriggerSleepAction( 5.10 ) //How long you want the healing over time to last (it's best to add 0.10 seconds to make sure the timer fires correctly the last time)
    //cleanup
    call DestroyGroup( g )
    call DestroyGroup( g2 )
    call FlushHandleLocals( t )
    call DestroyTimer( t )
    set t = null
    set g = null
    set g2 = null
    set caster = null
endfunction

function Cx takes nothing returns boolean
return ( GetSpellAbilityId() == ReturnAbilId() )
endfunction

//===========================================================================
function InitTrig_Cone_Of_Healing takes nothing returns nothing
    set gg_trg_Cone_Of_Healing = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Cone_Of_Healing, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Cone_Of_Healing, Condition( function Cx ) )
    call TriggerAddAction( gg_trg_Cone_Of_Healing, function Cone_Of_Healing_Actions )
endfunction
Ok delete/remake the trigger named "Cone Of Healing", convert to custom text, and paste the above over everything you see. Then goto the object editor find your ability. Press ctrl+d you should see something like 'A001' or 'A000' or something like that, where your spell's name was originally. See the part (on the top of all my jass) where it says = 'A000', replace the A000 with whatever you saw for your ability.
 
Level 10
Joined
Jun 10, 2007
Messages
557
Ok I added some more code he should have included, and even fixed the code for a messed up function name.
JASS:
code
Ok delete/remake the trigger named "Cone Of Healing", convert to custom text, and paste the above over everything you see. Then goto the object editor find your ability. Press ctrl+d you should see something like 'A001' or 'A000' or something like that, where your spell's name was originally. See the part (on the top of all my jass) where it says = 'A000', replace the A000 with whatever you saw for your ability.


Hooboy...

WorldEdit let me save, let me enable it, etc., but when I tried it in game, it was pretty ineffective. It didn't seem to do anything. The buff only appeared on the target unit, if there was no target, it didn't do anything at all. I DID make sure to set the ID to the ability's ID, I double checked it, and it still didn't function. EDIT: Actually, going back and setting numbers to obscene... well... numbers, it does work, but only on the target... still a rather big problem, though.

Now I feel like I wasted your time... :sad:
 
Level 11
Joined
Aug 25, 2006
Messages
971
No its ok, I should have checked that the trigger worked before I gave it to. Anyway, I'll make it work then post it again.

I assumed the code he wrote, once implemented would work!

I'm rebuilding several parts of his code with a completely different structure.
 
Last edited:
Level 11
Joined
Aug 25, 2006
Messages
971
Finally, done.
If you have JassNewGenPack uncomment the first 6 lines in 'Cone Of Healing' and copy the trigger to your map. If you don't have the pack, copy all the triggers under 'TheTrigger' to your map and save. If you wait another 10 - 20 minutes I'll add some fun SFX. You can test the ability by playing the map. (Cast the spell at a unit while holding alt)

Suggestion: Make your ability able to target units only meaning, make it unable to target the ground. Why? Because if you tell him to cast behind himself he will only be half way done turning before he casts it. This will make him cast in the wrong direction. I built a small bit of code that automatically detects if you've targeted a unit or the ground. If you targeted the ground, it takes its best guess. If you targeted the unit, it automatically compensates and casts the spell torwards that unit.

UPCOMING: SFX version!

Note: Its taking me longer because I've decided to completely and totally rebuild it. The one bellow works, but its MUI is slightly bugged. I'm rebuilding it the way it should have been done from the start. Just make sure you have vJass, because structs = fun. Gamecache = @#&^

Originally I was keeping something that was still based off of his scheme. Now I'm building my own damn scheme...


I've decided to use a linked list of structs to make it all work.
 

Attachments

  • BHEOLD.w3x
    19.5 KB · Views: 57
Last edited:
Level 11
Joined
Feb 22, 2006
Messages
752
wd40bomber7 said:
Personally I think you overused gamecache. Learn how to use vJass, structs really kick @$$.

Ugh...I know how to use vJass, but once again, I did it with gamecache so he didn't have to dl any preprocessors or JNGP, but w/e.

I've redid it using vJass and completely avoided gamecache. However, now you require Jasshelper or Jass NewGen pack to compile the script. You will also need CSCache (since I used CSData to attach the struct to the timer, thereby avoiding even ONE instance of gamecache...unless you have more than 8191 handles in your map).

JASS:
scope ConeOfHealing

globals
    private constant integer abilid = 'A000' //change this to match raw code in your map
endglobals

//configuration functions below, change returns to w/e you want (btw the integer int is supposed to be the caster's intelligence)

private constant function ToHeal takes integer int returns real
    return int * 50.00
endfunction

private constant function ToHealSmall takes integer int returns real
    return int * 5.00
endfunction

private constant function Distance takes integer int returns real
    return 250.00
endfunction

private constant function AngleInterval takes integer int returns real
    return 45.00 //this is actually half the interval, since the script would actually check from -45.00 to 45.00 degrees
endfunction

private constant function HealInterval takes integer int returns real
    return 1.00 //How often healing over time occurs
endfunction

private constant function Duration takes integer int returns real
    return 5.10 //How long you want the healing over time to last (it's best to add 0.10 seconds to make sure the timer fires correctly the last time)
endfunction

//====================================================================================

struct coneofhealingdata
    timer t = CreateTimer()
    group g = CreateGroup()
    group g2 = CreateGroup()
    real tohealsmall
    method onDestroy takes nothing returns nothing
        call DestroyTimer( t )
        call DestroyGroup( g )
        call DestroyGroup( g2 )
    endmethod
endstruct

function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real //A remake of Blizzard's AngleBetweenPoints() function only with coordinates instead of locations and more efficient
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction

function Cone_Of_Healing_Conditions takes nothing returns boolean
    return ( GetSpellAbilityId() == abilid )
endfunction

function Heal_Over_Time takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local coneofhealingdata data = GetCSData( t )
    local group g = data.g2
    local group g2 = data.g
    local unit u
    local real life
    local real maxlife
    //once again, we loop through every unit in group g (remember, it was saved as g2 in the timer so this is the group with units in it)
    loop
        set u = FirstOfGroup( g )
        exitwhen ( u == null )
        set life = GetUnitState( u, UNIT_STATE_LIFE )
        set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
        if ( life >= 0.405 ) then //make sure the unit is alive; no sense in healing dead units
            if ( life + tohealsmall >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + tohealsmall )
            endif
            call GroupAddUnit( g2, u ) //save the unit to g2 (empty group), so that the trigger may use it later
        endif
        call GroupRemoveUnit( g, u )
     endloop
     //this is why i swapped the variable names, so that it makes more sense here when I swap the saved handles (why do I have to swap them? because the function assumes the group saved as "g2" is the one with units in it - take a look at the local var declarations at the beginning)
     set data.g = g
     set data.g2 = g2
     //cleanup
     set t = null
     set g = null
     set g2 = null
endfunction

function Cone_Of_Healing_Actions takes nothing returns nothing
    local coneofhealingdata data = coneofhealingdata.create()
    local unit caster = GetTriggerUnit()
    local unit target = GetSpellTargetUnit()
    local unit u
    local location targetloc = GetSpellTargetLoc()
    local player owner = GetOwningPlayer( caster )
    local real casterx = GetUnitX( caster )
    local real castery = GetUnitY( caster )
    local real targetx
    local real targety
    local real axis
    local real angle
    local real life
    local real maxlife
    local integer int = GetHeroInt( caster )
    if ( target == null ) then //check to see if spell was cast on target
        set targetx = GetLocationX( targetloc )
        set targety = GetLocationY( targetloc )
    else
        set targetx = GetUnitX( target )
        set targety = GetUnitY( target )
    endif
    set axis = GetAngleBetweenPoints( casterx, castery, targetx, targety )
    call GroupEnumUnitsInRange( data.g, casterx, castery, Distance( int ), null ) //We select all units around the caster within a radius equal to the distance specified
    //Now we loop through every unit in the group
    loop
        set u = FirstOfGroup( data.g )
        exitwhen ( u == null )
        set angle = GetAngleBetweenPoints( casterx, castery, GetUnitX( u ), GetUnitY( u ) ) //We get the angle between the selected unit and the caster
        if ( IsUnitAlly( u, owner ) and ( angle - axis ) <= AngleInterval( int ) and ( angle - axis ) >= ( 0 - AngleInterval( int ) ) ) then //Weed out enemies (you don't want to heal those surely) and also any units whose angle they make with the caster do not fall within a certain interval
            set life = GetUnitState( u, UNIT_STATE_LIFE )
            set maxlife = GetUnitState( u, UNIT_STATE_MAX_LIFE )
            if ( life + toheal >= maxlife ) then
                call SetUnitState( u, UNIT_STATE_LIFE, maxlife )
            else
                call SetUnitState( u, UNIT_STATE_LIFE, life + toheal )
            endif
            call GroupAddUnit( data.g2, u ) //we add all units that pass our test to a second unit group to be used later for the healing over time
        endif
        call GroupRemoveUnit( data.g, u )
    endloop
    call TimerStart( data.t, HealInterval( int ), true, function Heal_Over_Time ) //start the timer that controls when the healing over time actions occur (you could use loops and TriggerSleepAction() and that would be more efficient, but timers are more accurate and they allow for waits less than 0.27 seconds)
    call SetCSData( data.t, data )
    call TriggerSleepAction( Duration( int ) )
    //cleanup
    call data.destroy()
    set caster = null
endfunction

endscope

//====================================================================================

function InitTrig_Cone_Of_Healing takes nothing returns nothing
    set gg_trg_Cone_Of_Healing = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Cone_Of_Healing, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Cone_Of_Healing, Condition( function Cone_Of_Healing_Conditions ) )
    call TriggerAddAction( gg_trg_Cone_Of_Healing, function Cone_Of_Healing_Actions )
endfunction


Btw, I really don't know why the script wasn't working. From what you were describing it sounded like something wrong with the if-then statements within the loop in Cone_Of_Healing_Actions (but then again why would it work for the target unit???), but I double checked and I can't find anything wrong with them.
 
Level 11
Joined
Aug 25, 2006
Messages
971
Ok first, sorry for the double post. I really think this is worth it. (This is like my second double post ever.)

I completed the spell. It looks/works great. You need NewGenPack for it to work.

Aznricepuff when you log on I want your permission (after all about .01% of what you did is still in here) to claim nearly total credits for this spell.

If you don't have vJass you can use the above one, but its very buggy compared to this one. (Not to mention there aren't any SFX)

Heres the code, but you really need the stuff in the map.

Argh! what tags do I use to make hidden text?
JASS:
//VJASS REQUIRED!!!
globals
    real udg_Olg //healamount
    unit udg_Org //Unit pass
    real udg_Omg //Facing pos
    gamecache udg_GameC = InitGameCache("jasslocalvars.w3v")
    trigger TimerSx
    integer FirstStruct = -1
    integer LastStruct = -1
    group UnitsEffected = CreateGroup() //The only reason this is needed is so a unit can't be effected twice.

    //All Constants (No longer functions)
    constant integer EffectUnit = 'h000' //THIS NEEDS TO BE CHANGED TO THE rawcode of the sfx unit!
    constant integer ReturnAbilId = 'A000' //THIS NEEDS TO BE CHANGED to the rawcode of your spell!
    constant string EaBuff = "HolyAurora.MDX" //The buff of the spell
    constant real Factor_Level_1 = 40.0 //This will be multiplied with the casters INT to get the MAX damage healed
    constant real Factor_Level_2 = 60.0 //Level 2 of the ability
    constant real Factor_Level_3 = 80.0 //Level 3
    constant real TimerCycle = .1
    constant real SpellTime = 30.0 //Amount of time spell lasts on a unit 
    constant real ConeSize = 850.0 //How far the cone goes out from the caster, MUST BE AT LEAST 50 MORE THEN CASTING DISTANCE
    constant real ConeWideth = 30.0 //To get the actual width multiply the value here by 2. (ITs in degrees)
    
endglobals

struct Uni
    integer NextStruct
    integer PrevStruct
    real Timer
    effect SFX
    real HealValue
    unit Effecte
endstruct
function DestroyLink takes integer Struct returns nothing //This patches the linked list
local Uni Nar = Struct
local Uni Patched
    call DestroyEffect(Nar.SFX)
    call GroupRemoveUnit(UnitsEffected,Nar.Effecte)
    if not (Nar.PrevStruct == -1) then//These lines of code effectively patch the linked list
        set Patched = Nar.PrevStruct
        set Patched.NextStruct = Nar.NextStruct
    else
        set FirstStruct = Nar.NextStruct
    endif
    if not (Nar.NextStruct == -1) then
        set Patched = Nar.NextStruct
        set Patched.PrevStruct = Nar.PrevStruct
    else
        set LastStruct = Nar.PrevStruct
    endif
    call Nar.destroy() //Clean the struct!
endfunction


// ===========================
function H2I takes handle h returns integer
    return h
    return 0
endfunction
// ===========================
function LocalVars takes nothing returns gamecache
    return udg_GameC
endfunction
function SetHandleHandle takes handle subject, string name, handle value returns nothing
    if value==null then
        call FlushStoredInteger(LocalVars(),I2S(H2I(subject)),name)
    else
        call StoreInteger(LocalVars(), I2S(H2I(subject)), name, H2I(value))
    endif
endfunction
function SetHandleReal takes handle subject, string name, real value returns nothing
    if value==0 then
        call FlushStoredReal(LocalVars(), I2S(H2I(subject)), name)
    else
        call StoreReal(LocalVars(), I2S(H2I(subject)), name, value)
    endif
endfunction
function GetHandleReal takes handle subject, string name returns real
    return GetStoredReal(LocalVars(), I2S(H2I(subject)), name)
endfunction

function GetHandleUnit takes handle subject, string name returns unit
    return GetStoredInteger(LocalVars(), I2S(H2I(subject)), name)
    return null
endfunction


function GetAngleBetweenPoints takes real x1, real y1, real x2, real y2 returns real //A remake of Blizzard's AngleBetweenPoints() function only with coordinates instead of locations and more efficient
    return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction

    function CAT takes real Angle returns real //Used to take any number and make it into a valid angle!!
    local real NewAngle = Angle

        if NewAngle > 360 then
            loop
            exitwhen NewAngle < 360
                set NewAngle = NewAngle - 360
            endloop
        endif
        if NewAngle < 0 then
            loop
            exitwhen NewAngle > 0
                set NewAngle = NewAngle + 360
            endloop
        endif        
    return NewAngle
    endfunction

//Cast effect, its more complicated then it looks!
constant function TimS takes nothing returns real //These have to do with SFX timing, i wouldn't suggest messing with them
    return .4
endfunction
constant function TimR takes nothing returns real //I'd delete these, but if you change the animation, you might need these
    return TimS() + 0.0
endfunction
constant function TimE takes nothing returns real
    return TimS() + TimR() + 0.0
endfunction
constant function Step takes nothing returns real
    return 20.0 //Increasing this will increase the distance the graphic projects outward before its destruction
endfunction
//constant function TimeBeforeBuff takes nothing returns real
//    return .5
//endfunction
function StepForward takes unit u returns nothing
    local real x = GetUnitX(u) + Step() * Cos(GetUnitFacing(u) * bj_DEGTORAD)
    local real y = GetUnitY(u) + Step() * Sin(GetUnitFacing(u) * bj_DEGTORAD)
    call SetUnitX(u,x)
    call SetUnitY(u,y)
endfunction
function RunSFX takes nothing returns nothing
local timer Q = GetExpiredTimer()
local real Time = GetHandleReal(Q,"Time") + .01
local unit U = GetHandleUnit(Q,"uM")
    call SetHandleReal(Q,"Time",Time)
    if Time <= TimS() then
        call StepForward(U)
    endif
    if Time > TimS() and Time <= TimR() then
        call SetUnitTimeScale(U,0)
        call StepForward(U)
    endif
    if Time > TimR() and Time <= TimE() then
        call SetUnitTimeScale(U,1)
        call StepForward(U)
    endif
    if Time > TimE() then
        call SetHandleHandle(Q,"uM",null)
        call SetHandleReal(Q,"Time",0)
        call RemoveUnit(U)
        call PauseTimer(Q)
        call DestroyTimer(Q)
    endif
    
endfunction
function RunOutWardEffect takes nothing returns nothing
local unit u = GetSpellAbilityUnit()
local real ux = GetUnitX( u )
local real uy = GetUnitY( u )
local unit Nex = GetSpellTargetUnit()
local real ANG
local unit Jar
local timer Q = CreateTimer()
    if Nex != null then
        set ANG = CAT(GetAngleBetweenPoints(ux,uy,GetUnitX(Nex),GetUnitY(Nex)))
    else
        set ANG = GetUnitFacing( u )
    endif
    set Jar = CreateUnit(Player(14),EffectUnit,ux,uy,ANG)
    call SetHandleHandle(Q,"uM",Jar)
    call TimerStart(Q,.01,true,function RunSFX)
    set u = null
    set Jar = null
endfunction    

function RegisterHeal takes nothing returns nothing
local unit u = GetEnumUnit()
local Uni Nar = Uni.create()
local Uni Sa
local effect Ka = AddSpecialEffectTarget(EaBuff,u,"origin")
    if LastStruct == -1 then //No other structs, time to build the first link in the list
        set LastStruct = Nar 
        set FirstStruct = Nar
        set Nar.PrevStruct = -1
    else //Otherwise we need to tell it were the new last struct
        set Sa = LastStruct
        set Sa.NextStruct = Nar //Link up
        set Nar.PrevStruct = Sa
        set Nar.NextStruct = -1
        set LastStruct = Nar
    endif
    set Nar.SFX = Ka
    set Nar.HealValue = udg_Olg
    set Nar.Timer = SpellTime
    set Nar.Effecte = u
    set Ka = null
    set u = null   
endfunction

function FacingCorrect takes nothing returns boolean
local unit u = GetFilterUnit()
local real casterx = GetUnitX( udg_Org )
local real castery = GetUnitY( udg_Org )
local real angle = GetAngleBetweenPoints( casterx, castery, GetUnitX( u ), GetUnitY( u ) )
local boolean RetVal
    set RetVal = (( CAT(angle) - udg_Omg ) >= 0-ConeWideth ) and  ( IsUnitAlly( udg_Org, GetOwningPlayer(u) )) and (( CAT(angle) - udg_Omg ) <= ConeWideth)
    set u = null
    return RetVal
endfunction
function HealU takes nothing returns nothing
local real life
local real maxlife
local unit u = GetEnumUnit()
    set life = GetWidgetLife(u)
    set maxlife = GetUnitState(u, UNIT_STATE_MAX_LIFE)
    if ( life >= 0 ) then 
        if ( life + udg_Olg >= maxlife ) then
            call SetWidgetLife(u,maxlife)
        else
            call SetWidgetLife(u, life + udg_Olg)
        endif
    endif
    set u = null
endfunction
function Cone_Of_Healing_Actions takes nothing returns nothing
    local group g = CreateGroup()
    local unit caster = GetSpellAbilityUnit()
    local player owner = GetOwningPlayer( caster )
    local real casterx = GetUnitX( caster )
    local real castery = GetUnitY( caster ) //this is how much the initial heal is
    local real tohealsmall
    local unit Nex = GetSpellTargetUnit()
    
    
    if GetUnitAbilityLevel(caster,ReturnAbilId) == 1 then
        set tohealsmall = (((GetHeroInt( caster, true ) * Factor_Level_1) * TimerCycle) / SpellTime)
    elseif GetUnitAbilityLevel(caster,ReturnAbilId) == 2 then
        set tohealsmall = (((GetHeroInt( caster, true ) * Factor_Level_2) * TimerCycle) / SpellTime)
    elseif GetUnitAbilityLevel(caster,ReturnAbilId) == 3 then
        set tohealsmall = (((GetHeroInt( caster, true ) * Factor_Level_3) * TimerCycle) / SpellTime)
    endif    
    if Nex != null then
        set udg_Omg = CAT(GetAngleBetweenPoints(casterx,castery,GetUnitX(Nex),GetUnitY(Nex)))
    else
        set udg_Omg = GetUnitFacing( caster )
    endif
   
    set udg_Org = caster
    call GroupEnumUnitsInRange( g, casterx, castery, ConeSize, Filter(function FacingCorrect) )
    call GroupRemoveGroup(UnitsEffected,g)
    set udg_Olg = tohealsmall
    call ForGroup(g, function HealU)
    call ForGroup(g, function RegisterHeal)
    
    call GroupAddGroup(g,UnitsEffected)
    call EnableTrigger(TimerSx)
    
    call DestroyGroup(g)
    set g = null
    set caster = null
endfunction

function Cx takes nothing returns boolean
return ( GetSpellAbilityId() == ReturnAbilId )
endfunction

function MakeStuffHeal takes nothing returns nothing
local Uni CurStruct
local Uni BurnStruct
local real life
local real maxlife
    if FirstStruct == -1 then
        call DisableTrigger(TimerSx)
    else
        set CurStruct = FirstStruct
        loop
        exitwhen CurStruct == -1

            set life = GetWidgetLife(CurStruct.Effecte)
            set maxlife = GetUnitState(CurStruct.Effecte, UNIT_STATE_MAX_LIFE)
            if ( life >= 0 ) then 
                if ( life + CurStruct.HealValue >= maxlife ) then
                    call SetWidgetLife(CurStruct.Effecte,maxlife)
                else
                    call SetWidgetLife(CurStruct.Effecte, life + CurStruct.HealValue)
                endif
            endif
            set CurStruct.Timer = CurStruct.Timer - TimerCycle
            if CurStruct.Timer <= 0 then
                set BurnStruct = CurStruct
                set CurStruct = CurStruct.NextStruct
                call DestroyLink(BurnStruct)
            else
                set CurStruct = CurStruct.NextStruct
            endif
        endloop
        //if FirstStruct == -1 then
        //    set LastStruct = -1
        //endif
    endif
endfunction
//===========================================================================
function InitTrig_Cone_Of_Healing takes nothing returns nothing
    set gg_trg_Cone_Of_Healing = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Cone_Of_Healing, EVENT_PLAYER_UNIT_SPELL_CAST )
    call TriggerAddCondition( gg_trg_Cone_Of_Healing, Condition( function Cx ) )
    call TriggerAddAction( gg_trg_Cone_Of_Healing, function Cone_Of_Healing_Actions ) 
    call TriggerAddAction( gg_trg_Cone_Of_Healing, function RunOutWardEffect )
    set TimerSx = CreateTrigger( )
    call TriggerRegisterTimerEventPeriodic( TimerSx, TimerCycle )
    call TriggerAddAction( TimerSx, function MakeStuffHeal )
endfunction
 

Attachments

  • BHEOLD.w3x
    43.9 KB · Views: 77
Level 11
Joined
Feb 22, 2006
Messages
752
Nice script, not going to go over the SFX part cuz I'm sure you got it right...and cuz I'm too lazy ;)

And btw, you can take all the credit if you want. In fact I wouldn't really care if you 100% copied my script and didn't give me credit. I'm not really looking for credit for spells or scripts (maps are a whole 'nother issue though).

That being said, I've got two potential issues with your script.

One is your CAT function. I don't think you need to check if angles are greater than 360. I'm pretty sure GetUnitFacing will only return 0.00 to 360.00 and the GetAngleBetweenPoints function will only return -180.00 to 180.00.

The second is with your linked list of structs. Why not just use a global struct array? It gets the job done the same way only with less code clutter; none of that destroying and patching links. (Not really an issue more of an observation.)
 
Level 11
Joined
Aug 25, 2006
Messages
971
Reorganizing arrays are a pain. Instead of patching links you have to fill gaps in the array, which is just as bad in my opinon. The CAT function was needed because.. will I don't know why. I only know if you remove it, the spell doesn't work. Perhaps I used it in a few places where it wasn't needed, but I know before I put it, nothing worked. The buff part of the SFX was really easy to add to the struct. The cast sfx had to be rigged up a bit, because the sfx itself doesn't move forwards. It just sort of makes a small triangle and dissapears, so I rigged something up to make it project more.

The constants I added just make it easier for others. I figured that I spent so much time on this I might as well submit it to the spells section for others.
 
Level 3
Joined
Mar 24, 2007
Messages
51
The constants I added just make it easier for others. I figured that I spent so much time on this I might as well submit it to the spells section for others.

u're right, it got really good ^^ congratulations
 
Level 11
Joined
Aug 25, 2006
Messages
971
Copy the spell trigger, the dummy unit, the ability (or make your own), and all the imports. The dummy unit is required to make the casting sfx work correctly. Its model is the sfx.
Sfx's don't use rawcodes, they use pathnames like "humans\chickens\chicken.mdx" Just make sure when you import the imports that they have the same path as they did in my map.
 
Level 10
Joined
Jun 10, 2007
Messages
557
Sorry for taking so long to respond, a few things came up...

I used NewGen editor, copied everything from the map to my own, etc. And it won't let me enable the trigger because of error. I know you said the first one is the only problem, and the rest is the syntax going bananas, but the first error was "Expected end of line", on the... second or third line in the trigger, a comment line. There were also a bunch of Expected end of line errors after that, but still.

Also, is there a way I can change the HolyAurora sfx to a different sfx?
 
Level 11
Joined
Aug 25, 2006
Messages
971
Ok you need to disable normal WE syntax.

Do the following Under JassHelper make sure 'Enable JassHelper' is checked. Under Grimorie make sure 'Disable WE Syntax checking' is checked.

Which sfx do you want to change, the one you use on casting, or the buff? You can change both:
To change the cast animation, you must import then new one, and give the dummy unit that model.
To change the buff animation, import the new one, and change the constant which involves the buff path to the path of the new buff.

Sorry it took me so long to get back to you.
 
Level 11
Joined
Aug 25, 2006
Messages
971
You must be mistaken! Your not using a cracked version of warcraft are you?
Look at these two screenshots, make sure your menus match mine!

How do I know your mistaken? Because if WE syntax checker is disabled, why is your screenshot of it? JASSHelper's syntax checker looks a lot different.
 

Attachments

  • One.jpg
    One.jpg
    71.2 KB · Views: 88
  • Two.jpg
    Two.jpg
    83.2 KB · Views: 89
Level 10
Joined
Jun 10, 2007
Messages
557
They match those, I saved and restarted... Won't let me enable.

And no, I'm not using a cracked copy of Warcraft.

But in the middle of typing this, I duplicated your menu exactly and it let me enable it. However, the cast sfx didn't show... hold on a sec.

EDIT: Aaaand it works! The casting sfx is a bit wonky, but it's not ridiculous! Thank you so much!
 
Last edited:
Status
Not open for further replies.
Top