Name | Type | is_array | initial_value |
library Wispering initializer init
globals
private constant real Dt = 1.0 / 32.0
private constant real Tau = 6.283185
private constant integer Ability_Id = 'A000'
private constant integer Heal_Buff_Id = 'A001'
private constant integer Damage_Buff_Id = 'A002'
private constant integer Wisp_id = 'e000'
private constant string Wisp_Tail_Model = "Abilities\\Spells\\NightElf\\SpiritOfVengeance\\SpiritOfVengeanceBirthMissile.mdx"
// other models that seem good as a tail
// "Abilities\\Spells\\Other\\HealingSpray\\HealBottleMissile.mdx"
// "Abilities\\Spells\\Undead\\DevourMagic\\DevourMagicBirthMissile.mdx"
// "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdx"
// "Abilities\\Weapons\\ProcMissile\\ProcMissile.mdx"
// "abilities\\weapons\\zigguratmissile\\zigguratmissile.mdx"
// if a target unit can get into a zeppelin we hide the wisp
private constant boolean Check_Unit_Loaded = false
endglobals
private keyword Any
native UnitAlive takes unit u returns boolean
// we use these offsets to position the wisp such that it kind of appears attached
// on the back of the units
//
private struct Back_Offsets extends array
real dist // how far away in the opposite of the unit's facing direction
real fly_height // how far up/z direction
real origin_offset // some models use this so that the wisp appears properly aligned
endstruct
globals
private hashtable ht = InitHashtable()
private integer next_back_offsets = 0
endglobals
private function back_offets takes integer unit_id, real dist, real fly_height, real origin_offset returns nothing
local Back_Offsets bo
set next_back_offsets = next_back_offsets + 1
set bo = Back_Offsets(next_back_offsets)
set bo.dist = dist
set bo.fly_height = fly_height
set bo.origin_offset = origin_offset
call SaveInteger(ht, 0, unit_id, bo)
endfunction
private function back_offsets_init takes nothing returns nothing
local Back_Offsets default_back_offsets = Back_Offsets(0)
set default_back_offsets.dist = 40.0
set default_back_offsets.fly_height = 72.0
set default_back_offsets.origin_offset = 16.0
call back_offets('Hpal', /*dist:*/ 40.0, /*fly_height:*/ 72.0, /*origin_offset:*/ 16.0)
call back_offets('Otch', 45.0, 128.0, 0.0)
call back_offets('hfoo', 30.0, 40.0, 0.0)
call back_offets('ugho', 30.0, 50.0, 0.0)
call back_offets('ogru', 35.0, 50.0, 16.0)
call back_offets('edry', 11.0, 96.0, 16.0)
call back_offets('emtg', 30.0, 200.0, -8.0)
call back_offets('owyv', 22.0, 22.0, 16.0)
call back_offets('okod', 88.0, 106.0, 0.0)
call back_offets('abom', 22.0, 138.0, 0.0)
endfunction
private function get_back_offsets takes integer unit_id returns Back_Offsets
return Back_Offsets(LoadInteger(ht, 0, unit_id))
endfunction
// if we don't know the unit's scale we can't position the wisp on it's back
//
private function get_unit_scale takes unit u returns real
// there is no GetUnitScale native...
static if LIBRARY_Utils then
// but we could use leandrotp's memory library to get the value anyway
local integer p = ConvertHandle(u)
if p == 0 then
return 1.0
endif
return RMemory[(Memory[(p+0x28)/4]+0xE8)/4]
else
return 1.0
endif
endfunction
private function unit_set_wispering takes unit u, Any ws returns nothing
call SaveInteger(ht, 1, GetHandleId(u), ws)
endfunction
private function unit_get_wispering takes unit u returns Any
return LoadInteger(ht, 1, GetHandleId(u))
endfunction
private function unit_remove_wispering takes unit u returns nothing
call RemoveSavedInteger(ht, 1, GetHandleId(u))
endfunction
private function unit_set_wisp takes unit u, Any w returns nothing
call SaveInteger(ht, 2, GetHandleId(u), w)
endfunction
private function unit_get_wisp takes unit u returns Any
return LoadInteger(ht, 2, GetHandleId(u))
endfunction
private function unit_remove_wisp takes unit u returns nothing
call RemoveSavedInteger(ht, 2, GetHandleId(u))
endfunction
private function wisp_spawn_count takes integer level returns integer
return 4+level
endfunction
private function wisp_spawn_interval takes integer level returns real
return 0.5
endfunction
private function wisp_speed takes integer level returns real
return 500.0
endfunction
private function wisp_fly_duration takes integer level returns real
// wisps fly for at most this many seconds or until they've attached to a target
return 3.0
endfunction
private function wisp_attached_duration takes integer level returns real
return 5.0+level
endfunction
private function wisp_heal takes integer level returns real
return 15.0+5.0*level
endfunction
private function wisp_damage takes integer level returns real
return 15.0+5.0*level
endfunction
private function wisp_damage_target takes Any w, unit target, real damage returns nothing
local boolean melee_attack = false
local boolean range_attack = true
local attacktype attack_type = ATTACK_TYPE_NORMAL // spell
local damagetype damage_type = DAMAGE_TYPE_UNIVERSAL // ignore armor value
local weapontype weapon_type = WEAPON_TYPE_WHOKNOWS
call UnitDamageTarget(w.ws.caster, target, damage, melee_attack, range_attack, attack_type, damage_type, weapon_type)
endfunction
private function wisp_damage_aoe takes Any w, real x, real y returns nothing
local boolean melee_attack = false
local boolean range_attack = true
local attacktype attack_type = ATTACK_TYPE_NORMAL // spell
local damagetype damage_type = DAMAGE_TYPE_UNIVERSAL // ignore armor value
local weapontype weapon_type = WEAPON_TYPE_WHOKNOWS
local real radius = 200.0+50.0*w.ws.abi_level
local real damage = 75.0+25.0*w.ws.abi_level
call UnitDamagePoint(w.ws.caster, 0.0, radius, x, y, damage, melee_attack, range_attack, attack_type, damage_type, weapon_type)
endfunction
private function target_allowed takes unit target, /*Wisp*/ Any w returns boolean
return UnitAlive(target)
endfunction
globals
private integer Wispering = 1
private integer Wisp = 2
endglobals
globals
private integer Wisp_State_Flying = 1
private integer Wisp_State_Attached = 2
private integer Wisp_State_Dropping = 3
private integer Wisp_State_Dead = 4
endglobals
private struct Any
integer ty
Any prev
Any next
real dp_x
real dp_y
real dpo_x
real dpo_y
real t_mult
integer effect_delay
// Wispering
//
// Any prev
// Any next
integer ref_count
unit caster
player caster_owner
integer abi_level
integer spawn_count
real spawn_interval
integer spawn_delay
real spawn_x
real spawn_y
// real dp_x
// real dp_y
// real dpo_x
// real dpo_y
// real t_mult
integer fly_duration
integer attached_duration
// integer effect_delay
real heal
real damage
// Wisp
//
// Any prev
// Any next
integer state
unit u
Any ws
real p_x
real p_y
real p_z
// real dp_x
// real dp_y
// real dpo_x
// real dpo_y
real dpo_z
// real t_mult
real t
integer duration
effect tail
unit target
Back_Offsets bo
// integer effect_delay
endstruct
globals
private constant integer Death_Animation_Duration = R2I(2.0 * (1.0 / Dt))
private real Playable_Min_X
private real Playable_Min_Y
private real Playable_Max_X
private real Playable_Max_Y
endglobals
globals
private Any update_list = Any(0)
private code update_cb
private timer ticker = CreateTimer()
private group targets = CreateGroup()
endglobals
private function Any_create takes integer ty returns Any
local Any a = Any.create()
set a.ty = ty
return a
endfunction
private function add_to_update_list takes Any node returns nothing
set node.prev = update_list.prev
set node.next = update_list
set node.prev.next = node
set node.next.prev = node
endfunction
private function remove_from_update_list takes Any node returns nothing
set node.prev.next = node.next
set node.next.prev = node.prev
endfunction
private function Wispering_create takes unit caster, real stx, real sty returns Any
local real x
local real y
local real angle_between
local Any ws
local real speed
set ws = Any_create(Wispering)
set ws.ref_count = 1
set ws.caster = caster
call unit_set_wispering(ws.caster, ws)
set ws.caster_owner = GetOwningPlayer(caster)
set ws.abi_level = GetUnitAbilityLevel(caster, Ability_Id)
set ws.spawn_count = wisp_spawn_count(ws.abi_level)
set ws.spawn_interval = wisp_spawn_interval(ws.abi_level)
set ws.spawn_delay = 0
set x = GetUnitX(caster)
set y = GetUnitY(caster)
set angle_between = Atan2(sty - y, stx - x)
set ws.dp_x = Cos(angle_between)
set ws.dp_y = Sin(angle_between)
set ws.dpo_x = 128.0*-ws.dp_y
set ws.dpo_y = 128.0*ws.dp_x
set ws.spawn_x = x + 32.0*ws.dp_x
set ws.spawn_y = y + 32.0*ws.dp_y
set speed = wisp_speed(ws.abi_level)
set ws.dp_x = speed*ws.dp_x * Dt
set ws.dp_y = speed*ws.dp_y * Dt
set ws.t_mult = (speed / 400.0) * Tau
set ws.fly_duration = R2I(wisp_fly_duration(ws.abi_level) * (1.0 / Dt))
set ws.attached_duration = R2I(wisp_attached_duration(ws.abi_level) * (1.0 / Dt))
set ws.effect_delay = R2I(1.0 * (1.0 / Dt))
set ws.heal = wisp_heal(ws.abi_level)
set ws.damage = wisp_damage(ws.abi_level)
call add_to_update_list(ws)
if ws.prev == update_list then
call TimerStart(ticker, Dt, true, update_cb)
endif
return ws
endfunction
private function Wispering_destroy takes Any ws returns nothing
set ws.caster = null
call ws.destroy()
endfunction
private function inc_ref_count takes Any ws returns nothing
set ws.ref_count = ws.ref_count + 1
endfunction
private function dec_ref_count takes Any ws returns nothing
set ws.ref_count = ws.ref_count - 1
if ws.ref_count == 0 then
call Wispering_destroy(ws)
endif
endfunction
private function Wisp_create takes Any ws returns Any
local Any w = Any_create(Wisp)
set w.ws = ws
call inc_ref_count(ws)
set w.state = Wisp_State_Flying
set w.u = CreateUnit(ws.caster_owner, Wisp_id, ws.spawn_x, ws.spawn_y, 0.0)
set w.p_x = ws.spawn_x
set w.p_y = ws.spawn_y
set w.p_z = 128.0
call SetUnitFlyHeight(w.u, w.p_z, 0.0)
set w.dp_x = ws.dp_x
set w.dp_y = ws.dp_y
set w.dpo_x = ws.dpo_x
set w.dpo_y = ws.dpo_y
set w.dpo_z = 128.0
if (ws.spawn_count - (ws.spawn_count / 2) * 2) == 0 then
set w.dpo_x = -w.dpo_x
set w.dpo_y = -w.dpo_y
endif
set w.t_mult = ws.t_mult
set w.t = 0.0
set w.duration = ws.fly_duration
set w.tail = AddSpecialEffectTarget(Wisp_Tail_Model, w.u, "chest")
call add_to_update_list(w)
return w
endfunction
private function Wisp_destroy takes Any w returns nothing
call remove_from_update_list(w)
call DestroyEffect(w.tail)
set w.tail = null
call RemoveUnit(w.u)
set w.u = null
call unit_remove_wisp(w.target)
set w.target = null
call dec_ref_count(w.ws)
call w.destroy()
endfunction
private function wispering_on_tick takes Any ws returns nothing
if ws.spawn_count == 0 then
// stop channeling
// this also calls on_spell_endcast
//
call IssueImmediateOrderById(ws.caster, 0xD0019) // hold position
else
if ws.spawn_delay == 0 then
set ws.spawn_delay = R2I(ws.spawn_interval * (1.0 / Dt))
set ws.spawn_count = ws.spawn_count - 1
call Wisp_create(ws)
else
set ws.spawn_delay = ws.spawn_delay - 1
endif
endif
endfunction
private function wisp_set_state_dead takes Any w returns nothing
call UnitRemoveAbility(w.target, Heal_Buff_Id)
static if Check_Unit_Loaded then
if IsUnitLoaded(w.target) then
call Wisp_destroy(w)
return
endif
endif
call KillUnit(w.u)
set w.duration = Death_Animation_Duration
set w.state = Wisp_State_Dead
endfunction
private function wisp_set_state_dropping takes Any w, real z returns nothing
call UnitRemoveAbility(w.target, Damage_Buff_Id)
static if Check_Unit_Loaded then
// ideally we would want to iterate over all the units in the
// transport and damage them, but we can't...
// so currently loading a unit into a transport defuses the
// wisp's explosion
if IsUnitLoaded(w.target) then
call Wisp_destroy(w)
return
endif
endif
call SetUnitAnimationByIndex(w.u, 1)
set w.p_x = GetUnitX(w.target)
set w.p_y = GetUnitY(w.target)
set w.p_z = z
set w.dpo_z = 64.0*Dt
set w.state = Wisp_State_Dropping
endfunction
// we can call this function when we want to remove the wisp from a unit
// before it's attached duration has expired;
// example: the unit blinks, but the wisp can't follow it because it's not magical
//
public function unit_drop_wisp takes unit u returns boolean
local Any w = unit_get_wisp(u)
local real z
if w == 0 then
return false
endif
if IsUnitAlly(u, w.ws.caster_owner) then
call wisp_set_state_dead(w)
else
set z = GetUnitFlyHeight(w.target) + get_unit_scale(w.target) * w.bo.fly_height
call wisp_set_state_dropping(w, z)
endif
return true
endfunction
private function wisp_on_tick takes Any w returns nothing
local real x
local real y
local real z
local real ang
local unit u
local Back_Offsets bo
local real facing
local real scale
local real dist
local boolean is_ally
if w.state == Wisp_State_Flying then
set x = w.p_x
set y = w.p_y
set x = x + w.dp_x
set y = y + w.dp_y
set w.p_x = x
set w.p_y = y
set ang = w.t_mult * w.t
set w.t = w.t + Dt
set x = x + w.dpo_x * Sin(ang)
set y = y + w.dpo_y * Sin(ang)
set z = w.p_z + w.dpo_z * Cos(ang)
if (x < Playable_Min_X) or (x > Playable_Max_X) /*
*/ or (y < Playable_Min_Y) or (y > Playable_Max_Y) then
call Wisp_destroy(w)
return
endif
call SetUnitX(w.u, x)
call SetUnitY(w.u, y)
call SetUnitFlyHeight(w.u, z, 0.0)
call GroupEnumUnitsInRange(targets, w.p_x, w.p_y, 96.0, null)
call GroupRemoveUnit(targets, w.ws.caster)
loop
set u = FirstOfGroup(targets)
exitwhen u == null
call GroupRemoveUnit(targets, u)
if IsUnitInRangeXY(u, w.p_x, w.p_y, 96.0) /*
*/ and unit_get_wisp(u) == 0 /*
*/ and target_allowed(u, w) /*
*/ then
set w.target = u
call unit_set_wisp(u, w)
set w.bo = get_back_offsets(GetUnitTypeId(u))
call SetUnitAnimationByIndex(w.u, 4)
call DestroyEffect(w.tail)
if IsUnitAlly(u, w.ws.caster_owner) then
call UnitAddAbility(u, Heal_Buff_Id)
else
call UnitAddAbility(u, Damage_Buff_Id)
endif
set w.duration = w.ws.attached_duration
set w.state = Wisp_State_Attached
set u = null
call GroupClear(targets)
return
endif
endloop
if w.duration == 0 then
call Wisp_destroy(w)
return
endif
set w.duration = w.duration - 1
elseif w.state == Wisp_State_Attached then
set is_ally = IsUnitAlly(w.target, w.ws.caster_owner)
set x = GetUnitX(w.target)
set y = GetUnitY(w.target)
set facing = 0.017453 * GetUnitFacing(w.target)
set scale = get_unit_scale(w.target)
set dist = scale * w.bo.dist
set x = x + dist*-Cos(facing) + w.bo.origin_offset
set y = y + dist*-Sin(facing) + w.bo.origin_offset
set z = GetUnitFlyHeight(w.target) + scale * w.bo.fly_height
call SetUnitX(w.u, x)
call SetUnitY(w.u, y)
call SetUnitFlyHeight(w.u, z, 0.0)
if w.effect_delay == 0 then
set w.effect_delay = w.ws.effect_delay
if is_ally then
call SetUnitState(w.target, UNIT_STATE_LIFE, w.ws.heal + GetUnitState(w.target, UNIT_STATE_LIFE))
else
call wisp_damage_target(w, w.target, w.ws.damage)
endif
endif
set w.effect_delay = w.effect_delay - 1
static if Check_Unit_Loaded then
if IsUnitLoaded(w.target) then
call SetUnitVertexColor(w.u, 255, 255, 255, 0)
call SetUnitAnimationByIndex(w.u, 1)
else
call SetUnitVertexColor(w.u, 255, 255, 255, 255)
call SetUnitAnimationByIndex(w.u, 4)
endif
endif
if w.duration == 0 or not UnitAlive(w.target) then
if is_ally then
call wisp_set_state_dead(w)
else
call wisp_set_state_dropping(w, z)
endif
return
endif
set w.duration = w.duration - 1
elseif w.state == Wisp_State_Dropping then
if w.p_z < 16.0 then
call DestroyEffect(AddSpecialEffect("Units\\NightElf\\Wisp\\WispExplode.mdl", w.p_x, w.p_y))
call DestroyEffect(AddSpecialEffect("Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl", w.p_x, w.p_y))
call wisp_damage_aoe(w, w.p_x, w.p_y)
call wisp_set_state_dead(w)
return
endif
set w.p_z = w.p_z - w.dpo_z
call SetUnitFlyHeight(w.u, w.p_z, 0.0)
set w.dpo_z = w.dpo_z + 9.8*Dt
elseif w.state == Wisp_State_Dead then
if w.duration == 0 then
call Wisp_destroy(w)
return
endif
set w.duration = w.duration - 1
endif
endfunction
private function update takes nothing returns nothing
local Any a
local Any a_next
local Any ws
local integer ty
set a = update_list.next
if a == update_list then
call PauseTimer(ticker)
return
endif
loop
exitwhen a == update_list
set a_next = a.next
set ty = a.ty
if ty == Wispering then
call wispering_on_tick(a)
elseif ty == Wisp then
call wisp_on_tick(a)
endif
set a = a_next
endloop
endfunction
private function on_spell_channel takes nothing returns nothing
call Wispering_create(GetTriggerUnit(), GetSpellTargetX(), GetSpellTargetY())
endfunction
private function on_spell_endcast takes nothing returns nothing
local Any ws = unit_get_wispering(GetTriggerUnit())
call unit_remove_wispering(ws.caster)
call remove_from_update_list(ws)
call dec_ref_count(ws)
endfunction
private function on_spell takes nothing returns nothing
local eventid ev
if Ability_Id != GetSpellAbilityId() then
return
endif
set ev = GetTriggerEventId()
if ev == EVENT_PLAYER_UNIT_SPELL_CHANNEL then
call on_spell_channel()
elseif ev == EVENT_PLAYER_UNIT_SPELL_ENDCAST then
call on_spell_endcast()
endif
endfunction
private function init takes nothing returns nothing
local trigger trg
local rect rct
call back_offsets_init()
set trg = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_CHANNEL)
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddAction(trg, function on_spell)
set update_cb = function update
set rct = GetPlayableMapRect()
set Playable_Min_X = GetRectMinX(rct)
set Playable_Min_Y = GetRectMinY(rct)
set Playable_Max_X = GetRectMaxX(rct)
set Playable_Max_Y = GetRectMaxY(rct)
set rct = null
endfunction
endlibrary
library playground initializer init
globals
player red = Player(0)
player blue = Player(1)
unit hero
integer array unit_ids
integer unit_ids_count = 0
group units = CreateGroup()
endglobals
function unit_ids_init takes nothing returns nothing
set unit_ids[1] = 'Hpal'
set unit_ids[2] = 'Otch'
set unit_ids[3] = 'hfoo'
set unit_ids[4] = 'ugho'
set unit_ids[5] = 'ogru'
set unit_ids[6] = 'edry'
set unit_ids[7] = 'emtg'
set unit_ids[8] = 'owyv'
set unit_ids[9] = 'okod'
set unit_ids[10] = 'uabo'
set unit_ids_count = 10
endfunction
function reset_playground takes nothing returns nothing
local real x
local real y
local integer i
local unit u
set i = 0
loop
exitwhen i == 2
call GroupEnumUnitsOfPlayer(units, Player(i), null)
loop
set u = FirstOfGroup(units)
exitwhen u == null
call GroupRemoveUnit(units, u)
if u != hero then
call RemoveUnit(u)
endif
endloop
set i = i + 1
endloop
set i = 1
set x = -960.0
set y = 900.0
loop
exitwhen i > unit_ids_count
set u = CreateUnit(red, unit_ids[i], x, y, 270.0)
if unit_ids[i] == 'Hpal' then
call UnitAddAbility(u, 'AEbl')
elseif unit_ids[i] == 'Otch' then
call UnitAddItemById(u, 'stel')
endif
set u = null
set x = x + 192.0
set i = i + 1
endloop
set i = 1
set x = -960.0
set y = -1408.0
loop
exitwhen i > unit_ids_count
call CreateUnit(blue, unit_ids[i], x, y, 90.0)
set x = x + 192.0
set i = i + 1
endloop
if IsUnitType(hero, UNIT_TYPE_DEAD) then
call ReviveHero(hero, GetUnitX(hero), GetUnitY(hero), false)
endif
call SetUnitState(hero, UNIT_STATE_LIFE, GetUnitState(hero, UNIT_STATE_MAX_LIFE))
call SetUnitState(hero, UNIT_STATE_MANA, GetUnitState(hero, UNIT_STATE_MAX_MANA))
endfunction
private function init takes nothing returns nothing
local trigger trg
call unit_ids_init()
call SetPlayerAlliance(blue, red, ALLIANCE_SHARED_VISION, true)
call SetPlayerAlliance(blue, red, ALLIANCE_SHARED_CONTROL, true)
set hero = CreateUnit(red, 'E001', 0.0, 772.0, 270.0)
call SetHeroLevelBJ(hero, 5, false)
call SelectUnitForPlayerSingle(hero, red)
call SetCameraPositionForPlayer(red, GetUnitX(hero), GetUnitY(hero) - 384.0)
call reset_playground()
call DisplayTimedTextToPlayer(red, 0.0, 0.0, 5.0, "Press Esc to reset the playground")
set trg = CreateTrigger()
call TriggerRegisterPlayerEventEndCinematic(trg, red)
call TriggerAddAction(trg, function reset_playground)
endfunction
endlibrary
library unitdropwispexample initializer init uses Wispering
globals
private hashtable ht = InitHashtable()
endglobals
private function on_staff_of_teleportation_endcast takes nothing returns nothing
local timer tmr = GetExpiredTimer()
local integer hid = GetHandleId(tmr)
local unit u = LoadUnitHandle(ht, 0, hid)
call Wispering_unit_drop_wisp(u)
set u = null
call RemoveSavedHandle(ht, 0, hid)
call DestroyTimer(tmr)
set tmr = null
endfunction
private function on_spell_effect takes nothing returns nothing
local integer abi = GetSpellAbilityId()
local timer tmr
if abi != 'AEbl' and abi != 'AImt' then
return
endif
// wisps cannot travel through space instantaneously
//
if abi == 'AEbl' then // blink
call Wispering_unit_drop_wisp(GetTriggerUnit())
elseif abi == 'AImt' then // staff of teleportation
set tmr = CreateTimer()
call SaveUnitHandle(ht, 0, GetHandleId(tmr), GetTriggerUnit())
call TimerStart(tmr, 3.0, false, function on_staff_of_teleportation_endcast)
endif
endfunction
private function init takes nothing returns nothing
local trigger trg
set trg = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(trg, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(trg, function on_spell_effect)
endfunction
endlibrary
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i setobjecttype("units")
//! i createobject("ewsp","e000")
//! i makechange(current,"uabi","Arav,Aloc")
//! i makechange(current,"ubui","")
//! i makechange(current,"ufoo","0")
//! i makechange(current,"umvt","fly")
//! i makechange(current,"unam","wisp")
//! i makechange(current,"upgr","")
//! i makechange(current,"usca","0.5")
//! i makechange(current,"utip","")
//! i makechange(current,"utub","")
//! i createobject("Emns","E001")
//! i makechange(current,"uhab","A000")
//! i setobjecttype("abilities")
//! i createobject("ANcl","A000")
//! i makechange(current,"Ncl1","1","99999.0")
//! i makechange(current,"Ncl1","2","99999.0")
//! i makechange(current,"Ncl1","3","99999.0")
//! i makechange(current,"Ncl2","1","3")
//! i makechange(current,"Ncl2","2","3")
//! i makechange(current,"Ncl2","3","3")
//! i makechange(current,"Ncl3","1","17")
//! i makechange(current,"Ncl3","2","17")
//! i makechange(current,"Ncl3","3","17")
//! i makechange(current,"Ncl4","1","0.0")
//! i makechange(current,"Ncl4","2","0.0")
//! i makechange(current,"Ncl4","3","0.0")
//! i makechange(current,"Ncl5","1","0")
//! i makechange(current,"Ncl5","2","0")
//! i makechange(current,"Ncl5","3","0")
//! i makechange(current,"Ncl6","1","charm")
//! i makechange(current,"Ncl6","2","charm")
//! i makechange(current,"Ncl6","3","charm")
//! i makechange(current,"aani","spell")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNWispSplode.blp")
//! i makechange(current,"abpx","0")
//! i makechange(current,"abpy","2")
//! i makechange(current,"acat","")
//! i makechange(current,"acdn","1","1.0")
//! i makechange(current,"acdn","2","1.0")
//! i makechange(current,"acdn","3","1.0")
//! i makechange(current,"achd","0")
//! i makechange(current,"aeat","")
//! i makechange(current,"ahky","Q")
//! i makechange(current,"amcs","1","10")
//! i makechange(current,"amcs","2","10")
//! i makechange(current,"amcs","3","10")
//! i makechange(current,"anam","Wispering")
//! i makechange(current,"aran","1","800.0")
//! i makechange(current,"aran","2","800.0")
//! i makechange(current,"aran","3","800.0")
//! i makechange(current,"arar","ReplaceableTextures\\CommandButtons\\BTNWispSplode.blp")
//! i makechange(current,"aret","Learn |cff80DCDCWispering|r Level %d (|cff80DCDCQ|r)")
//! i makechange(current,"arhk","Q")
//! i makechange(current,"arut","Send a flock of |cff80DCDCWisps|r to |cff80DCDCHeal|r/|cff80DCDCDamage|r\nfriendly/enemy units over time.\n\n|cff80DCDCWisps|r: 5/6/7\n|cff80DCDCHeal|r: 20/25/30\n|cff80DCDCDamage|r: 20/25/30\n|cff80DCDCDuration|r: 6/7/8")
//! i makechange(current,"ata0","")
//! i makechange(current,"atat","")
//! i makechange(current,"atp1","1","|cff80DCDCWispering|r Level 1 (|cff80DCDCQ|r)")
//! i makechange(current,"atp1","2","|cff80DCDCWispering|r Level 2 (|cff80DCDCQ|r)")
//! i makechange(current,"atp1","3","|cff80DCDCWispering|r Level 3 (|cff80DCDCQ|r)")
//! i makechange(current,"aub1","1","Send a flock of |cff80DCDCWisps|r to |cff80DCDCHeal|r/|cff80DCDCDamage|r\nfriendly/enemy units over time.\n\n|cff80DCDCWisps|r: |cff80DCDC5|r/6/7\n|cff80DCDCHeal|r: |cff80DCDC20|r/25/30\n|cff80DCDCDamage|r: |cff80DCDC20|r/25/30\n|cff80DCDCDuration|r: |cff80DCDC6|r/7/8")
//! i makechange(current,"aub1","2","Send a flock of |cff80DCDCWisps|r to |cff80DCDCHeal|r/|cff80DCDCDamage|r\nfriendly/enemy units over time.\n\n|cff80DCDCWisps|r: 5/|cff80DCDC6|r/7\n|cff80DCDCHeal|r: 20/|cff80DCDC25|r/30\n|cff80DCDCDamage|r: 20/|cff80DCDC25|r/30\n|cff80DCDCDuration|r: 6/|cff80DCDC7|r/8")
//! i makechange(current,"aub1","3","Send a flock of |cff80DCDCWisps|r to |cff80DCDCHeal|r/|cff80DCDCDamage|r\nfriendly/enemy units over time.\n\n|cff80DCDCWisps|r: 5/6/|cff80DCDC7|r\n|cff80DCDCHeal|r: 20/25/|cff80DCDC30|r\n|cff80DCDCDamage|r: 20/25/|cff80DCDC30|r\n|cff80DCDCDuration|r: 6/7/|cff80DCDC8|r")
//! i createobject("Aasl","A001")
//! i makechange(current,"Slo1","1","0.0")
//! i makechange(current,"aare","1","0.0")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNWispSplode.blp")
//! i makechange(current,"abuf","1","B000")
//! i makechange(current,"anam","Wispering Heal Buff")
//! i makechange(current,"ansf","")
//! i makechange(current,"atar","1","self")
//! i createobject("Aasl","A002")
//! i makechange(current,"Slo1","1","0.0")
//! i makechange(current,"aare","1","0.0")
//! i makechange(current,"aart","ReplaceableTextures\\CommandButtons\\BTNWispSplode.blp")
//! i makechange(current,"abuf","1","B001")
//! i makechange(current,"anam","Wispering Damage Buff")
//! i makechange(current,"ansf","")
//! i makechange(current,"atar","1","self")
//! i setobjecttype("buffs")
//! i createobject("Basl","B000")
//! i makechange(current,"fart","ReplaceableTextures\\CommandButtons\\BTNWisp.blp")
//! i makechange(current,"fnsf"," (Wispering Heal Buff)")
//! i makechange(current,"ftat","")
//! i makechange(current,"ftip","|cff41FF41Wisp|r")
//! i makechange(current,"fube","This unit is being healed by the wisp on it's back.")
//! i createobject("Basl","B001")
//! i makechange(current,"fart","ReplaceableTextures\\CommandButtons\\BTNWisp.blp")
//! i makechange(current,"fnsf"," (Wispering Damage Buff)")
//! i makechange(current,"ftat","")
//! i makechange(current,"ftip","|cffEE2222Wisp|r")
//! i makechange(current,"fube","This unit is taking damage over time from the wisp on it's back.")
//! endexternalblock