//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xebasic
//**************************************************************************
//
// xebasic 0.4
// =======
// XE_DUMMY_UNITID : Rawcode of the dummy unit in your map. It should
// use the dummy.mdx model, so remember to import it as
// well, just use copy&paste to copy the dummy from the
// xe map to yours, then change the rawcode.
//
// XE_HEIGHT_ENABLER: Medivh's raven form ability, you may need to change
// this rawcode to another spell that morphs into a flier
// in case you modified medivh's spell in your map.
//
// XE_TREE_RECOGNITION: The ancients' Eat tree ability, same as with medivh
// raven form, you might have to change it.
//
// XE_ANIMATION_PERIOD: The global period of animation used by whatever
// timer that depends on it, if you put a low value
// the movement will look good but it may hurt your
// performance, if instead you use a high value it
// will not lag but will be fast.
//
// XE_MAX_COLLISION_SIZE: The maximum unit collision size in your map, if
// you got a unit bigger than 197.0 it would be
// a good idea to update this constant, since some
// enums will not find it. Likewise, if none of
// your units can go bellow X and X is much smaller
// than 197.0, it would be a good idea to update
// as well, since it will improve the performance
// those enums.
//
// Notice you probably don't have to update this library, unless I specify
// there are new constants which would be unlikely.
//
//**************************************************************************
//===========================================================================
globals
constant integer XE_DUMMY_UNITID = 'e000'
constant integer XE_HEIGHT_ENABLER = 'Amrf'
constant integer XE_TREE_RECOGNITION = 'Aeat'
constant real XE_ANIMATION_PERIOD = 0.025
constant real XE_MAX_COLLISION_SIZE = 197.0
endglobals
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xepreload initializer init requires xebasic, optional TimerUtils
//******************************************************************************
// xepreload 0.8
// ---------
// Ah, the joy of preloading abilities, it is such a necessary evil...
// Notice you are not supposed to use this system in places outside map init
//
// This one does the preloading and tries to minimize the hit on loading time
// for example, it only needs one single native call per ability preloaded.
//
//******************************************************************************
//==============================================================================
globals
private unit dum=null
endglobals
private keyword DebugIdInteger2IdString
//inline friendly (when debug mode is off..)
function XE_PreloadAbility takes integer abilid returns nothing
call UnitAddAbility(dum, abilid)
static if DEBUG_MODE then
if(dum==null) then
call BJDebugMsg("XE_PreloadAbility: do not load abilities after map init or during structs' onInit")
elseif GetUnitAbilityLevel(dum, abilid) == 0 then
call BJDebugMsg("XE_PreloadAbility: Ability "+DebugIdInteger2IdString.evaluate(abilid)+" does not exist.")
endif
endif
endfunction
// ................................................................................
//================================================================================
// Convert a integer id value into a 4-letter id code.
// * Taken from cheats.j so I don't have to code it again.
// * Used only on debug so making a whole library for it seemed silly
// * Private so people don't begin using xepreload just to call this function....
// * It will not work correctly if you paste this code in the custom script section
// due to the infamous % bug. Then again, if you do that then you probably
// deserve it....
//
private function DebugIdInteger2IdString takes integer value returns string
local string charMap = ".................................!.#$%&'()*+,-./0123456789:;<=>.@ABCDEFGHIJKLMNOPQRSTUVWXYZ[.]^_`abcdefghijklmnopqrstuvwxyz{|}~................................................................................................................................."
local string result = ""
local integer remainingValue = value
local integer charValue
local integer byteno
set byteno = 0
loop
set charValue = ModuloInteger(remainingValue, 256)
set remainingValue = remainingValue / 256
set result = SubString(charMap, charValue, charValue + 1) + result
set byteno = byteno + 1
exitwhen byteno == 4
endloop
return result
endfunction
//--------------------------------
private function kill takes nothing returns nothing
call RemoveUnit(dum)
set dum=null
static if (LIBRARY_TimerUtils ) then
call ReleaseTimer( GetExpiredTimer() )
else
call DestroyTimer(GetExpiredTimer())
endif
endfunction
private function init takes nothing returns nothing
local timer t
set dum = CreateUnit( Player(15), XE_DUMMY_UNITID, 0,0,0)
if( dum == null) then
debug call BJDebugMsg("xePreload : XE_DUMMY_UNITID ("+DebugIdInteger2IdString.evaluate(XE_DUMMY_UNITID)+") not added correctly to the map.")
endif
static if (LIBRARY_TimerUtils) then
set t=NewTimer()
else
set t=CreateTimer()
endif
call TimerStart(t,0.0,false,function kill)
set t=null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xefx initializer init requires xebasic
//**************************************************
// xefx 0.7
// --------
// Recommended: ARGB (adds ARGBrecolor method)
// For your movable fx needs
//
//**************************************************
//==================================================
globals
private constant integer MAX_INSTANCES = 8190 //change accordingly.
private constant real RECYCLE_DELAY = 4.0
//recycling, in order to show the effect correctly, must wait some time before
//removing the unit.
private timer recycler
private timer NOW
endglobals
private struct recyclebin extends array
unit u
real schedule
static recyclebin end=0
static recyclebin begin=0
static method Recycle takes nothing returns nothing
call RemoveUnit(.begin.u) //this unit is private, systems shouldn't mess with it.
set .begin.u=null
set .begin=recyclebin(integer(.begin)+1)
if(.begin==.end) then
set .begin=0
set .end=0
else
call TimerStart(recycler, .begin.schedule-TimerGetElapsed(NOW), false, function recyclebin.Recycle)
endif
endmethod
endstruct
private function init takes nothing returns nothing
set recycler=CreateTimer()
set NOW=CreateTimer()
call TimerStart(NOW,43200,true,null)
endfunction
struct xefx[MAX_INSTANCES]
public integer tag=0
private unit dummy
private effect fx=null
private real zang=0.0
private integer r=255
private integer g=255
private integer b=255
private integer a=255
private integer abil=0
static method create takes real x, real y, real facing returns xefx
local xefx this=xefx.allocate()
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, facing*bj_RADTODEG)
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
return this
endmethod
method operator owner takes nothing returns player
return GetOwningPlayer(this.dummy)
endmethod
method operator owner= takes player p returns nothing
call SetUnitOwner(this.dummy,p,false)
endmethod
method operator teamcolor= takes playercolor c returns nothing
call SetUnitColor(this.dummy,c)
endmethod
method operator scale= takes real value returns nothing
call SetUnitScale(this.dummy,value,value,value)
endmethod
//! textmacro XEFX_colorstuff takes colorname, colorvar
method operator $colorname$ takes nothing returns integer
return this.$colorvar$
endmethod
method operator $colorname$= takes integer value returns nothing
set this.$colorvar$=value
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
//! endtextmacro
//! runtextmacro XEFX_colorstuff("red","r")
//! runtextmacro XEFX_colorstuff("green","g")
//! runtextmacro XEFX_colorstuff("blue","b")
//! runtextmacro XEFX_colorstuff("alpha","a")
method recolor takes integer r, integer g , integer b, integer a returns nothing
set this.r=r
set this.g=g
set this.b=b
set this.a=a
call SetUnitVertexColor(this.dummy,this.r,this.g,this.b,this.a)
endmethod
implement optional ARGBrecolor
method operator abilityid takes nothing returns integer
return this.abil
endmethod
method operator abilityid= takes integer a returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(a!=0) then
call UnitAddAbility(this.dummy,a)
endif
set this.abil=a
endmethod
method operator abilityLevel takes nothing returns integer
return GetUnitAbilityLevel( this.dummy, this.abil)
endmethod
method operator abilityLevel= takes integer newLevel returns nothing
call SetUnitAbilityLevel(this.dummy, this.abil, newLevel)
endmethod
method flash takes string fx returns nothing
call DestroyEffect(AddSpecialEffectTarget(fx,this.dummy,"origin"))
endmethod
method operator xyangle takes nothing returns real
return GetUnitFacing(this.dummy)*bj_DEGTORAD
endmethod
method operator xyangle= takes real value returns nothing
call SetUnitFacing(this.dummy,value*bj_RADTODEG)
endmethod
method operator zangle takes nothing returns real
return this.zang
endmethod
method operator zangle= takes real value returns nothing
local integer i=R2I(value*bj_RADTODEG+90.5)
set this.zang=value
if(i>=180) then
set i=179
elseif(i<0) then
set i=0
endif
call SetUnitAnimationByIndex(this.dummy, i )
endmethod
method operator x takes nothing returns real
return GetUnitX(this.dummy)
endmethod
method operator y takes nothing returns real
return GetUnitY(this.dummy)
endmethod
method operator z takes nothing returns real
return GetUnitFlyHeight(this.dummy)
endmethod
method operator z= takes real value returns nothing
call SetUnitFlyHeight(this.dummy,value,0)
endmethod
method operator x= takes real value returns nothing
call SetUnitX(this.dummy,value)
endmethod
method operator y= takes real value returns nothing
call SetUnitY(this.dummy,value)
endmethod
method operator fxpath= takes string newpath returns nothing
if (this.fx!=null) then
call DestroyEffect(this.fx)
endif
if (newpath=="") then
set this.fx=null
else
set this.fx=AddSpecialEffectTarget(newpath,this.dummy,"origin")
endif
endmethod
method hiddenReset takes string newfxpath, real newfacing returns nothing
local real x = GetUnitX(this.dummy)
local real y = GetUnitY(this.dummy)
local real z = this.z
local real za = this.zangle
local integer level = this.abilityLevel
set fxpath=null
call RemoveUnit(this.dummy)
set this.dummy= CreateUnit(Player(15), XE_DUMMY_UNITID, x,y, newfacing*bj_RADTODEG)
if(level != 0) then
call UnitAddAbility(this.dummy, abilityid)
endif
call UnitAddAbility(this.dummy,XE_HEIGHT_ENABLER)
call UnitAddAbility(this.dummy,'Aloc')
call UnitRemoveAbility(this.dummy,XE_HEIGHT_ENABLER)
call SetUnitX(this.dummy,x)
call SetUnitY(this.dummy,y)
set this.z = z
set zangle = za
endmethod
private method onDestroy takes nothing returns nothing
if(this.abil!=0) then
call UnitRemoveAbility(this.dummy,this.abil)
endif
if(this.fx!=null) then
call DestroyEffect(this.fx)
set this.fx=null
endif
if (recyclebin.end==MAX_INSTANCES) then
//I'd like to see this happen...
call TimerStart(recycler,0,false,function recyclebin.Recycle)
call ExplodeUnitBJ(this.dummy)
else
set recyclebin.end.u=this.dummy
set recyclebin.end.schedule=TimerGetElapsed(NOW)+RECYCLE_DELAY
set recyclebin.end= recyclebin( integer(recyclebin.end)+1)
if( recyclebin.end==1) then
call TimerStart(recycler, RECYCLE_DELAY, false, function recyclebin.Recycle)
endif
call SetUnitOwner(this.dummy,Player(15),false)
endif
set this.dummy=null
endmethod
method hiddenDestroy takes nothing returns nothing
call ShowUnit(dummy,false)
call destroy()
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library xecollider initializer init requires xefx, xebasic
//****************************************************************
//*
//* xecollider 0.8
//* --------------
//* A xecollider object is a special effect that has a collision
//* size that can trigger a hit event and also many options to
//* configure its automatic movement.
//*
//* Please use .terminate() instead of .destroy() this ensures
//* that it will be safe to destroy it (else you would have to
//* worry about destroying it during the animation loop/etc.)
//*
//* To use this struct is a little different than the other
//* current parts of xe. Instead of just creating the xecollider
//* (which works, but it would only be a xefx that can have speed)
//* you probably need to make it do something special on the
//* unit hit event... For this reason, you need to make a new
//* struct extending xecollider that declares an onUnitHit method
//* you may also declare a loopControl method, very useful, can
//* help you reduce 'attaching'.
//*
//****************************************************************
//================================================================
globals
private constant real DEFAULT_COLLISION_SIZE = 50.0 // These are defaults, on one hand you can change them
private constant real DEFAULT_MAX_SPEED = 1500.0 // on the other hand, if a spell relies on the defaults
private constant real DEFAULT_EXPIRATION_TIME = 100. // changing them would make the behavior vary...
private constant real PI2 = 6.28318 //It might not be wise to change this
endglobals
//===========================================================================
// So, this exists merely so you can declare your own event handler methods
// if interfaces make your brain blow out, please skip the next four lines.
//
private interface eventHandler
method onUnitHit takes unit hitTarget returns nothing defaults nothing
method loopControl takes nothing returns nothing defaults nothing
endinterface
//===========================================================================
struct xecollider extends eventHandler
// use terminate() instead of .destroy() to "kill" the collider.
// don't worry, terminate will call destroy automatically.
//============================================================================
// delegates:
// We are delegating a xefx object so that people call all the xefx methods
// and member from a xecollider object. This means that from a user
// perspective a xecollider is also an instance of xefx.
//
// Notable ones are: .x , .y , .fxpath and .z ,
// check out xefx's documentation for more info.
//
private delegate xefx fx
//##==========================================================================
// public variables:
//
public real expirationTime = DEFAULT_EXPIRATION_TIME
// Movement speed for the missile.
public real speed = 0.0
// Speed added per second (notice you can use a negative value here)
public real acceleration = 0.0
// If there is acceleration, it is wise to have a cap...
public real maxSpeed = DEFAULT_MAX_SPEED
public real minSpeed = 0.0
public real angleSpeed = 0.0 //The increment in radians per second to the
// direction angle, allows curved movement.
private static integer lastSeen = 0
private group seen
private boolean silent = false
//##==========================================================================
// public methods:
//
//----
// Well, it is a good idea to actually create the missiles.
// notice that if your custom missile struct needs to declare its own create
// method, you can call this as allocate(x,y,dir).
//
// Sorry, no Loc version.
//
public static method create takes real x, real y, real dir returns xecollider
local xecollider xc= xecollider.allocate()
set xc.fx = xefx.create(x,y,dir)
set xc.dir=dir
set xecollider.V[xecollider.N]=xc
set xecollider.N=xecollider.N+1
if(xecollider.N==1) then
call TimerStart(xecollider.T, XE_ANIMATION_PERIOD, true, xecollider.timerLoopFunction )
endif
if(.lastSeen < integer(xc)) then //with this I do group recycling
set .lastSeen = integer(xc)
set xc.seen = CreateGroup()
endif
return xc
endmethod
//----
// The direction is just the angle in radians to which the missile's model faces
// and the automatic movement uses.
//
method operator direction takes nothing returns real
return this.dir
endmethod
method operator direction= takes real v returns nothing
set this.dir=v
set this.fx.xyangle=v
endmethod
//----
// The collisionSize
//
method operator collisionSize takes nothing returns real
return this.csize
endmethod
method operator collisionSize= takes real value returns nothing
set this.csize = value
//good long attribute name, but we use csize in the loop
//don't worry this gets inlined, it would also be helpful if
//I ever decide to add a control for assignment.
endmethod
//---
// targetUnit is a unit to follow (or try to follow), notice that homing
// options require an angleSpeed different to 0.0
//
public method operator targetUnit takes nothing returns unit
return this.homingTargetUnit
endmethod
public method operator targetUnit= takes unit u returns nothing
if(u==null) then
set this.angleMode= ANGLE_NO_MOVEMENT
else
set this.angleMode= ANGLE_HOMING_UNIT
endif
set this.homingTargetUnit=u
endmethod
//----
// targetPoint is a point to reach (or try to reach), notice that homing
// options require an angleSpeed different to 0.0
//
public method setTargetPoint takes real x, real y returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=x
set this.homingTargetY=y
endmethod
public method setTargetPointLoc takes location loc returns nothing
set this.angleMode= ANGLE_HOMING_POINT
set this.homingTargetX=GetLocationX(loc)
set this.homingTargetY=GetLocationY(loc)
endmethod
public method operator targetPointX takes nothing returns real
return this.homingTargetX
endmethod
public method operator targetPointY takes nothing returns real
return this.homingTargetY
endmethod
//----
// Call this in case you used targetUnit or TargetPoint so the missile
// forgets the order to home that target.
//
public method forgetTarget takes nothing returns nothing
set this.angleMode = ANGLE_NO_MOVEMENT
endmethod
public method operator rotating takes nothing returns boolean
return (angleMode ==ANGLE_ROTATING)
endmethod
public method operator rotating= takes boolean val returns nothing
if(val) then
set angleMode = ANGLE_ROTATING
elseif (angleMode == ANGLE_ROTATING) then
set angleMode = ANGLE_NO_MOVEMENT
endif
endmethod
method terminate takes nothing returns nothing
set this.dead=true
set this.fxpath=""
endmethod
/* declare hiddenDestroy so people don't call directly on the delegate xefx */
method hiddenDestroy takes nothing returns nothing
set silent = true
call terminate()
endmethod
//--------
private static timer T
private static integer N=0
private static xecollider array V
private static code timerLoopFunction //I use a code var so create can be above the timerloop function, more readable
private boolean dead=false
private real csize = DEFAULT_COLLISION_SIZE
private real dir
private static constant integer ANGLE_HOMING_UNIT =1
private static constant integer ANGLE_HOMING_POINT=2
private static constant integer ANGLE_NO_MOVEMENT=0
private static constant integer ANGLE_ROTATING=3
private integer angleMode =0
private unit homingTargetUnit = null
private real homingTargetX
private real homingTargetY
private method onDestroy takes nothing returns nothing
call GroupClear(this.seen)
if(this.silent) then
call this.fx.hiddenDestroy()
else
call this.fx.destroy()
endif
endmethod
private static xecollider cinstance
private static real newx
private static real newy
private static group enumGroup
private static boolexpr enumBoolexpr
private static unit array picked
private static integer pickedN
static method timerLoop takes nothing returns nothing
local integer i=0
local integer j=0
local integer c=0
local xecollider this
local real d
local real ns
local real wa
local real df1
local real df2
local unit u
loop
exitwhen (i== xecollider.N )
set this=.V[i] //adopt-a-instance
set this.expirationTime = this.expirationTime - XE_ANIMATION_PERIOD
if(.dead or (this.expirationTime <=0.0) ) then
call this.destroy()
else
set ns=this.angleSpeed*XE_ANIMATION_PERIOD
if (ns!=0.0) then
if(this.angleMode== ANGLE_HOMING_UNIT ) then
set u=this.homingTargetUnit
if ( (GetUnitTypeId(u)==0) or IsUnitType(u, UNIT_TYPE_DEAD) ) then
set this.angleMode= ANGLE_NO_MOVEMENT
set this.homingTargetUnit = null
else
set this.homingTargetX=GetUnitX(u)
set this.homingTargetY=GetUnitY(u)
endif
set u=null
endif
if (this.angleMode == ANGLE_ROTATING) then
//nothing (ns is already ns)
elseif( this.angleMode != ANGLE_NO_MOVEMENT) then
if(ns<=0) then
set ns=-ns
endif
set wa=Atan2(this.homingTargetY - this.y , this.homingTargetX-this.x)
//if(wa<0.0) then
// set wa=wa+PI2
//endif
set df1=wa-this.dir
set df2=(PI2+wa)-this.dir
if (df1<=0) then
if(df2<=0) then
if(df2>=df1) then
set df1=df2
endif
else
if(-df1>=df2) then
set df1=df2
endif
endif
else
if(df2<=0) then
if(-df2<=df1) then
set df1=df2
endif
else
if(df2<=df1) then
set df1=df2
endif
endif
endif
if(df1<=0) then
if(-df1>=ns) then
set ns=-ns
else
set ns=df1
endif
else
if(df1<=ns) then
set ns=df1
endif
endif
else
set ns = 0
endif
set d=this.dir
set d = d + ns
if(d>=PI2) then
set d=d - PI2
elseif(d<0) then
set d=d + PI2
endif
set this.dir = d
set this.xyangle = d
endif
// function calls are expensive, damned we are, long code inside of loop
// correct software dev. tells us this should go to another function,
// but this is Jass, not real life.
set .cinstance = this
set ns = this.speed + this.acceleration*XE_ANIMATION_PERIOD
if ( ns<this.minSpeed) then
set ns=this.minSpeed
elseif (ns>this.maxSpeed) then
set ns=this.maxSpeed
endif
set d=((this.speed+ns)/2) * XE_ANIMATION_PERIOD
set this.speed=ns
set .newx= .x+d*Cos(this.dir)
set .newy= .y+d*Sin(this.dir)
set .x=.newx
set .y=.newy
set xecollider.pickedN = 0
call GroupEnumUnitsInRange( .enumGroup, .newx, .newy, .csize + XE_MAX_COLLISION_SIZE, .enumBoolexpr)
call GroupClear(this.seen)
set j=0
loop
exitwhen (j==xecollider.pickedN)
call GroupAddUnit( this.seen, xecollider.picked[j])
set j=j+1
endloop
set .V[c]=this
set c=c+1
if( this.loopControl.exists and not this.dead ) then
call this.loopControl()
endif
endif
set i=i+1
endloop
//call BJDebugMsg("}")
set xecollider.N=c
if(c==0) then
call PauseTimer(xecollider.T)
endif
endmethod
private static method inRangeEnum takes nothing returns boolean
local xecollider this= .cinstance //adopt-a-instance
local unit u=GetFilterUnit()
if not IsUnitType(u, UNIT_TYPE_DEAD) and not(this.dead) and (GetUnitTypeId(u)!=XE_DUMMY_UNITID) and IsUnitInRangeXY(u, .newx, .newy, .csize) then
// ah, the advantages of a standardized unit id...
set xecollider.picked[xecollider.pickedN] = u
set xecollider.pickedN=xecollider.pickedN + 1
if not IsUnitInGroup (u, this.seen ) then
call this.onUnitHit(u)
endif
endif
set u=null
return false
endmethod
//============================================================================
// you aren't supposed to call doInit yourself, try not to do it.
//
static method doInit takes nothing returns nothing
set xecollider.enumGroup = CreateGroup()
set xecollider.enumBoolexpr = Condition( function xecollider.inRangeEnum)
set xecollider.timerLoopFunction = (function xecollider.timerLoop)
set xecollider.T=CreateTimer()
endmethod
endstruct
private function init takes nothing returns nothing
call xecollider.doInit()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
// o o o o o o o o o o o o o o o o o o o o o o o
// o Damage Over Time by Dynasti o Version 1.7 o
// o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
// o How To Implement o
// o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
// o o
// o 1. - Make sure that you opened the system in the newest version of JNGP o
// o 2. - Copy this trigger into you map o
// o o
// o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
// o How to Use o
// o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
// o o
// o - This system requires you to give 8 variables for it to function perfectly. o
// o Variables are: o
// o 1. - A unit that does the damage, lets call it " A " o
// o 2. - A widget that is given the damage, lets call it " B " o
// o 3. - A real value that is the damage, lets call it " D " o
// o 4. - A real value that is the time it takes to do the damage, lets call it " T " o
// o 5. - An attacktype that is used to regulate the damage, lets call it " AT " o
// o 6. - A damagetype that is used to regulate the damage, lets call it " DT " o
// o 7. - A string that is the model of the period effect, lets call it " EX " o
// o 8. - A string that is the attachment for the " EX " string, lets call it " ExAttach " o
// o o
// o The call: o
// o call DamageOverTimeEx( A , B , D , T , AT , DT , EX , ExAttach ) o
// o you have now succesfully called the function and started the damage over time. o
// o o
// o if you want something simpler and not have the effect do this: o
// o call DamageOverTime( A , B , D , T , AT , DT ) o
// o now you have done the simpler call. o
// o o
// o Tips: o
// o To not make the target " B " run away or run after you, do this: o
// o call DamageOverTimeEx( B , B , D , T , AT , DT , EX , ExAttach ) o
// o Or for the simpler call: o
// o call DamageOverTime( B , B , D , T , AT , DT ) o
// o o
// o What we now have done is set the unit that does the damage to the unit that takes the damage, " B ". o
// o This does so that the unit " B " does damage to itself and will not run away or attack any unit. o
// o NB: Damaging itself will result to bounty for the own dying unit or, no bounty at all for the killer. o
// o o
// o o
// o If for any reason you want to cancel you need to save the DOT struct as a variable in whatever you're using. o
// o o
// o First, you need to have the DOT struct variable stored as some integer to cancel a DOT. o
// o Example: o
// o [...] o
// o set SomeInteger = DamageOverTime(A, B, D, T, AT, DT, EX, ExAttach) o
// o o
// o Since function DamageOverTimeEx or DamageOverTime returns the struct to you, you can store it for later use o
// o IMPORTANT!: If you do the stopcall after the DoT has stopped you might screw up things. To do this, you o
// o can use the "check" function to check if the struct value you have stored is running and to o
// o what unit. o
// o o
// o The "Check" function: o
// o the function "IsDamageRunning takes DOT dat returns unit" will return 'null' as a value if the struct isn't o
// o running, and will return a unit handle if the struct is running. But wait, you might be thinking: o
// o What if it is another unit than the unit I started it with?! Well do this: o
// o [...] o
// o if MyUnitVariable == IsDamageRunning(StructVariable) then o
// o [your actions here] o
// o call EndDamageOverTime(StructVariable) o
// o else o
// o set StructVariable = 0 o
// o endif o
// o o
// o By doing this, it will insure you that you won't accedently end another unit's DOT effect o
// o o
// o o
// o - For more info go to: www.HiveWorkshop.com or visit www.Ngo.clan.su , user at both sites are Dynasti o
// o o
// o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o o
library DOT initializer Init
globals
private constant integer FPS = 40 // This is how many times the struct loop will run in a single second. 30-40 is recomended
// DO NOT TUCH! =)
private constant real Interval = (1. / FPS)
endglobals
private struct DOT
unit Attacker
widget Target
real Damage
integer EndCount
attacktype AttackType
damagetype DamageType
effect Effect
static integer array Index
static integer Total = 0
static timer Tim = null
static integer Count = -2147483648 // Just a random low number, it is needed
static method Loop takes nothing returns nothing
local DOT dat
local integer i = 0
set DOT.Count = DOT.Count + 1
loop
exitwhen i >= DOT.Total
set dat = DOT.Index[i]
if DOT.Count > dat.EndCount or GetWidgetLife(dat.Target) <= .405 then
call DestroyEffect(dat.Effect)
set dat.Effect = null
set dat.Attacker = null
set dat.Target = null
set dat.AttackType = null
set dat.DamageType = null
call dat.destroy()
set DOT.Total = DOT.Total - 1
set DOT.Index[i] = DOT.Index[DOT.Total]
set i = i - 1
else
call UnitDamageTarget(dat.Attacker, dat.Target, dat.Damage, false, false, dat.AttackType, dat.DamageType, null)
endif
set i = i + 1
endloop
if DOT.Total == 0 then
call PauseTimer(DOT.Tim)
endif
endmethod
static method Start takes unit Attacker, widget Target, real Damage, real Time, attacktype AttackType, damagetype DamageType, string Effect, string EffectAttach returns DOT
local DOT dat = DOT.allocate()
set dat.Attacker = Attacker
set dat.Target = Target
set dat.AttackType = AttackType
set dat.DamageType = DamageType
if Effect != "" and Effect != null then
if EffectAttach != "" and EffectAttach != null then
set dat.Effect = AddSpecialEffectTarget(Effect, Target, EffectAttach)
endif
endif
set dat.Damage = Damage * Interval / Time
set dat.EndCount = DOT.Count + R2I(Time / Interval)
if DOT.Total == 0 then
call TimerStart(DOT.Tim, Interval, true, function DOT.Loop)
endif
set DOT.Index[DOT.Total] = dat
set DOT.Total = DOT.Total + 1
return dat
endmethod
endstruct
//=========================================================================================
function DamageOverTimeEx takes unit Attacker, widget Target, real Damage, real Time, attacktype AttackType, damagetype DamageType, string Effect, string EffectAttach returns DOT
return DOT.Start(Attacker, Target, Damage, Time, AttackType, DamageType, Effect, EffectAttach)
endfunction
//=========================================================================================
private function Init takes nothing returns nothing
set DOT.Tim = CreateTimer()
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library GroupUtils initializer Init requires optional xebasic
//******************************************************************************
//* BY: Rising_Dusk
//*
//* This library is a combination of several features relevant to groups. First
//* and foremost, it contains a group stack that you can access dynamic groups
//* from. It also provides means to refresh groups and clear any shadow
//* references within them. The included boolexprs are there for backwards
//* compatibility with maps that happen to use them. Since the 1.24c patch,
//* null boolexprs used in GroupEnumUnits* calls no longer leak, so there is no
//* performance gain to using the BOOLEXPR_TRUE constant.
//*
//* Instead of creating/destroying groups, we have moved on to recycling them.
//* NewGroup pulls a group from the stack and ReleaseGroup adds it back. Always
//* remember to call ReleaseGroup on a group when you are done using it. If you
//* fail to do so enough times, the stack will overflow and no longer work.
//*
//* GroupRefresh cleans a group of any shadow references which may be clogging
//* its hashtable. If you remove a unit from the game who is a member of a unit
//* group, it will 'effectively' remove the unit from the group, but leave a
//* shadow in its place. Calling GroupRefresh on a group will clean up any
//* shadow references that may exist within it. It is only worth doing this on
//* groups that you plan to have around for awhile.
//*
//* Constants that can be used from the library:
//* [group] ENUM_GROUP As you might expect, this group is good for
//* when you need a group just for enumeration.
//* [boolexpr] BOOLEXPR_TRUE This is a true boolexpr, which is important
//* because a 'null' boolexpr in enumeration
//* calls results in a leak. Use this instead.
//* [boolexpr] BOOLEXPR_FALSE This exists mostly for completeness.
//*
//* This library also includes a simple implementation of a group enumeration
//* call that factors collision of units in a given area of effect. This is
//* particularly useful because GroupEnumUnitsInRange doesn't factor collision.
//*
//* In your map, you can just replace all instances of GroupEnumUnitsInRange
//* with GroupEnumUnitsInArea with identical arguments and your spells will
//* consider all units colliding with the area of effect. After calling this
//* function as you would normally call GroupEnumUnitsInRange, you are free to
//* do anything with the group that you would normally do.
//*
//* If you don't use xebasic in your map, you may edit the MAX_COLLISION_SIZE
//* variable below and the library will use that as the added radius to check.
//* If you use xebasic, however, the script will automatically use xe's
//* collision size variable.
//*
//* You are also able to use GroupUnitsInArea. This function returns all units
//* within the area, no matter what they are, which can be convenient for those
//* instances where you actually want that.
//*
//* Example usage:
//* local group MyGroup = NewGroup()
//* call GroupRefresh(MyGroup)
//* call ReleaseGroup(MyGroup)
//* call GroupEnumUnitsInArea(ENUM_GROUP, x, y, 350., BOOLEXPR_TRUE)
//* call GroupUnitsInArea(ENUM_GROUP, x, y, 350.)
//*
globals
//If you don't have xebasic in your map, this value will be used instead.
//This value corresponds to the max collision size of a unit in your map.
private constant real MAX_COLLISION_SIZE = 197.
//If you are insane and don't care about any of the protection involved in
//this library, but want this script to be really fast, set this to true.
private constant boolean LESS_SAFETY = false
endglobals
globals
//* Constants that are available to the user
group ENUM_GROUP = CreateGroup()
boolexpr BOOLEXPR_TRUE = null
boolexpr BOOLEXPR_FALSE = null
endglobals
globals
//* Hashtable for debug purposes
private hashtable ht = InitHashtable()
//* Temporary references for GroupRefresh
private boolean Flag = false
private group Refr = null
//* Arrays and counter for the group stack
private group array Groups
private integer Count = 0
//* Variables for use with the GroupUnitsInArea function
private real X = 0.
private real Y = 0.
private real R = 0.
private hashtable H = InitHashtable()
endglobals
private function HookDestroyGroup takes group g returns nothing
if g == ENUM_GROUP then
call BJDebugMsg(SCOPE_PREFIX+"Warning: ENUM_GROUP destroyed")
endif
endfunction
debug hook DestroyGroup HookDestroyGroup
private function AddEx takes nothing returns nothing
if Flag then
call GroupClear(Refr)
set Flag = false
endif
call GroupAddUnit(Refr, GetEnumUnit())
endfunction
function GroupRefresh takes group g returns nothing
set Flag = true
set Refr = g
call ForGroup(Refr, function AddEx)
if Flag then
call GroupClear(g)
endif
endfunction
function NewGroup takes nothing returns group
if Count == 0 then
set Groups[0] = CreateGroup()
else
set Count = Count - 1
endif
static if not LESS_SAFETY then
call SaveInteger(ht, 0, GetHandleId(Groups[Count]), 1)
endif
return Groups[Count]
endfunction
function ReleaseGroup takes group g returns boolean
local integer id = GetHandleId(g)
static if LESS_SAFETY then
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
else
if g == null then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Null groups cannot be released")
return false
elseif not HaveSavedInteger(ht, 0, id) then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Group not part of stack")
return false
elseif LoadInteger(ht, 0, id) == 2 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Groups cannot be multiply released")
return false
elseif Count == 8191 then
debug call BJDebugMsg(SCOPE_PREFIX+"Error: Max groups achieved, destroying group")
call DestroyGroup(g)
return false
endif
call SaveInteger(ht, 0, id, 2)
endif
call GroupClear(g)
set Groups[Count] = g
set Count = Count + 1
return true
endfunction
private function Filter takes nothing returns boolean
return IsUnitInRangeXY(GetFilterUnit(), X, Y, R)
endfunction
private function HookDestroyBoolExpr takes boolexpr b returns nothing
local integer bid = GetHandleId(b)
if HaveSavedHandle(H, 0, bid) then
//Clear the saved boolexpr
call DestroyBoolExpr(LoadBooleanExprHandle(H, 0, bid))
call RemoveSavedHandle(H, 0, bid)
endif
endfunction
hook DestroyBoolExpr HookDestroyBoolExpr
private constant function GetRadius takes real radius returns real
static if LIBRARY_xebasic then
return radius+XE_MAX_COLLISION_SIZE
else
return radius+MAX_COLLISION_SIZE
endif
endfunction
function GroupEnumUnitsInArea takes group whichGroup, real x, real y, real radius, boolexpr filter returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
local integer bid = 0
//Set variables to new values
set X = x
set Y = y
set R = radius
if filter == null then
//Adjusts for null boolexprs passed to the function
set filter = Condition(function Filter)
else
//Check for a saved boolexpr
set bid = GetHandleId(filter)
if HaveSavedHandle(H, 0, bid) then
//Set the filter to use to the saved one
set filter = LoadBooleanExprHandle(H, 0, bid)
else
//Create a new And() boolexpr for this filter
set filter = And(Condition(function Filter), filter)
call SaveBooleanExprHandle(H, 0, bid, filter)
endif
endif
//Enumerate, if they want to use the boolexpr, this lets them
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), filter)
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
function GroupUnitsInArea takes group whichGroup, real x, real y, real radius returns nothing
local real prevX = X
local real prevY = Y
local real prevR = R
//Set variables to new values
set X = x
set Y = y
set R = radius
//Enumerate
call GroupEnumUnitsInRange(whichGroup, x, y, GetRadius(radius), Condition(function Filter))
//Give back original settings so nested enumerations work
set X = prevX
set Y = prevY
set R = prevR
endfunction
private function True takes nothing returns boolean
return true
endfunction
private function False takes nothing returns boolean
return false
endfunction
private function Init takes nothing returns nothing
set BOOLEXPR_TRUE = Condition(function True)
set BOOLEXPR_FALSE = Condition(function False)
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Knockback
globals
//******** Library made by Berb with slight modifications by Patio_111 ********
//* Configuration
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//*
private constant real LOOP_REF = 0.04
//*
//*
//*****************************************************************************
endglobals
struct Knockback
readonly unit subject = null
readonly real velocity
readonly real rotation
private real rotationCos
private real rotationSin
readonly real friction
private static real checkItemX = GetRectMaxX(bj_mapInitialPlayableArea)
private static real checkItemY = GetRectMaxY(bj_mapInitialPlayableArea)
private static item checkItem
private static timer priv_stackLoop = CreateTimer()
private static constant real priv_stackLoopRef = LOOP_REF
private real priv_time
private boolean priv_stopped = false
private integer counter = 0
private string effectPeriodic
private integer effectFrequency
private integer priv_index
private static thistype array priv_stack
private static integer priv_stackN = 0
private method onDestroy takes nothing returns nothing
set thistype.priv_stackN = thistype.priv_stackN-1
set thistype.priv_stack[.priv_index] = thistype.priv_stack[thistype.priv_stackN]
set thistype.priv_stack[.priv_index].priv_index = .priv_index
if (.priv_stackN == 0) then
call PauseTimer(thistype.priv_stackLoop)
endif
endmethod
private static method onRef takes nothing returns nothing
local integer i = .priv_stackN-1
local thistype kb
local real x
local real y
local real iX
local real iY
loop
exitwhen(i < 0)
set kb = .priv_stack[i]
if (kb != 0) then
set x = GetWidgetX(kb.subject) + kb.velocity * kb.rotationCos
set y = GetWidgetY(kb.subject) + kb.velocity * kb.rotationSin
//use an item to check the pathability of the new coordinate and
//move the unit if the point is available.
call SetItemPosition(checkItem, x, y)
set iX = GetWidgetX(checkItem)
set iY = GetWidgetY(checkItem)
if ((iX-x)*(iX-x) + (iY-y)*(iY-y) <= 32) then
call SetUnitX(kb.subject, x)
call SetUnitY(kb.subject, y)
set kb.counter = kb.counter + 1
if kb.counter >= kb.effectFrequency then
call DestroyEffect(AddSpecialEffect(kb.effectPeriodic, x, y))
endif
endif
set kb.velocity = kb.velocity + kb.friction
set kb.priv_time = kb.priv_time - .priv_stackLoopRef
if (kb.priv_time <= 0.00) or kb.priv_stopped then
call PauseUnit(kb.subject, false)
call kb.destroy()
endif
endif
set i = i - 1
endloop
call SetItemPosition(checkItem, checkItemX, checkItemY)
call SetItemVisible(checkItem, false)
endmethod
static method create takes unit u, real angle, real distance, real time, string effect1, string periodiceffect, integer effectfreq returns thistype
local thistype kb = .allocate()
set kb.subject = u
set kb.velocity = 2*distance/time
set kb.rotation = angle
set kb.rotationCos = Cos(angle)
set kb.rotationSin = Sin(angle)
set kb.friction = -kb.velocity/time
set kb.priv_index = .priv_stackN
set kb.priv_time = time
set kb.velocity = kb.velocity * .priv_stackLoopRef
set kb.friction = kb.friction * .priv_stackLoopRef * .priv_stackLoopRef
set kb.effectPeriodic = periodiceffect
set kb.effectFrequency = effectfreq
call DestroyEffect(AddSpecialEffectTarget(effect1, kb.subject, "origin"))
if (.priv_stackN == 0) then
call TimerStart(.priv_stackLoop, .priv_stackLoopRef, true, function thistype.onRef)
endif
set .priv_stack[.priv_stackN] = kb
set .priv_stackN = .priv_stackN + 1
call PauseUnit(kb.subject, true)
return kb
endmethod
static method onInit takes nothing returns nothing
//the item will be have maintained invisibility as to not interfere with
//in-game actions, such as a unit picking the item up.
set checkItem=CreateItem('afac', checkItemX, checkItemY)
call SetItemVisible(checkItem, false)
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
GENERAL:
First of all, if you want to import any of the spells to you map, start out by moving the XE library, because all of the spells use it.
1. Export the model for the xe dummy unit from the import manager, and then import it into your map.
2. Copy the dummy unit "xe unit (Caster System?)" from the Object Editor into your map.
3a. Copy the triggers called "xebasic" and "xepreload" into your map (you may need more, depending on the spell, but these are the only ones used for all the spells).
3b. Skip this step if you are ONLY going to import the spell "Shadow Call". Copy the trigger "GroupUtils" from this map to yours.
4. Adjust the raw codes (see below***) of the dummy unit and other necessary values in the setup section of the trigger "xebasic" you copied into your map.
=========================================================================================================================================================================================================
Clock:
1. Copy the ability "Clock" from the Object Editor to your map.
2. Copy the triggers called "Clock" and "xefx" from this map to yours.
3. Adjust the raw code (see below***) of the spell in the setup section of the trigger "Clock" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Shock Blast
1. Copy the ability "Shock Blast" from the Object Editor to your map.
2. Copy the triggers called "Shock Blast" and "xefx" from this map to yours.
3. Adjust the raw code (see below***) of the spell in the setup section of the trigger "Shock Blast" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Lightning Blink
1. Copy the ability "Lightning Blink" from the Object Editor to your map.
2. Copy the triggers called "Lightning Blink" and "xefx" from this map to yours.
3. Adjust the raw code (see below***) of the spell in the setup section of the trigger "Lightning Blink" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Shadow Circle
1. Copy the ability "Shadow Circle" from the Object Editor to your map.
2. Copy the trigger called "Shadow Circle" from this map to yours.
3. Adjust the raw code (see below***) of the spell in the setup section of the trigger "Shadow Circle" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Thunderstorm
1. Copy the dummy unit "Multi-Use Dummy" from the Object Editor into your map.
2. Copy the ability "Thunderstorm" from the Object Editor to your map.
3. Copy the triggers called "Thunderstorm", "Knockback", and "xefx" from this map to yours.
4. Adjust the raw code (see below***) of the dummy unit in the setup section of the trigger "Knockback" you copied in your map.
5. Adjust the raw code of the spell in the setup section of the trigger "Thunderstorm" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Starfall Frenzy
1. Copy the ability "Starfall Frenzy" from the Object Editor to your map.
2. Copy the trigger called "Starfall Frenzy" from this map to yours.
3. Adjust the raw code (see below***) of the spell in the setup section of the trigger "Starfall Frenzy" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Shadow Call
1. Copy the ability "Shadow Call" from the Object Editor to your map.
2. Copy the triggers called "Shadow Call", "xefx", and "xecollider" from this map to yours.
3. Adjust the raw code (see below***) of the spell and of the unit you want the spell to summon in the setup section of the trigger "Shadow Call" you copied in your map.
-Done!
=========================================================================================================================================================================================================
Elemental Discharge
1. Copy the dummy unit "Multi-Use Dummy" from the Object Editor into your map.
2. Copy the abilities "Elemental Discharge", "Dummy Purge", and "Dummy Roots" from the Object Editor to your map.
3. Copy the triggers called "Elemental Discharge" and "DoT" from this map to yours.
4. Adjust the raw codes (see below***) of the ability and of the dummy spells and units in the setup section of the trigger "Elemental Discharge" you copied in your map.
-Done!
=========================================================================================================================================================================================================
***To find out the raw code of a spell or unit through the Object Editor, press Ctrl+D with the Object Editor open and look at the spell/unit.
=========================================================================================================================================================================================================
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Clock initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A001' //Raw code of the spell.
private constant real Arrow1Damage = 1000. //Damage the first (fast) arrow deals per second to units in full contact.
private constant real Arrow1DamageInc = 200. //Damage amount of the first arrow increased per level of the ability.
private constant real Arrow2Damage = 350. //Damage the second (slow) arrow deals every second upon contact with enemies. NOTE: This arrow moves very slowly, making the damage output big even though the number is small.
private constant real Arrow2DamageInc = 100. //Damage amount of the first arrow increased per level of the ability.
private constant real DamageAoE = 80. //Range from arrow dummies in which enemy units get damaged.
private constant boolean AttachClock = true //Wether to move the clock effect with the caster or not. (true - move, false - don't move).
private constant real HealBase = 200. //Amount healed over 8 secs for the duration of the spell at level 1. Affected by level increase below.
private constant real HealIncrease = 100. //Heal amount increased per level of the ability.
private constant real RotationStart = 90. //Starting rotation (degrees) of the arrows. 90 is most logical since it is the starting position of real clock arrows (doh).
private constant real RotationTotal = 360. //The total degrees the slow arrow rotates before finishing effect. (360 - default: 1 complete circle)
private constant real TotalTime = 8. //The total duration of the spell in seconds.
private constant real Interval = 0.04 //Period of time in which the arrow dummies are moved.
private constant real Separation = 120. //Separation distance between each light orb.
private constant integer DummyCount1 = 7 //Number of light orb dummies created for arrow 1 (fast arrow).
private constant integer DummyCount2 = 7 //Number of light orb dummies created for arrow 2 (slow arrow).
private constant real DummyHeight = 100. //Flying height of the light orb dummies.
private constant real DummySize = 3.5 //Size of the light orb dummies in each arrow.
private constant string DummyPath = "Abilities\\Weapons\\FaerieDragonMissile\\FaerieDragonMissile.mdl" //Effect that composes the clock's arrows.
private constant string SpawnEffect = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //Effect created when the dummies are spawned.
private constant string DeathEffect = "Abilities\\Spells\\Human\\HolyBolt\\HolyBoltSpecialArt.mdl" //Effect created when the dummies are destroyed.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = true //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer ClockTimer = CreateTimer()
private integer ClockActiveTotal = 0
private integer array ClockStructID
private boolexpr DamageExpr
private unit TempU
private real TempR
private constant real TotalExecutions = TotalTime / Interval
private constant real Rotation2Interval = RotationTotal / TotalExecutions * bj_DEGTORAD
private constant real Rotation1Interval = Rotation2Interval * 12.
endglobals
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, TempR, true, false, AttackT, DamageT, WeaponT)
endif
endif
set Temp = null
return false
endfunction
private struct Clock
private unit Caster
private xefx array Arrow1Dummy[DummyCount1]
private xefx array Arrow2Dummy[DummyCount2]
private integer ArrowMoveCount = 0
private integer AbilityLevel
private real CenterX
private real CenterY
private real Arrow1Rotation
private real Arrow2Rotation
private real PeriodicDmg1
private real PeriodicDmg2
static method create takes unit source returns Clock
local Clock c = Clock.allocate()
local real PointX
local real PointY
local integer Count = 0
set c.Caster = source
set c.Arrow1Rotation = RotationStart * bj_DEGTORAD
set c.Arrow2Rotation = RotationStart * bj_DEGTORAD
set c.AbilityLevel = GetUnitAbilityLevel(source, SpellID)
set c.CenterX = GetUnitX(c.Caster)
set c.CenterY = GetUnitY(c.Caster)
set c.PeriodicDmg1 = (Arrow1Damage + Arrow1DamageInc * (c.AbilityLevel - 1))/TotalExecutions
set c.PeriodicDmg2 = (Arrow2Damage + Arrow2DamageInc * (c.AbilityLevel - 1))/TotalExecutions
loop
set Count = Count + 1
set PointX = c.CenterX + Separation * (Count - 1) * Cos(c.Arrow1Rotation)
set PointY = c.CenterY + Separation * (Count - 1) * Sin(c.Arrow1Rotation)
set c.Arrow1Dummy[Count] = xefx.create(PointX, PointY, 0.)
set c.Arrow1Dummy[Count].fxpath = DummyPath
set c.Arrow1Dummy[Count].z = DummyHeight
set c.Arrow1Dummy[Count].scale = DummySize
call DestroyEffect(AddSpecialEffect(SpawnEffect, PointX, PointY))
exitwhen Count == DummyCount1
endloop
set Count = 0
loop
set Count = Count + 1
set PointX = c.CenterX + Separation * (Count - 1) * Cos(c.Arrow1Rotation)
set PointY = c.CenterY + Separation * (Count - 1) * Sin(c.Arrow1Rotation)
set c.Arrow2Dummy[Count] = xefx.create(PointX, PointY, 0.)
set c.Arrow2Dummy[Count].fxpath = DummyPath
set c.Arrow2Dummy[Count].z = DummyHeight
set c.Arrow2Dummy[Count].scale = DummySize
call DestroyEffect(AddSpecialEffect(SpawnEffect, PointX, PointY))
exitwhen Count == DummyCount2
endloop
if ClockActiveTotal == 0 then
call TimerStart(ClockTimer, Interval, true, function Clock.Move)
endif
set ClockStructID[ClockActiveTotal] = c
set ClockActiveTotal = ClockActiveTotal + 1
return c
endmethod
private static method Move takes nothing returns nothing
local Clock TempStruct
local real PointX
local real PointY
local integer Count = 0
local integer Count2 = 0
local unit Temp
loop
exitwhen Count >= ClockActiveTotal
set TempStruct = ClockStructID[Count]
if AttachClock == true then
set TempStruct.CenterX = GetUnitX(TempStruct.Caster)
set TempStruct.CenterY = GetUnitY(TempStruct.Caster)
endif
call SetWidgetLife(TempStruct.Caster, GetWidgetLife(TempStruct.Caster) + (HealBase + (HealIncrease * TempStruct.AbilityLevel - HealIncrease))/TotalExecutions)
set TempStruct.Arrow1Rotation = TempStruct.Arrow1Rotation - Rotation1Interval
set TempStruct.Arrow2Rotation = TempStruct.Arrow2Rotation - Rotation2Interval
set TempStruct.ArrowMoveCount = TempStruct.ArrowMoveCount + 1
loop
set Count2 = Count2 + 1
set PointX = TempStruct.CenterX + Separation * (Count2 - 1) * Cos(TempStruct.Arrow1Rotation)
set PointY = TempStruct.CenterY + Separation * (Count2 - 1) * Sin(TempStruct.Arrow1Rotation)
set TempStruct.Arrow1Dummy[Count2].x = PointX
set TempStruct.Arrow1Dummy[Count2].y = PointY
set TempU = TempStruct.Caster
set TempR = TempStruct.PeriodicDmg1
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DamageAoE, DamageExpr)
set TempU = null
exitwhen Count2 == DummyCount1
endloop
set Count2 = 0
loop
set Count2 = Count2 + 1
set PointX = TempStruct.CenterX + Separation * (Count2 - 1) * Cos(TempStruct.Arrow2Rotation)
set PointY = TempStruct.CenterY + Separation * (Count2 - 1) * Sin(TempStruct.Arrow2Rotation)
set TempStruct.Arrow2Dummy[Count2].x = PointX
set TempStruct.Arrow2Dummy[Count2].y = PointY
set TempU = TempStruct.Caster
set TempR = TempStruct.PeriodicDmg2
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DamageAoE, DamageExpr)
set TempU = null
exitwhen Count2 == DummyCount2
endloop
set Count2 = 0
if TempStruct.ArrowMoveCount == TotalExecutions or GetWidgetLife(TempStruct.Caster) <= 0.405 then
call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.CenterX, TempStruct.CenterY))
loop
set Count2 = Count2 + 1
set PointX = TempStruct.CenterX + Separation * Count2 * Cos(bj_PI/2.)
set PointY = TempStruct.CenterY + Separation * Count2 * Sin(bj_PI/2.)
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
exitwhen Count2 == 6
endloop
set Count2 = 0
loop
set Count2 = Count2 + 1
call TempStruct.Arrow1Dummy[Count2].destroy()
exitwhen Count2 == DummyCount1
endloop
set Count2 = 0
loop
set Count2 = Count2 + 1
call TempStruct.Arrow2Dummy[Count2].destroy()
exitwhen Count2 == DummyCount2
endloop
set ClockActiveTotal = ClockActiveTotal - 1
set ClockStructID[Count] = ClockStructID[ClockActiveTotal]
if ClockActiveTotal == 0 then
call PauseTimer(ClockTimer)
endif
call TempStruct.destroy()
elseif TempStruct.ArrowMoveCount > TotalExecutions then
call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.CenterX, TempStruct.CenterY))
loop
set Count2 = Count2 + 1
set PointX = TempStruct.CenterX + Separation * Count2 * Cos(bj_PI/2.)
set PointY = TempStruct.CenterY + Separation * Count2 * Sin(bj_PI/2.)
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
exitwhen Count2 == 6
endloop
set Count2 = 0
loop
set Count2 = Count2 + 1
call TempStruct.Arrow1Dummy[Count2].destroy()
exitwhen Count2 == DummyCount1
endloop
set Count2 = 0
loop
set Count2 = Count2 + 1
call TempStruct.Arrow2Dummy[Count2].destroy()
exitwhen Count2 == DummyCount2
endloop
set ClockActiveTotal = ClockActiveTotal - 1
set ClockStructID[Count] = ClockStructID[ClockActiveTotal]
if ClockActiveTotal == 0 then
call PauseTimer(ClockTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
endif
set Count = Count + 1
endloop
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call Clock.create(GetTriggerUnit())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger ClockTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ClockTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ClockTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(SpawnEffect)
call Preload(DeathEffect)
set DamageExpr = Condition(function DamageTargets)
set ClockTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ShockBlast initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A002' //Raw code of the spell.
private constant real OrbDamage = 100. //Area damage dealt when an orb explodes
private constant real OrbDamageIncrease = 30. //Damage increased per level when an orb explodes.
private constant real MaxDistance = 400. //Distance the orbs travel before exploding.
private constant real MaxDistIncrease = 100. //Distance that the orbs travel before exploding increased per level.
private constant real ExplodeRadius = 50. //Area in which an orb explodes upon contact with enemies and damages them.
private constant real ERadIncrease = 10. //AoE explosion increment per level.
private constant real ExplodeRadiusFinal = 100. //Area damaged when an orb explodes because of reaching maximum distance.
private constant real ERadFinalIncrease = 50. //Increase of AoE on orb explode because of max distance.
private constant real OrbSpeed = 13. //Distance the orbs travel per interval (0.05 seconds).
private constant integer DummyCount = 8 //Amount of lightning orbs created by the spell.
private constant real DummyHeight = 100. //Height of the lightning orbs.
private constant real DummySize = 3. //Size of the lightning orbs.
private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //Effect path of the dummies (lightning orbs, by default).
private constant string DeathEffect = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl" //Effect created when the lightning orbs are destroyed.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = true //Wether the spell affects buildings.
private constant boolean Flying = false //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer ShockTimer = CreateTimer()
private integer ShockActiveTotal = 0
private integer array ShockStructID
private boolexpr DamageExpr
private boolexpr EnumExpr
private unit TempU
private real TempI
endglobals
private function EnumFilter takes nothing returns boolean
return GetWidgetLife(GetFilterUnit()) > 0.405 and IsUnitEnemy(GetFilterUnit(), GetOwningPlayer(TempU))
endfunction
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if GetWidgetLife(Temp) > 0.405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, OrbDamage + OrbDamageIncrease * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
endif
endif
set Temp = null
return false
endfunction
private struct ShockBlast
private unit Caster
private xefx array ShockDummy[DummyCount]
private integer DeadOrbs = 0
private integer AbilityLevel
private real Distance = 0.
private real SourceX
private real SourceY
static method create takes unit source returns ShockBlast
local ShockBlast shock = ShockBlast.allocate()
local integer Count = 0
set shock.Caster = source
set shock.SourceX = GetUnitX(shock.Caster)
set shock.SourceY = GetUnitY(shock.Caster)
set shock.AbilityLevel = GetUnitAbilityLevel(shock.Caster, SpellID)
loop
set Count = Count + 1
set shock.ShockDummy[Count] = xefx.create(shock.SourceX, shock.SourceY, 0.)
set shock.ShockDummy[Count].fxpath = DummyPath
set shock.ShockDummy[Count].z = DummyHeight
set shock.ShockDummy[Count].scale = DummySize
exitwhen Count >= DummyCount
endloop
if ShockActiveTotal == 0 then
call TimerStart(ShockTimer, .05, true, function ShockBlast.Move)
endif
set ShockStructID[ShockActiveTotal] = shock
set ShockActiveTotal = ShockActiveTotal + 1
return shock
endmethod
private static method Move takes nothing returns nothing
local ShockBlast TempStruct
local real PointX
local real PointY
local unit Temp
local integer Count = 0
local integer Count2 = 0
loop
exitwhen Count >= ShockActiveTotal
set TempStruct = ShockStructID[Count]
set TempStruct.Distance = TempStruct.Distance + OrbSpeed
loop
set Count2 = Count2 + 1
if TempStruct.ShockDummy[Count2] != 0 then
set PointX = TempStruct.SourceX + TempStruct.Distance * Cos(Count2 * bj_PI * 2./DummyCount)
set PointY = TempStruct.SourceY + TempStruct.Distance * Sin(Count2 * bj_PI * 2./DummyCount)
set TempStruct.ShockDummy[Count2].x = PointX
set TempStruct.ShockDummy[Count2].y = PointY
set TempU = TempStruct.Caster
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, ExplodeRadius + (ERadIncrease * TempStruct.AbilityLevel - ERadIncrease), EnumExpr)
set TempU = null
set Temp = FirstOfGroup(ENUM_GROUP)
if Temp != null then
call TempStruct.ShockDummy[Count2].destroy()
set TempStruct.ShockDummy[Count2] = 0
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
call GroupClear(ENUM_GROUP)
set TempU = TempStruct.Caster
set TempI = TempStruct.AbilityLevel
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, ExplodeRadius + (ERadIncrease * TempStruct.AbilityLevel - ERadIncrease), DamageExpr)
set TempU = null
set TempStruct.DeadOrbs = TempStruct.DeadOrbs + 1
if TempStruct.DeadOrbs >= 8 then
set ShockActiveTotal = ShockActiveTotal - 1
set ShockStructID[Count] = ShockStructID[ShockActiveTotal]
if ShockActiveTotal == 0 then
call PauseTimer(ShockTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
endif
endif
endif
exitwhen Count2 == DummyCount
endloop
set Count2 = 0
if TempStruct.Distance >= MaxDistance + (MaxDistIncrease * TempStruct.AbilityLevel - MaxDistIncrease) then
loop
set Count2 = Count2 + 1
if TempStruct.ShockDummy[Count2] != 0 then
call DestroyEffect(AddSpecialEffect(DeathEffect, TempStruct.ShockDummy[Count2].x, TempStruct.ShockDummy[Count2].y))
set TempU = TempStruct.Caster
set TempI = TempStruct.AbilityLevel
call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.ShockDummy[Count2].x, TempStruct.ShockDummy[Count2].y, ExplodeRadiusFinal + (ERadFinalIncrease * TempStruct.AbilityLevel - ERadFinalIncrease), Condition(function DamageTargets))
set TempU = null
call TempStruct.ShockDummy[Count2].destroy()
set TempStruct.ShockDummy[Count2] = 0
endif
exitwhen Count2 == DummyCount
endloop
set ShockActiveTotal = ShockActiveTotal - 1
set ShockStructID[Count] = ShockStructID[ShockActiveTotal]
if ShockActiveTotal == 0 then
call PauseTimer(ShockTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
endif
set Count = Count + 1
endloop
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call ShockBlast.create(GetTriggerUnit())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger ShockTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ShockTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ShockTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(DeathEffect)
set DamageExpr = Condition(function DamageTargets)
set EnumExpr = Condition(function EnumFilter)
set ShockTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope LightningBlink initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
//This ability is based on blink ability to ease the triggering part. To change the maximum/minimum distance, go to Object Editor, find the ablility there, and change it.
private constant integer SpellID = 'A003' //Raw code of the spell.
private constant real ElevAngle = bj_DEGTORAD * 50. //The elevation angle between the starting point of the parabola and its maximum height in degrees (converted to radians with the constant).
private constant real DummySize = 3. //The size of the lightning orb.
private constant real OrbSpeed = 22. //Distance traveled by the lightning orb every 0.04 seconds.
private constant real Damage = 50. //Amount of damage done when the lightning orb explodes.
private constant real DamageInc = 30. //Damage increment per level.
private constant real DamageAoE = 100. //Area affected by the lightning ball's explotion.
private constant real DamageAoEInc = 50. //Area increment per level.
private constant integer JumpTotal = 1 //Total times the lightning orb can jump.
private constant integer JumpTotInc = 1 //Increase in the amount of times the orb can jump per level.
private constant real JumpAoE = 500. //The maximum range to where the orb can jump.
private constant real JumpMinD = 300. //Minimum distance between the target point and new target for the orb to jump.
private constant string DummyPath = "Abilities\\Weapons\\FarseerMissile\\FarseerMissile.mdl" //Effect path of the dummy (lightning orb, by default).
private constant string DeathEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Effect created when the orb explodes.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = false //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer OrbTimer = CreateTimer()
private integer OrbActiveTotal = 0
private integer array OrbStructID
private boolexpr MultiExpr
private boolean DoDamage
private unit TempU
private integer TempI
endglobals
private function DistBetweenPointsXY takes real X1, real Y1, real X2, real Y2 returns real
return SquareRoot((X2 - X1)*(X2 - X1) + (Y2 - Y1)*(Y2 - Y1))
endfunction
//--------Credits to moyack and Spec for this function!--------
function ParabolaZ takes real h, real d, real x returns real
return (4 * h / d) * (d - x) * (x / d)
endfunction
//-------------------------------------------------------------
private function RemoveUnits takes nothing returns nothing
call GroupRemoveUnit(ENUM_GROUP, GetEnumUnit())
endfunction
private function EnumFilter takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
if DoDamage then
call UnitDamageTarget(TempU, Temp, Damage + DamageInc * (TempI - 1), true, false, AttackT, DamageT, WeaponT)
else
return true
endif
endif
endif
set Temp = null
return false
endfunction
private struct L_Orb
private unit Caster
private unit Target
private xefx OrbDummy
private group Affected
private integer Jumps = 0
private integer JumpTotal
private integer AbilityLevel
private real Distance = 0.
private real CenterX
private real CenterY
private real Cosine
private real Sine
private real MaxD
static method create takes unit caster, location targetloc returns L_Orb
local L_Orb o = L_Orb.allocate()
local real TargetX = GetLocationX(targetloc)
local real TargetY = GetLocationY(targetloc)
set o.Affected = NewGroup()
set o.Caster = caster
set o.CenterX = GetUnitX(o.Caster)
set o.CenterY = GetUnitY(o.Caster)
set o.OrbDummy = xefx.create(o.CenterX, o.CenterY, 0.)
set o.OrbDummy.fxpath = DummyPath
set o.OrbDummy.scale = DummySize
set o.AbilityLevel = GetUnitAbilityLevel(o.Caster, SpellID)
set o.Cosine = Atan2(TargetY - o.CenterY, TargetX - o.CenterX)
set o.Sine = Sin(o.Cosine)
set o.Cosine = Cos(o.Cosine)
set o.MaxD = DistBetweenPointsXY(o.CenterX, o.CenterY, TargetX, TargetY)
set o.JumpTotal = o.JumpTotal + (JumpTotInc * o.AbilityLevel - JumpTotInc)
if OrbActiveTotal == 0 then
call TimerStart(OrbTimer, 0.04, true, function L_Orb.Move)
endif
set OrbStructID[OrbActiveTotal] = o
set OrbActiveTotal = OrbActiveTotal + 1
return o
endmethod
private static method Move takes nothing returns nothing
local L_Orb TempStruct
local integer Count = 0
local real TempR
local real PointX
local real PointY
local unit Temp
loop
exitwhen Count >= OrbActiveTotal
set TempStruct = OrbStructID[Count]
set TempStruct.Distance = TempStruct.Distance + OrbSpeed
set PointX = TempStruct.CenterX + TempStruct.Distance * TempStruct.Cosine
set PointY = TempStruct.CenterY + TempStruct.Distance * TempStruct.Sine
set TempR = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, PointX, PointY)
set TempStruct.OrbDummy.x = PointX
set TempStruct.OrbDummy.y = PointY
set TempStruct.OrbDummy.z = ParabolaZ(Tan(ElevAngle) * TempStruct.MaxD/2., TempStruct.MaxD, TempR)
if TempR >= TempStruct.MaxD then
call TempStruct.Explode(TempStruct.Caster, PointX, PointY)
if TempStruct.Jumps >= TempStruct.JumpTotal then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
else
set DoDamage = false
set TempU = TempStruct.Caster
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, JumpAoE, MultiExpr)
set TempU = null
call GroupRemoveUnit(ENUM_GROUP, TempStruct.Target)
call GroupRefresh(TempStruct.Affected)
call ForGroup(TempStruct.Affected, function RemoveUnits)
loop
set Temp = FirstOfGroup(ENUM_GROUP)
if Temp == null then
call TempStruct.OrbDummy.destroy()
set OrbActiveTotal = OrbActiveTotal - 1
set OrbStructID[Count] = OrbStructID[OrbActiveTotal]
if OrbActiveTotal == 0 then
call PauseTimer(OrbTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
exitwhen true
elseif DistBetweenPointsXY(TempStruct.OrbDummy.x, TempStruct.OrbDummy.y, GetUnitX(Temp), GetUnitY(Temp)) >= JumpMinD then
call GroupAddUnit(TempStruct.Affected, Temp)
set TempStruct.CenterX = TempStruct.OrbDummy.x
set TempStruct.CenterY = TempStruct.OrbDummy.y
set TempStruct.Distance = 0.
set TempStruct.Cosine = Atan2(GetUnitY(Temp) - TempStruct.CenterY, GetUnitX(Temp) - TempStruct.CenterX)
set TempStruct.Sine = Sin(TempStruct.Cosine)
set TempStruct.Cosine = Cos(TempStruct.Cosine)
set TempStruct.MaxD = DistBetweenPointsXY(TempStruct.CenterX, TempStruct.CenterY, GetUnitX(Temp), GetUnitY(Temp))
set TempStruct.Jumps = TempStruct.Jumps + 1
exitwhen true
else
call GroupRemoveUnit(ENUM_GROUP, Temp)
endif
endloop
endif
endif
set Count = Count + 1
endloop
endmethod
private method Explode takes unit caster, real locx, real locy returns nothing
local unit Temp
local real PointX
local real PointY
local real Distance = 0
local integer Count = 0
local integer Count2 = 0
call DestroyEffect(AddSpecialEffect(DeathEffect, locx, locy))
loop
set Count = Count + 1
set Distance = Distance + 70.
loop
set Count2 = Count2 + 1
set PointX = locx + Distance * Cos(Count2 * bj_PI/4.)
set PointY = locy + Distance * Sin(Count2 * bj_PI/4.)
call DestroyEffect(AddSpecialEffect(DeathEffect, PointX, PointY))
exitwhen Count2 == 8
endloop
set Count2 = 0
exitwhen Count == 3
endloop
set DoDamage = true
set TempU = this.Caster
set TempI = this.AbilityLevel
call GroupEnumUnitsInRange(ENUM_GROUP, locx, locy, DamageAoE + (DamageAoEInc * this.AbilityLevel - DamageAoEInc), MultiExpr)
set TempU = null
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
set .Target = null
call ReleaseGroup(.Affected)
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call L_Orb.create(GetTriggerUnit(), GetSpellTargetLoc())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger L_OrbTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(L_OrbTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(L_OrbTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(DeathEffect)
set MultiExpr = Condition(function EnumFilter)
set L_OrbTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ShadowCircle initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A005' //Raw code of the spell
private constant real AoE = 300. //Area affected by the shadow circle. (Remember to change this in Object Editor for aesthetic purposes).
private constant real AoEInc = 100. //Area increment per level. ---------------^
private constant real EffectAoE = 150. //Area affected by the shadow effect thingies (or whatever effect you choose).
private constant real EffectDamage = 8. //Damage done by the spiral's shadow effects every 0.05 seconds. :O
private constant real TotalDamage = 200. //Damage dealt to units inside the circle over the spell's duration.
private constant real TotDamageInc = 100. //Damage increase per level.
private constant real Duration = 5. //Duration of the spell in seconds after the circle is spawned.
private constant real DurationInc = 1.5 //Duration increased per level.
private constant real SpiralInterval = 0.05 //Period of time in which spiral effects are spawned.
private constant real CircleInterval = 0.2 //Period of time in which circle effects are spawned.
private constant string Effect = "Abilities\\Spells\\Undead\\AnimateDead\\AnimateDeadTarget.mdl" //Effects of the spiral and the circle.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = true //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer ShadowSpiralTimer = CreateTimer()
private timer ShadowExeTimer = CreateTimer()
private integer ShadowActiveTotal = 0
private integer array ShadowStructID
private boolexpr DamageExpr
private unit TempU
private real TempR
private constant real PeriodicDist = 200. * SpiralInterval
endglobals
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) and GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, TempR, true, false, AttackT, DamageT, WeaponT)
endif
endif
set Temp = null
return false
endfunction
private struct Shadow
private unit Caster
private integer Executions = 0
private integer AbilityLevel
private boolean Spiral = true
private real Distance = 0.
private real Rotation1 = 0.
private real CenterX
private real CenterY
private real Rotation2
private real Rotation3
private real Rotation4
private real Rotation5
private real TotalExe
private real PeriodicDamage
static method create takes unit caster, location targetloc returns Shadow
local Shadow s = Shadow.allocate()
set s.Caster = caster
set s.CenterX = GetLocationX(targetloc)
set s.CenterY = GetLocationY(targetloc)
set s.AbilityLevel = GetUnitAbilityLevel(caster, SpellID)
set s.TotalExe = (Duration + (DurationInc * s.AbilityLevel - DurationInc))/.2
set s.PeriodicDamage = (TotalDamage + (DurationInc * s.AbilityLevel - TotDamageInc))/s.TotalExe
if ShadowActiveTotal == 0 then
call TimerStart(ShadowSpiralTimer, SpiralInterval, true, function Shadow.InitSpiral)
call TimerStart(ShadowExeTimer, CircleInterval, true, function Shadow.Periodic)
endif
set ShadowStructID[ShadowActiveTotal] = s
set ShadowActiveTotal = ShadowActiveTotal + 1
return s
endmethod
private static method InitSpiral takes nothing returns nothing
local Shadow TempStruct
local real PointX
local real PointY
local integer Count = 0
local unit Temp
loop
exitwhen Count >= ShadowActiveTotal
set TempStruct = ShadowStructID[Count]
if TempStruct.Spiral then
set TempStruct.Rotation1 = TempStruct.Rotation1 + 15. * bj_DEGTORAD
set TempStruct.Distance = TempStruct.Distance + PeriodicDist
set PointX = TempStruct.CenterX + TempStruct.Distance * Cos(TempStruct.Rotation1)
set PointY = TempStruct.CenterY + TempStruct.Distance * Sin(TempStruct.Rotation1)
call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
set TempU = TempStruct.Caster
set TempR = EffectDamage
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectAoE, DamageExpr)
set TempU = null
if TempStruct.Distance >= AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc) then
set TempStruct.Spiral = false
set TempStruct.Rotation1 = 72. * bj_DEGTORAD
set TempStruct.Rotation2 = 144. * bj_DEGTORAD
set TempStruct.Rotation3 = 216. * bj_DEGTORAD
set TempStruct.Rotation4 = 288. * bj_DEGTORAD
set TempStruct.Rotation5 = 360. * bj_DEGTORAD
endif
endif
set Count = Count + 1
endloop
endmethod
//! textmacro ShadowEffects takes INTEGER
set TempStruct.Rotation$INTEGER$ = TempStruct.Rotation$INTEGER$ + 10. * bj_DEGTORAD
set PointX = TempStruct.CenterX + (AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc)) * Cos(TempStruct.Rotation$INTEGER$)
set PointY = TempStruct.CenterY + (AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc)) * Sin(TempStruct.Rotation$INTEGER$)
call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
//! endtextmacro
private static method Periodic takes nothing returns nothing
local Shadow TempStruct
local real PointX
local real PointY
local integer Count = 0
local integer Count2 = 0
local unit Temp
loop
exitwhen Count >= ShadowActiveTotal
set TempStruct = ShadowStructID[Count]
if TempStruct.Spiral == false then
set TempStruct.Executions = TempStruct.Executions + 1
//! runtextmacro ShadowEffects("1")
//! runtextmacro ShadowEffects("2")
//! runtextmacro ShadowEffects("3")
//! runtextmacro ShadowEffects("4")
//! runtextmacro ShadowEffects("5")
set TempU = TempStruct.Caster
set TempR = TempStruct.PeriodicDamage
call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.CenterX, TempStruct.CenterY, AoE + (AoEInc * TempStruct.AbilityLevel - AoEInc), DamageExpr)
set TempU = null
if TempStruct.Executions >= TempStruct.TotalExe then
set ShadowActiveTotal = ShadowActiveTotal - 1
set ShadowStructID[Count] = ShadowStructID[ShadowActiveTotal]
if ShadowActiveTotal == 0 then
call PauseTimer(ShadowSpiralTimer)
call PauseTimer(ShadowExeTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
endif
endif
set Count = Count + 1
endloop
endmethod
method onDestroy takes nothing returns nothing
set .Caster = null
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call Shadow.create(GetTriggerUnit(), GetSpellTargetLoc())
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger ShadowTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ShadowTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ShadowTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(Effect)
set DamageExpr = Condition(function DamageTargets)
set ShadowTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope Thunderstorm initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A000' //Raw code of the spell.
private constant real SpellDamage = 50. //Damage dealt by each lightning bolt.
private constant real DamageInc = 50. //Damage increased per level of the spell.
private constant real MaxRad = 650. //Maximum range at which lightning bolts spawn.
private constant real MaxRadInc = 50. //Increase of maximum radius per level.
private constant real DmgRadius = 200. //Radius affected by each lightning bolt (damage and knockback area).
private constant real DmgRadInc = 0. //Radius increase per level of the spell.
//=============================== Knockback section =================================
//Credits to Berb for his knockback library!
private constant boolean DoKnockback = true //Wether to knockback units hit by lightning bolts or not (if false, values in knockback section don't have any effect).
private constant real KnockDist = 150. //The distance units are knockbacked.
private constant real KnDistInc = 50. //Knockback distance increased per level.
private constant real KnockTime = .7 //Time it takes to complete the knockback on each unit.
private constant string KnockEffect1 = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl" //Individual effect spawn on knockback start.
private constant string KnockEffectP = "" //Effect spawned every 8 (or whatever the frequency is) executions of the periodic function, which runs every 0.04 seconds.
private constant integer EffectFrequency = 8 //Modifies the frequency in which the periodic effect spawns.
//===================================================================================
private constant integer MinBolts = 4 //Minimum number of lightning bolts spawned.
private constant integer MinBoltsInc = 1 //Minimum number of lightning bolts increased per level.
private constant integer MaxBolts = 8 //Maximum number of lightning bolts spawned.
private constant integer MaxBoltsInc = 1 //Maximum number of lightning bolts increased per level.
private constant real SpawnInterval = .1 //Time amount between the creation of each lightning bolt in seconds.
private constant real Pause = 1. //Time amount between the warning effects placement and the actual spawning of lightning bolts in seconds.
private constant string PreEffect = "Abilities\\Spells\\NightElf\\TrueshotAura\\TrueshotAura.mdl" //"Warning" effect placed before the lightning hits.
private constant string Effect1 = "Abilities\\Spells\\Other\\Monsoon\\MonsoonBoltTarget.mdl" //Effect spawned when lightning bolt hits (this is the actual lightning bolt).
private constant string Effect2 = "Abilities\\Spells\\Human\\ThunderClap\\ThunderClapCaster.mdl" //Extra effect spawned when lightning bolt hits.
private constant attacktype AtkType = ATTACK_TYPE_NORMAL //Attack type of the spell's damage.
private constant damagetype DmgType = DAMAGE_TYPE_NORMAL //Damage type of the spell's damage.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = true //Wether the spell affects buildings.
private constant boolean Flying = true //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private boolexpr DamageExpr
private unit TempU
private integer TempI
private real TempR1
private real TempR2
endglobals
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if GetWidgetLife(Temp) > .405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, SpellDamage + DamageInc * TempI, true, false, AtkType, DmgType, WeaponT)
if DoKnockback and (IsUnitType(Temp, UNIT_TYPE_STRUCTURE)) == false then
call Knockback.create(Temp, Atan2(GetUnitY(Temp) - TempR2, GetUnitX(Temp) - TempR1), KnockDist, KnockTime, KnockEffect1, KnockEffectP, EffectFrequency)
endif
endif
endif
set Temp = null
return false
endfunction
private function Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local xefx array TempE
local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
local integer LightningNumber = GetRandomInt(MinBolts + MinBoltsInc * (AbiLevel - 1), MaxBolts + MaxBoltsInc * (AbiLevel - 1))
local integer Count = 0
local real TempA
local real TempD
local real PointX
local real PointY
loop
set Count = Count + 1
set TempA = GetRandomReal(0., 2. * bj_PI)
set TempD = SquareRoot(GetRandomReal(0., 1.)) * (MaxRad + MaxRadInc * (AbiLevel - 1))
set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
set TempE[Count] = xefx.create(PointX, PointY, 0.)
set TempE[Count].fxpath = PreEffect
exitwhen Count == LightningNumber
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval) //I know what you're thinking - "Ughh, waits? What the @#$! is wrong with you?" Hey... at least it's not GUI, so it's still MUI.
endif
endloop
set Count = 0
call TriggerSleepAction(Pause)
loop
set Count = Count + 1
set PointX = TempE[Count].x
set PointY = TempE[Count].y
call DestroyEffect(AddSpecialEffect(Effect1, PointX, PointY))
call DestroyEffect(AddSpecialEffect(Effect2, PointX, PointY))
set TempU = Caster
set TempI = AbiLevel - 1
static if DoKnockback then
set TempR1 = PointX
set TempR2 = PointY
endif
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, DmgRadius + DmgRadInc * (AbiLevel - 1), DamageExpr)
set TempU = null
call TempE[Count].destroy()
exitwhen Count == LightningNumber
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval)
endif
endloop
set Caster = null
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger StormTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(StormTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddAction(StormTrig, function Actions)
call TriggerAddCondition(StormTrig, Condition(function Conditions))
call XE_PreloadAbility(SpellID)
call Preload(KnockEffect1)
call Preload(KnockEffectP)
call Preload(PreEffect)
call Preload(Effect1)
call Preload(Effect2)
set DamageExpr = Condition(function DamageTargets)
set StormTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope StarfallFrenzy initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A004' //Raw code of the spell.
private constant real TotalDmg = 150. //Total damage dealt by the spell.
private constant real TotalDmgInc = 100. //Total damage increase per level.
private constant real Rad = 250. //Radius of the circular region affected by the spell.
private constant real RadInc = 50. //Radius increment per level.
private constant real Duration = 3. //Duration of the spell.
private constant real DurationInc = .5 //Duration of the spell increased per level.
private constant real DamageDelay = .7 //Delay in seconds before the spell starts damaging units (set so the spell damages units only after starfall effects hit the ground).
private constant string Effect = "Abilities\\Spells\\NightElf\\Starfall\\StarfallTarget.mdl" //Effect of the spell.
private constant attacktype AtkType = ATTACK_TYPE_NORMAL //Attack type of the spell's damage.
private constant damagetype DmgType = DAMAGE_TYPE_NORMAL //Damage type of the spell's damage.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = true //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private timer StarTimer = CreateTimer()
private integer StarActiveTotal = 0
private integer array StarStructID
private boolexpr DamageExpr
private unit TempU
private real TempR
endglobals
private function DamageTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local boolean B1
local boolean B2
local boolean B3
if GetWidgetLife(Temp) > .405 and IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
call UnitDamageTarget(TempU, Temp, TempR, true, false, AtkType, DmgType, WeaponT)
endif
endif
set Temp = null
return false
endfunction
private struct Starfall
private unit Caster
private integer AbiLevel
private boolean Damage
private real ElapsedTime = 0.
private real CenterX
private real CenterY
private real PeriodicDmg
private real Radius
private real TotalTime
static method create takes unit caster, real targetx, real targety returns Starfall
local Starfall s = Starfall.allocate()
set s.Caster = caster
set s.CenterX = targetx
set s.CenterY = targety
set s.AbiLevel = GetUnitAbilityLevel(s.Caster, SpellID)
set s.Radius = Rad + RadInc * (s.AbiLevel - 1)
set s.TotalTime = Duration + DurationInc * (s.AbiLevel - 1)
set s.Damage = false
set s.PeriodicDmg = (TotalDmg + TotalDmgInc * (s.AbiLevel - 1))*0.04/s.TotalTime
if StarActiveTotal == 0 then
call TimerStart(StarTimer, .04, true, function Starfall.Periodic)
endif
set StarStructID[StarActiveTotal] = s
set StarActiveTotal = StarActiveTotal + 1
return s
endmethod
private static method Periodic takes nothing returns nothing
local Starfall TempStruct
local real TempA
local real TempD
local real PointX
local real PointY
local integer Count = 0
loop
exitwhen Count >= StarActiveTotal
set TempStruct = StarStructID[Count]
set TempStruct.ElapsedTime = TempStruct.ElapsedTime + .04
if TempStruct.ElapsedTime >= DamageDelay then
set TempStruct.Damage = true
endif
if TempStruct.Damage then
set TempU = TempStruct.Caster
set TempR = TempStruct.PeriodicDmg
call GroupEnumUnitsInRange(ENUM_GROUP, TempStruct.CenterX, TempStruct.CenterY, TempStruct.Radius, DamageExpr)
endif
set TempA = GetRandomReal(0., 2. * bj_PI)
set TempD = SquareRoot(GetRandomReal(0., 1.)) * TempStruct.Radius
set PointX = TempStruct.CenterX + TempD * Cos(TempA)
set PointY = TempStruct.CenterY + TempD * Sin(TempA)
call DestroyEffect(AddSpecialEffect(Effect, PointX, PointY))
if TempStruct.ElapsedTime >= TempStruct.TotalTime then
set StarActiveTotal = StarActiveTotal - 1
set StarStructID[Count] = StarStructID[StarActiveTotal]
if StarActiveTotal == 0 then
call PauseTimer(StarTimer)
endif
set Count = Count - 1
call TempStruct.destroy()
endif
set Count = Count + 1
endloop
endmethod
private method onDestroy takes nothing returns nothing
set .Caster = null
endmethod
endstruct
private function ConditionsActions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
call Starfall.create(GetTriggerUnit(), GetLocationX(GetSpellTargetLoc()), GetLocationY(GetSpellTargetLoc()))
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger StarTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(StarTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(StarTrig, Condition(function ConditionsActions))
call XE_PreloadAbility(SpellID)
call Preload(Effect)
set DamageExpr = Condition(function DamageTargets)
set StarTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ShadowCall initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A007' //Raw code of the spell.
private constant integer SummonID = 'h003' //Raw code of the unit to be summoned by the spell.
private constant integer SummonQty = 2 //Amount of units summoned at level 1.
private constant integer QtyIncrease = 1 //Amount of units summoned increased per level.
private constant real Radius = 600. //Radius around the hero of the circular area in which units can be spawned.
private constant real Collision = 50. //The distance the missile has to be at from the target point in order to explode.
private constant real MissileSpeed = 400. //Distance traveled per second by the missiles.
private constant real SpawnInterval = .1 //Amount of time between the spawning of each missile.
private constant real MissileHeight = 64. //Height at which missiles fly.
private constant string MissileModel = "Abilities\\Weapons\\ZigguratMissile\\ZigguratMissile.mdl" //Model used for the missiles.
private constant string FinalEffect = "Objects\\Spawnmodels\\Undead\\UCancelDeath\\UCancelDeath.mdl" //Effect created once the missile explodes.
//------------------------------------------------SETUP END------------------------------------------------
private timer MissileTimer = CreateTimer()
private integer MActiveTotal = 0
private integer array MStructID
endglobals
private struct Missile extends xecollider
private player Owner
static method create takes real x, real y, real dir, player owner returns Missile
local Missile m = Missile.allocate(x, y, dir)
set m.Owner = owner
return m
endmethod
private method loopControl takes nothing returns nothing
if SquareRoot((.x - .targetPointX)*(.x - .targetPointX) + (.y - .targetPointY)*(.y - .targetPointY)) <= Collision then
call .terminate()
endif
endmethod
private method onDestroy takes nothing returns nothing
call CreateUnit(.Owner, SummonID, .x, .y, GetRandomReal(0., 360.))
call DestroyEffect(AddSpecialEffect(FinalEffect, .x, .y))
endmethod
endstruct
private function Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local Missile TempStruct
local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
local integer Count = 0
local real TempA
local real TempD
local real PointX
local real PointY
loop
set TempA = GetRandomReal(0., 2. * bj_PI)
set TempD = SquareRoot(GetRandomReal(0., 1.)) * Radius
set PointX = GetUnitX(Caster) + TempD * Cos(TempA)
set PointY = GetUnitY(Caster) + TempD * Sin(TempA)
set TempStruct = Missile.create(GetUnitX(Caster), GetUnitY(Caster), TempA, GetOwningPlayer(Caster))
set TempStruct.fxpath = MissileModel
set TempStruct.z = MissileHeight
set TempStruct.speed = MissileSpeed
set TempStruct.angleSpeed = bj_PI
call TempStruct.setTargetPoint(PointX, PointY)
exitwhen Count + 1 == SummonQty + QtyIncrease * (AbiLevel - 1)
set Count = Count + 1
if SpawnInterval != 0. then
call TriggerSleepAction(SpawnInterval)
endif
endloop
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger ShadowCallTrig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ(ShadowCallTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(ShadowCallTrig, Condition(function Conditions))
call TriggerAddAction(ShadowCallTrig, function Actions)
call XE_PreloadAbility(SpellID)
call Preload(MissileModel)
call Preload(FinalEffect)
set ShadowCallTrig = null
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
scope ElementalDischarge initializer Init
globals
//--------------------------------------------------SETUP--------------------------------------------------
private constant integer SpellID = 'A006' //Raw code of the spell.
private constant integer DummyID = 'h000' //Raw code of the dummy unit.
//The dummy Purge and Entangling Roots spells can be configured in the object editor.
//The damage, duration, and other stats set in the Object Editor in each level are
// the values applied when the effects are used on a unit.
private constant integer PurgeID = 'A008' //Raw code of the "Dummy Purge" spell.
private constant integer RootsID = 'A009' //Raw code of the "Dummy Roots" spell.
private constant real FireChance = 33.34 //Percent chance to summon a fire ring.
private constant real LightningChance = 33.33 //Percent chance to summon a lightning ring.
private constant real NatureChance = 33.33 //Percent chance to summon a nature ring.
//----------------------------------> 100.00 //The sum of these three values must be equal to 100. If not, one of the values will be adjusted.
private constant real EffectDmgRad = 75. //Radius in which the effects that compose the rings deal damage.
private constant real FireDamage = 10. //Instant damage dealt by each little fire effect.
private constant real FireDmgInc = 5. //Instant fire damage increase per level.
private constant real FireDoT = 30. //Damage over time dealt by each fire effect.
private constant real FireDoTInc = 5. //Fire damage over time increase per level.
private constant real FireDoTDur = 5. //Duration of the damage over time in seconds.
private constant real LightningDamage = 20. //Instant damage dealy by each lightning effect.
private constant real LightningDmgInc = 5. //Instant lightning damage increase per level.
private constant real NatureHealing = 30. //Instant healing done by each nature effect.
private constant real NatureHealInc = 5. //Instant nature healing increase per level.
private constant real RingSeparation = 100. //Distance between each elemental ring.
private constant integer RingAmount = 4 //Amount of rings spawned by the spell.
private constant integer RingInc = 1 //Increase in ring amount per level.
private constant integer RingEffects = 6 //Amount of effects that compose the innermost ring.
private constant integer RingEffInc = 4 //Increase in amount of effects per ring (the first ring has 16 effects, the second has 20 effects, etc).
private constant string FireEffect = "Abilities\\Spells\\Human\\MarkOfChaos\\MarkOfChaosTarget.mdl" //Effects that compose the fire rings.
private constant string FireDoTEffect = "Abilities\\Spells\\Other\\BreathOfFire\\BreathOfFireDamage.mdl" //Damage attached to units affected by fire damage over time.
private constant string LightningEffect = "Abilities\\Weapons\\Bolt\\BoltImpact.mdl" //Effects that compose the lightning rings.
private constant string NatureEffect = "Objects\\Spawnmodels\\NightElf\\EntBirthTarget\\EntBirthTarget.mdl" //Effects that compoose the nature rings.
private constant attacktype AttackT = ATTACK_TYPE_NORMAL //Attack type of the spell.
private constant damagetype DamageT = DAMAGE_TYPE_NORMAL //Damage type of the spell.
private constant weapontype WeaponT = WEAPON_TYPE_WHOKNOWS //Weapon type of the spell (never knew what it was, so I always set it to null, hehe).
private constant boolean MagicImmune = false //Wether the spell affects magic immune units.
private constant boolean Building = false //Wether the spell affects buildings.
private constant boolean Flying = false //Wether the spell affects flying units.
//------------------------------------------------SETUP END------------------------------------------------
private constant boolean PercentNotExact = (FireChance + LightningChance + NatureChance) != 100.
private boolexpr AffectExpr
private unit TempU
private integer TempI1
private integer TempI2
private real TempR1
private real TempR2
private real PercentBound1 = FireChance
private real PercentBound2 = FireChance + LightningChance
endglobals
private function AffectTargets takes nothing returns boolean
local unit Temp = GetFilterUnit()
local unit DummyUnit
local boolean B1
local boolean B2
local boolean B3
if GetWidgetLife(Temp) > 0.405 then
set B1 = IsUnitType(Temp, UNIT_TYPE_MAGIC_IMMUNE) == false
set B2 = IsUnitType(Temp, UNIT_TYPE_STRUCTURE) == false
set B3 = IsUnitType(Temp, UNIT_TYPE_FLYING) == false
static if MagicImmune then
set B1 = true
endif
static if Building then
set B2 = true
endif
static if Flying then
set B3 = true
endif
if B1 and B2 and B3 then
if IsUnitEnemy(Temp, GetOwningPlayer(TempU)) then
if TempI1 < 2 then
call UnitDamageTarget(TempU, Temp, TempR1, true, false, AttackT, DamageT, WeaponT)
if TempI1 == 0 then
call DamageOverTimeEx(TempU, Temp, TempR2, FireDoTDur, AttackT, DamageT, FireDoTEffect, "origin")
else
set DummyUnit = CreateUnit(GetOwningPlayer(TempU), DummyID, GetUnitX(TempU), GetUnitY(TempU), 0.)
call UnitApplyTimedLife(DummyUnit, 'BTLF', 1.)
call UnitAddAbility(DummyUnit, PurgeID)
call SetUnitAbilityLevel(DummyUnit, PurgeID, TempI2)
call IssueTargetOrder(DummyUnit, "purge", Temp)
set DummyUnit = null
endif
else
set DummyUnit = CreateUnit(GetOwningPlayer(TempU), DummyID, GetUnitX(TempU), GetUnitY(TempU), 0.)
call UnitApplyTimedLife(DummyUnit, 'BTLF', 1.)
call UnitAddAbility(DummyUnit, RootsID)
call SetUnitAbilityLevel(DummyUnit, RootsID, TempI2)
call IssueTargetOrder(DummyUnit, "entanglingroots", Temp)
set DummyUnit = null
endif
elseif TempI1 == 2 and IsUnitAlly(Temp, GetOwningPlayer(TempU)) then
call SetWidgetLife(Temp, GetWidgetLife(Temp) + TempR1)
endif
endif
endif
set Temp = null
return false
endfunction
private function Actions takes nothing returns nothing
local unit Caster = GetTriggerUnit()
local real SourceX = GetUnitX(Caster)
local real SourceY = GetUnitY(Caster)
local integer AbiLevel = GetUnitAbilityLevel(Caster, SpellID)
local integer Count = 1
local integer Count2 = 1
local real Randomizer
local real PointX
local real PointY
loop
set Randomizer = GetRandomReal(0., 100.)
loop
set PointX = SourceX + Count * RingSeparation * Cos(bj_PI*2./(RingEffects + RingEffInc * (Count - 1)) * Count2)
set PointY = SourceY + Count * RingSeparation * Sin(bj_PI*2./(RingEffects + RingEffInc * (Count - 1)) * Count2)
if Randomizer <= PercentBound1 then
set TempU = Caster
set TempI1 = 0
set TempR1 = FireDamage + FireDmgInc * (AbiLevel - 1)
set TempR2 = FireDoT + FireDoTInc * (AbiLevel - 1)
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, AffectExpr)
set TempU = null
call DestroyEffect(AddSpecialEffect(FireEffect, PointX, PointY))
elseif Randomizer <= PercentBound2 then
set TempU = Caster
set TempI1 = 1
set TempI2 = AbiLevel
set TempR1 = LightningDamage + LightningDmgInc * (AbiLevel - 1)
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, AffectExpr)
set TempU = null
call DestroyEffect(AddSpecialEffect(LightningEffect, PointX, PointY))
else
set TempU = Caster
set TempI1 = 2
set TempI2 = AbiLevel
set TempR1 = NatureHealing + NatureHealInc * (AbiLevel - 1)
call GroupEnumUnitsInRange(ENUM_GROUP, PointX, PointY, EffectDmgRad, AffectExpr)
set TempU = null
call DestroyEffect(AddSpecialEffect(NatureEffect, PointX, PointY))
endif
exitwhen Count2 >= RingEffects + RingEffInc * (Count - 1)
set Count2 = Count2 + 1
endloop
exitwhen Count >= RingAmount + RingInc * (AbiLevel - 1)
set Count = Count + 1
set Count2 = 0
call TriggerSleepAction(.1)
endloop
set Caster = null
endfunction
private function Conditions takes nothing returns boolean
if GetSpellAbilityId() == SpellID then
return true
endif
return false
endfunction
private function Init takes nothing returns nothing
local trigger DischargeTrig = CreateTrigger()
local integer Randomizer
call TriggerRegisterAnyUnitEventBJ(DischargeTrig, EVENT_PLAYER_UNIT_SPELL_EFFECT)
call TriggerAddCondition(DischargeTrig, Condition(function Conditions))
call TriggerAddAction(DischargeTrig, function Actions)
static if PercentNotExact then
set Randomizer = GetRandomInt(1, 3)
if Randomizer == 1 then
set PercentBound1 = 100. - LightningChance - NatureChance
set PercentBound2 = PercentBound1 + LightningChance
elseif Randomizer == 2 then
set PercentBound2 = FireChance + 100. - FireChance - NatureChance
endif
endif
call XE_PreloadAbility(SpellID)
call Preload(FireEffect)
call Preload(FireDoTEffect)
call Preload(LightningEffect)
call Preload(NatureEffect)
set AffectExpr = Condition(function AffectTargets)
set DischargeTrig = null
endfunction
endscope