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

Gummy Leash v2.5

Description

Summons a mythical orb upon the target point. Nearby enemy units will be captured by an elastical energy stream which will keep them from moving further away from the orb.​

Preview
235220-albums7646-picture90790.gif

External Dependencies
- IsTerrainWalkable by Anitarf and Vexorian
- CTL by Nestharus
- AutoFly by Nestharus
- WorldBounds by Nestharus
- UnitIndexer by Nestharus
- GetClosestWidget by Bannar
- SpellEffectEvent by Bribe
- UnitZ by Garfield1337
Code
Consists of 2 triggers:
JASS:
library GummyLink uses CTL, WorldBounds, UnitZ, IsTerrainWalkable optional AutoFly
    /*
                        Gummy Link v2.5
        
        Descriptions:
            A library used to create magical link between
            two units which will drag the target toward the
            source elastically.
           
         !! Source of any link should not be able to move !!
       
        External Dependencies:
            (Required)
            - CTL
            - UnitZ
            - WorldBounds
            - UnitIndexer
            - IsTerrainWalkable
           
            (Optional)
            - AutoFly
           
        How to use:
            struct GummyLink
                1. Create connection between two units
                static method connect takes unit s, unit t, real z, real power, real elastic, string mdl, boolean is3D returns thistype
                    s       => source
                    t       => target
                    z       => lightning z offset
                    power   => drag power
                    elastic => link elasticity
                    mdl     => lightning model string
                    is3D      => simulate 3D effect
               
                2. Remove a connection
                method remove takes nothing returns nothing
           
        Credits:
            - IsTerrainWalkable by Anitarf and Vexorian
            - CTL               by Nestharus
            - AutoFly           by Nestharus
            - WorldBounds       by Nestharus
            - UnitIndexer       by Nestharus
            - GetClosestWidget  by Bannar
            - SpellEffectEvent  by Bribe
            - UnitZ             by Garfield1337
            - OrbDragonX.mdx    by Frankster
            - BTNManaBreathe    by D.ee
            - LaserBlue.blp     by Erkki2
           
        Link:
            hiveworkshop.com/forums/spells-569/gummy-leash-v1-0-a-257717/
    */
                        // CONFIGURATIONS
    globals
        // Unit flying height reset rate
        private constant real RESET_RATE = 15.0
       
        // Add KB effect after unit's link destroy
        private constant boolean ENABLE_KB = true
    endglobals
   
    // Just skip these KB datas if ENABLE_KB is false
    private module KBData
        // Friction rate when units flung on the ground
        static constant real FRICTION_FACTOR_GROUND = 0.04
       
        // Friction rate when units flung in the air
        static constant real FRICTION_FACTOR_AIR = 0.5
       
        // General destructable heights
        // For better idea, this is minimum height
        // before pathability checking is performed
        static constant real DEST_HEIGHT = 100.0
    endmodule
   
    // =============================================================================
       
    // Check whether given coords is in map bound or not
    private constant function inBound takes real x, real y returns boolean
        return x < WorldBounds.maxX and y < WorldBounds.maxY and x > WorldBounds.minX and y > WorldBounds.minY
    endfunction
   
    private module KBModule
   
        unit target
        real speed
        real rate
        real cos
        real sin
       
        implement KBData
   
        implement CTL
            local real f
            local real x
            local real y
           
        implement CTLExpire
            if .speed > .rate then
                set x = GetUnitX(.target) + .speed * .cos
                set y = GetUnitY(.target) + .speed * .sin
                set f = GetUnitFlyHeight(.target)
                if f > DEST_HEIGHT then
                    // Ignore any pathability issue except world bounds
                    if inBound(x, y) then
                        call SetUnitX(.target, x)
                        call SetUnitY(.target, y)
                    endif
                else
                    if IsTerrainWalkable(x, y) then
                        call SetUnitX(.target, x)
                        call SetUnitY(.target, y)
                    else
                        call destroy()
                        set .target = null
                    endif
                endif
                if f > .01 then
                    set .speed = .speed - .rate * FRICTION_FACTOR_AIR
                else
                    set .speed = .speed - .rate
                endif
            else
                call destroy()
                set .target = null
            endif
        implement CTLEnd
       
        static method knock takes unit u, real a, real s returns nothing
       
            local thistype this = create()
           
            set .target = u
            set .speed  = s
            set .cos    = Cos(a)
            set .sin    = Sin(a)
            set .rate   = s * FRICTION_FACTOR_GROUND
           
        endmethod
       
    endmodule
   
    // Add simple KB effect to a unit
    private struct SimpleKB extends array
        static if ENABLE_KB then
            implement KBModule
        endif
    endstruct
   
    // Struct used to modify and save unit's fly height
    // since the default one doesn't fit my needs
    private struct UnitZ extends array
   
        unit unit
        real rate
        real height
       
        static real array Height
        static thistype array Index
       
        implement CTL
            local integer dex
           
        implement CTLExpire
            set dex = GetUnitUserData(.unit)
            if Height[dex] > .height then
                set Height[dex] = Height[dex] - .rate
                if Height[dex] < .height then
                    set Height[dex] = .height
                endif
                call SetUnitFlyHeight(.unit, Height[dex], 0)
            elseif Height[dex] < .height then
                set Height[dex] = Height[dex] + .rate
                if Height[dex] > .height then
                    set Height[dex] = .height
                endif
                call SetUnitFlyHeight(.unit, Height[dex], 0)
            else
                set Index[dex] = 0
                call destroy()
                set .unit = null
            endif
        implement CTLEnd
       
        static method get takes unit u returns real
            return Height[GetUnitUserData(u)]
        endmethod
       
        static method apply takes unit u, real h, real r returns nothing
           
            local thistype this
            local integer  dex  = GetUnitUserData(u)
           
            // If instant rate
            if r < 1 then
                set Height[dex] = h
                call SetUnitFlyHeight(u, Height[dex], 0)
                if Index[dex] != 0 then
                    set Index[dex].height = h
                endif
            else
                if Index[dex] == 0 then
                    set this        = create()
                    set .unit       = u
                    set .height     = h
                    set .rate       = r
                    set Index[dex]  = this
                    set Height[dex] = GetUnitFlyHeight(u)
                else
                    set Index[dex].height = h
                    set Index[dex].rate = r
                endif
            endif
           
        endmethod
       
        static method onIndex takes nothing returns boolean
            set Height[GetIndexedUnitId()] = GetUnitFlyHeight(GetIndexedUnit())
            return false
        endmethod
       
        static method onInit takes nothing returns nothing
            call RegisterUnitIndexEvent(Condition(function thistype.onIndex), UnitIndexer.INDEX)
        endmethod
       
    endstruct
   
    struct GummyLink extends array
       
        readonly unit source
        readonly unit target
       
        private real z
        private real pow
        private real elstc
       
        private boolean   b
        private boolean   is3D
        private lightning link
       
        private static real    array sX
        private static real    array sY
        private static real    array tX
        private static real    array tY
        private static real    array HVel
        private static real    array VVel
        private static real    array Height
        private static boolean array Bool
       
        private static constant real TAU = bj_PI*2
        private static constant real HP  = bj_PI/2
        private static constant real M   = 0.2
       
        // Gets circular differences between two radians
        private static method circDifference takes real a, real b returns real
       
            local real r = RAbsBJ(a-b)
           
            if r * 2 <= TAU then
                return r
            else
                return TAU - r
            endif
           
        endmethod
       
        implement CTL
            local real    a
            local real    v
            local real    z
            local integer sDex
            local integer tDex
           
        implement CTLExpire
            set sDex = GetUnitUserData(.source)
            set tDex = GetUnitUserData(.target)
            // Automatically destroy link if unit has been removed
            if .b and GetUnitTypeId(.source) != 0 and GetUnitTypeId(.target) != 0 then
                set a = Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex])
                // Calculate horizontal velocity
                if HVel[tDex] >= 0 then
                    set HVel[tDex] = HVel[tDex] - .elstc
                else
                    set HVel[tDex] = HVel[tDex] - .pow
                endif
               
                set tX[tDex] = tX[tDex] + HVel[tDex] * Cos(a)
                set tY[tDex] = tY[tDex] + HVel[tDex] * Sin(a)
                if circDifference(a, Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex])) > HP then
                    set HVel[tDex] = HVel[tDex] *  -1
                endif
               
                set z  = GetUnitZ(.source) + .z
                // If 3D simulation is enabled
                if .is3D then
                    // Calculate vertical velocity
                    if Height[tDex] < z then
                        set VVel[tDex] = VVel[tDex] + .pow*M
                    else
                        set VVel[tDex] = VVel[tDex] - (.elstc+elstc*(1-M))
                    endif
                   
                    set Height[tDex] = Height[tDex] + VVel[tDex]
                    if Height[tDex] < 0 then
                        set Height[tDex] = 0
                        if VVel[tDex] < HVel[tDex] then
                            set VVel[tDex] = 0
                        endif
                    endif
                    call UnitZ.apply(.target, Height[tDex], 0)
                endif
               
                if inBound(tX[tDex], tY[tDex]) then
                    call SetUnitX(.target, tX[tDex])
                    call SetUnitY(.target, tY[tDex])
                    // Update the lightning
                    call MoveLightningEx(.link, false, sX[sDex], sY[sDex], z, tX[tDex], tY[tDex], GetUnitZ(.target) + .z)
                endif
            else
                call SetUnitPathing(.source, true)
                call SetUnitPathing(.target, true)
                static if ENABLE_KB then
                    // Determine KB direction
                    if HVel[tDex] < 0 then
                        call SimpleKB.knock(.target, Atan2(sY[sDex] - tY[tDex], sX[sDex] - tX[tDex]), RAbsBJ(HVel[tDex]))
                    else
                        call SimpleKB.knock(.target, Atan2(tY[tDex] - sY[sDex], tX[tDex] - sX[sDex]), HVel[tDex])
                    endif
                endif
               
                // Reset fly height
                if .is3D then
                    call UnitZ.apply(.target, GetUnitDefaultFlyHeight(.target), RESET_RATE)
                endif
               
                call DestroyLightning(.link)
                set Bool[tDex] = false
                set HVel[tDex] = 0
                set VVel[tDex] = -RESET_RATE
               
                call destroy()
                set .source = null
                set .target = null
                set .link   = null
            endif
        implement CTLEnd
       
        method remove takes nothing returns nothing
            set .b = false
        endmethod
       
        static method connect takes unit s, unit t, real z, real power, real elastic, string mdl, boolean is3D returns thistype
           
            local thistype this = 0
            local integer  dex
           
            if not Bool[GetUnitUserData(t)] then
                set this      = create()
               
                set .z        = z
                set .source   = s
                set .target   = t
                set .is3D     = is3D
               
                set .pow      = power
                set .elstc    = elastic
                set .b        = true
                set .link     = AddLightningEx(mdl, false, GetUnitX(s), GetUnitY(s), GetUnitZ(s) + z, GetUnitX(t), GetUnitY(t), GetUnitZ(t) + z)
               
                set dex       = GetUnitUserData(s)
                set sX[dex]   = GetUnitX(s)
                set sY[dex]   = GetUnitY(s)
                set Bool[dex] = true
               
                set dex       = GetUnitUserData(t)
                set tX[dex]   = GetUnitX(t)
                set tY[dex]   = GetUnitY(t)
                set Bool[dex] = true
               
                call SetUnitPathing(s, false)
                call SetUnitPathing(t, false)
                if is3D then
                    set Height[dex]  = UnitZ.get(t)
                    static if not LIBRARY_AutoFly then
                        if UnitAddAbility(t, 'Amrf') and UnitRemoveAbility(t, 'Amrf') then
                        endif
                    endif
                endif
            debug else
                debug call BJDebugMsg("Connecting failed: target is already connected to other instance.")
            endif
           
            return this
        endmethod
       
    endstruct
   
endlibrary
JASS:
scope GummyLeash
    /*
                        Gummy Leash v2.5
                       
        Descriptions:
            Summons a mythical orb upon the target point.
            Nearby enemy units will be captured by an
            elastical energy stream which will keep them
            from moving further away from the orb.
       
        Requires:
            - GummyLink
           
        External Dependencies:
            (Required)
            - CTL
            - UnitIndexer
            - WorldBounds
           
            (Optional)
            - GetClosestWidget
            - SpellEffectEvent
           
        How to import:
            - Install and configure all dependencies properly
            - Copy GummyLeash trigger group into your map
            - Import all necessary stuffs (OE & import data) to your map
            - Configure the spell
           
        Credits:
            - IsTerrainWalkable by Anitarf and Vexorian
            - CTL               by Nestharus
            - AutoFly           by Nestharus
            - WorldBounds       by Nestharus
            - UnitIndexer       by Nestharus
            - GetClosestWidget  by Bannar
            - SpellEffectEvent  by Bribe
            - UnitZ             by Garfield1337
            - OrbDragonX.mdx    by Frankster
            - BTNManaBreathe    by D.ee
            - LaserBlue.blp     by Erkki2
           
        Link:
            hiveworkshop.com/forums/spells-569/gummy-leash-v1-0-a-257717/
    */
                        // CONFIGURATIONS
                       
    // Native declaration
    native UnitAlive takes unit id returns boolean
   
    globals
   
        // Summoned unit's rawcode
        private constant integer    WARD_ID             = 'h000'
       
        // Main ability's rawcode
        private constant integer    MAIN_ID             = 'A000'
       
        // Adjust z offset of the lightning
        // As example, if you use a floating model for the ward
        // then you can adjust the lightning's z offset here
        private constant real       Z_OFFSET            = 75.0
       
        // Lightning effect string
        private constant string     LIGHTNING_EFFECT    = "BLUE"
       
        // Allow ward to detect for new targets during lifespan
        private constant boolean    ALLOW_NEW_TARGET    = true
       
        // Simulate 3D effect for GummyLink
        private constant boolean    ENABLE_3D           = true
       
        // Add timed life bar to the ward
        private constant boolean    ADD_TIMED_LIFE      = true
        private constant integer    TIMED_LIFE          = 'BTLF'
       
        // Maximum possible target
        // Set this to target count at max ability level
        private constant integer    MAXIMUM_TARGET      = 5
       
        // Attached sfx on victims
        private constant string     TARGET_SFX          = "war3mapImported\\GummyLeash.mdx"
        private constant string     TARGET_SFX_PT       = "chest"
       
        // Add buff effect (stun) to targets
        private constant boolean    ADD_BUFF            = true
       
        // Damage configuration
        private constant attacktype ATTACK_TYPE         = ATTACK_TYPE_NORMAL
        private constant damagetype DAMAGE_TYPE         = DAMAGE_TYPE_NORMAL
        private constant weapontype WEAPON_TYPE         = WEAPON_TYPE_WHOKNOWS
       
    endglobals
   
    // Just skip these buff datas if ADD_BUFF is false
    private module BuffData
   
        // Dummy unit's rawcode
        static  constant integer    DUMMY_ID            = 'h001'
       
        // Buff ability rawcode
        static  constant integer    SPELL_ID            = 'A001'
       
        // Buff rawcode
        static  constant integer    BUFF_ID             = 'B000'
       
        // Stun order id
        static  constant integer    BUFF_ORDER          = 852127 // stomp
       
        // The higher the safer but just leave it
        static  constant real       BUFF_SAFETY         = 64.0
       
    endmodule
   
    // Maximum targets per ward
    private constant function count takes integer level returns integer
        return 2 + level
    endfunction
   
    // Ward's lifespan
    private constant function duration takes integer level returns real
        return 3.0 + 2.0 * level
    endfunction
   
    // Apply damage interval
    private constant function delay takes integer level returns real
        return 0.0
    endfunction
   
    // Dealt damage amount
    private constant function damage takes integer level returns real
        return 0.0
    endfunction
   
    // Detect AoE
    private constant function aoe takes integer level returns real
        return 600.0
    endfunction
   
    // Drag power of the link (when shrinking)
    private constant function power takes integer level returns real
        return 3.2
    endfunction
   
    // Drag power of the link (when stretching)
    private constant function elasticity takes integer level returns real
        return 3.2
    endfunction
   
    // Configure desired targets
    // By default, it's unable to targets structures and magic immune
    private function targets takes unit u, player p returns boolean
        return UnitAlive(u) and IsUnitEnemy(u, p) and not IsUnitType(u, UNIT_TYPE_MECHANICAL)
    endfunction
   
    // This function is called whenever a unit takes damage from this spell
    private function onDamage takes unit caster, unit target returns nothing
    endfunction
   
    // This function is called whenever a unit is released by this spell
    private function onRelease takes unit caster, unit target returns nothing
    endfunction
   
    // =============================================================================

    private struct LeashBuff extends array
       
        unit    target
        boolean dispose
        effect  sfx
       
        static unit  Caster
        static group Group = CreateGroup()
        static boolean  array Bool
        static thistype array Index
       
        static if ADD_BUFF then
            implement BuffData
        endif
       
        method remove takes nothing returns nothing
            set .dispose = true
        endmethod
       
        implement CTLExpire
            if .dispose or not UnitAlive(.target) then
                set Bool[GetUnitUserData(.target)] = false
                static if ADD_BUFF then
                    call UnitRemoveAbility(.target, BUFF_ID)
                endif
                call DestroyEffect(.sfx)
                call destroy()
                set .sfx = null
                set .target = null
            endif
        implement CTLEnd
       
        static method apply takes unit target returns thistype
       
            local thistype this = 0
            local integer  dex  = GetUnitUserData(target)
            local real     x
            local real     y
           
            if Bool[dex] then
                set this = Index[dex]
            else
                if not(IsUnitType(target, UNIT_TYPE_STRUCTURE)) then
                    set this = create()
                   
                    set .target = target
                    set .dispose = false
                    set .sfx = AddSpecialEffectTarget(TARGET_SFX, .target, TARGET_SFX_PT)
                    set Bool[dex]  = true
                    set Index[dex] = this
                   
                    static if ADD_BUFF then
                        set x = GetUnitX(target)
                        set y = GetUnitY(target)
                        // If unit don't have the buff yet
                        if GetUnitAbilityLevel(target, BUFF_ID) == 0 then
                            call SetUnitX(Caster, x)
                            call SetUnitY(Caster, y)
                            call IssueImmediateOrderById(Caster, BUFF_ORDER)
                            call SetUnitPosition(Caster, WorldBounds.maxX, WorldBounds.maxY)
                        endif
                        // Remove unwanted buff
                        call GroupEnumUnitsInRange(Group, x, y, BUFF_SAFETY, null)
                        loop
                            set target = FirstOfGroup(Group)
                            exitwhen target == null
                            call GroupRemoveUnit(Group, target)
                            if GetUnitAbilityLevel(target, BUFF_ID) > 0 and not Bool[GetUnitUserData(target)] then
                                call UnitRemoveAbility(target, BUFF_ID)
                            endif
                        endloop
                    endif
                debug else
                    debug call BJDebugMsg("Error occured: failed to attach buff")
                endif
            endif
           
            return this
        endmethod
       
        private static method onInit takes nothing returns nothing
            static if ADD_BUFF then
                set Caster = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE), DUMMY_ID, WorldBounds.maxX, WorldBounds.maxY, 0)
                call UnitAddAbility(Caster, SPELL_ID)
            endif
        endmethod
       
    endstruct
   
    // Container for targets per instance
    private struct LeashTargets
        unit      array target[MAXIMUM_TARGET]
        GummyLink array link[MAXIMUM_TARGET]
        LeashBuff array buff[MAXIMUM_TARGET]
    endstruct

    private struct GummyLeash extends array
       
        real x
        real y
        real dur
       
        real aoe
        real pow
        real elstc
       
        real dly
        real dlyx
        real dmg
       
        integer ct
        integer ctx
       
        player p
        unit caster
        unit ward
       
        LeashTargets t
       
        static group    Group = CreateGroup()
        static player   TempPlayer
        static thistype TempDex
        static boolean array Bool
       
        // Target filtration, made to be compatible for GetClosestUnit
        static method filter takes nothing returns boolean
            return targets(GetFilterUnit(), TempPlayer)
        endmethod
       
        implement CTL
            local unit    u
            local integer i
            local integer dex
           
        implement CTLExpire
            if .dur > 0.03125 and UnitAlive(.ward) then
                set .dur = .dur - 0.03125
                set .dly = .dly - 0.03215
                set i = 0
                loop
                    exitwhen i >= .ct
                    if UnitAlive(.t.target[i]) then
                        if .dly <= 0 and .dmg > 0 then
                            call onDamage(.caster, .t.target[i])
                            call UnitDamageTarget(.caster, .t.target[i], .dmg, true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
                        endif
                    else
                        call onRelease(.caster, .t.target[i])
                        set Bool[GetUnitUserData(.t.target[i])] = false
                        call .t.link[i].remove()
                        // Deindex
                        set .ct = .ct - 1
                        set .t.target[i] = .t.target[.ct]
                        set .t.link[i] = .t.link[.ct]
                        set .t.buff[i] = .t.buff[.ct]
                        set .t.target[.ct] = null
                        set i = i - 1
                    endif
                    set i = i + 1
                endloop
                if .dly <= 0 then
                    set .dly = .dlyx
                endif
           
                static if ALLOW_NEW_TARGET then
                    if .ct < .ctx then
                        set TempPlayer = .p
                        call GroupEnumUnitsInRange(Group, .x, .y, .aoe, function thistype.filter)
                        loop
                            exitwhen .ct >= .ctx
                            static if LIBRARY_GetClosestWidget then
                                set u = GetClosestUnitInGroup(.x, .y, Group)
                            else
                                set u = FirstOfGroup(Group)
                            endif
                            exitwhen u == null
                            call GroupRemoveUnit(Group, u)
                           
                            set dex = GetUnitUserData(u)
                            if not Bool[dex] then
                                set .t.buff[.ct] = LeashBuff.apply(u)
                                // If successfully buffed
                                if .t.buff[.ct] != 0 then
                                    set .t.link[.ct] = GummyLink.connect(.ward, u, Z_OFFSET, .pow, .elstc, LIGHTNING_EFFECT, ENABLE_3D)
                                    // If successfully connected
                                    if .t.link[.ct] != 0 then
                                        set Bool[dex] = true
                                        set .t.target[.ct] = u
                                        set .ct = .ct + 1
                                    endif
                                endif
                            endif
                        endloop
                        set u = null
                    endif
                endif
            else
                set i = 0
                loop
                    exitwhen i >= .ct
                    call onRelease(.caster, .t.target[i])
                    set Bool[GetUnitUserData(.t.target[i])] = false
                    call .t.link[i].remove()
                    call .t.buff[i].remove()
                    set .t.target[i] = null
                    set i = i + 1
                endloop
                call KillUnit(.ward)
                call .t.destroy()
                call destroy()
                set .ward = null
                set .caster = null
            endif
           
        implement CTLEnd
       
        static method onCast takes nothing returns boolean
       
            local thistype this = create()
            local integer  level
            local integer  dex
            local unit     fog
           
            set .caster = GetTriggerUnit()
            set .p      = GetTriggerPlayer()
            set .ward   = CreateUnit(.p, WARD_ID, GetSpellTargetX(), GetSpellTargetY(), bj_UNIT_FACING)
            set .x      = GetUnitX(.ward)
            set .y      = GetUnitY(.ward)
            set .ct     = 0
           
            set level   = GetUnitAbilityLevel(.caster, MAIN_ID)
            set .aoe    = aoe(level)
            set .dur    = duration(level)
            set .elstc  = elasticity(level)
            set .pow    = power(level)
            set .ctx    = count(level)
            set .dlyx   = delay(level)
            set .dmg    = damage(level)
            set .dly    = .dlyx
           
            set .t      = LeashTargets.create()
            static if ADD_TIMED_LIFE then
                call UnitApplyTimedLife(.ward, TIMED_LIFE, .dur)
            endif
           
            set TempPlayer = .p
            call GroupEnumUnitsInRange(Group, .x, .y, .aoe, function thistype.filter)
            loop
                exitwhen .ct >= .ctx
                static if LIBRARY_GetClosestWidget then
                    set fog = GetClosestUnitInGroup(.x, .y, Group)
                else
                    set fog = FirstOfGroup(Group)
                endif
                exitwhen fog == null
                call GroupRemoveUnit(Group, fog)
               
                set dex = GetUnitUserData(fog)
                if not Bool[dex] then
                    set .t.buff[.ct] = LeashBuff.apply(fog)
                    // If successfully buffed
                    if .t.buff[.ct] != 0 then
                        set .t.link[.ct] = GummyLink.connect(.ward, fog, Z_OFFSET, .pow, .elstc, LIGHTNING_EFFECT, ENABLE_3D)
                        // If successfully connected
                        if .t.link[.ct] != 0 then
                            set Bool[dex] = true
                            set .t.target[.ct] = fog
                            set .ct = .ct + 1
                        endif
                    endif
                endif
            endloop
            set fog = null
           
            return false
        endmethod
           
        static if not LIBRARY_SpellEffectEvent then
            private static method checkId takes nothing returns boolean
                if GetSpellAbilityId() == MAIN_ID then
                    call thistype.onCast()
                endif
                return false
            endmethod
        endif
       
        static method onInit takes nothing returns nothing
            static if LIBRARY_SpellEffectEvent then
                call RegisterSpellEffectEvent(MAIN_ID, function thistype.onCast)
            else
                set gg_trg_GummyLeash = CreateTrigger()
                call TriggerRegisterAnyUnitEventBJ(gg_trg_GummyLeash, EVENT_PLAYER_UNIT_SPELL_EFFECT)
                call TriggerAddAction(gg_trg_GummyLeash, Condition(function thistype.checkId))
            endif
        endmethod
       
    endstruct
   
endscope
Credits
- IsTerrainWalkable by Anitarf and Vexorian
- CTL by Nestharus
- WorldBounds by Nestharus
- AutoFly by Nestharus
- UnitIndexer by Nestharus
- GetClosestWidget by Bannar
- SpellEffectEvent by Bribe
- UnitZ by Garfield1337
- OrbDragonX.mdx by Frankster
- BTNManaBreathe by D.ee
- LaserBlue.blp by Erkki2
Author Message
Please, report any bug/glitch/error on this thread! Thank you.

Latest update changelog (v2.5):
- Now the spell works with spell immune units
- Some variable names improvements
I don't see any gum here.
4/5

:O
Contents

Gummy Leash (Map)

Reviews
14:18, 10th Mar 2015 IcemanBo: Very nice and good looking spell. http://www.hiveworkshop.com/forums/spells-569/gummy-leash-v2-2-a-257717/index2.html#post2662797
PI isn't mathematically wrong. It's a lame stunt for some people trying to get famous, we've already had "TAU" even before they came up with this un-original idea of re-naming 2*Pi*R... I just don't see the point of adding more names to try to complicate it some more. Over-complicating stuff will only raise a barrier between people and make it even worse. Unless of course I am missing something.

Edit: Pi the also the only equation to never repeat itself after each number, Tau however does. I would rather the simple formula we have now over extra effort and in this case extra kb. . . Unless this way is faster for wc3 to process it.
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Tau is better, but less popular. + 2x more pie on Tau day, whats not to like?

On topic:
The spell looks really cool, but the orb is much higher than the point where the leashes connects. I didnt encounter any bugs.

Ops, fixed. Thanks :)

And there are some adjustment on gravity factor.
 
Level 19
Joined
Mar 18, 2012
Messages
1,716
Delete "local unit f" in static method onCast.

You may have a local unit leak here, if more than the maximum valid amount (.ctx) of units are enumerated.

Like I already mentioned above, null instance arrays like ward, s (in LeashBuff), ... once you don't need them anymore.

Is it really required to check if the buff has been applied to a wrong unit?

What does NORMAL_Z do, what should I expect when increasing/decreasing this value?
Make the description more userfriendly

Function names could follow the JPAG standard --> function MyFunction takes ...
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Delete "local unit f" in static method onCast
Ok
You may have a local unit leak here, if more than the maximum valid amount (.ctx) of units are enumerated
Ok
Like I already mentioned above, null instance arrays like ward, s (in LeashBuff), ... once you don't need them anymore.
I've asked about this to you multiple times yet you haven't answered. Since I don't understand how default struct destroy() method works, I'm affraid I will misnulled things. So the question is: am I need to null after/before I call destroy() method?
Is it really required to check if the buff has been applied to a wrong unit?
Yes
What does NORMAL_Z do, what should I expect when increasing/decreasing this value?
Make the description more userfriendly
It allows user to adjust lightning's normal z, so it doesnt looks like hitting units' feet.
Function names could follow the JPAG standard --> function MyFunction takes ...
I think camelCase is better for private.

Thanks..
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
I've asked about this to you multiple times yet you haven't answered. Since I don't understand how default struct destroy() method works, I'm affraid I will misnulled things. So the question is: am I need to null after/before I call destroy() method?
Doesn't make any difference. You can do it before or afterwards. Destroy is a simple recycling process, it does not influence the current struct instance you are dealing with.

In your case destroy looks like the following: ns is next stack, t is the current instance.
JASS:
            set ns[t]=ns[0]
            set ns[0]=t
 
Spell works very nice and it's visuals also look very smooth. The code is well structured and written efficient.

if r * 2 <= TAU then
->
if r <= bj_PI
:D

I don't want be nitpicky but some names could be better.
For example boolean array Bool is just not descriptive at all.

Personaly I would prefer having as less imports as possible. You could still recommend the imports in your spell description.

Additionaly you could think of adding a damage feature, like over time, or also if spell finished.
It would not take too much of changes I think.

Else I like the concept much. Great spell!

Approved.
 
Great spell, and extremely good visuals.
If I may recommend, use in-game models for less imports :)

One of few I could say extremely well done, it deserves 5/5 ^^

I'll get a view on the code later, but still, this is very rare and well done, Quilnez ^^

EDIT :
I don't think Unit Indexer has to be Nes'
Bribe's can do the job well (judging by GummyLink's code)

EDIT 2 :
I just noticed that the image doesn't use your special cover in it, why? It looks cool with the cover (personal taste)

Code :
Pretty much great, can't spot errors there, except some variables aren't descriptive, that's all ^^
 
Last edited:

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Wow this spell is nice and I like, I'll use this on my map.

Hey, thanks for using. Just post here if you need any help.

Great spell, and extremely good visuals.
If I may recommend, use in-game models for less imports :)

One of few I could say extremely well done, it deserves 5/5 ^^

I'll get a view on the code later, but still, this is very rare and well done, Quilnez ^^
Thanks, daffa. I believe 180KB is not a problem for anyone.

I don't think Unit Indexer has to be Nes'
Bribe's can do the job well (judging by GummyLink's code)
The code is in vJass so it's much better to use Nestharus'.

I just noticed that the image doesn't use your special cover in it, why? It looks cool with the cover (personal taste)
Hmm. What image? Which cover?
 
Should I add somekind of afterward effects (like dizziness or stun)? I think trapping enemies for 9 seconds is too destructive already in a map like dota (AoS). But just let me know if you want it, and maybe DPS?

Adding such would be nice with it's configuration of course, the more configurable it is, the better. That's my view though :)
 
Level 4
Joined
Dec 3, 2014
Messages
64
Cool Spell i like it, but i have trouble to change it into a "fire-like" looking spell. Would be cool if you would add a version with parts of Airbind(or how is it called in english) from the human-dragenhawkrider.
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
Latest changes can be found in the main post.
I'm planning to do more updates to this spell. With my refined knowledge I probably can make a smoother and more realistic gravitational physic.

I love this spell but it does not account for deep water and will leave units there.
Added to update list
 

Deleted member 247165

D

Deleted member 247165

This spell looks extraordinary and I love the concept! 5/5 from me!
 

Kazeon

Hosted Project: EC
Level 33
Joined
Oct 12, 2011
Messages
3,449
I don't remember how exactly to import this, but I will elaborate the instructions a bit:
- Install and configure all dependencies properly
-> All dependencies all inclued in "Ext. Dependency" trigger folder
-> What needs special attention is installing Unit Indexer. It requires you to copy UnitIndex ability in object editor. Then you have to configure the indexer based on that ability raw code in your map. It also requires Event and WorldBounds so you also have to import those (included already).​
- Copy GummyLeash trigger group into your map
-> Clear enough​
- Import all necessary stuffs (OE & import data) to your map
-> Import all objects from object editor, including units, abilities, and buffs, to your map.
-> Export all custom files to your map, including the lightning.slk file. Make sure to change the path accordingly.​
- Configure the spell
-> Configure the spell configuration.
-> Main configurations:
JASS:
        // Summoned unit's rawcode
        private constant integer   WARD_ID             = 'h000'
=> Orb's raw code
JASS:
        // Main ability's rawcode
        private constant integer   MAIN_ID             = 'A000'
=> Main ability's (Gummy Leash) raw code

-> Sub configurations (inside "BuffData" module):
JASS:
        // Dummy unit's rawcode
        static  constant integer   DUMMY_ID            = 'h001'
The dummy unit acts as stunner. I forgot what's its name but make sure you copy it to your map.
JASS:
        // Buff ability rawcode
        static  constant integer   SPELL_ID            = 'A001'
Iirc it's named "Gummy Leash (Pause)" or something. Just copy it to your map as well.
JASS:
        // Buff rawcode
        static  constant integer   BUFF_ID             = 'B000'
The stun buff. Also copy it to your map.
Btw you can press ctrl+D to see object's raw code in the object editor.
 
Top