Name | Type | is_array | initial_value |
//TESH.scrollpos=210
//TESH.alwaysfold=0
// * Punitive Surge v1.01 by Maker *
// * Spell idea by Apheraz Lucent *
// * The caster rushes to the targeted point *
// * executing a spinning attack damaging *
// * enemies. The caster leaves a quickly *
// * fading "tail" of shadows of himself. *
scope PunitiveSurge initializer Punitive_Surge
globals
// How fast the unit moves at the beginning
private constant real INITIAL_SPEED = 12.
// Speed multiplier between loops, how fast the unit accelerates
private constant real ACCELERATION = 1.08
// Maximum charge speed
private constant real MAX_SPEED = 30.
// Damage per ability level
private constant real DAMAGE = 100.
// Damage bonus per ability level, not applied at level 1
private constant real BONUS_DAMAGE = 50
// Update interval for moving loops. 1/loopTime = Frames per second
private constant real LOOP_TIME = 0.03125
// AoE of the spell
private constant real RADIUS = 128.
// Animation speed for the attack ( 1.00 = normal speed )
private constant real ANIMATION_SPEED = 0.75
// Raw code of the ability
private constant integer ABILITYCODE = 'A000'
// The dummy's raw code
private constant integer DUMMYCODE = 'E001'
// The pathing item's raw code
private constant integer ITEMCODE = 'I000'
// How transparent the dummies are at beginning, 255 means 100% visible, 177,5 = 50% visible
private constant integer DUMMY_INITIAL_FADE = 175
// How fast the dummies fade
private constant integer FADE_RATE = 10
// ^Play around with those values to get to desired^
// ^tail fade time and tail length^
// Vertex color values for dummy. Default caster color = 255, 255, 255
// 0 , 0 , 0 = completely black
private constant integer RED = 100
private constant integer GREEN = 100
private constant integer BLUE = 100
// The animation that the dummies play
private constant integer ANIMATION = 6
// Attack type of the ability
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_HERO
// Damage type of the attack
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_NORMAL
// Effect on targets hit
private constant string HIT_EFFECT = "Objects\\Spawnmodels\\Human\\HumanBlood\\HumanBloodFootman.mdl"
// Effect on caster
private constant string CASTER_WEAPON_EFFECT = "Abilities\\Weapons\\AvengerMissile\\AvengerMissile.mdl"
// Effect attachment points for caster
private constant string ATTACH_1 = "weapon"
private constant string ATTACH_2 = "right hand"
// Effect attachment point for targets
private constant string ATTACH_3 = "chest"
// How many different sound effects are used
private constant integer SOUNDS = 3
// How loud the sounds will be. 255 = 100% volume, 0 = 0% volume
private constant integer SOUND_VOLUME = 255
//** DONT CHANGE THE VARIABLES BELOW **//
private unit U
private real R
private player P
private group UG
private sound array sounds
private integer casters = 0
private integer dummies = 0
private timer casterTimer = CreateTimer()
private timer dummyTimer = CreateTimer()
private group casterGroup = CreateGroup()
private group dummyGroup = CreateGroup()
private item itemPathing
private hashtable rageHash = InitHashtable()
endglobals
// Retuns the damage done by the spell
private constant function getDamage takes integer i returns real
return DAMAGE + BONUS_DAMAGE * (i - 1)
endfunction
// Returns distance between two points
private function getDistance takes real x1 , real x2 , real y1 , real y2 returns real
local real dx = x2 - x1
local real dy = y2 - y1
return SquareRoot(dx * dx + dy * dy )
endfunction
// Returns an angle between two points
private function getAngle takes real x1 , real x2 , real y1 , real y2 returns real
return Atan2(y2 - y1 , x2 - x1 )
endfunction
// Playes a sound on unit
function playSoundOnUnit takes unit u , sound soundHandle returns nothing
call AttachSoundToUnit( soundHandle , u )
call SetSoundVolume( soundHandle , SOUND_VOLUME )
call StartSound( soundHandle )
endfunction
// Filters unwanted units from getting damaged
// Damages targets and applies sound and sfx
private function unitFilter takes nothing returns boolean
local unit v = GetFilterUnit()
local sound s = sounds[GetRandomInt(1 , SOUNDS)]
if not(IsUnitType( v , UNIT_TYPE_DEAD )) and IsUnitEnemy( v , P ) and not(IsUnitInGroup(v, UG )) and not (IsUnitType( v, UNIT_TYPE_STRUCTURE ) ) then
call UnitDamageTarget( U , v , R , false , false , ATTACK_TYPE , DAMAGE_TYPE , WEAPON_TYPE_WHOKNOWS )
call DestroyEffect(AddSpecialEffectTarget( HIT_EFFECT , v , "ATTACH_3" ) )
call GroupAddUnit( UG , v )
call playSoundOnUnit( v , s )
endif
set v = null
set s = null
return true
endfunction
// Fades out and removes dummies
private function dummyLoop takes nothing returns nothing
local unit u = GetEnumUnit()
local integer i1 = GetHandleId(u)
local integer i2 = LoadInteger(rageHash , i1 , StringHash("fade") )
if i2 > 0 then
set i2 = i2 - FADE_RATE
call SetUnitVertexColor(u , RED , GREEN , BLUE , i2 )
call SaveInteger(rageHash , i1 , StringHash("fade") , i2 )
else
set dummies = dummies - 1
call GroupRemoveUnit( dummyGroup , u )
call FlushChildHashtable(rageHash , i1)
call RemoveUnit(u)
if dummies == 0 then
call PauseTimer(dummyTimer)
endif
endif
set u = null
endfunction
private function dummyTimerExpire takes nothing returns nothing
call ForGroup( dummyGroup , function dummyLoop )
endfunction
// Moves caster and checks collision
private function casterLoop takes nothing returns nothing
local unit u = GetEnumUnit()
local unit v
local integer i1 = GetHandleId(u)
local integer i2
local boolean b = LoadBoolean(rageHash , i1 , StringHash("boolean") )
local group g = CreateGroup()
local real r1 = GetUnitX(u)
local real r2 = GetUnitY(u)
local real r3 = LoadReal(rageHash , i1 , StringHash("x") )
local real r4 = LoadReal(rageHash , i1 , StringHash("y") )
local real r5 = getDistance( r1 , r3 , r2 , r4 )
local real r6
local real r7
local real r8
// Checks that the caster isn't dead and that the caster isn't close to
// the cast target point yet
if not (IsUnitType( u , UNIT_TYPE_DEAD) ) and r5 > MAX_SPEED and b == true then
set r5 = LoadReal(rageHash , i1 , StringHash("speed") ) * ACCELERATION
set r6 = LoadReal(rageHash , i1 , StringHash("angle") )
// Limits max speed
if r5 > MAX_SPEED then
set r5 = MAX_SPEED
endif
call SaveReal(rageHash , i1 , StringHash("speed") , r5 )
// Pathability is done by moving an item to the point the caster
// is supposed to move to. If the item's coordinates after moving it
// do not match the move to point's coordinates, the point is unpathable.
// If the point is pathable, move the caster there.
// If not, stop the spell.
// Prevents the caster from passing strcutures, trees, cliffs, pathing
// blockers and going off the map.
set r7 = r1 + r5 * Cos(r6)
set r8 = r2 + r5 * Sin(r6)
call SetItemVisible( itemPathing , true )
call SetItemPosition( itemPathing , r7 , r8 )
set r5 = GetItemX(itemPathing)
set r6 = GetItemY(itemPathing)
call SetItemVisible( itemPathing , false )
if r7 == r5 and r8 == r6 then
call SetUnitX(u , r7)
call SetUnitY(u , r8)
// Set globals so they can be passed used in the filter function
set U = u
set R = getDamage(GetUnitAbilityLevel( u , ABILITYCODE ))
set P = GetOwningPlayer(u)
set UG = LoadGroupHandle(rageHash , i1 , StringHash("group") )
call GroupEnumUnitsInRange( g , r7 , r8 , RADIUS , function unitFilter )
// Create a fading "tail" dummy
set v = CreateUnit( Player(15) , DUMMYCODE , r1 , r2 , GetUnitFacing(u) )
set i2 = GetHandleId(v)
set dummies = dummies + 1
call GroupAddUnit( dummyGroup , v )
call SetUnitColor( v , GetPlayerColor( P ) )
call SetUnitTimeScale( v , ANIMATION_SPEED )
call SetUnitAnimationByIndex( v , ANIMATION )
call SetUnitVertexColor( v , RED , GREEN , BLUE , DUMMY_INITIAL_FADE )
call SaveInteger(rageHash , i2 , StringHash("fade") , DUMMY_INITIAL_FADE )
if dummies == 1 then
call TimerStart( dummyTimer , LOOP_TIME , true , function dummyTimerExpire )
endif
set v = null
else
call SaveBoolean(rageHash , i1 , StringHash("boolean") , false )
endif
else
set casters = casters - 1
call GroupRemoveUnit( casterGroup , u )
call DestroyGroup(LoadGroupHandle(rageHash, i1 , StringHash("group") ) )
call DestroyEffect(LoadEffectHandle(rageHash , i1 , StringHash("wEffect1") ) )
call DestroyEffect(LoadEffectHandle(rageHash , i1 , StringHash("wEffect2") ) )
call FlushChildHashtable(rageHash , i1)
if casters == 0 then
call PauseTimer(casterTimer)
endif
endif
call DestroyGroup(g)
set u = null
set g = null
endfunction
private function casterTimerExpire takes nothing returns nothing
call ForGroup( casterGroup , function casterLoop )
endfunction
// This runs when the caster begins casting the ability.
// Begins casting -event is chose over Starts the effect - event
// since there's casting delay betweeb those events (default = 0.5 seconds).
// Begins castin -event fires sooner. It does not bug even if the unit gets a stop
// order during the ast time.
private function Actions takes nothing returns nothing
local unit u = GetTriggerUnit()
local integer i = GetHandleId(u)
local integer i2 = GetUnitAbilityLevel( u , ABILITYCODE )
local real x1 = GetUnitX(u)
local real y1 = GetUnitY(u)
local real x2 = GetSpellTargetX()
local real y2 = GetSpellTargetY()
local real r1 = getDamage(i2)
local real r2
local real r3
local real r4
// Checks that the target point is pathable.
// If not, use the nearest pathable point as the new
// target point.
call SetItemVisible( itemPathing , true )
call SetItemPosition( itemPathing , x2 , y2 )
set r3 = GetItemX(itemPathing)
set r4 = GetItemY(itemPathing)
if r3 == x2 and r4 == y2 then
set r2 = getAngle( x1 , x2 , y1 , y2 )
else
set r2 = getAngle( x1 , r3 , y1 , r4 )
set x2 = r3
set y2 = r4
endif
call SetUnitTimeScale( u , ANIMATION_SPEED )
call SetItemVisible( itemPathing , false )
call GroupAddUnit(casterGroup , u)
set casters = casters + 1
set UG = CreateGroup()
call SaveReal(rageHash , i , StringHash("x") , x2 )
call SaveReal(rageHash , i , StringHash("y") , y2 )
call SaveReal(rageHash , i , StringHash("angle") , r2 )
call SaveReal(rageHash , i , StringHash("damage") , r1 )
call SaveReal(rageHash , i , StringHash("speed") , INITIAL_SPEED )
call SaveGroupHandle(rageHash , i , StringHash("group") , UG )
call SaveBoolean(rageHash , i , StringHash("boolean") , true )
call SaveEffectHandle(rageHash , i , StringHash("wEffect1") , AddSpecialEffectTarget(CASTER_WEAPON_EFFECT , u , ATTACH_1 ) )
call SaveEffectHandle(rageHash , i , StringHash("wEffect2") , AddSpecialEffectTarget(CASTER_WEAPON_EFFECT , u , ATTACH_2 ) )
if casters == 1 then
call TimerStart( casterTimer , LOOP_TIME , true , function casterTimerExpire )
endif
set u = null
endfunction
private function Conditions takes nothing returns boolean
return GetSpellAbilityId() == ABILITYCODE
endfunction
private function Punitive_Surge takes nothing returns nothing
local trigger t = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_CAST )
call TriggerAddCondition( t , Condition( function Conditions ) )
call TriggerAddAction( t , function Actions )
set itemPathing = CreateItem( ITEMCODE , 0 , 0 )
call SetItemVisible( itemPathing , false )
set sounds[1] = gg_snd_MetalHeavySliceFlesh1
set sounds[2] = gg_snd_MetalHeavySliceFlesh2
set sounds[3] = gg_snd_MetalHeavySliceFlesh3
set t = null
endfunction
endscope