• 🏆 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!

[snippet] MapGrid

Level 31
Joined
Jul 10, 2007
Messages
6,306
JASS:
library MapGrid /* v1.0.0.4
*************************************************************************************
*
*   Creates a grid of 64x64 tiles across the map. These tiles allow one to figure out
*   whenever a unit moves a certain distance. This is useful for checking when units
*   leave ranges of other units. Rather than a timer or a unit movement list, one can
*   check only when units have moved a certain distance.
*
*   While this is not as accurate as other things, it performs much better.
*
*************************************************************************************
*
*   */uses/*
*       */ WorldBounds /*       hiveworkshop.com/forums/jass-functions-413/snippet-worldbounds-180494/
*       */ Event /*             hiveworkshop.com/forums/submissions-414/snippet-event-186555/
*       */ UnitIndexer /*       hiveworkshop.com/forums/jass-functions-413/unit-indexer-172090/
*
**************************************************************************************
*
*   struct MapGrid extends array
*
*       readonly static Event CHANGE_CELL
*           -   Fires whenever a unit changes to a different cell on the map (128x128 tile)
*       readonly static unit EVENT_UNIT
*           -   The unit that changed cells
*
*************************************************************************************/
    globals
        private integer cy=0
        private integer my=0
        private integer cx=0
        private region r1=null
        private boolean array ur
        private integer array un
        private timer ut=CreateTimer()
    endglobals
    private function C takes nothing returns boolean
        local integer y=cy
        local integer x=cx
        local integer dy=my
        local rect r2
        loop
            exitwhen y>=dy
            set r2=Rect(x,y,x+64,y+64)
            call RegionAddRect(r1,r2)
            call RemoveRect(r2)
            set y=y+128
        endloop
        set r2=null
        return false
    endfunction
    private function T takes nothing returns nothing
        local UnitIndex i=un[0]
        loop
            set ur[i]=false
            call i.unlock()
            set i=un[i]
            exitwhen i==0
        endloop
        set un[0]=0
    endfunction
    private function B takes nothing returns boolean
        local UnitIndex i=GetIndexedUnitId()
        call i.lock()
        set un[i]=un[0]
        set un[0]=i
        set ur[i]=true
        call TimerStart(ut,0,false,function T)
        return false
    endfunction
    private module I
        private static method E takes nothing returns boolean
            local unit u=EVENT_UNIT
            set EVENT_UNIT=GetTriggerUnit()
            if (not ur[GetUnitUserData(EVENT_UNIT)]) then
                call CHANGE_CELL.fire()
            endif
            set EVENT_UNIT=u
            set u=null
            return false
        endmethod
        private static method onInit takes nothing returns nothing
            //will loop over the entire map and create a region+rect at every 128x128 square
            local integer x=R2I(WorldBounds.minX)+512
            local trigger t=CreateTrigger()
            local conditionfunc b=Condition(function C)
            local integer mx
            local integer s=-1
            local trigger q=CreateTrigger()
            local region r2=CreateRegion()
            local region r3
            set r1=CreateRegion()
            call TriggerRegisterEnterRegion(q,r1,null)
            call TriggerRegisterLeaveRegion(q,r1,null)
            call TriggerRegisterEnterRegion(q,r2,null)
            call TriggerRegisterLeaveRegion(q,r2,null)
            set CHANGE_CELL=CreateEvent()
            call TriggerAddCondition(q,Condition(function thistype.E))
            call TriggerAddCondition(t,b)
            set cy=R2I(WorldBounds.minY)+256
            set my=R2I(WorldBounds.maxY)-256
            set mx=R2I(WorldBounds.maxX)-512
            loop
                exitwhen x>=mx
                set cx=x
                call TriggerEvaluate(t)
                set x=x+64
                set cy=cy+64*s
                set s=-s
                set r3=r1
                set r1=r2
                set r2=r3
            endloop
            call DestroyCondition(b)
            call DestroyTrigger(t)
            set t=null
            set b=null
            set q=null
            set r1=null
            set r2=null
            set r3=null
            call RegisterUnitIndexEvent(Condition(function B),UnitIndexer.INDEX)
        endmethod
    endmodule
    struct MapGrid extends array
        readonly static Event CHANGE_CELL=0
        readonly static unit EVENT_UNIT=null
        implement I
    endstruct
endlibrary
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
Can you give us the option to iterate through each cell?
It would be pretty useful =)

I don't see how looping through each cell would be useful since not all cells are even placed and they are spaced funny, lol.

131072 handles for a 480x480 map... let's think for a sec

a 480x480 map is 64256x64256

64256/128=502
502*502=252004

so with just rects alone, it should be having 252004 handles.

Like I said, not everything is placed =P.
 
I don't see how looping through each cell would be useful since not all cells are even placed and they are spaced funny, lol.

131072 handles for a 480x480 map... let's think for a sec

a 480x480 map is 64256x64256

64256/128=502
502*502=252004

so with just rects alone, it should be having 252004 handles.

Like I said, not everything is placed =P.

lol My map is 480x480
You just convinced me NOT to use your resource ^^ xD
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
er... it doesn't have 252004 rects in it though, it actually has 63001 across a 480x480 map ;P.

I had calculate the 131k wrong ;).

I've tested in a 480x480 map. Loads fast, no lag, no wc3 crash on exit, exits fast.

edit
This is what grid looks like (50)
Code:
   *     *     *     *     *
*     *     *     *     *
   *     *     *     *     *
*     *     *     *     *
   *     *     *     *     *
*     *     *     *     *
   *     *     *     *     *
*     *     *     *     *

not this (100)
Code:
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
*  *  *  *  *  *  *  *  *  *
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
It would be cheaper to simply have a region/rect per unit on the map to detect when they move a certain distance.

Actually, it wouldn't be cheaper, and there is a reason to paper the entire area ;P. Custom auras : \... there's no UNIT_LEAVE_RANGE event /cry. Also, this allows you to update ranges when a unit crosses a certain distance rather than check on a timer.
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
er... you really don't understand this at all do you... and the fact that you suggested a group enum is baffling. No, you wouldn't even be able to run the map using a group enum, it'd just freeze. Probably couldn't even run 50 units on it.


This is the best way to do it as it has by far the lowest overhead during gameplay and it works.
 

Bribe

Code Moderator
Level 50
Joined
Sep 26, 2009
Messages
9,468
You're right. Though it would be more accurate to display a circular area than rectangles can.

One thing you can improve is to remove all 60K rects ;)

Instead of:

JASS:
call RegionAddRect(r1,Rect(x,y,x+128,y+128))

Clean up your memory leaks:

JASS:
local rect r
...
set r = Rect(x,y,x+128,y+128)
call RegionAddRect(r1, r)
call RemoveRect(r)

It preserves the cell inside the region forever even if the rect is removed. In fact it would be even better to use MoveRectTo with just one single rect ;).
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
ah, you are right Oo

ah that is brilliant =)

edit
Changed this to 64x64 tiles since it uses WAY less memory now =D. It now pops wc3 up to like 130 megs instead of 270 =P.

edit
So.. after some testing, all I can say is that this has its pros and cons.


This ran about every .15 seconds with maximum speed units and the 64x64 tiles. When competing against a .15 timer, it lost with all units moving, 1/2 units moving, 1/3 units move, etc. It finally won on 1/8 units moving though.

The reason for this is because a timer just has the 1 dynamic code + loop whereas this has 1 dynamic code for every single unit upon firing.

The pro of this is that it only fires when units have crossed a boundary. The con is that this seems to be worse than .15 second period timers in almost every situation.

If this were upped to 128x128 tiles, then it'd have to compete with a .3 second timer, and the same thing would happen.

The thing is though, the timer will more than likely hit the op limit with 1600 units running (what this was tested on), so I kind of cheated by giving the timer the advantage.



So it has been determined that this is mostly useless due to the masses of executed dynamic code. It only gets useful when the majority of units are not moving.

edit
Ok... so I found out that for a 522 speed unit travels a 64x64 tile in around .338944369 seconds, meaning that timers just win hands down =P.

The reason timers are faster is again because of the dynamic code =). Everyone would probably be safe with a .34 second interval =P.
 
Last edited:
Level 5
Joined
Dec 1, 2008
Messages
120
From my experience (in XANID), you should reduce -32.00 from both maxX and maxY of a rect. Else rect(-32.00, -32.00, 32.00, 32.00) when added to region is actually rect(-32.00, -32.00, 64.00, 64.00). At least when checking with IsUnitInRegion, it seems to be bigger, dunno about the event.
 
Top