1. This site uses cookies. By continuing to use this site, you are agreeing to our use of cookies. Learn More.
  2. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  3. Music Contest #8 - Hive Soundtrack is up! Create the soundtrack for the upcoming videos of Hive Workshop's YouTube Channel.
    Dismiss Notice
  4. Check out the entries of the latest Mini-Mapping Contest: Stealth! Cast your vote for here!
    Dismiss Notice

Frostmyr v1.7

Submitted by Quilnez
This bundle is marked as approved. It works and satisfies the submission rules.
Intro
Hello. Have been a while since the last time I submitted a spell. This one is inspired by one of abilities in Digimon Rumble Arena on PlayStation 1. I forgot what's the name but it's one of Sakuyamon's ability. Where she summons and controls some swirling projectiles thingy.

This is the nastiest spell I have ever made so far, needs more than 3 days just to make the movement and the effect looks right. It's a bit weird tho to use "Frostmyr" for the name as it's my favorite nickname I always use in several online games I play. However, the name was actually designed for a relic weapon in my project, Guild Master. But meh, I will use it anyway for the spell name since I'm out of idea.

Basically, Frostmyr is a unique spell where the caster are able to control the movement of the spell (projectiles).
Description
The caster summons and controls 5 freezing projectiles which will periodically combusts his mana as cost. Every projectile deals damage periodically to nearby opponents. While controlling these projectiles, the caster will be unable to move and attack until it's ordered to stop. The spell will also stop once the caster's mana is depleted or it's dead.

Level 1 - Burns 20 mana points per second. Deals 80 damage per second.
Level 2 - Burns 30 mana points per second. Deals 160 damage per second.
Level 3 - Burns 40 mana points per second. Deals 240 damage per second.
To control the projectile you need to have the caster in your selection. Then simply right-click on the target location/unit. If you have a unit as target, the projectile will automatically follow it until it's dead.

Requirements
(Required)
• TimerUtils
• Stack
• WorldBounds
• UnitIndexer
• UnitZ
• RSound
(Optional)
• AutoFly
by Vexorian
by Nestharus
by Nestharus
by Nestharus
by Garfield1337
by Quilnez


by Nestharus
| wc3c.net/showthread.php?t=101322
| github.com/nestharus/JASS/tree/master/jass/Data%20Structures/Stack
| github.com/nestharus/JASS/tree/master/jass/Systems/WorldBounds
| hiveworkshop.com/forums/spells-569/unit-indexer-v5-3-0-1-a-260859/
| hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
| hiveworkshop.com/threads/snippet-rapidsound.258991/


| github.com/nestharus/JASS/tree/master/jass/Systems/AutoFly
How to Install
• Export all files in Import Manager to your map
• Copy dummy unit, main spell, and cancel spell to your map
• Copy Frostmyr and Scripts folders at Trigger Editor to your map
• Just delete duplicated external scripts
• Install all used scripts (libraries) properly
• Configure the spell properly (spell id, dummy id, etc.)
Code
Code (vJASS):
scope Frostmyr

// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//                          Frostmyr v1.7
//                          ¯¯¯¯¯¯¯¯¯¯¯¯¯
//                       Created by: Quilnez
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//      1. Description
//          The caster summons and controls 5 freezing projectiles which will  periodically
//          combusts his mana as cost. Every projectile deals damage periodically to nearby
//          opponents.  While controlling these projectiles, the caster will  be unable  to
//          move  and attack until it's ordered to stop. The spell will also stop once  the
//          caster's mana is depleted or it's dead.
//
//              Level 1 - Burns 20 mana points per second. Deals 80  damage per second.
//              Level 2 - Burns 30 mana points per second. Deals 160 damage per second.
//              Level 3 - Burns 40 mana points per second. Deals 240 damage per second.
//
//          To control the  projectile you need to have the caster in your selection.  Then
//          simply  right-click on the target location/unit. If you have a unit as  target,
//          the projectile will automatically follow it until it's dead.
//
//      2. External scripts
//          (Required)
//              • TimerUtils    by Vexorian     | wc3c.net/showthread.php?t=101322
//              • Stack         by Nestharus    | github.com/nestharus/JASS/tree/master/jass/Data%20Structures/Stack
//              • World Bounds  by Nestharus    | github.com/nestharus/JASS/tree/master/jass/Systems/WorldBounds
//              • Unit Indexer  by Nestharus    | hiveworkshop.com/forums/spells-569/unit-indexer-v5-3-0-1-a-260859/
//              • UnitZ         by Garfield1337 | hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
//              • RapidSound    by Quilnez      | hiveworkshop.com/threads/snippet-rapidsound.258991/
//          (Optional)
//              • AutoFly       by Nestharus    | github.com/nestharus/JASS/tree/master/jass/Systems/AutoFly
//
//      3. How to Install
//          • Export all files in Import Manager to your map
//          • Copy dummy unit, main spell, and cancel spell to your map
//          • Copy Frostmyr and Scripts folders at Trigger Editor to your map
//          • Just delete duplicated external scripts
//          • Install all used scripts (libraries) properly
//          • Configure the spell properly (spell id, dummy id, etc.)
//
//      4. Credits
//          • BTNIce_by67chrome.blp by 67chrome
//          • Mini Comet.mdx        by 00110000
//          • dummy.mdx             by Vexorian
//          • Cold Wind ambient     by Google.com
//
//      5. Link
//          Visit this link to check out the newest update of this spell:
//            hiveworkshop.com/forums/spells.php?id=ixhbcf
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//                          CONFIGURATION
//                          ¯¯¯¯¯¯¯¯¯¯¯¯¯
        globals
//
//          A. General
//
//          1. Main spell's raw code
            private constant integer SPELL_ID       = 'A000'
//
//          2. Cancel spell's raw code
            private constant integer CANCEL_ID      = 'A001'
//
//          3. Dummy unit's raw code
            private constant integer DUMMY_ID       = 'h000'
//
//          4. Order id used to give order to missiles
            private constant integer ORDER_ID       = 851971 //smart
//
//          5. Played animation for caster
            private constant string  ANIMATION      = "channel"
//
//          6. Created special effect whenever a unit takes damage from spell
            private constant string  DAMAGE_SFX     = "war3mapImported\\ForstmyrTarget.mdx"
            private constant string  DAMAGE_SFX_PT  = "chest"
//
//          7. Played sound effect whenever a unit takes damage from spell
            private constant string  DAMAGE_SOUND   = "war3mapImported\\FrostmyrDamage.wav"
            private constant integer DSOUND_VOLUME  = 127
//
//          8. Times given for special effects to decay
            private constant real    SFX_DECAY_TIME = 5.0
//
//          9. Sound effect for missiles
            private constant string  SOUND_PATH     = "war3mapImported\\FrostmyrAmbient.wav"
            private constant real    PITCH          = 0.5
            private constant integer VOLUME         = 65
//
//          10. Dealt damage configurations
            private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL
            private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_COLD
            private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
//
//          B. Missile
//
//          1. Missile's model filepath
            private constant string  MODEL_PATH     = "war3mapImported\\Mini Comet.mdx"
//
//          2. If true missiles will be able to enter idle state
//              - Makes it easier to control
//              - Increases damage efficiency
            private constant boolean ALLOW_IDLE     = true
//
//          3. Default fly height for missiles
            private constant real    Z_OFFSET       = 125.0
//
//          4. Missile trajectory configurations
            private constant real    LENGTH         = 100.0
            private constant real    WIDTH          = 100.0
            private constant real    HEIGHT         = 100.0
//
//          5. Movespeed for missiles
            private constant real    MOVE_RATE      = 13.0
//
//          6. Turn rate for missiles
            private constant real    TURN_RATE      = 3.0*bj_DEGTORAD
//
//          7. Initial offset for missiles
            private constant real    SPAWN_OFFSET   = 100.0
//
//          8. Rotate (spin) rate for missiles
//              - Use negative value to inverse the rotation
            private constant real    SPIN_RATE      = 5*bj_DEGTORAD
//
//          9. Speed factor when missiles deaccelerate
//              - Only takes effect if ALLOW_IDLE is true
//              - Must be between 0 ~ 1
//              - The higher the slower
            private constant real    SLOW_RATE      = 0.95
//
//          10. Maximum range between missile and target to deal damage
            private constant real    COLLISION_SIZE = 120.0
//
//          Better not to touch this
            private constant real    INTERVAL       = 0.03125
//
        endglobals
//
//          C. Non-constant
//
//          1. Created missile per cast
            private function MissileCount takes integer level returns integer
                return 5
            endfunction
//
//          2. Damage dealt by every single missile
            private function DamageAmount takes integer level returns real
                return 10.0 * level
            endfunction
//
//          3. Delay before next damage will be dealt
            private function DamageDelay takes integer level returns real
                return 0.125
            endfunction
//
//          4. Combusted mana amount every certain times
            private function ManaCost takes integer level returns real
                return 2.5 + 2.5 *level
            endfunction
//
//          5. Delay between mana reduction
            private function CostDelay takes integer level returns real
                return 0.25
            endfunction
//
//          6. Target classification that can be hit by this spell
            private function filterTarget takes player caster, unit target returns boolean
                return IsUnitEnemy(target, caster) and not(IsUnitType(target, UNIT_TYPE_STRUCTURE) or IsUnitType(target, UNIT_TYPE_MECHANICAL))
            endfunction
//
//          7. This function let you to set up what condition to stop the channel
//             The caster will stop channeling when this function returns true
            private function stopChannel takes unit caster returns boolean
                return GetUnitAbilityLevel(caster, 'BSTN') > 0 // Stop when the caster is stunned
            endfunction
//
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
//                          EVENT CATCHER
//                          ¯¯¯¯¯¯¯¯¯¯¯¯¯
        private module EventCatcher
//          Called whenever a unit takes damage from this spell
//              - caster : caster of the spell (damage source)
//              - target : damaged unit
//              - level  : caster's Frostmyr ability level
            static method onDamage takes unit caster, unit target, integer level returns nothing
            endmethod
        endmodule
//
//
//
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
    native UnitAlive takes unit id returns boolean
 
    private module InitModule
        private static method onInit takes nothing returns nothing
            call init()
        endmethod
    endmodule
 
    private struct FrostmyrMissile extends array
 
        effect sfx
        unit missile
     
        real hOffset
        real vOffset
     
        real hMax
        real vMax
     
        implement Stack
     
    endstruct
 
    private struct Frostmyr
     
        real locX
        real locY
        real targetX
        real targetY
        real missileX
        real missileY
     
        real angle
        real hOffset
        real vOffset
        real damage
        real damageDelay
        real damageDelayX
        real manaCost
        real costDelay
        real costDelayX
     
        real missileSpin
        real missileDistance
        real missileDistanceX
        real missileHPos
        real missileVPos
        real missileAngle
     
        boolean idle
        integer count
        integer level
     
        sound  sfx
        player owner
        unit   caster
        unit   target
        timer  tick
     
        FrostmyrMissile stack
     
        static RSound SoundFx
        static trigger OrderTrigg= CreateTrigger()
        static group TempGroup   = CreateGroup()
        static integer array Index
        static constant real TAU = bj_PI*2
        static constant real HP  = bj_PI/2
     
        implement optional EventCatcher
     
        method destroy takes nothing returns nothing
         
            local FrostmyrMissile node = .stack.first
         
            // Kill missiles
            loop
                exitwhen node == 0
                call UnitApplyTimedLife(node.missile, 'BTLF', SFX_DECAY_TIME)
                call DestroyEffect(node.sfx)
                set node.missile = null
                set node.sfx     = null
                set node         = node.next
            endloop
         
            call ReleaseTimer(.tick)
            call StopSound(.sfx, true, true)
            call .stack.destroy()
            call deallocate()
            // Remove leaks
            set .caster = null
            set .target = null
            set .tick   = null
            set .sfx    = null
         
        endmethod
     
        method stop takes nothing returns nothing
         
            // Reset animation if not dead
            if UnitAlive(.caster) then
                call SetUnitAnimation(.caster, "stand")
            endif
            set Index[GetUnitUserData(.caster)] = 0
            call UnitRemoveType(.caster, UNIT_TYPE_PEON)
            call UnitRemoveAbility(.caster, CANCEL_ID)
            call SetUnitAbilityLevel(.caster, SPELL_ID, .level)
            call destroy()
         
        endmethod
     
        static method onPeriodic takes nothing returns nothing
         
            local FrostmyrMissile node
            local thistype this = GetTimerData(GetExpiredTimer())
            local boolean  b
            local unit     u
         
            local real     a
            local real     a2
            local real     as
            local real     d
            local real     f
            local real     h
            local real     m
         
            local real     v
            local real     x
            local real     y
            local real     z
            local real     x2
            local real     y2
            local real     z2
            local real     sf
         
            // If have to stop channeling
            if not UnitAlive(.caster) or stopChannel(.caster) then
                call stop()
                return
            endif
         
            if .target != null then
                if UnitAlive(.target) then
                    set .targetX = GetUnitX(.target)
                    set .targetY = GetUnitY(.target)
                else
                    set .target  = null
                endif
            endif
         
            // Check if the projectiles need to move
            set b = not ALLOW_IDLE or (.targetX-.locX)*(.targetX-.locX)+(.targetY-.locY)*(.targetY-.locY) >= MOVE_RATE*MOVE_RATE
            if b then
                set a = Atan2(.targetY-.locY, .targetX-.locX)
                if TURN_RATE > 0 and Cos(.angle-a) < Cos(TURN_RATE) then
                    if Sin(a-.angle) >= 0 then
                        set .angle = .angle + TURN_RATE
                    else
                        set .angle = .angle - TURN_RATE
                    endif
                else
                    set .angle = a
                endif
                set .locX = .locX + MOVE_RATE * Cos(.angle)
                set .locY = .locY + MOVE_RATE * Sin(.angle)
                // Stop if the order point exceeds map boundaries
                if .locX >= WorldBounds.maxX or .locX <= WorldBounds.minX or .locY >= WorldBounds.maxY or .locY <= WorldBounds.minY then
                    call stop()
                    return
                endif
                call SetSoundPosition(.sfx, .locX, .locY, Z_OFFSET)
            endif
         
            set .damageDelay = .damageDelay - INTERVAL
            set .costDelay   = .costDelay - INTERVAL
            if .costDelay <= 0 then
                set m = GetUnitState(.caster, UNIT_STATE_MANA) - .manaCost
                call SetUnitState(.caster, UNIT_STATE_MANA, m)
                set .costDelay = .costDelayX
                if m <= 0 then
                    call stop()
                    return
                endif
            endif
         
            // If idle calculate deaccelerated speed
            if .idle then
                set sf = missileDistanceX/2
                set sf = 1-RAbsBJ(.missileDistance-sf)/sf*SLOW_RATE
            else
                set sf = 1
            endif
            set .missileDistance = .missileDistance + MOVE_RATE*sf
            if .missileDistance > .missileDistanceX then
                set .missileDistance = .missileDistanceX
            endif
         
            // Adjust control angle
            set a = Atan2(.locY-.missileY, .locX-.missileX)
            if TURN_RATE > 0 and Cos(.missileAngle-a) < Cos(TURN_RATE) then
                if Sin(a-.missileAngle) >= 0 then
                    set .missileAngle = .missileAngle + TURN_RATE
                else
                    set .missileAngle = .missileAngle - TURN_RATE
                endif
            else
                set .missileAngle = a
            endif
         
            // Update missiles
            set .missileSpin = .missileSpin + SPIN_RATE*.missileHPos
            set f    = (.missileDistanceX-.missileDistance)*(.missileDistance/.missileDistanceX)
            set node = .stack.first
            set as   = TAU/.count
            set a    = as /2
            loop
                exitwhen node == 0
             
                set node.hMax = WIDTH *Sin(a+.missileSpin)
                set node.vMax = HEIGHT*Cos(a+.missileSpin)
             
                set h  = (4*node.hMax/.missileDistanceX)*f
                set v  = (4*node.vMax/.missileDistanceX)*f
                set d  = SquareRoot(.missileDistance*.missileDistance+h*h)
                set a2 = .missileAngle-Atan(h/.missileDistance)
                set x  = .missileX + d * Cos(a2)
                set y  = .missileY + d * Sin(a2)
             
                if x < WorldBounds.maxX and x > WorldBounds.minX and y < WorldBounds.maxY and y > WorldBounds.minY then
                    call ShowUnit(node.missile, true)
                    call SetUnitX(node.missile, x)
                    call SetUnitY(node.missile, y)
                    call SetUnitFacing(node.missile, .missileAngle*bj_RADTODEG)
                    call SetUnitFlyHeight(node.missile, Z_OFFSET+v*.missileVPos, 0)
                 
                    set z  = GetUnitZ(node.missile)
                    if .damageDelay <= 0 then
                        call GroupEnumUnitsInRange(TempGroup, x, y, COLLISION_SIZE, null)
                        loop
                            set u = FirstOfGroup(TempGroup)
                            exitwhen u == null
                            call GroupRemoveUnit(TempGroup, u)
                            if UnitAlive(u) and filterTarget(.owner, u) then
                                set x2 = GetUnitX(u)
                                set y2 = GetUnitY(u)
                                set z2 = GetUnitZ(u)
                                set d  = SquareRoot((x-x2)*(x-x2)+(y-y2)*(y-y2))
                                set h  = RAbsBJ(z2-z)
                                // Spherical collision
                                if d*d+h*h < COLLISION_SIZE*COLLISION_SIZE then
                                    static if thistype.onDamage.exists then
                                        call onDamage(.caster, u, .level)
                                    endif
                                    call SoundFx.play(x2, y2, z2, DSOUND_VOLUME)
                                    call UnitDamageTarget(.caster, u, .damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                                    call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX, u, DAMAGE_SFX_PT))
                                endif
                            endif
                        endloop
                    endif
                else
                    // Temporary hide missile if exceeds map boundaries
                    call ShowUnit(node.missile, false)
                endif
             
                set a = a + as
                set node = node.next
            endloop
         
            if .damageDelay <= 0 then
                set .damageDelay = .damageDelayX
            endif
         
            if .missileDistance == .missileDistanceX then
                set .missileX        = x
                set .missileY        = y
                set .missileAngle    = Atan2(.locY-.missileY, .locX-.missileX)
                set .missileDistance = 0
                set .missileVPos     = -.missileVPos
                if b then
                    set .idle = false
                    set .missileHPos = -.missileHPos
                else
                    set .idle = true
                    set .missileHPos = 1
                endif
            endif
         
        endmethod
     
        static method orderStop takes nothing returns nothing
         
            local timer    t    = GetExpiredTimer()
            local thistype this = GetTimerData(t)
         
            call DisableTrigger(OrderTrigg)
            call IssueImmediateOrder(.caster, "stop")
            call SetUnitAnimation(.caster, ANIMATION)
            call EnableTrigger(OrderTrigg)
            call ReleaseTimer(t)
            set t = null
         
        endmethod
     
        static method onOrder takes nothing returns boolean
     
            local thistype this = Index[GetUnitUserData(GetTriggerUnit())]
            local integer  id
            local widget   w
            local unit     u
         
            // If unit is casting Frostmyr
            if this != 0 then
                set id = GetIssuedOrderId()
                if id == ORDER_ID then
                    set u = GetOrderTargetUnit()
                    if u == null then
                        set w = GetOrderTarget()
                        if w == null then
                            set .targetX = GetOrderPointX()
                            set .targetY = GetOrderPointY()
                        else
                            set .targetX = GetWidgetX(w)
                            set .targetY = GetWidgetY(w)
                        endif
                        set .target = null
                        set w = null
                    else
                        set .target = u
                        set u = null
                    endif
                    set .idle = false
                endif
                // This is done because seems like immediate (no target) order
                // is somehow unable to prevent attack move order immediately
                if id == 851983 then // If order is "attack"
                    call IssuePointOrder(.caster, "move", 0, 0)
                else
                    call TimerStart(NewTimerEx(this), 0, false, function thistype.orderStop)
                endif
            endif
         
            return false
        endmethod
     
        static method onCast takes nothing returns boolean
         
            local FrostmyrMissile node
            local thistype this
            local integer data = GetUnitUserData(GetTriggerUnit())
            local integer id   = GetSpellAbilityId()
            local integer i
            local integer h
            local integer v
            local real    a
            local real    as
         
            if id == SPELL_ID and Index[data] == 0 then
                set this         = allocate()
             
                set .caster      = GetTriggerUnit()
                set .owner       = GetTriggerPlayer()
                set .angle       = GetUnitFacing(.caster)*bj_DEGTORAD
                set .missileX    = GetUnitX(.caster) + SPAWN_OFFSET * Cos(.angle)
                set .missileY    = GetUnitY(.caster) + SPAWN_OFFSET * Sin(.angle)
                set .missileAngle= .angle
             
                set .locX        = .missileX + LENGTH * Cos(.angle)
                set .locY        = .missileY + LENGTH * Sin(.angle)
                set .targetX     = .locX
                set .targetY     = .locY
                set .idle        = ALLOW_IDLE
                set .hOffset     = 0
                set .vOffset     = 0
             
                set .level       = GetUnitAbilityLevel(.caster, SPELL_ID)
                set .damage      = DamageAmount(.level)
                set .damageDelayX= DamageDelay (.level)
                set .manaCost    = ManaCost    (.level)
                set .costDelayX  = CostDelay   (.level)
             
                set .count       = MissileCount(.level)
                set .stack       = FrostmyrMissile.create()
                set .damageDelay = .damageDelayX
                set .costDelay   = .costDelayX
                set as           = TAU/.count
                set a            = as/2
                set i            = .count
                set .missileSpin = 0
                loop
                    set i             = i - 1
                    set node          = .stack.push()
                    set node.missile  = CreateUnit(.owner, DUMMY_ID, .missileX, .missileY, .angle*bj_RADTODEG)
                    set node.sfx      = AddSpecialEffectTarget(MODEL_PATH, node.missile, "origin")
                    set node.hMax     = WIDTH *Sin(a)
                    set node.vMax     = HEIGHT*Cos(a)
                    static if not LIBRARY_AutoFly then
                        if UnitAddAbility(node.missile, 'Amrf') and UnitRemoveAbility(node.missile, 'Amrf') then
                        endif
                    endif
                    call PauseUnit(node.missile, true)
                    call SetUnitFlyHeight(node.missile, Z_OFFSET, 0)
                    set a             = a + as
                    exitwhen i        == 0
                endloop
             
                set .locX             = .targetX
                set .locY             = .targetY
                set .missileDistanceX = LENGTH*2
                set .missileDistance  = 0
                set .missileHPos      = 1
                set .missileVPos      = 1
                set .tick             = NewTimerEx(this)
                set .sfx              = CreateSound(SOUND_PATH, true, true, false, 10, 10, "")
             
                set Index[GetUnitUserData(.caster)] = this
                call SetSoundVolume(.sfx, VOLUME)
                call SetSoundPitch(.sfx, PITCH)
                call StartSound(.sfx)
                call UnitAddType(.caster, UNIT_TYPE_PEON)
                call SetUnitAnimation(.caster, ANIMATION)
                // Credits to Lambdadelta for this trick to hide main spell
                call SetUnitAbilityLevel(.caster, SPELL_ID, 999)
                call IncUnitAbilityLevel(.caster, SPELL_ID)
                call UnitAddAbility(.caster, CANCEL_ID)
                call TimerStart(.tick, INTERVAL, true, function thistype.onPeriodic)
            elseif id == CANCEL_ID and Index[data] != 0 then
                set this = Index[data]
                call stop()
            endif
         
            return false
        endmethod
     
        // Needed so that the sound effect will appear since the first cast
        static method preloadSound takes nothing returns nothing
         
            local sound s = CreateSound(SOUND_PATH, false, false, true, 12700, 12700, "")
         
            call StartSound(s)
            call StopSound(s, true, false)
            call ReleaseTimer(GetExpiredTimer())
            set s = null
         
        endmethod
     
        static method init takes nothing returns nothing
         
            local trigger t  = CreateTrigger()
            local integer i  = 0
            local player  p
         
            loop
                exitwhen i > 11
                set p = Player(i)
                call TriggerRegisterPlayerUnitEvent(t,  p, EVENT_PLAYER_UNIT_SPELL_ENDCAST, null)
                call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
                call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
                call TriggerRegisterPlayerUnitEvent(OrderTrigg, p, EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
                set i = i + 1
            endloop
         
            call TriggerAddCondition(t, Condition(function thistype.onCast))
            call TriggerAddCondition(OrderTrigg, Condition(function thistype.onOrder))
            call TimerStart(NewTimer(), 0, false,  function thistype.preloadSound)
         
            set SoundFx = RSound.create(DAMAGE_SOUND, true, true, 12700, 12700)
            set t = null
         
        endmethod
     
        implement InitModule
     
    endstruct
 
endscope

Credits
• BTNIce_by67chrome.blp
• Mini Comet.mdx
• dummy.mdx
• Cold Wind ambient

by 67chrome
by 00110000
by Vexorian
by Google.com
Changelog
(v1.7)
• Replaced ExSound with it's newer version, RapidSound, more robust


(v1.6)
• Fixed missile height bug without AutoFly


(v1.5)
• Fixed order glitches on "holdposition" and "attackmove" order


(v1.4)
• Fixed TimerUtils version
• Privatized demo functions
• Some reworks on idle state handling
• Order point speed and turn rate now sticks to missile's speed and turn rate to avoid faulty idle state
• Some adjustments on animation and mana cost
• Improved documentations


(v1.3)
• Added new configuration to allow user to decide when to stop channeling
• Improved documentations


(v1.2)
• Fixed non-unit widget target locking
• Some animation adjustments
• Fixed some documentation typos


(v1.1)
• Improved documentation
• Added custom on damage sound effect
• Balanced spell cooldown
• Fixed tooltips typo
• AutoFly is now optional

Keywords:
frostmyr
Contents

Frostmyr (Map)

Reviews
Moderator
22:55, 18th Jan 2016 BPower: The spell is very unique in concept and fun to cast. The configuration possibilities are really great. Excellent coding and presentation. I couldn't find anything wrong in the code. Rated with 5/5
  1. 22:55, 18th Jan 2016
    BPower:

    The spell is very unique in concept and fun to cast.

    The configuration possibilities are really great.
    Excellent coding and presentation.
    I couldn't find anything wrong in the code.

    Rated with 5/5
     
  2. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,470
    Resources:
    13
    Maps:
    3
    Spells:
    10
    Resources:
    13
    What a splendor! I have just a little critique off the bat, but I am just looking for ways to help.

    - Use a linked list or linear stack for looping all spell indices instead of TimerUtils. Or, since you use Nestharus' resources, Constant Timer Loop 32 is another golden option.

    - Pause dummy units. It cuts their resource usage tremendously.
     
  3. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,011
    Resources:
    30
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    20
    Resources:
    30
    I have never tried it yet before. And I use TimerUtils for another purpose like delayed order. I use TimerUtils for it instead of creating some sort of order queue, to simplify things. Then for CTL, I think it's no good to have multiple timer resources in a single spell.
    Will do in the next update.

    Thanks for your suggestions. :)
     
  4. Daffa the Mage

    Daffa the Mage

    Joined:
    Jan 30, 2013
    Messages:
    5,547
    Resources:
    11
    Maps:
    4
    Spells:
    7
    Resources:
    11
    This one... Is EPIC! :D
     
  5. PurgeandFire

    PurgeandFire

    Code Moderator

    Joined:
    Nov 11, 2006
    Messages:
    7,217
    Resources:
    5
    Icons:
    1
    Spells:
    4
    Resources:
    5
    I haven't looked at the code, but the spell effect itself is gorgeous. Great job on the presentation!
     
  6. BlackRangerXIII

    BlackRangerXIII

    Joined:
    Dec 25, 2010
    Messages:
    957
    Resources:
    0
    Resources:
    0
    That's beautiful
     
    Last edited: Dec 28, 2015
  7. Chaosy

    Chaosy

    Joined:
    Jun 9, 2011
    Messages:
    9,126
    Resources:
    8
    Spells:
    8
    Resources:
    8
    Fancy stuff. I wish I knew sin and cos properly so that I could do such as well.
     
  8. Kam

    Kam

    Patch Moderator

    Joined:
    Aug 3, 2004
    Messages:
    2,468
    Resources:
    19
    Models:
    7
    Icons:
    1
    Maps:
    11
    Resources:
    19
    Does that apply to any dummy unit?
     
  9. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,470
    Resources:
    13
    Maps:
    3
    Spells:
    10
    Resources:
    13
    If you're just using them as missiles, it's perfect.
     
  10. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,011
    Resources:
    30
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    20
    Resources:
    30
    I think it applies to any unit.
     
  11. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,470
    Resources:
    13
    Maps:
    3
    Spells:
    10
    Resources:
    13
    It applies to any unit who isn't using any orders, though it must be unpaused to give it a timed life.
     
  12. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,011
    Resources:
    30
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    20
    Resources:
    30
    Interesting. Maybe because timed life has/uses/is a buff, since buffs are paused as well when the unit is paused. I wonder tho how the expiration progress bar will look like when the unit is paused. Would it be stuck at zero or not moving at all?
     
    Last edited: Jan 6, 2016
  13. Bribe

    Bribe

    Joined:
    Sep 26, 2009
    Messages:
    7,470
    Resources:
    13
    Maps:
    3
    Spells:
    10
    Resources:
    13
    Pausing is intended to disrupt all of a unit's normal game engine traffic. Orders are ignored, cooldowns cease to keep track of time elapsed, health and mana regeneration are halted, so yes a paused unit will be less taxing on the game engine because the engine isn't doing anything with it.

    Full credit for the PauseUnit trick goes to Nestharus: http://www.hiveworkshop.com/forums/2122621-post27.html
     
  14. IcemanBo

    IcemanBo

    Code Moderator

    Joined:
    Sep 6, 2013
    Messages:
    5,142
    Resources:
    14
    Maps:
    2
    Spells:
    11
    Template:
    1
    Resources:
    14
    Is it currently bugged? Seems not to work.
     
  15. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,011
    Resources:
    30
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    20
    Resources:
    30
    Have just re-downloaded the map and all work fine to me. :/
     
  16. IcemanBo

    IcemanBo

    Code Moderator

    Joined:
    Sep 6, 2013
    Messages:
    5,142
    Resources:
    14
    Maps:
    2
    Spells:
    11
    Template:
    1
    Resources:
    14
    Have you changed something? Anyways, it seems to work now.^

    Edit: I got the issue. With Vexorian's JassHelper it compiles, though only works properly with Cohaders.

    Instead of using 0-timeout timer , it could be used this trick to stop the caster:

    Code (vJASS):

    call DisableTrigger(trigger)
    call PauseUnit(unit, true)
    call IssueImmediateOrder(unit, "stop")
    call PauseUnit(unit, false)
    call EnableTrigger(trigger)
     

    The currently used method works fine, too, but the method above might be more instant.

    It would be really cool if the Frostmyr attack would have a freezing/slowing effect on targets.
    You know where targets gets blue and are slowed slightly. It would fit to the "Frost" theme pretty well.

    Code (vJASS):

    // 5. Movespeed for missiles
           private constant real    MOVE_RATE      = 30.0


    // 6. Missile's order point move rate
            private function MoveSpeed takes integer level returns real
                return 15.
            endfunction
     

    For first could be added that it's per tick.
    The second afterwerds might be a bit confusing. Could you maybe re-phrase it?

    What do you think about adding a distance to caster check a for potential end of the spell?
    I'm not sure if it was a useful feature for some case, or it would be not needed. Idea?
     
    Last edited: Jan 9, 2016
  17. Quilnez

    Quilnez

    Joined:
    Oct 12, 2011
    Messages:
    3,011
    Resources:
    30
    Icons:
    2
    Tools:
    1
    Maps:
    7
    Spells:
    20
    Resources:
    30
    It compiles both with vex's and coh's jass helper. But I always use vex's and never use cohadar's.

    Interesting. But why disable trigger tho? What trigger to disable?

    I think it would be kinda overpowered. And in the original idea, it doesn't have slowing effect. That's why I added onDamage event so the user may attach any effect they like.

    I don't really understand this part.

    Good idea ;] But it better has some sort of range indicator.
     
  18. IcemanBo

    IcemanBo

    Code Moderator

    Joined:
    Sep 6, 2013
    Messages:
    5,142
    Resources:
    14
    Maps:
    2
    Spells:
    11
    Template:
    1
    Resources:
    14
    The trigger that was fired to run the function.

    Was asking to explain this part a bit better:
    // 6. Missile's order point move rate
     
    Last edited: Jan 10, 2016
  19. IcemanBo

    IcemanBo

    Code Moderator

    Joined:
    Sep 6, 2013
    Messages:
    5,142
    Resources:
    14
    Maps:
    2
    Spells:
    11
    Template:
    1
    Resources:
    14
    Any news? Besides I still think some slower/freezing feature would be cool. :D
    But ya..