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

[JASS] Subdivide Map in Regions

Status
Not open for further replies.
Level 20
Joined
Jul 14, 2011
Messages
3,213
Hi! Thanks for reading.
I want to make a region grid in the whole map on MapInit being able to edit the grid size. My goal is to make a triggered mapview system where the player gets vision of a portion of the map once a unit goes inside that region, and not by Wc3 default Unit Vision Sight, similar to some SNES RPG maps.

My first though was getting the bj_playableMapArea reals (maxX, maxY, minX, minY) and divide them by gridsize to have my rect gridsize (so if divided by 100, I can fill the map with 100 regions of the same size), but then i don't know how to make the rects to fill the map.

I guess after that i could create a grid of points, and then create region centered at those points :goblin_cry:
 
You're on the right track, just have a two-dimensional loop where you create the rects using the map dimensions divided by the rect count. Here is a rough jass example:

JASS:
globals
    private constant real GRID_SIZE = 512
endglobals

function SetupRects takes nothing returns nothing
    local integer i = 0 //X index
    local integer j = 0 //Y index
    local real width = GetRectMaxX(bj_playableMapArea) - GetRectMinX(bj_playableMapArea)
    local real height = GetRectMaxY(bj_playableMapArea) - GetRectMinY(bj_playableMapArea)
    local rect rct
    local region rgn

    loop
        exitwhen i > R2I(width/GRID_SIZE)-1 //You could put this in a variable for the sake of efficiency.

        set j = 0 //We need to start at 0 for every x
        loop
            exitwhen j > R2I(height/GRID_SIZE)-1

            set rct = Rect(i*GRID_SIZE, j*GRID_SIZE, (i+1)*GRID_SIZE, (j+1)*GRID_SIZE)
            //Then, create a region from the rect, and use the region for a "unit enters region event" for a trigger. 
            //Inside the action, you can add visibility for triggering player to the entered region using 
            //fog modifiers and GetTriggeringRegion(). 
            set rgn = CreateRegion()
            call RegionAddRect(rgn, rct)
            //...Create the trigger, register the event, etc.
            set i = i+1
        endloop
        set j = j+1
    endloop

    set rct = null
    set rgn = null
endfunction
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
In this case, GridSize = 512 means that each region will be 512x512, or that there will be 512 regions in total distributed amongs the map?
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
Fingolfin tried your script but coulnd't make it work, it wasn't gridding the whole map but just like 1/3 of the top area on the X axis, and 3/4 of the Y axis. I also had an error with the private global. Finally i got this working:

JASS:
globals
    real GRID_SIZE = 2048
    hashtable Hash = InitHashtable()
    
endglobals

function RecVisionCond takes nothing returns boolean
    return IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == true
endfunction

function RecVision takes nothing returns nothing
    local integer regid = GetHandleId(GetTriggeringRegion())
    local rect rct = LoadRectHandle(Hash, regid, 0)
    set bj_lastCreatedFogModifier = CreateFogModifierRect(GetOwningPlayer(GetTriggerUnit()), FOG_OF_WAR_VISIBLE, rct, true, false)
    call FogModifierStart(bj_lastCreatedFogModifier)
    call FlushChildHashtable(Hash, regid)
    set rct = null
endfunction

function SetupRects takes nothing returns nothing
    local trigger TRIG = CreateTrigger()
    local rect rct
    local region rgn
    local real BaseX = 0
    local real BaseY = 0
    local real loopx = 0
    local real loopy = 0
    local real maxX = GetRectMaxX(bj_mapInitialPlayableArea)
    local real maxY = GetRectMaxY(bj_mapInitialPlayableArea)
    local real minX = GetRectMinX(bj_mapInitialPlayableArea)
    local real minY = GetRectMinY(bj_mapInitialPlayableArea)
    local integer id
   
    call FogEnable(true)
    call FogMaskEnable(true)
   
    loop
        set loopx = minX + (BaseX*GRID_SIZE)
        exitwhen loopx > maxX
        set BaseY = 0
            loop
            set loopy = minY + (BaseY*GRID_SIZE)
            exitwhen loopy > maxY
            set BaseY = BaseY+1
            set rct = Rect(loopx, loopy, loopx+GRID_SIZE, loopy+GRID_SIZE)
            set rgn = CreateRegion()
            call SaveRectHandle(Hash, GetHandleId(rgn), 0, rct)
            call RegionAddRect(rgn, rct)
            call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetRectCenter(rct), bj_UNIT_FACING ) //Debug
            call TriggerRegisterEnterRegion(TRIG, rgn, null)
            call TriggerAddAction(TRIG, function RecVision)
            call TriggerAddCondition(TRIG, function RecVisionCond)
        endloop
        set BaseX = BaseX+1
    endloop

    set rct = null
    set rgn = null
endfunction

function InitTrig_RecSetup takes nothing returns nothing
    set gg_trg_RecSetup = CreateTrigger(  )
    call TriggerRegisterTimerEvent(gg_trg_RecSetup, 1, false)
    call TriggerAddAction(gg_trg_RecSetup, function SetupRects )
endfunction

Basically i started from minX and minY increasing maxY by GRID_SIZE, after that was bigger than maxY of playable area, i increased the base X by GRID_SIZE, and set Y to 0 to repeat the process. (I'm aware of the unit creation leak). I couldn't make the grid smaller because the loop was reaching it's itterations limit. A gridsize of 1024 was just doing around half ot he map (256 x 256) It works with 2048 but I'd really like to have it at 1024, any suggestions to break the loop in 2 parts?
 
Last edited:
If you have trouble with the operation limit, then I recommend outsourcing the inner loop into a seperate trigger.

Also, there is a problem with your loop in the exitwhen condition that I corrected:
exitwhen loopy > maxY should be exitwhen loopy+GRID_SIZE > maxY
This is to go sure no rect is drawn over the map borders, which might cause your script to crash.

JASS:
globals
     real GRID_SIZE = 2048
     hashtable Hash = InitHashtable()
     
     real tempMinX = 0
     real tempMinY = 0
     real tempMaxX = 0
     real tempMaxY = 0
endglobals

function RecVisionCond takes nothing returns boolean
     return IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == true
endfunction

function RecVision takes nothing returns nothing
     local integer regid = GetHandleId(GetTriggeringRegion())
     local rect rct = LoadRectHandle(Hash, regid, 0)
     set bj_lastCreatedFogModifier = CreateFogModifierRect(GetOwningPlayer(GetTriggerUnit()), FOG_OF_WAR_VISIBLE, rct, true, false)
     call FogModifierStart(bj_lastCreatedFogModifier)
     call FlushChildHashtable(Hash, regid)
     set rct = null
endfunction

function DoDivide takes nothing returns boolean
        local rect rct = Rect(tempMinX, tempMinY, tempMaxX, tempMaxY)
        local region rgn = CreateRegion()
        local trigger TRIG = CreateTrigger()
        call SaveRectHandle(Hash, GetHandleId(rgn), 0, rct)
        call RegionAddRect(rgn, rct)
        call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetRectCenter(rct), bj_UNIT_FACING ) //Debug
        call TriggerRegisterEnterRegion(TRIG, rgn, null)
        call TriggerAddAction(TRIG, function RecVision)
        call TriggerAddCondition(TRIG, function RecVisionCond)
        set rct = null
        set rgn = null
        set TRIG = null
     return false
endfunction

function SetupRects takes nothing returns nothing
     local real BaseX = 0
     local real BaseY = 0
     local real loopx = 0
     local real loopy = 0
     local real maxX = GetRectMaxX(bj_mapInitialPlayableArea)
     local real maxY = GetRectMaxY(bj_mapInitialPlayableArea)
     local real minX = GetRectMinX(bj_mapInitialPlayableArea)
     local real minY = GetRectMinY(bj_mapInitialPlayableArea)
     local integer id
     local trigger Divide = CreateTrigger()
     call TriggerAddCondition(Divide, Condition(function DoDivide))
    
     call FogEnable(true)
     call FogMaskEnable(true)
    
     
     loop
         set tempMinX = minX + (BaseX*GRID_SIZE)
         set tempMaxX = tempMinX +GRID_SIZE
         exitwhen tempMaxX > maxX
         set BaseY = 0
         loop
             set BaseY = BaseY+1
             set tempMinY = minY + (BaseY*GRID_SIZE)
             set tempMaxY = tempMinY+GRID_SIZE
             exitwhen (tempMaxY) > maxY
             call TriggerEvaluate(Divide)
         endloop
         set BaseX = BaseX+1
     endloop

     set rct = null
     set rgn = null
endfunction

function InitTrig_RecSetup takes nothing returns nothing
     set gg_trg_RecSetup = CreateTrigger(  )
     call TriggerRegisterTimerEvent(gg_trg_RecSetup, 1, false)
     call TriggerAddAction(gg_trg_RecSetup, function SetupRects )
endfunction
 
Level 6
Joined
Jun 18, 2004
Messages
119
Be aware of strange bugs that may occur with rects/regions.

- For one, regions are fixed on a 32x32 grid, whereas rects can contain any number. This causes the "Is unit in region" bug when a unit enters a region, but is not yet in the rect.

- Second, the maxX and maxY values of rects are rounded down, but to the NEXT 32 gridpoint. What I mean is: if you set a Rect(0,0,0,0), it's region size is actually 0,0,32,32. The fix is to go slightly under the region point, i.e., Rect(0,0,31.99,31.99) has the desired size of 0,0,32,32

If you just want to use rects without regions then there is no problem.
 
Level 20
Joined
Jul 14, 2011
Messages
3,213
@Zwiebelchen: Thanks for the tips.
Also, there is a problem with your loop in the exitwhen condition that I corrected:
exitwhen loopy > maxY should be exitwhen loopy+GRID_SIZE > maxY
This is to go sure no rect is drawn over the map borders, which might cause your script to crash.

I was setting the loopy+grid before the exitwhen evaluation. Woudln't it stop inmediatly when detected?

JASS:
            set loopy = minY + (BaseY*GRID_SIZE)
            exitwhen loopy > maxY

Your outsourcing allows me to do a 1024 grid, but ignores the regions along the map borders. I'll try to reword this a bit. Thanks for suggestions.

@Blimb: Interesting... however it's not that critical for the playability in this case.

EDIT: Thinking about the outsourcing came with the idea of making the Y axis part of the loop as an independent function, and call it within the X axis loop. Tested and worked with the 1024 size.

JASS:
globals
    real GRID_SIZE = 1024
    hashtable Hash = InitHashtable()
    
endglobals

function RecVisionCond takes nothing returns boolean
    return IsUnitType(GetTriggerUnit(), UNIT_TYPE_HERO) == true
endfunction

function RecVision takes nothing returns nothing
    local integer regid = GetHandleId(GetTriggeringRegion())
    local rect rct = LoadRectHandle(Hash, regid, 0)
    set bj_lastCreatedFogModifier = CreateFogModifierRect(GetOwningPlayer(GetTriggerUnit()), FOG_OF_WAR_VISIBLE, rct, true, false)
    call FogModifierStart(bj_lastCreatedFogModifier)
    call FlushChildHashtable(Hash, regid)
    set rct = null
endfunction

function RctYLoop takes real BaseX, real loopx returns nothing
    local real BaseY = 0
    local real loopy = 0
    local trigger TRIG = CreateTrigger()
    local rect rct
    local region rgn
    local real maxY = GetRectMaxY(bj_mapInitialPlayableArea)
    local real minY = GetRectMinY(bj_mapInitialPlayableArea)
    local integer id
    
    loop
        set loopy = minY + (BaseY*GRID_SIZE)
        exitwhen loopy > maxY
        set BaseY = BaseY+1
        set rct = Rect(loopx, loopy, loopx+GRID_SIZE, loopy+GRID_SIZE)
        set rgn = CreateRegion()
        call SaveRectHandle(Hash, GetHandleId(rgn), 0, rct)
        call RegionAddRect(rgn, rct)
        call CreateNUnitsAtLoc( 1, 'hfoo', Player(0), GetRectCenter(rct), bj_UNIT_FACING )
        call TriggerRegisterEnterRegion(TRIG, rgn, null)
        call TriggerAddAction(TRIG, function RecVision)
        call TriggerAddCondition(TRIG, function RecVisionCond)
    endloop
    set rct = null
    set rgn = null
    set TRIG = null
endfunction

function RctXLoop takes nothing returns nothing
    local real BaseX = 0
    local real loopx = 0
    local real maxX = GetRectMaxX(bj_mapInitialPlayableArea)
    local real minX = GetRectMinX(bj_mapInitialPlayableArea)

    call FogEnable(true)
    call FogMaskEnable(true)
   
    loop
        set loopx = minX + (BaseX*GRID_SIZE)
        exitwhen loopx > maxX
        call RctYLoop(BaseX, loopx)
        set BaseX = BaseX+1
    endloop
endfunction

function InitTrig_RecSetup takes nothing returns nothing
    set gg_trg_RecSetup = CreateTrigger(  )
    call TriggerRegisterTimerEvent(gg_trg_RecSetup, 1, false)
    call TriggerAddAction(gg_trg_RecSetup, function RctXLoop)
endfunction
 
Last edited:
If that worked, then the operation limit was never really your problem to begin with and you can revert it back to the nested loop actually.

Your "outsourcing" approach is fundamentally different to mine in the way that yours will not actually reset the operation limit.
You just call the Y loop within the X loop. To reset the operation limit, you must execute or evaluate a trigger, use executeFunc or call it within a ForForce callback.

In my example, you can fix the border rects by changing the exitwhen conditions to tempMinX and tempMinY.
 
Status
Not open for further replies.
Top