//!*********************************EX HYDRATIA [Paladon]******************************!\\
//**************************************************************************************\\
scope ExHydratia initializer SetEvent
native UnitAlive takes unit id returns boolean
globals
//!**********************************THE SETTINGS*****************************!\\
//*****************************************************************************\\
// General Settings
private constant integer SPELL_ID = 'A000' // Spell's rawcode
private constant integer EFFECT_ID = 'A001' // Armour effect's rawcode
private constant integer DUMMY_ID = 'n000' // Dummy's rawcode
private constant real TIMER = 0.01 // Interval of the timer
private constant real INTERVAL_TIMER = 0.5 // Time between the intervals of dehydration
private constant integer INIT_DEHYDRATION_COUNT = 3 // Intervals (Level 1)
private constant integer INCR_DEHYDRATION_COUNT = 1 // Intervals added per each level (Level 2 and higher)
private constant string SFX_WATER_MISSILE = "war3mapImported\\WaterEssence.mdl" // Model of the missiles
private constant string SFX_DEHYDRATION_EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveDamage.mdl" // Model of the "death" animation displayed upon creation of the missiles
private constant string SFX_HEAL_EFFECT = "Abilities\\Spells\\NightElf\\Blink\\BlinkTarget.mdl" // Model of the "death" animation displayed upon impact on enemied units
private constant string DUMMY_ATTACH = "chest" // Attachment point of the effects on the "Dummy" model (Missile)
private constant string ENEMY_ATTACH = "origin" // Attachment point of the effects on the enemy (Dehydration)
private constant string HEAL_ATTACH = "origin" // Attachment point of the effects on your hero (Heal)
// Orbital Settings
private constant real ORBITAL_SPEED_MIN = 5.55 // Defines the minimal orbital speed
private constant real ORBITAL_SPEED_MAX = 7.55 // Defines the maximal orbital speed
private constant real ORBITAL_ANGLE_SPIN_MAX = -0.65 // Defines the minimal speed of the spin of the orbital movement
private constant real ORBITAL_ANGLE_SPIN_MIN = 0.65 // Defines the maximal speed of the spin of the orbital movement
private constant real ORBITAL_SIZE_MIN = 110. // Defines the minimal size of the orbital movement of a single orb
private constant real ORBITAL_SIZE_MAX = 150. // Defines the maximal size of the orbital movement of a single orb
private constant real ORBITAL_HEIGHT_MIN = 0. // Defines partially the height of the orbital (minimal).
private constant real ORBITAL_HEIGHT_MAX = 60. // Defines partially the height of the orbital (maximal).
//! I recommend playing around with these values for ground units to find something fitting. For air units, i recommend for the height simply "0".
// Damage / Heal Settings
private constant real INIT_DAMAGE_DEHYDRATION = 30. // Defines the damage dealt per dehydration interval (lvl 1)
private constant real INCR_DAMAGE_DEHYDRATION = 0. // Defines the damage added per level (lvl 2 and up) for each dehydration interval
private constant real INIT_HEAL_HITPOINTS = 15. // Defines the amount of hitpoints healed per hydration counter (see below) by reaching the limit (lvl 1)
private constant real INCR_HEAL_HITPOINTS = 0. // Defines the amount of hitpoints healed added for level 2 and higher
private constant real INIT_HEAL_MANA = 15. // Defines the amount of mana healed per hydration counter (see below) by reaching the limit (lvl 1)
private constant real INCR_HEAL_MANA = 0. // Defines the amount of mana healed added for level 2 and higher
// Misc Settings
private constant integer TRIGGER_HYDRATION_COUNTER = 6 // Defines the "amount" of water needed to trigger the hydration
private constant real MISSILE_SPEED = 5.5 // The speed of a released missile outside the orbital
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_NORMAL // The attacktype of the damage dealt
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL // The damagetype of the damage dealt
private constant weapontype WEAPON_TYPE = null // The weapontype of the damage dealt
//!*******************************END OF THE SETTINGS*************************!\\
//**Do not modify anything below this line unless you know what you are doing**\\
//!***************************************************************************!\\
private real array R
endglobals
private struct S
private unit hero
private unit missile
private unit target
private integer state
private integer level
private real d1 // Distance reference
private real angle // Not always used as angle but in cases of reference as the corresponding rad/deg
private real t1 // The first time counter
private real t2 // The second time counter
private real x // coordinate for the orbital
private real y // coordinate for the orbital
private real z // coordinate for the orbital
private real OSp // Orbital Speed
private real OAS // Orbital Angle Spin
private real OSi // Orbital Size
private real OHe // Orbital Height
private real OEx // Orbital Value Reference (also used for directional purposes)
private effect sfx
private static S array indx
private static integer counter = 0
private static timer time = CreateTimer()
static method Execution takes nothing returns nothing
local S d
local integer i = 0
local integer l = 0
local integer a = 0
local integer b = 0
local unit u
loop
exitwhen i >= S.counter
set d = S.indx[i]
if d.state == 0 then
// Remove & Recycle
call DestroyEffect(d.sfx)
call RemoveUnit(d.missile)
set d.hero = null
set d.missile = null
set d.target = null
set d.sfx = null
call d.destroy()
set S.counter = S.counter - 1
set S.indx[i] = d.indx[S.counter]
set i = i - 1
elseif d.state == 2 or d.state == 4 then
// Launch and move the missile
// X/Y Movement
if d.state == 4 then
set R[0] = GetUnitX(d.target)
set R[1] = GetUnitY(d.target)
else
set R[0] = d.x
set R[1] = d.y
endif
set R[2] = GetUnitX(d.missile)
set R[3] = GetUnitY(d.missile)
set R[4] = Atan2(R[1]-R[3],R[0]-R[2])
call SetUnitX(d.missile,R[2] + MISSILE_SPEED * Cos(R[4]))
call SetUnitY(d.missile,R[3] + MISSILE_SPEED * Sin(R[4]))
call SetUnitFacing(d.missile,R[4]*bj_RADTODEG)
// Z Movement
set R[0] = R[2] - R[0]
set R[1] = R[3] - R[1]
set R[2] = SquareRoot(R[0]*R[0]+R[1]*R[1])
if d.state == 4 then
set R[0] = GetUnitFlyHeight(d.target)
else
set R[0] = d.z
endif
set R[1] = GetUnitFlyHeight(d.missile)
set R[0] = R[0]-R[1]
set R[0] = R[0]/(R[2]/MISSILE_SPEED)
call SetUnitFlyHeight(d.missile,R[1]+R[0],0)
if R[2] <= MISSILE_SPEED then // Missile reached target
if d.state == 2 then
set d.state = 3
set l = GetUnitAbilityLevel(d.hero,EFFECT_ID)
if l == 0 then
call UnitAddAbility(d.hero,EFFECT_ID)
elseif l+1 >= TRIGGER_HYDRATION_COUNTER then // Trigger Heal
call UnitRemoveAbility(d.hero,EFFECT_ID)
call DestroyEffect(AddSpecialEffectTarget(SFX_HEAL_EFFECT,d.hero,HEAL_ATTACH))
set u = d.hero
set a = d
set b = 0
loop
exitwhen b >= S.counter
set d = S.indx[b]
if d.hero == u and d.state == 3 then
set d.state = 4
set d.target = u
endif
set b = b + 1
endloop
set d = a
else // Simple increase
call SetUnitAbilityLevel(d.hero,EFFECT_ID,l+1)
endif
else // Heal effect, state = 4
call SetWidgetLife(d.hero,INIT_HEAL_HITPOINTS + (INCR_HEAL_HITPOINTS * d.level) + GetWidgetLife(d.hero))
call SetUnitState(d.hero, UNIT_STATE_MANA,INIT_HEAL_MANA + (INCR_HEAL_MANA * d.level) + GetUnitState(d.hero,UNIT_STATE_MANA))
set d.state = 0
endif
elseif not UnitAlive(d.hero) then
set d.state = 0
endif
elseif d.state == 1 then // Creates a missile and triggers dehydration on the enemy
set d.t1 = d.t1 + TIMER
if not UnitAlive(d.target) or not UnitAlive(d.hero) then
set d.state = 0
elseif d.t1 >= d.t2 then
set d.t1 = 0
// init single dehydration
set R[0] = GetUnitX(d.target)
set R[1] = GetUnitY(d.target)
set d.missile = CreateUnit(GetOwningPlayer(d.hero),DUMMY_ID,R[0],R[1],0)
call UnitAddAbility(d.missile,'Arav')
call UnitRemoveAbility(d.missile,'Arav')
// The following 4 lines are necessary since an initial setting cannot work
call SetUnitPathing(d.missile,false)
call SetUnitX(d.missile,R[0])
call SetUnitY(d.missile,R[1])
call SetUnitFlyHeight(d.missile,GetUnitFlyHeight(d.target),0)
call DestroyEffect(AddSpecialEffectTarget(SFX_DEHYDRATION_EFFECT,d.target,ENEMY_ATTACH))
call UnitDamageTarget(d.hero,d.target,INIT_DAMAGE_DEHYDRATION + (INCR_DAMAGE_DEHYDRATION * d.level), true, false, ATTACK_TYPE, DAMAGE_TYPE, WEAPON_TYPE)
// Prepares the data for the movement and orbital shape
set d.sfx = AddSpecialEffectTarget(SFX_WATER_MISSILE,d.missile,DUMMY_ATTACH)
set d.OAS = GetRandomReal(ORBITAL_ANGLE_SPIN_MIN,ORBITAL_ANGLE_SPIN_MAX)*bj_DEGTORAD
set d.state = 2
endif
endif
if d.state == 1 or d.state == 2 or d.state == 3 then
set d.angle = d.angle + d.OAS
set d.d1 = d.d1 + d.OSp
set R[0] = (d.d1/d.OEx)*bj_DEGTORAD
set R[1] = d.OSi*Sin(R[0])
set R[2] = d.angle*bj_DEGTORAD
set d.x = GetUnitX(d.hero)+R[1]*Cos(R[2])
set d.y = GetUnitY(d.hero)+R[1]*Sin(R[2])
set d.z = (d.OSi-d.OHe)*Cos(R[0])+d.OHe+GetUnitFlyHeight(d.hero)
if d.state == 3 then
call SetUnitX(d.missile,d.x)
call SetUnitY(d.missile,d.y)
call SetUnitFlyHeight(d.missile,d.z,0)
call SetUnitFacing(d.missile,d.angle)
endif
// Since the orbs move with a circular movement, we can reset the angle.
if d.angle >= 360. then
set d.angle = d.angle - 360.
endif
if not UnitAlive(d.hero) then
set d.state = 0
endif
endif
set i = i + 1
endloop
set u = null
set i = 0
if S.counter == 0 then
call PauseTimer(S.time)
endif
endmethod
static method SetStruct takes unit hero, unit target, real amount, real specific, real time, integer level returns nothing
// This method mainly inits the orbital movement data and determines the movement shape
local S d = S.allocate()
set d.hero = hero
set d.target = target
set d.level = level
set d.state = 1
set d.d1 = 0
set d.angle = (360/amount) * specific
set d.t1 = 0
set d.t2 = time
set d.OSp = GetRandomReal(ORBITAL_SPEED_MIN,ORBITAL_SPEED_MAX)
set d.OSi = GetRandomReal(ORBITAL_SIZE_MIN,ORBITAL_SIZE_MAX)
set d.OAS = 5. // constant to create the "waterwave" effect when the missile is moving
set d.OHe = GetRandomReal(ORBITAL_HEIGHT_MIN,ORBITAL_HEIGHT_MAX)
set d.OEx = d.OSi/90
if S.counter == 0 then
call TimerStart(S.time,TIMER,true,function S.Execution)
endif
set S.indx[S.counter] = d
set S.counter = S.counter + 1
endmethod
static method Dehydrate takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer l = GetUnitAbilityLevel(u,SPELL_ID)-1
set R[0] = 0
set R[1] = INIT_DEHYDRATION_COUNT + (INCR_DEHYDRATION_COUNT * l)
loop
exitwhen R[0] == R[1]
call S.SetStruct(u,GetSpellTargetUnit(),R[1],R[0],INTERVAL_TIMER*R[0],l)
set R[0] = R[0] + 1
endloop
set u = null
endmethod
endstruct
private function Trigger takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call S.Dehydrate()
endif
return true
endfunction
private function SetEvent takes nothing returns nothing
local trigger t = CreateTrigger()
// Preloading the armour effect
local unit u = CreateUnit(Player(15),DUMMY_ID,0,0,0)
call UnitAddAbility(u,EFFECT_ID)
call UnitRemoveAbility(u,EFFECT_ID)
call RemoveUnit(u)
// Trigger settings
call TriggerRegisterAnyUnitEventBJ(t,EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(t, Condition(function Trigger))
// Preloading effects
call Preload(SFX_WATER_MISSILE)
call Preload(SFX_DEHYDRATION_EFFECT)
call Preload(SFX_HEAL_EFFECT)
call PreloadStart()
set u = null
endfunction
endscope