library GigaDrain/* 1.0 by Almia
*************************************************************************************
*
* Giga Drain
*
* Deals 100/200/300 damage to the target unit, per 0.125 seconds. The caster
* then absorbs the damage dealt, healing for 50/60/70 % of the damage dealt.
* The healing/damage is divided to the number of particles released.
*
*
*************************************************************************************
*
* */ requires /*
*
* */MissileRecycler /* hiveworkshop.com/forums/jass-resources-412/system-missilerecycler-206086/
* */Projection /* hiveworkshop.com/forums/2038279-post655.html
* */GetUnitCollision /* github.com/nestharus/JASS/blob/master/jass/Systems/GetUnitCollision/script.j
* */MapBounds /* hiveworkshop.com/forums/jass-resources-412/snippet-mapbounds-222870/
* */ZLibrary /* hiveworkshop.com/forums/jass-resources-412/snippet-zlibrary-237821/
* */SpellEffectEvent /* hiveworkshop.com/forums/jass-resources-412/snippet-spelleffectevent-187193/
*
*************************************************************************************
*
* Credits
*
* Nestharus - GUC
* Bribe - MissileRecycler, SpellEffectEvent
* D.O.G. - ZLibrary
* Adiktuz - MapBounds
*
*************************************************************************************/
globals
/*
* ability code
*/
private constant integer ABIL = 'giga'
/*
* Timeout iteration
*/
private constant real TIMEOUT = 0.03125
/*
* Damage values
*/
private constant real DAMAGE_BASE = 0
private constant real DAMAGE_PER_LEVEL = 100
/*
* attack type and damage type for damage
*/
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_CHAOS
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_UNIVERSAL
/*
* Heal potency (heal = damage*potency)
*/
private constant real HEAL_POTENCY_BASE = 0.4
private constant real HEAL_POTENCY_PER_LEVEL = 0.1
/*
* SFX applied to the target unit
*/
private constant string ON_CAST_TARGET_EFFECT = ""
private constant string ON_CAST_TARGET_ATTACH = ""
/*
* SFX applied to the unit when healed.
*/
private constant string ON_HEAL_EFFECT = ""
private constant string ON_HEAL_ATTACH = ""
/*
* SFX applied to the target when damaged.
*/
private constant string ON_DAMAGE_EFFECT = ""
private constant string ON_DAMAGE_ATTACH = ""
/*
* Release delay of particles
*/
private constant real PARTICLE_DELAY = 0.125
/*
* Particles per delay
*/
private constant integer PARTICLES_PER_DELAY = 4
/*
* Total number of particles released
*/
private constant integer PARTICLE_COUNT = 32
/*
* Model of particles
*/
private constant string PARTICLE_MODEL = "Abilities\\Spells\\Undead\\Possession\\PossessionTarget.mdl"
/*
* Bonus z applied to the unit(approximate height of the "chest"
*/
private constant real UNIT_Z_BONUS = 64
/*
* Time it takes for each particle to transform their appearances
*/
private constant real APPEARANCE_DURATION = 3.0
/*
* Particle sizes (newSize = random(start - variation, start + variation)
*/
private constant real START_SIZE = 1.5
private constant real START_SIZE_VARIATION = 0.5
private constant real END_SIZE = 0.25
private constant real END_SIZE_VARIATION = 0.125
private constant integer START_ALPHA = 255
private constant integer START_ALPHA_VARIATION = 0
private constant integer END_ALPHA = 255
private constant integer END_ALPHA_VARIATION = 0
private constant integer START_RED = 0
private constant integer START_RED_VARIATION = 0
private constant integer END_RED = 0
private constant integer END_RED_VARIATION = 0
private constant integer START_GREEN = 255
private constant integer START_GREEN_VARIATION = 0
private constant integer END_GREEN = 255
private constant integer END_GREEN_VARIATION = 0
private constant integer START_BLUE = 0
private constant integer START_BLUE_VARIATION = 0
private constant integer END_BLUE = 0
private constant integer END_BLUE_VARIATION = 0
/*
* Gravitational speed applied to each particles
* towards their target
*/
private constant real GRAVITY = 0.6129375
/*
* Angles of particles when released.
*/
private constant real ANGLE_START = bj_PI/2
private constant real ANGLE_VARIATION = bj_PI
private constant real ANGLE_Z_START = bj_PI/2
private constant real ANGLE_Z_VARIATION = bj_PI
/*
* How fast the particles travel(also influenced by gravity)
*/
private constant real OUTWARD_SPEED = 250
private constant real OUTWARD_SPEED_VARIATION = 125
endglobals
private function GetDamage takes integer lvl returns real
return DAMAGE_BASE + DAMAGE_PER_LEVEL*lvl
endfunction
private function GetParticleDamage takes integer lvl returns real
return GetDamage(lvl)/PARTICLE_COUNT
endfunction
private function GetHealPotency takes integer lvl returns real
return HEAL_POTENCY_BASE + HEAL_POTENCY_PER_LEVEL*lvl
endfunction
private function GetParticleHeal takes real damage, integer lvl returns real
return (damage*GetHealPotency(lvl))/PARTICLE_COUNT
endfunction
private function GetVarianceReal takes real base, real variation returns real
if variation == 0 then
return base
endif
return GetRandomReal(base - variation, base + variation)
endfunction
private function GetVarianceInt takes integer base, integer variation returns integer
if variation == 0 then
return base
endif
return GetRandomInt(base - variation, base + variation)
endfunction
private function DamageTarget takes unit s, unit t, real amount returns nothing
call UnitDamageTarget(s, t, amount, false, false, ATTACK_TYPE, DAMAGE_TYPE, null)
endfunction
private function HealTarget takes unit s, real amount returns nothing
call SetWidgetLife(s, GetWidgetLife(s) + amount)
endfunction
private function SetPitch takes unit u, real pitch returns nothing
local integer i = R2I(pitch * 57.2957795 + 90.5)
if (179 < i) then
set i = 179
elseif (0 > i) then
set i = 0
endif
call SetUnitAnimationByIndex(u, i)
endfunction
private function Linear takes real a, real b, real t returns real
return a + (b - a)*t
endfunction
private function LinearI takes integer a, integer b, real t returns integer
return R2I(Linear(I2R(a), I2R(b), t))
endfunction
private struct Particles
// the particle
private unit u
// it's target and collision size of the target
private unit target
private real csize
// the speed and direction
private real speed
private real face
private real face3d
// the appearance duration
private real cdur
// the amount of heal
private real amount
private effect mdl
//start scale and argb
private real ss
private integer sa
private integer sr
private integer sg
private integer sb
//end scale and argb
private real es
private integer ea
private integer er
private integer eg
private integer eb
private static thistype array p
private static thistype array n
private static constant timer t = CreateTimer()
method destroy takes nothing returns nothing
call deallocate()
set n[p[this]] = n[this]
set p[n[this]] = p[this]
if n[0] == 0 then
call PauseTimer(t)
endif
call RecycleMissile(u)
call DestroyEffect(mdl)
set u = null
set target = null
set csize = 0
set speed = 0
set face = 0
set face3d = 0
set cdur = 0
set amount = 0
set mdl = null
set ss = 0
set sa = 0
set sr = 0
set sg = 0
set sb = 0
set es = 0
set ea = 0
set er = 0
set eg = 0
set eb = 0
endmethod
private static method pr takes nothing returns nothing
local thistype this = n[0]
local real angle2
local real angle3
local real px
local real py
local real ptz
local real pz
local real dx
local real dy
local real dtz
local real dz
local real nx
local real ny
local real nz
local real dist2d
local real dist3d
local real pct
loop
exitwhen 0 == this
/*
* Check if target is alive
*/
if UnitAlive(target) then
/*
* if true, initialize coordinates for both the target and the particle
*/
set px = GetUnitX(u)
set py = GetUnitY(u)
set ptz = GetSurfaceZ(px, py)
set pz = GetUnitFlyHeight(u) + ptz
set dx = GetUnitX(target)
set dy = GetUnitY(target)
set dtz = GetSurfaceZ(dx, dy)
set dz = GetUnitFlyHeight(target) + dtz + UNIT_Z_BONUS
/*
* Get the 2d and 3d distances
*/
set dist2d = GetMagnitude2D(dx - px, dy - py)
set dist3d = GetMagnitude3D(dx - px, dy - py, dz - pz)
/*
* Check if particle is within the spherical collision
*/
if dist3d <= csize then
/*
* If true, heal the target then destroy the particle
*/
call HealTarget(target, amount)
call DestroyEffect(AddSpecialEffectTarget(ON_HEAL_EFFECT, target, ON_HEAL_ATTACH))
call destroy()
else
/*
* if not, get the angle towards the target
*/
set angle2 = GetAngle2D(dx - px, dy - py)
set angle3 = GetAngle3D(dist2d, dz - pz)
/*
* Get the next coordinates for the unit(w/o gravity)
*/
set nx = px + speed*Cos(face)*Cos(face3d)
set ny = py + speed*Sin(face)*Cos(face3d)
set nz = pz + speed*Sin(face3d)
/*
* Apply gravity for the new coordinates
*/
set nx = nx + GRAVITY*Cos(angle2)*Cos(angle3)
set ny = ny + GRAVITY*Sin(angle2)*Cos(angle3)
set nz = nz + GRAVITY*Sin(angle3)
/*
* Apply new facing
*/
set face = GetAngle2D(nx - px, ny - py)
set face3d = GetAngle3D(GetMagnitude2D(nx - px, ny - py), nz - pz)
/*
* Move the particle to the coordinates, bounded.
*/
call SetUnitX(u, GetBoundedX(nx))
call SetUnitY(u, GetBoundedY(ny))
call SetUnitFlyHeight(u, nz - GetSurfaceZ(nx, ny), 0)
/*
* Apply pitch and facing
*/
call SetUnitFacing(u, face*bj_RADTODEG)
call SetPitch(u, face3d)
/*
* Check if the appearance timer has not yet ended.
*/
if cdur < APPEARANCE_DURATION then
/*
* If true, get the percentage of the progress
*/
set cdur = cdur + TIMEOUT
set pct = cdur/APPEARANCE_DURATION
/*
* Apply linear calculation to get the new appearance values
*/
call SetUnitScale(u, Linear(ss, es, pct), 0, 0)
call SetUnitVertexColor(u, LinearI(sr, er, pct), LinearI(sg, eg, pct), LinearI(sb, eb, pct), LinearI(sa, ea, pct))
endif
endif
else
/*
* Destroy the particle
*/
call destroy()
endif
set this = n[this]
endloop
endmethod
static method create takes unit source, unit victim, integer level returns thistype
local thistype this = allocate()
/*
* Get the coordinates of the victim
*/
local real x = GetUnitX(victim)
local real y = GetUnitY(victim)
local real z = GetUnitZ(victim)
/*
* Get the damage
*/
local real damage = GetParticleDamage(level)
/*
* get the facing
*/
set face = GetVarianceReal(ANGLE_START, ANGLE_VARIATION)
set face3d = GetVarianceReal(ANGLE_Z_START, ANGLE_Z_VARIATION)
/*
* create the particles
*/
set u = GetRecycledMissile(x, y, z, face*bj_RADTODEG)
call SetPitch(u, face3d)
/*
* Add the model to it.
*/
set mdl = AddSpecialEffectTarget(PARTICLE_MODEL, u, "origin")
/*
* get the appearance values
*/
//! textmacro GD_APPEAR takes att, ATT
set s$att$ = GetVarianceInt(START_$ATT$, START_$ATT$_VARIATION)
set e$att$ = GetVarianceInt(END_$ATT$, END_$ATT$_VARIATION)
//! endtextmacro
//! runtextmacro GD_APPEAR("a", "ALPHA")
//! runtextmacro GD_APPEAR("r", "RED")
//! runtextmacro GD_APPEAR("g", "GREEN")
//! runtextmacro GD_APPEAR("b", "BLUE")
set ss = GetVarianceReal(START_SIZE, START_SIZE_VARIATION)
set es = GetVarianceReal(END_SIZE, END_SIZE_VARIATION)
/*
* Apply the appearances
*/
call SetUnitScale(u, ss, 0, 0)
call SetUnitVertexColor(u, sr, sg, sb, sa)
/*
* Get the speed of the particle
*/
set speed = GetVarianceReal(OUTWARD_SPEED, OUTWARD_SPEED_VARIATION)*TIMEOUT
/*
* Damage the target
*/
call DamageTarget(source, victim, damage)
/*
* Get the heal amount
*/
set amount = GetParticleHeal(damage, level)
/*
* get the target's collision size
*/
set target = source
set csize = GetUnitCollision(target)
set n[this] = 0
set p[this] = p[0]
set n[p[0]] = this
set p[0] = this
if p[this] == 0 then
call TimerStart(t, TIMEOUT, true, function thistype.pr)
endif
return this
endmethod
endstruct
private struct GigaDrain
private integer count
private real spawnDelay
private unit target
private unit source
private integer level
private static thistype array p
private static thistype array n
private static constant timer t = CreateTimer()
private method destroy takes nothing returns nothing
call deallocate()
set n[p[this]] = n[this]
set p[n[this]] = p[this]
if n[0] == 0 then
call PauseTimer(t)
endif
set spawnDelay = 0
set count = 0
set level = 0
set source = null
set target = null
endmethod
private static method pr takes nothing returns nothing
local thistype this = n[0]
local integer i = 0
loop
exitwhen 0 == this
/*
* Check if the count has not yet reached the max count an
* if both the source and the target is still alive
*/
if count < PARTICLE_COUNT and UnitAlive(target) and UnitAlive(source) then
/*
* Check if spawn delay has finished
*/
if spawnDelay > 0 then
set spawnDelay = spawnDelay - TIMEOUT
else
/*
* if true, release the particles
*/
loop
set i = i + 1
set count = count + 1
call Particles.create(source, target, level)
exitwhen i == PARTICLES_PER_DELAY or count >= PARTICLE_COUNT
endloop
set i = 0
/*
* reset the spawn delay timer
*/
set spawnDelay = PARTICLE_DELAY
call DestroyEffect(AddSpecialEffectTarget(ON_DAMAGE_EFFECT, target, ON_DAMAGE_ATTACH))
endif
else
call destroy()
endif
set this = n[this]
endloop
endmethod
private static method onCast takes nothing returns boolean
local thistype this
local integer i = 0
local unit s = GetTriggerUnit()
local unit tg = GetSpellTargetUnit()
local integer lvl = GetUnitAbilityLevel(s, ABIL)
if PARTICLE_DELAY < TIMEOUT then
loop
set i = i + 1
call Particles.create(s, tg, lvl)
exitwhen i == PARTICLE_COUNT
endloop
else
set this = allocate()
set spawnDelay = PARTICLE_DELAY
set source = s
set target = tg
set level = lvl
set count = 0
set n[this] = 0
set p[this] = p[0]
set n[p[0]] = this
set p[0] = this
if p[this] == 0 then
call TimerStart(t, TIMEOUT, true, function thistype.pr)
endif
endif
call DestroyEffect(AddSpecialEffectTarget(ON_CAST_TARGET_EFFECT, tg, ON_CAST_TARGET_ATTACH))
set s = null
set tg = null
return false
endmethod
private static method onInit takes nothing returns nothing
call RegisterSpellEffectEvent(ABIL, function thistype.onCast)
endmethod
endstruct
endlibrary