//*************************************************************************************************
// Caster System Free Attach Variables / Sets and Tables (from 12.7)
//
// Requirement: gamecache global variable udg_cscache
//
//*************************************************************************************************
//##Begin of CS Gamecache engine##
//=================================================================================================
// GameCache - Return bug module : Without gamecache or return bug, JASS would be a
// retarded-limited scripting language.
//
//=================================================================================================
// a.k.a H2I, changed name to CS_H2I to prevent conflicts with other systems (I intended this
// system to be easy to copy
//
function CS_H2I takes handle h returns integer
return h
return 0
endfunction
//=================================================================================================
// Main Gamecache handler
//
function CSCache takes nothing returns gamecache
if udg_cscache==null then
call FlushGameCache(InitGameCache("CasterSystem.vx"))
set udg_cscache=InitGameCache("CasterSystem.vx")
call StoreInteger(udg_cscache,"misc","TableMaxReleasedIndex",100)
endif
return udg_cscache
endfunction
//==================================================================================================
// Attachable vars : Attacheable variables are what most other people call Handle Variables, they
// allow to relate data with any handle, using a label, and its value, the stuff auto flushes if
// the value is 0, false, "", or null .
//
// Differences between Attacheable variables and "Local Handle Variables" :
// - The names of the functions
// - The name of the function group does not cause confusion, it is difficult to say:
// "you should set local handle variables to null at the end of a function" since
// it sounds as if you were talking about the "Local Handle Variables"
// - Also Have Attacheable Sets.
// - And can work together with Tables.
//
// Notes: don't "attach" variables on texttags nor those handle types used mostly for parameters
// (for example damagetype) , Although there is no reason to do so anyways
//
// Gamecache stuff are NOT Case Sensitive, don't ever use "" for label (Crashes game!)
//
//============================================================================================================
// For integers
//
function AttachInt takes handle h, string label, integer x returns nothing
local string k=I2S(CS_H2I(h))
if x==0 then
call FlushStoredInteger(CSCache(),k,label)
else
call StoreInteger(CSCache(),k,label,x)
endif
endfunction
function GetAttachedInt_FromSet takes handle h, gamecache g returns integer
return GetStoredInteger(g,I2S(CS_H2I(h))+";"+GetStoredString(g,"argpass","set"),GetStoredString(g,"argpass","seti"))
endfunction
function GetAttachedInt takes handle h, string label returns integer
if (label=="") then
return GetAttachedInt_FromSet(h,CSCache())
endif
return GetStoredInteger(CSCache(), I2S(CS_H2I(h)), label)
endfunction
//=============================================================================================================
function AttachReal takes handle h, string label, real x returns nothing
local string k=I2S(CS_H2I(h))
if x==0 then
call FlushStoredReal(CSCache(),k,label)
else
call StoreReal(CSCache(),k,label,x)
endif
endfunction
function GetAttachedReal takes handle h, string label returns real
return GetStoredReal(CSCache(),I2S(CS_H2I(h)),label)
endfunction
//=============================================================================================================
function AttachBoolean takes handle h, string label, boolean x returns nothing
local string k=I2S(CS_H2I(h))
if not x then
call FlushStoredBoolean(CSCache(),k,label)
else
call StoreBoolean(CSCache(),k,label,x)
endif
endfunction
function GetAttachedBoolean takes handle h, string label returns boolean
return GetStoredBoolean(CSCache(),I2S(CS_H2I(h)),label)
endfunction
//=============================================================================================================
function AttachString takes handle h, string label, string x returns nothing
local string k=I2S(CS_H2I(h))
if x=="" then
call FlushStoredString(CSCache(),k,label)
else
call StoreString(CSCache(),k,label,x)
endif
endfunction
function GetAttachedString takes handle h, string label returns string
return GetStoredString(CSCache(),I2S(CS_H2I(h)),label)
endfunction
//=============================================================================================================
function AttachObject takes handle h, string label, handle x returns nothing
local string k=I2S(CS_H2I(h))
if (x==null) then
call FlushStoredInteger(CSCache(),k,label)
else
call StoreInteger(CSCache(),k,label,CS_H2I(x))
endif
endfunction
function GetAttachedObject takes handle h, string label returns handle
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedWidget takes handle h, string label returns widget
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedRect takes handle h, string label returns rect
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedRegion takes handle h, string label returns region
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedTimerDialog takes handle h, string label returns timerdialog
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedUnit takes handle h, string label returns unit
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedItem takes handle h, string label returns item
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedEffect takes handle h, string label returns effect
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedDestructable takes handle h, string label returns destructable
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedTrigger takes handle h, string label returns trigger
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedTimer takes handle h, string label returns timer
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedGroup takes handle h, string label returns group
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedTriggerAction takes handle h, string label returns triggeraction
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedLightning takes handle h, string label returns lightning
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedImage takes handle h, string label returns image
return GetAttachedInt(h,label)
return null
endfunction
function GetAttachedUbersplat takes handle h, string label returns ubersplat
return GetAttachedInt(h,label)
return null
endfunction
//============================================================================================================
// Attached Sets: Attachable Sets are handy in some situations and are a part of attachable variables,
// you can add integers or objects to a set, order doesn't matter and adding the same object twice is
// meaningless. CleanAttachedVars is always ready to clean every set owned by the handle.
//
//============================================================================================================
function AttachedSetAddInt takes handle h, string setn, integer int returns nothing
local gamecache g=CSCache()
local string k=I2S(CS_H2I(h))
local integer n
local integer x=GetStoredInteger(g,k,"#setnumberof;"+setn)
local integer y
if x==0 then
set y=GetStoredInteger(g,k,"#totalsets")+1
call StoreInteger(g,k,"#totalsets",y)
call StoreInteger(g,k,"#setnumberof;"+setn,y)
call StoreString(g,k,"#setName;"+I2S(y),setn)
endif
set k=k+";"+setn
if not HaveStoredInteger(g,k,"Pos"+I2S(int)) then
set n=GetStoredInteger(g,k,"n")+1
call StoreInteger(g,k,"n",n)
call StoreInteger(g,k,I2S(n),int)
call StoreInteger(g,k,"Pos"+I2S(int),n)
endif
set g=null
endfunction
function AttachedSetAddObject takes handle h, string setn, handle val returns nothing
call AttachedSetAddInt(h,setn,CS_H2I(val))
endfunction
//============================================================================================================
function AttachedSetHasInt takes handle h, string setn, integer int returns boolean
return HaveStoredInteger(CSCache(),I2S(CS_H2I(h))+";"+setn,"Pos"+I2S(int))
endfunction
function AttachedSetHasObject takes handle h, string setn, handle val returns boolean
return AttachedSetHasInt(h,setn,CS_H2I(val))
endfunction
//============================================================================================================
function GetAttachedSetSize takes handle h, string setn returns integer
return GetStoredInteger(CSCache(),I2S(CS_H2I(h))+";"+setn,"n")
endfunction
//============================================================================================================
function AttachedSetRemInt takes handle h, string setn, integer int returns nothing
local gamecache g=CSCache()
local string k=I2S(CS_H2I(h))+";"+setn
local integer n
local integer x
local integer y
if HaveStoredInteger(g,k,"Pos"+I2S(int)) then
set x=GetStoredInteger(g,k,"Pos"+I2S(int))
set n=GetStoredInteger(g,k,"n")
if x!=n then
set y=GetStoredInteger(g,k,I2S(n))
call StoreInteger(g,k,I2S(x),y)
call StoreInteger(g,k,"Pos"+I2S(y),x)
endif
call FlushStoredInteger(g,k,"Pos"+I2S(int))
call FlushStoredInteger(g,k,I2S(n))
call StoreInteger(g,k,"n",n-1)
endif
set g=null
endfunction
function AttachedSetRemObject takes handle h, string setn, handle val returns nothing
call AttachedSetRemInt(h,setn,CS_H2I(val))
endfunction
//============================================================================================================
function FromSetElement takes string setn, integer index returns string
local gamecache g=CSCache()
call StoreString(g,"argpass","set",setn)
call StoreString(g,"argpass","seti",I2S(index))
set g=null
return ""
endfunction
//============================================================================================================
function ClearAttachedSet takes handle h, string setn returns nothing
call FlushStoredMission(CSCache(),I2S(CS_H2I(h))+";"+setn)
endfunction
function CleanAttachedVars takes handle h returns nothing
local gamecache g=CSCache()
local string k=I2S(CS_H2I(h))
local integer n=GetStoredInteger(g,k,"#totalsets")
local integer i=1
loop
exitwhen i>n
call FlushStoredMission(g,k+";"+GetStoredString(g,k,"#setName;"+I2S(i)))
set i=i+1
endloop
call FlushStoredMission(g, k )
set g=null
endfunction
function CleanAttachedVars_NoSets takes handle h returns nothing
call FlushStoredMission(CSCache(), I2S(CS_H2I(h)) )
endfunction
//=============================================================================================
// Tables
//
// Tables are lame, the real name would be hash tables, they are just abbreviated usage
// of gamecache natives with the addition that you can also Copy the values of a table to
// another one, but don't expect it to be automatic, it must use a FieldData object to know
// which fields and of wich types to copy, Copying a table to another, with a lot of Fields,
// should surelly be lag friendly.
//
// The other thing about tables is that I can say that the Attached variables of a handle work
// inside a table and GetAttachmentTable which is just return bug and I2S , works to allow you
// to manipulate a handle's attached variables through a table.
//
// NewTable and DestroyTable were created to allow to create tables in the fly, but you can
// simply use strings for tables, but place the table names should be between "("")" for example
// "(mytable)" to avoid conflicts with other caster system stuff.
//
function NewTableIndex takes nothing returns integer
local gamecache g=CSCache()
local integer n=GetStoredInteger(g,"misc","FreeTableTotal")
local integer i
if (n>0) then
set i=GetStoredInteger(g,"misc","FreeTable1")
if (n>1) then
call StoreInteger(g,"misc","FreeTable1", GetStoredInteger(g,"misc","FreeTable"+I2S(n)) )
call FlushStoredInteger(g,"misc","FreeTable"+I2S(n))
endif
call StoreInteger(g,"misc","FreeTableTotal", n-1)
else
set i=GetStoredInteger(g,"misc","TableMaxReleasedIndex")+1
call StoreInteger(g,"misc","TableMaxReleasedIndex",i)
endif
call StoreBoolean(g,"misc","Created"+I2S(i),true)
set g=null
return i
endfunction
function NewTable takes nothing returns string
return I2S(NewTableIndex())
endfunction
function GetAttachmentTable takes handle h returns string
return I2S(CS_H2I(h))
endfunction
//============================================================================================================
function DestroyTable takes string table returns nothing
local gamecache g=CSCache()
local integer i=S2I(table)
local integer n
if (i!=0) and (GetStoredBoolean(g,"misc","Created"+table)) then
call FlushStoredBoolean(g,"misc","Created"+table)
set n=GetStoredInteger(g,"misc","FreeTableTotal")+1
call StoreInteger(g,"misc","FreeTableTotal",n)
call StoreInteger(g,"misc","FreeTable"+I2S(n),i)
endif
call FlushStoredMission(g,table)
set g=null
endfunction
//============================================================================================================
function ClearTable takes string table returns nothing
call FlushStoredMission(CSCache(),table)
endfunction
//============================================================================================================
function SetTableInt takes string table, string field, integer val returns nothing
local gamecache g=CSCache()
if (val==0) then
call FlushStoredInteger(g,table,field)
else
call StoreInteger(g,table,field,val)
endif
set g=null
endfunction
function GetTableInt takes string table, string field returns integer
return GetStoredInteger(CSCache(),table,field)
endfunction
//============================================================================================================
function SetTableReal takes string table, string field, real val returns nothing
local gamecache g=CSCache()
if (val==0) then
call FlushStoredReal(g,table,field)
else
call StoreReal(g,table,field,val)
endif
set g=null
endfunction
function GetTableReal takes string table, string field returns real
return GetStoredReal(CSCache(),table,field)
endfunction
//============================================================================================================
function SetTableBoolean takes string table, string field, boolean val returns nothing
local gamecache g=CSCache()
if (not(val)) then
call FlushStoredBoolean(g,table,field)
else
call StoreBoolean(g,table,field,val)
endif
set g=null
endfunction
function GetTableBoolean takes string table, string field returns boolean
return GetStoredBoolean(CSCache(),table,field)
endfunction
//============================================================================================================
function SetTableString takes string table, string field, string val returns nothing
local gamecache g=CSCache()
if (val=="") or (val==null) then
call FlushStoredString(g,table,field)
else
call StoreString(g,table,field,val)
endif
set g=null
endfunction
function GetTableString takes string table, string field returns string
return GetStoredString(CSCache(),table,field)
endfunction
//============================================================================================================
// You may ask why am I using thousands of functions instead of multi-use return bug exploiters? Well,
// these make the thing much easier to read (in my opinion) and it is also better in performance since we
// have less function calls (H2U(GetTableObject("table","unit"))) would be worse than GetTableUnit that is
// quite direct.
//
function SetTableObject takes string table, string field, handle val returns nothing
local gamecache g=CSCache()
if (val==null) then
call FlushStoredInteger(g,table,field)
else
call StoreInteger(g,table,field,CS_H2I(val))
endif
set g=null
endfunction
function GetTableObject takes string table, string field returns handle
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableWidget takes string table, string field returns widget
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableRect takes string table, string field returns rect
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableRegion takes string table, string field returns region
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableTimerDialog takes string table, string field returns timerdialog
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableUnit takes string table, string field returns unit
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableItem takes string table, string field returns item
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableEffect takes string table, string field returns effect
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableDestructable takes string table, string field returns destructable
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableTrigger takes string table, string field returns trigger
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableTimer takes string table, string field returns timer
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableGroup takes string table, string field returns group
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableTriggerAction takes string table, string field returns triggeraction
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableLightning takes string table, string field returns lightning
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableImage takes string table, string field returns image
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
function GetTableUbersplat takes string table, string field returns ubersplat
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
//============================================================================================================
// Returns true if the fiel contains a value different from 0, false, null, or "" (depending on the type)
// it is worthless to use this with boolean, since it would be the same as reading the boolean value
//
function HaveSetField takes string table, string field, integer fieldType returns boolean
if (fieldType == bj_GAMECACHE_BOOLEAN) then
return HaveStoredBoolean(CSCache(),table,field)
elseif (fieldType == bj_GAMECACHE_INTEGER) then
return HaveStoredInteger(CSCache(),table,field)
elseif (fieldType == bj_GAMECACHE_REAL) then
return HaveStoredReal(CSCache(),table,field)
elseif (fieldType == bj_GAMECACHE_STRING) then
return HaveStoredString(CSCache(),table,field)
endif
return false
endfunction
//============================================================================================================
// Allows to copy a table to another one, but it needs a FieldData object to know which fields of which type
// it is supposed to copy.
//
function CopyTable takes integer FieldData, string sourceTable, string destTable returns nothing
local gamecache g=CSCache()
local integer i=1
local string k=I2S(FieldData)
local string k2
local string k3
local integer n=GetStoredInteger(g,k,"N")
local integer t
loop
exitwhen (i>n)
set k2=I2S(i)
set t=GetStoredInteger(g,k,k2)
set k3=GetStoredString(g,k,k2)
if (t==bj_GAMECACHE_BOOLEAN) then
if (HaveStoredBoolean(g,sourceTable,k3)) then
call StoreBoolean(g,destTable,k3,GetStoredBoolean(g,sourceTable,k3))
else
call FlushStoredBoolean(g,destTable,k3)
endif
elseif (t==bj_GAMECACHE_INTEGER) then
if (HaveStoredInteger(g,sourceTable,k3)) then
call StoreInteger(g,destTable,k3,GetStoredInteger(g,sourceTable,k3))
else
call FlushStoredInteger(g,destTable,k3)
endif
elseif (t==bj_GAMECACHE_REAL) then
if (HaveStoredReal(g,sourceTable,k3)) then
call StoreReal(g,destTable,k3,GetStoredReal(g,sourceTable,k3))
else
call FlushStoredReal(g,destTable,k3)
endif
elseif (t==bj_GAMECACHE_STRING) then
if (HaveStoredString(g,sourceTable,k3)) then
call StoreString(g,destTable,k3,GetStoredString(g,sourceTable,k3))
else
call FlushStoredString(g,destTable,k3)
endif
endif
set i=i+1
endloop
set g=null
endfunction
//=============================================================================================
// FieldData inherits from Table, was just designed to be used by CopyTable.
//
function FieldData_Create takes nothing returns integer
return NewTableIndex()
endfunction
//============================================================================================================
// valueType uses the same integer variables from blizzard.j :
// bj_GAMECACHE_BOOLEAN, bj_GAMECACHE_INTEGER, bj_GAMECACHE_REAL and bj_GAMECACHE_STRING
//
function FieldData_AddField takes integer fielddata, string field, integer valueType returns nothing
local gamecache g=CSCache()
local string k=I2S(fielddata)
local integer n=GetStoredInteger(g,k,"N")+1
local string k2=I2S(n)
call StoreString(g,k,k2,field)
call StoreInteger(g,k,k2,valueType)
call StoreInteger(g,k,"N",n)
set g=null
endfunction
//=============================================================================================
// Destroys Field Data
function FieldData_Destroy takes integer fielddata returns nothing
call DestroyTable(I2S(fielddata))
endfunction
//##End of CS Gamecache engine##
//External Functions:
function GetTableTextTag takes string table, string field returns texttag
return GetStoredInteger(CSCache(),table,field)
return null
endfunction
//##End of External functions##
Name | Type | is_array | initial_value |
angle | real | No | |
cscache | gamecache | No | |
group | group | No | |
loc | location | No | |
unit | unit | No |
//***************************************************************************
//* Knockback Functions
//*
//* Author: vile
//*
//* Requirements:
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//* - Attachable Vars (http://wc3jass.com/viewtopic.php?t=2336)
//* - This trigger
//* - Optional: Single target unit for knocking back units (Like Storm Bolt)
//* - Optional: Area of effect instant ability for knocking units in an area (Like War Stomp)
//* - Optional: My sliding effects (Press F12 to see the files)
//*
//* Notes:
//* ¯¯¯¯¯¯
//* I have attached to this spell demo map my Sliding Effects.
//* They are used to prevent repetitive effects to prevent lag.
//* If you want a repetitive effect, you can configure the option
//* through the configuration section below.
//* Give me credit if you use either the effects or the knockback
//* functions.
//* These functions follows the JESP STANDARD, they can be used
//* by copying the needed stuff described above, however, the demo
//* spells are NOT multi instanceable since they are just demonstrations.
//* The Knockback Unit spell is an example of how it can be multi instanceable
//* by using local variables. The second Knockback Area example is how
//* it looks like using global variablse which isn't multi instanceable.
//* So make your choice.
//* NOTE that if you use an initial distance of greater than 35, the
//* unit will slide along the cliffs too. 35 is a good and stable speed,
//* just play with the break value if you want bigger range.
//* Enjoy.
//*
//* Special Note:
//* ¯¯¯¯¯¯¯¯¯¯¯¯¯
//* If you happen to see weird bugs, it is not the script's fault,
//* but rather a bug with the attachment variables. This happens when
//* setting the timer variables to null. I didnt set them to null by
//* default since this spell uses a few timers, but if you want, just
//* scroll down and remove the "//" before each line in the set t = null
//* lines.
//*
//***************************************************************************
// Knockback Functions Configuration Section
//***************************************************************************
constant function KnockbackFunctions_WaterEffect takes nothing returns string
return "MDX\\SlideWater.mdx" // The effect used when the unit slides on water
endfunction
constant function KnockbackFunctions_GroundEffect takes nothing returns string
return "MDX\\Dust.mdx" // The effect used when the unit slides on the ground
endfunction
constant function KnockbackFunctions_Attachment takes nothing returns string
return "right foot" // The part of the unit that the sliding effect is attached to
endfunction
constant function KnockbackFunctions_EffectCheckInterval takes nothing returns real
return .33 // The interval of checking the unit's slide effects
endfunction
constant function KnockbackFunctions_GroupExcemptions takes nothing returns boolean
return true
// This function will excempt units who shouldn't be knocked back
// like mechanical units or structures, or even flying units.
// It's more relevant for the knockback group function.
// Setting it to false will result mechanical, flying units, etc, to
// be knocked back as well.
endfunction
constant function KnockbackFunctions_DestroyTreesOfTypes takes integer id returns boolean
return id == 'ATtr' or id == 'ATtc'
// The rawcode for the destructables to destroy while the unit is
// being knocked back. These are examples of Ashenvale Tree Wall
// and Ashenvale Cantropy Tree. To add more, go to the object
// editor, click on the View tab, and select Display Values as
// Raw codes, then in the destructables tab go and check your
// destructable's raw code. Make sure you add an "or id == <rawcode"
// in the above line, like in the example.
endfunction
//***************************************************************************
// Knockback Functions Code (Don't touch this unless you know jass)
//***************************************************************************
function KnockbackFunctions_UnitMoveExcemptions takes unit u returns boolean
return not IsUnitType(u, ConvertUnitType(2)) and not IsUnitType(u, ConvertUnitType(3)) and not IsUnitType(u, ConvertUnitType(13)) and not IsUnitType(u, ConvertUnitType(15)) and not IsUnitType(u, ConvertUnitType(17)) and not IsUnitType(u, ConvertUnitType(18)) and GetUnitAbilityLevel(u, 'BEer') <= 0 and GetUnitAbilityLevel(u, 'Aloc') <= 0 and GetUnitAbilityLevel(u, 'Avul') <= 0 and GetUnitAbilityLevel(u, 'Bweb') <= 0
endfunction
function KnockbackFunctions_DestroySFXTimedTarget takes nothing returns nothing
local timer t = GetExpiredTimer()
local string s = GetAttachmentTable(t)
call DestroyEffect(GetTableEffect(s, "TempSFX"))
call ClearTable(s)
call DestroyTimer(t)
//set t = null
endfunction
function KnockbackFunctions_AddSFXTimedTarget takes unit u, string modelpath, string attachment, real dur returns nothing
local timer t = CreateTimer()
local string s = GetAttachmentTable(t)
call SetTableObject(s, "TempSFX", AddSpecialEffectTarget(modelpath, u, attachment))
call TimerStart(t, dur, false, function KnockbackFunctions_DestroySFXTimedTarget)
//set t = null
endfunction
function KnockbackFunctions_CheckSlideEffect_Child takes nothing returns nothing
local timer tmr = GetExpiredTimer()
local string s = GetAttachmentTable(tmr)
local timer t = GetTableTimer(s, "Knockback_OriginalTimer")
local unit u = GetTableUnit(s, "Knockback_CheckUnit")
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real check = KnockbackFunctions_EffectCheckInterval()
local string eye = GetAttachmentTable(u)
if GetTableBoolean(eye, "KnockbackFunctions_EffectBoolean") or GetTableBoolean(eye, "KnockbackFunctions_EffectBooleanGroup") then
if IsTerrainPathable(x, y, ConvertPathingType(6)) then
call KnockbackFunctions_AddSFXTimedTarget(u, KnockbackFunctions_GroundEffect(), KnockbackFunctions_Attachment(), check)
elseif not IsTerrainPathable(GetUnitX(u), GetUnitY(u),ConvertPathingType(1)) then
call KnockbackFunctions_AddSFXTimedTarget(u, KnockbackFunctions_WaterEffect(), KnockbackFunctions_Attachment(), check)
endif
else
call ClearTable(s)
call DestroyTimer(tmr)
endif
//set tmr = null
//set t = null
set u = null
endfunction
function KnockbackFunctions_CheckSlideEffect takes unit u, timer t returns nothing
local timer tmr = CreateTimer()
local string s = GetAttachmentTable(tmr)
local real x = GetUnitX(u)
local real y = GetUnitY(u)
local real check = KnockbackFunctions_EffectCheckInterval()
call SetTableObject(s, "Knockback_CheckUnit", u)
call SetTableObject(s, "Knockback_OriginalTimer", t)
call TimerStart(tmr, check, true, function KnockbackFunctions_CheckSlideEffect_Child)
if IsTerrainPathable(x, y, ConvertPathingType(6)) then
call KnockbackFunctions_AddSFXTimedTarget(u, KnockbackFunctions_GroundEffect(), KnockbackFunctions_Attachment(), check)
elseif not IsTerrainPathable(GetUnitX(u), GetUnitY(u),ConvertPathingType(1)) then
call KnockbackFunctions_AddSFXTimedTarget(u, KnockbackFunctions_WaterEffect(), KnockbackFunctions_Attachment(), check)
endif
//set tmr = null
endfunction
function KnockbackFunctions_DestroyDestructables takes nothing returns nothing
local destructable d = GetEnumDestructable()
local integer id = GetDestructableTypeId(d)
if KnockbackFunctions_DestroyTreesOfTypes(id) then
call KillDestructable(d)
endif
set d = null
endfunction
function KnockbackFunctions_KBGroupEffect takes nothing returns nothing
local timer tmr = GetExpiredTimer()
local string s = GetAttachmentTable(tmr)
local unit t = GetEnumUnit()
local real x1 = GetTableReal(s, "KnockbackFunctions_GroupX")
local real y1 = GetTableReal(s, "KnockbackFunctions_GroupY")
local real x2 = GetUnitX(t)
local real y2 = GetUnitY(t)
local real dist = GetTableReal(s, "KnockbackFunctions_Distance")
local real a
local real x
local real y
local rect r
if GetTableBoolean(s, "KnockbackFunctions_Out") then
set a = 57.29582*Atan2(y2-y1, x2-x1)
else
set a = 57.29582*Atan2(y1-y2, x1-x2)
endif
set x = x2+dist*Cos(a*.017453)
set y = y2+dist*Sin(a*.017453)
if GetTableBoolean(s, "KnockbackFunctions_DestroyDestructables") then
set r = Rect(x-128, y-128, x+128, y+128)
call EnumDestructablesInRect(r, null, function KnockbackFunctions_DestroyDestructables)
call RemoveRect(r)
endif
call SetUnitPosition(t, x, y)
set t = null
set r = null
//set tmr = null
endfunction
function KnockbackFunctions_ClearGroupEffects takes nothing returns nothing
local string eye = GetAttachmentTable(GetEnumUnit())
call SetTableBoolean(eye, "KnockbackFunctions_EffectBooleanGroup", false)
endfunction
function KnockbackFunctions_KBGroupTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local string s = GetAttachmentTable(t)
local group g = GetTableGroup(s, "KnockbackFunctions_Group")
local real dist = GetTableReal(s, "KnockbackFunctions_Distance")
local real break = GetTableReal(s, "KnockbackFunctions_Break")
if dist > 0 then
call ForGroup(g, function KnockbackFunctions_KBGroupEffect)
call SetTableReal(s, "KnockbackFunctions_Distance", dist-break)
else
call ForGroup(g, function KnockbackFunctions_ClearGroupEffects)
if GetTableBoolean(s, "KnockbackFunctions_DestroyGroup") then
call DestroyGroup(g)
endif
call ClearTable(s)
call DestroyTimer(t)
endif
set g = null
//set t = null
endfunction
function KnockbackFunctions_CheckSlideEffectGroup takes group g, timer t returns nothing
local unit temp
local group g2 = CreateGroup()
local string eye
call GroupAddGroup(g, g2)
loop
set temp = FirstOfGroup(g2)
exitwhen temp == null
set eye = GetAttachmentTable(temp)
call SetTableBoolean(eye, "KnockbackFunctions_EffectBooleanGroup", true)
call KnockbackFunctions_CheckSlideEffect(temp, t)
call GroupRemoveUnit(g2, temp)
set temp = null
endloop
call DestroyGroup(g2)
set g2 = null
endfunction
function KnockbackFunctions_KBGroup takes group g, real x, real y, real speed, real break, real interval, boolean out, boolean destroygroup, boolean destroydestructables, boolean eyecandy returns nothing
local timer t = CreateTimer()
local string s = GetAttachmentTable(t)
local unit u
local group g2 = CreateGroup()
call GroupAddGroup(g, g2)
call SetTableObject(s, "KnockbackFunctions_Group", g)
call SetTableReal(s, "KnockbackFunctions_GroupX", x)
call SetTableReal(s, "KnockbackFunctions_GroupY", y)
call SetTableReal(s, "KnockbackFunctions_Distance", speed)
call SetTableReal(s, "KnockbackFunctions_Break", break)
call SetTableBoolean(s, "KnockBackFunctions_Out", out)
call SetTableBoolean(s, "KnockBackFunctions_DestroyGroup", destroygroup)
call SetTableBoolean(s, "KnockBackFunctions_DestroyDestructables", destroydestructables)
call SetTableBoolean(s, "KnockBackFunctions_EyeCandy", eyecandy)
if KnockbackFunctions_GroupExcemptions() then
loop
set u = FirstOfGroup(g2)
exitwhen u == null
if not KnockbackFunctions_UnitMoveExcemptions(u) then
call GroupRemoveUnit(g, u)
endif
call GroupRemoveUnit(g2, u)
set u = null
endloop
endif
call DestroyGroup(g2)
if CountUnitsInGroup(g) > 0 then
call TimerStart(t, interval, true, function KnockbackFunctions_KBGroupTimer)
if eyecandy then
call KnockbackFunctions_CheckSlideEffectGroup(g, t)
endif
else
call ClearTable(s)
call DestroyTimer(t)
endif
//set t = null
set u = null
set g2 = null
endfunction
function KnockbackFunctions_KBUnitTimer takes nothing returns nothing
local timer t = GetExpiredTimer()
local string s = GetAttachmentTable(t)
local unit u = GetTableUnit(s, "Knockback_Target")
local real cos = GetTableReal(s, "Knockback_Cos")
local real sin = GetTableReal(s, "Knockback_Sin")
local real dist = GetTableReal(s, "Knockback_Distance")
local real break = GetTableReal(s, "Knockback_Break")
local real x = GetUnitX(u)+dist*cos
local real y = GetUnitY(u)+dist*sin
local string eye = GetAttachmentTable(u)
local rect r
if dist > 0 then
if GetTableBoolean(s, "Knockback_DestroyDestructables") then
set r = Rect(x-128, y-128, x+128, y+128)
call EnumDestructablesInRect(r, null, function KnockbackFunctions_DestroyDestructables)
call RemoveRect(r)
endif
call SetUnitPosition(u, x, y)
call SetTableReal(s, "Knockback_Distance", dist-break)
else
call SetTableBoolean(eye, "KnockbackFunctions_EffectBoolean", false)
call ClearTable(s)
call DestroyTimer(t)
endif
set u = null
//set t = null
set r = null
endfunction
function KnockbackFunctions_KBUnit takes unit u, real angle, real speed, real break, real interval, boolean destroydestructables, boolean eyecandy returns nothing
local timer t = CreateTimer()
local string s = GetAttachmentTable(t)
local string eye = GetAttachmentTable(u)
call SetTableReal(s, "Knockback_Cos", Cos(angle*.017453))
call SetTableReal(s, "Knockback_Sin", Sin(angle*.017453))
call SetTableObject(s, "Knockback_Target", u)
call SetTableReal(s, "Knockback_Distance", speed)
call SetTableReal(s, "Knockback_Break", break)
call SetTableBoolean(s, "Knockback_DestroyDestructables", destroydestructables)
call TimerStart(t, interval, true, function KnockbackFunctions_KBUnitTimer)
if eyecandy then
call SetTableBoolean(eye, "KnockbackFunctions_EffectBoolean", true)
call KnockbackFunctions_CheckSlideEffect(u, t)
endif
//set t = null
// I don't recommend setting t = null because of possible bugs.
endfunction
function InitTrig_KnockbackFunctions takes nothing returns nothing
set gg_trg_KnockbackFunctions = CreateTrigger()
endfunction
This is the JESP standard document, if a map contains this document it means that there are
spells that follow this standard.
Spells of this map that follow the standard:
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- "Knockback Functions"
Advantage of the Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- Implementing spells that follow the standard is relatively easier than implementing JASS
spells that don't follow the standard.
- Configuring/Balancing spells that follow the standard is relatively easier than
implementing JASS spells that don't follow the standard.
- Users may do the following procedure to make a new ability that uses the spell's script :
* Create a new Trigger with a name (case sensitive)
* Convert that trigger to custom text.
* Copy the spell's script to a text editor like Notepad or your OS equivalent.
* Replace the spell's Code name with the name you used on the trigger.
* Copy the new text to the new trigger
* Duplicate the Spell's original objects to have new ones for the new spell script.
You are now able to use that new version of the spell.
- In case two guys give the same name to 2 different spells, there are no conflict problems
because you can easily change the name of one of them
What is the JESP Standard?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
The JESP standard was designed to make spell sharing much better. And to make sure JASS
enhanced spells follow a rule, to prevent chaos.
What does JESP Standard stands for?
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
JASS
Enhanced
Spell
Pseudotemplate
Requirements for a spell to follow the JESP Standard
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
- The spell is written in JASS
- The spell is 100% multi instanceable.
- The spell script supports spells of any number of levels.
- The Spell has a specific code name.
- All the spell's specific code is inside the spell's "Trigger" (Trigger== that custom text
slot that world editor calls Trigger, the spell may use as many 'trigger' objects as needed)
- Every single function or handle variables (attached variables) of a public object,
Begins with the spell's codename plus "_" . (This means that replacing the codename with
another value would create a non conflicting new instance of the spell).
- Does not use any Global variable that is specific for the spell (It may use non-specific
global variables)
- The Spell's trigger must have the spell's codename as name
- The Spell's InitTrig function must be named: InitTrig_<CodeName>
- The spell has a header of functions used to configure it.
- Eyecandy and spell's balance have to be easy to configure
- The rawcode of the spell has to be input once.
- The name of the author should be included in the spell's script.
- The reason to exist of this standard is spell sharing. This document should be included
in the map explaining which spell follows the standard, and to promote the standard.
Implementing JESP Spells
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Table of contents
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
I. Introduction
II. Copying objects
III. Implementing needed functions/systems/variables
IV. Copying the spell trigger and changing rawcodes
V. Configuring the spell
VI. Configuring eyecandy
VII. Final notes
I. Introduction
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This document will give you detailed information about how to import and configure JASS spells.
It is mainly written for spells that follows the JESP Standard, since the standard makes implementing/
configuring the spells easier, and more the same. An amount of the instructions written here will also
be the same for importing regular JASS spells, depending on how much they reminds of JESP spells.
Before implementing ANYTHING into your map save a backup of it. If you do some wrong, you can just pick
up the backup, save another backup and try again. Else you can risk losing your map.
NOTE: If something isn't working or you gets errors when saving your map, try again.
If it still doesn't work after that, try to contact the author of the spell map and ask him/her for
help with implementing it or ask in the forums (I suggest trying to contact the author first).
II. Copying objects
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This can take some time depending on how many objects the spell needs, but it is probably the simplest
part of implementing a spell.
Open the Object Editor, click on the 'Abilities' tab, select the spell and press CTRL+C or click on the
copy button. Open the map you want to implement the spell into and press CTRL+V or the paste button.
If the spell needs additional objects so as buffs, dummy units or dummy spells, copy them too.
The procedure is the same.
If the spell itself or any dummy spells uses buffs, or if you have dummy units that have custom
abilities, those fields will most likely point to something else than in the original maps.
You can read more about the reason of that in rawcodes part of this document.
Now simply just make sure that those fields points to the correct objects.
III. Implementing needed functions/systems/variables
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If the spell needs any extra functions, systems or variables to work, those needs to be implemented
before thespell itself. The spells header/a read me in the map should tell you which
systems/functions/variables it needs and where to find and how implement them.
Some spell makers have, however, not done so. If they haven't and you can see that the spell(s) uses
custom functions or if you keeps getting errors after implementing the spells.
So if a map needs extra functions or variables and there is no instructions, try the following, it will
hopefully work:
A) Copy Variables
Copy all variables from the map containing the spell(s) you want to the map you will implement them
into.
The easiest way to do this, is to create a GUI trigger, make it uses all variables (how and when the
trigger uses them does not matter).
Then go to File --> Preferences in the World Editor. Under the 'General' tab, check the
'Automatically create unknown variables while pastin trigger data' box.
Then select the trigger you just created, press CTRL+C or click on the copy button. Then open your map,
press CTRL+V or click on the paste button. Now when the trigger is pasted into your map all the
variables are automatically created for you. Then delete the trigger, you won't need it anymore.
B) Copy 'Custom Script'
Custom Script is another name for JASS, the scripting language used for WarCraft III.
At the top of the trigger list, there is a blue map icon with the name of the map file.
If you click that you will be taken to the maps custom script section.
A function is a block of text that basically looks like this:
function <functionName> takes <parameterList> returns <returnValue>
<actions, eventually a return statement>
endfunction
Note that the block of text can start with 'constant function' instead of just 'function'.
We won't go into detail with functions now, but look at the functions name.
If you have implemented other functions in your map already, ensure that none of the functions in the
spell map are identical to (has the same name) as any functions already in your map's custom script
section.
Copy all functions you do not already have into your map's custom script section and save the map.
NOTE: Needed functions are often in seperated triggers. People that does so will most likely also take
the time and say that the spells needs X function which can be found in Y trigger.
They will most likely also give implementation instructions in that trigger.
If they haven't done so, the procedure is the same check that you don't have functions with those names
in your map already, if you don't have, copy the functions to the Custom Script Section. Do NOT just
copy the trigger.
When the function(s) is/are in a trigger, there will also be a function called:
function InitTrig_<TriggerName> takes nothing returns nothing
Never implement that function.
IV. Copying the spell and changing rawcodes
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
Now it is time to copy the spell trigger itself. That shouldn't be too hard, just select the spell,
press CTRL+C or click the copy button, open your map and press CTRL+V or click on the paste button.
Save the map.
Now it's time to change rawcodes of the spell and other objects used by the trigger.
But, what is a rawcode?
A rawcode is a four letters long code. Each object (everything found in the Object Editor are objects)
have an unique rawcode. Because of that, rawcodes often changes when you copy an object from one map
to another.
When a spell follows the JESP Standard, you only have to write the rawcode of an object once.
That will in almost every case be in a configuration function looking like this:
constant function MySpell_SpellId takes nothing returns integer
return 'XXXX'
endfunction
the constant word doesn't have to be there, and the function will most likely also have another name.
The only important thing in that function is the rawcode, in this case XXXX.
You must replace that with the rawcode of the spell in your map.
To do so, open the Object Editor, select the spell and press CTRL+D or check 'Display values as raw
data' in the view menu.
Now the name of the object has changed to "xxxx:yyyy (Object Name)" if it's a custom object or just
"xxxx (object name)" if it isn't a custom object. It will in most cases be a custom object though.
In both cases it is the 4 letters marked as xxxx here that is the rawcode, so put them into the
configuration function instead of the code that was there before.
Note that rawcodes are case sensitive so A00P is not the same as a00p.
Do this with all objects the spell's trigger wants.
V. Configuring the spell
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
For a spell to follow the JESP Standard, it must have a header of functions used to configure it.
Those functions are used for various things, that can be everything from spell damage and AoE, to which
and how many units to hit.
constant function MySpell_Damage takes real level returns real
return 25*level
endfunction
Note that the word constant is not always there.
This function takes a real argument called 'level' and returns another real value, which will be used
by the spell scripts to deal damage.
It can also take other arguments, arguments of other types, next is a short introduction to the most
commonly used variable types by configuration functions.
integer - An integer is a number without decimals. Rawcodes are integers too.
Examples of integer values: 1, -27, 'A005'.
real - A real is a number with decimals. Normally you can use integers as reals directly, but that is
not possible in return statements == configuration functions.
If you multiply an integer with a real (or the opposite) it will return a real value.
If you only have integers in your function, try to put a . behind it (no decimals are actually
needed, just the dot, the game will add a lot of 0 decimals then) or use the I2R function.
Examples of real values: 0., -1435.98, 0.27.
string - A string is simply text between quotes. If you have quotes in side the string, put a \ before
it, else you will get errors when saving (the \ character won't be displayed in game).
If you want a backslash in the string, for example if it's a filepath, use two backslashes
instead, the first one is only used for control.
Examples of string values: "Hello World!", "Bob \"Teaspoon\" Johnson", "Textures\\gutz.blp".
boolean - A boolean value is either true or false. Nothing else. Examples aren't really needed.
That is the most common values used by configuration function. Many functions takes an argument called
'level'. That is, of course, the level of the spell, and can be used for calculating stuff.
It is sometimes an integer and sometimes a real, depends on the type the function will return. If it is
a real, it will always just be the integer with 0 decimals added.
The functions will most likely have descritive names and/or comments explaining what they do.
Just change the returned values to edit it, don't care about the rest.
VI. Configuring eyecandy
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
If you want to change the eyecandy of a JESP Spell, it can often be done in the Object Editor, on the
spell itself.
The author will hopefully have written from which fields in the Object Editor that certain values are
extracted from, but if he/she hasn't, the easiest solution is to look at the spell in game, find the
effect you want to edit, open Object Editor and check all values untill you find the one that is
equal to the ingame effect and change it.
NOTE: The filename in object editor must end with .mdl, sometimes the editor doesn't automatically add
that extension. If that is the case, just add .mdl to the file name manually.
If the value isn't extracted from Object Editor, it is the spell script itself. Simply find the
configuration function where it is, and change it. To get the path of another model, you can simply
set a model used in Object Editor to that model, press ok, open the field again, and select the value
in the 'Custom' field. Copy it to the function, replace all single backslashes to double backslashes
and ensure that it ends with .mdl. As it is a string, it must be within quotes.
Example:
constant function MySpell_Effect takes nothing returns string
return "Abilities\\Spells\\Human\\Defend\\DefendCaster.mdl"
endfunction
VII. Final notes
¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
This tutorial hopefully cleared out some things, and you should be able to implement spells and
configure them so they fits your needs.
If there was something you didn't understand, read it again. If that doesn't help, try to ask in the
forums at Wc3Campaigns (http://www.wc3campaigns.net) or The Jass Vault (http://www.wc3jass.com) or
contact the author.
Thanks for reading!
Thanks for using my Knockback functions.
If you have any bug reports or questions, feel free to check
out the latest version of this demo map at http://wc3campaigns.net
Give me credits if you use this in your map.
You can also contact me at:
MSN/Email: [email protected]
ICQ: 15370294
function Init takes nothing returns nothing
local texttag t = CreateTextTag()
call SetTextTagText(t, "Spell Immune", .024)
call SetTextTagPosUnit(t, gg_unit_nvdw_0027, 90)
call SetTextTagColor(t, 124, 188, 255, 0)
call SetTextTagVisibility(t, true)
call SetTableObject("[TextTag]", "t", t)
call BJDebugMsg("|CFFED1C24Testmap & Spell author: |CFF90FF90vile|r
|CFFC69C6D*This spell is Multi-Instanceable and follows the JESP standard*|r
|CFFFF8A00*CREDITS:|r
- |CFF008AFFVexorian|r for his Attachable Vars
- |CFF008AFFKaTTaNa|r for his Handle Variables|r
- |CFF008AFFBlade.dk|r for his JESP Documentation|r
|CFF90FF90Give me credits if you use this in your map.|r")
call FogEnable(false)
call FogMaskEnable(false)
call SetPlayerState(Player(0), ConvertPlayerState(7), IntegerTertiaryOp(true, 1, 0))
call SetPlayerState(Player(12), ConvertPlayerState(7), IntegerTertiaryOp(true, 1, 0))
call SetTimeOfDay(0)
call SelectUnitForPlayerSingle(gg_unit_Nplh_0051, Player(0))
call DestroyTimer(GetExpiredTimer())
set t = null
endfunction
function Refresher_Cond takes nothing returns boolean
return GetSpellAbilityId() == 'ACro' or GetSpellAbilityId() == 'ANwm'
endfunction
function Refresher takes nothing returns nothing
local unit u = GetSpellAbilityUnit()
local integer id = GetSpellAbilityId()
if id == 'ACro' then
call SetUnitState(u, UNIT_STATE_LIFE, GetUnitState(u, UNIT_STATE_MAX_LIFE))
call SetUnitState(u, UNIT_STATE_MANA, GetUnitState(u, UNIT_STATE_MAX_MANA))
call UnitResetCooldown(u)
elseif id == 'ANwm' then
call SetHeroLevel(u, GetHeroLevel(u)+1, true)
endif
set u = null
endfunction
function Stuff_Actions takes nothing returns nothing
local unit d = GetDyingUnit()
local player p = GetOwningPlayer(d)
local texttag t = GetTableTextTag("[TextTag]", "t")
local unit new
if IsUnitType(d, UNIT_TYPE_HERO) then
call TriggerSleepAction(5)
call ReviveHero(d, -393.7, -585.7, true)
call SetCameraPositionForPlayer(p, GetUnitX(d), GetUnitY(d))
call SelectUnitForPlayerSingle(d, p)
else
call TriggerSleepAction(15)
set new = CreateUnit(p, GetUnitTypeId(d), GetUnitX(d), GetUnitY(d), GetRandomReal(0, 360))
if GetUnitTypeId(d) == 'nvdw' then
call DestroyTextTag(t)
set t = CreateTextTag()
call SetTextTagText(t, "Spell Immune", .024)
call SetTextTagPosUnit(t, new, 90)
call SetTextTagColor(t, 124, 188, 255, 0)
call SetTextTagVisibility(t, true)
call SetTableObject("[TextTag]", "t", t)
endif
endif
set d = null
set t = null
set p = null
set new = null
endfunction
function InitTrig_Stuff takes nothing returns nothing
local trigger refresh = CreateTrigger()
set gg_trg_Stuff = CreateTrigger()
call TimerStart(CreateTimer(), .01, false, function Init)
call TriggerRegisterAnyUnitEventBJ(gg_trg_Stuff, EVENT_PLAYER_UNIT_DEATH)
call TriggerAddAction( gg_trg_Stuff, function Stuff_Actions)
call TriggerRegisterUnitEvent(refresh, gg_unit_Nplh_0051, EVENT_UNIT_SPELL_EFFECT)
call TriggerAddCondition(refresh, Condition(function Refresher_Cond))
call TriggerAddAction(refresh, function Refresher)
set refresh = null
endfunction
function Destructables_Actions takes nothing returns nothing
local destructable d = GetDyingDestructable()
call TriggerSleepAction(60)
call DestructableRestoreLife(d, GetDestructableMaxLife(d), true)
set d = null
endfunction
function InitTrig_Destructables_Register takes nothing returns nothing
call TriggerRegisterDeathEvent(gg_trg_Destructables_Revive, GetEnumDestructable())
endfunction
function InitTrig_Destructables_Revive takes nothing returns nothing
local rect r = GetWorldBounds()
set gg_trg_Destructables_Revive = CreateTrigger()
call EnumDestructablesInRect(r, null, function InitTrig_Destructables_Register)
call TriggerAddAction(gg_trg_Destructables_Revive, function Destructables_Actions)
call RemoveRect(r)
set r = null
endfunction