library PandaRoll requires CTL, OrderEvent
//*-------------------------------------------------------------------------------------------
//* __________________________________________________________________________________________
//* PANDA ROLL - v1.01
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* Requirements
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* - CTL by Nestharus
//* - OrderEvent by Bribe
//*
//* Importing
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
//* 1. Copy the ability, buff, dummy unit, and import dummy.mdx model
//* 2. Implement the required libraries
//* 3. Copy this trigger
//* 4. Configure the spell, and make sure the buff is correct
//*-------------------------------------------------------------------------------------------
//* CALIBRATION SECTION
//*-------------------------------------------------------------------------------------------
globals
// the rawcode of the ability
private constant integer SPELL_ID = 'A000'
// the rawcode of the buff
private constant integer BUFF_ID = 'B000'
// the rawcode of the dummy unit
private constant integer DUMMY_ID = 'e000'
// order string turn on
private constant string ORDER_ON_ID = "immolation"
// order string turn off
private constant string ORDER_OFF_ID = "unimmolation"
// effect on cast
private constant string CAST_FX = "Abilities\\Spells\\Other\\Doom\\DoomDeath.mdl"
// effect on hit
private constant string HIT_FX = "Abilities\\Weapons\\DemolisherFireMissile\\DemolisherFireMissile.mdl"
// dummy effect
private constant string DUMMY_FX = "Units\\Creeps\\FirePandarenBrewmaster\\FirePandarenBrewmaster_Missile.mdl"
// dummy scaling
private constant real DUMMY_SCALE = 1.7
// dummy fly height
private constant real DUMMY_Z = 100.
// roll speed
private constant real SPEED = 1000.
// distance increment
private constant real DISTANCE_INC = SPEED * .031250000
// attack type
private constant attacktype AT = ATTACK_TYPE_NORMAL
// damage type
private constant damagetype DT = DAMAGE_TYPE_MAGIC
// weapon type
private constant weapontype WT = WEAPON_TYPE_WHOKNOWS
// caster vertex color
private constant integer RED_C_CASTER = 255
private constant integer BLUE_C_CASTER = 255
private constant integer GREEN_C_CASTER = 255
private constant integer ALPHA_C_CASTER = 255
// max distance
private constant real MAX_DISTANCE_VALUE = 2147483647.
// preload fx
private constant boolean PRELOAD = true
endglobals
// area of effect to find the closest target
private constant function GetAoE takes integer lvl returns real
return 700. + lvl * 100.
endfunction
// damage per level
private constant function GetDamage takes integer lvl returns real
return 10. * lvl
endfunction
//*-----------------------------------------------------------------------------------
//* END OF CALIBRATION SECTION
//*-----------------------------------------------------------------------------------
private struct Spell extends array
static unit array caster //caster unit
static unit array target //target unit
static unit array dummy //dummy unit
static effect array fx //dummy effect
static integer array lvl //level of ability when it cast
implement CTL
local real xCaster
local real yCaster
local real xTarget
local real yTarget
local real tempX
local real tempY
local real angle
local real dx
local real dy
local unit t
local real nearestDist
local real dist
implement CTLExpire
set xCaster = GetUnitX(caster[this])
set yCaster = GetUnitY(caster[this])
set xTarget = GetUnitX(target[this])
set yTarget = GetUnitY(target[this])
set angle = Atan2(yTarget-yCaster,xTarget-xCaster)
set dx = DISTANCE_INC * Cos(angle)
set dy = DISTANCE_INC * Sin(angle)
set t = null
set nearestDist = MAX_DISTANCE_VALUE
if GetUnitAbilityLevel(caster[this], BUFF_ID) == 0 or target[this] == null or IsUnitType(target[this],UNIT_TYPE_DEAD) then
call SetUnitPathing(caster[this], true)
call SetUnitVertexColor(caster[this], RED_C_CASTER, BLUE_C_CASTER, GREEN_C_CASTER, ALPHA_C_CASTER)
call IssueImmediateOrder(caster[this], ORDER_OFF_ID)
call DestroyEffect(AddSpecialEffect(CAST_FX, GetUnitX(caster[this]), GetUnitY(caster[this])))
call DestroyEffect(fx[this])
call RemoveUnit(dummy[this])
set caster[this] = null
set target[this] = null
set dummy[this] = null
set fx[this] = null
call destroy()
else
call SetUnitPosition(caster[this], xCaster + dx, yCaster + dy)
call SetUnitX(dummy[this], GetUnitX(caster[this]))
call SetUnitY(dummy[this], GetUnitY(caster[this]))
if (xTarget-xCaster)*(xTarget-xCaster)+(yTarget-yCaster)*(yTarget-yCaster)<2500 then
call DestroyEffect(AddSpecialEffect(HIT_FX, GetUnitX(target[this]), GetUnitY(target[this])))
call UnitDamageTarget(caster[this], target[this], GetDamage(lvl[this]), true, false, AT, DT, WT)
call GroupEnumUnitsInRange(bj_lastCreatedGroup, xCaster, yCaster, GetAoE(lvl[this]), null)
loop
set t = FirstOfGroup(bj_lastCreatedGroup)
exitwhen t == null
set tempX = xCaster - GetUnitX(t)
set tempY = yCaster - GetUnitY(t)
set dist = tempX * tempX + tempY * tempY
if dist < nearestDist and (t != target[this] and IsUnitEnemy(t, GetOwningPlayer(caster[this])) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) and not IsUnitType(t,UNIT_TYPE_DEAD)) then
set nearestDist = dist
set target[this] = t
exitwhen true
endif
call GroupRemoveUnit(bj_lastCreatedGroup, t)
endloop
endif
endif
implement CTLNull
set t = null
implement CTLEnd
private static method onStart takes unit u, integer level returns nothing
local thistype this = create()
local unit t = null
local real nearestDist = MAX_DISTANCE_VALUE
local real dist
local real x
local real y
local real tempX
local real tempY
set target[this] = null
set caster[this] = u
set lvl[this] = level
set x = GetUnitX(u)
set y = GetUnitY(u)
call DestroyEffect(AddSpecialEffect(CAST_FX, x, y))
set dummy[this] = CreateUnit(GetOwningPlayer(u), DUMMY_ID, GetUnitX(u), GetUnitY(u), GetUnitFacing(u))
set fx[this] = AddSpecialEffectTarget(DUMMY_FX, dummy[this], "origin")
call SetUnitScale(dummy[this], DUMMY_SCALE, DUMMY_SCALE, DUMMY_SCALE)
call SetUnitFlyHeight(dummy[this], DUMMY_Z, 0)
call GroupEnumUnitsInRange(bj_lastCreatedGroup, x, y, GetAoE(lvl[this]), null)
loop
set t = FirstOfGroup(bj_lastCreatedGroup)
exitwhen t == null
set tempX = x - GetUnitX(t)
set tempY = y - GetUnitY(t)
set dist = tempX * tempX + tempY * tempY
if dist < nearestDist and (t != target[this] and IsUnitEnemy(t, GetOwningPlayer(caster[this])) and not IsUnitType(t, UNIT_TYPE_STRUCTURE) and not IsUnitType(t,UNIT_TYPE_DEAD)) then
set nearestDist = dist
set target[this] = t
exitwhen true
endif
call GroupRemoveUnit(bj_lastCreatedGroup, t)
endloop
call SetUnitPathing(u, false)
call SetUnitVertexColor(caster[this], RED_C_CASTER, BLUE_C_CASTER, GREEN_C_CASTER, 0)
set t = null
endmethod
private static method onRun takes nothing returns nothing
if GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID) > 0 then
call onStart(GetTriggerUnit(), GetUnitAbilityLevel(GetTriggerUnit(), SPELL_ID))
endif
endmethod
private static method onInit takes nothing returns nothing
call RegisterOrderEvent(OrderId(ORDER_ON_ID), function thistype.onRun)
static if PRELOAD then
call Preload(CAST_FX)
call Preload(HIT_FX)
call Preload(DUMMY_FX)
endif
endmethod
endstruct
endlibrary