Name | Type | is_array | initial_value |
//TESH.scrollpos=108
//TESH.alwaysfold=0
library TimerUtils initializer init
//*********************************************************************
//* TimerUtils (red+blue+orange flavors for 1.24b+) 2.0
//* ----------
//*
//* 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.wc3c.net
//*
//* For your timer needs:
//* * Attaching
//* * Recycling (with double-free protection)
//*
//* set t=NewTimer() : Get a timer (alternative to CreateTimer)
//* set t=NewTimerEx(x) : Get a timer (alternative to CreateTimer), call
//* Initialize timer data as x, instead of 0.
//*
//* 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.
//*
//* Multi-flavor:
//* Set USE_HASH_TABLE to true if you don't want to complicate your life.
//*
//* If you like speed and giberish try learning about the other flavors.
//*
//********************************************************************
//================================================================
globals
//How to tweak timer utils:
// USE_HASH_TABLE = true (new blue)
// * SAFEST
// * SLOWEST (though hash tables are kind of fast)
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = true (orange)
// * kinda safe (except there is a limit in the number of timers)
// * ALMOST FAST
//
// USE_HASH_TABLE = false, USE_FLEXIBLE_OFFSET = false (red)
// * THE FASTEST (though is only faster than the previous method
// after using the optimizer on the map)
// * THE LEAST SAFE ( you may have to tweak OFSSET manually for it to
// work)
//
private constant boolean USE_HASH_TABLE = true
private constant boolean USE_FLEXIBLE_OFFSET = false
private constant integer OFFSET = 0x100000
private integer VOFFSET = OFFSET
//Timers to preload at map init:
private constant integer QUANTITY = 256
//Changing this to something big will allow you to keep recycling
// timers even when there are already AN INCREDIBLE AMOUNT of timers in
// the stack. But it will make things far slower so that's probably a bad idea...
private constant integer ARRAY_SIZE = 8190
endglobals
//==================================================================================================
globals
private integer array data[ARRAY_SIZE]
private hashtable ht
endglobals
//It is dependent on jasshelper's recent inlining optimization in order to perform correctly.
function SetTimerData takes timer t, integer value returns nothing
static if(USE_HASH_TABLE) then
// new blue
call SaveInteger(ht,0,GetHandleId(t), value)
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-VOFFSET]=value
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
set data[GetHandleId(t)-OFFSET]=value
endif
endfunction
function GetTimerData takes timer t returns integer
static if(USE_HASH_TABLE) then
// new blue
return LoadInteger(ht,0,GetHandleId(t) )
elseif (USE_FLEXIBLE_OFFSET) then
// orange
static if (DEBUG_MODE) then
if(GetHandleId(t)-VOFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-VOFFSET]
else
// new red
static if (DEBUG_MODE) then
if(GetHandleId(t)-OFFSET<0) then
call BJDebugMsg("SetTimerData: Wrong handle id, only use SetTimerData on timers created by NewTimer")
endif
endif
return data[GetHandleId(t)-OFFSET]
endif
endfunction
//==========================================================================================
globals
private timer array tT[ARRAY_SIZE]
private integer tN = 0
private constant integer HELD=0x28829022
//use a totally random number here, the more improbable someone uses it, the better.
private boolean didinit = false
endglobals
private keyword init
//==========================================================================================
// I needed to decide between duplicating code ignoring the "Once and only once" rule
// and using the ugly textmacros. I guess textmacros won.
//
//! textmacro TIMERUTIS_PRIVATE_NewTimerCommon takes VALUE
// On second thought, no.
//! endtextmacro
function NewTimerEx takes integer value returns timer
if (tN==0) then
if (not didinit) then
//This extra if shouldn't represent a major performance drawback
//because QUANTITY rule is not supposed to be broken every day.
call init.evaluate()
set tN = tN - 1
else
//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, make sure all timers are getting recycled correctly")
set tT[0]=CreateTimer()
static if( not USE_HASH_TABLE) then
debug call BJDebugMsg("In case of errors, please increase it accordingly, or set TimerUtils_USE_HASH_TABLE to true")
static if( USE_FLEXIBLE_OFFSET) then
if (GetHandleId(tT[0])-VOFFSET<0) or (GetHandleId(tT[0])-VOFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
else
if (GetHandleId(tT[0])-OFFSET<0) or (GetHandleId(tT[0])-OFFSET>=ARRAY_SIZE) then
//all right, couldn't fix it
call BJDebugMsg("NewTimer: Unable to allocate a timer, you should probably set TimerUtils_USE_HASH_TABLE to true or fix timer leaks.")
return null
endif
endif
endif
endif
else
set tN=tN-1
endif
call SetTimerData(tT[tN],value)
return tT[tN]
endfunction
function NewTimer takes nothing returns timer
return NewTimerEx(0)
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==ARRAY_SIZE) 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 init takes nothing returns nothing
local integer i=0
local integer o=-1
local boolean oops = false
if ( didinit ) then
return
else
set didinit = true
endif
static if( USE_HASH_TABLE ) then
set ht = InitHashtable()
loop
exitwhen(i==QUANTITY)
set tT[i]=CreateTimer()
call SetTimerData(tT[i], HELD)
set i=i+1
endloop
set tN = QUANTITY
else
loop
set i=0
loop
exitwhen (i==QUANTITY)
set tT[i] = CreateTimer()
if(i==0) then
set VOFFSET = GetHandleId(tT[i])
static if(USE_FLEXIBLE_OFFSET) then
set o=VOFFSET
else
set o=OFFSET
endif
endif
if (GetHandleId(tT[i])-o>=ARRAY_SIZE) then
exitwhen true
endif
if (GetHandleId(tT[i])-o>=0) then
set i=i+1
endif
endloop
set tN = i
exitwhen(tN == QUANTITY)
set oops = true
exitwhen not USE_FLEXIBLE_OFFSET
debug call BJDebugMsg("TimerUtils_init: Failed a initialization attempt, will try again")
endloop
if(oops) then
static if ( USE_FLEXIBLE_OFFSET) then
debug call BJDebugMsg("The problem has been fixed.")
//If this message doesn't appear then there is so much
//handle id fragmentation that it was impossible to preload
//so many timers and the thread crashed! Therefore this
//debug message is useful.
elseif(DEBUG_MODE) then
call BJDebugMsg("There were problems and the new timer limit is "+I2S(i))
call BJDebugMsg("This is a rare ocurrence, if the timer limit is too low:")
call BJDebugMsg("a) Change USE_FLEXIBLE_OFFSET to true (reduces performance a little)")
call BJDebugMsg("b) or try changing OFFSET to "+I2S(VOFFSET) )
endif
endif
endif
endfunction
endlibrary
//TESH.scrollpos=36
//TESH.alwaysfold=0
library KBS initializer Init
//***************************************************************************************
//** **
//** Knockback System (KBS) **By kenny!** **
//** v 2.04 **
//** **
//** A system I made that can be used for knockback spells in a variety of **
//** applications in a map. User defined variables make it easy to give the desired **
//** effect for any spell. **
//** **
//** Requires: **
//** - A vJASS preprocessor (NewGen Pack). **
//** - No other systems. **
//** - A dummy unit. One can be found in this map ('u000') **
//** - The special effects found in the import/export section of this map. **
//** They are the "Dust.mdx" and the "SlideWater.mdx". Or you can use your **
//** own if you have some. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Other Information: **
//** - Angle is taken in degrees. But can easily be changed if you know how. **
//** - Units will be stopped if they hit the map borders. **
//** - Remember to import the special effect into your map if you need them. **
//** - There are two functions that can be used in this system. **
//** - NEW FUNCTION: KBS_BeginCommon() is for all the common knockback uses in **
//** your map. It only takes 4 parameters, and use global parameters for Area **
//** AllowMove and CheckPathing. Making it an icredibly easy function to use. **
//** **
//** Sliding Distances: **
//** - This is some general knowledge on the different distances and how to **
//** get them. **
//** - Sliding distances have changed in this version of the system. **
//** - The function call (KBS_Begin/Ex) now takes distance and duration **
//** parameters instead of startspeed and deceleration. **
//** - Therefore the parameter 'Distance' will set the length of the knockback **
//** while the parameter 'Duration" will set the time it will take. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Implementation: **
//** - First off you need the NewGen world editor. This is a must. **
//** - Copy this trigger into your map. Or make a trigger in your map and **
//** convert it to custom text. Then copy this text into that trigger. **
//** - Now you will either need to export the special effects from this map to **
//** your map, or you will need your own. I do not take credit for the **
//** special effects found in this map. **
//** - Once you have the effects in your map. Find the configuration block **
//** that is underneath all this green text. Change the WATER_SFX and the **
//** DIRT_SFX strings to the ones in your map. These should be the same if **
//** you exported the ones from this map. **
//** - Make sure you have the dummy unit needed. It can be any unit, but i **
//** suggest you use the one found in this map, it is labelled 'Dummy Unit' **
//** or 'u000'. **
//** - Your done! Save your map, make a spell using this system and try it out. **
//** **
//** NOTE: Please report any bugs to me at thehelper.net via PM. (User name: kenny!)**
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Usage: **
//** //** **
//** call KBS_Begin(Target,Distance,Duration,Angle,Area) **
//** or **
//**call KBS_BeginEx(Target,Distance,Duration,Angle,Effect,Area,AllowMove,CheckPathing)**
//** or **
//** call KBS_BeginCommon(Target,Distance,Duration,Angle) **
//** **
//** - Target - The unit that will be used in the knockback. **
//** - Distance - The distance of the knockback (how far the unit will slide). **
//** - Duration - The duration of the knockback refers to how long the unit **
//** will slide for. **
//** - Angle - The angle that the unit will be knocked back at. **
//** THIS MUST BE IN DEGREES. <- Easier for GUI users. **
//** - Effect - This is only used in the extended function. If you do not **
//** want one of the predefined effects, you can choose your own. **
//** However, this effect is attached to the unit and removed at **
//** the end, so non-looping or repeating effect wont work. **
//** - Area - This determines whether or not trees will be knocked down. **
//** For trees to be knocked down, a positive number (real) must **
//** be used, such as 150.00, which would give a radius of 150.00 **
//** in which trees will be knocked down. **
//** For trees to not be knocked down, a negative number (real) **
//** must be used, such as -150.00, which would create a radius **
//** that if a tree comes within it, the unit will stop moving. **
//** For none of those effects, the number 0 (0.00) can be used. **
//** This will just cause the units to "bounce" off trees. **
//** -Allowmove - This boolean will decided whether or not you want the unit **
//** to have the ability to move while being knocked back. **
//** "true" will allow them to move, while "false" will not. **
//** -CheckPathing - A boolean that, if true, will check for unpathable terrain **
//** such as a wall or cliff, or where doodads may be. If false **
//** it will ignore these changes and the unit will be pushed **
//** along the wall, cliff or doodad. **
//** **
//** REMEMBER: Positive = trees destroyed, Negative = trees not destroyed. **
//** **
//** Example of Usage: **
//** **
//** -call KBS_Begin(target,500.00,2.00,270.00,150.00) **
//** or **
//** -call KBS_BeginEx(target,500.00,2.00,270.00,"Effect.mdl",-150.00,true,true) **
//** **
//** The first one will cause the target unit of a spell to be knocked back 500.00 **
//** range over a 2.00 second duration. It will be knocked back at an angle of **
//** 270.00, which I'm pretty sure is south, and it will knock down trees within **
//** a 150.00 radius around the unit. **
//** **
//** The bottom one of the two will do the exact same as the top, however you are **
//** able to choose your own special effect that will be attached to the unit. As **
//** you can see, the number for Area (destroying trees) is negative, therefore **
//** no trees will be knocked down, and the unit will stop moving if it comes in **
//** contact with a tree. There is also an extra boolean nearing end, this is for **
//** allowing units to move while sliding. It is true, meaning units are allowed. **
//** There is also an added feature that will check for pathable terrain and stop a **
//** unit if it comes in contact with things such as walls, cliffs or doodads. **
//** If true, the unit will stop moving if it comes in direct contact will a wall **
//** or cliff. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Other functions: **
//** **
//** - call KBS_IsUnitSliding(Target) **
//** **
//** - This function checks if a picked unit is currently sliding using this **
//** system. It will return true if it is. **
//** **
//** - call KBS_StopUnitSliding(Target) **
//** **
//** - This function will stop a picked unit from sliding (using this system). **
//** It also returns true if the unit is stopped. **
//** **
//** - These functions can be used in conjunction with each other, by checking if a **
//** unit is sliding then stopping it if it is. The regular Storm Bolt spell in **
//** the Test Spells section uses these two function in conjunction with each **
//** other as an example. **
//** **
//***************************************************************************************
//***************************************************************************************
//** **
//** Some points on checking for pathable terrain: **
//** **
//**` - The area specified for destroying trees must be at least 75.00-100.00 range **
//** larger than the distance for checking for pathing, which is below in the **
//** globals block. **
//** - When using KBS_BeginEx() and using either 0.00 or a negative value for the **
//** parameter 'Area' (destroying trees) it is better to have CheckPathing as **
//** FALSE. This is due to the fact that setting CheckPathing to TRUE will **
//** override the 'Area' Parameter and stop a unit if it gets near trees. **
//** - Basically what this means is that you cannot get a unit to just 'bounce' off **
//** trees anymore, it is either destroy trees or get stopped by them. **
//** **
//***************************************************************************************
globals
// CONFIGURABLES:
private constant integer DUMMY_ID = 'h007' // This is the dummy unit.
private constant real INTERVAL = 0.04 // Recommended Interval.
private constant string ATTACH_POINT = "chest" // Attachment point for effects.
private constant string WATER_SFX = "war3mapImported\\SlideWater.mdx" // Water slide effect.
private constant string DIRT_SFX = "war3mapImported\\Dust.mdx" // Ground slide effect.
private constant string COLLISION_SFX = "Abilities\\Weapons\\AncientProtectorMissile\\AncientProtectorMissile.mdl" // Collision effect when unit hits a wall/cliff etc.
private constant boolean SHOW_COLLISION = true // Whether or not effect will show if a unit collides with a wall/cliff etc.
// The follow are used when using KBS_BeginCommon() for the common parameters that will be used.
private constant real COMMON_AREA = 150.00 // The area in which trees will be destroyed.
private constant boolean COMMON_ALLOW_MOVE = false // Whether or not to allow units to move.
private constant boolean COMMON_CHECK_PATHING = false // Whether or not to check pathing.
// The following raw codes will probably not need to be changed for most maps.
private constant integer HARVEST = 'Ahrl' // Raw code for: Harvest(Ghouls Lumber).
private constant integer LOCUST = 'Aloc' // Raw code for Locust ability.
// The following value is used for terrain pathability checking.
private constant real DISTANCE = 50.00
// The above is the distance in front of the unit that terrain pathability
// will be checked. A value of 40.00-50.00 is recommended. Also make sure
// that this distance is AT LEAST 75.00-100.00 units less than the ranges set
// for destroying trees, otherwise it wont work properly.
// Example: Keep above distance at 50.00, and use 150.00 for knocking down trees.
// This seems to work very well.
private constant real MAX_RANGE = 10.00
// Max range for checking for pathability. I suggest you leave this alone.
endglobals
//***************************************************************************************
//** **
//** DO NOT TOUCH BELOW HERE UNLESS YOU KNOW WHAT YOUR ARE DOING! **
//** **
//***************************************************************************************
private keyword Data
globals
private Data array D
private boolean array BA
private item array Hidden
private integer Hidden_max = 0
private integer Total = 0
private real Game_maxX = 0.00
private real Game_minX = 0.00
private real Game_maxY = 0.00
private real Game_minY = 0.00
private rect Rect1 = null
private unit Tree_dummy = null
private item Item = null
private timer Timer = null
private boolexpr Tree_filt = null
endglobals
//=======================================================================
private function Filter_items takes nothing returns nothing
if IsItemVisible(GetEnumItem()) then
set Hidden[Hidden_max] = GetEnumItem()
call SetItemVisible(Hidden[Hidden_max],false)
set Hidden_max = Hidden_max + 1
endif
endfunction
// Thanks to Vexorian for original concept, and Anitarf for the up-to-date version. (Slightly midified).
private function Check_pathability takes real x1, real y1 returns boolean
local real x2 = 0.00
local real y2 = 0.00
call SetRect(Rect1,0.00,0.00,128.00,128.00)
call MoveRectTo(Rect1,x1,y1)
call EnumItemsInRect(Rect1,null,function Filter_items)
call SetItemPosition(Item,x1,y1)
set x2 = GetItemX(Item)
set y2 = GetItemY(Item)
call SetItemVisible(Item,false)
loop
exitwhen Hidden_max <= 0
set Hidden_max = Hidden_max - 1
call SetItemVisible(Hidden[Hidden_max],true)
set Hidden[Hidden_max] = null
endloop
return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2) < MAX_RANGE * MAX_RANGE
endfunction
//=======================================================================
private function Destroy_trees takes nothing returns nothing
if BA[0] then
call KillDestructable(GetEnumDestructable()) // Used to destroy destructables.
else
set BA[1] = true
endif
endfunction
// Thanks to PitzerMike for this function. (Modified Slightly).
private function Check_trees takes nothing returns boolean
local destructable dest = GetFilterDestructable()
local boolean result = false
if GetDestructableLife(dest) > 0.405 then
call ShowUnit(Tree_dummy,true)
call SetUnitX(Tree_dummy,GetWidgetX(dest))
call SetUnitY(Tree_dummy,GetWidgetY(dest))
set result = IssueTargetOrder(Tree_dummy,"harvest",dest)
call IssueImmediateOrder(Tree_dummy,"stop")
call ShowUnit(Tree_dummy,false)
set dest = null
return result
endif
set dest = null
return result
endfunction
// Checks if a point is on the map boundaries.
private function Outside_bounds takes real x, real y returns boolean
return (x > Game_maxX or y > Game_maxY or x < Game_minX or y < Game_minY)
endfunction
//=======================================================================
private struct Data
unit targ = null
real speed = 0.00
real decrease = 0.00
real sin = 0.00
real cos = 0.00
real radius = 0.00
integer sfxmode = 0
effect sfx = null
boolean custom = false
boolean allowmove = false
boolean pathing = false
boolean forcestop = false
// Checking for terrain type.
method terrain takes nothing returns integer
local real x = GetUnitX(.targ)
local real y = GetUnitY(.targ)
if IsTerrainPathable(x,y,PATHING_TYPE_FLOATABILITY) then
return 1
elseif not IsTerrainPathable(x,y,PATHING_TYPE_WALKABILITY) then
return 2
endif
return 0
endmethod
static method create takes unit Target, real Distance1, real Duration, real Angle1, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns Data
local Data d = Data.allocate()
// Allocates struct members to user defined variables.
set d.targ = Target
set d.speed = (2.00 * Distance1) / (Duration + 1.00)
set d.decrease = d.speed / Duration
set d.sin = Sin(Angle1)
set d.cos = Cos(Angle1)
set d.radius = Area
set d.allowmove = AllowMove
set d.pathing = CheckPathing
set d.sfxmode = d.terrain()
if Effect != "" and Effect != null then
set d.custom = true
endif
// Adding effects to the unit.
if d.custom then
set d.sfx = AddSpecialEffectTarget(Effect,d.targ,ATTACH_POINT)
else
if d.sfxmode == 1 then
set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
elseif d.sfxmode == 2 then
set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
endif
endif
return d
endmethod
private method onDestroy takes nothing returns nothing
set .targ = null
if .sfx != null then
call DestroyEffect(.sfx) // Destroys effects if needed.
set .sfx = null
endif
set BA[0] = false
set BA[1] = false
endmethod
endstruct
//=======================================================================
private function Update takes nothing returns nothing
local integer i = 1
local integer newmode = 0
local integer height = 0
local Data d = 0
local real x = 0.00
local real y = 0.00
local real newx = 0.00
local real newy = 0.00
loop
exitwhen i > Total
set d = D[i]
set newmode = d.sfxmode
set x = GetUnitX(d.targ)
set y = GetUnitY(d.targ)
// Destroys trees if wanted, or stops the unit
if d.radius != 0.00 then
set BA[0] = d.radius > 0.00
call SetRect(Rect1,x - d.radius,y - d.radius,x + d.radius,y + d.radius)
call EnumDestructablesInRect(Rect1,Tree_filt,function Destroy_trees)
endif
// Checks for terrain pathability, such as walls and cliffs.
if d.pathing then
if Check_pathability(x + DISTANCE * d.cos,y + DISTANCE * d.sin) == false then
set height = 1
if SHOW_COLLISION then
call DestroyEffect(AddSpecialEffect(COLLISION_SFX,x,y))
endif
endif
endif
if not d.custom then
set d.sfxmode = d.terrain() // Checks for pathing again.
// Adds special effect if terrain changes.
if d.sfxmode == 1 and newmode == 2 then
call DestroyEffect(d.sfx)
set d.sfx = AddSpecialEffectTarget(DIRT_SFX,d.targ,ATTACH_POINT)
elseif d.sfxmode == 2 and newmode == 1 then
call DestroyEffect(d.sfx)
set d.sfx = AddSpecialEffectTarget(WATER_SFX,d.targ,ATTACH_POINT)
endif
endif
if d.speed <= 0 or Outside_bounds(x,y) or BA[1] or height == 1 or d.forcestop == true then
call d.destroy() // Finish knockback
set D[i] = D[Total]
set Total = Total - 1
set i = i - 1
set height = 0
else
set newx = x + d.speed * d.cos
set newy = y + d.speed * d.sin
// Allows unit to move while sliding, if specified.
if d.allowmove then
call SetUnitX(d.targ,newx)
call SetUnitY(d.targ,newy)
else
call SetUnitPosition(d.targ,newx,newy)
endif
set d.speed = d.speed - d.decrease // Sets new speed.
endif
set i = i + 1
endloop
if Total <= 0 then
call PauseTimer(Timer)
set Total = 0
endif
endfunction
//=======================================================================
// Checks if a unit is sliding - returns true if it is.
public function IsUnitSliding takes unit Target returns boolean
local integer i = 1
loop
exitwhen i > Total
if D[i].targ == Target then
return true
endif
set i = i + 1
endloop
return false
endfunction
//=======================================================================
// Stops a unit from sliding - returns true if a unit is stopped.
public function StopUnitSliding takes unit Target returns boolean
local integer i = 1
loop
exitwhen i > Total
if D[i].targ == Target then
set D[i].forcestop = true
return true
endif
set i = i + 1
endloop
return false
endfunction
//=======================================================================
// Extended function - gives the most customisation for a single unit knockback.
public function BeginEx takes unit Target, real Distance1, real Duration, real Angle1, string Effect, real Area, boolean AllowMove, boolean CheckPathing returns boolean
local Data d = 0
if Target == null or Distance1 <= 0.00 or Duration <= 0.00 then
debug call BJDebugMsg("Error: Invalid input in KBS_Begin(Ex)") // Error message.
return false
endif
set d = Data.create(Target,Distance1,(Duration/INTERVAL),(Angle1 * 0.01745328),Effect,Area,AllowMove,CheckPathing)
set Total = Total + 1
if Total == 1 then
call TimerStart(Timer,INTERVAL,true,function Update)
endif
set D[Total] = d
return true
endfunction
//=======================================================================
// Basic function - Can be used in a wide variety of ways and gives a basic knockback.
public function Begin takes unit Target, real Distance1, real Duration, real Angle1, real Area returns nothing
call BeginEx(Target,Distance1,Duration,Angle1,"",Area,false,false)
endfunction
// Common function - For all basic knockback needs, easy to use and simple to remember.
public function BeginCommon takes unit Target, real Distance1, real Duration, real Angle1 returns nothing
call BeginEx(Target,Distance1,Duration,Angle1,null,COMMON_AREA,COMMON_ALLOW_MOVE,COMMON_CHECK_PATHING)
endfunction
//=======================================================================
// Sets map boundries and sets timer, rect and filter.
private function Init takes nothing returns nothing
set Timer = CreateTimer()
set Rect1 = Rect(0.00,0.00,1.00,1.00)
set Tree_filt = Filter(function Check_trees)
// Map bounds
set Game_maxX = GetRectMaxX(bj_mapInitialPlayableArea) - 64.00
set Game_maxY = GetRectMaxY(bj_mapInitialPlayableArea) - 64.00
set Game_minX = GetRectMinX(bj_mapInitialPlayableArea) + 64.00
set Game_minY = GetRectMinY(bj_mapInitialPlayableArea) + 64.00
// Boolean values for destroying trees
set BA[0] = false
set BA[1] = false
// Creating unit for destroying trees
set Tree_dummy = CreateUnit(Player(PLAYER_NEUTRAL_PASSIVE),DUMMY_ID,0.00,0.00,0.00)
call SetUnitPathing(Tree_dummy,false)
call ShowUnit(Tree_dummy,false)
call UnitAddAbility(Tree_dummy,HARVEST)
call UnitAddAbility(Tree_dummy,LOCUST)
// Creating item for pathability checking
set Item = CreateItem('ciri',0.00,0.00)
call SetItemVisible(Item,false)
endfunction
endlibrary
//TESH.scrollpos=86
//TESH.alwaysfold=0
scope Chernobyliss initializer OnInit
//Chernobyliss by Strikest
//Requires TimerUtils by Vexorian(http://www.wc3c.net/showthread.php?t=101322)
//Optionally requires KBS by kenny(http://www.thehelper.net/threads/knockback-system-kbs.96837/)
globals
private constant integer ABIL_ID = 'A000' //Raw code of ability.
private constant integer SLOW_ID = 'A001' //Raw code of the slow ability that is attached to affected units.
private constant string EFFECT1 = "war3mapImported\\Acid Ex.mdx"//Path of the desired special effect that is shown at target area.
private constant string EFFECT2 = "war3mapImported\\VenomousGaleV2_Portrait.mdx" //Path of the desired special effect you want attached to affected units.
private constant string EFFECT3 = "Abilities\\Spells\\Other\\AcidBomb\\BottleMissile.mdl"//Path of the desired special effect that is shown when a unit is affected.
private constant string ATTACH = "chest" //String of the desired attachment point for EFFECT2.
private constant string ATTACH2 = "chest"//String of the desired attachment point for EFFECT3
private constant string KB_SFX = "" //Path of the desired special effect you want attached to knocked back units.
private constant real TIMER_INTERVAL = 32. //Timer interval.
private constant real KB_DURATION = .6 //Duration of optional knockback.
private constant boolean CHANGE_VERTEX_COLOR = true//Choose whether or not you want to gradually change the vertex color of affected units as their health decreases.
private constant attacktype ATTACK_TYPE = ATTACK_TYPE_MAGIC //Attacktype you want
private constant damagetype DAMAGE_TYPE = DAMAGE_TYPE_MAGIC //Damagetype you want
private constant real TREE_AOE = 0.0
private constant boolean ALLOW_MOVE = false
private constant boolean CHECK_PATHING = false
//Copied from kenny's KBS trigger
//** - TREE_AOE - This determines whether or not trees will be knocked down. **
//** For trees to be knocked down, a positive number (real) must **
//** be used, such as 150.00, which would give a radius of 150.00 **
//** in which trees will be knocked down. **
//** For trees to not be knocked down, a negative number (real) **
//** must be used, such as -150.00, which would create a radius **
//** that if a tree comes within it, the unit will stop moving. **
//** For none of those effects, the number 0 (0.00) can be used. **
//** This will just cause the units to "bounce" off trees.
//** -ALLOW_MOVE - This boolean will decided whether or not you want the unit **
//** to have the ability to move while being knocked back. **
//** "true" will allow them to move, while "false" will not. **
//** -CHECK_PATHING - A boolean that, if true, will check for unpathable terrain **
//** such as a wall or cliff, or where doodads may be. If false **
//** it will ignore these changes and the unit will be pushed **
//** along the wall, cliff or doodad.
private constant group GROUP = CreateGroup( )//Don't touch.
private unit CASTER //Don't touch this either.
private real SPELL_X
private real SPELL_Y
endglobals
private constant function DamagePerSecond takes integer level,integer herostr,integer heroagi, integer heroint returns real
return (7. + level * 2.) / TIMER_INTERVAL
endfunction
private constant function Duration takes integer level returns real
return 12. + level * 2.
endfunction
private constant function AoE takes integer level returns real //Area of effect of the spell.
return 215. + (10. * level)
endfunction
private constant function SpreadAoE takes integer level returns real //Area of effect around affected units that it will spread to.
return 170.
endfunction
private constant function KBDistance takes integer level returns real //Distance the enemy will optionally be knocked back from target point.
return 200. + level * 10.
endfunction
private constant function ChanceToSpread takes integer level returns real //Chance to spread the toxin. A value of 1. will mean that it has a 100% of spreading.
return 100. / TIMER_INTERVAL
endfunction
private constant function FilterUnits takes unit u returns boolean //Filter which units will be afffected.
return IsUnitEnemy( u, GetOwningPlayer( CASTER ) )
endfunction
//////////////////////////////////////////////////////NO TOUCHY PAST THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING/////////////////////////////////////////////////////////////////////////////
private struct Cbliss
unit cast
unit t
real x
real y
real dur
integer lvl
effect attach
method destroy takes nothing returns nothing
set this.cast = null
set this.t = null
set this.attach = null
call this.deallocate()
endmethod
endstruct
native UnitAlive takes unit id returns boolean
private function Handler takes nothing returns nothing
local timer t = GetExpiredTimer( )
local Cbliss data = GetTimerData( t )
local unit u
local unit ug = null
local unit cast
local integer lvl = data.lvl
local real dur = data.dur - 1. / TIMER_INTERVAL
local real life
local real mlife
local real plife
set cast = data.cast
if data.dur <= 0 or not UnitAlive(data.t) then
call DestroyEffect( data.attach )
call UnitRemoveAbility( data.t, SLOW_ID )
static if CHANGE_VERTEX_COLOR then
call SetUnitVertexColor(data.t,255,255,255,255)
endif
call data.destroy( )
call ReleaseTimer( t )
else
call UnitDamageTarget(data.cast,data.t,DamagePerSecond(data.lvl,GetHeroStr(data.cast,true),GetHeroAgi(data.cast,true),GetHeroInt(data.cast,true)),false,true,ATTACK_TYPE,DAMAGE_TYPE,WEAPON_TYPE_WHOKNOWS)
static if CHANGE_VERTEX_COLOR then
set life = GetWidgetLife(data.t)
set mlife = GetUnitState(data.t,UNIT_STATE_MAX_LIFE)
set plife = life/mlife
if plife >= .3 then
call SetUnitVertexColor(data.t,R2I(plife* 255.),255,R2I(plife* 255.),255)
else
call SetUnitVertexColor(data.t,60,130,60,255)
endif
endif
set data.dur = data.dur - 1. / TIMER_INTERVAL
call SetTimerData( t, data )
call TimerStart( t, 1. / TIMER_INTERVAL, false, function Handler )
set data.x = GetUnitX(data.t)
set data.y = GetUnitY(data.t)
call GroupEnumUnitsInRange( GROUP,data.x,data.y, SpreadAoE(lvl), null )
loop
set ug = FirstOfGroup(GROUP)
exitwhen ug == null
if UnitAlive(ug) and GetUnitAbilityLevel(ug,SLOW_ID) == 0 and data.dur > 1. / TIMER_INTERVAL and GetRandomInt(0,100) <= R2I(ChanceToSpread(lvl)) then
if FilterUnits(ug) then
set data = data.create()
set data.cast = cast
set data.t = ug
set data.x = GetUnitX(ug)
set data.y = GetUnitY(ug)
set data.lvl = lvl
set data.dur = dur
set data.attach = AddSpecialEffectTarget(EFFECT2,ug,ATTACH)
call DestroyEffect(AddSpecialEffectTarget(EFFECT3,ug,ATTACH2))
call UnitAddAbility(ug,SLOW_ID)
call SetUnitAbilityLevel(ug,SLOW_ID,lvl)
call TimerStart(NewTimerEx(data),1. / TIMER_INTERVAL,false,function Handler)
endif
endif
call GroupRemoveUnit(GROUP,ug)
endloop
endif
set t = null
set u = null
set ug = null
set cast = null
endfunction
private function FilterActions takes nothing returns boolean
local Cbliss data
local unit u = GetFilterUnit()
local real x1 = SPELL_X
local real y1 = SPELL_Y
local real x2 = GetUnitX(u)
local real y2 = GetUnitY(u)
local real a = bj_RADTODEG * Atan2(y2 - y1, x2 - x1)
local integer lvl = GetUnitAbilityLevel(CASTER,ABIL_ID)
if UnitAlive(u) then
if FilterUnits(u) then
static if LIBRARY_KBS then
call KBS_BeginEx( u, KBDistance(lvl), KB_DURATION, a, KB_SFX, TREE_AOE, ALLOW_MOVE, CHECK_PATHING )
endif
set data = Cbliss.create()
set data.cast = CASTER
set data.t = u
set data.x = x2
set data.y = y2
set data.attach = AddSpecialEffectTarget(EFFECT2,u,ATTACH)
set data.lvl = lvl
set data.dur = Duration(lvl)
call DestroyEffect(AddSpecialEffectTarget(EFFECT3,u,ATTACH2))
call UnitAddAbility(u,SLOW_ID)
call SetUnitAbilityLevel(u,SLOW_ID,lvl)
call TimerStart(NewTimerEx(data),1. / TIMER_INTERVAL,false,function Handler)
endif
endif
set u = null
return false
endfunction
private function OnSpell takes nothing returns boolean
local real x = GetSpellTargetX()
local real y = GetSpellTargetY()
if GetSpellAbilityId() == ABIL_ID then
call DestroyEffect(AddSpecialEffect(EFFECT1,x,y))
set CASTER = GetTriggerUnit()
set SPELL_X = x
set SPELL_Y = y
call GroupEnumUnitsInRange(GROUP,x,y,AoE(GetUnitAbilityLevel(CASTER,ABIL_ID)), Filter( function FilterActions ) )
endif
return false
endfunction
private function OnInit takes nothing returns nothing
local trigger trig = CreateTrigger()
call TriggerRegisterAnyUnitEventBJ( trig, EVENT_PLAYER_UNIT_SPELL_EFFECT )
call TriggerAddCondition( trig, Condition( function OnSpell ) )
set trig = null
endfunction
endscope
//Code indented using The_Witcher's Script Language Aligner
//Download the newest version and report bugs at www.hiveworkshop.com