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

Meteor Swarm (V 1.20)

Description


Upload Date: 17:38, 6th Apr 2012
Update V 1.10
  • 11:42 PM 4/8/2012
Update V 1.11
  • 11:15 AM 4/11/2012
Update V 1.12
  • 5:50 PM 4/12/2012
Update V 1.20
  • 12:15 AM 4/28/2012

Learn

Learn Meteor Swarm [Level 1]
Learn Meteor Swarm [Level 2]
Learn Meteor Swarm [Level 3]
Learn Meteor Swarm [Level 4]
Learn Meteor Swarm [Level 5]
Calls down waves of fiery boulders with random sizes that fall at random points in the target area dealing fire damage to enemy units and buildings in the effective area, except for the first meteor which is always at the centre of the area and deals maximum damage.

Level 1 - Calls 30 meteors that deals 100 to 300 fire damage.
Level 2 - Calls 35 meteors that deals 125 to 350 fire damage.
Level 3 - Calls 40 meteors that deals 150 to 400 fire damage.
Level 4 - Calls 45 meteors that deals 175 to 450 fire damage.
Level 5 - Calls 50 meteors that deals 200 to 500 fire damage.

[Deals 10% of damage only to trees]
Normal
Meteor Swarm - [Level 1]
Calls down 30 waves of firey boulders with random sizes that fall at random points in the target area dealing 100 to 300 fire damage to enemy units and buildings in the effective area. The first meteor is always at the centre and deals the maximum damage.

[Deals 10% of damage only to trees]


Meteor Swarm - [Level 2]
Calls down 35 waves of firey boulders with random sizes that fall at random points in the target area dealing 125 to 350 fire damage to enemy units and buildings in the effective area. The first meteor is always at the centre and deals the maximum damage.

[Deals 10% of damage only to trees]


Meteor Swarm - [Level 3]
Calls down 40 waves of firey boulders with random sizes that fall at random points in the target area dealing 150 to 400 fire damage to enemy units and buildings in the effective area. The first meteor is always at the centre and deals the maximum damage.

[Deals 10% of damage only to trees]


Meteor Swarm - [Level 4]
Calls down 45 waves of firey boulders with random sizes that fall at random points in the target area dealing 175 to 450 fire damage to enemy units and buildings in the effective area. The first meteor is always at the centre and deals the maximum damage.

[Deals 10% of damage only to trees]


Meteor Swarm - [Level 5]
Calls down 50 waves of firey boulders with random sizes that fall at random points in the target area dealing 200 to 500 fire damage to enemy units and buildings in the effective area. The first meteor is always at the centre and deals the maximum damage.

[Deals 10% of damage only to trees]


Icon by M0rbid


Sevion: for his 'Alloc' module

Magtheridon96: For his very good and detailed review
Adiktuz: Suggested many good ideas
RetroSexual: For his help in providing me with a trueshot aura model with zero height
Bribe


If someone finds he/she didn't get a well earned credit for using a resource(s) he/she made, contact me.
  • Will add destructible targets
  • Will add Shake Camera option
  • Will add a cast-at-bound fail safe function


- Older versions is included in the map for full change preview, its in a category named 'Older Versions'

V 1.00
  • Release

  • Changed some variable names.
  • DUMMYID and ABIL_ID changed from public to private.
  • Added static if to constant boolean.
  • Added set t = null in the function Init_MeteorSwarm.
  • Merged actions in condition function and removed TriggerAddAction(t, function MeteorSwarm_Actions).
  • Renamed both static method Start and fall to meteorCall and meteorFall.
  • Renamed dat to this.
  • Removed if/then/else block from MatchingUnit.
  • method meteorDamage now uses bj_lastCreatedGroup instead of creating and destroying a local or even instead of a global group.
  • Changed GetRandomReal(1.0,360.0) to GetRandomReal(0.0,2*bj_PI) and so I can remove the bj_DEGTORAD from both Cos and Sin functions.
  • Used one hashtable only.
  • method meteorDamageandMatchingUnit are now normal functions outside the struct.
  • Added the option to let the first falling meteor to deal maximum damage or random instead of random only.

  • Added an indicator to show the targeted area. You also have the option to show/hide this indicator.
  • You can target destructables now and also you have the option to disable it.
  • The spell now will function even if cast outside the boundary.
  • Modified the BoundSentinel library; it now use the playable area not the camera bound in case the user changed the camera bound to minimum.

  • Added Camera shake option
  • Added Unit-Explode-on-Death option if killed by a meteor

  • Implemented CTL
  • Merged a library with the scope
  • Changed the scope name to MeteorSwarm


  • PM me or post for any inquiry/problem(s)/suggestion(s)
  • Credit when use.
The spell is pasted below.
JASS:
// Meteor Swarm Version 1.20

scope MeteorSwarm initializer Init_MeteorSwarm

    globals
        private real MIN_X
        private real MIN_Y
        private real MAX_X
        private real MAX_Y
        private constant real SAFETY_OFFSET = 16.00

        private constant integer M_DUMMY_ID  = 'e000'
        private constant integer I_DUMMY_ID  = 'b000'
        private constant integer ABILITY_ID  = 'A000'
        private          integer index       = 0
        private          integer array temp_this
        private constant integer INT_MIN    = 3                  // Minimum Interval Between Meteors Threshold
        private constant integer INT_MAX    = 32                 // Maximum Interval Between Meteors Threshold
        private constant real    SCALE_MIN  = 0.80               // Minimum Meteor Scale Threshold
        private constant real    SCALE_MAX  = 3.50               // Maximum Meteor Scale Threshold
        private constant real    H_MIN      = 750.00             // Minimum Meteor Height Threshold
        private constant real    H_MAX      = 1000.00            // Maximum Meteor Height Threshold
        private constant real    INIT_DIST  = 1000.00
        private constant real    STEP       = 50.00
        private constant real    CHECK_DIST = 32.00

        private constant integer HT_OFFSET = 4096
        private hashtable mht  = InitHashtable()                 // Meteor Hashtable
        private constant timer   shake_timer = CreateTimer()
        private unit CASTER
        private real DAMAGE

//====================================================================================================
//                                           User-edit data                                         //
//====================================================================================================
// Make sure to change the dummy spell text to be appropriate with the following variables.

// General Formula: ConstantFactor + AbilityLevel * AbilityLevelFactor + PrevLevelFactor * PrevLevelValue

// The following variable can be changed according to the desired results
    // Number of Meteors Variables:
        private constant boolean NUM_TABLE   = false    // true/false to use Table/Formula
        // Table:
        private constant integer array nMeteors         // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant integer NUM_CF      = 25       // changes 'ConstantFactor'
        private constant integer NUM_LF      = 5        // changes 'AbilityLevelFactor'
        private constant real    NUM_PLF     = 0        // changes 'PrevLevelFactor'

    // Minimum Damage Variables:
        private constant boolean D_MIN_TABLE = false    // true/false to use Table/Formula
        // Table:
        private constant real array MinDamage           // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant real D_MIN_CF       = 75.00    // changes 'ConstantFactor'
        private constant real D_MIN_LF       = 25.00    // changes 'AbilityLevelFactor'
        private constant real D_MIN_PLF      = 0        // changes 'PrevLevelFactor'

    // Maximum Damage Variables:
        private constant boolean D_MAX_TABLE = false    // true/false to use Table/Formula
        // Table:
        private constant real array MaxDamage           // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant real D_MAX_CF       = 250.00   // changes 'ConstantFactor'
        private constant real D_MAX_LF       = 50.00    // changes 'AbilityLevelFactor'
        private constant real D_MAX_PLF      = 0        // changes 'PrevLevelFactor'

    // Area of Effect (AoE) Variables:
    // Grand Area of Effect:
        private constant boolean GR_TABLE    = false    // true/false to use Table/Formula
        // Table:
        private constant real array GrandArea           // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant real GR_CF          = 750.00   // changes 'ConstantFactor'
        private constant real GR_LF          = 50.00    // changes 'AbilityLevelFactor'
        private constant real GR_PLF         = 0        // changes 'PrevLevelFactor'

    // Maximum AOE per Meteor:
        private constant boolean MAXR_TABLE  = false    // true/false to use Table/Formula
        // Table:
        private constant real array LargeArea           // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant real MAXR_CF        = 150.00   // changes 'ConstantFactor'
        private constant real MAXR_LF        = 10.00    // changes 'AbilityLevelFactor'
        private constant real MAXR_PLF       = 0        // changes 'PrevLevelFactor'

    // Minimum AOE per Meteor:
        private constant boolean MINR_TABLE  = false    // true/false to use Table/Formula
        // Table:
        private constant real array SmallArea           // set the values in the function named "Spell_Data_Table"
        // Formula:
        private constant real MINR_CF        = 100.00   // changes 'ConstantFactor'
        private constant real MINR_LF        = 10.00    // changes 'AbilityLevelFactor'
        private constant real MINR_PLF       = 0        // changes 'PrevLevelFactor'

    // Targets:
        private constant boolean FRIENDLY    = true     // true/false  to  harm  enemy/all  units
        private constant boolean GROUND      = true     // true/false  to  include/exclude  ground units
        private constant boolean FLYING      = false    // true/false  to  include/exclude  flying units
        private constant boolean STRUCTURE   = true     // true/false  to  include/exclude  structure units
        private constant boolean DESTRUCTABLES = true   // true/false to enable/disable destructables
        private constant real DESTRUCTABLE_FACTOR = 10.00 // this is the damage dealt to destructables divisor

    // Flags:
        private constant boolean INIT_MAX_D         = true // true/false to enable/disable maximum damage on first meteor
        private constant boolean KILLS_EXPLODE      = true // true/false to enable/disable unit explosion if killed by a meteor
        private constant boolean SHOW_INDICATOR     = true // true/false to enable/disable cast area of effect indicator
        private constant boolean ALLOW_CAMERA_SHAKE = true // true/false to enable/disable camera shaking

    endglobals

    // Just IGNORE this function (Table) IF you are using a formula (Just fold it)
    private function Spell_Data_Table takes nothing returns nothing
        // Number of Meteors Table
        set  nMeteors[1] = 30
        set  nMeteors[2] = 35
        set  nMeteors[3] = 40
        set  nMeteors[4] = 45
        set  nMeteors[5] = 50
        // Minimum Damage Table
        set MinDamage[1] = 100.00
        set MinDamage[2] = 125.00
        set MinDamage[3] = 150.00
        set MinDamage[4] = 175.00
        set MinDamage[5] = 200.00
        // Maximum Damage Table
        set MaxDamage[1] = 300.00
        set MaxDamage[2] = 350.00
        set MaxDamage[3] = 400.00
        set MaxDamage[4] = 450.00
        set MaxDamage[5] = 500.00
        // Grand Area of Effect
        set GrandArea[1] = 800.00
        set GrandArea[2] = 850.00
        set GrandArea[3] = 900.00
        set GrandArea[4] = 950.00
        set GrandArea[5] = 1000.00
        // Maximum Area of Effect per Meteor
        set LargeArea[1] = 160.00
        set LargeArea[2] = 170.00
        set LargeArea[3] = 180.00
        set LargeArea[4] = 190.00
        set LargeArea[5] = 200.00
        // Minimum Area of Effect per Meteor
        set SmallArea[1] = 110.00
        set SmallArea[2] = 120.00
        set SmallArea[3] = 130.00
        set SmallArea[4] = 140.00
        set SmallArea[5] = 150.00
//====================================================================================================
//                                           End of User-edit data                                  //
//====================================================================================================
    endfunction

    private function MeteorsNumber takes integer al returns integer
        local integer prevNMet
        static if NUM_TABLE then
            return nMeteors[al]
        else
            if al == 1 then
                set prevNMet = 0
            else
                set prevNMet = MeteorsNumber(al - 1)
            endif
            return NUM_CF + NUM_LF * al + R2I(NUM_PLF) * prevNMet
        endif
    endfunction

    private function DamageMin takes integer al returns real
        local real prevMinDmg
        static if D_MIN_TABLE then
            return MinDamage[al]
        else
            if al == 1 then
                set prevMinDmg = 0.
            else
                set prevMinDmg = DamageMin(al - 1)
            endif
            return D_MIN_CF + D_MIN_LF * al + D_MIN_PLF * prevMinDmg
        endif
    endfunction

    private function DamageMax takes integer al returns real
        local real prevMaxDmg
        static if D_MAX_TABLE then
            return MaxDamage[al]
        else
            if al == 1 then
                set prevMaxDmg = 0.
            else
                set prevMaxDmg = DamageMax(al - 1)
            endif
            return D_MAX_CF + D_MAX_LF * al + D_MAX_PLF * prevMaxDmg
        endif
    endfunction

    private function AreaGrand takes integer al returns real
        local real prevGrArea
        static if GR_TABLE then
            return GrandArea[al]
        else
            if al == 1 then
                set prevGrArea = 0.
            else
                set prevGrArea = AreaGrand(al - 1)
            endif
            return GR_CF + GR_LF * al + GR_PLF * prevGrArea
        endif
    endfunction

    private function MinAreaPerMeteor takes integer al returns real
        local real prevMinA
        static if MINR_TABLE then
            return SmallArea[al]
        else
            if al == 1 then
                set prevMinA = 0.
            else
                set prevMinA = MinAreaPerMeteor(al - 1)
            endif
            return MINR_CF + MINR_LF * al + MINR_PLF * prevMinA
        endif
    endfunction

    private function MaxAreaPerMeteor takes integer al returns real
        local real prevMaxA
        static if MAXR_TABLE then
            return LargeArea[al]
        else
            if al == 1 then
                set prevMaxA = 0.
            else
                set prevMaxA = MaxAreaPerMeteor(al - 1)
            endif
            return MAXR_CF + MAXR_LF * al + MAXR_PLF * prevMaxA
        endif
    endfunction

    private function GetSpellTargetXBounded takes nothing returns real
        local real x = GetSpellTargetX()
        if x < MIN_X then
            return MIN_X + SAFETY_OFFSET
        elseif x > MAX_X then
            return MAX_X - SAFETY_OFFSET
        else
            return x
        endif
    endfunction

    private function GetSpellTargetYBounded takes nothing returns real
        local real y = GetSpellTargetY()
        if y < MIN_Y then
            return MIN_Y + SAFETY_OFFSET
        elseif y > MAX_Y then
            return MAX_Y - SAFETY_OFFSET
        else
            return y
        endif
    endfunction

    private function MatchingUnit takes unit u,player owner returns boolean
        return not( IsUnitType(u,UNIT_TYPE_DEAD)             or/*
        */ IsUnitType(u,UNIT_TYPE_MAGIC_IMMUNE)              or/*
        */(    FRIENDLY  and IsUnitAlly(u,owner))            or/*
        */(not FLYING    and IsUnitType(u,UNIT_TYPE_FLYING)) or/*
        */(not GROUND    and IsUnitType(u,UNIT_TYPE_GROUND)) or/*
        */(not STRUCTURE and IsUnitType(u,UNIT_TYPE_STRUCTURE)))
    endfunction

    private function meteorDamageUnits takes player owner,unit caster,real X, real Y,real rad,real damage returns nothing
        local unit target

        call GroupEnumUnitsInRange(bj_lastCreatedGroup,X,Y,rad,null)

        loop
            set target = FirstOfGroup(bj_lastCreatedGroup)
            exitwhen target == null
            if MatchingUnit(target,owner) then
                static if KILLS_EXPLODE then
                    call SetUnitExploded(target,true)
                endif
                call UnitDamageTarget(caster,target,damage,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
                static if KILLS_EXPLODE then
                    call SetUnitExploded(target,false)
                endif
            endif
            call GroupRemoveUnit(bj_lastCreatedGroup,target)
        endloop
    endfunction

    /* this function/filter is not needed for now and is turned off
    private function matchingDestructable takes nothing returns boolean
        return GetDestructableTypeId(GetFilterDestructable()) == WHO_KNOWS_WHAT_TYPE
    endfunction*/

    private function meteorDamageDestructables takes nothing returns nothing
        call UnitDamageTarget(CASTER,GetEnumDestructable(),DAMAGE,false,false,ATTACK_TYPE_NORMAL,DAMAGE_TYPE_FIRE,null)
    endfunction

    private struct Meteor extends array
        player  owner
        unit    caster
        unit    target_indicator
        unit    meteor
        real    TX
        real    TY
        real    slope
        real    const
        real    face
        real    grandarea
        real    minarea
        real    maxarea
        real    mindamage
        real    maxdamage
        integer meteors
        integer ibem        // Interval Between Each Meteor
        integer cfm         // Current Falling Meteors


        static method cameraShake takes thistype this returns nothing
            local integer i = 0
            local real camX = GetCameraTargetPositionX()
            local real camY = GetCameraTargetPositionY()
            local real dx = this.TX - camX
            local real dy = this.TY - camY
            local real x1 = SquareRoot( dx*dx + dy*dy )
            local real x2
            local real magnitude

                loop
                    exitwhen i >= index
                    set this = temp_this[i]
                    set dx = this.TX - camX
                    set dy = this.TY - camY
                    set x2 = SquareRoot( dx*dx + dy*dy )
                    if x1 > x2 then
                        set x1 = x2
                    endif
                    set i = i + 1
                endloop
                set magnitude = 20.0 * Pow(bj_E,- 0.001 * x1) // magnitude = 20*e^(-x)
                call CameraSetSourceNoiseEx(magnitude,1000.,false)
                call CameraSetTargetNoiseEx(magnitude,1000.,false)
                
                if index == 0 then
                    call CameraSetSourceNoise(0, 0)
                    call CameraSetTargetNoise(0, 0)
                endif
        endmethod


        implement CTL
            local integer i = 0 // Instance Counter index
            local integer j = 0 // Meteor Counter index
            local unit u        // This Dummy Unit
            local unit nu       // Next Dummy Unit
            local real x        // This Meteor X
            local real y        // This Meteor Y
            local real z        // This Meteor Z
            local real ix       // This Meteor Initial X
            local real iy       // This Meteor Initial Y
            local real tx       // This Meteor Target  X
            local real ty       // This Meteor Target  Y
            local real dx       // Delta X
            local real dy       // Delta Y
            local real dv       // XY Plane variable
            local real face     // This Meteor Facing
            local real slope    // This Meteor Fall Line Slope
            local real const    // This Meteor Fall Line Constant
            local real ratio    // This Meteor Scaling Ratio
            local real ntx      // Next Target X
            local real nty      // Next Target Y
            local real nface    // Next Facing
            local real nix      // Next Initial X
            local real niy      // Next Initial Y
            local real niz      // Next Initial Z
            local real rnd_rad  // Random Radius
            local real rnd_ang  // Random Angle
            local real bound    // Bound Size of Falling Meteor w.r.t Grand Area
            local real scale    // Next Meteor Scaling
            local real nratio   // Next Meteor Scaling Ratio
            local real accurate_radius  // Accurate Radius
            local real accurate_damage  // Accurate Damage
            local rect effectrect       // effective Rect for destructables
            
        implement CTLExpire

                set this.ibem = this.ibem - 1
                if this.ibem <= 0 then
                    set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
                    set this.cfm = this.cfm + 1
                    if this.cfm <= this.meteors then
                        // launch another meteor
                        set bound = this.grandarea - this.maxarea
                        loop
                            set rnd_rad = GetRandomReal(0.0,bound)
                            set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
                            set ntx = this.TX + rnd_rad*Cos(rnd_ang)
                            set nty = this.TY + rnd_rad*Sin(rnd_ang)
                            exitwhen ntx > MIN_X and ntx < MAX_X and nty > MIN_Y and nty < MAX_Y
                        endloop
                        loop
                            set nface = GetRandomReal(0.0,2.*bj_PI)
                            set nix = ntx - INIT_DIST * Cos(nface)
                            set niy = nty - INIT_DIST * Sin(nface)
                            exitwhen nix > MIN_X and nix < MAX_X and niy > MIN_Y and niy < MAX_Y
                        endloop
                        set niz = GetRandomReal(H_MIN,H_MAX)
                        set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
                        call SetUnitPathing(nu,false)
                        set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
                        set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
                        call SetUnitScale(nu,scale,scale,scale)
                        call SetUnitFlyHeight(nu,niz,0.0)

                        call SaveUnitHandle(mht,this,this.cfm,nu)
                        call SaveBoolean(mht,this,this.cfm,false)
                        call SaveReal(mht, this, this.cfm,ntx)
                        call SaveReal(mht, this,-this.cfm,nty)
                        call SaveReal(mht,-this, this.cfm,nix)
                        call SaveReal(mht,-this,-this.cfm,niy)
                        call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
                        call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
                        call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
                        call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
                    endif
                endif

                set j = 1
                loop
                    exitwhen j > this.cfm

                    set u = LoadUnitHandle(mht,this,j)
                    if not(LoadBoolean(mht,this,j)) then
                        set tx = LoadReal(mht, this, j)
                        set ty = LoadReal(mht, this,-j)
                        set ix = LoadReal(mht,-this, j)
                        set iy = LoadReal(mht,-this,-j)
                        set x = GetUnitX(u)
                        set y = GetUnitY(u)
                        set dx = tx - x
                        set dy = ty - y
                        set ratio = LoadReal(mht,this + HT_OFFSET,-j)
                        set accurate_radius = ratio * (this.maxarea   - this.minarea  ) + this.minarea
                        set accurate_damage = ratio * (this.maxdamage - this.mindamage) + this.mindamage

                        if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
                            call KillUnit(u)
                            call SaveBoolean(mht,this,j,true)
                            // Shake camera
                            static if ALLOW_CAMERA_SHAKE then
                                call cameraShake(this)
                            endif
                            // Do Damage stuff
                            call meteorDamageUnits(this.owner,this.caster,tx,ty,accurate_radius,accurate_damage)
                            static if DESTRUCTABLES then
                                set CASTER = this.caster
                                set DAMAGE = accurate_damage/DESTRUCTABLE_FACTOR
                                set effectrect = Rect(tx - accurate_radius, ty - accurate_radius, tx + accurate_radius, ty + accurate_radius)
                                call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
                                call RemoveRect(effectrect)
                            endif
                            if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
                            */ LoadUnitHandle(mht,this,this.meteors) != null then
                                set index = index - 1
                                set temp_this[this] = temp_this[index]
                                call cameraShake(this)
                                static if SHOW_INDICATOR then
                                    call RemoveUnit(this.target_indicator)
                                    set this.target_indicator = null
                                endif
                                call this.destroy()
                                call FlushChildHashtable(mht, this)
                                call FlushChildHashtable(mht,-this)
                                call FlushChildHashtable(mht, this + HT_OFFSET)
                                call FlushChildHashtable(mht,-this - HT_OFFSET)

                            endif

                        else // Move the meteor
                            set face = LoadReal(mht,this + HT_OFFSET,j)
                            set slope = LoadReal(mht,-this - HT_OFFSET,j)
                            set const = LoadReal(mht,-this - HT_OFFSET,-j)
                            set x = x + STEP * Cos(face)
                            set y = y + STEP * Sin(face)
                            set dx = x - ix
                            set dy = y - iy
                            set dv = SquareRoot( dx*dx + dy*dy )
                            set z = slope * dv + const
                            call SetUnitX(u,x)
                            call SetUnitY(u,y)
                            call SetUnitFlyHeight(u,z,0.0)
                        endif
                    endif
                    set j = j + 1
                endloop

        implement CTLNull

            set u = null
            set nu = null
            set effectrect = null
            
        implement CTLEnd


        static method meteorCall takes unit caster, real tx, real ty returns nothing
            local thistype this = create()
            local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
            local unit u    // Dummy Unit
            local real cx   // Cast Point X
            local real cy   // Cast Point Y
            local real ix   // First Meteor Initial X
            local real iy   // First Meteor Initial Y
            local real iz   // First Meteor Initial Z
            local real dx
            local real dy
            local real face
            local real scale
            local real ratio
            local real indicator_scale = 9. + I2R(al)

            set temp_this[index] = this
            set this.caster = caster
            set this.owner = GetOwningPlayer(caster)
            set this.TX = tx
            set this.TY = ty
            set cx = GetUnitX(caster)
            set cy = GetUnitY(caster)
            set dx = tx - cx
            set dy = ty - cy
            set face = Atan2(dy,dx)
            set ix = tx - INIT_DIST * Cos(face)
            set iy = ty - INIT_DIST * Sin(face)
            set iz = GetRandomReal(H_MIN,H_MAX)
            static if SHOW_INDICATOR then
                set this.target_indicator = CreateUnit(this.owner,I_DUMMY_ID,tx,ty,0.0)
                call SetUnitVertexColor(this.target_indicator, 255,255,255,255)
                call SetUnitTimeScale(this.target_indicator,0.7)
                call SetUnitScale(this.target_indicator,indicator_scale,indicator_scale,indicator_scale)
                call SetUnitPathing(this.target_indicator,false)
                call SetUnitX(this.target_indicator,tx)
                call SetUnitY(this.target_indicator,ty)
            endif
            set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
            call SetUnitPathing(u,false)
            static if INIT_MAX_D then
                set scale = SCALE_MAX
            else
                set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
            endif
            set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
            call SetUnitScale(u,scale,scale,scale)
            call SetUnitFlyHeight(u,iz,0.0)
            set this.cfm = 1
            set this.ibem = GetRandomInt(INT_MIN,INT_MAX)

            call SaveUnitHandle(mht,this,1,u)
            call SaveBoolean(mht,this,1,false)
            call SaveReal(mht, this, 1,tx)
            call SaveReal(mht, this,-1,ty)
            call SaveReal(mht,-this, 1,ix)
            call SaveReal(mht,-this,-1,iy)
            call SaveReal(mht, this + HT_OFFSET, 1,face)
            call SaveReal(mht, this + HT_OFFSET,-1,ratio)
            call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
            call SaveReal(mht,-this - HT_OFFSET,-1,iz)

            set this.meteors   = MeteorsNumber(al)
            set this.grandarea = AreaGrand(al)
            set this.maxarea   = MaxAreaPerMeteor(al)
            set this.minarea   = MinAreaPerMeteor(al)
            set this.maxdamage = DamageMax(al)
            set this.mindamage = DamageMin(al)

            set u = null

            set index = index + 1

        endmethod

    endstruct

    private function MeteorSwarm_Conditions takes nothing returns boolean
        if GetSpellAbilityId() == ABILITY_ID then
            call Meteor.meteorCall(GetTriggerUnit(),GetSpellTargetXBounded(),GetSpellTargetYBounded())
        endif
        return false
    endfunction

    private function Init_MeteorSwarm takes nothing returns nothing
        local trigger t = CreateTrigger()
        call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
        call TriggerAddCondition( t, function MeteorSwarm_Conditions )
        call RemoveUnit(CreateUnit(Player(12),M_DUMMY_ID,0.,0.,0.))
        call RemoveUnit(CreateUnit(Player(12),I_DUMMY_ID,0.,0.,0.))
        call Spell_Data_Table()
        set MIN_X = GetCameraBoundMinX() - 512.000
        set MIN_Y = GetCameraBoundMinY() - 256.000
        set MAX_X = GetCameraBoundMaxX() + 512.000
        set MAX_Y = GetCameraBoundMaxY() + 256.000
        set t = null
    endfunction

endscope
IMPORTANT: You will need JNGP to customize it to your needs.


Keywords:
Meteor,Meteors,Comet,Comets,Asteroid,Asteroids,Swarm,Swarming,Shower,Showering,Rain,Raining,Fire,Fiery,Fall,Falling,Chaos,Catastrophe.
Contents

Meteor Swarm v1.13 (Map)

Reviews
16 July 2012 Bribe: Well-coded and as nice effects. Approved 4.4/5.
Level 29
Joined
Mar 10, 2009
Messages
5,016
when I was still learning jass, I made spells with separate timers, pausing and destroying it (which is btw, nothing wrong), then eventually I learned how efficient it be to recycle timers, not to mention the less time I spent on making things...
that's why if you want to be a jasser you must implement efficient things >>> libraries coz it will make your work so awesome...
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Meaning instead of 1 timer running per spell which uses a timer, having a centralized timer system like T32 or CTL is better since you'll only be running the timer run by the library for all spells/systems that use it (either T32 or CTL)...

ok, you mean; someone is making a map and he used my spell and used another spells from another people and it happened that those spells use timer too, so we will have lots of timer running which could lag the map and make it is inefficient.

I made spells with separate timers, pausing and destroying it (which is btw, nothing wrong)
Just a learning question, I am not destroying the timer if you check my code I am only pausing it when there is no instance in use. So is pausing/running the same timer does not considered timer recycling ?
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
I have added the CTL library to the spell and I was hoping that someone could report for any errors the spell might cause before I publish the next update.

~Thanks in Advance~

I have pasted the struct only and the rest is as it is, also you will find lots of comments which I intended to leave them to notify for the changes I made.
Meteor Swarm
JASS:
    private struct Meteor extends array
        player  owner
        unit    caster
        unit    targetindicator
        unit    meteor
        real    TX
        real    TY
        real    slope
        real    const
        real    face
        real    grandarea
        real    minarea
        real    maxarea
        real    mindamage
        real    maxdamage
        integer meteors
        integer ibem        // Interval Between Each Meteor
        integer cfm         // Current Falling Meteors

        implement Alloc

        static method cameraShake takes nothing returns nothing
            local integer i = 0
            local thistype this = temp_dat[i]
            local real camX = GetCameraTargetPositionX()
            local real camY = GetCameraTargetPositionY()
            local real dx = this.TX - camX
            local real dy = this.TY - camY
            local real x1 = SquareRoot( dx*dx + dy*dy )
            local real x2
            local real magnitude

            if START_SHAKING then
                loop
                    exitwhen i >= index
                    set this = temp_dat[i]
                    set dx = this.TX - camX
                    set dy = this.TY - camY
                    set x2 = SquareRoot( dx*dx + dy*dy )
                    if x1 > x2 then
                        set x1 = x2
                    endif
                    set i = i + 1
                endloop
                set magnitude = 20.0 * Pow(bj_E,- 0.001 * x1) // magnitude = 20*e^(-x)
                call CameraSetSourceNoiseEx(magnitude,1000.,false)
                call CameraSetTargetNoiseEx(magnitude,1000.,false)
                
                if index == 0 then
                    set START_SHAKING = false
                    call PauseTimer(shake_timer)
                    call CameraSetSourceNoise(0, 0)
                    call CameraSetTargetNoise(0, 0)
                endif
            endif
        endmethod


        implement CTL
            //local thistype this
            local integer i //= 0 // Instance Counter index
            local integer j //= 0 // Meteor Counter index
            local unit u        // This Dummy Unit
            local unit nu       // Next Dummy Unit
            local real x        // This Meteor X
            local real y        // This Meteor Y
            local real z        // This Meteor Z
            local real ix       // This Meteor Initial X
            local real iy       // This Meteor Initial Y
            local real tx       // This Meteor Target  X
            local real ty       // This Meteor Target  Y
            local real dx       // Delta X
            local real dy       // Delta Y
            local real dv       // XY Plane variable
            local real face     // This Meteor Facing
            local real slope    // This Meteor Fall Line Slope
            local real const    // This Meteor Fall Line Constant
            local real ratio    // This Meteor Scaling Ratio
            local real ntx      // Next Target X
            local real nty      // Next Target Y
            local real nface    // Next Facing
            local real nix      // Next Initial X
            local real niy      // Next Initial Y
            local real niz      // Next Initial Z
            local real rnd_rad  // Random Radius
            local real rnd_ang  // Random Angle
            local real bound    // Bound Size of Falling Meteor w.r.t Grand Area
            local real scale    // Next Meteor Scaling
            local real nratio   // Next Meteor Scaling Ratio
            local real accurate_radius  // Accurate Radius
            local real accurate_damage  // Accurate Damage
            local rect effectrect // effective Rect for destructables
            
        implement CTLExpire

            set i = 0
            //loop
                //exitwhen i >= index
                //set this = temp_dat[i]

                set this.ibem = this.ibem - 1
                if this.ibem <= 0 then
                    set this.ibem = GetRandomInt(INT_MIN,INT_MAX)
                    set this.cfm = this.cfm + 1
                    if this.cfm <= this.meteors then
                        // launch another meteor
                        set bound = this.grandarea - this.maxarea
                        loop
                            set rnd_rad = GetRandomReal(0.0,bound)
                            set rnd_ang = GetRandomReal(0.0,2.*bj_PI)
                            set ntx = this.TX + rnd_rad*Cos(rnd_ang)
                            set nty = this.TY + rnd_rad*Sin(rnd_ang)
                            exitwhen ntx > MIN_X and ntx < MAX_X and nty > MIN_Y and nty < MAX_Y
                        endloop
                        loop
                            set nface = GetRandomReal(0.0,2.*bj_PI)
                            set nix = ntx - INIT_DIST * Cos(nface)
                            set niy = nty - INIT_DIST * Sin(nface)
                            exitwhen nix > MIN_X and nix < MAX_X and niy > MIN_Y and niy < MAX_Y
                        endloop
                        set niz = GetRandomReal(H_MIN,H_MAX)
                        set nu = CreateUnit(this.owner,M_DUMMY_ID,nix,niy,nface*bj_RADTODEG)
                        call SetUnitPathing(nu,false)
                        set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
                        set nratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
                        call SetUnitScale(nu,scale,scale,scale)
                        call SetUnitFlyHeight(nu,niz,0.0)

                        call SaveUnitHandle(mht,this,this.cfm,nu)
                        call SaveBoolean(mht,this,this.cfm,false)
                        call SaveReal(mht, this, this.cfm,ntx)
                        call SaveReal(mht, this,-this.cfm,nty)
                        call SaveReal(mht,-this, this.cfm,nix)
                        call SaveReal(mht,-this,-this.cfm,niy)
                        call SaveReal(mht, this + HT_OFFSET, this.cfm,nface)
                        call SaveReal(mht, this + HT_OFFSET,-this.cfm,nratio)
                        call SaveReal(mht,-this - HT_OFFSET, this.cfm,-niz/INIT_DIST)
                        call SaveReal(mht,-this - HT_OFFSET,-this.cfm,niz)
                    endif
                endif

                set j = 1
                loop
                    exitwhen j > this.cfm

                    set u = LoadUnitHandle(mht,this,j)
                    if not(LoadBoolean(mht,this,j)) then
                        set tx = LoadReal(mht, this, j)
                        set ty = LoadReal(mht, this,-j)
                        set ix = LoadReal(mht,-this, j)
                        set iy = LoadReal(mht,-this,-j)
                        set x = GetUnitX(u)
                        set y = GetUnitY(u)
                        set dx = tx - x
                        set dy = ty - y
                        set ratio = LoadReal(mht,this + HT_OFFSET,-j)
                        set accurate_radius = ratio * (this.maxarea   - this.minarea  ) + this.minarea
                        set accurate_damage = ratio * (this.maxdamage - this.mindamage) + this.mindamage

                        if SquareRoot( dx*dx + dy*dy ) < CHECK_DIST or GetUnitFlyHeight(u) < CHECK_DIST then
                            call KillUnit(u)
                            call SaveBoolean(mht,this,j,true)
                            // Shake camera
                            static if ALLOW_CAMERA_SHAKE then
                                set START_SHAKING = true
                            endif
                            // Do Damage stuff
                            call meteorDamageUnits(this.owner,this.caster,tx,ty,accurate_radius,accurate_damage)
                            static if DESTRUCTABLES then
                                set CASTER = this.caster
                                set DAMAGE = accurate_damage/DESTRUCTABLE_FACTOR
                                set effectrect = Rect(tx - accurate_radius, ty - accurate_radius, tx + accurate_radius, ty + accurate_radius)
                                call EnumDestructablesInRect(effectrect,null,function meteorDamageDestructables)
                            endif
                            if IsUnitType(LoadUnitHandle(mht,this,this.meteors),UNIT_TYPE_DEAD) and/*
                               */ LoadUnitHandle(mht,this,this.meteors) != null then
                                //set index = index - 1
                                //set temp_dat[i] = temp_dat[index]
                                //set i = i - 1
                                /*static*/ if SHOW_INDICATOR then
                                    call RemoveUnit(this.targetindicator)
                                    set this.targetindicator = null
                                endif
                                call this.destroy()
                                call FlushChildHashtable(mht, this)
                                call FlushChildHashtable(mht,-this)
                                call FlushChildHashtable(mht, this + HT_OFFSET)
                                call FlushChildHashtable(mht,-this - HT_OFFSET)
                
                                if index == 0 then
                                    //call PauseTimer(tim)
                                    //call this.destroy()
                                endif

                                //call this.deallocate()
                            endif

                        else // Move the meteor
                            set face = LoadReal(mht,this + HT_OFFSET,j)
                            set slope = LoadReal(mht,-this - HT_OFFSET,j)
                            set const = LoadReal(mht,-this - HT_OFFSET,-j)
                            set x = x + STEP * Cos(face)
                            set y = y + STEP * Sin(face)
                            set dx = x - ix
                            set dy = y - iy
                            set dv = SquareRoot( dx*dx + dy*dy )
                            set z = slope * dv + const
                            call SetUnitX(u,x)
                            call SetUnitY(u,y)
                            call SetUnitFlyHeight(u,z,0.0)
                        endif
                    endif
                    set j = j + 1
                endloop

                //set i = i + 1
            //endloop

        implement CTLNull

            set u = null
            set nu = null
            
        implement CTLEnd


        static method meteorCall takes unit caster, real tx, real ty returns nothing
            local thistype this = create()
            local integer al = GetUnitAbilityLevel(caster,ABILITY_ID)
            local unit u    // Dummy Unit
            local real cx   // Cast Point X
            local real cy   // Cast Point Y
            local real ix   // First Meteor Initial X
            local real iy   // First Meteor Initial Y
            local real iz   // First Meteor Initial Z
            local real dx
            local real dy
            local real face
            local real scale
            local real ratio
            local real temp = 9. + I2R(al)

            set temp_dat[index] = this
            set this.caster = caster
            set this.owner = GetOwningPlayer(caster)
            set this.TX = tx
            set this.TY = ty
            set cx = GetUnitX(caster)
            set cy = GetUnitY(caster)
            set dx = tx - cx
            set dy = ty - cy
            set face = Atan2(dy,dx)
            set ix = tx - INIT_DIST * Cos(face)
            set iy = ty - INIT_DIST * Sin(face)
            set iz = GetRandomReal(H_MIN,H_MAX)
            static if SHOW_INDICATOR then
                set this.targetindicator = CreateUnit(this.owner,I_DUMMY_ID,tx,ty,0.0)
                call SetUnitVertexColor(this.targetindicator, 255,255,255,255)
                call SetUnitTimeScale(this.targetindicator,0.7)
                call SetUnitScale(this.targetindicator,temp,temp,temp)
                call SetUnitPathing(this.targetindicator,false)
                call SetUnitX(this.targetindicator,tx)
                call SetUnitY(this.targetindicator,ty)
            endif
            set u = CreateUnit(this.owner,M_DUMMY_ID,ix,iy,face*bj_RADTODEG)
            call SetUnitPathing(u,false)
            static if INIT_MAX_D then
                set scale = SCALE_MAX
            else
                set scale = GetRandomReal(SCALE_MIN,SCALE_MAX)
            endif
            set ratio = (scale - SCALE_MIN)/(SCALE_MAX-SCALE_MIN)
            call SetUnitScale(u,scale,scale,scale)
            call SetUnitFlyHeight(u,iz,0.0)
            set this.cfm = 1
            set this.ibem = GetRandomInt(INT_MIN,INT_MAX)

            call SaveUnitHandle(mht,this,1,u)
            call SaveBoolean(mht,this,1,false)
            call SaveReal(mht, this, 1,tx)
            call SaveReal(mht, this,-1,ty)
            call SaveReal(mht,-this, 1,ix)
            call SaveReal(mht,-this,-1,iy)
            call SaveReal(mht, this + HT_OFFSET, 1,face)
            call SaveReal(mht, this + HT_OFFSET,-1,ratio)
            call SaveReal(mht,-this - HT_OFFSET, 1,-iz/INIT_DIST)
            call SaveReal(mht,-this - HT_OFFSET,-1,iz)

            set this.meteors   = MeteorsNumber(al)
            set this.grandarea = AreaGrand(al)
            set this.maxarea   = MaxAreaPerMeteor(al)
            set this.minarea   = MinAreaPerMeteor(al)
            set this.maxdamage = DamageMax(al)
            set this.mindamage = DamageMin(al)

            if index == 0 then
                //call TimerStart(tim,SPEED,true, function thistype.meteorFall )
                call thistype.create()
                static if ALLOW_CAMERA_SHAKE then
                    //call TimerStart(shake_timer,0.04,true,function thistype.cameraShake)
                endif

            endif

            set index = index + 1

            set u = null

        endmethod

    endstruct
 
Level 29
Joined
Mar 10, 2009
Messages
5,016
ofc it's slower, why?, coz TimerStart, PauseTimer and DestroyTimer are natives
that executes on it's own while libraries calls many codes after the create
method not to mention the loop in the expire...
take a look at TimerUtils for example, after caling the NewTimerEx it executes many codes...
but on the other hand, recycling timers are efficient and can easily attach data to it...
 
Top