wraithseeker
Tired.
- Reaction score
- 122
Bump, approve this moderators! or at least review it.
//------------------------------------------------------------------------------------\\
// Path of Shadows [v11] \\
// By kenny! \\
// Constructed using vJASS \\
// Requires NewGen WE & GTrigger \\
//------------------------------------------------------------------------------------\\
scope PathofShadows initializer Init
globals
// Configurables:
// Integers and Reals:
private constant integer LEARN_ID = 039;A000039;
private constant integer READY_ID = 039;A001039;
private constant integer COOLDOWN_ID = 039;A002039;
private constant integer ANIM_INDEX = 7
private constant integer RED = 255
private constant integer GREEN = 255
private constant integer BLUE = 255
private constant integer ALPHA = 128
private constant real INTERVAL = 0.04 // Or 0.02
private constant real DISTANCE = 50.00
private constant real COLLISION = 100.00
// Special Effects:
private constant boolean PLAY_DASH_SFX = false
private constant string DASH_SFX = "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl"
private constant boolean PLAY_CONSTANT_SFX = false
private constant string CONSTANT_SFX = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
private constant string CONSTANT_POINT = "chest"
private constant boolean PLAY_COOLDOWN_SFX = true
private constant string COOLDOWN_SFX = "Abilities\\Spells\\Undead\\CarrionSwarm\\CarrionSwarmDamage.mdl"
private constant string COOLDOWN_POINT = "overhead"
private constant boolean PLAY_DAMAGE_SFX = true
private constant string DAMAGE_SFX = "Abilities\\Spells\\Undead\\DeathandDecay\\DeathandDecayTarget.mdl"
private constant string DAMAGE_POINT = "head"
// Error Messsage:
private constant string ERROR_MSG = "Your hero does not have enough mana to walk the path of shadows."
// Booleans:
private constant boolean ALLOW_PRELOAD = true
private constant boolean ALLOW_PATHING = true
private constant boolean ALLOW_MANA_COST = false
private constant boolean ALLOW_DAMAGE = true
private constant boolean ALLOW_ERROR_MSG = true
private constant boolean ALLOW_ORDER_MOVE = true
private constant boolean ALLOW_ORDER_SMART = true
private constant boolean ALLOW_ORDER_ATTACK = true
// Attack/Damage/Weapon types of damage dealt:
private constant attacktype A_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype D_TYPE = DAMAGE_TYPE_UNIVERSAL
private constant weapontype W_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
//---------------------------------------------------------------------\\
globals
private constant real REQUIRED_DIST = 20.00
private constant real OFFSET = 15.00
private constant integer ALPHA_START = 250
private constant integer ALPHA_MINUS = 50
private constant integer LOCUST_ID = 039;Aloc039;
private constant integer CROW_ID = 039;Amrf039;
endglobals
//---------------------------------------------------------------------\\
private function Max_dist takes integer lvl returns real
return 400.00 + (100.00 * lvl)
endfunction
private function Min_dist takes integer lvl returns real
return 200.00 + (50.00 * lvl)
endfunction
private function Movement_speed takes integer lvl returns real
return 1000.00 + (250.00 * lvl)
endfunction
private function Time_scale takes integer lvl returns real
return 1.00 + (1.00 * lvl)
endfunction
private function Cool_down takes integer lvl returns real
return 2.00 + (0.00 * lvl)
endfunction
private function Damage_dealt takes integer lvl returns real
return 25.00 * lvl
endfunction
private function Mana_cost takes integer lvl returns integer
return 20 + (20 * lvl)
endfunction
private function Enemy_filt takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitType(GetFilterUnit(),UNIT_TYPE_STRUCTURE) == false and GetUnitFlyHeight(GetFilterUnit()) < 40.00
endfunction
//---------------------------------------------------------------------\\
// \\
// DO NOT TOUCH PAST THIS POINT UNLESS YOU KNOW WHAT YOUR ARE DOING! \\
// \\
//---------------------------------------------------------------------\\
globals
// Needed non-constant globals:
private item array H1 // Needed for hidding necessary items.
private integer H1_total = 0 // Needed for counting hidden items.
private real Game_maxX = 0.00 // Used to set up map bounds.
private real Game_minX = 0.00 // Used to set up map bounds.
private real Game_maxY = 0.00 // Used to set up map bounds.
private real Game_minY = 0.00 // Used to set up map bounds.
private item Path_item = null // Item needed for pathing detection.
private rect Path_rect = null // Rect needed for pathing detection.
private sound Error_sound = null // Error sound used for error message.
// Needed Constant Globals:
private constant real MAX_RANGE = 10.00 // Needed for checking pathing.
private constant integer ORDER_STOP = 851972 // Order ID of the command "stop".
private constant integer ORDER_MOVE = 851986 // Order ID of the command "move".
private constant integer ORDER_SMART = 851971 // Order ID of the command "Smart".
private constant integer ORDER_ATTACK = 851983 // Order ID of the command "attack".
endglobals
//---------------------------------------------------------------------\\
private function Safe_x takes real x returns real
if x < Game_minX then
return Game_minX
elseif x > Game_maxX then // Sets a safe x position.
return Game_maxX
endif
return x
endfunction
private function Safe_y takes real y returns real
if y < Game_minY then
return Game_minY
elseif y > Game_maxY then // Sets a safe x position.
return Game_maxY
endif
return y
endfunction
//---------------------------------------------------------------------\\
private function Filter_items takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set H1[H1_total] = GetEnumItem()
call SetItemVisible(H1[H1_total],false) // Hides unwanted items.
set H1_total = H1_total + 1
endif
endfunction
// Thanks to Vexorian for original concept, and Anitarf for the up-to-date version. (Slightly midified).
private function Check_pathability takes real x1, real y1 returns boolean
local real x2 = 0.00
local real y2 = 0.00
call MoveRectTo(Path_rect,x1,y1)
call EnumItemsInRect(Path_rect,null,function Filter_items)
call SetItemPosition(Path_item,x1,y1)
set x2 = GetItemX(Path_item)
set y2 = GetItemY(Path_item)
call SetItemVisible(Path_item,false)
loop
exitwhen H1_total <= 0
set H1_total = H1_total - 1
call SetItemVisible(H1[H1_total],true)
set H1[H1_total] = null
endloop
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < MAX_RANGE * MAX_RANGE
endfunction
//---------------------------------------------------------------------\\
private function Sim_error takes player whichplayer, string message returns nothing
set message = "\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n|cffffcc00" + message + "|r"
if (GetLocalPlayer() == whichplayer) then
call ClearTextMessages()
call DisplayTimedTextToPlayer(whichplayer,0.5125,0.97120,2.00,message)
call StartSound(Error_sound) // Replicates a sim error.
endif
endfunction
//---------------------------------------------------------------------\\
private function Is_dashing takes unit whichunit returns boolean
local integer i = 1
private keyword Data1
loop
exitwhen i > Data1.Total
if Data1.D1<i>.cast == whichunit then
return true
endif
set i = i + 1
endloop
return false
endfunction
//---------------------------------------------------------------------\\
private struct Shadow2
unit shadow = null
integer alpha = 0
integer anim = 0
integer id = 0
static Shadow2 array S2
static timer Timer = null
static integer Total = 0
static method create takes unit caster, integer id, integer animation, real angle, real timescale, real x, real y returns Shadow2
local Shadow2 s2 = Shadow2.allocate()
set s2.id = id
set s2.anim = animation
set s2.alpha = ALPHA_START
set s2.shadow = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),s2.id,x,y,GetUnitFacing(caster))
call SetUnitColor(s2.shadow,GetPlayerColor(GetOwningPlayer(caster)))
call SetUnitVertexColor(s2.shadow,255,255,255,s2.alpha)
call SetUnitAnimationByIndex(s2.shadow,s2.anim)
call SetUnitTimeScale(s2.shadow,timescale)
call UnitAddAbility(s2.shadow,LOCUST_ID)
call SetUnitPathing(s2.shadow,false)
call SetUnitX(s2.shadow,x + OFFSET * Cos(angle))
call SetUnitY(s2.shadow,y + OFFSET * Sin(angle))
if GetUnitFlyHeight(caster) > 1.00 then
call UnitAddAbility(s2.shadow,CROW_ID)
call UnitRemoveAbility(s2.shadow,CROW_ID)
call SetUnitFlyHeight(s2.shadow,GetUnitFlyHeight(caster),0.00)
endif
set Shadow2.Total = Shadow2.Total + 1
set Shadow2.S2[Shadow2.Total] = s2
if Shadow2.Total == 1 then
call TimerStart(Shadow2.Timer,INTERVAL,true,function Shadow2.update)
endif
return s2
endmethod
static method update takes nothing returns nothing
local Shadow2 s2 = 0
local integer i = 1
loop
exitwhen i > Shadow2.Total
set s2 = Shadow2.S2<i>
if s2.alpha <= 0 then
call s2.destroy()
set Shadow2.S2<i> = Shadow2.S2[Shadow2.Total]
set Shadow2.Total = Shadow2.Total - 1
set i = i - 1
else
call SetUnitVertexColor(s2.shadow,255,255,255,s2.alpha)
call SetUnitAnimationByIndex(s2.shadow,s2.anim)
set s2.alpha = s2.alpha - ALPHA_MINUS
endif
set i = i + 1
endloop
if Shadow2.Total <= 0 then
call PauseTimer(Shadow2.Timer)
set Shadow2.Total = 0
endif
endmethod
method onDestroy takes nothing returns nothing
call RemoveUnit(.shadow)
set .shadow = null
endmethod
static method onInit takes nothing returns nothing
set Shadow2.Timer = CreateTimer()
endmethod
endstruct
//---------------------------------------------------------------------\\
private struct Shadow1
unit cast = null
real scale = 0.00
real x = 0.00
real y = 0.00
integer id = 0
integer anim = 0
boolean stop = false
static Shadow1 array S1
static timer Timer = null
static integer Total = 0
static method create takes unit caster, integer unitid, integer animation, real timescale returns Shadow1
local Shadow1 s1 = Shadow1.allocate()
set s1.cast = caster
set s1.id = unitid
set s1.anim = animation
set s1.scale = timescale
set s1.x = GetUnitX(s1.cast)
set s1.y = GetUnitY(s1.cast)
set Shadow1.Total = Shadow1.Total + 1
set Shadow1.S1[Shadow1.Total] = s1
if Shadow1.Total == 1 then
call TimerStart(Shadow1.Timer,INTERVAL,true,function Shadow1.update)
endif
return s1
endmethod
private static method update takes nothing returns nothing
local Shadow1 s1 = 0
local integer i = 1
local real x = 0.00
local real y = 0.00
local real d = 0.00
loop
exitwhen i > Shadow1.Total
set s1 = Shadow1.S1<i>
if s1.stop then
call s1.destroy()
set Shadow1.S1<i> = Shadow1.S1[Shadow1.Total]
set Shadow1.Total = Shadow1.Total - 1
set i = i - 1
else
set x = GetUnitX(s1.cast)
set y = GetUnitY(s1.cast)
set d = ((x - s1.x) * (x - s1.x) + (y - s1.y) * (y - s1.y))
if d >= REQUIRED_DIST * REQUIRED_DIST then
call Shadow2.create(s1.cast,s1.id,s1.anim,Atan2((s1.y - y),(s1.x - x)),s1.scale,x,y)
endif
set s1.x = x
set s1.y = y
endif
set i = i + 1
endloop
if Shadow1.Total <= 0 then
call PauseTimer(Shadow1.Timer)
set Shadow1.Total = 0
endif
endmethod
private method onDestroy takes nothing returns nothing
set .cast = null
endmethod
private static method onInit takes nothing returns nothing
set Shadow1.Timer = CreateTimer()
endmethod
endstruct
//---------------------------------------------------------------------\\
private struct Data1
unit cast = null
effect sfx = null
widget t = null
group g = null
real tx = 0.00
real ty = 0.00
real move = 0.00
real cos = 0.00
real sin = 0.00
integer ticks = 0
integer lvl = 0
integer id = 0
Shadow1 s1 = 0
static Data1 array D1
static integer Total = 0
static timer Timer = null
static group Group = null
static boolexpr Filt = null
static method create takes real dist, real targx, real targy, widget targ, integer order returns Data1
local Data1 d1 = Data1.allocate()
local real angle = 0.00
local real castx = 0.00
local real casty = 0.00
set d1.cast = GetTriggerUnit()
set castx = GetUnitX(d1.cast)
set casty = GetUnitY(d1.cast)
set d1.tx = targx
set d1.ty = targy
set d1.lvl = GetUnitAbilityLevel(d1.cast,COOLDOWN_ID)
set d1.move = Movement_speed(d1.lvl) * INTERVAL
set angle = Atan2((d1.ty - casty),(d1.tx - castx))
set d1.cos = Cos(angle)
set d1.sin = Sin(angle)
set d1.t = targ
set d1.id = order
set d1.s1 = Shadow1.create(d1.cast,GetUnitTypeId(d1.cast),ANIM_INDEX,Time_scale(d1.lvl))
if dist > Max_dist(d1.lvl) then
set dist = Max_dist(d1.lvl) // Sets max distance.
endif
set d1.ticks = R2I(dist / d1.move)
// Sets up unit.
call SetUnitPathing(d1.cast,false)
call SetUnitFacing(d1.cast,angle * bj_RADTODEG)
call SetUnitTimeScale(d1.cast,Time_scale(d1.lvl))
call SetUnitVertexColor(d1.cast,RED,GREEN,BLUE,ALPHA)
if ALLOW_DAMAGE then // If we want to damage units, create group.
set d1.g = CreateGroup()
endif
if PLAY_DASH_SFX then
call DestroyEffect(AddSpecialEffect(DASH_SFX,castx,casty)) // Plays DASH_SFX if wanted.
endif
if PLAY_CONSTANT_SFX then
set d1.sfx = AddSpecialEffectTarget(CONSTANT_SFX,d1.cast,CONSTANT_POINT) // Plays CONSTANT_SFX if wanted.
endif
set Data1.Total = Data1.Total + 1
set Data1.D1[Data1.Total] = d1
if Data1.Total == 1 then // Starts timer if its not running.
call TimerStart(Data1.Timer,INTERVAL,true,function Data1.update)
endif
return d1
endmethod
static method update takes nothing returns nothing
local Data1 d1 = 0
local integer i = 1
local integer j = 0
local real x = 0.00
local real y = 0.00
local unit u = null
loop
exitwhen i > Data1.Total
set d1 = Data1.D1<i>
set x = GetUnitX(d1.cast)
set y = GetUnitY(d1.cast)
if ALLOW_PATHING then // Checks for pathing, if desired.
if Check_pathability(x + DISTANCE * d1.cos,y + DISTANCE * d1.sin) == false then
set j = 1
endif
endif
if d1.ticks <= 0 or j == 1 or GetWidgetLife(d1.cast) < 0.406 then // Stops when the max dist has been reached, or if the location isnt pathable.
call d1.destroy()
set Data1.D1<i> = Data1.D1[Data1.Total]
set Data1.Total = Data1.Total - 1
set i = i - 1
set j = 0
else
set x = Safe_x(x + d1.move * d1.cos)
set y = Safe_y(y + d1.move * d1.sin)
call SetUnitX(d1.cast,x)
call SetUnitY(d1.cast,y) // Sets new position.
call SetUnitAnimationByIndex(d1.cast,ANIM_INDEX)
call IssueImmediateOrderById(d1.cast,ORDER_STOP)
// Damages unit who are hit by the caster.
if ALLOW_DAMAGE then
call GroupEnumUnitsInRange(Data1.Group,x,y,COLLISION,Data1.Filt)
loop
set u = FirstOfGroup(Data1.Group)
exitwhen u == null
call GroupRemoveUnit(Data1.Group,u)
if not(IsUnitInGroup(u,d1.g)) and IsUnitEnemy(u,GetOwningPlayer(d1.cast)) == true then
call GroupAddUnit(d1.g,u)
call UnitDamageTarget(d1.cast,u,Damage_dealt(d1.lvl),false,false,A_TYPE,D_TYPE,W_TYPE)
if PLAY_DAMAGE_SFX then
call DestroyEffect(AddSpecialEffectTarget(DAMAGE_SFX,u,DAMAGE_POINT))
endif
endif
endloop
endif
set d1.ticks = d1.ticks - 1
endif
set i = i + 1
endloop
if Data1.Total <= 0 then
call PauseTimer(Data1.Timer)
set Data1.Total = 0
endif
set u = null
endmethod
method onDestroy takes nothing returns nothing
if .sfx != null then
call DestroyEffect(.sfx)
endif
// Resets the casting unit.
set .s1.stop = true
call SetUnitPathing(.cast,true)
call SetUnitTimeScale(.cast,1.00)
call SetUnitAnimation(.cast,"stand")
call SetUnitVertexColor(.cast,255,255,255,255)
if .t != null then
call IssueTargetOrderById(.cast,.id,.t)
else
call IssuePointOrderById(.cast,.id,.tx,.ty)
endif
if PLAY_DASH_SFX then
call DestroyEffect(AddSpecialEffect(DASH_SFX,GetUnitX(.cast),GetUnitY(.cast))) // Plays DASH_SFX if wanted.
endif
if ALLOW_DAMAGE then // If the group has been created, destroy it.
call GroupClear(.g)
call DestroyGroup(.g)
endif
set .g = null
set .t = null
set .sfx = null
set .cast = null
endmethod
static method onInit takes nothing returns nothing
set Data1.Timer = CreateTimer()
set Data1.Group = CreateGroup()
set Data1.Filt = Filter(function Enemy_filt)
endmethod
endstruct
//---------------------------------------------------------------------\\
private struct Data2
unit cast = null
real time = 0.00
static Data2 array D2
static integer Total = 0
static timer Timer = null
static method create takes nothing returns Data2
local Data2 d2 = Data2.allocate()
local integer lvl = 0
set d2.cast = GetTriggerUnit()
set lvl = GetUnitAbilityLevel(d2.cast,READY_ID)
set d2.time = Cool_down(lvl)
call UnitRemoveAbility(d2.cast,READY_ID)
call UnitAddAbility(d2.cast,COOLDOWN_ID) // Changes the dummy ability to the second one.
call SetUnitAbilityLevel(d2.cast,COOLDOWN_ID,lvl)
set Data2.Total = Data2.Total + 1
set Data2.D2[Data2.Total] = d2
if Data2.Total == 1 then
call TimerStart(Data2.Timer,INTERVAL,true,function Data2.update)
endif
return d2
endmethod
static method update takes nothing returns nothing
local Data2 d2 = 0
local integer i = 1
loop
exitwhen i > Data2.Total
set d2 = Data2.D2<i>
if d2.time <= 0.00 or GetWidgetLife(d2.cast) < 0.406 then // stop when the cooldown has been reached or the hero died.
call d2.destroy()
set Data2.D2<i> = Data2.D2[Data2.Total]
set Data2.Total = Data2.Total - 1
set i = i - 1
else
set d2.time = d2.time - INTERVAL // Loop through the structs and add time on.
endif
set i = i + 1
endloop
if Data2.Total <= 0 then
call PauseTimer(Data2.Timer)
set Data2.Total = 0
endif
endmethod
method onDestroy takes nothing returns nothing
local string sfx = ""
local integer lvl = GetUnitAbilityLevel(.cast,COOLDOWN_ID)
call UnitRemoveAbility(.cast,COOLDOWN_ID)
call UnitAddAbility(.cast,READY_ID)
call SetUnitAbilityLevel(.cast,READY_ID,lvl) // Resets the ability.
if PLAY_COOLDOWN_SFX then // Plays the special effect to the local player if wanted.
set sfx = COOLDOWN_SFX
if GetLocalPlayer() != GetOwningPlayer(.cast) then
set sfx = ""
endif
call DestroyEffect(AddSpecialEffectTarget(sfx,.cast,COOLDOWN_POINT))
endif
set .cast = null
endmethod
static method onInit takes nothing returns nothing
set Data2.Timer = CreateTimer()
endmethod
endstruct
//---------------------------------------------------------------------\\
private function Move_actions takes nothing returns boolean
local unit cast = GetTriggerUnit()
local widget targ = GetOrderTarget()
local real castx = GetUnitX(cast)
local real casty = GetUnitY(cast)
local real targx = 0.00
local real targy = 0.00
local real dist = 0.00
local integer lvl = GetUnitAbilityLevel(cast,READY_ID)
local integer id = GetIssuedOrderId()
if lvl > 0 and Is_dashing(cast) == false then
if targ != null then
set targx = GetWidgetX(targ)
set targy = GetWidgetY(targ)
else
set targx = GetOrderPointX()
set targy = GetOrderPointY()
endif
set dist = SquareRoot((targx - castx) * (targx - castx) + (targy - casty) * (targy - casty))
if dist > Min_dist(lvl) then // Checks for minimum distance, if there is any.
if ALLOW_MANA_COST then // Checks for mana cost, if there is any.
if GetUnitState(cast,UNIT_STATE_MANA) >= Mana_cost(lvl) then
call SetUnitState(cast,UNIT_STATE_MANA,GetUnitState(cast,UNIT_STATE_MANA) - Mana_cost(lvl)) // If there is enoguh mana, use the skill.
call Data2.create()
call Data1.create(dist,targx,targy,targ,id) // Start path of shadows if the unit has enough mana.
else
if ALLOW_ERROR_MSG then
call Sim_error(GetOwningPlayer(cast),ERROR_MSG) // Otherwise, don't. And show an error message.
endif
endif
else
call Data2.create() // If there is no mana cost, just run the skill.
call Data1.create(dist,targx,targy,targ,id)
endif
endif
endif
set cast = null
set targ = null
return false // Used condition so that it compiles with GTrigger.
endfunction
//---------------------------------------------------------------------\\
private function Learn_conditions takes nothing returns boolean
local unit cast = GetLearningUnit()
if GetUnitAbilityLevel(cast,LEARN_ID) == 1 then
call UnitAddAbility(cast,READY_ID) // If its the first level of the ability, then add the dummy one.
else
if GetUnitAbilityLevel(cast,READY_ID) > 0 then
call IncUnitAbilityLevel(cast,READY_ID) // Else, just level the dummy one.
elseif GetUnitAbilityLevel(cast,COOLDOWN_ID) > 0 then
call IncUnitAbilityLevel(cast,COOLDOWN_ID)
endif
endif
set cast = null
return false // Used condition, as it is safer than action.
endfunction
//---------------------------------------------------------------------\\
private function Init takes nothing returns nothing
local trigger trig = CreateTrigger()
// Setting up map bounds:
set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
set Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
set Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
// Registering target and point order events:
if ALLOW_ORDER_MOVE then
call GT_RegisterPointOrderEvent(trig,ORDER_MOVE)
call GT_RegisterTargetOrderEvent(trig,ORDER_MOVE)
endif
if ALLOW_ORDER_SMART then
call GT_RegisterPointOrderEvent(trig,ORDER_SMART)
call GT_RegisterTargetOrderEvent(trig,ORDER_SMART)
endif
if ALLOW_ORDER_ATTACK then
call GT_RegisterPointOrderEvent(trig,ORDER_ATTACK)
call GT_RegisterTargetOrderEvent(trig,ORDER_ATTACK)
endif
// Adding action to above events:
call TriggerAddCondition(trig,Condition(function Move_actions))
// Registering learn ability event:
call GT_AddLearnsAbilityAction(function Learn_conditions,LEARN_ID)
// Preloads effects, if desired:
if ALLOW_PRELOAD then
call DestroyEffect(AddSpecialEffect(DASH_SFX,0.00,0.00))
call DestroyEffect(AddSpecialEffect(CONSTANT_SFX,0.00,0.00))
call DestroyEffect(AddSpecialEffect(COOLDOWN_SFX,0.00,0.00))
call DestroyEffect(AddSpecialEffect(DAMAGE_SFX,0.00,0.00))
endif
// Sets up rect and item to check for pathing. As well as Sound variable:
set Error_sound = CreateSoundFromLabel("InterfaceError",false,false,false,10,10)
set Path_rect = Rect(0.00,0.00,128.00,128.00)
set Path_item = CreateItem(039;ciri039;,0.00,0.00)
call SetItemVisible(Path_item,false)
endfunction
endscope
</i></i></i></i></i></i></i></i></i>
As said... Make it allow the unit to go around stuff by using the unit's facing as the direction to propel. Max out the turn speed for the duration of the spell, heh.
Oh, and maybe make the image units a bit transparent? They kinda look just a little ugly.