- Joined
- Feb 24, 2018
- Messages
- 71
Hi there ! I've added Custom Multishot v2.2c + Orbs add-on v1.3.1 in my map but I can't figure out how can I set the number of targets for the multishot ability. For example : level 1 - 2 targets , level 2 - 4 targets , level 3 - 6 targets. Any idea ? Thanks !
JASS:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// --------------------------------- ADVANCED - PART OF WereElf's CUSTOM MULTISHOT -------------------------------- //
// This library is used to make Multishot able to target, and damage Magic Immune enemies, when they can resist //
// ultimates (a gameplay constant is used for that). //
// When you have this library + the Multishot library, you can use "MultishotTargetAdvaned" and //
// "MultishotPointAdvanced" functions. //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library AdvancedMultishot requires MultiMisc
globals
// ALWAYS_USE_ADVANCED allows you to use ONLY the advanced version, even if the enemies are NOT immune to magic.
// It's still only used only when calling the Advanced function.
public constant boolean ALWAYS_USE_ADVANCED = false
private timer Looper = CreateTimer()
private boolean On = false
private constant real REFRESH_RATE = 0.03125
endglobals
// This struct is also used against Magic Immune enemies - it removes their immunity, so the Multishot missile can be cast
// and then it returns their immunities.
private struct ImmunityList
boolean main = false
boolean i1 = false
boolean i2 = false
boolean i3 = false
boolean i4 = false
unit u
// 'Amim', 'ACm2', 'ACm3' and 'ACmi' are magic immunities. They are usually constant, unless you use only custom magic immunities.
static method create takes unit u returns ImmunityList
local ImmunityList IL = ImmunityList.allocate()
set IL.u = u
if GetUnitAbilityLevel(u, 'Amim') > 0 then
call UnitRemoveAbility(u, 'Amim')
set IL.i1 = true
set IL.main = true
endif
if GetUnitAbilityLevel(u, 'ACm2') > 0 then
call UnitRemoveAbility(u, 'ACm2')
set IL.i2 = true
set IL.main = true
endif
if GetUnitAbilityLevel(u, 'ACm3') > 0 then
call UnitRemoveAbility(u, 'ACm3')
set IL.i3 = true
set IL.main = true
endif
if GetUnitAbilityLevel(u, 'ACmi') > 0 then
call UnitRemoveAbility(u, 'ACmi')
set IL.i4 = true
set IL.main = true
endif
return IL
endmethod
method onDestroy takes nothing returns nothing
if .i1 then
call UnitAddAbility(.u, 'Amim')
endif
if .i2 then
call UnitAddAbility(.u, 'ACm2')
endif
if .i3 then
call UnitAddAbility(.u, 'ACm3')
endif
if .i4 then
call UnitAddAbility(.u, 'ACmi')
endif
endmethod
endstruct
private struct TimeManager
real x
real y
real speed
unit targ
MultiMisc_DamProperties damage
readonly thistype next
readonly thistype prev
static method operator first takes nothing returns thistype
return thistype(0).next
endmethod
static method operator last takes nothing returns thistype
return thistype(0).prev
endmethod
static method create takes real x, real y, unit u, real speed, MultiMisc_DamProperties DP returns thistype
local thistype this = thistype.allocate()
set thistype(0).next.prev = this
set this.next = thistype(0).next
set this.prev = thistype(0)
set thistype(0).next = this
set this.x = x
set this.y = y
set this.speed = speed*REFRESH_RATE
set this.targ = u
set this.damage = DP
return this
endmethod
method onDestroy takes nothing returns nothing
if this.first == this.last then
set On = false
endif
set this.next.prev = this.prev
set this.prev.next = this.next
endmethod
static method refresh takes nothing returns nothing
local thistype this = thistype.first
local real x
local real y
local real h
local real w
local real dist
local real a
loop
exitwhen this == 0
if not IsUnitType(this.targ, UNIT_TYPE_DEAD) then
set x = GetUnitX(this.targ)
set y = GetUnitY(this.targ)
set w = x - this.x
set h = y - this.y
set dist = w*w + h*h
if dist <= this.speed*this.speed then
set udg_MS_Damage = this.damage.amount
if Multishot_WANT_MULTISHOT_HIT_EVENT then
set udg_MS_Dummy = this.damage.dummy
set udg_MS_Hit_Unit = this.targ
set udg_MS_Source = this.damage.source
set udg_MS_Current_Group = this.damage.shotGroup
set udg_MS_Main_Target = this.damage.mainTarget
set udg_MS_Missile = this.damage.missile
set udg_MS_UnitsInGroup = this.damage.unitsShot
set udg_MS_Hit_Event = 1.00
set udg_MS_Hit_Event = 0.00
endif
set Multishot_Damage = true
call UnitDamageTarget( this.damage.source, this.targ, udg_MS_Damage, true, false, this.damage.aType, this.damage.dType, null )
set Multishot_Damage = false
set this.damage.alreadyShot = this.damage.alreadyShot + 1
if this.damage.alreadyShot == this.damage.unitsShot then
call TimerStart(this.damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
endif
call thistype.destroy(this)
else
set a = Atan2(h, w)
set this.x = this.x + this.speed*Cos(a)
set this.y = this.y + this.speed*Sin(a)
endif
else
call thistype.destroy(this)
endif
set this = this.next
endloop
if On then
call TimerStart(Looper, REFRESH_RATE, false, function TimeManager.refresh)
endif
endmethod
endstruct
// This function removes the unit's magic immunities (so it can shoot them), shoots them, and if they have had magic immunity
// it calculates the time the shot needs to reach the target (roughly), and calls the function above to start a timer,
// Which will artificially damage the unit, when the timer expires.
// Then it returns the units' immunities.
function MS_Shoot takes MultiMisc_DamProperties DP, real x, real y, real shotSpeed, unit target returns nothing
local real delay
local ImmunityList IL = ImmunityList.create(target)
local TimeManager TM
call IssueTargetOrder(DP.dummy, Multishot_SPELL_ORDER, target)
if IL.main or ALWAYS_USE_ADVANCED then
set TM = TimeManager.create(x, y, target, shotSpeed, DP)
if not On then
call TimerStart(Looper, REFRESH_RATE, false, function TimeManager.refresh)
set On = true
endif
endif
call ImmunityList.destroy(IL)
endfunction
endlibrary
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------- CUSTOM MULTISHOT BY WereElf ------------------------------------------ //
// This is simply a Multishot, which is a lot better than the Barrage based one. //
// The differences between this Multishot and the Barrage based one are: //
// //
// 1) This multishot can be configured to hit only enemies infront of the shooter, while Barrage based can only //
// target ALL the enemies in range (certain amount of them). //
// //
// 2) This multishot works with Orb effects (if you use the "Orbs" add-on library. //
// //
// 3) You can make your own "orb effects" when you use this Multishot, since you get a "On Hit" event. //
// //
// 4) You can set the targets allowed to 1 and 2, while for the Barrage based one - the minimum is 3. //
// //
// 5) You can use this multishot for SPELLS! //
// //
// 6) You can put some weird configurations for this multishot (like - hit allied buildings, and enemy units). //
// //
// I could keep on listing the possibillities, offered by this library, but I wouldn't. I pointed out the most //
// important ones. //
// //
// HOW TO MAKE IT WORK: //
// //
// 0.a) You need a damage detecting system on your map. The multishot is built, using 1 of the most basic DDS(es), //
// so it will work with any DDS. However, if you are using a different one - you need to go to the bottom of this //
// library, and change the variables in the last textmacro to the ones, used by the other DDS. //
// //
// 0.b) You need to have a dummy on your map. //
// //
// 1) Make a custom buff on your map, which will be used ONLY by the Multishot skills. //
// //
// 2) Make as many as you need skills, based on Acid Bomb, make them cost 0 mana, deal 0 damage, and make them //
// APPLY THE BUFF YOU MADE IN STEP (1). //
// //
// 3) Scroll down to the globals block of this library, and set the variables to "your" values. //
// //
// 4) When you want to make a trigger, using Multishot, just call "MultishotTarget" or "MultishotPoint", according //
// to your needs. //
// //
// 5) You could make a library with some pre-set Multishot functions (like MultishoRt in the sample map), so you //
// don't have to put all the arguments when you call Multishot, but simply the ones you need. //
// //
// If you have the AdvancedMultishot library (add-on), you can also use "MultishotTargetAdvanced" and //
// "MultishotPointAdvanced". The advanced functions are able to hit magic immune enemies, even if you have "Magic //
// immune units resist ultimates" set to true. //
// //
// THE ARGUMENTS THE FUNCTIONS TAKE: //
// //
// MultishotTarg (both normal and the advanced) take: //
// 1 (unit) - Shooter - who is shooting //
// 2 (unit) - Target - who is the original target of the shot //
// 3 (real) - Damage - the damage the shot should deal //
// 4 (integer) - Targets - how many targets its allowed to shoot at once //
// 5 (real) - Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot //
// 6 (real) - Range - the range multishot chooses enemies from. //
// 7 (boolean) - Always hits main targer - does the main target always get shot, or it only has a chance to get //
// shot (if more than the max amount of units are in range) //
// 8 (attacktype) - Attack type - the attack type of the shooter. Its used for correct armor estimation, and thats //
// the type of damage dealt in the end. //
// 9 (integer) - Missile ability - the ability id of the Multishots missile. //
// 10 (real) - Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45, //
// multishot will act like the target is at 90 degrees. //
// 11 (boolean) - Fixed damage - if you want to deal some fixed value of damage, not based on the damage dealt - //
// set this to true, otherwise - false. //
// 12 (damagetype) - Damage type - if "Fixed damage" is set to false - this will act as "DAMAGE_TYPE_NORMAL", no //
// matter what. Otherwise - this is the damage type multishot deals. //
// 13 (boolexpr) - Conditions - What units shall it target //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION //
// 14 (real) - Missile speed - simply put-in the missiles speed //
// //
// MultishotPoint (both normal and the advanced) take: //
// 1 (unit) Caster - who is shooting //
// 2 (real) Damage - the damage dealt //
// 3 (integer) Targets - how many targets its allowed to shoot at once //
// 4 (real) Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot //
// 5 (real) Range - the range multishot chooses enemies from //
// 6 (attacktype) Attack Type - the attack type of the damage dealt //
// 7 (integer) Missile ability - the ability id of the Multishots missile //
// 8 (real) Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45, //
// multishot will act like the target is at 90 degrees //
// 9 (damagetype) Damage type - the damage type of the damage dealth //
// 10 (real) Target X - the x of the targeted point //
// 11 (real) Target Y - the y of the targeted point //
// 12 (boolexpr) - Conditions - What units shall it target //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION //
// 13 (real) Missile speed - simply put-in the missiles speed //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library MultiMisc
public struct DamProperties
real amount
attacktype aType
damagetype dType
unit source
unit mainTarget
group shotGroup
integer missile
integer unitsShot = 0
boolean advanced
integer alreadyShot = 0
boolean preDummy
unit dummy
timer clearer
static method create takes nothing returns DamProperties
local DamProperties DP = DamProperties.allocate()
set DP.shotGroup = CreateGroup()
set DP.clearer = CreateTimer()
set DP.mainTarget = null
return DP
endmethod
method onDestroy takes nothing returns nothing
call GroupClear(this.shotGroup)
call DestroyGroup(this.shotGroup)
call DestroyTimer(this.clearer)
endmethod
endstruct
public function ClearLeaks takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer tid = GetHandleId(t)
local integer id = LoadInteger(Multishot_Table, tid, 'dmid')
local DamProperties damage = LoadInteger(Multishot_Table, id, 'damp')
call UnitApplyTimedLife(damage.dummy, 'BTLF', 0.01)
call DamProperties.destroy(damage)
call FlushChildHashtable(Multishot_Table, id)
call FlushChildHashtable(Multishot_Table, tid)
set t = null
endfunction
endlibrary
library Multishot initializer Init requires MultiMisc, optional AdvancedMultishot
// DEBUG_MULTISHOT displays how the parabola looks like
// WANT_DAMAGE_BLOCK is a boolean. If set to true - Multishot will automatically block the incoming damage. Set it to false only
// if you want to do the damage block by yourself in the Multishot calling function.
// HP_BONUS_ABILITY is the Item ability, giving 30000 HP to the target unit for the damage block, and for the 'armor test'
// BUFF_APPLIED_ID is the Id of the buff, applied by the Multishot.
// DUMMY_ID is the Id of your dummy unit.
// TEST_DAMAGE_AMOUNT is the amount of damage the target takes to estimate its armor. Higher values give more precise results, but they may instantly kill the target.
// SPELL_ORDER is the string of the order, issued to the dummy to make it cast the multishot arrow. You could change it if
// you are using another spell as base, but acidbomb is the only good spell for this purpose.
// DUMMY_LIFE_TIME is the time the dummy stays alive. Set this to an amount, enough for the multishot to hit all its targets.
// Don't touch the rest.
globals
private constant boolean DEBUG_MULTISHOT = false
private constant boolean WANT_DAMAGE_BLOCK = false
private constant integer HP_BONUS_ABILITY = 'A02P'
private constant integer BUFF_APPLIED_ID = 'B005'
private constant integer DUMMY_ID = 'h00C'
private constant real MAX_COLLISION_SIZE = 196.00
private constant real TEST_DAMAGE_AMOUNT = 15000.00
public constant string SPELL_ORDER = "acidbomb"
public constant boolean WANT_MULTISHOT_HIT_EVENT = true
public constant real MAX_DUMMY_LIFE_TIME = 20.00 // higher values allow slower missiles.
public boolean Damage = false
public hashtable Table = InitHashtable()
private group g = CreateGroup()
private effect array debug_effect // used only when DEBUG_MULTISHOT is true
endglobals
// This sctuct is used when blocking the original damage.
private struct DamBlocker
real health
unit u
static method create takes unit u returns DamBlocker
local DamBlocker DB
set DB = DamBlocker.allocate()
set DB.u = u
set DB.health = GetWidgetLife(u)
return DB
endmethod
static method BlockDamage takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = LoadInteger(Table, GetHandleId(t), 'blcr')
call UnitRemoveAbility(this.u, HP_BONUS_ABILITY)
call SetWidgetLife(this.u, this.health)
call thistype.destroy(this)
call FlushChildHashtable(Table, GetHandleId(t))
call DestroyTimer(t)
set t = null
endmethod
endstruct
// Calculates the unit's base damage, by multiplaying the damage dealt by the target's Armor rating
private function GetBaseDamage takes MultiMisc_DamProperties damage, real dam returns nothing
local timer time
local real max_hp
local DamBlocker DB = DamBlocker.create(damage.mainTarget)
if GetUnitAbilityLevel(damage.mainTarget, 'BNab') > 0 then
call UnitRemoveAbility(damage.mainTarget, 'BNab')
endif
static if WANT_DAMAGE_BLOCK then
if GetUnitAbilityLevel(DB.u, HP_BONUS_ABILITY) == 0 then
set time = CreateTimer()
call SaveInteger(Table, GetHandleId(time), 'blcr', DB)
call TimerStart(time, 0.00, false, function DamBlocker.BlockDamage)
set time = null
endif
endif
call UnitAddAbility(damage.mainTarget, HP_BONUS_ABILITY)
set max_hp = GetUnitState(damage.mainTarget, UNIT_STATE_MAX_LIFE)
call SetWidgetLife(damage.mainTarget, max_hp)
call UnitDamageTarget(damage.dummy, damage.mainTarget, TEST_DAMAGE_AMOUNT, true, false, damage.aType, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
set damage.amount = (TEST_DAMAGE_AMOUNT/(max_hp - GetWidgetLife(damage.mainTarget)))*dam
call SetWidgetLife(damage.mainTarget, max_hp)
static if not WANT_DAMAGE_BLOCK then
call UnitRemoveAbility(damage.mainTarget, HP_BONUS_ABILITY)
call SetWidgetLife(damage.mainTarget, DB.health)
call DamBlocker.destroy(DB)
endif
endfunction
function DrawParabola takes real range, real arc, real x, real y, real tarx, real tary, real ofst returns nothing
local real a
local real b
local real m = range*Sin(arc/2)
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local integer count = 0
local real tx = x
local real ty = y
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
loop
exitwhen count > 20
set x = tx - Cos(init_ang)*parabola + Cos(init_ang + bj_PI/2)*(count - 10)*m/10
set y = ty - Sin(init_ang)*parabola + Sin(init_ang + bj_PI/2)*(count - 10)*m/10
set b = SquareRoot(4*parabola*parabola + (count - 10)*m*(count - 10)*m/100)
set a = (init_ang + bj_PI/2) - Atan2(yFocus - y, xFocus - x)
if a == bj_PI/2 then
set a = parabola
else
set a = b*Cos(a)/Sin(2*a)
endif
set x = x + Cos(init_ang)*a
set y = y + Sin(init_ang)*a
call DestroyEffect(debug_effect[count])
set debug_effect[count] = AddSpecialEffect("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl", x, y)
set count = count + 1
endloop
endfunction
function ConfigureDamProp takes unit u, damagetype dType, attacktype aType, unit mainTarget, integer missile, boolean advanced returns MultiMisc_DamProperties
local MultiMisc_DamProperties this
if GetUnitTypeId(u) == DUMMY_ID then
set this = LoadInteger(Table, GetHandleId(u), 'damp') + 0
if this == 0 then
set this = MultiMisc_DamProperties.create()
call SaveInteger(Table, GetHandleId(u), 'damp', this)
endif
set this.dummy = u
set this.source = udg_MS_Source
set this.preDummy = true
else
set this = MultiMisc_DamProperties.create()
set this.dummy = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), 0.00)
call SaveInteger(Table, GetHandleId(this.dummy), 'damp', this)
call UnitApplyTimedLife(this.dummy, 'BTLF', MAX_DUMMY_LIFE_TIME + 0.05)
set this.source = u
set this.preDummy = false
endif
call UnitAddAbility(this.dummy, missile)
set this.dType = dType
set this.aType = aType
set this.mainTarget = mainTarget
set this.missile = missile
set this.advanced = advanced
call SaveInteger(Table, GetHandleId(this.clearer), 'dmid', GetHandleId(this.dummy))
call TimerStart(this.clearer, MAX_DUMMY_LIFE_TIME, false, function MultiMisc_ClearLeaks)
return this
endfunction
function ShootAllTargets takes MultiMisc_DamProperties damage, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
local real x = GetUnitX(damage.dummy)
local real y = GetUnitY(damage.dummy)
local player p = GetOwningPlayer(damage.source)
local real xTarget
local real yTarget
local unit FoG
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
local real tempAngle
local boolean b
call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
call GroupRemoveUnit(g, damage.mainTarget)
endif
call GroupRemoveUnit(g, damage.source)
loop
set FoG = FirstOfGroup(g)
exitwhen FoG == null
set xTarget = GetUnitX(FoG)
set yTarget = GetUnitY(FoG)
set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
if condition == null then
set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
else
set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
endif
if b then
if speed > 0 then
static if LIBRARY_AdvancedMultishot then
call MS_Shoot(damage, x, y, speed, FoG)
endif
else
call IssueTargetOrder(damage.dummy, SPELL_ORDER, FoG)
endif
call GroupAddUnit(damage.shotGroup, FoG)
set damage.unitsShot = damage.unitsShot + 1
endif
call GroupRemoveUnit(g, FoG)
endloop
endfunction
function ShootRandomTargets takes MultiMisc_DamProperties damage, integer targets, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
local integer left_c = 0
local integer right_c = 0
local unit array units
local real x = GetUnitX(damage.dummy)
local real y = GetUnitY(damage.dummy)
local player p = GetOwningPlayer(damage.source)
local unit FoG
local integer dice
local real xTarget
local real yTarget
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
local real tempAngle
local boolean b
if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
call GroupRemoveUnit(g, damage.mainTarget)
set targets = targets - 1
endif
call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
call GroupRemoveUnit(g, damage.source)
loop
set FoG = FirstOfGroup(g)
exitwhen FoG == null
set xTarget = GetUnitX(FoG)
set yTarget = GetUnitY(FoG)
set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
if R2I(tempAngle/(2*bj_PI)) != 0 then
set tempAngle = ModuloReal(tempAngle, 2*bj_PI)
endif
if condition == null then
set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
else
set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
endif
if b then
if tempAngle >= 0 and tempAngle < bj_PI then
set left_c = left_c + 1
set dice = GetRandomInt(1, left_c)
if dice <= targets and ( dice <= (targets + 1)/2 or (units[left_c] == null and left_c <= targets)) then
if left_c <= targets and ( left_c <= (targets + 1)/2 or units[left_c] == null ) then
set units[left_c] = units[dice]
endif
set units[dice] = FoG
endif
else
set right_c = right_c + 1
set dice = GetRandomInt(1, right_c)
if dice <= targets and ( dice <= (targets + 1)/2 or (units[targets + 1 - right_c] == null and right_c <= targets)) then
if right_c <= targets and ( right_c <= (targets + 1)/2 or units[targets + 1 - right_c] == null ) then
set units[targets + 1 - right_c] = units[targets + 1 - dice]
endif
set units[targets + 1 - dice] = FoG
endif
endif
endif
call GroupRemoveUnit(g, FoG)
endloop
set dice = 1
loop
exitwhen dice > targets
if units[dice] != null then
if speed > 0 then
static if LIBRARY_AdvancedMultishot then
call MS_Shoot(damage, x, y, speed, units[dice])
endif
else
call IssueTargetOrder(damage.dummy, SPELL_ORDER, units[dice])
endif
call GroupAddUnit(damage.shotGroup, units[dice])
set damage.unitsShot = damage.unitsShot + 1
set units[dice] = null
elseif targets - right_c > dice then
set dice = targets - right_c - 1
endif
set dice = dice + 1
endloop
endfunction
function MultishotTarget takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition returns nothing
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
endfunction
function MultishotTargetAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition, real shotSpeed returns nothing
local MultiMisc_DamProperties damage
static if LIBRARY_AdvancedMultishot then
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
else
call MultishotTarget(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType, condition)
endif
endfunction
function MultishotPoint takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition returns nothing
local MultiMisc_DamProperties damage
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
set damage.amount = initialDamage
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endfunction
function MultishotPointAdvanced takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition, real shotSpeed returns nothing
local MultiMisc_DamProperties damage
static if LIBRARY_AdvancedMultishot then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
set damage.amount = initialDamage
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
else
call MultishotPoint(shooter, initialDamage, targets, arc, initRange, attackType, missileArt, offsetAngle, damageType, xSpell, ySpell, condition)
endif
endfunction
function MultishotLight takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType returns nothing
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
endfunction
function MultishotLightAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, real shotSpeed returns nothing
static if LIBRARY_AdvancedMultishot then
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
else
call MultishotLight(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType)
endif
endfunction
//! textmacro MulthishotOnHit takes SOURCE, VICTIM, AMOUNT, EVENT
// Checks if a unit, that has taken damage, has the Buff, applied by the Multishot. If it has it - applies the MS damage.
// When a unit is hit by the multishot's missile - the buff is removed, and the damage is applied
// It also sets a variable to 0.00 and then to 1.00 for the Extra triggers to run.
private function MultishotDelay takes nothing returns boolean
local MultiMisc_DamProperties damage
if GetUnitAbilityLevel($VICTIM$, BUFF_APPLIED_ID ) > 0 then
call UnitRemoveAbility( $VICTIM$, BUFF_APPLIED_ID )
static if AdvancedMultishot_ALWAYS_USE_ADVANCED then
if damage.advanced then
return false
endif
endif
set damage = LoadInteger(Table, GetHandleId($SOURCE$), 'damp')
set udg_MS_Damage = damage.amount
if WANT_MULTISHOT_HIT_EVENT then
set udg_MS_Dummy = $SOURCE$
set udg_MS_Hit_Unit = $VICTIM$
set udg_MS_Source = damage.source
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_Main_Target = damage.mainTarget
set udg_MS_Missile = damage.missile
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Hit_Event = 1.00
set udg_MS_Hit_Event = 0.00
endif
set Damage = true
call UnitDamageTarget( udg_MS_Source, $VICTIM$, udg_MS_Damage, true, true, damage.aType, damage.dType, null )
set Damage = false
set damage.alreadyShot = damage.alreadyShot + 1
if damage.alreadyShot == damage.unitsShot then
call TimerStart(damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
endif
endif
return false
endfunction
// Initiation Trigger :P
function Init takes nothing returns nothing
set gg_trg_Multishot_v2_2_c = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_Multishot_v2_2_c, "$EVENT$", EQUAL, 0 ) // change the 0 to something else if needed.
call TriggerAddCondition( gg_trg_Multishot_v2_2_c, Condition( function MultishotDelay ) )
endfunction
//! endtextmacro
//! runtextmacro MulthishotOnHit("udg_DamageEventSource","udg_DamageEventTarget","udg_DamageEventAmount","udg_DamageEvent")
endlibrary
JASS:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------- CUSTOM MULTISHOT BY WereElf ------------------------------------------ //
// This is simply a Multishot, which is a lot better than the Barrage based one. //
// The differences between this Multishot and the Barrage based one are: //
// //
// 1) This multishot can be configured to hit only enemies infront of the shooter, while Barrage based can only //
// target ALL the enemies in range (certain amount of them). //
// //
// 2) This multishot works with Orb effects (if you use the "Orbs" add-on library. //
// //
// 3) You can make your own "orb effects" when you use this Multishot, since you get a "On Hit" event. //
// //
// 4) You can set the targets allowed to 1 and 2, while for the Barrage based one - the minimum is 3. //
// //
// 5) You can use this multishot for SPELLS! //
// //
// 6) You can put some weird configurations for this multishot (like - hit allied buildings, and enemy units). //
// //
// I could keep on listing the possibillities, offered by this library, but I wouldn't. I pointed out the most //
// important ones. //
// //
// HOW TO MAKE IT WORK: //
// //
// 0.a) You need a damage detecting system on your map. The multishot is built, using 1 of the most basic DDS(es), //
// so it will work with any DDS. However, if you are using a different one - you need to go to the bottom of this //
// library, and change the variables in the last textmacro to the ones, used by the other DDS. //
// //
// 0.b) You need to have a dummy on your map. //
// //
// 1) Make a custom buff on your map, which will be used ONLY by the Multishot skills. //
// //
// 2) Make as many as you need skills, based on Acid Bomb, make them cost 0 mana, deal 0 damage, and make them //
// APPLY THE BUFF YOU MADE IN STEP (1). //
// //
// 3) Scroll down to the globals block of this library, and set the variables to "your" values. //
// //
// 4) When you want to make a trigger, using Multishot, just call "MultishotTarget" or "MultishotPoint", according //
// to your needs. //
// //
// 5) You could make a library with some pre-set Multishot functions (like MultishoRt in the sample map), so you //
// don't have to put all the arguments when you call Multishot, but simply the ones you need. //
// //
// If you have the AdvancedMultishot library (add-on), you can also use "MultishotTargetAdvanced" and //
// "MultishotPointAdvanced". The advanced functions are able to hit magic immune enemies, even if you have "Magic //
// immune units resist ultimates" set to true. //
// //
// THE ARGUMENTS THE FUNCTIONS TAKE: //
// //
// MultishotTarg (both normal and the advanced) take: //
// 1 (unit) - Shooter - who is shooting //
// 2 (unit) - Target - who is the original target of the shot //
// 3 (real) - Damage - the damage the shot should deal //
// 4 (integer) - Targets - how many targets its allowed to shoot at once //
// 5 (real) - Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot //
// 6 (real) - Range - the range multishot chooses enemies from. //
// 7 (boolean) - Always hits main targer - does the main target always get shot, or it only has a chance to get //
// shot (if more than the max amount of units are in range) //
// 8 (attacktype) - Attack type - the attack type of the shooter. Its used for correct armor estimation, and thats //
// the type of damage dealt in the end. //
// 9 (integer) - Missile ability - the ability id of the Multishots missile. //
// 10 (real) - Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45, //
// multishot will act like the target is at 90 degrees. //
// 11 (boolean) - Fixed damage - if you want to deal some fixed value of damage, not based on the damage dealt - //
// set this to true, otherwise - false. //
// 12 (damagetype) - Damage type - if "Fixed damage" is set to false - this will act as "DAMAGE_TYPE_NORMAL", no //
// matter what. Otherwise - this is the damage type multishot deals. //
// 13 (boolexpr) - Conditions - What units shall it target //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION //
// 14 (real) - Missile speed - simply put-in the missiles speed //
// //
// MultishotPoint (both normal and the advanced) take: //
// 1 (unit) Caster - who is shooting //
// 2 (real) Damage - the damage dealt //
// 3 (integer) Targets - how many targets its allowed to shoot at once //
// 4 (real) Arc - this determines the width of the area multishot chooses targets from. Set it to 180 if you want //
// ALL the units in range to get shot //
// 5 (real) Range - the range multishot chooses enemies from //
// 6 (attacktype) Attack Type - the attack type of the damage dealt //
// 7 (integer) Missile ability - the ability id of the Multishots missile //
// 8 (real) Offset angle - (example:) if the main target is at 45 deg from the shooter, if this is set to 45, //
// multishot will act like the target is at 90 degrees //
// 9 (damagetype) Damage type - the damage type of the damage dealth //
// 10 (real) Target X - the x of the targeted point //
// 11 (real) Target Y - the y of the targeted point //
// 12 (boolexpr) - Conditions - What units shall it target //
// NEXT ONE IS ONLY FOR THE ADVANCED VERSION //
// 13 (real) Missile speed - simply put-in the missiles speed //
// //
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
library MultiMisc
public struct DamProperties
real amount
attacktype aType
damagetype dType
unit source
unit mainTarget
group shotGroup
integer missile
integer unitsShot = 0
boolean advanced
integer alreadyShot = 0
boolean preDummy
unit dummy
timer clearer
static method create takes nothing returns DamProperties
local DamProperties DP = DamProperties.allocate()
set DP.shotGroup = CreateGroup()
set DP.clearer = CreateTimer()
set DP.mainTarget = null
return DP
endmethod
method onDestroy takes nothing returns nothing
call GroupClear(this.shotGroup)
call DestroyGroup(this.shotGroup)
call DestroyTimer(this.clearer)
endmethod
endstruct
public function ClearLeaks takes nothing returns nothing
local timer t = GetExpiredTimer()
local integer tid = GetHandleId(t)
local integer id = LoadInteger(Multishot_Table, tid, 'dmid')
local DamProperties damage = LoadInteger(Multishot_Table, id, 'damp')
call UnitApplyTimedLife(damage.dummy, 'BTLF', 0.01)
call DamProperties.destroy(damage)
call FlushChildHashtable(Multishot_Table, id)
call FlushChildHashtable(Multishot_Table, tid)
set t = null
endfunction
endlibrary
library Multishot initializer Init requires MultiMisc, optional AdvancedMultishot
// DEBUG_MULTISHOT displays how the parabola looks like
// WANT_DAMAGE_BLOCK is a boolean. If set to true - Multishot will automatically block the incoming damage. Set it to false only
// if you want to do the damage block by yourself in the Multishot calling function.
// HP_BONUS_ABILITY is the Item ability, giving 30000 HP to the target unit for the damage block, and for the 'armor test'
// BUFF_APPLIED_ID is the Id of the buff, applied by the Multishot.
// DUMMY_ID is the Id of your dummy unit.
// TEST_DAMAGE_AMOUNT is the amount of damage the target takes to estimate its armor. Higher values give more precise results, but they may instantly kill the target.
// SPELL_ORDER is the string of the order, issued to the dummy to make it cast the multishot arrow. You could change it if
// you are using another spell as base, but acidbomb is the only good spell for this purpose.
// DUMMY_LIFE_TIME is the time the dummy stays alive. Set this to an amount, enough for the multishot to hit all its targets.
// Don't touch the rest.
globals
private constant boolean DEBUG_MULTISHOT = false
private constant boolean WANT_DAMAGE_BLOCK = false
private constant integer HP_BONUS_ABILITY = 'A02P'
private constant integer BUFF_APPLIED_ID = 'B005'
private constant integer DUMMY_ID = 'h00C'
private constant real MAX_COLLISION_SIZE = 196.00
private constant real TEST_DAMAGE_AMOUNT = 15000.00
public constant string SPELL_ORDER = "acidbomb"
public constant boolean WANT_MULTISHOT_HIT_EVENT = true
public constant real MAX_DUMMY_LIFE_TIME = 20.00 // higher values allow slower missiles.
public boolean Damage = false
public hashtable Table = InitHashtable()
private group g = CreateGroup()
private effect array debug_effect // used only when DEBUG_MULTISHOT is true
endglobals
// This sctuct is used when blocking the original damage.
private struct DamBlocker
real health
unit u
static method create takes unit u returns DamBlocker
local DamBlocker DB
set DB = DamBlocker.allocate()
set DB.u = u
set DB.health = GetWidgetLife(u)
return DB
endmethod
static method BlockDamage takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = LoadInteger(Table, GetHandleId(t), 'blcr')
call UnitRemoveAbility(this.u, HP_BONUS_ABILITY)
call SetWidgetLife(this.u, this.health)
call thistype.destroy(this)
call FlushChildHashtable(Table, GetHandleId(t))
call DestroyTimer(t)
set t = null
endmethod
endstruct
// Calculates the unit's base damage, by multiplaying the damage dealt by the target's Armor rating
private function GetBaseDamage takes MultiMisc_DamProperties damage, real dam returns nothing
local timer time
local real max_hp
local DamBlocker DB = DamBlocker.create(damage.mainTarget)
if GetUnitAbilityLevel(damage.mainTarget, 'BNab') > 0 then
call UnitRemoveAbility(damage.mainTarget, 'BNab')
endif
static if WANT_DAMAGE_BLOCK then
if GetUnitAbilityLevel(DB.u, HP_BONUS_ABILITY) == 0 then
set time = CreateTimer()
call SaveInteger(Table, GetHandleId(time), 'blcr', DB)
call TimerStart(time, 0.00, false, function DamBlocker.BlockDamage)
set time = null
endif
endif
call UnitAddAbility(damage.mainTarget, HP_BONUS_ABILITY)
set max_hp = GetUnitState(damage.mainTarget, UNIT_STATE_MAX_LIFE)
call SetWidgetLife(damage.mainTarget, max_hp)
call UnitDamageTarget(damage.dummy, damage.mainTarget, TEST_DAMAGE_AMOUNT, true, false, damage.aType, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
set damage.amount = (TEST_DAMAGE_AMOUNT/(max_hp - GetWidgetLife(damage.mainTarget)))*dam
call SetWidgetLife(damage.mainTarget, max_hp)
static if not WANT_DAMAGE_BLOCK then
call UnitRemoveAbility(damage.mainTarget, HP_BONUS_ABILITY)
call SetWidgetLife(damage.mainTarget, DB.health)
call DamBlocker.destroy(DB)
endif
endfunction
function DrawParabola takes real range, real arc, real x, real y, real tarx, real tary, real ofst returns nothing
local real a
local real b
local real m = range*Sin(arc/2)
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local integer count = 0
local real tx = x
local real ty = y
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
loop
exitwhen count > 20
set x = tx - Cos(init_ang)*parabola + Cos(init_ang + bj_PI/2)*(count - 10)*m/10
set y = ty - Sin(init_ang)*parabola + Sin(init_ang + bj_PI/2)*(count - 10)*m/10
set b = SquareRoot(4*parabola*parabola + (count - 10)*m*(count - 10)*m/100)
set a = (init_ang + bj_PI/2) - Atan2(yFocus - y, xFocus - x)
if a == bj_PI/2 then
set a = parabola
else
set a = b*Cos(a)/Sin(2*a)
endif
set x = x + Cos(init_ang)*a
set y = y + Sin(init_ang)*a
call DestroyEffect(debug_effect[count])
set debug_effect[count] = AddSpecialEffect("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget.mdl", x, y)
set count = count + 1
endloop
endfunction
function ConfigureDamProp takes unit u, damagetype dType, attacktype aType, unit mainTarget, integer missile, boolean advanced returns MultiMisc_DamProperties
local MultiMisc_DamProperties this
if GetUnitTypeId(u) == DUMMY_ID then
set this = LoadInteger(Table, GetHandleId(u), 'damp') + 0
if this == 0 then
set this = MultiMisc_DamProperties.create()
call SaveInteger(Table, GetHandleId(u), 'damp', this)
endif
set this.dummy = u
set this.source = udg_MS_Source
set this.preDummy = true
else
set this = MultiMisc_DamProperties.create()
set this.dummy = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), 0.00)
call SaveInteger(Table, GetHandleId(this.dummy), 'damp', this)
call UnitApplyTimedLife(this.dummy, 'BTLF', MAX_DUMMY_LIFE_TIME + 0.05)
set this.source = u
set this.preDummy = false
endif
call UnitAddAbility(this.dummy, missile)
set this.dType = dType
set this.aType = aType
set this.mainTarget = mainTarget
set this.missile = missile
set this.advanced = advanced
call SaveInteger(Table, GetHandleId(this.clearer), 'dmid', GetHandleId(this.dummy))
call TimerStart(this.clearer, MAX_DUMMY_LIFE_TIME, false, function MultiMisc_ClearLeaks)
return this
endfunction
function ShootAllTargets takes MultiMisc_DamProperties damage, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
local real x = GetUnitX(damage.dummy)
local real y = GetUnitY(damage.dummy)
local player p = GetOwningPlayer(damage.source)
local real xTarget
local real yTarget
local unit FoG
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
local real tempAngle
local boolean b
call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
call GroupRemoveUnit(g, damage.mainTarget)
endif
call GroupRemoveUnit(g, damage.source)
loop
set FoG = FirstOfGroup(g)
exitwhen FoG == null
set xTarget = GetUnitX(FoG)
set yTarget = GetUnitY(FoG)
set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
if condition == null then
set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
else
set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
endif
if b then
if speed > 0 then
static if LIBRARY_AdvancedMultishot then
call MS_Shoot(damage, x, y, speed, FoG)
endif
else
call IssueTargetOrder(damage.dummy, SPELL_ORDER, FoG)
endif
call GroupAddUnit(damage.shotGroup, FoG)
set damage.unitsShot = damage.unitsShot + 1
endif
call GroupRemoveUnit(g, FoG)
endloop
endfunction
function ShootRandomTargets takes MultiMisc_DamProperties damage, integer targets, real range, real arc, real tarx, real tary, real ofst, boolexpr condition, real speed returns nothing
local integer left_c = 0
local integer right_c = 0
local unit array units
local real x = GetUnitX(damage.dummy)
local real y = GetUnitY(damage.dummy)
local player p = GetOwningPlayer(damage.source)
local unit FoG
local integer dice
local real xTarget
local real yTarget
local real parabola = (range-(range*Cos(arc/2 * bj_DEGTORAD)*Cos(arc/2 * bj_DEGTORAD)))/( 4.00 * Cos(arc/2 * bj_DEGTORAD))
local real init_ang = (RSignBJ(parabola) - 1)*bj_PI/2 + Atan2(tary-y,tarx-x) + ofst*bj_DEGTORAD
local real xFocus = x + parabola*Cos(init_ang)
local real yFocus = y + parabola*Sin(init_ang)
local real tempAngle
local boolean b
if damage.mainTarget != null and IsUnitInGroup(damage.mainTarget, damage.shotGroup) then
call GroupRemoveUnit(g, damage.mainTarget)
set targets = targets - 1
endif
call GroupEnumUnitsInRange(g, x, y, range + MAX_COLLISION_SIZE, condition)
call GroupRemoveUnit(g, damage.source)
loop
set FoG = FirstOfGroup(g)
exitwhen FoG == null
set xTarget = GetUnitX(FoG)
set yTarget = GetUnitY(FoG)
set tempAngle = Atan2(yTarget-yFocus,xTarget-xFocus) - init_ang
if R2I(tempAngle/(2*bj_PI)) != 0 then
set tempAngle = ModuloReal(tempAngle, 2*bj_PI)
endif
if condition == null then
set b = IsUnitEnemy(FoG, p) and not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
else
set b = not IsUnitType(FoG, UNIT_TYPE_DEAD) and (xTarget-xFocus)*(xTarget-xFocus)+(yTarget-yFocus)*(yTarget-yFocus) <= (( 2.00*parabola)/( 1 - Cos(tempAngle)))*(( 2.00*parabola)/( 1 - Cos(tempAngle))) and IsUnitInRangeXY(FoG, x, y, range)
endif
if b then
if tempAngle >= 0 and tempAngle < bj_PI then
set left_c = left_c + 1
set dice = GetRandomInt(1, left_c)
if dice <= targets and ( dice <= (targets + 1)/2 or (units[left_c] == null and left_c <= targets)) then
if left_c <= targets and ( left_c <= (targets + 1)/2 or units[left_c] == null ) then
set units[left_c] = units[dice]
endif
set units[dice] = FoG
endif
else
set right_c = right_c + 1
set dice = GetRandomInt(1, right_c)
if dice <= targets and ( dice <= (targets + 1)/2 or (units[targets + 1 - right_c] == null and right_c <= targets)) then
if right_c <= targets and ( right_c <= (targets + 1)/2 or units[targets + 1 - right_c] == null ) then
set units[targets + 1 - right_c] = units[targets + 1 - dice]
endif
set units[targets + 1 - dice] = FoG
endif
endif
endif
call GroupRemoveUnit(g, FoG)
endloop
set dice = 1
loop
exitwhen dice > targets
if units[dice] != null then
if speed > 0 then
static if LIBRARY_AdvancedMultishot then
call MS_Shoot(damage, x, y, speed, units[dice])
endif
else
call IssueTargetOrder(damage.dummy, SPELL_ORDER, units[dice])
endif
call GroupAddUnit(damage.shotGroup, units[dice])
set damage.unitsShot = damage.unitsShot + 1
set units[dice] = null
elseif targets - right_c > dice then
set dice = targets - right_c - 1
endif
set dice = dice + 1
endloop
endfunction
function MultishotTarget takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition returns nothing
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
endfunction
function MultishotTargetAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, boolexpr condition, real shotSpeed returns nothing
local MultiMisc_DamProperties damage
static if LIBRARY_AdvancedMultishot then
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, condition, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
else
call MultishotTarget(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType, condition)
endif
endfunction
function MultishotPoint takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition returns nothing
local MultiMisc_DamProperties damage
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
set damage.amount = initialDamage
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endfunction
function MultishotPointAdvanced takes unit shooter, real initialDamage, integer targets, real arc, real initRange, attacktype attackType, integer missileArt, real offsetAngle, damagetype damageType, real xSpell, real ySpell, boolexpr condition, real shotSpeed returns nothing
local MultiMisc_DamProperties damage
static if LIBRARY_AdvancedMultishot then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), xSpell, ySpell, offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, null, missileArt, false)
set damage.amount = initialDamage
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, xSpell, ySpell, offsetAngle, condition, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
else
call MultishotPoint(shooter, initialDamage, targets, arc, initRange, attackType, missileArt, offsetAngle, damageType, xSpell, ySpell, condition)
endif
endfunction
function MultishotLight takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType returns nothing
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call IssueTargetOrder(damage.dummy, SPELL_ORDER, damage.mainTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, 0.00)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
endfunction
function MultishotLightAdvanced takes unit shooter, unit multiTarget, real initialDamage, integer targets, real arc, real initRange, boolean alwaysHitsMainTarget, attacktype attackType, integer missileArt, real offsetAngle, boolean fixedDamage, damagetype damageType, real shotSpeed returns nothing
static if LIBRARY_AdvancedMultishot then
local MultiMisc_DamProperties damage
if not Damage then
static if DEBUG_MULTISHOT then
call DrawParabola(initRange, arc, GetUnitX(shooter), GetUnitY(shooter), GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle)
endif
set damage = ConfigureDamProp(shooter, damageType, attackType, multiTarget, missileArt, false)
if fixedDamage then
set damage.amount = initialDamage
else
call GetBaseDamage(damage, initialDamage)
endif
if alwaysHitsMainTarget then
call MS_Shoot(damage, GetUnitX(damage.dummy), GetUnitY(damage.dummy), shotSpeed, multiTarget)
call GroupAddUnit(damage.shotGroup, damage.mainTarget)
set damage.unitsShot = damage.unitsShot + 1
if targets == 1 then
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
return
endif
endif
if targets == 0 or targets > 1000 then
call ShootAllTargets(damage, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
else
call ShootRandomTargets(damage, targets, initRange, arc, GetUnitX(multiTarget), GetUnitY(multiTarget), offsetAngle, null, shotSpeed)
endif
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Dummy = damage.dummy
endif
else
call MultishotLight(shooter, multiTarget, initialDamage, targets, arc, initRange, alwaysHitsMainTarget, attackType, missileArt, offsetAngle, fixedDamage, damageType)
endif
endfunction
//! textmacro MulthishotOnHit takes SOURCE, VICTIM, AMOUNT, EVENT
// Checks if a unit, that has taken damage, has the Buff, applied by the Multishot. If it has it - applies the MS damage.
// When a unit is hit by the multishot's missile - the buff is removed, and the damage is applied
// It also sets a variable to 0.00 and then to 1.00 for the Extra triggers to run.
private function MultishotDelay takes nothing returns boolean
local MultiMisc_DamProperties damage
if GetUnitAbilityLevel($VICTIM$, BUFF_APPLIED_ID ) > 0 then
call UnitRemoveAbility( $VICTIM$, BUFF_APPLIED_ID )
static if AdvancedMultishot_ALWAYS_USE_ADVANCED then
if damage.advanced then
return false
endif
endif
set damage = LoadInteger(Table, GetHandleId($SOURCE$), 'damp')
set udg_MS_Damage = damage.amount
if WANT_MULTISHOT_HIT_EVENT then
set udg_MS_Dummy = $SOURCE$
set udg_MS_Hit_Unit = $VICTIM$
set udg_MS_Source = damage.source
set udg_MS_Current_Group = damage.shotGroup
set udg_MS_Main_Target = damage.mainTarget
set udg_MS_Missile = damage.missile
set udg_MS_UnitsInGroup = damage.unitsShot
set udg_MS_Hit_Event = 1.00
set udg_MS_Hit_Event = 0.00
endif
set Damage = true
call UnitDamageTarget( udg_MS_Source, $VICTIM$, udg_MS_Damage, true, true, damage.aType, damage.dType, null )
set Damage = false
set damage.alreadyShot = damage.alreadyShot + 1
if damage.alreadyShot == damage.unitsShot then
call TimerStart(damage.clearer, 0.00, false, function MultiMisc_ClearLeaks)
endif
endif
return false
endfunction
// Initiation Trigger :P
function Init takes nothing returns nothing
set gg_trg_Multishot_v2_2_c = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_Multishot_v2_2_c, "$EVENT$", EQUAL, 0 ) // change the 0 to something else if needed.
call TriggerAddCondition( gg_trg_Multishot_v2_2_c, Condition( function MultishotDelay ) )
endfunction
//! endtextmacro
//! runtextmacro MulthishotOnHit("udg_DamageEventSource","udg_DamageEventTarget","udg_DamageEventAmount","udg_DamageEvent")
endlibrary
-
Multishot caller
-
Events
-
Game - DamageModifierEvent becomes Equal to 1.00
-
-
Conditions
-
(Level of Multishot Never Miss for DamageEventSource) Greater than 0
-
-
Actions
-
Custom script: local integer dice
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
(Unit-type of DamageEventSource) Equal to Alleria Windrunner
-
-
Then - Actions
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
DamageEventAmount Greater than or equal to 125.00
-
-
Then - Actions
-
Custom script: call MultishotNormal( 0 ,55.00, 900.00, ATTACK_TYPE_CHAOS, 'A001')
-
-
Else - Actions
-
Custom script: call MultishotNormal( 7 , 55.00, 900.00, ATTACK_TYPE_CHAOS, 'A00P')
-
-
-
Custom script: if not Multishot_Damage then
-
Set DamageEventAmount = 0.00
-
Set DamageEventType = DamageTypeBlocked
-
Custom script: endif
-
-
Else - Actions
-
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
-
If - Conditions
-
(Unit-type of DamageEventSource) Equal to Value
-
-
Then - Actions
-
Custom script: set dice = GetRandomInt(2, 4)
-
Custom script: call MultishotNormal( dice , 60.00, 600.00, ATTACK_TYPE_PIERCE, 'A00P')
-
Custom script: if not Multishot_Damage then
-
Set DamageEventAmount = 0.00
-
Set DamageEventType = DamageTypeBlocked
-
Custom script: endif
-
-
Else - Actions
-
-
-
-
-