Name | Type | is_array | initial_value |
//TESH.scrollpos=17
//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=57
//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 = 'h000' // 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=0
//TESH.alwaysfold=0
//
// _ ___ ___ ___ _______________________________________________
// /_\ |_ _| \/ __| || A D V A N C E D I N D E X I N G ||
// / _ \ | || |) \__ \ || A N D ||
// /_/ \_\___|___/|___/ || D A T A S T O R A G E ||
// By Jesus4Lyf ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// v 1.1.0
// What is AIDS?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// AIDS assigns unique integers between 1 and 8191 to units which enter
// the map. These can be used for arrays and data attaching.
//
// AIDS also allows you to define structs which are created automatically
// when units enter the map, and filtering which units should be indexed
// as well as for which units these structs should be created.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Simply create a new trigger object called AIDS, go to 'Edit -> Convert
// to Custom Text', and replace everything that's there with this script.
//
// Save the map, close it, reopen it, and then delete the "!" from the
// FAR left side of the next lines (so "external" will line up with this line):
//! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
//
// At the top of the script, there is a 'UnitIndexingFilter' constant
// function. If the function returns true for the unit, then that unit
// will be automatically indexed. Setting this to true will automatically
// index all units. Setting it to false will disable automatic indexing.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function GetUnitId takes unit u returns integer
// - This returns the index of an indexed unit. This will return 0
// if the unit has not been indexed.
// - This function inlines. It does not check if the unit needs an
// index. This function is for the speed freaks.
// - Always use this if 'UnitIndexingFilter' simply returns true.
//
// function GetUnitIndex takes unit u returns integer
// - This will return the index of a unit if it has one, or assign
// an index if the unit doesn't have one (and return the new index).
// - Use this if 'UnitIndexingFilter' doesn't return true.
//
// function GetIndexUnit takes integer index returns unit
// - This returns the unit which has been assigned the 'index'.
//
// AIDS Structs:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - Insert: //! runtextmacro AIDS() at the top of a struct to make it
// an AIDS struct.
// - AIDS structs cannot be created or destroyed manually. Instead, they
// are automatically created when an appropriate unit enters the map.
// - You cannot give members default values in their declaration.
// (eg: private integer i=5 is not allowed)
// - You cannot use array members.
// - AIDS structs must "extend array". This will remove some unused
// functions and enforce the above so there will be no mistakes.
// - There are four optional methods you can use in AIDS structs:
// - AIDS_onCreate takes nothing returns nothing
// - This is called when the struct is 'created' for the unit.
// - In here you can assign members their default values, which
// you would usually assign in their declarations.
// (eg: set this.i=5)
// - AIDS_onDestroy takes nothing returns nothing
// - This is called when the struct is 'destroyed' for the unit.
// - This is your substitute to the normal onDestroy method.
// - AIDS_filter takes unit u returns boolean
// - This is similar to the constant filter in the main system.
// - Each unit that enters the map will be tested by each AIDS
// struct filter. If it returns true for that unit, that unit
// will be indexed if it was not already, the AIDS struct will
// have its AIDS_onCreate method called, and later have its
// AIDS_onDestroy method called when the index is recycled.
// - Not declaring this will use the default AIDS filter instead.
// - AIDS_onInit takes nothing returns nothing
// - This is because I stole your onInit function with my textmacro.
// - You can use '.unit' from any AIDS struct to get the unit for which
// the struct is for.
// - The structs id will be the units index, so getting the struct for
// a unit inlines to a single native call, and you can typecast between
// different AIDS structs. This is the premise of AIDS.
// - Never create or destroy AIDS structs directly.
// - You can call .AIDS_addLock() and AIDS_removeLock() to increase or
// decrease the lock level on the struct. If a struct's lock level is
// not 0, it will not be destroyed until it is reduced to 0. Locks just
// put off AIDS struct destruction in case you wish to attach to a timer
// or something which must expire before the struct data disappears.
// Hence, not freeing all locks will leak the struct (and index).
//
// PUI and AutoIndex:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - AIDS includes the PUI textmacros and the AutoIndex module, because
// these systems are not compatible with AIDS but have valid and distinct
// uses.
// - The PUI textmacros are better to use for spells than AIDS structs,
// because they are not created for all units, just those targetted by
// the spell (or whatever else is necessary).
// - The AutoData module is good for very simple array syntax for data
// attachment (although I don't recommend that people actually use it,
// it's here mostly for compatability). Note that unlike the PUI textmacros,
// units must pass the AIDS filter in order for this module to work with
// them. This is exactly as the same as in AutoIndex itself (AutoIndex
// has a filter too).
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Romek, for writing 90% of this user documentation, challenging my
// interface, doing some testing, suggesting improvements and inspiring
// me to re-do my code to include GetUnitIndex as non-inlining.
// - grim001, for writing the AutoData module, and AutoIndex. I used the
// on-enter-map method that he used. Full credits for the AutoData module.
// - Cohadar, for writing his PUI textmacros. Full credits to him for these,
// except for my slight optimisations for this system.
// Also, I have used an optimised version of his PeriodicRecycler from
// PUI in this system to avoid needing a RemoveUnitEx function.
// - Vexorian, for helping Cohadar on the PUI textmacro.
// - Larcenist, for suggesting the AIDS acronym. Originally he suggested
// 'Alternative Index Detection System', but obviously I came up with
// something better. In fact, I'd say it looks like the acronym was
// an accident. Kinda neat, don't you think? :P
//
// Final Notes:
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// - With most systems, I wouldn't usually add substitutes for alternative
// systems. However, UnitData systems are an exception, because they
// are incompatible with eachother. Since using this system forbids
// people from using the real PUI or AutoIndex, and a lot of resources
// use either of these, it made sense not to break them all.
//
// - If this documentation confused you as to how to use the system, just
// leave everything as default and use GetUnitId everywhere.
//
// - To use this like PUI (if you don't like spamming indices) simply
// make the AIDS filter return false, and use GetUnitIndex.
//
library AIDS initializer InitAIDS requires TimerUtils
//==============================================================================
// Configurables
//
globals
private constant boolean USE_PERIODIC_RECYCLER = false
private constant real PERIOD = 0.03125 // Recycles 32 units/second max.
// Lower to be able to recycle faster.
// Only used if USE_PERIODIC_RECYCLER
// is set to true.
private constant integer LEAVE_DETECTION_ABILITY = 'AIDS'
endglobals
private function UnitIndexingFilter takes unit u returns boolean
return true
endfunction
//==============================================================================
// System code
//
globals
// The unit stored at an index.
private unit array IndexUnit
private integer array LockLevel
endglobals
//==============================================================================
globals
// Recycle stack
private integer array RecycledIndex
private integer MaxRecycledIndex = 0
// Previous highest index
private integer MaxIndex = 0
endglobals
//==============================================================================
globals
private integer array DecayingIndex
private integer MaxDecayingIndex=0
private integer DecayChecker=0
endglobals
globals
private timer UndefendTimer=CreateTimer()
private integer array UndefendIndex
private integer UndefendStackIndex=0
endglobals
globals
private integer array UndefendExpiringIndex
private integer UndefendExpiringIndexLevel=0
endglobals
//==============================================================================
globals
// The Add/Remove stack (or assign/recycle stack).
//
// Indexing can become recusive since units can be created on index
// assignment or deallocation.
// To support this, a stack is used to store the event response results.
private integer ARStackLevel=0
private integer array ARStackIndex
private unit array ARStackUnit
// A later discovery revealed that the Add/Remove stack did not need to be
// used for deallocation. The alternative used works fine...
endglobals
public constant function GetEnteringIndexUnit takes nothing returns unit
return ARStackUnit[ARStackLevel]
endfunction
public function GetIndexOfEnteringUnit takes nothing returns integer
// Called in AIDS structs when units do not pass the initial AIDS filter.
if ARStackIndex[ARStackLevel]==0 then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
endif
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetIndexOfEnteringUnitAllocated takes nothing returns integer
// Called in AIDS structs when units have passed the initial AIDS filter.
return ARStackIndex[ARStackLevel]
endfunction
public constant function GetDecayingIndex takes nothing returns integer
static if USE_PERIODIC_RECYCLER then
return DecayingIndex[DecayChecker]
else
return UndefendExpiringIndex[UndefendExpiringIndexLevel]
endif
endfunction
//==============================================================================
globals
// For structs and such which need to do things on unit index assignment.
private trigger OnEnter=CreateTrigger()
// The same, but for when units pass the initial filter anyway.
private trigger OnEnterAllocated=CreateTrigger()
// For structs and such which need to do things on unit index deallocation.
private trigger OnDeallocate=CreateTrigger()
endglobals
public function RegisterOnEnter takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnter,b)
endfunction
public function RegisterOnEnterAllocated takes boolexpr b returns triggercondition
return TriggerAddCondition(OnEnterAllocated,b)
endfunction
public function RegisterOnDeallocate takes boolexpr b returns triggercondition
return TriggerAddCondition(OnDeallocate,b)
endfunction
//==============================================================================
function GetIndexUnit takes integer index returns unit
debug if index==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of index 0.")
debug elseif IndexUnit[index]==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the unit of unassigned index.")
debug endif
return IndexUnit[index]
endfunction
function GetUnitId takes unit u returns integer
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the id (inlines) of null unit.")
debug elseif GetUnitUserData(u)==0 then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to use GetUnitId (inlines) when you should be using GetUnitIndex (unit didn't pass filter).")
debug endif
return GetUnitUserData(u)
endfunction
globals//locals
private integer getindex
endglobals
function GetUnitIndex takes unit u returns integer // Cannot be recursive.
debug if u==null then
debug call BJDebugMsg("|cFFFF0000Error using AIDS:|r Trying to get the index of null unit.")
debug endif
set getindex=GetUnitUserData(u)
if getindex==0 then
// Get new index, from recycler first, else new.
// Store the current index in getindex.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set getindex=MaxIndex
else // Get from recycle stack.
set getindex=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(u,getindex)
set IndexUnit[getindex]=u
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=getindex
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Do not fire things here. No AIDS structs will be made at this point.
endif
return getindex
endfunction
//==============================================================================
public function AddLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]+1
endfunction
public function RemoveLock takes integer index returns nothing
set LockLevel[index]=LockLevel[index]-1
static if not USE_PERIODIC_RECYCLER then
if GetUnitUserData(IndexUnit[index])==0 and LockLevel[index]==0 then
// Increment stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel+1
set UndefendExpiringIndex[UndefendExpiringIndexLevel]=index
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Decrement stack for recursion.
set UndefendExpiringIndexLevel=UndefendExpiringIndexLevel-1
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=index
// Null the unit.
set IndexUnit[index]=null
endif
endif
endfunction
//==============================================================================
static if USE_PERIODIC_RECYCLER then
private function PeriodicRecycler takes nothing returns nothing
if MaxDecayingIndex>0 then
set DecayChecker=DecayChecker+1
if DecayChecker>MaxDecayingIndex then
set DecayChecker=1
endif
if GetUnitUserData(IndexUnit[DecayingIndex[DecayChecker]])==0 then
if LockLevel[DecayingIndex[DecayChecker]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=DecayingIndex[DecayChecker]
// Null the unit.
set IndexUnit[DecayingIndex[DecayChecker]]=null
// Remove index from decay list.
set DecayingIndex[DecayChecker]=DecayingIndex[MaxDecayingIndex]
set MaxDecayingIndex=MaxDecayingIndex-1
endif
endif
endif
endfunction
else
private function UndefendFilter takes nothing returns boolean
return IsUnitType(GetFilterUnit(),UNIT_TYPE_DEAD)
endfunction
private function OnUndefendTimer takes nothing returns nothing
loop
exitwhen UndefendStackIndex==0
set UndefendStackIndex=UndefendStackIndex-1
set UndefendExpiringIndex[0]=UndefendIndex[UndefendStackIndex]
if IndexUnit[UndefendExpiringIndex[0]]!=null then
if GetUnitUserData(IndexUnit[UndefendExpiringIndex[0]])==0 then
if LockLevel[UndefendExpiringIndex[0]]==0 then
// Fire things.
call TriggerEvaluate(OnDeallocate)
// Add the index to the recycler stack.
set MaxRecycledIndex=MaxRecycledIndex+1
set RecycledIndex[MaxRecycledIndex]=UndefendExpiringIndex[0]
// Null the unit.
set IndexUnit[UndefendExpiringIndex[0]]=null
endif
endif
endif
endloop
endfunction
globals//locals
private integer UndefendFilterIndex
endglobals
private function OnUndefend takes nothing returns boolean
if GetIssuedOrderId()==852056 then // If undefend then...
set UndefendFilterIndex=GetUnitUserData(GetOrderedUnit())
if UndefendIndex[UndefendStackIndex-1]!=UndefendFilterIndex then // Efficiency perk.
set UndefendIndex[UndefendStackIndex]=UndefendFilterIndex
set UndefendStackIndex=UndefendStackIndex+1
call TimerStart(UndefendTimer,0,false,function OnUndefendTimer)
endif
endif
return false
endfunction
endif
//==============================================================================
public function IndexEnum takes nothing returns boolean // Can be recursive...
// Start by adding another level on the AR stack (for recursion's sake).
set ARStackLevel=ARStackLevel+1
// Store the current unit on the (new) top level of the AR stack.
set ARStackUnit[ARStackLevel]=GetFilterUnit()
if GetUnitUserData(ARStackUnit[ARStackLevel])==0 then // Has not been indexed.
if UnitIndexingFilter(ARStackUnit[ARStackLevel]) then
// Get new index, from recycler first, else new.
// Store the current index on the (new) top level of the AR stack.
if MaxRecycledIndex==0 then // Get new.
set MaxIndex=MaxIndex+1
set ARStackIndex[ARStackLevel]=MaxIndex
else // Get from recycle stack.
set ARStackIndex[ARStackLevel]=RecycledIndex[MaxRecycledIndex]
set MaxRecycledIndex=MaxRecycledIndex-1
endif
// Store index on unit.
call SetUnitUserData(ARStackUnit[ARStackLevel],ARStackIndex[ARStackLevel])
set IndexUnit[ARStackIndex[ARStackLevel]]=ARStackUnit[ARStackLevel]
static if USE_PERIODIC_RECYCLER then
// Add index to recycle list.
set MaxDecayingIndex=MaxDecayingIndex+1
set DecayingIndex[MaxDecayingIndex]=ARStackIndex[ARStackLevel]
else
// Add leave detection ability.
call UnitAddAbility(ARStackUnit[ARStackLevel],LEAVE_DETECTION_ABILITY)
call UnitMakeAbilityPermanent(ARStackUnit[ARStackLevel],true,LEAVE_DETECTION_ABILITY)
endif
// Fire things.
call TriggerEvaluate(OnEnter)
else
// The unit did not pass the filters, so does not need to be auto indexed.
// However, for certain AIDS structs, it may still require indexing.
// These structs may index the unit on their creation.
// We flag that an index must be assigned by setting the current index to 0.
set ARStackIndex[ARStackLevel]=0
// Fire things.
call TriggerEvaluate(OnEnter)
endif
endif
// Decrement the stack.
set ARStackLevel=ARStackLevel-1
return false
endfunction
//==============================================================================
private function InitAIDS takes nothing returns nothing
local region r=CreateRegion()
local group g=CreateGroup()
local integer n=15
static if USE_PERIODIC_RECYCLER then
call TimerStart(UndefendTimer,PERIOD,true,function PeriodicRecycler)
else
local trigger t=CreateTrigger()
loop
call TriggerRegisterPlayerUnitEvent(t,Player(n),EVENT_PLAYER_UNIT_ISSUED_ORDER,Filter(function UndefendFilter))
call SetPlayerAbilityAvailable(Player(n),LEAVE_DETECTION_ABILITY,false)
// Capture "undefend" orders.
exitwhen n==0
set n=n-1
endloop
set n=15
call TriggerAddCondition(t,Filter(function OnUndefend))
set t=null
endif
// This must be done first, due to recursion. :)
call RegionAddRect(r,GetWorldBounds())
call TriggerRegisterEnterRegion(CreateTrigger(),r,Filter(function IndexEnum))
set r=null
loop
call GroupEnumUnitsOfPlayer(g,Player(n),Filter(function IndexEnum))
//Enum every non-filtered unit on the map during initialization and assign it a unique
//index. By using GroupEnumUnitsOfPlayer, even units with Locust can be detected.
exitwhen n==0
set n=n-1
endloop
call DestroyGroup(g)
set g=null
endfunction
//==============================================================================
public struct DEFAULT extends array
method AIDS_onCreate takes nothing returns nothing
endmethod
method AIDS_onDestroy takes nothing returns nothing
endmethod
static method AIDS_filter takes unit u returns boolean
return UnitIndexingFilter(u)
endmethod
static method AIDS_onInit takes nothing returns nothing
endmethod
endstruct
//===========================================================================
// Never create or destroy AIDS structs directly.
// Also, do not initialise members except by using the AIDS_onCreate method.
//===========================================================================
//! textmacro AIDS
// This magic line makes default methods get called which do nothing
// if the methods are otherwise undefined.
private static delegate AIDS_DEFAULT AIDS_DELEGATE=0
//-----------------------------------------------------------------------
// Gotta know whether or not to destroy on deallocation...
private boolean AIDS_instanciated
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns thistype
return GetUnitId(whichUnit)
endmethod
method operator unit takes nothing returns unit
// Allows structVar.unit to return the unit.
return GetIndexUnit(this)
endmethod
//-----------------------------------------------------------------------
method AIDS_addLock takes nothing returns nothing
call AIDS_AddLock(this)
endmethod
method AIDS_removeLock takes nothing returns nothing
call AIDS_RemoveLock(this)
endmethod
//-----------------------------------------------------------------------
private static method AIDS_onEnter takes nothing returns boolean
// At this point, the unit might not have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation.
set thistype(AIDS_GetIndexOfEnteringUnit()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onEnterAllocated takes nothing returns boolean
// At this point, the unit must have been assigned an index.
if thistype.AIDS_filter(AIDS_GetEnteringIndexUnit()) then
// Flag it for destruction on deallocation. Slightly faster!
set thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_instanciated=true
// Can use inlining "Assigned" function now, as it must be assigned.
call thistype(AIDS_GetIndexOfEnteringUnitAllocated()).AIDS_onCreate()
endif
return false
endmethod
private static method AIDS_onDeallocate takes nothing returns boolean
if thistype(AIDS_GetDecayingIndex()).AIDS_instanciated then
call thistype(AIDS_GetDecayingIndex()).AIDS_onDestroy()
// Unflag destruction on deallocation.
set thistype(AIDS_GetDecayingIndex()).AIDS_instanciated=false
endif
return false
endmethod
//-----------------------------------------------------------------------
private static method onInit takes nothing returns nothing
call AIDS_RegisterOnEnter(Filter(function thistype.AIDS_onEnter))
call AIDS_RegisterOnEnterAllocated(Filter(function thistype.AIDS_onEnterAllocated))
call AIDS_RegisterOnDeallocate(Filter(function thistype.AIDS_onDeallocate))
// Because I robbed you of your struct's onInit method.
call thistype.AIDS_onInit()
endmethod
//! endtextmacro
endlibrary
library PUI uses AIDS
//===========================================================================
// Allowed PUI_PROPERTY TYPES are: unit, integer, real, boolean, string
// Do NOT put handles that need to be destroyed here (timer, trigger, ...)
// Instead put them in a struct and use PUI textmacro
//===========================================================================
//! textmacro PUI_PROPERTY takes VISIBILITY, TYPE, NAME, DEFAULT
$VISIBILITY$ struct $NAME$
private static unit array pui_unit
private static $TYPE$ array pui_data
//-----------------------------------------------------------------------
// Returns default value when first time used
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns $TYPE$
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
if .pui_unit[pui] != whichUnit then
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = $DEFAULT$
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, $TYPE$ whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
endmethod
endstruct
//! endtextmacro
//===========================================================================
// Never destroy PUI structs directly.
// Use .release() instead, will call .destroy()
//===========================================================================
//! textmacro PUI
private static unit array pui_unit
private static integer array pui_data
private static integer array pui_id
//-----------------------------------------------------------------------
// Returns zero if no struct is attached to unit
//-----------------------------------------------------------------------
static method operator[] takes unit whichUnit returns integer
local integer pui = GetUnitId(whichUnit) // Changed from GetUnitIndex.
// Switched the next two lines for optimisation.
if .pui_unit[pui] != whichUnit then
if .pui_data[pui] != 0 then
// recycled index detected
call .destroy(.pui_data[pui])
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endif
endif
return .pui_data[pui]
endmethod
//-----------------------------------------------------------------------
// This will overwrite already attached struct if any
//-----------------------------------------------------------------------
static method operator[]= takes unit whichUnit, integer whichData returns nothing
local integer pui = GetUnitIndex(whichUnit)
if .pui_data[pui] != 0 then
call .destroy(.pui_data[pui])
endif
set .pui_unit[pui] = whichUnit
set .pui_data[pui] = whichData
set .pui_id[whichData] = pui
endmethod
//-----------------------------------------------------------------------
// If you do not call release struct will be destroyed when unit handle gets recycled
//-----------------------------------------------------------------------
method release takes nothing returns nothing
local integer pui= .pui_id[integer(this)]
call .destroy()
set .pui_unit[pui] = null
set .pui_data[pui] = 0
endmethod
//! endtextmacro
endlibrary
library AutoIndex uses AIDS
module AutoData
private static thistype array data
// Fixed up the below to use thsitype instead of integer.
static method operator []= takes unit u, thistype i returns nothing
set .data[GetUnitId(u)] = i //Just attaching a struct to the unit
endmethod //using the module's thistype array.
static method operator [] takes unit u returns thistype
return .data[GetUnitId(u)] //Just returning the attached struct.
endmethod
endmodule
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~ KT ~~ Key Timers 2 ~~ By Jesus4Lyf ~~ Version 1.7.1 ~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// What is Key Timers?
// - Key Timers attaches structs to timers, or more to the point timed
// effects.
// - You can specify different periods.
// - Key Timers only uses one timer with one trigger per low period
// to keep things efficient, especially within the looping.
// - Key Timers alternatively uses one trigger per instance for all higher
// periods to allow accurate expirations in a stable and efficient fashion.
//
// =Pros=
// - Easy to use.
// - Fastest attachment loading system (storing in parallel arrays).
// - Fastest execution system for low periods (all functions on one trigger).
// - Allows multiple periods to be used.
// - No H2I. Backwards compatability through patch 1.23b.
//
// =Cons=
// - The code passed into KT2 must call KT_GetData exactly once.
// - Periods must be a multiple of 0.00125 seconds. Not 0.007, for example.
//
// Functions:
// - KT_Add(userFunc, struct, period)
// - KT_GetData returns the struct
//
// - userFunc is to be a user function that takes nothing and returns boolean.
// It will be executed by the system every period until it returns true.
//
// - KT_GetData is to be used inside func, it will return the struct passed to
// to the Add function. It must be called exactly once within the func.
//
// Details:
// - KT2 treats low periods and high periods differently, optimizing each
// with appropriate speed and accuracy, although this effect is invisible
// to you, the mapper.
//
// - While func returns false the timer will continue to call it each period.
// Once func returns true the instance will be detached from system.
//
// Thanks:
// - Daxtreme: For encouraging me to return to Key Timers 2, rather than
// leave it to rot. His interest in the system restored it, and helped
// it to become what it is now. :)
//
// - Captain Griffen: For his work on Rapid Timers, demonstrating that it
// is possible to attach all functions to one trigger, and that it is
// indeed faster.
//
// - Cohadar: Told me to make Key Timers without a textmacro.
// Thanks to him for helping me with the original Key Timers system too.
// Also, I'd like to thank him for his work on Timer Ticker (TT) which
// demonstrated how to use triggers/conditions in this sort of system,
// which has been used in Key Timers 2.
//
// How to import:
// - Create a trigger named KT.
// - Convert it to custom text and replace the whole trigger text with this.
//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
library KT initializer Init
///////////////
// Constants //
////////////////////////////////////////////////////////////////////////////
// That bit that users may play with if they know what they're doing.
// Not touching these at all is recommended.
globals
// Period Threshold is the point at which Key Timers 2 will switch from
// using the single timer per period mechanism to using TAZO, which is
// better for higher periods due to the first tick being accurate.
private constant real PERIODTHRESHOLD=0.3 // MUST be below 10.24 seconds.
// Tazo's Instance Maximum is the number of periods you can have
// which is greater or equal to the Period Threshold. This may be
// removed completely in later versions.
private constant integer TAZO_INSTANCEMAX=64
// Tazo uses the small part of Key Timers 2 to reconstruct triggers
// over time. Here you can set the period. This should synchronise
// with whatever period around 0.03 which you use the most.
// This too, may be removed in future versions.
private constant real TAZO_RECONSTRUCT_PERIOD=0.03125
endglobals
//////////////////////////
// Previous KT2 Globals //
////////////////////////////////////////////////////////////////////////////
// These needed to be moved here for TAZO to hook GetData.
globals
private timer array KeyTimer
private trigger array TimerTrigger
private integer array KeyTimerListPointer
private integer array KeyTimerListEndPointer
private triggercondition array TriggerCond
private boolexpr array Boolexpr
private integer array Data
private integer array Next
private integer array Prev
private integer TrigMax=0
private integer array NextMem
private integer NextMemMaxPlusOne=1
private integer array ToAddMem
private triggercondition array ToRemove
private boolexpr array ToDestroy
private boolean array IsAdd
private integer AddRemoveMax=0
// Locals
private integer t_id=-1
private integer t_mem
private integer t_lastmem
private integer a_id
private integer a_mem
// Code Chunks
private conditionfunc RemoveInstanceCond
endglobals
//////////////////
// Previous KT2 //
////////////////////////////////////////////////////////////////////////////
// The rest of the KT2 implementation
// Globals have been moved to top.
private function KeyTimerLoop takes nothing returns nothing
set t_id=R2I(TimerGetTimeout(GetExpiredTimer())*800)
set t_mem=KeyTimerListEndPointer[t_id]
call TriggerEvaluate(TimerTrigger[t_id])
set t_mem=0
loop
exitwhen t_mem==AddRemoveMax
set t_mem=t_mem+1
if IsAdd[t_mem] then
set TriggerCond[ToAddMem[t_mem]]=TriggerAddCondition(TimerTrigger[t_id],Boolexpr[ToAddMem[t_mem]])
else
call TriggerRemoveCondition(TimerTrigger[t_id],ToRemove[t_mem])
call DestroyBoolExpr(ToDestroy[t_mem])
endif
endloop
set AddRemoveMax=0
set t_id=-1
endfunction
private function RemoveInstance takes nothing returns boolean
// Will only fire if code returns true.
set AddRemoveMax=AddRemoveMax+1
set IsAdd[AddRemoveMax]=false
set ToRemove[AddRemoveMax]=TriggerCond[t_lastmem]
set ToDestroy[AddRemoveMax]=Boolexpr[t_lastmem]
if Next[t_lastmem]==0 then
set KeyTimerListEndPointer[t_id]=Prev[t_lastmem]
endif
set Prev[Next[t_lastmem]]=Prev[t_lastmem]
if Prev[t_lastmem]==0 then
set KeyTimerListPointer[t_id]=Next[t_lastmem]
if KeyTimerListPointer[t_id]<1 then
call PauseTimer(KeyTimer[t_id])
endif
else
set Next[Prev[t_lastmem]]=Next[t_lastmem]
endif
set NextMem[NextMemMaxPlusOne]=t_lastmem
set NextMemMaxPlusOne=NextMemMaxPlusOne+1
return false
endfunction
private function KTadd takes code func, integer data, real period returns nothing
set a_id=R2I(period*800)
if KeyTimer[a_id]==null then
set KeyTimer[a_id]=CreateTimer()
set TimerTrigger[a_id]=CreateTrigger()
endif
if NextMemMaxPlusOne==1 then
set TrigMax=TrigMax+1
set a_mem=TrigMax
else
set NextMemMaxPlusOne=NextMemMaxPlusOne-1
set a_mem=NextMem[NextMemMaxPlusOne]
endif
set Boolexpr[a_mem]=And(Condition(func),RemoveInstanceCond)
if t_id==a_id then
set AddRemoveMax=AddRemoveMax+1
set IsAdd[AddRemoveMax]=true
set ToAddMem[AddRemoveMax]=a_mem
else
if KeyTimerListPointer[a_id]<1 then
call TimerStart(KeyTimer[a_id],a_id/800.0,true,function KeyTimerLoop)
set KeyTimerListEndPointer[a_id]=a_mem
endif
set TriggerCond[a_mem]=TriggerAddCondition(TimerTrigger[a_id],Boolexpr[a_mem])
endif
set Data[a_mem]=data
set Prev[a_mem]=0
set Next[a_mem]=KeyTimerListPointer[a_id]
set Prev[KeyTimerListPointer[a_id]]=a_mem
set KeyTimerListPointer[a_id]=a_mem
endfunction
public function GetData takes nothing returns integer // Gets hooked by TAZO.
set t_lastmem=t_mem
set t_mem=Prev[t_mem]
return Data[t_lastmem]
endfunction
private function KTinit takes nothing returns nothing
set RemoveInstanceCond=Condition(function RemoveInstance)
endfunction
//////////
// TAZO //
////////////////////////////////////////////////////////////////////////////
// KT2 implementation for higher periods (low frequency).
globals
private constant integer TAZO_DATAMEM=8190 // Added for KT2 hook. Don't change.
endglobals
globals
private conditionfunc TAZO_LoadDataCond
private conditionfunc TAZO_RemoveInstanceCond
private integer array TAZO_Data
private boolexpr array TAZO_Boolexpr
private trigger array TAZO_AvailableTrig
private integer TAZO_Max=0
private trigger array TAZO_ReconstructTrig
private integer array TAZO_ReconstructCount
endglobals
globals//locals
private integer TAZO_ReconKey
endglobals
private function TAZO_Reconstructer takes nothing returns boolean
set TAZO_ReconKey=GetData()
call TriggerExecute(TAZO_ReconstructTrig[TAZO_ReconKey])
set TAZO_ReconstructCount[TAZO_ReconKey]=TAZO_ReconstructCount[TAZO_ReconKey]-1
if TAZO_ReconstructCount[TAZO_ReconKey]==0 then
set TAZO_Max=TAZO_Max+1
set TAZO_AvailableTrig[TAZO_Max]=TAZO_ReconstructTrig[TAZO_ReconKey]
return true
endif
return false
endfunction
globals//locals
private trigger TAZO_DeadTrig
private integer TAZO_DeadCount
endglobals
private function TAZO_Reconstruct takes nothing returns boolean
set TAZO_DeadTrig=GetTriggeringTrigger()
set TAZO_DeadCount=GetTriggerExecCount(TAZO_DeadTrig)
call DestroyTrigger(TAZO_DeadTrig)
call DestroyBoolExpr(TAZO_Boolexpr[TAZO_DeadCount])
set TAZO_ReconstructTrig[TAZO_DeadCount]=CreateTrigger()
set TAZO_ReconstructCount[TAZO_DeadCount]=TAZO_DeadCount
call KTadd(function TAZO_Reconstructer,TAZO_DeadCount,TAZO_RECONSTRUCT_PERIOD)
return false
endfunction
private function TAZO_LoadData takes nothing returns boolean
// KT2 Data Hook
set t_mem=TAZO_DATAMEM
set Data[TAZO_DATAMEM]=TAZO_Data[GetTriggerExecCount(GetTriggeringTrigger())]
// End KT2 Data Hook
return false
endfunction
globals//locals
private integer TAZO_AddKey
private trigger TAZO_AddTrigger
endglobals
public function TAZOadd takes code func, integer data, real period returns nothing
set TAZO_AddTrigger=TAZO_AvailableTrig[TAZO_Max]
set TAZO_AddKey=GetTriggerExecCount(TAZO_AddTrigger)
set TAZO_Max=TAZO_Max-1
set TAZO_Data[TAZO_AddKey]=data
set TAZO_Boolexpr[TAZO_AddKey]=And(Condition(func),TAZO_RemoveInstanceCond)
call TriggerAddCondition(TAZO_AddTrigger,TAZO_LoadDataCond)
call TriggerAddCondition(TAZO_AddTrigger,TAZO_Boolexpr[TAZO_AddKey])
call TriggerRegisterTimerEvent(TAZO_AddTrigger,period,true)
endfunction
private function InitTrigExecCount takes trigger t, integer d returns nothing
if d>64 then
call InitTrigExecCount.execute(t,d-64)
set d=64
endif
loop
exitwhen d==0
set d=d-1
call TriggerExecute(t)
endloop
endfunction
private function TAZOinit takes nothing returns nothing
set TAZO_LoadDataCond=Condition(function TAZO_LoadData)
set TAZO_RemoveInstanceCond=Condition(function TAZO_Reconstruct)
// Allow for GetData
set Next[TAZO_DATAMEM]=TAZO_DATAMEM
set Prev[TAZO_DATAMEM]=TAZO_DATAMEM
// End allow for GetData
loop
set TAZO_Max=TAZO_Max+1
set TAZO_AvailableTrig[TAZO_Max]=CreateTrigger()
call InitTrigExecCount.execute(TAZO_AvailableTrig[TAZO_Max],TAZO_INSTANCEMAX+1-TAZO_Max)
exitwhen TAZO_Max==TAZO_INSTANCEMAX
endloop
endfunction
///////////////
// Interface //
////////////////////////////////////////////////////////////////////////////
// Stitches it all together neatly.
public function Add takes code func, integer data, real period returns nothing
if period<PERIODTHRESHOLD then
call KTadd(func,data,period)
else
call TAZOadd(func,data,period)
endif
endfunction
private function Init takes nothing returns nothing
call KTinit()
call TAZOinit()
endfunction
endlibrary
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// End of Key Timers 2
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//TESH.scrollpos=0
//TESH.alwaysfold=0
//
// _____ _____
// __ __ ___ _ ____\_ \ / /
// / |/ |/ _ \ \ / / __| \ / /
// / / | / | (_) | \/ /| __| \ \/ /
// /_/|__/|_|\___/_\__/_|____|_ \ /
// / __\ _ \ __| __| \ / \ By Jesus4Lyf
// \__ \ __/ __| __| |) | /\ \
// \___/_| |____|____|___/ / \ \ v 1.0.1
// /____/ \____\
// What is MoveSpeedX?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// MoveSpeedX allows you to modify a unit's movement speed, either by a
// factor of its current speed or by a pure amount. It is possible to disobey
// the 522 movement speed limit in Warcraft III in this way.
//
// Please note that setting the speed too high will bug waypoints.
//
// How to implement?
// ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
// Create a new trigger object called MoveSpeedX, go to 'Edit -> Convert to
// Custom Text', and replace everything that's there with this script.
//
// Functions:
// ¯¯¯¯¯¯¯¯¯¯¯¯
// function MSX_AddSpeedPure takes unit whichUnit, real speed returns nothing
// function MSX_RemoveSpeedPure takes unit whichUnit, real speed returns nothing
// - Adds or removes a pure amount of speed from the unit.
//
// function MSX_AddSpeedFactor takes unit whichUnit, real factor returns nothing
// function MSX_RemoveSpeedFactor takes unit whichUnit, real factor returns nothing
// - Adds or removes a factor of speed from the unit.
// For example, to add 10% movement speed to unit u, you would
// call MS_AddSpeedFactor(u, 0.1).
// - This stacks incrementally. So +10% +10% = +20%.
// - This factor does not increase pure speed, only the unit's
// speed outside of this system (so it does include buffs).
//
// function MSX_GetMoveSpeed takes unit whichUnit returns real
// - Returns the unit's move speed, inclusive of MoveSpeedX bonuses.
//
// Thanks:
// ¯¯¯¯¯¯¯¯¯
// - Frozenhelfir for the idea.
//
library MSX uses AIDS, KT
globals
//=================================================================
// This is the period on which all units will be run.
// If you lower this value, movement bonuses will be smoother,
// but will require more processing power (lag more).
// Also, the lower this is, the higher the move speed can be
// before it starts bugging on waypoints. The lowest valid
// period is 0.00125. A period of 0.00625 is very robust.
private constant real PERIOD=0.00625
//=================================================================
endglobals
//=================================================================
// This is a filter that determines whether or not a unit may have
// its move speed modified. Units that pass this filter will be
// indexed by AIDS if they have not already been.
private function VALID_UNITS_FILTER takes unit u returns boolean
return GetUnitAbilityLevel(u,'Aloc')==0 and not IsUnitType(u,UNIT_TYPE_STRUCTURE)
endfunction
//=================================================================
globals//locals
private unit Unit
private real X
private real Y
private real Xinc
private real Yinc
private real Dist
endglobals
private struct SpeedData extends array
real pureBonus
real factorBonus
private boolean remove
private boolean onTimer
private real x
private real y
private static method AIDS_filter takes unit u returns boolean
return VALID_UNITS_FILTER(u)
endmethod
private method AIDS_onCreate takes nothing returns nothing
set this.pureBonus=0.0
set this.factorBonus=0.0
endmethod
private method AIDS_onDestroy takes nothing returns nothing
set this.remove=true
endmethod
//! runtextmacro AIDS()
private static method periodic takes nothing returns boolean
local thistype this=thistype(KT_GetData())
if this.remove then
set this.onTimer=false
return true
endif
set Unit=this.unit
set X=GetUnitX(Unit)
set Y=GetUnitY(Unit)
if (not IsUnitPaused(Unit)) and GetUnitAbilityLevel(Unit,'BSTN')==0 and GetUnitAbilityLevel(Unit,'BPSE')==0 then
if X!=this.x or Y!=this.y then
set Xinc=X-this.x
set Yinc=Y-this.y
if this.factorBonus!=0.0 then
set X=X+Xinc*this.factorBonus
set Y=Y+Yinc*this.factorBonus
endif
if this.pureBonus!=0.0 then
set Dist=SquareRoot(Xinc*Xinc+Yinc*Yinc)
set X=X+Xinc/Dist*this.pureBonus
set Y=Y+Yinc/Dist*this.pureBonus
endif
call SetUnitX(Unit,X)
call SetUnitY(Unit,Y)
endif
endif
set this.x=X
set this.y=Y
return false
endmethod
method checkTimer takes nothing returns nothing
if not this.onTimer then
call KT_Add(function thistype.periodic,this,PERIOD)
set this.onTimer=true
set this.remove=false
set this.x=GetUnitX(this.unit)
set this.y=GetUnitY(this.unit)
endif
if this.pureBonus==0.0 and this.factorBonus==0.0 then
set this.remove=true
endif
endmethod
endstruct
public function AddSpeedPure takes unit u, real speed returns nothing
local SpeedData d=SpeedData[u]
set d.pureBonus=d.pureBonus+speed*PERIOD
call d.checkTimer()
endfunction
public function SetPureSpeed takes unit u, real speed returns nothing
local SpeedData d=SpeedData[u]
set d.pureBonus=speed*PERIOD
call d.checkTimer()
endfunction
public function RemoveSpeedPure takes unit u, real speed returns nothing
local SpeedData d=SpeedData[u]
set d.pureBonus=d.pureBonus-speed*PERIOD
call d.checkTimer()
endfunction
public function RemoveAllSpeed takes unit u returns nothing
local SpeedData d=SpeedData[u]
set d.pureBonus=0.0
call d.checkTimer()
endfunction
public function AddSpeedFactor takes unit u, real factor returns nothing
local SpeedData d=SpeedData[u]
set d.factorBonus=d.factorBonus+factor
call d.checkTimer()
endfunction
public function RemoveSpeedFactor takes unit u, real factor returns nothing
local SpeedData d=SpeedData[u]
set d.factorBonus=d.factorBonus-factor
call d.checkTimer()
endfunction
public function GetMoveSpeed takes unit u returns real
local SpeedData d=SpeedData[u]
return GetUnitMoveSpeed(u)*(1.0+d.factorBonus)+d.pureBonus
endfunction
public function GetMoveSpeed1 takes unit u returns real
local SpeedData d=SpeedData[u]
return GetUnitMoveSpeed(u)+d.pureBonus/PERIOD
endfunction
endlibrary
//TESH.scrollpos=0
//TESH.alwaysfold=0
Condense and Surf spells, made by me, Strikest. You can import these spells into your map as long as you credit me.
1. Copy the required systems folder and this spells folder to your map. Optionally copy the Optional systems, if you want insane movespeed while surfing.
2. Export the Bubbles.mdx, Dust.mdx, SlideWater.mdx, WaterBlast.mdx, and OrbWaterX.mdx models to your map. All other models and icons are optional.
3. Copy the Puddle, and Orb Water Dummy units to the map. You can also optionally copy the Dumb Footman unit, or use your own dummy unit if you already have one.
4. Copy the Surf Damage, Surf Slow, Condense Magic Amp, and Surf Move Speed Bonus abilities to your map. You can also optionally copy the dummy abilities for Surf and Condense, or make your own.
5. If you decided to copy the optional libraries, save your map, then close and reopen it, then go to the AIDS trigger, and delete this line: //! external ObjectMerger w3a Adef AIDS anam "State Detection" ansf "(AIDS)" aart "" arac 0
6. Go to the KBS trigger, and change the raw code of the dummy to the one in your map.
7. Go to the Surf trigger, and change the raw codes to the ones in your map.
8. Go to the Condense trigger, and change the raw codes to the ones in your map.
9. Delete these instructions. Or keep it, if you really like my words :D
//TESH.scrollpos=0
//TESH.alwaysfold=0
-Grendel for the HeroSealedPython model and its portrait.
DonDustin for the BlueBasiliskMissile.
MajorSonnWaitts for the Bubbles model.
Frankster for the OrbWaterX model.
kenny for rhe KBS system, Dust and SlideWater models.
JesusHipster for rhe WaterBlast model.
Vexorian for vJass and TimerUtils.
Jesus4Lyf for AIDS, KT, and MSX
PeeKay for the Condense icon.
67chrome for the Hero icon.
//TESH.scrollpos=99
//TESH.alwaysfold=0
scope SurfS initializer OnInit
//Surf by Strikest
//Requires TimerUtils by Vexorian(http://www.wc3c.net/showthread.php?t=101322), KBS by kenny(http://www.thehelper.net/threads/knockback-system-kbs.96837/)
//Optionally uses AIDS,KT, and MSX by Jesus4Lyf(http://www.thehelper.net/threads/movespeedx.119384/)
globals
private constant integer ABIL_ID = 'A002' //Raw code of ability.
private constant integer UNIT_ID = 'n000' //Raw code of the puddle dummy unit.
private constant integer MOVE_ID = 'A003' //Raw code of the movement speed bonus ability.
private constant integer SLOW_ID = 'S000' //Raw code of the slow aura ability.
private constant integer DMG_ID = 'A001' //Raw code of the damage over time ability.
private constant integer ORDER_ID = 852177 //Order ID of the damage over time ability.
private constant string EFFECT = "Abilities\\Spells\\Other\\CrushingWave\\CrushingWaveMissile.mdl" //Path of the desired special effect you want attached to the caster.
private constant string ATTACH = "origin" //String of the desired attachment point for the caster.
private constant string KB_SFX = "war3mapImported\\SlideWater.mdx" //Path of the desired special effect you want attached to knocked back units.
private constant real KB_AOE = 170. //Area of effect around caster that units will be knocked back.
private constant real KB_DISTANCE = 124. //Distance the enemy will be knocked back.
private constant real MINIMUM_DISTANCE = 30. //Minimum distance required for another puddle to spawn.
private constant real TIMER_INTERVAL = 50. //Timer interval.
private constant real KB_DURATION = 0.6 //Duration of knockback.
private constant boolean ALLOW_NO_COLLISION = false //Choose whether or not you want to turn collision off for the caster.
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.
endglobals
private constant function DURATION takes integer level returns integer
return 12 + level * 2
endfunction
private constant function BONUS_SPEED takes integer level returns real //Amount of speed gained through MSX trigger. Requires MSX. Be careful with this, if the value is too high, it might bug.
return 500.
endfunction
private constant function FILTER_UNITS takes unit u returns boolean //Filter which units will get knocked back.
return IsUnitEnemy( u, GetOwningPlayer( CASTER ) )
endfunction
//////////////////////////////////////////////////////NO TOUCHY PAST THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING/////////////////////////////////////////////////////////////////////////////
private struct Surf
unit cast
real x
real y
integer dur
effect attach
endstruct
native UnitAlive takes unit id returns boolean
private function FilterActions takes nothing returns boolean
local unit u = GetFilterUnit( )
local real x1 = GetUnitX( CASTER )
local real y1 = GetUnitY( CASTER )
local real x2 = GetUnitX( u )
local real y2 = GetUnitY( u )
local real a = bj_RADTODEG * Atan2( y2 - y1, x2 - x1 )
if UnitAlive(u) then
if FILTER_UNITS(u) then
call KBS_BeginEx( u, KB_DISTANCE, KB_DURATION, a, KB_SFX, TREE_AOE, ALLOW_MOVE, CHECK_PATHING )
endif
endif
set u = null
return false
endfunction
private function Handler takes nothing returns nothing
local timer t = GetExpiredTimer( )
local Surf data = GetTimerData( t )
local real x1 = data.x
local real y1 = data.y
local real x2 = GetUnitX( data.cast )
local real y2 = GetUnitY( data.cast )
local real d = ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 )
local unit u
if data.dur <= 0 then
call DestroyEffect( data.attach )
if ALLOW_NO_COLLISION then
call SetUnitPathing( data.cast, true )
endif
static if LIBRARY_MSX then
call MSX_RemoveAllSpeed( data.cast )
endif
call UnitRemoveAbility( data.cast, MOVE_ID )
call data.destroy( )
call ReleaseTimer( t )
else
set CASTER = data.cast
call GroupEnumUnitsInRange( GROUP, GetUnitX( data.cast ), GetUnitY( data.cast ), KB_AOE, Filter( function FilterActions ) )
if d >= MINIMUM_DISTANCE * MINIMUM_DISTANCE then
set u = CreateUnit( GetOwningPlayer( data.cast ), UNIT_ID, x2, y2, 0. )
call SetUnitAbilityLevel( u, SLOW_ID, GetUnitAbilityLevel( data.cast, ABIL_ID ) )
call SetUnitAbilityLevel( u, DMG_ID, GetUnitAbilityLevel( data.cast, ABIL_ID ) )
call IssueImmediateOrderById( u, ORDER_ID )
call UnitApplyTimedLife( u, 'BTLF', ( I2R( data.dur ) * .02 ) )
set data.x = x2
set data.y = y2
endif
set data.dur = data.dur - 1
call SetTimerData( t, data )
call TimerStart( t, 1./TIMER_INTERVAL, false, function Handler )
endif
set t = null
set u = null
endfunction
private function Actions takes nothing returns nothing
local Surf data = Surf.create( )
set data.cast = GetTriggerUnit( )
set data.x = GetUnitX( data.cast )
set data.y = GetUnitY( data.cast )
set data.attach = AddSpecialEffectTarget( EFFECT, data.cast, ATTACH )
set data.dur = 50 * DURATION( GetUnitAbilityLevel( data.cast, ABIL_ID ) )
call UnitAddAbility( data.cast, MOVE_ID )
static if LIBRARY_MSX then
call MSX_SetPureSpeed( data.cast, BONUS_SPEED( GetUnitAbilityLevel( data.cast, ABIL_ID ) ) )
endif
if ALLOW_NO_COLLISION then
call SetUnitPathing( data.cast, false )
endif
call TimerStart(NewTimerEx(data), 1/TIMER_INTERVAL, false, function Handler )
endfunction
private function OnSpell takes nothing returns boolean
if GetSpellAbilityId( ) == ABIL_ID then
call Actions( )
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
//TESH.scrollpos=140
//TESH.alwaysfold=0
scope Condense initializer OnInit
//Condense by Strikest
//Requires TimerUtils by Vexorian(http://www.wc3c.net/showthread.php?t=101322)
globals
//Configurables
private constant integer ABIL_ID = 'A000' //Raw code of ability
private constant integer DBFF_ID = 'A004' //Raw code of magic amplification ability
private constant integer ORB_ID = 'h001' //Raw code of the water orb dummy
private constant integer CROW_ID = 'Amrf' //Raw code of Crow Form ability in your map. Should be 'Amrf', unless you modified it.
private constant string EFFECT = "Objects\\Spawnmodels\\Naga\\NagaDeath\\NagaDeath.mdl" //Path of the special effect model when orb makes contact
private constant string ATTACH_POINT = "origin" //String of the attachment point you want
private constant boolean BOOLEAN_INCLUDE_GREEN_INT = true //Choose whether or not to include green int bonuses in damage calculations( If factoring int ).
private constant real AOE = 116. //Area of effect of the orb
private constant real ORB_DISTANCE = 400. //Distance of orb away from caster.
private constant real ROTATION_SPEED = .03125 //Lower number = faster orb rotation, and vice versa.
private constant attacktype ATK_TYPE = ATTACK_TYPE_NORMAL //Attacktype you want
private constant damagetype DMG_TYPE = DAMAGE_TYPE_NORMAL //Damagetype you want
private constant group GROUP = CreateGroup( )//Dont touch this
private group GROUP2 //Or this
private unit CASTER //Or this
private integer CAST_LVL //Nor this
endglobals
private constant function DAMAGE takes integer levelofpassive, integer levelofcastspell, integer heroint returns real //Configure damage in this function
return levelofcastspell * heroint + 0.
endfunction
private constant function AMOUNT_OF_REVOLUTIONS takes integer levelofpassive, integer levelofcastspell returns integer //Configure how many times the orb will make a complete revolution around the caster
return levelofcastspell
endfunction
private constant function DURATION_NORMAL takes integer levelofpassive, integer levelofcastspell, integer heroint returns real //Configure the duration of the debuff for non - hero units
return 10.
endfunction
private constant function DURATION_HERO takes integer levelofpassive, integer levelofcastspell, integer heroint returns real //Configure the duration of the debuff for hero units
return 5.
endfunction
private constant function FILTER_UNITS takes unit u returns boolean //Filter which units will get affected by the orb.
return IsUnitEnemy( u, GetOwningPlayer( CASTER ) ) and not IsUnitInGroup( u, GROUP2 ) and not IsUnitType( u, UNIT_TYPE_MAGIC_IMMUNE )
endfunction
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~NO TOUCHY PAST THIS POINT UNLESS YOU KNOW WHAT YOU ARE DOING~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
private struct Cdnse
unit cast
unit orb
integer lvl
integer dur
real a
group g
static method create takes nothing returns Cdnse
local Cdnse data = Cdnse.allocate( )
if data.g == null then
set data.g = CreateGroup( )
endif
return data
endmethod
method onDestroy takes nothing returns nothing
call GroupClear( .g )
endmethod
endstruct
private struct Dbuff
unit u = null
endstruct
native UnitAlive takes unit id returns boolean
private function Handler2 takes nothing returns nothing
local timer t = GetExpiredTimer( )
local Dbuff data = GetTimerData( t )
call UnitRemoveAbility( data.u, DBFF_ID )
call data.destroy( )
call ReleaseTimer( t )
set t = null
endfunction
private function FilterActions takes nothing returns boolean
local unit u = GetFilterUnit( )
local integer i = GetUnitAbilityLevel( CASTER, ABIL_ID )
local integer int = GetHeroInt( CASTER, BOOLEAN_INCLUDE_GREEN_INT )
local real damage = DAMAGE( i, CAST_LVL, int )
local Dbuff data
local timer t
if UnitAlive(u) then
if FILTER_UNITS(u) then
call UnitDamageTarget( CASTER, u, damage, false, false, ATK_TYPE, DMG_TYPE, WEAPON_TYPE_WHOKNOWS )
call GroupAddUnit( GROUP2, u )
call DestroyEffect( AddSpecialEffectTarget( EFFECT, u, ATTACH_POINT ) )
//If you are capable enough, you can add any extra effects you want here
if GetUnitAbilityLevel( u, DBFF_ID ) != 1 then
call UnitAddAbility( u, DBFF_ID )
set data = Dbuff.create( )
set data.u = u
set t = NewTimer( )
call SetTimerData( t, data )
if IsUnitType( u, UNIT_TYPE_HERO ) then
call TimerStart( t, DURATION_HERO( i, CAST_LVL, int ), false, function Handler2 )
else
call TimerStart( t, DURATION_NORMAL( i, CAST_LVL, int ), false, function Handler2 )
endif
endif
endif
endif
set u = null
set t = null
return false
endfunction
private function Handler takes nothing returns nothing
local timer t = GetExpiredTimer( )
local Cdnse data = GetTimerData( t )
local real x
local real y
if data.dur <= 0 then
call RemoveUnit( data.orb )
call data.destroy( )
call ReleaseTimer( t )
else
set data.a = data.a + 4.
set x = GetUnitX( data.cast ) + 400. * Cos( data.a * bj_DEGTORAD )
set y = GetUnitY( data.cast ) + 400. * Sin( data.a * bj_DEGTORAD )
call SetUnitX( data.orb, x )
call SetUnitY( data.orb, y )
if GetUnitFlyHeight( data.cast ) > 0 then
call SetUnitFlyHeight( data.orb, GetUnitFlyHeight( data.cast ), 0 )
endif
set CASTER = data.cast
set CAST_LVL = data.lvl
set GROUP2 = data.g
call GroupEnumUnitsInRange( GROUP, x, y, AOE, Filter( function FilterActions ) )
set data.dur = data.dur - 4
call SetTimerData( t, data )
call TimerStart( t, ROTATION_SPEED, false, function Handler )
endif
set t = null
endfunction
private function Actions takes nothing returns nothing
local unit cast = GetTriggerUnit( )
local real casx = GetUnitX( cast )
local real casy = GetUnitY( cast )
local real a = GetRandomReal( 0., 360. ) + GetUnitFacing( cast )
local real tarx
local real tary
local timer t = NewTimer( )
local Cdnse data = Cdnse.create( )
set tarx = casx + ORB_DISTANCE * Cos( a * bj_DEGTORAD )
set tary = casy + ORB_DISTANCE * Sin( a * bj_DEGTORAD )
set data.cast = cast
set data.lvl = GetUnitAbilityLevel( cast, GetSpellAbilityId( ) )
set data.dur = 360 * AMOUNT_OF_REVOLUTIONS( GetUnitAbilityLevel( cast, ABIL_ID ), data.lvl )
set data.a = a
set data.orb = CreateUnit( GetOwningPlayer( cast ), ORB_ID, tarx, tary, 0.0 )
call SetUnitPathing( data.orb, false )
call UnitAddAbility( data.orb, CROW_ID )
call SetTimerData( t, data )
call TimerStart( t, ROTATION_SPEED, false, function Handler )
set cast = null
set t = null
endfunction
private function OnSpell takes nothing returns boolean
if GetUnitAbilityLevel( GetTriggerUnit( ), ABIL_ID ) != 0 then
call Actions( )
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