Spell Falcon Punch

Dinowc

don't expect anything, prepare for everything
Reaction score
223
yeah ofc the idea isn't mine :p
I've made this spell since HeX.16 requested it
and I thought I could share it here

Falcon Punch

icons_5938_btn.jpg

Charges towards the target knocking any enemy unit along the way. Upon reaching the target, Falcon Assassin deals AoE damage to enemy units and burns them for 7 seconds. Target is then knocked back in the air, dealing additional AoE damage on impact.

Primary Damage: 120 + 50 per lvl
Secondary Damage: 50 + 25 per lvl
Burn Damage: 10 + 5 per lvl
Range: 800 + 50 per lvl
Primary Aoe: 250 + 25 per lvl
Secondary AoE: 200 + 25 per lvl


strig1.jpg


strig2.jpg


JASS:
scope punch initializer init // requires Timer Utils, AIDS, Knockback System (Kenny's), Group Utils

// CONFIGURABLE:
globals
    private constant integer SPELL_ID = 'A000'
    private constant integer PHOENIX_DUMMY = 'h000'
    private constant integer STORM_CROW = 'Arav'
    
    private constant real INTERVAL = 0.03125
    private constant real MAX_SPEED = 1300.
    private constant real START_SPEED = 100.
    private constant real ACCELERATION = 800. // speed increase after 1 second
    private constant real FLY_SPEED = 900.
    private constant boolean INVULNERABLE = false // unit is invulnerable while sliding
    private constant real MINIMUM_CAST_RANGE = 0.
    private constant boolean COUNTER_PUNCH_DISABLED = false // setting this to true will cause the faster sliding unit to punch the target if both are sliding and targeting each other
    
    private constant boolean CHECK_PATHING = true // for going over cliffs
    private constant boolean DESTROYS_TREES = true // if it's set to false, it will stop charging if he hits a tree (or any other destructible)
    private constant real TREE_CUT_AOE = 100.
    
    private constant string CAST_SFX = "Abilities\\Spells\\NightElf\\BattleRoar\\RoarCaster.mdl"
    private constant string STRIKE_SFX = "Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl"
    private constant string STRIKE_SFX_ATTACH = "chest"
    private constant string IMPACT_SFX = "Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
    
    private constant real BASE_PRIMARY_DAMAGE = 120.
    private constant real PRIMARY_DAMAGE_PER_LVL = 50.
    private constant real BASE_SECONDARY_DAMAGE = 50.
    private constant real SECONDARY_DAMAGE_PER_LVL = 25.
    
    private constant real BASE_PRIMARY_AOE = 250.
    private constant real PRIMARY_AOE_PER_LVL = 25.
    private constant real BASE_SECONDARY_AOE = 200.
    private constant real SECONDARY_AOE_PER_LVL = 25.
    
    private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC
    private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
    
    private constant real BASE_BURN_DAMAGE = 10.
    private constant real BURN_DAMAGE_PER_LVL = 5.
    private constant real BURN_INTERVAL = 0.50
    private constant real BASE_BURN_DURATION = 7.
    private constant real BURN_DURATION_PER_LVL = 0.
    private constant string BURN_SFX = "Environment\\LargeBuildingFire\\LargeBuildingFire2.mdl"
    private constant string BURN_SFX_ATTACH = "chest"
    
    private constant attacktype BURN_ATTACK_TYPE = ATTACK_TYPE_MAGIC
    private constant damagetype BURN_DAMAGE_TYPE = DAMAGE_TYPE_FIRE
    
    private constant real MAX_HEIGHT = 200.
    private constant real FLY_DISTANCE = 700.
    private constant real FLY_RATE = 800.
    private constant real SLIDE_DISTANCE = 300. // distance of the additional sliding once unit falls to the ground
    private constant real SLIDE_DURATION = 1.
    private constant boolean FLY_OVER_CLIFFS = true
    private constant boolean FLIER_INVULNERABLE = false // unit is invulnerable while flying
    
    private constant real KNOCK_SPEED = 750. // speed at which are units being knocked; this is not the speed of knocked units, but of the sliding caster
    private constant real KNOCK_DISTANCE = 250.
    private constant real KNOCK_DURATION = 0.6
    private constant real KNOCK_AOE = 100. // AoE at which are nearby enemies knocked back
    private constant real KNOCK_OFFSET = 50. // distance between the center of enumeration rect and the caster
    
    private constant real PHOENIX_OFFSET = -10. // distance between the caster and the phoenix dummy
    private constant integer PHOENIX_TRANSPARENCY = 75
    private constant real PHOENIX_ANIMATION_SPEED = 1.5
    private constant real IMPACT_RANGE = 125. // distance between the caster and the target before the target is hit
    private constant integer ANIMATION_ID = 5
    private constant real PAUSE_ANIMATION = 0.25 // pauses animation after this many seconds
endglobals

// NOT SO MUCH CONFIGURABLE:
globals
    private group G = CreateGroup()
    private unit Caster
    private unit Target
    private player Owner
    private integer Lvl
    private rect R
    private real Slide_Speed = MAX_SPEED * INTERVAL
    private real Fly_Speed = FLY_SPEED * INTERVAL
    private real X
    private real Y
    private real Acc = ACCELERATION * INTERVAL * INTERVAL
    private real MaxX
    private real MaxY
    private real MinX
    private real MinY
    private real Damage
    private integer Index
    private group Sliders = CreateGroup()
endglobals

private function PolarX takes real dist, real angle returns real
    return dist * Cos(angle * bj_DEGTORAD)
endfunction

private function PolarY takes real dist, real angle returns real
    return dist * Sin(angle * bj_DEGTORAD)
endfunction

private function GetDistance takes real x1, real y1, real x2, real y2 returns real
    local real dx = x2 - x1
    local real dy = y2 - y1
    return SquareRoot(dx * dx + dy * dy)
endfunction

private function GetAngle takes real x1, real y1, real x2, real y2 returns real
    return bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
endfunction

private function GetParabolaZ takes real x returns real
    return 4 * MAX_HEIGHT * x * (FLY_DISTANCE - x) / (FLY_DISTANCE * FLY_DISTANCE)
endfunction

private function PrimaryDamage takes integer lvl returns real
    return BASE_PRIMARY_DAMAGE + (lvl - 1) * PRIMARY_DAMAGE_PER_LVL
endfunction

private function SecondaryDamage takes integer lvl returns real
    return BASE_SECONDARY_DAMAGE + (lvl - 1) * SECONDARY_DAMAGE_PER_LVL
endfunction

private function BurnDamage takes integer lvl returns real
    return BASE_BURN_DAMAGE + (lvl - 1) * BURN_DAMAGE_PER_LVL
endfunction

private function PrimaryAoe takes integer lvl returns real
    return BASE_PRIMARY_AOE + (lvl - 1) * PRIMARY_AOE_PER_LVL
endfunction

private function SecondaryAoe takes integer lvl returns real
    return BASE_SECONDARY_AOE + (lvl - 1) * SECONDARY_AOE_PER_LVL
endfunction

private function BurnDuration takes integer lvl returns real
    return BASE_BURN_DURATION + (lvl - 1) * BURN_DURATION_PER_LVL
endfunction

private function SafeX takes real x returns real
    if x > MaxX then
        return MaxX
    endif
    
    if x < MinX then
        return MinX
    endif
    
    return x
endfunction

private function SafeY takes real y returns real
    if y > MaxY then
        return MaxY
    endif
    
    if y < MinY then
        return MinY
    endif
    
    return y
endfunction

private struct dot
    unit caster
    group g
    real dmg
    timer t
    real ticks
    real duration
    
    private method onDestroy takes nothing returns nothing
        call ReleaseTimer(this.t)
        call ReleaseGroup(this.g)
    endmethod
endstruct

private struct slide
    unit caster
    unit target
    unit phoenix
    real dmg1
    real dmg2
    real ticks
    real dist
    real cos
    real sin
    real acc
    real angle
    boolean paused
    boolean sliding
    timer t
    
    static method create takes unit caster, unit target returns thistype
        local thistype d = thistype.allocate()
        local real angle = GetUnitFacing(caster)
        local integer lvl = GetUnitAbilityLevel(caster, SPELL_ID)
        
        set d.caster = caster
        set d.target = target
        set d.phoenix = CreateUnit(GetOwningPlayer(d.caster), PHOENIX_DUMMY, GetUnitX(d.caster) + PolarX(PHOENIX_OFFSET, angle), GetUnitY(d.caster) + PolarY(PHOENIX_OFFSET, angle), angle)
        call SetUnitVertexColor(d.phoenix, 255, 255, 255, PHOENIX_TRANSPARENCY)
        call SetUnitTimeScale(d.phoenix, PHOENIX_ANIMATION_SPEED)
        set d.t = NewTimer()
        set d.dmg1 = PrimaryDamage(lvl)
        set d.dmg2 = SecondaryDamage(lvl)
        set d.ticks = 0.
        set d.dist = 0.
        set d.acc = START_SPEED * INTERVAL
        set d.paused = false
        set d.sliding = true
        call GroupAddUnit(Sliders, caster)
        
        return d
    endmethod
    
    private method onDestroy takes nothing returns nothing
        call ReleaseTimer(this.t)
        set this.caster = null
        set this.target = null
        set this.phoenix = null
    endmethod
endstruct

globals
    private slide array D
endglobals

private function PrimaryStrike takes nothing returns boolean
    local unit picked = GetFilterUnit()
    if IsUnitEnemy(picked, Owner) and GetWidgetLife(picked) > 0. then
        call UnitDamageTarget(Caster, picked, Damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
        set picked = null
        return true
    endif
    set picked = null
    return false
endfunction

private function SecondaryStrike takes nothing returns boolean
    local unit picked = GetFilterUnit()
    if IsUnitEnemy(picked, Owner) and GetWidgetLife(picked) > 0. then
        call UnitDamageTarget(Caster, picked, Damage, false, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
    endif
    set picked = null
    return false
endfunction

private function KnockUnits takes nothing returns boolean
    local unit picked = GetFilterUnit()
    if IsUnitEnemy(picked, Owner) and GetWidgetLife(picked) > 0. and picked != Target and KBS_IsUnitSliding(picked) == false then
        call KBS_BeginCommon(picked, KNOCK_DISTANCE, KNOCK_DURATION, GetAngle(X, Y, GetUnitX(picked), GetUnitY(picked)))
    endif
    set picked = null
    return false
endfunction

private function EndSlide takes nothing returns nothing
    call PauseUnit(D[Index].caster, false)
    call SetUnitPathing(D[Index].caster, true)
    call SetUnitTimeScale(D[Index].caster, 1.)
    call SetUnitAnimation(D[Index].caster, "stand")
    call KillUnit(D[Index].phoenix)
    call SetUnitInvulnerable(D[Index].target, false)
    call SetUnitInvulnerable(D[Index].caster, false)
    call GroupRemoveUnit(Sliders, D[Index].caster)
    call PauseTimer(D[Index].t)
    call D[Index].destroy()
endfunction

private function DestructibleFilter takes nothing returns nothing
    local destructable d = GetEnumDestructable()
    static if DESTROYS_TREES then
        call KillDestructable(d)
    else
        call EndSlide()
    endif
    set d = null
endfunction

private function DoT takes nothing returns nothing
    local unit picked = GetEnumUnit()
    if GetWidgetLife(picked) > 0. then
        call UnitDamageTarget(Caster, picked, Damage, false, false, BURN_ATTACK_TYPE, BURN_DAMAGE_TYPE, WEAPON_TYPE_WHOKNOWS)
        call DestroyEffect(AddSpecialEffectTarget(BURN_SFX, picked, BURN_SFX_ATTACH))
    endif
    set picked = null
endfunction

private function DamageOverTime takes nothing returns nothing
    local dot d = GetTimerData(GetExpiredTimer())
    set Caster = d.caster
    set Damage = d.dmg
    call ForGroup(d.g, function DoT)
    set d.ticks = d.ticks + BURN_INTERVAL
    if d.ticks > d.duration then
        call PauseTimer(d.t)
        call d.destroy()
    endif
endfunction

private function callback takes nothing returns nothing
    local slide d = GetTimerData(GetExpiredTimer())
    local real x1 = GetUnitX(d.caster)
    local real y1 = GetUnitY(d.caster)
    local real x2 = GetUnitX(d.target)
    local real y2 = GetUnitY(d.target)
    local real angle = GetAngle(x1, y1, x2, y2)
    local real cos
    local real sin
    local real dist
    local dot a
    local real speed
    local boolean b = true
    
    set Owner = GetOwningPlayer(d.caster)
    set Index = GetUnitIndex(d.caster)

    if d.sliding then
    
    if d.acc < Slide_Speed then
        set d.acc = d.acc + Acc
    else
        set d.acc = Slide_Speed
    endif
    
    set cos = PolarX(d.acc, angle)
    set sin = PolarY(d.acc, angle)
    set dist = GetDistance(x1, y1, x2, y2)
    call SetUnitX(d.caster, x1 + cos)
    call SetUnitY(d.caster, y1 + sin)
    call SetUnitX(d.phoenix, GetUnitX(d.phoenix) + cos)
    call SetUnitY(d.phoenix, GetUnitY(d.phoenix) + sin)
    
    set X = x1
    set Y = y1
    set cos = cos + PolarX(KNOCK_OFFSET, angle)
    set sin = sin + PolarY(KNOCK_OFFSET, angle)
    call MoveRectTo(R, X, Y)
    
    static if CHECK_PATHING then
        if IsTerrainPathable(x1 + cos, y1 + sin, PATHING_TYPE_WALKABILITY) == true then
            call EndSlide()
        endif
    endif
    
    call EnumDestructablesInRect(R, null, function DestructibleFilter)
    
    set Target = d.target
    
    if d.acc / INTERVAL > KNOCK_SPEED then
        call GroupEnumUnitsInRange(G, x1 + cos, y1 + sin, KNOCK_AOE, Filter(function KnockUnits))
    endif
    
    call SetUnitFacingTimed(d.caster, angle, 0.01)
    call SetUnitFacingTimed(d.phoenix, angle, 0.01)
    
    if dist <= IMPACT_RANGE + (Slide_Speed * 2.5) then
        call SetUnitTimeScale(d.caster, 0.8)
    endif
    
    if GetWidgetLife(d.target) <= 0. and IsUnitInGroup(d.caster, Sliders) == true then
        call EndSlide()
    endif
    
    if dist <= IMPACT_RANGE then
        
        set Index = GetUnitIndex(d.target)
        static if COUNTER_PUNCH_DISABLED then
            if D[Index].target == d.caster and D[Index].acc > d.acc then
                set b = false
            endif
        endif
        
        if b == true then
            call DestroyEffect(AddSpecialEffectTarget(STRIKE_SFX, d.target, STRIKE_SFX_ATTACH))
            set Caster = d.caster
            set Lvl = GetUnitAbilityLevel(d.caster, SPELL_ID)
        
            set a = dot.create()
            set a.caster = d.caster
            set a.g = NewGroup()
            set a.t = NewTimer()
            set a.ticks = 0.
            set a.dmg = BurnDamage(Lvl) * BURN_INTERVAL
            set a.duration = BurnDuration(Lvl)
            set Damage = d.dmg1
        
            call GroupEnumUnitsInRange(a.g, x2, y2, PrimaryAoe(Lvl), Filter(function PrimaryStrike))
            call SetTimerData(a.t, a)
            call TimerStart(a.t, BURN_INTERVAL, true, function DamageOverTime)
        
            call SetUnitTimeScale(d.caster, 1.)
            
            if COUNTER_PUNCH_DISABLED or D[Index].target != d.caster then
            
            if IsUnitInGroup(d.target, Sliders) then
                call EndSlide()
            endif
            
            endif
            
            call KillUnit(d.phoenix)
            call PauseUnit(d.caster, false)
            call PauseUnit(d.target, true)
            call SetUnitAnimation(D[Index].caster, "stand")
            call IssueImmediateOrder(d.caster, "holdposition")
        
            static if FLIER_INVULNERABLE then
                call SetUnitInvulnerable(d.target, true)
            endif
            
            call UnitAddAbility(d.target, STORM_CROW)
            call UnitRemoveAbility(d.target, STORM_CROW)
            call SetUnitPathing(d.target, false)
            call SetUnitPathing(d.caster, true)
            
            call GroupRemoveUnit(Sliders, d.caster)
            set d.angle = angle
            set d.sliding = false
        endif
    endif
    
    set d.ticks = d.ticks + INTERVAL
    if d.ticks > PAUSE_ANIMATION and d.paused == false then
        call SetUnitTimeScale(d.caster, 0.)
        set d.paused = true
    endif
    
    else
        set d.cos = PolarX(Fly_Speed, d.angle)
        set d.sin = PolarY(Fly_Speed, d.angle)
        if d.dist <= FLY_DISTANCE then
            set X = x2 + d.cos
            set Y = y2 + d.sin
            if FLY_OVER_CLIFFS then
                call SetUnitX(d.target, SafeX(X))
                call SetUnitY(d.target, SafeY(Y))
            elseif IsTerrainPathable(X, Y, PATHING_TYPE_WALKABILITY) == false then
                call SetUnitX(d.target, SafeX(X))
                call SetUnitY(d.target, SafeY(Y))
            endif
            call SetUnitFlyHeight(d.target, GetParabolaZ(d.dist), FLY_RATE)
        elseif KBS_IsUnitSliding(d.target) == false and d.dist < SLIDE_DISTANCE + FLY_DISTANCE then
            call SetUnitFlyHeight(d.target, 0., FLY_RATE)
            call SetUnitInvulnerable(d.target, false)
            call SetUnitInvulnerable(d.caster, false)
            call DestroyEffect(AddSpecialEffectTarget(IMPACT_SFX, d.target, "origin"))
            call SetUnitPathing(d.target, true)
            call KBS_BeginCommon(d.target, SLIDE_DISTANCE, SLIDE_DURATION, d.angle)
            set Lvl = GetUnitAbilityLevel(d.caster, SPELL_ID)
            set Damage = d.dmg2
            call GroupEnumUnitsInRange(G, x2 + d.cos, y2 + d.sin, SecondaryAoe(Lvl), Filter(function SecondaryStrike))
        else
            call PauseUnit(d.target, false)
            call PauseTimer(d.t)
            call d.destroy()
        endif
        set d.dist = d.dist + Fly_Speed
    endif
    
endfunction

private function actions takes nothing returns boolean
    local unit caster
    local unit target
    local slide d
    local real angle
    if GetSpellAbilityId() == SPELL_ID then
        set caster = GetTriggerUnit()
        set target = GetSpellTargetUnit()
        set d = slide.create(caster, target)
        call IssueImmediateOrder(caster, "stop")
        call PauseUnit(caster, true)
        static if INVULNERABLE then
            call SetUnitInvulnerable(caster, true)
        endif
        call SetUnitPathing(caster, false)
        call SetUnitAnimationByIndex(caster, ANIMATION_ID)
        set D[GetUnitIndex(caster)] = d
        call SetTimerData(d.t, d)
        call TimerStart(d.t, INTERVAL, true, function callback)
    endif
    set caster = null
    set target = null
    return false
endfunction

private function actions2 takes nothing returns boolean
    local unit caster
    local real x
    local real y
    if GetSpellAbilityId() == SPELL_ID then
        set caster = GetTriggerUnit()
        set x = GetUnitX(caster)
        set y = GetUnitY(caster)
        if GetDistance(x, y, GetSpellTargetX(), GetSpellTargetY()) >= MINIMUM_CAST_RANGE then
            call DestroyEffect(AddSpecialEffect(CAST_SFX, GetUnitX(caster), GetUnitY(caster)))
        else
            call IssueImmediateOrder(caster, "stop")
        endif
    endif
    set caster = null
    return false
endfunction

private function init takes nothing returns nothing
    local trigger t = CreateTrigger()
    
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function actions))
    
    set t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST)
    call TriggerAddCondition(t, Condition(function actions2))
    
    set R = Rect(-TREE_CUT_AOE, -TREE_CUT_AOE, TREE_CUT_AOE, TREE_CUT_AOE)
    
    set MaxX = GetRectMaxX(bj_mapInitialPlayableArea)
    set MaxY = GetRectMaxY(bj_mapInitialPlayableArea)
    set MinX = GetRectMinX(bj_mapInitialPlayableArea)
    set MinY = GetRectMinY(bj_mapInitialPlayableArea)
endfunction

endscope


any feedback appreciated

fixed some bugs and added counter punching :D
 

Attachments

  • Falcon Punch v1.3.w3x
    105 KB · Views: 710

Dinowc

don't expect anything, prepare for everything
Reaction score
223
Fun spell but the charging is quite slow... :/
you mean the casting time is too long? (when he jumps and growls)
I did that on purpose, but it can be faster by lowering the cast time/backswing thingies

if you mean that he accelerates too slow, there's a configuration for that (you can even set the initial speed, so you don't need to accelerate at all)

EDIT: reuploaded the version with lower casting time

EDIT2: updated with a FLYING_OVER_CLIFFS boolean (since HeX.16 requested it)
 

HeX.16

Isn't Trollin You Right Now
Reaction score
131
Perfect with my request now.

Thank you again Dinowc

Ill let you know if i find any bugs/places for improvement.

E:Units that die from the fire damage(burned) dont give the caster a bounty.
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
E:Units that die from the fire damage(burned) dont give the caster a bounty.
yeah about that... I based the DoT effect on Inner Fire with negative effects
it doesn't damage them, but causes them to have reduced health regen instead

that way they actually kill themselves and you don't gain bounty (probably exp as well)
I couldn't find any other spell to base the DoT from (didn't want to trigger that too, was too bored xd)

I'll try to do it in v1.2
 

HeX.16

Isn't Trollin You Right Now
Reaction score
131
I would attach pheonix fire to the pheonix dummy and set its level.
Works imo
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
I would attach pheonix fire to the pheonix dummy and set its level.
Works imo

wont work since I'm using only 1 global dummy for every instance of the spell
no problem I'm fixing the DoT problem right now...

EDIT: fixed
 

DrEvil

FCRI Associate!
Reaction score
111
pretty awesome spell :)
but I was probably the only person to be so impatient trying to download it while you were updating to version 1.2 I was so fired up...
but more to my point,
I used it on a shaman at an angle where he got flung into a little enclosure between cliffs and is invulnerable.. maybe a possible fix? somehow.
bug.png
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
pretty awesome spell :)
but I was probably the only person to be so impatient trying to download it while you were updating to version 1.2 I was so fired up...
but more to my point,
I used it on a shaman at an angle where he got flung into a little enclosure between cliffs and is invulnerable.. maybe a possible fix? somehow.
bug.png
=0

I've been testing the spell for like a half an hour and haven't noticed that one yet xd
I'll see what I can do...
 

HeX.16

Isn't Trollin You Right Now
Reaction score
131
Very nice now. All working 110% customisable

@DrEvil try set
JASS:
    private constant boolean FLY_OVER_CLIFFS = true

to
JASS:
    private constant boolean FLY_OVER_CLIFFS = false
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
@DrEvil
w8
was he permanently invulnerable or just while flying? because he is supposed to be invulnerable while flying
 

DrEvil

FCRI Associate!
Reaction score
111
ahh yeah -.-'
didn't realise it was on by default :D ( because in the code here it's set false.. xD )
+rep for a meaningless but devastating spell ( by meaningless I mean you just can't falcon punch random strangers )

@Dinowc
yeah permanent invulnerability ( no spazzing on the ground )
 

Laiev

Hey Listen!!
Reaction score
188
if you're still searching for a spell dps, incinerate :p or use the best way, timer with damage function

fast edit: and why you use this function not private?

JASS:
function GetParabolaZ takes real x returns real
 

Dinowc

don't expect anything, prepare for everything
Reaction score
223
yeah permanent invulnerability ( no spazzing on the ground )
I'm afraid I don't know how to fix that one :(

as I said, it never happened to me so I don't know the reason why he stays invulnerable

added an option to make caster/target invulnerable while sliding/flying
if you're still searching for a spell dps, incinerate or use the best way, timer with damage function
thanks
I already did it with a timer
fast edit: and why you use this function not private?
I was just copy/pasting that Acehart's magical function xd
wasn't even looking, nice find though
 
General chit-chat
Help Users
  • No one is chatting at the moment.

      The Helper Discord

      Members online

      Affiliates

      Hive Workshop NUON Dome World Editor Tutorials

      Network Sponsors

      Apex Steel Pipe - Buys and sells Steel Pipe.
      Top