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

Custom Multishot (COMEPLETE)

Status
Not open for further replies.
Level 12
Joined
Jan 2, 2016
Messages
973
Custom Multishot (COMEPLETE and UPDATED)

First I want to thank Wietlol for helping me with these triggers. Wouldn't have been able to finish this ability without him!
Let's not forget about SAUS, who helped me with the different configurations for different unit types.
And last, but not least - Zwiebelchen, who told me what my trigger was lacking (easy configuration, limitation to amount of units hit, and to make it completely MUI). He also helped me with the part to make it able to damage magic immune enemies
(for the "advanced" version)

FILES:
Custom Multishot v2.1.0.2 (vJASS) - greatly improved functionality (totally different way to use it).
View attachment Multishot Simple v1.6.3.w3x
View attachment Multishot Advanced v1.6.3.w3x

Describtion:
The "Custom Multishot" is a Multishot ability, as you guessed.
What's different about it from Barrage based Multishot?:
1) This Multishot has a configurable angle - you can configure how wide the area can be (the area from which it picks the units it can hit), and you can configure the angle of the area (it can be f. ex 90 degreese left from the original target).
2) You can make it hit only 2 targets, or even 1 target, and you can make that target DIFERENT than the original one.
3) You can potentially add bonus effects (f.ex: slow/armor reduce/DoT/stun, etc..) to the projectiles. (Maybe I'll make this automatic too in future updates)
4) It doesn't hit the same units every attack (if you have limited the amount of units it hits) every shot, it's not attacking always the same targets. It also hits the same amount of units from the left and from the right side of the target (if possible).
5) You can configure wether or not the original target should be getting hit.

Features:
1) The triggers can't be abused by spamming "stop"
2) Stacks with criticals.
3) It's MUI! (Thanks to Wietlol)
4) It's user friendly - players only need to set some variables in one trigger and viola!
5) Different unit types can have different range, angle and amount of targets. You can also configure different units to have a different attack type and you can make the range of multishot upgradeable by "Improved Bows", and different units can have different missile art!

Known issues:
If the original target has evasion, and it's an allied unit - if you miss it, no arrow will spawn.
On the contrary - if the unit is enemy, you can never miss it (maybe I'll trigger evasion in future versions)

There are 2 versions of this Multishot - a simple and an advanced one.
Simple version - used when gameplay constant "Magic Immune units resist ultimates" = false
Advanced version - used when gameplay constant "Magic Immune units resist ultimates" = true


Steps "how to make the Custom Multishot":

PREPERATIONS:
0) Get a damage detecting system on your map. I am using GDD on mine. If you use some other DDS - you need to change the event for the initial trigger accordingly.
1) Make a dummy passive ability based on critical strike, with 100% sucess chance, and 0% damage multiplier, and "Never miss" checked, and call it "Multishot" (or however you like)
2) Make a dummy unit (if you don't have one on your map already)
IMPORTANT: Set the dummy's turn rate to 0 and its cast backswing and cast point also to 0, so you can use only 1 dummy to hit all targets.
3) Make a custom buff, with no "Art-Target"
4) Make a custom ability (for the dummy to cast). Base it on a skill that deals damage and adds a buff to the unit, and replace its buff with the one you made in step (3). ~I used "Acid Bomb" ability~
Make its cooldown, mana cost and damage = 0
Set its projectile art to the one you want. This is what your missile will look like.
You can copy-paste the ability and give it different art for different units.
For the simple version - make it a hero ability, and make it require 6lvl.
5) Make the units, which are going to use this multishot have an "Instant" Weapon Type. Otherwise: your unit will fire a shot, and when the shot reaches the target - it woudln't deal any damage, but a cluster of arrows will appear from your unit.
6) Make a custom ability, based on an item ability, giving 30000 HP.
7) Create a hashtable in the game initialization and store it in a variable ~I called my variable "Table", as I'm using 1 for many triggers~

-------- That's about all you need to do in the Object editor (when we don't count step (0), which is a "general" one, and step (7), which is 2 extra actions in the initialization trigger --------

TRIGGERS:

Configurations triggers:
JASS:
//  YOU CAN USE THE GUI VERSION OF THE CONFIGURATIONS IF YOU ARE GETTING LOST IN THE JASS ONES, OR IF YOU ARE MORE COMFORTABLE WITH GUI.               //
//  HOWEVER - IF YOU DECIDE TO USE THIS CONFIGURATIONS TRIGGER - YOU CAN DELETE THE "MULTISHOT" GLOBAL VARIABLES.                                      //
//  IF YOU USE THE JASS VERSION OF THE CONFIGURATIONS - Temp_Unit (udg_Temp_Unit) AND THE GDD VARIABLES ARE THE ONLY ONES NEEDED FOR MULTISHOT TO WORK.//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  

    // unit_type = the unit type of the unit you are setting the configurations for
    // multishot_angle = if the shape of the area was (but it isn't) a cone, what angle should it have in its base?
    // multishot_range = the range of your unit (or the range of the multishot, if they are different)
    // multishot_targets = the max amount of units that can be hit at once (leave to 0 if you want ALL units to be hit)
    // upgradeable = can the unit's range be upgraded with "Improved Bows"
    // attack_type = the attack type of the unit: 0 = spell; 1 = normal; 2 = pierce; 3 = siege; 4 = magic; 5 = chaos; 6 = hero; 7 (and above) = instant kill
    // always_hits_main_target = does the unit with multishot always hit its original target
    // missile = the missile model (you need to have different dummy abilities, with different models to use this option)
    // offset_angle = the angle the parabola will be at, instead of straight towards the original target.
    // ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

function Save_Properties takes integer ut, real ang, real rang, integer targ, integer a_type, boolean upg, boolean ahmt, real ofs_ang, integer misl returns nothing
    set ang = ( ( rang - ( rang * Pow(CosBJ(( ang / 2.00 )), 2.00) ) ) / ( 4.00 * CosBJ(( ang / 2.00 )) ) )
    set rang = ( rang + 62.00 )
     call SaveReal(udg_Table, ut, 'rang', rang)
     call SaveReal(udg_Table, ut , 'para', ang)
     call SaveReal(udg_Table, ut , 'ofst', ofs_ang * bj_DEGTORAD)
     call SaveBoolean(udg_Table, ut , 'upgr', upg)
     call SaveBoolean(udg_Table, ut , 'ahmt', ahmt)
     call SaveInteger(udg_Table, ut , 'targ', targ)
     call SaveInteger(udg_Table, ut , 'atyp', a_type)
     call SaveInteger(udg_Table, ut , 'misl', misl)
endfunction

function Trig_Multishot_Configuration_Actions takes nothing returns nothing
    // Death Archer
    local integer unit_type = 'h001'
    local real multishot_angle = 50.00
    local real multishot_range = 900.00
    local integer multishot_targets = 3
    local integer attack_type = 5
    local boolean upgradeable = false
    local boolean always_hits_main_target = false
    local real offset_angle = 90.00
    local integer missile = 'A001'
    call Save_Properties(unit_type, multishot_angle, multishot_range, multishot_targets, attack_type, upgradeable, always_hits_main_target, offset_angle, missile)
    // Elite Night Elf Archer
    set unit_type = 'e00E'
    set multishot_angle = 40.00
    set multishot_range = 600.00
    set multishot_targets = 3
    set attack_type = 2
    set upgradeable = true
    set always_hits_main_target = true
    set offset_angle = 0.00
    set missile = 'A00P'
    call Save_Properties(unit_type, multishot_angle, multishot_range, multishot_targets, attack_type, upgradeable, always_hits_main_target, offset_angle, missile)
    // Copy-paste configurations above this line //
    call DestroyTrigger(GetTriggeringTrigger())
endfunction


//===========================================================================
function InitTrig_Multishot_Configuration takes nothing returns nothing
    set gg_trg_Multishot_Configuration = CreateTrigger(  )
    call TriggerRegisterTimerEventSingle( gg_trg_Multishot_Configuration, 0.00 )
    call TriggerAddAction( gg_trg_Multishot_Configuration, function Trig_Multishot_Configuration_Actions )
endfunction
  • Multishot Constants
    • Events
      • Time - Elapsed game time is 0.00 seconds
    • Conditions
    • Actions
      • -------- MultishotAngle = if the shape of the area was (but it isn't) a cone, what angle should it have in its base? --------
      • -------- MultishotRange = the range of your unit (or the range of the multishot, if they are different) --------
      • -------- MultishotTargets = the max amount of units that can be hit at once (leave to 0 if you want ALL units to be hit) --------
      • -------- Upgradeable = can the unit's range be upgraded with "Improved Bows" --------
      • -------- AttackType = the attack type of the unit: 0 = spell; 1 = normal; 2 = pierce; 3 = siege; 4 = magic; 5 = chaos; 6 = hero; 7 (and above) = instant kill --------
      • -------- Always_Hits_Main_Target = does the unit with multishot always hit its original target --------
      • -------- Missile = the missile model (you need to have different dummy abilities, with different models to use this option) --------
      • -------- OffsetAngle = the angle the parabola will be at, instead of straight towards the original target. --------
      • -------- ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------
      • -------- Death Archer --------
      • Set UnitType = Death Archer
      • Set MultishotAngle = 50.00
      • Set MultishotRange = 900.00
      • Set MultishotTargets = 9
      • Set AttackType = 5
      • Set Upgradeable = False
      • Set Always_Hits_Main_Target = True
      • Set OffsetAngle = 90.00
      • Set Missile = Arrow 2
      • Custom script: call ExecuteFunc("Save_Properties")
      • -------- Elite Night Elf Archer --------
      • Set UnitType = Elite Night Elf Archer
      • Set MultishotAngle = 40.00
      • Set MultishotRange = 600.00
      • Set MultishotTargets = 3
      • Set AttackType = 2
      • Set Upgradeable = True
      • Set Always_Hits_Main_Target = True
      • Set OffsetAngle = 0.00
      • Set Missile = Arrow 1
      • Custom script: call ExecuteFunc("Save_Properties")
      • -------- copy-paste properties above this line --------
      • -------- Do NOT touch the actions below --------
      • Custom script: call DestroyTrigger(GetTriggeringTrigger())
      • Custom script: endfunction
      • Custom script: function Save_Properties takes nothing returns nothing
      • Set MultishotAngle = ((MultishotRange - (MultishotRange x (Power((Cos((MultishotAngle / 2.00))), 2.00)))) / (4.00 x (Cos((MultishotAngle / 2.00)))))
      • Set MultishotRange = (MultishotRange + 62.00)
      • Custom script: call SaveReal(udg_Table, udg_UnitType, 'rang', udg_MultishotRange)
      • Custom script: call SaveReal(udg_Table, udg_UnitType , 'para', udg_MultishotAngle)
      • Custom script: call SaveReal(udg_Table, udg_UnitType , 'ofst', udg_OffsetAngle * bj_DEGTORAD)
      • Custom script: call SaveBoolean(udg_Table, udg_UnitType , 'upgr', udg_Upgradeable)
      • Custom script: call SaveBoolean(udg_Table, udg_UnitType , 'ahmt', udg_Always_Hits_Main_Target)
      • Custom script: call SaveInteger(udg_Table, udg_UnitType , 'targ', udg_MultishotTargets)
      • Custom script: call SaveInteger(udg_Table, udg_UnitType , 'atyp', udg_AttackType)
      • Custom script: call SaveInteger(udg_Table, udg_UnitType , 'misl', udg_Missile)
Simply set and forget.

Advanced version trigger:
JASS:
function Hit_Damage takes nothing returns nothing
    local integer id = GetHandleId(udg_GDD_DamageSource)
    local real damage = LoadReal(udg_Table, id, 'dama')
    local integer a_type = LoadInteger(udg_Table, id, 'atyp')
    call UnitRemoveBuffBJ( 'B005', udg_GDD_DamagedUnit )
    call UnitDamageTargetBJ( udg_GDD_DamageSource, udg_GDD_DamagedUnit, damage, ConvertAttackType(a_type), DAMAGE_TYPE_NORMAL )
endfunction

function Multishot_Delay takes nothing returns boolean
    if ( not ( UnitHasBuffBJ(udg_GDD_DamagedUnit, 'B005') == true ) ) then
        return false
    endif
    call Hit_Damage()
    return true
endfunction

function TableClear takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), 'dumy')
    call FlushChildHashtable(udg_Table, GetHandleId(u))
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function DamageBlock takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), 'targ')
    local real hp = LoadReal(udg_Table, GetHandleId(t), 'life')
    call UnitRemoveAbility(u, 'A02P')
    call SetWidgetLife(u, hp)
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function DamageApply takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local integer id = GetHandleId(t)
    local unit targ = LoadUnitHandle(udg_Table, id, 'targ')
    local unit dummy = LoadUnitHandle(udg_Table, id, 'dumy')
    local real dam = LoadReal(udg_Table, id, 'dmge')
    local integer a_type = LoadInteger(udg_Table, id, 'atyp')
    call UnitDamageTarget(dummy, targ, dam, true, false, ConvertAttackType(a_type), DAMAGE_TYPE_NORMAL, null)
    call FlushChildHashtable(udg_Table, id)
    call DestroyTimer(t)
    set t = null
    set targ = null
    set dummy = null
endfunction

function ImmunityPart takes unit u, integer id, integer old_immunity returns integer
    if( GetUnitAbilityLevel(u, id) > 0 ) then
        call UnitRemoveAbility(u,id)
        return id
    endif
    return old_immunity
endfunction

function FakeDamage takes unit targ, real dam, unit d_dealer, real delay, integer a_type returns nothing
    local timer t = CreateTimer()
    local integer id = GetHandleId(t)
    call SaveUnitHandle(udg_Table, id, 'targ', targ)
    call SaveUnitHandle(udg_Table, id, 'dumy', d_dealer)
    call SaveReal(udg_Table, id, 'dmge', dam)
    call SaveInteger(udg_Table, id, 'atyp', a_type)
    call TimerStart(t, delay, false, function DamageApply)
    set t = null
    set targ = null
    set d_dealer = null
endfunction

function Shoot takes unit target, real damage, unit dummy, integer a_type, real x1, real y1, real x2, real y2 returns nothing
    local integer immunity = 0
    local real delay
    set immunity = ImmunityPart(target, 'Amim', immunity)
    set immunity = ImmunityPart(target, 'ACm2', immunity)
    set immunity = ImmunityPart(target, 'ACm3', immunity)
    set immunity = ImmunityPart(target, 'ACmi', immunity)
    call IssueTargetOrder(dummy, "acidbomb", target)
    if immunity != 0 then
        set delay = SquareRoot(((y2-y1)*(y2-y1))+((x2-x1)*(x2-x1)))/900.00
        call FakeDamage(target, damage, dummy, delay, a_type)
        call UnitAddAbility(target, immunity)
    endif
    set target = null
    set dummy = null
endfunction

function Multishot takes nothing returns nothing
    local unit m_targ = udg_GDD_DamagedUnit
    local unit shooter = udg_GDD_DamageSource
    local real x_u = GetUnitX(shooter)
    local real y_u = GetUnitY(shooter)
    local real x_t = GetUnitX(m_targ)
    local real y_t = GetUnitY(m_targ)
    local real x_p
    local real y_p
    local unit s_targ
    local real health = GetWidgetLife(m_targ)
    local timer time = CreateTimer()
    local integer id = GetUnitTypeId(shooter)
    local integer targets = LoadInteger(udg_Table, id, 'targ')
    local integer a_type = LoadInteger(udg_Table, id, 'atyp')
    local real range = LoadReal(udg_Table, id, 'rang')
    local real dist = LoadReal(udg_Table, id, 'para')
    local boolean upg = LoadBoolean(udg_Table, id, 'upgr')
    local boolean al_hits_main_tar = LoadBoolean(udg_Table, id, 'ahmt')
    local integer missile = LoadInteger(udg_Table, id, 'misl')
    local real ofst_ang = LoadReal(udg_Table, id, 'ofst')
    local real dam
    local real init_ang = Atan2(y_t-y_u,x_t-x_u) + ofst_ang
    local unit dummy = CreateUnit(GetOwningPlayer(shooter), 'h00C', x_u, y_u, 0.00)
    local real max_hp
    local group g
    local integer count
    local integer d_id = GetHandleId(dummy)
    local timer table_clear = CreateTimer()
    local integer left_c
    local integer right_c
    local unit array left
    local unit array right
    local integer dice
    set x_p = x_u + dist*Cos(init_ang)
    set y_p = y_u + dist*Sin(init_ang)
    set init_ang = (RSignBJ(dist)-1)*bj_PI/2 + init_ang
    if upg then
        set range = range + ( 200.00 * I2R(GetPlayerTechCountSimple('Reib', GetOwningPlayer(shooter))) )
    endif
    call UnitApplyTimedLife(dummy, 'BTLF', 2.00)
    call UnitAddAbility(dummy, missile)
    call UnitAddAbility(m_targ, 'A02P')
    set max_hp = GetUnitState(m_targ, UNIT_STATE_MAX_LIFE)
    call SetWidgetLife(m_targ, max_hp)
    call UnitDamageTargetBJ(dummy, m_targ, 15000.00, ConvertAttackType(a_type), DAMAGE_TYPE_NORMAL)
    set dam = (15000.00/(max_hp - GetWidgetLife(m_targ))) * udg_GDD_Damage
    call SetWidgetLife(m_targ, max_hp)
    call SaveReal(udg_Table, d_id, 'dama', dam)
    call SaveInteger(udg_Table, d_id, 'atyp', a_type)
    call SaveUnitHandle(udg_Table, GetHandleId(time), 'targ', m_targ)
    call SaveReal(udg_Table, GetHandleId(time), 'life', health)
    call TimerStart(time, 0.00, false, function DamageBlock)
    set time = null
    if al_hits_main_tar then
        call Shoot(m_targ, dam, dummy, a_type, x_u , y_u , x_t , y_t)
    endif
    if IsPlayerAlly(GetOwningPlayer(shooter), GetOwningPlayer(m_targ)) then
        if not al_hits_main_tar then
            call Shoot(m_targ, dam, dummy, a_type, x_u , y_u , x_t , y_t)
        endif
    else
        set g = CreateGroup()
        set s_targ = null
        if targets == 0 then
            call GroupEnumUnitsInRange(g, x_u , y_u , range, null)
                loop
                    set s_targ = FirstOfGroup(g)
                    exitwhen s_targ == null
                    if s_targ != m_targ or not al_hits_main_tar then
                        set x_t = GetUnitX(s_targ)
                        set y_t = GetUnitY(s_targ)
                        set ofst_ang = Atan2(y_t-y_p,x_t-x_p)-init_ang
                        if IsPlayerEnemy(GetOwningPlayer(shooter), GetOwningPlayer(s_targ)) and IsUnitAliveBJ(s_targ) and (x_t-x_p)*(x_t-x_p)+(y_t-y_p)*(y_t-y_p) <= (( 2.00 * dist) / ( 1 - Cos(ofst_ang)))*((2.00 * dist) / ( 1 - Cos(ofst_ang))) then
                            call Shoot(s_targ, dam, dummy, a_type, x_u , y_u , x_t , y_t)
                        endif
                    endif
                    call GroupRemoveUnit(g, s_targ)
                endloop
        else
            if al_hits_main_tar then
                set count = 1
            else
                set count = 0
            endif
            set left_c = -1
            set right_c = -1
            call GroupEnumUnitsInRange(g, x_u, y_u, range, null)
                loop
                    set s_targ = FirstOfGroup(g)
                    exitwhen s_targ == null
                    if s_targ != m_targ or not al_hits_main_tar then
                        set x_t = GetUnitX(s_targ)
                        set y_t = GetUnitY(s_targ)
                        set ofst_ang = ModuloReal(Atan2(y_t-y_p,x_t-x_p)-init_ang, 2*bj_PI)
                        if IsPlayerEnemy(GetOwningPlayer(shooter), GetOwningPlayer(s_targ)) and IsUnitAliveBJ(s_targ) and (x_t-x_p)*(x_t-x_p)+(y_t-y_p)*(y_t-y_p) <= (( 2.00 * dist) / ( 1 - Cos(ofst_ang)))*(( 2.00 * dist) / ( 1 - Cos(ofst_ang))) then
                            if ofst_ang >= 0 or ofst_ang < bj_PI then
                                set left_c = left_c+1
                                set left[left_c] = s_targ
                            else
                                set right_c = right_c+1
                                set right[right_c] = s_targ
                            endif
                        endif
                    endif
                    call GroupRemoveUnit(g, s_targ)
                endloop
            if ( left_c > right_c ) then
                loop
                    exitwhen right_c == -1
                    if count <= targets/2 then
                        set dice = GetRandomInt(0, right_c)
                        set x_t = GetUnitX(right[dice])
                        set y_t = GetUnitY(right[dice])
                        call Shoot(right[dice], dam, dummy, a_type, x_u, y_u, x_t, y_t)
                        set count = count + 1
                        set right[dice] = right[right_c]
                    endif
                    set right[right_c] = null
                    set right_c = right_c - 1
                endloop
                loop
                    exitwhen left_c == -1
                    if count < targets then
                        set dice = GetRandomInt(0, left_c)
                        set x_t = GetUnitX(left[dice])
                        set y_t = GetUnitY(left[dice])
                        call Shoot(left[dice], dam, dummy, a_type, x_u, y_u, x_t, y_t)
                        set count = count + 1
                        set left[dice] = left[left_c]
                    endif
                    set left[left_c] = null
                    set left_c = left_c - 1
                endloop
            else
                loop
                    exitwhen left_c == -1
                    if count <= targets/2 then
                        set dice = GetRandomInt(0, left_c)
                        set x_t = GetUnitX(left[dice])
                        set y_t = GetUnitY(left[dice])
                        call Shoot(left[dice], dam, dummy, a_type, x_u, y_u, x_t, y_t)
                        set count = count + 1
                        set left[dice] = left[left_c]
                    endif
                    set left[left_c] = null
                    set left_c = left_c - 1
                endloop
                loop
                    exitwhen right_c == -1
                    if count < targets then
                        set dice = GetRandomInt(0, right_c)
                        set x_t = GetUnitX(right[dice])
                        set y_t = GetUnitY(right[dice])
                        call Shoot(right[dice], dam, dummy, a_type, x_u, y_u, x_t, y_t)
                        set count = count + 1
                        set right[dice] = right[right_c]
                    endif
                    set right[right_c] = null
                    set right_c = right_c - 1
                endloop
            endif
        endif
        call DestroyGroup(g)
        set g = null
    endif
    call SaveUnitHandle(udg_Table, GetHandleId(table_clear), 'dumy', dummy)
    call TimerStart(table_clear, 1.9, false, function TableClear)
    set table_clear = null
    set m_targ = null
    set shooter = null
    set time = null
    set dummy = null
endfunction

function Trig_Multishot_Conditions takes nothing returns boolean
    if ( not ( GetUnitAbilityLevelSwapped('A000', udg_GDD_DamageSource) > 0 ) ) then
        return false
    endif
    call Multishot()
    return false
endfunction

//===========================================================================
function InitTrig_Multishot takes nothing returns nothing
    local trigger delay
    set gg_trg_Multishot = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Multishot, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Multishot, Condition( function Trig_Multishot_Conditions ) )
    set delay = CreateTrigger(  )
    call TriggerRegisterVariableEvent( delay, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( delay, Condition( function Multishot_Delay ) )
    set delay = null
endfunction

Simple version trigger:
JASS:
function Hit_Damage takes nothing returns nothing
    local integer id = GetHandleId(udg_GDD_DamageSource)
    local real damage = LoadReal(udg_Table, id, 'dama')
    local integer a_type = LoadInteger(udg_Table, id, 'atyp')
    call UnitRemoveBuffBJ( 'B005', udg_GDD_DamagedUnit )
    call UnitDamageTargetBJ( udg_GDD_DamageSource, udg_GDD_DamagedUnit, damage, ConvertAttackType(a_type), DAMAGE_TYPE_NORMAL )
endfunction

function Multishot_Delay takes nothing returns boolean
    if ( not ( UnitHasBuffBJ(udg_GDD_DamagedUnit, 'B005') == true ) ) then
        return false
    endif
    call Hit_Damage()
    return true
endfunction

function TableClear takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), 'dumy')
    call FlushChildHashtable(udg_Table, GetHandleId(u))
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function DamageBlock takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local unit u = LoadUnitHandle(udg_Table, GetHandleId(t), 'targ')
    local real hp = LoadReal(udg_Table, GetHandleId(t), 'life')
    call UnitRemoveAbility(u, 'A02P')
    call SetWidgetLife(u, hp)
    call FlushChildHashtable(udg_Table, GetHandleId(t))
    call DestroyTimer(t)
    set t = null
    set u = null
endfunction

function Multishot takes nothing returns nothing
    local unit m_targ = udg_GDD_DamagedUnit
    local unit shooter = udg_GDD_DamageSource
    local real x_u = GetUnitX(shooter)
    local real y_u = GetUnitY(shooter)
    local real x_t = GetUnitX(m_targ)
    local real y_t = GetUnitY(m_targ)
    local real x_p
    local real y_p
    local unit s_targ
    local real health = GetWidgetLife(m_targ)
    local timer time = CreateTimer()
    local integer id = GetUnitTypeId(shooter)
    local integer targets = LoadInteger(udg_Table, id, 'targ')
    local integer a_type = LoadInteger(udg_Table, id, 'atyp')
    local real range = LoadReal(udg_Table, id, 'rang')
    local real dist = LoadReal(udg_Table, id, 'para')
    local boolean upg = LoadBoolean(udg_Table, id, 'upgr')
    local boolean al_hits_main_tar = LoadBoolean(udg_Table, id, 'ahmt')
    local integer missile = LoadInteger(udg_Table, id, 'misl')
    local real ofst_ang = LoadReal(udg_Table, id, 'ofst')
    local real dam
    local real init_ang = Atan2(y_t-y_u,x_t-x_u) + ofst_ang
    local unit dummy = CreateUnit(GetOwningPlayer(shooter), 'h00C', x_u, y_u, 0.00)
    local real max_hp
    local group g
    local integer count
    local integer d_id = GetHandleId(dummy)
    local timer table_clear = CreateTimer()
    local integer left_c
    local integer right_c
    local unit array left
    local unit array right
    local integer dice
    set x_p = x_u + dist*Cos(init_ang)
    set y_p = y_u + dist*Sin(init_ang)
    set init_ang = (RSignBJ(dist)-1)*bj_PI/2 + init_ang
    if upg then
        set range = range + ( 200.00 * I2R(GetPlayerTechCountSimple('Reib', GetOwningPlayer(shooter))) )
    endif
    call UnitApplyTimedLife(dummy, 'BTLF', 2.00)
    call UnitAddAbility(dummy, missile)
    call UnitAddAbility(m_targ, 'A02P')
    set max_hp = GetUnitState(m_targ, UNIT_STATE_MAX_LIFE)
    call SetWidgetLife(m_targ, max_hp)
    call UnitDamageTargetBJ(dummy, m_targ, 15000.00, ConvertAttackType(a_type), DAMAGE_TYPE_NORMAL)
    set dam = (15000.00/(max_hp - GetWidgetLife(m_targ))) * udg_GDD_Damage
    call SetWidgetLife(m_targ, max_hp)
    call SaveReal(udg_Table, d_id, 'dama', dam)
    call SaveInteger(udg_Table, d_id, 'atyp', a_type)
    call SaveUnitHandle(udg_Table, GetHandleId(time), 'targ', m_targ)
    call SaveReal(udg_Table, GetHandleId(time), 'life', health)
    call TimerStart(time, 0.00, false, function DamageBlock)
    set time = null
    if al_hits_main_tar then
        call IssueTargetOrder(dummy, "acidbomb", m_targ)
    endif
    if IsPlayerAlly(GetOwningPlayer(shooter), GetOwningPlayer(m_targ)) then
        if not al_hits_main_tar then
            call IssueTargetOrder(dummy, "acidbomb", m_targ)
        endif
    else
        set g = CreateGroup()
        set s_targ = null
        if targets == 0 then
            call GroupEnumUnitsInRange(g, x_u , y_u , range, null)
                loop
                    set s_targ = FirstOfGroup(g)
                    exitwhen s_targ == null
                    if s_targ != m_targ or not al_hits_main_tar then
                        set x_t = GetUnitX(s_targ)
                        set y_t = GetUnitY(s_targ)
                        set ofst_ang = Atan2(y_t-y_p,x_t-x_p)-init_ang
                        if IsPlayerEnemy(GetOwningPlayer(shooter), GetOwningPlayer(s_targ)) and IsUnitAliveBJ(s_targ) and (x_t-x_p)*(x_t-x_p)+(y_t-y_p)*(y_t-y_p) <= (( 2.00 * dist) / ( 1 - Cos(ofst_ang)))*(( 2.00 * dist) / ( 1 - Cos(ofst_ang))) then
                            call IssueTargetOrder(dummy, "acidbomb", s_targ)
                        endif
                    endif
                    call GroupRemoveUnit(g, s_targ)
                endloop
        else
            if al_hits_main_tar then
                set count = 1
            else
                set count = 0
            endif
            set left_c = -1
            set right_c = -1
            call GroupEnumUnitsInRange(g, x_u, y_u, range, null)
                loop
                    set s_targ = FirstOfGroup(g)
                    exitwhen s_targ == null
                    if s_targ != m_targ or not al_hits_main_tar then
                        set x_t = GetUnitX(s_targ)
                        set y_t = GetUnitY(s_targ)
                        set ofst_ang = ModuloReal(Atan2(y_t-y_p,x_t-x_p)-init_ang , 2*bj_PI)
                        if IsPlayerEnemy(GetOwningPlayer(shooter), GetOwningPlayer(s_targ)) and IsUnitAliveBJ(s_targ) and (x_t-x_p)*(x_t-x_p)+(y_t-y_p)*(y_t-y_p) <= (( 2.00 * dist) / ( 1 - Cos(ofst_ang)))*(( 2.00 * dist) / ( 1 - Cos(ofst_ang))) then
                            if ofst_ang >= 0 or ofst_ang < bj_PI then
                                set left_c = left_c+1
                                set left[left_c] = s_targ
                            else
                                set right_c = right_c+1
                                set right[right_c] = s_targ
                            endif
                        endif
                    endif
                    call GroupRemoveUnit(g, s_targ)
                endloop
            if ( left_c > right_c ) then
                loop
                    exitwhen right_c == -1
                    if count <= targets/2 then
                        set dice = GetRandomInt(0, right_c)
                        set x_t = GetUnitX(right[dice])
                        set y_t = GetUnitY(right[dice])
                        call IssueTargetOrder(dummy, "acidbomb", right[dice])
                        set count = count + 1
                        set right[dice] = right[right_c]
                    endif
                    set right[right_c] = null
                    set right_c = right_c - 1
                endloop
                loop
                    exitwhen left_c == -1
                    if count < targets then
                        set dice = GetRandomInt(0, left_c)
                        set x_t = GetUnitX(left[dice])
                        set y_t = GetUnitY(left[dice])
                        call IssueTargetOrder(dummy, "acidbomb", left[dice])
                        set count = count + 1
                        set left[dice] = left[left_c]
                    endif
                    set left[left_c] = null
                    set left_c = left_c - 1
                endloop
            else
                loop
                    exitwhen left_c == -1
                    if count <= targets/2 then
                        set dice = GetRandomInt(0, left_c)
                        set x_t = GetUnitX(left[dice])
                        set y_t = GetUnitY(left[dice])
                        call IssueTargetOrder(dummy, "acidbomb", left[dice])
                        set count = count + 1
                        set left[dice] = left[left_c]
                    endif
                    set left[left_c] = null
                    set left_c = left_c - 1
                endloop
                loop
                    exitwhen right_c == -1
                    if count < targets then
                        set dice = GetRandomInt(0, right_c)
                        set x_t = GetUnitX(right[dice])
                        set y_t = GetUnitY(right[dice])
                        call IssueTargetOrder(dummy, "acidbomb", right[dice])
                        set count = count + 1
                        set right[dice] = right[right_c]
                    endif
                    set right[right_c] = null
                    set right_c = right_c - 1
                endloop
            endif
        endif
        call DestroyGroup(g)
        set g = null
    endif
    call SaveUnitHandle(udg_Table, GetHandleId(table_clear), 'dumy', dummy)
    call TimerStart(table_clear, 1.9, false, function TableClear)
    set table_clear = null
    set m_targ = null
    set shooter = null
    set time = null
    set dummy = null
endfunction

function Trig_Multishot_Conditions takes nothing returns boolean
    if ( not ( GetUnitAbilityLevelSwapped('A000', udg_GDD_DamageSource) > 0 ) ) then
        return false
    endif
    call Multishot()
    return false
endfunction

//===========================================================================
function InitTrig_Multishot takes nothing returns nothing
    local trigger delay
    set gg_trg_Multishot = CreateTrigger(  )
    call TriggerRegisterVariableEvent( gg_trg_Multishot, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( gg_trg_Multishot, Condition( function Trig_Multishot_Conditions ) )
    set delay = CreateTrigger(  )
    call TriggerRegisterVariableEvent( delay, "udg_GDD_Event", EQUAL, 0 )
    call TriggerAddCondition( delay, Condition( function Multishot_Delay ) )
    set delay = null
endfunction

EXPLANATIONS:
The configurations trigger simply sets some variables for each unit type, that is going to use the multishot.
The main trigger:
1) Saves the target's HP into a variable
2) Gives 30000 HP to the unit
3) Damages the unit for 15000 damage, to determine its armor type and value
4) Resets the unit's life to normal (after 0.00 timer)
5) Picks all the units in range (the unit's range)
6) If the unit is in a parabolic area (towards the main target + the offset angle) - it gets shot (by a dummy)
7) When the missile reaches the enemy - it loads values, stored in the dummy's hashtable (the dmg amount and the attack type), and causes the dummy to damage the target for the said damage, with the said attack type.

FIX LIST:
Version 1.0.1:
1) turned out MultiParaPt didn't need 2 values after all;
2) added extra condition: if there aren't enough enemies in MultiGroup[2] it will hit all the enemies from [2] and the rest will be from [1]. If there are enough enemies in [2] - it will start from [1] and take the rest from [2]. Before - if [2] didn't have enough units in it, but [1] had plenty - the multishot wouldn't have used up all the arrows it is allowed to.
Version 1.1:
1) Removed MS_Count, now it's completely MUI.
2) Stopped using the dummy's custom value to determine how much damage should it deal - using a hashtable now instead.
3) Replaced MS_Damage (array) with TempReal (NOT an array).
4) Trigger optimization (reduced the trigger's lenght)
Version 1.2:
- It can be configured differently for different unit types!
Version 1.2.1:
1) The parabola is now defined with polar coordinates - 2 points less needed for its definition AND 1 condition less needed. (The way it was defined before - it was shooting the enemies in 2 parabolas - 1 of which was 180 degreese from the original one, thus another condition was needed)
2) When shooting magic immune enemies - it spawns arrows now (unlike before), but they still don't deal damage.
Version 1.2.2:
1) Improved main trigger - now it doesn't need to divide by 0
2) Now I'm giving back the target's immunity right after shooting, instead of after 0,00 seconds.
3) Reduced (a bit) trigger lenght.
Version 1.3:
1) Fixed a bug, which appeared in V1.3.2 - was hitting main target twice
2) Now it hits and damages the main target, even if it's magic immune.
3) It doesn't add magic immune enemies to MultiGroup[1 and 2] anymore, to waste multishot's attacks (until I make the multishot able to hit them).
Version 1.4:
1) Multishot can hit Magic Immune units, even if they aren't the main target
2) Multishot's attack type can be configured differently for different unit types
3) Made 2 versions of the Multishot triggers: "Simple Multishot" - when magic immune units can't resist ultimates, and "Advanced Version" - when magic immune units can resist ultimates.
-------- Made 2 Sample Maps - 1 with the Simple Version, and 1 with the Advanced Version --------
Version 1.4.1:
1) Fixed a minor bug - if the "MultishotAngle" was set to a too high value, and the archer was mooved close to the target - she was shooting all the enemies in range. If you set the MultishotAngle to 180 - she will still do that.
2) Multishot (Advanced) is now 100% in JASS. - Needs 1 trigger less for its functionality, and it's more reliable.
Version 1.5:
1) Changed the configurations trigger - reduced its lenght, and now the unit-type configuring is automatic (you just need to set a variable).
2) Now MultishotAngle works exactly as you expect it to: angles from 0 to 179 deg hit units infront of the shooter, 180 deg hits all units (all around the shooter), angles from 181 to 360 hit enemies behind the shooter.
3) Now you can configure wether or not the main target should always be hit, or not (This doesn't do anything if you have set MultishotTargets to 0 unit to hit all enemies).
Version 1.6:
1) Both of the versions are now in JASS.
2) Only the 1 Global variable (+ the Global ones from the DDS) used. Reworked it to use local variables.
3) Fixed the "missing" issue (tho now the multishot never misses).
4) Now you can also configure missile art for different unit types.
5) Added an "offset angle" - if you want to hit only enemies 90 degreese to the left from the target - set it to 90 (this can be configured differently for different unit types)
6) Generally improved the code.
7) Now the configurations trigger has a JASS version and a GUI version (delete or disable the one, which you aren't going to use)
8) Fixed bug - before it was stacking the attack type bonus 2-ce (instead of once)
Version 1.6.0.1:
-Fixed bug on the advanced version (appeared in v1.6) - was removing a point while still needing it
Version 1.6.1:
1) Removed the Owner_Check function. Now the check is done inside the "if".
2) No more adding the units into 2 unit groups, and then returning random units form these groups back to the original group. Now the units are added into 2 unit arrays, and then random units are chosen and shot.
3) Due to (2) - there isn't anymore need to save the unit group handle and the counting integer into the dummy's hashtable. And udg_Temp_Unit (global unit variable) isn't needed anymore.
Version 1.6.2:
1) Replaced the 'locations' with 'coordinates' (x and y of locations), thus the trigger is faster.
2) Now the calculation of distance between points, and angle between points is done by simple formulas, instead of function calls, thus making the trigger even lighter.
3) Added a demo trigger (had some bugs, and I was using that trigger for testing, but decided to leave it there as an example of how you can configure multishot in the game)
Version 1.6.2.1:
1) Fixed a minor bug (which appeared in v1.6.2) - it was putting all the enemies either in 'left', either in 'right', instead of distributing them.
2) Fixed bug (has always been there, but hadn't noticed it)- multishot was targeting dead units, thus reducing the actual amount of units shot.
Version 1.6.3:
1) Fixed a major issue with the Advanced version (it was just 1 variable that was wrong, but it was causing a HUGE malfunction. Was using "left" instead of "right" in 1 place) - in some situations - the Multishot was hitting only targets left from the main target. (This bug must've appeared in v1.6.2 or 1.6.2.1, and since it the malfunction wasn't always happening - I didn't notice it right away.)
2) Made it work with radians, instead of degrees.
3) Improved the overall functionality
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
This has several flaws and wouldn't be approved in the spells section in it's current state.


Here are some of them:
- no configurables. The cone angle, range, number of missiles, damage type and allowed target types should be configurable as variables
- you use unit custom value. This is reserved to unit indexers.
- your life bonus ability used to detect damage reduction is 1) not needed and 2) will cause problems with other spells getting the max life of the unit, as it is removed after a nulltimer instead of instantly
- as the trigger of your ability is not an attack event, but a damage event, the multishot will not fire simultanously with the normal attack
- you create a dummy unit for each arrow instead of having ONE dummy unit cast all the arrows
- you can not be sure that the damage dealt to the unit while the "Arrow(MS)" buff is active is truly from the multishot ability, especially not when units deal AoE damage
- your MS_Count variable is weirdly implemented and will cause bugs if more than 100 multishot arrows are used at the same time (as it will arbitrarily reset the index to 0 then, overwriting existing values for arrows mid-air). This is not how we do dynamic indexing!
 
Level 12
Joined
Jan 2, 2016
Messages
973
- no configurables. The cone angle, range, number of missiles, damage type and allowed target types should be configurable as variables
- you use unit custom value. This is reserved to unit indexers.
- your life bonus ability used to detect damage reduction is 1) not needed and 2) will cause problems with other spells getting the max life of the unit, as it is removed after a nulltimer instead of instantly
- as the trigger of your ability is not an attack event, but a damage event, the multishot will not fire simultanously with the normal attack
- you create a dummy unit for each arrow instead of having ONE dummy unit cast all the arrows
- you can not be sure that the damage dealt to the unit while the "Arrow(MS)" buff is active is truly from the multishot ability, especially not when units deal AoE damage
- your MS_Count variable is weirdly implemented and will cause bugs if more than 100 multishot arrows are used at the same time (as it will arbitrarily reset the index to 0 then, overwriting existing values for arrows mid-air). This is not how we do dynamic indexing!
Are you sure you read my trigger? o.o
I'm creating the Dummy BEFORE starting the loop and then I'm making it cast "Arrow" to each picked unit (matching conditions) in the loop...

And I clearly said "Read the notes that I'm leaving" You can clearly see me explaining "how to limit the max number of targets" and "how to change the "angle" of the parabula". Today I will post pictures to make it more clear.

But if will make you happy - I could change the "Constants" to some variables, which players set themselves.

As for the custom value - I'm touching only the Dummy's custom value, not any of the units'. You don't need the custom value of the dummy, right?
Unless you need it for things like I'm using it for, but even if you need it - you will be making other dummies with other spells and you can set their values to other stuff. This trigger woudln't mess up with them.

I would gladly start removing the bonus life I'm giving the units in the 1-st trigger, but it will remove it before the damage is taken, thus the unit will 1) take damage before the arrow reaches it 2) will be damaged 2-ce.

As for the "I can't be sure that the units taking damage with "Arrow(MS) are taking damage from the multishot" - I can... They take 0 damage from the acid bomb and get the Arrow(MS) buff - the DDS runs and removes the Arrow(MS) before the unit can take any other damage, thus it woudln't run the event if the hit isn't from the "Arrow"

And... about the MS_Count... I will repeat myself "READ THE NOTES" I am explaining how you need to increase how much this count can grow and how to increase the Array's size, based on how many multishots can happen in the map for 1 second (1 second is the time needed for all the arrows to find their targets - when the targets are at max range)

EDIT: I read the post you linked... Nothing new under the sun. I am using dynamic indexing in some of my other triggers. However, in order to achieve dynamic indexing for the multishot - I'd need to make a counter how many times has the dummy casted "Arrow", and then I'd need to count how many times has the 3-rd trigger ran for the dummy which has cast it, BUT if a unit dies before the arrow has reached it - it will break the whole system.
You can just make the array size = 8192 and allow the MS_Counter to go as high as 8191, if you really worry that too many units will be casting it at the same time.
From the flaws you pointed out - only the 3-rd one is "real"... the only way I can think of (right now) how to avoid adding so much HP and removing it after a 0,00 timer is if another DDS is used instead (one that can block damage), but the map in which I made this trigger is using GDD, and it's a bit too late to rework all of my triggers, which are using DDS.
EDIT 2: I just tested the multishot on units with life bonus ability, based on the one which I'm using for the life bonus - the multishot works just fine.
 
Last edited:

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
This part is untrue. Since the attack type is set to instant, the multishot will launch projectiles at the normal time.
This is super incovenient, though, because you want your archer to fire missiles normally if they don't have that ability.

"If the prophet won't come to the mountain, the mountain must come to the prophet".
An ability should be adjusted for the unit, not the unit for the ability.

Are you sure you read my trigger? o.o
I'm creating the Dummy BEFORE starting the loop and then I'm making it cast "Arrow" to each picked unit (matching conditions) in the loop...
My bad. I'm not good at reading GUI triggers.

And I clearly said "Read the notes that I'm leaving" You can clearly see me explaining "how to limit the max number of targets" and "how to change the "angle" of the parabula". Today I will post pictures to make it more clear.

But if will make you happy - I could change the "Constants" to some variables, which players set themselves.
This is not to make me happy. These are the rules for the spell submissions. And I read that paragraph. Doesn't change the fact that your ability bugs out every 100 missiles.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
That's true, but so far there is no known way to avoid that. As such, it applies to all systems that simulate attacks for whatever reason.
Hmm... about time someone writes an "attack launched" library that uses some clever hacks to generate the missing event between "A unit is attacked" and "A unit takes damage".
 
Level 12
Joined
Jan 2, 2016
Messages
973
Okay, I updated it :)
Now players just set 3 variables and viola!
The condition for players to be able to set the max amount of targets being hit made the trigger 2-3 times longer :D But I defined it in such way, that if people decide to leave it hit all the enemies - it would act like it acted before (just 1 more "if" added).
I also spent half of the day trying to figure why wasn't the new version of the parabula working properly. Then I figured that MultiParaPt needed 2 values - and then it started working wonders! :)
EDIT: nope, MultiParaPt could be used with a single value :p
This new version of the parabula is like an elipse and the old version is like a circle, if you know what I mean :D
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
I am usint the custom value of the DUMMY. Please explain me how can this mess up with anything. Only way I can think of is if someone has a trigger "Set custom value of damaged unit to custom value of damaged unit + custom value of damage source". Personally I haven't used such triggers, and at the moment I can't think of a situation, where they'd be useful (except maybe if you want to have some inheritance in your map). But even then you could: Make another type of dummy let's say "Dummy 2" and use it ONLY for this multishot, and in the other trigger add condition "Damage source NOT equal to Dummy 2". Problem - solved.

Or if you REALLY don't wanna use the custom value - you can just replace that line with "set MultiDummy[MS_Count] to last created unit" after creating the dummy, then in the damage trigger add a loop from 1 to 100 (or to the value you've allowed MS_Count to grow to) and an if "if damage source is equal to MultiDummy[loop], then deal damage equal to MS_Damage[loop]", but as you can see - my method is easier.

Another method you can use is with hashtables, but I still haven't figured out a way to use unit variables as key for the hashtables. If there IS a way to do that - then you woudln't even be limited to 100 (or even 8192, if you've increased the MS_Damage's size and allowed the MS_Count to reach higher values) shots per second. You can simply save the damage into the hashtable, and call it in the damage trigger.

EDIT: Okay, I found a way to do this with a hashtable, so now I don't need to use the Dummy's custom value AND I don't need MS_Counter, and MS_Damage (which I replaced with TempReal) is NOT an array anymore :p
 
Last edited:
Level 12
Joined
Jan 2, 2016
Messages
973
Say, do you think I should re-build this system in vJASS?
Or should I keep it the way it is?
Or should I create a vJASS version, but keep this one as well?

EDIT: Meanwhile - I updated it. Fixed a bug on the Advanced version, and made it work with radians (instead of deg). I also fixed my nooby - jass if's (I had stuff like "if ( not (always_hits_main_targ) ) then" now it's simply "if not always_hits_main_targ then"; or "if ((s_targ == m_targ) and (always_hits_main_targ == true)) then (nothing) else (actions)" is now "if s_targ != m_targ or not always_hits_main_targ then (actions) endif") :p
This was my 1-st trigger made in jass, so back then I really wasn't so used to writing scripts :D

EDIT 2: I think I will make the vJASS version.
While updating the Multishot's version - I noticed that if you aren't the one who made this system (me) - you may not fully understand what's going on, and it may not work on your map(s).
Especially since there is 1 item ability, which ID you need to put in the trigger, and you need to search for it. While in vJASS I can just make a constant for it, and you can easily set it :p

EDIT 3: I made a vJASS version - link to it.
It's much easier to configure it, and it combines the Simple and the Avanced version into 1.
 
Last edited:
Level 18
Joined
Nov 21, 2012
Messages
835
I'm thinking about hero like Paladin who whats to use CustomMultishot.
What if it is a melee hero, but user wants to use CM for Paladin's ranged attacks.
And 2nd: hero can carry frost orb or any other which means missle will be diffrent. But in system we can register parameters for unit-type not for unit.
 
Level 12
Joined
Jan 2, 2016
Messages
973
Check version 1.8.2.
It's in vJASS, but it works in a completely different way.
There you don't configure the multishot for unit-types, you put whatever event you want, give it YOUR conditions, and simply call a function to make multishot happen.

You will see in the sameple map how you can put different configurations for the same unit.

You will also see how you can make spells, using on Multishot :)

And I guess you'd need to make several missile spells, and check if the Paladin is carrying an orb - if he is, use the missile art, for the orb :p

However, orbs will behave strangely - their effect will appear before the missile has hit the target.
I would recomend using "dummy" orbs, and trigger their effect in the "On Hit" event.

Tho I realize, that this would require another trigger to replace the real orb with a dummy orb if picked by the Paladin, who has the multishot.
And if he doesn't - change the orbs to dummy ones, when he learns the multishot ability.

I know it's complicated, but it is possible :p
 
Last edited:
Status
Not open for further replies.
Top