• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Burst Laser v3.2

full


NB: this system was made before special effects could be manipulated through functions, and as such, the overhead on it is quite significant if your lasers are going to contain a lot of launch/impact effects. When I made this system back then, it didn't appear to lag nearly as much as it does now, as you can attest from the video above, so unless v1.32 of the game is doing something very different with how units are processed, I can only recommend that you use Burst Laser sparingly - a spell, an attack for an expensive, rare unit, a summon, etc. If you are on newer versions of the game, there are way more efficient ways of doing what this system is doing, so probably avoid this.

v3.2 fixed an issue where null BLAssignWeapon instances were being destroyed, which consequently was causing the Archmage's spell to malfunction after a few casts.
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


JASS:
    Burst Laser v3.2 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/

BurstLaser Engine

BurstLaser Assign Weapon

BL Module onAttack

JASS:
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
JASS:
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
JASS:
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.2 (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...
It's looking like you'll have to wait a while for the updated map, but the fix is really simple. If you go to BL Module onIndex, at the bottom, you will see this:
JASS:
else
    call BLAssignWeapon[Source].destroy()
    set BL_UsesBurstLaser[udg_UDex] = false
endif

Modify it with this:

JASS:
else
    if BLAssignWeapon[Source] != 0 then
        call BLAssignWeapon[Source].destroy()
        set BL_UsesBurstLaser[udg_UDex] = false
    endif
endif
 
Level 9
Joined
Dec 16, 2017
Messages
343
Screenshot 2022-02-17 192648.png

When it starts map loading, this happens.

Yeah, i copied the jass text you posted from the beginning as it was faster and easier, i've just put these screenshots to show you(in case someone downloads it and doesn't read the comments)
 
Top