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

[vJASS] Help With This Spell

Status
Not open for further replies.
Level 17
Joined
Feb 11, 2011
Messages
1,860
Hello,

I am requesting your help once again. Since I am learning vJASS, I am trying to 'upgrade' all of my spells to vJASS. I made this rather complicated one using normal JASS. However, now I am having trouble getting it to work, mainly because using TriggerSleepAction and PolledWait don't work. Here is the script:
JASS:
scope EnergyFissure initializer InitTrigger

    globals
        unit Effect
        timer Timer
        real Distance
    endglobals
    
    private function kill takes nothing returns nothing
        call KillUnit(Effect)
    endfunction
        
    private function group_act takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), 'h003', x, y, bj_UNIT_FACING)
        call RemoveGuardPosition(bj_lastCreatedUnit)
        call UnitAddAbility(bj_lastCreatedUnit, 'A04V')
        call SetUnitAbilityLevel(bj_lastCreatedUnit, 'A04V', udg_int)
        call IssueTargetOrder(bj_lastCreatedUnit, "manaburn", GetEnumUnit())
        call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
    endfunction
    
    private function group_cond takes nothing returns boolean
        return IsPlayerEnemy(GetOwningPlayer(GetFilterUnit()), GetOwningPlayer(GetTriggerUnit())) == true
    endfunction
    
    private function act_2 takes nothing returns nothing
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(Effect), GetUnitY(Effect), Distance, Condition(function group_cond))
        call ForGroup(bj_lastCreatedGroup, function group_act)
    endfunction
    
    private function act_1 takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        local real x2
        local real y2
        local integer i = 1
        local integer i2 = 1
        local real wait_time
        local group ug = CreateGroup()
        local boolean b = false
        loop
            exitwhen i == 4
            set Distance = (150 * i) + 100
            set wait_time = Distance / 300
            loop
                exitwhen i2 == 9
                set x2 = (x + 100) * Cos(i2 * 45 * bj_DEGTORAD)
                set y2 = (y + 100) * Sin(i2 * 45 * bj_DEGTORAD)
                set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), 'h003', x, y, bj_UNIT_FACING)
                call RemoveGuardPosition(bj_lastCreatedUnit)
                call UnitAddAbility(bj_lastCreatedUnit, 'A015')
                call SetUnitAbilityLevel(bj_lastCreatedUnit, 'A015', i)
                if (IsTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY)) == true then
                    call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, true)
                    set b = true
                endif
                call IssuePointOrder(bj_lastCreatedUnit, "carrionswarm", x2, y2)
                if (b == true) then
                    call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, false)
                endif
                call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
                set i2 = i2 + 1
            endloop
            call TimerStart(Timer, wait_time, false, function act_2)
            call TriggerSleepAction(wait_time)
            set i = i + 1
            set i2 = 1
        endloop
        call TimerStart(Timer, 1, false, function kill)
    endfunction

    private function cond takes nothing returns boolean
        if (GetSpellAbilityId() == 'A04R') then
            set Effect = CreateUnit(GetTriggerPlayer(), 'h00J', GetSpellTargetX(), GetSpellTargetY(), bj_UNIT_FACING)
            call RemoveGuardPosition(Effect)
            call TimerStart(Timer, 1, false, function act)
        endif
        return false
    endfunction

    private function InitTrigger 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
Explanation
Energy Fissure:
Creates a void of energy at the target location. After 1 second, it sends out three pulses around the void. Once the pulses reach their maximum distance, they dissipate and all units that are within the distance lose some mana.Each pulse travels further than the previous one and zaps more mana.
Pulse 1: 250 distance, 100 mana burned.
Pulse 2: 400 distance, 200 mana burned.
Pulse 3: 550 distance, 300 mana burned.
Notes:

  • Does not need to be MUI.
  • Waves are a purple version of Crushing Wave.
  • 8 of these waves create 1 pulse.
  • Mana is only burned when each pulse reaches its maximum distance.
  • The next pulse triggers when the previous one reaches its maximum distance.
  • The waves' speed is 300.
This is the effect that is created when you cast the spell. The pulses come out of here.
void1d.jpg


This shows what the pulses look like.
void2.jpg


This shows what happens when the pulses reach their maximum range.
void3.jpg

Feel free to ask me for more information, because this sounds quite complicated. Thanks for any help!
 
Level 37
Joined
Mar 6, 2006
Messages
9,240
I recommend you learn to code without TSA. Use timers instead. TSA is inaccurate. TSA always adds either 0.1, 0.125, 0.15, 0.175 or 0.2 seconds to the specified value, chosen randomly. A 0.5 seconds wait turns into 0.6, 0.625, 0.65, 0.675 or 0.7 second wait.
 
Level 6
Joined
Jun 16, 2007
Messages
235
Thanks. Is there anything else that is wrong with the spell?

Yes, you are putting alphanumeric codes all over it instead of using constants.
Example:
JASS:
//==============================================================================
//  When in avatar mode you can bash big units :)
//==============================================================================
scope GreaterBash initializer Init

globals
    private constant real AVATAR_DURATION = 60.0 // seconds
endglobals

//==============================================================================
private function UnavatarConditions takes nothing returns boolean
	return GetUnitTypeId(GetTriggerUnit()) == UID_paladin
endfunction

//==============================================================================
private function UnavatarActions takes nothing returns nothing
    call SetUnitAbilityLevel(GetTriggerUnit(), AID_bash, 1) // return bash to normal
endfunction

//==============================================================================
private function Conditions takes nothing returns boolean
	return GetSpellAbilityId() == AID_avatar
endfunction

//==============================================================================
private function Actions takes nothing returns nothing
    local unit caster = GetTriggerUnit()
    call SetUnitAbilityLevel(caster, AID_bash, 2) // greater bash in avatar mode
    call TriggerSleepAction(AVATAR_DURATION)
    call SetUnitAbilityLevel(caster, AID_bash, 1) // return bash to normal
    set caster = null
endfunction
 
Level 17
Joined
Feb 11, 2011
Messages
1,860
Okay, so I re-wrote the spell from the beginning. It is working now, except for some small things. The timers seem to be inaccurate - they are meant to expire when each pulse reaches its maximum range, except they seem to fire before. Also, mana is burned from units the first two times, but after the third pulse, nothing happens.

JASS:
scope EnergyFissure initializer InitTrigger

    globals
        unit Effect = null
        timer Timer = CreateTimer()
        timer Loop_Timer = CreateTimer()
        real Duration = 0
        integer Index = 0
        
        constant integer PULSE_EFFECT = 'A001'
        constant integer MANA_BURN = 'A002'
        constant integer DUMMY = 'h001'
        constant integer ENERGY_FISSURE = 'h000'
    endglobals
    
    private function group_act takes nothing returns nothing
        set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), 'hpea', GetUnitX(GetEnumUnit()), GetUnitY(GetEnumUnit()), 0)
        call UnitAddAbility(bj_lastCreatedUnit, MANA_BURN)
        call RemoveGuardPosition(bj_lastCreatedUnit)
        call SetUnitAbilityLevel(bj_lastCreatedUnit, MANA_BURN, Index)
        call IssueTargetOrder(bj_lastCreatedUnit, "manaburn", GetEnumUnit())
        call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
    endfunction
    
    private function group_cond takes nothing returns boolean
        return (IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(Effect)) == true)
    endfunction
    
    private function act_2 takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, ((150 * Index) + 100), Condition(function group_cond))
        call ForGroup(bj_lastCreatedGroup, function group_act)
    endfunction
   
    private function act takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        local real x2 = 0
        local real y2 = 0
        local integer i = 0
        local boolean b = true
        set Index = Index + 1
        set Duration = ((150 * Index) + 100) / 300
        loop
            set x2 = x + (100 * Cos(i * 45 * bj_DEGTORAD))
            set y2 = y + (100 * Sin(i * 45 * bj_DEGTORAD))
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), DUMMY, x, y, bj_UNIT_FACING)
            call RemoveGuardPosition(bj_lastCreatedUnit)
            call UnitAddAbility(bj_lastCreatedUnit, PULSE_EFFECT)
            call SetUnitAbilityLevel(bj_lastCreatedUnit, PULSE_EFFECT, Index)
            if (IsTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY) == false) then
                set b = false
                call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, true)
            endif
            call IssuePointOrder(bj_lastCreatedUnit, "carrionswarm", x2, y2)
            if (b == false) then
                call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, false)
            endif
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
            exitwhen i == 7
            set i = i + 1
        endloop
        call TimerStart(Timer, Duration, false, function act_2)
        if (Index < 3) then
            call TimerStart(Loop_Timer, 2 + Duration, false, function act)
        else
            call UnitApplyTimedLife(Effect, 'BTLF', 1)
            set Effect = null
        endif
    endfunction
    
    private function cond takes nothing returns boolean
        if (GetSpellAbilityId() == 'ANsi') then
            set Effect = CreateUnit(GetTriggerPlayer(), ENERGY_FISSURE, GetSpellTargetX(), GetSpellTargetY(), bj_UNIT_FACING)
            set Index = 0
            call TimerStart(Timer, 1, false, function act)
        endif
        return false
    endfunction

    private function InitTrigger 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
 
Level 17
Joined
Feb 11, 2011
Messages
1,860
Update:
I have fixed the spell. I think I was creating the dummies at the wrong location and something weird happened...

Final script (any improvements?):
JASS:
scope EnergyFissure initializer InitTrigger

    globals
        unit Effect = null
        timer Timer = CreateTimer()
        timer Loop_Timer = CreateTimer()
        real Duration = 0
        integer Index = 0
    endglobals
    
    private function group_act takes nothing returns nothing
        set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), 'h003', GetUnitX(Effect), GetUnitY(Effect), 0)
        call UnitAddAbility(bj_lastCreatedUnit, 'A04V')
        call RemoveGuardPosition(bj_lastCreatedUnit)
        call SetUnitAbilityLevel(bj_lastCreatedUnit, 'A04V', Index)
        call IssueTargetOrder(bj_lastCreatedUnit, "manaburn", GetEnumUnit())
        call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
    endfunction
    
    private function group_cond takes nothing returns boolean
        return (IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(Effect)) == true) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) == false)
    endfunction
    
    private function act_2 takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        local real radius = Duration * 300
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, radius, Condition(function group_cond))
        call ForGroup(bj_lastCreatedGroup, function group_act)
    endfunction
   
    private function act takes nothing returns nothing
        local real x = GetUnitX(Effect)
        local real y = GetUnitY(Effect)
        local real x2 = 0
        local real y2 = 0
        local integer i = 0
        local boolean b = true
        set Index = Index + 1
        set Duration = ((150 * Index) + 100)
        set Duration = Duration / 300
        loop
            set x2 = x + (100 * Cos(i * 45 * bj_DEGTORAD))
            set y2 = y + (100 * Sin(i * 45 * bj_DEGTORAD))
            set bj_lastCreatedUnit = CreateUnit(GetOwningPlayer(Effect), 'h003', x, y, bj_UNIT_FACING)
            call RemoveGuardPosition(bj_lastCreatedUnit)
            call UnitAddAbility(bj_lastCreatedUnit, 'A015')
            call SetUnitAbilityLevel(bj_lastCreatedUnit, 'A015', Index)
            if (IsTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY) == false) then
                set b = false
                call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, true)
            endif
            call IssuePointOrder(bj_lastCreatedUnit, "carrionswarm", x2, y2)
            if (b == false) then
                call SetTerrainPathable(x2, y2, PATHING_TYPE_WALKABILITY, false)
            endif
            call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1)
            exitwhen i == 7
            set i = i + 1
        endloop
        call TimerStart(Timer, Duration, false, function act_2)
        if (Index < 3) then
            call TimerStart(Loop_Timer, 1 + Duration, false, function act)
        else
            call UnitApplyTimedLife(Effect, 'BTLF', 1)
        endif
    endfunction
    
    private function cond takes nothing returns boolean
        if (GetSpellAbilityId() == 'A04R') then
            set Effect = CreateUnit(GetTriggerPlayer(), 'h00J', GetSpellTargetX(), GetSpellTargetY(), bj_UNIT_FACING)
            set Index = 0
            call TimerStart(Timer, 1, false, function act)
        endif
        return false
    endfunction

    private function InitTrigger 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
 
Last edited:
Level 6
Joined
Jun 16, 2007
Messages
235
I forgot to mention that you do not put identifier constants in your spell scopes but in separate libraries.
Example:
JASS:
//===========================================================================
//  Ability identifiers
//===========================================================================
library AID

globals
//===========================================================================
//  CREEPS
//===========================================================================
    // Skeletal Mage
    public constant integer autotarget_1 = 'Sa01'
    public constant integer immediate_1 = 'Si01'
    public constant integer death_coil = 'A026'
    public constant integer undead_summon_1 = 'A009'
    
    // Dark Ranger
    public constant integer autotarget_2 = 'Sa02'
    public constant integer immediate_2 = 'Si02'
    public constant integer undead_summon_2 = 'A00L'
    public constant integer black_arrow = 'A00K'
    
    // Heretic
    public constant integer raise_ghoul = 'A02B'
    public constant integer unholy_frenzy = 'A00B'
    
    // Necromancer
    public constant integer autotarget_3 = 'Sa03'
    public constant integer immediate_3 = 'Si03'
    
//===========================================================================
//  HEROES
//===========================================================================
    public constant integer inventory = 'AInv'

    // Paladin
    public constant integer holy_light = 'A00N'
    public constant integer divine_shield = 'A000'
    public constant integer turn_undead = 'A00P'
    public constant integer turn_undead_dummy = 'A00O'
    public constant integer avatar = 'AHav'
    public constant integer bash = 'A00R'
    
    // Water Mage
    public constant integer water_splash = 'A00U'
    public constant integer water_elemental = 'A00Q'
    public constant integer water_elemental_nocd = 'A00V'
    public constant integer runestone_pebble = 'A00T'
    
    // Frost Archer
    public constant integer ice_shards = 'A00X'
    public constant integer ice_shards_dummy = 'A00Y'
    public constant integer ice_age = 'A020'
    public constant integer freez = 'A022' // also ice age dummy
    
    // Rasta
    public constant integer mass_hex = 'A004'
    public constant integer dummy_hex = 'A003'    
    public constant integer mass_serpent_wards = 'A006'    
    
//===========================================================================
//  ITEMS
//===========================================================================
    public constant integer revive_hero = 'A002' // scroll
    
    public constant integer tome_str = 'Astr'
    public constant integer tome_agi = 'Aagi'
    public constant integer tome_int = 'Aint'
    public constant integer tome_cohadar = 'A00H'
    
    public constant integer dummy_str = 'A008'
    public constant integer dummy_agi = 'A005'
    public constant integer dummy_int = 'A007'
    
    public constant integer bonus_str = 'Sstr'
    public constant integer bonus_agi = 'Sagi'
    public constant integer bonus_int = 'Sint'
    
    public constant integer blink = 'A00G' // scroll
endglobals

endlibrary
 
Level 4
Joined
Mar 27, 2008
Messages
112
I forgot to mention that you do not put identifier constants in your spell scopes but in separate libraries.
Example:
JASS:
//===========================================================================
//  Ability identifiers
//===========================================================================
library AID

globals
//===========================================================================
//  CREEPS
//===========================================================================
    // Skeletal Mage
    public constant integer autotarget_1 = 'Sa01'
    public constant integer immediate_1 = 'Si01'
    public constant integer death_coil = 'A026'
    public constant integer undead_summon_1 = 'A009'
    
    // Dark Ranger
    public constant integer autotarget_2 = 'Sa02'
    public constant integer immediate_2 = 'Si02'
    public constant integer undead_summon_2 = 'A00L'
    public constant integer black_arrow = 'A00K'
    
    // Heretic
    public constant integer raise_ghoul = 'A02B'
    public constant integer unholy_frenzy = 'A00B'
    
    // Necromancer
    public constant integer autotarget_3 = 'Sa03'
    public constant integer immediate_3 = 'Si03'
    
//===========================================================================
//  HEROES
//===========================================================================
    public constant integer inventory = 'AInv'

    // Paladin
    public constant integer holy_light = 'A00N'
    public constant integer divine_shield = 'A000'
    public constant integer turn_undead = 'A00P'
    public constant integer turn_undead_dummy = 'A00O'
    public constant integer avatar = 'AHav'
    public constant integer bash = 'A00R'
    
    // Water Mage
    public constant integer water_splash = 'A00U'
    public constant integer water_elemental = 'A00Q'
    public constant integer water_elemental_nocd = 'A00V'
    public constant integer runestone_pebble = 'A00T'
    
    // Frost Archer
    public constant integer ice_shards = 'A00X'
    public constant integer ice_shards_dummy = 'A00Y'
    public constant integer ice_age = 'A020'
    public constant integer freez = 'A022' // also ice age dummy
    
    // Rasta
    public constant integer mass_hex = 'A004'
    public constant integer dummy_hex = 'A003'    
    public constant integer mass_serpent_wards = 'A006'    
    
//===========================================================================
//  ITEMS
//===========================================================================
    public constant integer revive_hero = 'A002' // scroll
    
    public constant integer tome_str = 'Astr'
    public constant integer tome_agi = 'Aagi'
    public constant integer tome_int = 'Aint'
    public constant integer tome_cohadar = 'A00H'
    
    public constant integer dummy_str = 'A008'
    public constant integer dummy_agi = 'A005'
    public constant integer dummy_int = 'A007'
    
    public constant integer bonus_str = 'Sstr'
    public constant integer bonus_agi = 'Sagi'
    public constant integer bonus_int = 'Sint'
    
    public constant integer blink = 'A00G' // scroll
endglobals

endlibrary

Uhmm? Why? You can just as easily have globals for each spell it doesn't matter it's just how you prefer to code...
 
Level 6
Joined
Jun 16, 2007
Messages
235
Uhmm? Why? You can just as easily have globals for each spell it doesn't matter it's just how you prefer to code...

Because same codes are sometimes used in more than one spell.
Because when you copy spells between maps, codes change and you need to update them.
(and it is easiest to do it when it is all in one place)

And because the way I prefer to code is the way of Tao.
 
Level 17
Joined
Feb 11, 2011
Messages
1,860
Okay, thanks guys. Appreciate the tips!

EDIT:
If I set a global variable as private in a scope, can I set another variable with the same name in another scope? Will both "Unit" variables be set correctly? For example:
JASS:
scope Test initializer TrigTest
    
    globals
        private unit Unit = null
    endglobals
    
    private function act takes nothing returns nothing
        set Unit = udg_Hero[1]
        call KillUnit(Unit)
    endfunction
    
    private function TrigTest takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerChatEvent(t, Player(0), "test", true)
        call TriggerAddAction(t, function act)
        set t = null
    endfunction
    
endscope
JASS:
scope Test2 initializer TrigTest
    
    globals
        private unit Unit = null
    endglobals
    
    private function act takes nothing returns nothing
        set Unit = udg_Hero[2]
        call KillUnit(Unit)
    endfunction
    
    private function TrigTest takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterPlayerChatEvent(t, Player(0), "test2", true)
        call TriggerAddAction(t, function act)
        set t = null
    endfunction
    
endscope
 
Last edited:
Status
Not open for further replies.
Top