• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[System] DestructableHider

Level 16
Joined
Aug 7, 2009
Messages
1,403
Yes, a negative performance boost. Doodads do not suffer this problem, they work properly; it's wise to convert destructables to doodads whenever possible, and not the opposite. The terrain for my map is being remade from scratch, and I told my friend not to use any destructibles; using doodads is just faster and better (given you don't need the special attributes desctrictibles have)
 
Create the following variables:

Code:
DestructableHider_hash (Hashtable)
DestructableHider_columns (Integer)
DestructableHider_rows (Integer)
DestructableHider_lastrow (Integer)
DestructableHider_lastcolumn (Integer)
DestructableHider_mapMinX (Real)
DestructableHider_mapMinY (Real)

And paste this code into a JASS trigger:

JASS:
//
//        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)
//            - hiding destructables that block pathing is safe, as pathing will not change even if the destructable is hidden
//            - 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
//            
//            

// Configurables

// 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.
constant function DestructableHider_CHECK_LIFE takes nothing returns real
    return 37.
endfunction

// The refreshing rate of the system (Recommended: 0.05 - 0.1)
constant function DestructableHider_INTERVAL takes nothing returns real
    return 0.5
endfunction

// The edge size of the tiles (Should be about the same as sign radius (Not diameter) of the camera. For 3D Cams, use the FarZ value)
constant function DestructableHider_TILE_SIZE takes nothing returns real
    return 512.
endfunction

// End Configurables

function DestructableHider___filt takes nothing returns boolean
    return GetDestructableMaxLife(GetFilterDestructable()) == DestructableHider_CHECK_LIFE()
endfunction

function DestructableHider_register takes destructable d returns nothing
    local integer id = R2I((GetDestructableY(d) - udg_DestructableHider_mapMinY) / DestructableHider_TILE_SIZE()) * udg_DestructableHider_columns + R2I((GetDestructableX(d) - udg_DestructableHider_mapMinX) / DestructableHider_TILE_SIZE())
    local integer count = LoadInteger(udg_DestructableHider_hash, id, 0)+1
    call SaveInteger(udg_DestructableHider_hash, id, 0, count)
    call SaveDestructableHandle(udg_DestructableHider_hash, id, count, d)
    //the destructable remains visible until once viewed by the camera
endfunction

function DestructableHider_unregister takes destructable d returns nothing
    local integer id = R2I((GetDestructableY(d) - udg_DestructableHider_mapMinY) / DestructableHider_TILE_SIZE()) * udg_DestructableHider_columns + R2I((GetDestructableX(d) - udg_DestructableHider_mapMinX) / DestructableHider_TILE_SIZE())
    local integer count = LoadInteger(udg_DestructableHider_hash, id, 0)
    local integer a = 1
    //not very efficient, but it does the job
    loop
        exitwhen a > count
        if LoadDestructableHandle(udg_DestructableHider_hash, id, a) == d then
            if a < count then //not the last in list
                call SaveDestructableHandle(udg_DestructableHider_hash, id, a, LoadDestructableHandle(udg_DestructableHider_hash, id, count)) //move the last one into this slot
            endif
            call RemoveSavedHandle(udg_DestructableHider_hash, id, count) //clean the garbage WITH FIRE1111one!11!oneeleven!1cos(0)~~desu
            call SaveInteger(udg_DestructableHider_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

function DestructableHider___autoregister takes nothing returns nothing
    local destructable d = GetEnumDestructable()
    local integer id = R2I((GetDestructableY(d) - udg_DestructableHider_mapMinY) / DestructableHider_TILE_SIZE()) * udg_DestructableHider_columns + R2I((GetDestructableX(d) - udg_DestructableHider_mapMinX) / DestructableHider_TILE_SIZE())
    local integer count = LoadInteger(udg_DestructableHider_hash, id, 0) + 1
    call SaveInteger(udg_DestructableHider_hash, id, 0, count)
    call SaveDestructableHandle(udg_DestructableHider_hash, id, count, d)
    call ShowDestructable(d, false) //initially hide everything
    set d = null
endfunction

function DestructableHider___periodic takes nothing returns nothing
    local integer row = R2I((GetCameraTargetPositionY() - udg_DestructableHider_mapMinY) / DestructableHider_TILE_SIZE())
    local integer column = R2I((GetCameraTargetPositionX() - udg_DestructableHider_mapMinX) / DestructableHider_TILE_SIZE())
    local integer lastid = udg_DestructableHider_lastrow * udg_DestructableHider_columns + udg_DestructableHider_lastcolumn
    local integer id = row * udg_DestructableHider_columns + column
    local integer id2 = 0
    local integer a = 0
    local integer count = LoadInteger(udg_DestructableHider_hash, lastid, 0)
    //local integer hiddenthiscycle = 0
    //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
            call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, lastid, a), false)
            //set hiddenthiscycle = hiddenthiscycle + 1
        endloop
        if udg_DestructableHider_lastcolumn > 0 then //hide last tile: west
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, lastid - 1, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, lastid - 1, a), false)
                //set hiddenthiscycle = hiddenthiscycle + 1
            endloop
        endif
        if udg_DestructableHider_lastcolumn + 1 < udg_DestructableHider_columns then //hide last tile: east
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, lastid + 1, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, lastid + 1, a), false)
                //set hiddenthiscycle = hiddenthiscycle + 1
            endloop
        endif
        if udg_DestructableHider_lastrow > 0 then //hide last tile: south
            set id2 = lastid - udg_DestructableHider_columns
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id2, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2, a), false)
                //set hiddenthiscycle = hiddenthiscycle + 1
            endloop
            if udg_DestructableHider_lastcolumn > 0 then //hide last tile: southwest
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 - 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 - 1, a), false)
                    //set hiddenthiscycle = hiddenthiscycle + 1
                endloop
            endif
            if udg_DestructableHider_lastcolumn + 1 < udg_DestructableHider_columns then //hide last tile: southeast
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 + 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 + 1, a), false)
                    //set hiddenthiscycle = hiddenthiscycle + 1
                endloop
            endif
        endif
        if udg_DestructableHider_lastrow + 1 < udg_DestructableHider_rows then //hide last tile: north
            set id2 = lastid + udg_DestructableHider_columns
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id2, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2, a), false)
                //set hiddenthiscycle = hiddenthiscycle + 1
            endloop
            if udg_DestructableHider_lastcolumn > 0 then //hide last tile: northwest
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 - 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 - 1, a), false)
                    //set hiddenthiscycle = hiddenthiscycle + 1
                endloop
            endif
            if udg_DestructableHider_lastcolumn + 1 < udg_DestructableHider_columns then //hide last tile: northeast
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 + 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 + 1, a), false)
                    //set hiddenthiscycle = hiddenthiscycle + 1
                endloop
            endif
        endif
        //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(udg_DestructableHider_hash, id, 0)
        loop //show tile: center
            exitwhen a >= count
            set a = a + 1
            call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id, a), true)
            //set shownthiscycle = shownthiscycle + 1
        endloop
        if column > 0 then //show tile: west
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id - 1, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id - 1, a), true)
                //set shownthiscycle = shownthiscycle + 1
            endloop
        endif
        if column + 1 < udg_DestructableHider_columns then //show tile: east
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id + 1, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id + 1, a), true)
                //set shownthiscycle = shownthiscycle + 1
            endloop
        endif
        if row > 0 then //show tile: south
            set id2 = id - udg_DestructableHider_columns
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id2, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2, a), true)
                //set shownthiscycle = shownthiscycle + 1
            endloop
            if column > 0 then //show tile: southwest
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 - 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 - 1, a), true)
                    //set shownthiscycle = shownthiscycle + 1
                endloop
            endif
            if column + 1 < udg_DestructableHider_columns then //show tile: southeast
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2+1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 + 1, a), true)
                    //set shownthiscycle = shownthiscycle + 1
                endloop
            endif
        endif
        if row + 1 < udg_DestructableHider_rows then //show tile: north
            set id2 = id + udg_DestructableHider_columns
            set a = 0
            set count = LoadInteger(udg_DestructableHider_hash, id2, 0)
            loop
                exitwhen a >= count
                set a = a + 1
                call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2, a), true)
                //set shownthiscycle = shownthiscycle + 1
            endloop
            if column > 0 then //show tile: northwest
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 - 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 - 1, a), true)
                    //set shownthiscycle = shownthiscycle + 1
                endloop
            endif
            if column + 1 < udg_DestructableHider_columns then //show tile: northeast
                set a = 0
                set count = LoadInteger(udg_DestructableHider_hash, id2 + 1, 0)
                loop
                    exitwhen a >= count
                    set a = a + 1
                    call ShowDestructable(LoadDestructableHandle(udg_DestructableHider_hash, id2 + 1, a), true)
                    //set shownthiscycle = shownthiscycle + 1
                endloop
            endif
        endif
        //call BJDebugMsg("shown this cycle: "+I2S(shownthiscycle))
    endif
    set udg_DestructableHider_lastrow = row
    set udg_DestructableHider_lastcolumn = column
endfunction

function InitTrig_DestructableHider takes nothing returns nothing
    set udg_DestructableHider_hash = InitHashtable()
    set udg_DestructableHider_mapMinX = GetRectMinX(bj_mapInitialPlayableArea)
    set udg_DestructableHider_mapMinY = GetRectMinY(bj_mapInitialPlayableArea)
    set udg_DestructableHider_rows = R2I((GetRectMaxY(bj_mapInitialPlayableArea) - udg_DestructableHider_mapMinY) / DestructableHider_TILE_SIZE()) + 1
    set udg_DestructableHider_columns = R2I((GetRectMaxX(bj_mapInitialPlayableArea) - udg_DestructableHider_mapMinX)/ DestructableHider_TILE_SIZE()) + 1
    call TimerStart(CreateTimer(), DestructableHider_INTERVAL(), true, function DestructableHider___periodic)
    call EnumDestructablesInRect(bj_mapInitialPlayableArea, Filter(function DestructableHider___filt), function DestructableHider___autoregister)
endfunction

Done.
 
Level 16
Joined
Aug 20, 2009
Messages
1,552
i wanted to add something to it, aside the main function.

because this causes desyncs in my map with a vector system. (which has a getTerrainZ function)

if any dummy units registered in a stored unit type list (yes, there are multiple types of dummy) are nearby the destructible, it would unhide.

anyone want to help me with this?

the map is multiplayer btw.
 
This would only be a problem with destructables that are interacted with or have pathing (I think). Any of those that you think may cause a problem, just set the life to 37.0 so it gets excluded by the system.

I did make a version that checks for nearby units but I don't think that it is very optimal. In the end you might not save much performance, especially with a lot of units.
 
Level 16
Joined
Aug 20, 2009
Messages
1,552
there is only 3 to 4 units that needs check.

probably, can you make it like this..?

only checks destructible in unit range if unit group X is not empty.

so i think it would save a lot of processing!

yes, i will add all my projectiles to unit group, and they will be removed when they died / no longer used.

it would reduce tons of lag i believe.

it will work normally if the unit group is empty.

but it will check destructibles in 300 range of every unit in unit group
ifits not
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
A did a quick look on it and found a way to deal with it without enumerations:

However, the cost is that the system is now much less effective, as now a lot of destructables remain falsely shown until viewed again once.

Also, the starting state of all destructables is now shown until viewed once, instead of hidden like in the original system. This was a required change for your setup.
This means that the system gets more and more effective at increasing FPS once the players moved the camera across the map, but will start out without any fps change at all (as everything is visible initially).
JASS:
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
 
Level 3
Joined
Nov 30, 2012
Messages
30
I found a problem that can make users off-line in lan game,about nightelf's eat tree or just attack a destructable.
I suggest it's because CameraX/Y and HideDestructable make data out of step between users.
And sorry for my poor English.:goblin_cry:
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I found a problem that can make users off-line in lan game,about nightelf's eat tree or just attack a destructable.
I suggest it's because CameraX/Y and HideDestructable make data out of step between users.
And sorry for my poor English.:goblin_cry:
Its written in the documentation that only non-interactable destructables should be registered to the system.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
Nice, reduces FPS alot

Make it work with setting camera height and it will be one of the best RPG systems :D
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Nice, reduces FPS alot

Make it work with setting camera height and it will be one of the best RPG systems :D
I suppose you mean dynamically adjusting the square size depending on the game view?
While it looks like a good idea, in the end it would have the opposite effect: slowing down the map instead of speeding it up, as then the whole "split the map into squares" thing would become a lot more complicated.

The system was coded nice and simple for maximum performance. Even with huge square sizes (I always use 6144 with default wc3 camera, which exceeds the default drawing range and is a multiple of 1024 for maximum efficiency on square division), the speed increase is enormous on large maps (Gaias ORPG: from 35fps to a stable 55-60 fps - map size 256x480).
For smaller maps, this system shouldn't be needed anyway (if the map is less in playable area than square size*3, the system won't do anything most of the time, as all squares are almost always within range), as the amount of destructables won't be that high (assuming there is no destructable spam, which is always a sign of bad terrain design).
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
I recently got a lot of PMs about this system and felt I need to state something again that might not have been obvious enough on the initial posting:

- hiding walkable platforms is only safe, as long as you dont get a location Z for a location placed on that destructable globally
-> this is an important note if you use projectile or missile systems! In case you have such a system in your map and there is any interaction other than the units flyHeight for visuals (make sure there is no z-collision detection), make sure to blacklist all walkable destructables!
EDIT: Forget about this. There is no missile system that has "real" z-collision detection. You can use any of the missile systems safely with this!
 
Last edited:
Level 19
Joined
Mar 18, 2012
Messages
1,716
uh, I better convert those trees from doodads to destructables than

So you say converting doodads into destructables, results in a perfomance boost, in case you hide them. I'm no expert, but destructables are objects you can interact with (enumerate, attack, destroy, hide, show...), whereas doodads not.
Which leads me to the assumption that destructables are handled costlier within the wc3 mechanics and thus hiding destructables might not bring the wanted boost you expect.

Don't get me wrong this resource is briliant, I just want to say converting everything into destructs is possibly not as effective as you think.

Maybe someone here has the knowledge and can clear things up.
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Just to make things clear:
Do not convert doodads into destructables, unless you want to use the other features of destructables. It's just not worth it. Even with everything hidden, destructables are still way more performance costly than doodads.

This system is only for one purpose: Increasing performance of maps that use destructables for walkability, dynamic pathing blocking and sight blocking.
 
Dude dude dude dude. I just tested this on MineralZ (Mainly uses destructables for a random environment) and I went from like 30-40 fps to a SOLID 60. THIS SHIT IS PURE GOLD.

GOLD. YOU MUST TRY THIS.



Quick question: If you remove a destructable locally hid to one player, would it cause a desync? Or is that what unregister is for? Jk looked at the code and answered my own question.
It won't cause desync if you unregister before removal.

+rep!!!
 

Zwiebelchen

Hosted Project GR
Level 35
Joined
Sep 17, 2009
Messages
7,236
Dude dude dude dude. I just tested this on MineralZ (Mainly uses destructables for a random environment) and I went from like 30-40 fps to a SOLID 60. THIS SHIT IS PURE GOLD.

GOLD. YOU MUST TRY THIS.



Quick question: If you remove a destructable locally hid to one player, would it cause a desync? Or is that what unregister is for? Jk looked at the code and answered my own question.
It won't cause desync if you unregister before removal.

+rep!!!
Yes, you should definitely unregister a destructable before removing it from the game permanently. The system won't bug if you don't do this, but the removed destructable will leave a shadow reference in the system (which isn't a big deal if you just remove destructables once, but if you dynamically create & remove multiple times, it might have an impact on performance eventually).
In terms of possible desyncs, it doesn't matter if you don't unregister before removing it.
 
Level 4
Joined
Mar 30, 2018
Messages
76
i tried to apply thie trigger on a map, but it gives me this result:
upload_2018-12-10_9-27-11.png
 
Top