1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.
    Dismiss Notice
  3. A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!
    Dismiss Notice
  4. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Burst Laser v3.1

Submitted by Spellbound
This bundle is marked as approved. It works and satisfies the submission rules.
[​IMG]


Version History
v3.1 dummies no longer stored in unit groups but in a hashtable.
v3.0 Major rework. System was completely broken and was leaking pretty badly. Rewrote half the library and now does not use ZLibrary anymore. Limit on number of lasers removed. Burst Lasers are now assigned as weapon. Setup takes a bit longer, but now every unit can have multiple laser weapons that can be modified independently. Some variable names altered.
v2.0 - rewrote entire system to use structs instead. Arrays were bugging out.
v1.1 - Fixed a small issue with the angle of the launch coordinates bugging out on subsequent lasers in a set when the target was moving.
v1.0 - Release


Code (vJASS):


    Burst Laser v3.1 by Spellbound
        Special thanks to
        Aniki for introducing me to structs,
        ADG for helping, big time, to understant said structs,
        and IcemanBo for being very patient with debugging this system like a pro.
     
    Burst Laser is an attempt to expand the concept of lightning attacks, giving you the ability to customise it to have multiple volleys of 'lasers' with a launch and impact effect.
    While this system was initially meant to be exclusively a custom attack system, I was given the idea to make it more open to other possibilities like spells, and as such I rewrote
    the whole thing to run with BurstLaser.create(), which can be used for spells. See USAGE - ADVANCED below and check Arcane Lasers for more information.
 
    http://www.hiveworkshop.com/threads/burst-laser-v1-1.290007/
 
        REQUIREMENTS
 
    Okay, so this I think is the biggest problem with the system because it has a lot of requirements. The setup can be slightly daunting, but most of the fields are optional. Just
    read the descriptions and it should all be smooth sailing.
 
    For starters, this is vJASS, so WEX is your first requirement.
    https://www.hiveworkshop.com/threads/sharpcraft-world-editor-extended-bundle.292127/
 
    - ArmorUtils -------------- by Rising_Dusk      http://www.wc3c.net/showthread.php?t=105849
        - Logarithm ----------- by Vexorian         http://www.wc3c.net/showthread.php?t=101346
    - TimerUtils -------------- by Vexorian         http://www.wc3c.net/showthread.php?t=101322
    - GetUnitCollision -------- by Nestharus        http://www.thehelper.net/threads/getunitcollision.153722/
        - Table --------------- by Bribe            http://www.hiveworkshop.com/threads/snippet-new-table.188084/
    - DummyRecylcer ----------- by Flux             http://www.hiveworkshop.com/threads/dummy-recycler-v1-25.277659/
        - [opt] WorldBounds --- by Nestharus        https://github.com/nestharus/JASS/blob/master/jass/Systems/WorldBounds/script.j
    - Damage Engine ----------- by Bribe            http://www.hiveworkshop.com/threads/gui-damage-engine-v3-6-0-1.201016/
 
    Pick ONE of the follwing two. The both do the same thing, but Unit Event has some neat added functionality that you can use, like detecting unit transformation, etc.
    They are required for Damage Engine to work.
    - GUI Unit Indexer -------- by Bribe            http://www.hiveworkshop.com/threads/gui-unit-indexer-1-2-0-2.197329/
    - GUI Unit Event ---------- by Bribe            http://www.hiveworkshop.com/threads/gui-unit-event-v2-2-1-0.201641/#resource-45995
 
 
        INSTALLATION
    * Make sure your editor can automatically generate variables. File > Preferences, under General, tick 'Automatically create unknown variables while pasting trigger data'.
    * Go to the Object Editor and under Abilities, copy the Cheat Death, Detect Spell, and GetUnitArmorLifeBonus spells. If you're using Unit Event instead of Unit Indexer,
      copy the other two abilities as well. Make your life easy and set their Ids to DEa1, DEa2, lif&, UErd (Removal Detect) and UEtd (Transform Detect) respectively.
    * Copy over the Requirements folder.
    * Copy over the Burst Laser folder.
 
 
        USAGE - BASIC
    BurstLaser works by assigning weapons to units. For starters, in the Object editor, the missile art of the unit must be none and the projectile speed set to the maximum. Then,
    a BurstLaser weapon must be assigned to it. This can be done on indexing, like I have in this demo map, or in any other circumstance. For example, you are making an RPG and you
    wish for your hero to obtain a new weapon. He/She/It picks up a gun, and now they can attack using BurstLaser!
 
    Before you assign a weapon, however, you must set it up for. In the BurstLaser Setup Arsenal trigger, you create weapons that have a specific number attachmed to the. This way,
    when you assign a weapon, you need only referrence that number and, voila, those parameters are copied over to your unit.
 
    The last thing to know about assigning weapons is that your unit can have multiple BurstLaser weapons. This way, you can assign multiple ones and the moment it needs to be fired,
    you need only point to the weapon slot and that specific weapon will be used. Chech USUAGE - ADVANCED to see how it is done.
 
    call BLAssignWeapon.create(the unit, the weapon slot, the arsenal number)
 
    If you desire to have your lasers launch from something other than your attacker, you can make use of the BL_LaserSentry[Index] variable. If that value is not null, the unit
    stored in that variable will act as the source of the lasers. All usual modifiers like height offset, distance offset, angle offset, launch scatter, etc apply normally. See
    the Spellbreaker for an example. SentryCreate onIndex creates the sentry and Sentry Orbit makes it go around the Spellbreaker.
 
 
        USAGE - ADVANCED
    The trigger BL Module onAttack uses Bribe'
s Damage Engine' to detect when a unit has inflicted damage and then fires a BurstLaser weapon. This is done by calling:
 
    call BurstLaser.create(unit Source, unit Target, unit Sentry [optional], integer Instance Index, integer Weapon Number, real The Damage Inflicted)
 
 
        KNOWN BUGS
    • call BurstLaser.create() on spell effect event eventually stops working. The more lasers fired per set, the fewer casts are allowed. When it ceases to work, all BurstLaser
    weapons on that one unit stops working. I haven'
t the faintest idea why something so bizarre would happen at all.
 
 
        EVENTS
    You also have access to events through "Game - BL_EVENT becomes Equal to 1.00" or "call TriggerRegisterVariableEvent( YourTrigger, "udg_BL_EVENT", EQUAL, 1.00 )"
    These variables are meant to be readonly, so DO NOT SET THEM TO ANYTHING.
 
    When udg_BL_EVENT = 1.00 -------- A Laser set starts. This triggers on the very first Laser in a set.
    When udg_BL_EVENT = 2.00 -------- Every time a laser in a set fires.
    When udg_BL_EVENT = 3.00 -------- When the last Laser fires.
    When udg_BL_EVENT = 4.00 -------- When the Laser set instance ends.
 
        The values you can use following an EVENT are:
     
        udg_BL_EVENT_Source
        udg_BL_EVENT_Target
        udg_BL_EVENT_Sentry
     
        udg_BL_EVENT_Launch_x
        udg_BL_EVENT_Launch_y
        udg_BL_EVENT_Launch_z
     
        udg_BL_EVENT_Impact_x
        udg_BL_EVENT_Impact_y
        udg_BL_EVENT_Impact_z
     
        udg_BL_EVENT_DummyLaunch    - this give you access to the launch dummy, should you want to add effects to it and such.
        udg_BL_EVENT_DummyImpact    - same as above
        udg_BL_EVENT_DummyArea      - this one is ONLY available if your impact and area scales are different. Otherwise simply refer to udg_BL_EVENT_DummyImpact
                                      since they share the exact same co-ordinates.
     
        udg_BL_EVENT_Damage - this is affected by LaserDivideDamage[ID]. If true, udg_BL_EVENT_Damage == your base damage / number of lasers in a set.

        I assume the use of those variables are self-explanatory. Setting them to anything does nothing.
 
 
        LIGHTNING EFFECTS
    The lightning effect I use in this demo map are of my own making. You can download there here: https://www.hiveworkshop.com/threads/lightning-effect-pack.290740/


  • Code (vJASS):
    library BurstLaser requires BL, GetUnitCollision, TimerUtils, ArmorUtils, DummyRecycler

        globals
            private hashtable hash = InitHashtable()
            private hashtable lightningStorage = InitHashtable()
            private hashtable dummyStorage = InitHashtable()
            private trigger interrupt_trigger = CreateTrigger()
            private group DamageGroup = CreateGroup()
            private location zLoc = Location(0., 0.)
           
            private constant real MAX_COLLISION_SIZE = 196.
            private constant real COLLISION_PERCENTAGE = .8
            private constant real TIMEOUT = .03125
            private constant real DEATH_TIME = 1.
            private constant integer COUNTER_KEY = -1
           
            //LaserInstance-specific vars
            private integer MaxLasers = 0
            private integer array LaserIndex
            private lightning array Laser
            private timer LaserTimer = CreateTimer()
        endglobals
       
        struct LaserInstance
       
            real xLaunch
            real yLaunch
            real zLaunch
            real xImpact
            real yImpact
            real zImpact
            real xLaunchUpdate
            real yLaunchUpdate
            real zLaunchUpdate
            real xImpactUpdate
            real yImpactUpdate
            real zImpactUpdate
            real lifespan
            real red
            real blue
            real green
            real alpha
            real lockAngle
            real dot
            real dotReset
            real fadeAmount
            unit source
            unit target
            unit sentry
            boolean updateLaunchPoint
            boolean updateImpactPoint
            integer laserSetInstance
            integer laserSlot
           
           
            private method destroy takes nothing returns nothing
                set this.source = null
                set this.target = null
                set this.sentry = null
                set this.laserSetInstance = 0
                call this.deallocate()
            endmethod
           
           
            private static method Timer takes nothing returns nothing
                local integer i = 0
                local integer storageIndex
                local integer iRec
                local thistype this
                local thistype LaserSet
                local BurstLaser BLSet
               
                local real launch_x
                local real launch_y
                local real launch_z
               
                local real impact_x
                local real impact_y
                local real impact_z
               
                local unit mark
                local boolean updateLaser = false
                local boolean destroySet
               
                loop
               
                    set i = i + 1
                    exitwhen i > MaxLasers
                    set this = LaserIndex[i]
                    set BLSet = this.laserSetInstance
                   
                    if not BLSet.destructionPhase then
                   
                        if this.updateLaunchPoint then
                            if this.sentry == null then
                                set mark = this.source
                            else
                                set mark = this.sentry
                            endif
                            set launch_x = GetUnitX(mark) - this.xLaunchUpdate
                            set launch_y = GetUnitY(mark) - this.yLaunchUpdate
                            call MoveLocation(zLoc, launch_x, launch_y)
                            set launch_z = (GetUnitFlyHeight(mark) + GetLocationZ(zLoc)) - this.zLaunchUpdate
                            set updateLaser = true
                        else
                            set launch_x = this.xLaunch
                            set launch_y = this.yLaunch
                            set launch_z = this.zLaunch
                        endif
                       
                        if this.updateImpactPoint then
                            set impact_x = GetUnitX(this.target) - this.xImpactUpdate
                            set impact_y = GetUnitY(this.target) - this.yImpactUpdate
                            call MoveLocation(zLoc, impact_x, impact_y)
                            set impact_z = (GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)) - this.zImpactUpdate
                            set updateLaser = true
                        else
                            set impact_x = this.xImpact
                            set impact_y = this.yImpact
                            set impact_z = this.zImpact
                        endif
                       
                        if updateLaser then
                            call MoveLightningEx(Laser[i], true, launch_x, launch_y, launch_z, impact_x, impact_y, impact_z)
                        endif
                       
                        if this.lifespan > 0. then
                            set this.lifespan = this.lifespan - TIMEOUT
                        else
                            set this.alpha = this.alpha - this.fadeAmount
                            if this.alpha <= 0. then
                                call DestroyLightning(Laser[i])
                                set Laser[i] = null
                                call this.destroy()
                                set iRec = i
                                loop
                                    set Laser[iRec] = Laser[iRec + 1]
                                    set LaserIndex[iRec] = LaserIndex[iRec + 1]
                                    set iRec = iRec + 1
                                    exitwhen iRec > MaxLasers
                                endloop
                                call RemoveSavedHandle(lightningStorage, BLSet, this.laserSlot)
                                set MaxLasers = MaxLasers - 1
                               
                                //Check if all lasers from the set have been fired and have been destroyed
                                if BLSet.laser_count == BLSet.laser_num then
                                    set destroySet = true
                                    set storageIndex = 0
                                    loop
                                        set storageIndex = storageIndex + 1
                                        exitwhen storageIndex > BLSet.laser_num
                                        if LoadLightningHandle(lightningStorage, BLSet, storageIndex) != null then
                                           set destroySet = false
                                        endif
                                    endloop
                                    if destroySet then
                                        set BLSet.destructionPhase = true
                                        if BLSet.interval <= 0. then
                                            set BLSet.interval = DEATH_TIME
                                        endif
                                    endif
                                endif
                            else
                                call SetLightningColor(Laser[i], this.red, this.green, this.blue, this.alpha)
                            endif
                        endif
                       
                        //Lingering Laser damage will only fire if greater than 0.
                        if this.dotReset > 0. then
                            set this.dot = this.dot - TIMEOUT
                            if this.dot <= 0. then
                                set this.dot = this.dotReset
                                call BurstLaser.ApplySfxAndDamage(launch_x, launch_y, launch_z, impact_x, impact_y, impact_z, BLSet)
                            endif
                        endif
                       
                    endif
                   
                endloop
               
                if MaxLasers < 1 then
                    call PauseTimer(LaserTimer)
                endif
            endmethod
           
           
            static method create takes real launch_x, real launch_y, real launch_z, real impact_x, real impact_y, real impact_z, BurstLaser BLSet returns thistype
           
                local real x
                local real y
                local unit mark
                local integer i
                local thistype this = allocate()
               
                set this.laserSetInstance = BLSet
               
                set this.source = BLSet.source
                set this.target = BLSet.target
                set this.sentry = BLSet.sentry
               
                set this.xLaunch = launch_x
                set this.yLaunch = launch_y
                set this.zLaunch = launch_z
               
                set this.xImpact = impact_x
                set this.yImpact = impact_y
                set this.zImpact = impact_z
               
                set this.updateLaunchPoint = BLSet.update_launch
                if this.updateLaunchPoint then
                    if this.sentry == null then
                        set mark = this.source
                    else
                        set mark = this.sentry
                    endif
                    set x = GetUnitX(this.sentry)
                    set y = GetUnitY(this.sentry)
                    set this.xLaunchUpdate = x - launch_x
                    set this.yLaunchUpdate = y - launch_y
                    call MoveLocation(zLoc, launch_x, launch_y)
                    set this.zLaunchUpdate = (GetUnitFlyHeight(mark) + GetLocationZ(zLoc)) - launch_z
                    set mark = null
                endif
               
                set this.updateImpactPoint = BLSet.update_impact
                if this.updateImpactPoint then
                    set x = GetUnitX(this.target)
                    set y = GetUnitY(this.target)
                    set this.xImpactUpdate = x - impact_x
                    set this.yImpactUpdate = y - impact_y
                    call MoveLocation(zLoc, impact_x, impact_y)
                    set this.zImpactUpdate = (GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)) - impact_z
                endif
               
                set this.red            = BLSet.red
                set this.blue           = BLSet.blue
                set this.green          = BLSet.green
                set this.alpha          = BLSet.alpha
                set this.fadeAmount     = BLSet.alpha / BLSet.fade_time * TIMEOUT
                set this.dot            = BLSet.laser_dot
                set this.dotReset       = BLSet.laser_dot
                set this.lifespan       = BLSet.duration
               
                set MaxLasers = MaxLasers + 1
                if MaxLasers == 1 then
                    call TimerStart(LaserTimer, TIMEOUT, true, function thistype.Timer)
                endif
                set Laser[MaxLasers] = AddLightningEx(BLSet.laser_string, true, launch_x, launch_y, launch_z, impact_x, impact_y, impact_z)
                call SetLightningColor(Laser[MaxLasers], this.red, this.green, this.blue, this.alpha)
                set LaserIndex[MaxLasers] = this
               
                call SaveLightningHandle(lightningStorage, BLSet, BLSet.laser_count, Laser[MaxLasers])
                set this.laserSlot = BLSet.laser_count
               
                return this
            endmethod
           
           
        endstruct
       
       
       
        struct BurstLaser
            unit source
            unit target
            unit sentry
           
            unit dummy_launch
            unit dummy_impact
            unit dummy_area
           
            player owner
            integer id
           
            real damage
           
            real red
            real blue
            real green
            real alpha
            real fade_amount
            real fade_time
           
            real duration
            integer count
            real interval
           
            real lock_angle
            real lock_distance
            real lock_height
           
            boolean fading
            boolean grounded
            boolean hit_all
            boolean friendly_fire
            boolean uninterruptible
           
            real launch_offset
            real launch_height
            real launch_angle
            real launch_x
            real launch_y
            real launch_z
            real launch_scatter_x
            real launch_scatter_y
            real launch_scatter_z
            real launch_scale
            real impact_x
            real impact_y
            real impact_z
            real impact_scatter_x
            real impact_scatter_y
            real impact_scatter_z
            real impact_scale
            real area_scale
            real lock_on_angle
           
            real target_coll
           
            real laser_angle
            boolean update_source_loc   //If true, subsequent lasers in a volley will launch from the initial laser's launch point and not relative to the source.
            boolean update_target_loc   //If false, Lasers in a set will strike the same point instead of updating their impact location to follow a moving target.
            boolean launch_from_target  //If true, the launch point will be relative to the target's location, not the source.
            boolean update_launch       //if true then Lasers will update their launch location relative to the source's.
            boolean update_impact       //if true then Lasers will update their impact location relative to the target's.
            boolean fixed_impact        //If true lasers will impact a fixed distance from the source. range_start and range_per_laser will modify this value.
            real range_start            //Adds or subtracts a certain amount of distance from a laser's impact location.
            real range_per_laser        //Adds or substracts distance per Laser in a set.
            real laser_dot              //the laser's sfx and damage will repeat every laser_dot seconds until the laser ends.
           
            string laser_string
            string launch_string
            string impact_string
            string area_string
           
            real aoe
            attacktype attack_type
            integer damage_type
           
            real initial_x
            real initial_y
           
            timer tmr_volley
            integer dummy_count
           
            boolean reduce_launch_overhead
            boolean reduce_impact_overhead
           
            integer laser_num
            integer laser_count
           
            real deathTime
            boolean destructionPhase
            integer hashIndex
           
            static code timer_volley_handler
            static code interruption_event_handler
           
           
            method destroy takes nothing returns nothing
                local integer id = GetUnitUserData(this.source)
                local integer i = 0
                local unit dummy
               
                //EVENT
                set udg_BL_EVENT_Source = this.source
                set udg_BL_EVENT_Target = this.target
                set udg_BL_EVENT_Sentry = this.sentry
                set udg_BL_EVENT_Launch_x = launch_x
                set udg_BL_EVENT_Launch_y = launch_y
                set udg_BL_EVENT_Launch_z = launch_z
                set udg_BL_EVENT_Impact_x = impact_x
                set udg_BL_EVENT_Impact_y = impact_y
                set udg_BL_EVENT_Impact_z = impact_z
                set udg_BL_EVENT_Damage = this.damage
                set udg_BL_EVENT = 4.
                set udg_BL_EVENT_Source = null
                set udg_BL_EVENT_Target = null
                set udg_BL_EVENT_Sentry = null
                set udg_BL_EVENT_Launch_x = 0.
                set udg_BL_EVENT_Launch_y = 0.
                set udg_BL_EVENT_Launch_z = 0.
                set udg_BL_EVENT_Impact_x = 0.
                set udg_BL_EVENT_Impact_y = 0.
                set udg_BL_EVENT_Impact_z = 0.
                set udg_BL_EVENT_Damage = 0.
                set udg_BL_EVENT = 0.
                //END EVENT
               
                set this.owner = null
                set this.source = null
                set this.target = null
                set this.sentry = null
               
                if this.dummy_launch != null then
                    set this.dummy_launch = null
                endif
                if this.dummy_impact != null then
                    set this.dummy_impact = null
                endif
                if this.dummy_area != null then
                    set this.dummy_area = null
                endif
               
                loop
                    set i = i + 1
                    exitwhen i > this.dummy_count
                    call RecycleDummy(LoadUnitHandle(dummyStorage, this, i))
                endloop
                call FlushChildHashtable(dummyStorage, this)
               
                call ReleaseTimer(tmr_volley)
                call RemoveSavedInteger(hash, id, this.hashIndex)
               
                call this.deallocate()
            endmethod
           
           
            static method ApplySfxAndDamage takes real launch_x, real launch_y, real launch_z, real impact_x, real impact_y, real impact_z, BurstLaser this returns nothing
                local unit u = null
               
                //Launch
                if this.reduce_launch_overhead then
                    if this.launch_string != null then
                        if this.dummy_launch == null then
                            set this.dummy_launch = GetRecycledDummy(launch_x, launch_y, launch_z, 0.)
                            set this.dummy_count = this.dummy_count + 1
                            call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_launch)
                        else
                            call SetUnitX(this.dummy_launch, launch_x)
                            call SetUnitY(this.dummy_launch, launch_y)
                            call MoveLocation(zLoc, launch_x, launch_y)
                            call SetUnitFlyHeight(this.dummy_launch, launch_z + GetLocationZ(zLoc), 0.)
                        endif
                        set udg_BL_EVENT_Dummy_Launch = this.dummy_launch
                        call SetUnitScale(this.dummy_launch, this.launch_scale, 0., 0.)
                        call DestroyEffect(AddSpecialEffectTarget(this.launch_string, this.dummy_launch, "origin"))
                    endif
                else
                    if this.launch_string != null then
                        set this.dummy_launch = GetRecycledDummy(launch_x, launch_y, launch_z, 0.)
                        call SetUnitScale(this.dummy_launch, this.launch_scale, 0., 0.)
                        call DestroyEffect(AddSpecialEffectTarget(this.launch_string, this.dummy_launch, "origin"))
                        set this.dummy_count = this.dummy_count + 1
                        call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_launch)
                        set udg_BL_EVENT_Dummy_Launch = this.dummy_launch
                        set this.dummy_launch = null
                    endif
                endif
               
                //Impact and Area
                if this.reduce_impact_overhead then
                    call MoveLocation(zLoc, impact_x, impact_y)
                    if this.impact_string != null then
                        if this.dummy_impact == null then
                            set this.dummy_impact = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                            set this.dummy_count = this.dummy_count + 1
                            call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_impact)
                        else
                            call SetUnitX(this.dummy_impact, impact_x)
                            call SetUnitY(this.dummy_impact, impact_y)
                            call SetUnitFlyHeight(this.dummy_impact, impact_z + GetLocationZ(zLoc), 0.)
                        endif
                        set udg_BL_EVENT_Dummy_Impact = this.dummy_impact
                        call SetUnitScale(this.dummy_impact, this.impact_scale, 0., 0.)
                        call DestroyEffect(AddSpecialEffectTarget(this.impact_string, this.dummy_impact, "origin"))
                    endif
                    if this.area_string != null then
                        if this.area_scale != this.impact_scale then
                            if this.dummy_area == null then
                                set this.dummy_area = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                                set this.dummy_count = this.dummy_count + 1
                            call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_area)
                            else
                                call SetUnitX(this.dummy_area, impact_x)
                                call SetUnitY(this.dummy_area, impact_y)
                                call SetUnitFlyHeight(this.dummy_area, impact_z + GetLocationZ(zLoc), 0.)
                            endif
                            set udg_BL_EVENT_Dummy_Area = this.dummy_area
                            call SetUnitScale(this.dummy_impact, this.area_scale, 0., 0.)
                            call DestroyEffect(AddSpecialEffectTarget(this.area_string, this.dummy_area, "origin"))
                        else
                            if this.dummy_impact == null then
                                set this.dummy_impact = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                                set this.dummy_count = this.dummy_count + 1
                                call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_impact)
                            else
                                call SetUnitX(this.dummy_impact, impact_x)
                                call SetUnitY(this.dummy_impact, impact_y)
                                call SetUnitFlyHeight(this.dummy_impact, impact_z + GetLocationZ(zLoc), 0.)
                            endif
                            set udg_BL_EVENT_Dummy_Impact = this.dummy_impact
                            call SetUnitScale(this.dummy_impact, this.impact_scale, 0., 0.)
                            call DestroyEffect(AddSpecialEffectTarget(this.area_string, this.dummy_impact, "origin"))
                        endif
                    endif
                else
                    if this.impact_string != null then
                        set this.dummy_impact = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                        set this.dummy_count = this.dummy_count + 1
                        call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_impact)
                        call DestroyEffect(AddSpecialEffectTarget(this.impact_string, this.dummy_impact, "origin"))
                        set udg_BL_EVENT_Dummy_Impact = this.dummy_impact
                    endif
                    if this.area_string != null then
                        if this.area_scale != this.impact_scale then
                            set this.dummy_area = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                            set this.dummy_count = this.dummy_count + 1
                            call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_area)
                            set udg_BL_EVENT_Dummy_Area = this.dummy_area
                            call SetUnitScale(this.dummy_area, this.area_scale, 0., 0.)
                            call DestroyEffect(AddSpecialEffectTarget(this.area_string, this.dummy_area, "origin"))
                            set this.dummy_area = null
                        else
                            if this.dummy_impact == null then
                                set this.dummy_impact = GetRecycledDummy(impact_x, impact_y, impact_z, 0.)
                                set this.dummy_count = this.dummy_count + 1
                                call SaveUnitHandle(dummyStorage, this, this.dummy_count, this.dummy_impact)
                            endif
                            call SetUnitScale(this.dummy_impact, this.impact_scale, 0., 0.)
                            call DestroyEffect(AddSpecialEffectTarget(this.area_string, this.dummy_impact, "origin"))
                        endif
                        if this.dummy_impact != null then
                            set this.dummy_impact = null
                        endif
                    endif
                endif
               
                //Damage
                if this.aoe > 0. then
                    call GroupEnumUnitsInRange(DamageGroup, impact_x, impact_y, this.aoe + MAX_COLLISION_SIZE, null)
                    loop
                        set u = FirstOfGroup(DamageGroup)
                        call GroupRemoveUnit(DamageGroup, u)
                        exitwhen u == null
                        if IsUnitInRangeXY(u, impact_x, impact_y, this.aoe) and UnitAlive(u) and /*
                        [Friendly Fire] */
    ( (this.friendly_fire and (IsUnitEnemy(u, this.owner) or IsUnitAlly(u, this.owner))) or /*
                        [Enemy]         */
     ( (not this.friendly_fire and IsUnitEnemy(u, this.owner)) or  /*
                        [Friendly]      */
       (not this.friendly_fire and IsUnitAlly(u, this.owner) and u == this.target) ) ) and /*
                        [Magic Immune]  */
    ( (this.attack_type == ATTACK_TYPE_MAGIC and not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE)) or /*
                        [Ethereal]      */
     (this.attack_type != ATTACK_TYPE_MAGIC and not IsUnitType(u, UNIT_TYPE_ETHEREAL)) ) then
                            if not this.hit_all and /*
                            [Ground]    */
    ( (not IsUnitType(u, UNIT_TYPE_FLYING) and not IsUnitType(this.target, UNIT_TYPE_FLYING)) or /*
                            [Flying]    */
    (IsUnitType(u, UNIT_TYPE_FLYING) and IsUnitType(this.target, UNIT_TYPE_FLYING)) ) then
                                set udg_NextDamageType = this.damage_type
                                call UnitDamageTarget(this.source, u, this.damage, true, true, this.attack_type, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                                call TriggerEvaluate(udg_ClearDamageEvent)
                            elseif this.hit_all then
                                set udg_NextDamageType = this.damage_type
                                call UnitDamageTarget(this.source, u, this.damage, true, true, this.attack_type, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                                call TriggerEvaluate(udg_ClearDamageEvent)
                            endif
                        endif
                    endloop
                else
                    set udg_NextDamageType = this.damage_type
                    call UnitDamageTarget(this.source, this.target, this.damage, true, true, this.attack_type, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS)
                    call TriggerEvaluate(udg_ClearDamageEvent)
                endif
            endmethod
           
           
           
            private static method Interruption_Event takes nothing returns nothing
                local integer id = GetUnitUserData(GetTriggerUnit())
                local integer counter = LoadInteger(hash, id, COUNTER_KEY)
                //local integer i = 0
                local thistype this
                loop
                    exitwhen counter < 1
                    set this = LoadInteger(hash, id, counter)
                    if not this.uninterruptible and this.interval > 0. then
                        /*call PauseTimer(this.tmr_volley)
                        loop
                            exitwhen i >= this.laser_num
                            set this.lifespan <= 0.
                            set this.alpha = true
                            set i = i + 1
                        endloop*/

                        set this.laser_num = this.laser_count //This is set in case of interruption
                    endif
                    set counter = counter - 1
                endloop
                call SaveInteger(hash, id, COUNTER_KEY, 0)
            endmethod
           
           
           
            private static method BurstLaser_Volley takes nothing returns nothing
                local thistype this = GetTimerData(GetExpiredTimer())
               
                local integer id = GetUnitUserData(this.source)
                local integer counter  = LoadInteger(hash, id, COUNTER_KEY)
               
                local real source_x = GetUnitX(this.source)
                local real source_y = GetUnitY(this.source)
                local real target_x = GetUnitX(this.target)
                local real target_y = GetUnitY(this.target)
                local real angle
               
                local real xLaunch
                local real yLaunch
                local real zLaunch
               
                local real launch_scatter_x
                local real launch_scatter_y
                local real launch_scatter_z
               
                local real xImpact
                local real yImpact
                local real zImpact
               
                local boolean destroySet
                local integer i
               
                if not this.destructionPhase then
               
                    if this.laser_count < this.laser_num and UnitAlive(this.source) then //If there are still lasers in the set to be fired
                       
                        if this.sentry == null then
                            if this.launch_from_target then
                                if this.update_source_loc then
                                    set angle = this.lock_on_angle//Atan2(this.launch_y - target_y, this.launch_x - target_x) + this.launch_angle
                                    set xLaunch = target_x + Cos(angle) * this.launch_offset
                                    set yLaunch = target_y + Sin(angle) * this.launch_offset
                                    call MoveLocation(zLoc, xLaunch, yLaunch)
                                    set zLaunch = GetUnitFlyHeight(this.source) + this.launch_height + GetLocationZ(zLoc)
                                else
                                    set xLaunch = this.launch_x
                                    set yLaunch = this.launch_y
                                    set zLaunch = this.launch_z
                                endif
                            else
                                if this.update_source_loc then
                                    set angle = Atan2(target_y - source_y, target_x - source_x) + this.launch_angle
                                    set xLaunch = source_x + Cos(angle + this.launch_angle) * this.launch_offset
                                    set yLaunch = source_y + Sin(angle + this.launch_angle) * this.launch_offset
                                    call MoveLocation(zLoc, xLaunch, yLaunch)
                                    set zLaunch = GetUnitFlyHeight(this.source) + this.launch_height + GetLocationZ(zLoc)
                                else
                                    set xLaunch = this.launch_x
                                    set yLaunch = this.launch_y
                                    set zLaunch = this.launch_z
                                endif
                            endif
                        else
                            if this.update_source_loc then
                                set angle = Atan2(target_y - source_y, target_x - source_x) + this.launch_angle
                                set xLaunch = GetUnitX(this.sentry) + Cos(angle + this.launch_angle) * this.launch_offset
                                set yLaunch = GetUnitY(this.sentry) + Sin(angle + this.launch_angle) * this.launch_offset
                                call MoveLocation(zLoc, xLaunch, yLaunch)
                                set zLaunch = GetUnitFlyHeight(this.sentry) + this.launch_height + GetLocationZ(zLoc)
                            else
                                set xLaunch = this.launch_x
                                set yLaunch = this.launch_y
                                set zLaunch = this.launch_z
                            endif
                        endif
                        set launch_scatter_x = GetRandomReal(this.launch_scatter_x*-1, this.launch_scatter_x)
                        set launch_scatter_y = GetRandomReal(this.launch_scatter_y*-1, this.launch_scatter_y)
                        set launch_scatter_z = GetRandomReal(this.launch_scatter_z*-1, this.launch_scatter_z)
                        set xLaunch = xLaunch + launch_scatter_x
                        set yLaunch = yLaunch + launch_scatter_y
                        set zLaunch = zLaunch + launch_scatter_z
                       
                        if this.fixed_impact then //if true, start from the source's point
                            if this.update_target_loc then
                                if this.range_per_laser != 0. then
                                    set angle = Atan2(target_y - yLaunch, target_x - xLaunch)
                                    set this.impact_x = this.impact_x + Cos(angle) * this.range_per_laser
                                    set this.impact_y = this.impact_y + Sin(angle) * this.range_per_laser
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                else
                                    set angle = Atan2(yLaunch - target_y, xLaunch - target_x)
                                    set this.impact_x = target_x + Cos(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set this.impact_y = target_y + Sin(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                endif
                                call MoveLocation(zLoc, xImpact, yImpact)
                                if this.grounded then
                                    set this.impact_z = 0. + GetLocationZ(zLoc)
                                    set zImpact = 0. + GetLocationZ(zLoc)
                                else
                                    set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                                    set zImpact = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                                endif
                            else
                                if this.range_per_laser != 0. then
                                    set this.impact_x = this.impact_x + Cos(this.laser_angle) * this.range_per_laser
                                    set this.impact_y = this.impact_y + Sin(this.laser_angle) * this.range_per_laser
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                else
                                    set angle = Atan2(yLaunch - this.initial_y, xLaunch - this.initial_x)
                                    set this.impact_x = target_x + Cos(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set this.impact_y = target_y + Sin(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                    call MoveLocation(zLoc, impact_x, yImpact)
                                    set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                                endif
                                call MoveLocation(zLoc, xImpact, yImpact)
                                if this.grounded then
                                    set this.impact_z = 0. + GetLocationZ(zLoc)
                                    set zImpact = 0. + GetLocationZ(zLoc)
                                else
                                    set zImpact = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                                endif
                            endif
                        else
                            if this.update_target_loc then
                                if this.range_per_laser != 0. then
                                    set angle = Atan2(target_y - yLaunch, target_x - xLaunch)
                                    set this.impact_x = this.impact_x + Cos(angle) * this.range_per_laser
                                    set this.impact_y = this.impact_y + Sin(angle) * this.range_per_laser
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                else
                                    set angle = Atan2(yLaunch - target_y, xLaunch - target_x)
                                    set this.impact_x = target_x + Cos(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set this.impact_y = target_y + Sin(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                endif
                                call MoveLocation(zLoc, xImpact, yImpact)
                                if this.grounded then
                                    set this.impact_z = 0. + GetLocationZ(zLoc)
                                    set zImpact = 0. + GetLocationZ(zLoc)
                                else
                                    set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                                    set zImpact = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                                endif
                            else
                                if this.range_per_laser != 0. then
                                    set this.impact_x = this.impact_x + Cos(this.laser_angle) * this.range_per_laser
                                    set this.impact_y = this.impact_y + Sin(this.laser_angle) * this.range_per_laser
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                else
                                    set angle = Atan2(yLaunch - this.initial_y, xLaunch - this.initial_x)
                                    set this.impact_x = target_x + Cos(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set this.impact_y = target_y + Sin(angle) * (this.target_coll * COLLISION_PERCENTAGE)
                                    set xImpact = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                                    set yImpact = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                                    call MoveLocation(zLoc, xImpact, yImpact)
                                    set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                                endif
                                call MoveLocation(zLoc, xImpact, yImpact)
                                if this.grounded then
                                    set this.impact_z = 0. + GetLocationZ(zLoc)
                                    set zImpact = 0. + GetLocationZ(zLoc)
                                else
                                    set zImpact = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                                endif
                            endif
                        endif
                       
                        call BurstLaser.ApplySfxAndDamage(xLaunch, yLaunch, zLaunch, xImpact, yImpact, zImpact, this)
                       
                        //Laser
                        if this.laser_string != null then
                            call LaserInstance.create(xLaunch, yLaunch, zLaunch, xImpact, yImpact, zImpact, this)
                        endif
                        set this.laser_count = this.laser_count + 1
                       
                        //EVENT
                        set udg_BL_EVENT_Source = this.source
                        set udg_BL_EVENT_Target = this.target
                        set udg_BL_EVENT_Sentry = this.sentry
                        set udg_BL_EVENT_Launch_x = xLaunch
                        set udg_BL_EVENT_Launch_y = yLaunch
                        set udg_BL_EVENT_Launch_z = zLaunch
                        set udg_BL_EVENT_Impact_x = xImpact
                        set udg_BL_EVENT_Impact_y = yImpact
                        set udg_BL_EVENT_Impact_z = zImpact
                        set udg_BL_EVENT_Damage = this.damage
                        set udg_BL_EVENT = 2.
                        set udg_BL_EVENT_Source = null
                        set udg_BL_EVENT_Target = null
                        set udg_BL_EVENT_Sentry = null
                        set udg_BL_EVENT_Launch_x = 0.
                        set udg_BL_EVENT_Launch_y = 0.
                        set udg_BL_EVENT_Launch_z = 0.
                        set udg_BL_EVENT_Impact_x = 0.
                        set udg_BL_EVENT_Impact_y = 0.
                        set udg_BL_EVENT_Impact_z = 0.
                        set udg_BL_EVENT_Damage = 0.
                        set udg_BL_EVENT_Dummy_Launch = null
                        set udg_BL_EVENT_Dummy_Impact = null
                        set udg_BL_EVENT = 0.
                        //END EVENT
                       
                    elseif this.laser_count == this.laser_num or not UnitAlive(this.source) then
                   
                        //EVENT
                        set udg_BL_EVENT_Source = this.source
                        set udg_BL_EVENT_Target = this.target
                        set udg_BL_EVENT_Sentry = this.sentry
                        set udg_BL_EVENT_Launch_x = xLaunch
                        set udg_BL_EVENT_Launch_y = yLaunch
                        set udg_BL_EVENT_Launch_z = zLaunch
                        set udg_BL_EVENT_Impact_x = xImpact
                        set udg_BL_EVENT_Impact_y = yImpact
                        set udg_BL_EVENT_Impact_z = zImpact
                        set udg_BL_EVENT_Damage = this.damage
                        set udg_BL_EVENT = 3.
                        set udg_BL_EVENT_Source = null
                        set udg_BL_EVENT_Target = null
                        set udg_BL_EVENT_Sentry = null
                        set udg_BL_EVENT_Launch_x = 0.
                        set udg_BL_EVENT_Launch_y = 0.
                        set udg_BL_EVENT_Launch_z = 0.
                        set udg_BL_EVENT_Impact_x = 0.
                        set udg_BL_EVENT_Impact_y = 0.
                        set udg_BL_EVENT_Impact_z = 0.
                        set udg_BL_EVENT_Damage = 0.
                        set udg_BL_EVENT = 0.
                        //END EVENT
                       
                        call PauseTimer(this.tmr_volley)
                        call SaveInteger(hash, id, COUNTER_KEY, LoadInteger(hash, id, counter) - 1) // decrease counter
                       
                        set this.laser_num = this.laser_count //This is set in case of interruption.
                       
                        set i = 0
                        set destroySet = true
                        loop
                            set i = i + 1
                            exitwhen i > this.laser_num
                            if LoadLightningHandle(lightningStorage, this, i) != null then
                                set destroySet = false
                            endif
                            if destroySet then
                                //destructionPhase is used to terminate the instance slightly after
                                //the last laser in a set has been destroyed to allow special effects
                                //on dummies to play out.
                                set this.destructionPhase = true
                                if this.interval <= 0. then
                                    set this.interval = DEATH_TIME
                                endif
                            endif
                        endloop
                    endif
                else
               
                    set this.deathTime = this.deathTime - this.interval
                    if this.deathTime <= 0. then
                        call this.destroy()
                    endif
                                       
                endif
            endmethod
           
           
           
            static method create takes unit unit_source, unit unit_target, unit unit_sentry, BLAssignWeapon wpn, integer id, real damage returns thistype
           
                local thistype this = allocate()
               
                local integer idSource = GetUnitUserData(unit_source)
                local integer counter = LoadInteger(hash, idSource, COUNTER_KEY) // get instance counter
               
                local real source_x = GetUnitX(unit_source)
                local real source_y = GetUnitY(unit_source)
                local real target_x = GetUnitX(unit_target)
                local real target_y = GetUnitY(unit_target)
                local real sentry_x = 0.
                local real sentry_y = 0.
                local real startangle
               
                local real range_offset = 0.
                local real launch_spread_x = 0.
                local real launch_spread_y = 0.
                local real launch_spread_z = 0.
                local real impact_spread_x = 0.
                local real impact_spread_y = 0.
                local real impact_spread_z = 0.
               
                local integer iLoop = 0
                local unit u = null
               
                set counter = counter + 1
                call SaveInteger(hash, idSource, COUNTER_KEY, counter) // increase instance counter
                call SaveInteger(hash, idSource, counter, this) // bind instance to unit id
                set this.hashIndex = counter // so the instance knows where it's stored
               
                set this.owner = GetOwningPlayer(unit_source)
                set this.source = unit_source
                set this.target = unit_target
                set this.sentry = unit_sentry
               
                set this.target_coll = GetUnitCollision(unit_target)
               
                set this.laser_string = wpn.LaserString[id]
                set this.red = wpn.RED[id]
                set this.blue = wpn.BLUE[id]
                set this.green = wpn.GREEN[id]
                set this.alpha = wpn.ALPHA[id]
               
                set this.launch_string = wpn.LaserLaunchFX[id]
                set this.impact_string = wpn.LaserImpactFX[id]
                set this.area_string = wpn.LaserAreaFX[id]
               
                set this.launch_scale = wpn.LaserLaunchScale[id]
                set this.impact_scale = wpn.LaserImpactScale[id]
                set this.area_scale = wpn.LaserAreaScale[id]
               
                set this.duration = wpn.LaserDuration[id]
                set this.fade_time = wpn.LaserFadeTime[id]
                set this.interval = wpn.LaserInterval[id]
                set this.laser_dot = wpn.LaserDoT[id]
                set this.aoe = wpn.LaserAOE[id]
                set this.grounded = wpn.IsLaserGrounded[id]
               
                set this.launch_offset = wpn.LaserLaunchOffset[id]
                set this.launch_height = wpn.LaserLaunchHeight[id]
                set this.launch_angle = wpn.LaserLaunchAngle[id]
               
                set this.launch_scatter_x = wpn.LaserLaunchScatterX[id]
                set this.launch_scatter_y = wpn.LaserLaunchScatterY[id]
                set this.launch_scatter_z = wpn.LaserLaunchScatterZ[id]
               
                set this.impact_scatter_x = wpn.LaserImpactScatterX[id]
                set this.impact_scatter_y = wpn.LaserImpactScatterY[id]
                set this.impact_scatter_z = wpn.LaserImpactScatterZ[id]
               
                set this.update_target_loc = wpn.UpdateTargetLoc[id]
                set this.update_source_loc = wpn.UpdateSourceLoc[id]
                set this.launch_from_target = wpn.LaunchFromTargetLoc[id]
                set this.update_launch = wpn.UpdateLaunchPoint[id]
                set this.update_impact = wpn.UpdateImpactPoint[id]
                set this.fixed_impact = wpn.FixedImpactLoc[id]
               
                set this.range_start = wpn.LaserRangeStart[id]
                set this.range_per_laser = wpn.LaserRangePerLaser[id]
                set this.attack_type = wpn.LaserAttackType[id]
                set this.damage_type = wpn.LaserDamageType[id]
               
                set this.uninterruptible = wpn.Uninterruptible[id]
                set this.hit_all = wpn.LaserHitAll[id]
                set this.friendly_fire = wpn.LaserFriendlyFire[id]
                set this.reduce_launch_overhead = wpn.ReduceLaunchOverhead[id]
                set this.reduce_impact_overhead = wpn.ReduceImpactOverhead[id]
               
                set this.destructionPhase = false
                set this.deathTime = DEATH_TIME
               
                set this.laser_num = wpn.NumberOfLasers[id]
                set this.laser_count = 0
               
                if this.sentry == null then
                    if this.launch_from_target then
                        set startangle = Atan2(source_y - target_y, source_x - target_x) + this.launch_angle
                        if this.update_source_loc  then
                            set this.lock_on_angle = startangle
                        endif
                        if this.launch_offset > 0. then
                            set this.launch_x = target_x + Cos(startangle) * this.launch_offset
                            set this.launch_y = target_y + Sin(startangle) * this.launch_offset
                        else
                            set this.launch_x = target_x
                            set this.launch_y = target_y
                        endif
                        call MoveLocation(zLoc, this.launch_x, this.launch_y)
                        set this.launch_z = GetUnitFlyHeight(this.target) + this.launch_height + GetLocationZ(zLoc)
                    else
                        set startangle = Atan2(target_y - source_y, target_x - source_x) + this.launch_angle
                        if this.launch_offset > 0. then
                            set this.launch_x = source_x + Cos(startangle) * this.launch_offset
                            set this.launch_y = source_y + Sin(startangle) * this.launch_offset
                        else
                            set this.launch_x = source_x
                            set this.launch_y = source_y
                        endif
                        call MoveLocation(zLoc, this.launch_x, this.launch_y)
                        set this.launch_z = GetUnitFlyHeight(this.source) + this.launch_height + GetLocationZ(zLoc)
                    endif
                else
                    set sentry_x = GetUnitX(this.sentry)
                    set sentry_y = GetUnitY(this.sentry)
                    set startangle = Atan2(target_y - sentry_y, target_x - sentry_x) + this.launch_angle
                    if this.launch_offset > 0. then
                        set this.launch_x = sentry_x + Cos(startangle) * this.launch_offset
                        set this.launch_y = sentry_y + Sin(startangle) * this.launch_offset
                    else
                        set this.launch_x = sentry_x
                        set this.launch_y = sentry_y
                    endif
                    call MoveLocation(zLoc, this.launch_x, this.launch_y)
                    set this.launch_z = GetUnitFlyHeight(this.sentry) + this.launch_height + GetLocationZ(zLoc)
                endif
               
                set launch_spread_x = this.launch_x + GetRandomReal(this.launch_scatter_x*-1, this.launch_scatter_x)
                set launch_spread_y = this.launch_y + GetRandomReal(this.launch_scatter_y*-1, this.launch_scatter_y)
                set launch_spread_z = this.launch_z + GetRandomReal(this.launch_scatter_z*-1, this.launch_scatter_z)
                   
                set range_offset = ((this.laser_num-1) * this.range_per_laser + this.range_start) * .5
                if this.fixed_impact then //if true, start from the source's point
                    set startangle = Atan2(target_y - source_y, target_x - source_x)
                    set this.impact_x = source_x + Cos(startangle) * (range_offset + this.range_start)
                    set this.impact_y = source_y + Sin(startangle) * (range_offset + this.range_start)
                    set startangle = startangle + wpn.LaserDirectionalTilt[id]
                    set this.impact_x = this.impact_x + Cos(startangle) * (range_offset)
                    set this.impact_y = this.impact_y + Sin(startangle) * (range_offset)
                    set impact_spread_x = this.impact_x + GetRandomReal(this.impact_scatter_x*-1, this.impact_scatter_x)
                    set impact_spread_y = this.impact_y + GetRandomReal(this.impact_scatter_y*-1, this.impact_scatter_y)
                    call MoveLocation(zLoc, impact_spread_x, impact_spread_y)
                    if this.grounded then
                        set this.impact_z = 0. + GetLocationZ(zLoc)
                        set impact_spread_z = 0.
                    else
                        set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                        set impact_spread_z = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                    endif
                else
                    set startangle = Atan2(target_y - source_y, target_x - source_x) + wpn.LaserDirectionalTilt[id] + 3.14159
                    if this.range_per_laser != 0. then
                        set this.impact_x = target_x + Cos(startangle) * (range_offset + this.range_start)
                        set this.impact_y = target_y + Sin(startangle) * (range_offset + this.range_start)
                    else
                        set this.impact_x = target_x + Cos(startangle) * (this.target_coll * COLLISION_PERCENTAGE)
                        set this.impact_y = target_y + Sin(startangle) * (this.target_coll * COLLISION_PERCENTAGE)
                    endif
                    set impact_spread_x = this.impact_x + GetRandomReal(this.impact_scatter_x * -1, this.impact_scatter_x)
                    set impact_spread_y = this.impact_y + GetRandomReal(this.impact_scatter_y * -1, this.impact_scatter_y)
                    call MoveLocation(zLoc, impact_spread_x, impact_spread_y)
                    if this.grounded then
                        set this.impact_z = 0. + GetLocationZ(zLoc)
                        set impact_spread_z = 0.
                    else
                        set this.impact_z = GetUnitFlyHeight(this.target) + GetLocationZ(zLoc)
                        set impact_spread_z = this.impact_z + GetRandomReal(this.impact_scatter_z*-1, this.impact_scatter_z) + this.target_coll * COLLISION_PERCENTAGE
                    endif
                endif
               
                //If lasers can inflict DoT, diving the damage by the maximum number of hits the laser can inflict.
                if wpn.LaserDoT[id] > 0. then
                    set this.damage = damage / ((wpn.LaserDuration[id] + wpn.LaserFadeTime[id]) / wpn.LaserDoT[id])
                else
                    set this.damage = damage
                endif
               
                set this.dummy_count = 0
                call BurstLaser.ApplySfxAndDamage(launch_spread_x, launch_spread_y, launch_spread_z, impact_spread_x, impact_spread_y, impact_spread_z, this)
               
                //Laser
                if this.laser_string != null then
                    call LaserInstance.create(launch_spread_x, launch_spread_y, launch_spread_z, impact_spread_x, impact_spread_y, impact_spread_z, this)
                endif
                set this.laser_count = this.laser_count + 1
               
                //EVENT
                set udg_BL_EVENT_Source = this.source
                set udg_BL_EVENT_Target = this.target
                set udg_BL_EVENT_Sentry = this.sentry
                set udg_BL_EVENT_Launch_x = launch_x
                set udg_BL_EVENT_Launch_y = launch_y
                set udg_BL_EVENT_Launch_z = launch_z
                set udg_BL_EVENT_Impact_x = impact_x
                set udg_BL_EVENT_Impact_y = impact_y
                set udg_BL_EVENT_Impact_z = impact_z
                set udg_BL_EVENT_Damage = this.damage
                set udg_BL_EVENT = 1.
                set udg_BL_EVENT_Source = null
                set udg_BL_EVENT_Target = null
                set udg_BL_EVENT_Sentry = null
                set udg_BL_EVENT_Launch_x = 0.
                set udg_BL_EVENT_Launch_y = 0.
                set udg_BL_EVENT_Launch_z = 0.
                set udg_BL_EVENT_Impact_x = 0.
                set udg_BL_EVENT_Impact_y = 0.
                set udg_BL_EVENT_Impact_z = 0.
                set udg_BL_EVENT_Damage = 0.
                set udg_BL_EVENT = 0.
                //END EVENT
               
                set this.laser_angle = startangle + 3.14159
                set this.initial_x = this.impact_x
                set this.initial_y = this.impact_y
                set this.count = this.laser_num - 1
                if this.laser_num > 1 then
                    set this.tmr_volley = NewTimerEx(this)
                    call TimerStart(this.tmr_volley, this.interval, true, timer_volley_handler)
                endif
               
                return this
            endmethod
           
           
            private static method onInit takes nothing returns nothing
                local integer iLoop = 0
                set timer_volley_handler = function thistype.BurstLaser_Volley
                set interruption_event_handler = function thistype.Interruption_Event
                loop
                    call TriggerRegisterPlayerUnitEvent(interrupt_trigger, Player(iLoop), EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, null)
                    call TriggerRegisterPlayerUnitEvent(interrupt_trigger, Player(iLoop), EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, null)
                    call TriggerRegisterPlayerUnitEvent(interrupt_trigger, Player(iLoop), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
                    set iLoop = iLoop + 1
                    exitwhen iLoop == bj_MAX_PLAYER_SLOTS
                endloop
                call TriggerAddAction(interrupt_trigger, interruption_event_handler)
            endmethod
           
        endstruct
       
    endlibrary
  • Code (vJASS):
    library BL
     
        /*** If you're getting a duplicate of UnitAlive, you can delete or comment out this one ***/
        native UnitAlive takes unit id returns boolean
     
        globals
            public boolean array UsesBurstLaser
            public unit array LaserSentry
            private constant integer MAX_WEAPONS = 3
        endglobals
     
        struct BLAssignWeapon
            integer instance

            boolean array Uninterruptible[MAX_WEAPONS]      //If true, lasers will keep firing even if the source is stunned or dead.
            boolean array IsLaserGrounded[MAX_WEAPONS]      //If true, then your Lasers will always strike the ground. Invalidates LaserSpreadZ. Can be useful for AoE ground attacks.
            boolean array UpdateImpactPoint[MAX_WEAPONS]    //If true, the Laser will latch on to it's impact point relative to it's target. That can cause weird behaviour, use trial and error.
            integer array NumberOfLasers[MAX_WEAPONS]       //The number of lasers that will be shot per attack. Will default to 1 if the value it less than 1.
            real array LaserDuration[MAX_WEAPONS]           //How long an individual laser will last before it starts to fade. 32 == 1 second.
            real array LaserFadeTime[MAX_WEAPONS]           //How long it takes for each laser to fade. 32 == 1 second.
            real array LaserInterval[MAX_WEAPONS]           //Time between laser in seconds.
            real array LaserDoT[MAX_WEAPONS]                //the number of intervals that will pass before the sfx and damage are repeated PER LASER. Set to <= 0 to ignore. Useful for impact-locked lasers.
            string array LaserLaunchFX[MAX_WEAPONS]         //The string of the special effect that plays on the launch dummy.
            string array LaserImpactFX[MAX_WEAPONS]         //The string of the special effect that plays on the impact dummy.
            string array LaserAreaFX[MAX_WEAPONS]           //The string of the special effect that plays on the impact dummy in addition to the ImpactFX. Works better with an AOE laser.
            string array LaserString[MAX_WEAPONS]           //The string of lightning effect itself.
            real array LaserAOE[MAX_WEAPONS]                //If greater that 0. will deal damage in an area around the impact.

            real array LaserLaunchOffset[MAX_WEAPONS]       //How far forward with the Launch effect be. Use negative values to move backwards.
            real array LaserLaunchHeight[MAX_WEAPONS]       //Adjusts the Launch effect along the Z axis. Positive value go up, negative values go down.
            real array LaserLaunchAngle[MAX_WEAPONS]        //By how many radians will the Launch effect be offset by? Positive values adjust the angle counter-clockwise.
            real array LaserLaunchScatterX[MAX_WEAPONS]     //How much your laser's launch will scatter along the X axis.
            real array LaserLaunchScatterY[MAX_WEAPONS]     //How much your laser's launch will scatter along the Y axis.
            real array LaserLaunchScatterZ[MAX_WEAPONS]     //How much your laser's launch will scatter along the Z axis.
            real array LaserLaunchScale[MAX_WEAPONS]        //The size of your launch special effects. 1. is 100%.
            real array LaserImpactScatterX[MAX_WEAPONS]     //How much your laser will spread out along the X axis.
            real array LaserImpactScatterY[MAX_WEAPONS]     //How much your laser will spread out along the Y axis.
            real array LaserImpactScatterZ[MAX_WEAPONS]     //How much your laser will spread out along the Z axis.
            real array LaserImpactScale[MAX_WEAPONS]        //The size of your impact special effects. 1. is 100%.
            real array LaserAreaScale[MAX_WEAPONS]          //Setting this value different from LaserImpactScale will mean a new dummy will have to be created.
                                                            //Unless really necessary, it's recommended to keep this the same value as LaserImpactScale.

            boolean array LaserDivideDamage[MAX_WEAPONS]    //If true, the total damage will be divided amoung the number of lasers.
            boolean array UpdateTargetLoc[MAX_WEAPONS]      //If false, Lasers in a set will strike the same point instead of updating their impact location to follow a moving target.
            boolean array UpdateSourceLoc[MAX_WEAPONS]      //If false, consequence shots in a volley will launch from the initial point
            boolean array UpdateLaunchPoint[MAX_WEAPONS]    //If true, lasers will follow source if it moves away.
            boolean array LaunchFromTargetLoc[MAX_WEAPONS]  //If true, then launch point will be relative to the target rather than the source. Sentries take priority, however.
            boolean array FixedImpactLoc[MAX_WEAPONS]       //If true will set LaserRangeStart's starting point to the attacker (source of the laser) instead of the target
            real array LaserRangeStart[MAX_WEAPONS]         //Adds or subtracts a certain amount of distance from it's starting point.
            real array LaserRangePerLaser[MAX_WEAPONS]      //Adds or substracts distance per Laser in a set.
            real array LaserDirectionalTilt[MAX_WEAPONS]    //This pivots the angle of the Laser by a certain amount so that the direction from which the Laser starts is rotated. Uses radians.
                                                            //A value of 1.5708 radians (90°) with make a laser start from right to left, for example.
                                                            //-0.785398 radians (-45°) will start the laser from left to right, moving diagonally.
                                                            //This is only useful if LaserRangePerLaser is > 0.

            attacktype array LaserAttackType[MAX_WEAPONS]   //Vanilla attack type like ATTACK_TYPE_HERO, ATTACK_TYPE_PIERCE, etc
            integer array LaserDamageType[MAX_WEAPONS]      //The damage type from Damage Engine
            boolean array LaserHitAll[MAX_WEAPONS]          //The laser will harm all units in the LaserAOE. If LaserAOE is <= 0. this does nothing.
            boolean array LaserFriendlyFire[MAX_WEAPONS]    //AOE damage will damage allies
            unit array LaserSentry[MAX_WEAPONS]             //This is optional. If something is set as the Sentry, the lasers will shoot from that unit instead.

            real array RED[MAX_WEAPONS]                     //Red tint of the Laser. 1. is 100%, and 0. is 0%.
            real array BLUE[MAX_WEAPONS]                    //Blue tint of the Laser. 1. is 100%, and 0. is 0%.
            real array GREEN[MAX_WEAPONS]                   //Green tint of the Laser. 1. is 100%, and 0. is 0%.
            real array ALPHA[MAX_WEAPONS]                   //Transparency of the Laser. 1. fully visible. 0. is completely transparent.

            boolean array ReduceLaunchOverhead[MAX_WEAPONS] //If true, only 1 dummy will be used for your launch special effect, but if the launch scatters per volley, the
                                                            //special effects may jump around since they're bound to only one unit. Some special effects don't have that
                                                            //problem, so it's preferrable to test lasers with this set to true first, as the alternative means a new dummy
                                                            //per laser per launch, which creates a lot of overhead.
            boolean array ReduceImpactOverhead[MAX_WEAPONS] //Same as above, but for impacts and area special effects.

            method destroy takes nothing returns nothing
                local integer iLoop = 0
                loop
                    exitwhen iLoop > MAX_WEAPONS
                    set this.LaserString[iLoop]             = null
                    set this.RED[iLoop]                     = 1.
                    set this.BLUE[iLoop]                    = 1.
                    set this.GREEN[iLoop]                   = 1.
                    set this.ALPHA[iLoop]                   = 1.
                    set this.LaserLaunchFX[iLoop]           = null
                    set this.LaserImpactFX[iLoop]           = null
                    set this.LaserAreaFX[iLoop]             = null
                    set this.LaserLaunchScale[iLoop]        = 1.
                    set this.LaserImpactScale[iLoop]        = 1.
                    set this.LaserAreaScale[iLoop]          = 1.
                    set this.NumberOfLasers[iLoop]          = 1
                    set this.LaserDuration[iLoop]           = 0.
                    set this.LaserFadeTime[iLoop]           = 0.
                    set this.LaserInterval[iLoop]           = 0.
                    set this.LaserDoT[iLoop]                = 0.
                    set this.LaserAOE[iLoop]                = 0.
                    set this.IsLaserGrounded[iLoop]         = false
                    set this.LaserLaunchOffset[iLoop]       = 0.
                    set this.LaserLaunchHeight[iLoop]       = 0.
                    set this.LaserLaunchAngle[iLoop]        = 0.
                    set this.LaserLaunchScatterX[iLoop]     = 0.
                    set this.LaserLaunchScatterY[iLoop]     = 0.
                    set this.LaserLaunchScatterZ[iLoop]     = 0.
                    set this.LaserImpactScatterX[iLoop]     = 0.
                    set this.LaserImpactScatterY[iLoop]     = 0.
                    set this.LaserImpactScatterZ[iLoop]     = 0.
                    set this.UpdateTargetLoc[iLoop]         = false
                    set this.UpdateLaunchPoint[iLoop]       = false
                    set this.UpdateImpactPoint[iLoop]       = false
                    set this.LaunchFromTargetLoc[iLoop]     = false
                    set this.UpdateSourceLoc[iLoop]         = false
                    set this.FixedImpactLoc[iLoop]          = false
                    set this.LaserRangeStart[iLoop]         = 0.
                    set this.LaserRangePerLaser[iLoop]      = 0.
                    set this.LaserDirectionalTilt[iLoop]    = 0.
                    set this.LaserAttackType[iLoop]         = null
                    set this.LaserDamageType[iLoop]         = 0
                    set this.LaserDivideDamage[iLoop]       = false
                    set this.Uninterruptible[iLoop]         = false
                    set this.LaserHitAll[iLoop]             = false
                    set this.LaserFriendlyFire[iLoop]       = false
                    set this.ReduceLaunchOverhead[iLoop]    = true
                    set this.ReduceImpactOverhead[iLoop]    = true
                    set iLoop = iLoop + 1
                endloop
                call this.deallocate()
            endmethod


            static method operator [] takes unit u returns thistype
                return thistype(GetUnitUserData(u)).instance
            endmethod


            static method create takes unit u, integer wpn_num, integer arsenal_num returns thistype
                local integer id = GetUnitUserData(u)
                local thistype this
             
                if thistype(id).instance == 0 then
                    set this = allocate()
                    set thistype(id).instance = this
                    set UsesBurstLaser[id] = true
                else
                    set this = thistype(id).instance
                endif
             
                if BL_NumberOfLasers[arsenal_num] > 0 then
                    set this.NumberOfLasers[wpn_num]    = BL_NumberOfLasers[arsenal_num]
                else
                    set this.NumberOfLasers[wpn_num]    = 1
                endif
             
                set this.LaserString[wpn_num]           = BL_LaserString[arsenal_num]
                set this.RED[wpn_num]                   = BL_RED[arsenal_num]
                set this.BLUE[wpn_num]                  = BL_BLUE[arsenal_num]
                set this.GREEN[wpn_num]                 = BL_GREEN[arsenal_num]
                set this.ALPHA[wpn_num]                 = BL_ALPHA[arsenal_num]
                set this.LaserLaunchFX[wpn_num]         = BL_LaserLaunchFX[arsenal_num]
                set this.LaserImpactFX[wpn_num]         = BL_LaserImpactFX[arsenal_num]
                set this.LaserAreaFX[wpn_num]           = BL_LaserAreaFX[arsenal_num]
                set this.LaserLaunchScale[wpn_num]      = BL_LaserLaunchScale[arsenal_num]
                set this.LaserImpactScale[wpn_num]      = BL_LaserImpactScale[arsenal_num]
                set this.LaserAreaScale[wpn_num]        = BL_LaserAreaScale[arsenal_num]
                set this.LaserDuration[wpn_num]         = BL_LaserDuration[arsenal_num]
                set this.LaserFadeTime[wpn_num]         = BL_LaserFadeTime[arsenal_num]
                set this.LaserInterval[wpn_num]         = BL_LaserInterval[arsenal_num]
                set this.LaserDoT[wpn_num]              = BL_LaserDoT[arsenal_num]
                set this.LaserAOE[wpn_num]              = BL_LaserAOE[arsenal_num]
                set this.IsLaserGrounded[wpn_num]       = BL_IsLaserGrounded[arsenal_num]
                set this.LaserLaunchOffset[wpn_num]     = BL_LaserLaunchOffset[arsenal_num]
                set this.LaserLaunchHeight[wpn_num]     = BL_LaserLaunchHeight[arsenal_num]
                set this.LaserLaunchAngle[wpn_num]      = BL_LaserLaunchAngle[arsenal_num]
                set this.LaserLaunchScatterX[wpn_num]   = BL_LaserLaunchScatterX[arsenal_num]
                set this.LaserLaunchScatterY[wpn_num]   = BL_LaserLaunchScatterY[arsenal_num]
                set this.LaserLaunchScatterZ[wpn_num]   = BL_LaserLaunchScatterZ[arsenal_num]
                set this.LaserImpactScatterX[wpn_num]   = BL_LaserImpactScatterX[arsenal_num]
                set this.LaserImpactScatterY[wpn_num]   = BL_LaserImpactScatterY[arsenal_num]
                set this.LaserImpactScatterZ[wpn_num]   = BL_LaserImpactScatterZ[arsenal_num]
                set this.UpdateTargetLoc[wpn_num]       = BL_UpdateTargetLoc[arsenal_num]
                set this.UpdateSourceLoc[wpn_num]       = BL_UpdateSourceLoc[arsenal_num]
                set this.UpdateLaunchPoint[wpn_num]     = BL_UpdateLaunchPoint[arsenal_num]
                set this.UpdateImpactPoint[wpn_num]     = BL_UpdateImpactPoint[arsenal_num]
                set this.LaunchFromTargetLoc[wpn_num]   = BL_LaunchFromTargetLoc[arsenal_num]
                set this.FixedImpactLoc[wpn_num]        = BL_FixedImpactLoc[arsenal_num]
                set this.LaserRangeStart[wpn_num]       = BL_LaserRangeStart[arsenal_num]
                set this.LaserRangePerLaser[wpn_num]    = BL_LaserRangePerLaser[arsenal_num]
                set this.LaserDirectionalTilt[wpn_num]  = BL_LaserDirectionalTilt[arsenal_num]
                set this.LaserAttackType[wpn_num]       = BL_LaserAttackType[arsenal_num]
                set this.LaserDamageType[wpn_num]       = BL_LaserDamageType[arsenal_num]
                set this.LaserDivideDamage[wpn_num]     = BL_LaserDivideDamage[arsenal_num]
                set this.Uninterruptible[wpn_num]       = BL_Uninterruptible[arsenal_num]
                set this.LaserHitAll[wpn_num]           = BL_LaserHitAll[arsenal_num]
                set this.LaserFriendlyFire[wpn_num]     = BL_LaserFriendlyFire[arsenal_num]
                set this.ReduceLaunchOverhead[wpn_num]  = BL_ReduceLaunchOverhead[arsenal_num]
                set this.ReduceImpactOverhead[wpn_num]  = BL_ReduceImpactOverhead[arsenal_num]
             
                return this
            endmethod
             
        endstruct
     
        //Weapon Setup variables
        globals
            string array BL_LaserString
            real array BL_RED
            real array BL_BLUE
            real array BL_GREEN
            real array BL_ALPHA
            string array BL_LaserLaunchFX
            string array BL_LaserImpactFX
            string array BL_LaserAreaFX
            real array BL_LaserLaunchScale
            real array BL_LaserImpactScale
            real array BL_LaserAreaScale
            integer array BL_NumberOfLasers
            real array BL_LaserDuration
            real array BL_LaserFadeTime
            real array BL_LaserInterval
            real array BL_LaserDoT
            real array BL_LaserAOE
            boolean array BL_IsLaserGrounded
            real array BL_LaserLaunchOffset
            real array BL_LaserLaunchHeight
            real array BL_LaserLaunchAngle
            real array BL_LaserLaunchScatterX
            real array BL_LaserLaunchScatterY
            real array BL_LaserLaunchScatterZ
            real array BL_LaserImpactScatterX
            real array BL_LaserImpactScatterY
            real array BL_LaserImpactScatterZ
            boolean array BL_UpdateTargetLoc
            boolean array BL_UpdateSourceLoc
            boolean array BL_UpdateLaunchPoint
            boolean array BL_UpdateImpactPoint
            boolean array BL_LaunchFromTargetLoc
            boolean array BL_FixedImpactLoc
            real array BL_LaserRangeStart
            real array BL_LaserRangePerLaser
            real array BL_LaserDirectionalTilt
            attacktype array BL_LaserAttackType
            integer array BL_LaserDamageType
            boolean array BL_LaserDivideDamage
            boolean array BL_Uninterruptible
            boolean array BL_LaserHitAll
            boolean array BL_LaserFriendlyFire
            boolean array BL_ReduceLaunchOverhead
            boolean array BL_ReduceImpactOverhead
        endglobals
     
    endlibrary
  • Code (vJASS):

    library BurstLaserOnAttackModule initializer init requires BurstLaser

        globals
            private trigger BL_Module_onAttack = CreateTrigger()
        endglobals

        function BL_Module_onAttack_Actions takes nothing returns boolean
            local real Damage = 0.
            local integer Source_ID = GetUnitUserData(udg_DamageEventSource)
            local integer Weapon_Index = 1
            local BLAssignWeapon blwpn
            //local integer Struct_ID = BLAssignWeapon[udg_DamageEventSource]
            if BL_UsesBurstLaser[Source_ID] and udg_DamageEventType == 0 and not udg_IsDamageSpell then
                set udg_DamageEventAmount = 0.
             
                //If you want to have temporary sentries, you may create them here and apply an expiration timer to them. Used mostly for very conplex launch effects.
             
                //Here you can change the Weapon_Index to a different value to switch the laser weapon used. For example, the Gryphon Rider has 2 laser weapons, and
                //Weapon_Index 2 is used against air units. Otherwise, the defaul index is 1.
                if GetUnitTypeId(udg_DamageEventSource) == 'hgry' then
                    if IsUnitType(udg_DamageEventTarget, UNIT_TYPE_FLYING) then
                        set Weapon_Index = 2
                    endif
                 
                endif
             
                //Here we check if the damage will be split per laser in a set or not.
                set blwpn = BLAssignWeapon[udg_DamageEventSource]
                if blwpn.LaserDivideDamage[Weapon_Index] then
                    set Damage = GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget)) / blwpn.NumberOfLasers[Weapon_Index]
                else
                    set Damage = GetFullDamage(udg_DamageEventPrevAmt, GetUnitArmor(udg_DamageEventTarget))
                endif
             
                call BurstLaser.create(udg_DamageEventSource, udg_DamageEventTarget, BL_LaserSentry[Source_ID], blwpn, Weapon_Index, Damage)
            endif
            return false
        endfunction

        //===========================================================================
        function init takes nothing returns nothing
            call TriggerRegisterVariableEvent( BL_Module_onAttack, "udg_DamageModifierEvent", EQUAL, 1.00 )
            call TriggerAddCondition( BL_Module_onAttack, function BL_Module_onAttack_Actions )
        endfunction

    endlibrary
     
Contents

Burst Laser v3.1 (Map)

Reviews
KILLCIDE
A lot of libraries, but the system is rad. The demo and API are also pretty simple to follow. The only downside are the bugs you mentioned in the README and the lightning effects getting all wonky on air units over terrain. Needs Fixed Nothing...
  1. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    Well if the SpecialEffectZ was lagging for you, then the dummy method is going to be worse because the initial version of the system used dummies - from a recycler. It was lag hell. I tried a new version with DummyRecycler and it's just the same result all over again.

    I could try something different which is to have two dummies per instance - one for launch and one for impact - rather than two per laser. It could work, but just out of curiosity, I've attached the dummy version. Does it lag less or more than the AddSpecialEffctZ version?

    EDIT: At the peak of the fight vs riflemen, I get about 20 fps drop if I use two dummies per instance rather than per laser. Far more manageable, but there will still be a need to modify certain special effects to not update their position when playing their death animations.
     

    Attached Files:

    Last edited: Dec 20, 2016
  2. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Can't test it these days.
     
  3. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Was there some change or so with lags/fsp drops? Or maybe someone has done tests?
     
  4. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    The map attached on post #21 uses two dummies per lightning effect. Since I don't get lag with AddSpecialEffectZ I wanted to see if the lag with the dummy units was less or worse.

    v2.2 (attached here) uses up to two dummy units per instance, not lightning effect. The lag is less severe, but now the impact effects just jump around with the dummy. I need a 3D artist to help me understand how to modify effects to make it work with this, then it'll be viable.
     

    Attached Files:

  5. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Oh. ..

    Have you tried to use enough dummies so it won't "jump" with the units, but not with creating mass units, but with using something like

    Dummy Recycler v1.25
    [Snippet] DelayedDummyRecycler
    [Snippet] Unit Recycler

    I would preload like 30-50 dummy units or so at init, and then would give it a try.
    New creation is always pretty heavy, so it might also help a bit when done too often.

    Have not read code now again how exactly the code manages effect creation, but too massive effect spam can also cause some FPS drop.
    Maybe adding some sort of timeout, to create effects only each 0.1-0.2 seconds instead of each 0.03125000 seconds would maybe little help, too. (idk, if it would be the case for you)

    Next week or so I can probably test again, too.
     
  6. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    I am already using Dummy Recycler, though I didn't know you could preload dummies. Isn't Warcraft III bad with handling large numbers of units though? (Most of the stuff I do, I plan them with the idea that they would be usable in an altered melee. So, large armies.).

    v2.1 uses a dummy for every lightning effect with Dummy Recycler.
     
  7. IcemanBo

    IcemanBo

    Joined:
    Sep 6, 2013
    Messages:
    6,549
    Resources:
    23
    Maps:
    3
    Spells:
    11
    Template:
    1
    Tutorials:
    5
    JASS:
    3
    Resources:
    23
    Yes, but hidden + paused dummies is better than normal units for sure!
    Ok, this sounds pretty right to me then. :)
     
  8. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    v2.1 lags a lot when your have a bunch of units though. The cluster of riflemen to the right is meant to stress-test what an actual melee combat may contain (even if it's a bit much). There is a significant amount of lag that is reduced when I use only two dummies per instance. Idk, I can't really tell if the lag is just me expecting too much of my system and using 2 dummies per laser is enough.
     
  9. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    593
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    Hey SpellBound I've a suggestion

    Instead of attaching the configuration data (which is based on the unittype) to uDex when a unit enters the map, I guess it would be better to just attach them directly to the unittypeId and then just run the configurations once on map init.
    Example:
    Code (vJASS):
    library BurstLaserConfiguration

        private struct Init extends array

            static method getIndex takes integer unittype returns integer
               //convert the unittype to a small integer so you can use them on arrays
            endmethod

            static method onInit takes nothing returns nothing
                local integer id = getIndex('hrif')

                set vars[id] = value
                //...

                set id = getIndex('hpea')
                set vars[id] = value
                //...

                //Same for other unittypes

            endmethod

        endstruct

    endlibrary



    Or better if you can even attach the configuration to an abiliy instead of the unittype or unituserdata so that a unit can have multiple laser attacks at once.
     
  10. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    Huh, I was unaware that you could actually do that. I think someone else tried to explain that method to me before but I didn't get it until now :p
    It would be cool to have multiple laser attacks, tho there is one issue think could arise from this: if you want to modify the laser (like say you want to increase the colour of a laser if the unit has a specific buff), wouldn't that change the laser for all units of the same type/with the same ability instead of working individually?
     
  11. AGD

    AGD

    Joined:
    Mar 29, 2016
    Messages:
    593
    Resources:
    14
    Spells:
    8
    Tutorials:
    1
    JASS:
    5
    Resources:
    14
    There's a way to handle it. Upon the lasers' creation, you're gonna apply the default color configuration (The one set on map init) and if the user wants to manipulate the color (or damage for example) of the lasers of a certain unit, you could just give them methods for editing the stats of a specific laser instance, then they can freely manipulate the stats dynamically most preferably upon creation since you've already provided them the events. Now, what you have to add is another method to retrieve the current fired/started lasers. But of course you could think of another design for this, It's just an example.
     
  12. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    Well I don't think I have the energy to keep working on this system. As soon as I find a way to circumvent the lag, I'm not gonna touch this.
     
  13. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,504
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    @Spellbound If that's the case, I will set this to Awaiting Update for now.
     
  14. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    @KILLCIDE MAJOR system rework. I stess-tested this and it's not leaking anymore. Would really appreciate it if one of the code mods could take a look at it :)

    PS: I use a unit group to store dummies and recycle them afterwards. The group is them destroyed because it's per-attack-instance. Should I use a hashtable instead? Is group creation/destruction slower than hashtables?
     
  15. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,504
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    iterating through objects in a unit group would be slower than hashtables. Though the difference would be negligible.
     
  16. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    Well, I've updated the system to use a hashtable instead of a unit group for dummies. Didn't really make much sense to keep creating/destroying unit groups over and over again.
     
  17. nhocklanhox6

    nhocklanhox6

    Joined:
    Feb 12, 2012
    Messages:
    335
    Resources:
    28
    Models:
    6
    Spells:
    21
    Tutorials:
    1
    Resources:
    28
    Oh my, it's sooo awesome, love it soooo much ><. I really love the lightning :D
     
    Last edited: Oct 26, 2017
  18. KILLCIDE

    KILLCIDE

    Administrator

    Joined:
    Jul 22, 2015
    Messages:
    3,504
    Resources:
    20
    Models:
    2
    Icons:
    10
    Spells:
    7
    Tutorials:
    1
    Resources:
    20
    A lot of libraries, but the system is rad. The demo and API are also pretty simple to follow. The only downside are the bugs you mentioned in the README and the lightning effects getting all wonky on air units over terrain.

    Needs Fixed

    • Nothing

    Suggestions

    • A brief description of the configurables required to setup a weapon would be appreciated. I know some of them are pretty self explanatory, but others are not (especially the booleans).
    • For Setup Arsenal, look into modules.
    • You should really look into encapsulation. Pretty much everything is public and it just feels wrong. Like here "...These variables are meant to be readonly, so DO NOT SET THEM TO ANYTHING." Why not make them readonly or private so that they will get an error if they attempt to do something?
    • Delete the Unused Stuff folder. There is really no point of that being there :p

    Status



    Approved
     
  19. Spellbound

    Spellbound

    Joined:
    Jan 9, 2005
    Messages:
    2,034
    Resources:
    18
    Models:
    1
    Icons:
    1
    Skins:
    5
    Maps:
    1
    Spells:
    9
    JASS:
    1
    Resources:
    18
    I completely forgot to reply to this lol

    I'll see about updating the Setup Arsenal with proper documentation. They're in Assign Weapon as opposed to Setup Arsenal because the latter trigger was created at a later date. Assign Weapon was originally a combination of both.

    Can you give me a brief example of module would work for Setup Arsenal? I've never worked with modules before.

    Hmm, I'll do some research.

    Well they're mostly there a backups for overlooked ideas but I guess I can remove them for uploading to the Hive.

    Thanks for the review!
     
  20. Clanzion

    Clanzion

    Joined:
    Jul 4, 2016
    Messages:
    458
    Resources:
    0
    Resources:
    0
    Hey, Spellbound, any chance you may update to utilize the new sfx natives to mitigate the lagging problems?