Name | Type | is_array | initial_value |
AfterDamageEvent | real | No | |
BouncingLightGroup | group | Yes | |
ChainFrost_X | real | Yes | |
ChainFrost_Y | real | Yes | |
ChainFrostGroup | group | Yes | |
ClearDamageEvent | trigger | No | |
DAMAGE_FACTOR_BRACERS | real | No | |
DAMAGE_FACTOR_ELUNES | real | No | |
DAMAGE_FACTOR_ETHEREAL | real | No | |
DamageBlockingAbility | abilcode | No | |
DamageEvent | real | No | |
DamageEventAmount | real | No | |
DamageEventOverride | boolean | No | |
DamageEventPrevAmt | real | No | |
DamageEventSource | unit | No | |
DamageEventsWasted | integer | No | |
DamageEventTarget | unit | No | |
DamageEventTrigger | trigger | No | |
DamageEventType | integer | No | |
DamageModifierEvent | real | No | |
DamageTypeBlocked | integer | No | |
DamageTypeCriticalStrike | integer | No | |
DamageTypeExplosive | integer | No | |
DamageTypeHeal | integer | No | |
DamageTypeReduced | integer | No | |
DmgEvBracers | itemcode | No | |
DmgEvRecursionN | integer | No | |
DmgEvRunning | boolean | No | |
DmgEvStarted | boolean | No | |
DmgEvTimer | timer | No | |
DmgEvTrig | trigger | No | |
GDD__Integers | integer | Yes | |
GDD__LeftMapGroup | group | No | |
GDD__TriggerArray | trigger | Yes | |
GDD__UnitArray | unit | Yes | |
GDD_Damage | real | No | |
GDD_DamagedUnit | unit | No | |
GDD_DamageSource | unit | No | |
GDD_Event | real | No | |
HideDamageFrom | boolean | Yes | |
IsDamageSpell | boolean | No | |
LastDamageHP | real | No | |
LastDmgPrevAmount | real | Yes | |
LastDmgPrevType | integer | Yes | |
LastDmgSource | unit | Yes | |
LastDmgTarget | unit | Yes | |
LastDmgValue | real | Yes | |
LastDmgWasSpell | boolean | Yes | |
MS_Current_Group | group | No | |
MS_Damage | real | No | |
MS_Dummy | unit | No | |
MS_Hit_Event | real | No | 1.00 |
MS_Hit_Unit | unit | No | |
MS_Main_Target | unit | No | |
MS_Missile | integer | No | |
MS_Source | unit | No | |
MS_UnitsInGroup | integer | No | |
NextDamageOverride | boolean | No | |
NextDamageType | integer | No | |
SpellDamageAbility | abilcode | No | |
Table | hashtable | No | |
Temp_Point | location | No | |
TempInteger | integer | No | |
TempPoint | location | No | |
UDex | integer | No | |
UDexGen | integer | No | |
UDexNext | integer | Yes | |
UDexPrev | integer | Yes | |
UDexRecycle | integer | No | |
UDexUnits | unit | Yes | |
UDexWasted | integer | No | |
UnitDamageRegistered | boolean | Yes | |
UnitIndexerEnabled | boolean | No | |
UnitIndexEvent | real | No | |
UnitIndexLock | integer | Yes |
//TESH.scrollpos=41
//TESH.alwaysfold=0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// --------------------------------- 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
//TESH.scrollpos=199
//TESH.alwaysfold=0
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// ----------------------------------------- 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
//TESH.scrollpos=81
//TESH.alwaysfold=0
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* ----------------------- ORBS = ADD-ON LIBRARY FOR WereElf's CUSOM MULTISHOT (By WereElf) ------------------------
This library allows MS to work with Orb effects for units with the Multishot ability. When it's used - ALL the units,
shot get affected by the orb effect.
The way it works is simple: When a unit is hit by Multishot - this library checks if the hit was done by a "special"
missile type. If it is - it summons a SPECIAL DUMMY, with enabled attack, gives it the right orb ability, and makes it
attack the target. A trigger is created. When the unit takes damage from the special dummy - the dummy is ordered to stop,
the trigger is destroyed, and the dummy is given a timed life.
NOTE: don't use the missile abilities, used for the orbs for normal Multishot, UNLESS you want the units' normal attacks
to act like the orbs.
REQUIREMENTS:
1) WereElf's Custom Multishot library (ofcourse)
2) A SPECIAL dummy, with attack enabled!!
3) Object editor:
- Each orb (of the ones listed below), which you want to use on your map must have 2 versions - a normal one (which is
already there), and one custom, for the Multishot
- Each orb type needs TWO custom orb abilities - 1 for the dummy, and one for the item.
* For the dummy - it should have its damage bonus set to 0.
* For the item - it needs to have its properties WITHOUT the effect of the orb. Since we don't want the orb effect
to apply before the shot has reached the unit.
- Each orb type requires a missile ability, with different Id, and different missile art (the orb's missile art)
4) You need to make a trigger: when a unit, which uses the multishot picks up an orb - replace it with the custom orb.
And when such orb is dropped - replace it with normal one.
5) Give the orb abilities, with no effect (just damage bonus) to the items.
6) The WANT_MULTISHOT_HIT_EVENT in the Multishot library must be set to true.
7) When all of the above is done - set the variables in the globals block below.
HOW TO USE:
- Before calling the Multishot function for a hero (or a unit with inventory) - call "GetActualMissile" function, and
give as arguments ( the shooter, the Id of the unit's default missile )
*/
library OrbsAddOn initializer Init
globals
// The ID of the SPECIAL dummy (you need to make a dummy with enabled attack)
constant integer ORB_DUMMY = 'h000'
// How much life do you want mask of death to steal on units with multishot
constant real LIFE_STEAL = 0.20 // from each unit hit
// The IDs of the Orb ITEMS.
constant integer ORB_OF_FROST = 'I001'
constant integer ORB_OF_CORRUPTION = 'I003'
constant integer ORB_OF_DARKNESS = 'I000'
constant integer ORB_OF_LIGHTNING = 'I005'
constant integer ORB_OF_FIRE = 'I002'
constant integer ORB_OF_SLOW = 'I006'
constant integer ORB_OF_VENOM = 'I007'
constant integer MASK_OF_DEATH = 'I004'
// The IDs of the Orb MISSILES.
constant integer FROST_MISSILE = 'A008'
constant integer CORRUPTION_MISSILE = 'A00E'
constant integer DARKNESS_MISSILE = 'A00H'
constant integer LIGHTNING_MISSILE = 'A00L'
constant integer FIRE_MISSILE = 'A00N'
constant integer SLOW_MISSILE = 'A00Q'
constant integer VENOM_MISSILE = 'A00O'
constant integer LIFE_STEAL_MISSILE = 'A00W'
// The IDs of the Dummy Orbs (with the effect, without the damage bonus)
constant integer DUMMY_FROST = 'A00T'
constant integer DUMMY_CORRUPTION = 'A00F'
constant integer DUMMY_DARKNESS = 'A00J'
constant integer DUMMY_LIGHTNING = 'A009'
constant integer DUMMY_SLOW = 'A00V'
constant integer DUMMY_VENOM = 'A00U'
// Used for triggering the Orb of Fire
private group FireGroup = CreateGroup()
endglobals
// When this function is called - it checks all of the inventory slots if they contain orbs.
// If an orb is found - the missile Id of the orb is returned (and the loop stops)
// If no orb is found - it returns the unit's default missile.
function GetActualMissile takes unit u, integer default_missile returns integer
local integer i = 0
local integer j
loop
exitwhen i > bj_MAX_INVENTORY
set j = GetItemTypeId(UnitItemInSlot(u, i))
if j == ORB_OF_FROST then
return FROST_MISSILE
elseif j == ORB_OF_CORRUPTION then
return CORRUPTION_MISSILE
elseif j == ORB_OF_DARKNESS then
return DARKNESS_MISSILE
elseif j == ORB_OF_LIGHTNING then
return LIGHTNING_MISSILE
elseif j == ORB_OF_FIRE then
return FIRE_MISSILE
elseif j == ORB_OF_VENOM then
return VENOM_MISSILE
elseif j == ORB_OF_SLOW then
return SLOW_MISSILE
elseif j == MASK_OF_DEATH then
return LIFE_STEAL_MISSILE
endif
set i = i + 1
endloop
return default_missile
endfunction
// ORB OF FIRE EFFECT
// Simply triggers the splash.
private function OrbOfFire takes nothing returns nothing
local real x = GetUnitX(udg_MS_Hit_Unit)
local real y = GetUnitY(udg_MS_Hit_Unit)
local unit u
local player p = GetOwningPlayer(udg_MS_Source)
call GroupEnumUnitsInRange(FireGroup, x, y, 175, null)
loop
set u = FirstOfGroup(FireGroup)
exitwhen u == null
if u != udg_MS_Hit_Unit and IsUnitEnemy( u , p ) then
call UnitDamageTarget(udg_MS_Dummy, u, udg_MS_Damage/5, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
endif
call GroupRemoveUnit(FireGroup, u)
endloop
set p = null
endfunction
// LIFE STEAL EFFECT
// Heals the shooter for % of the damage dealt. Special effect appears only when the main target is hit,
private function LifeSteal takes nothing returns nothing
local real hp = GetWidgetLife(udg_MS_Source)
call SetWidgetLife(udg_MS_Source, hp + LIFE_STEAL*udg_MS_Damage)
if udg_MS_Hit_Unit == udg_MS_Main_Target then
call DestroyEffect(AddSpecialEffectTarget("Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl", udg_MS_Source, "origin"))
endif
endfunction
// Orb function caller
// When the special dummy hits the target unit - the trigger is destroyed, and the dummy is given 0.01 sec timed life.
private function DummyHit takes nothing returns boolean
if GetUnitTypeId(udg_DamageEventSource) == ORB_DUMMY then
call IssueImmediateOrder(udg_DamageEventSource, "stop")
call UnitApplyTimedLife(udg_DamageEventSource, 'BTLF', 0.01)
endif
return false
endfunction
// When a unit is hit by the Multishot - it checks if the missile is any of the orb missiles.
// If it is - it summons the special dummy, gives it the orb ability, and makes it attack the unit.
// It also creates a trigger to register when the dummy hits the unit.
// Unless it's orb of fire. In that case - it calls the function above.
//! textmacro OrbDummy takes TYPE, EXTRA
set d = CreateUnit(GetOwningPlayer(udg_MS_Source), ORB_DUMMY, GetUnitX(udg_MS_Hit_Unit) - 50, GetUnitY(udg_MS_Hit_Unit), 0.00)
call UnitApplyTimedLife(d, 'BTLF', 1)
call UnitAddAbility( d, DUMMY_$TYPE$ )
$EXTRA$
call IssueTargetOrder( d, "attack", udg_MS_Hit_Unit )
set d = null
//! endtextmacro
private function Trig_On_Hit_Actions takes nothing returns boolean
local unit d
if udg_MS_Missile == FIRE_MISSILE then
call OrbOfFire()
return false
elseif udg_MS_Missile == LIFE_STEAL_MISSILE then
call LifeSteal()
return false
endif
if udg_MS_Missile == CORRUPTION_MISSILE then
//! runtextmacro OrbDummy("CORRUPTION","")
elseif udg_MS_Missile == DARKNESS_MISSILE then
//! runtextmacro OrbDummy("DARKNESS","")
elseif udg_MS_Missile == LIGHTNING_MISSILE then
//! runtextmacro OrbDummy("LIGHTNING","")
elseif udg_MS_Missile == FROST_MISSILE then
//! runtextmacro OrbDummy("FROST","")
elseif udg_MS_Missile == SLOW_MISSILE then
//! runtextmacro OrbDummy("SLOW","")
elseif udg_MS_Missile == VENOM_MISSILE then
//! runtextmacro OrbDummy("VENOM","call UnitAddAbility( d, 'Apo2' )")
endif
return false
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger()
set gg_trg_Custom_Orb_Effects = CreateTrigger()
call TriggerRegisterVariableEvent( gg_trg_Custom_Orb_Effects, "udg_MS_Hit_Event", EQUAL, 0 )
call TriggerAddCondition( gg_trg_Custom_Orb_Effects, Condition(function Trig_On_Hit_Actions) )
call TriggerRegisterVariableEvent( t, "udg_DamageEvent", EQUAL, 0 )
call TriggerAddCondition( t, Condition(function DummyHit))
set t = null
endfunction
endlibrary
//TESH.scrollpos=48
//TESH.alwaysfold=0
//! novjass
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
MultishotBasic takes the same arguments as MultishotTarget, without the boolexpr. Its set to target only enemies.
//! endnovjass
library MultishoRt requires Multishot, optional OrbsAddOn
globals
private player ConditionPlayer
endglobals
function FilterEnemies takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), ConditionPlayer)
endfunction
function FilterAllies takes nothing returns boolean
return IsUnitAlly(GetFilterUnit(), ConditionPlayer) and GetFilterUnit() != GetTriggerUnit()
endfunction
function MultishotNormal takes integer targets, real arc, real range, attacktype attackType, integer missile returns nothing
call MultishotLightAdvanced(udg_DamageEventSource, udg_DamageEventTarget, udg_DamageEventAmount, targets, arc, range, true, attackType, missile, 0.00, false, DAMAGE_TYPE_NORMAL, 900.00)
endfunction
function MultishotSpell takes unit caster, unit target, integer targets, real arc, real range, real damage, integer missile returns nothing
set ConditionPlayer = GetOwningPlayer(caster)
call MultishotTarget(caster, target, damage, targets, arc, range, true, ATTACK_TYPE_NORMAL, missile, 0.00, true, DAMAGE_TYPE_MAGIC, Condition(function FilterEnemies))
endfunction
function MultishotPointShort takes unit caster, real x, real y, real damage, integer missile returns nothing
set ConditionPlayer = GetOwningPlayer(caster)
call MultishotPoint(caster, damage, 0, 25, 1000, ATTACK_TYPE_NORMAL, missile, 0.00, DAMAGE_TYPE_MAGIC, x, y, Condition(function FilterEnemies))
endfunction
function MultishotHeal takes unit caster, location loc, real amount, integer missile returns nothing
set ConditionPlayer = GetOwningPlayer(caster)
call MultishotPointAdvanced(caster, amount, 0, 90, 500, ATTACK_TYPE_NORMAL, missile, 0.00, DAMAGE_TYPE_NORMAL, GetLocationX(loc), GetLocationY(loc), Condition(function FilterAllies), 500.00)
endfunction
function MultishotOrb takes integer targets, real arc, real range, integer missile returns nothing
local real speed
local integer temp = missile
static if LIBRARY_OrbsAddOn then
set missile = GetActualMissile(udg_DamageEventSource, missile)
if missile == temp then
set speed = 900
else
set speed = 1050
endif
else
set speed = 900
endif
set ConditionPlayer = GetOwningPlayer(udg_DamageEventSource)
call MultishotLightAdvanced(udg_DamageEventSource, udg_DamageEventTarget, udg_DamageEventAmount, targets, arc, range, true, ATTACK_TYPE_HERO, missile, 0.00, false, DAMAGE_TYPE_NORMAL, speed)
endfunction
endlibrary
//TESH.scrollpos=27
//TESH.alwaysfold=0
scope BouncingLight2
globals
private player CondPlayer
private group CondGroup
constant integer MAX_TARGETS_ALLOWED = 4
endglobals
private function BouncingLightCond takes nothing returns boolean
return (IsUnitEnemy(GetFilterUnit(), CondPlayer) and not IsUnitInGroup(GetFilterUnit(), CondGroup))
endfunction
function Trig_Bouncing_Light_2_Conditions takes nothing returns boolean
local real x
local real y
local integer pl
local real a
local integer t
if udg_MS_Missile == 'A012' then
set x = GetUnitX(udg_MS_Hit_Unit)
set y = GetUnitY(udg_MS_Hit_Unit)
set CondPlayer = GetOwningPlayer(udg_MS_Source)
set pl = GetPlayerId(CondPlayer)
call SetUnitPosition(udg_MS_Dummy, x, y)
set CondGroup = udg_MS_Current_Group
if GetRandomInt(1, 100) <= 10 then
set t = 2
else
set t = 1
endif
call MultishotPointAdvanced(udg_MS_Dummy, 400, t, 180, 300, ATTACK_TYPE_CHAOS, 'A012', 0.00, DAMAGE_TYPE_UNIVERSAL, x + 100, y + 100, Condition(function BouncingLightCond), 900.00)
elseif udg_MS_Missile == 'A014' and udg_MS_UnitsInGroup < MAX_TARGETS_ALLOWED then
set x = GetUnitX(udg_MS_Hit_Unit)
set y = GetUnitY(udg_MS_Hit_Unit)
set CondGroup = udg_MS_Current_Group
set CondPlayer = GetOwningPlayer(udg_MS_Source)
call SetUnitPosition(udg_MS_Dummy, x, y)
call MultishotPointAdvanced(udg_MS_Dummy, udg_MS_Damage*0.8, 1, 180, 300, ATTACK_TYPE_PIERCE, 'A014', 0, DAMAGE_TYPE_NORMAL, x + 100, y + 100, Condition(function BouncingLightCond),900.00)
elseif GetUnitTypeId(udg_MS_Source) == 'Emoo' and udg_MS_UnitsInGroup < MAX_TARGETS_ALLOWED then
set x = GetUnitX(udg_MS_Hit_Unit)
set y = GetUnitY(udg_MS_Hit_Unit)
set CondGroup = udg_MS_Current_Group
set CondPlayer = GetOwningPlayer(udg_MS_Source)
call SetUnitPosition(udg_MS_Dummy, x, y)
call MultishotPointAdvanced(udg_MS_Dummy, udg_MS_Damage*0.8, 1, 180, 300, ATTACK_TYPE_HERO, udg_MS_Missile, 0, DAMAGE_TYPE_NORMAL, x + 100, y + 100, Condition(function BouncingLightCond),900.00)
endif
return false
endfunction
//===========================================================================
function InitTrig_Special takes nothing returns nothing
set gg_trg_Special = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_Special, "udg_MS_Hit_Event", EQUAL, 0 )
call TriggerAddCondition( gg_trg_Special, Condition( function Trig_Bouncing_Light_2_Conditions ) )
endfunction
endscope
//TESH.scrollpos=6
//TESH.alwaysfold=0
scope ChainFrost2
globals
private player CondPlayer
private group CondGroup
endglobals
private function ChainFrostCond takes nothing returns boolean
return (IsUnitEnemy(GetFilterUnit(), CondPlayer) and not IsUnitInGroup(GetFilterUnit(), CondGroup))
endfunction
function Trig_Chain_Frost_2_Conditions takes nothing returns boolean
local real x
local real y
local integer pl = GetPlayerId(GetOwningPlayer(udg_MS_Source))
local real a
if IsUnitInGroup(udg_MS_Hit_Unit, udg_ChainFrostGroup[pl]) and udg_MS_Missile == 'A010' then
set x = GetUnitX(udg_MS_Hit_Unit)
set y = GetUnitY(udg_MS_Hit_Unit)
set CondPlayer = GetOwningPlayer(udg_MS_Source)
set a = Atan2(y - udg_ChainFrost_Y[pl], x - udg_ChainFrost_X[pl])
set CondGroup = udg_MS_Current_Group
call SetUnitPosition(udg_MS_Dummy, x, y)
call MultishotPointAdvanced(udg_MS_Dummy, 300, 3, 30, 500, ATTACK_TYPE_CHAOS, 'A010', 0.00, DAMAGE_TYPE_UNIVERSAL, x + 100*Cos(a), y + 100*Sin(a), Condition(function ChainFrostCond), 300.00)
endif
return false
endfunction
//===========================================================================
function InitTrig_Chain_Reaction_2 takes nothing returns nothing
set gg_trg_Chain_Reaction_2 = CreateTrigger( )
call TriggerRegisterVariableEvent( gg_trg_Chain_Reaction_2, "udg_MS_Hit_Event", EQUAL, 0 )
call TriggerAddCondition( gg_trg_Chain_Reaction_2, Condition( function Trig_Chain_Frost_2_Conditions ) )
endfunction
endscope
// GUI-Friendly Damage Detection -- v1.2.1 -- by Weep
// http:// www.thehelper.net/forums/showthread.php?t=137957
//
// Requires: only this trigger and its variables.
//
// -- What? --
// This snippet provides a leak-free, GUI-friendly implementation of an "any unit takes
// damage" event. It requires no JASS knowledge to use.
//
// It uses the Game - Value Of Real Variable event as its method of activating other
// triggers, and passes the event responses through a few globals.
//
// -- Why? --
// The traditional GUI method of setting up a trigger than runs when any unit is damaged
// leaks trigger events. This snippet is easy to implement and removes the need to do
// you own GUI damage detection setup.
//
// -- How To Implement --
// 0. Before you copy triggers that use GDD into a new map, you need to copy over GDD
// with its GDD Variable Creator trigger, or there will be a problem: the variables
// won't be automatically created correctly.
//
// 1. Be sure "Automatically create unknown variables while pasting trigger data" is
// enabled in the World Editor general preferences.
// 2. Copy this trigger category ("GDD") and paste it into your map.
// (Alternately: create the variables listed in the globals block below, create a
// trigger named "GUI Friendly Damage Detection", and paste in this entire text.)
// 3. Create your damage triggers using Game - Value Of Real Variable as the event,
// select GDD_Event as the variable, and leave the rest of the settings to the default
// "becomes Equal to 0.00".
// The event responses are the following variables:
// GDD_Damage is the amount of damage, replacing Event Response - Damage Taken.
// GDD_DamagedUnit is the damaged unit, replacing Event Response - Triggering Unit.
// Triggering Unit can still be used, if you need to use waits.
// Read the -- Notes -- section below for more info.
// GDD_DamageSource is the damaging unit, replacing Event Response - Damage Source.
//
// -- Notes --
// GDD's event response variables are not wait-safe; you can't use them after a wait in
// a trigger. If you need to use waits, Triggering Unit (a.k.a. GetTriggerUnit()) can
// be used in place of GDD_DamageSource. There is no usable wait-safe equivalent to
// Event Damage or Damage Source; you'll need to save the values yourself.
//
// Don't write any values to the variables used as the event responses, or it will mess
// up any other triggers using this snippet for their triggering. Only use their values.
//
// This uses arrays, so can detect damage for a maximum of 8190 units at a time, and
// cleans up data at a rate of 33.33 per second, by default. This should be enough for
// most maps, but if you want to change the rate, change the value returned in the
// GDD_RecycleRate function at the top of the code, below.
//
// By default, GDD will not register units that have Locust at the moment of their
// entering the game, and will not recognize when they take damage (which can only
// happen if the Locust ability is later removed from the unit.) To allow a unit to have
// Locust yet still cause GDD damage events if Locust is removed, you can either design
// the unit to not have Locust by default and add it via triggers after creation, or
// edit the GDD_Filter function at the top of the code, below.
//
// -- Credits --
// Captain Griffin on wc3c.net for the research and concept of GroupRefresh.
//
// Credit in your map not needed, but please include this README.
//
// -- Version History --
// 1.2.1: Minor code cleaning. Added configuration functions. Updated documentation.
// 1.2.0: Made this snippet work properly with recursive damage.
// 1.1.1: Added a check in order to not index units with the Locust ability (dummy units).
// If you wish to check for damage taken by a unit that is unselectable, do not
// give the unit-type Locust in the object editor; instead, add the Locust ability
// 'Aloc' via a trigger after its creation, then remove it.
// 1.1.0: Added a check in case a unit gets moved out of the map and back.
// 1.0.0: First release.
//===================================================================
// Configurables.
function GDD_RecycleRate takes nothing returns real //The rate at which the system checks units to see if they've been removed from the game
return 0.03
endfunction
function GDD_Filter takes unit u returns boolean //The condition a unit has to pass to have it registered for damage detection
return GetUnitAbilityLevel(u, 'Aloc') == 0 //By default, the system ignores Locust units, because they normally can't take damage anyway
endfunction
//===================================================================
// This is just for reference.
// If you use JassHelper, you could uncomment this section instead of creating the variables in the trigger editor.
// globals
// real udg_GDD_Event = 0.
// real udg_GDD_Damage = 0.
// unit udg_GDD_DamagedUnit
// unit udg_GDD_DamageSource
// trigger array udg_GDD__TriggerArray
// integer array udg_GDD__Integers
// unit array udg_GDD__UnitArray
// group udg_GDD__LeftMapGroup = CreateGroup()
// endglobals
//===================================================================
// System code follows. Don't touch!
function GDD_Event takes nothing returns boolean
local unit damagedcache = udg_GDD_DamagedUnit
local unit damagingcache = udg_GDD_DamageSource
local real damagecache = udg_GDD_Damage
set udg_GDD_DamagedUnit = GetTriggerUnit()
set udg_GDD_DamageSource = GetEventDamageSource()
set udg_GDD_Damage = GetEventDamage()
set udg_GDD_Event = 1.
set udg_GDD_Event = 0.
set udg_GDD_DamagedUnit = damagedcache
set udg_GDD_DamageSource = damagingcache
set udg_GDD_Damage = damagecache
set damagedcache = null
set damagingcache = null
return false
endfunction
function GDD_AddDetection takes nothing returns boolean
// if(udg_GDD__Integers[0] > 8190) then
// call BJDebugMsg("GDD: Too many damage events! Decrease number of units present in the map or increase recycle rate.")
// ***Recycle rate is specified in the GDD_RecycleRate function at the top of the code. Smaller is faster.***
// return
// endif
if(IsUnitInGroup(GetFilterUnit(), udg_GDD__LeftMapGroup)) then
call GroupRemoveUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
elseif(GDD_Filter(GetFilterUnit())) then
set udg_GDD__Integers[0] = udg_GDD__Integers[0]+1
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = GetFilterUnit()
set udg_GDD__TriggerArray[udg_GDD__Integers[0]] = CreateTrigger()
call TriggerRegisterUnitEvent(udg_GDD__TriggerArray[udg_GDD__Integers[0]], udg_GDD__UnitArray[udg_GDD__Integers[0]], EVENT_UNIT_DAMAGED)
call TriggerAddCondition(udg_GDD__TriggerArray[udg_GDD__Integers[0]], Condition(function GDD_Event))
endif
return false
endfunction
function GDD_PreplacedDetection takes nothing returns nothing
local group g = CreateGroup()
local integer i = 0
loop
call GroupEnumUnitsOfPlayer(g, Player(i), Condition(function GDD_AddDetection))
set i = i+1
exitwhen i == bj_MAX_PLAYER_SLOTS
endloop
call DestroyGroup(g)
set g = null
endfunction
function GDD_GroupRefresh takes nothing returns nothing
// Based on GroupRefresh by Captain Griffen on wc3c.net
if (bj_slotControlUsed[5063] == true) then
call GroupClear(udg_GDD__LeftMapGroup)
set bj_slotControlUsed[5063] = false
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetEnumUnit())
endfunction
function GDD_Recycle takes nothing returns nothing
if(udg_GDD__Integers[0] <= 0) then
return
elseif(udg_GDD__Integers[1] <= 0) then
set udg_GDD__Integers[1] = udg_GDD__Integers[0]
endif
if(GetUnitTypeId(udg_GDD__UnitArray[udg_GDD__Integers[1]]) == 0) then
call DestroyTrigger(udg_GDD__TriggerArray[udg_GDD__Integers[1]])
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = null
set udg_GDD__TriggerArray[udg_GDD__Integers[1]] = udg_GDD__TriggerArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[1]] = udg_GDD__UnitArray[udg_GDD__Integers[0]]
set udg_GDD__UnitArray[udg_GDD__Integers[0]] = null
set udg_GDD__Integers[0] = udg_GDD__Integers[0]-1
endif
set udg_GDD__Integers[1] = udg_GDD__Integers[1]-1
endfunction
function GDD_LeaveMap takes nothing returns boolean
local boolean cached = bj_slotControlUsed[5063]
if(udg_GDD__Integers[2] < 64) then
set udg_GDD__Integers[2] = udg_GDD__Integers[2]+1
else
set bj_slotControlUsed[5063] = true
call ForGroup(udg_GDD__LeftMapGroup, function GDD_GroupRefresh)
set udg_GDD__Integers[2] = 0
endif
call GroupAddUnit(udg_GDD__LeftMapGroup, GetFilterUnit())
set bj_slotControlUsed[5063] = cached
return false
endfunction
// ===========================================================================
function InitTrig_GUI_Friendly_Damage_Detection takes nothing returns nothing
local region r = CreateRegion()
call RegionAddRect(r, GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(), r, Condition(function GDD_AddDetection))
call TriggerRegisterLeaveRegion(CreateTrigger(), r, Condition(function GDD_LeaveMap))
call GDD_PreplacedDetection()
call TimerStart(CreateTimer(), GDD_RecycleRate(), true, function GDD_Recycle)
set r = null
endfunction
//TESH.scrollpos=273
//TESH.alwaysfold=0
//===========================================================================
// Damage Engine lets you detect, amplify, block or nullify damage. It even
// lets you detect if the damage was physical or from a spell. Just reference
// DamageEventAmount/Source/Target or the boolean IsDamageSpell, to get the
// necessary damage event data.
//
// - Detect damage: use the event "DamageEvent Equal to 1.00"
// - To change damage before it's dealt: use the event "DamageModifierEvent Equal to 1.00"
// - Detect damage after it was applied, use the event "AfterDamageEvent Equal to 1.00"
// - Detect spell damage: use the condition "IsDamageSpell Equal to True"
// - Detect zero-damage: use the event "DamageEvent Equal to 2.00" (an AfterDamageEvent will not fire for this)
//
// You can specify the DamageEventType before dealing triggered damage. To prevent an already-improbable error, I recommend running the trigger "ClearDamageEvent (Checking Conditions)" after dealing triggered damage from within a damage event:
// - Set NextDamageType = DamageTypeWhatever
// - Unit - Cause...
// - Trigger - Run ClearDamageEvent (Checking Conditions)
//
// You can modify the DamageEventAmount and the DamageEventType from a "DamageModifierEvent Equal to 1.00" trigger.
// - If the amount is modified to negative, it will count as a heal.
// - If the amount is set to 0, no damage will be dealt.
//
// If you need to reference the original in-game damage, use the variable "DamageEventPrevAmt".
//
//===========================================================================
// Programming note about "integer i" and "udg_DmgEvRecursionN": integer i
// ranges from -1 upwards. "udg_DmgEvRecursionN" ranges from 0 upwards.
// "integer i" is always 1 less than "udg_DmgEvRecursionN"
//
function DmgEvResetVars takes nothing returns nothing
local integer i = udg_DmgEvRecursionN - 2
set udg_DmgEvRecursionN = i + 1
if i >= 0 then
set udg_DamageEventPrevAmt = udg_LastDmgPrevAmount[i]
set udg_DamageEventAmount = udg_LastDmgValue[i]
set udg_DamageEventSource = udg_LastDmgSource[i]
set udg_DamageEventTarget = udg_LastDmgTarget[i]
set udg_IsDamageSpell = udg_LastDmgWasSpell[i]
set udg_DamageEventType = udg_LastDmgPrevType[i]
endif
endfunction
function CheckDamagedLifeEvent takes boolean clear returns nothing
if clear then
set udg_NextDamageOverride = false
set udg_NextDamageType = 0
endif
if udg_DmgEvTrig != null then
call DestroyTrigger(udg_DmgEvTrig)
set udg_DmgEvTrig = null
if udg_IsDamageSpell then
call SetWidgetLife(udg_DamageEventTarget, RMaxBJ(udg_LastDamageHP, 0.41))
if udg_LastDamageHP <= 0.405 then
if udg_DamageEventType < 0 then
call SetUnitExploded(udg_DamageEventTarget, true)
endif
//Kill the unit
call DisableTrigger(udg_DamageEventTrigger)
call UnitDamageTarget(udg_DamageEventSource, udg_DamageEventTarget, -999, false, false, null, DAMAGE_TYPE_UNIVERSAL, null)
call EnableTrigger(udg_DamageEventTrigger)
endif
elseif GetUnitAbilityLevel(udg_DamageEventTarget, udg_DamageBlockingAbility) > 0 then
call UnitRemoveAbility(udg_DamageEventTarget, udg_DamageBlockingAbility)
call SetWidgetLife(udg_DamageEventTarget, udg_LastDamageHP)
endif
if udg_DamageEventAmount != 0.00 and not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_AfterDamageEvent = 0.00
set udg_AfterDamageEvent = 1.00
set udg_AfterDamageEvent = 0.00
endif
call DmgEvResetVars()
endif
endfunction
function DmgEvOnExpire takes nothing returns nothing
set udg_DmgEvStarted = false
call CheckDamagedLifeEvent(true)
endfunction
function PreCheckDamagedLifeEvent takes nothing returns boolean
call CheckDamagedLifeEvent(true)
return false
endfunction
function OnUnitDamage takes nothing returns boolean
local boolean override = udg_DamageEventOverride
local integer i = udg_DmgEvRecursionN - 1
local string s
local real prevAmount
local real life
local real prevLife
local unit u
call CheckDamagedLifeEvent(false) //in case the unit state event failed and the 0.00 second timer hasn't yet expired
if i >= 0 then
if i < 16 then
set udg_LastDmgPrevAmount[i]= udg_DamageEventPrevAmt
set udg_LastDmgValue[i] = udg_DamageEventAmount
set udg_LastDmgSource[i] = udg_DamageEventSource
set udg_LastDmgTarget[i] = udg_DamageEventTarget
set udg_LastDmgWasSpell[i] = udg_IsDamageSpell
set udg_LastDmgPrevType[i] = udg_DamageEventType
else
set s = "WARNING: Recursion error when dealing damage! Make sure when you deal damage from within a DamageEvent trigger, do it like this:\n\n"
set s = s + "Trigger - Turn off (This Trigger)\n"
set s = s + "Unit - Cause...\n"
set s = s + "Trigger - Turn on (This Trigger)"
//Delete the next couple of lines to disable the in-game recursion crash warnings
call ClearTextMessages()
call DisplayTimedTextToPlayer(GetLocalPlayer(), 0.00, 0.00, 999.00, s)
return false
endif
endif
set udg_DmgEvRecursionN = i + 2
set u = GetTriggerUnit()
set prevAmount = GetEventDamage()
set udg_DamageEventSource = GetEventDamageSource()
set udg_DamageEventAmount = prevAmount
set udg_DamageEventTarget = u
set udg_DamageEventType = udg_NextDamageType
set udg_NextDamageType = 0
set udg_DamageEventOverride = udg_NextDamageOverride
set udg_NextDamageOverride = false
if prevAmount == 0.00 then
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEventPrevAmt = 0.00
set udg_DamageEvent = 0.00
set udg_DamageEvent = 2.00
set udg_DamageEvent = 0.00
endif
call DmgEvResetVars()
else
if not udg_DmgEvStarted then
set udg_DmgEvStarted = true
call TimerStart(udg_DmgEvTimer, 0.00, false, function DmgEvOnExpire)
endif
set udg_IsDamageSpell = prevAmount < 0.00
if udg_IsDamageSpell then
set prevAmount = -udg_DamageEventAmount
set life = 1.00
if IsUnitType(u, UNIT_TYPE_ETHEREAL) and not IsUnitType(u, UNIT_TYPE_HERO) then
set life = life*udg_DAMAGE_FACTOR_ETHEREAL //1.67
endif
if GetUnitAbilityLevel(u, 'Aegr') > 0 then
set life = life*udg_DAMAGE_FACTOR_ELUNES //0.80
endif
if udg_DmgEvBracers != 0 and IsUnitType(u, UNIT_TYPE_HERO) then
//Inline of UnitHasItemOfTypeBJ without the potential handle ID leak.
set i = 6
loop
set i = i - 1
if GetItemTypeId(UnitItemInSlot(u, i)) == udg_DmgEvBracers then
set life = life*udg_DAMAGE_FACTOR_BRACERS //0.67
exitwhen true
endif
exitwhen i == 0
endloop
endif
set udg_DamageEventAmount = prevAmount*life
endif
set udg_DamageEventPrevAmt = prevAmount
set udg_DamageModifierEvent = 0.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 1.00
if not udg_DamageEventOverride then
set udg_DamageModifierEvent = 2.00
set udg_DamageModifierEvent = 3.00
endif
endif
set udg_DamageEventOverride = override
if udg_DamageEventAmount > 0.00 then
set udg_DamageModifierEvent = 4.00
endif
set udg_DamageModifierEvent = 0.00
if not udg_HideDamageFrom[GetUnitUserData(udg_DamageEventSource)] then
set udg_DamageEvent = 0.00
set udg_DamageEvent = 1.00
set udg_DamageEvent = 0.00
endif
call CheckDamagedLifeEvent(true) //in case the unit state event failed from a recursive damage event
//All events have run and the damage amount is finalized.
set life = GetWidgetLife(u)
set udg_DmgEvTrig = CreateTrigger()
call TriggerAddCondition(udg_DmgEvTrig, Filter(function PreCheckDamagedLifeEvent))
if not udg_IsDamageSpell then
if udg_DamageEventAmount != prevAmount then
set life = life + prevAmount - udg_DamageEventAmount
if GetUnitState(u, UNIT_STATE_MAX_LIFE) < life then
set udg_LastDamageHP = life - prevAmount
call UnitAddAbility(u, udg_DamageBlockingAbility)
endif
call SetWidgetLife(u, RMaxBJ(life, 0.42))
endif
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, LESS_THAN, RMaxBJ(0.41, life - prevAmount/2.00))
else
set udg_LastDamageHP = GetUnitState(u, UNIT_STATE_MAX_LIFE)
set prevLife = life
if life + prevAmount*0.75 > udg_LastDamageHP then
set life = RMaxBJ(udg_LastDamageHP - prevAmount/2.00, 1.00)
call SetWidgetLife(u, life)
set life = (life + udg_LastDamageHP)/2.00
else
set life = life + prevAmount*0.50
endif
set udg_LastDamageHP = prevLife - (prevAmount - (prevAmount - udg_DamageEventAmount))
call TriggerRegisterUnitStateEvent(udg_DmgEvTrig, u, UNIT_STATE_LIFE, GREATER_THAN, life)
endif
set u = null
endif
return false
endfunction
function CreateDmgEvTrg takes nothing returns nothing
set udg_DamageEventTrigger = CreateTrigger()
call TriggerAddCondition(udg_DamageEventTrigger, Filter(function OnUnitDamage))
endfunction
function SetupDmgEv takes nothing returns boolean
local integer i = udg_UDex
local unit u
if udg_UnitIndexEvent == 1.00 then
set u = udg_UDexUnits[i]
if GetUnitAbilityLevel(u, 'Aloc') == 0 and TriggerEvaluate(gg_trg_Damage_Engine_Config) then
set udg_UnitDamageRegistered[i] = true
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, u, EVENT_UNIT_DAMAGED)
call UnitAddAbility(u, udg_SpellDamageAbility)
call UnitMakeAbilityPermanent(u, true, udg_SpellDamageAbility)
endif
set u = null
else
set udg_HideDamageFrom[i] = false
if udg_UnitDamageRegistered[i] then
set udg_UnitDamageRegistered[i] = false
set udg_DamageEventsWasted = udg_DamageEventsWasted + 1
if udg_DamageEventsWasted == 32 then //After 32 registered units have been removed...
set udg_DamageEventsWasted = 0
//Rebuild the mass EVENT_UNIT_DAMAGED trigger:
call DestroyTrigger(udg_DamageEventTrigger)
call CreateDmgEvTrg()
set i = udg_UDexNext[0]
loop
exitwhen i == 0
if udg_UnitDamageRegistered[i] then
call TriggerRegisterUnitEvent(udg_DamageEventTrigger, udg_UDexUnits[i], EVENT_UNIT_DAMAGED)
endif
set i = udg_UDexNext[i]
endloop
endif
endif
endif
return false
endfunction
//===========================================================================
function InitTrig_Damage_Engine takes nothing returns nothing
local unit u = CreateUnit(Player(15), 'uloc', 0, 0, 0)
local integer i = 16
//Create this trigger with UnitIndexEvents in order add and remove units
//as they are created or removed.
local trigger t = CreateTrigger()
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 1.00)
call TriggerRegisterVariableEvent(t, "udg_UnitIndexEvent", EQUAL, 2.00)
call TriggerAddCondition(t, Filter(function SetupDmgEv))
set t = null
//Run the configuration trigger to set all configurables:
if gg_trg_Damage_Engine_Config == null then
//It's possible this InitTrig_ function ran first, in which case use ExecuteFunc.
call ExecuteFunc("Trig_Damage_Engine_Config_Actions")
else
call TriggerExecute(gg_trg_Damage_Engine_Config)
endif
//Create trigger for storing all EVENT_UNIT_DAMAGED events.
call CreateDmgEvTrg()
//Create GUI-friendly trigger for cleaning up after UnitDamageTarget.
set udg_ClearDamageEvent = CreateTrigger()
call TriggerAddCondition(udg_ClearDamageEvent, Filter(function PreCheckDamagedLifeEvent))
//Disable SpellDamageAbility for every player.
loop
set i = i - 1
call SetPlayerAbilityAvailable(Player(i), udg_SpellDamageAbility, false)
exitwhen i == 0
endloop
//Preload abilities.
call UnitAddAbility(u, udg_DamageBlockingAbility)
call UnitAddAbility(u, udg_SpellDamageAbility)
call RemoveUnit(u)
set u = null
endfunction