• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

[vJASS] Unsure how to make this Stomp Spell

Status
Not open for further replies.
Level 2
Joined
Oct 15, 2008
Messages
17
Hi,

i'm trying to make this Hoof Stomp spell vJASS MUI but it's not working.

i've already made a GUI working version but converting it is tricky.

i've uploaded the test map which contains both GUI and my vJASS versions so you can test them and see how the spell is meant to work etc.

If it helps, i can post the GUI version here too.

below is the vJASS code for the spell:

JASS:
scope HoofStomp initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
   globals
        private constant integer SPELL_ID = 'A000'   //rw of teh ability
        private constant integer DUMMY_ID = 'h019'    //rw of the dummy unit
        private constant real INTERVAL = 0.03    //cicles of the timer
        private constant string STOMP_EFFECT = "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl"
        private constant string SPELL_EFFECT = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC  //the attack type of the spell
        private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL  //the damage type of the spell
    endglobals
    
    private function Duration takes integer level returns real
        return 3.
    endfunction
    
    private function Range takes integer level returns real
        return 350.
    endfunction
    
    private function Targets takes unit target returns boolean
    //the units the spell will affect
        return not IsUnitType(target, UNIT_TYPE_STRUCTURE) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
    globals
        private group all
        private group copy
        private boolexpr b
    endglobals
//===========================================================================
//Function made by Blade.dk; Search for [url=http://www.wc3campaigns.com]wc3campaigns.com[/url] for more info
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup(g, function GroupAddGroupEnum)
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets(GetFilterUnit())
    endfunction
//===========================================================================
    
    private struct Data
        unit caster
        unit target
        real spellX
        real spellY
        real duration
        real range
        integer level
        location p  
        
        static method Loop takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype data = GetTimerData(t)
            local real x
            local real y
            local location tp
            local unit dum
            
            call GroupEnumUnitsInRange(all, data.spellX, data.spellY, data.range, b)
            set copy = CopyGroup(all)
            set data.target = FirstOfGroup(copy)
            loop
                exitwhen(data.target == null)
                set data.target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, data.target)
                if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then             
                    set tp = GetUnitLoc(data.target)
                    call SetUnitFacingToFaceLocTimed(data.target, data.p, 0.01)
                    call SetUnitPositionLoc(data.target, PolarProjectionBJ(tp, 10.00, (GetUnitFacing(data.target) + 180.00)))
                endif
            endloop
            set data.duration = data.duration - INTERVAL
            if data.duration <= 0. then
                call ReleaseTimer(t)
                call data.destroy()
                call TriggerSleepAction(0.50)
                loop
                    exitwhen(data.target == null)
                    set data.target = FirstOfGroup(copy)
                    call GroupRemoveUnit(copy, data.target)
                    if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then
                        call SetUnitFlyHeightBJ(data.target, 0.00, 740.00 )
                    endif
                endloop
                call TriggerSleepAction(0.69)
                loop
                    exitwhen(data.target == null)
                    set data.target = FirstOfGroup(copy)
                    call GroupRemoveUnit(copy, data.target)
                    if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then
                        call SetUnitPathing(data.target, true)
                        set dum = CreateUnitAtLoc(GetOwningPlayer(data.caster), 'e008', data.p, bj_UNIT_FACING )
                        call IssueTargetOrder(dum, "firebolt", data.target)
                        call UnitApplyTimedLifeBJ(1.00, 'BTLF', dum)
                    endif
                endloop
            endif
        endmethod
//===========================================================================
        static method Actions takes unit caster, real spellX, real spellY, integer level returns thistype
            local thistype data = thistype.create()
            local timer t = NewTimer()
            local unit = target
            local effect = e
            local effect = e2
            local location = p
            
            set p = GetUnitLoc(caster)
            call TerrainDeformationCraterBJ(2.00, false, p, 250.00, 95.00)
            set e = AddSpecialEffectLocBJ(p, STOMP_EFFECT)
            set e2 = AddSpecialEffectLocBJ(p, SPELL_EFFECT)
            call DestroyEffect(e)
            call DestroyEffect(e2)
            set spellX = GetLocationX(p)
            set spellY = GetLocationY(p)
            call GroupEnumUnitsInRange(all, spellX, spellY, Range(level), b)
            set copy = CopyGroup(all)
            set target = FirstOfGroup(copy)
            loop
                exitwhen(target == null)
                set target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, target)
                if IsUnitEnemy(target, GetOwningPlayer(caster)) then             
                    call SetUnitFacingToFaceLocTimed(target, p, 0.19)
                    call UnitAddAbilityBJ('Amrf', target)
                    call SetUnitFlyHeightBJ(target, 500.00, 910.00)
                    call UnitRemoveAbilityBJ('Amrf', target)
                    call SetUnitPathing(target, false)
                endif
            endloop

            set data.caster = caster
            set data.target = target
            set data.spellX = spellX
            set data.spellY = spellY
            set data.p = p
            set data.range = Range(level)
            set data.duration = Duration(level)
            set data.level = level
            call SetTimerData(t, data)
            call TimerStart(t, INTERVAL, true, function thistype.Loop)
            return data
        endmethod
endstruct 

private function Conditions takes nothing returns boolean
    local unit caster
    local real x
    local real y 
    local integer level
    if GetSpellAbilityId() == SPELL_ID then
        set caster = GetTriggerUnit()
        set x = GetSpellTargetX()
        set y = GetSpellTargetY()
        set level = GetUnitAbilityLevel(caster, SPELL_ID)
        call Data.Actions(caster, x, y, level)
    endif
    set caster = null
    return false
endfunction

private function Init takes nothing returns nothing
        local trigger HoofStompTrg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(HoofStompTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(HoofStompTrg, Condition( function Conditions ) )
        
        //setting globals
        set all = CreateGroup()
        set copy = CreateGroup()
        set b = Condition(function Pick)
        
        //preloading effects
        call Preload(STOMP_EFFECT)
        call Preload(SPELL_EFFECT) 
        
        //preloading the ability
        set bj_lastCreatedUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, SPELL_ID)
        call KillUnit(bj_lastCreatedUnit)
endfunction
endscope

Thanks very much!
 

Attachments

  • HoofStomp.w3x
    36.9 KB · Views: 60
Level 2
Joined
Oct 15, 2008
Messages
17
i've taken the wait actions out and added a timer but it still doesnt work. here's my new code:

JASS:
scope HoofStomp initializer Init
//===========================================================================
//=============================SETUP START===================================
//===========================================================================
   globals
        private constant integer SPELL_ID = 'A00H'   //rw of teh ability
        private constant integer DUMMY_ID = 'h019'    //rw of the dummy unit
        private constant real INTERVAL = 0.03    //cicles of the timer
        private constant string STOMP_EFFECT = "Abilities\\Spells\\Orc\\EarthQuake\\EarthQuakeTarget.mdl"
        private constant string SPELL_EFFECT = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
        private constant attacktype A_TYPE = ATTACK_TYPE_MAGIC  //the attack type of the spell
        private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL  //the damage type of the spell
    endglobals
    
    private function Duration takes integer level returns real
        return 3.
    endfunction
    
    private function Range takes integer level returns real
        return 350.
    endfunction
    
    private function Targets takes unit target returns boolean
    //the units the spell will affect
        return not IsUnitType(target, UNIT_TYPE_STRUCTURE) and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
//===========================================================================
//=============================SETUP END=====================================
//===========================================================================
    globals
        private group all
        private group copy
        private boolexpr b
    endglobals
//===========================================================================
//Function made by Blade.dk; Search for [url=http://www.wc3campaigns.com]wc3campaigns.com[/url] for more info
    private function CopyGroup takes group g returns group
        set bj_groupAddGroupDest = CreateGroup()
        call ForGroup(g, function GroupAddGroupEnum)
        return bj_groupAddGroupDest
    endfunction
//===========================================================================
    private function Pick takes nothing returns boolean
        return Targets(GetFilterUnit())
    endfunction
//===========================================================================
    
    private struct Data
        unit caster
        unit target
        real spellX
        real spellY
        real duration
        real range
        integer level
        location p  
        
        static method End takes nothing returns nothing
            local timer t3 = GetExpiredTimer()
            local thistype data = GetTimerData(t3)
            local unit = dum
            
            loop
                exitwhen(data.target == null)
                set data.target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, data.target)
                if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then
                    call SetUnitPathing(data.target, true)
                    set dum = CreateUnitAtLoc(GetOwningPlayer(data.caster), 'e008', data.p, bj_UNIT_FACING )
                    call IssueTargetOrder(dum, "firebolt", data.target)
                    call UnitApplyTimedLifeBJ(1.00, 'BTLF', dum)
                endif
            endloop
            call ReleaseTimer(t3)
            call data.destroy()
        endmethod
        
        static method Float takes nothing returns nothing
            local timer t2 = GetExpiredTimer()
            local thistype data = GetTimerData(t2)
            
            loop
                exitwhen(data.target == null)
                set data.target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, data.target)
                if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then
                    call SetUnitFlyHeightBJ(data.target, 0.00, 740.00 )
                endif
            endloop
            call ReleaseTimer(t2)
            call data.destroy()
        endmethod
        
        static method Loop takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype data = GetTimerData(t)
            local location tp
            
            call GroupEnumUnitsInRange(all, data.spellX, data.spellY, data.range, b)
            set copy = CopyGroup(all)
            set data.target = FirstOfGroup(copy)
            loop
                exitwhen(data.target == null)
                set data.target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, data.target)
                if IsUnitEnemy(data.target, GetOwningPlayer(data.caster)) then             
                    set tp = GetUnitLoc(data.target)
                    call SetUnitFacingToFaceLocTimed(data.target, data.p, 0.01)
                    call SetUnitPositionLoc(data.target, PolarProjectionBJ(tp, 10.00, (GetUnitFacing(data.target) + 180.00)))
                endif
            endloop
            set data.duration = data.duration - INTERVAL
            if data.duration <= 0. then
                call ReleaseTimer(t)
                call data.destroy()
            endif
        endmethod
//===========================================================================
        static method Actions takes unit caster, real spellX, real spellY, integer level returns thistype
            local thistype data = thistype.create()
            local timer t = NewTimer()
            local timer t2 = NewTimer()
            local timer t3 = NewTimer()
            local unit = target
            local effect = e
            local effect = e2
            local location = p
            
            set p = GetUnitLoc(caster)
            call TerrainDeformationCraterBJ(2.00, false, p, 250.00, 95.00)
            set e = AddSpecialEffectLocBJ(p, STOMP_EFFECT)
            set e2 = AddSpecialEffectLocBJ(p, SPELL_EFFECT)
            call DestroyEffect(e)
            call DestroyEffect(e2)
            set spellX = GetLocationX(p)
            set spellY = GetLocationY(p)
            call GroupEnumUnitsInRange(all, spellX, spellY, Range(level), b)
            set copy = CopyGroup(all)
            set target = FirstOfGroup(copy)
            loop
                exitwhen(target == null)
                set target = FirstOfGroup(copy)
                call GroupRemoveUnit(copy, target)
                if IsUnitEnemy(target, GetOwningPlayer(caster)) then             
                    call SetUnitFacingToFaceLocTimed(target, p, 0.19)
                    call UnitAddAbilityBJ('Amrf', target)
                    call SetUnitFlyHeightBJ(target, 500.00, 910.00)
                    call UnitRemoveAbilityBJ('Amrf', target)
                    call SetUnitPathing(target, false)
                endif
            endloop

            set data.caster = caster
            set data.target = target
            set data.spellX = spellX
            set data.spellY = spellY
            set data.p = p
            set data.range = Range(level)
            set data.duration = Duration(level)
            set data.level = level
            call SetTimerData(t, data)
            call TimerStart(t, INTERVAL, true, function thistype.Loop)
            call SetTimerData(t2, data)
            call TimerStart(t2, 0.50, false, function thistype.Float)
            call SetTimerData(t3, data)
            call TimerStart(t3, 0.69, false, function thistype.End)
            return data
        endmethod
endstruct 

private function Conditions takes nothing returns boolean
    local unit caster
    local real x
    local real y 
    local integer level
    if GetSpellAbilityId() == SPELL_ID then
        set caster = GetTriggerUnit()
        set x = GetSpellTargetX()
        set y = GetSpellTargetY()
        set level = GetUnitAbilityLevel(caster, SPELL_ID)
        call Data.Actions(caster, x, y, level)
    endif
    set caster = null
    return false
endfunction

private function Init takes nothing returns nothing
        local trigger HoofStompTrg = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ(HoofStompTrg, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition(HoofStompTrg, Condition( function Conditions ) )
        
        //setting globals
        set all = CreateGroup()
        set copy = CreateGroup()
        set b = Condition(function Pick)
        
        //preloading effects
        call Preload(STOMP_EFFECT)
        call Preload(SPELL_EFFECT) 
        
        //preloading the ability
        set bj_lastCreatedUnit = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, 0, 0, 0)
        call UnitAddAbility(bj_lastCreatedUnit, SPELL_ID)
        call KillUnit(bj_lastCreatedUnit)
endfunction
endscope
 
vJass and MUI should never be used in the same sentence :p
When you use vJass MU-Instancability is assumed :p

Suggestions:
-Hold control and click on those red evil BJs so you can remove them.
-Put the spell in a library
-Put the initialization in a static method (onInit) inside a module and implement that module into a struct:
JASS:
private module Init
    private static method onInit takes nothing returns nothing
        // your init here
    endmethod
endmodule
private struct Inits extends array
    implement Init
endstruct


edit


JASS:
private function Conditions takes nothing returns boolean
    local unit caster
    local real x
    local real y 
    local integer level
    if GetSpellAbilityId() == SPELL_ID then
        set caster = GetTriggerUnit()
        set x = GetSpellTargetX()
        set y = GetSpellTargetY()
        set level = GetUnitAbilityLevel(caster, SPELL_ID)
        call Data.Actions(caster, x, y, level)
    endif
    set caster = null
    return false
endfunction

->

JASS:
private function Conditions takes nothing returns boolean
    local unit u
    if GetSpellAbilityId() == SPELL_ID then
        set u=GetTriggerUnit()
        call Data.Actions(u,GetSpellTargetX(),GetSpellTargetY(),GetUnitAbilityLevel(u,SPELL_ID))
    endif
    set u=null
    return false
endfunction
 
GetSpellTargetX and Y has problems coz the spell is 'no target', not target point...

Anyway, I've made a spell similar to this before, but no damage...
JASS:
//Spell Name: Mass Knock Leap
//Made by: Mckill2009

scope MassKnockLeap initializer init

globals
    private constant integer            SPELLID = 'A000'
    private constant real            TIMERSPEED = 0.03125
    private constant real           SPEEDOFFSET = 20
    private constant real               BASEAOE = 50
    private constant real          BASEDISTANCE = 100
    private constant real             HEIGHTDIV = 3
    private constant string                 SFX = "Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
    private constant hashtable             HASH = InitHashtable()
endglobals

//===CONFIGURABLES:

private function AOE takes integer i returns real
    return 250 + i * BASEAOE
endfunction
    
private function DISTANCE takes integer i returns real
    return 300 + i * BASEDISTANCE
endfunction
    


//===END OF CONFIGURABLES:

private struct MassKL
    unit caster
    unit target
    real casX
    real casY
    real tarX
    real tarY
    real angle
    real distx
    real distance
    real height
    integer level
    static integer DATA
    
    static method onZ takes real h, real d, real x returns real
        return (4 * h / d) * (d - x) * (x / d)
    endmethod
        
    static method onLoop takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local thistype data = LoadInteger(HASH, GetHandleId(t), 1)
        if data.distance > data.distx then
            set data.distx = data.distx+SPEEDOFFSET
            call SetUnitX(data.target, data.tarX + data.distx * Cos(data.angle))
            call SetUnitY(data.target, data.tarY + data.distx * Sin(data.angle))
            call SetUnitFlyHeight(data.target, thistype.onZ(data.height, data.distance, data.distx), data.distance)
        else
            call SetUnitFlyHeight(data.target, GetUnitDefaultFlyHeight(data.target), data.distance)
            call PauseTimer(t)
            call DestroyTimer(t)
        endif
        set t = null
    endmethod
        
    static method onCreate2 takes unit u, unit target, integer level returns thistype
        local thistype data = thistype.create()
        local timer t = CreateTimer()            
        set data.casX = GetUnitX(u)
        set data.casY = GetUnitY(u)
        set data.tarX = GetUnitX(target)
        set data.tarY = GetUnitY(target)
        set data.caster = u
        set data.target = target
        set data.distance = DISTANCE(level)
        set data.height = data.distance / HEIGHTDIV
        set data.angle = Atan2(data.tarY - data.casY, data.tarX - data.casX)
        set data.distx = 0
        call UnitAddAbility(target, 'Amrf')
        call UnitRemoveAbility(target, 'Amrf')
        call SaveInteger(HASH, GetHandleId(t), 1, data)
        call TimerStart(t, TIMERSPEED, true, function thistype.onLoop)
        return data
    endmethod
        
    static method onScope takes nothing returns boolean
        local thistype this = DATA
        local unit u = GetFilterUnit()
        if IsUnitEnemy(u, GetOwningPlayer(.caster)) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) then
            call MassKL.onCreate2(.caster, u, .level)
        endif
        set u = null
        return false
    endmethod    
    
    static method onCreate takes unit u, real x, real y, integer i returns thistype
        local thistype data = thistype.create()
        call DestroyEffect(AddSpecialEffect(SFX, GetUnitX(u), GetUnitY(u)))
        set data.caster = u
        set DATA = data
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, AOE(i), function thistype.onScope)
        return data
    endmethod
endstruct 

private function COND takes nothing returns boolean
    local unit u
    if GetSpellAbilityId()==SPELLID then
        set u = GetTriggerUnit()
        call MassKL.onCreate(u, GetUnitX(u), GetUnitY(u), GetUnitAbilityLevel(u, SPELLID))
    endif
    set u = null
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function COND))
    set t = null
endfunction

endscope
 
JASS:
private function Conditions takes nothing returns boolean
    if GetSpellAbilityId() == SPELL_ID then
        call Data.Actions(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY(), GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID))
    endif
    return false
endfunction

2 calls of GetTriggerUnit() will have better efficiency than
declaring a local, setting it to the triggering unit, referencing
it twice, then nulling it.

Importantly, it's less text as well so it keeps the map size
down.
 
Level 2
Joined
Oct 15, 2008
Messages
17
thanks guys this has helped a lot.
i now know how to fix evil red bjs and understand how the stomp spell should execute.

+reps

edit: SOLVED!
 
Last edited:
2 calls of GetTriggerUnit() will have better efficiency than
declaring a local, setting it to the triggering unit, referencing
it twice, then nulling it.

I dont know... :S
Has that been tested yet?
Actually, come to think of it, at the C++ level, GetTriggerUnit probably just returns an instance of class unit through an array read :p
The array index being trigger dependent.

When we want to repeat a function call thrice, that's where I draw the line and include a local :p
 
Status
Not open for further replies.
Top