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:
Simply set and forget.
Advanced version trigger:
Simple version trigger:
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:
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)
-
-
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
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: