// import zinc "jass//chatargs.zn"
Name | Type | is_array | initial_value |
aaa | string | Yes | |
CF_Snd | integer | No | |
Chain_Missile | unit | No | |
ChainFrost_Group | group | No | |
ChainFrost_Jumps | integer | No | |
DarkDragon | string | Yes | |
Distance | real | No | |
Dummy | unit | Yes | |
i | integer | No | |
ImgEffect | unit | Yes | |
LastTarget | unit | No | |
Lich | unit | No | |
Max_Jumps | integer | No | |
maxTimerStored | integer | No | -1 |
Missile | unit | Yes | |
Missile_Point | location | No | |
Nova_Damage | abilcode | No | |
Nova_Point | location | No | |
Nova_Target | unit | No | |
Run_Time | integer | No | |
StoredTimer | timer | Yes | |
StoredTimerData | integer | Yes | |
Target | unit | Yes |
//TESH.scrollpos=654
//TESH.alwaysfold=0
//===========
//Readme: ==
//==============================================
//DotA based spell Chain Frost: maded by IceFrog
//This version of Chain Frost is maded by Dark Dragon
//----------------------------------------------
//Jumps many times dealing heavy damage and
//slowing enemys
//**********************************************
//As Chain Lightning just much better can
//jump more then once on same target
//**********************************************
//You have right to use this spell in your map
//and edit it ( do anything )
//----------------------------------------------
//However my system UserTimerData will not
//pass as taking it and using it your map
//whitout credits
//==============================================
//Need additional help mail me at:
//<[email protected]>
//**********************************************
//Have Fun!!!
//**********************************************
//Used Globals - that need to be in every map
//globals
// trigger gg_trg_Chain_Frost_jass_123 = null
// unit udg_LastTarget = null
// timer array udg_StoredTimer
// integer array udg_StoredTimerData
// integer udg_maxTimerStored = -1
//endglobals
//========================
//User own stile settings:
//========================
//Boolean type function which returns shuld chain frost use custom sounds
constant function UseSounds takes nothing returns boolean
//Set TRUE to use custom sounds or FALSE to disable custom sounds
//Note: You can use this even if you have slow computer
//Default: TRUE
return TRUE
endfunction
//Boolean type function which returns shuld chain frost use custom graphics
constant function UsingGraphics takes nothing returns boolean
//Set TRUE to use custom graphics or FALSE to disable custom graphics
//Note: You can use this even if you have slow computer
//Default: TRUE
return TRUE
endfunction
//====---==== End user setup ====---====\\
//==== ** ==== ** === ** ===
//Note: You need to edit this raw codes to match your map
//If you dont knowe your raw code in map, follow this steps:
//1. Go to object editor. Hotkey(F6)
//2. Find the spell or unit you copyed.
//3. Press Ctrl + D
//4. Now you will see raw data.
//For any needed object here find raw code in object editor
//One more thing on custom unit you will see 2x raw code
//The raw code of custom unit and the raw code of unit he was created
//Use always first raw code (left one)
//==== ** ==== ** === ** ===
//--- Raw Codes ---
//--- Chain Frost Raw Code --- Needed raw code ---
constant function CF_RawCode takes nothing returns integer
return 'A005'
endfunction
//--- Chain Frost Upg Raw Code --- Needed raw code ---
constant function CFU_RawCode takes nothing returns integer
return 'A004'
endfunction
//--- Dummy Raw Code --- Needed raw code ---
constant function Dum_RawCode takes nothing returns integer
return 'u000'
endfunction
//--- Missile Raw Code --- Needed raw code ---
constant function Mis_RawCode takes nothing returns integer
return 'u001'
endfunction
//--- Missile Graphics Raw Code --- Needed raw code ---
constant function Grp_RawCode takes nothing returns integer
return 'u002'
endfunction
//--- Damage lvl 1 Raw Code --- Needed raw code ---
constant function DL1_RawCode takes nothing returns integer
return 'A001'
endfunction
//--- Damage lvl 2 Raw Code --- Needed raw code ---
constant function DL2_RawCode takes nothing returns integer
return 'A002'
endfunction
//--- Damage lvl 3 Raw Code --- Needed raw code ---
constant function DL3_RawCode takes nothing returns integer
return 'A003'
endfunction
//--- Damage lvl 4 Raw Code --- Needed raw code ---
constant function DL4_RawCode takes nothing returns integer
return 'A000'
endfunction
//--- Locus Raw Code --- This is constant raw code ---
constant function Loc_RawCode takes nothing returns integer
return 'Aloc'
endfunction
//--- Generic Timer Raw Code --- This is constant raw code ---
constant function GT_RawCode takes nothing returns integer
return 'BTLF'
endfunction
//============================================
//--- Conversion Functions --- Integer Main
//============================================
//*** Unit to Integer
constant function U2I takes unit u returns integer
return u
return 0
endfunction
//*** Integer to Unit
constant function I2U takes integer i returns unit
return i
return null
endfunction
//*** Group to Integer
constant function G2I takes group g returns integer
return g
return 0
endfunction
//*** Integer to Group
constant function I2G takes integer i returns group
return i
return null
endfunction
//===================================================================================
//Functions
//--- Returns angle bettwen coordinates ( points )
function AngleBetweenCoord takes real x1, real y1, real x2, real y2 returns real
return bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
endfunction
//Polar Projection X
function PolarProjectionX takes real x, real dist, real angle returns real
return x + dist * Cos( angle * bj_DEGTORAD )
endfunction
//Polar Projection Y
function PolarProjectionY takes real y, real dist, real angle returns real
return y + dist * Sin( angle * bj_DEGTORAD )
endfunction
//Returns Disatance between coordiantes ( points )
function DistanceBetweenCoord takes real x1, real y1, real x2, real y2 returns real
local real x = x2-x1
local real y = y2-y1
return SquareRoot( x*x + y*y )
endfunction
//--- Checks is unit invulnerable
function GetDamageFactorEx takes unit whichUnit returns real
local unit dummy
local real life = GetUnitState(whichUnit, UNIT_STATE_LIFE)
local real x = GetCameraBoundMinX() + 100
local real y = GetCameraBoundMinY() + 100
set dummy = CreateUnit( Player(PLAYER_NEUTRAL_PASSIVE), Dum_RawCode(), x, y, 0 )
call UnitAddAbility( dummy, Loc_RawCode() )
call UnitApplyTimedLife( dummy, GT_RawCode(), 0.25 )
call UnitDamageTarget( dummy, whichUnit, 0.01, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_UNIVERSAL, WEAPON_TYPE_WHOKNOWS )
set dummy = null
set x = 0.00
set y = 0.00
return life - GetUnitState(whichUnit, UNIT_STATE_LIFE)
endfunction
//Used for storing timer data
function SetUserTimerData takes timer whichTimer, integer data returns nothing
//local integer i = 0
//loop
//exitwhen i > udg_maxTimerStored
// if ( whichTimer == udg_StoredTimer[i] ) then
// set udg_StoredTimerData[i] = data
// return
// endif
// set i = i + 1
//endloop
set udg_maxTimerStored = udg_maxTimerStored + 1
if ( udg_maxTimerStored > JASS_MAX_ARRAY_SIZE ) then
set udg_maxTimerStored = udg_maxTimerStored - 1
return
endif
set udg_StoredTimer[udg_maxTimerStored] = whichTimer
set udg_StoredTimerData[udg_maxTimerStored] = data
endfunction
//Returns timer data
constant function GetUserTimerData takes timer whichTimer returns integer
local integer i = 0
loop
exitwhen i > udg_maxTimerStored
if ( udg_StoredTimer[i] == whichTimer ) then
return udg_StoredTimerData[i]
endif
set i = i + 1
endloop
return 0
endfunction
//Clears timer data
function ClearUserTimerData takes timer whichTimer, boolean destroyTimer returns nothing
local integer i = 0
local boolean changeData = false
loop
exitwhen i > udg_maxTimerStored
if ( udg_StoredTimer[i] == whichTimer and changeData == false ) then
set changeData = true
if ( destroyTimer ) then
call PauseTimer(whichTimer)
call DestroyTimer(whichTimer)
endif
set udg_StoredTimer[i] = null
set udg_StoredTimerData[i] = 0
endif
if ( changeData and udg_StoredTimer[i+1] != null ) then
set udg_StoredTimer[i] = udg_StoredTimer[i+1]
set udg_StoredTimerData[i] = udg_StoredTimerData[i+1]
elseif ( changeData and udg_StoredTimer[i+1] == null ) then
set udg_StoredTimer[i] = null
set udg_StoredTimerData[i] = 0
set udg_maxTimerStored = i - 1
endif
set i = i + 1
endloop
endfunction
//PlaySoundEx plays sound using coordintes ( 3D sounds must get positioned )
function PlaySoundEx takes sound soundHandle, real x, real y, real z, boolean kill returns nothing
call StopSound( soundHandle, false, false )
call SetSoundPosition( soundHandle, x, y, z )
call StartSound( soundHandle )
if ( kill ) then
call KillSoundWhenDone( soundHandle )
endif
endfunction
// ===== End Functions ======
//Main Condition
function IsChainFrostCasted takes nothing returns boolean
//Check if ability is cast "chain frost" or "chain frost upgraded(scepter)"
return GetSpellAbilityId() == CF_RawCode() or GetSpellAbilityId() == CFU_RawCode()
endfunction
//--- Chain Frost Group ---
function Chain_Frost_group takes nothing returns boolean
return ((IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) == false) and (IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false) and ( IsUnitVisible(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) ) and ( IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(GetTriggerUnit())) ) and (GetUnitState( GetFilterUnit(), UNIT_STATE_LIFE ) > 0) and (GetDamageFactorEx( GetFilterUnit() ) != 0) and (udg_LastTarget != GetFilterUnit()) )
endfunction
//Returns random unit
function GroupGiveRandomUnit takes nothing returns nothing
local unit u = GetEnumUnit()
set bj_groupRandomConsidered = bj_groupRandomConsidered + 1
if ( GetRandomInt( 1, bj_groupRandomConsidered ) == 1 ) then
set bj_groupRandomCurrentPick = u
endif
set u = null
endfunction
//**** ****
//Graphics Main Function
//**** ****
function GraphicsMove takes nothing returns nothing
local unit u = GetEnumUnit()
local real rad = GetUnitFacing(u) * bj_DEGTORAD
//Moves picked graphics using speed of 200
call SetUnitPosition( u, GetUnitX(u)+ 2*Cos(rad), GetUnitY(u)+ 2*Sin(rad) )
set u = null
endfunction
function GraphicsKill takes nothing returns nothing
//Kill units insted of remove because of effects
call KillUnit( GetEnumUnit() )
endfunction
function GraphicsMain takes nothing returns nothing
local timer t = GetExpiredTimer()
local group g = I2G(GetUserTimerData(t))
local unit u = FirstOfGroup(g)
local integer exec = GetUnitUserData(u)+1
call SetUnitUserData( u, exec )
if exec == 100 then
call ForGroup( g, function GraphicsKill )
call ClearUserTimerData( t, true )
call DestroyGroup(g)
set t = null
set g = null
set u = null
return
endif
call ForGroup( g, function GraphicsMove )
set t = null
set g = null
set u = null
endfunction
//Missile Move Timer
function SetMissileXY takes nothing returns nothing
local timer t = GetExpiredTimer()
local unit dummy = I2U(GetUserTimerData(t))
local unit missile = I2U(GetUnitUserData(dummy))
local unit target = I2U(GetUnitUserData(missile))
local real speed = 8. //Speed * timer speed (8) Default
local real x = GetUnitX(missile)
local real y = GetUnitY(missile)
local real x2 = GetUnitX(target)
local real y2 = GetUnitY(target)
local real angle = AngleBetweenCoord( x, y, x2, y2 )
local real distance = 0.
set x = PolarProjectionX( x, speed, angle )
set y = PolarProjectionY( y, speed, angle )
call SetUnitPosition( missile, x, y )
call SetUnitFacing( missile, angle )
set distance = DistanceBetweenCoord( x, y, x2, y2 )
if ( distance <= 30 ) then
call ClearUserTimerData( t, true )
call RemoveUnit(missile)
//Quick blow ( precious timer ) if it did not
//there is debug after polledwait
call IssueTargetOrder( dummy, "frostnova", target )
//Nuls
set dummy = null
set missile = null
set target = null
endif
set t = null
set speed = 0.00
set angle = 0.00
set x = 0.00
set y = 0.00
set x2 = 0.00
set y2 = 0.00
set distance = 0.00
endfunction
//Main Trigger Actions
function Trig_Chain_Frost_jass_Actions takes nothing returns nothing
//Local variables declaire
local unit caster //Spell caster
local real x //Coordinate X
local real y //Coordinate Y
local real z //Z (height) is used only by sounds
local unit target //Spell target / chain target
local integer jumps //Current jumps in game
local integer max_jumps //Max spell jumps
local integer i //Loop index i
local integer frost_damage //Spell damage
local unit missile //Chain missile
local group frost_group //Random units in radius ( taken from this group )
local unit dummy //Dummy unit hurter
local real distance //Distance between target and last target
local real height //Needed hight for missile
local real raise //Raise time for missile to get hight using distance
local player p //Player owner of caster (u)
local real angle //Angle used to make dummy facing
local boolexpr bx //BooleanExpresion for Filter function used by frost_group
local timer t //Timer which moves missile
local real sleep //Used in PolledWait for max precius sleep
local real missileFlyHeight //Constant missile fly height //Default: 30
local real missileSpeed //Constant missile speed //Default: 800
local real timerSpeed //Constant missile timer move speed //Default: 0.01
local sound frost_blow //Frost blow sound
local sound field_snd //Frost field sound
local sound missile_fly //Missile fly sound
local sound spell_start //Spell start sound
local group grp_group //Graphics group
local unit grpU //Graphics unit
local terraindeformation td //Used in custom graphics ( this var is not needed ) you can call funct directly
//Init sounds
if ( UseSounds() ) then
set frost_blow = CreateSound( "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget1.wav", false, true, true, 10, 10, "DefaultEAXON" )
call SetSoundDuration( frost_blow, 2904 )
call SetSoundVolume( frost_blow, 110 )
call SetSoundPitch( frost_blow, 0.8 )
call SetSoundChannel( frost_blow, 0 )
set field_snd = CreateSound( "Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget1.wav", false, true, true, 10, 10, "DefaultEAXON" )
call SetSoundDuration( field_snd, 2669 )
call SetSoundVolume( field_snd, 110 )
call SetSoundPitch( field_snd, 0.7 )
call SetSoundChannel( field_snd, 0 )
set missile_fly = CreateSound( "Abilities\\Spells\\NightElf\\FaerieDragonInvis\\PhaseShift1.wav", false, true, true, 10, 10, "DefaultEAXON" )
call SetSoundDuration( missile_fly, 1251 )
call SetSoundVolume( missile_fly, 80 )
call SetSoundPitch( missile_fly, 0.7 )
call SetSoundChannel( missile_fly, 0 )
set spell_start = CreateSound( "Abilities\\Spells\\Other\\BreathOfFrost\\BreathOfFrost1.wav", false, true, true, 10, 10, "DefaultEAXON" )
call SetSoundDuration( spell_start, 2235 )
call SetSoundVolume( spell_start, 90 )
call SetSoundPitch( spell_start, 0.9 )
call SetSoundChannel( spell_start, 0 )
endif
//Init start vars value
set caster = GetTriggerUnit()
set x = GetUnitX(caster)
set y = GetUnitY(caster)
set target = GetSpellTargetUnit()
set p = GetOwningPlayer(caster)
set z = 0
set missileFlyHeight = 30
set missileSpeed = 800
set timerSpeed = 0.01
//Play chain frost start sound
if ( UseSounds() ) then
call PlaySoundEx( spell_start, x, y, z, true )
endif
//Get jumps and max_jumps
if ( GetSpellAbilityId() == CF_RawCode() ) then
set jumps = 6
else
set jumps = 7
endif
set max_jumps = jumps
//Get level damage
if ( GetUnitAbilityLevel( caster, CF_RawCode() ) == 1 ) then
set frost_damage = DL1_RawCode()
elseif ( ( GetUnitAbilityLevel( caster, CF_RawCode() ) == 2 ) or ( GetUnitAbilityLevel( caster, CFU_RawCode() ) == 1 ) ) then
set frost_damage = DL2_RawCode()
elseif ( ( GetUnitAbilityLevel( caster, CF_RawCode() ) == 3 ) or ( GetUnitAbilityLevel( caster, CFU_RawCode() ) == 2 ) ) then
set frost_damage = DL3_RawCode()
else
set frost_damage = DL4_RawCode()
endif
//Jumps loop main
loop
exitwhen (jumps < 1)
set jumps = jumps - 1
set frost_group = CreateGroup()
//Load missile / pick random nerby unit
if ( jumps + 1 == max_jumps ) then
set missile = CreateUnit( p, Mis_RawCode(), x, y, AngleBetweenCoord( x, y, GetUnitX(target), GetUnitY(target) ) )
else
set x = GetUnitX(target)
set y = GetUnitY(target)
set bx = Filter( function Chain_Frost_group )
set udg_LastTarget = target
call GroupEnumUnitsInRange( frost_group, x, y, 550, bx )
set udg_LastTarget = null
call DestroyBoolExpr( bx )
set bj_groupRandomConsidered = 0
call ForGroup( frost_group, function GroupGiveRandomUnit )
set target = bj_groupRandomCurrentPick
call DestroyGroup(frost_group)
//End trigger if there is no target
if ( target == null or GetUnitState( target, UNIT_STATE_LIFE ) <= 0 ) then
set jumps = 0
set max_jumps = 0
set distance = 0.00
set x = 0.00
set y = 0.00
set p = null
set caster = null
set target = null
set frost_damage = 0
set missileFlyHeight = 0.00
set missileSpeed = 0.00
set timerSpeed = 0.00
set i = 0
call KillSoundWhenDone( frost_blow )
call KillSoundWhenDone( field_snd )
call KillSoundWhenDone( missile_fly )
return
endif
set missile = CreateUnit( p, Mis_RawCode(), x, y, AngleBetweenCoord( x, y, GetUnitX(target), GetUnitY(target) ) )
if ( height > 30. ) then
call SetUnitFlyHeight( missile, height, 0. )
endif
set missileFlyHeight = GetUnitFlyHeight(missile)
endif
//Play missile sound
if ( UseSounds() ) then
set height = GetUnitFlyHeight(target)
if ( height > missileFlyHeight ) then
set z = ( height / 2 ) + ( missileFlyHeight / 2 )
else
set z = missileFlyHeight
endif
call PlaySoundEx( missile_fly, x, y, z, false )
set height = 0.00
set z = 0.00
endif
//Play field sound
if ( UseSounds() ) then
set distance = DistanceBetweenCoord( x, y, GetUnitX(target), GetUnitY(target) ) / 2
set x = PolarProjectionX( x, distance, AngleBetweenCoord( x, y, GetUnitX(target), GetUnitY(target) ) )
set y = PolarProjectionY( y, distance, AngleBetweenCoord( GetUnitX(missile), y, GetUnitX(target), GetUnitY(target) ) )
call PlaySoundEx( field_snd, x, y, z, false )
endif
//Prepare to move missile and dummy load + other stuff
call UnitAddAbility( missile, Loc_RawCode() )
set x = GetUnitX(target)
set y = GetUnitY(target)
set distance = DistanceBetweenCoord( x, y, GetUnitX(missile), GetUnitY(missile) )
call UnitApplyTimedLife( missile, GT_RawCode(), distance / 800 ) // To reduce bugs
//Terrain Deformation ( graphics setup )
if ( UsingGraphics() ) then
set td = TerrainDeformWave( GetUnitX(missile), GetUnitY(missile), x, y, distance, missileSpeed, 100, 70, 750, 1 )
set td = null
endif
//Missile fly height setup
set height = GetUnitFlyHeight( target )
if ( height > missileFlyHeight ) then
set height = GetUnitFlyHeight(target)
set raise = height / ( distance / 800 )
call SetUnitFlyHeight( missile, height, raise )
set raise = 0.00
elseif ( height < missileFlyHeight ) then
set raise = ( missileFlyHeight - height ) / ( distance / 800 )
call SetUnitFlyHeight( missile, height, raise )
endif
set angle = GetUnitFacing(target) - 180
set x = PolarProjectionX( x, 150, angle )
set y = PolarProjectionY( y, 150, angle )
set angle = 0.00
set dummy = CreateUnit( p, Dum_RawCode(), x, y, AngleBetweenCoord( x, y, GetUnitX(target), GetUnitY(target) ) )
call UnitAddAbility( dummy, Loc_RawCode() )
call UnitAddAbility( dummy, frost_damage )
//Make AI target not to run
if ( ( jumps + 1 == max_jumps ) and ( GetPlayerController(GetOwningPlayer(target)) != MAP_CONTROL_USER ) ) then
call TriggerSleepAction(-1)
call IssuePointOrder( target, "attack", GetUnitX(caster), GetUnitY(caster) )
endif
//===== Timer Setup =======
//=== Move missile ===
//===== *********** =======
call SetUnitUserData(missile, U2I(target))
call SetUnitUserData(dummy, U2I(missile))
set t = CreateTimer()
call SetUserTimerData( t, U2I(dummy) )
call TimerStart( t, timerSpeed, true, function SetMissileXY )
//Sleep while missile dont get target
if ( jumps + 1 == max_jumps ) then
set sleep = ( distance / missileSpeed ) - 0.30
else
set sleep = ( distance / missileSpeed ) - 0.15
endif
if ( sleep < bj_WAIT_FOR_COND_MIN_INTERVAL ) then
set sleep = bj_WAIT_FOR_COND_MIN_INTERVAL
endif
call PolledWait( sleep )
set sleep = 0.00
//Frost blow sound
if ( UseSounds() ) then
set x = GetUnitX(target)
set y = GetUnitY(target)
call PlaySoundEx( frost_blow, x, y, z, false )
endif
//Conitinue code if we have more jumps
call IssueTargetOrder( dummy, "frostnova", target ) //Reduce bugs
call UnitApplyTimedLife( dummy, GT_RawCode(), 1.00 )
//Graphics main code
if ( UsingGraphics() ) then
set i = 0
set grp_group = CreateGroup()
loop
exitwhen i >= 10
set grpU = CreateUnit( p, Grp_RawCode(), x, y, i*36 )
call UnitAddAbility( grpU, Loc_RawCode() )
call GroupAddUnit( grp_group, grpU )
set i = i + 1
endloop
set t = CreateTimer()
call SetUserTimerData( t, G2I(grp_group) )
call TimerStart( t, timerSpeed, true, function GraphicsMain )
endif
set missile = null
set dummy = null
endloop
//Clear some vars
call KillSoundWhenDone( frost_blow )
call KillSoundWhenDone( field_snd )
call KillSoundWhenDone( missile_fly )
set caster = null
set target = null
set p = null
set x = 0.00
set y = 0.00
set max_jumps = 0
set distance = 0.00
set frost_damage = 0
set missileFlyHeight = 0.00
set missileSpeed = 0.00
set timerSpeed = 0.00
set i = 0
endfunction
//===========================================================================
function InitTrig_Chain_Frost_jass_123 takes nothing returns nothing
local boolexpr bx = Filter(null)
//Create chain frost trigger
set gg_trg_Chain_Frost_jass_123 = CreateTrigger( )
//Triggers events / condition
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 0), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 1), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 2), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 3), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 4), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 5), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 6), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 7), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 8), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player( 9), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(10), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(11), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(12), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(13), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(14), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call TriggerRegisterPlayerUnitEvent( gg_trg_Chain_Frost_jass_123, Player(15), EVENT_PLAYER_UNIT_SPELL_EFFECT, bx )
call DestroyBoolExpr(bx)
call TriggerAddCondition( gg_trg_Chain_Frost_jass_123, Condition( function IsChainFrostCasted ) )
// Main Trigger Actions
call TriggerAddAction( gg_trg_Chain_Frost_jass_123, function Trig_Chain_Frost_jass_Actions )
//-----------------------------------------------------------------------
//Preloader
call Preload("Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl")
call Preload("Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl")
call Preload("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl")
call Preload("Abilities\\Spells\\Other\\FrostDamage\\FrostDamage.mdl")
call PreloadEnd(2.)
endfunction
//TESH.scrollpos=197
//TESH.alwaysfold=0
// ====================================================================================
// Spell: Chain Frost
// Autor: Dark Dragon (Spell from DotA)
//
// Installation:
//
// 1) Make sure you have opened the JNGPS (1.2+) editor
// 2) Copy: Spells: Chain Frost ('CFro'), Chain Frost (Upgraded) ('CFru'), Chain Frost Upgrade
// 3) Triggers: This trigger "Chain Frost"
// 4) Paste all of that in your map, save map, give abilities to your units and enjoy!
// 5) Please do note that below you can edit spell constants to change spell style to your own needs
// ====================================================================================
// *** We want to make sure that dd librarys are included! ***
//! import "ddup.j"
library ChainFrost initializer Init requires DDUniversalPack
// -------------------------------------------------------------------
// EDITABLE GLOBALS
// -------------------------------------------------------------------
globals
// *** Main spell rawcode ***
// Default: 'CFro'
private constant integer CHAIN_FROST = 'CFro'
// *** Main spell updated rawcode ***
// Default: 'CFru'
private constant integer CHAIN_FROST_UPDATED = 'CFru'
// *** Missile effect path ***
// Default: "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
private constant string MISSILE_PATH = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
// *** Custom graphic effect path ***
// Default: "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
private constant string GRAPHIC_EFFECT_PATH = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
// *** Frost effect path ***
// Default: "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
private constant string FROST_EFFECT_PATH = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
// *** Pick closest unit? if false picks random unit ***
// Default: false
private constant boolean PICK_CLOSEST_UNIT = false
// *** Use custom sounds? ***
// Default: true
private constant boolean USE_CSOUNDS = true
// *** Use custom graphic ***
// Default: true
private constant boolean USE_CGRAPHIC = true
// *** Missile start speed ***
// Default: 200.
private constant real MISSILE_SSPEED = 200.
// *** Missile acceleration rate ***
// Default: 1200.
private constant real MISSILE_ACC = 1200.
// *** Missile end speed ***
// Default: 800.
private constant real MISSILE_ESPEED = 800.
// *** Particles of graphic count ***
// Default: 10
private constant integer PARTICLE_COUNT = 10
// *** Use custom AI ***
// Default: true
private constant boolean USE_CAI = true
// *** Level data ***
// {
private real array SlowDur[8191]
private real array Damage[8191]
private real array AoE[8191]
private integer array Jumps[8191]
// }
endglobals
// *** Level data setup function ***
private function Setup takes nothing returns nothing
// How much damage is dealed per level
// Default: 280, 370, 460, 550
set Damage[1] = 280.
set Damage[2] = 370.
set Damage[3] = 460.
set Damage[4] = 550.
// Slow duration in seconds per level
// Default: 5, 5, 5, 5
set SlowDur[1] = 5.
set SlowDur[2] = 5.
set SlowDur[3] = 5.
set SlowDur[4] = 5.
// Area of Effect per level in which units are picked for next jump
// Default: 550, 550, 550, 550
set AoE[1] = 550.
set AoE[2] = 550.
set AoE[3] = 550.
set AoE[4] = 550.
// Spell jumps
// Default: 6, 6, 6, 7
set Jumps[1] = 6
set Jumps[2] = 6
set Jumps[3] = 6
set Jumps[4] = 7
endfunction
//===================================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
//===================================================================================
// *** Custom types ***
private struct cfrost
// *** Members ***
graphic missile
player owner
integer jumps
real dmg
real aoe
real dur
real dist
unit target
unit caster
float vel
real startz
boolean fly
real mid_dist
static cfrost Temp = Null
static constant real Z_OFFSET = 40.
static constant real MS_SCALE = 1.1
// *** Constructor ***
static method create takes unit caster, unit target, integer lvl returns cfrost
local cfrost this = cfrost.allocate()
// *** Members set ***
set this.missile = AddGraphic(MISSILE_PATH, GetUnitX(caster), GetUnitY(caster), cfrost.Z_OFFSET, E_AngWidget(caster, target), cfrost.MS_SCALE)
set this.owner = GetOwningPlayer(caster)
set this.jumps = Jumps[lvl]
set this.dmg = Damage[lvl]
set this.aoe = AoE[lvl]
set this.dur = SlowDur[lvl]
set this.dist = 0.
set this.target = target
set this.caster = caster
set this.vel = Float(MISSILE_SSPEED)
set this.startz = 0.
set this.fly = false
set this.mid_dist = E_DistWidget(caster, target) / 2.
// *** member actions ***
call FloatLimits(this.vel, MISSILE_SSPEED, MISSILE_ESPEED, true)
return (this)
endmethod
// *** Destructor ***
method onDestroy takes nothing returns nothing
call DestroyGraphicEx(this.missile, 1.5)
call DestroyFloat(this.vel)
set this.target = null
set this.caster = null
endmethod
// *** NewZ operator ***
public method operator newz takes nothing returns real
return ((this.dist * (GetUnitFlyHeight(this.target)-this.startz)) / (this.dist + SquareRoot(hypot(this.missile.GetX()-GetUnitX(this.target), this.missile.GetY()-GetUnitY(this.target))))) + this.startz+cfrost.Z_OFFSET
endmethod
// *** x oper ***
public method operator x takes nothing returns real
return this.missile.GetX()
endmethod
// *** y oper ***
public method operator y takes nothing returns real
return this.missile.GetY()
endmethod
// *** Moves the missile ***
public method Move takes nothing returns boolean
local real vel = GetFloat(this.vel)*THREAD_DELAY*.001
local real rad = Atan2(GetUnitY(this.target)-this.missile.GetY(), GetUnitX(this.target)-this.missile.GetX())
call this.vel.Add(MISSILE_ACC*THREAD_DELAY*.001)
set this.dist = this.dist + vel
if (hypot(GetUnitX(this.target)-this.missile.GetX(), GetUnitY(this.target)-this.missile.GetY()) <= 900.) then
call this.missile.Move(GetUnitX(this.target), GetUnitY(this.target), GetUnitFlyHeight(this.target)+cfrost.Z_OFFSET)
return (true)
else
call this.missile.Move(this.missile.GetX() + vel * Cos(rad), this.missile.GetY() + vel * Sin(rad), this.newz)
endif
return (false)
endmethod
endstruct
// *** Nexus ***
private type nexus extends graphic array [PARTICLE_COUNT]
//--- Chain Frost Group ---
private function CFrostPick takes nothing returns boolean
return not IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) and not IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) and IsUnitVisible(GetFilterUnit(), cfrost.Temp.owner) and IsUnitEnemy(GetFilterUnit(), cfrost.Temp.owner) and not IsUnitDead(GetFilterUnit()) and not IsUnitInvulnerable(GetFilterUnit()) and (cfrost.Temp.target != GetFilterUnit())
endfunction
// *** Graphic timed destroy ***
private function DestroyGraphicAfter takes nothing returns nothing
local nexus n = nexus(GetSleepData())
local integer i
//! runtextmacro FORX("i", "0", "PARTICLE_COUNT", "call DestroyGraphicEx(i:n, 1.5)")
call n.destroy()
endfunction
//Missile Move Timer
private function MoveMissile takes cfrost cf returns boolean
// *** Locals ***
local integer i
local graphic g
local nexus n
local real orad = GetRandomReal(0., TWO_PI)
// *** Fly sound ***
if (USE_CSOUNDS and not cf.fly) then
set cf.fly = true
call GenericSoundDD("Abilities\\Spells\\NightElf\\FaerieDragonInvis\\PhaseShift1.wav", 80./1.27, cf.x, cf.y, .7)
endif
// *** Field sound
if (USE_CSOUNDS and cf.mid_dist != -1. and cf.dist >= cf.mid_dist) then
set cf.mid_dist = -1.
call GenericSoundDD("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget1.wav", 110./1.27, cf.x, cf.y, .7)
endif
// *** Hit target? ***
if (cf.Move()) then
// *** Deal damage and slow ***
call UnitDamageTarget(cf.caster, cf.target, cf.dmg, true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS)
call SlowUnit(cf.target, cf.dur)
call DestroyEffect(AddSpecialEffect(FROST_EFFECT_PATH, cf.x, cf.y))
// *** Sounds ***
if (USE_CSOUNDS) then
call GenericSoundDD("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget1.wav", 110./1.27, cf.x, cf.y, .8)
endif
// *** Seek next target jump ***
set cf.jumps = cf.jumps - 1
// *** Check is random target or closest ***
set cfrost.Temp = cf
if (PICK_CLOSEST_UNIT) then
set cf.target = GetNearestUnitEx(cf.x, cf.y, cf.aoe, Filter(function CFrostPick))
else
set cf.target = GetRandomUnitInRangeEx(cf.x, cf.y, cf.aoe, Filter(function CFrostPick))
endif
// *** Graphice display ***
if (USE_CGRAPHIC) then
set n = nexus.create()
//! runtextmacro FOR("i", "0", "PARTICLE_COUNT")
set g = AddGraphic(GRAPHIC_EFFECT_PATH, cf.x, cf.y, cfrost.Z_OFFSET, (i*360./PARTICLE_COUNT)+(orad*RADTODEG), 1.)
call PushGraphic(g, 500., 0., 500., 200., (i*TWO_PI/PARTICLE_COUNT)+orad, 0., LineData())
set i:n = g
//! runtextmacro ENDFOR()
call SleepEx(200./500., function DestroyGraphicAfter, integer(n))
endif
// *** End or no? ***
if (cf.target != null and cf.jumps > 0) then
set cf.startz = cf.missile.GetZ()-cfrost.Z_OFFSET
set cf.dist = 0.
set cf.fly = false
set cf.mid_dist = E_DistWidget(cf.caster, cf.target) / 2.
return (false)
else
// *** End spell here ***
call cf.destroy()
return (true)
endif
endif
// *** Continue spell ***
return (false)
endfunction
//Main Condition
private constant function Conditions takes nothing returns boolean
//Check if ability is cast "chain frost" or "chain frost upgraded(scepter)"
return GetSpellAbilityId() == CHAIN_FROST or GetSpellAbilityId() == CHAIN_FROST_UPDATED
endfunction
//Main Trigger Actions
private function Actions takes nothing returns nothing
//Local variables declaire
local unit caster = GetTriggerUnit() //Spell caster
local real x = GetUnitX(caster) //Coordinate X
local real y = GetUnitY(caster) //Coordinate Y
local unit target = GetSpellTargetUnit() //Spell target / chain target
local integer level = GetUnitAbilityLevel(caster, GetSpellAbilityId())
local cfrost cf
//Play chain frost start sound
if ( USE_CSOUNDS ) then
call GenericSoundDD("Abilities\\Spells\\Other\\BreathOfFrost\\BreathOfFrost1.wav", 90./1.27, x, y, .9)
endif
//Get jumps and max_jumps
if (GetSpellAbilityId() == CHAIN_FROST_UPDATED) then
set level = level + 1
endif
// *** cf load ***
set cf = cfrost.create(caster, target, level)
// *** Move missile thread ***
call NewThread(THREAD_DELAY, integer(cf), callback.MoveMissile, false)
// *** AI ***
if (USE_CAI and GetPlayerController(GetOwningPlayer(target)) != MAP_CONTROL_USER) then
call Sleep(MinSleep())
call IssuePointOrder(target, "attack", x, y)
endif
// *** Null locals ***
set caster = null
set target = null
endfunction
//===========================================================================
private function Init takes nothing returns nothing
// *** Main trigger load ***
call onSpellEffectRegister(function Conditions, function Actions)
// User setup
call Setup()
//-----------------------------------------------------------------------
//Preloader
call Preload(GRAPHIC_EFFECT_PATH)
call Preload(FROST_EFFECT_PATH)
call Preload(MISSILE_PATH)
call Preload("Abilities\\Spells\\Other\\FrostDamage\\FrostDamage.mdl")
call Preload("Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget1.wav")
call Preload("Abilities\\Spells\\NightElf\\Barkskin\\BarkSkinTarget1.wav")
call Preload("Abilities\\Spells\\NightElf\\FaerieDragonInvis\\PhaseShift1.wav")
call Preload("Abilities\\Spells\\Other\\BreathOfFrost\\BreathOfFrost1.wav")
call PreloadEnd(2.)
endfunction
endlibrary
//==========================================================================
// Dark Dragon Library Code v1.3
//
// * Made on Warcraft III v1.30.4
//
// Installation:
//
// 1) Export instantdummy.mdx from this map and import it to your map, leave path at "war3mapImported/instantdummy.mdx"
// 2) Copy this trigger to your map, save your map and then change below line "// external ... " or copy "DD Dummy" and paste it in your map
// 3) Copy and paste "Unit Chill" ability from this map to your map
// 4) Match the rawcodes below to your map or use same ones as below
//
// Credits:
// ('Vexorian' - dummy.mdx)
//============================================================================
// *** Change "// external" to "//! external", save your map, close map, change back from "//!" to "//" and save map again.
// *** This will create dummy in your map
//
// ==================================
// external ObjectMerger w3u ushd dumy uabi "Aloc,Amrf" uble 0 ucbs 0 ucpt 0 umxp 0 umxr 0 umdl "war3mapImported\instantdummy.mdx" ushu "None" umvh 0 umvs 1 umas 1 umis 1 ucol 0 ufoo 0 uhom 1 umpi 10000 umpm 10000 usid 1 usin 1 unam "DD Dummy"
// ==================================
//! zinc
library DDLib requires optional TimerUtils, optional GroupUtils
{
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// *** Lib constants ***
public {
// ----------------------------------------------------------------------
// * Start modify/match rawcodes to your map
constant integer DD_DUMMYCODE = 'dumy';
constant integer DD_ABILITY_CROWN_FORM = 'Amrf';
constant integer DD_CHILL = 'Achl';
constant integer DD_CHILL_BUFF = 'Bfro';
// * End modify
// ----------------------------------------------------------------------
constant integer p_null = (0x0);
constant real DD_INTERVAL = .017;
// map min and max coords
real DDMinX = 0.;
real DDMinY = 0.;
real DDMaxX = 0.;
real DDMaxY = 0.;
}
private {
constant integer HARVEST_ID = 'Ahrl';
constant real TRIGGER_REFRESH_RATE = (60.)*3.; /// damage detection trigger
unit TreeChecker = null;
trigger TempTrig = null;
integer NTrig = 0;
trigger DmgTrig[];
p_real EnumVec = p_null;
boolexpr EnumFilter = null;
sound ErrorSound = null;
timer GameElapsedTimer = null;
constant integer RND_INT_MAX_ARRAY_N = 100;
integer RndInt[], RndIntWriteN = 00, RndIntReadN = 00;
trigger TrigMouseEvent = null;
force RndGenForce = null;
real RndElapsedTime = 0.;
}
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// * types
public {
// *** pointer to list of data ***
type p_integer extends integer[8];
type p_real extends real[8];
type p_unit extends unit[8];
function H2ID(handle h) -> integer {
return GetHandleId(h) - 0x100000;
}
function New_pInteger(integer i) -> p_integer
{ p_integer p = p_integer.create(); p[0] = i; return p; }
function New_pReal(real r) -> p_real
{ p_real p = p_real.create(); p[0] = r; return p; }
function New_pUnit(unit u) -> p_unit
{ p_unit p = p_unit.create(); p[0] = u; return p; }
function pVector(real x, real y, real z) -> p_real {
p_real v = p_real.create();
v[0] = x; v[1] = y; v[2] = z;
return v;
}
// --------------------------------------------------------------------------------------
function DDMsg(string str) {
DisplayTimedTextFromPlayer(GetLocalPlayer(), 0., 0., 30., str);
}
// --------------------------------------------------------------------------------------
function DisplayErrorMsgPlayer(player p, real dur, string msg) {
if (GetLocalPlayer() == p) {
StartSound(ErrorSound);
DisplayTimedTextFromPlayer(p, 0., 0., dur, "|cffffcc00"+ msg +"|r");
}
}
}
// -----------------------------------------------------------------------
// -> ***** private globals *****
// -----------------------------------------------------------------------
private {
location TempLoc = Location(0., 0.);
timer TimerStack[];
integer TimN = 0;
group GroupStack[];
integer GrpN = 0;
unit DummyStack[];
integer DumN = 0;
integer TimTicks[];
integer TimData[];
timer TimTim1[];
timer TimTim2[];
integer UnitStackData = 0;
unit UnitStack[];
integer US_N = 0;
public hashtable DDHT = InitHashtable();
}
// -----------------------------------------------------------------------
public {
// *** Global funcs
function Pw_2(real x) -> real {
return x*x;
}
function DDHypot(real x, real y) -> real {
return (x*x) + (y*y);
}
function DDTerrZ(real x, real y) -> real {
MoveLocation(TempLoc, x, y);
return GetLocationZ(TempLoc);
}
function DDWidgetTerrZ(widget w) -> real {
MoveLocation(TempLoc, GetWidgetX(w), GetWidgetY(w));
return GetLocationZ(TempLoc);
}
function DDEffectTerrZ(effect e) -> real {
MoveLocation(TempLoc, BlzGetLocalSpecialEffectX(e), BlzGetLocalSpecialEffectY(e));
return GetLocationZ(TempLoc);
}
function DDGetUnitZ(unit u) -> real {
return BlzGetUnitZ(u) + GetUnitFlyHeight(u);
}
// =================================================================
// *** Save Handle data ****
// =================================================================
function DDSet(handle h, integer id, integer val) {
SaveInteger(DDHT, id+1, GetHandleId(h), val);
}
function DDGet(handle h, integer id) -> integer {
return LoadInteger(DDHT, id+1, GetHandleId(h));
}
function DDHas(handle h, integer id) -> boolean {
return HaveSavedInteger(DDHT, id+1, GetHandleId(h));
}
function DDFlush(integer id) {
FlushChildHashtable(DDHT, id+1);
}
// =================================================================
// *** Timer Handling ****
// =================================================================
// -> check if timer is recycled
function DDIsTimRecycled(timer t) -> boolean {
integer i;
for(i=TimN-01; i >= 00; i-=01)
if (TimerStack[i] == t)
return true;
return false;
}
// -> Load timer for recycling
function DDLoadTim() -> timer {
static if (LIBRARY_TimerUtils) { return NewTimer(); }
else {
if (TimN > 0) {
TimN -= 1;
return TimerStack[TimN];
}
return CreateTimer();
}
}
// -> recycle loaded timer
function DDRecycleTim(timer t) {
static if (LIBRARY_TimerUtils) { ReleaseTimer(t); }
else {
static if (DEBUG_MODE)
if (DDIsTimRecycled(t)) {
DDMsg("Multiple recycle of timer!");
return;
}
TimerStack[TimN] = t;
TimN += 1;
}
}
// ** Get data stored on expired timer
function DDTimData() -> integer {
return TimData[H2ID(GetExpiredTimer())];
}
// *** Custom timer tick
function DDCTimTick(timer t) -> integer {
return TimTicks[H2ID(t)];
}
// *** Gets current tick and adds next one ***
function DDTimTick() -> integer {
integer id = H2ID(GetExpiredTimer());
TimTicks[id] += 01;
return TimTicks[id];
}
// ** start timer with data storage
function DDStartTim(real secs, boolean looping, integer pdata, code func) -> timer {
timer t = DDLoadTim();
TimData[H2ID(t)] = pdata;
TimerStart(t, secs, looping, func);
return t;
}
// ** start timer with data storage, and launches it instantly
function DDStartTimInst(real secs, boolean looping, integer pdata, code func) -> timer {
timer t1 = DDLoadTim(), t2 = DDLoadTim(), t3 = DDLoadTim();
TimData[H2ID(t2)] = pdata;
TimerStart(t2, 0., false, func);
TimTim1[H2ID(t3)] = t1;
TimTim2[H2ID(t3)] = t2;
TimerStart(t3, .005, false, function() {
timer t = GetExpiredTimer();
integer id = H2ID(t);
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
t = TimTim2[id];
if (DDIsTimRecycled(t))
t = TimTim1[id];
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
});
TimData[H2ID(t1)] = pdata;
TimerStart(t1, secs, looping, func);
return t1;
}
// *** Quit expired timer ***
function DDQuitTim() {
timer t = GetExpiredTimer();
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
function DDQuitTimEx(timer t) {
TimTicks[H2ID(t)] = 00;
PauseTimer(t);
static if (LIBRARY_TimerUtils)
ReleaseTimer(t);
else {
TimerStack[TimN] = t;
TimN += 1;
}
}
// =================================================================
// *** Group Handling ****
// =================================================================
// -> Load timer for recycling
function DDLoadGroup() -> group {
static if (LIBRARY_GroupUtils) { return NewGroup(); }
else {
if (GrpN > 0) {
GrpN -= 1;
return GroupStack[GrpN];
}
return CreateGroup();
}
}
// -> Recycle group
function DDRecycleGroup(group g) {
static if (LIBRARY_GroupUtils) { ReleaseGroup(g); }
else {
GroupClear(g);
GroupStack[GrpN] = g;
GrpN += 1;
}
}
// --------------------------------------------------------
// -- Quick filter area
private integer GroupFilterData = 00;
function DDGroupFilterArea(real x, real y, real radius, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRange(g, x, y, radius, Filter(func));
DDRecycleGroup(g);
}
// --------------------------------------------------------
// -- Quick filter rect
function DDGroupFilterRect(rect r, integer data, code func) {
group g = DDLoadGroup();
GroupFilterData = data;
GroupEnumUnitsInRect(g, r, Filter(func));
DDRecycleGroup(g);
}
function DDGFilterData() -> integer {
return GroupFilterData;
}
function DDGFilterDataSet(integer data) {
GroupFilterData = data;
}
// --------------------------------------------------------
// *** Filtrates and fills units in to memory
function DDGroupFillMemArea(real x, real y, real radius, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRange(g, x, y, radius, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDGroupFillMemRect(rect r, integer data, code filter) {
group g = DDLoadGroup();
boolexpr exp = And(Filter(filter), Filter(function() -> boolean {
UnitStack[US_N] = GetFilterUnit();
US_N += 1;
return false;
}));
US_N = 0;
UnitStack[0] = null;
UnitStackData = data;
GroupEnumUnitsInRect(g, r, exp);
DDRecycleGroup(g);
DestroyBoolExpr(exp);
exp = null;
}
function DDMemUnitN() -> integer { return US_N; }
function DDMemUnitData() -> integer { return UnitStackData; }
function DDMemUnit(integer index) -> unit {
if (US_N == 0) return null;
debug {
if (index < 0) {
BJDebugMsg("DDMemUnit: index less than 0");
index = 0;
} else if (index >= US_N) {
BJDebugMsg("DDMemUnit: index greater than units alloc size");
index = 0;
}
}
return UnitStack[index];
}
// --------------------------------------------------------
// --------------------------------------------------------
// ***
// =================================================================
// *** Dummy Handling ****
// =================================================================
// -> Load dummy for recycling
function DDLoadDummy() -> unit {
if (DumN > 0) {
DumN -= 1;
PauseUnit(DummyStack[DumN], false);
return DummyStack[DumN];
}
return CreateUnit(Player(0xF), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
}
// *** prepares/setups dummy for spell casting
function DDLoadSpellDummy(player owner, real x, real y, integer abil, integer abilLevel) -> unit {
unit dummy = DDLoadDummy();
SetUnitOwner(dummy, owner, false);
SetUnitX(dummy, x);
SetUnitY(dummy, y);
if (abil != p_null) {
UnitAddAbility(dummy, abil);
SetUnitAbilityLevel(dummy, abil, abilLevel);
}
return dummy;
}
// -> Recycle dummy
function DDRecycleDummy(unit u) {
PauseUnit(u, true);
DummyStack[DumN] = u;
DumN += 1;
}
// -> Recycle dummy timed
function DDRecycleDummyTimed(unit u, real secs) {
DDStartTim(secs, false, New_pUnit(u), function() {
DDRecycleDummy(p_unit(DDTimData())[0]);
p_unit(DDTimData()).destroy();
DDQuitTim();
});
}
// *** shares vision for timed amount, usually for dummy casting
function DDUnitShareVisionTimed(unit u, player toP, real secs) {
p_integer pi = p_integer.create();
pi[0] = New_pUnit(u);
pi[1] = GetPlayerId(toP);
UnitShareVision(u, toP, true);
DDStartTim(secs, false, pi, function() {
p_integer pi = DDTimData();
UnitShareVision(p_unit(pi[0])[0], Player(pi[1]), false);
p_unit(pi[0])[0] = null;
p_unit(pi[0]).destroy();
pi.destroy();
DDQuitTim();
});
}
// *** Remove ability timed ***
private struct uratimed {
private {
unit u;
integer abil;
}
static method create(unit whichUnit, integer id, real time) -> uratimed {
thistype this = allocate();
u = whichUnit;
abil = id;
DDStartTim(time, false, this, function() {
thistype this = DDTimData();
UnitRemoveAbility(u, abil);
DDQuitTim();
deallocate();
});
return 0;
}
}
function DDRemoveAbilityTimed(unit u, integer abil, real secs) { uratimed.create(u, abil, secs); }
function DDSpellDamage(unit u, unit v, real dmg) {
real life = GetWidgetLife(v);
real dmgfactor = 1.;
if (IsUnitType(v, UNIT_TYPE_HERO)) {
if (UnitHasItemOfTypeBJ(v, 'brac'))
dmgfactor = .5;
else
dmgfactor = .75;
}
if (life > dmg*dmgfactor) {
SetWidgetLife(v, life-(dmg*dmgfactor));
} else
UnitDamageTarget(u, v, 99999., false, true, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, WEAPON_TYPE_WHOKNOWS);
}
// -------------------------------------------------------------------------------------
// *** Chills target unit
private struct chill {
unit u, dmy;
real dur;
static chill Data[];
//static key CHILL_KEY;
}
function DDUnitChill(player p, unit u, real dur) -> boolean {
//chill c = DDGet(u, chill.CHILL_KEY);
chill c = chill.Data[H2ID(u)];
unit d;
real rad;
if (c == p_null) {
c = chill.create();
c.u = u; c.dur = dur;
chill.Data[H2ID(u)] = c;
//DDSet(u, chill.CHILL_KEY, c);
d = DDLoadDummy();
c.dmy = d;
rad = GetUnitFacing(d) * bj_DEGTORAD;
SetUnitOwner(d, p, false);
UnitAddAbility(d, DD_CHILL);
SetUnitX(d, GetUnitX(u) - 20.*Cos(rad));
SetUnitY(d, GetUnitY(u) - 20.*Sin(rad));
if (IssueTargetOrder(d, "frostnova", u)) {
DDStartTim(.1, true, c, function() {
chill c = DDTimData();
c.dur -= .1;
if (c.dur <= 0. || GetUnitAbilityLevel(c.u, DD_CHILL_BUFF) == 00) {
UnitRemoveAbility(c.u, DD_CHILL_BUFF);
UnitRemoveAbility(c.dmy, DD_CHILL);
DDRecycleDummy(c.dmy);
chill.Data[H2ID(c.u)] = p_null;
//DDSet(c.u, chill.CHILL_KEY, p_null);
c.u = null;
c.destroy();
DDQuitTim();
}
});
return true;
}
return false;
}
c.dur = dur;
return true;
}
// ------------------------------------------------------------------------------------------------
struct fade {
unit u;
real trans;
real rate, e_trans, dur;
static constant real INTERVAL = .1;
static method create(unit u, real dur, real s_trans, real e_trans) -> fade {
fade this = allocate();
this.u = u;
this.trans = s_trans;
this.rate = ((e_trans-s_trans)/dur)*fade.INTERVAL;
this.e_trans = e_trans;
this.dur = dur;
return this;
}
}
// *** Fades unit over time ***
public function DDFadeUnit(unit u, integer from_alpha, integer to_alpha, real duration) {
fade f = fade.create(u,
duration,
from_alpha/2.55,
to_alpha/2.55);
SetUnitVertexColor(u, 255, 255, 255, from_alpha);
// --- Start thread ---
DDStartTim(fade.INTERVAL, true, f, function() {
fade f = DDTimData();
f.trans += f.rate;
f.dur -= fade.INTERVAL;
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.trans*2.55));
if (f.dur < 0.) {
SetUnitVertexColor(f.u, 255, 255, 255, R2I(f.e_trans*2.55));
f.u = null;
f.destroy();
DDQuitTim();
}
});
}
// ------------------------------------------------------------------------------------------------
// Check if unit is invulnerable
function DDIsUnitInvulnerable(unit u) -> boolean {
unit d = DDLoadDummy();
real hp = GetWidgetLife(u);
boolean flag;
UnitDamageTarget(d, u, 1., true, false, null, null, null);
flag = GetWidgetLife(u) == hp;
SetWidgetLife(u, hp);
DDRecycleDummy(d);
return flag;
}
// *** check if unit is ward
function DDIsUnitWard(unit whichUnit) -> boolean {
return GetUnitDefaultMoveSpeed(whichUnit) == 0.;
}
// =================================================================
// *** Effect Handling ****
// =================================================================
// -----------------------------------------------
// *** Define movable effect
struct ddeffect {
private {
effect e;
real fac; // facing angle in radians
real effZ; // pitch in radians
real decay;
real stepTrans, cTrans, eTrans;
static constant real EFFECT_DECAY = 5.;
}
// =========================================================================================
// =========================================================================================
static method create(string mdl, real x, real y, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = 0.;
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
return this;
}
static method createZ(string mdl, real x, real y, real z, real facRad, real size) -> ddeffect {
ddeffect this = allocate();
this.e = AddSpecialEffect(mdl, x, y);
this.fac = facRad;
this.effZ = z-DDTerrZ(x, y);
BlzSetSpecialEffectRoll(this.e, facRad);
BlzSetSpecialEffectScale(this.e, size);
BlzSetSpecialEffectZ(this.e, z);
return this;
}
// -----------------
method destroy() {
DestroyEffect(this.e);
this.e = null;
deallocate();
}
// *** destroys effect timed
method destroyx(real decayTime) {
DDStartTim(decayTime, false, this, function() {
ddeffect se = DDTimData();
BlzSetSpecialEffectPosition(se.e, DDMaxX, DDMaxY, 0.);
DestroyEffect(se.e);
se.e = null;
se.deallocate();
DDQuitTim();
});
}
// =========================================================================================
// =========================================================================================
method operator Z=(real z) { BlzSetSpecialEffectZ(this.e, z); }
method operator X() -> real { return BlzGetLocalSpecialEffectX(this.e); }
method operator Y() -> real { return BlzGetLocalSpecialEffectY(this.e); }
method operator Z() -> real { return BlzGetLocalSpecialEffectZ(this.e); }
method operator WZ() -> real { return DDEffectTerrZ(this.e); }
method operator Height() -> real { return this.Z-this.WZ; }
method operator Facing=(real facRad) { BlzSetSpecialEffectRoll(this.e, facRad); this.fac = facRad; }
method operator Facing() -> real { return this.fac; }
method Position(real x, real y) { BlzSetSpecialEffectPosition(this.e, x, y, this.effZ+this.WZ); }
method PositionZ(real x, real y, real z) { BlzSetSpecialEffectPosition(this.e, x, y, z); }
method Animation(animtype at) { BlzPlaySpecialEffect(this.e, at); }
method AnimationSpeed(real animSpeed) { BlzSetSpecialEffectTimeScale(this.e, animSpeed/100.); }
//method operator Pitch=(integer pitch) { SetUnitAnimationByIndex(u, pitch); }
//method Face(widget w) { Facing = Atan2(GetWidgetY(w)-Y, GetWidgetX(w)-X)*bj_RADTODEG; }
method Fade(real startTransparency, real endTransparency, real duration) {
this.cTrans = startTransparency;
this.eTrans = endTransparency;
this.stepTrans = .1*(endTransparency-startTransparency) / duration;
BlzSetSpecialEffectAlpha(this.e, R2I(startTransparency*2.55));
DDStartTim(.1, true, this, function() {
ddeffect dde = DDTimData();
dde.cTrans += dde.stepTrans;
if (dde.stepTrans > 0.)
if (dde.cTrans >= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
else
if (dde.cTrans <= dde.eTrans) {
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.eTrans*2.55));
DDQuitTim();
return;
}
BlzSetSpecialEffectAlpha(dde.e, R2I(dde.cTrans*2.55));
});
}
}
private type timedeffect extends effect[01];
function DDDestroyEffectTimed(effect e, real secs) {
timedeffect te = timedeffect.create();
te[00] = e;
DDStartTim(secs, true, te, function() {
timedeffect te = DDTimData();
DestroyEffect(te[00]);
te.destroy();
DDQuitTim();
});
}
}
// ----------------------------------------------------------------------------
// *** Main damage detection function, registers any damage dealt to units ***
public function DDTriggerRegisterAnyUnitDamaged(trigger t) {
DmgTrig[NTrig] = t;
NTrig += 1;
}
function InitDamageDetection() {
code c = function() {
if (TempTrig != null)
DestroyTrigger(TempTrig);
TempTrig = CreateTrigger();
TriggerRegisterEnterRectSimple(TempTrig, bj_mapInitialPlayableArea);
TriggerAddCondition(TempTrig, function() -> boolean {
integer i;
// *** Check if we need to exec triggers or register an unit ***
if (GetTriggerEventId() == EVENT_UNIT_DAMAGED) {
for(i=0; i < NTrig; i+=1)
if (IsTriggerEnabled(DmgTrig[i]))
if (TriggerEvaluate(DmgTrig[i]))
TriggerExecute(DmgTrig[i]);
}
else
// *** Register new unit ***
TriggerRegisterUnitEvent(GetTriggeringTrigger(),
GetTriggerUnit(),
EVENT_UNIT_DAMAGED);
return false;
});
DDGroupFilterRect(bj_mapInitialPlayableArea, 00, function() -> boolean {
TriggerRegisterUnitEvent(TempTrig, GetFilterUnit(), EVENT_UNIT_DAMAGED);
return false;
});
};
trigger t = CreateTrigger();
TriggerAddAction(t, c);
TriggerExecute(t);
DestroyTrigger(t);
TimerStart(CreateTimer(), TRIGGER_REFRESH_RATE, true, c);
t = null;
}
// ---------------------------------------------------------------------------------
// *** Main enum dests in range function ***
public function DDEnumDestsInRange(p_real vec, real radius, boolexpr filter, code pfunc) {
rect r = Rect(vec[0]-radius, vec[1]-radius, vec[0]+radius, vec[1]+radius);
EnumVec[0] = vec[0];
EnumVec[1] = vec[1];
EnumVec[2] = radius;
if (filter != null) filter = And(EnumFilter, filter);
else filter = EnumFilter;
EnumDestructablesInRect(r, filter, pfunc);
if (filter != EnumFilter) { DestroyBoolExpr(filter); filter = null; }
RemoveRect(r);
r = null;
}
function InitEnumDests() {
EnumVec = p_real.create();
EnumFilter = Filter(function() -> boolean {
return Pw_2(EnumVec[0]-GetDestructableX(GetFilterDestructable())) + Pw_2(EnumVec[1]-GetDestructableY(GetFilterDestructable())) < Pw_2(EnumVec[2]);
});
}
// --------------------------------------------------------------------------------------
// *** checks is destruct tree ***
public function DDIsDestructableTree(destructable d) -> boolean {
if (d != null) {
PauseUnit(TreeChecker, false);
if (IssueTargetOrder(TreeChecker, "harvest", d)) {
PauseUnit(TreeChecker, true);
return true;
}
PauseUnit(TreeChecker, true);
}
return false;
}
function InitDestTreeCheck() {
TreeChecker = CreateUnit(Player(bj_PLAYER_NEUTRAL_EXTRA), DD_DUMMYCODE, DDMaxX, DDMaxY, 0.);
UnitAddAbility(TreeChecker, HARVEST_ID);
PauseUnit(TreeChecker, true);
}
// --------------------------------------------------------------------------------------
public function DDNewTextTagUnit(unit whichUnit, string text, real dur, real red, real green, real blue, real transparency) {
CreateTextTagUnitBJ( text, whichUnit, 0., 11.00, red, green, blue, transparency );
SetTextTagPermanentBJ( bj_lastCreatedTextTag, false );
SetTextTagVelocityBJ( bj_lastCreatedTextTag, 48.00, 90 );
SetTextTagFadepointBJ( bj_lastCreatedTextTag, dur-1.25 );
SetTextTagLifespanBJ( bj_lastCreatedTextTag, dur );
}
// --------------------------------------------------------------------------------------
struct cameranoisedata {
player p[12];
integer n=00;
}
public function DDCameraSetSourceNoiseForPlayers(real x, real y, real mag, real vel, real maxDist, real duration) {
integer i;
real d;
cameranoisedata cnd = cameranoisedata.create();
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( Pw_2(GetCameraTargetPositionX()-x) + Pw_2(GetCameraTargetPositionY()-y) );
if (d < maxDist) {
cnd.p[cnd.n] = Player(i);
CameraSetSourceNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
CameraSetTargetNoise(mag-(d*(mag/maxDist)), vel-(d*(vel/maxDist)));
cnd.n += 01;
}
}
}
DDStartTim(duration, false, cnd, function() {
cameranoisedata cnd = DDTimData();
while(cnd.n != 00) {
cnd.n -= 01;
if (GetLocalPlayer() == cnd.p[cnd.n])
CameraSetSourceNoise(0., 0.);
CameraSetTargetNoise(0., 0.);
}
cnd.destroy();
DDQuitTim();
});
}
// --------------------------------------------------------------------------------------
hashtable GenSndTable = null;
public function DDGenericSound(string file, real vol, real x, real y, real mxDist, real pitch) {
integer sh = StringHash(file),
snd_n = LoadInteger(GenSndTable, sh, 04);
sound s = LoadSoundHandle(GenSndTable, sh, snd_n);
real d;
integer i;
if (s == null) {
s = CreateSound(file, false, false, false, 10, 10, "");
SaveSoundHandle(GenSndTable, sh, snd_n, s);
} else if (GetSoundIsPlaying(s)) {
StopSound(s, false, false);
}
SetSoundPitch(s, pitch);
snd_n += 01;
if (snd_n == 04)
snd_n = 00;
SaveInteger(GenSndTable, sh, 04, snd_n);
// Play sound and shake camera for players within spell cast range
for (i=00; i < bj_MAX_PLAYERS; i+=01) {
if (GetLocalPlayer() == Player(i)) {
d = SquareRoot( DDHypot(GetCameraTargetPositionX()-x, GetCameraTargetPositionY()-y) );
if (d < mxDist) {
SetSoundVolume(s, R2I((vol-d*(vol/mxDist))*1.27));
StartSound(s);
}
}
}
}
public function DDGetGameElapsedTime() -> real {
return TimerGetElapsed(GameElapsedTimer);
}
public function DDGetRndReal(real min, real max) -> real {
real rnd = ((max-min)/1000000.)*I2R(RndInt[RndIntReadN]);
debug if (max > 1000000.)
DDMsg("ERROR: \"DDGetRndNumber\" - 'max' variable is greater than 1000000!");
RndIntReadN += 01; if (RndIntReadN == RND_INT_MAX_ARRAY_N) RndIntReadN = 00;
return min + rnd;
}
public function DDGetRndInt(integer min, integer max) -> integer {
return R2I( DDGetRndReal(I2R(min), I2R(max)) );
}
// ====================================================================
function onInit() {
InitDamageDetection();
InitDestTreeCheck();
InitEnumDests();
DDMinX = GetRectMinX(bj_mapInitialPlayableArea);
DDMinY = GetRectMinY(bj_mapInitialPlayableArea);
DDMaxX = GetRectMaxX(bj_mapInitialPlayableArea);
DDMaxY = GetRectMaxY(bj_mapInitialPlayableArea);
GenSndTable = InitHashtable();
ErrorSound = CreateSound( "Sound\\Interface\\Error.wav", false, false, false, 10, 10, "" );
SetSoundParamsFromLabel( ErrorSound, "InterfaceError" );
SetSoundDuration( ErrorSound, 614 );
SetSoundVolume(ErrorSound, 127);
GameElapsedTimer = CreateTimer();
TimerStart(GameElapsedTimer, 10800., false, null);
for(RndIntWriteN=00; RndIntWriteN < RND_INT_MAX_ARRAY_N; RndIntWriteN+=01)
RndInt[RndIntWriteN] = GetRandomInt(00, 1000000);
RndIntWriteN = 00;
RndGenForce = CreateForce();
TrigMouseEvent = CreateTrigger();
ForForce(bj_FORCE_ALL_PLAYERS, function() {
if (GetPlayerController(GetEnumPlayer()) == MAP_CONTROL_USER)
TriggerRegisterPlayerEvent(TrigMouseEvent, GetEnumPlayer(), EVENT_PLAYER_MOUSE_MOVE);
});
TriggerAddCondition(TrigMouseEvent, Condition(function() -> boolean {
real mouseN;
boolean xFirst = GetRandomInt(00, 01) == 01;
if (!IsPlayerInForce(GetTriggerPlayer(), RndGenForce)) {
// example: input x = 578.4571496
// output rnd n = 4571498
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
if (mouseN == 0.)
return false;
//mouseN *= 100.;
RndInt[RndIntWriteN] = R2I((mouseN - I2R(R2I(mouseN))) * 1000.);
//DDMsg(I2S(RndInt[RndIntWriteN]));
//RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
if (xFirst)
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseY()) * 100.;
else
mouseN = RAbsBJ(BlzGetTriggerPlayerMouseX()) * 100.;
RndInt[RndIntWriteN] += R2I((mouseN - I2R(R2I(mouseN))) * 1000.)*1000;
//DDMsg(I2S(RndInt[RndIntWriteN]));
RndIntWriteN += 01; if (RndIntWriteN == RND_INT_MAX_ARRAY_N) RndIntWriteN = 00;
ForceAddPlayer(RndGenForce, GetTriggerPlayer());
}
if (DDGetGameElapsedTime()-RndElapsedTime > .125) {
ForceClear(RndGenForce);
RndElapsedTime = DDGetGameElapsedTime();
}
return false;
}));
}
}
//! endzinc
//巸ڮ铨SH.scrollpos=0
//TESH.alwaysfold=0
// ====================================================================================
// Spell: Chain Frost
// Autor: Dark Dragon (Spell from DotA)
//
// Installation:
//
// 1) Make sure you have opened the JNGP editor
// 2) Copy from object editor: Chain Frost ('CFro'), Chain Frost (Upgraded) ('CFru'), Chain Frost Upgrade (Scepter) and "Unit Chill" and match its rawcode in "DD Library"
// 3) Triggers: This trigger "Chain Frost" and "DD Library"
// 4) Paste all of that in your map, save map, give abilities to your units and enjoy!
// 5) Please do note that bellow you can edit spell constants to change spell style to your own needs.
//
// Credits:
// ('Hey its X' and 'Chriz' - FrostEffect.mdx)
// ====================================================================================
//! zinc
library ChainFrost requires DDLib
{
// -------------------------------------------------------------------
// EDITABLE GLOBALS
// -------------------------------------------------------------------
private {
// *** Main spell rawcode ***
// Default: 'CFro'
constant integer CHAIN_FROST = 'CFro';
// *** Main spell updated rawcode, if you dont use upgraded, then just change 'CFru' to 0 ***
// Default: 'CFru'
constant integer CHAIN_FROST_UPGRADED = 'CFru';
// *** Frost nova for chain frost rawcode / NOTE: Now modify in DD Library DD_CHILL rawcode ***
// Default: 'A000'
//constant integer FROST_NOVA = 'CfFn';
// *** Frost nova dummy order / NOTE: no longer in use ***
// Default: "frostnova"
//constant string FROST_NOVA_ORDER = "frostnova";
// *** Missile effect path ***
// Default: "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl"
constant string MISSILE_PATH = "Abilities\\Weapons\\FrostWyrmMissile\\FrostWyrmMissile.mdl";
// *** Nova effect path ***
// Default: "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl"
constant string FROST_PATH = "Abilities\\Spells\\Undead\\FrostNova\\FrostNovaTarget.mdl";
// *** Missile speed ***
// Default: 600.
constant real MISSILE_SPEED = 600.;
// *** Missile size ***
// Default: 1.4
constant real MISSILE_SCALE = 1.4;
// *** Missile height offset, that is the min z of missile ***
// Default: 80.
constant real MISSILE_Z_OFFSET = 80.;
// *** Jumps to nerby units first or any within aoe range
// Default: true
constant boolean CF_AOE_PICK_NERBY_UNIT = true;
// *** Use custom sound ***
// Default: true
constant boolean USE_CSOUND = true;
// *** Custom sound path ***
// Default: "Abilities\\Spells\\Other\\BreathOfFrost\\BreathOfFrost1.wav"
constant string SOUND_PATH = "Abilities\\Spells\\Other\\BreathOfFrost\\BreathOfFrost1.wav";
// *** Use custom graphic ***
// Default: true
constant boolean USE_CGRAPHIC = true;
// *** Custom nova remenant effect path ***
// Default: "SpecialEffects\\FrostEffect.mdx"
constant string NOVA_PATH = "SpecialEffects\\FrostEffect.mdx";
// *** Nova effect lifespan in seconds ***
// Default: 6.
constant real NOVA_LIFESPAN = 6.;
// *** Nova percentage size ***
// Default: 1.25
constant real NOVA_SCALE = 1.25;
// *** Nova decay time in seconds ***
// Default: .4
constant real NOVA_DECAY_TIME = .4;
// *** Custom graphic effect path ***
// Default: "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl"
constant string GRAPHIC_EFFECT_PATH = "Abilities\\Weapons\\ZigguratFrostMissile\\ZigguratFrostMissile.mdl";
// *** Particles of graphic count ***
// Default: 8
constant integer PARTICLE_COUNT = 10;
// *** Particles travel distance ***
// Default: 180.
constant real PARTICLE_MIN_RADIUS = 100.;
// Default: 300.
constant real PARTICLE_MAX_RADIUS = 300.;
// *** Particle start height offset, that is the min z of particle ***
// Default: 10.
constant real PARTICLE_MIN_HEIGHT = 10.;
// *** Particles travel height, MIN_HEIGHT + MAX_HEIGHT is overall max height ***
// Default: 180.
constant real PARTICLE_MAX_HEIGHT = 180.;
// *** Particles lifespan in seconds ***
// Default: .8
constant real PARTICLE_LIFESPAN = 1.2;
// *** Particles percentage size ***
// Default: .9
constant real PARTICLE_SCALE = .8;
// *** Custom graphic aftereffect path ***
// Default: "Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorDamage.mdl"
constant string GRAPHIC_AFTEREFFECT_PATH = "Abilities\\Spells\\Undead\\FrostArmor\\FrostArmorDamage.mdl";
// *** every x seconds play animation
// Default: .6
constant real GRAPHIC_AFTEREFFECT_ANIMTIME = .6;
// *** how many seconds does aftereffect last?
// Default: 4.2
constant real GRAPHIC_AFTEREFFECT_DURATION = 4.2;
// *** Make sure missile is created at caster position, if in debug mode it tells you to enable this, you should ***
// Default: false
constant boolean CHECK_MISSILE_POSITION = false;
// *** Level data ***
// * Jump range
function JumpRange(integer level) -> real {
real jrange[];
// ----------------------
// The range the spell uses to pick next target
// Default: 500., 550., 550., 600.
jrange[1] = 500.;
jrange[2] = 550.;
jrange[3] = 550.;
jrange[4] = 600.;
// ----------------------
return jrange[level];
}
// * Max Damage per jump
function NovaDamage(integer level) -> real {
real dmg[];
// ----------------------
// Max damage per jump
// Default: 250, 360, 475, 600
dmg[1] = 250.;
dmg[2] = 360.;
dmg[3] = 475.;
dmg[4] = 600.;
// ----------------------
return dmg[level];
}
// * Nova hero damage percentage alternation, heros will receive percentage of NovaDamage(level)
function NovaHeroDamagePercentage(integer level) -> real {
real hero_dmg_perc[];
hero_dmg_perc[1] = 77.;
hero_dmg_perc[2] = 79.;
hero_dmg_perc[3] = 81.;
hero_dmg_perc[4] = 81.;
return hero_dmg_perc[level];
}
// * Energy contained in nova/max damge spell can deal per cast
function CFMaxDamage(integer level) -> real {
real mdmg[];
// ----------------------
// Max damage per jump
// Default: 1500, 2520, 3325, 4800
mdmg[1] = 6. * 250.;
mdmg[2] = 7. * 360.;
mdmg[3] = 7. * 475.;
mdmg[4] = 8. * 600.;
return mdmg[level];
}
// * Frost / slow duration in seconds
function FrostSlowDuration(integer level) -> real {
real dur[];
// ----------------------
// Duration of freezing effect
// Default: 8, 9, 10, 10
dur[1] = 8.;
dur[2] = 9.;
dur[3] = 10.;
dur[4] = 10.;
return dur[level];
}
// *** this is filter for choosing next unit to jump on
function JumpUnitFilter() -> code {
return function() -> boolean {
cfdata cf = DDMemUnitData(); // data access from filterd spell instance
return (!IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) &&
!IsUnitType(GetFilterUnit(), UNIT_TYPE_MECHANICAL) &&
!IsUnitType(GetFilterUnit(), UNIT_TYPE_ANCIENT) &&
!IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) &&
!IsUnitType(GetFilterUnit(), UNIT_TYPE_DEAD) &&
IsUnitVisible(GetFilterUnit(), cf.owner) &&
IsUnitEnemy(GetFilterUnit(), cf.owner) &&
!DDIsUnitWard(GetFilterUnit()) &&
BlzIsUnitSelectable(GetFilterUnit()) &&
!BlzIsUnitInvulnerable(GetFilterUnit()) &&
(cf.target != GetFilterUnit())); // (cf.target is current target, so choose next target should not be same unit)
};
}
}
//===================================================================================
// DO NOT EDIT BELOW IF YOU DONT KNOW JASS
//===================================================================================
// *** custom types ***
//type iceparticles extends effect[PARTICLE_COUNT];
struct iceparticles {
effect e[PARTICLE_COUNT];
real dx[PARTICLE_COUNT], dy[PARTICLE_COUNT];
method operator [](integer index) -> effect {
return this.e[index];
}
method operator []=(integer index, effect e) {
this.e[index] = e;
}
}
// ----------------------------------------------
// *** Chain frost data struct ***
struct cfdata {
unit caster, target, ltarget /*,dummy*/;
integer lvl;
real aoe, mx_dmg;
player owner;
ddeffect missile;
//static sound CastSnd = null;
//static constant real CONST_SPEED = DD_INTERVAL*PARTICLE_RADIUS/PARTICLE_LIFESPAN;
//static cfdata Temp = p_null;
//static unit ChainNexus[];
//static integer CNN = 0;
static method create(unit c, unit t, integer lvl) -> cfdata {
cfdata this = allocate();
caster = c;
target = t;
//dummy = null;
owner = GetOwningPlayer(caster);
this.lvl = lvl;
missile = p_null;
mx_dmg = CFMaxDamage(lvl);
aoe = JumpRange(lvl);
//ltarget = null;
return this;
}
// --------------------------------------------------------------------------------------------------------------
// *** Parabolic particle motion
static method ParticleZ(integer mxtick, integer tick) -> real {
return ( (-PARTICLE_MAX_HEIGHT/Pw_2(.5*mxtick)) * Pw_2(tick-(mxtick*.5)) + PARTICLE_MAX_HEIGHT );
}
// --------------------------------------------------------------------------------------------------------------
// *** load custom particles at given target position
static method CreateParticleDispersion(unit target) {
iceparticles ip = iceparticles.create();
integer i;
real x = GetUnitX(target), y = GetUnitY(target);
real spd, rad;
// *** create missiles
for(i=0; i < PARTICLE_COUNT; i+=1) {
ip[i] = AddSpecialEffect(GRAPHIC_EFFECT_PATH, x, y);
BlzSetSpecialEffectRoll(ip[i], i*2.*bj_PI/PARTICLE_COUNT);
BlzSetSpecialEffectScale(ip[i], PARTICLE_SCALE);
BlzSetSpecialEffectZ(ip[i], DDGetUnitZ(target) + PARTICLE_MIN_HEIGHT);
spd = DD_INTERVAL*DDGetRndReal(PARTICLE_MIN_RADIUS, PARTICLE_MAX_RADIUS)/PARTICLE_LIFESPAN;
rad = i*2.*bj_PI/PARTICLE_COUNT;
ip.dx[i] = spd * Cos(rad);
ip.dy[i] = spd * Sin(rad);
//ip[i] = xeffect.create(GRAPHIC_EFFECT_PATH, x, y, i*360./PARTICLE_COUNT, PARTICLE_SCALE);
//ip[i].Z = GetUnitFlyHeight(target) + PARTICLE_MIN_HEIGHT;
}
// *** start loop
DDStartTim(DD_INTERVAL, true, ip, function() {
integer i, tick = DDTimTick(), mxtick = R2I(PARTICLE_LIFESPAN/DD_INTERVAL);
iceparticles ip = DDTimData();
boolean in_air = true;
real x, y;
// *** movement ***
for(i=0; i < PARTICLE_COUNT; i+=1) {
if (in_air) {
//rad = i*2.*bj_PI/PARTICLE_COUNT;
BlzSetSpecialEffectPosition(ip[i], BlzGetLocalSpecialEffectX(ip[i]) + ip.dx[i],
BlzGetLocalSpecialEffectY(ip[i]) + ip.dy[i],
BlzGetLocalSpecialEffectZ(ip[i]) + /*wz-DDEffectTerrZ(ip[i]) +*/ ParticleZ(mxtick, tick) - ParticleZ(mxtick, tick-1));
/*ip[i].X += (PARTICLE_RADIUS/PARTICLE_LIFESPAN)*DD_INTERVAL * Cos(i*2.*bj_PI/PARTICLE_COUNT);
ip[i].Y += (PARTICLE_RADIUS/PARTICLE_LIFESPAN)*DD_INTERVAL * Sin(i*2.*bj_PI/PARTICLE_COUNT);
ip[i].Z += wz-ip[i].WZ + ParticleZ(mxtick, tick) - ParticleZ(mxtick, tick-1);*/
if (BlzGetLocalSpecialEffectZ(ip[i])-DDEffectTerrZ(ip[i]) < 10.) {
in_air = false;
i = -01;
}
} else {
x = BlzGetLocalSpecialEffectX(ip[i]);
y = BlzGetLocalSpecialEffectY(ip[i]);
DestroyEffect(ip[i]);
ip[i] = AddSpecialEffect(GRAPHIC_AFTEREFFECT_PATH, x, y);
BlzSetSpecialEffectScale(ip[i], PARTICLE_SCALE);
}
}
// *** done? ***
if (!in_air) {
DDStartTim(GRAPHIC_AFTEREFFECT_ANIMTIME, true, ip, function() {
iceparticles ip = DDTimData();
integer i;
if (DDTimTick() == GRAPHIC_AFTEREFFECT_DURATION/GRAPHIC_AFTEREFFECT_ANIMTIME) {
for(i=00; i < PARTICLE_COUNT; i+=01) {
DestroyEffect(ip[i]);
ip[i] = null;
}
ip.destroy();
DDQuitTim();
return;
}
for(i=00; i < PARTICLE_COUNT; i+=01)
BlzPlaySpecialEffect(ip[i], ANIM_TYPE_BIRTH);
});
DDQuitTim();
}
});
}
// --------------------------------------------------------------------------------------------------------------
// *** creates nova effect ***
static method CreateNovaEffect(unit target) {
if (!IsUnitType(target, UNIT_TYPE_FLYING))
ddeffect.create(NOVA_PATH, GetUnitX(target), GetUnitY(target), GetRandomReal(0., 2.*bj_PI), NOVA_SCALE).destroyx(NOVA_LIFESPAN);
}
method NewMissile(unit c) {
missile = ddeffect.createZ(MISSILE_PATH,
GetUnitX(c), GetUnitY(c), DDGetUnitZ(c) + MISSILE_Z_OFFSET,
Atan2(-GetUnitY(c)+GetUnitY(target), -GetUnitX(c)+GetUnitX(target)),
MISSILE_SCALE);
}
method MissileHit() {
real dmg = RMinBJ(NovaDamage(lvl), mx_dmg);
/*
dummy = DDLoadDummy();
SetUnitOwner(dummy, owner, false);
SetUnitX(dummy, GetUnitX(target) - 5. * Cos( DmyRad() ));
SetUnitY(dummy, GetUnitY(target) - 5. * Sin( DmyRad() ));
UnitAddAbility(dummy, FROST_NOVA);
*/
missile.destroy();
missile = p_null;
DDUnitShareVisionTimed(target, owner, 1.); // make sure dummy can cast spell
//if (IssueTargetOrder(dummy, FROST_NOVA_ORDER, target)) {
if ( DDUnitChill(owner, target, FrostSlowDuration(lvl)) ) {
static if (USE_CGRAPHIC) {
CreateNovaEffect(target);
CreateParticleDispersion(target);
}
// *** Main effect
DestroyEffect( AddSpecialEffectTarget(FROST_PATH, target, "origin") );
// *** Main damage
if (!IsUnitType(target, UNIT_TYPE_HERO)) {
if (GetWidgetLife(target) > dmg)
SetWidgetLife(target, GetWidgetLife(target)-dmg);
else {
dmg = GetWidgetLife(target);
UnitDamageTarget(caster, target, 32767., true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS);
}
} else {
if (GetWidgetLife(target) > dmg*NovaHeroDamagePercentage(lvl)/100.)
SetWidgetLife(target, GetWidgetLife(target)-dmg*NovaHeroDamagePercentage(lvl)/100.);
else {
dmg = GetWidgetLife(target)*100./NovaHeroDamagePercentage(lvl);
UnitDamageTarget(caster, target, 32767., true, false, ATTACK_TYPE_CHAOS, DAMAGE_TYPE_COLD, WEAPON_TYPE_WHOKNOWS);
}
}
mx_dmg -= dmg;
}
// -------------------------------------------------------------------------------------------------
// *** fill nexus with new targets ***
ltarget = target;
static if (CF_AOE_PICK_NERBY_UNIT) {
// *** get new target in nerby range
DDGroupFillMemArea(GetUnitX(target), GetUnitY(target), aoe*.4, this, JumpUnitFilter());
if (DDMemUnitN() > 00)
target = DDMemUnit(DDGetRndInt(0, DDMemUnitN()-1));
else {
// *** get new target in far range
DDGroupFillMemArea(GetUnitX(target), GetUnitY(target), aoe, this, JumpUnitFilter());
target = DDMemUnit(DDGetRndInt(0, DDMemUnitN()-1));
}
} else {
// *** get new target in far range
DDGroupFillMemArea(GetUnitX(target), GetUnitY(target), aoe*.4, this, JumpUnitFilter());
target = DDMemUnit(DDGetRndInt(0, DDMemUnitN()-1));
}
/*
DDStartTim(1., false, New_pUnit(dummy), function() {
p_unit pu = DDTimData();
UnitRemoveAbility(pu[00], FROST_NOVA);
DDRecycleDummy(pu[00]);
pu.destroy();
DDQuitTim();
});*/
}
method MissileMove() -> boolean {
real rad = Atan2(GetUnitY(target)-missile.Y, GetUnitX(target)-missile.X);
real dist = SquareRoot( DDHypot(GetUnitX(target)-missile.X, GetUnitY(target)-missile.Y) );
real dz = DDGetUnitZ(target) - missile.Z + MISSILE_Z_OFFSET;
missile.PositionZ(missile.X + MISSILE_SPEED*DD_INTERVAL*Cos(rad),
missile.Y + MISSILE_SPEED*DD_INTERVAL*Sin(rad),
missile.Z + ((dz/dist) * MISSILE_SPEED*DD_INTERVAL));
/*missile.X += MISSILE_SPEED*DD_INTERVAL*Cos(rad);
missile.Y += MISSILE_SPEED*DD_INTERVAL*Sin(rad);
missile.Z += (dz/dist) * (MISSILE_SPEED*DD_INTERVAL);*/
missile.Facing = rad;
//BJDebugMsg(R2S(rad*bj_RADTODEG));
return dist <= 3.*MISSILE_SPEED*DD_INTERVAL;
}
}
// =================================================================
// =================================================================
function onInit() {
trigger t = CreateTrigger();
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_CAST);
TriggerRegisterAnyUnitEventBJ(t, EVENT_PLAYER_UNIT_SPELL_EFFECT);
TriggerAddCondition(t, Condition(function() -> boolean {
unit u = GetTriggerUnit(), v = GetSpellTargetUnit();
integer abillvl;
cfdata cf;
// ----------------------------------------------------------------
// *** condition ****
if (GetSpellAbilityId() != CHAIN_FROST && GetSpellAbilityId() != CHAIN_FROST_UPGRADED)
return false;
// ----------------------------------------------------------------
if (GetTriggerEventId() == EVENT_PLAYER_UNIT_SPELL_CAST) {
if (IsUnitType(v, UNIT_TYPE_MAGIC_IMMUNE)) {
IssuePointOrder(u, "move",
GetUnitX(u)+20.*Cos(GetUnitFacing(u)*bj_DEGTORAD),
GetUnitY(u)+20.*Sin(GetUnitFacing(u)*bj_DEGTORAD));
DisplayErrorMsgPlayer(GetOwningPlayer(u), bj_QUEUE_DELAY_HINT, "Can't cast on magic immune units!");
}
u = null; v = null;
return false;
}
// ----------------------------------------------------------------
abillvl = GetUnitAbilityLevel(u, GetSpellAbilityId());
if (GetSpellAbilityId() == CHAIN_FROST_UPGRADED) abillvl += 1;
// *** store data ***
cf = cfdata.create(u, v, abillvl);
cf.NewMissile(cf.caster);
debug if (!IsUnitInRangeXY(cf.caster, cf.missile.X, cf.missile.Y, 100.) && !CHECK_MISSILE_POSITION)
BJDebugMsg("|cffff0000Error:|rChain Frost missile not created at caster position, please enable \"CHECK_MISSILE_POSITION\".");
//UnitShareVision(cf.target, cf.owner, true);
if (USE_CSOUND)
DDGenericSound(SOUND_PATH, 100., GetUnitX(cf.caster), GetUnitY(cf.caster), 3500., 1.);
// *** init timer and start periodic spell actions ***
DDStartTim(DD_INTERVAL, true, cf, function() {
cfdata cf = DDTimData();
static if (CHECK_MISSILE_POSITION) {
if (DDTimTick() == 01)
cf.missile.PositionZ(GetUnitX(cf.caster), GetUnitY(cf.caster), MISSILE_Z_OFFSET);
}
// *** move missile and check if reached target ***
if (cf.MissileMove()) {
// *** hit the target ***
cf.MissileHit();
// *** end spell ***
if (cf.target == null || cf.mx_dmg < 0.1) {
//UnitShareVision(cf.ltarget, cf.owner, false);
cf.destroy();
DDQuitTim();
return;
}
// *** new missile ***
if (cf.missile == p_null && cf.mx_dmg > 0.) {
cf.NewMissile(cf.ltarget);
//UnitShareVision(cf.ltarget, cf.owner, false);
//UnitShareVision(cf.target, cf.owner, true);
}
}
});
u = null; v = null;
return false;
}));
}
}
//! endzinc
// ============================================================================================
// NOTE: This lua script is for old version of spell, I recommend to just copy and paste abilities and dummy to your map
// Change me from /* to // and then back to /* after saving and reopening the map
// |
// ˇ
/*
// Credits: PurgeandFire for lua tutorial
//! externalblock extension=lua ObjectMerger $FILENAME$
//! i -- ===================================================
//! i CHAIN_FROST_DAMAGE_ID = "CfFn"
//! i FREEZING_FIELD_ABIL_ID = "FFld"
//! i FREEZING_FIELD_IMPROVED_ABIL_ID = "FFdu" -- Set this value to "nil" if not using upgrader
//! i CHAIN_FROST_ABIL_ID = "CFro"
//! i CHAIN_FROST_IMPROVED_ABIL_ID = "CFru" -- Set this value to "nil" if not using upgrader or if not using chain frost spell
//! i AGHANIM_SCEPTER_ABIL_ID = "Aghs"
//! i -- ===================================================
//! i setobjecttype("abilities")
//! i createobject("AUfn", CHAIN_FROST_DAMAGE_ID)
//! i makechange(current,"anam", "Chain Frost Chill")
//! i makechange(current,"alev", "1")
//! i makechange(current,"aher", false)
// i local i = 0
// i local dmg = { "250.", "360.", "475.", "600." }
// i for i=1, 4 do
// i local si = tostring(i)
//! i makechange(current,"Ufn1",si,"0.")
//! i makechange(current,"Ufn2",si,0.)
//! i makechange(current,"aare",si,"0.")
//! i makechange(current,"ahdu",si,"10.")
//! i makechange(current,"adur",si,"10.")
//! i makechange(current,"amcs",si,"0")
//! i makechange(current,"aran",si,"99999.")
// i end
//! i createobject("AUsl", CHAIN_FROST_ABIL_ID)
//! i makechange(current,"anam", "Chain Frost")
//! i makechange(current,"alev", "3")
//! i makechange(current,"abpx", "3")
//! i makechange(current,"arpx", "3")
//! i makechange(current,"arhk", "C")
//! i makechange(current,"ahky", "C")
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNBreathofFrost.blp")
//! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\BTNBreathofFrost.blp")
//! i makechange(current,"aret", "Learn |cffffcc00C|rhain Frost - [|cffffcc00Level ".. string.char(37) .."d|r]")
//! i makechange(current,"arut", "Sends a breath of frost that jumps n times through nearby enemys dealing damage and slowing them for 10 seconds. |n|n|cFF0054A6Level 1|r - Each jump deals 250 damage, 6 jumps.|n|cFF0054A6Level 2|r - Each jump deals 360 damage, 7 jumps.|n|cFF0054A6Level 3|r - Each jump deals 475 damage, 7 jumps.")
//! i local i = 0
//! i local manacost = { "200", "325", "500" }
//! i for i=1, 3 do
//! i local si = tostring(i)
//! i makechange(current,"Usl1",si,".01")
//! i makechange(current,"aran",si,"750.")
//! i makechange(current,"abuf",si,nil)
//! i makechange(current,"acdn",si,tostring(22.5-2.5*i))
//! i makechange(current,"ahdu",si,".01")
//! i makechange(current,"adur",si,".01")
//! i makechange(current,"amcs",si, manacost[i])
//! i makechange(current,"atp1",si,"|cffffcc00C|rhain Frost - [|cffffcc00Level "..si.."|r]")
//! i end
//! i makechange(current,"aub1","1", "Sends a breath of frost that jumps 6 times through nearby enemys dealing 250 damage and slowing for 10 seconds.")
//! i makechange(current,"aub1","2", "Sends a breath of frost that jumps 7 times through nearby enemys dealing 360 damage and slowing for 10 seconds.")
//! i makechange(current,"aub1","3", "Sends a breath of frost that jumps 7 times through nearby enemys dealing 475 damage and slowing for 10 seconds.")
//! i if (CHAIN_FROST_IMPROVED_ABIL_ID ~= nil) then
//! i createobject("AUsl", CHAIN_FROST_IMPROVED_ABIL_ID)
//! i makechange(current,"anam", "Chain Frost")
//! i makechange(current,"ansf", " ( Upgraded )")
//! i makechange(current,"alev", "3")
//! i makechange(current,"abpx", "3")
//! i makechange(current,"arpx", "3")
//! i makechange(current,"arhk", "C")
//! i makechange(current,"ahky", "C")
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNBreathofFrost.blp")
//! i makechange(current,"arar", "ReplaceableTextures\\CommandButtons\\BTNBreathofFrost.blp")
//! i makechange(current,"aret", "Learn |cffffcc00C|rhain Frost - [|cffffcc00Level ".. string.char(37) .."d|r]")
//! i makechange(current,"arut", "Sends a breath of frost that jumps n times through nearby enemys dealing damage and slowing them for 10 seconds. |n|n|cFF0054A6Level 1|r - Each jump deals 360 damage, 7 jumps.|n|cFF0054A6Level 2|r - Each jump deals 475 damage, 7 jumps.|n|cFF0054A6Level 3|r - Each jump deals 600 damage, 8 jumps.")
//! i local i = 0
//! i local manacost = { "200", "325", "500" }
//! i for i=1, 3 do
//! i local si = tostring(i)
//! i makechange(current,"Usl1",si,".01")
//! i makechange(current,"aran",si,"750.")
//! i makechange(current,"abuf",si,nil)
//! i makechange(current,"acdn",si,tostring(22.5-2.5*i))
//! i makechange(current,"ahdu",si,".01")
//! i makechange(current,"adur",si,".01")
//! i makechange(current,"amcs",si, manacost[i])
//! i makechange(current,"atp1",si,"|cffffcc00C|rhain Frost - [|cffffcc00Level "..si.."|r]")
//! i end
//! i makechange(current,"aub1","1", "Sends a breath of frost that jumps 7 times through nearby enemys dealing 360 damage and slowing for 10 seconds.")
//! i makechange(current,"aub1","2", "Sends a breath of frost that jumps 7 times through nearby enemys dealing 475 damage and slowing for 10 seconds.")
//! i makechange(current,"aub1","3", "Sends a breath of frost that jumps 8 times through nearby enemys dealing 600 damage and slowing for 10 seconds.")
//! i end
//! i if (FREEZING_FIELD_IMPROVED_ABIL_ID ~= nil or CHAIN_FROST_IMPROVED_ABIL_ID ~= nil) then
//! i createobject("ANeg", AGHANIM_SCEPTER_ABIL_ID)
//! i makechange(current,"aart", "ReplaceableTextures\\CommandButtons\\BTNINV_Wand_05.blp")
//! i makechange(current,"Neg4","1", CHAIN_FROST_ABIL_ID..","..CHAIN_FROST_IMPROVED_ABIL_ID)
//! i if (FREEZING_FIELD_IMPROVED_ABIL_ID ~= nil) then
//! i makechange(current,"Neg3","1", FREEZING_FIELD_ABIL_ID..","..FREEZING_FIELD_IMPROVED_ABIL_ID)
//! i else
//! i makechange(current,"Neg3","1", nil)
//! i end
//! i makechange(current,"Neg5","1", nil)
//! i makechange(current,"Neg6","1", nil)
//! i makechange(current,"Neg1","1", "0.")
//! i makechange(current,"Neg2","1", "0.")
//! i makechange(current,"aher", false)
//! i makechange(current,"abuf","1", nil)
//! i makechange(current,"alev", "1")
//! i makechange(current,"arac", "nightelf")
//! i makechange(current,"anam", "CF & FF Upgrader")
//! i end
//! endexternalblock
*/
// ^
// |
// Change me from */ to // and then back to */ after saving and reopening the map
// ============================================================================================