library DestructableHider initializer init
/*
by Zwiebelchen v1.2
Destructables create an enormous amount of overhead on warcraft III maps, almost the same as units, especially walkable destructables.
Thus, a large amount of destructables creates a huge drop of FPS in the game, even on fast machines, due to the poor engine of WC3.
This effect is fairly noticable at an amount of even less than 1000 destructables, which is reached very fast when using invisible platforms.
Warcraft III automaticly hides units outside the screen to save performance, however it does not do so for destructables, for unknown reasons.
The purpose of this fully automatic system is to hide all those destructables, that are currently not viewed anyway, to save a lot of processing time.
To do that, the entire map is splitted into tiles of an editable size and all destructables within those tiles are stored into a table, to allow fast access.
When a tile is viewed, all destructables on adjacent tiles will also be shown, so that moving the camera doesnt create ugly popup effects when the center of the view is on the edge of a tile.
However, there are some rules you need to consider, in order to make your map work without desyncs in multiplayer:
- never hide destructables units or players can interact with (attackable, selectable or destructable)
- never hide destructables that block pathing, as hiding a blocking destructable will fuck up the warcraft III pathing
- hiding destructables that need to be enumed is safe; hidden destructables can be enumerated
- hiding walkable platforms is also safe, as long as you dont get a location Z for a location placed on that destructable globally; this is not 100% safe anyway
In order for the system to register a destructable to this system, you need to set the maximum life of this destructable to the "checklife" value in the systems globals.
All destructables with that life number will be hidden/shown by the system. All others will be ignored.
Optional API:
public function register takes destructable returns nothing
adds a destructable to the system; the destructable remains visible until viewed once by the player
public function unregister takes destructable returns nothing
removes a destructable from the system, also unhides the destructable in case it was hidden
*/
globals
//==== CONFIGURABLES ====
public constant group EXCEPTIONGROUP = CreateGroup() //the group of exceptional units
private constant real EXCEPTIONDISTANCE = 300 //the distance the system tracks exceptional units when hiding destructables
private constant real checklife = 37 //all destructables that shall be hidden by the system must have this maximum life value set in the object editor (otherwise will be ignored by the system)
//simply select a number you won't need in your map
private constant real interval = 0.1 //the refreshing rate of the system (recommended: 0.05-0.1)
private constant real tilesize = 5120 //the edge size of the tiles; should be about the same as sight radius (not diameter) of the camera; for 3d cams, use the FarZ value
//==== END OF CONFIGURABLES ====
private hashtable hash = InitHashtable()
private integer columns = 0
private integer rows = 0
private integer lastrow = 0
private integer lastcolumn = 0
private real mapMinX = 0
private real mapMinY = 0
private real tempX = 0
private real tempY = 0
private boolean exceptionfound = false
endglobals
private function filt takes nothing returns boolean
return GetDestructableMaxLife(GetFilterDestructable()) == checklife
endfunction
public function register takes destructable d returns nothing
local integer id = R2I((GetDestructableY(d)-mapMinY)/tilesize)*columns + R2I((GetDestructableX(d)-mapMinX)/tilesize)
local integer count = LoadInteger(hash, id, 0)+1
call SaveInteger(hash, id, 0, count)
call SaveDestructableHandle(hash, id, count, d)
//the destructable remains visible until once viewed by the camera
endfunction
public function unregister takes destructable d returns nothing
local integer id = R2I((GetDestructableY(d)-mapMinY)/tilesize)*columns + R2I((GetDestructableX(d)-mapMinX)/tilesize)
local integer count = LoadInteger(hash, id, 0)
local integer a = 1
//not very efficient, but it does the job
loop
exitwhen a > count
if LoadDestructableHandle(hash, id, a) == d then
if a < count then //not the last in list
call SaveDestructableHandle(hash, id, a, LoadDestructableHandle(hash, id, count)) //move the last one into this slot
endif
call RemoveSavedHandle(hash, id, count) //clean the garbage
call SaveInteger(hash, id, 0, count-1)
exitwhen true
endif
set a = a+1
endloop
call ShowDestructable(d, true) //make sure its shown again in case it was hidden
endfunction
private function autoregister takes nothing returns nothing
local destructable d = GetEnumDestructable()
local integer id = R2I((GetDestructableY(d)-mapMinY)/tilesize)*columns + R2I((GetDestructableX(d)-mapMinX)/tilesize)
local integer count = LoadInteger(hash, id, 0)+1
call SaveInteger(hash, id, 0, count)
call SaveDestructableHandle(hash, id, count, d)
call ShowDestructable(d, true) //initially unhide everything
set d = null
endfunction
private function check takes nothing returns nothing
if IsUnitInRangeXY(GetEnumUnit(), tempX, tempY, EXCEPTIONDISTANCE) then
set exceptionfound = true
endif
endfunction
private function periodic takes nothing returns nothing
local integer row = R2I((GetCameraTargetPositionY()-mapMinY)/tilesize)
local integer column = R2I((GetCameraTargetPositionX()-mapMinX)/tilesize)
local integer lastid = lastrow*columns + lastcolumn
local integer id = row*columns + column
local integer id2 = 0
local integer a = 0
local integer count = LoadInteger(hash, lastid, 0)
local destructable temp = null
debug local integer hiddenthiscycle = 0
debug local integer shownthiscycle = 0
if id != lastid then //only check for tiles if the camera has left the last tile
loop //hide last tile: center
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, lastid, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
if lastcolumn > 0 then //hide last tile: west
set a = 0
set count = LoadInteger(hash, lastid-1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, lastid-1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
if lastcolumn+1 < columns then //hide last tile: east
set a = 0
set count = LoadInteger(hash, lastid+1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, lastid+1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
if lastrow > 0 then //hide last tile: south
set id2 = lastid-columns
set a = 0
set count = LoadInteger(hash, id2, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
if lastcolumn > 0 then //hide last tile: southwest
set a = 0
set count = LoadInteger(hash, id2-1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2-1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
if lastcolumn+1 < columns then //hide last tile: southeast
set a = 0
set count = LoadInteger(hash, id2+1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2+1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
call ShowDestructable(LoadDestructableHandle(hash, id2+1, a), false)
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
endif
if lastrow+1 < rows then //hide last tile: north
set id2 = lastid+columns
set a = 0
set count = LoadInteger(hash, id2, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
if lastcolumn > 0 then //hide last tile: northwest
set a = 0
set count = LoadInteger(hash, id2-1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2-1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
if lastcolumn+1 < columns then //hide last tile: northeast
set a = 0
set count = LoadInteger(hash, id2+1, 0)
loop
exitwhen a >= count
set a = a + 1
set exceptionfound = false
set temp = LoadDestructableHandle(hash, id2+1, a)
set tempX = GetDestructableX(temp)
set tempY = GetDestructableX(temp)
call ForGroup(EXCEPTIONGROUP, function check)
if exceptionfound then
call ShowDestructable(temp, true)
else
call ShowDestructable(temp, false)
endif
call ShowDestructable(LoadDestructableHandle(hash, id2+1, a), false)
debug set hiddenthiscycle = hiddenthiscycle + 1
endloop
endif
endif
debug call BJDebugMsg("hidden this cycle: "+I2S(hiddenthiscycle))
//all destructables of the old tile and adjacent tiles hidden, now show the new tiles:
set a = 0
set count = LoadInteger(hash, id, 0)
loop //show tile: center
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
if column > 0 then //show tile: west
set a = 0
set count = LoadInteger(hash, id-1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id-1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
if column+1 < columns then //show tile: east
set a = 0
set count = LoadInteger(hash, id+1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id+1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
if row > 0 then //show tile: south
set id2 = id-columns
set a = 0
set count = LoadInteger(hash, id2, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
if column > 0 then //show tile: southwest
set a = 0
set count = LoadInteger(hash, id2-1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2-1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
if column+1 < columns then //show tile: southeast
set a = 0
set count = LoadInteger(hash, id2+1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2+1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
endif
if row+1 < rows then //show tile: north
set id2 = id+columns
set a = 0
set count = LoadInteger(hash, id2, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
if column > 0 then //show tile: northwest
set a = 0
set count = LoadInteger(hash, id2-1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2-1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
if column+1 < columns then //show tile: northeast
set a = 0
set count = LoadInteger(hash, id2+1, 0)
loop
exitwhen a >= count
set a = a + 1
call ShowDestructable(LoadDestructableHandle(hash, id2+1, a), true)
debug set shownthiscycle = shownthiscycle + 1
endloop
endif
endif
debug call BJDebugMsg("shown this cycle: "+I2S(shownthiscycle))
endif
set temp = null
set lastrow = row
set lastcolumn = column
endfunction
private function init takes nothing returns nothing
set mapMinX = GetRectMinX(bj_mapInitialPlayableArea)
set mapMinY = GetRectMinY(bj_mapInitialPlayableArea)
set rows = R2I((GetRectMaxY(bj_mapInitialPlayableArea)-mapMinY)/tilesize)+1
set columns = R2I((GetRectMaxX(bj_mapInitialPlayableArea)-mapMinX)/tilesize)+1
call TimerStart(CreateTimer(), interval, true, function periodic)
call EnumDestructablesInRect(bj_mapInitialPlayableArea, Filter(function filt), function autoregister)
endfunction
endlibrary