[Solved] Multishot Trigger

Status
Not open for further replies.
Level 4
Joined
Feb 24, 2018
Messages
71
Hi there ! I've added Custom Multishot v2.2c + Orbs add-on v1.3.1 in my map but I can't figure out how can I set the number of targets for the multishot ability. For example : level 1 - 2 targets , level 2 - 4 targets , level 3 - 6 targets. Any idea ? Thanks !
JASS:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// --------------------------------- ADVANCED - PART OF WereElf's CUSTOM MULTISHOT -------------------------------- //
// This library is used to make Multishot able to target, and damage Magic Immune enemies, when they can resist     //
// ultimates (a gameplay constant is used for that).                                                                //
// When you have this library + the Multishot library, you can use "MultishotTargetAdvaned" and                     //
// "MultishotPointAdvanced" functions.                                                                              //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library AdvancedMultishot requires MultiMisc

    globals
// ALWAYS_USE_ADVANCED allows you to use ONLY the advanced version, even if the enemies are NOT immune to magic.
// It's still only used only when calling the Advanced function.
        public constant boolean ALWAYS_USE_ADVANCED = false
        private timer Looper = CreateTimer()
        private boolean On = false
        private constant real REFRESH_RATE = 0.03125
    endglobals

// This struct is also used against Magic Immune enemies - it removes their immunity, so the Multishot missile can be cast
// and then it returns their immunities.
    private struct ImmunityList
        boolean main = false
        boolean i1 = false
        boolean i2 = false
        boolean i3 = false
        boolean i4 = false
        unit u

// 'Amim', 'ACm2', 'ACm3' and 'ACmi' are magic immunities. They are usually constant, unless you use only custom magic immunities.
        static method create takes unit u returns ImmunityList
            local ImmunityList IL = ImmunityList.allocate()
            set IL.u = u
            if GetUnitAbilityLevel(u, 'Amim') > 0 then
                call UnitRemoveAbility(u, 'Amim')
                set IL.i1 = true
                set IL.main = true
            endif
            if GetUnitAbilityLevel(u, 'ACm2') > 0 then
                call UnitRemoveAbility(u, 'ACm2')
                set IL.i2 = true
                set IL.main = true
            endif
            if GetUnitAbilityLevel(u, 'ACm3') > 0 then
                call UnitRemoveAbility(u, 'ACm3')
                set IL.i3 = true
                set IL.main = true
            endif
            if GetUnitAbilityLevel(u, 'ACmi') > 0 then
                call UnitRemoveAbility(u, 'ACmi')
                set IL.i4 = true
                set IL.main = true
            endif
            return IL
        endmethod
       
        method onDestroy takes nothing returns nothing
            if .i1 then
                call UnitAddAbility(.u, 'Amim')
            endif
            if .i2 then
                call UnitAddAbility(.u, 'ACm2')
            endif
            if .i3 then
                call UnitAddAbility(.u, 'ACm3')
            endif
            if .i4 then
                call UnitAddAbility(.u, 'ACmi')
            endif
        endmethod
    endstruct
   
    private struct TimeManager
        real x
        real y
        real speed
        unit targ
        MultiMisc_DamProperties damage
   
        readonly thistype next
        readonly thistype prev
        static method operator first takes nothing returns thistype
            return thistype(0).next
        endmethod
        static method operator last takes nothing returns thistype
            return thistype(0).prev
        endmethod

        static method create takes real x, real y, unit u, real speed, MultiMisc_DamProperties DP returns thistype
            local thistype this = thistype.allocate()
            set thistype(0).next.prev = this
            set this.next = thistype(0).next
            set this.prev = thistype(0)
            set thistype(0).next = this
            set this.x = x
            set this.y = y
            set this.speed = speed*REFRESH_RATE
            set this.targ = u
            set this.damage = DP
            return this
        endmethod

        method onDestroy takes nothing returns nothing
            if this.first == this.last then
                set On = false
            endif
            set this.next.prev = this.prev
            set this.prev.next = this.next
        endmethod

        static method refresh takes nothing returns nothing
            local thistype this = thistype.first
            local real x
            local real y
            local real h
            local real w
            local real dist
            local real a
            loop
                exitwhen this == 0
                if not IsUnitType(this.targ, UNIT_TYPE_DEAD) then
                    set x = GetUnitX(this.targ)
                    set y = GetUnitY(this.targ)
                    set w = x - this.x
                    set h = y - this.y
                    set dist = w*w + h*h
                    if dist <= this.speed*this.speed then
                        set udg_MS_Damage = this.damage.amount
                            if Multishot_WANT_MULTISHOT_HIT_EVENT then
                            set udg_MS_Dummy = this.damage.dummy
                            set udg_MS_Hit_Unit = this.targ
                            set udg_MS_Source = this.damage.source
                            set udg_MS_Current_Group = this.damage.shotGroup
                            set udg_MS_Main_Target = this.damage.mainTarget
                            set udg_MS_Missile = this.damage.missile
                            set udg_MS_UnitsInGroup = this.damage.unitsShot
                            set udg_MS_Hit_Event = 1.00
                            set udg_MS_Hit_Event = 0.00
                        endif
                        set Multishot_Damage = true
                        call UnitDamageTarget( this.damage.source, this.targ, udg_MS_Damage, true, false, this.damage.aType, this.damage.dType, null )
                        set Multishot_Damage = false
                        set this.damage.alreadyShot = this.damage.alreadyShot + 1
                        if this.damage.alreadyShot == this.damage.unitsShot then
                            call TimerStart(this.damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
                        endif
                        call thistype.destroy(this)
                    else
                        set a = Atan2(h, w)
                        set this.x = this.x + this.speed*Cos(a)
                        set this.y = this.y + this.speed*Sin(a)
                    endif
                else
                    call thistype.destroy(this)
                endif
                set this = this.next
            endloop
            if On then
                call TimerStart(Looper, REFRESH_RATE, false, function TimeManager.refresh)
            endif
        endmethod
    endstruct

// This function removes the unit's magic immunities (so it can shoot them), shoots them, and if they have had magic immunity
// it calculates the time the shot needs to reach the target (roughly), and calls the function above to start a timer,
// Which will artificially damage the unit, when the timer expires.
// Then it returns the units' immunities.
    function MS_Shoot takes MultiMisc_DamProperties DP, real x, real y, real shotSpeed, unit target returns nothing
        local real delay
        local ImmunityList IL = ImmunityList.create(target)
        local TimeManager TM
        call IssueTargetOrder(DP.dummy, Multishot_SPELL_ORDER, target)
        if IL.main or ALWAYS_USE_ADVANCED then
            set TM = TimeManager.create(x, y, target, shotSpeed, DP)
            if not On then
                call TimerStart(Looper, REFRESH_RATE, false, function TimeManager.refresh)
                set On = true
            endif
        endif
        call ImmunityList.destroy(IL)
    endfunction

endlibrary
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------- CUSTOM MULTISHOT BY WereElf ------------------------------------------ //
// This is simply a Multishot, which is a lot better than the Barrage based one.                                    //
// The differences between this Multishot and the Barrage based one are:                                            //
//                                                                                                                  //
// 1) This multishot can be configured to hit only enemies infront of the shooter, while Barrage based can only     //
// target ALL the enemies in range (certain amount of them).                                                        //
//                                                                                                                  //
// 2) This multishot works with Orb effects (if you use the "Orbs" add-on library.                                  //
//                                                                                                                  //
// 3) You can make your own "orb effects" when you use this Multishot, since you get a "On Hit" event.              //
//                                                                                                                  //
// 4) You can set the targets allowed to 1 and 2, while for the Barrage based one - the minimum is 3.               //
//                                                                                                                  //
// 5) You can use this multishot for SPELLS!                                                                        //
//                                                                                                                  //
// 6) You can put some weird configurations for this multishot (like - hit allied buildings, and enemy units).      //
//                                                                                                                  //
// I could keep on listing the possibillities, offered by this library, but I wouldn't. I pointed out the most      //
// important ones.                                                                                                  //
//                                                                                                                  //
// HOW TO MAKE IT WORK:                                                                                             //
//                                                                                                                  //
// 0.a) You need a damage detecting system on your map. The multishot is built, using 1 of the most basic DDS(es),  //
// so it will work with any DDS. However, if you are using a different one - you need to go to the bottom of this   //
// library, and change the variables in the last textmacro to the ones, used by the other DDS.                      //
//                                                                                                                  //
// 0.b) You need to have a dummy on your map.                                                                       //
//                                                                                                                  //
// 1) Make a custom buff on your map, which will be used ONLY by the Multishot skills.                              //
//                                                                                                                  //
// 2) Make as many as you need skills, based on Acid Bomb, make them cost 0 mana, deal 0 damage, and make them      //
// APPLY THE BUFF YOU MADE IN STEP (1).                                                                             //
//                                                                                                                  //
// 3) Scroll down to the globals block of this library, and set the variables to "your" values.                     //
//                                                                                                                  //
// 4) When you want to make a trigger, using Multishot, just call "MultishotTarget" or "MultishotPoint", according  //
// to your needs.                                                                                                   //
//                                                                                                                  //
// 5) You could make a library with some pre-set Multishot functions (like MultishoRt in the sample map), so you    //
// don't have to put all the arguments when you call Multishot, but simply the ones you need.                       //
//                                                                                                                  //
// If you have the AdvancedMultishot library (add-on), you can also use "MultishotTargetAdvanced" and               //
// "MultishotPointAdvanced". The advanced functions are able to hit magic immune enemies, even if you have "Magic   //
// immune units resist ultimates" set to true.                                                                      //
//                                                                                                                  //
// THE ARGUMENTS THE FUNCTIONS TAKE:                                                                                //
//                                                                                                                  //
// MultishotTarg (both normal and the advanced) take:                                                               //
// 1 (unit) - Shooter - who is shooting                                                                             //
// 2 (unit) - Target - who is the original target of the shot                                                       //
// 3 (real) - Damage - the damage the shot should deal                                                              //
// 4 (integer) - Targets - how many targets its allowed to shoot at once                                            //
// 5 (real) - Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot                                                                               //
// 6 (real) - Range - the range multishot chooses enemies from.                                                     //
// 7 (boolean) - Always hits main targer - does the main target always get shot, or it only has a chance to get     //
// shot (if more than the max amount of units are in range)                                                         //
// 8 (attacktype) - Attack type - the attack type of the shooter. Its used for correct armor estimation, and thats  //
// the type of damage dealt in the end.                                                                             //
// 9 (integer) - Missile ability - the ability id of the Multishots missile.                                        //
// 10 (real) - Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45,    //
// multishot will act like the target is at 90 degrees.                                                             //
// 11 (boolean) - Fixed damage - if you want to deal some fixed value of damage, not based on the damage dealt -    //
// set this to true, otherwise - false.                                                                             //
// 12 (damagetype) - Damage type - if "Fixed damage" is set to false - this will act as "DAMAGE_TYPE_NORMAL", no    //
// matter what. Otherwise - this is the damage type multishot deals.                                                //
// 13 (boolexpr) - Conditions - What units shall it target                                                          //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION                                                                        //
// 14 (real) - Missile speed - simply put-in the missiles speed                                                     //
//                                                                                                                  //
// MultishotPoint (both normal and the advanced) take:                                                              //
// 1 (unit) Caster - who is shooting                                                                                //
// 2 (real) Damage - the damage dealt                                                                               //
// 3 (integer) Targets - how many targets its allowed to shoot at once                                              //
// 4 (real) Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want   //
// ALL the units in range to get shot                                                                               //
// 5 (real) Range - the range multishot chooses enemies from                                                        //
// 6 (attacktype) Attack Type - the attack type of the damage dealt                                                 //
// 7 (integer) Missile ability - the ability id of the Multishots missile                                           //
// 8 (real) Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45,       //
// multishot will act like the target is at 90 degrees                                                              //
// 9 (damagetype) Damage type - the damage type of the damage dealth                                                //
// 10 (real) Target X - the x of the targeted point                                                                 //
// 11 (real) Target Y - the y of the targeted point                                                                 //
// 12 (boolexpr) - Conditions - What units shall it target                                                          //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION                                                                        //
// 13 (real) Missile speed - simply put-in the missiles speed                                                       //
//                                                                                                                  //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library MultiMisc

    public struct DamProperties
        real amount
        attacktype aType
        damagetype dType
        unit source
        unit mainTarget
        group shotGroup
        integer missile
        integer unitsShot = 0
        boolean advanced
        integer alreadyShot = 0
        boolean preDummy
        unit dummy
        timer clearer

        static method create takes nothing returns DamProperties
            local DamProperties DP = DamProperties.allocate()
            set DP.shotGroup = CreateGroup()
            set DP.clearer = CreateTimer()
            set DP.mainTarget = null
            return DP
        endmethod

        method onDestroy takes nothing returns nothing
            call GroupClear(this.shotGroup)
            call DestroyGroup(this.shotGroup)
            call DestroyTimer(this.clearer)
        endmethod
    endstruct

    public function ClearLeaks takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)
        local integer id = LoadInteger(Multishot_Table, tid, 'dmid')
        local DamProperties damage = LoadInteger(Multishot_Table, id, 'damp')
        call UnitApplyTimedLife(damage.dummy, 'BTLF', 0.01)
        call DamProperties.destroy(damage)
        call FlushChildHashtable(Multishot_Table, id)
        call FlushChildHashtable(Multishot_Table, tid)
        set t = null
    endfunction

endlibrary

library Multishot initializer Init requires MultiMisc, optional AdvancedMultishot

// DEBUG_MULTISHOT displays how the parabola looks like
// WANT_DAMAGE_BLOCK is a boolean. If set to true - Multishot will automatically block the incoming damage. Set it to false only
// if you want to do the damage block by yourself in the Multishot calling function.
// HP_BONUS_ABILITY is the Item ability, giving 30000 HP to the target unit for the damage block, and for the 'armor test'
// BUFF_APPLIED_ID is the Id of the buff, applied by the Multishot.
// DUMMY_ID is the Id of your dummy unit.
// TEST_DAMAGE_AMOUNT is the amount of damage the target takes to estimate its armor. Higher values give more precise results, but they may instantly kill the target.
// SPELL_ORDER is the string of the order, issued to the dummy to make it cast the multishot arrow. You could change it if
// you are using another spell as base, but acidbomb is the only good spell for this purpose.
// DUMMY_LIFE_TIME is the time the dummy stays alive. Set this to an amount, enough for the multishot to hit all its targets.
// Don't touch the rest.
    globals
        private constant boolean DEBUG_MULTISHOT = false
        private constant boolean WANT_DAMAGE_BLOCK = false
        private constant integer HP_BONUS_ABILITY = 'A02P'
        private constant integer BUFF_APPLIED_ID = 'B005'
        private constant integer DUMMY_ID = 'h00C'
        private constant real MAX_COLLISION_SIZE = 196.00
        private constant real TEST_DAMAGE_AMOUNT = 15000.00
        public constant string SPELL_ORDER = "acidbomb"
        public constant boolean WANT_MULTISHOT_HIT_EVENT = true
        public constant real MAX_DUMMY_LIFE_TIME = 20.00 // higher values allow slower missiles.
        public boolean Damage = false
        public hashtable Table = InitHashtable()
        private group g = CreateGroup()
        private effect array debug_effect // used only when DEBUG_MULTISHOT is true
    endglobals

// This sctuct is used when blocking the original damage.
    private struct DamBlocker
        real health
        unit u

        static method create takes unit u returns DamBlocker
            local DamBlocker DB
            set DB = DamBlocker.allocate()
            set DB.u = u
            set DB.health = GetWidgetLife(u)
            return DB
        endmethod
   
        static method BlockDamage takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = LoadInteger(Table, GetHandleId(t), 'blcr')
            call UnitRemoveAbility(this.u, HP_BONUS_ABILITY)
            call SetWidgetLife(this.u, this.health)
            call thistype.destroy(this)
            call FlushChildHashtable(Table, GetHandleId(t))
            call DestroyTimer(t)
            set t = null
        endmethod
    endstruct

// Calculates the unit's base damage, by multiplaying the damage dealt by the target's Armor rating
    private function GetBaseDamage takes MultiMisc_DamProperties damage, real dam returns nothing
        local timer time
        local real max_hp
        local DamBlocker DB = DamBlocker.create(damage.mainTarget)
        if GetUnitAbilityLevel(damage.mainTarget, 'BNab') > 0 then
            call UnitRemoveAbility(damage.mainTarget, 'BNab')
        endif
        static if WANT_DAMAGE_BLOCK then
            if GetUnitAbilityLevel(DB.u, HP_BONUS_ABILITY) == 0 then
                set time = CreateTimer()
                call SaveInteger(Table, GetHandleId(time), 'blcr', DB)
                call TimerStart(time, 0.00, false, function DamBlocker.BlockDamage)
                set time = null
            endif
        endif
        call UnitAddAbility(damage.mainTarget, HP_BONUS_ABILITY)
        set max_hp = GetUnitState(damage.mainTarget, UNIT_STATE_MAX_LIFE)
        call SetWidgetLife(damage.mainTarget, max_hp)
        call UnitDamageTarget(damage.dummy, damage.mainTarget, TEST_DAMAGE_AMOUNT, true, false, damage.aType, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
        set damage.amount = (TEST_DAMAGE_AMOUNT/(max_hp - GetWidgetLife(damage.mainTarget)))*dam
        call SetWidgetLife(damage.mainTarget, max_hp)
        static if not WANT_DAMAGE_BLOCK then
            call UnitRemoveAbility(damage.mainTarget, HP_BONUS_ABILITY)
            call SetWidgetLife(damage.mainTarget, DB.health)
            call DamBlocker.destroy(DB)
        endif
    endfunction

    function DrawParabola takes real range, real arc, real x, real y, real tarx, real tary, real ofst returns nothing
        local real a
        local real b
        local real m = range*Sin(arc/2)
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local integer count = 0
        local real tx = x
        local real ty = y
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        loop
            exitwhen count > 20
            set x = tx - Cos(init_ang)*parabola + Cos(init_ang + bj_PI/2)*(count - 10)*m/10
            set y = ty - Sin(init_ang)*parabola + Sin(init_ang + bj_PI/2)*(count - 10)*m/10
            set b = SquareRoot(4*parabola*parabola + (count - 10)*m*(count - 10)*m/100)
            set a = (init_ang + bj_PI/2) - Atan2(yFocus - y, xFocus - x)
            if a == bj_PI/2 then
                set a = parabola
            else
                set a = b*Cos(a)/Sin(2*a)
            endif
            set x = x + Cos(init_ang)*a
            set y = y + Sin(init_ang)*a
            call DestroyEffect(debug_effect[count])
            set debug_effect[count] = AddSpecialEffect("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl", x, y)
            set count = count + 1
        endloop
    endfunction

    function ConfigureDamProp takes unit u, damagetype dType, attacktype aType, unit mainTarget, integer missile, boolean advanced returns MultiMisc_DamProperties
        local MultiMisc_DamProperties this
        if GetUnitTypeId(u) == DUMMY_ID then
            set this = LoadInteger(Table, GetHandleId(u), 'damp') + 0
            if this == 0 then
                set this = MultiMisc_DamProperties.create()
                call SaveInteger(Table, GetHandleId(u), 'damp', this)
            endif
            set this.dummy = u
            set this.source = udg_MS_Source
            set this.preDummy = true
        else
            set this = MultiMisc_DamProperties.create()
            set this.dummy = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), 0.00)
            call SaveInteger(Table, GetHandleId(this.dummy), 'damp', this)
            call UnitApplyTimedLife(this.dummy, 'BTLF', MAX_DUMMY_LIFE_TIME + 0.05)
            set this.source = u
            set this.preDummy = false
        endif
        call UnitAddAbility(this.dummy, missile)
        set this.dType = dType
        set this.aType = aType
        set this.mainTarget = mainTarget
        set this.missile = missile
        set this.advanced = advanced
        call SaveInteger(Table, GetHandleId(this.clearer), 'dmid', GetHandleId(this.dummy))
        call TimerStart(this.clearer, MAX_DUMMY_LIFE_TIME, false, function MultiMisc_ClearLeaks)
        return this
    endfunction

    function ShootAllTargets takes MultiMisc_DamProperties damage, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
        local real x = GetUnitX(damage.dummy)
        local real y = GetUnitY(damage.dummy)
        local player p = GetOwningPlayer(damage.source)
        local real xTarget
        local real yTarget
        local unit FoG
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        local real tempAngle
        local boolean b
        call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
        if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
            call GroupRemoveUnit(g, damage.mainTarget)
        endif
        call GroupRemoveUnit(g, damage.source)
        loop
            set FoG = FirstOfGroup(g)
            exitwhen FoG == null
            set xTarget = GetUnitX(FoG)
            set yTarget = GetUnitY(FoG)
            set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
            if condition == null then
                set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            else
                set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            endif
            if b then
                if speed > 0 then
                    static if LIBRARY_AdvancedMultishot then
                        call MS_Shoot(damage, x, y, speed, FoG)
                    endif
                else
                    call IssueTargetOrder(damage.dummy, SPELL_ORDER, FoG)
                endif
                call GroupAddUnit(damage.shotGroup, FoG)
                set damage.unitsShot = damage.unitsShot + 1
            endif
            call GroupRemoveUnit(g, FoG)
        endloop
    endfunction

    function ShootRandomTargets takes MultiMisc_DamProperties damage, integer targets, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
        local integer left_c = 0
        local integer right_c = 0
        local unit array units
        local real x = GetUnitX(damage.dummy)
        local real y = GetUnitY(damage.dummy)
        local player p = GetOwningPlayer(damage.source)
        local unit FoG
        local integer dice
        local real xTarget
        local real yTarget
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        local real tempAngle
        local boolean b
        if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
            call GroupRemoveUnit(g, damage.mainTarget)
            set targets = targets - 1
        endif
        call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
        call GroupRemoveUnit(g, damage.source)
        loop
            set FoG = FirstOfGroup(g)
            exitwhen FoG == null
            set xTarget = GetUnitX(FoG)
            set yTarget = GetUnitY(FoG)
            set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
            if R2I(tempAngle/(2*bj_PI)) != 0 then
                set tempAngle = ModuloReal(tempAngle, 2*bj_PI)
            endif
            if condition == null then
                set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            else
                set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            endif
            if b then
                if tempAngle >= 0 and tempAngle < bj_PI then
                    set left_c = left_c + 1
                    set dice = GetRandomInt(1, left_c)
                    if dice <= targets and ( dice <= (targets + 1)/2 or (units[left_c] == null and left_c <= targets)) then
                        if left_c <= targets and ( left_c <= (targets + 1)/2 or units[left_c] == null ) then
                            set units[left_c] = units[dice]
                        endif
                        set units[dice] = FoG
                    endif
                else
                    set right_c = right_c + 1
                    set dice = GetRandomInt(1, right_c)
                    if dice <= targets and ( dice <= (targets + 1)/2 or (units[targets + 1 - right_c] == null and right_c <= targets)) then
                        if right_c <= targets and ( right_c <= (targets + 1)/2 or units[targets + 1 - right_c] == null ) then
                            set units[targets + 1 - right_c] = units[targets + 1 - dice]
                        endif
                        set units[targets + 1 - dice] = FoG
                    endif
                endif
            endif
            call GroupRemoveUnit(g, FoG)
        endloop
        set dice = 1
        loop
            exitwhen dice > targets
            if units[dice] != null then
                if speed > 0 then
                    static if LIBRARY_AdvancedMultishot then
                        call MS_Shoot(damage, x, y, speed, units[dice])
                    endif
                else
                    call IssueTargetOrder(damage.dummy, SPELL_ORDER, units[dice])
                endif
                call GroupAddUnit(damage.shotGroup, units[dice])
                set damage.unitsShot = damage.unitsShot + 1
                set units[dice] = null
            elseif targets - right_c > dice then
                set dice = targets - right_c - 1
            endif
            set dice = dice + 1
        endloop
    endfunction

    function MultishotTarget takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition returns nothing
        local MultiMisc_DamProperties damage
        if not Damage then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
            if fixedDamage then
                set damage.amount = initialDamage
            else
                call GetBaseDamage(damage, initialDamage)
            endif
            if alwaysHitsMainTarget then
                call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
                call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                set damage.unitsShot = damage.unitsShot + 1
                if targets == 1 then
                    set udg_MS_Current_Group = damage.shotGroup
                    set udg_MS_UnitsInGroup = damage.unitsShot
                    set udg_MS_Dummy = damage.dummy
                    return
                endif
            endif
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        endif
    endfunction

    function MultishotTargetAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition, real shotSpeed returns nothing
        local MultiMisc_DamProperties damage
        static if LIBRARY_AdvancedMultishot then
            if not Damage then
                static if DEBUG_MULTISHOT then
                    call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
                endif
                set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
                if fixedDamage then
                    set damage.amount = initialDamage
                else
                    call GetBaseDamage(damage, initialDamage)
                endif
                if alwaysHitsMainTarget then
                    call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
                    call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                    set damage.unitsShot = damage.unitsShot + 1
                    if targets == 1 then
                        set udg_MS_Current_Group = damage.shotGroup
                        set udg_MS_UnitsInGroup = damage.unitsShot
                        set udg_MS_Dummy = damage.dummy
                        return
                    endif
                endif
                if targets == 0 or targets > 1000 then
                    call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
                else
                    call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
                endif
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Dummy = damage.dummy
            endif
        else
            call MultishotTarget(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType, condition)
        endif
    endfunction

    function MultishotPoint takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition returns nothing
        local MultiMisc_DamProperties damage
        static if DEBUG_MULTISHOT then
            call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
        endif
        set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
        set damage.amount = initialDamage
        if targets == 0 or targets > 1000 then
            call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
        else
            call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
        endif
        set udg_MS_Current_Group = damage.shotGroup
        set udg_MS_UnitsInGroup = damage.unitsShot
        set udg_MS_Dummy = damage.dummy
    endfunction

    function MultishotPointAdvanced takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition, real shotSpeed returns nothing
        local MultiMisc_DamProperties damage
        static if LIBRARY_AdvancedMultishot then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
            set damage.amount = initialDamage
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        else
            call MultishotPoint(shooter, initialDamage, targets, arc, initRange, attackType, missileArt, offsetAngle, damageType, xSpell, ySpell, condition)
        endif
    endfunction

    function MultishotLight takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType returns nothing
        local MultiMisc_DamProperties damage
        if not Damage then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
            if fixedDamage then
                set damage.amount = initialDamage
            else
                call GetBaseDamage(damage, initialDamage)
            endif
            if alwaysHitsMainTarget then
                call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
                call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                set damage.unitsShot = damage.unitsShot + 1
                if targets == 1 then
                    set udg_MS_Current_Group = damage.shotGroup
                    set udg_MS_UnitsInGroup = damage.unitsShot
                    set udg_MS_Dummy = damage.dummy
                    return
                endif
            endif
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        endif
    endfunction

    function MultishotLightAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, real shotSpeed returns nothing
        static if LIBRARY_AdvancedMultishot then
            local MultiMisc_DamProperties damage
            if not Damage then
                static if DEBUG_MULTISHOT then
                    call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
                endif
                set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
                if fixedDamage then
                    set damage.amount = initialDamage
                else
                    call GetBaseDamage(damage, initialDamage)
                endif
                if alwaysHitsMainTarget then
                    call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
                    call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                    set damage.unitsShot = damage.unitsShot + 1
                    if targets == 1 then
                        set udg_MS_Current_Group = damage.shotGroup
                        set udg_MS_UnitsInGroup = damage.unitsShot
                        set udg_MS_Dummy = damage.dummy
                        return
                    endif
                endif
                if targets == 0 or targets > 1000 then
                    call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
                else
                    call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
                endif
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Dummy = damage.dummy
            endif
        else
            call MultishotLight(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType)
        endif
    endfunction

//! textmacro MulthishotOnHit takes SOURCE, VICTIM, AMOUNT, EVENT

// Checks if a unit, that has taken damage, has the Buff, applied by the Multishot. If it has it - applies the MS damage.
// When a unit is hit by the multishot's missile - the buff is removed, and the damage is applied
// It also sets a variable to 0.00 and then to 1.00 for the Extra triggers to run.
    private function MultishotDelay takes nothing returns boolean
        local MultiMisc_DamProperties damage
        if GetUnitAbilityLevel($VICTIM$, BUFF_APPLIED_ID ) > 0 then
            call UnitRemoveAbility( $VICTIM$, BUFF_APPLIED_ID )
            static if AdvancedMultishot_ALWAYS_USE_ADVANCED then
                if damage.advanced then
                    return false
                endif
            endif
            set damage = LoadInteger(Table, GetHandleId($SOURCE$), 'damp')
            set udg_MS_Damage = damage.amount
            if WANT_MULTISHOT_HIT_EVENT then
                set udg_MS_Dummy = $SOURCE$
                set udg_MS_Hit_Unit = $VICTIM$
                set udg_MS_Source = damage.source
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_Main_Target = damage.mainTarget
                set udg_MS_Missile = damage.missile
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Hit_Event = 1.00
                set udg_MS_Hit_Event = 0.00
            endif
            set Damage = true
            call UnitDamageTarget( udg_MS_Source, $VICTIM$, udg_MS_Damage, true, true, damage.aType, damage.dType, null )
            set Damage = false
            set damage.alreadyShot = damage.alreadyShot + 1
            if damage.alreadyShot == damage.unitsShot then
                call TimerStart(damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
            endif
        endif
        return false
    endfunction

// Initiation Trigger :P
    function Init takes nothing returns nothing
        set gg_trg_Multishot_v2_2_c = CreateTrigger(  )
        call TriggerRegisterVariableEvent( gg_trg_Multishot_v2_2_c, "$EVENT$", EQUAL, 0 ) // change the 0 to something else if needed.
        call TriggerAddCondition( gg_trg_Multishot_v2_2_c, Condition( function MultishotDelay ) )
    endfunction
//! endtextmacro

//! runtextmacro MulthishotOnHit("udg_DamageEventSource","udg_DamageEventTarget","udg_DamageEventAmount","udg_DamageEvent")

endlibrary
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------- CUSTOM MULTISHOT BY WereElf ------------------------------------------ //
// This is simply a Multishot, which is a lot better than the Barrage based one.                                    //
// The differences between this Multishot and the Barrage based one are:                                            //
//                                                                                                                  //
// 1) This multishot can be configured to hit only enemies infront of the shooter, while Barrage based can only     //
// target ALL the enemies in range (certain amount of them).                                                        //
//                                                                                                                  //
// 2) This multishot works with Orb effects (if you use the "Orbs" add-on library.                                  //
//                                                                                                                  //
// 3) You can make your own "orb effects" when you use this Multishot, since you get a "On Hit" event.              //
//                                                                                                                  //
// 4) You can set the targets allowed to 1 and 2, while for the Barrage based one - the minimum is 3.               //
//                                                                                                                  //
// 5) You can use this multishot for SPELLS!                                                                        //
//                                                                                                                  //
// 6) You can put some weird configurations for this multishot (like - hit allied buildings, and enemy units).      //
//                                                                                                                  //
// I could keep on listing the possibillities, offered by this library, but I wouldn't. I pointed out the most      //
// important ones.                                                                                                  //
//                                                                                                                  //
// HOW TO MAKE IT WORK:                                                                                             //
//                                                                                                                  //
// 0.a) You need a damage detecting system on your map. The multishot is built, using 1 of the most basic DDS(es),  //
// so it will work with any DDS. However, if you are using a different one - you need to go to the bottom of this   //
// library, and change the variables in the last textmacro to the ones, used by the other DDS.                      //
//                                                                                                                  //
// 0.b) You need to have a dummy on your map.                                                                       //
//                                                                                                                  //
// 1) Make a custom buff on your map, which will be used ONLY by the Multishot skills.                              //
//                                                                                                                  //
// 2) Make as many as you need skills, based on Acid Bomb, make them cost 0 mana, deal 0 damage, and make them      //
// APPLY THE BUFF YOU MADE IN STEP (1).                                                                             //
//                                                                                                                  //
// 3) Scroll down to the globals block of this library, and set the variables to "your" values.                     //
//                                                                                                                  //
// 4) When you want to make a trigger, using Multishot, just call "MultishotTarget" or "MultishotPoint", according  //
// to your needs.                                                                                                   //
//                                                                                                                  //
// 5) You could make a library with some pre-set Multishot functions (like MultishoRt in the sample map), so you    //
// don't have to put all the arguments when you call Multishot, but simply the ones you need.                       //
//                                                                                                                  //
// If you have the AdvancedMultishot library (add-on), you can also use "MultishotTargetAdvanced" and               //
// "MultishotPointAdvanced". The advanced functions are able to hit magic immune enemies, even if you have "Magic   //
// immune units resist ultimates" set to true.                                                                      //
//                                                                                                                  //
// THE ARGUMENTS THE FUNCTIONS TAKE:                                                                                //
//                                                                                                                  //
// MultishotTarg (both normal and the advanced) take:                                                               //
// 1 (unit) - Shooter - who is shooting                                                                             //
// 2 (unit) - Target - who is the original target of the shot                                                       //
// 3 (real) - Damage - the damage the shot should deal                                                              //
// 4 (integer) - Targets - how many targets its allowed to shoot at once                                            //
// 5 (real) - Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot                                                                               //
// 6 (real) - Range - the range multishot chooses enemies from.                                                     //
// 7 (boolean) - Always hits main targer - does the main target always get shot, or it only has a chance to get     //
// shot (if more than the max amount of units are in range)                                                         //
// 8 (attacktype) - Attack type - the attack type of the shooter. Its used for correct armor estimation, and thats  //
// the type of damage dealt in the end.                                                                             //
// 9 (integer) - Missile ability - the ability id of the Multishots missile.                                        //
// 10 (real) - Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45,    //
// multishot will act like the target is at 90 degrees.                                                             //
// 11 (boolean) - Fixed damage - if you want to deal some fixed value of damage, not based on the damage dealt -    //
// set this to true, otherwise - false.                                                                             //
// 12 (damagetype) - Damage type - if "Fixed damage" is set to false - this will act as "DAMAGE_TYPE_NORMAL", no    //
// matter what. Otherwise - this is the damage type multishot deals.                                                //
// 13 (boolexpr) - Conditions - What units shall it target                                                          //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION                                                                        //
// 14 (real) - Missile speed - simply put-in the missiles speed                                                     //
//                                                                                                                  //
// MultishotPoint (both normal and the advanced) take:                                                              //
// 1 (unit) Caster - who is shooting                                                                                //
// 2 (real) Damage - the damage dealt                                                                               //
// 3 (integer) Targets - how many targets its allowed to shoot at once                                              //
// 4 (real) Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want   //
// ALL the units in range to get shot                                                                               //
// 5 (real) Range - the range multishot chooses enemies from                                                        //
// 6 (attacktype) Attack Type - the attack type of the damage dealt                                                 //
// 7 (integer) Missile ability - the ability id of the Multishots missile                                           //
// 8 (real) Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45,       //
// multishot will act like the target is at 90 degrees                                                              //
// 9 (damagetype) Damage type - the damage type of the damage dealth                                                //
// 10 (real) Target X - the x of the targeted point                                                                 //
// 11 (real) Target Y - the y of the targeted point                                                                 //
// 12 (boolexpr) - Conditions - What units shall it target                                                          //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION                                                                        //
// 13 (real) Missile speed - simply put-in the missiles speed                                                       //
//                                                                                                                  //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library MultiMisc

    public struct DamProperties
        real amount
        attacktype aType
        damagetype dType
        unit source
        unit mainTarget
        group shotGroup
        integer missile
        integer unitsShot = 0
        boolean advanced
        integer alreadyShot = 0
        boolean preDummy
        unit dummy
        timer clearer

        static method create takes nothing returns DamProperties
            local DamProperties DP = DamProperties.allocate()
            set DP.shotGroup = CreateGroup()
            set DP.clearer = CreateTimer()
            set DP.mainTarget = null
            return DP
        endmethod

        method onDestroy takes nothing returns nothing
            call GroupClear(this.shotGroup)
            call DestroyGroup(this.shotGroup)
            call DestroyTimer(this.clearer)
        endmethod
    endstruct

    public function ClearLeaks takes nothing returns nothing
        local timer t = GetExpiredTimer()
        local integer tid = GetHandleId(t)
        local integer id = LoadInteger(Multishot_Table, tid, 'dmid')
        local DamProperties damage = LoadInteger(Multishot_Table, id, 'damp')
        call UnitApplyTimedLife(damage.dummy, 'BTLF', 0.01)
        call DamProperties.destroy(damage)
        call FlushChildHashtable(Multishot_Table, id)
        call FlushChildHashtable(Multishot_Table, tid)
        set t = null
    endfunction

endlibrary

library Multishot initializer Init requires MultiMisc, optional AdvancedMultishot

// DEBUG_MULTISHOT displays how the parabola looks like
// WANT_DAMAGE_BLOCK is a boolean. If set to true - Multishot will automatically block the incoming damage. Set it to false only
// if you want to do the damage block by yourself in the Multishot calling function.
// HP_BONUS_ABILITY is the Item ability, giving 30000 HP to the target unit for the damage block, and for the 'armor test'
// BUFF_APPLIED_ID is the Id of the buff, applied by the Multishot.
// DUMMY_ID is the Id of your dummy unit.
// TEST_DAMAGE_AMOUNT is the amount of damage the target takes to estimate its armor. Higher values give more precise results, but they may instantly kill the target.
// SPELL_ORDER is the string of the order, issued to the dummy to make it cast the multishot arrow. You could change it if
// you are using another spell as base, but acidbomb is the only good spell for this purpose.
// DUMMY_LIFE_TIME is the time the dummy stays alive. Set this to an amount, enough for the multishot to hit all its targets.
// Don't touch the rest.
    globals
        private constant boolean DEBUG_MULTISHOT = false
        private constant boolean WANT_DAMAGE_BLOCK = false
        private constant integer HP_BONUS_ABILITY = 'A02P'
        private constant integer BUFF_APPLIED_ID = 'B005'
        private constant integer DUMMY_ID = 'h00C'
        private constant real MAX_COLLISION_SIZE = 196.00
        private constant real TEST_DAMAGE_AMOUNT = 15000.00
        public constant string SPELL_ORDER = "acidbomb"
        public constant boolean WANT_MULTISHOT_HIT_EVENT = true
        public constant real MAX_DUMMY_LIFE_TIME = 20.00 // higher values allow slower missiles.
        public boolean Damage = false
        public hashtable Table = InitHashtable()
        private group g = CreateGroup()
        private effect array debug_effect // used only when DEBUG_MULTISHOT is true
    endglobals

// This sctuct is used when blocking the original damage.
    private struct DamBlocker
        real health
        unit u

        static method create takes unit u returns DamBlocker
            local DamBlocker DB
            set DB = DamBlocker.allocate()
            set DB.u = u
            set DB.health = GetWidgetLife(u)
            return DB
        endmethod
   
        static method BlockDamage takes nothing returns nothing
            local timer t = GetExpiredTimer()
            local thistype this = LoadInteger(Table, GetHandleId(t), 'blcr')
            call UnitRemoveAbility(this.u, HP_BONUS_ABILITY)
            call SetWidgetLife(this.u, this.health)
            call thistype.destroy(this)
            call FlushChildHashtable(Table, GetHandleId(t))
            call DestroyTimer(t)
            set t = null
        endmethod
    endstruct

// Calculates the unit's base damage, by multiplaying the damage dealt by the target's Armor rating
    private function GetBaseDamage takes MultiMisc_DamProperties damage, real dam returns nothing
        local timer time
        local real max_hp
        local DamBlocker DB = DamBlocker.create(damage.mainTarget)
        if GetUnitAbilityLevel(damage.mainTarget, 'BNab') > 0 then
            call UnitRemoveAbility(damage.mainTarget, 'BNab')
        endif
        static if WANT_DAMAGE_BLOCK then
            if GetUnitAbilityLevel(DB.u, HP_BONUS_ABILITY) == 0 then
                set time = CreateTimer()
                call SaveInteger(Table, GetHandleId(time), 'blcr', DB)
                call TimerStart(time, 0.00, false, function DamBlocker.BlockDamage)
                set time = null
            endif
        endif
        call UnitAddAbility(damage.mainTarget, HP_BONUS_ABILITY)
        set max_hp = GetUnitState(damage.mainTarget, UNIT_STATE_MAX_LIFE)
        call SetWidgetLife(damage.mainTarget, max_hp)
        call UnitDamageTarget(damage.dummy, damage.mainTarget, TEST_DAMAGE_AMOUNT, true, false, damage.aType, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
        set damage.amount = (TEST_DAMAGE_AMOUNT/(max_hp - GetWidgetLife(damage.mainTarget)))*dam
        call SetWidgetLife(damage.mainTarget, max_hp)
        static if not WANT_DAMAGE_BLOCK then
            call UnitRemoveAbility(damage.mainTarget, HP_BONUS_ABILITY)
            call SetWidgetLife(damage.mainTarget, DB.health)
            call DamBlocker.destroy(DB)
        endif
    endfunction

    function DrawParabola takes real range, real arc, real x, real y, real tarx, real tary, real ofst returns nothing
        local real a
        local real b
        local real m = range*Sin(arc/2)
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local integer count = 0
        local real tx = x
        local real ty = y
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        loop
            exitwhen count > 20
            set x = tx - Cos(init_ang)*parabola + Cos(init_ang + bj_PI/2)*(count - 10)*m/10
            set y = ty - Sin(init_ang)*parabola + Sin(init_ang + bj_PI/2)*(count - 10)*m/10
            set b = SquareRoot(4*parabola*parabola + (count - 10)*m*(count - 10)*m/100)
            set a = (init_ang + bj_PI/2) - Atan2(yFocus - y, xFocus - x)
            if a == bj_PI/2 then
                set a = parabola
            else
                set a = b*Cos(a)/Sin(2*a)
            endif
            set x = x + Cos(init_ang)*a
            set y = y + Sin(init_ang)*a
            call DestroyEffect(debug_effect[count])
            set debug_effect[count] = AddSpecialEffect("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl", x, y)
            set count = count + 1
        endloop
    endfunction

    function ConfigureDamProp takes unit u, damagetype dType, attacktype aType, unit mainTarget, integer missile, boolean advanced returns MultiMisc_DamProperties
        local MultiMisc_DamProperties this
        if GetUnitTypeId(u) == DUMMY_ID then
            set this = LoadInteger(Table, GetHandleId(u), 'damp') + 0
            if this == 0 then
                set this = MultiMisc_DamProperties.create()
                call SaveInteger(Table, GetHandleId(u), 'damp', this)
            endif
            set this.dummy = u
            set this.source = udg_MS_Source
            set this.preDummy = true
        else
            set this = MultiMisc_DamProperties.create()
            set this.dummy = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), 0.00)
            call SaveInteger(Table, GetHandleId(this.dummy), 'damp', this)
            call UnitApplyTimedLife(this.dummy, 'BTLF', MAX_DUMMY_LIFE_TIME + 0.05)
            set this.source = u
            set this.preDummy = false
        endif
        call UnitAddAbility(this.dummy, missile)
        set this.dType = dType
        set this.aType = aType
        set this.mainTarget = mainTarget
        set this.missile = missile
        set this.advanced = advanced
        call SaveInteger(Table, GetHandleId(this.clearer), 'dmid', GetHandleId(this.dummy))
        call TimerStart(this.clearer, MAX_DUMMY_LIFE_TIME, false, function MultiMisc_ClearLeaks)
        return this
    endfunction

    function ShootAllTargets takes MultiMisc_DamProperties damage, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
        local real x = GetUnitX(damage.dummy)
        local real y = GetUnitY(damage.dummy)
        local player p = GetOwningPlayer(damage.source)
        local real xTarget
        local real yTarget
        local unit FoG
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        local real tempAngle
        local boolean b
        call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
        if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
            call GroupRemoveUnit(g, damage.mainTarget)
        endif
        call GroupRemoveUnit(g, damage.source)
        loop
            set FoG = FirstOfGroup(g)
            exitwhen FoG == null
            set xTarget = GetUnitX(FoG)
            set yTarget = GetUnitY(FoG)
            set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
            if condition == null then
                set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            else
                set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            endif
            if b then
                if speed > 0 then
                    static if LIBRARY_AdvancedMultishot then
                        call MS_Shoot(damage, x, y, speed, FoG)
                    endif
                else
                    call IssueTargetOrder(damage.dummy, SPELL_ORDER, FoG)
                endif
                call GroupAddUnit(damage.shotGroup, FoG)
                set damage.unitsShot = damage.unitsShot + 1
            endif
            call GroupRemoveUnit(g, FoG)
        endloop
    endfunction

    function ShootRandomTargets takes MultiMisc_DamProperties damage, integer targets, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
        local integer left_c = 0
        local integer right_c = 0
        local unit array units
        local real x = GetUnitX(damage.dummy)
        local real y = GetUnitY(damage.dummy)
        local player p = GetOwningPlayer(damage.source)
        local unit FoG
        local integer dice
        local real xTarget
        local real yTarget
        local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
        local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
        local real xFocus = x + parabola*Cos(init_ang)
        local real yFocus = y + parabola*Sin(init_ang)
        local real tempAngle
        local boolean b
        if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
            call GroupRemoveUnit(g, damage.mainTarget)
            set targets = targets - 1
        endif
        call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
        call GroupRemoveUnit(g, damage.source)
        loop
            set FoG = FirstOfGroup(g)
            exitwhen FoG == null
            set xTarget = GetUnitX(FoG)
            set yTarget = GetUnitY(FoG)
            set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
            if R2I(tempAngle/(2*bj_PI)) != 0 then
                set tempAngle = ModuloReal(tempAngle, 2*bj_PI)
            endif
            if condition == null then
                set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            else
                set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
            endif
            if b then
                if tempAngle >= 0 and tempAngle < bj_PI then
                    set left_c = left_c + 1
                    set dice = GetRandomInt(1, left_c)
                    if dice <= targets and ( dice <= (targets + 1)/2 or (units[left_c] == null and left_c <= targets)) then
                        if left_c <= targets and ( left_c <= (targets + 1)/2 or units[left_c] == null ) then
                            set units[left_c] = units[dice]
                        endif
                        set units[dice] = FoG
                    endif
                else
                    set right_c = right_c + 1
                    set dice = GetRandomInt(1, right_c)
                    if dice <= targets and ( dice <= (targets + 1)/2 or (units[targets + 1 - right_c] == null and right_c <= targets)) then
                        if right_c <= targets and ( right_c <= (targets + 1)/2 or units[targets + 1 - right_c] == null ) then
                            set units[targets + 1 - right_c] = units[targets + 1 - dice]
                        endif
                        set units[targets + 1 - dice] = FoG
                    endif
                endif
            endif
            call GroupRemoveUnit(g, FoG)
        endloop
        set dice = 1
        loop
            exitwhen dice > targets
            if units[dice] != null then
                if speed > 0 then
                    static if LIBRARY_AdvancedMultishot then
                        call MS_Shoot(damage, x, y, speed, units[dice])
                    endif
                else
                    call IssueTargetOrder(damage.dummy, SPELL_ORDER, units[dice])
                endif
                call GroupAddUnit(damage.shotGroup, units[dice])
                set damage.unitsShot = damage.unitsShot + 1
                set units[dice] = null
            elseif targets - right_c > dice then
                set dice = targets - right_c - 1
            endif
            set dice = dice + 1
        endloop
    endfunction

    function MultishotTarget takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition returns nothing
        local MultiMisc_DamProperties damage
        if not Damage then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
            if fixedDamage then
                set damage.amount = initialDamage
            else
                call GetBaseDamage(damage, initialDamage)
            endif
            if alwaysHitsMainTarget then
                call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
                call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                set damage.unitsShot = damage.unitsShot + 1
                if targets == 1 then
                    set udg_MS_Current_Group = damage.shotGroup
                    set udg_MS_UnitsInGroup = damage.unitsShot
                    set udg_MS_Dummy = damage.dummy
                    return
                endif
            endif
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        endif
    endfunction

    function MultishotTargetAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition, real shotSpeed returns nothing
        local MultiMisc_DamProperties damage
        static if LIBRARY_AdvancedMultishot then
            if not Damage then
                static if DEBUG_MULTISHOT then
                    call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
                endif
                set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
                if fixedDamage then
                    set damage.amount = initialDamage
                else
                    call GetBaseDamage(damage, initialDamage)
                endif
                if alwaysHitsMainTarget then
                    call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
                    call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                    set damage.unitsShot = damage.unitsShot + 1
                    if targets == 1 then
                        set udg_MS_Current_Group = damage.shotGroup
                        set udg_MS_UnitsInGroup = damage.unitsShot
                        set udg_MS_Dummy = damage.dummy
                        return
                    endif
                endif
                if targets == 0 or targets > 1000 then
                    call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
                else
                    call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
                endif
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Dummy = damage.dummy
            endif
        else
            call MultishotTarget(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType, condition)
        endif
    endfunction

    function MultishotPoint takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition returns nothing
        local MultiMisc_DamProperties damage
        static if DEBUG_MULTISHOT then
            call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
        endif
        set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
        set damage.amount = initialDamage
        if targets == 0 or targets > 1000 then
            call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
        else
            call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
        endif
        set udg_MS_Current_Group = damage.shotGroup
        set udg_MS_UnitsInGroup = damage.unitsShot
        set udg_MS_Dummy = damage.dummy
    endfunction

    function MultishotPointAdvanced takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition, real shotSpeed returns nothing
        local MultiMisc_DamProperties damage
        static if LIBRARY_AdvancedMultishot then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
            set damage.amount = initialDamage
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        else
            call MultishotPoint(shooter, initialDamage, targets, arc, initRange, attackType, missileArt, offsetAngle, damageType, xSpell, ySpell, condition)
        endif
    endfunction

    function MultishotLight takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType returns nothing
        local MultiMisc_DamProperties damage
        if not Damage then
            static if DEBUG_MULTISHOT then
                call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
            endif
            set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
            if fixedDamage then
                set damage.amount = initialDamage
            else
                call GetBaseDamage(damage, initialDamage)
            endif
            if alwaysHitsMainTarget then
                call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
                call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                set damage.unitsShot = damage.unitsShot + 1
                if targets == 1 then
                    set udg_MS_Current_Group = damage.shotGroup
                    set udg_MS_UnitsInGroup = damage.unitsShot
                    set udg_MS_Dummy = damage.dummy
                    return
                endif
            endif
            if targets == 0 or targets > 1000 then
                call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
            else
                call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
            endif
            set udg_MS_Current_Group = damage.shotGroup
            set udg_MS_UnitsInGroup = damage.unitsShot
            set udg_MS_Dummy = damage.dummy
        endif
    endfunction

    function MultishotLightAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, real shotSpeed returns nothing
        static if LIBRARY_AdvancedMultishot then
            local MultiMisc_DamProperties damage
            if not Damage then
                static if DEBUG_MULTISHOT then
                    call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
                endif
                set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
                if fixedDamage then
                    set damage.amount = initialDamage
                else
                    call GetBaseDamage(damage, initialDamage)
                endif
                if alwaysHitsMainTarget then
                    call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
                    call GroupAddUnit(damage.shotGroup, damage.mainTarget)
                    set damage.unitsShot = damage.unitsShot + 1
                    if targets == 1 then
                        set udg_MS_Current_Group = damage.shotGroup
                        set udg_MS_UnitsInGroup = damage.unitsShot
                        set udg_MS_Dummy = damage.dummy
                        return
                    endif
                endif
                if targets == 0 or targets > 1000 then
                    call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
                else
                    call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
                endif
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Dummy = damage.dummy
            endif
        else
            call MultishotLight(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType)
        endif
    endfunction

//! textmacro MulthishotOnHit takes SOURCE, VICTIM, AMOUNT, EVENT

// Checks if a unit, that has taken damage, has the Buff, applied by the Multishot. If it has it - applies the MS damage.
// When a unit is hit by the multishot's missile - the buff is removed, and the damage is applied
// It also sets a variable to 0.00 and then to 1.00 for the Extra triggers to run.
    private function MultishotDelay takes nothing returns boolean
        local MultiMisc_DamProperties damage
        if GetUnitAbilityLevel($VICTIM$, BUFF_APPLIED_ID ) > 0 then
            call UnitRemoveAbility( $VICTIM$, BUFF_APPLIED_ID )
            static if AdvancedMultishot_ALWAYS_USE_ADVANCED then
                if damage.advanced then
                    return false
                endif
            endif
            set damage = LoadInteger(Table, GetHandleId($SOURCE$), 'damp')
            set udg_MS_Damage = damage.amount
            if WANT_MULTISHOT_HIT_EVENT then
                set udg_MS_Dummy = $SOURCE$
                set udg_MS_Hit_Unit = $VICTIM$
                set udg_MS_Source = damage.source
                set udg_MS_Current_Group = damage.shotGroup
                set udg_MS_Main_Target = damage.mainTarget
                set udg_MS_Missile = damage.missile
                set udg_MS_UnitsInGroup = damage.unitsShot
                set udg_MS_Hit_Event = 1.00
                set udg_MS_Hit_Event = 0.00
            endif
            set Damage = true
            call UnitDamageTarget( udg_MS_Source, $VICTIM$, udg_MS_Damage, true, true, damage.aType, damage.dType, null )
            set Damage = false
            set damage.alreadyShot = damage.alreadyShot + 1
            if damage.alreadyShot == damage.unitsShot then
                call TimerStart(damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
            endif
        endif
        return false
    endfunction

// Initiation Trigger :P
    function Init takes nothing returns nothing
        set gg_trg_Multishot_v2_2_c = CreateTrigger(  )
        call TriggerRegisterVariableEvent( gg_trg_Multishot_v2_2_c, "$EVENT$", EQUAL, 0 ) // change the 0 to something else if needed.
        call TriggerAddCondition( gg_trg_Multishot_v2_2_c, Condition( function MultishotDelay ) )
    endfunction
//! endtextmacro

//! runtextmacro MulthishotOnHit("udg_DamageEventSource","udg_DamageEventTarget","udg_DamageEventAmount","udg_DamageEvent")

endlibrary
  • Multishot caller
    • Events
      • Game - DamageModifierEvent becomes Equal to 1.00
    • Conditions
      • (Level of Multishot Never Miss for DamageEventSource) Greater than 0
    • Actions
      • Custom script: local integer dice
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • (Unit-type of DamageEventSource) Equal to Alleria Windrunner
        • Then - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • DamageEventAmount Greater than or equal to 125.00
            • Then - Actions
              • Custom script: call MultishotNormal( 0 ,55.00, 900.00, ATTACK_TYPE_CHAOS, 'A001')
            • Else - Actions
              • Custom script: call MultishotNormal( 7 , 55.00, 900.00, ATTACK_TYPE_CHAOS, 'A00P')
          • Custom script: if not Multishot_Damage then
          • Set DamageEventAmount = 0.00
          • Set DamageEventType = DamageTypeBlocked
          • Custom script: endif
        • Else - Actions
          • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
            • If - Conditions
              • (Unit-type of DamageEventSource) Equal to Value
            • Then - Actions
              • Custom script: set dice = GetRandomInt(2, 4)
              • Custom script: call MultishotNormal( dice , 60.00, 600.00, ATTACK_TYPE_PIERCE, 'A00P')
              • Custom script: if not Multishot_Damage then
              • Set DamageEventAmount = 0.00
              • Set DamageEventType = DamageTypeBlocked
              • Custom script: endif
            • Else - Actions
 
Status
Not open for further replies.
Top