• 🏆 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] Help with MUI Group.

Status
Not open for further replies.
Level 8
Joined
Jun 18, 2007
Messages
214
Hello everyone. I have a small problem. Now, I made a spell, that creates a dummy unit and damages units in it's path. For a trigger to damage units only once I used a global group, and saved a hit unit in it. Now the problem is MUI. Until one spell is finished, the group i still filled with the units. Now I need a solution on how to make that group MUI, without using dynamic group creation, because If I clear the group just after usage next timer shot will not work, and it will still damage the same unit more then once.

Here's is the entire code of the spell

JASS:
constant function Venom_Orb_End_SFX takes nothing returns string
    return "Abilities\\Weapons\\ChimaeraAcidMissile\\ChimaeraAcidMissile.mdl"
endfunction

constant function Venom_Orb_DoT_SFX takes nothing returns string
    return "Abilities\\Spells\\Undead\\DeathCoil\\DeathCoilSpecialArt.mdl"
endfunction

constant function Venom_Orb_ID takes nothing returns integer
    return 'A000'
endfunction

constant function Venom_Orb_Dummy_ID takes nothing returns integer
    return 'h000'
endfunction

constant function Venom_Orb_Travel_Dist takes integer i returns real
    return 400.0 * i
endfunction

constant function Venom_Orb_Move_Speed takes nothing returns real
    return 30.0
endfunction

constant function Venom_Orb_Pick_Radius takes nothing returns real
    return 175.0
endfunction

constant function Venom_Orb_DoT_Time takes integer i returns real
    return 1.0 * i
endfunction

constant function Venom_Orb_Init_Damage takes integer i returns real
    return 25.0 * i
endfunction

constant function Venom_Orb_DoT_Damage takes integer i returns real
    return 20.0 * i
endfunction

constant function Venom_Orb_DoT_MoveInc takes nothing returns real
    return 0.5 // %
endfunction

constant function Venom_Orb_DoTimer_Loop takes nothing returns real
    return 0.5
endfunction

constant function Venom_Orb_MoveTimer_Loop takes nothing returns real
    return 0.03
endfunction

//======================================================================================================================
function Venom_Orb_Conditions takes nothing returns boolean
    return GetSpellAbilityId() == Venom_Orb_ID()
endfunction       
    
//======================================================================================================================
function Venom_Orb_DoT takes nothing returns nothing
 local timer dmg = GetExpiredTimer()
 
 local unit tar     = LoadUnitHandle(udg_NowHash, GetHandleId(dmg), 1)
 local unit damager = LoadUnitHandle(udg_NowHash, GetHandleId(dmg), 4)
 
 local real dur    = LoadReal      (udg_NowHash, GetHandleId(dmg), 2)
 local real curDur = LoadReal      (udg_NowHash, GetHandleId(dmg), 5)
 local real x      = LoadReal      (udg_NowHash, GetHandleId(dmg), 6)
 local real y      = LoadReal      (udg_NowHash, GetHandleId(dmg), 7)
 local real x1     = GetUnitX      (tar)
 local real y1     = GetUnitY      (tar)
 local real damage = LoadReal      (udg_NowHash, GetHandleId(dmg), 8)
 
 if curDur < dur and IsUnitType(tar, UNIT_TYPE_DEAD) == false then
 
    if x != x1 or y != y1 then
    
      set damage = damage + (damage * Venom_Orb_DoT_MoveInc())
      call UnitDamageTarget (damager, tar, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_POISON, WEAPON_TYPE_WHOKNOWS)
      call DestroyEffect    (AddSpecialEffect (Venom_Orb_DoT_SFX(), x1, y1))

     
      //Creating a floating text.
      call FloatText        (I2S(R2I(damage)) + "!", 0, 255, 0, x1, y1, 0.07, 0.5, 1.5) 

      call SaveReal         (udg_NowHash, GetHandleId(dmg), 5, curDur + Venom_Orb_DoTimer_Loop())
      call SaveReal         (udg_NowHash, GetHandleId(dmg), 6, x1)
      call SaveReal         (udg_NowHash, GetHandleId(dmg), 7, y1)
      set tar = null
      set damager = null
      set dmg = null
   else
   
      call UnitDamageTarget (damager, tar, damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_POISON, WEAPON_TYPE_WHOKNOWS)
      call DestroyEffect    (AddSpecialEffect (Venom_Orb_DoT_SFX(), x1, y1))
      call SaveReal         (udg_NowHash, GetHandleId(dmg), 5, curDur + Venom_Orb_DoTimer_Loop())
      
      //Another floating text.
      call FloatText        (I2S(R2I(damage)) + "!", 0, 255, 0, x1, y1, 0.07, 0.5, 1.5) 
      
      call SaveReal         (udg_NowHash, GetHandleId(dmg), 6, x1)
      call SaveReal         (udg_NowHash, GetHandleId(dmg), 7, y1)
      set tar = null
      set damager = null
      set dmg = null
   endif  
      
  else
  
     call PauseTimer(dmg)
     call DestroyTimer(dmg)
     call FlushChildHashtable(udg_NowHash, GetHandleId(dmg))
     set dmg = null
     set tar = null
     set damager = null
  endif   
endfunction


function Venom_Orb_Pick_ActionFilter takes nothing returns boolean
    local unit u    = GetFilterUnit()
    local timer dmg = null
    
   if IsUnitEnemy(u, GetOwningPlayer(udg_getdamager)) == true and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and IsUnitType(u, UNIT_TYPE_DEAD) == false and IsUnitInGroup(u, udg_damage_group) == false then
   
    call DestroyEffect   (AddSpecialEffect(Venom_Orb_End_SFX(), GetUnitX(u), GetUnitY(u)))
    call UnitDamageTarget(udg_getdamager, u, Venom_Orb_Init_Damage(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_POISON, WEAPON_TYPE_WHOKNOWS)
    
    set dmg = CreateTimer()
    call SaveUnitHandle  (udg_NowHash, GetHandleId(dmg), 1, u)
    call SaveUnitHandle  (udg_NowHash, GetHandleId(dmg), 4, udg_getdamager)
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 2, Venom_Orb_DoT_Time(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 5, 0.0) 
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 6, GetUnitX(u))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 7, GetUnitY(u))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 8, Venom_Orb_DoT_Damage(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())))
    call TimerStart      (dmg, Venom_Orb_DoTimer_Loop(), true, function Venom_Orb_DoT)
    set dmg = null
    call GroupAddUnit    (udg_damage_group, u)
   endif
   set u = null
   return false
endfunction  

function Venom_Orb_Move takes nothing returns nothing
 local timer move = GetExpiredTimer()
 local timer dmg  = null
 
 local unit miss    = LoadUnitHandle(udg_NowHash, GetHandleId(move), 1)
 local real tarX    = LoadReal      (udg_NowHash, GetHandleId(move), 2)
 local real tarY    = LoadReal      (udg_NowHash, GetHandleId(move), 3)
 local real uniX    = GetUnitX      (miss)
 local real uniY    = GetUnitY      (miss)
 local real dist    = SquareRoot    ((uniX - tarX) * (uniX - tarX) + (uniY - tarY) * (uniY - tarY))
 local real angle   = Atan2         (tarY - uniY, tarX - uniX)
 local real moveX   = uniX + Venom_Orb_Move_Speed() * Cos(angle)
 local real moveY   = uniY + Venom_Orb_Move_Speed() * Sin(angle)
 
 if dist <= Venom_Orb_Move_Speed() or IsTerrainPathable(moveX, moveY, PATHING_TYPE_FLYABILITY) == true then
 
   call PauseTimer(move)
   call DestroyTimer(move)
   call FlushChildHashtable(udg_NowHash, GetHandleId(move))
   set move = null
   call DestroyEffect(AddSpecialEffect(Venom_Orb_End_SFX(), uniX, uniY))
   call RemoveUnit(miss)
   call GroupClear(udg_damage_group)
   set miss = null
   
  else   

   call SetUnitPosition      (miss, moveX, moveY)
   
   set udg_getdamager   = LoadUnitHandle(udg_NowHash, GetHandleId(move), 5)
   call GroupEnumUnitsInRange(LoadGroupHandle(udg_NowHash, StringHash("Global G"), 0), moveX, moveY, Venom_Orb_Pick_Radius(), Condition(function Venom_Orb_Pick_ActionFilter))
   set miss = null
   set move = null
   
  endif
endfunction   


function Venom_Orb_Actions takes nothing returns nothing
 local timer move = CreateTimer()
 
 local unit cast = GetTriggerUnit()

 local real angle = Atan2   (GetSpellTargetY() - GetUnitY(cast), GetSpellTargetX() - GetUnitX(cast))
 local real crX   = GetUnitX(cast) + 15.0 * Cos(angle)
 local real crY   = GetUnitY(cast) + 15.0 * Sin(angle)
 
 local unit miss  = CreateUnit(GetOwningPlayer(cast), Venom_Orb_Dummy_ID(), crX, crY, angle * bj_RADTODEG)
 
 local real targetX = GetUnitX(cast) + Venom_Orb_Travel_Dist(GetUnitAbilityLevel(cast, Venom_Orb_ID())) * Cos(angle)
 local real targetY = GetUnitY(cast) + Venom_Orb_Travel_Dist(GetUnitAbilityLevel(cast, Venom_Orb_ID())) * Sin(angle)
 
 call SaveUnitHandle (udg_NowHash, GetHandleId(move), 1, miss)
 call SaveUnitHandle (udg_NowHash, GetHandleId(move), 5, cast) 
 call SaveReal       (udg_NowHash, GetHandleId(move), 2, targetX)
 call SaveReal       (udg_NowHash, GetHandleId(move), 3, targetY)
 
 call TimerStart    (move, Venom_Orb_MoveTimer_Loop(), true, function Venom_Orb_Move)
 
 set cast = null
 set miss = null
 set move = null
endfunction  

//=====================================================================================================================================
function InitTrig_Venom_Orb takes nothing returns nothing
    local trigger t = CreateTrigger()

    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Venom_Orb_Conditions))
    call TriggerAddAction(t, function Venom_Orb_Actions)

    call Preload(Venom_Orb_End_SFX())
    call PreloadStart()

endfunction

Now the problem that I have is here:

JASS:
f dist <= Venom_Orb_Move_Speed() or IsTerrainPathable(moveX, moveY, PATHING_TYPE_FLYABILITY) == true then
 
   call PauseTimer(move)
   call DestroyTimer(move)
   call FlushChildHashtable(udg_NowHash, GetHandleId(move))
   set move = null
   call DestroyEffect(AddSpecialEffect(Venom_Orb_End_SFX(), uniX, uniY))
   call RemoveUnit(miss)
   call GroupClear(udg_damage_group)
   set miss = null
   
  else   

   call SetUnitPosition      (miss, moveX, moveY)
   
   set udg_getdamager   = LoadUnitHandle(udg_NowHash, GetHandleId(move), 5)
   call GroupEnumUnitsInRange(LoadGroupHandle(udg_NowHash, StringHash("Global G"), 0), moveX, moveY, Venom_Orb_Pick_Radius(), Condition(function Venom_Orb_Pick_ActionFilter))
   set miss = null
   set move = null
   
  endif

As you see if the conditions are not met, the group is still filled with the old units so this part won't damage them twice:
JASS:
function Venom_Orb_Pick_ActionFilter takes nothing returns boolean
    local unit u    = GetFilterUnit()
    local timer dmg = null
    
   if IsUnitEnemy(u, GetOwningPlayer(udg_getdamager)) == true and IsUnitType(u, UNIT_TYPE_STRUCTURE) == false and IsUnitType(u, UNIT_TYPE_DEAD) == false and IsUnitInGroup(u, udg_damage_group) == false then
   
    call DestroyEffect   (AddSpecialEffect(Venom_Orb_End_SFX(), GetUnitX(u), GetUnitY(u)))
    call UnitDamageTarget(udg_getdamager, u, Venom_Orb_Init_Damage(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())), true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_POISON, WEAPON_TYPE_WHOKNOWS)
    
    set dmg = CreateTimer()
    call SaveUnitHandle  (udg_NowHash, GetHandleId(dmg), 1, u)
    call SaveUnitHandle  (udg_NowHash, GetHandleId(dmg), 4, udg_getdamager)
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 2, Venom_Orb_DoT_Time(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 5, 0.0) 
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 6, GetUnitX(u))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 7, GetUnitY(u))
    call SaveReal        (udg_NowHash, GetHandleId(dmg), 8, Venom_Orb_DoT_Damage(GetUnitAbilityLevel(udg_getdamager, Venom_Orb_ID())))
    call TimerStart      (dmg, Venom_Orb_DoTimer_Loop(), true, function Venom_Orb_DoT)
    set dmg = null
    call GroupAddUnit    (udg_damage_group, u)
   endif
   set u = null
   return false
endfunction

The group used is
JASS:
udg_damage_group
. Now how to make that group MUI, how to clear it in the part where the dummy unit is still moving without losing units. I tried saving the group in a hashtable and calling it in global group, but again after I clear the group it's still not working.
Thank you in advance!
 
Level 4
Joined
Dec 9, 2008
Messages
52
I use a hashtable. Whenever a spell hits a unit do
Code:
call SaveBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit),true)

Then to check if you've hit the same unit, do
Code:
if LoadBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit)) = true

There's a problem with this, however, because a unit won't be able to hit the same unit again. You cast the spell 10 seconds later and it does no damage. To solve this you do
Code:
call SaveBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit),false)
some time later.
 
Level 8
Joined
Jun 18, 2007
Messages
214
I use a hashtable. Whenever a spell hits a unit do
Code:
call SaveBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit),true)

Then to check if you've hit the same unit, do
Code:
if LoadBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit)) = true

There's a problem with this, however, because a unit won't be able to hit the same unit again. You cast the spell 10 seconds later and it does no damage. To solve this you do
Code:
call SaveBoolean(udg_hash,GetHandleId(damagingunit),GetHandleId(damagedunit),false)
some time later.

Hmm.... That's a good idea, but I can't use this, because I'm using boolexpr as callback for a group, which means I can't transfer SavedBoolean there (I can't use GetHandleId(u)) because u unit is only in the Action Filter function.
 
Level 8
Joined
Jun 18, 2007
Messages
214
Set the damager to a temporary global variable of type unit right before you check the group and then reference the global variable inside your filter function.

What do you mean? I did so, but I can't reset the boolean again, because I don't have the child key, since the child key is the filter unit. Damager was always set to a global variable.
 
Level 4
Joined
Dec 9, 2008
Messages
52
Oh whoops.

Alright here's another way to do it. Every damager has a group of units he's currently damaging. Use the damager as the parent key and some stringhash like "targets" as the child key and then a new group as the value. Then give each victim a cooldown value w/ the child key as the damager. Then cycle through all the damagers and then cycle through the victims, decreasing their cooldown for that damager. Ok that probably didn't make sense. So here's an example:

So BOB attacks JIM. BOB is added to ATTACKERS. JIM is added to BOB's group of victims. A cooldown of 10 is added to the JIM with BOB as the key. After 1 second, every unit in ATTACKERS is checked. So that means BOB get's checked. Then every unit in BOB's victim group is checked. That means JIM gets checked. BOB is used as a child key for JIM and 10 is returned. The 10 is decreased by 1 and saved again.

Now XXX attacks JIM, too. XXX is added to ATTACKERS. JIM is added to XXX's group of victims. A cooldown of 10 is added to JIM with XXX as the key. Now 2 seconds have gone by. Every unit in ATTACKERS is checked. So that means BOB get's checked. Then every unit in BOB's victim group is checked. That means JIM gets checked. BOB is used as a child key for JIM and 9 is returned. The 9 is decreased by 1 and saved again. Now XXX gets checked. hat means JIM gets checked again. XXX is used as a child key for JIM and 10 is returned. The 10 is decreased by 1 and saved again.

I hope that made sense =\
 
Level 8
Joined
Jun 18, 2007
Messages
214
Oh whoops.

Alright here's another way to do it. Every damager has a group of units he's currently damaging. Use the damager as the parent key and some stringhash like "targets" as the child key and then a new group as the value. Then give each victim a cooldown value w/ the child key as the damager. Then cycle through all the damagers and then cycle through the victims, decreasing their cooldown for that damager. Ok that probably didn't make sense. So here's an example:

So BOB attacks JIM. BOB is added to ATTACKERS. JIM is added to BOB's group of victims. A cooldown of 10 is added to the JIM with BOB as the key. After 1 second, every unit in ATTACKERS is checked. So that means BOB get's checked. Then every unit in BOB's victim group is checked. That means JIM gets checked. BOB is used as a child key for JIM and 10 is returned. The 10 is decreased by 1 and saved again.

Now XXX attacks JIM, too. XXX is added to ATTACKERS. JIM is added to XXX's group of victims. A cooldown of 10 is added to JIM with XXX as the key. Now 2 seconds have gone by. Every unit in ATTACKERS is checked. So that means BOB get's checked. Then every unit in BOB's victim group is checked. That means JIM gets checked. BOB is used as a child key for JIM and 9 is returned. The 9 is decreased by 1 and saved again. Now XXX gets checked. hat means JIM gets checked again. XXX is used as a child key for JIM and 10 is returned. The 10 is decreased by 1 and saved again.

I hope that made sense =\

Eh, I know you're trying to help, but forgive me I didn't understand it quite right :p. But, if I understood something right that means you're asking me to create another group, which means I would have to use dynamic group creation.
Now If I'm to use dynamic group creation, it would be no problem for me to achieve MUI, 'cause I would then just use another group in another trigger and then recall it every time I'm checking the filter.
But, I'm sure I was wrong and that you meant something else. It would be good, and greatly appreciated if you could post this as a JASS code, then it would I'm sure clarify a lot of things :p.
 
Level 4
Joined
Dec 9, 2008
Messages
52
Well then I guess I have good news and I have bad news =)

The good news is that you understood what I was saying, the bad news is that I was suggesting dynamic group creation.

Why, might I ask, is dynamic group creation a bad thing?
 
Status
Not open for further replies.
Top