Name | Type | is_array | initial_value |
//TESH.scrollpos=47
//TESH.alwaysfold=0
//! zinc
library Init
{
public unit Hero;
public real X[], Y[];
public real face[];
public integer N = 0;
function onInit( )
{
trigger t = CreateTrigger( );
trigger t2 = CreateTrigger( );
Hero = CreateUnit( Player(0), 'Hblm', GetStartLocationX( 0 ), GetStartLocationY( 0 ), 270. );
//: ==----
// Restore Hero
//: ==----
TriggerRegisterPlayerEvent( t, Player(0), EVENT_PLAYER_END_CINEMATIC );
TriggerAddAction( t, function( )
{
if ( GetWidgetLife( Hero ) < 0.405 )
ReviveHero( Hero, GetUnitX( Hero ), GetUnitY( Hero ), true );
else
{
SetUnitState( Hero, UNIT_STATE_LIFE, GetUnitState( Hero, UNIT_STATE_MAX_LIFE ) );
SetUnitState( Hero, UNIT_STATE_MANA, GetUnitState( Hero, UNIT_STATE_MAX_MANA ) );
SetHeroLevel( Hero, GetHeroLevel( Hero ) + 1, true );
}
UnitResetCooldown( Hero );
} );
//: ==----
// Respawn System
//: ==----
GroupEnumUnitsOfPlayer( bj_lastCreatedGroup, Player(PLAYER_NEUTRAL_AGGRESSIVE), function( ) -> boolean
{
X[N] = GetUnitX( GetFilterUnit( ) );
Y[N] = GetUnitY( GetFilterUnit( ) );
face[N] = GetUnitFacing( GetFilterUnit( ) );
SetUnitUserData( GetFilterUnit( ), N );
N += 1;
return false;
} );
TriggerRegisterPlayerUnitEvent( t2, Player( PLAYER_NEUTRAL_AGGRESSIVE ), EVENT_PLAYER_UNIT_DEATH, null );
TriggerAddAction( t2, function( )
{
integer i = GetUnitUserData( GetTriggerUnit( ) );
unit u = CreateUnit( Player( PLAYER_NEUTRAL_AGGRESSIVE ), GetUnitTypeId( GetTriggerUnit( ) ), X[i], Y[i], face[i] );
SetUnitUserData( u, i );
u = null;
} );
//: ==----
// Visibility
//: ==----
bj_lastCreatedFogModifier = CreateFogModifierRect( Player(0), FOG_OF_WAR_VISIBLE, bj_mapInitialPlayableArea, true, false );
FogModifierStart( bj_lastCreatedFogModifier );
//: ==----
// Game messages
//: ==----
DisplayTimedTextToPlayer( Player(0), 0., 0., 6., "Spellmaking Template by Wizardum" );
DisplayTimedTextToPlayer( Player(0), 0., 0., 6., " " );
DisplayTimedTextToPlayer( Player(0), 0., 0., 6., "Units repawn automatically in their initial positions" );
DisplayTimedTextToPlayer( Player(0), 0., 0., 6., " " );
DisplayTimedTextToPlayer( Player(0), 0., 0., 6., "Press Esc to restore your hero and level up." );
t = null;
t2 = null;
}
}
//! endzinc
//: ===------
// Customizable Properties
//: ===------
//*****************************\\
//=-=-=» Main Properties «=-=-=\\
//: ===------
//: ===------
// End of Customizable Properties
//: ===------
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! zinc
library DyMotionMain
{
constant integer FPS = 30;
public constant integer dm_DUMMY_ID = 'h000';
public constant integer dm_UNTOUCHABLE = 'Aloc';
public constant integer dm_FLYING = 'Amrf';
public constant integer dm_TREE_DETECTION = 'Aeat';
public constant real dm_UPDATE_INTERVAL = 1.0 / FPS;
public function CopyGroup( group gIn, group gOut )
{
bj_groupAddGroupDest = CreateGroup( );
ForGroup( gIn, function( ) { GroupAddUnit( bj_groupAddGroupDest, GetEnumUnit( ) ); } );
gOut = bj_groupAddGroupDest;
}
/*_____________________________________________
***********************************************
*****Dummy Recycle Struct*****
---------------------------------------------*/
public struct Recycle
{
private static group DeadDummies;
private static unit u;
static method operator Dummy( ) -> unit
{
u = FirstOfGroup( DeadDummies );
if ( u == null )
{
u = CreateUnit( Player( 15 ), dm_DUMMY_ID, 0.0, 0.0, 0.0 );
UnitAddAbility( u, dm_UNTOUCHABLE );
UnitAddAbility( u, dm_FLYING );
UnitRemoveAbility( u, dm_FLYING );
PauseUnit( u, true );
}
else
GroupRemoveUnit( DeadDummies, u );
return u;
}
static method operator Dummy=( unit value )
{
SetUnitOwner( value, Player(15), true );
PauseUnit( value, true );
SetUnitScale( value, 1., 1., 1. );
SetUnitTimeScale( value, 1. );
GroupAddUnit( DeadDummies, value );
}
private static method onInit( )
{
integer i;
DeadDummies = CreateGroup( );
for ( 1 <= i <= 200 )
{
u = CreateUnit( Player( 15 ), dm_DUMMY_ID, 0.0, 0.0, 0.0 );
UnitAddAbility( u, dm_UNTOUCHABLE );
UnitAddAbility( u, dm_FLYING );
UnitRemoveAbility( u, dm_FLYING );
PauseUnit( u, true );
GroupAddUnit( DeadDummies, u );
}
}
}
}
//! endzinc
/*_____________________________________________
***********************************************
*****Is Terrain Walkable Lib*****
---------------------------------------------*/
library IsTerrainWalkable initializer Init
globals
public real X = 0.0
public real Y = 0.0
private rect r
private item check
private item array hidden
private integer hiddenMax = 0
endglobals
private function Init takes nothing returns nothing
set check = CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r = Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
function IsTerrainWalkable takes real x, real y, real maxrange returns boolean
call MoveRectTo(r, x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
call SetItemPosition(check,x,y)
set X = GetItemX(check)
set Y = GetItemY(check)
call SetItemVisible(check,false)
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
return (x-X)*(x-X)+(y-Y)*(y-Y) < maxrange*maxrange
endfunction
endlibrary
/*_____________________________________________
***********************************************
*****Bound Sentinel Lib*****
---------------------------------------------*/
library BoundSentinel initializer init
globals
private constant boolean ALLOW_OUTSIDE_PLAYABLE_MAP_AREA = false
private real maxx
private real maxy
private real minx
private real miny
endglobals
private function dis takes nothing returns boolean
local unit u=GetTriggerUnit()
local real x=GetUnitX(u)
local real y=GetUnitY(u)
if(x>maxx) then
set x=maxx
elseif(x<minx) then
set x=minx
endif
if(y>maxy) then
set y=maxy
elseif(y<miny) then
set y=miny
endif
call SetUnitX(u,x)
call SetUnitY(u,y)
set u=null
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local region r = CreateRegion()
local rect map
local rect rc
if ALLOW_OUTSIDE_PLAYABLE_MAP_AREA then
set map = GetWorldBounds()
else
set map = bj_mapInitialPlayableArea
endif
set minx = GetRectMinX(map)
set miny = GetRectMinY(map)
set maxx = GetRectMaxX(map)
set maxy = GetRectMaxY(map)
set rc=Rect(minx,miny,maxx,maxy)
call RegionAddRect(r, rc)
call RemoveRect(rc)
if ALLOW_OUTSIDE_PLAYABLE_MAP_AREA then
call RemoveRect(map)
endif
call TriggerRegisterLeaveRegion(t,r, null)
call TriggerAddCondition(t, Condition(function dis))
set rc=null
set map = null
endfunction
endlibrary
//TESH.scrollpos=156
//TESH.alwaysfold=0
//! zinc
library DyMotionMotion requires DyMotionMain, DyMotionMath, DyMotionProjectile
{
//_______________________________________________
//***********************************************
// *****Globals*****
//-----------------------------------------------
constant real dm_DEFAULT_MAX_SPEED = 1800.;
constant real dm_DEFAULT_LIFE_TIME = 6.;
//-----------------------------------------------
// *****Globals End*****
//_______________________________________________
//***********************************************
interface MotionEvents
{
method onMotion( ) = null;
method onLoop( ) = null;
}
public struct dmMotion extends MotionEvents
{
real LifeTime = dm_DEFAULT_LIFE_TIME;
boolean Paused = false;
boolean AutoFacing = true;
static method create( real x, real y, real angle, unit u ) -> dmMotion
{
dmMotion m = dmMotion.allocate( );
dmMotion.Indexing( m );
if ( u == null ) m.msl = dmProjectile.create( x, y );
else m.msl = dmProjectile.createWithUnit( u );
m.Angle = angle;
return m;
}
/*_____________________________________________
***********************************************
*****Operators*****
---------------------------------------------*/
method operator Angle ( ) -> real
{ return Atan2( spd * sin, spd * cos ); }
method operator Angle= ( real value )
{ cos = Cos( value ); sin = Sin( value ); msl.XYangle = value; }
method operator Speed ( ) -> real
{ return spd / dm_UPDATE_INTERVAL; }
method operator Speed= ( real value )
{ spd = value * dm_UPDATE_INTERVAL; }
method operator Acceleration ( ) -> real
{ return acc / dm_UPDATE_INTERVAL; }
method operator Acceleration= ( real value )
{ acc = value * dm_UPDATE_INTERVAL; }
method operator AngleSpeed ( ) -> real
{ return angspd / dm_UPDATE_INTERVAL; }
method operator AngleSpeed= ( real value )
{ angspd = value * dm_UPDATE_INTERVAL; }
method operator MaxSpeed ( ) -> real
{ return maxspd / dm_UPDATE_INTERVAL; }
method operator MaxSpeed= ( real value )
{ maxspd = value * dm_UPDATE_INTERVAL; }
method operator MinSpeed ( ) -> real
{ return minspd / dm_UPDATE_INTERVAL; }
method operator MinSpeed= ( real value )
{ minspd = value * dm_UPDATE_INTERVAL; }
method operator OrbitRadius( ) -> real
{ return orbRadius; }
method operator OrbitRadius= ( real value )
{ orbRadius = value; }
/*---------------------------------------------
*****Operators End*****
_______________________________________________
**********************************************/
method SetHomingPoint( real x, real y )
{ hmX = x; hmY = y; homing = true; }
method SetHomingUnit( unit t )
{ HomingUnit = t; homing = true; }
method StopHoming( )
{ HomingUnit = null; homing = false; }
method SetOrbitingPoint( real x, real y, real radius, real anglespeed )
{ orbX = x; orbY = y; orbRadius = radius; AngleSpeed = anglespeed; orbiting = true; }
method SetOrbitingUnit( unit t, real radius, real anglespeed )
{ orbUnit = t; orbRadius = radius; AngleSpeed = anglespeed; orbiting = true; }
method StopOrbiting( )
{ orbUnit = null; orbRadius = 0.; angspd = 0.; orbiting = false; }
method Bounce( )
{
if ( Angle > dm_PiOVER2 && Angle < dm_3OVER2Pi )
Angle = -Angle;
else
Angle = bj_PI - Angle;
}
/*_____________________________________________
***********************************************
*****Internal Stuff*****
---------------------------------------------*/
private real maxspd = dm_DEFAULT_MAX_SPEED * dm_UPDATE_INTERVAL;
private real minspd = 0.;
private real spd = 0.; //speed
private real acc = 0.; //acceleration
private real angspd = 0.; //angle speed
private real cos = 0., sin = 0.;
private boolean homing = false; //homing flag
unit HomingUnit = null; //homing unit
private real hmX = 0., hmY = 0.; //homing coordinates
private boolean orbiting = false; //orbiting flag
private unit orbUnit = null; //orbits around this unit
private real orbX = 0., orbY = 0.; //orbits around this point
private real orbRadius = 0.;
private delegate dmProjectile msl;
private integer id;
private static timer Timer;
private static integer N = 0;
private static dmMotion M[];
private static code update;
static group tempGroup = CreateGroup( );
private static method Update( )
{
dmMotion m;
integer i;
real a;
for ( 0 <= i < dmMotion.N )
{
m = dmMotion.M[i];
if ( !m.Paused )
{
if ( m.angspd != 0.0 )
{
if ( m.AutoFacing )
m.Angle = m.Angle + m.angspd;
else
{
a = m.Angle + m.angspd;
m.cos = Cos( a );
m.sin = Sin( a );
}
}
if ( m.homing )
{
if ( m.HomingUnit != null )
{
m.hmX = GetUnitX( m.HomingUnit );
m.hmY = GetUnitY( m.HomingUnit );
}
if ( m.AutoFacing )
m.Angle = Atan2( m.hmY - m.Y, m.hmX - m.X );
else
{
a = Atan2( m.hmY - m.Y, m.hmX - m.X );
m.cos = Cos( a );
m.sin = Sin( a );
}
}
else if ( m.orbiting )
{
if ( m.orbUnit != null ) { m.orbX = GetUnitX( m.orbUnit ); m.orbY = GetUnitY( m.orbUnit ); }
m.X = m.orbX + m.orbRadius * m.cos;
m.Y = m.orbY + m.orbRadius * m.sin;
}
m.X = m.X + m.spd * m.cos;
m.Y = m.Y + m.spd * m.sin;
if ( m.spd < m.maxspd && m.spd >= m.minspd )
m.spd += m.acc;
if ( m.onMotion.exists ) m.onMotion( );
if ( m.LifeTime > 0. )
m.LifeTime -= dm_UPDATE_INTERVAL;
else
m.destroy( );
}
if ( m.onLoop.exists ) m.onLoop( );
}
}
private static method Indexing( dmMotion m )
{
if ( dmMotion.N == 0 ) TimerStart( dmMotion.Timer, dm_UPDATE_INTERVAL, true, dmMotion.update );
m.id = dmMotion.N;
dmMotion.M[dmMotion.N] = m;
dmMotion.N += 1;
}
method onDestroy( )
{
msl.destroy( );
dmMotion.N -= 1;
dmMotion.M[id] = dmMotion.M[dmMotion.N];
dmMotion.M[id].id = id;
if ( dmMotion.N == 0 ) PauseTimer( dmMotion.Timer );
}
private static method onInit( )
{ dmMotion.Timer = CreateTimer( ); dmMotion.update = function dmMotion.Update; }
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! zinc
library DyMotionProjectile requires DyMotionMain
{
public struct dmProjectile
{
private unit object;
private effect fx = null;
private boolean IsDummy = true;
static method create( real x, real y ) -> dmProjectile
{
dmProjectile msl = dmProjectile.allocate( );
msl.object = Recycle.Dummy;
msl.X = x;
msl.Y = y;
return msl;
}
static method createWithUnit( unit u ) -> dmProjectile
{
dmProjectile msl;
if ( GetUnitTypeId( u ) != dm_DUMMY_ID )
{
msl = dmProjectile.allocate( );
msl.object = u;
msl.IsDummy = false;
return msl;
}
debug BJDebugMsg( "The Unit is a Dummy Unit! Not allowed for these effects" );
return 0;
}
method operator FxPath= ( string value )
{
if ( fx != null ) DestroyEffect( fx ); fx = null;
if ( value == "" ) fx = null;
else fx = AddSpecialEffectTarget( value, object, "origin" );
}
method operator Owner ( ) -> player
{ return GetOwningPlayer( object ); }
method operator Owner= ( player value )
{ SetUnitOwner( object, value, true ); }
method operator Size= ( real value )
{ SetUnitScale( object, value, value, value ); }
method operator AnimationSpeed= ( real value )
{ SetUnitTimeScale( object, value ); }
method operator X ( ) -> real
{ return GetUnitX( object ); }
method operator X= ( real value )
{ SetUnitX( object, value ); }
method operator Y ( ) -> real
{ return GetUnitY( object ); }
method operator Y= ( real value )
{ SetUnitY( object, value ); }
method operator Z ( ) -> real
{ return GetUnitFlyHeight( object ); }
method operator Z= ( real value )
{ SetUnitFlyHeight( object, value, 0.0 ); }
method operator XYangle ( ) -> real
{ return GetUnitFacing( object ) * bj_DEGTORAD; }
method operator XYangle= ( real value )
{ SetUnitFacing( object, value * bj_RADTODEG ); }
method operator Object ( ) -> unit
{ return object; }
method onDestroy( )
{
DestroyEffect( fx );
if ( IsDummy )
Recycle.Dummy = object;
}
}
}
//! endzinc
//TESH.scrollpos=33
//TESH.alwaysfold=0
//! zinc
library DyMotionCollision requires DyMotionMain
{
//_______________________________________________
//***********************************************
// *****Globals*****
//-----------------------------------------------
constant real dm_DEFAULT_COLLISION_RADIUS = 90.;
//-----------------------------------------------
// *****Globals End*****
//_______________________________________________
//***********************************************
interface CollisionSettings
{ method EnumUnits( ) = null; }
public struct dmCollision extends CollisionSettings
{
player Owner = Player( 15 );
real Radius = dm_DEFAULT_COLLISION_RADIUS; //the collision radius
boolean Alive = true;
boolean HeroesOnly = false;
method operator EnemiesOnly ( ) -> boolean
{ return enemiesOnly; }
method operator EnemiesOnly= ( boolean value )
{ enemiesOnly = value; alliesOnly = false; }
method operator AlliesOnly ( ) -> boolean
{ return alliesOnly; }
method operator AlliesOnly= ( boolean value )
{ alliesOnly = value; enemiesOnly = false; }
private boolean enemiesOnly = true;
private boolean alliesOnly = false;
method EnumUnits( )
{
p = Owner;
c = this;
GroupEnumUnitsInRange( Group, tX, tY, Radius, function( ) -> boolean
{
f = GetFilterUnit( );
if ( c.HeroesOnly )
return IsUnitEnemy( f, p ) == c.enemiesOnly && IsUnitAlly( f, p ) == c.alliesOnly &&
IsUnitType( f, UNIT_TYPE_HERO ) && ( GetWidgetLife( f ) > 0.405 ) == c.Alive;
else
return IsUnitEnemy( f, p ) == c.enemiesOnly && IsUnitAlly( f, p ) == c.alliesOnly &&
( GetWidgetLife( f ) > 0.405 ) == c.Alive;
} );
}
method CheckCollisions( real X, real Y ) -> boolean
{
tX = X; tY = Y;
EnumUnits( );
b = false;
if ( FirstOfGroup( Group ) != null )
b = true;
GroupClear( Group );
p = null;
c = 0;
return b;
}
private static real tX, tY;
private static group Group = CreateGroup( );
private static unit f;
private static player p;
private static dmCollision c;
private static boolean b;
}
}
//! endzinc
//TESH.scrollpos=30
//TESH.alwaysfold=0
//! zinc
library DyMotionDamage requires DyMotionMain
{
//_______________________________________________
//***********************************************
// *****Globals*****
//-----------------------------------------------
constant integer dm_MAX_DAMAGE_FACTORS = 5;
constant damagetype dm_DEFAULT_DAMAGE_TYPE = DAMAGE_TYPE_UNKNOWN;
constant attacktype dm_DEFAULT_ATTACK_TYPE = ATTACK_TYPE_NORMAL;
constant weapontype dm_DEFAULT_WEAPON_TYPE = WEAPON_TYPE_WHOKNOWS;
//-----------------------------------------------
// *****Globals End*****
//_______________________________________________
//***********************************************
public struct dmDamage
{
boolean DamageSelf = false;
boolean DamageAllies = false;
boolean DamageEnemies = true;
boolean VisibleOnly = true;
boolean DamageTrees = false;
damagetype DmgType = dm_DEFAULT_DAMAGE_TYPE;
attacktype AtkType = dm_DEFAULT_ATTACK_TYPE;
weapontype WpnType = dm_DEFAULT_WEAPON_TYPE;
unittype ForcedUnitType = null;
unittype IgnoreUnitType = null;
integer ForcedUnitId = 0;
integer IgnoreUnitId = 0;
string FxPath = null;
string FxPoint = "origin";
method DamageTarget( unit source, unit target, real dmg )
{
real f = GetDamageFactor( source, target );
if ( GetWidgetLife( target ) > 0.405 )
UnitDamageTarget( source, target, dmg * f, true, false, AtkType, DmgType, WpnType );
if ( FxPath != null ) DestroyEffect( AddSpecialEffectTarget( FxPath, target, FxPoint ) );
}
method DamageGroup( unit source, group g, real dmg )
{
CopyGroup( g, tempGroup );
tempUnit = FirstOfGroup( tempGroup );
while ( tempUnit != null )
{ DamageTarget( source, tempUnit, dmg ); GroupRemoveUnit( tempGroup, tempUnit ); tempUnit = FirstOfGroup( tempGroup ); }
}
method DamageAoE( unit source, real x, real y, real radius, real dmg )
{
tempUnit = source;
tempReal = dmg;
instance = this;
GroupEnumUnitsInRange( tempGroup, x, y, radius, function( ) -> boolean {
instance.DamageTarget( tempUnit, GetFilterUnit( ), tempReal ); return false; } );
}
method AddUnitTypeFactor( unittype ut, real fct )
{
if ( unitFctN == dm_MAX_DAMAGE_FACTORS ) return;
unitFctType[unitFctN] = ut;
unitFct[unitFctN] = fct;
unitFctN += 1;
}
method AddBuffTypeFactor( integer buffId, real fct )
{
if ( buffFctN == dm_MAX_DAMAGE_FACTORS ) return;
buffFctId[buffFctN] = buffId;
buffFct[buffFctN] = fct;
buffFctN += 1;
}
//_______________________________________________
//***********************************************
// *****Private Stuff*****
//-----------------------------------------------
private method GetDamageFactor( unit s, unit t ) -> real
{
player p = GetOwningPlayer( s );
boolean isEnemy = IsUnitEnemy( t, p );
boolean isAlly = IsUnitAlly( t, p );
integer i = 0;
real f = 1.;
if ( !DamageSelf && s == t ) return 0.;
else if ( !DamageEnemies && isEnemy ) return 0.;
else if ( !DamageAllies && isAlly ) return 0.;
else if ( VisibleOnly && !IsUnitVisible( t, p ) ) return 0.;
if ( ForcedUnitType != null && !IsUnitType( t, ForcedUnitType ) ) return 0.;
else if ( IgnoreUnitType != null && IsUnitType( t, IgnoreUnitType ) ) return 0.;
else if ( ForcedUnitId != 0 && GetUnitTypeId( t ) != ForcedUnitId ) return 0.;
else if ( IgnoreUnitId != 0 && GetUnitTypeId( t ) == IgnoreUnitId ) return 0.;
while ( unitFctType[i] != null && i < dm_MAX_DAMAGE_FACTORS )
{
if ( IsUnitType( t, unitFctType[i] ) )
{
if ( unitFct[i] > 1. ) f += ( unitFct[i] - 1. );
else if ( unitFct[i] == 0. ) { f -= 1.; if ( f < 0. ) f = 0.; }
else f -= buffFct[i];
}
i += 1;
}
i = 0;
while ( buffFctId[i] != 0 && i < dm_MAX_DAMAGE_FACTORS )
{
if ( GetUnitAbilityLevel( t, buffFctId[i] ) > 0 )
{
if ( buffFct[i] > 1. ) f += ( buffFct[i] - 1. );
else if ( buffFct[i] == 0. ) { f -= 1.; if ( f < 0. ) f = 0.; }
else f -= buffFct[i];
}
i += 1;
}
return f;
}
private unittype unitFctType[dm_MAX_DAMAGE_FACTORS];
private real unitFct[dm_MAX_DAMAGE_FACTORS];
private integer unitFctN = 0;
private integer buffFctId[dm_MAX_DAMAGE_FACTORS];
private real buffFct[dm_MAX_DAMAGE_FACTORS];
private integer buffFctN = 0;
private static group tempGroup = CreateGroup( );
private static unit tempUnit = null;
private static real tempReal = 0.;
private static dmDamage instance = 0;
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! zinc
library DyMotionMath
{
public constant real dm_2Pi = 2 * bj_PI;
public constant real dm_PiOVER2 = bj_PI / 2;
public constant real dm_3OVER2Pi = 3 / 2 * bj_PI;
//If you want to use this in a condition, to check the distance, use this native function instead IsUnitInRange
public function GetDistance( real x1, real y1, real x2, real y2 ) -> real
{ real dx = x2 - x1, dy = y2 - y1; return Pow( dx * dx + dy * dy, 0.5 ); }
public function GetRandomRealInRingRegion( real maxRadius, real minRadius, real centerN ) -> real
{
real n, min = minRadius, max = maxRadius;
n = centerN;
if ( maxRadius < minRadius )
min = maxRadius;
while ( n >= centerN - min && n <= centerN + min )
n = GetRandomReal( centerN - max, centerN + max );
return n;
}
public function GetRandomPointInRingRegion( real maxRadius, real minRadius, real centerX, real centerY ) -> location
{
return Location( GetRandomRealInRingRegion( maxRadius, minRadius, centerX ),
GetRandomRealInRingRegion( maxRadius, minRadius, centerY ) );
}
}
//! endzinc
//TESH.scrollpos=0
//TESH.alwaysfold=0
//! zinc
library DyMotionAPI requires DyMotionMain, DyMotionMath, DyMotionMotion
{
//: ==----
// Function Knockback
//
//: ==----
public function Knockback( unit c, unit t, real speed, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - GetUnitY( c ), GetUnitX( t ) - GetUnitX( c ) ), speed, speed / 0.5 * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackAngle( unit t, real angle, real speed, string fx )
{ dmKnockback.create( t, angle * bj_DEGTORAD, speed, speed / 0.5 * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackAngleRadians( unit t, real angle, real speed, string fx )
{ dmKnockback.create( t, angle, speed, speed / 0.5 * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackXY( unit t, real x, real y, real speed, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - y, GetUnitX( t ) - x ), speed, speed / 0.5 * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackLoc( unit t, location loc, real speed, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - GetLocationY( loc ), GetUnitX( t ) - GetLocationX( loc ) ), speed, speed / 0.5 * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackTimed( unit c, unit t, real speed, real duration, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - GetUnitY( c ), GetUnitX( t ) - GetUnitX( c ) ), speed, speed / duration * dm_UPDATE_INTERVAL, fx ); }
//
public function KnockbackAoE( unit c, real x, real y, real radius, real speed, string fx )
{
tR1 = speed;
tS = fx;
tR2 = x;
tR3 = y;
GroupEnumUnitsInRange( tG, x, y, radius, function( ) -> boolean
{
tU = GetFilterUnit( );
if ( GetWidgetLife( tU ) > 0.405 ) KnockbackXY( tU, tR2, tR3, tR1, tS );
return false;
} );
}
//
public function Knockback_Ex( unit c, unit t, real speed, real friction, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - GetUnitY( c ), GetUnitX( t ) - GetUnitX( c ) ), speed, friction, fx ); }
//
public function KnockbackAngle_Ex( unit t, real angle, real speed, real friction, string fx )
{ dmKnockback.create( t, angle * bj_DEGTORAD, speed, friction, fx ); }
//
public function KnockbackAngleRadians_Ex( unit t, real angle, real speed, real friction, string fx )
{ dmKnockback.create( t, angle, speed, friction, fx ); }
//
public function KnockbackXY_Ex( unit t, real x, real y, real speed, real friction, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - y, GetUnitX( t ) - x ), speed, friction, fx ); }
//
public function KnockbackLoc_Ex( unit t, location loc, real speed, real friction, string fx )
{ dmKnockback.create( t, Atan2( GetUnitY( t ) - GetLocationY( loc ), GetUnitX( t ) - GetLocationX( loc ) ), speed, friction, fx ); }
//
public function KnockbackAoE_Ex( unit c, real x, real y, real radius, real speed, real friction, string fx )
{
tR1 = speed;
tS = fx;
tR2 = x;
tR3 = y;
tR4 = friction;
GroupEnumUnitsInRange( tG, x, y, radius, function( ) -> boolean
{
tU = GetFilterUnit( );
if ( GetWidgetLife( tU ) > 0.405 ) KnockbackXY_Ex( tU, tR2, tR3, tR1, tR4, tS );
return false;
} );
}
//: ==----
// Function Orbiting
//
//: ==----
public function MissileOrbitingUnit( unit u, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m = dmMotion.create( GetUnitX( u ), GetUnitY( u ), 0., null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingUnit( u, radius, angSpeed );
m.LifeTime = duration;
}
public function MissileOrbitingXY( real x, real y, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m = dmMotion.create( x, y, 0., null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingPoint( x, y, radius, angSpeed );
m.LifeTime = duration;
}
public function MissileOrbitingLocation( location loc, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m = dmMotion.create( GetLocationX( loc ), GetLocationY( loc ), 0., null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingPoint( GetLocationX( loc ), GetLocationY( loc ), radius, angSpeed );
m.LifeTime = duration;
}
public function MultipleMissilesOrbitingUnit( unit u, integer n, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m;
integer i;
real a;
real x = GetUnitX( u ), y = GetUnitY( u );
if ( n <= 1 ) n = 2;
a = dm_2Pi / n;
for ( 1 <= i <= n )
{
m = dmMotion.create( x, y, a * i, null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingUnit( u, radius, angSpeed );
m.LifeTime = duration;
}
}
public function MultipleMissilesOrbitingXY( real x, real y, integer n, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m;
integer i;
real a;
if ( n <= 1 ) n = 2;
a = dm_2Pi / n;
for ( 1 <= i <= n )
{
m = dmMotion.create( x, y, a * i, null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingPoint( x, y, radius, angSpeed );
m.LifeTime = duration;
}
}
public function MultipleMissilesOrbitingLocation( location loc, integer n, string sfx, real angSpeed, real radius, real duration )
{
dmMotion m;
integer i;
real a;
real x = GetLocationX( loc ), y = GetLocationY( loc );
if ( n <= 1 ) n = 2;
a = dm_2Pi / n;
for ( 1 <= i <= n )
{
m = dmMotion.create( x, y, a * i, null );
m.FxPath = sfx;
m.Speed = 100.;
m.SetOrbitingPoint( x, y, radius, angSpeed );
m.LifeTime = duration;
}
}
//: ==----
// temporary variables
//: ==----
group tG = CreateGroup( );
unit tU;
real tR1, tR2, tR3, tR4;
string tS;
//: ==----
// API structs
//: ==----
struct dmKnockback extends dmMotion
{
string Fx;
static method create( unit u, real angle, real speed, real friction, string fx ) -> dmKnockback
{
dmKnockback kb = dmKnockback.allocate( 0., 0., angle, u );
kb.Speed = speed;
kb.Acceleration = -friction;
kb.Fx = fx;
return kb;
}
method onMotion( )
{
DestroyEffect( AddSpecialEffect( Fx, X, Y ) );
if ( Speed <= -Acceleration || !IsTerrainWalkable( X, Y, 10. ) ) destroy( );
}
}
public struct dmJump extends dmMotion
{
private real MaxDist;
private real dist = 0.; //current distance
private real arc = 0.6;
private real iniZ;
private real defaultHeight = 0.;
method operator Arc= ( real value )
{ arc = 4 * value; }
method operator MaxHeight= ( real value )
{ arc = 4 * value / MaxDist; }
static method create( real x, real y, real angle, real maxDist ) -> dmJump
{
dmJump jp = dmJump.allocate( x, y, angle, null );
jp.MaxDist = maxDist;
MoveLocation( loc, x, y );
jp.iniZ = GetLocationZ( loc );
return jp;
}
static method createWithUnit( unit u, real angle, real maxDist ) -> dmJump
{
dmJump jp = dmJump.allocate( 0., 0., angle, u );
UnitAddAbility( u, dm_FLYING );
UnitRemoveAbility( u, dm_FLYING );
jp.MaxDist = maxDist;
jp.defaultHeight = GetUnitDefaultFlyHeight( u );
MoveLocation( loc, GetUnitX( u ), GetUnitY( u ) );
jp.iniZ = GetLocationZ( loc )+ jp.defaultHeight;
return jp;
}
method onMotion( )
{
dist += Speed * dm_UPDATE_INTERVAL;
MoveLocation( loc, X, Y );
H = arc * ( MaxDist - dist ) * ( dist / MaxDist ) - GetLocationZ( loc ) + iniZ;
if ( H > 0. ) Z = H;
else destroy( );
}
method onDestroy( )
{
Z = defaultHeight;
}
private static location loc = Location( 0., 0. );
private static real H = 0.;
}
/*struct dmOrbitMissile extends dmMotion
{
dmCollision collider;
dmDamage damager;
static method create( unit t, real angle, real anglespeed, real radius ) -> dmOrbitMissile
{
dmOrbitMissile ob = dmOrbitMissile.allocate( GetUnitX( t ), GetUnitY( t ), angle, null );
ob.SetOrbitingUnit( t, radius, anglespeed );
return ob;
}
method onMotion( )
{
if ( collider != 0 )
{
if ( collider.CheckCollisions( X, Y ) )
{
}
}
else if ( damager != 0 )
{
}
}
method onDestroy( )
{
}
}*/
public struct dmChain
{
static constant integer MAX_CHAIN_SEGMENTS = 800;
static constant real DEFAULT_CHAIN_SPEED = 1000.;
static constant real DEFAULT_MAX_DISTANCE = 2300.;
static constant real DEFAULT_CHAIN_INTERVAL = 0.1;
static constant real DEFAULT_CHAIN_COLLISION = 30.;
static constant string DEFAULT_TIP_CHAIN_FX = "Abilities\\Weapons\\WardenMissile\\WardenMissile.mdl";
static constant string DEFAULT_BODY_CHAIN_FX = "Abilities\\Weapons\\WardenMissile\\WardenMissile.mdl";
static constant real DEFAULT_TIP_SIZE = 4.;
static constant real DEFAULT_BODY_SIZE = 4.;
unit Caster, Target = null;
real tX, tY; //it can target a position instead of a unit
real Speed = DEFAULT_CHAIN_SPEED;
real Collision = DEFAULT_CHAIN_COLLISION;
real MaxDistance = DEFAULT_MAX_DISTANCE;
real Damage = 0.;
real TipSize = DEFAULT_TIP_SIZE;
real BodySize = DEFAULT_BODY_SIZE;
string TipFx = DEFAULT_TIP_CHAIN_FX;
string BodyFx = DEFAULT_BODY_CHAIN_FX;
boolean UseCasterFacingAngle = false;
method operator ChainInterval= ( real value )
{
PauseTimer( T );
TimerStart( T, value, true, function dmChain.Update );
interval = value;
}
method operator First ( ) -> dmChainSegment
{ return ChainSegments[0]; }
method operator Last ( ) -> dmChainSegment
{ return ChainSegments[ChainN-1]; }
private boolean HaveTarget = false;
private real interval = DEFAULT_CHAIN_INTERVAL;
private integer id;
static method createOnTarget( unit caster, unit target ) -> dmChain
{
dmChain c = dmChain.allocate( );
c.Caster = caster;
c.Target = target;
Indexing( c );
return c;
}
static method createOnPoint( unit caster, real x, real y ) -> dmChain
{
dmChain c = dmChain.allocate( );
c.Caster = caster;
c.tX = x; c.tY = y;
Indexing( c );
return c;
}
method NewChainSegment( )
{
dmChainSegment ch;
if ( ChainN >= MAX_CHAIN_SEGMENTS )
{
debug BJDebugMsg( "Segments limit reached" );
Return( );
return;
}
ch = dmChainSegment.create( GetUnitX( Caster ), GetUnitY( Caster ), 0., null );
ch.LifeTime = 2000.;
ch.Speed = Speed;
ch.Caster = Caster;
ch.tag = id;
if ( ChainN == 0 )
{
ch.FxPath = TipFx;
ch.Size = TipSize;
if ( Target != null )
ch.SetHomingUnit( Target );
else
ch.Angle = Atan2( tY - ch.Y, tX - ch.X );
}
else
{
ch.FxPath = BodyFx;
ch.Size = BodySize;
ch.SetHomingUnit( ChainSegments[ChainN - 1].Object );
}
ChainSegments[ChainN] = ch;
ChainN += 1;
}
method Return( )
{
dmChainSegment c;
integer i;
HaveTarget = true;
if ( Damage > 0. && First.Attached != null )
UnitDamageTarget( Caster, First.Attached, Damage, true, false, ATTACK_TYPE_NORMAL, DAMAGE_TYPE_NORMAL, null );
Last.SetHomingUnit( Caster );
Last.Returning = true;
Last.AutoFacing = false;
for ( ChainN - 1 > i > 0 )
{
c = ChainSegments[i];
c.SetHomingUnit( ChainSegments[i + 1].Object );
c.Returning = true;
c.AutoFacing = false;
}
First.SetHomingUnit( ChainSegments[1].Object );
First.Returning = true;
First.AutoFacing = false;
}
static method RemoveLastChainSegment( integer Id )
{
C[Id].ChainSegments[C[Id].ChainN-2].SetHomingUnit( C[Id].Caster );
C[Id].ChainN -= 1;
}
private static method Update( )
{
dmChainSegment cs[]; //chain segments
dmChain c;
unit u;
integer i, i2;
for ( 0 <= i < N )
{
c = C[i];
if ( !c.HaveTarget )
{
c.NewChainSegment( );
if ( c.Target != null )
{
if ( IsUnitInRange( c.First.Object, c.Target, c.Collision ) )
{
c.First.Attached = c.Target;
c.Return( );
}
}
else
{
if ( c.MaxDistance > 0. && IsTerrainWalkable( c.First.X, c.First.Y, 10. ) )
c.MaxDistance -= c.Speed * c.interval;
else
c.Return( );
if ( c.UseCasterFacingAngle )
c.First.Angle = GetUnitFacing( c.Caster ) * bj_DEGTORAD;
GroupEnumUnitsInRange( tempGroup, c.First.X, c.First.Y, c.Collision, null );
u = FirstOfGroup( tempGroup );
if ( IsUnitEnemy( u, GetOwningPlayer( c.Caster ) ) && GetWidgetLife( u ) > 0.405 )
{
c.First.Attached = u;
c.Return( );
}
}
}
else
{
for ( 1 <= i2 < c.ChainN )
{
cs[0] = c.ChainSegments[i2];
cs[1] = c.ChainSegments[i2 - 1];
cs[0].XYangle = Atan2( cs[1].Y - cs[0].Y, cs[1].X - cs[0].X );
}
}
if ( c.ChainN == 0 )
c.destroy( );
}
}
private dmChainSegment ChainSegments[dmChain.MAX_CHAIN_SEGMENTS];
private integer ChainN = 0;
private static group tempGroup = CreateGroup( );
private static dmChain C[];
private static integer N = 0;
private static timer T = CreateTimer( );
method onDestroy( )
{
N -= 1;
C[id] = C[N];
C[id].id = id;
if ( N == 0 )
PauseTimer( T );
}
private static method Indexing( dmChain c )
{
if ( N == 0 )
TimerStart( T, DEFAULT_CHAIN_INTERVAL, true, function dmChain.Update );
c.id = N;
C[N] = c;
N += 1;
}
}
struct dmChainSegment extends dmMotion
{
unit Attached = null;
unit Caster = null;
boolean Returning = false;
integer tag = 0;
method onMotion( )
{
if ( Attached != null )
{
SetUnitX( Attached, X );
SetUnitY( Attached, Y );
}
if ( Returning )
if ( IsUnitInRange( Object, Caster, dmChain.DEFAULT_CHAIN_COLLISION ) )
{
dmChain.RemoveLastChainSegment( tag );
destroy( );
}
}
}
}
//! endzinc
//TESH.scrollpos=162
//TESH.alwaysfold=0
//! zinc
library TheArcaneLeash requires DyMotionMain, DyMotionMath, DyMotionMotion, DyMotionAPI
{
//: ===------
// Customizable Properties
//: ===------
//*****************************\\
//=-=-=» Main Properties «=-=-=\\
//: ===------
// the raw code of the spell itself
constant integer SPELL_ID = 'A000';
//: ===------
// the raw code of the Dummy Stun Spell
constant integer STUN_SPELL_ID = 'A002';
//: ===------
// this is the buff that will be used when units are stunned
constant integer STUN_BUFF_ID = 'BSTN';
//: ===------
// this shouldn't be changed, unless you use another ability to stun, firebolt does that, so we put here the issue order id
constant string STUN_ORDER_ID = "thunderbolt";
//: ===------
// this is part of the Stun countdown, better not mess with it
constant real INTERVAL = 0.2;
//: ===------
// the radius at which each missile will pick a unit
constant real GRAB_RADIUS = 80.;
//: ===------
// This is the effect that will appear when the caster casts the spell
constant string FX = "AncientExplode.mdl";
//: ===------
// This effect appears when a grabbed unit is thrown away
constant string FX2 = "Abilities\\Spells\\Undead\\OrbOfDeath\\AnnihilationMissile.mdl";
//: ===------
// this is the damage dealt to the unit
function DAMAGE( integer lvl ) -> real
{ return 80. * lvl; }
//: ===------
// This is the duration of the stun, half duration for heroes
function STUN_DURATION( integer lvl ) -> real
{ return 3. * lvl; }
//***********************************\\
//=-=-=» Projectile Properties «=-=-=\\
//: ===------
// The model effect of the projectiles
constant string PROJECTILE_FX = "ArcaneMissileComplete.mdl";
//: ===------
// this is the trail effect of the projectiles
constant string PROJECTILE_FX2 = "Doodads\\Cinematic\\GlowingRunes\\GlowingRunes7.mdl";
//: ===------
// number of projectiles
constant integer PROJECTILE_N = 10;
//: ===------
// The speed of the projectiles while they are in the air
constant real PROJECTILE_INITIAL_SPEED = 200.;//per second
//: ===------
// The speed of the projectiles once they hit the ground
constant real PROJECTILE_GROUND_SPEED = 800.;//per second
//: ===------
// the angle speed at which the missiles will rotate, giving that awesome effect you've seen ^^
constant real PROJECTILE_ANGLE_SPEED = bj_PI;//per second
//: ===------
// The projectiles maximum height, or the maximum height they can reach during the flight
constant real PROJECTILE_MAX_HEIGHT = 300.;
//: ===------
// The maximum distance the projectiles will fly until hit the ground
constant real PROJECTILE_MAX_DISTANCE = 300.;
//: ===------
// this is the life time of the projectiles, it's also the same value of how long the hero is channeling the spell
// if you change this value, you better change the channel duration on the spell editor too
constant real PROJECTILE_DURATION = 6.;
//**********************************\\
//=-=-=» Knockback Properties «=-=-=\\
//: ===------
// Speed at which units will be knockbacked
constant real KNOCK_SPEED = 1500.;//per second
//: ===------
// the effect of the knockback, usually dust effects
constant string KNOCK_FX = "Small Dust.mdl";
//: ===------
// End of Customizable Properties
//: ===------
function SpellStartActions( )
{
Jump jp;
unit c = GetTriggerUnit( );
integer i;
real a = dm_2Pi / PROJECTILE_N;
real x = GetUnitX( c ), y = GetUnitY( c );
for ( 1 <= i <= PROJECTILE_N )
{
jp = Jump.create( c, x, y, a * i, PROJECTILE_MAX_DISTANCE );
jp.FxPath = PROJECTILE_FX;
jp.Speed = PROJECTILE_INITIAL_SPEED;
jp.MaxHeight = PROJECTILE_MAX_HEIGHT;
jp.LifeTime = PROJECTILE_DURATION;
jp.Size = 1.6;
}
DestroyEffect( AddSpecialEffect( FX, x, y ) );
c = null;
}
struct Data
{
unit stunned;
unit dummy;
real duration;
integer id;
static method StunUnit( unit stunned, real duration )
{
Data d = Data.allocate( );
d.stunned = stunned;
d.duration = duration;
d.dummy = Recycle.Dummy;
SetUnitX( d.dummy, GetUnitX( stunned ) );
SetUnitY( d.dummy, GetUnitY( stunned ) );
PauseUnit( d.dummy, false );
UnitAddAbility( d.dummy, STUN_SPELL_ID );
IssueTargetOrder( d.dummy, STUN_ORDER_ID, stunned );
if ( N == 0 ) TimerStart( T, INTERVAL, true, function Data.Update );
d.id = N;
dat[N] = d;
N += 1;
}
static method Update( )
{
integer i;
for ( 0 <= i < N )
{
if ( dat[i].duration > 0. )
dat[i].duration -= INTERVAL;
else
dat[i].destroy( );
}
}
method onDestroy( )
{
UnitRemoveAbility( stunned, STUN_BUFF_ID );
UnitRemoveAbility( dummy, STUN_SPELL_ID );
Recycle.Dummy = dummy;
N -= 1;
dat[id] = dat[N];
dat[id].id = id;
if ( N == 0 ) PauseTimer( T );
}
private static timer T = CreateTimer( );
private static Data dat[];
private static integer N = 0;
}
struct Jump extends dmMotion
{
private boolean IsFlying = true;
private real MaxDist;
private real dist = 0.; //current distance
private real arc = 0.6;
private real iniZ;
unit Grabbed = null;
unit Source;
method operator Arc= ( real value )
{ arc = 4 * value; }
method operator MaxHeight= ( real value )
{ arc = 4 * value / MaxDist; }
private static group GrabbedGroup = CreateGroup( );
static method create( unit caster, real x, real y, real angle, real maxDist ) -> Jump
{
Jump jp = Jump.allocate( x, y, angle, null );
jp.Source = caster;
jp.Owner = GetOwningPlayer( caster );
jp.MaxDist = maxDist;
MoveLocation( loc, x, y );
jp.iniZ = GetLocationZ( loc );
return jp;
}
method onMotion( )
{
unit u;
if ( GetWidgetLife( Source ) < 0.405 )
destroy( );
else
{
if ( IsFlying )
{
dist += Speed * dm_UPDATE_INTERVAL;
MoveLocation( loc, X, Y );
H = arc * ( MaxDist - dist ) * ( dist / MaxDist ) - GetLocationZ( loc ) + iniZ;
if ( H > 0. ) Z = H;
else
{
IsFlying = false;
Speed = PROJECTILE_GROUND_SPEED;
Z = 70.;
AngleSpeed = PROJECTILE_ANGLE_SPEED;
}
}
else
{
DestroyEffect( AddSpecialEffect( PROJECTILE_FX2, X, Y ) );
if ( Grabbed != null )
{
SetUnitX( Grabbed, X );
SetUnitY( Grabbed, Y );
}
else
{
GroupEnumUnitsInRange( tempGroup, X, Y, GRAB_RADIUS, null );
u = FirstOfGroup( tempGroup );
if ( u != null && IsUnitEnemy( u, Owner ) && GetWidgetLife( u ) > 0.405 && !IsUnitType( u, UNIT_TYPE_FLYING )
&& !IsUnitType( u, UNIT_TYPE_MAGIC_IMMUNE ) && !IsUnitInGroup( u, GrabbedGroup ) )
{
Grabbed = u;
GroupAddUnit( GrabbedGroup, u );
PauseUnit( u, true );
}
GroupClear( tempGroup );
u = null;
}
}
}
}
method onDestroy( )
{
if ( Grabbed != null )
{
KnockbackAngleRadians( Grabbed, Angle, KNOCK_SPEED, KNOCK_FX );
GroupRemoveUnit( GrabbedGroup, Grabbed );
PauseUnit( Grabbed, false );
UnitDamageTarget( Source, Grabbed, DAMAGE( GetUnitAbilityLevel( Source, SPELL_ID ) ), true, false, null, null, null );
if ( IsUnitType( Grabbed, UNIT_TYPE_HERO ) )
Data.StunUnit( Grabbed, STUN_DURATION( GetUnitAbilityLevel( Source, SPELL_ID ) ) / 2. );
else
Data.StunUnit( Grabbed, STUN_DURATION( GetUnitAbilityLevel( Source, SPELL_ID ) ) );
Grabbed = null;
DestroyEffect( AddSpecialEffect( FX2, X, Y ) );
}
}
private static location loc = Location( 0., 0. );
private static real H = 0.;
}
function onInit( )
{
trigger t = CreateTrigger( );
integer i;
unit u = Recycle.Dummy;
for ( 0 <= i < 12 )
TriggerRegisterPlayerUnitEvent( t, Player(i), EVENT_PLAYER_UNIT_SPELL_EFFECT, null );
TriggerAddCondition( t, function( ) -> boolean { return GetSpellAbilityId( ) == SPELL_ID; } );
TriggerAddAction( t, function SpellStartActions );
UnitAddAbility( u, SPELL_ID );
UnitRemoveAbility( u, SPELL_ID );
UnitAddAbility( u, STUN_SPELL_ID );
UnitRemoveAbility( u, STUN_SPELL_ID );
Recycle.Dummy = u;
Preload( FX );
Preload( FX2 );
Preload( PROJECTILE_FX );
Preload( PROJECTILE_FX2 );
Preload( KNOCK_FX );
u = null;
t = null;
}
}
//! endzinc