//***************************************************************************
//***************************************************************************
//***************************************************************************
// S H A D O W S I M A G E S S W O R D
// -----------------------------------------
// By: Nyuu
// Version: 1.1
//
// Sepll Description:
// ` - Use energy and power of the ground, Blade Master jump up from the ground to the target point,
// but because the power of the blade is too powerful, his sword cause the image of him has been splitted into two,
// this creates two illusions of him and has the same power plunges straight down to the ground,
// when the illusions hit the ground, it causing 100/150/200/250 damages to enemies simultaneously
// stunning them for 2/2.25/2.50/2.75 seconds around 250 range of the illusions, and then,
// the sword will pull them back, after mirage disappears, it creates a shock ground,
// making him invisible for 1.5 seconds.
// Credits:
// BoundSentinel - http://www.wc3c.net/showthread.php?t=102576
// - Installation:
// - Import/copy Spell Code and Requires folder to your map
// - Import/copy the custom ability and unit to your map and change the SPELL_ID, DUMMY_ID, DUMMY_SPELL_ID,
// BUFF_ID if needed
// - You may view the raw ID of the objects by pressing CTRL+D in the object editor
// - You may play with the configurables below
//
//
//***************************************************************************
//***************************************************************************
//***************************************************************************
library ShadowImagesSword
globals
//-------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------SPELL CONFIGURABLE------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------
//Spell rawcode, change if needed
private constant integer SPELL_ID ='A000'
//Dummy stun spell rawcode, change if needed
private constant integer DUMMY_SPELL_ID ='A001'
//Rawcode of the dummy id, change if needed
private constant integer DUMMY_CASTER ='e000'
//Rawcode of the buff id, change if needed
private constant integer BUFF_ID ='B000'
//Rawcode of the fly ability, you can leave it alone :)
private constant integer FLY_ABILITY ='Amrf'
//Images alpha increase when it come back to the caster
private constant integer FADE_BACK_PERIOD =3
//Original dummy alpha
private constant integer DUMMY_ALPHA =100
//Spell periodic, you can leave it alone :)
private constant real PERIODIC = .031250
//Time to pause the caster animation
private constant real ANIMATION_PAUSE_TIME=0.5
//Max height of the caster when he jumping
private constant real MAX_HEIGHT =400.
//The images will dehisce after reach this percent
private constant real DISTANCE_PERCENT =50.
//Caster jumping speed
private constant real SPEED =10.
//Enemies speed when they got hits from the caster
private constant real TARGET_SPEED =20.
//Enemies max height
private constant real TARGET_HEIGHT =300.
//Images separate speed
private constant real SEPARATE_SPEED =10.
//Images come back speed
private constant real DUMMY_FADE_BACK =5.
//Animation time after the images has separated
private constant real TIME_ANIMATION =0.4
//Sword distance of the images when they hit the ground
private constant real SWORD_DISTANCE =100.
//Slash area of effect
private constant real SLASH_AOE =500.
//Enemies knockback min distance
private constant real TARGET_MIN_DISTANCE =250.
//Enemies knockback max distance
private constant real TARGET_MAX_DISTANCE =250.
//Caster animation when he casting this spell
private constant string CASTER_ANIMATION ="slam"
//Spell dummy stun order id
private constant string DUMMY_ORDER ="thunderbolt"
//Spell dummy invisible order id
private constant string DUMMY_INVI_ORDER ="invisibility"
//The attachment of the images when they separate
private constant string ATTACHMENT ="weapon"
//Images sepatate effect
private constant string MIRROR_IMAGE_EFFECT ="Abilities\\Spells\\Orc\\MirrorImage\\MirrorImageCaster.mdl"
//Caster jump effect
private constant string JUMP_EFFECT ="Abilities\\Spells\\Orc\\WarStomp\\WarStompCaster.mdl"
//Effect attach on the images
private constant string SWORD_EFFECT ="Abilities\\Weapons\\IllidanMissile\\IllidanMissile.mdl"
//Effect of the images when they hit the ground
private constant string SLASH_EFFECT ="Abilities\\Spells\\Human\\Thunderclap\\ThunderClapCaster.mdl"
//Knockback effect when the enemies got hits
private constant string KNOCK_EFFECT ="Abilities\\Spells\\Human\\FlakCannons\\FlakTarget.mdl"
//Effect of the caster when the images is back
private constant string EFFECT_BACK ="Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl"
//**************************************************************
private constant attacktype AT = ATTACK_TYPE_HERO
private constant damagetype DT = DAMAGE_TYPE_DEATH
private constant weapontype WT = WEAPON_TYPE_CLAW_HEAVY_SLICE
//Damage base of this spell
private constant real DAMAGE_BASE = 100.
//**************************************************************
//-------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------
//------------------------------------------------------------
//----------------------NON CONFIGURABLE----------------------
//------------------------------------------------------------
/**/ private integer M=-1
/**/ private integer M2=-1
/**/ private integer array MData
/**/ private integer array MData2
/**/
/**/ private constant timer T=CreateTimer()
/**/ private constant timer T2=CreateTimer()
/**/
/**/ private constant group G=CreateGroup()
/**/
/**/ private location L=Location(0.,0.)
/**/ private unit DummyCaster
/**/ private unit First
//------------------------------------------------------------
//------------------------------------------------------------
//------------------------------------------------------------
endglobals
//Filter Function Settings******************************************
private constant function filterFunc takes unit filterUnit, player casterOwner returns boolean
return not IsUnitType(filterUnit,UNIT_TYPE_DEAD) and GetUnitTypeId(filterUnit) != 0 and IsUnitEnemy(filterUnit,casterOwner)
endfunction
//********************************************************************
//Damage settings*****************************************************
private constant function getDamage takes integer lvl returns real
return DAMAGE_BASE+(lvl+50.)
endfunction
//******************************************************************
function AddSpecialEffectZ takes string path, real x, real y, real z returns effect
local destructable d = CreateDestructableZ( 'OTip', x, y, z, 0.00, 1, 0 )
set bj_lastCreatedEffect = AddSpecialEffect( path, x, y )
call RemoveDestructable( d )
set d = null
return bj_lastCreatedEffect
endfunction
struct KnockBackZ
unit caster
real distance
real cos
real sin
real tick
real reach=0.
static method onPeriodic takes nothing returns nothing
local thistype this
local integer i=0
local real x
local real y
local real f
loop
exitwhen i>M2
set this=MData2[i]
if reach<distance then
set reach=reach+tick
set x=GetUnitX(caster)+cos
set y=GetUnitY(caster)+sin
call MoveLocation(L,x,y)
set f=(4*TARGET_HEIGHT*reach)/distance*(1-(reach/distance))+GetLocationZ(L)
call SetUnitPosition(caster,x,y)
call SetUnitFlyHeight(caster,f,0.)
call DestroyEffect(AddSpecialEffectZ(KNOCK_EFFECT,x,y,f))
else
set caster=null
set MData2[this]=MData2[M2]
set MData2[M2]=-1
set M2=M2-1
if M2==-1 then
call PauseTimer(T2)
endif
call destroy()
return
endif
set i=i+1
endloop
endmethod
static method knockAdd takes unit whichUnit,real whichAngle returns nothing
local thistype this=allocate()
set M2=M2+1
set MData2[M2]=this
set caster=whichUnit
set distance=GetRandomReal(TARGET_MIN_DISTANCE,TARGET_MAX_DISTANCE)
set tick=distance/TARGET_HEIGHT*TARGET_SPEED
set cos=tick*Cos(whichAngle)
set sin=tick*Sin(whichAngle)
if UnitAddAbility(caster,FLY_ABILITY) then
call UnitRemoveAbility(caster,FLY_ABILITY)
endif
if M2==0 then
call TimerStart(T2,PERIODIC,true,function thistype.onPeriodic)
endif
endmethod
endstruct
struct ShadowImagesSword
unit caster
unit dummy
effect e_caster
effect e_dummy
player casterOwner
boolean stopFading=false
real angle
real angle2
real max
real time=0.
real reach=0.
real tick
real dmg
real distance_counter=0.
real cos
real sin
integer fade_alpha
method GroupEnumUnits takes unit cast,real x, real y returns nothing
local real fx
call GroupEnumUnitsInRange(G,x,y,SLASH_AOE,null)
set x=GetUnitX(cast)
set y=GetUnitY(cast)
loop
set First=FirstOfGroup(G)
exitwhen First==null
if filterFunc(First,casterOwner) then
set fx=GetUnitX(First)
set time=GetUnitY(First)
call KnockBackZ.knockAdd(First,Atan2(time-y,fx-x))
call UnitDamageTarget(caster,First,dmg,true,false,AT,DT,WT)
call SetUnitX(DummyCaster,fx)
call SetUnitY(DummyCaster,time)
call IssueTargetOrder(DummyCaster,DUMMY_ORDER,First)
endif
call GroupRemoveUnit(G,First)
endloop
endmethod
static method onPeriodic takes nothing returns nothing
local thistype this
local integer i=0
local real x
local real y
local real percent
local real s
loop
exitwhen i>M
set this=MData[i]
if reach<max then
set x=GetUnitX(caster)+cos
set y=GetUnitY(caster)+sin
call SetUnitX(caster,x)
call SetUnitY(caster,y)
set x=GetUnitX(dummy)+cos
set y=GetUnitY(dummy)+sin
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
set reach=reach+tick
call MoveLocation(L,x,y)
set s=(4*MAX_HEIGHT*reach)/max*(1-(reach/max))+GetLocationZ(L)
call SetUnitFlyHeight(caster,s,0.)
call SetUnitFlyHeight(dummy,s,0.)
if not stopFading then
if time<ANIMATION_PAUSE_TIME then
set time=time+PERIODIC
else
call SetUnitTimeScale(dummy,0.)
call SetUnitTimeScale(caster,0.)
endif
set percent=reach/max*100.
if percent>= DISTANCE_PERCENT then
set stopFading=true
set e_caster=AddSpecialEffectTarget(SWORD_EFFECT,caster,ATTACHMENT)
set e_dummy=AddSpecialEffectTarget(SWORD_EFFECT,dummy,ATTACHMENT)
if GetLocalPlayer()==casterOwner then
call SelectUnit(caster,false)
endif
call UnitAddAbility(caster,'Aloc')
call SetUnitVertexColor(caster,255,255,255,DUMMY_ALPHA)
call DestroyEffect(AddSpecialEffectZ(MIRROR_IMAGE_EFFECT,x,y,s))
call SetUnitTimeScale(dummy,TIME_ANIMATION)
call SetUnitTimeScale(caster,TIME_ANIMATION)
endif
else
set percent=SEPARATE_SPEED*Cos(angle2)
set s=SEPARATE_SPEED*Sin(angle2)
set x=GetUnitX(dummy)+percent
set y=GetUnitY(dummy)+s
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
set x=GetUnitX(caster)-percent
set y=GetUnitY(caster)-s
call SetUnitX(caster,x)
call SetUnitY(caster,y)
set distance_counter=distance_counter+SEPARATE_SPEED
endif
if reach>=max then
set cos=SWORD_DISTANCE*Cos(angle)
set sin=SWORD_DISTANCE*Sin(angle)
set x=GetUnitX(caster)+cos
set y=GetUnitY(caster)+sin
call DestroyEffect(AddSpecialEffect(SLASH_EFFECT,x,y))
call SetUnitAbilityLevel(DummyCaster,DUMMY_SPELL_ID,fade_alpha)
set fade_alpha=DUMMY_ALPHA
call GroupEnumUnits(caster,x,y)
set x=GetUnitX(dummy)+cos
set y=GetUnitY(dummy)+sin
call DestroyEffect(AddSpecialEffect(SLASH_EFFECT,x,y))
call GroupEnumUnits(dummy,x,y)
set cos=DUMMY_FADE_BACK*Cos(angle2)
set sin=DUMMY_FADE_BACK*Sin(angle2)
call DestroyEffect(e_caster)
call DestroyEffect(e_dummy)
set e_caster=null
set e_dummy=null
endif
else
set x=GetUnitX(dummy)-cos
set y=GetUnitY(dummy)-sin
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
set x=GetUnitX(caster)+cos
set y=GetUnitY(caster)+sin
call SetUnitX(caster,x)
call SetUnitY(caster,y)
if distance_counter>DUMMY_FADE_BACK then
set distance_counter=distance_counter-DUMMY_FADE_BACK
set fade_alpha=fade_alpha+FADE_BACK_PERIOD
call SetUnitVertexColor(dummy,255,255,255,fade_alpha)
call SetUnitVertexColor(caster,255,255,255,fade_alpha)
else
call UnitRemoveAbility(caster,'Aloc')
call ShowUnit(caster,false)
call ShowUnit(caster,true)
call UnitRemoveAbility(caster,BUFF_ID)
call DestroyEffect(AddSpecialEffect(EFFECT_BACK,x,y))
call SetUnitX(DummyCaster,x)
call SetUnitY(DummyCaster,y)
call IssueTargetOrder(DummyCaster,DUMMY_INVI_ORDER,caster)
call SetUnitTimeScale(caster,1.)
call SetUnitAnimation(caster,"stand")
call SetUnitVertexColor(caster,255,255,255,255)
if GetLocalPlayer()==casterOwner then
call SelectUnit(caster,true)
endif
call RemoveUnit(dummy)
set caster=null
set dummy=null
set casterOwner=null
set MData[i]=MData[M]
set MData[M]=-1
set M=M-1
if M==-1 then
call PauseTimer(T)
endif
call destroy()
return
endif
endif
set i=i+1
endloop
endmethod
static method onCast takes nothing returns boolean
local thistype this
local real x
local real y
local real o
local real z
if GetSpellAbilityId()==SPELL_ID then
set this=allocate()
set M=M+1
set MData[M]=this
set caster=GetTriggerUnit()
set casterOwner=GetTriggerPlayer()
set x=GetUnitX(caster)
set y=GetUnitY(caster)
set o=GetSpellTargetX()
set z=GetSpellTargetY()
set fade_alpha=GetUnitAbilityLevel(caster,SPELL_ID)
set dmg=getDamage(fade_alpha)
set max=SquareRoot((x-o)*(x-o)+(y-z)*(y-z))
set angle2=(57.29583*Atan2(z-y,o-x)+90.)
set angle=(angle2-90.)
set angle2=angle2*.0174533
set tick=max/MAX_HEIGHT*SPEED
set dummy= CreateUnit(Player(15),GetUnitTypeId(caster),x,y,angle)
set angle=angle*.0174533
set cos=tick*Cos(angle)
set sin=tick*Sin(angle)
call SetUnitColor(dummy,GetPlayerColor(casterOwner))
if UnitAddAbility(dummy,FLY_ABILITY) and UnitAddAbility(caster,FLY_ABILITY) then
call UnitRemoveAbility(dummy,FLY_ABILITY)
call UnitRemoveAbility(caster,FLY_ABILITY)
call UnitAddAbility(dummy,'Aloc')
call SetUnitVertexColor(dummy,255,255,255,DUMMY_ALPHA)
call SetUnitX(dummy,x)
call SetUnitY(dummy,y)
endif
call SetUnitAnimation(caster,CASTER_ANIMATION)
call SetUnitAnimation(dummy,CASTER_ANIMATION)
call SetUnitX(DummyCaster,x)
call SetUnitY(DummyCaster,y)
call SetUnitAbilityLevel(DummyCaster,DUMMY_SPELL_ID,5)
call IssueTargetOrder(DummyCaster,DUMMY_ORDER,caster)
call DestroyEffect(AddSpecialEffect(JUMP_EFFECT,x,y))
if M==0 then
call TimerStart(T,PERIODIC,true,function thistype.onPeriodic)
endif
endif
return false
endmethod
static method onInit takes nothing returns nothing
local integer i=0
local trigger t=CreateTrigger()
loop
exitwhen i>15
call TriggerRegisterPlayerUnitEvent(t,Player(i),EVENT_PLAYER_UNIT_SPELL_EFFECT,null)
set i=i+1
endloop
call TriggerAddCondition(t,function thistype.onCast)
set DummyCaster=CreateUnit(Player(15),DUMMY_CASTER,0.,0.,0.)
set t=null
endmethod
endstruct
endlibrary