Name | Type | is_array | initial_value |
AddOrRemoveGroup | integer | No | |
Attachments | effect | Yes | |
Counter | integer | Yes | |
DDeflected | boolean | Yes | |
EAngle | real | Yes | |
ECount | integer | Yes | |
EFinish | location | Yes | |
EKey | integer | No | -1 |
EStart | location | Yes | |
EUnit | unit | Yes | |
FAngle | real | No | |
Missile | integer | No | |
Missile_onBoundaries | trigger | No | |
Missile_onCliff | trigger | No | |
Missile_onDestructable | trigger | No | |
Missile_onFinish | trigger | No | |
Missile_onHit | trigger | No | |
Missile_onItem | trigger | No | |
Missile_onMissile | trigger | No | |
Missile_onPause | trigger | No | |
Missile_onPeriod | trigger | No | |
Missile_onRemove | trigger | No | |
Missile_onResume | trigger | No | |
Missile_onTerrain | trigger | No | |
Missile_onTileset | trigger | No | |
MissileAcceleration | real | No | |
MissileAlpha | integer | No | |
MissileAnimation | integer | No | |
MissileArc | real | No | |
MissileAt | integer | No | |
MissileAttachModel | string | No | |
MissileAttachScale | real | No | |
MissileAttachX | real | No | |
MissileAttachY | real | No | |
MissileAttachZ | real | No | |
MissileBlue | integer | No | |
MissileCollideZ | boolean | No | |
MissileCollision | real | No | |
MissileCurve | real | No | |
MissileDamage | real | No | |
MissileData | integer | No | |
MissileDeflectPosition | location | No | |
MissileDeflectTarget | unit | No | |
MissileDeflectZ | real | No | |
MissileDestroy | boolean | No | |
MissileDetachEffect | effect | No | |
MissileDuration | real | No | |
MissileEvent | integer | No | |
MissileFinish | location | No | |
MissileFinishZ | real | No | |
MissileFlushUnit | unit | No | |
MissileGreen | integer | No | |
MissileGroup | integer | No | |
MissileGroupAddGroup | integer | No | |
MissileGroupCounted | integer | No | |
MissileGroupLocation | location | No | |
MissileGroupPlayer | player | No | |
MissileGroupRange | real | No | |
MissileGroupRect | rect | No | |
MissileGroupRemoveGroup | integer | No | |
MissileGroupSize | integer | No | |
MissileGroupType | integer | No | |
MissileHitDestructable | destructable | No | |
MissileHitItem | item | No | |
MissileHitMissile | integer | No | |
MissileHitted | boolean | No | |
MissileHittedUnit | unit | No | |
MissileHitUnit | unit | No | |
MissileLastPosition | location | No | |
MissileModel | string | No | |
MissileNextPosition | location | No | |
MissileNextZ | real | No | |
MissileOnBoundaries | integer | No | 8 |
MissileOnCliff | integer | No | 12 |
MissileOnDestructable | integer | No | 4 |
MissileOnFinish | integer | No | 7 |
MissileOnHit | integer | No | 2 |
MissileOnItem | integer | No | 5 |
MissileOnMissile | integer | No | 3 |
MissileOnPause | integer | No | 9 |
MissileOnPeriod | integer | No | 1 |
MissileOnRemove | integer | No | 11 |
MissileOnResume | integer | No | 10 |
MissileOnTerrain | integer | No | 6 |
MissileOnTileset | integer | No | 13 |
MissileOwner | player | No | |
MissilePaused | boolean | No | |
MissilePlayerColor | integer | No | |
MissilePosition | location | No | |
MissilePrevZ | real | No | |
MissileRed | integer | No | |
MissileRemoveLocations | boolean | No | true |
MissileRoll | boolean | No | |
MissileScale | real | No | |
MissileSource | unit | No | |
MissileSpeed | real | No | |
MissileStart | location | No | |
MissileStartZ | real | No | |
MissileTarget | unit | No | |
MissileTileset | integer | No | |
MissileTimeScale | real | No | |
MissileTravelled | real | No | |
MissileType | integer | No | |
MissileVelocity | real | No | |
MissileVision | real | No | |
MissileZ | real | No | |
Paused | boolean | Yes | |
r | real | No | |
Range | real | No | |
RLocation | location | No | |
SavedMissile | integer | No | |
SavedMissileGroup | integer | No | |
Theta | real | No | |
Tile | string | No | |
WAngle | real | Yes | |
WCount | integer | Yes | |
WFinish | location | Yes | |
WKey | integer | No | -1 |
WStart | location | Yes | |
WUnit | unit | Yes |
do
local funcs = {}
function onInit(code)
if type(code) == "function" then
table.insert(funcs, code)
end
end
local old = InitBlizzard
function InitBlizzard()
old()
for i = 1, #funcs do
funcs[i]()
end
funcs = nil
end
end
--[[ requires Indexer, TimedHandles, RegisterPlayerUnitEvent
-- --------------------------------------- Utilities v1.9 --------------------------------------- --
-- How to Import:
-- 1 - Copy this library into your map
-- 2 - Copy the dummy unit in object editor and match its raw code below
-- 3 - Copy the Indexer library over to your map and follow its install instructions
-- 4 - Copy the TimedHandles library over to your map and follow its install instructions
-- 5 - Copy the RegisterPlayerUnitEvent library over to your map and follow its install instructions
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- Configuration --
-- ---------------------------------------------------------------------------------------------- --
-- The dummy caster unit id
local DUMMY = FourCC('dumi')
-- Update period
local PERIOD = 0.031250000
-- location z
local location = Location(0,0)
-- Closest Unit
local bj_closestUnitGroup
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
-- Returns the terrain Z value
function GetLocZ(x, y)
MoveLocation(location, x, y)
return GetLocationZ(location)
end
-- Similar to GetUnitX and GetUnitY but for Z axis
function GetUnitZ(unit)
return GetLocZ(GetUnitX(unit), GetUnitY(unit)) + GetUnitFlyHeight(unit)
end
-- Similar to SetUnitX and SetUnitY but for Z axis
function SetUnitZ(unit, z)
SetUnitFlyHeight(unit, z - GetLocZ(GetUnitX(unit), GetUnitY(unit)), 0)
end
-- Anlge between 2D points
function AngleBetweenCoordinates(x, y, x2, y2)
return Atan2(y2 - y, x2 - x)
end
-- Similar to AddSpecialEffect but scales the effect and considers z and return it
function AddSpecialEffectEx(effect, x, y, z, scale)
bj_lastCreatedEffect = AddSpecialEffect(effect, x, y)
if z ~= 0 then
BlzSetSpecialEffectZ(bj_lastCreatedEffect, z + GetLocZ(x, y))
end
BlzSetSpecialEffectScale(bj_lastCreatedEffect, scale)
return bj_lastCreatedEffect
end
-- Returns a group of enemy units of the specified player within the specified AOE of x and y
function GetEnemyUnitsInRange(player, x, y, aoe, structures, magicImmune)
local group = CreateGroup()
local g = CreateGroup()
local unit
GroupEnumUnitsInRange(g, x, y, aoe, nil)
for i = 0, BlzGroupGetSize(g) - 1 do
unit = BlzGroupUnitAt(g, i)
if IsUnitEnemy(unit, player) and UnitAlive(unit) and (structures or (not IsUnitType(unit, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(unit, UNIT_TYPE_MAGIC_IMMUNE))) then
GroupAddUnit(group, unit)
end
end
DestroyGroup(g)
return group
end
-- Returns the closest unit in a unit group with center at x and y
function GetClosestUnitGroup(x, y, group)
local md = 100000
local unit
local dx
local dy
bj_closestUnitGroup = nil
for i = 0, BlzGroupGetSize(group) - 1 do
unit = BlzGroupUnitAt(group, i)
if UnitAlive(unit) then
dx = GetUnitX(unit) - x
dy = GetUnitY(unit) - y
if (dx*dx + dy*dy)/100000 < md then
bj_closestUnitGroup = unit
md = (dx*dx + dy*dy)/100000
end
end
end
return bj_closestUnitGroup
end
-- Link an effect to a unit buff or ability
function LinkEffectToBuff(unit, buffId, model, attach)
EffectLink:buff(unit, buffId, model, attach)
end
-- Link an effect to an unit item.
function LinkEffectToItem(unit, i, model, attach)
EffectLink:item(unit, i, model, attach)
end
-- Spams the specified effect model at a location with the given interval for the number of times count
function SpamEffect(model, x, y, z, scale, interval, count)
local timer = CreateTimer()
TimerStart(timer, interval, true, function()
if count > 0 then
DestroyEffect(AddSpecialEffectEx(model, x, y, z, scale))
else
PauseTimer(timer)
DestroyTimer(timer)
end
count = count - 1
end)
end
-- Spams the specified effect model attached to a unit for the given interval for the number of times count
function SpamEffectUnit(unit, model, attach, interval, count)
local timer = CreateTimer()
TimerStart(timer, interval, true, function()
if count > 0 then
DestroyEffect(AddSpecialEffectTarget(model, unit, attach))
else
PauseTimer(timer)
DestroyTimer(timer)
end
count = count - 1
end)
end
-- Add the specified ability to the specified unit for the given duration. Use hide to show or not the ability button.
function UnitAddAbilityTimed(unit, ability, duration, level, hide)
TimedAbility:add(unit, ability, duration, level, hide)
end
-- Resets the specified unit ability cooldown
function ResetUnitAbilityCooldown(unit, ability)
local timer = CreateTimer()
TimerStart(timer, 0.01, false, function()
BlzEndUnitAbilityCooldown(unit, ability)
PauseTimer(timer)
DestroyTimer(timer)
end)
end
-- Returns the distance between 2 coordinates in Warcraft III units
function DistanceBetweenCoordinates(x1, y1, x2, y2)
local dx = (x2 - x1)
local dy = (y2 - y1)
return SquareRoot(dx*dx + dy*dy)
end
-- Makes the specified source damage an area respecting some basic unit filters
function UnitDamageArea(unit, x, y, aoe, damage, attacktype, damagetype, structures, magicImmune, allies)
local group = CreateGroup()
local player = GetOwningPlayer(unit)
GroupEnumUnitsInRange(group, x, y, aoe, nil)
GroupRemoveUnit(group, unit)
for i = 0, BlzGroupGetSize(group) - 1 do
local u = BlzGroupUnitAt(group, i)
if UnitAlive(u) and (allies or IsUnitEnemy(u, player)) and (structures or (not IsUnitType(u, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))) then
UnitDamageTarget(unit, u, damage, true, false, attacktype, damagetype, nil)
end
end
DestroyGroup(group)
end
-- Makes the specified source damage a group. Creates a special effect if specified
function UnitDamageGroup(unit, group, damage, attacktype, damagetype, effect, attach, destroy)
for i = 0, BlzGroupGetSize(group) - 1 do
local u = BlzGroupUnitAt(group, i)
UnitDamageTarget(unit, u, damage, true, false, attacktype, damagetype, nil)
if effect and attach then
DestroyEffect(AddSpecialEffectTarget(effect, u, attach))
end
end
if destroy then
DestroyGroup(group)
end
return group
end
-- Returns a random range given a max value
function GetRandomRange(radius)
local r = GetRandomReal(0, 1) + GetRandomReal(0, 1)
if r > 1 then
return (2 - r)*radius
end
return r*radius
end
-- Returns a random value in the x/y coordinates depending on the value of boolean x
function GetRandomCoordInRange(center, radius, x)
local theta = 2*bj_PI*GetRandomReal(0, 1)
local r
if x then
r = center + radius*Cos(theta)
else
r = center + radius*Sin(theta)
end
return r
end
-- Clones the items in the source unit inventory to the target unit
function CloneItems(source, target, isIllusion)
for i = 0, bj_MAX_INVENTORY do
local item = UnitItemInSlot(source, i)
local j = GetItemCharges(item)
item = CreateItem(GetItemTypeId(item), GetUnitX(target), GetUnitY(target))
SetItemCharges(item, j)
UnitAddItem(target, item)
if isIllusion then
if GetItemTypeId(item) == FourCC('ankh') then
BlzItemRemoveAbility(item, FourCC('AIrc'))
end
BlzSetItemBooleanField(item, ITEM_BF_ACTIVELY_USED, false)
end
end
end
-- Add the mount for he unit mana pool
function AddUnitMana(unit, real)
SetUnitState(unit, UNIT_STATE_MANA, (GetUnitState(unit, UNIT_STATE_MANA) + real))
end
-- Add the specified amounts to a hero str/agi/int base amount
function UnitAddStat(unit, strength, agility, intelligence)
if strength ~= 0 then
SetHeroStr(unit, GetHeroStr(unit, false) + strength, true)
end
if agility ~= 0 then
SetHeroAgi(unit, GetHeroAgi(unit, false) + agility, true)
end
if intelligence ~= 0 then
SetHeroInt(unit, GetHeroInt(unit, false) + intelligence, true)
end
end
-- Returns the closest unit from the x and y coordinates in the map
function GetClosestUnit(x, y, boolexpr)
local group = CreateGroup()
local md = 100000
bj_closestUnitGroup = nil
GroupEnumUnitsInRect(group, bj_mapInitialPlayableArea, boolexpr)
for i = 0, BlzGroupGetSize(group) - 1 do
local unit = BlzGroupUnitAt(group, i)
if UnitAlive(unit) then
local dx = GetUnitX(unit) - x
local dy = GetUnitY(unit) - y
if (dx*dx + dy*dy)/100000 < md then
bj_closestUnitGroup = unit
md = (dx*dx + dy*dy)/100000
end
end
end
DestroyGroup(group)
DestroyBoolExpr(boolexpr)
return bj_closestUnitGroup
end
-- Creates a chain lightning with the specified ligihtning effect with the amount of bounces
function CreateChainLightning(source, target, damage, aoe, duration, interval, bounces, attacktype, damagetype, lightning, effect, attach, rebounce)
local player = GetOwningPlayer(source)
local group = GetEnemyUnitsInRange(player, GetUnitX(target), GetUnitY(target), aoe, false, false)
if BlzGroupGetSize(group) == 1 then
DestroyLightningTimed(AddLightningEx(lightning, true, GetUnitX(source), GetUnitY(source), GetUnitZ(source) + 60.0, GetUnitX(target), GetUnitY(target), GetUnitZ(target) + 60.0), duration)
DestroyEffect(AddSpecialEffectTarget(effect, target, attach))
UnitDamageTarget(source, target, damage, false, false, attacktype, damagetype, nil)
DestroyGroup(group)
else
local timer = CreateTimer()
local damaged = CreateGroup()
local prev = nil
local this = target
local next = nil
GroupRemoveUnit(group, this)
GroupAddUnit(damaged, this)
UnitDamageTarget(source, this, damage, false, false, attacktype, damagetype, nil)
DestroyEffect(AddSpecialEffectTarget(effect, this, attach))
TimerStart(timer, interval, true, function()
DestroyGroup(group)
if bounces > 0 then
group = GetEnemyUnitsInRange(player, GetUnitX(this), GetUnitY(this), aoe, false, false)
GroupRemoveUnit(group, this)
if not rebounce then
BlzGroupRemoveGroupFast(damaged, group)
end
if BlzGroupGetSize(group) == 0 then
PauseTimer(timer)
DestroyTimer(timer)
DestroyGroup(group)
DestroyGroup(damaged)
else
next = GetClosestUnitGroup(GetUnitX(this), GetUnitY(this), group)
if next == prev and BlzGroupGetSize(group) > 1 then
GroupRemoveUnit(group, prev)
next = GetClosestUnitGroup(GetUnitX(this), GetUnitY(this), group)
end
if next then
DestroyLightningTimed(AddLightningEx(lightning, true, GetUnitX(this), GetUnitY(this), GetUnitZ(this) + 60.0, GetUnitX(next), GetUnitY(next), GetUnitZ(next) + 60.0), duration)
DestroyEffect(AddSpecialEffectTarget(effect, next, attach))
GroupAddUnit(damaged, next)
UnitDamageTarget(source, next, damage, false, false, attacktype, damagetype, nil)
DestroyGroup(group)
prev = this
this = next
next = nil
else
PauseTimer(timer)
DestroyTimer(timer)
DestroyGroup(group)
DestroyGroup(damaged)
end
end
else
PauseTimer(timer)
DestroyTimer(timer)
DestroyGroup(group)
DestroyGroup(damaged)
end
bounces = bounces - 1
end)
end
DestroyGroup(group)
end
-- Add the specified amount to the specified player gold amount
function AddPlayerGold(player, amount)
SetPlayerState(player, PLAYER_STATE_RESOURCE_GOLD, GetPlayerState(player, PLAYER_STATE_RESOURCE_GOLD) + amount)
end
-- Creates a text tag in an unit position for a duration
function CreateTextOnUnit(unit, string, duration, red, green, blue, alpha)
local texttag = CreateTextTag()
SetTextTagText(texttag, string, 0.015)
SetTextTagPosUnit(texttag, unit, 0)
SetTextTagColor(texttag, red, green, blue, alpha)
SetTextTagLifespan(texttag, duration)
SetTextTagVelocity(texttag, 0.0, 0.0355)
SetTextTagPermanent(texttag, false)
end
-- Add health regeneration to the unit base value
function UnitAddHealthRegen(unit, regen)
BlzSetUnitRealField(unit, UNIT_RF_HIT_POINTS_REGENERATION_RATE, BlzGetUnitRealField(unit, UNIT_RF_HIT_POINTS_REGENERATION_RATE) + regen)
end
-- Retrieves a dummy from the pool. Facing angle in radians
function DummyRetrieve(player, x, y, z, face)
return DummyPool:retrieve(player, x, y, z, face)
end
-- Recycles a dummy unit type, putting it back into the pool.
function DummyRecycle(unit)
DummyPool:recycle(unit)
end
-- Recycles a dummy with a delay.
function DummyRecycleTimed(unit, delay)
DummyPool:timed(unit, delay)
end
-- Casts an ability in the target unit. Must have no casting time
function CastAbilityTarget(unit, ability, order, level)
local dummy = DummyRetrieve(GetOwningPlayer(unit), 0, 0, 0, 0)
UnitAddAbility(dummy, ability)
SetUnitAbilityLevel(dummy, ability, level)
IssueTargetOrder(dummy, order, unit)
UnitRemoveAbility(dummy, ability)
DummyRecycle(dummy)
end
-- Returns a random unit within a group
function GroupPickRandomUnitEx(group)
if BlzGroupGetSize(group) > 0 then
return BlzGroupUnitAt(group, GetRandomInt(0, BlzGroupGetSize(group) - 1))
else
return nil
end
end
-- Returns true if a unit is within a cone given a facing and fov angle in degrees (Less precise)
function IsUnitInConeEx(unit, x, y, face, fov)
return Acos(Cos((Atan2(GetUnitY(unit) - y, GetUnitX(unit) - x)) - face*bj_DEGTORAD)) < fov*bj_DEGTORAD/2
end
-- Returns true if a unit is within a cone given a facing, fov angle and a range in degrees (takes collision into consideration). Credits to AGD.
function IsUnitInCone(unit, x, y, range, face, fov)
if IsUnitInRangeXY(unit, x, y, range) then
x = GetUnitX(unit) - x
y = GetUnitY(unit) - y
range = x*x + y*y
if range > 0 then
face = face*bj_DEGTORAD - Atan2(y, x)
fov = fov*bj_DEGTORAD/2 + Asin(BlzGetUnitCollisionSize(unit)/SquareRoot(range))
return RAbsBJ(face) <= fov or RAbsBJ(face - 2.00*bj_PI) <= fov
end
return true
end
return false
end
-- Makes the source unit damage enemy unit in a cone given a direction, foy and range
function UnitDamageCone(unit, x, y, face, fov, aoe, damage, attacktype, damagetype, structures, magicImmune, allies)
local group = CreateGroup()
local player = GetOwningPlayer(unit)
GroupEnumUnitsInRange(group, x, y, aoe, nil)
GroupRemoveUnit(group, unit)
for i = 0, BlzGroupGetSize(group) - 1 do
local u = BlzGroupUnitAt(group, i)
if UnitAlive(u) and IsUnitInCone(u, x, y, aoe, face, fov) then
if (allies or IsUnitEnemy(u, player)) and (structures or (not IsUnitType(u, UNIT_TYPE_STRUCTURE))) and (magicImmune or (not IsUnitType(u, UNIT_TYPE_MAGIC_IMMUNE))) then
UnitDamageTarget(unit, u, damage, true, false, attacktype, damagetype, nil)
end
end
end
DestroyGroup(group)
end
-- Heals all allied units of specified player in an area
function HealArea(player, x, y, aoe, amount, effect, attach)
local group = CreateGroup()
local unit
GroupEnumUnitsInRange(group, x, y, aoe, nil)
for i = 0, BlzGroupGetSize(group) - 1 do
unit = BlzGroupUnitAt(group, i)
if IsUnitAlly(unit, player) and UnitAlive(unit) and not IsUnitType(unit, UNIT_TYPE_STRUCTURE) then
SetWidgetLife(unit, GetWidgetLife(unit) + amount)
if effect ~= "" then
if attach ~= "" then
DestroyEffect(AddSpecialEffectTarget(effect, unit, attach))
else
DestroyEffect(AddSpecialEffect(effect, GetUnitX(unit), GetUnitY(unit)))
end
end
end
end
DestroyGroup(group)
end
-- Pretty obvious.
function R2I2S(real)
return I2S(R2I(real))
end
-- Returns an ability real level field as a string. Usefull for toolltip manipulation.
function AbilityRealField(unit, ability, field, level, multiplier, integer)
if integer then
return R2I2S(BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, level)*multiplier)
else
return R2SW(BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, level)*multiplier, 1, 1)
end
end
-- Fix for camera pan desync. credits do Daffa
function SmartCameraPanBJModified(player, loc, duration)
local x = GetLocationX(loc)
local y = GetLocationY(loc)
local dx = x - GetCameraTargetPositionX()
local dy = y - GetCameraTargetPositionY()
local dist = SquareRoot(dx*dx + dy*dy)
if GetLocalPlayer() == player then
if dist >= bj_SMARTPAN_TRESHOLD_SNAP then
PanCameraToTimed(x, y, duration)
elseif dist >= bj_SMARTPAN_TRESHOLD_PAN then
PanCameraToTimed(x, y, duration)
else
-- User is close, dont move camera
end
end
end
-- Fix for camera pan desync. credits do Daffa
function SmartCameraPanBJModifiedXY(player, x, y, duration)
local dx = x - GetCameraTargetPositionX()
local dy = y - GetCameraTargetPositionY()
local dist = SquareRoot(dx*dx + dy*dy)
if GetLocalPlayer() == player then
if dist >= bj_SMARTPAN_TRESHOLD_SNAP then
PanCameraToTimed(x, y, duration)
elseif dist >= bj_SMARTPAN_TRESHOLD_PAN then
PanCameraToTimed(x, y, duration)
else
-- User is close, dont move camera
end
end
end
-- Start the cooldown for the source unit unit the new value
function StartUnitAbilityCooldown(unit, ability, cooldown)
local timer = CreateTimer()
TimerStart(timer, 0.01, false, function()
BlzStartUnitAbilityCooldown(unit, ability, cooldown)
PauseTimer(timer)
DestroyTimer(timer)
end)
end
-- ---------------------------------------------------------------------------------------------- --
-- Systems --
-- ---------------------------------------------------------------------------------------------- --
-- ----------------------------------------- Dummy Pool ----------------------------------------- --
do
local group = CreateGroup()
local player = Player(PLAYER_NEUTRAL_PASSIVE)
DummyPool = setmetatable({}, {})
local mt = getmetatable(DummyPool)
mt.__index = mt
function mt:recycle(unit)
if GetUnitTypeId(unit) ~= DUMMY then
print("[DummyPool] Error: Trying to recycle a non dummy unit")
else
GroupAddUnit(group, unit)
SetUnitX(unit, WorldBounds.maxX)
SetUnitY(unit, WorldBounds.maxY)
SetUnitOwner(unit, player, false)
ShowUnit(unit, false)
BlzPauseUnitEx(unit, true)
end
end
function mt:retrieve(owner, x, y, z, face)
if BlzGroupGetSize(group) > 0 then
bj_lastCreatedUnit = FirstOfGroup(group)
BlzPauseUnitEx(bj_lastCreatedUnit, false)
ShowUnit(bj_lastCreatedUnit, true)
GroupRemoveUnit(group, bj_lastCreatedUnit)
SetUnitX(bj_lastCreatedUnit, x)
SetUnitY(bj_lastCreatedUnit, y)
SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
BlzSetUnitFacingEx(bj_lastCreatedUnit, face*bj_RADTODEG)
SetUnitOwner(bj_lastCreatedUnit, owner, false)
else
bj_lastCreatedUnit = CreateUnit(owner, DUMMY, x, y, face*bj_RADTODEG)
SetUnitFlyHeight(bj_lastCreatedUnit, z, 0)
end
return bj_lastCreatedUnit
end
function mt:timed(unit, delay)
local timer = CreateTimer()
if GetUnitTypeId(unit) ~= DUMMY then
print("[DummyPool] Error: Trying to recycle a non dummy unit")
else
TimerStart(timer, delay, false, function()
GroupAddUnit(group, unit)
SetUnitX(unit, WorldBounds.maxX)
SetUnitY(unit, WorldBounds.maxY)
SetUnitOwner(unit, player, false)
ShowUnit(unit, false)
BlzPauseUnitEx(unit, true)
PauseTimer(timer)
DestroyTimer(timer)
end)
end
end
onInit(function()
local timer = CreateTimer()
local unit
TimerStart(timer, 0, false, function()
for i = 0, 20 do
unit = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
BlzPauseUnitEx(unit, false)
GroupAddUnit(group, unit)
end
PauseTimer(timer)
DestroyTimer(timer)
end)
end)
end
-- ---------------------------------------- Timed Ability --------------------------------------- --
do
TimedAbility = setmetatable({}, {})
local mt = getmetatable(TimedAbility)
mt.__index = mt
local timer = CreateTimer()
local ability = {}
local array = {}
local key = 0
function mt:destroy(i)
UnitRemoveAbility(self.unit, self.id)
array[i] = array[key]
key = key - 1
ability[self.unit][self.id] = nil
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:add(unit, id, duration, level, hide)
if not ability[unit] then ability[unit] = {} end
local this = ability[unit][id]
if not this then
this = {}
setmetatable(this, mt)
this.unit = unit
this.id = id
key = key + 1
array[key] = this
ability[unit][id] = this
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.duration <= 0 then
i = this:destroy(i)
end
this.duration = this.duration - PERIOD
i = i + 1
end
end)
end
end
if GetUnitAbilityLevel(unit, id) ~= level then
UnitAddAbility(unit, id)
SetUnitAbilityLevel(unit, id, level)
UnitMakeAbilityPermanent(unit, true, id)
BlzUnitHideAbility(unit, id, hide)
end
this.duration = duration
end
end
-- ----------------------------------------- Effect Link ---------------------------------------- --
do
EffectLink = setmetatable({}, {})
local mt = getmetatable(EffectLink)
mt.__index = mt
local timer = CreateTimer()
local array = {}
local items = {}
local key = 0
local k = 0
function mt:destroy(i, item)
DestroyEffect(self.effect)
if item then
items[i] = items[k]
k = k - 1
else
array[i] = array[key]
key = key - 1
if key == 0 then
PauseTimer(timer)
end
end
self = nil
return i - 1
end
function mt:buff(unit, buffId, model, attach)
local this = {}
setmetatable(this, mt)
key = key + 1
array[key] = this
this.unit = unit
this.buff = buffId
this.effect = AddSpecialEffectTarget(model, unit, attach)
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if GetUnitAbilityLevel(this.unit, this.buff) == 0 then
i = this:destroy(i, false)
end
i = i + 1
end
end)
end
end
function mt:item(unit, i, model, attach)
local this = {}
setmetatable(this, mt)
k = k + 1
items[k] = this
this.item = i
this.effect = AddSpecialEffectTarget(model, unit, attach)
end
onInit(function()
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function()
local item = GetManipulatedItem()
local i = 1
local this
while i <= k do
this = items[i]
if this.item == item then
i = this:destroy(i, true)
end
i = i + 1
end
end)
end)
end
end
--[[
-- ------------------------ Indexer v1.1 by Chopinski ----------------------- --
Simple Unit Indexer for LUA.
Simply copya nd paste to import
]]--
do
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
local ability = FourCC('Adef')
local onIndex = {}
local onDeindex = {}
local id = 0
local source
local SetUnitId = SetUnitUserData
function SetUnitUserData(unit, id) end
local function IndexUnit(unit)
if GetUnitUserData(unit) == 0 then
id = id + 1
source = unit
if GetUnitAbilityLevel(unit, ability) == 0 then
UnitAddAbility(unit, ability)
UnitMakeAbilityPermanent(unit, true, ability)
BlzUnitDisableAbility(unit, ability, true, true)
end
SetUnitId(unit, id)
for i = 1, #onIndex do
onIndex[i]()
end
source = nil
end
end
onInit(function()
local trigger = CreateTrigger()
local region = CreateRegion()
local rect = GetWorldBounds()
RegionAddRect(region, rect)
RemoveRect(rect)
TriggerRegisterEnterRegion(CreateTrigger(), region, Filter(function()
IndexUnit(GetFilterUnit())
end))
for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
GroupEnumUnitsOfPlayer(bj_lastCreatedGroup, Player(i), Filter(function()
IndexUnit(GetFilterUnit())
end))
TriggerRegisterPlayerUnitEvent(trigger, Player(i), EVENT_PLAYER_UNIT_ISSUED_ORDER, null)
end
TriggerAddCondition(trigger, Filter(function()
if GetIssuedOrderId() == 852056 then
if GetUnitAbilityLevel(GetTriggerUnit(), ability) == 0 then
source = GetTriggerUnit()
for i = 1, #onDeindex do
onDeindex[i]()
end
source = nil
end
end
end))
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function RegisterUnitIndexEvent(code)
if type(code) == "function" then
table.insert(onIndex, code)
end
end
function RegisterUnitDeindexEvent(code)
if type(code) == "function" then
table.insert(onDeindex, code)
end
end
function GetIndexUnit()
return source
end
end
--[[
Allow to retrieve the x and y position of a player mouse
]]--
do
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
local mouse = {}
local trigger = CreateTrigger()
onInit(function()
for i = 0, bj_MAX_PLAYER_SLOTS do
local player = Player(i)
if GetPlayerController(player) == MAP_CONTROL_USER and GetPlayerSlotState(player) == PLAYER_SLOT_STATE_PLAYING then
mouse[player] = {}
TriggerRegisterPlayerEvent(trigger, player, EVENT_PLAYER_MOUSE_MOVE)
end
end
TriggerAddCondition(trigger, Condition(function()
local player = GetTriggerPlayer()
mouse[player].x = BlzGetTriggerPlayerMouseX()
mouse[player].y = BlzGetTriggerPlayerMouseY()
end))
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function GetPlayerMouseX(player)
return mouse[player].x or 0
end
function GetPlayerMouseY(player)
return mouse[player].y or 0
end
end
--[[ mapbounds.lua v1.0.0 |
Description:
Nestharus's WorldBounds ported into Lua, with few simple but useful
additions. Aside from providing map boundary data thru variables,
also provides premade functions for checking whether a coordinate
is inside map boundaries, getting a random coordinate within
map boundaries, and getting a bounded coordinate value.
Requirements:
- None
API:
Prefixes:
MapBounds
- Refers to initial playable map bounds
WorldBounds
- Refers to world bounds
Variables:
number: <Prefix>.centerX
number: <Prefix>.centerY
number: <Prefix>.minX
number: <Prefix>.minY
number: <Prefix>.maxX
number: <Prefix>.maxY
rect: <Prefix>.rect
region: <Prefix>.region
- These variables are intended to be READONLY
Functions:
function <Prefix>:getRandomX() -> number
function <Prefix>:getRandomY() -> number
- Returns a random coordinate inside the bounds
function <Prefix>:getBoundedX(number: x, number: margin=0.00) -> number
function <Prefix>:getBoundedY(number: y, number: margin=0.00) -> number
- Returns a coordinate that is inside the bounds
function <Prefix>:containsX(number: x) -> boolean
function <Prefix>:containsY(number: y) -> boolean
- Checks if the bounds contain the input coordinate
]]--
MapBounds = setmetatable({}, {})
WorldBounds = setmetatable({}, getmetatable(MapBounds))
do
local mt = getmetatable(MapBounds)
mt.__index = mt
function mt:getRandomX()
return GetRandomReal(self.minX, self.maxX)
end
function mt:getRandomY()
return GetRandomReal(self.minY, self.maxY)
end
local function GetBoundedValue(bounds, v, minV, maxV, margin)
margin = margin or 0.00
if v < (bounds[minV] + margin) then
return bounds[minV] + margin
elseif v > (bounds[maxV] - margin) then
return bounds[maxV] - margin
end
return v
end
function mt:getBoundedX(x, margin)
return GetBoundedValue(self, x, "minX", "maxX", margin)
end
function mt:getBoundedY(y, margin)
return GetBoundedValue(self, y, "minY", "maxY", margin)
end
function mt:containsX(x)
return self:getBoundedX(x) == x
end
function mt:containsY(y)
return self:getBoundedY(y) == y
end
local function InitData(bounds)
bounds.region = CreateRegion()
bounds.minX = GetRectMinX(bounds.rect)
bounds.minY = GetRectMinY(bounds.rect)
bounds.maxX = GetRectMaxX(bounds.rect)
bounds.maxY = GetRectMaxY(bounds.rect)
bounds.centerX = (bounds.minX + bounds.maxX) / 2.00
bounds.centerY = (bounds.minY + bounds.maxY) / 2.00
RegionAddRect(bounds.region, bounds.rect)
end
local oldInit = InitGlobals
function InitGlobals()
oldInit()
MapBounds.rect = bj_mapInitialPlayableArea
WorldBounds.rect = GetWorldBounds()
InitData(MapBounds)
InitData(WorldBounds)
end
end
--[[ LineSegmentEnumeration v2.2a
API:
function LineSegment.EnumUnits(number: ax, number: ay, number: bx, number: by, number: offset, boolean: checkCollision)
function LineSegment.EnumDestructables(number: ax, number: ay, number: bx, number: by, number: offset)
function LineSegment.EnumItems(number: ax, number: ay, number: bx, number: by, number: offset)
- returns the enumerated widgets as a table
]]--
LineSegment = {}
do
local RECT = Rect(0, 0, 0, 0)
local GROUP = CreateGroup()
local ox
local oy
local dx
local dy
local da
local db
local ui
local uj
local wdx
local wdy
local uui
local uuj
local function prepare_rect(ax, ay, bx, by, offset)
local x_max
local x_min
local y_max
local y_min
-- get center coordinates of rectangle
ox, oy = 0.5*(ax + bx), 0.5*(ay + by)
-- get rectangle major axis as vector
dx, dy = 0.5*(bx - ax), 0.5*(by - ay)
-- get half of rectangle length (da) and height (db)
da, db = math.sqrt(dx*dx + dy*dy), offset
-- get unit vector of the major axis
ui, uj = dx/da, dy/da
-- prepare bounding rect
if ax > bx then
x_min, x_max = bx - offset, ax + offset
else
x_min, x_max = ax - offset, bx + offset
end
if ay > by then
y_min, y_max = by - offset, ay + offset
else
y_min, y_max = ay - offset, by + offset
end
SetRect(RECT, x_min, y_min, x_max, y_max)
end
local function rect_contains_widget(w, offset)
wdx, wdy = GetWidgetX(w) - ox, GetWidgetY(w) - oy
dx, dy = wdx*ui + wdy*uj, wdx*(-uj) + wdy*ui
da, db = da + offset, db + offset
return dx*dx <= da*da and dy*dy <= db*db
end
local function widget_filter(w, offset)
if rect_contains_widget(w, offset) then
table.insert(LineSegment.enumed, w)
end
end
function LineSegment.EnumUnits(ax, ay, bx, by, offset, checkCollision)
prepare_rect(ax, ay, bx, by, offset)
GroupEnumUnitsInRect(GROUP, RECT)
local enumed = {}
LineSegment.enumed = enumed
for i = 0, BlzGroupGetSize(GROUP) - 1 do
local u = BlzGroupUnitAt(GROUP, i)
widget_filter(u, checkCollision and BlzGetUnitCollisionSize(u) or 0.)
end
return enumed
end
function LineSegment.EnumDestructables(ax, ay, bx, by, offset)
prepare_rect(ax, ay, bx, by, offset)
local enumed = {}
LineSegment.enumed = enumed
EnumDestructablesInRect(RECT, Filter(function ()
widget_filter(GetFilterDestructable(), 0.)
end))
return enumed
end
function LineSegment.EnumItems(ax, ay, bx, by, offset)
prepare_rect(ax, ay, bx, by, offset)
local enumed = {}
LineSegment.enumed = enumed
EnumItemsInRect(RECT, Filter(function ()
widget_filter(GetFilterItem(), 0.)
end))
return enumed
end
end
--[[
/**************************************************************
*
* v1.0.5 by TriggerHappy
* ----------------------
*
* Use this to destroy a handle after X amount seconds.
*
* It's useful for things like effects where you may
* want it to be temporary, but not have to worry
* about the cleaning memory leak. By default it supports
* effects, lightning, weathereffect, items, ubersplats, and units.
*
* Installation
----------------------
* 1. Copy this script and over to your map inside a blank trigger.
*
* API
* ----------------------
* call DestroyEffectTimed(AddSpecialEffect("effect.mdx", 0, 0), 5)
* call DestroyLightningTimed(AddLightning("CLPB", true, 0, 0, 100, 100), 5)
*
**************************************************************/
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local PERIOD = 0.05
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
Timed = setmetatable({}, {})
local mt = getmetatable(Timed)
mt.__index = mt
local timer = CreateTimer()
local array = {}
local key = 0
function mt:destroy(i)
if self.flag == 0 then
DestroyEffect(self.type)
elseif self.flag == 1 then
DestroyLightning(self.type)
elseif self.flag == 2 then
RemoveWeatherEffect(self.type)
elseif self.flag == 3 then
RemoveItem(self.type)
elseif self.flag == 4 then
RemoveUnit(self.type)
elseif self.flag == 5 then
DestroyUbersplat(self.type)
elseif self.flag == 6 then
RemoveDestructable(self.type)
end
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:handle(type, duration, flag)
local this = {}
setmetatable(this, mt)
this.type = type
this.flag = flag
this.ticks = duration/PERIOD
key = key + 1
array[key] = this
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:destroy(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function DestroyEffectTimed(effect, duration)
Timed:handle(effect, duration, 0)
end
function DestroyLightningTimed(lightning, duration)
Timed:handle(lightning, duration, 1)
end
function RemoveWeatherEffectTimed(weathereffect, duration)
Timed:handle(weathereffect, duration, 2)
end
function RemoveItemTimed(item, duration)
Timed:handle(item, duration, 3)
end
function RemoveUnitTimed(unit, duration)
Timed:handle(unit, duration, 4)
end
function DestroyUbersplatTimed(ubersplat, duration)
Timed:handle(ubersplat, duration, 5)
end
function RemoveDestructableTimed(destructable, duration)
Timed:handle(destructable, duration, 6)
end
end
--[[
/* ------------------- SpellEffectEvent v1.1 by Chopinski ------------------ */
-- Full credits to Bribe for the original library. I just modified it to store
-- some usefull information in a table to be used later instead of calling
-- these functions all the time for every ability.
/* ----------------------------------- END ---------------------------------- */
]]--
do
local funcs = {}
local trigger = nil
local location = Location(0, 0)
Spell = {
source = {
unit,
player,
handle,
isHero,
isStructure,
id,
x,
y,
z
},
target = {
unit,
player,
handle,
isHero,
isStructure,
id,
x,
y,
z
},
ability,
level,
id,
x,
y,
z
}
local function GetUnitZ(unit)
MoveLocation(location, GetUnitX(unit), GetUnitY(unit))
return GetUnitFlyHeight(unit) + GetLocationZ(location)
end
local function GetSpellTargetZ()
MoveLocation(location, Spell.x, Spell.y)
if Spell.target.unit then
return GetUnitZ(Spell.target.unit)
else
return GetLocationZ(location)
end
end
function RegisterSpellEffectEvent(ability, code)
if type(code) == "function" then
if not trigger then
trigger = CreateTrigger()
TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_SPELL_EFFECT)
TriggerAddCondition(trigger, Condition(function()
local f = funcs[GetSpellAbilityId()]
if f then
Spell.source.unit = GetTriggerUnit()
Spell.source.player = GetOwningPlayer(Spell.source.unit)
Spell.source.handle = GetHandleId(Spell.source.unit)
Spell.source.id = GetUnitUserData(Spell.source.unit)
Spell.source.x = GetUnitX(Spell.source.unit)
Spell.source.y = GetUnitY(Spell.source.unit)
Spell.source.z = GetUnitZ(Spell.source.unit)
Spell.source.isHero = IsUnitType(Spell.source.unit, UNIT_TYPE_HERO)
Spell.source.isStructure = IsUnitType(Spell.source.unit, UNIT_TYPE_STRUCTURE)
Spell.target.unit = GetSpellTargetUnit()
Spell.target.player = GetOwningPlayer(Spell.target.unit)
Spell.target.handle = GetHandleId(Spell.target.unit)
Spell.target.id = GetUnitUserData(Spell.target.unit)
Spell.target.x = GetUnitX(Spell.target.unit)
Spell.target.y = GetUnitY(Spell.target.unit)
Spell.target.z = GetUnitZ(Spell.target.unit)
Spell.target.isHero = IsUnitType(Spell.target.unit, UNIT_TYPE_HERO)
Spell.target.isStructure = IsUnitType(Spell.target.unit, UNIT_TYPE_STRUCTURE)
Spell.x = GetSpellTargetX()
Spell.y = GetSpellTargetY()
Spell.z = GetSpellTargetZ()
Spell.id = GetSpellAbilityId()
Spell.level = GetUnitAbilityLevel(Spell.source.unit, Spell.id)
Spell.ability = BlzGetUnitAbility(Spell.source.unit, Spell.id)
f()
end
end))
end
funcs[ability] = code
end
end
end
-- Arcing Text Tag v1.0.0.3 by Maker encoded to Lua
DEFINITION = 1.0/32.0
SIZE_MIN = 0.013 -- Minimum size of text
SIZE_BONUS = 0.005 -- Text size increase
TIME_LIFE = 0.5 -- How long the text lasts
TIME_FADE = 0.3 -- When does the text start to fade
Z_OFFSET = 50 -- Height above unit
Z_OFFSET_BON = 50 -- How much extra height the text gains
VELOCITY = 2.0 -- How fast the text move in x/y plane
TMR = CreateTimer()
ANGLE_RND = true -- Is the angle random or fixed
if not ANGLE_RND then
ANGLE = bj_PI/2.0 -- If fixed, specify the Movement angle of the text.
end
tt = {}
as = {} -- angle, sin component
ac = {} -- angle, cos component
ah = {} -- arc height
t = {} -- time
x = {} -- origin x
y = {} -- origin y
str = {} -- text
ic = 0 -- Instance count
rn = {} ; rn[0] = 0
next = {} ; next[0] = 0
prev = {} ; prev[0] = 0 --Needed due to Lua not initializing them.
function ArcingTextTag(s, u)
local this = rn[0]
if this == 0 then
ic = ic + 1
this = ic
else
rn[0] = rn[this]
end
next[this] = 0
prev[this] = prev[0]
next[prev[0]] = this
prev[0] = this
str[this] = s
x[this] = GetUnitX(u)
y[this] = GetUnitY(u)
t[this] = TIME_LIFE
local a
if ANGLE_RND then
a = GetRandomReal(0, 2*bj_PI)
else
a = ANGLE
end
as[this] = Sin(a)*VELOCITY
ac[this] = Cos(a)*VELOCITY
ah[this] = 0.
if IsUnitVisible(u, GetLocalPlayer()) then
tt[this] = CreateTextTag()
SetTextTagPermanent(tt[this], false)
SetTextTagLifespan(tt[this], TIME_LIFE)
SetTextTagFadepoint(tt[this], TIME_FADE)
SetTextTagText(tt[this], s, SIZE_MIN)
SetTextTagPos(tt[this], x[this], y[this], Z_OFFSET)
end
if prev[this] == 0 then
TimerStart(TMR, DEFINITION, true, function()
local this = next[0]
local p
while (this ~= 0) do
p = Sin(bj_PI*t[this])
t[this] = t[this] - DEFINITION
x[this] = x[this] + ac[this]
y[this] = y[this] + as[this]
SetTextTagPos(tt[this], x[this], y[this], Z_OFFSET + Z_OFFSET_BON * p)
SetTextTagText(tt[this], str[this], SIZE_MIN + SIZE_BONUS * p)
if t[this] <= 0.0 then
tt[this] = null
next[prev[this]] = next[this]
prev[next[this]] = prev[this]
rn[this] = rn[0]
rn[0] = this
if next[0] == 0 then
PauseTimer(TMR)
end
end
this = next[this]
end
end)
end
return this
end
--[[
/**************************************************************
*
* RegisterPlayerUnitEvent
* v5.1.0.1
* By Magtheridon96
* Lua version by Chopinski
*
* I would like to give a special thanks to Bribe, azlier
* and BBQ for improving this library. For modularity, it only
* supports player unit events.
*
* Functions passed to RegisterPlayerUnitEvent must either
* return a boolean (false) or nothing. (Which is a Pro)
*
* Warning:
* --------
*
* - Don't use TriggerSleepAction inside registered code.
* - Don't destroy a trigger unless you really know what you're doing.
*
* API:
* ----
*
* - function RegisterPlayerUnitEvent takes playerunitevent whichEvent, code whichFunction returns nothing
* - Registers code that will execute when an event fires.
* - function RegisterPlayerUnitEventForPlayer takes playerunitevent whichEvent, code whichFunction, player whichPlayer returns nothing
* - Registers code that will execute when an event fires for a certain player.
* - function GetPlayerUnitEventTrigger takes playerunitevent whichEvent returns trigger
* - Returns the trigger corresponding to ALL functions of a playerunitevent.
*
**************************************************************/
]]--
do
local trigger = {}
local f = {}
local n = {}
function RegisterPlayerUnitEvent(playerunitevent, code)
if type(code) == "function" then
local i = GetHandleId(playerunitevent)
if not trigger[i] then
trigger[i] = CreateTrigger()
for j = 0, bj_MAX_PLAYERS do
TriggerRegisterPlayerUnitEvent(trigger[i], Player(j), playerunitevent, null)
end
end
if not n[i] then n[i] = 1 end
if not f[i] then f[i] = {} end
table.insert(f[i], code)
TriggerAddCondition(trigger[i], Filter(function()
f[i][n[i]]()
n[i] = n[i] + 1
if n[i] > #f[i] then n[i] = 1 end
end))
end
end
function RegisterPlayerUnitEventForPlayer(playerunitevent, code, player)
if type(code) == "function" then
local i = (bj_MAX_PLAYERS + 1) * GetHandleId(playerunitevent) + GetPlayerId(player)
if not trigger[i] then
trigger[i] = CreateTrigger()
TriggerRegisterPlayerUnitEvent(trigger[i], player, playerunitevent, null)
end
if not n[i] then n[i] = 1 end
if not f[i] then f[i] = {} end
table.insert(f[i], code)
TriggerAddCondition(event[i].trigger, Filter(function()
f[i][n[i]]()
n[i] = n[i] + 1
if n[i] > #f[i] then n[i] = 1 end
end))
end
end
function GetPlayerUnitEventTrigger(playerunitevent)
return trigger[GetHandleId(playerunitevent)]
end
end
--[[
-- ---------------------------------------- Tenacity v1.0 --------------------------------------- --
-- Intro
-- This library intension in to introduce to warcraft an easy way to
-- manipulate the duration of crowd control on units.
--
-- How it Works?
-- Working in conjuction with the Crowd Control Library this library allows you to control the
-- duration of disables provided in the Crowd Control library. It work similar to the Tenacity
-- status in League of Legends or the Status Resistence in Dota 2.
--
-- The are basically 3 types of tenacity: Normal (stacks multiplicatively),
-- Flat and Offset (stacks additively).The formula for calculation is:
-- newDuration = (duration - Offset) * [(1 - value1)*(1 - value2)*...] * (1 - Flat)
--
-- The system also allow negative values for Tenacity, resulting in increased
-- crowd control duration. Also note that tenacity will only work on CC applied through
-- the Crowd Control API
--
-- How to Import
-- 1. Copy the Indexer library into your map
-- 2. Copy this library into your map and you are done
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
function GetUnitTenacity(unit)
return Tenacity:get(unit, 0)
end
function GetUnitTenacityFlat(unit)
return Tenacity:get(unit, 1)
end
function GetUnitTenacityOffset(unit)
return Tenacity:get(unit, 2)
end
function SetUnitTenacity(unit, value)
Tenacity:set(unit, value, 0)
end
function SetUnitTenacityFlat(unit, value)
Tenacity:set(unit, value, 1)
end
function SetUnitTenacityOffset(unit, value)
Tenacity:set(unit, value, 2)
end
function UnitAddTenacity(unit, value)
Tenacity:add(unit, value, 0)
end
function UnitAddTenacityFlat(unit, value)
Tenacity:add(unit, value, 1)
end
function UnitAddTenacityOffset(unit, value)
Tenacity:add(unit, value, 2)
end
function UnitRemoveTenacity(unit, value)
Tenacity:remove(unit, value)
end
function GetTenacityDuration(unit, duration)
return Tenacity:calculate(unit, duration)
end
function RegisterTenacityUnit(unit)
return Tenacity:register(unit)
end
function DisplayTenacityStatus(unit)
Tenacity:print(unit)
end
-- ---------------------------------------------------------------------------------------------- --
-- System --
-- ---------------------------------------------------------------------------------------------- --
Tenacity = setmetatable({}, {})
local mt = getmetatable(Tenacity)
mt.__index = mt
local list = {}
function mt:get(unit, type)
if list[unit] then
if type == 0 then
if (#list[unit].normal or 0) > 0 then
return 1 - list[unit].tenacity
else
return 0
end
elseif type == 1 then
return list[unit].flat
else
return list[unit].offset
end
end
return 0
end
function mt:set(unit, value, type)
if not list[unit] then
self:register(unit)
end
if type == 0 then
list[unit].tenacity = value
elseif type == 1 then
list[unit].flat = value
else
list[unit].offset = value
end
end
function mt:add(unit, value, type)
if not list[unit] then
self:register(unit)
end
if type == 0 then
table.insert(list[unit].normal, value)
self:update(unit)
elseif type == 1 then
list[unit].flat = (list[unit].flat or 0) + value
else
list[unit].offset = (list[unit].offset or 0) + value
end
end
function mt:update(unit)
if list[unit] then
for i = 1, #list[unit].normal do
if i > 1 then
list[unit].tenacity = (list[unit].tenacity or 0) * (1 - list[unit].normal[i])
else
list[unit].tenacity = 1 - list[unit].normal[i]
end
end
end
end
function mt:remove(unit, value)
if value ~= 0 and list[unit] then
for i = 1, #list[unit].normal do
if list[unit].normal[i] == value then
table.remove(list[unit].normal, i)
break
end
end
self:update(unit)
end
end
function mt:calculate(unit, duration)
if duration ~= 0 and list[unit] then
if #list[unit].normal > 0 then
duration = (duration - list[unit].offset) * list[unit].tenacity * (1 - list[unit].flat)
else
duration = (duration - list[unit].offset) * (1 - list[unit].flat)
end
if duration <= 0 then
return 0
end
end
return duration
end
function mt:print(unit)
if list[unit] then
ClearTextMessages()
print("Tenacity Status for " .. GetUnitName(unit))
print("Tenacity List [" .. (list[unit].normal[1] or 0) .. " | " .. (list[unit].normal[2] or 0) .. " | " .. (list[unit].normal[3] or 0) .. " | " .. (list[unit].normal[4] or 0) .. " | " .. (list[unit].normal[5] or 0) .. " | " .. (list[unit].normal[6] or 0) .. " | ...] = " .. (self:get(unit, 0)))
print("Tenacity Flat [" .. (self:get(unit, 1)) .. "]")
print("Tenacity Offset [" .. (self:get(unit, 2)) .. "]")
end
end
function mt:register(unit)
if not list[unit] then
list[unit] = {}
list[unit].normal = {}
list[unit].flat = 0
list[unit].offset = 0
list[unit].tenacity = 0
end
return list[unit]
end
onInit(function()
RegisterUnitDeindexEvent(function()
list[GetIndexUnit()] = nil
end)
end)
end
--[[ requires Tenacity
-- ------------------------------------- Tenacity Utils v1.0 ------------------------------------ --
-- Utility Library that include a few extra functions to deal with Tenacity
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
function UnitAddTenacityTimed(unit, value, duration)
TenacityUtils:addTimed(unit, value, duration, 0)
end
function UnitAddTenacityFlatTimed(unit, value, duration)
TenacityUtils:addTimed(unit, value, duration, 1)
end
function UnitAddTenacityOffsetTimed(unit, value, duration)
TenacityUtils:addTimed(unit, value, duration, 2)
end
-- ---------------------------------------------------------------------------------------------- --
-- System --
-- ---------------------------------------------------------------------------------------------- --
TenacityUtils = setmetatable({}, {})
local mt = getmetatable(TenacityUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
local period = 0.03125000
function mt:remove(i)
if self.type == 0 then
Tenacity:remove(self.unit, self.value)
else
Tenacity:add(self.unit, -self.value, self.type)
end
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, value, duration, type)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.value = value
this.type = type
this.duration = duration
key = key + 1
array[key] = this
Tenacity:add(unit, value, type)
if key == 1 then
TimerStart(timer, period, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.duration <= 0 then
i = this:remove(i)
end
this.duration = this.duration - period
i = i + 1
end
end)
end
end
end
--[[ requires optional DamageInterface, optional Evasion, optional CriticalStrike, optional SpellPower, optional LifeSteal, optional SpellVamp, optional CooldownReduction, optional Tenacity
-- ---------------------------------------- NewBonus v2.4 --------------------------------------- --
-- Since ObjectMerger is broken and we still have no means to edit
-- bonus values (green values) i decided to create a light weight
-- Bonus library that works in the same way that the original Bonus Mod
-- by Earth Fury did. NewBonus requires patch 1.31+.
-- Credits to Earth Fury for the original Bonus idea
-- How to Import?
-- Importing bonus mod is really simple. Just copy the abilities with the
-- prefix "NewBonus" from the Object Editor into your map and match their new raw
-- code to the bonus types in the global block below. Then create a trigger called
-- NewBonus, convert it to custom text and paste this code there. You done!
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- Configuration --
-- ---------------------------------------------------------------------------------------------- --
-- If true will use the extended version of the system.
-- Make sure you have the DamageInterface and Cooldown Reduction libraries
NewBonus_EXTENDED = true
-- This is the maximum recursion limit allowed by the system.
-- Its value must be greater than or equal to 0. When equal to 0
-- no recursion is allowed. Values too big can cause screen freezes.
local RECURSION_LIMIT = 8
-- The bonus types
BONUS_DAMAGE = 1
BONUS_ARMOR = 2
BONUS_AGILITY = 3
BONUS_STRENGTH = 4
BONUS_INTELLIGENCE = 5
BONUS_HEALTH = 6
BONUS_MANA = 7
BONUS_MOVEMENT_SPEED = 8
BONUS_SIGHT_RANGE = 9
BONUS_HEALTH_REGEN = 10
BONUS_MANA_REGEN = 11
BONUS_ATTACK_SPEED = 12
BONUS_MAGIC_RESISTANCE = 13
BONUS_EVASION_CHANCE = 14
BONUS_CRITICAL_DAMAGE = 15
BONUS_CRITICAL_CHANCE = 16
BONUS_LIFE_STEAL = 17
BONUS_MISS_CHANCE = 18
BONUS_SPELL_POWER_FLAT = 19
BONUS_SPELL_POWER_PERCENT = 20
BONUS_SPELL_VAMP = 21
BONUS_COOLDOWN_REDUCTION = 22
BONUS_COOLDOWN_REDUCTION_FLAT = 23
BONUS_COOLDOWN_OFFSET = 24
BONUS_TENACITY = 25
BONUS_TENACITY_FLAT = 26
BONUS_TENACITY_OFFSET = 27
-- The abilities codes for each bonus
-- When pasting the abilities over to your map
-- their raw code should match the bonus here
local DAMAGE_ABILITY = FourCC('Z001')
local ARMOR_ABILITY = FourCC('Z002')
local STATS_ABILITY = FourCC('Z003')
local HEALTH_ABILITY = FourCC('Z004')
local MANA_ABILITY = FourCC('Z005')
local HEALTHREGEN_ABILITY = FourCC('Z006')
local MANAREGEN_ABILITY = FourCC('Z007')
local ATTACKSPEED_ABILITY = FourCC('Z008')
local MOVEMENTSPEED_ABILITY = FourCC('Z009')
local SIGHT_RANGE_ABILITY = FourCC('Z00A')
local MAGIC_RESISTANCE_ABILITY = FourCC('Z00B')
local CRITICAL_STRIKE_ABILITY = FourCC('Z00C')
local EVASION_ABILITY = FourCC('Z00D')
local LIFE_STEAL_ABILITY = FourCC('Z00E')
-- The abilities fields that are modified. For the sake of readability
local DAMAGE_FIELD = ABILITY_ILF_ATTACK_BONUS
local ARMOR_FIELD = ABILITY_ILF_DEFENSE_BONUS_IDEF
local AGILITY_FIELD = ABILITY_ILF_AGILITY_BONUS
local STRENGTH_FIELD = ABILITY_ILF_STRENGTH_BONUS_ISTR
local INTELLIGENCE_FIELD = ABILITY_ILF_INTELLIGENCE_BONUS
local HEALTH_FIELD = ABILITY_ILF_MAX_LIFE_GAINED
local MANA_FIELD = ABILITY_ILF_MAX_MANA_GAINED
local MOVEMENTSPEED_FIELD = ABILITY_ILF_MOVEMENT_SPEED_BONUS
local SIGHT_RANGE_FIELD = ABILITY_ILF_SIGHT_RANGE_BONUS
local HEALTHREGEN_FIELD = ABILITY_RLF_AMOUNT_OF_HIT_POINTS_REGENERATED
local MANAREGEN_FIELD = ABILITY_RLF_AMOUNT_REGENERATED
local ATTACKSPEED_FIELD = ABILITY_RLF_ATTACK_SPEED_INCREASE_ISX1
local MAGIC_RESISTANCE_FIELD = ABILITY_RLF_DAMAGE_REDUCTION_ISR2
local CRITICAL_CHANCE_FIELD = ABILITY_RLF_CHANCE_TO_CRITICAL_STRIKE
local CRITICAL_DAMAGE_FIELD = ABILITY_RLF_DAMAGE_MULTIPLIER_OCR2
local EVASION_FIELD = ABILITY_RLF_CHANCE_TO_EVADE_EEV1
local LIFE_STEAL_FIELD = ABILITY_RLF_LIFE_STOLEN_PER_ATTACK
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
function GetUnitBonus(unit, type)
return NewBonus:get(unit, type)
end
function SetUnitBonus(unit, type, value)
return NewBonus:set(unit, type, value, false)
end
function RemoveUnitBonus(unit, type)
if type == BONUS_CRITICAL_DAMAGE then
NewBonus:set(unit, type, 1, false)
else
NewBonus:set(unit, type, 0, false)
end
if type == BONUS_LIFE_STEAL then
UnitRemoveAbility(unit, LIFE_STEAL_ABILITY)
end
end
function AddUnitBonus(unit, type, value)
return NewBonus:add(unit, type, value)
end
function RegisterBonusEvent(code)
NewBonus:register(code, 0)
end
function RegisterBonusTypeEvent(type, code)
NewBonus:register(code, type)
end
function GetBonusUnit()
return NewBonus.unit[NewBonus.key]
end
function GetBonusType()
return NewBonus.type[NewBonus.key]
end
function SetBonusType(type)
if type >= BONUS_DAMAGE and type <= NewBonus.last then
NewBonus.type[NewBonus.key] = type
end
end
function GetBonusAmount()
return NewBonus.amount[NewBonus.key]
end
function SetBonusAmount(real)
NewBonus.amount[NewBonus.key] = real
end
-- ---------------------------------------------------------------------------------------------- --
-- System --
-- ---------------------------------------------------------------------------------------------- --
NewBonus = setmetatable({}, {})
local mt = getmetatable(NewBonus)
mt.__index = mt
NewBonus.linkType = 0
NewBonus.last = 0
NewBonus.key = 0
NewBonus.unit = {}
NewBonus.type = {}
NewBonus.amount = {}
local trigger = {}
local event = {}
local count = 0
function mt:checkOverflow(current, value)
if value > 0 and current > 2147483647 - value then
return 2147483647 - current
elseif value < 0 and current < -2147483648 - value then
return -2147483648 - current
else
return value
end
end
function mt:onEvent(key)
local i = 0
local next = -1
local prev = -2
count = count + 1
if NewBonus.amount[key] ~= 0 and (count - NewBonus.last < RECURSION_LIMIT) then
while NewBonus.type[key] ~= next and (i - NewBonus.last < RECURSION_LIMIT) do
next = NewBonus.type[key]
if event[next] then
for j = 1, #event[next] do
event[next][j]()
end
end
if NewBonus.type[key] ~= next then
i = i + 1
else
if next ~= prev then
for j = 1, #trigger do
trigger[j]()
end
if NewBonus.type[key] ~= next then
i = i + 1
prev = next
end
end
end
end
end
count = count - 1
NewBonus.key = key
end
function mt:setAbility(unit, ability, field, value, integer, adding)
if GetUnitAbilityLevel(unit, ability) == 0 then
UnitAddAbility(unit, ability)
UnitMakeAbilityPermanent(unit, true, ability)
end
if integer then
if adding then
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(unit, ability), field, 0, BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, ability), field, 0) + value) then
IncUnitAbilityLevel(unit, ability)
DecUnitAbilityLevel(unit, ability)
end
else
if BlzSetAbilityIntegerLevelField(BlzGetUnitAbility(unit, ability), field, 0, value) then
IncUnitAbilityLevel(unit, ability)
DecUnitAbilityLevel(unit, ability)
end
end
NewBonus.linkType = NewBonus.type[NewBonus.key]
if NewBonus.key > 0 then
NewBonus.key = NewBonus.key - 1
end
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, ability), field, 0)
else
if BlzSetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, 0, value) then
IncUnitAbilityLevel(unit, ability)
DecUnitAbilityLevel(unit, ability)
end
NewBonus.linkType = NewBonus.type[NewBonus.key]
if NewBonus.key > 0 then
NewBonus.key = NewBonus.key - 1
end
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, 0)
end
end
function mt:get(unit, type)
if type == BONUS_DAMAGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, DAMAGE_ABILITY), DAMAGE_FIELD, 0)
elseif type == BONUS_ARMOR then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, ARMOR_ABILITY), ARMOR_FIELD, 0)
elseif type == BONUS_HEALTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, HEALTH_ABILITY), HEALTH_FIELD, 0)
elseif type == BONUS_MANA then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, MANA_ABILITY), MANA_FIELD, 0)
elseif type == BONUS_AGILITY then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, STATS_ABILITY), AGILITY_FIELD, 0)
elseif type == BONUS_STRENGTH then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, STATS_ABILITY), STRENGTH_FIELD, 0)
elseif type == BONUS_INTELLIGENCE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, STATS_ABILITY), INTELLIGENCE_FIELD, 0)
elseif type == BONUS_MOVEMENT_SPEED then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, MOVEMENTSPEED_ABILITY), MOVEMENTSPEED_FIELD, 0)
elseif type == BONUS_SIGHT_RANGE then
return BlzGetAbilityIntegerLevelField(BlzGetUnitAbility(unit, SIGHT_RANGE_ABILITY), SIGHT_RANGE_FIELD, 0)
elseif type == BONUS_HEALTH_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, HEALTHREGEN_ABILITY), HEALTHREGEN_FIELD, 0)
elseif type == BONUS_MANA_REGEN then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, MANAREGEN_ABILITY), MANAREGEN_FIELD, 0)
elseif type == BONUS_ATTACK_SPEED then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ATTACKSPEED_ABILITY), ATTACKSPEED_FIELD, 0)
elseif type == BONUS_MAGIC_RESISTANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, MAGIC_RESISTANCE_ABILITY), MAGIC_RESISTANCE_FIELD, 0)
elseif type >= BONUS_EVASION_CHANCE and type <= NewBonus.last then
if NewBonus_EXTENDED and Damage and Evasion and Critical and SpellPower and LifeSteal and SpellVamp and Tenacity then
if type == BONUS_EVASION_CHANCE then
return GetUnitEvasionChance(unit)
elseif type == BONUS_MISS_CHANCE then
return GetUnitMissChance(unit)
elseif type == BONUS_CRITICAL_CHANCE then
return GetUnitCriticalChance(unit)
elseif type == BONUS_CRITICAL_DAMAGE then
return GetUnitCriticalMultiplier(unit)
elseif type == BONUS_SPELL_POWER_FLAT then
return GetUnitSpellPowerFlat(unit)
elseif type == BONUS_SPELL_POWER_PERCENT then
return GetUnitSpellPowerPercent(unit)
elseif type == BONUS_LIFE_STEAL then
return GetUnitLifeSteal(unit)
elseif type == BONUS_SPELL_VAMP then
return GetUnitSpellVamp(unit)
elseif type == BONUS_COOLDOWN_REDUCTION then
return GetUnitCooldownReduction(unit)
elseif type == BONUS_COOLDOWN_REDUCTION_FLAT then
return GetUnitCooldownReductionFlat(unit)
elseif type == BONUS_COOLDOWN_OFFSET then
return GetUnitCooldownOffset(unit)
elseif type == BONUS_TENACITY then
return GetUnitTenacity(unit)
elseif type == BONUS_TENACITY_FLAT then
return GetUnitTenacityFlat(unit)
elseif type == BONUS_TENACITY_OFFSET then
return GetUnitTenacityOffset(unit)
end
else
if type == BONUS_CRITICAL_CHANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, CRITICAL_STRIKE_ABILITY), CRITICAL_CHANCE_FIELD, 0)
elseif type == BONUS_CRITICAL_DAMAGE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, CRITICAL_STRIKE_ABILITY), CRITICAL_DAMAGE_FIELD, 0)
elseif type == BONUS_EVASION_CHANCE then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, EVASION_ABILITY), EVASION_FIELD, 0)
elseif type == BONUS_LIFE_STEAL then
return BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, LIFE_STEAL_ABILITY), LIFE_STEAL_FIELD, 0)
end
end
else
print("Invalid Bonus Type")
end
return -1
end
function mt:set(unit, type, value, adding)
if not adding then
NewBonus.key = NewBonus.key + 1
NewBonus.unit[NewBonus.key] = unit
NewBonus.type[NewBonus.key] = type
NewBonus.amount[NewBonus.key] = value
self:onEvent(NewBonus.key)
if NewBonus.amount[NewBonus.key] ~= value then
value = NewBonus.amount[NewBonus.key]
end
if NewBonus.type[NewBonus.key] ~= type then
return self:set(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key], NewBonus.amount[NewBonus.key], not adding)
end
else
NewBonus.unit[NewBonus.key] = unit
NewBonus.type[NewBonus.key] = type
NewBonus.amount[NewBonus.key] = value
end
if type == BONUS_DAMAGE then
return self:setAbility(unit, DAMAGE_ABILITY, DAMAGE_FIELD, value, true, adding)
elseif type == BONUS_ARMOR then
return self:setAbility(unit, ARMOR_ABILITY, ARMOR_FIELD, value, true, adding)
elseif type == BONUS_HEALTH then
local real = GetUnitLifePercent(unit)
if value == 0 and not adding then
BlzSetUnitMaxHP(unit, (BlzGetUnitMaxHP(unit) - self:get(unit, type)))
else
BlzSetUnitMaxHP(unit, (BlzGetUnitMaxHP(unit) + value))
end
self:setAbility(unit, HEALTH_ABILITY, HEALTH_FIELD, value, true, adding)
SetUnitLifePercentBJ(unit, real)
return value
elseif type == BONUS_MANA then
local real = GetUnitManaPercent(unit)
if value == 0 and not adding then
BlzSetUnitMaxMana(unit, (BlzGetUnitMaxMana(unit) - self:get(unit, type)))
else
BlzSetUnitMaxMana(unit, (BlzGetUnitMaxMana(unit) + value))
end
self:setAbility(unit, MANA_ABILITY, MANA_FIELD, value, true, adding)
SetUnitManaPercentBJ(unit, real)
return value
elseif type == BONUS_AGILITY then
return self:setAbility(unit, STATS_ABILITY, AGILITY_FIELD, value, true, adding)
elseif type == BONUS_STRENGTH then
return self:setAbility(unit, STATS_ABILITY, STRENGTH_FIELD, value, true, adding)
elseif type == BONUS_INTELLIGENCE then
return self:setAbility(unit, STATS_ABILITY, INTELLIGENCE_FIELD, value, true, adding)
elseif type == BONUS_MOVEMENT_SPEED then
return self:setAbility(unit, MOVEMENTSPEED_ABILITY, MOVEMENTSPEED_FIELD, value, true, adding)
elseif type == BONUS_SIGHT_RANGE then
if value == 0 and not adding then
BlzSetUnitRealField(unit, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(unit, UNIT_RF_SIGHT_RADIUS) - self:get(unit, type)))
else
BlzSetUnitRealField(unit, UNIT_RF_SIGHT_RADIUS, (BlzGetUnitRealField(unit, UNIT_RF_SIGHT_RADIUS) + value))
end
self:setAbility(unit, SIGHT_RANGE_ABILITY, SIGHT_RANGE_FIELD, value, true, adding)
return value
elseif type == BONUS_HEALTH_REGEN then
return self:setAbility(unit, HEALTHREGEN_ABILITY, HEALTHREGEN_FIELD, value, false, adding)
elseif type == BONUS_MANA_REGEN then
return self:setAbility(unit, MANAREGEN_ABILITY, MANAREGEN_FIELD, value, false, adding)
elseif type == BONUS_ATTACK_SPEED then
return self:setAbility(unit, ATTACKSPEED_ABILITY, ATTACKSPEED_FIELD, value, false, adding)
elseif type == BONUS_MAGIC_RESISTANCE then
return self:setAbility(unit, MAGIC_RESISTANCE_ABILITY, MAGIC_RESISTANCE_FIELD, value, false, adding)
elseif type >= BONUS_EVASION_CHANCE and type <= NewBonus.last then
if NewBonus_EXTENDED and Damage and Evasion and Critical and SpellPower and LifeSteal and SpellVamp and Tenacity then
if type == BONUS_EVASION_CHANCE then
SetUnitEvasionChance(unit, value)
elseif type == BONUS_MISS_CHANCE then
SetUnitMissChance(unit, value)
elseif type == BONUS_CRITICAL_CHANCE then
SetUnitCriticalChance(unit, value)
elseif type == BONUS_CRITICAL_DAMAGE then
SetUnitCriticalMultiplier(unit, value)
elseif type == BONUS_SPELL_POWER_FLAT then
SetUnitSpellPowerFlat(unit, value)
elseif type == BONUS_SPELL_POWER_PERCENT then
SetUnitSpellPowerPercent(unit, value)
elseif type == BONUS_LIFE_STEAL then
SetUnitLifeSteal(unit, value)
elseif type == BONUS_SPELL_VAMP then
SetUnitSpellVamp(unit, value)
elseif type == BONUS_COOLDOWN_REDUCTION then
if adding then
UnitAddCooldownReduction(unit, value)
else
SetUnitCooldownReduction(unit, value)
end
elseif type == BONUS_COOLDOWN_REDUCTION_FLAT then
SetUnitCooldownReductionFlat(unit, value)
elseif type == BONUS_COOLDOWN_OFFSET then
SetUnitCooldownOffset(unit, value)
elseif type == BONUS_TENACITY then
if adding then
UnitAddTenacity(unit, value)
else
SetUnitTenacity(unit, value)
end
elseif type == BONUS_TENACITY_FLAT then
SetUnitTenacityFlat(unit, value)
elseif type == BONUS_TENACITY_OFFSET then
SetUnitTenacityOffset(unit, value)
end
NewBonus.linkType = type
if NewBonus.key > 0 then
NewBonus.key = NewBonus.key - 1
end
return value
else
if type == BONUS_CRITICAL_CHANCE then
return self:setAbility(unit, CRITICAL_STRIKE_ABILITY, CRITICAL_CHANCE_FIELD, value, false, adding)
elseif type == BONUS_CRITICAL_DAMAGE then
return self:setAbility(unit, CRITICAL_STRIKE_ABILITY, CRITICAL_DAMAGE_FIELD, value, false, adding)
elseif type == BONUS_EVASION_CHANCE then
return self:setAbility(unit, EVASION_ABILITY, EVASION_FIELD, value, false, adding)
elseif type == BONUS_LIFE_STEAL then
return self:setAbility(unit, LIFE_STEAL_ABILITY, LIFE_STEAL_FIELD, value, false, adding)
end
end
else
print("Invalid Bonus Type")
end
return -1
end
function mt:add(unit, type, value)
if value ~= 0 then
NewBonus.key = NewBonus.key + 1
NewBonus.unit[NewBonus.key] = unit
NewBonus.type[NewBonus.key] = type
NewBonus.amount[NewBonus.key] = value
if type <= BONUS_SIGHT_RANGE then
NewBonus.amount[NewBonus.key] = self:checkOverflow(self:get(unit, type), R2I(value))
end
self:onEvent(NewBonus.key)
value = NewBonus.amount[NewBonus.key]
if NewBonus.type[NewBonus.key] <= BONUS_SIGHT_RANGE then
self:set(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key], self:checkOverflow(self:get(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key]), R2I(NewBonus.amount[NewBonus.key])), true)
else
if NewBonus_EXTENDED and Damage and Evasion and Critical and SpellPower and LifeSteal and SpellVamp and Tenacity then
if NewBonus.type[NewBonus.key] == BONUS_COOLDOWN_REDUCTION or NewBonus.type[NewBonus.key] == BONUS_TENACITY then
self:set(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key], NewBonus.amount[NewBonus.key], true)
else
self:set(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key], self:get(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key]) + NewBonus.amount[NewBonus.key], true)
end
else
self:set(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key], self:get(NewBonus.unit[NewBonus.key], NewBonus.type[NewBonus.key]) + NewBonus.amount[NewBonus.key], true)
end
end
return value
end
return -1
end
function mt:register(code, bonus)
if type(code) == "function" then
if bonus >= BONUS_DAMAGE and bonus <= NewBonus.last then
if not event[bonus] then event[bonus] = {} end
table.insert(event[bonus], code)
else
table.insert(trigger, code)
end
end
end
onInit(function()
if NewBonus_EXTENDED and Damage and Evasion and Critical and SpellPower and LifeSteal and SpellVamp and Tenacity then
NewBonus.last = BONUS_TENACITY_OFFSET
else
NewBonus.last = BONUS_LIFE_STEAL
end
end)
end
--[[ requires NewBonus, RegisterPlayerUnitEvent
-- ------------------------------------- NewBonusUtils v2.4 ------------------------------------- --
-- API:
-- function AddUnitBonusTimed takes unit u, integer bonus_type, real amount, real duration returns nothing
-- -> Add the specified amount for the specified bonus type for unit for a duration
-- -> Example: call AddUnitBonusTimed(GetTriggerUnit(), BONUS_ARMOR, 13, 10.5)
-- function LinkBonusToBuff takes unit u, integer bonus_type, real amount, integer buffId returns nothing
-- -> Links the bonus amount specified to a buff or ability. As long as the unit has the buff or
-- -> the ability represented by the parameter buffId the bonus is not removed.
-- -> Example: call LinkBonusToBuff(GetTriggerUnit(), BONUS_ARMOR, 10, 'B000')
-- function LinkBonusToItem takes unit u, integer bonus_type, real amount, item i returns nothing
-- -> Links the bonus amount specified to an item. As long as the unit has that item the bonus is not removed.
-- -> Note that it will work for items with the same id, because it takes as parameter the item object.
-- -> Example: call LinkBonusToItem(GetManipulatingUnit(), BONUS_ARMOR, 10, GetManipulatedItem())
-- function UnitCopyBonuses takes unit source, unit target returns nothing
-- -> Copy the source unit bonuses using the Add functionality to the target unit
-- -> Example: call UnitCopyBonuses(GetTriggerUnit(), GetSummonedUnit())
-- function UnitMirrorBonuses takes unit source, unit target returns nothing
-- -> Copy the source unit bonuses using the Set functionality to the target unit
-- -> Example: call UnitMirrorBonuses(GetTriggerUnit(), GetSummonedUnit())
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- Configuration --
-- ---------------------------------------------------------------------------------------------- --
local PERIOD = 0.03125000
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
function AddUnitBonusTimed(unit, type, amount, duration)
NewBonusUtils:linkTimed(unit, type, amount, duration, true)
end
function LinkBonusToBuff(unit, type, amount, buff)
NewBonusUtils:linkBuff(unit, type, amount, buff, false)
end
function LinkBonusToItem(unit, type, amount, item)
NewBonusUtils:linkItem(unit, type, amount, item)
end
function UnitCopyBonuses(source, target)
NewBonusUtils:copy(source, target)
end
function UnitMirrorBonuses(source, target)
NewBonusUtils:mirror(source, target)
end
-- ---------------------------------------------------------------------------------------------- --
-- System --
-- ---------------------------------------------------------------------------------------------- --
NewBonusUtils = setmetatable({}, {})
local mt = getmetatable(NewBonusUtils)
mt.__index = mt
local array = {}
local key = 0
local items = {}
local k = 0
local timer = CreateTimer()
function mt:destroy(i, item)
if NewBonus_EXTENDED and Damage and Evasion and Critical and SpellPower and LifeSteal and SpellVamp and Tenacity then
if self.type == BONUS_COOLDOWN_REDUCTION then
UnitRemoveCooldownReduction(self.unit, self.amount)
elseif self.type == BONUS_TENACITY then
UnitRemoveTenacity(self.unit, self.amount)
else
AddUnitBonus(self.unit, self.type, -self.amount)
end
else
AddUnitBonus(self.unit, self.type, -self.amount)
end
if item then
items[i] = items[k]
k = k - 1
else
array[i] = array[key]
key = key - 1
if key == 0 then
PauseTimer(timer)
end
end
self = nil
return i - 1
end
function mt:onPeriod()
local i = 1
local this
while i <= key do
this = array[i]
if this.timed then
if this.duration <= 0 then
i = this:destroy(i, false)
end
this.duration = this.duration - PERIOD
else
if GetUnitAbilityLevel(this.unit, this.buff) == 0 then
i = this:destroy(i, false)
end
end
i = i + 1
end
end
function mt:linkItem(unit, type, amount, item)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.item = item
this.amount = AddUnitBonus(unit, type, amount)
this.type = NewBonus.linkType
k = k + 1
items[k] = this
end
function mt:linkTimed(unit, type, amount, duration, timed)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.timed = timed
this.duration = duration
this.amount = AddUnitBonus(unit, type, amount)
this.type = NewBonus.linkType
key = key + 1
array[key] = this
if key == 1 then
TimerStart(timer, PERIOD, true, function() self:onPeriod() end)
end
end
function mt:linkBuff(unit, type, amount, buff, timed)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.timed = timed
this.buff = buff
this.amount = AddUnitBonus(unit, type, amount)
this.type = NewBonus.linkType
key = key + 1
array[key] = this
if key == 1 then
TimerStart(timer, PERIOD, true, function() self:onPeriod() end)
end
end
function mt:copy(source, target)
for i = 1, NewBonus.last do
if GetUnitBonus(source, i) ~= 0 then
AddUnitBonus(target, i, GetUnitBonus(source, i))
end
end
end
function mt:mirror(source, target)
for i = 1, NewBonus.last do
SetUnitBonus(target, i, GetUnitBonus(source, i))
end
end
onInit(function()
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DROP_ITEM, function()
local item = GetManipulatedItem()
local i = 1
local this
while i <= k do
this = items[i]
if this.item == item then
i = this:destroy(i, true)
end
i = i + 1
end
end)
end)
end
--[[ requires Utilities, WorldBounds, Indexer, TimerUtils, RegisterPlayerUnitEvent, optional Tenacity
-- ------------------------------------- Crowd Control v1.0 ------------------------------------- --
-- How to Import:
-- 1 - Copy the Utilities library over to your map and follow its install instructions
-- 2 - Copy the WorldBounds library over to your map and follow its install instructions
-- 3 - Copy the Indexer library over to your map and follow its install instructions
-- 4 - Copy the RegisterPlayerUnitEvent library over to your map and follow its install instructions
-- 5 - Copy the Tenacity library over to your map and follow its install instructions
-- 6 - Copy this library into your map
-- 7 - Copy the 14 buffs and 15 abilities with the CC prefix and match their raw code below.
-- ---------------------------------------- By Chopinski ---------------------------------------- --
]]--
do
-- ---------------------------------------------------------------------------------------------- --
-- Configuration --
-- ---------------------------------------------------------------------------------------------- --
-- The raw code of the silence ability
local SILENCE = FourCC('U000')
-- The raw code of the stun ability
local STUN = FourCC('U001')
-- The raw code of the attack slow ability
local ATTACK_SLOW = FourCC('U002')
-- The raw code of the movement slow ability
local MOVEMENT_SLOW = FourCC('U003')
-- The raw code of the banish ability
local BANISH = FourCC('U004')
-- The raw code of the ensnare ability
local ENSNARE = FourCC('U005')
-- The raw code of the purge ability
local PURGE = FourCC('U006')
-- The raw code of the hex ability
local HEX = FourCC('U007')
-- The raw code of the sleep ability
local SLEEP = FourCC('U008')
-- The raw code of the cyclone ability
local CYCLONE = FourCC('U009')
-- The raw code of the entangle ability
local ENTANGLE = FourCC('U010')
-- The raw code of the disarm ability
local DISARM = FourCC('U011')
-- The raw code of the fear ability
local FEAR = FourCC('U012')
-- The raw code of the taunt ability
local TAUNT = FourCC('U013')
-- The raw code of the true sight ability
local TRUE_SIGHT = FourCC('U014')
-- The raw code of the silence buff
local SILENCE_BUFF = FourCC('BU00')
-- The raw code of the stun buff
local STUN_BUFF = FourCC('BU01')
-- The raw code of the attack slow buff
local ATTACK_SLOW_BUFF = FourCC('BU02')
-- The raw code of the movement slow buff
local MOVEMENT_SLOW_BUFF = FourCC('BU03')
-- The raw code of the banish buff
local BANISH_BUFF = FourCC('BU04')
-- The raw code of the ensnare buff
local ENSNARE_BUFF = FourCC('BU05')
-- The raw code of the purge buff
local PURGE_BUFF = FourCC('BU06')
-- The raw code of the hex buff
local HEX_BUFF = FourCC('BU07')
-- The raw code of the sleep buff
local SLEEP_BUFF = FourCC('BU08')
-- The raw code of the cyclone buff
local CYCLONE_BUFF = FourCC('BU09')
-- The raw code of the entangle buff
local ENTANGLE_BUFF = FourCC('BU10')
-- The raw code of the disarm buff
local DISARM_BUFF = FourCC('BU11')
-- The raw code of the fear buff
local FEAR_BUFF = FourCC('BU12')
-- The raw code of the taunt buff
local TAUNT_BUFF = FourCC('BU13')
-- This is the maximum recursion limit allowed by the system.
-- Its value must be greater than or equal to 0. When equal to 0
-- no recursion is allowed. Values too big can cause screen freezes.
local RECURSION_LIMIT = 8
-- The Crowd Control types
CROWD_CONTROL_SILENCE = 0
CROWD_CONTROL_STUN = 1
CROWD_CONTROL_SLOW = 2
CROWD_CONTROL_SLOW_ATTACK = 3
CROWD_CONTROL_BANISH = 4
CROWD_CONTROL_ENSNARE = 5
CROWD_CONTROL_PURGE = 6
CROWD_CONTROL_HEX = 7
CROWD_CONTROL_SLEEP = 8
CROWD_CONTROL_CYCLONE = 9
CROWD_CONTROL_ENTANGLE = 10
CROWD_CONTROL_DISARM = 11
CROWD_CONTROL_FEAR = 12
CROWD_CONTROL_TAUNT = 13
CROWD_CONTROL_KNOCKBACK = 14
CROWD_CONTROL_KNOCKUP = 15
-- ---------------------------------------------------------------------------------------------- --
-- LUA API --
-- ---------------------------------------------------------------------------------------------- --
-- ------------------------------------------- Disarm ------------------------------------------- --
function DisarmUnit(unit, duration, model, point, stack)
CrowdControl:disarm(unit, duration, model, point, stack)
end
function IsUnitDisarmed(unit)
return CrowdControl:disarmed(unit)
end
-- -------------------------------------------- Fear -------------------------------------------- --
function FearUnit(unit, duration, model, point, stack)
CrowdControl:fear(unit, duration, model, point, stack)
end
function IsUnitFeared(unit)
return CrowdControl:feared(unit)
end
-- -------------------------------------------- Taunt ------------------------------------------- --
function TauntUnit(source, target, duration, model, point, stack)
CrowdControl:taunt(source, target, duration, model, point, stack)
end
function IsUnitTaunted(unit)
return CrowdControl:taunted(unit)
end
-- ------------------------------------------ Knockback ----------------------------------------- --
function KnockbackUnit(unit, angle, distance, duration, model, point, onCliff, onDestructable, onUnit, stack)
CrowdControl:knockback(unit, angle, distance, duration, model, point, onCliff, onDestructable, onUnit, stack)
end
function IsUnitKnockedBack(unit)
return CrowdControl:knockedback(unit)
end
-- ------------------------------------------- Knockup ------------------------------------------ --
function KnockupUnit(unit, height, duration, model, point, stack)
CrowdControl:knockup(unit, height, duration, model, point, stack)
end
function IsUnitKnockedUp(unit)
return CrowdControl:knockedup(unit)
end
-- ------------------------------------------- Silence ------------------------------------------ --
function SilenceUnit(unit, duration, model, point, stack)
CrowdControl:silence(unit, duration, model, point, stack)
end
function IsUnitSilenced(unit)
return CrowdControl:silenced(unit)
end
-- -------------------------------------------- Stun -------------------------------------------- --
function StunUnit(unit, duration, model, point, stack)
CrowdControl:stun(unit, duration, model, point, stack)
end
function IsUnitStunned(unit)
return CrowdControl:stunned(unit)
end
-- ---------------------------------------- Movement Slow --------------------------------------- --
function SlowUnit(unit, amount, duration, model, point, stack)
CrowdControl:slow(unit, amount, duration, model, point, stack)
end
function IsUnitSlowed(unit)
return CrowdControl:slowed(unit)
end
-- ----------------------------------------- Attack Slow ---------------------------------------- --
function SlowUnitAttack(unit, amount, duration, model, point, stack)
CrowdControl:slowAttack(unit, amount, duration, model, point, stack)
end
function IsUnitAttackSlowed(unit)
return CrowdControl:attackSlowed(unit)
end
-- ------------------------------------------- Banish ------------------------------------------- --
function BanishUnit(unit, duration, model, point, stack)
CrowdControl:banish(unit, duration, model, point, stack)
end
function IsUnitBanished(unit)
return CrowdControl:banished(unit)
end
-- ------------------------------------------- Ensnare ------------------------------------------ --
function EnsnareUnit(unit, duration, model, point, stack)
CrowdControl:ensnare(unit, duration, model, point, stack)
end
function IsUnitEnsnared(unit)
return CrowdControl:ensnared(unit)
end
-- -------------------------------------------- Purge ------------------------------------------- --
function PurgeUnit(unit, duration, model, point, stack)
CrowdControl:purge(unit, duration, model, point, stack)
end
function IsUnitPurged(unit)
return CrowdControl:purged(unit)
end
-- --------------------------------------------- Hex -------------------------------------------- --
function HexUnit(unit, duration, model, point, stack)
CrowdControl:hex(unit, duration, model, point, stack)
end
function IsUnitHexed(unit)
return CrowdControl:hexed(unit)
end
-- -------------------------------------------- Sleep ------------------------------------------- --
function SleepUnit(unit, duration, model, point, stack)
CrowdControl:sleep(unit, duration, model, point, stack)
end
function IsUnitSleeping(unit)
return CrowdControl:sleeping(unit)
end
-- ------------------------------------------- Cyclone ------------------------------------------ --
function CycloneUnit(unit, duration, model, point, stack)
CrowdControl:cyclone(unit, duration, model, point, stack)
end
function IsUnitCycloned(unit)
return CrowdControl:cycloned(unit)
end
-- ------------------------------------------ Entangle ------------------------------------------ --
function EntangleUnit(unit, duration, model, point, stack)
CrowdControl:entangle(unit, duration, model, point, stack)
end
function IsUnitEntangled(unit)
return CrowdControl:entangled(unit)
end
-- ------------------------------------------- Dispel ------------------------------------------- --
function UnitDispelCrowdControl(unit, type)
CrowdControl:dispel(unit, type)
end
function UnitDispelAllCrowdControl(unit)
CrowdControl:dispelAll(unit)
end
-- ------------------------------------------- Events ------------------------------------------- --
function RegisterCrowdControlEvent(type, code)
CrowdControl:register(type, code)
end
function RegisterAnyCrowdControlEvent(code)
CrowdControl:register(-1, code)
end
function GetCrowdControlUnit()
return CrowdControl.unit[CrowdControl.key]
end
function GetCrowdControlType()
return CrowdControl.type[CrowdControl.key]
end
function GetCrowdControlDuration()
return CrowdControl.duration[CrowdControl.key]
end
function GetCrowdControlAmount()
return CrowdControl.amount[CrowdControl.key]
end
function GetCrowdControlModel()
return CrowdControl.model[CrowdControl.key]
end
function GetCrowdControlBone()
return CrowdControl.point[CrowdControl.key]
end
function GetCrowdControlStack()
return CrowdControl.stack[CrowdControl.key]
end
function GetCrowdControlRemaining(unit, type)
return CrowdControl:remaining(unit, type)
end
function GetTauntSource()
return CrowdControl.source[CrowdControl.key]
end
function GetKnockbackAngle()
return CrowdControl.angle[CrowdControl.key]
end
function GetKnockbackDistance()
return CrowdControl.distance[CrowdControl.key]
end
function GetKnockupHeight()
return CrowdControl.height[CrowdControl.key]
end
function GetKnockbackOnCliff()
return CrowdControl.cliff[CrowdControl.key]
end
function GetKnockbackOnDestructable()
return CrowdControl.destructable[CrowdControl.key]
end
function GetKnockbackOnUnit()
return CrowdControl.agent[CrowdControl.key]
end
function SetCrowdControlUnit(unit)
CrowdControl.unit[CrowdControl.key] = unit
end
function SetCrowdControlType(type)
if type >= CROWD_CONTROL_SILENCE and type <= CROWD_CONTROL_KNOCKUP then
CrowdControl.type[CrowdControl.key] = type
end
end
function SetCrowdControlDuration(duration)
CrowdControl.duration[CrowdControl.key] = duration
end
function SetCrowdControlAmount(amount)
CrowdControl.amount[CrowdControl.key] = amount
end
function SetCrowdControlModel(model)
CrowdControl.model[CrowdControl.key] = model
end
function SetCrowdControlBone(point)
CrowdControl.point[CrowdControl.key] = point
end
function SetCrowdControlStack(stack)
CrowdControl.stack[CrowdControl.key] = stack
end
function SetTauntSource(unit)
CrowdControl.source[CrowdControl.key] = unit
end
function SetKnockbackAngle(angle)
CrowdControl.angle[CrowdControl.key] = angle
end
function SetKnockbackDistance(distance)
CrowdControl.distance[CrowdControl.key] = distance
end
function SetKnockupHeight(height)
CrowdControl.height[CrowdControl.key] = height
end
function SetKnockbackOnCliff(onCliff)
CrowdControl.cliff[CrowdControl.key] = onCliff
end
function SetKnockbackOnDestructable(onDestructable)
CrowdControl.destructable[CrowdControl.key] = onDestructable
end
function SetKnockbackOnUnit(onUnit)
CrowdControl.agent[CrowdControl.key] = onUnit
end
-- ---------------------------------------------------------------------------------------------- --
-- Systems --
-- ---------------------------------------------------------------------------------------------- --
-- ------------------------------------------ Knockback ----------------------------------------- --
do
Knockback = setmetatable({}, {})
local mt = getmetatable(Knockback)
mt.__index = mt
local timer = CreateTimer()
local rect = Rect(0., 0., 0., 0.)
local period = 0.03125
local array = {}
local struct = {}
local key = 0
function mt:destroy(i)
DestroyGroup(self.group)
DestroyEffect(self.effect)
BlzPauseUnitEx(self.unit, false)
struct[self.unit] = nil
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:knocked(unit)
return struct[unit] ~= nil
end
function mt:apply(target, angle, distance, duration, model, point, cliff, dest, hit)
local this
if duration > 0 and UnitAlive(target) then
if struct[target] then
this = struct[target]
else
this = {}
setmetatable(this, mt)
this.unit = target
this.collision = 2*BlzGetUnitCollisionSize(target)
this.group = CreateGroup()
key = key + 1
array[key] = this
struct[target] = this
BlzPauseUnitEx(target, true)
if model and point then
this.effect = AddSpecialEffectTarget(model, target, point)
end
if key == 1 then
TimerStart(timer, period, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.duration > 0 and UnitAlive(this.unit) then
local x = GetUnitX(this.unit) + this.offset*Cos(this.angle)
local y = GetUnitY(this.unit) + this.offset*Sin(this.angle)
this.duration = this.duration - period
if this.onUnit and this.collision > 0 then
GroupEnumUnitsInRange(this.group, x, y, this.collision, nil)
GroupRemoveUnit(this.group, this.unit)
for j = 0, BlzGroupGetSize(this.group) - 1 do
if UnitAlive(BlzGroupUnitAt(this.group, j)) then
this.duration = 0
break
end
end
end
if this.onDestructable and this.duration > 0 and this.collision > 0 then
SetRect(rect, x - this.collision, y - this.collision, x + this.collision, y + this.collision)
EnumDestructablesInRect(rect, nil, function()
if GetDestructableLife(GetEnumDestructable()) > 0 then
this.duration = 0
return
end
end)
end
if this.onCliff and this.duration > 0 then
if GetTerrainCliffLevel(GetUnitX(this.unit), GetUnitY(this.unit)) < GetTerrainCliffLevel(x, y) and GetUnitZ(this.unit) < (GetTerrainCliffLevel(x, y) - GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY))*bj_CLIFFHEIGHT then
this.duration = 0
end
end
if this.duration > 0 then
SetUnitX(this.unit, x)
SetUnitY(this.unit, y)
end
else
i = this:destroy(i)
end
i = i + 1
end
end)
end
end
this.angle = angle
this.distance = distance
this.duration = duration
this.onCliff = cliff
this.onDestructable = dest
this.onUnit = hit
this.offset = RMaxBJ(0.00000001, distance*period/RMaxBJ(0.00000001, duration))
end
end
end
-- ------------------------------------------- Knockup ------------------------------------------ --
do
Knockup = setmetatable({}, {})
local mt = getmetatable(Knockup)
mt.__index = mt
local knocked = {}
function mt:isUnitKnocked(unit)
return (knocked[unit] or 0) > 0
end
function mt:apply(unit, duration, height, model, point)
if duration > 0 then
local timer = CreateTimer()
local rate = height/duration
local effect
knocked[unit] = (knocked[unit] or 0) + 1
if model and point then
effect = AddSpecialEffect(model, unit, point)
end
if knocked[unit] == 1 then
BlzPauseUnitEx(unit, true)
end
UnitAddAbility(unit, FourCC('Amrf'))
UnitRemoveAbility(unit, FourCC('Amrf'))
SetUnitFlyHeight(unit, (GetUnitDefaultFlyHeight(unit) + height), rate)
TimerStart(timer, duration/2, false, function()
SetUnitFlyHeight(unit, GetUnitDefaultFlyHeight(unit), rate)
TimerStart(timer, duration/2, false, function()
DestroyEffect(effect)
PauseTimer(timer)
DestroyTimer(timer)
knocked[unit] = knocked[unit] - 1
if knocked[unit] == 0 then
BlzPauseUnitEx(unit, false)
end
end)
end)
end
end
end
-- -------------------------------------------- Fear -------------------------------------------- --
do
Fear = setmetatable({}, {})
local mt = getmetatable(Fear)
mt.__index = mt
local timer = CreateTimer()
local array = {}
local struct = {}
local flag = {}
local x = {}
local y = {}
local key = 0
local UPDATE = 0.2
local DIRECTION_CHANGE = 5
local MAX_CHANGE = 200.
local dummy
local ability
function mt:feared(unit)
return GetUnitAbilityLevel(unit, FEAR_BUFF) > 0
end
function mt:destroy(i)
flag[self.target] = true
IssueImmediateOrder(self.target, "stop")
DestroyEffect(self.effect)
array[i] = array[key]
key = key - 1
struct[self.target] = nil
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:apply(target, duration, effect, attach)
local this
if duration > 0 then
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, duration)
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, duration)
IncUnitAbilityLevel(dummy, FEAR)
DecUnitAbilityLevel(dummy, FEAR)
if IssueTargetOrder(dummy, "drunkenhaze", target) then
if struct[target] then
this = struct[target]
else
this = {}
setmetatable(this, mt)
key = key + 1
array[key] = this
struct[target] = this
this.target = target
this.change = 0
if effect and attach then
this.effect = AddSpecialEffectTarget(effect, target, attach)
end
if key == 1 then
TimerStart(timer, UPDATE, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if GetUnitAbilityLevel(this.target, FEAR_BUFF) > 0 then
this.change = this.change + 1
if this.change == DIRECTION_CHANGE then
this.change = 0
flag[this.target] = true
x[this.target] = GetRandomReal(GetUnitX(this.target) - MAX_CHANGE, GetUnitX(this.target) + MAX_CHANGE)
y[this.target] = GetRandomReal(GetUnitY(this.target) - MAX_CHANGE, GetUnitY(this.target) + MAX_CHANGE)
IssuePointOrder(this.target, "move", x[this.target], y[this.target])
end
else
i = this:destroy(i)
end
i = i + 1
end
end)
end
end
flag[target] = true
x[target] = GetRandomReal(GetUnitX(target) - MAX_CHANGE, GetUnitX(target) + MAX_CHANGE)
y[target] = GetRandomReal(GetUnitY(target) - MAX_CHANGE, GetUnitY(target) + MAX_CHANGE)
IssuePointOrder(target, "move", x[target], y[target])
end
end
end
function mt:onOrder()
local unit = GetOrderedUnit()
if self:feared(unit) and GetIssuedOrderId() ~= 851973 then
if not flag[unit] then
flag[unit] = true
IssuePointOrder(unit, "move", x[unit], y[unit])
else
flag[unit] = false
end
end
end
onInit(function()
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function()
Fear:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function()
Fear:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function()
Fear:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function()
Fear:onOrder()
end)
TimerStart(CreateTimer(), 0, false, function()
dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)
UnitAddAbility(dummy, TRUE_SIGHT)
UnitAddAbility(dummy, FEAR)
PauseTimer(GetExpiredTimer())
DestroyTimer(GetExpiredTimer())
ability = BlzGetUnitAbility(dummy, FEAR)
end)
end)
end
-- -------------------------------------------- Taunt ------------------------------------------- --
do
Taunt = setmetatable({}, {})
local mt = getmetatable(Taunt)
mt.__index = mt
Taunt.source = {}
local timer = CreateTimer()
local PERIOD = 0.2
local key = 0
local array = {}
local struct = {}
local dummy
local ability
function mt:taunted(unit)
return GetUnitAbilityLevel(unit, TAUNT_BUFF) > 0
end
function mt:destroy(i)
UnitDispelCrowdControl(self.target, CROWD_CONTROL_TAUNT)
IssueImmediateOrder(self.target, "stop")
DestroyEffect(self.effect)
if self.selected then
SelectUnitAddForPlayer(self.target, GetOwningPlayer(self.target))
end
array[i] = array[key]
key = key - 1
struct[self.target] = nil
Taunt.source[self.target] = nil
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:apply(source, target, duration, model, point)
local this
if duration > 0 and UnitAlive(source) and UnitAlive(target) then
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_NORMAL, 0, duration)
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_DURATION_HERO, 0, duration)
IncUnitAbilityLevel(dummy, TAUNT)
DecUnitAbilityLevel(dummy, TAUNT)
if IssueTargetOrder(dummy, "drunkenhaze", target) then
if struct[target] then
this = struct[target]
else
this = {}
setmetatable(this, mt)
key = key + 1
array[key] = this
struct[target] = this
this.target = target
this.selected = IsUnitSelected(target, GetOwningPlayer(target))
if this.selected then
SelectUnit(target, false)
end
if model and point then
this.effect = AddSpecialEffectTarget(model, target, point)
end
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if GetUnitAbilityLevel(this.target, TAUNT_BUFF) > 0 and UnitAlive(Taunt.source[this.target]) and UnitAlive(this.target) then
if IsUnitVisible(Taunt.source[this.target], GetOwningPlayer(this.target)) then
IssueTargetOrderById(this.target, 851983, Taunt.source[this.target])
else
IssuePointOrderById(this.target, 851986, GetUnitX(Taunt.source[this.target]), GetUnitY(Taunt.source[this.target]))
end
else
i = this:destroy(i)
end
i = i + 1
end
end)
end
end
Taunt.source[target] = source
if IsUnitVisible(source, GetOwningPlayer(target)) then
IssueTargetOrderById(target, 851983, source)
else
IssuePointOrderById(target, 851986, GetUnitX(source), GetUnitY(source))
end
end
end
end
function mt:onOrder()
local unit = GetOrderedUnit()
local order = GetIssuedOrderId()
if self:taunted(unit) and order ~= 851973 then
if order ~= 851983 and order ~= 851986 then
if IsUnitVisible(Taunt.source[unit], GetOwningPlayer(unit)) then
IssueTargetOrderById(unit, 851983, Taunt.source[unit])
else
IssuePointOrderById(unit, 851986, GetUnitX(Taunt.source[unit]), GetUnitY(Taunt.source[unit]))
end
else
if GetOrderTargetUnit() ~= Taunt.source[unit] and GetOrderTargetUnit() ~= nil then
if IsUnitVisible(source[id], GetOwningPlayer(target)) then
IssueTargetOrderById(unit, 851983, Taunt.source[unit])
else
IssuePointOrderById(unit, 851986, GetUnitX(Taunt.source[unit]), GetUnitY(Taunt.source[unit]))
end
end
end
end
end
function mt:onSelect()
local unit = GetTriggerUnit()
if self:taunted(unit) then
if IsUnitSelected(unit, GetOwningPlayer(unit)) then
SelectUnit(unit, false)
end
end
end
onInit(function()
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_ORDER, function()
Taunt:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_POINT_ORDER, function()
Taunt:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_TARGET_ORDER, function()
Taunt:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_ISSUED_UNIT_ORDER, function()
Taunt:onOrder()
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SELECTED, function()
Taunt:onSelect()
end)
TimerStart(CreateTimer(), 0, false, function()
dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)
UnitAddAbility(dummy, TRUE_SIGHT)
UnitAddAbility(dummy, TAUNT)
PauseTimer(GetExpiredTimer())
DestroyTimer(GetExpiredTimer())
ability = BlzGetUnitAbility(dummy, TAUNT)
end)
end)
end
-- ---------------------------------------- Crowd Control --------------------------------------- --
do
CrowdControl = setmetatable({}, {})
local mt = getmetatable(CrowdControl)
mt.__index = mt
CrowdControl.key = 0
CrowdControl.unit = {}
CrowdControl.source = {}
CrowdControl.amount = {}
CrowdControl.duration = {}
CrowdControl.angle = {}
CrowdControl.distance = {}
CrowdControl.height = {}
CrowdControl.model = {}
CrowdControl.point = {}
CrowdControl.stack = {}
CrowdControl.cliff = {}
CrowdControl.destructable = {}
CrowdControl.agent = {}
CrowdControl.type = {}
local trigger = {}
local timer = {}
local event = {}
local ability = {}
local buff = {}
local order = {}
local dummy
local count = 0
onInit(function()
local t = CreateTimer()
TimerStart(t, 0, false, function()
dummy = DummyRetrieve(Player(PLAYER_NEUTRAL_PASSIVE), GetRectCenterX(GetWorldBounds()), GetRectCenterY(GetWorldBounds()), 0, 0)
UnitAddAbility(dummy, SILENCE)
UnitAddAbility(dummy, STUN)
UnitAddAbility(dummy, ATTACK_SLOW)
UnitAddAbility(dummy, MOVEMENT_SLOW)
UnitAddAbility(dummy, BANISH)
UnitAddAbility(dummy, ENSNARE)
UnitAddAbility(dummy, PURGE)
UnitAddAbility(dummy, HEX)
UnitAddAbility(dummy, SLEEP)
UnitAddAbility(dummy, CYCLONE)
UnitAddAbility(dummy, ENTANGLE)
UnitAddAbility(dummy, DISARM)
UnitAddAbility(dummy, TRUE_SIGHT)
BlzUnitDisableAbility(dummy, SILENCE, true, true)
BlzUnitDisableAbility(dummy, STUN, true, true)
BlzUnitDisableAbility(dummy, ATTACK_SLOW, true, true)
BlzUnitDisableAbility(dummy, MOVEMENT_SLOW, true, true)
BlzUnitDisableAbility(dummy, BANISH, true, true)
BlzUnitDisableAbility(dummy, ENSNARE, true, true)
BlzUnitDisableAbility(dummy, PURGE, true, true)
BlzUnitDisableAbility(dummy, HEX, true, true)
BlzUnitDisableAbility(dummy, SLEEP, true, true)
BlzUnitDisableAbility(dummy, CYCLONE, true, true)
BlzUnitDisableAbility(dummy, ENTANGLE, true, true)
BlzUnitDisableAbility(dummy, DISARM, true, true)
ability[CROWD_CONTROL_SILENCE] = SILENCE
ability[CROWD_CONTROL_STUN] = STUN
ability[CROWD_CONTROL_SLOW] = MOVEMENT_SLOW
ability[CROWD_CONTROL_SLOW_ATTACK] = ATTACK_SLOW
ability[CROWD_CONTROL_BANISH] = BANISH
ability[CROWD_CONTROL_ENSNARE] = ENSNARE
ability[CROWD_CONTROL_PURGE] = PURGE
ability[CROWD_CONTROL_HEX] = HEX
ability[CROWD_CONTROL_SLEEP] = SLEEP
ability[CROWD_CONTROL_CYCLONE] = CYCLONE
ability[CROWD_CONTROL_ENTANGLE] = ENTANGLE
ability[CROWD_CONTROL_DISARM] = DISARM
ability[CROWD_CONTROL_FEAR] = FEAR
ability[CROWD_CONTROL_TAUNT] = TAUNT
buff[CROWD_CONTROL_SILENCE] = SILENCE_BUFF
buff[CROWD_CONTROL_STUN] = STUN_BUFF
buff[CROWD_CONTROL_SLOW] = MOVEMENT_SLOW_BUFF
buff[CROWD_CONTROL_SLOW_ATTACK] = ATTACK_SLOW_BUFF
buff[CROWD_CONTROL_BANISH] = BANISH_BUFF
buff[CROWD_CONTROL_ENSNARE] = ENSNARE_BUFF
buff[CROWD_CONTROL_PURGE] = PURGE_BUFF
buff[CROWD_CONTROL_HEX] = HEX_BUFF
buff[CROWD_CONTROL_SLEEP] = SLEEP_BUFF
buff[CROWD_CONTROL_CYCLONE] = CYCLONE_BUFF
buff[CROWD_CONTROL_ENTANGLE] = ENTANGLE_BUFF
buff[CROWD_CONTROL_DISARM] = DISARM_BUFF
buff[CROWD_CONTROL_FEAR] = FEAR_BUFF
buff[CROWD_CONTROL_TAUNT] = TAUNT_BUFF
order[CROWD_CONTROL_SILENCE] = "drunkenhaze"
order[CROWD_CONTROL_STUN] = "thunderbolt"
order[CROWD_CONTROL_SLOW] = "cripple"
order[CROWD_CONTROL_SLOW_ATTACK] = "cripple"
order[CROWD_CONTROL_BANISH] = "banish"
order[CROWD_CONTROL_ENSNARE] = "ensnare"
order[CROWD_CONTROL_PURGE] = "purge"
order[CROWD_CONTROL_HEX] = "hex"
order[CROWD_CONTROL_SLEEP] = "sleep"
order[CROWD_CONTROL_CYCLONE] = "cyclone"
order[CROWD_CONTROL_ENTANGLE] = "entanglingroots"
order[CROWD_CONTROL_DISARM] = "drunkenhaze"
PauseTimer(t)
DestroyTimer(t)
end)
end)
function mt:onEvent(key)
local i = 0
local next = -1
local prev = -2
count = count + 1
if count - CROWD_CONTROL_KNOCKUP < RECURSION_LIMIT then
while CrowdControl.type[key] ~= next or (i - CROWD_CONTROL_KNOCKUP > RECURSION_LIMIT) do
next = CrowdControl.type[key]
if event[next] then
for j = 1, #event[next] do
event[next][j]()
end
end
if CrowdControl.type[key] ~= next then
i = i + 1
else
if next ~= prev then
for j = 1, #trigger do
trigger[j]()
end
if CrowdControl.type[key] ~= next then
i = i + 1
prev = next
end
end
end
end
end
count = count - 1
CrowdControl.key = key
end
function mt:cast(source, target, amount, angle, distance, height, duration, model, point, stack, onCliff, onDestructable, onUnit, type)
if not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) and UnitAlive(target) and duration > 0 then
CrowdControl.key = CrowdControl.key + 1
CrowdControl.unit[CrowdControl.key] = target
CrowdControl.source[CrowdControl.key] = source
CrowdControl.amount[CrowdControl.key] = amount
CrowdControl.angle[CrowdControl.key] = angle
CrowdControl.distance[CrowdControl.key] = distance
CrowdControl.height[CrowdControl.key] = height
CrowdControl.duration[CrowdControl.key] = duration
CrowdControl.model[CrowdControl.key] = model
CrowdControl.point[CrowdControl.key] = point
CrowdControl.stack[CrowdControl.key] = stack
CrowdControl.cliff[CrowdControl.key] = onCliff
CrowdControl.destructable[CrowdControl.key] = onDestructable
CrowdControl.agent[CrowdControl.key] = onUnit
CrowdControl.type[CrowdControl.key] = type
self:onEvent(CrowdControl.key)
if Tenacity then
CrowdControl.duration[CrowdControl.key] = GetTenacityDuration(CrowdControl.unit[CrowdControl.key], CrowdControl.duration[CrowdControl.key])
end
if CrowdControl.duration[CrowdControl.key] > 0 and UnitAlive(CrowdControl.unit[CrowdControl.key]) then
local i = CrowdControl.unit[CrowdControl.key]
local j = CrowdControl.type[CrowdControl.key]
if not timer[i] then timer[i] = {} end
if not timer[i][j] then
timer[i][j] = CreateTimer()
end
if CrowdControl.stack[CrowdControl.key] then
if CrowdControl.type[CrowdControl.key] ~= CROWD_CONTROL_TAUNT then
CrowdControl.duration[CrowdControl.key] = CrowdControl.duration[CrowdControl.key] + TimerGetRemaining(timer[i][j])
else
if Taunt.source[CrowdControl.unit[CrowdControl.key]] == CrowdControl.source[CrowdControl.key] then
CrowdControl.duration[CrowdControl.key] = CrowdControl.duration[CrowdControl.key] + TimerGetRemaining(timer[i][j])
end
end
end
if CrowdControl.type[CrowdControl.key] ~= CROWD_CONTROL_FEAR and CrowdControl.type[CrowdControl.key] ~= CROWD_CONTROL_TAUNT and CrowdControl.type[CrowdControl.key] ~= CROWD_CONTROL_KNOCKBACK and CrowdControl.type[CrowdControl.key] ~= CROWD_CONTROL_KNOCKUP then
local spell = BlzGetUnitAbility(dummy, ability[CrowdControl.type[CrowdControl.key]])
BlzUnitDisableAbility(dummy, ability[CrowdControl.type[CrowdControl.key]], false, false)
BlzSetAbilityRealLevelField(spell, ABILITY_RLF_DURATION_NORMAL, 0, CrowdControl.duration[CrowdControl.key])
BlzSetAbilityRealLevelField(spell, ABILITY_RLF_DURATION_HERO, 0, CrowdControl.duration[CrowdControl.key])
if CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_SLOW then
BlzSetAbilityRealLevelField(spell, ABILITY_RLF_MOVEMENT_SPEED_REDUCTION_PERCENT_CRI1, 0, CrowdControl.amount[CrowdControl.key])
elseif CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_SLOW_ATTACK then
BlzSetAbilityRealLevelField(spell, ABILITY_RLF_ATTACK_SPEED_REDUCTION_PERCENT_CRI2, 0, CrowdControl.amount[CrowdControl.key])
end
IncUnitAbilityLevel(dummy, ability[CrowdControl.type[CrowdControl.key]])
DecUnitAbilityLevel(dummy, ability[CrowdControl.type[CrowdControl.key]])
if IssueTargetOrder(dummy, order[CrowdControl.type[CrowdControl.key]], CrowdControl.unit[CrowdControl.key]) then
UnitRemoveAbility(CrowdControl.unit[CrowdControl.key], buff[CrowdControl.type[CrowdControl.key]])
IssueTargetOrder(dummy, order[CrowdControl.type[CrowdControl.key]], CrowdControl.unit[CrowdControl.key])
TimerStart(timer[i][j], CrowdControl.duration[CrowdControl.key], false, function()
PauseTimer(timer[i][j])
DestroyTimer(timer[i][j])
timer[i][j] = nil
end)
if CrowdControl.model[CrowdControl.key] then
if CrowdControl.point[CrowdControl.key] then
LinkEffectToBuff(CrowdControl.unit[CrowdControl.key], buff[CrowdControl.type[CrowdControl.key]], CrowdControl.model[CrowdControl.key], CrowdControl.point[CrowdControl.key])
else
DestroyEffect(AddSpecialEffect(CrowdControl.model[CrowdControl.key], GetUnitX(CrowdControl.unit[CrowdControl.key]), GetUnitY(CrowdControl.unit[CrowdControl.key])))
end
end
else
PauseTimer(timer[i][j])
DestroyTimer(timer[i][j])
timer[i][j] = nil
end
BlzUnitDisableAbility(dummy, ability[CrowdControl.type[CrowdControl.key]], true, true)
else
if CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_FEAR then
Fear:apply(CrowdControl.unit[CrowdControl.key], CrowdControl.duration[CrowdControl.key], CrowdControl.model[CrowdControl.key], CrowdControl.point[CrowdControl.key])
elseif CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_TAUNT then
Taunt:apply(CrowdControl.source[CrowdControl.key], CrowdControl.unit[CrowdControl.key], CrowdControl.duration[CrowdControl.key], CrowdControl.model[CrowdControl.key], CrowdControl.point[CrowdControl.key])
elseif CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_KNOCKBACK then
Knockback:apply(CrowdControl.unit[CrowdControl.key], CrowdControl.angle[CrowdControl.key], CrowdControl.distance[CrowdControl.key], CrowdControl.duration[CrowdControl.key], CrowdControl.model[CrowdControl.key], CrowdControl.point[CrowdControl.key], CrowdControl.cliff[CrowdControl.key], CrowdControl.destructable[CrowdControl.key], CrowdControl.agent[CrowdControl.key])
elseif CrowdControl.type[CrowdControl.key] == CROWD_CONTROL_KNOCKUP then
Knockup:apply(CrowdControl.unit[CrowdControl.key], CrowdControl.duration[CrowdControl.key], CrowdControl.height[CrowdControl.key], CrowdControl.model[CrowdControl.key], CrowdControl.point[CrowdControl.key])
end
TimerStart(timer[i][j], CrowdControl.duration[CrowdControl.key], false, function()
PauseTimer(timer[i][j])
DestroyTimer(timer[i][j])
timer[i][j] = nil
end)
end
end
if CrowdControl.key > 0 then
CrowdControl.key = CrowdControl.key - 1
end
end
end
function mt:silence(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SILENCE)
end
function mt:silenced(unit)
return GetUnitAbilityLevel(unit, SILENCE_BUFF) > 0
end
function mt:stun(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_STUN)
end
function mt:stunned(unit)
return GetUnitAbilityLevel(unit, STUN_BUFF) > 0
end
function mt:slow(unit, amount, duration, model, point, stack)
self:cast(nil, unit, amount, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLOW)
end
function mt:slowed(unit)
return GetUnitAbilityLevel(unit, MOVEMENT_SLOW_BUFF) > 0
end
function mt:slowAttack(unit, amount, duration, model, point, stack)
self:cast(nil, unit, amount, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLOW_ATTACK)
end
function mt:attackSlowed(unit)
return GetUnitAbilityLevel(unit, ATTACK_SLOW_BUFF) > 0
end
function mt:banish(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_BANISH)
end
function mt:banished(unit)
return GetUnitAbilityLevel(unit, BANISH_BUFF) > 0
end
function mt:ensnare(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_ENSNARE)
end
function mt:ensnared(unit)
return GetUnitAbilityLevel(unit, ENSNARE_BUFF) > 0
end
function mt:purge(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_PURGE)
end
function mt:purged(unit)
return GetUnitAbilityLevel(unit, PURGE_BUFF) > 0
end
function mt:hex(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_HEX)
end
function mt:hexed(unit)
return GetUnitAbilityLevel(unit, HEX_BUFF) > 0
end
function mt:sleep(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_SLEEP)
end
function mt:sleeping(unit)
return GetUnitAbilityLevel(unit, SLEEP_BUFF) > 0
end
function mt:cyclone(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_CYCLONE)
end
function mt:cycloned(unit)
return GetUnitAbilityLevel(unit, CYCLONE_BUFF) > 0
end
function mt:entangle(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_ENTANGLE)
end
function mt:entangled(unit)
return GetUnitAbilityLevel(unit, ENTANGLE_BUFF) > 0
end
function mt:knockback(unit, angle, distance, duration, model, point, onCliff, onDestructable, onUnit, stack)
self:cast(nil, unit, 0, angle, distance, 0, duration, model, point, stack, onCliff, onDestructable, onUnit, CROWD_CONTROL_KNOCKBACK)
end
function mt:knockedback(unit)
return Knockback:knocked(unit)
end
function mt:knockup(unit, height, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, height, duration, model, point, stack, false, false, false, CROWD_CONTROL_KNOCKUP)
end
function mt:knockedup(unit)
return Knockup:isUnitKnocked(unit)
end
function mt:fear(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_FEAR)
end
function mt:feared(unit)
return Fear:feared(unit)
end
function mt:disarm(unit, duration, model, point, stack)
self:cast(nil, unit, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_DISARM)
end
function mt:disarmed(unit)
return GetUnitAbilityLevel(unit, DISARM_BUFF) > 0
end
function mt:taunt(source, target, duration, model, point, stack)
self:cast(source, target, 0, 0, 0, 0, duration, model, point, stack, false, false, false, CROWD_CONTROL_TAUNT)
end
function mt:taunted(unit)
return Taunt:taunted(unit)
end
function mt:dispel(unit, type)
if buff[type] then
UnitRemoveAbility(unit, buff[type])
if timer[unit] then
PauseTimer(timer[unit][type])
DestroyTimer(timer[unit][type])
timer[unit][type] = nil
end
end
end
function mt:dispelAll(unit)
self:dispel(unit, CROWD_CONTROL_SILENCE)
self:dispel(unit, CROWD_CONTROL_STUN)
self:dispel(unit, CROWD_CONTROL_SLOW)
self:dispel(unit, CROWD_CONTROL_SLOW_ATTACK)
self:dispel(unit, CROWD_CONTROL_BANISH)
self:dispel(unit, CROWD_CONTROL_ENSNARE)
self:dispel(unit, CROWD_CONTROL_PURGE)
self:dispel(unit, CROWD_CONTROL_HEX)
self:dispel(unit, CROWD_CONTROL_SLEEP)
self:dispel(unit, CROWD_CONTROL_CYCLONE)
self:dispel(unit, CROWD_CONTROL_ENTANGLE)
self:dispel(unit, CROWD_CONTROL_DISARM)
self:dispel(unit, CROWD_CONTROL_FEAR)
self:dispel(unit, CROWD_CONTROL_TAUNT)
end
function mt:remaining(unit, type)
if not timer[unit] then
return 0
else
return TimerGetRemaining(timer[unit][type])
end
end
function mt:register(id, code)
if type(code) == "function" then
if id >= CROWD_CONTROL_SILENCE and id <= CROWD_CONTROL_KNOCKUP then
if not event[id] then event[id] = {} end
table.insert(event[id], code)
else
table.insert(trigger, code)
end
end
end
end
end
--[[
/* --------------------- DamageInterface v2.4 by Chopinski --------------------- */
Allows for easy registration of specific damage type events like on attack
damage or on spell damage, etc...
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
-- This constant is used to define if the system will cache
-- extra information from a Damage Event, like the unit
-- Custom value (UnitUserData), a unit Handle Id, and more
-- Additionaly you can see the Cache function below
-- to have an idea and comment the members you want cached or not
local CACHE_EXTRA = true
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
Damage = {
source = {
unit,
player,
handle,
isHero,
isMelee,
isRanged,
isStructure,
isMagicImmune,
id,
x,
y,
z
},
target = {
unit,
player,
handle,
isHero,
isMelee,
isRanged,
isStructure,
isMagicImmune,
id,
x,
y,
z
},
damagetype,
attacktype,
isSpell,
isAttack,
isEnemy,
isAlly
}
local after = {}
local before = {}
local damage = {}
local damaging = {}
local trigger = CreateTrigger()
local location = Location(0, 0)
local function GetUnitZ(unit)
MoveLocation(location, GetUnitX(unit), GetUnitY(unit))
return GetUnitFlyHeight(unit) + GetLocationZ(location)
end
local function Cache(source, target, damagetype, attacktype)
Damage.damagetype = damagetype
Damage.attacktype = attacktype
Damage.source.unit = source
Damage.target.unit = target
Damage.isAttack = damagetype == DAMAGE_TYPE_NORMAL
Damage.isSpell = attacktype == ATTACK_TYPE_NORMAL
-- You can comment the members you dont want to be cached
-- or set CACHE_EXTRA = false to not save them at all
if CACHE_EXTRA then
Damage.source.player = GetOwningPlayer(source)
Damage.target.player = GetOwningPlayer(target)
Damage.isEnemy = IsUnitEnemy(target, Damage.source.player)
Damage.isAlly = IsUnitAlly(target, Damage.source.player)
Damage.source.isMelee = IsUnitType(source, UNIT_TYPE_MELEE_ATTACKER)
Damage.source.isRanged = IsUnitType(source, UNIT_TYPE_RANGED_ATTACKER)
Damage.target.isMelee = IsUnitType(target, UNIT_TYPE_MELEE_ATTACKER)
Damage.target.isRanged = IsUnitType(target, UNIT_TYPE_RANGED_ATTACKER)
Damage.source.isHero = IsUnitType(source, UNIT_TYPE_HERO)
Damage.target.isHero = IsUnitType(target, UNIT_TYPE_HERO)
Damage.source.isStructure = IsUnitType(source, UNIT_TYPE_STRUCTURE)
Damage.target.isStructure = IsUnitType(target, UNIT_TYPE_STRUCTURE)
Damage.source.isMagicImmune = IsUnitType(source, UNIT_TYPE_MAGIC_IMMUNE)
Damage.target.isMagicImmune = IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE)
Damage.source.x = GetUnitX(source)
Damage.source.y = GetUnitY(source)
Damage.source.z = GetUnitZ(source)
Damage.target.x = GetUnitX(target)
Damage.target.y = GetUnitY(target)
Damage.target.z = GetUnitZ(target)
Damage.source.id = GetUnitUserData(source)
Damage.target.id = GetUnitUserData(target)
Damage.source.handle = GetHandleId(source)
Damage.target.handle = GetHandleId(target)
end
end
onInit(function()
for i = 1, 7 do
after[i] = {}
before[i] = {}
end
TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_DAMAGING)
TriggerAddCondition(trigger, Filter(function()
if GetTriggerEventId() == EVENT_PLAYER_UNIT_DAMAGING then
Cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())
if Damage.damagetype ~= DAMAGE_TYPE_UNKNOWN then
local i = GetHandleId(Damage.attacktype) + 1
local j = GetHandleId(Damage.damagetype) + 1
if before[i][1] then
for k = 1, #before[i][1] do
before[i][1][k]()
end
end
if before[1][j] then
for k = 1, #before[1][j] do
before[1][j][k]()
end
end
if before[i][j] then
for k = 1, #before[i][j] do
before[i][j][k]()
end
end
for k = 1, #damaging do
damaging[k]()
end
end
end
end))
TriggerRegisterAnyUnitEventBJ(trigger, EVENT_PLAYER_UNIT_DAMAGED)
TriggerAddCondition(trigger, Filter(function()
if GetTriggerEventId() == EVENT_PLAYER_UNIT_DAMAGED then
Cache(GetEventDamageSource(), BlzGetEventDamageTarget(), BlzGetEventDamageType(), BlzGetEventAttackType())
if Damage.damagetype ~= DAMAGE_TYPE_UNKNOWN then
local i = GetHandleId(Damage.attacktype) + 1
local j = GetHandleId(Damage.damagetype) + 1
if after[i][1] then
for k = 1, #after[i][1] do
after[i][1][k]()
end
end
if after[1][j] then
if Damage.isAttack and Evasion then
if not Evasion.evade then
for k = 1, #after[1][j] do
after[1][j][k]()
end
end
else
for k = 1, #after[1][j] do
after[1][j][k]()
end
end
end
if after[i][j] then
for k = 1, #after[i][j] do
after[i][j][k]()
end
end
for k = 1, #damage do
damage[k]()
end
end
end
end))
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function RegisterDamageEvent(attacktype, damagetype, code)
if type(code) == "function" then
local i = GetHandleId(attacktype) + 1
local j = GetHandleId(damagetype) + 1
if not after[i][j] then after[i][j] = {} end
table.insert(after[i][j], code)
end
end
function RegisterAttackDamageEvent(code)
RegisterDamageEvent(nil, DAMAGE_TYPE_NORMAL, code)
end
function RegisterSpellDamageEvent(code)
RegisterDamageEvent(ATTACK_TYPE_NORMAL, nil, code)
end
function RegisterAnyDamageEvent(code)
if type(code) == "function" then
table.insert(damage, code)
end
end
function RegisterDamagingEvent(attacktype, damagetype, code)
if type(code) == "function" then
local i = GetHandleId(attacktype) + 1
local j = GetHandleId(damagetype) + 1
if not before[i][j] then before[i][j] = {} end
table.insert(before[i][j], code)
end
end
function RegisterAttackDamagingEvent(code)
RegisterDamagingEvent(nil, DAMAGE_TYPE_NORMAL, code)
end
function RegisterSpellDamagingEvent(code)
RegisterDamagingEvent(ATTACK_TYPE_NORMAL, nil, code)
end
function RegisterAnyDamagingEvent(code)
if type(code) == "function" then
table.insert(damaging, code)
end
end
end
--[[
/* ------------------------ Evasion v2.4 by Chopinski ----------------------- */
Evasion implements an easy way to register and detect a custom evasion event.
It works by monitoring custom evasion and missing values given to units,
and nulling damage when the odds given occurs.
It will only detect custom evasion, so all evasion or miss values given to a
unit must be done so using the public API provided by this system.
*Evasion requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
The API:
function RegisterEvasionEvent(function YourFunction)
-> YourFunction will run when a unit evades an attack.
function GetMissingUnit takes nothing returns unit
-> Returns the unit missing the attack
function GetEvadingUnit takes nothing returns unit
-> Returns the unit evading the attack
function GetEvadedDamage takes nothing returns real
-> Returns the amount of evaded damage
function GetUnitEvasionChance takes unit u returns real
-> Returns this system amount of evasion chance given to a unit
function GetUnitMissChance takes unit u returns real
-> Returns this system amount of miss chance given to a unit
function SetUnitEvasionChance takes unit u, real chance returns nothing
-> Sets unit evasion chance to specified amount
function SetUnitMissChance takes unit u, real chance returns nothing
-> Sets unit miss chance to specified amount
function UnitAddEvasionChance takes unit u, real chance returns nothing
-> Add to a unit Evasion chance the specified amount
function UnitAddMissChance takes unit u, real chance returns nothing
-> Add to a unit Miss chance the specified amount
function MakeUnitNeverMiss takes unit u, boolean flag returns nothing
-> Will make a unit never miss attacks no matter the evasion chance of the attacked unit
function DoUnitNeverMiss takes unit u returns boolean
-> Returns true if the unit will never miss an attack
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local TEXT_SIZE = 0.016
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
Evasion = {
evasion = {},
miss = {},
neverMiss = {},
source,
target,
damage,
evade
}
local event = {}
local function Text(unit, text, duration, red, green, blue, alpha)
local texttag = CreateTextTag()
SetTextTagText(texttag, text, TEXT_SIZE)
SetTextTagPosUnit(texttag, unit, 0)
SetTextTagColor(texttag, red, green, blue, alpha)
SetTextTagLifespan(texttag, duration)
SetTextTagVelocity(texttag, 0.0, 0.0355)
SetTextTagPermanent(texttag, false)
end
onInit(function()
RegisterAttackDamagingEvent(function()
local damage = GetEventDamage()
Evasion.evade = false
if damage > 0 and not ((Evasion.neverMiss[Damage.source.unit] or 0) > 0) then
Evasion.evade = GetRandomReal(0, 100) <= (Evasion.evasion[Damage.target.unit] or 0) or GetRandomReal(0, 100) <= (Evasion.miss[Damage.source.unit] or 0)
if Evasion.evade then
Evasion.source = Damage.source
Evasion.target = Damage.target
Evasion.damage = damage
for i = 1, #event do
event[i]()
end
BlzSetEventDamage(0)
BlzSetEventWeaponType(WEAPON_TYPE_WHOKNOWS)
Text(Evasion.source.unit, "miss", 1.5, 255, 0, 0, 255)
Evasion.damage = 0
Evasion.source = nil
Evasion.target = nil
end
end
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function RegisterEvasionEvent(code)
if type(code) == "function" then
table.insert(event, code)
end
end
function GetMissingUnit()
return Evasion.source.unit
end
function GetEvadingUnit()
return Evasion.target.unit
end
function GetEvadedDamage()
return Evasion.damage or 0
end
function GetUnitEvasionChance(unit)
return Evasion.evasion[unit] or 0
end
function GetUnitMissChance(unit)
return Evasion.miss[unit] or 0
end
function SetUnitEvasionChance(unit, real)
Evasion.evasion[unit] = real
end
function SetUnitMissChance(unit, real)
Evasion.miss[unit] = real
end
function UnitAddEvasionChance(unit, real)
if not Evasion.evasion[unit] then Evasion.evasion[unit] = 0 end
Evasion.evasion[unit] = Evasion.evasion[unit] + real
end
function UnitAddMissChance(unit, real)
if not Evasion.miss[unit] then Evasion.miss[unit] = 0 end
Evasion.miss[unit] = Evasion.miss[unit] + real
end
function MakeUnitNeverMiss(unit, flag)
if not Evasion.neverMiss[unit] then Evasion.neverMiss[unit] = 0 end
if flag then
Evasion.neverMiss[unit] = Evasion.neverMiss[unit] + 1
else
Evasion.neverMiss[unit] = Evasion.neverMiss[unit] - 1
end
end
function DoUnitNeverMiss(unit)
return Evasion.neverMiss[unit] > 0
end
end
--[[
/* ------------------------ CriticalStrike v2.4 by Chopinski ----------------------- */
CriticalStrike implements an easy way to register and detect a custom critical event.
allows the manipulation of a unit critical strike chance and multiplier, as well as
manipulating the critical damage dealt.
It works by monitoring custom critical strike chance and multiplier values given to units.
It will only detect custom critical strikes, so all critical chance given to a
unit must be done so using the public API provided by this system.
*CriticalStrike requires DamageInterface. Do not use TriggerSleepAction() with Evasion.
It also requires optional Evasion so that this library is written after the Evasion
library, so both custom events will not fire at the same time.
The API:
function RegisterCriticalStrikeEvent(function YourFunction)
-> YourFunction will run when a unit hits a critical strike.
function GetCriticalSource takes nothing returns unit
-> Returns the unit hitting a critical strike.
function GetCriticalTarget takes nothing returns unit
-> Returns the unit being hit by a critical strike.
function GetCriticalDamage takes nothing returns real
-> Returns the critical strike damage amount.
function GetUnitCriticalChance takes unit u returns real
-> Returns the chance to hit a critical strike to specified unit.
function GetUnitCriticalMultiplier takes unit u returns real
-> Returns the chance to hit a critical strike to specified unit.
function SetUnitCriticalChance takes unit u, real value returns nothing
-> Set's the unit chance to hit a critical strike to specified value.
-> 15.0 = 15%
function SetUnitCriticalMultiplier takes unit u, real value returns nothing
-> Set's the unit multiplier of damage when hitting a critical to value
-> 1.0 = increases the multiplier by 1. all units have a multiplier of 1.0
by default, so by adding 1.0, for example, the critical damage will be
2x the normal damage
function SetCriticalEventDamage takes real newValue returns nothing
-> Modify the critical damage dealt to the specified value.
function UnitAddCriticalStrike takes unit u, real chance, real multiplier returns nothing
-> Adds the specified values of chance and multiplier to a unit
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local TEXT_SIZE = 0.016
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
Critical = {
source,
target,
damage,
chance = {},
multiplier = {}
}
local event = {}
local function Text(unit, text, duration, red, green, blue, alpha)
local texttag = CreateTextTag()
SetTextTagText(texttag, text, TEXT_SIZE)
SetTextTagPosUnit(texttag, unit, 0)
SetTextTagColor(texttag, red, green, blue, alpha)
SetTextTagLifespan(texttag, duration)
SetTextTagVelocity(texttag, 0.0, 0.0355)
SetTextTagPermanent(texttag, false)
end
onInit(function()
RegisterAttackDamageEvent(function()
local damage = GetEventDamage()
if damage > 0 and GetRandomReal(0, 100) <= (Critical.chance[Damage.source.unit] or 0) and Damage.isEnemy and not Damage.target.isStructure and (Critical.multiplier[Damage.source.unit] or 0) > 0 then
Critical.source = Damage.source
Critical.target = Damage.target
Critical.damage = damage*(1 + (Critical.multiplier[Damage.source.unit] or 0))
for i = 1, #event do
event[i]()
end
BlzSetEventDamage(Critical.damage)
if Critical.damage > 0 then
Text(Critical.target.unit, (I2S(R2I(Critical.damage)) .. "!"), 1.5, 255, 0, 0, 255)
end
Critical.source = nil
Critical.target = nil
Critical.damage = 0
end
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function RegisterCriticalStrikeEvent(code)
if type(code) == "function" then
table.insert(event, code)
end
end
function GetCriticalSource()
return Critical.source.unit
end
function GetCriticalTarget()
return Critical.target.unit
end
function GetCriticalDamage()
return Critical.damage or 0
end
function GetUnitCriticalChance(unit)
return Critical.chance[unit] or 0
end
function GetUnitCriticalMultiplier(unit)
return Critical.multiplier[unit] or 0
end
function SetUnitCriticalChance(unit, real)
Critical.chance[unit] = real
end
function SetUnitCriticalMultiplier(unit, real)
Critical.multiplier[unit] = real
end
function SetCriticalEventDamage(real)
Critical.damage = real
end
function UnitAddCriticalStrike(unit, chance, multiplier)
if not Critical.chance[unit] then Critical.chance[unit] = 0 end
if not Critical.multiplier[unit] then Critical.multiplier[unit] = 0 end
Critical.chance[unit] = Critical.chance[unit] + chance
Critical.multiplier[unit] = Critical.multiplier[unit] + multiplier
end
end
--[[
/* ------------------------ SpellPower 2.4 by Chopinski ----------------------- */
SpellPower intends to simulate a system similiar to Ability Power from LoL or
Spell Amplification from Dota 2.
Whenever a units deals Spell damage, that dealt damage will be amplified by a flat
and percentile amount that represents a unit spell power bonus.
The formula for amplification is:
final damage = (initial damage + flat bonus) * (1 + percent bonus)
for percent bonus: 0.1 = 10% bonus
*SpellPower requires DamageInterface. Do not use TriggerSleepAction() within triggers.
The API:
function GetUnitSpellPowerFlat takes unit u returns real
-> Returns the Flat bonus of spell power of a unit
function GetUnitSpellPowerPercent takes unit u returns real
-> Returns the Percent bonus of spell power of a unit
function SetUnitSpellPowerFlat takes unit u, real value returns nothing
-> Set the Flat amount of Spell Power of a unit
function SetUnitSpellPowerPercent takes unit u, real value returns nothing
-> Set the Flat amount of Spell Power of a unit
function UnitAddSpellPowerFlat takes unit u, real amount returns nothing
-> Add to the Flat amount of Spell Power of a unit
function UnitAddSpellPowerPercent takes unit u, real amount returns nothing
-> Add to the Percent amount of Spell Power of a unit
function GetSpellDamage takes real amount, unit u returns real
-> Returns the amount of spell damage that would be dealt given an initial damage
]]--
do
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
SpellPower = {
flat = {},
percent = {}
}
onInit(function()
RegisterSpellDamageEvent(function()
local damage = GetEventDamage()
if damage > 0 then
BlzSetEventDamage((damage + (SpellPower.flat[Damage.source.unit] or 0))*(1 + (SpellPower.percent[Damage.source.unit] or 0)))
end
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function GetUnitSpellPowerFlat(unit)
return SpellPower.flat[unit] or 0
end
function GetUnitSpellPowerPercent(unit)
return SpellPower.percent[unit] or 0
end
function SetUnitSpellPowerFlat(unit, real)
SpellPower.flat[unit] = real
end
function SetUnitSpellPowerPercent(unit, real)
SpellPower.percent[unit] = real
end
function UnitAddSpellPowerFlat(unit, real)
if not SpellPower.flat[unit] then SpellPower.flat[unit] = 0 end
SpellPower.flat[unit] = SpellPower.flat[unit] + real
end
function UnitAddSpellPowerPercent(unit, real)
if not SpellPower.percent[unit] then SpellPower.percent[unit] = 0 end
SpellPower.percent[unit] = SpellPower.percent[unit] + real
end
function GetSpellDamage(unit, real)
return (real + (SpellPower.flat[unit] or 0))*(1 + (SpellPower.percent[unit] or 0))
end
end
--[[
/* ------------------------ LifeSteal v2.4 by Chopinski ----------------------- */
LifeSteal intends to simulate the Life Steal system in warcraft, and allow you
to easily change the life steal amount of any unit.
Whenever a unit deals Physical damage, and it has a value of life steal given by
this system, it will heal based of this value and the damage amount.
The formula for life steal is:
heal = damage * life steal
fror life steal: 0.1 = 10%
*LifeSteal requires DamageInterface. Do not use TriggerSleepAction() within triggers.
The API:
function SetUnitLifeSteal takes unit u, real amount returns nothing
-> Set the Life Steal amount for a unit
function GetUnitLifeSteal takes unit u returns real
-> Returns the Life Steal amount of a unit
function UnitAddLifeSteal takes unit u, real amount returns nothing
-> Add to the Life Steal amount of a unit the given amount
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local effect = "Abilities\\Spells\\Undead\\VampiricAura\\VampiricAuraTarget.mdl"
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
LifeSteal = {}
onInit(function()
RegisterAttackDamageEvent(function()
local damage = GetEventDamage()
if damage > 0 and (LifeSteal[Damage.source.unit] or 0) > 0 and not Damage.target.isStructure then
SetWidgetLife(Damage.source.unit, (GetWidgetLife(Damage.source.unit) + (damage * (LifeSteal[Damage.source.unit] or 0))))
DestroyEffect(AddSpecialEffectTarget(effect, Damage.source.unit, "origin"))
end
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function SetUnitLifeSteal(unit, real)
LifeSteal[unit] = real
end
function GetUnitLifeSteal(unit)
return LifeSteal[unit] or 0
end
function UnitAddLifeSteal(unit, real)
if not LifeSteal[unit] then LifeSteal[unit] = 0 end
LifeSteal[unit] = LifeSteal[unit] + real
end
end
--[[
/* ------------------------ SpellVamp v2.4 by Chopinski ----------------------- */
SpellVamp intends to introduce to warcraft a healing based on Spell damage, like
in LoL or Dota 2.
Whenever a unit deals Spell damage, and it has a value of spell vamp given by
this system, it will heal based of this value and the damage amount.
The formula for spell vamp is:
heal = damage * lspell vamp
fror spell vamp: 0.1 = 10%
*SpellVamp requires DamageInterface. Do not use TriggerSleepAction() within triggers.
The API:
function SetUnitSpellVamp takes unit u, real amount returns nothing
-> Set the Spell Vamp amount for a unit
function GetUnitSpellVamp takes unit u returns real
-> Returns the Spell Vamp amount of a unit
function UnitAddSpellVamp takes unit u, real amount returns nothing
-> Add to the Spell Vamp amount of a unit the given amount
]]--
do
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
SpellVamp = {}
onInit(function()
RegisterSpellDamageEvent(function()
local damage = GetEventDamage()
if damage > 0 and (SpellVamp[Damage.source.unit] or 0) > 0 and not Damage.target.isStructure then
SetWidgetLife(Damage.source.unit, (GetWidgetLife(Damage.source.unit) + (damage * (SpellVamp[Damage.source.unit] or 0))))
end
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function SetUnitSpellVamp(unit, real)
SpellVamp[unit] = real
end
function GetUnitSpellVamp(unit)
return SpellVamp[unit] or 0
end
function UnitAddSpellVamp(unit, real)
if not SpellVamp[unit] then SpellVamp[unit] = 0 end
SpellVamp[unit] = SpellVamp[unit] + real
end
end
--[[
/* ------- Utility Library for all the Damage Interface Custom Events ------- */
/* ---------------------------- v2.4 by Chopinski --------------------------- */
The API:
Evasion System:
function UnitAddEvasionChanceTimed takes unit u, real amount, real duration returns nothing
-> Add to a unit Evasion chance the specified amount for a given period
function UnitAddMissChanceTimed takes unit u, real amount, real duration returns nothing
-> Add to a unit Miss chance the specified amount for a given period
Critical Strike System:
function UnitAddCriticalStrikeTimed takes unit u, real chance, real multiplier, real duration returns nothing
-> Adds the specified values of chance and multiplier to a unit for a given period
function UnitAddCriticalChanceTimed takes unit u, real chance, real duration returns nothing
-> Adds the specified values of critical chance to a unit for a given period
function UnitAddCriticalMultiplierTimed takes unit u, real multiplier, real duration returns nothing
-> Adds the specified values of critical multiplier to a unit for a given period
Spell Power System:
function UnitAddSpellPowerFlatTimed takes unit u, real amount, real duration returns nothing
-> Add to the Flat amount of Spell Power of a unit for a given period
function UnitAddSpellPowerPercentTimed takes unit u, real amount, real duration returns nothing
-> Add to the Percent amount of Spell Power of a unit for a given period
function AbilitySpellDamage takes unit u, integer abilityId, abilityreallevelfield field returns string
-> Given an ability field, will return a string that represents the damage that would be dealt
taking into consideration the spell power bonusses of a unit.
function AbilitySpellDamageEx takes real amount, unit u returns string
-> Similar to GetSpellDamage will return the damage that would be dealt but as a string
Life Steal System:
function UnitAddLifeStealTimed takes unit u, real amount, real duration returns nothing
-> Add to the Life Steal amount of a unit the given amount for a given period
Spell Vamp System:
function UnitAddSpellVampTimed takes unit u, real amount, real duration returns nothing
-> Add to the Spell Vamp amount of a unit the given amount for a given period
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local PERIOD = 0.03125000
-- -------------------------------------------------------------------------- --
-- Evasion Utils --
-- -------------------------------------------------------------------------- --
do
EvasionUtils = setmetatable({}, {})
local mt = getmetatable(EvasionUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
if self.type then
UnitAddEvasionChance(self.unit, -self.amount)
else
UnitAddMissChance(self.unit, -self.amount)
end
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, amount, duration, type)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.amount = amount
this.ticks = duration/PERIOD
this.type = type
key = key + 1
array[key] = this
if type then
UnitAddEvasionChance(unit, amount)
else
UnitAddMissChance(unit, amount)
end
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
end
-- -------------------------------------------------------------------------- --
-- Critical Strike Utils --
-- -------------------------------------------------------------------------- --
do
CriticalUtils = setmetatable({}, {})
local mt = getmetatable(CriticalUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
UnitAddCriticalStrike(self.unit, -self.chance, -self.multiplier)
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, chance, multiplier, duration, type)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.chance = chance
this.multiplier = multiplier
this.ticks = duration/PERIOD
key = key + 1
array[key] = this
UnitAddCriticalStrike(unit, chance, multiplier)
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
end
-- -------------------------------------------------------------------------- --
-- Spell Power Utils --
-- -------------------------------------------------------------------------- --
do
SpellPowerUtils = setmetatable({}, {})
local mt = getmetatable(SpellPowerUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
if self.type then
UnitAddSpellPowerFlat(self.unit, -self.amount)
else
UnitAddSpellPowerPercent(self.unit, -self.amount)
end
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, amount, duration, type)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.amount = amount
this.ticks = duration/PERIOD
this.type = type
key = key + 1
array[key] = this
if type then
UnitAddSpellPowerFlat(unit, amount)
else
UnitAddSpellPowerPercent(unit, amount)
end
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
end
-- -------------------------------------------------------------------------- --
-- Life Steal Utils --
-- -------------------------------------------------------------------------- --
do
LifeStealUtils = setmetatable({}, {})
local mt = getmetatable(LifeStealUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
UnitAddLifeSteal(self.unit, -self.amount)
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, amount, duration)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.amount = amount
this.ticks = duration/PERIOD
key = key + 1
array[key] = this
UnitAddLifeSteal(unit, amount)
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
end
-- -------------------------------------------------------------------------- --
-- Spell Vamp Utils --
-- -------------------------------------------------------------------------- --
SpellVampUtils = setmetatable({}, {})
local mt = getmetatable(SpellVampUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
UnitAddSpellVamp(self.unit, -self.amount)
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, amount, duration)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.amount = amount
this.ticks = duration/PERIOD
key = key + 1
array[key] = this
UnitAddSpellVamp(unit, amount)
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function UnitAddEvasionChanceTimed(unit, amount, duration)
EvasionUtils:addTimed(unit, amount, duration, true)
end
function UnitAddMissChanceTimed(unit, amount, duration)
EvasionUtils:addTimed(unit, amount, duration, false)
end
function UnitAddCriticalStrikeTimed(unit, chance, multiplier, duration)
CriticalUtils:addTimed(unit, chance, multiplier, duration, 0)
end
function UnitAddCriticalChanceTimed(unit, chance, duration)
CriticalUtils:addTimed(unit, chance, 0, duration, 1)
end
function UnitAddCriticalMultiplierTimed(unit, multiplier, duration)
CriticalUtils:addTimed(unit, 0, multiplier, duration, 2)
end
function UnitAddSpellPowerFlatTimed(unit, amount, duration)
SpellPowerUtils:addTimed(unit, amount, duration, true)
end
function UnitAddSpellPowerPercentTimed(unit, amount, duration)
SpellPowerUtils:addTimed(unit, amount, duration, false)
end
function AbilitySpellDamage(unit, ability, field)
return I2S(R2I((BlzGetAbilityRealLevelField(BlzGetUnitAbility(unit, ability), field, GetUnitAbilityLevel(unit, ability) - 1) + (SpellPower.flat[unit] or 0)) * (1 + (SpellPower.percent[unit] or 0))))
end
function AbilitySpellDamageEx(real, unit)
return I2S(R2I((real + (SpellPower.flat[unit] or 0)) * (1 + (SpellPower.percent[unit] or 0))))
end
function UnitAddLifeStealTimed(unit, amount, duration)
LifeStealUtils:addTimed(unit, amount, duration)
end
function UnitAddSpellVampTimed(unit, amount, duration)
SpellVampUtils:addTimed(unit, amount, duration)
end
end
--[[ requires RegisterPlayerUnitEvent, Indexer
/* ------------------ Cooldown Reduction v1.9 by Chopinski ------------------ */
Intro
This library intension in to introduce to warcraft an easy way to
manipulate abilities cooldowns based on a cooldown reduction value that
is unique for each unit.
How it Works?
When casting an ability, its "new" cooldown is calculated based on the
amount of cooldown reduction of the casting unit. the formula for
calculation is:
Cooldown = (Default Cooldown - Cooldown Offset) * [(1 - source1)*(1 - source2)*...] * (1 - Cooldown Reduction Flat)
The system also allow negative values for CDR, resulting in increased
ability cooldown.
It does not acumulate because the abilities are registered automatically
on the first cast, saving its base cooldown (Object Editor values) and
always using this base value for calculation, so you can still edit
the ability via the editor and the system takes care of the rest.
How to Import
simply copy the CooldownReduction folder over to your map, and start
use the API functions
Requirements
CooldownReduction requires RegisterPlayerUnitEvent.
Credits to Magtheridon96 for RegisterPlayerUnitEvent.
It also requires patch 1.31+.
RegisterPlayerUnitEvent: www.hiveworkshop.com/threads/snippet-registerplayerunitevent.203338/
]]--
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
-- Use this function to filter out units you dont want to have abilities registered.
-- By default dummy units do not trigger the system.
local function UnitFilter(unit)
return GetUnitAbilityLevel(unit, FourCC('Aloc')) == 0
end
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
do
local units = {}
local abilities = {}
local defaults = {}
local set = {}
CDR = setmetatable({}, {})
local mt = getmetatable(CDR)
mt.__index = mt
function mt:update(unit)
local real
for i = 1, #abilities[unit] do
local k = abilities[unit][i]
local ability = BlzGetUnitAbility(unit, k)
local level = BlzGetAbilityIntegerField(ability, ABILITY_IF_LEVELS)
for j = 1, level do
if (units[unit].count or 0) > 0 then
real = ((defaults[k][j] - units[unit].offset) * units[unit].cooldown * (1 - units[unit].flat))
else
real = ((defaults[k][j] - units[unit].offset) * (1 - units[unit].flat))
end
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_COOLDOWN, j-1, real)
IncUnitAbilityLevel(unit, k)
DecUnitAbilityLevel(unit, k)
end
end
end
function mt:set(unit, real, type)
if not units[unit] then self:create(unit) end
if type == 0 then
units[unit].cooldown = real
elseif type == 1 then
units[unit].flat = real
else
units[unit].offset = real
end
self:update(unit)
end
function mt:get(unit, type)
if not units[unit] then self:create(unit) end
if type == 0 then
return units[unit].cooldown or 0
elseif type == 1 then
return units[unit].flat or 0
else
return units[unit].offset or 0
end
end
function mt:calculate(unit)
local real = 0
if (#units[unit].cdr or 0) > 0 then
for i = 1, #units[unit].cdr do
if i > 1 then
real = real * (1 - units[unit].cdr[i])
else
real = 1 - units[unit].cdr[i]
end
end
end
return real
end
function mt:calculateCooldown(unit, id, level, cooldown)
if not units[unit] then
self:create(unit)
else
local ability = BlzGetUnitAbility(unit, id)
local real
if (#units[unit].cdr or 0) > 0 then
real = ((cooldown - units[unit].offset) * units[unit].cooldown * (1 - units[unit].flat))
else
real = ((cooldown - units[unit].offset) * (1 - units[unit].flat))
end
BlzSetAbilityRealLevelField(ability, ABILITY_RLF_COOLDOWN, level-1, real)
IncUnitAbilityLevel(unit, id)
DecUnitAbilityLevel(unit, id)
end
end
function mt:simulateCooldown(unit, cooldown)
if not units[unit] then
self:create(unit)
return cooldown
else
local real
if (#units[unit].cdr or 0) > 0 then
real = ((cooldown - units[unit].offset) * units[unit].cooldown * (1 - units[unit].flat))
else
real = ((cooldown - units[unit].offset) * (1 - units[unit].flat))
end
return real
end
end
function mt:add(unit, real, type)
if real ~= 0 then
if not units[unit] then self:create(unit) end
if type == 0 then
table.insert(units[unit].cdr, real)
units[unit].count = units[unit].count + 1
units[unit].cooldown = self:calculate(unit)
elseif type == 1 then
units[unit].flat = units[unit].flat + real
else
units[unit].offset = units[unit].offset + real
end
self:update(unit)
end
end
function mt:remove(unit, real)
if real ~= 0 then
if not units[unit] then
self:create(unit)
return
end
for j = 1, #units[unit].cdr do
if units[unit].cdr[j] == real then
table.remove(units[unit].cdr, j)
units[unit].count = units[unit].count - 1
units[unit].cooldown = self:calculate(unit)
break
end
end
self:update(unit)
end
end
function mt:create(unit)
abilities[unit] = {}
set[unit] = {}
units[unit] = {}
units[unit].cdr = {}
units[unit].cooldown = 0
units[unit].offset = 0
units[unit].flat = 0
units[unit].count = 0
end
function mt:destroy(unit)
if abilities[unit] then
for i = 1, #abilities[unit] do
defaults[abilities[unit][i]] = nil
end
end
abilities[unit] = nil
set[unit] = nil
units[unit] = nil
units[unit].cdr = nil
units[unit].cooldown = nil
units[unit].offset = nil
units[unit].flat = nil
units[unit].count = nil
end
function mt:register(unit, id)
if UnitFilter(unit) then
if not units[unit] then self:create(unit) end
if not set[unit][id] then
set[unit][id] = true
table.insert(abilities[unit], id)
if not defaults[id] then defaults[id] = {} end
local ability = BlzGetUnitAbility(unit, id)
local levels = BlzGetAbilityIntegerField(ability, ABILITY_IF_LEVELS)
for i = 1, levels do
defaults[id][i] = BlzGetAbilityRealLevelField(ability, ABILITY_RLF_COOLDOWN, i - 1)
end
if (units[unit].count or 0) > 0 or units[unit].cooldown or units[unit].offset or units[unit].flat then
self:update(unit)
end
end
end
end
onInit(function()
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_SPELL_EFFECT, function()
CDR:register(GetTriggerUnit(), GetSpellAbilityId())
end)
RegisterPlayerUnitEvent(EVENT_PLAYER_HERO_SKILL, function()
CDR:register(GetTriggerUnit(), GetLearnedSkill())
end)
RegisterUnitDeindexEvent(function()
CDR:destroy(GetIndexUnit())
end)
end)
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function GetUnitCooldownReduction(unit)
return 1 - CDR:get(unit, 0)
end
function GetUnitCooldownReductionFlat(unit)
return CDR:get(unit, 1)
end
function GetUnitCooldownOffset(unit)
return CDR:get(unit, 2)
end
function SetUnitCooldownReduction(unit, real)
CDR:set(unit, real, 0)
end
function SetUnitCooldownReductionFlat(unit, real)
CDR:set(unit, real, 1)
end
function SetUnitCooldownOffset(unit, real)
CDR:set(unit, real, 2)
end
function UnitAddCooldownReduction(unit, real)
CDR:add(unit, real, 0)
end
function UnitAddCooldownReductionFlat(unit, real)
CDR:add(unit, real, 1)
end
function UnitAddCooldownOffset(unit, real)
CDR:add(unit, real, 2)
end
function UnitRemoveCooldownReduction(unit, real)
CDR:remove(unit, real)
end
function CalculateAbilityCooldown(unit, ability, level, cooldown)
CDR:calculateCooldown(unit, ability, level, cooldown)
end
function SimulateAbilityCooldown(unit, cooldown)
return CDR:simulateCooldown(unit, cooldown)
end
function RegisterAbility(unit, ability)
CDR:register(unit, ability)
end
function GetAbilityTable()
return units
end
end
--[[
/* --------------- Cooldown Reduction Utils v1.9 by Chopinski --------------- */
Intro
Utility Library that include a few extra functions to deal with
Cooldown Reduction
The API
function UnitAddCooldownReductionTimed takes unit u, real value, real duration returns nothing
-> Add to the amount of cdr of a unit for a given duration. Accepts positive and negative values.
-> It handles removing the bonus automatically
function UnitAddCooldownReductionFlatTimed takes unit u, real value, real duration returns nothing
-> Add to the amount of cdr flat of a unit for a given period. Accepts positive and negative values.
-> It handles removing the bonus automatically
function UnitAddCooldownOffsetTimed takes unit u, real value, real duration returns nothing
-> Add to the amount of cdr offset of a unit for a given period. Accepts positive and negative values.
-> It handles removing the bonus automatically
function GetUnitCooldownReductionEx takes unit u returns string
-> Returns the amount of cdr a unit has as a string factored by 100
-> example of return: 10.50 -> 0.105 internally.
function GetUnitCooldownReductionFlatEx takes unit u returns string
-> Returns the amount of cdr flat a unit has as a string factored by 100
-> example of return: 10.50 -> 0.105 internally.
function GetUnitCooldownOffsetEx takes unit u returns string
-> Returns the amount of cdr offset a unit has as a string
]]--
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
local PERIOD = 0.03125000
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
CDRUtils = setmetatable({}, {})
local mt = getmetatable(CDRUtils)
mt.__index = mt
local array = {}
local key = 0
local timer = CreateTimer()
function mt:remove(i)
if self.type == 0 then
UnitRemoveCooldownReduction(self.unit, self.value)
elseif self.type == 1 then
UnitAddCooldownReductionFlat(self.unit, -self.value)
else
UnitAddCooldownOffset(self.unit, -self.value)
end
array[i] = array[key]
key = key - 1
self = nil
if key == 0 then
PauseTimer(timer)
end
return i - 1
end
function mt:addTimed(unit, value, duration, type)
local this = {}
setmetatable(this, mt)
this.unit = unit
this.value = value
this.type = type
this.ticks = duration/PERIOD
key = key + 1
array[key] = this
if type == 0 then
UnitAddCooldownReduction(unit, value)
elseif type == 1 then
UnitAddCooldownReductionFlat(unit, value)
else
UnitAddCooldownOffset(unit, value)
end
if key == 1 then
TimerStart(timer, PERIOD, true, function()
local i = 1
local this
while i <= key do
this = array[i]
if this.ticks <= 0 then
i = this:remove(i)
end
this.ticks = this.ticks - 1
i = i + 1
end
end)
end
end
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function UnitAddCooldownReductionTimed(unit, value, duration)
CDRUtils:addTimed(unit, value, duration, 0)
end
function UnitAddCooldownReductionFlatTimed(unit, value, duration)
CDRUtils:addTimed(unit, value, duration, 1)
end
function UnitAddCooldownOffsetTimed(unit, value, duration)
CDRUtils:addTimed(unit, value, duration, 2)
end
function GetUnitCooldownReductionEx(unit)
return R2SW(CDR:get(unit, 0)*100, 1, 2)
end
function GetUnitCooldownReductionFlatEx(unit)
return R2SW(CDR:get(unit, 1)*100, 1, 2)
end
function GetUnitCooldownOffsetEx(unit)
return R2SW(CDR:get(u, 2), 1, 2)
end
end
do
local cMap = {}
local aMap = {}
local lastCondFunc
local waitFunc
local oldCond = Condition --If you dont want this Condition-overwrite behavior
--for any particular resource, use Filter() instead of Condition(). This tool
--is mainly for GUI users & the GUI->script compiled behavior uses Condition().
function Condition(func)
lastCondFunc = func
return oldCond(func)
end
local oldTAC = TriggerAddCondition
function TriggerAddCondition(trig, cond)
if lastCondFunc then
cMap[trig] = lastCondFunc --map the condition function to the trigger.
lastCondFunc = nil
cond = Filter(function()
local t = GetTriggeringTrigger()
if cMap[t]() then --Call the triggerconditions manually.
waitFunc = aMap[t]
waitFunc() --If this was caused by an event, call the trigger actions manually.
end
end)
end
return oldTAC(trig, cond) --use the regular event if a boolexpr or Filter
--was used instead of Condition()
end
local oldTAA = TriggerAddAction
function TriggerAddAction(trig, act)
aMap[trig] = act
return oldTAA(trig, function()
waitFunc = aMap[GetTriggeringTrigger()]
waitFunc() --If this was caused by an event, call the trigger actions manually.
end)
end
local oldEval = TriggerEvaluate
function TriggerEvaluate(trig)
local f = cMap[trig]
if f then return f() end
return oldEval(trig)
end
local oldExec = TriggerExecute
function TriggerExecute(trig)
waitFunc = aMap[trig]
waitFunc()
end
function RunTrigger(trig)
local conds = cMap[trig]
if IsTriggerEnabled(trig) and not conds or conds() then
waitFunc = aMap[trig]
waitFunc()
end
end
local skipNext = false
function EnableWaits()
if skipNext then
skipNext = false
return false
end
skipNext = true
coroutine.resume(coroutine.create(function()
waitFunc()
end))
return true
end
end
--[[
/* -------------------- Missile Effect v2.8 by Chopinski -------------------- */
Credits to Forsakn for the first translation of Missile Effect to LUA
]]--
do
MissileEffect = setmetatable({}, {})
local mt = getmetatable(MissileEffect)
mt.__index = mt
function mt:destroy()
local size = #self.array
for i = 1, size do
local this = self.array[i]
DestroyEffect(this.effect)
this = nil
end
DestroyEffect(self.effect)
self = nil
end
function mt:scale(effect, scale)
self.size = scale
BlzSetSpecialEffectScale(effect, scale)
end
function mt:orient(yaw, pitch, roll)
self.yaw = yaw
self.pitch = pitch
self.roll = roll
BlzSetSpecialEffectOrientation(self.effect, yaw, pitch, roll)
for i = 1, #self.array do
local this = self.array[i]
this.yaw = yaw
this.pitch = pitch
this.roll = roll
BlzSetSpecialEffectOrientation(this.effect, yaw, pitch, roll)
end
end
function mt:move(x, y, z)
if not (x > WorldBounds.maxX or x < WorldBounds.minX or y > WorldBounds.maxY or y < WorldBounds.minY) then
BlzSetSpecialEffectPosition(self.effect, x, y, z)
for i = 1, #self.array do
local this = self.array[i]
BlzSetSpecialEffectPosition(this.effect, x - this.x, y - this.y, z - this.z)
end
return true
end
return false
end
function mt:attach(model, dx, dy, dz, scale)
local this = {}
this.x = dx
this.y = dy
this.z = dz
this.yaw = 0
this.pitch = 0
this.roll = 0
this.path = model
this.size = scale
this.effect = AddSpecialEffect(model, dx, dy)
BlzSetSpecialEffectZ(this.effect, dz)
BlzSetSpecialEffectScale(this.effect, scale)
BlzSetSpecialEffectPosition(this.effect, BlzGetLocalSpecialEffectX(this.effect) - dx, BlzGetLocalSpecialEffectY(this.effect) - dy, BlzGetLocalSpecialEffectZ(this.effect) - dz)
table.insert(self.array, this)
return this.effect
end
function mt:detach(effect)
for i = 1, #self.array do
local this = self.array[i]
if this.effect == effect then
table.remove(self.array, i)
DestroyEffect(effect)
this = nil
break
end
end
end
function mt:setColor(red, green, blue)
BlzSetSpecialEffectColor(self.effect, red, green, blue)
end
function mt:timeScale(real)
BlzSetSpecialEffectTimeScale(self.effect, real)
end
function mt:alpha(integer)
BlzSetSpecialEffectAlpha(self.effect, integer)
end
function mt:playerColor(integer)
BlzSetSpecialEffectColorByPlayer(self.effect, Player(integer))
end
function mt:animation(integer)
BlzPlaySpecialEffect(self.effect, ConvertAnimType(integer))
end
function mt:create(x, y, z)
local this = {}
setmetatable(this, mt)
this.path = ""
this.size = 1
this.yaw = 0
this.pitch = 0
this.roll = 0
this.array = {}
this.effect = AddSpecialEffect("", x, y)
BlzSetSpecialEffectZ(this.effect, z)
return this
end
end
--[[ requires Missiles
/* -------------------- Missile Utils v2.8 by Chopinski -------------------- */
// This is a simple Utils library for the Relativistic Missiles system.
/* ----------------------------------- END ---------------------------------- */
]]--
do
-- -------------------------------------------------------------------------- --
-- System --
-- -------------------------------------------------------------------------- --
MissileGroup = setmetatable({}, {})
local mt = getmetatable(MissileGroup)
mt.__index = mt
local collection = {}
local keys = {}
local index = 1
function mt:destroy()
table.insert(keys, self.key)
collection[self.key] = nil
self.group = nil
self.set = nil
self = nil
end
function mt:missileAt(i)
if #self.group > 0 and i <= #self.group - 1 then
return self.group[i + 1]
else
return 0
end
end
function mt:remove(missile)
for i = 1, #self.group do
if self.group[i] == missile then
self.set[missile] = nil
table.remove(self.group, i)
break
end
end
end
function mt:insert(missile)
table.insert(self.group, missile)
self.set[missile] = missile
end
function mt:clear()
local size = #self.group
for i = 1, size do
self.set[i] = nil
self.group[i] = nil
end
end
function mt:contains(missile)
return self.set[missile] ~= nil
end
function mt:addGroup(this)
for i = 1, #this.group do
if not self:contains(this.group[i]) then
self:insert(this.group[i])
end
end
end
function mt:removeGroup(this)
for i = 1, #this.group do
if self:contains(this.group[i]) then
self:remove(this.group[i])
end
end
end
function mt:create()
local this = {}
setmetatable(this, mt)
this.group = {}
this.set = {}
if #keys > 0 then
this.key = keys[#keys]
keys[#keys] = nil
else
this.key = index
index = index + 1
end
collection[this.key] = this
return this
end
-- -------------------------------------------------------------------------- --
-- LUA API --
-- -------------------------------------------------------------------------- --
function CreateMissileGroup()
local group = MissileGroup:create()
return group.key
end
function DestroyMissileGroup(group)
if group ~= 0 then
local this = collection[group]
this:destroy()
end
end
function MissileGroupGetSize(group)
if group ~= 0 then
local this = collection[group]
return #this.group
else
return 0
end
end
function GroupMissileAt(group, position)
if group ~= 0 then
local this = collection[group]
return this:missileAt(position)
else
return 0
end
end
function ClearMissileGroup(group)
if group ~= 0 then
local this = collection[group]
this:clear()
end
end
function IsMissileInGroup(missile, group)
if group ~= 0 and missile ~= 0 then
local this = collection[group]
if #this.group > 0 then
return this:contains(missile)
else
return false
end
else
return false
end
end
function GroupRemoveMissile(group, missile)
if group ~= 0 and missile ~= 0 then
local this = collection[group]
if #this.group > 0 then
this:remove(missile)
end
end
end
function GroupAddMissile(group, missile)
if group ~= 0 and missile ~= 0 then
local this = collection[group]
if not this:contains(missile) then
this:insert(missile)
end
end
end
function GroupPickRandomMissile(group)
if group ~= 0 then
local this = collection[group]
if #this.group > 0 then
return this:missileAt(GetRandomInt(0, #this.group - 1))
else
return 0
end
else
return 0
end
end
function FirstOfMissileGroup(group)
if group ~= 0 then
local this = collection[group]
if #this.group > 0 then
return this.group[1]
else
return 0
end
else
return 0
end
end
function GroupAddMissileGroup(source, destiny)
if source ~= 0 and destiny ~= 0 then
local this = collection[source]
if #this.group > 0 and source ~= destiny then
local group = collection[destiny]
group:addGroup(this)
end
end
end
function GroupRemoveMissileGroup(source, destiny)
if source ~= 0 and destiny ~= 0 then
local this = collection[source]
if source == destiny then
this:clear()
elseif #this.group > 0 then
local group = collection[destiny]
group:removeGroup(this)
end
end
end
function GroupEnumMissilesOfType(group, type)
if group ~= 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
if missile.type == type then
this:insert(missile.key)
end
end
end
end
end
function GroupEnumMissilesOfTypeCounted(group, type, amount)
local i = 0
local j = amount
if group ~= 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
while i <= Missiles.count and j > 0 do
local missile = Missiles.collection[i]
if missile.type == type then
this:insert(missile.key)
end
j = j - 1
i = i + 1
end
end
end
end
function GroupEnumMissilesOfPlayer(group, player)
if group ~= 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
if missile.owner == player then
group:insert(missile.key)
end
end
end
end
end
function GroupEnumMissilesOfPlayerCounted(group, player, amount)
local i = 0
local j = amount
if group ~= 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
while i <= Missiles.count and j > 0 do
local missile = Missiles.collection[i]
if missile.owner == player then
group:insert(missile.key)
end
j = j - 1
i = i + 1
end
end
end
end
function GroupEnumMissilesInRect(group, rect)
if group ~= 0 and rect then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
if GetRectMinX(rect) <= missile.x and missile.x <= GetRectMaxX(rect) and GetRectMinY(rect) <= missile.y and missile.y <= GetRectMaxY(rect) then
this:insert(missile.key)
end
end
end
end
end
function GroupEnumMissilesInRectCounted(group, rect, amount)
local i = 0
local j = amount
if group ~= 0 and rect then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
while i <= Missiles.count and j > 0 do
local missile = Missiles.collection[i]
if GetRectMinX(rect) <= missile.x and missile.x <= GetRectMaxX(rect) and GetRectMinY(rect) <= missile.y and missile.y <= GetRectMaxY(rect) then
this:insert(missile.key)
end
j = j - 1
i = i + 1
end
end
end
end
function GroupEnumMissilesInRangeOfLoc(group, location, radius)
if group ~= 0 and location and radius > 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
local dx = missile.x - GetLocationX(location)
local dy = missile.y - GetLocationY(location)
if SquareRoot(dx*dx + dy*dy) <= radius then
this:insert(missile.key)
end
end
end
end
end
function GroupEnumMissilesInRangeOfLocCounted(group, location, radius, amount)
local i = 0
local j = amount
if group ~= 0 and location and radius > 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
while i <= Missiles.count and j > 0 do
local missile = Missiles.collection[i]
local dx = missile.x - GetLocationX(location)
local dy = missile.y - GetLocationY(location)
if SquareRoot(dx*dx + dy*dy) <= radius then
this:insert(missile.key)
end
j = j - 1
i = i + 1
end
end
end
end
function GroupEnumMissilesInRange(group, x, y, radius)
if group ~= 0 and radius > 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
local dx = missile.x - x
local dy = missile.y - y
if SquareRoot(dx*dx + dy*dy) <= radius then
this:insert(missile.key)
end
end
end
end
end
function GroupEnumMissilesInRangeCounted(group, x, y, radius, amount)
local i = 0
local j = amount
if group ~= 0 and radius > 0 then
if Missiles.count > -1 then
local this = collection[group]
if #this.group > 0 then
this:clear()
end
while i <= Missiles.count and j > 0 do
local missile = Missiles.collection[i]
local dx = missile.x - x
local dy = missile.y - x
if SquareRoot(dx*dx + dy*dy) <= radius then
this:insert(missile.key)
end
j = j - 1
i = i + 1
end
end
end
end
-- -------------------------------------------------------------------------- --
-- GUI API --
-- -------------------------------------------------------------------------- --
function PickedMissile()
udg_Missile = GroupMissileAt(udg_MissileGroup, udg_MissileAt)
end
function MissileDestroyGroup()
DestroyMissileGroup(udg_MissileGroup)
end
function MissilesInRange()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRange(udg_MissileGroup, GetLocationX(udg_MissileGroupLocation), GetLocationY(udg_MissileGroupLocation), udg_MissileGroupRange)
RemoveLocation(udg_MissileGroupLocation)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesInRangeCounted()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRangeCounted(udg_MissileGroup, GetLocationX(udg_MissileGroupLocation), GetLocationY(udg_MissileGroupLocation), udg_MissileGroupRange, udg_MissileGroupCounted)
RemoveLocation(udg_MissileGroupLocation)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesInRangeOfLoc()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRangeOfLoc(udg_MissileGroup, udg_MissileGroupLocation, udg_MissileGroupRange)
RemoveLocation(udg_MissileGroupLocation)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesInRangeOfLocCounted()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRangeOfLocCounted(udg_MissileGroup, udg_MissileGroupLocation, udg_MissileGroupRange, udg_MissileGroupCounted)
RemoveLocation(udg_MissileGroupLocation)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesInRect()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRect(udg_MissileGroup, udg_MissileGroupRect)
RemoveRect(udg_MissileGroupRect)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesInRectCounted()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesInRectCounted(udg_MissileGroup, udg_MissileGroupRect, udg_MissileGroupCounted)
RemoveRect(udg_MissileGroupRect)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesOfPlayer()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesOfPlayer(udg_MissileGroup, udg_MissileGroupPlayer)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesOfPlayerCounted()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesOfPlayerCounted(udg_MissileGroup, udg_MissileGroupPlayer, udg_MissileGroupCounted)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesOfType()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesOfType(udg_MissileGroup, udg_MissileGroupType)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissilesOfTypeCounted()
udg_MissileGroup = CreateMissileGroup()
GroupEnumMissilesOfTypeCounted(udg_MissileGroup, udg_MissileGroupType, udg_MissileGroupCounted)
udg_MissileGroupSize = MissileGroupGetSize(udg_MissileGroup)
end
function MissileGroupClear()
ClearMissileGroup(udg_MissileGroup)
end
function MissileGroupAdd()
GroupAddMissile(udg_MissileGroup, udg_Missile)
end
function MissileGroupRemove()
GroupRemoveMissile(udg_MissileGroup, udg_Missile)
end
function MissileGroupAddGroup()
GroupAddMissileGroup(udg_MissileGroupAddGroup, udg_MissileGroup)
end
function MissileGroupRemoveGroup()
GroupRemoveMissileGroup(udg_MissileGroupRemoveGroup, udg_MissileGroup)
end
end
--[[
----------------------- Missiles v2.8 by Chopinski -----------------------
Thanks and Credits to BPower, Dirac and Vexorian for the Missile Library's at which i based
this Missiles library. Credits and thanks to AGD and for the effect orientation ideas.
This version of Missiles requires patch 1.31+. Thanks to Forsakn for the first translation
of the vJASS version of Missiles into LUA.
How to Import:
1 - Copy this and MissileEffect librarys into your map
--]]
do
-- -------------------------------------------------------------------------- --
-- Configuration --
-- -------------------------------------------------------------------------- --
-- The update period of the system
local PERIOD = 1. / 40.
-- The max amount of Missiles processed in a PERIOD
-- You can play around with both these values to find
-- your sweet spot. If equal to 0, the system will
-- process all missiles at once every period.
local SWEET_SPOT = 250
-- the avarage collision size compensation when detecting collisions
local COLLISION_SIZE = 128.
-- item size used in z collision
local ITEM_SIZE = 16.
-- Raw code of the dummy unit used for vision
local DUMMY = FourCC('dumi')
-- Needed, dont touch. Seriously, dont touch!
local location = Location(0., 0.)
local rect = Rect(0., 0., 0., 0.)
local collection = {}
local function GetLocZ(x, y)
MoveLocation(location, x, y)
return GetLocationZ(location)
end
local function GetUnitZ(unit)
return GetLocZ(GetUnitX(unit), GetUnitY(unit)) + GetUnitFlyHeight(unit)
end
local function SetUnitZ(unit, z)
SetUnitFlyHeight(unit, z - GetLocZ(GetUnitX(unit), GetUnitY(unit)), 0)
end
local function GetMapCliffLevel()
return GetTerrainCliffLevel(WorldBounds.maxX, WorldBounds.maxY)
end
-- -------------------------------------------------------------------------- --
-- Pool --
-- -------------------------------------------------------------------------- --
do
Pool = setmetatable({}, {})
local mt = getmetatable(Pool)
mt.__index = mt
local player = Player(PLAYER_NEUTRAL_PASSIVE)
local group = CreateGroup()
function mt:recycle(unit)
if GetUnitTypeId(unit) == DUMMY then
GroupAddUnit(group, unit)
SetUnitX(unit, WorldBounds.maxX)
SetUnitY(unit, WorldBounds.maxY)
SetUnitOwner(unit, player, false)
PauseUnit(unit, true)
end
end
function mt:retrieve(x, y, z, face)
if BlzGroupGetSize(group) > 0 then
bj_lastCreatedUnit = FirstOfGroup(group)
PauseUnit(bj_lastCreatedUnit, false)
GroupRemoveUnit(group, bj_lastCreatedUnit)
SetUnitX(bj_lastCreatedUnit, x)
SetUnitY(bj_lastCreatedUnit, y)
SetUnitZ(bj_lastCreatedUnit, z)
BlzSetUnitFacingEx(bj_lastCreatedUnit, face)
else
bj_lastCreatedUnit = CreateUnit(player, DUMMY, x, y, face)
SetUnitZ(bj_lastCreatedUnit, z)
UnitRemoveAbility(bj_lastCreatedUnit, FourCC('Amrf'))
end
return bj_lastCreatedUnit
end
function mt:recycleTimed(unit, delay)
if GetUnitTypeId(unit) == DUMMY then
local timer = CreateTimer()
TimerStart(timer, delay, false, function()
self:recycle(unit)
PauseTimer(timer)
DestroyTimer(timer)
end)
end
end
onInit(function()
local timer = CreateTimer()
TimerStart(timer, 0, false, function()
for i = 0, SWEET_SPOT do
local unit = CreateUnit(player, DUMMY, WorldBounds.maxX, WorldBounds.maxY, 0)
PauseUnit(unit, false)
GroupAddUnit(group, unit)
UnitRemoveAbility(unit, FourCC('Amrf'))
end
PauseTimer(timer)
DestroyTimer(timer)
end)
end)
end
-- -------------------------------------------------------------------------- --
-- Coordinates --
-- -------------------------------------------------------------------------- --
do
Coordinates = setmetatable({}, {})
local mt = getmetatable(Coordinates)
mt.__index = mt
function mt:destroy()
self = nil
end
function mt:math(a, b)
local dx
local dy
while true do
dx = b.x - a.x
dy = b.y - a.y
dx = dx * dx + dy * dy
dy = SquareRoot(dx)
if dx ~= 0. and dy ~= 0. then
break
end
b.x = b.x + .01
b.z = b.z - GetLocZ(b.x - .01, b.y) + GetLocZ(b.x, b.y)
end
a.square = dx
a.distance = dy
a.angle = Atan2(b.y - a.y, b.x - a.x)
a.slope = (b.z - a.z) / dy
a.alpha = Atan(a.slope)
-- Set b.
if b.ref == a then
b.angle = a.angle + bj_PI
b.distance = dy
b.slope = -a.slope
b.alpha = -a.alpha
b.square = dx
end
end
function mt:link(a, b)
a.ref = b
b.ref = a
self:math(a, b)
end
function mt:move(toX, toY, toZ)
self.x = toX
self.y = toY
self.z = toZ + GetLocZ(toX, toY)
if self.ref ~= self then
self:math(self, self.ref)
end
end
function mt:create(x, y, z)
local c = {}
setmetatable(c, mt)
c.ref = c
c:move(x, y, z)
return c
end
end
-- -------------------------------------------------------------------------- --
-- Missiles --
-- -------------------------------------------------------------------------- --
Missiles = setmetatable({}, {})
local mt = getmetatable(Missiles)
mt.__index = mt
Missiles.collection = {}
Missiles.count = -1
local timer = CreateTimer()
local group = CreateGroup()
local id = -1
local pid = -1
local last = 0
local dilation = 1
local array = {}
local missiles = {}
local frozen = {}
local keys = {}
local index = 1
local yaw = 0
local pitch = 0
local travelled = 0
function mt:OnHit()
self:setup()
if self.onHit then
udg_MissileEvent = udg_MissileOnHit
if self.allocated and udg_MissileCollision > 0 then
GroupEnumUnitsInRange(group, self.x, self.y, udg_MissileCollision + COLLISION_SIZE, nil)
local unit = FirstOfGroup(group)
while unit do
if array[self][unit] == nil then
if IsUnitInRangeXY(unit, self.x, self.y, udg_MissileCollision) then
if udg_MissileCollideZ then
local dx = GetLocZ(GetUnitX(unit), GetUnitY(unit)) + GetUnitFlyHeight(unit)
local dy = BlzGetUnitCollisionSize(unit)
if dx + dy >= self.z - udg_MissileCollision and dx <= self.z + udg_MissileCollision then
array[self][unit] = true
udg_MissileHitUnit = unit
if self.allocated and IsTriggerEnabled(self.onHit) then
udg_Missile = self.key
if TriggerEvaluate(self.onHit) then
TriggerExecute(self.onHit)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitUnit = nil
self:terminate()
break
end
end
end
udg_MissileHitUnit = nil
end
else
array[self][unit] = true
udg_MissileHitUnit = unit
if self.allocated and IsTriggerEnabled(self.onHit) then
udg_Missile = self.key
if TriggerEvaluate(self.onHit) then
TriggerExecute(self.onHit)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitUnit = nil
self:terminate()
break
end
end
end
udg_MissileHitUnit = nil
end
end
end
GroupRemoveUnit(group, unit)
unit = FirstOfGroup(group)
end
end
end
end
function mt:OnMissile()
if self.onMissile then
udg_MissileEvent = udg_MissileOnMissile
if self.allocated and udg_MissileCollision > 0 then
for i = 0, Missiles.count do
local missile = Missiles.collection[i]
if missile ~= self then
if array[self][missile] == nil then
local dx = missile.x - self.x
local dy = missile.y - self.y
if SquareRoot(dx*dx + dy*dy) <= udg_MissileCollision then
array[self][missile] = true
udg_MissileHitMissile = missile.key
if self.allocated and IsTriggerEnabled(self.onMissile) then
udg_Missile = self.key
if TriggerEvaluate(self.onMissile) then
TriggerExecute(self.onMissile)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitMissile = 0
self:terminate()
break
end
end
end
udg_MissileHitMissile = 0
end
end
end
end
end
end
end
function mt:OnDestructable()
if self.onDestructable then
udg_MissileEvent = udg_MissileOnDestructable
if self.allocated and udg_MissileCollision > 0 then
local dx = udg_MissileCollision
SetRect(rect, self.x - dx, self.y - dx, self.x + dx, self.y + dx)
EnumDestructablesInRect(rect, nil, function()
local destructable = GetEnumDestructable()
if array[self][destructable] == nil then
if udg_MissileCollideZ then
local dz = GetLocZ(GetWidgetX(destructable), GetWidgetY(destructable))
local tz = GetDestructableOccluderHeight(destructable)
if dz + tz >= self.z - udg_MissileCollision and dz <= self.z + udg_MissileCollision then
array[self][destructable] = true
udg_MissileHitDestructable = destructable
if self.allocated and IsTriggerEnabled(self.onDestructable) then
udg_Missile = self.key
if TriggerEvaluate(self.onDestructable) then
TriggerExecute(self.onDestructable)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitDestructable = nil
self:terminate()
return
end
end
end
udg_MissileHitDestructable = nil
end
else
array[self][destructable] = true
udg_MissileHitDestructable = destructable
if self.allocated and IsTriggerEnabled(self.onDestructable) then
udg_Missile = self.key
if TriggerEvaluate(self.onDestructable) then
TriggerExecute(self.onDestructable)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitDestructable = nil
self:terminate()
return
end
end
end
udg_MissileHitDestructable = nil
end
end
end)
end
end
end
function mt:OnItem()
if self.onItem then
udg_MissileEvent = udg_MissileOnItem
if self.allocated and udg_MissileCollision > 0 then
local dx = udg_MissileCollision
SetRect(rect, self.x - dx, self.y - dx, self.x + dx, self.y + dx)
EnumItemsInRect(rect, nil, function()
local item = GetEnumItem()
if array[self][item] == nil then
if udg_MissileCollideZ then
local dz = GetLocZ(GetItemX(item), GetItemY(item))
if dz + ITEM_SIZE >= self.z - udg_MissileCollision and dz <= self.z + udg_MissileCollision then
array[self][item] = true
udg_MissileHitItem = item
if self.allocated and IsTriggerEnabled(self.onItem) then
udg_Missile = self.key
if TriggerEvaluate(self.onItem) then
TriggerExecute(self.onItem)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitItem = nil
self:terminate()
return
end
end
end
udg_MissileHitItem = nil
end
else
array[self][item] = true
udg_MissileHitItem = item
if self.allocated and IsTriggerEnabled(self.onItem) then
udg_Missile = self.key
if TriggerEvaluate(self.onItem) then
TriggerExecute(self.onItem)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
udg_MissileHitItem = nil
self:terminate()
return
end
end
end
udg_MissileHitItem = nil
end
end
end)
end
end
end
function mt:OnCliff()
if self.onCliff then
local dx = GetTerrainCliffLevel(self.nextX, self.nextY)
local dy = GetTerrainCliffLevel(self.x, self.y)
udg_MissileEvent = udg_MissileOnCliff
if dy < dx and self.z < (dx - GetMapCliffLevel())*bj_CLIFFHEIGHT then
if self.allocated and IsTriggerEnabled(self.onCliff) then
udg_Missile = self.key
if TriggerEvaluate(self.onCliff) then
TriggerExecute(self.onCliff)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
end
end
function mt:OnTerrain()
if self.onTerrain then
udg_MissileEvent = udg_MissileOnTerrain
if GetLocZ(self.x, self.y) > self.z then
if self.allocated and IsTriggerEnabled(self.onTerrain) then
udg_Missile = self.key
if TriggerEvaluate(self.onTerrain) then
TriggerExecute(self.onTerrain)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
end
end
function mt:OnTileset()
if self.onTileset then
udg_MissileTileset = GetTerrainType(self.x, self.y)
udg_MissileEvent = udg_MissileOnTileset
if udg_MissileTileset ~= self.tileset then
if self.allocated and IsTriggerEnabled(self.onTileset) then
udg_Missile = self.key
if TriggerEvaluate(self.onTileset) then
TriggerExecute(self.onTileset)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
self.tileset = udg_MissileTileset
udg_MissileTileset = 0
end
end
function mt:OnPeriod()
if self.onPeriod then
udg_MissileEvent = udg_MissileOnPeriod
if self.allocated and IsTriggerEnabled(self.onPeriod) then
udg_Missile = self.key
if TriggerEvaluate(self.onPeriod) then
TriggerExecute(self.onPeriod)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
end
function mt:OnOrient()
local a
-- Homing or not
if udg_MissileTarget ~= nil and GetUnitTypeId(udg_MissileTarget) ~= 0 then
self.impact:move(GetUnitX(udg_MissileTarget), GetUnitY(udg_MissileTarget), GetUnitFlyHeight(udg_MissileTarget) + self.toZ)
local dx = self.impact.x - self.nextX
local dy = self.impact.y - self.nextY
a = Atan2(dy, dx)
self.travel = self.origin.distance - SquareRoot(dx*dx + dy*dy)
else
a = self.origin.angle
self.target = nil
udg_MissileTarget = nil
end
-- turn rate
if self.turn ~= 0 and not (Cos(self.cA - a) >= Cos(self.turn)) then
if Sin(a - self.cA) >= 0 then
self.cA = self.cA + self.turn
else
self.cA = self.cA - self.turn
end
else
self.cA = a
end
local vel = self.veloc*dilation
yaw = self.cA
travelled = self.travel + vel
self.veloc = self.veloc + self.acceleration
self.travel = travelled
pitch = self.origin.alpha
self.prevX = self.x
self.prevY = self.y
self.prevZ = self.z
self.x = self.nextX
self.y = self.nextY
self.z = self.nextZ
self.nextX = self.x + vel*Cos(yaw)
self.nextY = self.y + vel*Sin(yaw)
udg_MissileVelocity = self.veloc
udg_MissileTravelled = travelled
udg_MissileSpeed = self.veloc/PERIOD
-- arc calculation
local s = travelled
local d = self.origin.distance
local h = self.height
if h ~= 0 or self.origin.slope ~= 0 then
self.nextZ = 4*h*s*(d-s)/(d*d) + self.origin.slope*s + self.origin.z
pitch = pitch - Atan(((4*h)*(2*s - d))/(d*d))
end
-- curve calculation
local c = self.open
if c ~= 0 then
local dx = 4 * c * s * (d - s) / (d * d)
a = yaw + bj_PI / 2
self.x = self.x + dx * Cos(a)
self.y = self.y + dx * Sin(a)
yaw = yaw + Atan(-((4 * c) * (2 * s - d)) / (d * d))
end
end
function mt:OnFinish()
if travelled >= self.origin.distance - 0.0001 then
self.finished = true
if self.onFinish then
udg_MissileEvent = udg_MissileOnFinish
if self.allocated and IsTriggerEnabled(self.onFinish) then
udg_Missile = self.key
if TriggerEvaluate(self.onFinish) then
TriggerExecute(self.onFinish)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
else
if self.travel > 0 and not self.paused then
self:terminate()
end
end
else
if self.travel > 0 then
self:terminate()
end
end
else
if self.travel > 0 then
self:terminate()
end
end
else
self:terminate()
end
else
if not udg_MissileRoll then
self.effect:orient(yaw, -pitch, 0)
else
self.effect:orient(yaw, -pitch, Atan2(self.open, self.height))
end
end
end
function mt:OnBoundaries()
if not self.effect:move(self.x, self.y, self.z) then
if self.onBoundaries then
udg_MissileEvent = udg_MissileOnBoundaries
if self.allocated and IsTriggerEnabled(self.onBoundaries) then
udg_Missile = self.key
if TriggerEvaluate(self.onBoundaries) then
TriggerExecute(self.onBoundaries)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
else
if self.dummy then
SetUnitX(self.dummy, self.x)
SetUnitY(self.dummy, self.y)
end
end
self:cleanup()
end
function mt:OnRemove()
local this
if self.allocated and self.launched then
self.allocated = false
if self.pkey ~= -1 then
this = frozen[pid]
this.pkey = self.pkey
frozen[self.pkey] = frozen[pid]
pid = pid - 1
self.pkey = -1
end
if self.onRemove then
udg_MissileEvent = udg_MissileOnRemove
if IsTriggerEnabled(self.onRemove) then
udg_Missile = self.key
if TriggerEvaluate(self.onRemove) then
TriggerExecute(self.onRemove)
self:update()
end
end
end
if self.dummy then
Pool:recycle(self.dummy)
end
this = Missiles.collection[Missiles.count]
this.index = self.index
Missiles.collection[self.index] = Missiles.collection[Missiles.count]
Missiles.count = Missiles.count - 1
self.index = -1
self.onHit = nil
self.onMissile = nil
self.onDestructable = nil
self.onItem = nil
self.onCliff = nil
self.onTerrain = nil
self.onTileset = nil
self.onFinish = nil
self.onBoundaries = nil
self.onPause = nil
self.onResume = nil
self.onRemove = nil
self.origin:destroy()
self.impact:destroy()
self.effect:destroy()
self:reset()
array[self] = nil
end
end
function mt:OnResume(flag)
local this
self.paused = flag
udg_MissilePaused = flag
if not self.paused and self.pkey ~= -1 then
id = id + 1
missiles[id] = self
this = frozen[pid]
this.pkey = self.pkey
frozen[self.pkey] = frozen[pid]
pid = pid - 1
self.pkey = -1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
dilation = (id + 1)/SWEET_SPOT
else
dilation = 1.
end
if id == 0 then
TimerStart(timer, PERIOD, true, function() Missiles:move() end)
end
if self.onResume then
udg_MissileEvent = udg_MissileOnResume
if self.allocated and IsTriggerEnabled(self.onResume) then
self:setup()
if TriggerEvaluate(self.onResume) then
TriggerExecute(self.onResume)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
else
if self.finished then
self:terminate()
end
end
end
end
else
if self.finished then
self:terminate()
end
end
end
end
function mt:OnPause()
pid = pid + 1
self.pkey = pid
frozen[pid] = self
if self.onPause then
udg_MissileEvent = udg_MissileOnPause
if self.allocated and IsTriggerEnabled(self.onPause) then
self:setup()
if TriggerEvaluate(self.onPause) then
TriggerExecute(self.onPause)
self:update()
if udg_MissileDestroy then
udg_MissileDestroy = false
self:terminate()
end
end
end
end
end
-------------------------- Model of the missile --------------------------
function mt:model(effect)
DestroyEffect(self.effect.effect)
self.effect.path = effect
self.Model = effect
self.effect.effect = AddSpecialEffect(effect, self.origin.x, self.origin.y)
BlzSetSpecialEffectZ(self.effect.effect, self.origin.z)
BlzSetSpecialEffectYaw(self.effect.effect, self.cA)
end
----------------------------- Curved movement ----------------------------
function mt:curve(value)
self.open = Tan(value * bj_DEGTORAD) * self.origin.distance
self.Curve = value --Atan(self.open/self.origin.distance)*bj_RADTODEG
end
----------------------------- Arced Movement -----------------------------
function mt:arc(value)
self.height = Tan(value * bj_DEGTORAD) * self.origin.distance / 4
self.Arc = value --Atan(4*self.height/self.origin.distance)*bj_RADTODEG
end
------------------------------ Effect scale ------------------------------
function mt:scale(value)
self.effect.size = value
self.effect:scale(self.effect.effect, value)
self.Scale = value
end
------------------------------ Missile Speed -----------------------------
function mt:speed(value)
self.veloc = value * PERIOD
self.Speed = value
local vel = self.veloc*dilation
local s = self.travel + vel
local d = self.origin.distance
self.nextX = self.x + vel*Cos(self.cA)
self.nextY = self.y + vel*Sin(self.cA)
if self.height ~= 0 or self.origin.slope ~= 0 then
self.nextZ = 4*self.height*s*(d-s)/(d*d) + self.origin.slope*s + self.origin.z
self.z = self.nextZ
end
end
------------------------------- Flight Time ------------------------------
function mt:duration(value)
self.veloc = RMaxBJ(0.00000001, (self.origin.distance - self.travel) * PERIOD / RMaxBJ(0.00000001, value))
self.Duration = value
local vel = self.veloc*dilation
local s = self.travel + vel
local d = self.origin.distance
self.nextX = self.x + vel*Cos(self.cA)
self.nextY = self.y + vel*Sin(self.cA)
if self.height ~= 0 or self.origin.slope ~= 0 then
self.nextZ = 4*self.height*s*(d-s)/(d*d) + self.origin.slope*s + self.origin.z
self.z = self.nextZ
end
end
-- ------------------------------- Sight Range ------------------------------ --
function mt:vision(sightRange)
self.Vision = sightRange
if self.dummy then
SetUnitOwner(self.dummy, self.owner, false)
BlzSetUnitRealField(self.dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
else
if not self.owner then
if self.source then
self.dummy = Pool:retrieve(self.x, self.y, self.z, 0)
SetUnitOwner(self.dummy, GetOwningPlayer(self.source), false)
BlzSetUnitRealField(self.dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
end
else
self.dummy = Pool:retrieve(self.x, self.y, self.z, 0)
SetUnitOwner(self.dummy, self.owner, false)
BlzSetUnitRealField(self.dummy, UNIT_RF_SIGHT_RADIUS, sightRange)
end
end
end
-- ------------------------------- Time Scale ------------------------------- --
function mt:timeScale(real)
self.TimeScale = real
self.effect:timeScale(real)
end
-- ---------------------------------- Alpha --------------------------------- --
function mt:alpha(integer)
self.Alpha = integer
self.effect:alpha(integer)
end
-- ------------------------------ Player Color ------------------------------ --
function mt:playerColor(integer)
self.playercolor = integer
self.effect:playerColor(integer)
end
-- -------------------------------- Animation ------------------------------- --
function mt:animation(integer)
self.Animation = integer
self.effect:animation(integer)
end
---------------------------- Bound and Deflect ---------------------------
function mt:bounce()
self.origin:move(self.x, self.y, self.z - GetLocZ(self.x, self.y))
travelled = 0
self.travel = 0
self.finished = false
end
function mt:deflect(tx, ty, tz)
local locZ = GetLocZ(self.x, self.y)
if self.z < locZ and self.onTerrain then
self.nextX = self.prevX
self.nextY = self.prevY
self.nextZ = self.prevZ
end
self.toZ = tz
self.target = nil
udg_MissileTarget = nil
self.impact:move(tx, ty, tz)
self.origin:move(self.x, self.y, self.z - locZ)
travelled = 0
self.travel = 0
self.finished = false
end
function mt:deflectTarget(unit)
self:deflect(GetUnitX(unit), GetUnitY(unit), self.toZ)
self.target = unit
udg_MissileTarget = unit
end
---------------------------- Flush hit targets ---------------------------
function mt:flushAll()
array[self] = nil
array[self] = {}
end
function mt:flush(widget)
if widget then
array[self][widget] = nil
end
end
function mt:hitted(widget)
return array[self][widget]
end
-- ----------------------- Missile attachment methods ----------------------- --
function mt:attach(model, dx, dy, dz, scale)
return self.effect:attach(model, dx, dy, dz, scale)
end
function mt:detach(effect)
if effect then
self.effect:detach(effect)
end
end
-- ------------------------------ Missile Pause ----------------------------- --
function mt:pause(flag)
self:OnResume(flag)
end
-- ---------------------------------- Color --------------------------------- --
function mt:color(red, green, blue)
self.effect:setColor(red, green, blue)
end
-- ------------------------------ Reset members ----------------------------- --
function mt:reset()
self.launched = false
self.finished = false
self.collideZ = false
self.paused = false
self.roll = false
self.source = nil
self.target = nil
self.owner = nil
self.dummy = nil
self.open = 0.
self.height = 0.
self.veloc = 0.
self.acceleration = 0.
self.collision = 0.
self.damage = 0.
self.travel = 0.
self.turn = 0.
self.data = 0.
self.type = 0
self.tileset = 0
self.pkey = -1
self.index = -1
self.Model = ""
self.Duration = 0
self.Scale = 1
self.Speed = 0
self.Arc = 0
self.Curve = 0
self.Vision = 0
self.TimeScale = 0.
self.Alpha = 0
self.playercolor = 0
self.Animation = 0
end
-------------------------------- Terminate -------------------------------
function mt:terminate()
self:OnRemove()
end
-------------------------- Destroys the missile --------------------------
function mt:remove(i)
if self.paused then
self:OnPause()
else
self:OnRemove()
end
missiles[i] = missiles[id]
id = id - 1
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
dilation = (id + 1) / SWEET_SPOT
else
dilation = 1
end
if id == -1 then
PauseTimer(timer)
end
if not self.allocated then
table.insert(keys, self.key)
collection[self.key] = nil
self = nil
end
return i - 1
end
-- -------------------------------- GUI Setup ------------------------------- --
function mt:setup()
udg_Missile = self.key
udg_MissileStart = Location(self.origin.x, self.origin.y)
udg_MissileStartZ = self.origin.z
udg_MissileFinish = Location(self.impact.x, self.impact.y)
udg_MissileFinishZ = self.impact.z
udg_MissileTarget = self.target
udg_MissileSource = self.source
udg_MissileOwner = self.owner
udg_MissileRoll = self.roll
udg_MissilePaused = self.paused
udg_MissileType = self.type
udg_MissileData = self.data
udg_MissileModel = self.Model
udg_MissileScale = self.Scale
udg_MissileTimeScale = self.TimeScale
udg_MissileAlpha = self.Alpha
udg_MissilePlayerColor = self.playercolor
udg_MissileAnimation = self.Animation
udg_MissileDamage = self.damage
udg_MissileCollision = self.collision
udg_MissileCollideZ = self.collideZ
udg_MissileVelocity = self.veloc
udg_MissileTravelled = self.travel
udg_MissileSpeed = self.Speed
udg_MissileDuration = self.Duration
udg_MissileArc = self.Arc
udg_MissileCurve = self.Curve
udg_MissileVision = self.Vision
udg_MissileAcceleration = self.acceleration
udg_MissilePosition = Location(self.x, self.y)
udg_MissileZ = self.z
udg_MissileLastPosition = Location(self.prevX, self.prevY)
udg_MissilePrevZ = self.prevZ
udg_MissileNextPosition = Location(self.nextX, self.nextY)
udg_MissileNextZ = self.nextZ
end
function mt:update()
if not self.paused then
self.damage = udg_MissileDamage
self.collision = udg_MissileCollision
self.acceleration = udg_MissileAcceleration
self.collideZ = udg_MissileCollideZ
self.source = udg_MissileSource
self.target = udg_MissileTarget
self.owner = udg_MissileOwner
self.roll = udg_MissileRoll
self.type = udg_MissileType
self.data = udg_MissileData
if self.Model ~= udg_MissileModel then
self:model(udg_MissileModel)
end
if self.Scale ~= udg_MissileScale then
self:scale(udg_MissileScale)
end
if self.Speed ~= udg_MissileSpeed then
self:speed(udg_MissileSpeed)
end
if self.Duration ~= udg_MissileDuration then
self:duration(udg_MissileDuration)
end
if self.Arc ~= udg_MissileArc then
self:arc(udg_MissileArc)
end
if self.Curve ~= udg_MissileCurve then
self:curve(udg_MissileCurve)
end
if self.Vision ~= udg_MissileVision then
self:vision(udg_MissileVision)
end
if self.TimeScale ~= udg_MissileTimeScale then
self:timeScale(udg_MissileTimeScale)
end
if self.Alpha ~= udg_MissileAlpha then
self:alpha(udg_MissileAlpha)
end
if self.playercolor ~= udg_MissilePlayerColor then
self:playerColor(udg_MissilePlayerColor)
end
if self.Animation ~= udg_MissileAnimation then
self:animation(udg_MissileAnimation)
end
RemoveLocation(udg_MissilePosition)
RemoveLocation(udg_MissileLastPosition)
RemoveLocation(udg_MissileNextPosition)
udg_MissilePosition = Location(self.x, self.y)
udg_MissileZ = self.z
udg_MissileLastPosition = Location(self.prevX, self.prevY)
udg_MissilePrevZ = self.prevZ
udg_MissileNextPosition = Location(self.nextX, self.nextY)
udg_MissileNextZ = self.nextZ
end
end
function mt:cleanup()
RemoveLocation(udg_MissileStart)
RemoveLocation(udg_MissileFinish)
RemoveLocation(udg_MissilePosition)
RemoveLocation(udg_MissileLastPosition)
RemoveLocation(udg_MissileNextPosition)
udg_Missile = 0
udg_MissileStart = nil
udg_MissileStartZ = 0
udg_MissileFinish = nil
udg_MissileFinishZ = 0
udg_MissileTarget = nil
udg_MissileSource = nil
udg_MissileOwner = nil
udg_MissileRoll = false
udg_MissilePaused = false
udg_MissileType = 0
udg_MissileData = 0
udg_MissileTilset = 0
udg_MissileModel = ""
udg_MissileScale = 0
udg_MissileTimeScale = 0
udg_MissileAlpha = 0
udg_MissilePlayerColor = 0
udg_MissileAnimation = 0
udg_MissileDamage = 0
udg_MissileCollision = 0
udg_MissileCollideZ = false
udg_MissileVelocity = 0
udg_MissileTravelled = 0
udg_MissileSpeed = 0
udg_MissileDuration = 0
udg_MissileArc = 0
udg_MissileCurve = 0
udg_MissileVision = 0
udg_MissileAcceleration = 0
udg_MissilePosition = nil
udg_MissileZ = 0
udg_MissileLastPosition = nil
udg_MissilePrevZ = 0
udg_MissileNextPosition = nil
udg_MissileNextZ = 0
end
-- ---------------------------- Missiles movement --------------------------- --
function mt:move()
local i = 0
local j = 0
if SWEET_SPOT > 0 then
i = last
else
i = 0
end
while not ((j >= SWEET_SPOT and SWEET_SPOT > 0) or j > id) do
local this = missiles[i]
if this.allocated and not this.paused then
this:OnHit()
this:OnMissile()
this:OnDestructable()
this:OnItem()
this:OnCliff()
this:OnTerrain()
this:OnTileset()
this:OnPeriod()
this:OnOrient()
this:OnFinish()
this:OnBoundaries()
else
i = this:remove(i)
j = j - 1
end
i = i + 1
j = j + 1
if i > id and SWEET_SPOT > 0 then
i = 0
end
end
last = i
end
-- --------------------------- Launch the Missile --------------------------- --
function mt:launch()
if not self.launched and self.allocated then
self.launched = true
id = id + 1
missiles[id] = self
Missiles.count = Missiles.count + 1
self.index = Missiles.count
Missiles.collection[Missiles.count] = self
if id + 1 > SWEET_SPOT and SWEET_SPOT > 0 then
dilation = (id + 1) / SWEET_SPOT
else
dilation = 1.
end
if id == 0 then
TimerStart(timer, PERIOD, true, function() Missiles:move() end)
end
end
end
--------------------------- Main Creator method --------------------------
function mt:create(x, y, z, toX, toY, toZ)
local this = {}
setmetatable(this, mt)
array[this] = {}
if #keys > 0 then
this.key = keys[#keys]
keys[#keys] = nil
else
this.key = index
index = index + 1
end
collection[this.key] = this
this:reset()
this.origin = Coordinates:create(x, y, z)
this.impact = Coordinates:create(toX, toY, toZ)
this.effect = MissileEffect:create(x, y, this.origin.z)
Coordinates:link(this.origin, this.impact)
this.allocated = true
this.cA = this.origin.angle
this.x = x
this.y = y
this.z = this.impact.z
this.prevX = x
this.prevY = y
this.prevZ = this.impact.z
this.nextX = x
this.nextY = y
this.nextZ = this.impact.z
this.toZ = toZ
return this
end
-- -------------------------------------------------------------------------- --
-- GUI API --
-- -------------------------------------------------------------------------- --
function MissileCreate()
local missile = Missiles:create(GetLocationX(udg_MissileStart), GetLocationY(udg_MissileStart), udg_MissileStartZ, GetLocationX(udg_MissileFinish), GetLocationY(udg_MissileFinish), udg_MissileFinishZ)
if udg_MissileRemoveLocations then
RemoveLocation(udg_MissileStart)
RemoveLocation(udg_MissileFinish)
else
udg_MissileRemoveLocations = true
end
udg_Missile = missile.key
udg_MissileStartZ = 0
udg_MissileFinishZ = 0
udg_MissileStart = nil
udg_MissileFinish = nil
if udg_MissileModel ~= "" then
missile:model(udg_MissileModel)
udg_MissileModel = ""
end
if udg_MissileScale ~= 0 then
missile:scale(udg_MissileScale)
udg_MissileScale = 0
end
if udg_MissileSpeed ~= 0 then
missile:speed(udg_MissileSpeed)
udg_MissileSpeed = 0
end
if udg_MissileDuration ~= 0 then
missile:duration(udg_MissileDuration)
udg_MissileDuration = 0
end
if udg_MissileArc ~= 0 then
missile:arc(udg_MissileArc)
udg_MissileArc = 0
end
if udg_MissileCurve ~= 0 then
missile:curve(udg_MissileCurve)
udg_MissileCurve = 0
end
if udg_MissileDamage ~= 0 then
missile.damage = udg_MissileDamage
udg_MissileDamage = 0
end
if udg_MissileCollision > 0 then
missile.collision = udg_MissileCollision
udg_MissileCollision = 0
end
if udg_MissileAcceleration ~= 0 then
missile.acceleration = udg_MissileAcceleration
udg_MissileAcceleration = 0
end
if udg_MissileCollideZ then
missile.collideZ = udg_MissileCollideZ
udg_MissileCollideZ = false
end
if udg_MissileSource ~= nil then
missile.source = udg_MissileSource
udg_MissileSource = nil
end
if udg_MissileTarget ~= nil then
missile.target = udg_MissileTarget
udg_MissileTarget = nil
end
if udg_MissileOwner ~= nil then
missile.owner = udg_MissileOwner
udg_MissileOwner = nil
end
if udg_MissileVision ~= 0 then
missile:vision(udg_MissileVision)
udg_MissileVision = 0
end
if udg_MissileRoll then
missile.roll = udg_MissileRoll
udg_MissileRoll = false
end
if udg_MissileType ~= 0 then
missile.type = udg_MissileType
udg_MissileType = 0
end
if udg_MissileData ~= 0 then
missile.data = udg_MissileData
udg_MissileData = 0
end
if udg_MissileTimeScale ~= 0 then
missile:timeScale(udg_MissileTimeScale)
udg_MissileTimeScale = 0
end
if udg_MissileAlpha ~= 0 then
missile:alpha(udg_MissileAlpha)
udg_MissileAlpha = 0
end
if udg_MissilePlayerColor ~= 0 then
missile:playerColor(udg_MissilePlayerColor)
udg_MissilePlayerColor = 0
end
if udg_MissileAnimation ~= 0 then
missile:animation(udg_MissileAnimation)
udg_MissileAnimation = 0
end
if udg_Missile_onPeriod ~= nil then
missile.onPeriod = udg_Missile_onPeriod
udg_Missile_onPeriod = nil
end
if udg_Missile_onHit ~= nil then
missile.onHit = udg_Missile_onHit
udg_Missile_onHit = nil
end
if udg_Missile_onDestructable ~= nil then
missile.onDestructable = udg_Missile_onDestructable
udg_Missile_onDestructable = nil
end
if udg_Missile_onItem ~= nil then
missile.onItem = udg_Missile_onItem
udg_Missile_onItem = nil
end
if udg_Missile_onMissile ~= nil then
missile.onMissile = udg_Missile_onMissile
udg_Missile_onMissile = nil
end
if udg_Missile_onCliff ~= nil then
missile.onCliff = udg_Missile_onCliff
udg_Missile_onCliff = nil
end
if udg_Missile_onTerrain ~= nil then
missile.onTerrain = udg_Missile_onTerrain
udg_Missile_onTerrain = nil
end
if udg_Missile_onTileset ~= nil then
missile.onTileset = udg_Missile_onTileset
udg_Missile_onTileset = nil
end
if udg_Missile_onFinish ~= nil then
missile.onFinish = udg_Missile_onFinish
udg_Missile_onFinish = nil
end
if udg_Missile_onBoundaries ~= nil then
missile.onBoundaries = udg_Missile_onBoundaries
udg_Missile_onBoundaries = nil
end
if udg_Missile_onResume ~= nil then
missile.onResume = udg_Missile_onResume
udg_Missile_onResume = nil
end
if udg_Missile_onPause ~= nil then
missile.onPause = udg_Missile_onPause
udg_Missile_onPause = nil
end
if udg_Missile_onRemove ~= nil then
missile.onRemove = udg_Missile_onRemove
udg_Missile_onRemove = nil
end
missile:launch()
end
function MissileBounce()
local missile = collection[udg_Missile]
missile:bounce()
end
function MissileDeflectTarget()
local missile = collection[udg_Missile]
missile:deflectTarget(udg_MissileDeflectTarget)
end
function MissileDeflect()
local missile = collection[udg_Missile]
missile:deflect(GetLocationX(udg_MissileDeflectPosition), GetLocationY(udg_MissileDeflectPosition), udg_MissileDeflectZ)
RemoveLocation(udg_MissileDeflectPosition)
udg_MissileDeflectPosition = nil
end
function MissileFlush()
local missile = collection[udg_Missile]
missile:flush(udg_MissileFlushUnit)
udg_MissileFlushUnit = nil
end
function MissileFlushAll()
local missile = collection[udg_Missile]
missile:flushAll()
end
function MissileHitted()
local missile = collection[udg_Missile]
udg_MissileHitted = missile:hitted(udg_MissileHittedUnit)
end
function MissileAttach()
local missile = collection[udg_Missile]
bj_lastCreatedEffect = missile:attach(udg_MissileAttachModel, udg_MissileAttachX, udg_MissileAttachY, udg_MissileAttachZ, udg_MissileAttachScale)
end
function MissileDetach()
local missile = collection[udg_Missile]
missile:detach(udg_MissileDetachEffect)
udg_MissileDetachEffect = nil
end
function MissilePause()
local missile = collection[udg_Missile]
missile:pause(true)
end
function MissileResume()
local missile = collection[udg_Missile]
missile:pause(false)
end
function MissileColor()
local missile = collection[udg_Missile]
missile:color(udg_MissileRed, udg_MissileGreen, udg_MissileBlue)
end
end
do
onInit(function()
local trigger = CreateTrigger()
TriggerRegisterDestDeathInRegionEvent(trigger, GetPlayableMapRect())
TriggerAddCondition(trigger, Condition(function()
local destructable = GetDyingDestructable()
local timer = CreateTimer()
TimerStart(timer, 10, false, function()
DestructableRestoreLife(destructable, GetDestructableMaxLife(destructable), true)
DestroyTimer(timer)
end)
end))
RegisterPlayerUnitEvent(EVENT_PLAYER_UNIT_DEATH, function()
local unit = GetTriggerUnit()
local x = GetUnitX(unit)
local y = GetUnitY(unit)
local f = GetUnitFacing(unit)
if IsUnitType(unit, UNIT_TYPE_HERO) then
ReviveHero(unit, x, y, true)
else
local timer = CreateTimer()
TimerStart(timer, 5, false, function()
CreateUnit(Player(0), GetUnitTypeId(unit), x, y, f)
DestroyTimer(timer)
end)
end
end)
end)
end