Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Ht
//! novjass
// Usage:
globals
Ht ht
endglobals
function main takes nothing returns nothing
local integer hid
local integer my_int
// on init
set ht = Ht.create()
// in "create"
call ht.select()
set hid = ht.h2i(/*CreateTrigger()*/ null)
set my_int = 42
set ht[hid].int = my_int
// inlined:
set ht.select()[ht.h2i(/*CreateTrigger()*/ null)].int = 42
// in "handler"
call ht.select()
set hid = ht.h2i(/*GetTriggeringTrigger()*/ null)
set my_int = ht[hid].int
// inlined:
set my_int = ht.select()[ht.h2i(/*CreateTrigger()*/ null)].int
if ht[hid].int_e() then
call BJDebugMsg(I2S(my_int))
call ht[hid].int_d()
if not ht[hid].int_e() then
call BJDebugMsg("removed " + I2S(my_int))
endif
endif
endfunction
//! endnovjass
globals
private hashtable ht = InitHashtable()
endglobals
struct Ht
private static integer pk1
private static integer pk2
static method h2i takes handle h returns integer
return GetHandleId(h)
endmethod
static method s2i takes string s returns integer
return StringHash(s)
endmethod
// set the parent key, and make 2 copies of it
method select takes nothing returns thistype
set pk1 = this
set pk2 = this
return this
endmethod
method operator[] takes integer i returns thistype
return this + i
endmethod
method operator int= takes integer x returns nothing
call SaveInteger(ht, pk1, this - pk2, x) // this - pk2 = i
endmethod
method operator int takes nothing returns integer
return LoadInteger(ht, pk1, this - pk2)
endmethod
method int_e takes nothing returns boolean // int-exists?
return HaveSavedInteger(ht, pk1, this - pk2)
endmethod
method int_d takes nothing returns nothing // int-delete/destroy
call RemoveSavedInteger(ht, pk1, this - pk2)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library updatelist
// The updatelist module requires that the implementing struct has defined the 2 static fields:
// static real ul_timer_frequency = 1.0 / 32.0
// static code ul_update_handler = function thistype.ul_update
//
module updatelist
readonly static timer ul_timer = CreateTimer()
readonly static thistype array ul_list
readonly static integer array ul_pos
readonly static integer ul_count = 0
method ul_add takes nothing returns nothing
set ul_count = ul_count + 1
set ul_pos[this] = ul_count
set ul_list[ul_count] = this
if ul_count == 1 then
call TimerStart(ul_timer, ul_timer_frequency, true, ul_update_handler)
endif
endmethod
method ul_remove takes nothing returns nothing
set ul_pos[ ul_list[ul_count] ] = ul_pos[this]
set ul_list[ ul_pos[this] ] = ul_list[ul_count]
set ul_count = ul_count - 1
if ul_count <= 0 then
call PauseTimer(ul_timer)
endif
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library v3 initializer init
static if DEBUG_MODE then
function panic takes string s returns nothing
call BJDebugMsg("|cffFF0000[v3] error: " + s)
if 1 / 0 == 1 then
endif
endfunction
endif
globals
private real vlen = 0.0
public location loc = Location(0.0, 0.0)
public real loc_x = 0.0
public real loc_y = 0.0
public real loc_z = 0.0
private real zl = 0.0
private real zr = 0.0
private real zu = 0.0
private real zd = 0.0
endglobals
globals
private v3 fl_head = 1
private v3 vtmp = 8001
private v3 array next
endglobals
private function init takes nothing returns nothing
local v3 v
// initialize our free list of owned vectors
// owned vectors need to be manually "destroyed"
set v = 1
loop
exitwhen v >= 8000
set next[v] = v3(v + 1)
set v = v + 1
endloop
set next[v3(8000)] = 0
// initialize our circular list of temporary vectors
// temporary vectors are automatically reused, i.e
// they can't be "destroyed"
set v = 8001
loop
exitwhen v >= 8190
set next[v] = v3(v + 1)
set v = v + 1
endloop
set next[v3(8190)] = 8001
// do we really need 190 temporary vectors?
endfunction
struct v3 extends array
real x
real y
real z
method to_owned takes nothing returns v3
local v3 v = fl_head
static if DEBUG_MODE then
if v == 0 then
call panic("cannot allocate a v3 instance|r")
endif
endif
set fl_head = next[fl_head]
set v.x = this.x
set v.y = this.y
set v.z = this.z
return v
endmethod
method destroy takes nothing returns nothing
static if DEBUG_MODE then
if this >= 8001 then
call panic("cannot destroy temporary v3(" + I2S(this) + ")")
endif
endif
set next[this] = fl_head
set fl_head = this
endmethod
// can't overload operator=, and set is a keyword
method set_eq takes v3 v returns nothing
set this.x = v.x
set this.y = v.y
set this.z = v.z
endmethod
method operator== takes v3 v returns boolean
return this.x == v.x and this.y == v.y and this.z == v.z
endmethod
method len takes nothing returns real
return SquareRoot(this.x * this.x + this.y * this.y + this.z * this.z)
endmethod
method len_sq takes nothing returns real
return this.x * this.x + this.y * this.y + this.z * this.z
endmethod
method len_xy takes nothing returns real
return SquareRoot(this.x * this.x + this.y * this.y)
endmethod
method len_xy_sq takes nothing returns real
return this.x * this.x + this.y * this.y
endmethod
method dot takes v3 v returns real
return this.x * v.x + this.y * v.y + this.z * v.z
endmethod
method ang_rot takes nothing returns real
return Atan2(this.y, this.x)
endmethod
method ang_aoa takes nothing returns real
return Atan2(this.z, SquareRoot(this.x * this.x + this.y * this.y))
endmethod
endstruct
//! textmacro v3_result
local v3 result = vtmp
set vtmp = next[vtmp]
//! endtextmacro
// we would get a "Identifier already in use: v3" error if we call this function v3, i.e
// can't have a struct and a function with the same name :/
//
function v3c takes real x, real y, real z returns v3
//! runtextmacro v3_result()
set result.x = x
set result.y = y
set result.z = z
return result
endfunction
function v3a takes v3 a, v3 b returns v3
//! runtextmacro v3_result()
set result.x = a.x + b.x
set result.y = a.y + b.y
set result.z = a.z + b.z
return result
endfunction
function v3s takes v3 a, v3 b returns v3
//! runtextmacro v3_result()
set result.x = a.x - b.x
set result.y = a.y - b.y
set result.z = a.z - b.z
return result
endfunction
function v3sc takes real s, v3 v returns v3
//! runtextmacro v3_result()
set result.x = s * v.x
set result.y = s * v.y
set result.z = s * v.z
return result
endfunction
function v3sc2 takes v3 v, real s returns v3
//! runtextmacro v3_result()
set result.x = s * v.x
set result.y = s * v.y
set result.z = s * v.z
return result
endfunction
// normalize-or-z
function v3n takes v3 v returns v3
//! runtextmacro v3_result()
set vlen = SquareRoot(v.x * v.x + v.y * v.y + v.z * v.z)
if not (vlen != 0.0) then
set result.x = 0.0
set result.y = 0.0
set result.z = 1.0
else
set vlen = 1.0 / vlen
set result.x = v.x * vlen
set result.y = v.y * vlen
set result.z = v.z * vlen
endif
return result
endfunction
function v3_from_polar takes real angle, real radius returns v3
//! runtextmacro v3_result()
set result.x = radius * Cos(angle)
set result.y = radius * Sin(angle)
set result.z = 0.0
return result
endfunction
function v3_from_spherical takes real rot, real aoa, real radius returns v3
//! runtextmacro v3_result()
set result.x = radius * Cos(rot) * Cos(aoa)
set result.y = radius * Sin(rot) * Cos(aoa)
set result.z = radius * Sin(aoa)
return result
endfunction
function v3_from_unit takes unit u returns v3
//! runtextmacro v3_result()
set result.x = GetUnitX(u)
set result.y = GetUnitY(u)
call MoveLocation(loc, result.x, result.y)
set result.z = GetLocationZ(loc)
return result
endfunction
function v3_from_spell_target takes nothing returns v3
//! runtextmacro v3_result()
set result.x = GetSpellTargetX()
set result.y = GetSpellTargetY()
call MoveLocation(loc, result.x, result.y)
set result.z = GetLocationZ(loc)
return result
endfunction
function v3_terrain_normal takes real x, real y returns v3
//! runtextmacro v3_result()
call MoveLocation(loc, x - 16.0, y)
set zl = GetLocationZ(loc)
call MoveLocation(loc, x + 16.0, y)
set zr = GetLocationZ(loc)
call MoveLocation(loc, x, y + 16.0)
set zu = GetLocationZ(loc)
call MoveLocation(loc, x, y - 16.0)
set zd = GetLocationZ(loc)
set result.x = zl - zr
set result.y = zd - zu
set result.z = 32.0
set vlen = 1.0 / SquareRoot(result.x * result.x + result.y * result.y + 1024.0)
set result.x = result.x * vlen
set result.y = result.y * vlen
set result.z = result.z * vlen
return result
endfunction
function v3_terrain_z takes real x, real y returns real
call MoveLocation(loc, x, y)
return GetLocationZ(loc)
endfunction
function v3_map takes real x, real a1, real b1, real a2, real b2 returns real
local real t = (x - a1) / (b1 - a1)
local real result = (1.0 - t) * a2 + t * b2
return result
endfunction
function v3_map_clamp takes real x, real a1, real b1, real a2, real b2 returns real
local real t = (x - a1) / (b1 - a1)
local real result = (1.0 - t) * a2 + t * b2
if result < a2 then
set result = a2
elseif result > b2 then
set result = b2
endif
return result
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library withmanacharge requires Ht, updatelist, v3
globals
private constant integer MANA_CHARGE = 'A002'
private constant integer MANA_CHARGE_ON = 852589
private constant integer MANA_CHARGE_OFF = 852590
private constant string MANA_CHARGE_HK = "X"
private constant integer MANA_DISCHARGE = 'A001'
private constant real MIN_REQUIRED_MANA = 50.0
private constant real MAX_MANA_CHARGE = 100.0
private constant real MANA_CHARGE_TEXT_SIZE = 9.0 * 0.0023
// if the mana-discharge is casted on a point within the circle with a center point
// the position of the caster and with `this` radius the mana-discharge will act
// as a propeller for the caster and would move them in the opposite direction,
// otherwise the mana-discharge acts as an instant linear projectile with a limited range
private constant real DISCHARGE_GROUND_RADIUS = 384.0
private constant real GRAVITY = -50.0
private constant real DT = 1.0 / 32.0
// UnitDamageTarget arguments
private constant boolean MELEE_ATTACK = false
private constant boolean RANGE_ATTACK = true
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // spell
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL // ignore armor value
private constant weapontype WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS
endglobals
// it takes `this` many seconds to charge up to `max-mana-charge`
private function max_mana_charge_time_for_level takes integer level returns real
return 5.0 - (level - 1)
endfunction
// the caster can only hold the max-mana-charge for `this` many seconds
// after that the charge would be discharged onto the caster
private function max_mana_charge_hold_time_for_level takes integer level returns real
return 10.0 + 2.0 * (level - 1)
endfunction
private function charge_to_velocity takes real charge returns real
return 15.0 * charge
endfunction
private function charge_to_self_damage takes real charge, integer level returns real
return (2.6667 + 0.0 * level) * charge
endfunction
private function charge_to_damage takes real charge, integer level returns real
return (1.0 + level) * charge
endfunction
private function max_hit_distance_for_level takes integer level returns real
return 800.0 + (level - 1) * 100.0
endfunction
private keyword Mana_Charge
native UnitAlive takes unit u returns boolean
private function targets_allowed takes Mana_Charge mc, unit u, real z_hit returns boolean
local real uz
local real z_diff
if not UnitAlive(u) then
return false
endif
if not IsUnitEnemy(u, mc.owner) then
return false
endif
set v3_loc_x = GetUnitX(u)
set v3_loc_y = GetUnitY(u)
call MoveLocation(v3_loc, v3_loc_x, v3_loc_y)
set uz = GetLocationZ(v3_loc) + GetUnitFlyHeight(u)
set z_diff = uz - z_hit
if z_diff < 0.0 then
set z_diff = -z_diff
endif
if z_diff > 128.0 then
return false
endif
return true
endfunction
globals
private effect effect_result
endglobals
private function AddSpecialEffectZ takes string effec_path, real x, real y, real z returns effect
local destructable platform = CreateDestructableZ('OTip', x, y, z, 0, 1, 0)
set effect_result = AddSpecialEffect(effec_path, x, y)
call RemoveDestructable(platform)
set platform = null
return effect_result
endfunction
private struct Mana_Charge
static real ul_timer_frequency = DT
static code ul_update_handler
implement updatelist
static real map_min_x
static real map_max_x
static real map_min_y
static real map_max_y
static Ht ht
static group G = CreateGroup()
static constant integer STATE_WAIT_TURN_OFF = 0
static constant integer STATE_TURN_OFF = 1
static constant integer STATE_CHARGING = 2
static constant integer STATE_MAX_CHARGED = 3
static constant integer STATE_DISCHARGING_GROUND = 4
static constant integer STATE_LIGHTNING_FADE = 5
integer state
unit caster
player owner
integer level
// turn-off
integer turn_off_delay_ticks
// charging
real mana_dec
real mana_charge
texttag tt
// max-charged
integer charge_hold_ticks
// discharging-ground
v3 p
v3 tp
v3 dp
v3 ddp
real dp_rot
// lightning-fade
integer li_max_ticks
integer li_ticks
lightning li
effect li_effect
private static code on_event_handler
private static method onInit takes nothing returns nothing
local trigger t
local rect r = GetPlayableMapRect()
set map_min_x = GetRectMinX(r) + 64.0
set map_max_x = GetRectMaxX(r) - 64.0
set map_min_y = GetRectMinY(r) + 64.0
set map_max_y = GetRectMaxY(r) - 64.0
set ht = Ht.create()
call ExecuteFunc("s__" + "thistype" + "_set_handlers")
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_ISSUED_ORDER)
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(t, on_event_handler)
endmethod
method mana_charge_off takes nothing returns nothing
// selecting a unit and calling ForceUIKey doesn't happen instantly/synchronously
// so we transition to this wait/do-nothing state until the turn-off order is issued
set this.state = STATE_WAIT_TURN_OFF
if this.owner == GetLocalPlayer() then
call SelectUnit(this.caster, true)
// issue MANA_CHARGE_OFF order without interrupting the caster's current order
call ForceUIKey(MANA_CHARGE_HK)
endif
endmethod
method mana_charge_tt_update_pos takes nothing returns nothing
// call SetTextTagPosUnit(this.tt, this.caster, /*height-offset:*/ 32.0)
call SetTextTagPos(this.tt, GetUnitX(this.caster) - 24.0, GetUnitY(this.caster), /*height-offset:*/ 144.0)
endmethod
method destroy takes nothing returns nothing
call ht[ht.h2i(this.caster)].int_d()
set this.caster = null
set this.owner = null
call this.ul_remove()
call this.deallocate()
endmethod
private static method on_event takes nothing returns nothing
local eventid ee = GetTriggerEventId()
local integer o
local real lightning_fade_time = 0.5
local real charge
local real velocity
local real dp_aoa
local real dist_xy
local real max_hit_distance
local real hit_radius = 128.0
local thistype this
local v3 tpp
local unit u
local v3 curr_p
local v3 step
local integer i
local v3 n
local v3 a
local v3 up
local v3 ua
local v3 ul
local real z
if ee == EVENT_PLAYER_UNIT_ISSUED_ORDER then
set o = GetIssuedOrderId()
if o == MANA_CHARGE_ON then
set this = allocate()
set this.caster = GetTriggerUnit()
set this.owner = GetOwningPlayer(this.caster)
if GetUnitState(this.caster, UNIT_STATE_MANA) < MIN_REQUIRED_MANA then
// ForceUIKey needs a bit of delay
set this.state = STATE_TURN_OFF
set this.turn_off_delay_ticks = R2I(0.1 / DT)
else
set this.state = STATE_CHARGING
set this.level = GetUnitAbilityLevel(this.caster, MANA_CHARGE)
set this.mana_dec = (MAX_MANA_CHARGE / max_mana_charge_time_for_level(this.level)) * DT
set this.mana_charge = 0.0
set this.tt = CreateTextTag()
call SetTextTagText(this.tt, "0 mc", MANA_CHARGE_TEXT_SIZE)
call this.mana_charge_tt_update_pos()
call SetTextTagColor(this.tt, /*R:*/ 0x0E, /*G:*/ 0x2F, /*B:*/ 0xA7, /*A:*/ 0xFF)
call SetTextTagVisibility(this.tt, IsPlayerAlly(this.owner, GetLocalPlayer()))
call SetTextTagPermanent(this.tt, true)
call UnitAddAbility(this.caster, MANA_DISCHARGE)
endif
set ht.select()[ht.h2i(this.caster)].int = this
call this.ul_add()
elseif o == MANA_CHARGE_OFF then
set this = ht.select()[ht.h2i(GetTriggerUnit())].int
call DestroyTextTag(this.tt)
set this.tt = null
call UnitRemoveAbility(this.caster, MANA_DISCHARGE)
call this.destroy()
endif
elseif ee == EVENT_PLAYER_UNIT_SPELL_EFFECT then
if GetSpellAbilityId() != MANA_DISCHARGE then
return
endif
set this = ht.select()[ht.h2i(GetTriggerUnit())].int
call DestroyTextTag(this.tt)
set this.tt = null
set this.p = v3_from_unit(this.caster)
set this.tp = v3_from_spell_target()
set tpp = v3s(this.p, this.tp)
set dist_xy = tpp.len_xy()
if dist_xy <= DISCHARGE_GROUND_RADIUS then
set this.state = STATE_DISCHARGING_GROUND
set this.p = this.p.to_owned()
set this.tp = this.tp.to_owned()
set this.dp_rot = tpp.ang_rot()
set dp_aoa = v3_map(dist_xy, DISCHARGE_GROUND_RADIUS, 0.0, 60.0, 90.0)
set charge = v3_map(this.mana_charge, 0.0, MAX_MANA_CHARGE, 0.0, 100.0)
set velocity = charge_to_velocity(charge)
set this.dp = v3_from_spherical(this.dp_rot, bj_DEGTORAD * dp_aoa, velocity * DT).to_owned()
set this.ddp = v3sc(DT, v3c(0.0, 0.0, GRAVITY)).to_owned()
set this.li = AddLightningEx("DRAM", false, this.p.x, this.p.y, this.p.z, tp.x, tp.y, tp.z)
set this.li_max_ticks = R2I(lightning_fade_time / DT)
set this.li_ticks = this.li_max_ticks
set this.dp_rot = bj_RADTODEG * this.dp_rot
call SetUnitFacing(this.caster, this.dp_rot)
call SetUnitPropWindow(this.caster, 0.0) // disable caster movement
call UnitAddAbility(this.caster, 'Amrf')
call UnitRemoveAbility(this.caster, 'Amrf')
else
set this.state = STATE_LIGHTNING_FADE
set this.li_max_ticks = R2I(lightning_fade_time / DT)
set this.li_ticks = this.li_max_ticks
set max_hit_distance = max_hit_distance_for_level(this.level)
set i = R2I(max_hit_distance / hit_radius + 0.5)
set curr_p = v3c(this.p.x, this.p.y, 0.0)
set step = v3_from_polar(tpp.ang_rot() + bj_PI, hit_radius)
set z = this.p.z + 64.0
loop
exitwhen i <= 0
call GroupEnumUnitsInRange(G, curr_p.x, curr_p.y, hit_radius, null)
loop
set u = FirstOfGroup(G)
exitwhen u == null
call GroupRemoveUnit(G, u)
if targets_allowed(this, u, z) then
exitwhen true
endif
endloop
if u != null then
call GroupClear(G)
exitwhen true
endif
call curr_p.set_eq(v3a(curr_p, step))
set i = i - 1
endloop
if u != null then
set n = v3n(step)
set n.z = 0.0
set a = v3c(this.p.x, this.p.y, 0.0)
set up = v3c(GetUnitX(u), GetUnitY(u), 0.0)
set ua = v3s(a, up)
set ul = v3a(up, v3s(ua, v3sc(ua.dot(n), n)))
set this.li_effect = AddSpecialEffectZ("Abilities\\Spells\\Orc\\Purge\\PurgeBuffTarget.mdl", ul.x, ul.y, z)
set this.li = AddLightningEx("DRAM", false, this.p.x, this.p.y, z, ul.x, ul.y, z)
call UnitDamageTarget(this.caster, u, charge_to_damage(this.mana_charge, this.level), MELEE_ATTACK, RANGE_ATTACK, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
set u = null
else
set this.li = AddLightningEx("DRAM", false, this.p.x, this.p.y, z, curr_p.x, curr_p.y, z)
endif
endif
// it would be better if we could disable/silence the mana charge ability while
// the caster is discharging on the ground instead of removing/hiding it
call UnitRemoveAbility(this.caster, MANA_CHARGE)
// the mana discharge ability can only be used once after the mana charge
// is successfully (having enough mana) turned on, again it would be nicer to
// be able to disable/silence it instead of removing/hiding it
// note: this needs to be called after GetSpellTargetX|Y otherwise we get 0.0
call UnitRemoveAbility(this.caster, MANA_DISCHARGE)
endif
endmethod
private static method ul_update takes nothing returns nothing
local thistype this
local real mana
local integer i
set i = 1
loop
exitwhen i > ul_count
set this = ul_list[i]
if this.state == STATE_WAIT_TURN_OFF then
// do nothing while mana charge is turning off
elseif this.state == STATE_TURN_OFF then
set this.turn_off_delay_ticks = this.turn_off_delay_ticks - 1
if this.turn_off_delay_ticks <= 0 then
call this.mana_charge_off()
endif
elseif this.state == STATE_CHARGING then
set this.mana_charge = this.mana_charge + this.mana_dec
set mana = GetUnitState(this.caster, UNIT_STATE_MANA) - this.mana_dec
call SetUnitState(this.caster, UNIT_STATE_MANA, mana)
if mana < 1.0 or this.mana_charge > MAX_MANA_CHARGE then
set this.state = STATE_MAX_CHARGED
set this.charge_hold_ticks = R2I(max_mana_charge_hold_time_for_level(this.level) / DT)
if this.mana_charge > MAX_MANA_CHARGE then
set this.mana_charge = MAX_MANA_CHARGE
endif
endif
call SetTextTagText(this.tt, I2S(R2I(this.mana_charge)) + " mc", MANA_CHARGE_TEXT_SIZE)
call this.mana_charge_tt_update_pos()
elseif this.state == STATE_MAX_CHARGED then
call this.mana_charge_tt_update_pos()
set this.charge_hold_ticks = charge_hold_ticks - 1
if this.charge_hold_ticks == 0 then
call DestroyEffect(AddSpecialEffect("Units\\NightElf\\Wisp\\WispExplode.mdl", GetUnitX(this.caster), GetUnitY(this.caster)))
call UnitDamageTarget(this.caster, this.caster, charge_to_self_damage(this.mana_charge, this.level), MELEE_ATTACK, RANGE_ATTACK, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
call this.mana_charge_off()
endif
elseif this.state == STATE_DISCHARGING_GROUND then
call this.dp.set_eq(v3a(this.dp, this.ddp))
call this.p.set_eq(v3a(this.p, this.dp))
if this.p.x < map_min_x then
set this.p.x = map_min_x
elseif this.p.x > map_max_x then
set this.p.x = map_max_x
endif
if this.p.y < map_min_y then
set this.p.y = map_min_y
elseif this.p.y > map_max_y then
set this.p.y = map_max_y
endif
call SetUnitX(this.caster, this.p.x)
call SetUnitY(this.caster, this.p.y)
call MoveLocation(v3_loc, this.p.x, this.p.y)
set v3_loc_z = GetLocationZ(v3_loc)
call SetUnitFlyHeight(this.caster, this.p.z - v3_loc_z, 0.0)
call SetUnitFacing(this.caster, this.dp_rot)
call MoveLightningEx(this.li, false, this.tp.x, this.tp.y, this.tp.z, this.p.x, this.p.y, this.p.z)
if this.p.z < v3_loc_z then
set this.state = STATE_LIGHTNING_FADE
call this.p.destroy()
call this.tp.destroy()
call this.dp.destroy()
call this.ddp.destroy()
call SetUnitFlyHeight(this.caster, 0.0, 0.0)
call SetUnitPropWindow(this.caster, bj_DEGTORAD * GetUnitDefaultPropWindow(this.caster))
endif
elseif this.state == STATE_LIGHTNING_FADE then
set this.li_ticks = this.li_ticks - 1
if this.li_ticks <= 0 then
call DestroyLightning(this.li)
set this.li = null
if this.li_effect != null then
call DestroyEffect(this.li_effect)
set this.li_effect = null
endif
call UnitAddAbility(this.caster, MANA_CHARGE)
call SetUnitAbilityLevel(this.caster, MANA_CHARGE, this.level)
call this.destroy()
set i = i - 1
else
call SetLightningColor(this.li, 1.0, 1.0, 1.0, I2R(this.li_ticks) / this.li_max_ticks)
endif
endif
set i = i + 1
endloop
endmethod
private static method set_handlers takes nothing returns nothing
set on_event_handler = function thistype.on_event
set ul_update_handler = function thistype.ul_update
endmethod
endstruct
endlibrary