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

[vJASS] Help using CTL

Status
Not open for further replies.
Level 16
Joined
Mar 3, 2006
Messages
1,564
This thread will be divided into two sections.

First,

I am trying to understand timers so I could use CTL, what is the meaning of a merged timers. And what are the problems merged timers cause ?

Second part will be about using CTL but lets stick to the first question for now.

Thanks in advance for help
 
Hmm how to explain, idea behind this: http://www.hiveworkshop.com/forums/jass-resources-412/system-timer-tools-201165/ is to group all same timer loops into single one.

So instead:
- Timer 1 loop each 0.03 s
- Timer 2 loop each 0.03 s
- Timer 3 loop each 0.03 s
- Timer 4 loop each 0.50 s
- Timer 5 loop each 1.00 s

We have:
- Timer 1' loop each 0.03 s (this one will contain timer actions 1/2/3 from above )
- Timer 2' loop each 0.50 s
- Timer 3' loop each 1.00 s

And ofc that mad guy like Nes make it safe so you don't have interactions between 2 timer actions run on merged one and so on.

Everything else is explained in resource thread so I won't bother you with more details.

It's safe, fast and more or less easy for use.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Copy whole library into map. If it require som other libraries imort them as well. The use it in your code... You have examples in resource thread.

No offense meant, but your answer didn't help me. The examples in the resource thread; those I can't understand.

Ok, perhaps I should paste my spell that will need that library, so you could help me more.

The spell is in my signature, its called Meteor Swarm
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
ok I tried to implement the CTL in this spell so I did the following but its wrong and gives me and error, could someone help me correct it.

JASS:
        static method meteorCall takes unit caster, real tx, real ty returns nothing

            implement CTL

                local thistype this = thistype.allocate()
                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)

            implement CTLExpire

                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 )

                    static if ALLOW_CAMERA_SHAKE then
                        call TimerStart(shake_timer,0.04,true,function thistype.cameraShake)
                    endif

                endif

                set index = index + 1

            implement CTLNull

                set u = null

            implement CTLEnd

        endmethod
 
CTL modules shouldn't be implemented inside functions, they should be implemented directly inside the struct.

Also, when you declare locals, you shouldn't give them values, you should set their values after CTLExpire.
This is because CTLExpire is where the loop starts for all the instances registered to the timer.

And you shouldn't be using allocate, you should be calling thistype.create() outside of the implementations.
thistype.create() works like allocate as well.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Ok, mag, this an outline of the spell could you point where I should implement those modules.

JASS:
    private struct Meteor extends array
        // Struct members
        
        implement Alloc
        
        static method cameraShake takes nothing returns nothing
            local integer i = 0
            local thistype this = temp_dat[i]
            // some locals here

            if START_SHAKING then
                loop
                endloop
                if index == 0 then
                    set START_SHAKING = false
                    call PauseTimer(shake_timer)
                    call CameraSetSourceNoise(0, 0)
                    call CameraSetTargetNoise(0, 0)
                endif
            endif
        endmethod

        static method meteorFall takes nothing returns nothing
            local thistype this
            // lots of another locals here

            loop
                // some stuff here
                set j = 1
                loop
                    exitwhen CONDITIONS
                    if CONDITIONS then
                        if CONDITIONS then
                            // Shake camera
                            // Do Damage stuff
                            if CONDITIONS then
                                // some actions
                                if index == 0 then
                                    call PauseTimer(tim)
                                endif
                                call this.deallocate()
                            endif
                        else // Move the meteor
                        endif
                    endif
                    set j = j + 1
                endloop

                set i = i + 1
            endloop
            set u = null
            set nu = null

        endmethod

        static method meteorCall takes unit caster, real tx, real ty returns nothing
            local thistype this = thistype.allocate()
            // locals here

            // some variable setting and function calls are here
            if index == 0 then
                call thistype.create()
                call TimerStart(tim,SPEED,true, function thistype.meteorFall )
                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
 
Hmmm let me ask you something, why you use module and such things at first place?
I guess you want to create spell and you want it to be optimized and fast.
But ask yourself who will use it?

Then take a look at those scripts you want to use, timer tools or table or any other script will be effective only if you have 5+ spells that use them in some map.
You will shorten spell code, YES, but true fact is that it will run script you linked and actual code will be much longer.

Some map makers code in GUI and they don't need scripts, it's useless to import 5 different libraries for single spell.

So to get to the point you should make that timer script optional so when map maker import it it run faster if he has linked library or normally if he don't.
Private hashtable, 2 methods, maybe 3 and few struct "globals" will work just fine.

Think about it.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Exactly my point, when I look at the script I asked myself this : Isn't this going to take longer than a normal timer to be executed (not to mention how long is TimerTools).

But you see one told me to use that library so I could get better spell.

He told me this "this spell is 4/5 until you use timer library" and after all its the user rating that matters because they are the one who are going to use it.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
Approved spell is already good enough, 4/5 5/5 rated by spell mod (it's awesome) just show to people how useful/unique or editable it can be.

Director's cut usually go to systems or spells that changed wc3 modding history.

Agree. But I will take this opportunity to improve and learn more vJASS, who knows may be I could later use this in a new system or a spell.

I don't know how this guy Nestharus is thinking but he is very smart and brilliant.
 
Level 16
Joined
Mar 3, 2006
Messages
1,564
There is no bug in the script but the first cast works fine and the others are instant; meaning once you cast you don't see the meteor falling its just a flash and the effect is done. What wrong did I do ?


JASS:
// Meteor Swarm Version 1.13

scope MS 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_dat
        private constant integer INT_MIN    = 1                 // Minimum Interval Between Meteors Threshold
        private constant integer INT_MAX    = 10                // 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    SPEED      = 0.01
        private constant real    STEP       = 64.00
        private constant real    CHECK_DIST = 32.00
        private boolean START_SHAKING = false

        private constant integer HT_OFFSET = 4096
        private hashtable mht  = InitHashtable()                 // Meteor Hashtable
        //private constant group damage_group = CreateGroup()
        private constant timer   tim         = CreateTimer()
        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 * I2R(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    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 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 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 = thistype.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

    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
 
Status
Not open for further replies.
Top