• 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.

[JASS] Help with UnitGroup

Status
Not open for further replies.
Level 20
Joined
Jul 14, 2011
Messages
3,213
Hi! I made this trigger to deal the sum of the last 5 hits to the target. Now I want to pick every unit in 350 from the position of the target to cast Purge on them. I've been trying for about 30 minutes to understand how to create the UnitGroups and else in JASS without leaking or bugging.

JASS:
function Electric7Overcharge_Conditions takes nothing returns boolean
    return GetUnitTypeId(udg_GDD_DamageSource) == 'h01S'
endfunction

function Electric_7_Overcharge_Actions takes nothing returns nothing
    local integer Id = GetHandleId(udg_GDD_DamageSource) // Damage Source ID
    local integer Hits = (LoadInteger(udg_Hash, Id, 4) + 1) // Amount of Hits
    local real Damage = (LoadReal(udg_Hash, Id, 5) + udg_GDD_Damage) // Amount of damage dealt
    local texttag TT = CreateTextTag() // The Critical TextTag
    
    call SaveInteger(udg_Hash, Id, 4, Hits) // Update Hits
    call SaveReal(udg_Hash, Id, 5, Damage) // Update Damage
    
    // Deal Damage if "Hits > 5" and display TextTag"
    if Hits == 5 then
        call DisableTrigger(gg_trg_Electric_7_Overcharge)
        call UnitDamageTarget(udg_GDD_DamageSource, udg_GDD_DamagedUnit, Damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
        call EnableTrigger(gg_trg_Electric_7_Overcharge)
        // Text Tag Critical
        call SetTextTagPermanent(TT, false)
        call SetTextTagText(TT, (I2S(R2I(Damage)) + "!"), 0.036)
        call SetTextTagPos(TT, GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 0)
        call SetTextTagColor(TT, 230, 0, 0, 255)
        call SetTextTagVelocity(TT, (0.08 * Cos(90 * bj_DEGTORAD)), ((0.08 * Sin(90 * bj_DEGTORAD))))
        call SetTextTagVisibility(TT, true)
        call SetTextTagLifespan(TT, 3)
        call SetTextTagFadepoint(TT, 2)
        // Clearing values
        call SaveInteger(udg_Hash, Id, 4, 0)
        call SaveReal(udg_Hash, Id, 5, 0)
        // Special Effect
        set udg_SfxQ = (udg_SfxQ + 1)
        set udg_Sfx[udg_SfxQ] = AddSpecialEffect("war3mapImported\\LightningsLong.mdx", GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit))
        set udg_Reals = (udg_Reals + 1)
        // Here's where the Unit Group thing goes.
    endif
    
    set TT = null
    
endfunction

//===========================================================================
function InitTrig_Electric_7_Overcharge takes nothing returns nothing
    set gg_trg_Electric_7_Overcharge = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Electric_7_Overcharge, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Electric_7_Overcharge, function Electric7Overcharge_Conditions )
    call TriggerAddAction( gg_trg_Electric_7_Overcharge, function Electric_7_Overcharge_Actions )
endfunction

What I want to do is basically this, adding the actions in the JASS script below.

  • Actions
    • Custom script: set bj_wantDestroyGroup == true
    • Unit Group - Pick every unit in (Units within 350.00 of (Position of GDD_DamagedUnit) matching (((Picked unit) belongs to an enemy of (Owner of GDD_DamageSource)) Equal to True)) and do (Actions)
      • Loop - (Actions)
And these are the actions inside the loop

JASS:
function Electric_7_OverChargeGroup_Actions takes nothing returns nothing
    call CreateUnit(GetOwningPlayer(udg_GDD_DamageSource), 'h021', GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 0.00)
    call UnitAddAbility(bj_lastCreatedUnit, 'A004')
    call UnitApplyTimedLife(bj_lastCreatedUnit, 'BTLF', 1.00)
    call IssueTargetOrder(GetEnumUnit(), "purge", bj_lastCreatedUnit)
endfunction
 
Last edited:
Put this above the map header...
JASS:
native UnitAlive takes unit u returns boolean

function HitThem takes unit target, integer abilityID, integer level returns nothing
   local unit first
   local unit dummy
   call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(target), GetUnitY(target), 350, null)
   loop
      set first = FirstofGroup(bj_lastCreatedGroup)
      exitwhen first==null
      if UnitAlive(first) then //put more conditions here
         set dummy = CreateUnit(....
             //purge code here
         set dummy = null
      endif
      call GroupRemoveUnit(bj_lastCreatedGroup, first)
   endloop   
endfunction
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Actually, looks a lot easier and simpler, but I'd like to know what you did there. Why in the map header?, How to call that action from the ElectricOverCharge trigger?

I basically copied the GUI function to JASS and managed to achieve everything except getting the dummy cast the ability over the Enum unit, but doesn't seem really "functional" to use 3 functions per group.

I can see you just picked all the units and filtered them inside the loop with If/Then/Else. But you set unit(bj_lastCreatedGroup) ¿? Unit = Group?

I'm sorry if I'm being a rock-head for not "just doing it". Please, be patient. I'm just starting jass. I managed it to work with the loop in the same trigger, but the dummy doesn't cast the ability, tough I can do it manually, they don't, and the orderstring of the ability (slow) is "purge"...

JASS:
function Electric7Overcharge_Conditions takes nothing returns boolean
    return GetUnitTypeId(udg_GDD_DamageSource) == 'h01S'
endfunction

function Electric_7_Overcharge_Actions takes nothing returns nothing
    local integer Id = GetHandleId(udg_GDD_DamageSource) // Damage Source ID
    local integer Hits = (LoadInteger(udg_Hash, Id, 4) + 1) // Amount of Hits
    local real Damage = (LoadReal(udg_Hash, Id, 5) + udg_GDD_Damage) // Amount of damage dealt
    local texttag TT = CreateTextTag() // The Critical TextTag
    local unit first
    local unit dummy
    
    call SaveInteger(udg_Hash, Id, 4, Hits) // Update Hits
    call SaveReal(udg_Hash, Id, 5, Damage) // Update Damage
    
    // Deal Damage if "Hits > 5" and display TextTag"
    if Hits == 5 then
        call DisableTrigger(gg_trg_Electric_7_Overcharge)
        call UnitDamageTarget(udg_GDD_DamageSource, udg_GDD_DamagedUnit, Damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
        call EnableTrigger(gg_trg_Electric_7_Overcharge)
        // Clearing values
        call SaveInteger(udg_Hash, Id, 4, 0)
        call SaveReal(udg_Hash, Id, 5, 0)
        // Special Effect
        set udg_SfxQ = (udg_SfxQ + 1)
        set udg_Sfx[udg_SfxQ] = AddSpecialEffect("war3mapImported\\LightningsLong.mdx", GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit))
        set udg_Reals = (udg_Reals + 1)
        // Purging Near Units
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 1.00, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first == null
            if (GetUnitState(first, UNIT_STATE_LIFE) <= 0) and (IsUnitEnemy(first, GetOwningPlayer(udg_GDD_DamageSource)) == true)then
                set dummy = CreateUnit(GetOwningPlayer(udg_GDD_DamageSource), 'h021', GetUnitX(first), GetUnitY(first), 90.00)
                call UnitAddAbility(dummy, 'A004')
                call UnitApplyTimedLife(dummy, 'BTLF', 5)
                call IssueTargetOrder(dummy , "slow", first)
                call BJDebugMsg(I2S(GetUnitCurrentOrder(dummy))) // This is giving me "0". 
                set dummy = null
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
    endif

endfunction

//===========================================================================
function InitTrig_Electric_7_Overcharge takes nothing returns nothing
    set gg_trg_Electric_7_Overcharge = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Electric_7_Overcharge, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Electric_7_Overcharge, function Electric7Overcharge_Conditions )
    call TriggerAddAction( gg_trg_Electric_7_Overcharge, function Electric_7_Overcharge_Actions )
endfunction
 
Last edited:
Why in the map header?, coz you wrote it in GUI-JASS convertion and its easy to call it that way, therefore making it 'like'
a library in your map so that other spells can use it also...

It doesnt cast coz your AOE is 1.0 (not 350), and your filtering with a life less than or equal to 0...
try this...
JASS:
call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 350, null)
loop
    set first = FirstOfGroup(bj_lastCreatedGroup)
    exitwhen first == null
    if not IsUnitType(first,UNIT_TYPE_DEAD) and IsUnitEnemy(first, GetOwningPlayer(udg_GDD_DamageSource)) then
        set dummy = CreateUnit(GetOwningPlayer(udg_GDD_DamageSource), 'h021', GetUnitX(first), GetUnitY(first), 90.00)
        call UnitAddAbility(dummy, 'A004')
        call UnitApplyTimedLife(dummy, 'BTLF', 5)
        call IssueTargetOrder(dummy , "slow", first)
        call BJDebugMsg(I2S(GetUnitCurrentOrder(dummy)))
        set dummy = null
    endif
    call GroupRemoveUnit(bj_lastCreatedGroup, first)
endloop
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Thanks! You're right, I was ordering the unit to cast to the dead unit.

Why can i use "bj_lastCreatedGroup" if I havent created any group?
How's that "first" is automatically nulled and doesn't require me to "set first = null"?

EDIT: I hate the small time gap between damage dealt -> Dummy Slow Cast. I managed to fake some kind of storm delay creating a small lightningbolt before ordering the dummy to cast, and to syncronize the dummy cast with the second Lightning.

Now: How could I improve this code keeping the effect (or improving it)?

JASS:
function Electric7Overcharge_Conditions takes nothing returns boolean
    return GetUnitTypeId(udg_GDD_DamageSource) == 'h01S'
endfunction

function Electric_7_Overcharge_Actions takes nothing returns nothing
    local integer Id = GetHandleId(udg_GDD_DamageSource) // Damage Source ID
    local integer Hits = (LoadInteger(udg_Hash, Id, 4) + 1) // Amount of Hits
    local real Damage = (LoadReal(udg_Hash, Id, 5) + udg_GDD_Damage) // Amount of damage dealt
    local texttag TT = CreateTextTag() // The Critical TextTag
    local unit first
    local unit dummy
    
    call SaveInteger(udg_Hash, Id, 4, Hits) // Update Hits
    call SaveReal(udg_Hash, Id, 5, Damage) // Update Damage
    
    // Deal Damage if "Hits > 5" and display TextTag"
    if Hits == 5 then
        set udg_SfxQ = (udg_SfxQ + 1)
        set udg_Sfx[udg_SfxQ] = AddSpecialEffect("Doodads\\Cinematic\\Lightningbolt\\Lightningbolt.mdl", GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit))
        set udg_Reals = (udg_Reals + 1)
        call DisableTrigger(gg_trg_Electric_7_Overcharge)
        call UnitDamageTarget(udg_GDD_DamageSource, udg_GDD_DamagedUnit, Damage, true, true, ATTACK_TYPE_HERO, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
        call EnableTrigger(gg_trg_Electric_7_Overcharge)
        // Text Tag Critical
        call SetTextTagPermanent(TT, false)
        call SetTextTagText(TT, (I2S(R2I(Damage)) + "!"), 0.036)
        call SetTextTagPos(TT, GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 0)
        call SetTextTagColor(TT, 230, 0, 0, 255)
        call SetTextTagVelocity(TT, (0.08 * Cos(90 * bj_DEGTORAD)), ((0.08 * Sin(90 * bj_DEGTORAD))))
        call SetTextTagVisibility(TT, true)
        call SetTextTagLifespan(TT, 3)
        call SetTextTagFadepoint(TT, 2)
        // Clearing values
        call SaveInteger(udg_Hash, Id, 4, 0)
        call SaveReal(udg_Hash, Id, 5, 0)
        // Purging Near Units
        call GroupEnumUnitsInRange(bj_lastCreatedGroup, GetUnitX(udg_GDD_DamagedUnit), GetUnitY(udg_GDD_DamagedUnit), 350, null)
        loop
            set first = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen first == null
            if not IsUnitType(first,UNIT_TYPE_DEAD) and IsUnitEnemy(first, GetOwningPlayer(udg_GDD_DamageSource)) then
                set dummy = CreateUnit(GetOwningPlayer(udg_GDD_DamageSource), 'h021', GetUnitX(first), GetUnitY(first), 90.00)
                call UnitAddAbility(dummy, 'A005')
                call UnitApplyTimedLife(dummy, 'BTLF', 0.75)
                call IssueTargetOrder(dummy , "slow", first)
                call BJDebugMsg(I2S(GetUnitCurrentOrder(dummy)))
                set dummy = null
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup, first)
        endloop
        // Special Effect
        set udg_SfxQ = (udg_SfxQ + 1)
        call TriggerSleepAction(0.35)
        set udg_Sfx[udg_SfxQ] = AddSpecialEffect("war3mapImported\\LightningsLong.mdx", GetUnitX(GetTriggerUnit()), GetUnitY(GetTriggerUnit()))
        set udg_Reals = (udg_Reals + 1)
    endif
    
    set TT = null

endfunction

//===========================================================================
function InitTrig_Electric_7_Overcharge takes nothing returns nothing
    set gg_trg_Electric_7_Overcharge = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Electric_7_Overcharge, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Electric_7_Overcharge, function Electric7Overcharge_Conditions )
    call TriggerAddAction( gg_trg_Electric_7_Overcharge, function Electric_7_Overcharge_Actions )
endfunction
 
Last edited:
so that you dont need to create nor destroy groups, coz bj_lastCreatedGroup is a pre-created group already...

about the delay, if your spell is simple you could use locals then delay it with a wait, like you did or you could use a timer, take the
ID of a timer, save the 2nd effect then destroy it in the timer loop, you dont need to array it...
sample of a local delay...
JASS:
function Trig_Untitled_Trigger_002_Actions takes nothing returns nothing
    local effect sfx = AddSpecialEffect("Abilities\\Spells\\Other\\Incinerate\\FireLordDeathExplode.mdl",0,0)
    call TriggerSleepAction(0.35)
    call DestroyEffect(sfx)
    call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\MassTeleport\\MassTeleportTarget.mdl",0,0))
    set sfx = null
endfunction

//===========================================================================
function InitTrig_Untitled_Trigger_002 takes nothing returns nothing
    set gg_trg_Untitled_Trigger_002 = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_Untitled_Trigger_002, 5 )
    call TriggerAddAction( gg_trg_Untitled_Trigger_002, function Trig_Untitled_Trigger_002_Actions )
endfunction

EDIT:
your spell is same as my Sudden Blow and Charged Bolt spell in my map Field of Conflict, its an open resource, so you may see the code...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Thanks a lot ! The problem with the delay is the cast time of default Wc3 abilities. Any abiliti requires about 0.35 second in order to be cast. it's the case with "Slow". I was creating the Lightning effect at the same time I ordered the dummy to cast "Purge" but the Lightning was showing first because of the castimg time between "Begins casting an ability" and "Starts the effect of an ability", So, I had to delay the Special effect to make it simultaneous with the Purge dummy casts. I created an small lightning before so it looks great.

This is a TD, but I wanted to make it different and special. It isn't any "special" ability, it's just a Lightning that shocks units purging them.

I array the Special effects from 1 to 10, when Reals = 10, it loops from 1 to 10 and destroys special effects. I did this because DestroyEffect(AddSpecialEffect()) was "cutting off" the death animation of the sfx, and I tough this was a good way to destroy them, since only the Effect#10 gets cut off, and it isn't noticeable in most effects.
 
This...
Bribe said:
The dummy unit must have the 'Amov' ability removed, as well. Its cast point/backswing must be 0, and the spell's cast time must be 0.

and as I said, your effect leaks and should be destroyed, some effects cant be displayed
properly though by DestroyEffect(Add... so you need to delay that, tha's why I created a local for it...
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
But why they leak if I destroy them after storing in the variable?

Knowing that thing about the CastSwing and else, I can now remove the special effect (setting it as the dummy model) and remove the Group Creation using thunderclap.

Thanks everyone.
 
Last edited:
Status
Not open for further replies.
Top