• 🏆 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] Group leak

Status
Not open for further replies.
Level 5
Joined
Sep 16, 2008
Messages
47
Hi.

i got a problem in this:

JASS:
scope FrostWyrm //version 1.0
/*---------------------------------------------------------------------------------------------------------
*
*       Requires:
*
*-----------------------------------------------------------------------------------------------------------
*
*   - CTL                       [By Nestharus]
*   - WorldBounds               [By Nestharus]
*   - IsDestructableTree        [By PitzerMike]
*   - SpellEffectEvent          [By Bribe]
*   - RegisterPlayerUnitEvent   [By Magtheridon9]
*
*-----------------------------------------------------------------------------------------------------------
*
*       How to import:
*
*-----------------------------------------------------------------------------------------------------------
*
*   - Copy Frost wyrm Ability from object editor ---> Abilities
*   - Copy Frost wyrm (Freeze) Ability from objest editor ---> Abilities
*   - Copy Frost wyrm (Freeze) Buff from object editor ---> Buffs/Effects
*   - Copy Frost wyrm - Dummy 1 from objedt editor ---> Units
*   - Copy Frost wyrm - Dummy 2 from object editor ---> Units
*   - Copy Frost wyrm Trigger from Trigger editor
*   - Copy Libraries folder from Trigger editor
*
*   - In object editor --> Abilities go to Frost wyrm (freeze) ability and set Buffs to Frost wyrm (Freeze)
*
*-----------------------------------------------------------------------------------------------------------
*
*       Changelog:
*
*-----------------------------------------------------------------------------------------------------------
*
*       v1.0 
*       - First release.
*       
*-----------------------------------------------------------------------------------------------------------*/

    /***********************************************************************************
    *                                                                                  *
    *                                 CONFIGURATION                                    *
    *                                                                                  *                                               
    ***********************************************************************************/


    globals
    
                            /*Ability and dummies rawcodes*/
        
      /*******************************************************************************/ 
      /**/                                                                         /**/
      /**/  private constant integer        SPELL_CODE              =   'A000'     /**/ /*Main Ability rawcode*/
      /**/  private constant integer        FREEZE_CODE             =   'A001'     /**/ /*Freeze ability rawcode*/
      /**/  private constant integer        WYRM_CODE               =   'h000'     /**/ /*Frost wyrm - Dummy 1 rawcode*/
      /**/  private constant integer        BREATH_CODE             =   'h003'     /**/ /*Frost wyrm - Dummy 2 rawcode*/
      /**/                                                                         /**/
      /*******************************************************************************/
    
    
                            /*Frost wyrm configuration*/
        
        //Frost wyrm scale
        private constant real           WYRM_SCALE              =   1
        //Frost wyrm spawn height
        private constant real           WYRM_HEIGHT_START       =   800
        //Frost wyrm destination height. Once it will get on this height it will not fly lower.
        private constant real           WYRM_HEIGHT             =   450
        //Time at which forst wyrm will reach WYRM_HEIGHT. If you will se too high like
        //3.0 frost wyrm will never reach WYRM_HEIGHT so it will just keep going lower.
        private constant real           WYRM_HEIGHT_TIME        =   3.0
        //Frost wyrm will not spawn on caster, it will spawn back by this distance
        private constant real           WYRM_CREATE_OFF         =   400
        //Frost wyrm will start it's animation once he will travel this distance
        private constant real           WYRM_ANIM_DISTANCE      =   175
        //Frost wyrm animation used for breath
        private constant string         WYRM_ANIMATION          =   "attack"       
        
                            /*Frost breath configuration*/
       
        //Cooldown between creating another one dummy for frost breath. At this
        //moment it's 2 * 0.031250
        private constant integer        BREATH_CD               =   2
        //To make dummie spawn from wyrm's head not from chest i set this distance
        private constant real           BREATH_CREATE_OFF       =   125
        //Frost wyrm will start breating after it will travel this distance
        private constant real           BREATH_START            =   400
        //Breath dummies scale
        private constant real           BREATH_SCALE            =   1
        //Bretah dummies start height
        private constant real           BREATH_HEIGHT           =   400
        //Time at which breat dummies reach the BREATH_LAND_HEIGHT 
        private constant real           BREATH_LAND_TIME        =   0.25
        //When breath dummies will get on it or lower height they will explode
        private constant real           BREATH_LAND_HEIGHT      =   20
        //Breath dummies scale when they land on the ground to make effect bigger
        private constant real           BREATH_DEATH_SCALE      =   1.8
        //Bretah dummies nimation played when they land on the ground
        private constant string         BREATH_ANIM             =   "death"
        //We stop frost wyrm after this time/ 1 = 0.031250
        private constant integer        BREATH_ANIM_STOP        =   28
        
        
                            /*Move speeds configuration*/
        
        //Wyrm movement speed before breathing
        private constant real           MOVE_SPEED_FIRST        =   600
        //Wyrm movement speed when it is breating
        private constant real           MOVE_SPEED_SECOND       =   300
        
                            /*Damage configuraton*/
        
        //Damage range
        private constant real           DAMAGE_RANGE            =   100
        //Attack type
        private constant attacktype     ATTACK_TYPE             =   ATTACK_TYPE_MAGIC
        //Damage type
        private constant damagetype     DAMAGE_TYPE             =   DAMAGE_TYPE_DEATH
        
                            /*Booleans*/
        
        //Should forst breath kill tress?
        private constant boolean        DESTROY_TREE            =   true
        //Should frost breath also freeze enemy units?
        private constant boolean        FREEZE                  =   true
            
                            /*Don't touch*/
                            
        //Dummy used to freeze targets
        private unit FREEZE_DUMMY
        
    endglobals
    
    /*Function to calculate damage dealt to every unit*/
    private function GetDamage takes integer level returns real
        return level * 25.00 + 25.00
    endfunction

    /*Function to calculate distancetravaled by wyrm once he start breathing*/
    private function GetDistance takes integer level returns real
        return level * 50.00 + 650.00
    endfunction
    
    /*Which units should not be damaged*/
    private function TargetFilter takes unit u, player p returns boolean
        return IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_FLYING)
    endfunction
    
    /*Which units should not be freezed*/
    private function FreezeFilter takes unit u, player p returns boolean
        return IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_DEAD) and not IsUnitType(u, UNIT_TYPE_FLYING) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)
    endfunction
    
    /***********************************************************************************
    *                                                                                  *
    *                               CONFIGURATION END                                  *
    *                                                                                  *                                               
    ***********************************************************************************/
    
    private function Tree_Kill takes nothing returns boolean
        call KillTree(GetFilterDestructable())
        return false
    endfunction
    
    /***********************************************************************************
    *                                                                                  *
    *                            FROST BREATH DUMMIES MOVEMENT                         *
    *                                                                                  *                                               
    ***********************************************************************************/
    
    private struct FrostBreath extends array
    
        private static unit array dummy
        
        private static player array owner
        
        private static real array damage
        private static real array height
        private static real array heightDec
        
        private static integer array level
        
        private static group array ignoreGroup
        static group damageGroup
        static rect treeRect
    
        implement CTL

            local unit t
            local real dx
            local real dy
        
        implement CTLExpire
        
            set dx = GetUnitX(dummy[this])
            set dy = GetUnitY(dummy[this])
        
            set height[this] = height[this] - heightDec[this]
            
            call SetUnitFlyHeight(dummy[this], height[this], 0)
            
            if height[this] <= BREATH_LAND_HEIGHT then
            
                 call GroupEnumUnitsInRange(damageGroup, dx, dy, DAMAGE_RANGE, null)
            
                loop
            
                    set t = FirstOfGroup(damageGroup)
                
                    exitwhen t == null
                
                    if TargetFilter(t, owner[this]) and not IsUnitInGroup(t, ignoreGroup[this]) then
                    
                        call UnitDamageTarget(dummy[this], t, damage[this], true, true, ATTACK_TYPE, DAMAGE_TYPE, null)
                        call GroupAddUnit(ignoreGroup[this], t)
                        
                        static if FREEZE then
                    
                            if FreezeFilter(t, owner[this]) then
                                call SetUnitAbilityLevel(FREEZE_DUMMY, FREEZE_CODE, level[this])
                                call IssueTargetOrder(FREEZE_DUMMY, "thunderbolt", t)
                            endif                        
                    
                        endif
                
                    endif
                    call GroupRemoveUnit(damageGroup, t)
                endloop
                
                call DestroyGroup(damageGroup)
                
                static if DESTROY_TREE then
                    call SetRect(treeRect, dx - DAMAGE_RANGE, dy - DAMAGE_RANGE, dx + DAMAGE_RANGE, dy + DAMAGE_RANGE)
                    call EnumDestructablesInRect(treeRect,function Tree_Kill,null)
                endif
            
                call SetUnitScale(dummy[this], BREATH_DEATH_SCALE, 0, 0)
            
                call SetUnitAnimation(dummy[this], BREATH_ANIM)
                call UnitApplyTimedLife(dummy[this], 'BTLF', 0.75)
            
                set dummy[this] = null
                
                call destroy()
            
            endif
        
        implement CTLNull
        implement CTLEnd
    
        static method onBreath takes unit d, player p, integer l, real h, group g returns nothing
        
            local thistype this = create()
            
            set dummy[this] = d
            set owner[this] = p
            
            set level[this] = l
            
            set damage[this] = GetDamage(l)
            set height[this] = h
            set heightDec[this] = height[this] / (BREATH_LAND_TIME * 32)
            
            set damageGroup = CreateGroup()
            set ignoreGroup[this] = g
            
            static if DESTROY_TREE then
                set treeRect = Rect(0, 0, 0, 0)
            endif
        
        endmethod
    
    endstruct
    
    /***********************************************************************************
    *                                                                                  *
    *                               FROST WYRM MOVEMENT                                *
    *                                                                                  *                                               
    ***********************************************************************************/

    private struct FrostWyrm extends array
    
        private static unit array caster
        private static unit array wyrm
        
        private static player array owner
        
        private static real array angle
        private static real array fSpeed
        private static real array sSpeed
        private static real array fDistance
        private static real array sDistance
        private static real array cDistance
        private static real array wyrmHeight
        private static real array wyrmHeightDec
        
        private static integer array level
        private static integer array breathCD
        private static integer array breathCCD
        private static integer array breathStop
        
        private static boolean array wyrmBreath
        private static boolean array wyrmAnimation
        
        private static group array ignoreGroup
        
        implement CTL
        
            local unit d
            local real dx
            local real dy
            local real mx
            local real my
            local real crx
            local real cry
        
        implement CTLExpire
        
            set dx = GetUnitX(wyrm[this])
            set dy = GetUnitY(wyrm[this])
            
            if wyrmBreath[this] then
            
                set crx = dx + BREATH_CREATE_OFF * Cos(angle[this])
                set cry = dy + BREATH_CREATE_OFF * Sin(angle[this])
                
                if breathCCD[this] == breathCD[this] then
                
                    set d = CreateUnit(owner[this], BREATH_CODE, crx, cry, angle[this] * bj_RADTODEG)
                    
                    call SetUnitScale(d, BREATH_SCALE, 0, 0)
                    call SetUnitFlyHeight(d, wyrmHeight[this], 0)
                    
                    call FrostBreath.onBreath(d, owner[this], level[this], wyrmHeight[this], ignoreGroup[this])
                
                    set breathCCD[this] = breathCCD[this] - breathCD[this]
                    
                endif
                
                set breathCCD[this] = breathCCD[this] + 1
            
            endif
            
            if fDistance[this] > 0  then
            
                set mx = dx + fSpeed[this] * Cos(angle[this])
                set my = dy + fSpeed[this] * Sin(angle[this])
                
                if (mx < WorldBounds.maxX and mx > WorldBounds.minX and my < WorldBounds.maxY and my > WorldBounds.minY) then
                
                    call SetUnitX(wyrm[this], mx)
                    call SetUnitY(wyrm[this], my)
                    
                endif
                
                set cDistance[this] = cDistance[this] + fSpeed[this]
                set fDistance[this] = fDistance[this] - fSpeed[this]
            
                if cDistance[this] >= WYRM_ANIM_DISTANCE and wyrmAnimation[this] then
                
                    call SetUnitAnimation(wyrm[this], WYRM_ANIMATION)
                
                    set wyrmAnimation[this] = false
                
                endif
                
            else
            
                if sDistance[this] > 0 then
                
                    set mx = dx + sSpeed[this] * Cos(angle[this])
                    set my = dy + sSpeed[this] * Sin(angle[this])
                    
                    if (mx < WorldBounds.maxX and mx > WorldBounds.minX and my < WorldBounds.maxY and my > WorldBounds.minY) then
                    
                        call SetUnitX(wyrm[this], mx)
                        call SetUnitY(wyrm[this], my)
                        
                    endif
                    
                    set cDistance[this] = cDistance[this] + sSpeed[this]
                    set sDistance[this] = sDistance[this] - sSpeed[this]
                
                else
                
                    call SetUnitTimeScale(wyrm[this], 1)
                
                    call SetUnitAnimation(wyrm[this], "death")
                    call UnitApplyTimedLife(wyrm[this], 'BTLF', 1.00)
                    
                    call DestroyGroup(ignoreGroup[this])
                
                    set caster[this] = null
                    set wyrm[this] = null
                    
                    set ignoreGroup[this] = null
                    
                    call destroy()
                
                endif
            
            endif
            
            if wyrmHeight[this] > WYRM_HEIGHT then
            
                set wyrmHeight[this] = wyrmHeight[this] - wyrmHeightDec[this]
                
                call SetUnitFlyHeight(wyrm[this], wyrmHeight[this], 0)
            
            endif
            
            set breathStop[this] = breathStop[this] - 1
            
            if breathStop[this] == 0 then
            
                call SetUnitTimeScale(wyrm[this], 0)
            
            endif
            
            if cDistance[this] >= BREATH_START then
            
                set wyrmBreath[this] = true
            
            endif
            
            set d = null
        
        implement CTLNull
        implement CTLEnd
    
        private static method onCast takes nothing returns nothing
        
            local thistype this = create()
            
            local real cx
            local real cy
            local real tx
            local real ty
            local real crx
            local real cry
            
            set caster[this] = GetTriggerUnit()
            set owner[this] = GetTriggerPlayer()
            set level[this] = GetUnitAbilityLevel(caster[this], SPELL_CODE)
            
            set cx = GetUnitX(caster[this])
            set cy = GetUnitY(caster[this])
            set tx = GetSpellTargetX()
            set ty = GetSpellTargetY()
            
            set angle[this] = Atan2(ty - cy, tx - cx)
            
            set crx = cx - WYRM_CREATE_OFF * Cos(angle[this])
            set cry = cy - WYRM_CREATE_OFF * Sin(angle[this])
            
            set wyrm[this] = CreateUnit(owner[this], WYRM_CODE, crx, cry, angle[this] * bj_RADTODEG)
            
            call SetUnitScale(wyrm[this], WYRM_SCALE, 0, 0)
            call SetUnitFlyHeight(wyrm[this], WYRM_HEIGHT_START, 0)
            
            set fDistance[this] = WYRM_CREATE_OFF
            set sDistance[this] = GetDistance(level[this])
            
            set fSpeed[this] = MOVE_SPEED_FIRST * 0.031250
            set sSpeed[this] = MOVE_SPEED_SECOND * 0.031250
            
            set wyrmAnimation[this] = true
            
            set cDistance[this] = 0
            
            set breathCD[this] = BREATH_CD
            set breathCCD[this] = BREATH_CD
            
            set breathStop[this] = BREATH_ANIM_STOP
            
            set wyrmBreath[this] = false
            
            set wyrmHeight[this] = WYRM_HEIGHT_START
            set wyrmHeightDec[this] = (WYRM_HEIGHT_START - WYRM_HEIGHT) / (WYRM_HEIGHT_TIME * 32)
            
            set ignoreGroup[this] = CreateGroup()

        endmethod
        
        private static method onInit takes nothing returns nothing
            call RegisterSpellEffectEvent(SPELL_CODE,function thistype.onCast)
            
            set FREEZE_DUMMY = CreateUnit(Player(0), WYRM_CODE, 0, 0, 0)
            
            call ShowUnit(FREEZE_DUMMY, false)
            call UnitAddAbility(FREEZE_DUMMY, FREEZE_CODE)
        endmethod
    
    endstruct
endscope

Here is thread: http://www.hiveworkshop.com/forums/spells-569/frost-wyrm-v1-0-a-266896/

So IcemanBo said that i can't re-assign group. Why i need it? Becuase i need to units be damaged only ONCE during spell. if i will create group in onBreath it will damage units many times making damage impossible to calculate.

How to deal with it?
 
Static variables behave exactly the same as this:
JASS:
globals
    group ignoreGroup
endglobals
In your original spell, when you would call FrostBreath.onBreath(), you used a static ignoreGroup. But what happens if two units end up casting FrostBreath.onBreath()? One will overwrite the other during the spell's cast.

You'll want a group per instance (as you've done it in the main post) and then you'll add the units you've damaged to the ignore group, and check if the unit is in that group before dealing damage to it. In fact, it seems like you've already done something along those lines. You should be on the right track, I'm not sure why it would be damaging them twice.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Example:
JASS:
// Excact same behaviour:
//
// Btw in structs:
//      group hitUnits equals static group array hitUnits
//
    globals
        group enumGroup
        group array hitUnits
    endglobals
        
    struct MyStruct
    
        static group enumGroup = CreateGroup()
        group hitUnits// same as static group array hitUnits.
    
    endstruct

Now in spells, where you want to exclude already hit units:
JASS:
    struct MyStruct
        // both required
        static group enumGroup = CreateGroup()
        static group array hitUnits
    
//------------------------------------------------------------------
    
        // Fill enumGroup with units.
        // After the loop enumGroup will be empty again.
        call GroupEnumUnitsInRange(enumGroup, 0, 0, 0, null)
        loop
            set u == FirstOfGroup(enumGroup)
            exitwhen u == null
            call GroupRemoveUnit(enumGroup, u)
        
            // Already hit? Check inside group of struct instance "this"
            if not IsUnitInGroup(u, hitUnits[this]) then
                call GroupAddUnit(hitUnits[this], u)
            endif
        
        endloop
        
//-----------------------------------------------------------------
        
        // Once your spell is done, destroy hitUnits.
        call DestroyGroup(hitUnits[this]
        
        // onCast create hitUnits 
        set hitUnits[this] = CreateGroup()
    endstruct
 
Level 5
Joined
Sep 16, 2008
Messages
47
Ok but problem is bigger xD

If i will do that units will be damaged per every onBreath runs.

One dummy will land = damage enemy units, second will land = damage enemu units.

And i was going to do like that:

One dummy land = damage enemy units, second will land = can't be damaged because is in unit group.

It is making dmg possible to calculate and equal to every enemy unit.
 
Level 5
Joined
Sep 16, 2008
Messages
47
Let's say damage range is 100.
Dummy drops on the ground damaging all units in 100 range, second do the same, third too.
Movement speed is 20. So enemy units should be damaged 5 times (if i am right). But if unit will move it can be damaged 6, 7 or even 8 times making damage very high and not equal to every unit.
 
Status
Not open for further replies.
Top