globals
//User settings:
real towerHeight = 600 //Height from which first lightning shall leave tower. 275 is about the height of the orb of an Arcane Tower with standard size
real heightOffset = 25 //Target lightning height offset. This changes the height on which the lightning should hit the target. If set to 0, lightnings will be on ground level. Unit flying height is automatically added.
real additionalTargetRange = 350 //The maximum range at which towers can detect targets from other targets. First target range is attacking range of attacking unit
integer amountTargets = 5 //Amount of targets the tower can attack. Includes main target. Has to be at least 1
real damageReductionFactor = 0.9 //Indicates damage reduction per lightning jump. Set 1 for no reduction. Greater than 1 for dmg increase. 0.9 equals 90% of previous damage for each additional target
string lightningType = "CLPB" //Determines which lightning graphic shall be used
boolean canAttackMagicImmune = false //Determines whether or not towers can target spell immune units. Needs to have fitting attack- and damage types, otherwhise towers can focus spell immune units but won't deal damage
attacktype attackType = ATTACK_TYPE_MAGIC //The attack type which is used to damage target units. List of attack types can be found below
damagetype damageType = DAMAGE_TYPE_LIGHTNING //The damage type which is used to damage units. List of attack types can be found below
//Consecutive damage increase system:
integer ticksTillDamageReset = 100 //How many ticks until tower bonus damage will be reset. 100 ticks equal 5 seconds when trigger is set to periodic 0.05 seconds
integer ticksTillMaxDamage = 100 //How many ticks until towers reach their maximum bonus damage. 100 ticks equal 5 seconds when trigger is set to periodic 0.05 seconds
real startDamage = 1 //Amount of damage towers deal per tick at the beginning of their attacks
real maxDamage = 10 //Max amount of damage towers can deal when attacking. Can be less then start damage to reduce damage over time.
//Lightning colour can be changed during damage changing time. Set start and max colour to same values if colour should not be changed over time. 1 is standard colour. Value range is from 0 to 1
real startRed = 1 //Red starting value
real startGreen = 1 //Green starting value
real startBlue = 1 //Blue starting value
real startAlpha = 1 //Alpha starting value. Determines lightning transparency. 1 is maximum visibility, 0 is invisible
real maxDamageRed = 0 //Red maximum value
real maxDamageGreen = 1 //Green maximum value
real maxDamageBlue = 0 //Blue maximum value
real maxDamageAlpha = 1 //Alpha maximum value. Determines lightning transparency. 1 is maximum visibility, 0 is invisible
endglobals
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
//Damage types are:
//DAMAGE_TYPE_NORMAL
//DAMAGE_TYPE_ENHANCED
//DAMAGE_TYPE_POISON
//DAMAGE_TYPE_DISEASE
//DAMAGE_TYPE_ACID
//DAMAGE_TYPE_DEMOLITION
//DAMAGE_TYPE_SLOWPOISON
//DAMAGE_TYPE_UNKNOWN
//DAMAGE_TYPE_UNIVERSAL
//Following damage types won't deal damage to spell immune units
//DAMAGE_TYPE_COLD
//DAMAGE_TYPE_DEATH
//DAMAGE_TYPE_DEFENSIVE
//DAMAGE_TYPE_DIVINE
//DAMAGE_TYPE_FIRE
//DAMAGE_TYPE_FORCE
//DAMAGE_TYPE_LIGHTNING
//DAMAGE_TYPE_MAGIC
//DAMAGE_TYPE_MIND
//DAMAGE_TYPE_PLANT
//DAMAGE_TYPE_SHADOWSTRIKE
//DAMAGE_TYPE_SONIC
//DAMAGE_TYPE_SPIRITLINK
//Attack types are:
//ATTACK_TYPE_MELEE
//ATTACK_TYPE_PIERCE
//ATTACK_TYPE_SIEGE
//ATTACK_TYPE_HERO
//ATTACK_TYPE_CHAOS
//ATTACK_TYPE_SPELLS
//Following attack types won't deal damage to spell immune units:
//ATTACK_TYPE_MAGIC
//Lightning graphics are:
//"CLPB": Chain Lightning Primary
//"CLSB": Chain Lightning Secondary
//"DRAB": Drain
//"DRAL": Drain Life
//"DRAM": Drain Mana
//"AFOD": Finger of Death
//"FORK": Forked Lightning
//"HWPB": Healing Wave Primary
//"HWSB": Healing Wave Secondary
//"CHIM": Lightning Attack
//"LEAS": Magic Leash
//"MBUR": Mana Burn
//"MFPB": Mana Flare
//"SPLK": Spirit Link
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function IsUnitCycloned takes unit u returns boolean
return GetUnitAbilityLevel(u,'Bcyc')>0 or GetUnitAbilityLevel(u,'Bcy2')>0//Bcyc -> Cyclone, Bcy2 -> Cyclone
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function IsUnitSleeping takes unit u returns boolean
return (GetUnitAbilityLevel(u,('BUsp'))>0) or (GetUnitAbilityLevel(u,('BUst'))>0)
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function IsUnitInvulnerable takes unit u returns boolean
return GetUnitAbilityLevel(u,'Avul')==1 or IsUnitSleeping(u) or IsUnitCycloned(u)
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function FirstTargetIsValid takes unit target, unit attacker returns boolean
return (IsUnitInvulnerable(target) == false and (IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) == false or canAttackMagicImmune) and IsUnitAliveBJ(target) and IsUnitInRange(target, attacker, GetUnitAcquireRange(attacker)) == true)
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function NTargetIsValid takes integer n, unit attacker returns boolean
local integer handleID = GetHandleId(attacker)
local unit nTarget
local unit previousTarget
local boolean nTargetValid = false
local boolean previousTargetValid = false
if n > 0 then
set nTarget = LoadUnitHandleBJ(n * 3, handleID, udg_hashLasertower)
set previousTarget = LoadUnitHandleBJ((n - 1) * 3, handleID, udg_hashLasertower)
set nTargetValid = IsUnitInvulnerable(nTarget) == false and (IsUnitType(nTarget, UNIT_TYPE_MAGIC_IMMUNE) == false or canAttackMagicImmune) and IsUnitAliveBJ(nTarget) and IsUnitInRange(previousTarget, nTarget, additionalTargetRange) == true and IsPlayerEnemy(GetOwningPlayer(nTarget), GetOwningPlayer(attacker)) == true
set previousTargetValid = NTargetIsValid(n - 1, attacker)
else
set nTarget = LoadUnitHandleBJ(n * 3, handleID, udg_hashLasertower)
set nTargetValid = FirstTargetIsValid(nTarget, attacker)
set previousTargetValid = true
endif
set nTarget = null
set previousTarget = null
return (nTargetValid and previousTargetValid)
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function RemoveAllTargets takes unit attacker returns nothing
local integer handleID = GetHandleId(attacker)
local lightning nLightning
local integer n = 0
loop
set nLightning = LoadLightningHandleBJ((n * 3) + 1, handleID, udg_hashLasertower)
call DestroyLightning(nLightning)
call SaveUnitHandleBJ(null, n * 3, handleID, udg_hashLasertower)
call SaveLightningHandleBJ(null, (n * 3) + 1, handleID, udg_hashLasertower)
call SaveBooleanBJ(false, (n * 3) + 2, handleID, udg_hashLasertower)
set n = n + 1
exitwhen(n == amountTargets)
endloop
set nLightning = null
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function RemoveNTarget takes unit tower, integer n returns nothing
local integer handleID = GetHandleId(tower)
local lightning nLightning = LoadLightningHandleBJ((n * 3) + 1, handleID, udg_hashLasertower)
call DestroyLightning(nLightning)
call SaveUnitHandleBJ(null, n * 3, handleID, udg_hashLasertower)
call SaveLightningHandleBJ(null, (n * 3) + 1, handleID, udg_hashLasertower)
call SaveBooleanBJ(false, (n * 3) + 2, handleID, udg_hashLasertower)
if n == 0 then
call SaveBooleanBJ(false, (amountTargets * 3) + 1, handleID, udg_hashLasertower)
endif
set nLightning = null
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function SetTarget takes unit target, unit attacker returns nothing
local integer handleID = GetHandleId(attacker)
local lightning currentLightning = LoadLightningHandleBJ(1, handleID, udg_hashLasertower)
local location attackerLoc = GetUnitLoc(attacker)
local location targetLoc = GetUnitLoc(target)
local unit currentTarget = LoadUnitHandleBJ(0, handleID, udg_hashLasertower)
local boolean hasFirstTarget = LoadBooleanBJ(2, handleID, udg_hashLasertower)
call SaveBooleanBJ(true, (amountTargets * 3) + 1, handleID, udg_hashLasertower)
if hasFirstTarget == false then
call RemoveAllTargets(attacker)
call DestroyLightning(currentLightning)
call SaveUnitHandleBJ(target, 0, handleID, udg_hashLasertower)
call SaveLightningHandleBJ(AddLightningEx(lightningType, true, GetLocationX(attackerLoc), GetLocationY(attackerLoc), GetLocationZ(attackerLoc) + towerHeight, GetLocationX(targetLoc), GetLocationY(targetLoc), GetLocationZ(targetLoc) + GetUnitFlyHeight(target) + heightOffset), 1, handleID, udg_hashLasertower)
call SaveBooleanBJ(true, 2, handleID, udg_hashLasertower)
endif
set currentLightning = null
set attackerLoc = null
set targetLoc = null
set currentTarget = null
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function CheckForNTarget takes unit previousTarget, unit attackingUnit, integer n returns nothing
local group g = CreateGroup()
local group validTargets = CreateGroup()
local group final = CreateGroup()
local unit currentUnit
local location previousTargetLocation = GetUnitLoc(previousTarget)
local integer handleID = GetHandleId(attackingUnit)
local unit finalUnit
local location finalUnitLoc
local boolean isPreviousTarget = false
local integer currentN
local unit previousUnit
local boolean previousTargetIsActive
if n > 0 then
set previousTargetIsActive = LoadBooleanBJ(((n - 1) * 3) + 2, handleID, udg_hashLasertower)
if previousTargetIsActive == true then
call GroupEnumUnitsInRangeOfLoc(g, previousTargetLocation, additionalTargetRange, null)
loop
set currentUnit = FirstOfGroup(g)
exitwhen currentUnit == null
if IsUnitInvulnerable(currentUnit) == false and IsPlayerEnemy(GetOwningPlayer(currentUnit), GetOwningPlayer(attackingUnit)) == true and (IsUnitType(currentUnit, UNIT_TYPE_MAGIC_IMMUNE) == false or canAttackMagicImmune) and IsUnitAliveBJ(currentUnit) then
call GroupAddUnit(validTargets, currentUnit)
endif
call GroupRemoveUnit(g, currentUnit)
endloop
loop
set currentUnit = FirstOfGroup(validTargets)
exitwhen currentUnit == null
set currentN = n - 1
set isPreviousTarget = false
loop
set previousUnit = LoadUnitHandleBJ(currentN * 3, handleID, udg_hashLasertower)
if currentUnit == previousUnit then
set isPreviousTarget = true
endif
set currentN = currentN - 1
exitwhen currentN < 0
exitwhen isPreviousTarget == true
endloop
if isPreviousTarget == false then
call GroupAddUnit(final, currentUnit)
endif
call GroupRemoveUnit(validTargets, currentUnit)
endloop
set finalUnit = GroupPickRandomUnit(final)
if finalUnit != null then
set finalUnitLoc = GetUnitLoc(finalUnit)
call SaveUnitHandleBJ(finalUnit, n * 3, handleID, udg_hashLasertower)
call SaveLightningHandleBJ(AddLightningEx(lightningType, true, GetLocationX(previousTargetLocation), GetLocationY(previousTargetLocation), GetLocationZ(previousTargetLocation) + GetUnitFlyHeight(previousTarget) + heightOffset, GetLocationX(finalUnitLoc), GetLocationY(finalUnitLoc), GetLocationZ(finalUnitLoc) + GetUnitFlyHeight(finalUnit) + heightOffset), (n * 3) + 1, handleID, udg_hashLasertower)
call SaveBooleanBJ(true, (n * 3) + 2, handleID, udg_hashLasertower)
endif
endif
endif
call DestroyGroup(g)
call DestroyGroup(validTargets)
call DestroyGroup(final)
set g = null
set validTargets = null
set final = null
set finalUnit = null
set finalUnitLoc = null
set currentUnit = null
set previousTargetLocation = null
set previousUnit = null
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function TowersLoopFunction takes nothing returns nothing
local unit attacker = GetEnumUnit()
local location attackerLoc = GetUnitLoc(attacker)
local integer handleID = GetHandleId(attacker)
local unit firstTarget = LoadUnitHandleBJ(0, handleID, udg_hashLasertower)
local location firstTargetLoc = GetUnitLoc(firstTarget)
local lightning firstLightning = LoadLightningHandleBJ(1, handleID, udg_hashLasertower)
local boolean hasFirstTarget = LoadBooleanBJ(2, handleID, udg_hashLasertower)
local unit previousTarget
local location previousTargetLoc
local boolean hasPreviousTarget
local unit nTarget
local location nTargetLoc
local lightning nLightning
local boolean hasNTarget
local integer n
local boolean isAttacking
local integer currentTimeTick
local boolean changeColour = false
local real currentDamage = LoadRealBJ((amountTargets * 3) + 3, handleID, udg_hashLasertower)
local real currentRed = LoadRealBJ((amountTargets * 3) + 4, handleID, udg_hashLasertower)
local real currentGreen = LoadRealBJ((amountTargets * 3) + 5, handleID, udg_hashLasertower)
local real currentBlue = LoadRealBJ((amountTargets * 3) + 6, handleID, udg_hashLasertower)
local real currentAlpha = LoadRealBJ((amountTargets * 3) + 7, handleID, udg_hashLasertower)
if IsUnitAliveBJ(attacker) == true then
set isAttacking = LoadBooleanBJ((amountTargets * 3) + 1, handleID, udg_hashLasertower)
if isAttacking == false then
set currentTimeTick = LoadIntegerBJ((amountTargets * 3) + 2, handleID, udg_hashLasertower)
if currentTimeTick >= ticksTillDamageReset then
call SaveRealBJ(startDamage, (amountTargets * 3) + 3, handleID, udg_hashLasertower)
call SaveRealBJ(startRed, (amountTargets * 3) + 4, handleID, udg_hashLasertower)
call SaveRealBJ(startGreen, (amountTargets * 3) + 5, handleID, udg_hashLasertower)
call SaveRealBJ(startBlue, (amountTargets * 3) + 6, handleID, udg_hashLasertower)
call SaveRealBJ(startAlpha, (amountTargets * 3) + 7, handleID, udg_hashLasertower)
call SaveBooleanBJ(false, (amountTargets * 3) + 1, handleID, udg_hashLasertower)
call SaveIntegerBJ(0, (amountTargets * 3) + 2, handleID, udg_hashLasertower)
else
call SaveIntegerBJ(currentTimeTick + 1, (amountTargets * 3) + 2, handleID, udg_hashLasertower)
endif
else
call SaveIntegerBJ(0, (amountTargets * 3) + 2, handleID, udg_hashLasertower)
if maxDamage > startDamage then
if currentDamage < maxDamage then
set currentDamage = currentDamage + ((maxDamage - startDamage) / I2R(ticksTillMaxDamage))
set currentRed = currentRed + ((maxDamageRed - startRed) / I2R(ticksTillMaxDamage))
set currentGreen = currentGreen + ((maxDamageGreen - startGreen) / I2R(ticksTillMaxDamage))
set currentBlue = currentBlue + ((maxDamageBlue - startBlue) / I2R(ticksTillMaxDamage))
set currentAlpha = currentAlpha + ((maxDamageAlpha - startBlue) / I2R(ticksTillMaxDamage))
if currentRed < 0 then
set currentRed = 0
endif
if currentGreen < 0 then
set currentGreen = 0
endif
if currentBlue < 0 then
set currentBlue = 0
endif
if currentAlpha < 0 then
set currentAlpha = 0
endif
if currentRed > 1 then
set currentRed = 1
endif
if currentGreen > 1 then
set currentGreen = 1
endif
if currentBlue > 1 then
set currentBlue = 1
endif
if currentAlpha > 1 then
set currentAlpha = 1
endif
call SaveRealBJ(currentDamage, (amountTargets * 3) + 3, handleID, udg_hashLasertower)
call SaveRealBJ(currentRed, (amountTargets * 3) + 4, handleID, udg_hashLasertower)
call SaveRealBJ(currentGreen, (amountTargets * 3) + 5, handleID, udg_hashLasertower)
call SaveRealBJ(currentBlue, (amountTargets * 3) + 6, handleID, udg_hashLasertower)
call SaveRealBJ(currentAlpha, (amountTargets * 3) + 7, handleID, udg_hashLasertower)
endif
elseif maxDamage < startDamage then
if currentDamage > maxDamage then
set currentDamage = currentDamage + ((maxDamage - startDamage) / I2R(ticksTillMaxDamage))
set currentRed = currentRed + ((maxDamageRed - startRed) / I2R(ticksTillMaxDamage))
set currentGreen = currentGreen + ((maxDamageGreen - startGreen) / I2R(ticksTillMaxDamage))
set currentBlue = currentBlue + ((maxDamageBlue - startBlue) / I2R(ticksTillMaxDamage))
set currentAlpha = currentAlpha + ((maxDamageAlpha - startBlue) / I2R(ticksTillMaxDamage))
if currentRed < 0 then
set currentRed = 0
endif
if currentGreen < 0 then
set currentGreen = 0
endif
if currentBlue < 0 then
set currentBlue = 0
endif
if currentAlpha < 0 then
set currentAlpha = 0
endif
if currentRed > 1 then
set currentRed = 1
endif
if currentGreen > 1 then
set currentGreen = 1
endif
if currentBlue > 1 then
set currentBlue = 1
endif
if currentAlpha > 1 then
set currentAlpha = 1
endif
call SaveRealBJ(currentDamage, (amountTargets * 3) + 3, handleID, udg_hashLasertower)
call SaveRealBJ(currentRed, (amountTargets * 3) + 4, handleID, udg_hashLasertower)
call SaveRealBJ(currentGreen, (amountTargets * 3) + 5, handleID, udg_hashLasertower)
call SaveRealBJ(currentBlue, (amountTargets * 3) + 6, handleID, udg_hashLasertower)
call SaveRealBJ(currentAlpha, (amountTargets * 3) + 7, handleID, udg_hashLasertower)
endif
endif
endif
if hasFirstTarget == true then
if FirstTargetIsValid(firstTarget, attacker) == true then
call UnitDamageTargetBJ(attacker, firstTarget, currentDamage, attackType, damageType)
call SetLightningColor(firstLightning, currentRed, currentGreen, currentBlue, currentAlpha)
call MoveLightningEx(firstLightning, true, GetLocationX(attackerLoc), GetLocationY(attackerLoc), GetLocationZ(attackerLoc) + towerHeight, GetLocationX(firstTargetLoc), GetLocationY(firstTargetLoc), GetLocationZ(firstTargetLoc) + GetUnitFlyHeight(firstTarget) + heightOffset)
if amountTargets > 1 then
set n = 1
loop
set nTarget = LoadUnitHandleBJ(n * 3, handleID, udg_hashLasertower)
set nTargetLoc = GetUnitLoc(nTarget)
set nLightning = LoadLightningHandleBJ((n * 3) + 1, handleID, udg_hashLasertower)
set hasNTarget = LoadBooleanBJ((n * 3) + 2, handleID, udg_hashLasertower)
set previousTarget = LoadUnitHandleBJ((n - 1) * 3, handleID, udg_hashLasertower)
set previousTargetLoc = GetUnitLoc(previousTarget)
set hasPreviousTarget = LoadBooleanBJ(((n - 1) * 3) + 2, handleID, udg_hashLasertower)
if hasNTarget == true then
if NTargetIsValid(n, attacker) and hasPreviousTarget == true then
call UnitDamageTargetBJ(attacker, nTarget, currentDamage * Pow(damageReductionFactor, n), attackType, damageType)
call SetLightningColor(nLightning, currentRed, currentGreen, currentBlue, currentAlpha)
call MoveLightningEx(nLightning, true, GetLocationX(previousTargetLoc), GetLocationY(previousTargetLoc), GetLocationZ(previousTargetLoc) + GetUnitFlyHeight(previousTarget) + heightOffset, GetLocationX(nTargetLoc), GetLocationY(nTargetLoc), GetLocationZ(nTargetLoc) + GetUnitFlyHeight(nTarget) + heightOffset)
else
call RemoveNTarget(attacker, n)
endif
else
call CheckForNTarget(previousTarget, attacker, n)
endif
set n = n + 1
exitwhen(n == amountTargets)
endloop
endif
else
call RemoveNTarget(attacker, 0)
endif
else
call RemoveAllTargets(attacker)
endif
else
call RemoveAllTargets(attacker)
call GroupRemoveUnit(udg_towers, attacker)
endif
set attacker = null
set attackerLoc = null
set firstTarget = null
set firstTargetLoc = null
set firstLightning = null
set nTarget = null
set nTargetLoc = null
set nLightning = null
set previousTarget = null
set previousTargetLoc = null
endfunction
function DoTowerActions takes nothing returns nothing
call ForGroupBJ(udg_towers, function TowersLoopFunction)
endfunction
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Name | Type | is_array | initial_value |
hashLasertower | hashtable | No | |
towers | group | No |