//TESH.scrollpos=3
//TESH.alwaysfold=0
scope Ultima initializer Init
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤
//¤ *****************
//¤ - Ultima -
//¤ *****************
//¤ KeyTimers version
//¤
//¤ By: Daxtreme
//¤
//¤ --> How to implement in your map:
//¤
//¤ 1. Copy the spell "Ultima" into your map.
//¤ 2. Copy the unit named "Ultima" into your map.
//¤ 3. Copy this trigger into your map.
//¤ 4. Import the Invoke.mdx model into your map.
//¤ 5. Import the "Textures\RibbonNE1_blue.blp" texture into your map. (Keep the path as is)
//¤ 6. Import the "Textures\Blue_Glow2.blp" into your map. (Keep the path as is)
//¤ 7. (Optional) Define as variables the warcraft3 sounds featured in the Sound Editor.
//¤ For this, you need to search the sounds by name and select "Use as sound" when found.
//¤ 8. Copy the trigger named "KT" into your map.
//¤ 9. Copy the trigger named "GT" into your map
//¤ 10. Change the rawcodes in the configuration section so they fit with your map's.
//¤
//¤ --> How to customize it:
//¤
//¤ You can configure the spell using the few globals and functions just below. Change their values.
//¤
//¤
//¤ CREDITS:
//¤
//¤ - JetFangInferno's CharmTarget.
//¤ - BuilderBob's sound edit.
//¤ - Jesus4Lyf's KeyTimers2 and GTrigger Events sytems.
//¤ - Tinki3's spell map test template.
//¤
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//
// *** Configuration Section ***
//
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
globals
private constant integer RawCode = 'A000' // The spell's rawcode in the editor
private constant integer dummyid = 'h000' // The "ultima" unit's id
private constant real AreaEffect = 1000. // Damage AoE of the spell at the end
private constant real dummylife = .25 // The dummy unit's life span. Keep this low (Below 1.5)
private constant real period = 0.01 // Defines the rate at which the sphere augments in proportions
// I recommend keeping this very low if you don't want the sphere
// growing awkwardly as it is rather big at the end.
private constant real damage = 1000. // Multiply this by the level of the spell for damage value.
// Note that this spell wasn't meant to be balanced in a normal game.
private constant real duration = 5. // In seconds. You must change this into the Object Editor too.
private constant real SphereIncrement = 1. // Factor determining the sphere's growing speed. It is set to 1 meaning
// that every (period), its size is set to
// 100% + SphereIncrement * currentcount. In short, if period = 0.01,
// the sphere increases in size by 1% every 0.01 second = 100 times a second.
private constant real NoReturnPoint = 1. // Minimal duration needed before the spell gets out of control and finishes
// unleashing, even if the caster no longer is casting.
// Do not set this above 1., else the spell will never function.
// Expects values as so 0 < NoReturnPoint <= 1, 0.5 meaning the spell
// can be interrupted as long as the caster has been channeling
// for 50% of the total duration. Exceeding this state will cause the spell
// to trigger its end instantly when casting is stopped.
private constant attacktype AType = ATTACK_TYPE_NORMAL // Attack type. This one means "Spell".
private constant damagetype DType = DAMAGE_TYPE_UNIVERSAL // Damage type. This one means no damage reduction.
private filterfunc non_leaking_filter = null
private unit forFilter = null
endglobals
private function Damage takes unit u returns real
return I2R(GetUnitAbilityLevel(u, RawCode)) * damage
endfunction
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
//
// *** Spell Code ***
//
// --> I highly recommend editing the following only if you know what you're doing.
//
//¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
private constant function DummyFilter takes nothing returns boolean
return TRUE
endfunction
private function DeathConditions takes nothing returns boolean
return GetUnitTypeId(GetTriggerUnit()) == dummyid
endfunction
private struct data
unit die
unit c
integer lvl
integer id
real j = 0.
real i = 0.
real x
real y
endstruct
private function FilterEnemies takes nothing returns boolean
return IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(forFilter))
endfunction
private function Timer takes nothing returns boolean
local data d = KT_GetData()
local group g
local unit targ
local unit dummy
local real advancement = ((d.i * period) * (1. / period)) / (duration * (1. / period))
// END OF SPELL
if ( (GetUnitCurrentOrder(d.c) != d.id) and (advancement < NoReturnPoint ) ) then
set dummy = CreateUnit(GetOwningPlayer(d.c), dummyid, d.x, d.y, 0)
call UnitApplyTimedLife(dummy, 'BTLF', 1.5) // 1.5: Optimal visual effect time
call SetUnitScalePercent(dummy, 105. + (SphereIncrement * d.i), 105. + (SphereIncrement * d.i), 105. + (SphereIncrement * d.i))
call data.destroy(d)
set dummy = null
set d.c = null
return true
elseif ( (GetUnitCurrentOrder(d.c) != d.id) and (advancement >= NoReturnPoint ) ) then
set dummy = CreateUnit(GetOwningPlayer(d.c), dummyid, d.x, d.y, 0)
call UnitApplyTimedLife(dummy, 'BTLF', .25) // 0.25: Optimal visual effect time
call SetUnitScalePercent(dummy, 10000., 10000., 10000.)
set g = CreateGroup()
set forFilter = d.c
call GroupEnumUnitsInRange(g, d.x, d.y, AreaEffect, Condition(function FilterEnemies))
loop
set targ = FirstOfGroup(g)
exitwhen targ == null
call UnitDamageTarget(d.c, targ, Damage(d.c), true, false, AType, DType, WEAPON_TYPE_WHOKNOWS)
call GroupRemoveUnit(g, targ)
endloop
/*
call SetSoundPosition(gg_snd_LightningBolt, d.x, d.y, 0.)
call SetSoundVolume(gg_snd_LightningBolt, PercentToInt(100., 127))
if (gg_snd_LightningBolt != null) then
call StartSound(gg_snd_LightningBolt)
endif
*/
call DestroyGroup(g)
call data.destroy(d)
set d.c = null
set dummy = null
set g = null
set targ = null
return true
endif
// SPHERE INCREASING IN SIZE
set d.i = d.i + 1.
set dummy = CreateUnit(GetOwningPlayer(d.c), dummyid, d.x, d.y, 0)
call UnitApplyTimedLife(dummy, 'BTLF', dummylife)
call SetUnitScalePercent(dummy, 100.00 + (SphereIncrement * d.i), 100.00 + (SphereIncrement * d.i), 100.00 + (SphereIncrement * d.i))
set dummy = null
return false
endfunction
private function Actions takes nothing returns boolean
local data d = data.create()
set d.c = GetTriggerUnit()
set d.x = GetUnitX(d.c)
set d.y = GetUnitY(d.c)
set d.id = GetUnitCurrentOrder(d.c)
/*
call SetSoundPosition(gg_snd_DarkSummoningTarget1, d.x, d.y, 0.)
call SetSoundVolume(gg_snd_DarkSummoningTarget1, PercentToInt(100., 127))
if (gg_snd_DarkSummoningTarget1 != null) then
call StartSound(gg_snd_DarkSummoningTarget1)
endif
*/
set d.lvl = GetUnitAbilityLevel(d.c, RawCode)
call KT_Add(function Timer, d, period)
return false
endfunction
private function DeathCallBack takes nothing returns boolean
local data d = KT_GetData()
if d.j > .1 / period then // 0.1: optimal remove timer.
call RemoveUnit(d.die)
set d.die = null
return true
endif
set d.j = d.j + 1.
return false
endfunction
private function DeathActions takes nothing returns nothing
local data d = data.create()
set d.die = GetTriggerUnit()
call KT_Add(function DeathCallBack, d, period)
endfunction
//===========================================================================
private function Init takes nothing returns nothing
local trigger t = CreateTrigger( )
local integer index = 0
set non_leaking_filter = Filter( function DummyFilter )
loop
call TriggerRegisterPlayerUnitEvent(t, Player(index), EVENT_PLAYER_UNIT_DEATH, non_leaking_filter)
set index = index + 1
exitwhen index == bj_MAX_PLAYER_SLOTS
endloop
call TriggerAddCondition( t, Condition( function DeathConditions ) )
call TriggerAddAction( t, function DeathActions )
set t = null
call GT_AddActionToStartsEffectEvent(RawCode, function Actions)
endfunction
endscope