• 🏆 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] Spell only works on the caster

Status
Not open for further replies.
Level 12
Joined
Mar 23, 2008
Messages
942
Spell don't heal

I have a spell that heals in a area for 10s, but its not working at all.

AREA NON-CHANNEL HEALING
JASS:
scope SoutenKisshunNC initializer InSoutenKisshunNC

globals
    private integer RADIUS = 300
endglobals

private struct SK
    unit caster
    real heal
    real x
    real y
    integer count
    unit sfx
endstruct

globals
    private player tp
endglobals

private function SoutenKisshunNC_Conditions takes nothing returns boolean
    return ( GetSpellAbilityId() == 'A04Z' )
endfunction

private function SoutenKisshunNC_Group takes nothing returns boolean
    return ( IsUnitEnemy(GetFilterUnit(), tp) != true ) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

private function SoutenKisshunNC_Heal takes nothing returns nothing
    local SK s = GetTimerData(GetExpiredTimer())
    local group hgroup = CreateGroup()
    local unit tu
    if (s.count <= 40)  then
        set tp = GetOwningPlayer(s.caster)
        call GroupEnumUnitsInRange(hgroup, s.x, s.y, RADIUS, Condition(function SoutenKisshunNC_Group)) 
        loop
            set tu = FirstOfGroup(hgroup)
            exitwhen tu == null
            call SetUnitState(tu, UNIT_STATE_LIFE, GetUnitState(tu, UNIT_STATE_LIFE) + s.heal)
            call GroupRemoveUnit(hgroup, tu)
        endloop
        call DestroyGroup(hgroup)
        set hgroup = null
        set tp = null
        set s.count = s.count + 1
    else
        call ReleaseTimer(GetExpiredTimer())
        call s.destroy()
        call RemoveUnit(s.sfx)
        set s.sfx = null
        set s.caster = null
    endif
endfunction

private function SoutenKisshunNC_Actions takes nothing returns nothing
    local SK s = SK.create()
    local timer t = NewTimer()
    local location target = GetSpellTargetLoc()
    set s.x = GetLocationX(target)
    set s.y = GetLocationY(target)
    set s.caster = GetTriggerUnit()
    set s.heal = GetUnitAbilityLevel(s.caster, 'A04R') * 2.5
    set s.sfx = CreateUnit(GetOwningPlayer(s.caster), 'h01A', s.x, s.y, 90)
    call SetUnitScale(s.sfx, 2, 2, 2)
    call SetTimerData(t,s)
    call TimerStart(t, 0.25, true, function SoutenKisshunNC_Heal)
    call RemoveLocation(target)
    set target = null
endfunction

//===========================================================================
function InSoutenKisshunNC takes nothing returns nothing
    local trigger t_SoutenKisshunNC = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t_SoutenKisshunNC, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t_SoutenKisshunNC, Condition( function SoutenKisshunNC_Conditions ) )
    call TriggerAddAction( t_SoutenKisshunNC, function SoutenKisshunNC_Actions )
    set t_SoutenKisshunNC = null
endfunction

endscope

Thanks in advance!
 
Last edited:
First of all, can I just say that you have some very bad programming practices. All constant numbers should be constant variables declared in your globals block, along with RADIUS, though they should also be constant. Declare a constant variable like this:
JASS:
globals
    ...
    constant <type> <name> = <x>
    ...
endglobals
. This makes your code changeable easily if you ever want to revise some of the durations.

Secondly, it leaks a boolexpr. You can't use a boolexpr just by doing Condition(function <x>) without it leaking, as this creates a boolexpr but doesn't remove it. Obviously this will leak.

Thirdly, the way you compare values is inefficient. Rather than doing IsUnitEnemy(GetFilterUnit(), tp) != true), it's more efficient and easier to type if you simply do IsUnitEnemy(!GetFilterUnit(), tp)). It does the same thing, just without having to compare anything due to you giving the if statement a straight boolean argument, which makes the if statement twice as efficient.

And about your problem - try doing all of your loop actions inside the enum function rather than looping through the group and removing the units etc. and store the struct in a temporary global variable to transfer it to the enum function. While this might not solve your problem, try doing everything I've suggested, and if it still doesn't work, post the new code, with the changes, and I'll try to address the problem from there.

Hope I helped

Element of Water
 
Level 12
Joined
Mar 23, 2008
Messages
942
I changed what I understand, but IsUnitEnemy(!GetFilterUnit(), tp)) gave me "Unexpected !"

Also, to compare I have the channeling version of it working:

JASS:
scope SoutenKisshunUP initializer InSoutenKisshunUP

private struct SK
    unit caster
    real heal
    real x
    real y
    integer radius
    unit sfx
endstruct

globals
    private player tp
endglobals

private function SoutenKisshunUP_Conditions takes nothing returns boolean
    return ( GetSpellAbilityId() == 'A04X' ) or ( GetSpellAbilityId() == 'A04Y' )
endfunction

private function SoutenKisshunUP_Group takes nothing returns boolean
    return IsUnitAlly(GetFilterUnit(), tp) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

private function SoutenKisshunUP_Heal takes nothing returns nothing
    local SK s = GetTimerData(GetExpiredTimer())
    local group hgroup = CreateGroup()
    local unit tu
    if (GetUnitCurrentOrder(s.caster) == 852063)  then
        set tp = GetOwningPlayer(s.caster)
        call GroupEnumUnitsInRange(hgroup, s.x, s.y, s.radius, Condition(function SoutenKisshunUP_Group)) 
        loop
            set tu = FirstOfGroup(hgroup)
            exitwhen tu == null
            call SetUnitState(tu, UNIT_STATE_LIFE, GetUnitState(tu, UNIT_STATE_LIFE) + s.heal)
            call GroupRemoveUnit(hgroup, tu)
        endloop
        call DestroyGroup(hgroup)
        set hgroup = null
        set tp = null
    else
        call ReleaseTimer(GetExpiredTimer())
        call s.destroy()
        set s.caster = null
        set s.sfx = null
        set s.caster = null
    endif
endfunction

private function SoutenKisshunUP_Actions takes nothing returns nothing
    local SK s = SK.create()
    local timer t = NewTimer()
    local location target = GetSpellTargetLoc()
    set s.x = GetLocationX(target)
    set s.y = GetLocationY(target)
    set s.caster = GetTriggerUnit()
    if ( GetSpellAbilityId() == 'A04X' ) then
        set s.radius = 200
        set s.heal = GetUnitAbilityLevel(s.caster, 'A04X') * 2.5
        set s.sfx = CreateUnit(GetOwningPlayer(s.caster), 'h01A', s.x, s.y, 90)
    else
        set s.radius = 250
        set s.heal = GetUnitAbilityLevel(s.caster, 'A04Y') * 2.5
        set s.sfx = CreateUnit(GetOwningPlayer(s.caster), 'h01A', s.x, s.y, 90)
        call SetUnitScale(s.sfx, 1.5, 1.5, 1.5)
    endif
    call SetUnitAnimation(s.caster, "stand")
    call SetTimerData(t,s)
    call TimerStart(t, 0.25, true, function SoutenKisshunUP_Heal)
    call RemoveLocation(target)
    set target = null
endfunction

//===========================================================================
function InSoutenKisshunUP takes nothing returns nothing
    local trigger t_SoutenKisshunUP = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t_SoutenKisshunUP, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t_SoutenKisshunUP, Condition( function SoutenKisshunUP_Conditions ) )
    call TriggerAddAction( t_SoutenKisshunUP, function SoutenKisshunUP_Actions )
    set t_SoutenKisshunUP = null
endfunction

endscope

JASS:
scope SoutenKisshunNC initializer InSoutenKisshunNC

globals
    private integer RADIUS = 300
    private integer COUNT = 40
    private real INTERVAL = 0.25
endglobals

private struct SK
    unit caster
    real heal
    real x
    real y
    integer count
    unit sfx
endstruct

globals
    private player tp
endglobals

private function SoutenKisshunNC_Conditions takes nothing returns boolean
    return ( GetSpellAbilityId() == 'A04Z' )
endfunction

private function SoutenKisshunNC_Group takes nothing returns boolean
    return IsUnitAlly(GetFilterUnit(), tp) and (GetWidgetLife(GetFilterUnit()) > 0.405)
endfunction

private function SoutenKisshunNC_Heal takes nothing returns nothing
    local SK s = GetTimerData(GetExpiredTimer())
    local group hgroup = CreateGroup()
    local unit tu
    if (s.count <= COUNT)  then
        set tp = GetOwningPlayer(s.caster)
        call GroupEnumUnitsInRange(hgroup, s.x, s.y, RADIUS, Condition(function SoutenKisshunNC_Group)) 
        loop
            set tu = FirstOfGroup(hgroup)
            exitwhen tu == null
            call SetUnitState(tu, UNIT_STATE_LIFE, GetUnitState(tu, UNIT_STATE_LIFE) + s.heal)
            call GroupRemoveUnit(hgroup, tu)
        endloop
        call DestroyGroup(hgroup)
        set hgroup = null
        set tp = null
        set s.count = s.count + 1
        call SetUnitAnimation(s.sfx, "stand")
    else
        call ReleaseTimer(GetExpiredTimer())
        call s.destroy()
        call RemoveUnit(s.sfx)
        set s.sfx = null
        set s.caster = null
    endif
endfunction

private function SoutenKisshunNC_Actions takes nothing returns nothing
    local SK s = SK.create()
    local timer t = NewTimer()
    local location target = GetSpellTargetLoc()
    set s.count = 0
    set s.x = GetLocationX(target)
    set s.y = GetLocationY(target)
    set s.caster = GetTriggerUnit()
    set s.heal = GetUnitAbilityLevel(s.caster, 'A04R') * 2.5
    set s.sfx = CreateUnit(GetOwningPlayer(s.caster), 'h01A', s.x, s.y, 0)
    call SetUnitScale(s.sfx, 2, 2, 2)
    call SetTimerData(t,s)
    call TimerStart(t, INTERVAL, true, function SoutenKisshunNC_Heal)
    call RemoveLocation(target)
    set target = null
endfunction

//===========================================================================
function InSoutenKisshunNC takes nothing returns nothing
    local trigger t_SoutenKisshunNC = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ( t_SoutenKisshunNC, EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t_SoutenKisshunNC, Condition( function SoutenKisshunNC_Conditions ) )
    call TriggerAddAction( t_SoutenKisshunNC, function SoutenKisshunNC_Actions )
    set t_SoutenKisshunNC = null
endfunction

endscope

Obs* Of couse I have bad programming habits, its the first thing bigger than "hello world" I ever programmed...

Edit: Lol found the error:

JASS:
set s.heal = GetUnitAbilityLevel(s.caster, 'A04Z') * 2.5

but if you want give me programming tips, feel free to do it xD
 
Level 20
Joined
Apr 22, 2007
Messages
1,960
ehm, you can't do ! in Jass. :p

You can do if not IsUnitEnemy(GetFilterUnit(), tp), though. It really doesn't change much, it's more of a habit and doesn't actually change results (noticeably anyway) unless you work with the IsUnitType function, which is fucked up.

And NO, boolexprs do not leak in this situation. They are actually stored in a sort of table to which the boolexpr will always reference. Destroying them has proven to be pretty damn useless and will cause bugs occasionally. Note that there are some times when you will have to destroy them, but not here anyway.

And I'm too lazy to actually review your script. lol :p
 
ehm, you can't do ! in Jass. :p
Sorry, had my c++ head on kinda. And i put it in the wrong place :p

And NO, boolexprs do not leak in this situation. They are actually stored in a sort of table to which the boolexpr will always reference. Destroying them has proven to be pretty damn useless and will cause bugs occasionally. Note that there are some times when you will have to destroy them, but not here anyway.
Always thought they did... weird. When do they leak then?
 
Status
Not open for further replies.
Top