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

Firestorm v1.0

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
  • Like
Reactions: FibSaSk
Summons fireballs that circle around the caster. The fireballs launches and damages any enemy units that come within 500 range, knocking them back outside of the 500 range in the process.

Level 1 - 3 fireballs, 50 damage.
Level 2 - 6 fireballs, 100 damage.
Level 3 - 9 fireballs, 150 damage.
Level 4 - 12 fireballs, 200 damage.

Cooldown: 0 seconds

Requires: Jesus4Lyf's Key Timers 2 Link:http://www.thehelper.net/forums/showthread.php?t=78392

Requires: Nestharus's : Recycle Link: http://www.thehelper.net/forums/showthread.php?t=136087&highlight=Recycle

MUI, Had a ton of help, from Glenphir AKA iAnayami from thehelper.net.

Code:
JASS:
scope Firestorm initializer OnInit

//=====================================================================//
//                       CONFIGURATION                                 //
//=====================================================================//

globals
    private constant integer ABILID = 'ABFS' // raw code of ability "Firestorm"
    private constant integer DUMMYID = 'h002' // raw code of unit "Firestorm Dummy"
    private constant real AOE = 500.0 // area of effect
    private constant real MAXHEIGHT = 500.0 // maximum height of fireballs
    private constant real SPEED = 1000.0 // knockback speed (per second)
    private constant real HRATE = 250.0 // rate of change in height (per second)
    private constant real OFFRATE = 500.0 // rate of change in distance (per second)
    private constant real KNOCKBACK = 300.00 // knockback distance
endglobals

private function GetFireCount takes integer level returns integer
    return level * 3 // number of fireballs
endfunction

private function GetDamage takes integer level returns real
    return level * 50.0 // damage per fireball
endfunction

//=====================================================================//
//                       END CONFIGURATION                             //
//=====================================================================//
//Don't touch unless you know what you're doing

globals
    private hashtable HASH = InitHashtable()
    private group GROUP = CreateGroup()
    private unit FILTER
    private unit CASTER
    private unit FIRE
endglobals

native UnitAlive takes unit id returns boolean

private struct Firestorm
    real damage
    real interval
    integer count
    group firegroup
    unit caster
endstruct

private struct Knockback
    real damage
    real height
    real speed
    real interval
    real x
    real y
    real angle
    real dist
    boolean check
    unit caster
    unit fire
    unit target
endstruct

private function GroupFilter takes nothing returns boolean
    local unit u = GetFilterUnit()
    if IsUnitEnemy(u, GetOwningPlayer(CASTER)) and UnitAlive(u) and not IsUnitType(u, UNIT_TYPE_STRUCTURE) and not LoadBoolean(HASH, GetHandleId(u), 0) then
        set FILTER = u
    endif
    set u = null
    return false
endfunction

private function GroupFunc takes nothing returns nothing
    local real x = GetUnitX(FILTER)
    local real y = GetUnitY(FILTER)
    local real mindist = 99999.0
    local unit u = GetEnumUnit()
    local real dx = GetUnitX(u) - x
    local real dy = GetUnitY(u) - y
    local real dist = SquareRoot(dx * dx + dy * dy)
    if dist < mindist then
        set FIRE = u
        set mindist = dist
    endif
    set u = null
endfunction

private function DamageKnock takes nothing returns boolean
    local Knockback k = KT_GetData()
    local real dx
    local real dy
    local real x
    local real y
    local real a
    local effect e
    if k.dist <= 0 and k.check then
        call k.destroy()
        call SaveBoolean(HASH, GetHandleId(k.target), 0, false)
        call FlushChildHashtable(HASH, GetHandleId(k.target))
        set e = null
        return true
    endif
    if k.check then
        set x = GetUnitX(k.target)
        set y = GetUnitY(k.target)
        set a = Atan2(y - k.y, x - k.x)
        set x = x + k.speed * Cos(k.angle)
        set y = y + k.speed * Sin(k.angle)
        call SetUnitPosition(k.target, x, y)
        set k.dist = k.dist - k.speed
    else
        set dx = GetUnitX(k.fire)
        set dy = GetUnitY(k.fire)
        set x = GetUnitX(k.target) - dx
        set y = GetUnitY(k.target) - dy
        if SquareRoot(x * x + y * y) <= 50 then
            call UnitDamageTarget(k.caster, k.target, k.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
            set e = AddSpecialEffect("Abilities\\Weapons\\RedDragonBreath\\RedDragonMissile.mdl", dx, dy)
            call DestroyEffect(e)
            set e = AddSpecialEffect("Objects\\Spawnmodels\\Other\\NeutralBuildingExplosion\\NeutralBuildingExplosion.mdl", dx, dy)
            call DestroyEffect(e)
            set k.x = GetUnitX(k.caster)
            set k.y = GetUnitY(k.caster)
            set k.dist = KNOCKBACK
            set k.angle = Atan2(GetUnitY(k.target) - dy, GetUnitX(k.target) - dx)
            call RemoveUnit(k.fire)
            set k.check = true
        else
            set k.height = (GetUnitFlyHeight(k.fire) - 50) / ((SquareRoot(x * x + y * y) / SPEED) / k.interval)
            call SetUnitFlyHeight(k.fire, GetUnitFlyHeight(k.fire) - k.height, 0)
            set x = x + dx
            set y = y + dy
            set a = Atan2(y - dy, x - dx)
            set x = dx + k.speed * Cos(a)
            set y = dy + k.speed * Sin(a)
            call SetUnitPosition(k.fire, x, y)
            call SetUnitFacing(k.fire, a * bj_RADTODEG)
        endif
    endif
    set e = null
    return false
endfunction

globals
    private group Periodic_group
endglobals
private function Periodic takes nothing returns boolean
    local Firestorm d = KT_GetData()
    local Knockback k
    local real a
    local real x
    local real y
    local real dx = GetUnitX(d.caster)
    local real dy = GetUnitY(d.caster)
    local real r
    local real min
    local real max
    local unit u
    if d.count == 0 then
        call Group.release(d.firegroup)
        call d.destroy()
        return true
    endif
    set CASTER = d.caster
    set FILTER = null
    call GroupEnumUnitsInRange(GROUP, GetUnitX(d.caster), GetUnitY(d.caster), AOE, Filter(function GroupFilter))
    if FILTER != null then
        set k = Knockback.create()
        call ForGroup(d.firegroup, function GroupFunc)
        set k.damage = d.damage
        set k.speed = d.interval * SPEED
        set k.interval = 0.04
        set k.check = false
        set k.caster = d.caster
        set k.fire = FIRE
        set k.target = FILTER
        call SaveBoolean(HASH, GetHandleId(k.target), 0, true)
        call KT_Add(function DamageKnock, k, k.interval)
        call FlushChildHashtable(HASH, GetHandleId(k.fire))
        call GroupRemoveUnit(d.firegroup, FIRE)
        set d.count = d.count - 1
    endif
    set Periodic_group = Group.get()
    call GroupAddGroup(d.firegroup, Periodic_group)
    loop
        set u = FirstOfGroup(Periodic_group)
        exitwhen u == null
        set x = GetUnitX(u) - dx
        set y = GetUnitY(u) - dy
        set a = GetUnitFacing(u)
        if SquareRoot(x * x + y * y) >= AOE then
            set a = a + GetRandomReal(0, 90.0)
            call SaveBoolean(HASH, GetHandleId(u), 0, true)
        elseif SquareRoot(x * x + y * y) <= 150.0 then
            set a = a + GetRandomReal(-90.0, 0)
            call SaveBoolean(HASH, GetHandleId(u), 0, false)
        else
            if LoadBoolean(HASH, GetHandleId(u), 0) then
                set a = a + GetRandomReal(-15.0, 45.0)
            else
                set a = a + GetRandomReal(-45.0, 15.0)
            endif
        endif
        set x = GetUnitX(u) + GetRandomReal(0, (d.interval * OFFRATE)) * Cos(a * bj_DEGTORAD)
        set y = GetUnitY(u) + GetRandomReal(0, (d.interval * OFFRATE)) * Sin(a * bj_DEGTORAD)
        set a = 90.0 + (Atan2(y - dy, x - dx) * bj_RADTODEG)
        call SetUnitPosition(u, x, y)
        call SetUnitFacing(u, a)
        set r = GetUnitFlyHeight(u)
        if not LoadBoolean(HASH, GetHandleId(u), 1) then
            if r >= MAXHEIGHT then
                call SaveBoolean(HASH, GetHandleId(u), 1, true)
                set r = 0
            else
                set r = GetRandomReal(0.0, d.interval * HRATE)
            endif
        else
            if r <= 50 then
                call SaveBoolean(HASH, GetHandleId(u), 1, false)
                set r = 0
            else
                set r = GetRandomReal(-d.interval * HRATE, 0)
            endif
        endif
        set r = GetUnitFlyHeight(u) + r
        call SetUnitFlyHeight(u, r, 0)
        call GroupRemoveUnit(Periodic_group, u)
    endloop
    call Group.release(Periodic_group)
    return false
endfunction

private function Actions takes nothing returns nothing
    local Firestorm d = Firestorm.create()
    local real a
    local real x
    local real y
    local integer i
    local integer loopmax
    local integer level
    local unit u
    set d.caster = GetTriggerUnit()
    set d.firegroup = Group.get()
    set level = GetUnitAbilityLevel(d.caster, ABILID)
    set d.damage = GetDamage(level)
    set d.interval = 0.04
    set d.count = 0
    set loopmax = GetFireCount(level)
    set i = 0
    loop
        exitwhen i == loopmax
        set x = GetUnitX(d.caster) + GetRandomReal(100.0, AOE) * Cos(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
        set y = GetUnitY(d.caster) + GetRandomReal(100.0, AOE) * Sin(GetRandomReal(0.0, 360.0) * bj_DEGTORAD)
        set a = 90.0 + Atan2(y - GetUnitY(d.caster), x - GetUnitX(d.caster)) * bj_RADTODEG
        set u = CreateUnit(GetOwningPlayer(d.caster), DUMMYID, x, y, a)
        call SetUnitFlyHeight(u, GetRandomReal(50.0, MAXHEIGHT), 0)
        call SaveBoolean(HASH, GetHandleId(u), 0, false)
        call SaveBoolean(HASH, GetHandleId(u), 1, false)
        call GroupAddUnit(d.firegroup, u)
        set d.count = d.count + 1
        set i = i + 1
    endloop
    call KT_Add(function Periodic, d, d.interval)
    set u = null
endfunction

private function Conditions takes nothing returns boolean
    return GetSpellAbilityId() == ABILID
endfunction

private function OnInit takes nothing returns nothing
    local trigger t = CreateTrigger()
    call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
    call TriggerAddCondition(t, Condition(function Conditions))
    call TriggerAddAction(t, function Actions)
endfunction

endscope

Keywords:
fire, fireball, ball, floating, flying, vJass, knockback, knock, back
Contents

Firestorm (Map)

Reviews
12th Dec 2015 IcemanBo: For long time as NeedsFix. Rejected. Bribe: The demo map doesn't open because the war3map.j file didn't get run through JassHelper properly (it didn't compile to vanilla JASS).

Moderator

M

Moderator

12th Dec 2015
IcemanBo: For long time as NeedsFix. Rejected.

Bribe:

The demo map doesn't open because the war3map.j file didn't get run through JassHelper properly (it didn't compile to vanilla JASS).
 
Level 5
Joined
Oct 24, 2007
Messages
90
Took a quick look at the spell and at the coding, and it looks neat, in my opinion :thumbs_up: Seems like you took a while working on this. The only thing I can directly point out is that the knockback effect could be better - it looks too rough and jumpy. You could make it smoother, and maybe make the unit decelerate while being knocked back until it reaches a complete halt (like most good knockback systems out there).

Might take a detailed look at the coding later. So far, I give it a 4/5 :)
 
JASS:
globals
    private hashtable HASH = InitHashtable()
    private group GROUP = CreateGroup()
    private unit FILTER
    private unit CASTER
    private unit FIRE
endglobals

1. All-capital letters are reserved for constants.
2. You don't need a whole hashtable for this, only the Table library
3. KeyTimers2 is a bad choice for this, it should use Timer32.
4. Your periodic timeout is not configurable (bad) making KeyTimers2 even more of a waste.
 
Top