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

[JASS] AOE Holy Light

Status
Not open for further replies.
Level 2
Joined
Jun 26, 2014
Messages
10
AOE Holy Light : Solved

So basically, I'm new to Jass - and I got a problem I can't solve.

My idea: Whenever a unit is casting "Holy Light" on a target,
the area around the target will be healed with Holy Light.
JASS:
function Trig_Holy_Light_Conditions takes nothing returns boolean
    if ( not ( GetSpellAbilityId() == 'A018' ) ) then
        return false
    endif
    return true
endfunction

function Trig_Holy_Light_Actions takes nothing returns nothing
    local unit caster = GetSpellAbilityUnit()
    local location target = GetSpellTargetLoc()
    local group ally_group = CreateGroup()
    local unit temp
    
    call GroupEnumUnitsInRangeOfLoc(ally_group, target, 500, null)
    loop
        set temp = FirstOfGroup(ally_group)
        exitwhen (temp == null)
            if ( not (IsUnitAlly(temp, GetOwningPlayer(temp)) )) then
                call GroupRemoveUnit(ally_group, temp)
            else
                call CasterCastAbility(GetOwningPlayer(caster), 'A018', "?????", temp)
                call GroupRemoveUnit(ally_group, temp)
            endif
    endloop
endfunction

//===========================================================================
function InitTrig_Holy_Light takes nothing returns nothing
    set gg_trg_Holy_Light = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Holy_Light, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Holy_Light, Condition( function Trig_Holy_Light_Conditions ) )
    call TriggerAddAction( gg_trg_Holy_Light, function Trig_Holy_Light_Actions )
endfunction
 
Last edited:
I don't think you should use the caster system. Try doing it without it (you'll learn the actual process). The caster system helps to shorten your code by doing a lot of stuff automatically, but if you learn the process, you'll find yourself far more relaxed when coding. :)

Now, let's dig into the problem. First, determine exactly what you must do for your spell: all units in an area will be healed for X hit points. That is a naive view of the spell. Once you get to the coding, you'll realize there are a bunch of more factors: what is the area radius? only allies should be healed! how do I cast holy light on each target? etc.

One of the most useful things for mass-spells are dummy casters. These are invisible units that cast abilities on multiple targets. Instead of creating an ability that heals all the units, just create a regular holy light spell, and have the dummy cast it on all the necessary units. Now, before you actually move on to the JASS, you should really read through this tutorial:
http://www.hiveworkshop.com/forums/...79/single-target-spell-multiple-units-242153/

It'll help a lot, and it is exactly what you're looking for. It'll tell you how to set up a dummy caster. Instead of setting up a sleep ability, you'll set up a holy light ability with the effect you want, 0 cooldown, huge range, and 0 mana cost. So the goal will be to create the dummy unit and cast the spell on all allied units in the group.

Here is an example of what the code might look like:
JASS:
// Assume 'A018' is the mass holy light ability
// Assume 'A019' is the dummy holy light ability
// Assume 'n000' is the dummy unit caster

function Trig_Holy_Light_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A018' // shortened
endfunction

function Trig_Holy_Light_Actions takes nothing returns nothing
    local unit caster = GetSpellAbilityUnit()
    local real target_x = GetSpellTargetX() // use coordinates in JASS, it is easier!
    local real target_y = GetSpellTargetY()
    local group ally_group = CreateGroup()
    local unit dummy = CreateUnit(GetTriggerPlayer(), 'n001', target_x, target_y, 0)
    local unit temp
    
    call UnitAddAbility(dummy, 'A019') // add the dummy holy light ability
    call GroupEnumUnitsInRange(ally_group, target_x, target_y, 500, null)
    loop
        set temp = FirstOfGroup(ally_group)
        exitwhen (temp == null)
        if ( not (IsUnitAlly(temp, GetOwningPlayer(temp)) )) then
            call IssueTargetOrder(dummy, "?????", temp) // order the dummy to holy light the ally
        endif
        call GroupRemoveUnit(ally_group, temp)
    endloop

    call UnitApplyTimedLife(dummy, 'BTLF', 0.01) // get rid of the dummy by adding an expire timer
    call DestroyGroup(ally_group)

    // null variables to prevent leaking
    set caster = null
    set ally_group = null
    set dummy = null
endfunction

//===========================================================================
function InitTrig_Holy_Light takes nothing returns nothing
    set gg_trg_Holy_Light = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Holy_Light, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Holy_Light, Condition( function Trig_Holy_Light_Conditions ) )
    call TriggerAddAction( gg_trg_Holy_Light, function Trig_Holy_Light_Actions )
endfunction

Read through it closely. Sadly, I don't have time to explain it all ;P but feel free to ask if you have any questions. I'll be happy to respond, because it is a lot to take in at once. But this isn't quite functional yet:
JASS:
call IssueTargetOrder(dummy, "?????", temp) // order the dummy to holy light the ally

What do you do here? What goes in the "?????"? Each ability has an order string. This determines what input to give to order a spell to be used via script. Simply check the object editor, and look at the field labeled "Order String". For example, if it is stormbolt, you would input "stormbolt" where "?????" is.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
isnt 0.01 seconds expiration time a bit too short? consider the fact that if the dummy has any animation, he has to get up to cast point before he can start the effect of ability + some abilities have altered cast points for delays, + the dummy has to turn, unless it has 0 movement speed. But if you did this before and it work, then correct me :D
 
Level 2
Joined
Jun 26, 2014
Messages
10
Okay, I tried to follow the tutorial you showed me, but I couldn't understand anything ...
I want a Hero to cast the Holy Light, not a standard unit.

I didn't get the "Create a Roar" or an instant spell .. He didn't even use it?

What if its a spell with levels? How?
 
Last edited by a moderator:

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
You can retrieve spell of given ability via:
call GetUnitAbilityLevel(unit, 'ability code')

After that, you can modify every configurable by implementing formulas such as:
JASS:
private function GetHolyLightAoE takes integer abilityLvl returns real
    return 40 + (abilityLvl * 3)
endfunction

@Chaosy - if we use dummies with 0 cast point, it's sometimes might be better to use one dummy instead of one per target as presented in Purge's example.
 
Level 2
Joined
Jun 26, 2014
Messages
10
If you are healing using hitpoint modifications (set life to life + x) then you need to make sure that you are not healing units with less than 0.405 life or are considered dead to avoid potentially bugging other systems.

It seems like I've to use your idea or is there another way to cast (from a dummy) a rank 2 spell?
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,191
In GUI it is somewhere in the units category I think. Either that or heroes, I forget.

Here is the JASS for it (proof of existence, you can happily use GUI action that interfaces with it).
JASS:
native SetUnitAbilityLevel takes unit whichUnit,integer abilcode,integer level returns integer
 
Level 2
Joined
Jun 26, 2014
Messages
10
I appreciate the help from everyone!

but after reading each post, shouldn't this work? It doesn't :'(

JASS:
function Trig_Holy_Light_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == 'A018'
endfunction

function Trig_Holy_Light_Actions takes nothing returns nothing
    local unit caster = GetSpellAbilityUnit()
    local real target_x = GetSpellTargetX()
    local real target_y = GetSpellTargetY()
    local group ally_group = CreateGroup()
    local unit dummy = CreateUnit(GetTriggerPlayer(), 'h004', target_x, target_y, 0)
    local integer level = GetUnitAbilityLevel(caster, 'A018')
    local unit temp
    
    call UnitAddAbility(dummy, 'A018')
       
    if (level == 1) then
        call SetUnitAbilityLevel(dummy, 'A018', 1)
    elseif (level == 2) then
        call SetUnitAbilityLevel(dummy, 'A018', 2)
    elseif (level == 3) then
        call SetUnitAbilityLevel(dummy, 'A018', 3)
    elseif (level == 4) then
        call SetUnitAbilityLevel(dummy, 'A018', 4)
    elseif (level == 5) then
        call SetUnitAbilityLevel(dummy, 'A018', 5)
    elseif (level == 6) then
        call SetUnitAbilityLevel(dummy, 'A018', 6)
    elseif (level == 7) then
        call SetUnitAbilityLevel(dummy, 'A018', 7)
    elseif (level == 8) then
        call SetUnitAbilityLevel(dummy, 'A018', 8)
    elseif (level == 9) then
        call SetUnitAbilityLevel(dummy, 'A018', 9)
    elseif (level == 10) then
        call SetUnitAbilityLevel(dummy, 'A018', 10)
    endif
    
    call GroupEnumUnitsInRange(ally_group, target_x, target_y, 500, null)
    
    loop
        set temp = FirstOfGroup(ally_group)
        exitwhen (temp == null)
            if ((IsUnitAlly(temp, GetOwningPlayer(temp)))) then
                call IssueTargetOrder(dummy, "holybolt", temp)
            endif
            call GroupRemoveUnit(ally_group, temp)
    endloop
    
    call UnitApplyTimedLife(dummy, 'BTLF', 0.01)
    call DestroyGroup(ally_group)
    
    set caster = null
    set ally_group = null
    set dummy = null
endfunction

//===========================================================================
function InitTrig_Holy_Light takes nothing returns nothing
    set gg_trg_Holy_Light = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Holy_Light, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Holy_Light, Condition( function Trig_Holy_Light_Conditions ) )
    call TriggerAddAction( gg_trg_Holy_Light, function Trig_Holy_Light_Actions )
endfunction
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Enjoy.
Btw, you sure caster' and dummy' ability has the same id? If so, this may trigger infinite loop. Make sure this "Holy Light" is placeholder ability that does nothing, and the real effect is triggered via dummy and its "true" holy light.
JASS:
function Trig_Holy_Light_Conditions takes nothing returns boolean
    local unit caster
    local player owner
    local real target_x
    local real target_y
    local group ally_group
    local unit dummy
    local integer level
    local unit temp

    if ( GetSpellAbilityId() == 'A018' ) then

        set caster = GetTriggerUnit()
        set owner = GetTriggerPlayer()
        set target_x = GetSpellTargetX()
        set target_y = GetSpellTargetY()
        set ally_group = CreateGroup()
        set dummy = CreateUnit(owner, 'h004', target_x, target_y, 0)
        set level = GetUnitAbilityLevel(caster, 'A018')
    
        call UnitAddAbility(dummy, 'A018')
        call SetUnitAbilityLevel(dummy, 'A018', level)

        call GroupEnumUnitsInRange(ally_group, target_x, target_y, 500, null)

        loop
            set temp = FirstOfGroup(ally_group)
            exitwhen (temp == null)

            if (IsUnitAlly(temp, owner)) then
                call IssueTargetOrder(dummy, "holybolt", temp)
            endif

            call GroupRemoveUnit(ally_group, temp)
        endloop
    
        call UnitApplyTimedLife(dummy, 'BTLF', 0.01)
        call DestroyGroup(ally_group)
    
        set caster = null
        set owner = null
        set ally_group = null
        set dummy = null
    endif

    return false
endfunction

//===========================================================================
function InitTrig_Holy_Light takes nothing returns nothing
    set gg_trg_Holy_Light = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Holy_Light, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Holy_Light, Condition( function Trig_Holy_Light_Conditions ) )
endfunction
 
Level 2
Joined
Jun 26, 2014
Messages
10
Enjoy.
Btw, you sure caster' and dummy' ability has the same id? If so, this may trigger infinite loop. Make sure this "Holy Light" is placeholder ability that does nothing, and the real effect is triggered via dummy and its "true" holy light.
JASS:
function Trig_Holy_Light_Conditions takes nothing returns boolean
    local unit caster
    local player owner
    local real target_x
    local real target_y
    local group ally_group
    local unit dummy
    local integer level
    local unit temp

    if ( GetSpellAbilityId() == 'A018' ) then

        set caster = GetTriggerUnit()
        set owner = GetTriggerPlayer()
        set target_x = GetSpellTargetX()
        set target_y = GetSpellTargetY()
        set ally_group = CreateGroup()
        set dummy = CreateUnit(owner, 'h004', target_x, target_y, 0)
        set level = GetUnitAbilityLevel(caster, 'A018')
    
        call UnitAddAbility(dummy, 'A018')
        call SetUnitAbilityLevel(dummy, 'A018', level)

        call GroupEnumUnitsInRange(ally_group, target_x, target_y, 500, null)

        loop
            set temp = FirstOfGroup(ally_group)
            exitwhen (temp == null)

            if (IsUnitAlly(temp, owner)) then
                call IssueTargetOrder(dummy, "holybolt", temp)
            endif

            call GroupRemoveUnit(ally_group, temp)
        endloop
    
        call UnitApplyTimedLife(dummy, 'BTLF', 0.01)
        call DestroyGroup(ally_group)
    
        set caster = null
        set owner = null
        set ally_group = null
        set dummy = null
    endif

    return false
endfunction

//===========================================================================
function InitTrig_Holy_Light takes nothing returns nothing
    set gg_trg_Holy_Light = CreateTrigger(  )
    call TriggerRegisterAnyUnitEventBJ( gg_trg_Holy_Light, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( gg_trg_Holy_Light, Condition( function Trig_Holy_Light_Conditions ) )
endfunction

What do u mean with placeholder? ...
 

Bannar

Code Reviewer
Level 26
Joined
Mar 19, 2008
Messages
3,140
Adding 'A018' to dummies, considering that caster's ability is also 'A018' isn't smart. It's easy to enter infinite-loop trap. You can, however, avoid this by Disabling and Enabling given trigger, yet here I'd rather use common approach:

Placeholder - I've used this word here to avoid confusing you with another dummy word. However, really, in cases such as this, casting unit has access only to "dummy-ability" -> ability that does nothing, has no effect except for being an indicator for a player. Whole effect is performed via dummies which have adequate abilities added to them prior the execution of actuall spell effect.

Your case:
a) caster has target-type of spell (possibly with range indicator -> show the AoE), with no real dmg/heal/effect/etc
b) contrary, dummies that will be created on trigger's evaluation will possess "true" healing spell which they will actually use on their targets
 
Level 2
Joined
Jun 26, 2014
Messages
10
Adding 'A018' to dummies, considering that caster's ability is also 'A018' isn't smart. It's easy to enter infinite-loop trap. You can, however, avoid this by Disabling and Enabling given trigger, yet here I'd rather use common approach:

Placeholder - I've used this word here to avoid confusing you with another dummy word. However, really, in cases such as this, casting unit has access only to "dummy-ability" -> ability that does nothing, has no effect except for being an indicator for a player. Whole effect is performed via dummies which have adequate abilities added to them prior the execution of actuall spell effect.

Your case:
a) caster has target-type of spell (possibly with range indicator -> show the AoE), with no real dmg/heal/effect/etc
b) contrary, dummies that will be created on trigger's evaluation will possess "true" healing spell which they will actually use on their targets

So basically you want me to create 2 Holy Lights.

1st: Is for the dummy, that actually heals.
2nd: Is for the hero which doesn't do anything (heal)?
 
Level 2
Joined
Jun 26, 2014
Messages
10
That is how we usually do some uber effective spells :) Whatmore, when you want to addup additional effects (not just limited to 1), you quickly realise that base caster's spell doesn't really matter. Effect is trigger' creation anyway.

Okay, thanks :).
 
Level 2
Joined
Jun 26, 2014
Messages
10
I got it working! I had to change the mana cost and cooldown to 0 on the dummy spell! Thanks everyone who helped me though this!

I'm so exited to create more! :D
 
Level 25
Joined
Sep 26, 2009
Messages
2,378
Just for future note, spells that have simple instant effect - like Holy Light in this example, shouldn't use dummies as there's no need and you will simplify the code.

What you did:
You created dummy, created two version of Holy Light spell.
The code created either 1 dummy for the whole spell or 1 dummy per unit affected by this spell.
You created unit group in which contains all units allied to the caster.
For each dummy, you add the Holy Light ability to it, raised that ability's level and ordered the dummy to cast it.


What you can do:
You set up a real called e.g. heal_amount and set it the amount it should heal. So if Holy Light heals for 200/400/600, then the real should be set as [200 * (level of caster's Holy Light)]
I think this is how it would look like:
JASS:
local real heal_amount = 200.0 * I2R(GetUnitAbilityLevel(GetTriggerUnit(), 'A018'))
You create unit group which contains allies of the caster around cast point and one by one, you increase their health by heal_amount and create special effect of Holy Light which you also immediately destroy.
And that's it.


You want to use dummies when you need to:
a) apply buffs
b) shoot projectiles
c) shoot lightning
I can't think of many more uses. Usage of dummies for all 3 cases is even more needed when making AoE spells.

Examples:
Mass Charm - doesn't need dummies. It is a spell with simple instant effect (checks unit's classification, unit's level, units that pass change owner and a special effect is created above them)
Death Coil Volley (basically shoots Death Coils at all around) - needs dummies. While the spell has simple effect, it is not instant... there's a projectile that has to fly towards the target first.
Mass Bloodlust - needs dummies. While you could simulate the effects by giving hidden abilities to affected units, it is simply better to have dummy cast the actual spell on them.
 
Status
Not open for further replies.
Top