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

This ability needs some small fixes...

Level 14
Joined
Jul 19, 2007
Messages
772
I have imported an ability from a Dota Spellpack again and it seems to work like it should but it seems to damage buildings, magic immune units and dead enemy units and also heal dead units. This is maybe not a big problem but it's still annoying. Could anyone fix pls.
JASS:
//TESH.scrollpos=184
//TESH.alwaysfold=0
scope Nymph2 initializer int
globals
    private constant integer ABI_VOLATIVE_POD = 'A0IV'
    private constant integer ABI_POWDER = 'A005'
    private constant integer ABI_N_ZEAL = 'A002'
    private constant integer ABI_TELE = 'A006'
    private constant integer SUB_ABI_STUN = 'A004'
    private constant integer SUB_ABI_SLEEP = 'A003'
    private constant integer SUB_ABI_TELE = 'A007'
    private constant integer DUMMY_VOLATIVE_POD = 'h030'
    private constant integer DUMMY_N_ZEAL = 'h003'
endglobals
//====================================================================
//====================================================================
private struct FairyVolative
    unit caster
    unit dummy
    real damage
   
    method destroy takes nothing returns nothing
        set this.caster = null
        set this.dummy = null
    endmethod
   
    static method create takes unit u, real x, real y returns FairyVolative
        local FairyVolative d = FairyVolative.allocate()
        local timer t = NewTimer()
        set d.caster = u
        set d.dummy = CreateUnit( GetOwningPlayer( u ), DUMMY_VOLATIVE_POD, x, y, 0 )
        call SetUnitAnimation( d.dummy, "birth" )
        call SetUnitTimeScale( d.dummy, 3 )
        call QueueUnitAnimation( d.dummy, "stand" )
        set d.damage = 15. + GetUnitAbilityLevel(u, ABI_VOLATIVE_POD)*60
        call SetTimerData( t , d )
        call TimerStart( t, 2.5, false, function thistype.onDeath  )
        return d
    endmethod
   
    static method onDeath takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local FairyVolative d = GetTimerData( t )
        local real x = GetUnitX( d.dummy )
        local real y = GetUnitY( d.dummy )
        local group g = NewGroup()
        local unit u
        local player p = GetOwningPlayer( d.caster )
        call GroupUnitsInArea( g, x, y, 300 )
        call DestroyEffect( AddSpecialEffect("Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl",x,y ) )
        loop
            set u = FirstOfGroup(g)
            exitwhen u == null
                if IsUnitAlly(u, p) then
                    call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                else
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                    call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                endif
            call GroupRemoveUnit(g,u)
        endloop
        call KillUnit( d.dummy )
        call SetUnitAnimation( d.dummy, "death spell" )
        call ReleaseGroup( g )
        call ReleaseTimer( t )
        set g = null
        set t = null
        call d.destroy()
    endmethod
endstruct

private function FairyVolativeAct takes nothing returns boolean
    local FairyVolative data
    if GetSpellAbilityId() == ABI_VOLATIVE_POD then
        set data = FairyVolative.create( GetTriggerUnit(),GetSpellTargetX(), GetSpellTargetY() )
    endif
    return false
endfunction
//====================================================================
//====================================================================
private struct FairyTele
    unit caster
    static method create takes unit u returns FairyTele
        local FairyTele data = FairyTele.allocate()
        local timer t = NewTimer()
        call SetTimerData( t, data )
        set data.caster = u
        call UnitAddAbility( u, SUB_ABI_TELE )
        call TimerStart( t, 4, false, function FairyTele.timeEnd )
        set t = null
        return data
    endmethod
    static method timeEnd takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local FairyTele data = GetTimerData(t)
        call UnitRemoveAbility( data.caster, SUB_ABI_TELE )
        set data.caster = null
        set t = null
        call data.destroy()
    endmethod
endstruct
//====================================================================
//====================================================================

private function int takes nothing returns nothing
        local trigger t = CreateTrigger()

    call TriggerRegisterAnyUnitEventBJ( t,  EVENT_PLAYER_UNIT_SPELL_EFFECT )
    call TriggerAddCondition( t, Condition( function FairyVolativeAct ) )
   
    call XE_PreloadAbility(ABI_VOLATIVE_POD)
    call XE_PreloadAbility(ABI_POWDER)
    call XE_PreloadAbility(ABI_N_ZEAL)
    call XE_PreloadAbility(ABI_TELE)
    call XE_PreloadAbility(SUB_ABI_STUN)
    call XE_PreloadAbility(SUB_ABI_SLEEP)
    call XE_PreloadAbility(SUB_ABI_TELE)
    set t = null
endfunction
endscope
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
You will need to add checks to the following part of the script...
Code:
                if IsUnitAlly(u, p) then
                    call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                else
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                    call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                endif

The result should look something like the following with the place holder code filled in.
Code:
                if !unit is alive! and !unit is not magic immune! and !unit is not a building! then
                    if IsUnitAlly(u, p) then
                        call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                        call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                    else
                        call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                        call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                    endif
                endif
Fragments between "!" should be replaced with actual code to do what is described. These are used to show where/how you should add the desired tests to the code logic.
 
Level 14
Joined
Jul 19, 2007
Messages
772
You will need to add checks to the following part of the script...
Code:
                if IsUnitAlly(u, p) then
                    call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                else
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                    call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                endif

The result should look something like the following with the place holder code filled in.
Code:
                if !unit is alive! and !unit is not magic immune! and !unit is not a building! then
                    if IsUnitAlly(u, p) then
                        call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                        call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                    else
                        call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                        call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                    endif
                endif
Fragments between "!" should be replaced with actual code to do what is described. These are used to show where/how you should add the desired tests to the code logic.
Sry I don't understand what u mean with replace to the actual code... And are you sure it wont damage dead units, magic immunes and buildings because after the "else" there is no unit check.
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,202
Sry I don't understand what u mean with replace to the actual code... And are you sure it wont damage dead units, magic immunes and buildings because after the "else" there is no unit check.
If you fill in the placeholder fragments after making the above change suggestion it should not. Since it would filter out dead, magic immune or building units before either healing or damaging them.
 
Level 20
Joined
Feb 27, 2019
Messages
593
Create a trigger with your desired conditions.
  • Untitled Trigger 001
    • Events
    • Conditions
      • ((Triggering unit) is an illusion) Equal to True
    • Actions
Turn the trigger into Custom Text. Select trigger > Edit > Convert to Custom Text
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    if ( not ( IsUnitIllusionBJ(GetTriggerUnit()) == true ) ) then
        return false
    endif
    return true
endfunction

function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
Take out your desired functions and then scrap the Custom Text trigger. Anything inbetween the () means that the function takes/requires those arguments/things. In this case IsUnitIllusionBJ takes a unit and GetTriggerUnit takes nothing.
JASS:
IsUnitIllusionBJ(GetTriggerUnit()) == true
 
Level 14
Joined
Jul 19, 2007
Messages
772
Create a trigger with your desired conditions.
  • Untitled Trigger 001
    • Events
    • Conditions
      • ((Triggering unit) is an illusion) Equal to True
    • Actions
Turn the trigger into Custom Text. Select trigger > Edit > Convert to Custom Text
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    if ( not ( IsUnitIllusionBJ(GetTriggerUnit()) == true ) ) then
        return false
    endif
    return true
endfunction

function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
Take out your desired functions and then scrap the Custom Text trigger. Anything inbetween the () means that the function takes/requires those arguments/things. In this case IsUnitIllusionBJ takes a unit and GetTriggerUnit takes nothing.
JASS:
IsUnitIllusionBJ(GetTriggerUnit()) == true
Sry don't really get it and what has illusions with my ability to do? It's not meant to not be damaging or healing illusions.
 
Level 20
Joined
Feb 27, 2019
Messages
593
When Dr Super Good said Fragments between "!" should be replaced with actual code to do what is described they inferred that the ! should be removed too. They are only symbols for showing which text should be replaced. If I say Dinodin said "Hello" I actaully meant they said Hello without the ". The " are the same as the ! in this context.

I posted an example of a way to get the actual code. Do you know how to convert a trigger into Custom Text?
 
Level 14
Joined
Jul 19, 2007
Messages
772
When Dr Super Good said Fragments between "!" should be replaced with actual code to do what is described they inferred that the ! should be removed too. They are only symbols for showing which text should be replaced. If I say Dinodin said "Hello" I actaully meant they said Hello without the ". The " are the same as the ! in this context.

I posted an example of a way to get the actual code. Do you know how to convert a trigger into Custom Text?
No I don't know that I think. But I'm afraid of JASS-triggers and trying to avoid it as much as possible... I would be happy if anyone could fix the JASS-script to me with the codes so it doesn't damage buildings, dead units or magic immune units and not heal dead units either... Here is the spellpack Spellpack
 
Level 14
Joined
Jul 19, 2007
Messages
772
Inside the Trigger Editor select the trigger to be Converted to Custom Text. Press Edit and choose Convert to Custom Text. This changes the trigger from displaying GUI (Graphical user interface) to the actual jass code of the trigger.
Well I tried but I can't click on "Convert to Custom Text" for some reason...
convert.png
 
Level 20
Joined
Feb 27, 2019
Messages
593
Well I tried but I can't click on "Convert to Custom Text" for some reason... View attachment 449961
You might be trying to convert a trigger that is already code. Its meant to be used on GUI triggers to turn them into code. In this case on a throwaway trigger created solely to convert some GUI functions to code. The trigger to be converted has to be selected. Read one of my previous post.
 
Level 14
Joined
Jul 19, 2007
Messages
772
You might be trying to convert a trigger that is already code. Its meant to be used on GUI triggers to turn them into code. In this case on a throwaway trigger created solely to convert some GUI functions to code. The trigger to be converted has to be selected. Read one of my previous post.
Ok so I made it like this now to check if the unit is a structure of not.
JASS:
function Trig_Untitled_Trigger_001_Conditions takes nothing returns boolean
    if ( not ( IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == true ) ) then
        return false
    endif
    return true
endfunction

function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_001 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_001 = CreateTrigger(  )
    call TriggerAddCondition( gg_trg_Untitled_Trigger_001, Condition( function Trig_Untitled_Trigger_001_Conditions ) )
    call TriggerAddAction( gg_trg_Untitled_Trigger_001, function Trig_Untitled_Trigger_001_Actions )
endfunction
What to do now?
 
Level 20
Joined
Feb 27, 2019
Messages
593
There is a bit to pick apart here so Ill try to be simple.

We already know what "if" is and we have it in Dr Super Goods code already. Same goes for "then". The "not" part inverts the result of the condition that is returned and gui always does this with conditions for reasons but we dont really need that. Gui also always creates a new function for conditions but thats not really needed either. What is interesting to us is the specific function that is used to check a units classification. In this case
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == true
which would be true if the triggering unit is a structure. If we want the triggering unit not to be a structure we change the true part to false
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == false
I am being pretty generic, there are other ways to do it but this way is the most generic and will follow the logic used for other types of conditions and not only booleans.

F.e. this is a valid way to check for a true boolean
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE)
whilst this is valid to check for a false boolean
JASS:
not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE)
One can use and or or to require both conditions have a desired value or either one have a desired value
JASS:
if IsUnitIllusion(GetTriggerUnit()) and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
JASS:
if IsUnitIllusion(GetTriggerUnit()) or not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
 
Level 14
Joined
Jul 19, 2007
Messages
772
There is a bit to pick apart here so Ill try to be simple.

We already know what "if" is and we have it in Dr Super Goods code already. Same goes for "then". The "not" part inverts the result of the condition that is returned and gui always does this with conditions for reasons but we dont really need that. Gui also always creates a new function for conditions but thats not really needed either. What is interesting to us is the specific function that is used to check a units classification. In this case
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == true
which would be true if the triggering unit is a structure. If we want the triggering unit not to be a structure we change the true part to false
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) == false
I am being pretty generic, there are other ways to do it but this way is the most generic and will follow the logic used for other types of conditions and not only booleans.

F.e. this is a valid way to check for a true boolean
JASS:
IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE)
whilst this is valid to check for a false boolean
JASS:
not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE)
One can use and or or to require both conditions have a desired value or either one have a desired value
JASS:
if IsUnitIllusion(GetTriggerUnit()) and not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
JASS:
if IsUnitIllusion(GetTriggerUnit()) or not IsUnitType(GetTriggerUnit(), UNIT_TYPE_STRUCTURE) then
Hmm I tried and made it like this
JASS:
if (u, GetOwningPlayer(DATA.u)) and not(IsUnitType(u, UNIT_TYPE_STRUCTURE)) and not(IsUnitType(u, UNIT_TYPE_MECHANICAL)) and not(IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE and IsUnitDeadBJ(GetTriggerUnit()) == false ) ) then
Is that correct or did I do something wrong?
 

Uncle

Warcraft Moderator
Level 64
Joined
Aug 10, 2018
Messages
6,576
That looks correct, here's the final product:
vJASS:
if IsUnitDeadBJ(u) == false and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and IsUnitType(u, UNIT_TYPE_MECHANICAL) == false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false then

                if IsUnitAlly(u, p) then
                    call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                else
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                    call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                endif

endif
I find "== false" to be easier to understand although slightly more code to write.
 
Last edited:
Level 14
Joined
Jul 19, 2007
Messages
772
That looks correct, here's the final product:
Code:
if IsUnitDeadBJ(u) == false and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and IsUnitType(u, UNIT_TYPE_MECHANICAL) == false and IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE) == false then

                if IsUnitAlly(u, p) then
                    call SetWidgetLife( u, GetWidgetLife(u) + d.damage )
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", u, "origin" ) )
                else
                    call DestroyEffect( AddSpecialEffectTarget("Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl", u, "chest" ) )
                    call UnitDamageTarget( d.caster, u, d.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_MAGIC, null )
                endif

endif
I find "== false" to be easier to understand although slightly more code to write.
Oh thanks, that finally solved the problem!
 
Top