//TESH.scrollpos=0
//TESH.alwaysfold=0
Name | Type | is_array | initial_value |
//TESH.scrollpos=36
//TESH.alwaysfold=0
library_once TimerUtils initializer redInit
//*********************************************************************
//* TimerUtils (Red flavor)
//* ----------
//*
//* To implement it , create a custom text trigger called TimerUtils
//* and paste the contents of this script there.
//*
//* To copy from a map to another, copy the trigger holding this
//* library to your map.
//*
//* (requires vJass) More scripts: htt://www.wc3campaigns.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* ReleaseTimer(t) : Relese a timer (alt to DestroyTimer)
//* SetTimerData(t,2) : Attach value 2 to timer
//* GetTimerData(t) : Get the timer's value.
//* You can assume a timer's value is 0
//* after NewTimer.
//*
//* Red flavor: Fastest, method in existence for timer attaching,
//* only takes an array lookup, H2I and subtraction.
//* However, all the code in your map requires extra care
//* not to forget to call ReleaseTimer. It also requires
//* to preload a lot of timers at map init, they use
//* memory and handle ids.
//*
//* I recommend you run your map in debug mode the first
//* time after adding it, make sure you can see map init messages
//* if nothing appears, all is done, if an error appears, it might
//* suggest you a value with OFFSET, in that case, update that value
//* if it still does not work after updating (rare), try a bigger
//* OFFSET by 1000 for example. (Sounds hard? Then use blue or purple
//* timerutils that are friendlier though not as fast)
//*
//********************************************************************
//================================================================
globals
private constant integer OFFSET = 0x100000
private constant integer QUANTITY = 256
private constant integer ARRAY_SIZE = 8191 //changing this to a higher value would effectively
//cripple the performance making this thing irrelevant
endglobals
//=================================================================================================
private function H2I takes handle h returns integer
return GetHandleId( h )
endfunction
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
debug if(H2I(t)-OFFSET<0) then
debug call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
debug endif
set data[H2I(t)-OFFSET]=value
endfunction
function GetTimerData takes timer t returns integer
debug if(H2I(t)-OFFSET<0) then
debug call BJDebugMsg("GetTimerData: Wrong handle id, only use GetTimerData on timers created by NewTimer")
debug endif
return data[H2I(t)-OFFSET]
endfunction
//==========================================================================================
globals
private timer array tT
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
endglobals
//==========================================================================================
function NewTimer takes nothing returns timer
if (tN==0) then
//If this happens then the QUANTITY rule has already been broken, try to fix the
// issue, else fail.
debug call BJDebugMsg("NewTimer: Warning, Exceeding TimerUtils_QUANTITY, please increase it for your map, fix your map's timer leaks or switch to blue flavor when applicable")
set tT[0]=CreateTimer()
if (H2I(tT[0])-OFFSET<0) or (H2I(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably switch to the blue flavor or fix timer leaks.")
return null
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],0)
return tT[tN]
endfunction
//==========================================================================================
function ReleaseTimer takes timer t returns nothing
if(t==null) then
debug call BJDebugMsg("Warning: attempt to release a null timer")
return
endif
if (tN==8191) then
debug call BJDebugMsg("Warning: Timer stack is full, destroying timer!!")
//stack is full, the map already has much more troubles than the chance of bug
call DestroyTimer(t)
else
call PauseTimer(t)
if(GetTimerData(t)==HELD) then
debug call BJDebugMsg("Warning: ReleaseTimer: Double free!")
return
endif
call SetTimerData(t,HELD)
set tT[tN]=t
set tN=tN+1
endif
endfunction
private function redInit takes nothing returns nothing
local integer i=0
local integer bestoffset=-1
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set bestoffset=H2I(tT[i])
endif
if (H2I(tT[i])-OFFSET>=ARRAY_SIZE) then
debug call BJDebugMsg("TimerUtils_redInit: Failed a initializing attempt")
debug call BJDebugMsg("The timer limit is "+I2S(i))
debug call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low, to change OFFSET to "+I2S(bestoffset) )
exitwhen true
endif
if (H2I(tT[i])-OFFSET>=0) then
set i=i+1
endif
endloop
set tN=i
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library_once VectorLib
//Done by Anitarf
globals
private location loc = Location(0.0,0.0)
endglobals
struct vector
real x
real y
real z
static method create takes real x, real y, real z returns vector
local vector v = vector.allocate()
set v.x=x
set v.y=y
set v.z=z
return v
endmethod
method getLength takes nothing returns real
return SquareRoot(.x*.x + .y*.y + .z*.z)
endmethod
static method sum takes vector augend, vector addend returns vector
local vector v = vector.allocate()
set v.x = augend.x+addend.x
set v.y = augend.y+addend.y
set v.z = augend.z+addend.z
return v
endmethod
method add takes vector addend returns nothing
set this.x=this.x+addend.x
set this.y=this.y+addend.y
set this.z=this.z+addend.z
endmethod
static method difference takes vector minuend, vector subtrahend returns vector
local vector v = vector.allocate()
set v.x = minuend.x-subtrahend.x
set v.y = minuend.y-subtrahend.y
set v.z = minuend.z-subtrahend.z
return v
endmethod
method subtract takes vector subtrahend returns nothing
set this.x=this.x-subtrahend.x
set this.y=this.y-subtrahend.y
set this.z=this.z-subtrahend.z
endmethod
method scale takes real factor returns nothing
set this.x=this.x*factor
set this.y=this.y*factor
set this.z=this.z*factor
endmethod
method setLength takes real length returns nothing
local real l = SquareRoot(.x*.x + .y*.y + .z*.z)
if l == 0.0 then
debug call BJDebugMsg("Attempted to set the length of a vector with no length!")
return
endif
set l = length/l
set this.x = this.x*l
set this.y = this.y*l
set this.z = this.z*l
endmethod
static method dotProduct takes vector a, vector b returns real
return (a.x*b.x+a.y*b.y+a.z*b.z)
endmethod
static method crossProduct takes vector a, vector b returns vector
local vector v = vector.allocate()
set v.x = a.y*b.z - a.z*b.y
set v.y = a.z*b.x - a.x*b.z
set v.z = a.x*b.y - a.y*b.x
return v
endmethod
static method projectionVector takes vector projected, vector direction returns vector
local vector v = vector.allocate()
local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
if l == 0.0 then
call v.destroy()
debug call BJDebugMsg("Attempted to project onto a vector with no length!")
return null
endif
set l = (projected.x*direction.x+projected.y*direction.y+projected.z*direction.z) / l
set v.x = direction.x*l
set v.y = direction.y*l
set v.z = direction.z*l
return v
endmethod
method projectVector takes vector direction returns nothing
local real l = direction.x*direction.x+direction.y*direction.y+direction.z*direction.z
if l == 0.0 then
debug call BJDebugMsg("Attempted to project onto a vector with no length!")
return
endif
set l = (this.x*direction.x+this.y*direction.y+this.z*direction.z) / l
set this.x = direction.x*l
set this.y = direction.y*l
set this.z = direction.z*l
endmethod
static method projectionPlane takes vector projected, vector normal returns vector
local vector v = vector.allocate()
local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
if l == 0.0 then
call v.destroy()
debug call BJDebugMsg("Attempted to project onto an undefined plane!")
return null
endif
set l = (projected.x*normal.x+projected.y*normal.y+projected.z*normal.z) / l
set v.x = projected.x - normal.x*l
set v.y = projected.y - normal.y*l
set v.z = projected.z - normal.z*l
return v
endmethod
method projectPlane takes vector normal returns nothing
local real l = normal.x*normal.x+normal.y*normal.y+normal.z*normal.z
if l == 0.0 then
debug call BJDebugMsg("Attempted to project onto an undefined plane!")
return
endif
set l = (this.x*normal.x+this.y*normal.y+this.z*normal.z) / l
set this.x = this.x - normal.x*l
set this.y = this.y - normal.y*l
set this.z = this.z - normal.z*l
endmethod
static method getAngle takes vector a, vector b returns real
local real l = SquareRoot(a.x*a.x + a.y*a.y + a.z*a.z)*SquareRoot(b.x*b.x + b.y*b.y + b.z*b.z)
if l == 0 then
debug call BJDebugMsg("Attempted to get angle between vectors with no length!")
return 0.0
endif
return Acos((a.x*b.x+a.y*b.y+a.z*b.z)/l) //angle is returned in radians
endmethod
method rotate takes vector axis, real angle returns nothing //angle is taken in radians
local real xx
local real xy
local real xz
local real yx
local real yy
local real yz
local real zx
local real zy
local real zz
local real al = axis.x*axis.x+axis.y*axis.y+axis.z*axis.z //axis length^2
local real f
local real c = Cos(angle)
local real s = Sin(angle)
if al == 0.0 then
debug call BJDebugMsg("Attempted to project onto a vector with no length!")
return
endif
set f = (this.x*axis.x+this.y*axis.y+this.z*axis.z) / al
set zx = axis.x*f
set zy = axis.y*f
set zz = axis.z*f //axis component of rotated vector
set xx = this.x-zx
set xy = this.y-zy
set xz = this.z-zz //component of vector perpendicular to axis
set al = SquareRoot(al)
set yx = (axis.y*xz - axis.z*xy)/al
set yy = (axis.z*xx - axis.x*xz)/al //y same length as x by using cross product and dividing with axis length
set yz = (axis.x*xy - axis.y*xx)/al //x,y - coordinate system in which we rotate
set this.x=xx*c+yx*s+zx
set this.y=xy*c+yy*s+zy
set this.z=xz*c+yz*s+zz
endmethod
static method createTerrainPoint takes real x, real y returns vector
local vector v = vector.allocate()
call MoveLocation(loc,x,y)
set v.x=x
set v.y=y
set v.z=GetLocationZ(loc)
return v
endmethod
method getTerrainPoint takes real x, real y returns nothing
call MoveLocation(loc,x,y)
set this.x=x
set this.y=y
set this.z=GetLocationZ(loc)
endmethod
static method createTerrainNormal takes real x, real y, real sampleRadius returns vector
local vector v = vector.allocate()
local real z1
local real z2
local real z3
local real z4
call MoveLocation(loc, x-sampleRadius, y)
set z1=GetLocationZ(loc)
call MoveLocation(loc, x+sampleRadius, y)
set z2=GetLocationZ(loc)
call MoveLocation(loc, x, y-sampleRadius)
set z3=GetLocationZ(loc)
call MoveLocation(loc, x, y+sampleRadius)
set z4=GetLocationZ(loc)
set sampleRadius=2*sampleRadius
set v.x = (z1-z2)*sampleRadius
set v.y = (z3-z4)*sampleRadius
set v.z = sampleRadius*sampleRadius
return v
endmethod
method getTerrainNormal takes real x, real y, real sampleRadius returns nothing
local real z1
local real z2
local real z3
local real z4
call MoveLocation(loc, x-sampleRadius, y)
set z1=GetLocationZ(loc)
call MoveLocation(loc, x+sampleRadius, y)
set z2=GetLocationZ(loc)
call MoveLocation(loc, x, y-sampleRadius)
set z3=GetLocationZ(loc)
call MoveLocation(loc, x, y+sampleRadius)
set z4=GetLocationZ(loc)
set sampleRadius=2*sampleRadius
set this.x = (z1-z2)*sampleRadius
set this.y = (z3-z4)*sampleRadius
set this.z = sampleRadius*sampleRadius
endmethod
method isInCylinder takes vector cylinderOrigin, vector cylinderHeight, real cylinderRadius returns boolean
local real l
local real x = this.x-cylinderOrigin.x
local real y = this.y-cylinderOrigin.y
local real z = this.z-cylinderOrigin.z
if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z < 0.0 then //point below cylinder
return false
endif
set x = x-cylinderHeight.x
set y = y-cylinderHeight.y
set z = z-cylinderHeight.z
if x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z > 0.0 then //point above cylinder
return false
endif
set l = cylinderHeight.x*cylinderHeight.x+cylinderHeight.y*cylinderHeight.y+cylinderHeight.z*cylinderHeight.z
if l == 0.0 then
debug call BJDebugMsg("Cylinder with no height!")
return false
endif
set l = (x*cylinderHeight.x+y*cylinderHeight.y+z*cylinderHeight.z) / l
set x = x - cylinderHeight.x*l
set y = y - cylinderHeight.y*l
set z = z - cylinderHeight.z*l
if x*x+y*y+z*z > cylinderRadius*cylinderRadius then //point outside cylinder
return false
endif
return true
endmethod
method isInCone takes vector coneOrigin, vector coneHeight, real coneRadius returns boolean
local real l
local real x = this.x-coneOrigin.x
local real y = this.y-coneOrigin.y
local real z = this.z-coneOrigin.z
if x*coneHeight.x+y*coneHeight.y+z*coneHeight.z < 0.0 then //point below cone
return false
endif
set l = coneHeight.x*coneHeight.x+coneHeight.y*coneHeight.y+coneHeight.z*coneHeight.z
if l == 0.0 then
debug call BJDebugMsg("cone with no height!")
return false
endif
set l = (x*coneHeight.x+y*coneHeight.y+z*coneHeight.z) / l
set x = x - coneHeight.x*l
set y = y - coneHeight.y*l
set z = z - coneHeight.z*l
if SquareRoot(x*x+y*y+z*z) > coneRadius*(1.0-l) then //point outside cone
return false
endif
return true
endmethod
method isInSphere takes vector sphereOrigin, real sphereRadius returns boolean
if sphereRadius*sphereRadius < ((this.x-sphereOrigin.x)*(this.x-sphereOrigin.x)+(this.y-sphereOrigin.y)*(this.y-sphereOrigin.y)+(this.z-sphereOrigin.z)*(this.z-sphereOrigin.z)) then
return false
endif
return true
endmethod
endstruct
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library Heights
//Meins
globals
private location loc = Location( 0.00, 0.00 )
endglobals
function GetFloorHeight takes real x, real y returns real
call MoveLocation( loc, x, y )
return GetLocationZ( loc )
endfunction
function GetUnitZ takes unit whichUnit returns real
local real z = ( GetFloorHeight( GetUnitX( whichUnit ), GetUnitY( whichUnit ) ) + GetUnitFlyHeight( whichUnit ) )
set whichUnit = null
return z
endfunction
function SetUnitZ takes unit whichUnit, real z returns nothing
local boolean whichUnitHasNotAmrf = ( GetUnitAbilityLevel( whichUnit, 'Amrf' ) <= 0 ) and IsUnitType(whichUnit, UNIT_TYPE_FLYING) == false
if ( whichUnitHasNotAmrf ) then
call UnitAddAbility( whichUnit, 'Amrf' )
endif
call SetUnitFlyHeight( whichUnit, z - GetFloorHeight( GetUnitX( whichUnit ), GetUnitY( whichUnit ) ), 0.00 )
if ( whichUnitHasNotAmrf ) then
call UnitRemoveAbility( whichUnit, 'Amrf' )
endif
set whichUnit = null
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
library IsTerrainWalkable initializer Init
//*****************************************************************
//* IsTerrainWalkable
//*
//* rewritten in vJass by: Anitarf
//* original implementation: Vexorian
//*
//* A function for checking if a point is pathable for ground
//* units (it does so by attempting to move an item there and
//* checking where it ended up), typically used to stop sliding
//* units before they end up stuck in trees. If the point is not
//* pathable, the function will also determine the nearest point
//* that is (the point where the item ends up).
//*****************************************************************
globals
// this value is how far from a point the item may end up for the point to be considered pathable
private constant real MAX_RANGE = 10.0
// the following two variables are set to the position of the item after each pathing check
// that way, if a point isn't pathable, these will be the coordinates of the nearest point that is
public real X = 0.0
public real Y = 0.0
// END OF CALIBRATION SECTION
// ================================================================
private rect r
private item check
private item array hidden
private integer hiddenMax = 0
endglobals
private function Init takes nothing returns nothing
set check = CreateItem('ciri',0,0)
call SetItemVisible(check,false)
set r = Rect(0.0,0.0,128.0,128.0)
endfunction
private function HideBothersomeItem takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set hidden[hiddenMax]=GetEnumItem()
call SetItemVisible(hidden[hiddenMax],false)
set hiddenMax=hiddenMax+1
endif
endfunction
// ================================================================
function IsTerrainWalkable takes real x, real y returns boolean
// first, hide any items in the area so they don't get in the way of our item
call MoveRectTo(r, x,y)
call EnumItemsInRect(r,null,function HideBothersomeItem)
// try to move the check item and get it's coordinates
call SetItemPosition(check,x,y)//this unhides the item...
set X = GetItemX(check)
set Y = GetItemY(check)
call SetItemVisible(check,false)//...so we must hide it again
// before returning, unhide any items that got hidden at the start
loop
exitwhen hiddenMax<=0
set hiddenMax=hiddenMax-1
call SetItemVisible(hidden[hiddenMax],true)
set hidden[hiddenMax]=null
endloop
// return pathability status
return (x-X)*(x-X)+(y-Y)*(y-Y) < MAX_RANGE*MAX_RANGE
endfunction
endlibrary
//TESH.scrollpos=357
//TESH.alwaysfold=0
scope ArcaniaZero initializer init
//================================================================================================
// Arcania Zero
// by
// cedi
//
// Requires:
// TimerUtils by Vexorian
// Vector by Anitarf
// Heights by cedi
// IsTerrainWalkable by Anitarf
//================================================================================================
private keyword Arcania //System
private keyword Grabbed //System
private keyword ArcKB //System
globals
private constant integer SPELL_ID = 'A000'
//Raw Code of the Spell
private constant integer BUFF_ID = 'B000'
//Raw Code of the Buff, which is applied by the spell
private constant integer MAX_TARGETS = 10
//The max amount of units sucked at the same time
private constant real RED = 1.00
//Amount of Red color of the lightning ( Between 1 - 0 )
private constant real GREEN = 1.00
//Amount of Green color of the lightning ( Between 1 - 0 )
private constant real BLUE = 1.00
//Amount of Blue color of the lightning ( Between 1 - 0 )
private constant real ALPHA = 1.00
//Amount of Alpha color of the lightning ( Between 1 - 0 )
private constant real TIMER_INTERVAL = 0.035
//Interval of the moving should be around 0.035
private constant real PICK_INTERVAL = 0.10
//Each x will the spell search for new targets. Don't use a to small number
private constant real DRAG_SFX_INTERVAL = 0.10
//Each x the System will create a drag effect
private constant real KB_SFX_INTERVAL = 0.20
//Each x the System will create a knockback effect
private constant real COLLISION_RANGE = 100.00
//The range the suckeds unit have to reach to be knocked away
private constant real ADD_HEIGHT = 20.00
//Bonus Height that the lightning hits the middle of the sucked units
private constant real START_SPEED = 300.00
//The sucking speed in ms
private constant real ACCELERATION = 20.00
//The acceleration of the sucking speed in ms
private constant real INSTA_KILL_SPEED = 2000.00
//The needed speed to insta kill a unit in ms
private constant real START_KB_SPEED = 600.00
//The start speed of the knockback in ms
private constant real KB_SLOW_DOWN = 60.00
//Friction in ms
private constant real KB_MIN_SPEED = 20.00
//The knockback ends when the knockback speed is equal or smaller then this in ms
private constant string CANCEL_SFX = "Abilities\\Spells\\Human\\DispelMagic\\DispelMagicTarget.mdl"
//The effect when the connection is canceled
private constant string COLLISION_SFX = "Abilities\\Spells\\NightElf\\Blink\\BlinkCaster.mdl"
//The effect when the unit collides
private constant string DRAG_SFX = "Abilities\\Spells\\Undead\\ReplenishMana\\ReplenishManaCaster.mdl"
//The effect when the units are sliding
private constant string GRAB_SFX = "Abilities\\Spells\\Undead\\AbsorbMana\\AbsorbManaBirthMissile.mdl"
//The effect attached to sucked units
private constant string LIGHTNING_STRING = "DRAM"
//String code of the lightning
private constant string KB_SFX = "Objects\\Spawnmodels\\Undead\\ImpaleTargetDust\\ImpaleTargetDust.mdl"
//Slide effect on the knockback
private constant string INSTA_KILL_SFX = "Objects\\Spawnmodels\\Human\\HumanLargeDeathExplode\\HumanLargeDeathExplode.mdl"
//Damage dealt only to enemies?
private constant boolean DAMAGE_ONLY_ENEMY = true
//The Attacktyp which is used to deal damage.
private constant attacktype ATTACK = ATTACK_TYPE_MAGIC
//System Variables
private Arcania TEMPDATA
private ArcKB TEMPKB
private integer TEMPINT = 0
private real REAL_START_SPEED = START_SPEED * TIMER_INTERVAL
private real REAL_ACCELERATION = ACCELERATION * TIMER_INTERVAL
private real REAL_INSTA_KILL_SPEED = INSTA_KILL_SPEED * TIMER_INTERVAL
private real REAL_START_KB_SPEED = START_KB_SPEED * TIMER_INTERVAL
private real REAL_KB_SLOW_DOWN = KB_SLOW_DOWN * TIMER_INTERVAL
private real REAL_KB_MIN_SPEED = KB_MIN_SPEED * TIMER_INTERVAL
private real TEMPREAL = 0.00
private vector TEMPVEC
private group TEMPGROUP = CreateGroup()
private real array DURATION
private real array AOE
private real array DAMAGE_COLLISION
private real array DAMAGE
private real array MANALOSE_COLLISION
private real array MANALOSE
endglobals
//Duration of the ability, should be the same for the spell
private function SET_DURATION takes nothing returns nothing
set DURATION[1] = 5.00
set DURATION[2] = 6.00
set DURATION[3] = 7.00
set DURATION[4] = 8.00
set DURATION[5] = 9.00
endfunction
//Suck AoE
private function SET_AOE takes nothing returns nothing
set AOE[1] = 500.00
set AOE[2] = 600.00
set AOE[3] = 700.00
set AOE[4] = 800.00
set AOE[5] = 900.00
endfunction
//Damage on collision
private function SET_DAMAGE_COLLISION takes nothing returns nothing
set DAMAGE_COLLISION[1] = 50.00
set DAMAGE_COLLISION[2] = 60.00
set DAMAGE_COLLISION[3] = 70.00
set DAMAGE_COLLISION[4] = 80.00
set DAMAGE_COLLISION[5] = 90.00
endfunction
//Damage per second ( when the unit is sucked )
private function SET_DAMAGE takes nothing returns nothing
set DAMAGE[1] = 50.00 * TIMER_INTERVAL
set DAMAGE[2] = 60.00 * TIMER_INTERVAL
set DAMAGE[3] = 70.00 * TIMER_INTERVAL
set DAMAGE[4] = 80.00 * TIMER_INTERVAL
set DAMAGE[5] = 90.00 * TIMER_INTERVAL
endfunction
//Mana lose on collision
private function SET_MANALOSE_COLLISION takes nothing returns nothing
set MANALOSE_COLLISION[1] = 50.00
set MANALOSE_COLLISION[2] = 60.00
set MANALOSE_COLLISION[3] = 70.00
set MANALOSE_COLLISION[4] = 80.00
set MANALOSE_COLLISION[5] = 90.00
endfunction
//Mana lose on suck, per second
private function SET_MANA takes nothing returns nothing
set MANALOSE[1] = 50.00 * TIMER_INTERVAL
set MANALOSE[2] = 60.00 * TIMER_INTERVAL
set MANALOSE[3] = 70.00 * TIMER_INTERVAL
set MANALOSE[4] = 80.00 * TIMER_INTERVAL
set MANALOSE[5] = 90.00 * TIMER_INTERVAL
endfunction
//================================================================================================
// SYSTEM
//================================================================================================
private function KBOuterControl takes nothing returns nothing
set TEMPKB = GetTimerData( GetExpiredTimer() )
call TEMPKB.InnerControl()
endfunction
private function GetNewAngle takes real angle returns real
if(angle<=45 and angle>=-45) then
set angle=angle*-1
elseif(angle>45 and angle<=135) then
set angle=180-angle
elseif(angle>135 and angle>=-135) then
set angle=360-angle
else
set angle=-180-angle
endif
return angle
endfunction
private struct ArcKB
unit target = null
Arcania data = 0
real cos = 0.00
real sin = 0.00
real speed = REAL_START_KB_SPEED
real interval = KB_SFX_INTERVAL
timer time = null
method onDestroy takes nothing returns nothing
call ReleaseTimer( .time )
if .data.mytargets != null and .data != 0 then
call GroupRemoveUnit( .data.mytargets, .target )
endif
endmethod
method InnerControl takes nothing returns nothing
local real x
local real y
set .speed = .speed - REAL_KB_SLOW_DOWN
if .speed <= REAL_KB_MIN_SPEED then
call .destroy()
endif
set x = GetUnitX( .target ) + .cos * .speed
set y = GetUnitY( .target ) + .sin * .speed
if IsTerrainWalkable( x, y ) then
call SetUnitX( .target, x )
call SetUnitY( .target, y )
else
call .destroy()
endif
set .interval = .interval - TIMER_INTERVAL
if .interval <= 0.00 then
set .interval = KB_SFX_INTERVAL
call DestroyEffect( AddSpecialEffect( KB_SFX, x, y ) )
endif
endmethod
static method createEx takes unit caster, unit buffed, unit target, Arcania data returns thistype
local thistype this = thistype.allocate()
set TEMPREAL = Atan2(GetUnitY( buffed ) - GetUnitY( target ), GetUnitX( buffed ) - GetUnitX( target ))
set TEMPREAL = GetNewAngle( TEMPREAL )
set .target = target
set .cos = Cos( TEMPREAL )
set .sin = Sin( TEMPREAL )
set .time = NewTimer()
set .data = data
call SetTimerData( .time, this )
call TimerStart( .time, TIMER_INTERVAL, true, function KBOuterControl )
return this
endmethod
static method create takes unit caster, unit buffed, unit target, Arcania data returns thistype
local thistype this = thistype.allocate()
set TEMPREAL = Atan2(GetUnitY( target ) - GetUnitY( buffed ), GetUnitX( target ) - GetUnitX( buffed ))
set .target = target
set .cos = Cos( TEMPREAL )
set .sin = Sin( TEMPREAL )
set .time = NewTimer()
set .data = data
call SetTimerData( .time, this )
call TimerStart( .time, TIMER_INTERVAL, true, function KBOuterControl )
return this
endmethod
endstruct
private function OuterControl takes nothing returns nothing
set TEMPDATA = GetTimerData( GetExpiredTimer() )
call TEMPDATA.InnerControl()
endfunction
private function IsTarget takes nothing returns boolean
return GetWidgetLife( GetFilterUnit() ) > 0.405 and IsUnitType(GetFilterUnit(), UNIT_TYPE_MAGIC_IMMUNE) == false and IsUnitType(GetFilterUnit(), UNIT_TYPE_STRUCTURE) == false and IsUnitEnemy( GetFilterUnit(), GetOwningPlayer( TEMPDATA.caster ) ) and not IsUnitInGroup( GetFilterUnit(), TEMPDATA.mytargets ) and GetFilterUnit() != TEMPDATA.buffed and GetUnitAbilityLevel( GetFilterUnit(), BUFF_ID ) <= 0
endfunction
private struct Grabbed
unit grabbed = null
Arcania root = 0
lightning light = null
real speed = REAL_START_SPEED
real sfxinterval = DRAG_SFX_INTERVAL
effect grabbedsfx = null
vector vec
method End takes nothing returns nothing
debug call BJDebugMsg( "END" )
call DestroyEffect( AddSpecialEffectTarget( CANCEL_SFX, .grabbed, "origin" ) )
call .destroy()
endmethod
private method Collision takes nothing returns nothing
if not DAMAGE_ONLY_ENEMY or IsUnitEnemy( .root.buffed, GetOwningPlayer( .root.caster ) ) then
call UnitDamageTarget( .root.caster, .root.buffed, DAMAGE_COLLISION[.root.level], true, false, ATTACK, DAMAGE_TYPE_NORMAL, null )
call SetUnitState( .root.buffed, UNIT_STATE_MANA, GetUnitState( .grabbed, UNIT_STATE_MANA ) - MANALOSE_COLLISION[.root.level] )
endif
call UnitDamageTarget( .root.caster, .grabbed, DAMAGE_COLLISION[.root.level], true, false, ATTACK, DAMAGE_TYPE_NORMAL, null )
call SetUnitState( .grabbed, UNIT_STATE_MANA, GetUnitState( .grabbed, UNIT_STATE_MANA ) - MANALOSE_COLLISION[.root.level] )
call DestroyEffect( AddSpecialEffectTarget( COLLISION_SFX, .grabbed, "origin" ) )
call ArcKB.create( .root.caster, .root.buffed, .grabbed, .root )
//Lets do some damage and knock away
endmethod
method Control takes nothing returns boolean
local real x
local real y
//Death?
if GetWidgetLife( .grabbed ) <= 0.405 or IsUnitType( .grabbed, UNIT_TYPE_DEAD) == true or .grabbed == null or GetUnitAbilityLevel( .grabbed, BUFF_ID ) >= 1 then
call GroupRemoveUnit( .root.mytargets, .grabbed )
return false
endif
//New Direction
set TEMPREAL = Atan2(GetUnitY( .root.buffed ) - GetUnitY( .grabbed ), GetUnitX( .root.buffed ) - GetUnitX( .grabbed ))
//New Speed
set .speed = .speed + REAL_ACCELERATION
//Jep Insta kill ( could only happen when the unit is sucked by two buffs ).
if .speed >= REAL_INSTA_KILL_SPEED then
debug call BJDebugMsg( "insta kill" )
debug call BJDebugMsg( R2S( .speed ) )
call SetWidgetLife( .grabbed, 1.00 )
call UnitDamageTarget( .root.caster, .grabbed, 10.00, true, false, ATTACK, DAMAGE_TYPE_NORMAL, null )
call GroupRemoveUnit( .root.mytargets, .grabbed )
call DestroyEffect( AddSpecialEffectTarget( INSTA_KILL_SFX, .grabbed, "origin" ) )
return false
endif
//New Position
set TEMPVEC.x = Cos( TEMPREAL ) * .speed
set TEMPVEC.y = Sin( TEMPREAL ) * .speed
call .vec.add( TEMPVEC )
call .vec.scale( 0.5 )
call SetUnitX( .grabbed, GetUnitX( .grabbed ) + .vec.x )
call SetUnitY( .grabbed, GetUnitY( .grabbed ) + .vec.y )
call UnitDamageTarget( .root.caster, .grabbed, DAMAGE[.root.level], true, false, ATTACK, DAMAGE_TYPE_NORMAL, null )
call SetUnitState( .grabbed, UNIT_STATE_MANA, GetUnitState( .grabbed, UNIT_STATE_MANA ) - MANALOSE[.root.level] )
if not IsTerrainWalkable( GetUnitX( .grabbed ), GetUnitY( .grabbed ) ) then
debug call BJDebugMsg( "not walkable" )
call ArcKB.createEx( .root.caster, .root.buffed, .grabbed, .root )
return false
endif
//Collision?
set x = GetUnitX( .root.buffed ) - GetUnitX( .grabbed )
set y = GetUnitY( .root.buffed ) - GetUnitY( .grabbed )
set TEMPREAL = SquareRoot( x * x + y * y )
if TEMPREAL <= COLLISION_RANGE then
//Jep it's a collision
call .Collision()
return false
else
//Move light and create some nice effects
if .light != null then
call MoveLightningEx( .light, true, GetUnitX( .root.buffed ), GetUnitY( .root.buffed ), GetUnitZ( .root.buffed ) + ADD_HEIGHT, GetUnitX( .grabbed ), GetUnitY( .grabbed ), GetUnitZ( .grabbed ) + ADD_HEIGHT )
endif
//Some cool effects
set .sfxinterval = .sfxinterval - TIMER_INTERVAL
if .sfxinterval <= 0.00 then
set .sfxinterval = DRAG_SFX_INTERVAL
call DestroyEffect( AddSpecialEffect( DRAG_SFX, GetUnitX( .grabbed ), GetUnitY( .grabbed ) ) )
endif
endif
//Lets leave this
return true
endmethod
static method create takes Arcania root, unit target returns thistype
local thistype this = thistype.allocate()
local real angle = Atan2(GetUnitY( .root.buffed ) - GetUnitY( target ), GetUnitX( .root.buffed ) - GetUnitX( target ))
set .root = root
set .grabbed = target
set .light = AddLightningEx( LIGHTNING_STRING, true, GetUnitX( root.buffed ), GetUnitY( root.buffed ), GetUnitZ( root.buffed ) + ADD_HEIGHT, GetUnitX( target ), GetUnitY( target ), GetUnitZ( target ) + ADD_HEIGHT )
call SetLightningColor( .light, RED, GREEN, BLUE, ALPHA )
set .grabbedsfx = AddSpecialEffectTarget( GRAB_SFX, target, "chest" )
set .vec = vector.create( Cos( angle ) * REAL_START_SPEED, Sin( angle ) * REAL_START_SPEED, 0.00 )
return this
endmethod
method onDestroy takes nothing returns nothing
call DestroyEffect( .grabbedsfx )
set .grabbedsfx = null
call DestroyLightning( .light )
set .light = null
endmethod
endstruct
private struct Arcania
unit buffed = null
unit caster = null
integer count = 0
timer time = null
integer level = 1
boolean buffon = false
real duration = 0.00
real pickinterval = PICK_INTERVAL
real thereissomethingwrong = 5.00
group mytargets = CreateGroup()
boolean end = false
Grabbed array grabbeds[MAX_TARGETS]
private method PickThem takes nothing returns nothing
local unit u
if .count <= MAX_TARGETS then
set TEMPDATA = this
call GroupEnumUnitsInRange( TEMPGROUP, GetUnitX( .buffed ), GetUnitY( .buffed ), AOE[.level], Condition( function IsTarget ) )
loop
set u = FirstOfGroup( TEMPGROUP )
exitwhen u == null or .count > MAX_TARGETS
set .grabbeds[.count] = Grabbed.create( this, u )
call GroupAddUnit( .mytargets, u )
call GroupRemoveUnit( TEMPGROUP, u )
set .count = .count + 1
endloop
call GroupClear( TEMPGROUP )
endif
//Lets pick'em and don't forget to add them to your group
endmethod
private method SuckThem takes nothing returns nothing
local integer i = 0
//Should work
loop
exitwhen i >= .count //Exitwhen i reached free place
if .grabbeds[i] != 0 then //Is Grabbeds set?
if not .grabbeds[i].Control() then //Returns false when it should be destroyed
set .count = .count - 1 //Set count == last needed place
call .grabbeds[i].destroy() //Destroys light and sfx
set .grabbeds[i] = 0 //Clear the place of the destroyed struct
if i != .count then //If last place == destroyed struct
set .grabbeds[i] = .grabbeds[.count] //Set cleared place == last struct
set .grabbeds[.count] = 0 //Clear that place
endif
endif
endif
set i = i + 1 //Next
endloop
//Jep I remove them of the group when the knockback is done
debug call BJDebugMsg( "Count: " + I2S( .count ) )
endmethod
method InnerControl takes nothing returns nothing
set TEMPINT = GetUnitAbilityLevel( .buffed, BUFF_ID ) //Level wird gesetzt
if .end then //DIE DIE DIE DIE
call .destroy()
return
endif
if not .buffon then //Buff applayed?
if TEMPINT > 0 then
set .buffon = true //Jep
else
set .thereissomethingwrong = .thereissomethingwrong - TIMER_INTERVAL
if .thereissomethingwrong <= 0.00 then
debug call BJDebugMsg( "CRITICAL ERROR, BUFF IS NOT APPLIED" )
set .end = true
call .destroy()
return
endif
endif
else
//All Actions
if TEMPINT <= 0 then //Buff away == destroy
set .end = true
call .destroy()
return
endif
//Time's up
set .duration = .duration - TIMER_INTERVAL
if .duration <= 0.00 then
set .end = true
call .destroy()
return
endif
//Picking
set .pickinterval = .pickinterval - PICK_INTERVAL
if .pickinterval <= 0.00 and .end == false then
set .pickinterval = PICK_INTERVAL
call .PickThem()
endif
//Sucking
if .end == false then
call .SuckThem()
endif
endif
endmethod
method onDestroy takes nothing returns nothing
local integer i = 0
call ReleaseTimer( .time )
debug call BJDebugMsg( "Endcount: " + I2S( .count ) )
loop
exitwhen i == .count //Go through and destroy them
if .grabbeds[i] != 0 then //If that struct exists
debug call BJDebugMsg( I2S( i ) )
call .grabbeds[i].End() //Destroy it in a special way
endif
set i = i + 1
endloop
call GroupClear( .mytargets )
call DestroyGroup( .mytargets )
set .mytargets = null
endmethod
static method create takes unit caster, unit target returns thistype
local thistype this = thistype.allocate()
set .buffed = target
set .caster = caster
set .time = NewTimer()
set .level = GetUnitAbilityLevel( caster, SPELL_ID )
set .duration = DURATION[.level]
call SetTimerData( .time, this )
call TimerStart( .time, TIMER_INTERVAL, true, function OuterControl )
return this
endmethod
endstruct
private function IsSpell takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID then
call Arcania.create( GetTriggerUnit(), GetSpellTargetUnit() )
endif
return false
endfunction
private function HasBuff takes nothing returns boolean
if GetSpellAbilityId() == SPELL_ID and GetUnitAbilityLevel( GetSpellTargetUnit(), BUFF_ID ) >= 1 then
call IssueImmediateOrder( GetTriggerUnit(), "stop" )
call DisplayTextToPlayer( GetOwningPlayer( GetTriggerUnit() ), 0.5, -0.50, "|cffffcc00Target has buff already|r" )
endif
return false
endfunction
private function init takes nothing returns nothing
local trigger t = CreateTrigger()
local trigger t2 = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( t2, EVENT_PLAYER_UNIT_SPELL_CHANNEL )
call TriggerAddCondition( t2, Condition( function HasBuff ) )
call TriggerRegisterAnyUnitEventBJ( t, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( t, Condition( function IsSpell ) )
set TEMPVEC = vector.create( 0.00, 0.00, 0.00 )
call SET_DURATION()
call SET_AOE()
call SET_DAMAGE_COLLISION()
call SET_DAMAGE()
call SET_MANALOSE_COLLISION()
call SET_MANA()
endfunction
endscope
//TESH.scrollpos=0
//TESH.alwaysfold=0
function Trig_Revive_Units_Conditions takes nothing returns boolean
return IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == false
endfunction
function Trig_Revive_Units_Actions takes nothing returns nothing
call TriggerSleepAction( 10.00 )
call CreateUnit( GetOwningPlayer( GetTriggerUnit() ), GetUnitTypeId( GetTriggerUnit() ), GetUnitX( GetTriggerUnit() ), GetUnitY( GetTriggerUnit() ), GetUnitFacing( GetTriggerUnit() ) )
endfunction
//===========================================================================
function InitTrig_Revive_Units takes nothing returns nothing
set gg_trg_Revive_Units = CreateTrigger( )
call TriggerRegisterAnyUnitEventBJ( gg_trg_Revive_Units, EVENT_PLAYER_UNIT_DEATH )
call TriggerAddCondition( gg_trg_Revive_Units, Condition( function Trig_Revive_Units_Conditions ) )
call TriggerAddAction( gg_trg_Revive_Units, function Trig_Revive_Units_Actions )
endfunction