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

Frost Wyrm v1.01

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Description:


Detah knight calls for the frost wyrm that takes deep breath freezing all enemy units for 3 seconds.​

Requires:

[self=https://github.com/nestharus/JASS/tree/master/jass/Systems/ConstantTimerLoop32]CTL[/self]​
[self=https://github.com/nestharus/JASS/tree/master/jass/Systemswor]World Bounds[/self]​
[self=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-isdestructabletree-248054/]IsDestructableTree[/self]​
[self=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/]SpellEffectEvent[/self]​
[self=http://www.hiveworkshop.com/forums/jass-resources-412/snippet-registerplayerunitevent-203338/]RegisterPlayerUnitEvent[/self]​


Code:

JASS:
scope FrostWyrm //version 1.1
/*---------------------------------------------------------------------------------------------------------
*
*       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.
*       v1.01
*       - Remade Tree_Kill function
*       - Fixed unit group leak
*       - No longer nulling players
*       - Changed dummies names
*       
*-----------------------------------------------------------------------------------------------------------*/

    /***********************************************************************************
    *                                                                                  *
    *                                 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 * 3.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
        
        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)
                call DestroyGroup(ignoreGroup[this])
                
                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 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] = CreateGroup()
            
            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
        
        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])
                
                    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)
                
                    set caster[this] = null
                    set wyrm[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)

        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


v1.0
- First release


Keywords:
Frost, Wyrm, breath, freeze, deathknight
Contents

Frost Wyrm v1.0 (Map)

Reviews
11:20, 24st Jun 2015 IcemanBo: Read reply in thread.
Implement native IsUnitAlive or also check for UnitTypeId == 0
in combination with your current check for unit type if a unit is dead.

You use the Tree Snippet by BPower, you can take more usage of it:
JASS:
private function Tree_Kill takes nothing returns boolean
    local destructable dummy = GetFilterDestructable()
    if IsDestructableTree(dummy) then
        call KillDestructable(dummy)
    endif
    set dummy = null
    return false
endfunction
->
JASS:
private function Tree_Kill takes nothing returns nothing
    call KillTree(GetFilterDestructable())
endfunction

For me the coding looks a bit weird, the use of static arrays in combination with structs like this.
Why not member of an instance?

static group ignoreGroup -> how does this work MUI? All catsers will use the same group.

After a FoG loop you don't need to null the local unit variable. It is already.
(btw players also don't need to be nulled necessarily, they are never removed/destroyed from game)

onBreath you always re-assign your static variables.
I don't quite understand it. You will leak on multiple assignment.
(onCast as well.)
 
Level 5
Joined
Sep 16, 2008
Messages
47
Ok, so. In: http://www.hiveworkshop.com/forums/spells-569/rain-cold-arrows-v1-04-a-266632/

BPower said that using just static unit groups, rects without arrays is enough.

Ok so now i know that i don't need to null player.

"onBreath you always re-assign your static variables.
I don't quite understand it. You will leak on multiple assignment.
( onCast as well.) "

What do you mean here? What should i do with it?

And about tree kill function:
JASS:
private function Tree_Kill takes nothing returns nothing
    call KillTree(GetFilterDestructable())
endfunction

I think i can't set it to
JASS:
returns nothing
it displays error because of

JASS:
call EnumDestructablesInRect(treeRect[this],function Tree_Kill,null)
 
If you need a group only for temp enumerations, then yes, one static/global group is enough.
If you need a group to keep track of "ignored" units for each instance, then you can't use one global group,
as you can't differ anymore which unit belongs to which instance.

Yes, I just wrote it by hand, you're right. The filter needs to return boolean.

What do you mean here? What should i do with it?
Well, you need to find a work around. If you overwrite an already assigned group variable, you will leak.
Try to find a solution, or also feel welcome to post in forum for questions. I hope you understand the issue with the re-assignment.
 
Hm, there still exists this issue with groups.

Your static enum group (that is only used for temp enumeration),
can be used over and over again. No sense in re-assigning and destroying it.

Other point can be mentioned here, too:
If you can't ensure the group is destroyed before you re-assign the group variable,
then you have a potential leak, as when you overwrite the old group with a
CreateGroup(), you will have a group leak!
I advice you to http://www.hiveworkshop.com/forums/trigger-gui-editor-tutorials-279/memory-leaks-263410/#post2661133 to get a general idea, if there are still something unclear.

call FrostBreath.onBreath
^This function is called potentialy more often, so the group[this] may be also overwritten,
before it was destroyed properly, and cause malfunctions.

Your treeRect may also get overwritten, as this will run multiple times:
JASS:
static if DESTROY_TREE then
    set treeRect = Rect(0, 0, 0, 0)
endif

if breathCCD[this] == breathCD[this] then
->
if breathCCD[this] >= breathCD[this] then

Your check for unit is dead still should be changed.

When working much with group creation/destruction, then it
might be a good idea to consiger to use GroupUtils to recycle groups.
 
Top