-
Are you planning to upload your awesome spell or system to Hive? Please review the rules here.Dismiss Notice
-
Find your way through the deepest dungeon in the 18th Mini Mapping Contest Poll.Dismiss Notice
-
A brave new world lies beyond the seven seas. Join the 34th Modeling Contest today!Dismiss Notice
-
Check out the Staff job openings thread.Dismiss Notice
Dismiss Notice
Hive 3 Remoosed BETA - NOW LIVE. Go check it out at BETA Hive Workshop! Post your feedback in this new forum BETA Feedback.
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.
Mana Charge
Submitted by
Aniki
- Tags:
- Target Ground, Target Object, vJASS
- Filesize:
- 35.18 KB
- Rating:
-
(0 votes)
- Downloads:
- 401
- Uploaded:
- Dec 24, 2016
- Updated:
- Dec 24, 2016
- Resources:
- 1
- Author(s):
- Aniki
- State:
- Substandard

This bundle is marked as substandard. It may contain bugs, not perform optimally or otherwise be in violation of the submission rules.
Mana particles can be conjured by a caster to form a charge that can be instantly discharged onto unsuspecting foes from a distance or the ground itself in which case it acts as a propeller. A Mana Charge cannot be sustained for long periods of time due to the damage it causes to the caster.
library withmanacharge requires /*
*/Ht, /* http://www.hiveworkshop.com/threads/t-real-x-vs-t-x-real.288833/
*/ updatelist, /* http://www.hiveworkshop.com/threads/fire-and-ice-system.290464/#post-3120100
*/ v3 /* in the map */
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
Code (vJASS):
library withmanacharge requires /*
*/Ht, /* http://www.hiveworkshop.com/threads/t-real-x-vs-t-x-real.288833/
*/ updatelist, /* http://www.hiveworkshop.com/threads/fire-and-ice-system.290464/#post-3120100
*/ v3 /* in the map */
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
Contents