library StarfallSpell initializer init uses /*
*/ xebasic /* http://www.wc3c.net/showthread.php?t=101150
*/, xedummy /* -//-
*/, xefx /* -//-
*/, TimerUtils /* http://www.wc3c.net/showthread.php?t=101322
*/, GroupUtils /* http://www.wc3c.net/showthread.php?t=104464
*/, UnitZ /* http://www.hiveworkshop.com/forums/jass-resources-412/snippet-getterrainz-unitz-236942/
*/
globals
private constant integer SPELL_ID = 'A000'
private constant real INITIAL_BEHIND = 200
private constant real INITIAL_Z = 1200
private constant real FALLING_SPEED = 1000
private constant string MODEL = "Abilities\\Spells\\Undead\\DevourMagic\\DevourMagicBirthMissile.mdl"
private constant real SCALE = 3.0
// "Abilities\\Spells\\NightElf\\Starfall\\StarfallTarget.mdl"
//"Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
endglobals
private function IsUnitInvulnerable takes unit u returns boolean
return GetUnitAbilityLevel(u, 'Avul') > 0 or IsUnitLoaded(u)
endfunction
private function targets_allowed takes unit caster, unit target returns boolean
return not IsUnitType(target, UNIT_TYPE_DEAD) /*
*/ and IsUnitEnemy(target, GetOwningPlayer(caster)) /*
*/ and not IsUnitType(target, UNIT_TYPE_MAGIC_IMMUNE) /*
*/ and not IsUnitInvulnerable(target)
endfunction
private function distance_between takes unit u1, unit u2 returns real
local real x1 = GetUnitX(u1)
local real y1 = GetUnitY(u1)
local real x2 = GetUnitX(u2)
local real y2 = GetUnitY(u2)
local real dx = x2 - x1
local real dy = y2 - y1
return SquareRoot(dx * dx + dy * dy)
endfunction
private function say takes string s returns nothing
call BJDebugMsg(s)
// call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, s)
endfunction
globals
private hashtable HT = InitHashtable()
private constant real DT = 0.03125
private real tx
private real ty
private real tz
private real dx
private real dy
private real dz
private real dist_behind
private real vlen
private real mvlen
private real array random_delays
private constant integer random_delays_count = 65
endglobals
private function init_random_delays takes nothing returns nothing
set random_delays[0] = 0
set random_delays[1] = 16
set random_delays[2] = 31
set random_delays[3] = 47
set random_delays[4] = 63
set random_delays[5] = 78
set random_delays[6] = 94
set random_delays[7] = 109
set random_delays[8] = 125
set random_delays[9] = 141
set random_delays[10] = 156
set random_delays[11] = 172
set random_delays[12] = 188
set random_delays[13] = 203
set random_delays[14] = 219
set random_delays[15] = 234
set random_delays[16] = 250
set random_delays[17] = 266
set random_delays[18] = 281
set random_delays[19] = 297
set random_delays[20] = 313
set random_delays[21] = 328
set random_delays[22] = 344
set random_delays[23] = 359
set random_delays[24] = 375
set random_delays[25] = 391
set random_delays[26] = 406
set random_delays[27] = 422
set random_delays[28] = 438
set random_delays[29] = 453
set random_delays[30] = 469
set random_delays[31] = 485
set random_delays[32] = 500
set random_delays[33] = 516
set random_delays[34] = 531
set random_delays[35] = 547
set random_delays[36] = 563
set random_delays[37] = 578
set random_delays[38] = 594
set random_delays[39] = 609
set random_delays[40] = 625
set random_delays[41] = 641
set random_delays[42] = 656
set random_delays[43] = 672
set random_delays[44] = 688
set random_delays[45] = 703
set random_delays[46] = 719
set random_delays[47] = 735
set random_delays[48] = 750
set random_delays[49] = 766
set random_delays[50] = 782
set random_delays[51] = 797
set random_delays[52] = 813
set random_delays[53] = 828
set random_delays[54] = 844
set random_delays[55] = 859
set random_delays[56] = 875
set random_delays[57] = 891
set random_delays[58] = 906
set random_delays[59] = 922
set random_delays[60] = 938
set random_delays[61] = 953
set random_delays[62] = 969
set random_delays[63] = 984
set random_delays[64] = 1000
endfunction
public keyword Starfall
private keyword FallingStars
private struct FallingStar extends array
FallingStars fs
xefx star
static method move_star takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
local FallingStars fsx = this.fs
local real speed = fsx.sf.speed
local real angle_behind = (GetUnitFacing(fsx.target) - 180) * bj_DEGTORAD
set tx = GetUnitX(fsx.target)
set ty = GetUnitY(fsx.target)
set tz = GetUnitZ(fsx.target)
set dx = tx - star.x
set dy = ty - star.y
set dz = tz - star.z
set vlen = SquareRoot(dx * dx + dy * dy + dz * dz)
set mvlen = 1 / vlen
set dx = dx * mvlen
set dy = dy * mvlen
set dz = dz * mvlen
set star.x = star.x + dx * speed * DT
set star.y = star.y + dy * speed * DT
set star.z = star.z + dz * speed * DT
set dx = tx - star.x
set dy = ty - star.y
set dz = tz - star.z
set dist_behind = SquareRoot(dx * dx + dy * dy)
set star.x = tx + Cos(angle_behind) * (dist_behind)
set star.y = ty + Sin(angle_behind) * (dist_behind)
set star.xyangle = Atan2(ty - star.y, tx - star.x)
set star.zangle = -Atan((star.z - tz) / dist_behind)
if vlen <= 32 then
if not IsUnitInvulnerable(fsx.target) then
call UnitDamageTarget(fsx.sf.caster, fsx.target, fsx.sf.damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS)
endif
call ReleaseTimer(t)
call star.destroy()
endif
set t = null
endmethod
private static thistype that = 0
static method fall takes FallingStars fs returns thistype
local real angle_behind
local thistype this = that - (that / 8190) * 8190 + 1
set that = this
set this.fs = fs
set angle_behind = GetUnitFacing(fs.target) - 180
set tx = GetUnitX(fs.target) + Cos(angle_behind * bj_DEGTORAD) * fs.sf.initial_behind
set ty = GetUnitY(fs.target) + Sin(angle_behind * bj_DEGTORAD) * fs.sf.initial_behind
set star = xefx.create(tx, ty, (angle_behind + 180) * bj_DEGTORAD)
set star.fxpath = fs.sf.model
set star.scale = fs.sf.scale
set star.z = fs.sf.initial_z + GetUnitZ(fs.target)
set star.zangle = -Atan(star.z / fs.sf.initial_behind)
call TimerStart(NewTimerEx(this), DT, true, function thistype.move_star)
return this
endmethod
endstruct
private struct FallingStars extends array
Starfall sf
unit target
private static thistype self
private static trigger call_starfall
private static method star_fall takes nothing returns nothing
local timer t = GetExpiredTimer()
local thistype this = GetTimerData(t)
if sf.duration > 0 then
if distance_between(sf.caster, target) <= sf.AOE and targets_allowed(sf.caster, target) then
call FallingStar.fall(this)
endif
call ReleaseTimer(t)
set self = this
call TriggerExecute(call_starfall) // self.starfall(), i.e drop another star
else
call ReleaseTimer(t)
endif
set t = null
endmethod
private static method starfall takes nothing returns nothing
local thistype this = self
local real random_delay = random_delays[GetRandomInt(0, random_delays_count - 1)]
local real delay
set delay = random_delay / 1000.0
if GetRandomInt(1, 100) <= 50 then
set delay = -delay
endif
set delay = sf.initial_delay + delay
call TimerStart(NewTimerEx(this), delay, false, function thistype.star_fall)
endmethod
private static thistype that = 0
static method fall_over takes Starfall sf, unit target returns thistype
local thistype this = that - (that / 8190) * 8190 + 1
set that = this
set this.sf = sf
set this.target = target
set self = this
call thistype.starfall()
return this
endmethod
private static method onInit takes nothing returns nothing
call ExecuteFunc(SCOPE_PRIVATE + "init_random_delays")
set call_starfall = CreateTrigger()
call TriggerAddAction(call_starfall, function thistype.starfall)
endmethod
endstruct
struct Starfall extends array
unit caster
real AOE
real duration
real damage
real initial_delay
real initial_behind
real initial_z
real speed
string model
real scale
private group affected_units
private boolean stopped
private static thistype self
private static trigger call_starfall
private static method starfall_recur takes nothing returns nothing
local timer t = GetExpiredTimer()
local Starfall this = GetTimerData(t)
set duration = duration - initial_delay
if duration > 0 then
call ReleaseTimer(t)
set t = null
set self = this
call TriggerExecute(call_starfall) // call self.starfall()
return
else
if not stopped then
call IssueImmediateOrder(caster, "stop")
endif
call stop()
call ReleaseTimer(t)
set t = null
endif
endmethod
private static method starfall takes nothing returns nothing
local thistype this = self
local unit u
call GroupUnitsInArea(ENUM_GROUP, GetUnitX(caster), GetUnitY(caster), AOE)
loop
set u = FirstOfGroup(ENUM_GROUP)
exitwhen u == null
call GroupRemoveUnit(ENUM_GROUP, u)
if targets_allowed(caster, u) and not IsUnitInGroup(u, affected_units) then
call GroupAddUnit(affected_units, u)
call FallingStars.fall_over(this, u)
endif
endloop
call TimerStart(NewTimerEx(this), initial_delay, false, function thistype.starfall_recur)
endmethod
private static thistype that = 0
static method cast takes unit caster, real AOE, real duration, real damage, real initial_delay, real initial_behind, real initial_z, real falling_speed, string model, real scale returns thistype
local thistype this = that - (that / 8190) * 8190 + 1
set that = this
set this.caster = caster
set this.AOE = AOE
set this.duration = duration
set this.damage = damage
set this.initial_delay = initial_delay
set this.initial_behind = initial_behind
set this.initial_z = initial_z
set this.speed = falling_speed
set this.model = model
set this.scale = scale
set affected_units = NewGroup()
set stopped = false
call SetUnitAnimation(caster, "channel")
set self = this
call thistype.starfall() // call self.starfall()
return this
endmethod
method stop takes nothing returns nothing
if stopped then
return
endif
set stopped = true
set duration = 0
call ReleaseGroup(affected_units)
endmethod
private static method onInit takes nothing returns nothing
set call_starfall = CreateTrigger()
call TriggerAddAction(call_starfall, function thistype.starfall)
endmethod
endstruct
private function on_spell_effect takes nothing returns boolean
local Starfall sf
local unit caster
local integer spell_level
local real AOE
local real damage
local real duration
local real initial_delay
local real initial_behind
local real initial_z
local real falling_speed
local string model
local real scale
if GetSpellAbilityId() != SPELL_ID then
return false
endif
set caster = GetTriggerUnit()
set spell_level = GetUnitAbilityLevel(caster, SPELL_ID)
set AOE = 1000
set duration = 30 // seconds
set damage = 50
set initial_delay = 1.5
set initial_behind = INITIAL_BEHIND
set initial_z = INITIAL_Z
set falling_speed = FALLING_SPEED
set model = MODEL
set scale = SCALE
set sf /*
*/ = Starfall.cast( /*
*/ caster /*
*/ , AOE /*
*/ , duration /*
*/ , damage /*
*/ , initial_delay /*
*/ , initial_behind /*
*/ , initial_z /*
*/ , falling_speed /*
*/ , model /*
*/ , scale /*
*/ )
call SaveInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID, sf)
set caster = null
return false
endfunction
private function on_end_cast takes nothing returns boolean
local integer sf
if GetSpellAbilityId() == SPELL_ID then
set sf = LoadInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID)
call Starfall(sf).stop()
call FlushChildHashtable(HT, GetHandleId(GetTriggerUnit()))
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function on_spell_effect))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function on_end_cast))
endfunction
endlibrary
library Rockets initializer init uses StarfallSpell
globals
private constant integer SPELL_ID = 'A001'
private constant real INITIAL_BEHIND = 500
private constant real INITIAL_Z = 800
private constant real FALLING_SPEED = 700
private constant string MODEL = "Abilities\\Weapons\\Mortar\\MortarMissile.mdl"
private constant real SCALE = 1.0
endglobals
globals
private hashtable HT = InitHashtable() // might be a good idea to use a SPELL_HT for the whole map
endglobals
private function on_spell_effect takes nothing returns boolean
local StarfallSpell_Starfall sf
local unit caster
local integer spell_level
local real AOE
local real damage
local real duration
local real initial_delay
local real initial_behind
local real initial_z
local real falling_speed
local string model
local real scale
if GetSpellAbilityId() != SPELL_ID then
return false
endif
set caster = GetTriggerUnit()
set spell_level = GetUnitAbilityLevel(caster, SPELL_ID)
set AOE = 1000
set duration = spell_level * 5
set damage = 50
set initial_delay = 0.5
set initial_behind = INITIAL_BEHIND
set initial_z = INITIAL_Z
set falling_speed = FALLING_SPEED
set model = MODEL
set scale = SCALE
set sf /*
*/ = StarfallSpell_Starfall.cast( /*
*/ caster /*
*/ , AOE /*
*/ , duration /*
*/ , damage /*
*/ , initial_delay /*
*/ , initial_behind /*
*/ , initial_z /*
*/ , falling_speed /*
*/ , model /*
*/ , scale /*
*/ )
call SaveInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID, sf)
set caster = null
return false
endfunction
private function on_end_cast takes nothing returns boolean
local integer sf
if GetSpellAbilityId() == SPELL_ID then
set sf = LoadInteger(HT, GetHandleId(GetTriggerUnit()), SPELL_ID)
call StarfallSpell_Starfall(sf).stop()
call FlushChildHashtable(HT, GetHandleId(GetTriggerUnit()))
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function on_spell_effect))
set t = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_ENDCAST)
call TriggerAddCondition(t, Condition(function on_end_cast))
endfunction
endlibrary