Grid System

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Due to the popularity of this resource, I have decided to update it to a Lua version.

Note: The Lua upload utilizes both Bribe's Total Initialization and Eikonium's Debug Utils. Both of these are optional and not required to work.

Information

Code - Jass v1

Code - Lua v1

Code - Jass v2

Code - Lua v2

Changelog

A simple grid system that allows for easier building/tower placement.

Jass & Lua v1:

How to use:
Press G to show/hide. Once you have pressed to show you have to click somewhere and it will appear at that location.

-gc RRGGBB //set the color of the grid, based on inputed hex value.
-gs XX //sets the grids size based on inputed value. Note that it has to be between defined limits.

Jass & Lua v2:

How to use:
Type "-grid"

have a good day
unknown.png

JASS:
library GridSystem initializer init

//Grid System By CanFight

    globals
        private image array gridEffect
        private boolean showHide
        private boolean waitForClick
        private integer gridRed = 200
        private integer gridGreen = 200
        private integer gridBlue = 200
        private integer gridAlpha  = 255
        private integer gridSize = 8
        private integer gridSizeMax = 32
        private integer gridSizeMin = 4
    endglobals
 
    private function ShowHideGrid takes nothing returns nothing
        local integer i = 0
        if GetLocalPlayer() == GetTriggerPlayer() then
            if showHide then
                set showHide = false 
                loop
                    exitwhen i >= gridSizeMax * gridSizeMax
                    call SetImageColor(gridEffect[i],255,255,255,0)
                    set i = i + 1
                endloop
            else
                set waitForClick = true
            endif
        endif
    endfunction

    private function MouseMove takes nothing returns nothing endfunction
        local real x = I2R(R2I(BlzGetTriggerPlayerMouseX()) / 128) * 128
        local real y = I2R(R2I(BlzGetTriggerPlayerMouseY()) / 128) * 128
        local real tx
        local real ty
        local integer i = 0
        local integer ix = 0
        local integer iy = 0
        if GetLocalPlayer() == GetTriggerPlayer() then
            if waitForClick then
                set waitForClick = false
                set showHide = true
                loop
                    exitwhen i >= gridSize * gridSize
                    set tx = x + ix * 128 - gridSize / 2 * 128
                    set ty = y + iy * 128 - gridSize / 2 * 128
                    call SetImageColor(gridEffect[i],gridRed,gridGreen,gridBlue,gridAlpha)
                    call SetImagePosition(gridEffect[i],tx,ty,0)
                    set ix = ix + 1
                    if ix >= gridSize then
                        set ix = 0
                        set iy = iy + 1
                    endif
                    set i = i + 1
                endloop
            endif
        endif
    endfunction
 
    private function GetHexSignValue takes string sign returns integer
        if sign == "a" or sign == "A" then
            return 10
        endif
        if sign == "b" or sign == "B" then
            return 11
        endif
        if sign == "c" or sign == "C" then
            return 12
        endif
        if sign == "d" or sign == "D" then
            return 13
        endif
        if sign == "e" or sign == "E" then
            return 14
        endif
        if sign == "f" or sign == "F" then
            return 15
        endif
        return S2I(sign)
    endfunction

    private function SetGridProp takes nothing returns nothing
        local string s = GetEventPlayerChatString()
        local integer i = 0
        if SubString(s, 0, 4) == "-gc " and GetLocalPlayer() == GetTriggerPlayer() then
            set gridRed = GetHexSignValue(SubString(s, 4, 5)) * 16 + GetHexSignValue(SubString(s, 5, 6))
            set gridGreen = GetHexSignValue(SubString(s, 6, 7)) * 16 + GetHexSignValue(SubString(s, 7, 8))
            set gridBlue = GetHexSignValue(SubString(s, 8, 9)) * 16 + GetHexSignValue(SubString(s, 9, 10))
            if showHide then
                loop
                    exitwhen i >= gridSize * gridSize
                    call SetImageColor(gridEffect[i],gridRed,gridGreen,gridBlue,gridAlpha)
                    set i = i + 1
                endloop
            endif
            return
        endif
 
        if SubString(s, 0, 4) == "-gs " and GetLocalPlayer() == GetTriggerPlayer() then
            set gridSize = S2I(SubString(s, 4, 6))
            if gridSize < gridSizeMin then
                set gridSize = gridSizeMin
            endif
            if gridSize > gridSizeMax then
                set gridSize = gridSizeMax
            endif
            set showHide = true
            call ShowHideGrid()
        endif
    endfunction
 

    private function init takes nothing returns nothing
        local integer i = 0
        local trigger t = CreateTrigger()
        local trigger t2 = CreateTrigger()
        local trigger t3 = CreateTrigger()
        call TriggerAddAction(t, function ShowHideGrid)
        call TriggerAddAction(t2, function MouseMove)
        call TriggerAddAction(t3, function SetGridProp)
        loop
            exitwhen i > 23
            call BlzTriggerRegisterPlayerKeyEvent(t, Player(i),OSKEY_G,0,true)
            call TriggerRegisterPlayerEvent(t2, Player(i), EVENT_PLAYER_MOUSE_DOWN)
            call TriggerRegisterPlayerChatEvent( t3, Player(i), "", false )
            set i = i + 1
        endloop
        set showHide = false
        set waitForClick = false
        set i = 0
        loop
            exitwhen i >= gridSizeMax * gridSizeMax
            set gridEffect[i] = CreateImage("Gridplane.dds",128,128,0,0,0,0, 1,1,1, 1)
            call SetImageRenderAlways( gridEffect[i], true )
            call SetImageColor( gridEffect[i], 255, 255, 255, 0)
            set i = i + 1
        endloop
    endfunction

endlibrary
Lua:
OnInit.global("PlayerGrid", function(require)
    local BUTTON_KEY = OSKEY_G
    GridSystem = {}
    function GridSystem:init()
        self.gridEffect = {}
        self.showHide = false
        self.waitForClick = false
        self.gridRed = 200
        self.gridGreen = 200
        self.gridBlue = 200
        self.gridAlpha = 255
        self.gridSize = 8
        self.gridSizeMax = 32
        self.gridSizeMin = 4
        for i = 1, self.gridSizeMax * self.gridSizeMax do
            self.gridEffect[i] = CreateImage("Gridplane.dds", 128, 128, 0, 0, 0, 0, 1, 1, 1, 1)
            SetImageRenderAlways(self.gridEffect[i], true)
            SetImageColor(self.gridEffect[i], 255, 255, 255, 0)
        end
        local t = CreateTrigger()
        local t2 = CreateTrigger()
        local t3 = CreateTrigger()
        for i = 0, bj_MAX_PLAYER_SLOTS do
            BlzTriggerRegisterPlayerKeyEvent(t, Player(i), BUTTON_KEY, 0, true)
            TriggerRegisterPlayerEvent(t2, Player(i), EVENT_PLAYER_MOUSE_DOWN)
            TriggerRegisterPlayerChatEvent(t3, Player(i), "", false)
        end
        TriggerAddAction(t, function() self:ShowHideGrid() end)
        TriggerAddAction(t2, function() self:MouseMove() end)
        TriggerAddAction(t3, function() self:SetGridProp() end)
    end

    function GridSystem:ShowHideGrid()
        if GetLocalPlayer() ~= GetTriggerPlayer() then
            return
        end
        if self.showHide then
            self.showHide = false
            for i, image in ipairs(self.gridEffect) do
                SetImageColor(image, 255, 255, 255, 0)
            end
        else
            self.waitForClick = true
        end
    end

    function GridSystem:MouseMove()
        local x = math.floor(BlzGetTriggerPlayerMouseX() / 128) * 128
        local y = math.floor(BlzGetTriggerPlayerMouseY() / 128) * 128
        if GetLocalPlayer() ~= GetTriggerPlayer() or not self.waitForClick then return end
        self.waitForClick = false
        self.showHide = true
        local ix, iy = 0, 0
        for i, image in ipairs(self.gridEffect) do
            if i > self.gridSize * self.gridSize then break end
            local tx = x + ix * 128 - self.gridSize / 2 * 128
            local ty = y + iy * 128 - self.gridSize / 2 * 128
            SetImageColor(image, self.gridRed, self.gridGreen, self.gridBlue, self.gridAlpha)
            SetImagePosition(image, tx, ty, 0)
            ix = ix + 1
            if ix >= self.gridSize then
                ix = 0
                iy = iy + 1
            end
        end
    end

    function GridSystem:GetHexSignValue(sign)
        local hex = { ["a"] = 10, ["b"] = 11, ["c"] = 12, ["d"] = 13, ["e"] = 14, ["f"] = 15 }
        return hex[sign:lower()] or tonumber(sign)
    end

    function GridSystem:SetGridProp()
        local s = GetEventPlayerChatString()
        if GetLocalPlayer() ~= GetTriggerPlayer() then
            return
        end
        if s:sub(1, 4) == "-gc " then
            self.gridRed = self:GetHexSignValue(s:sub(5, 5)) * 16 + self:GetHexSignValue(s:sub(6, 6))
            self.gridGreen = self:GetHexSignValue(s:sub(7, 7)) * 16 + self:GetHexSignValue(s:sub(8, 8))
            self.gridBlue = self:GetHexSignValue(s:sub(9, 9)) * 16 + self:GetHexSignValue(s:sub(10, 10))
            if self.showHide then
                for i, image in ipairs(self.gridEffect) do
                    if i > self.gridSize * self.gridSize then break end
                    SetImageColor(image, self.gridRed, self.gridGreen, self.gridBlue, self.gridAlpha)
                end
            end
        elseif s:sub(1, 4) == "-gs " then
            self.gridSize = tonumber(s:sub(5, 7))
            self.gridSize = math.max(self.gridSizeMin, math.min(self.gridSize, self.gridSizeMax))
            self.showHide = true
            self:ShowHideGrid()
        end
    end

    GridSystem:init()
end)
JASS:
scope MapGrid initializer Init

    globals
        private constant string ACTIVATOR = "-grid"
        private constant string IMAGE_TEXTURE_PATH = "Gridplane2x2.dds"
        private constant integer RED = 255
        private constant integer GREEN = 0
        private constant integer BLUE = 0
        private constant integer ALPHA = 255

        private real minX
        private real minY
        private real maxX
        private real maxY
        private integer gridWidth
        private integer gridHeight
        private image array gridImages
        private integer imageCount = 0
        private boolean showHide = false
    endglobals

    private function Ceil takes real value returns integer
        local integer i = R2I(value)
        if value > I2R(i) then
            return i + 1
        endif
        return i
    endfunction

    private function ToggleGrid takes nothing returns nothing
        local player trigPlayer = GetTriggerPlayer()
        local integer i = 0
        if trigPlayer != GetLocalPlayer() then
            return
        endif
        set showHide = not showHide
        loop
            exitwhen i >= imageCount
            call SetImageRenderAlways(gridImages[i], showHide)
            set i = i + 1
        endloop
    endfunction

    private function Init takes nothing returns nothing
        local trigger t
        local integer i = 0
        local integer ix
        local integer iy
        local real tx
        local real ty
        set minX = GetRectMinX(bj_mapInitialPlayableArea)
        set minY = GetRectMinY(bj_mapInitialPlayableArea)
        set maxX = GetRectMaxX(bj_mapInitialPlayableArea)
        set maxY = GetRectMaxY(bj_mapInitialPlayableArea)
        set gridWidth  = Ceil((maxX - minX) / 64)
        set gridHeight = Ceil((maxY - minY) / 64)
        set imageCount = 0
        set iy = 0
        loop
            exitwhen iy >= gridHeight
            set ix = 0
            loop
                exitwhen ix >= gridWidth
                set tx = minX + 64. * ix
                set ty = minY + 64. * iy

                if not IsTerrainPathable(tx, ty, PATHING_TYPE_BUILDABILITY) then
                    set gridImages[imageCount] = CreateImage(IMAGE_TEXTURE_PATH, 64, 64, 10., tx, ty, 0., 0, 0, 0, 1)
                    call SetImageRenderAlways(gridImages[imageCount], false)
                    call SetImageColor(gridImages[imageCount], RED, GREEN, BLUE, ALPHA)
                    set imageCount = imageCount + 1
                endif

                set ix = ix + 1
            endloop
            set iy = iy + 1
        endloop
        set t = CreateTrigger()
        loop
            exitwhen i >= bj_MAX_PLAYER_SLOTS
            call TriggerRegisterPlayerChatEvent(t, Player(i), ACTIVATOR, false)
            set i = i + 1
        endloop
        call TriggerAddAction(t, function ToggleGrid)
        set t = null
        return
    endfunction

endscope
Lua:
OnInit.global("MapGrid", function(require)
    local ACTIVATOR = "-grid"
    local IMAGE_TEXTURE_PATH = "Gridplane2x2.dds"
    local RED = 255
    local GREEN = 0
    local BLUE = 0
    local ALPHA = 255
    GridSystem = {}
    function GridSystem:init()
        self.minX = GetRectMinX(bj_mapInitialPlayableArea)
        self.minY = GetRectMinY(bj_mapInitialPlayableArea)
        self.maxX = GetRectMaxX(bj_mapInitialPlayableArea)
        self.maxY = GetRectMaxY(bj_mapInitialPlayableArea)
        self.gridWidth = math.ceil((self.maxX - self.minX) / 64)
        self.gridHeight = math.ceil((self.maxY - self.minY) / 64)
        self.gridEffect = {}
        self.showHide = false
        local index = 0
        for iy = 0, self.gridHeight - 1 do
            for ix = 0, self.gridWidth - 1 do
                local tx = self.minX + ix * 64
                local ty = self.minY + iy * 64
                if IsTerrainPathable(tx, ty, PATHING_TYPE_BUILDABILITY) == false then
                    index = index + 1
                    self.gridEffect[index] = CreateImage(IMAGE_TEXTURE_PATH, 64, 64, 10, tx, ty, 0, 0, 0, 0, 1)
                    SetImageRenderAlways(self.gridEffect[index], true)
                    SetImageRenderAlways(self.gridEffect[index], false)
                    SetImageColor(self.gridEffect[index], RED, GREEN, BLUE, ALPHA)
                end
            end
        end
        local t = CreateTrigger()
        for i = 0, bj_MAX_PLAYER_SLOTS - 1 do
            TriggerRegisterPlayerChatEvent(t, Player(i), ACTIVATOR, false)
        end
        TriggerAddAction(t, function() self:toggleGrid() end)
        -- Useful debug prints
        --print(self.gridWidth)
        --print(self.gridHeight)
        --print(#self.gridEffect)
    end
    function GridSystem:toggleGrid()
        if GetLocalPlayer() ~= GetTriggerPlayer() then return end
        self.showHide = not self.showHide
        for _, image in ipairs(self.gridEffect) do
            SetImageRenderAlways(image, self.showHide)
        end
    end
    GridSystem:init()
end)

March 16, 2020 Release - Jass Version.
March 11, 2024 Update - Added Lua Version.
January 22, 2025 Update - Added Jass v2 and Lua v2 Version.
Contents

Grid System Template (Map)

Reviews
Wrda
While this resource is quite great, the lack of a clear API takes a few points (the chat commands can stay as they are). The Lua version can be slightly improved regarding the hex conversion, the OOP style doesn't make much sense in this context but...

AGD

AGD

Level 16
Joined
Mar 29, 2016
Messages
688
Cool. Maybe allow more than just showing/hiding the grid. Ex: a mode wherein single grids are shown on mouse hover and hidden again when not hovered.


exitwhen i > 23 -> exitwhen i == bj_MAX_PLAYER_SLOTS
Avoid inlining constants if possible. Think about all older resources that uses 12 as the player count. Guess what? They now need to edited everytime they are imported to newer maps. Not saying this will happen again, but it should be a lesson and its better to just observe the best practice in all areas.

You should move the show/hide hotkeys and chat commands into a configuration section at the top. They should not be hardcoded. I see a lot of hardcoding in fact - the default grid color, the individual grid size (128), etc.

Maybe you could also allow to change the size of the individual grid, in multiples of 128.

You could store the repeatedly used function such as GetTriggerPlayer() into local variables, while you could also set GetLocalPlayer() into a global variable at map init.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
Quick Lua review:
Lua:
function GridSystem:GetHexSignValue(sign)
Get rid of that and use ->
Lua:
---@param sign string|integer
---@return integer
local function hex2Dec(sign)
    return tonumber(tostring(sign), 16)
end

Would be nice to be able to configure the grid to 32, 64, 128, and even 256 size.
I agree that API which takes player for grid, to either show, hide, get position, change colour & alpha is better than having these events.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
Here comes an official review:
This should worked to have a clear API instead of having commands and events.
ShowHideGrid remade to handle show/hide for a player, MouseMove to "SetGridPosition" for a player, "SetGridColour" instead of SetGridProp, "SetGridSize" instead of typing "-gs #".
The ability to change grid tile size would be a great addition, instead of always being 128.

VJASS:
JASS:
if sign == "a" or sign == "A" then
    return 10
endif
StringCase(sign, false) == "a" simplifies. However, if things are reworked to a function like "SetGridColour", then it would take 0-255 rrggbbaa values
Lua:
As in my previous post, hex2Dec simplifies the entire process easily.
You also should have your VJASS version in a seperate map, and not included in the Lua version.

Quite useful system for placement related maps. The only major gripe is the API.
 

Wrda

Spell Reviewer
Level 28
Joined
Nov 18, 2012
Messages
2,010
While this resource is quite great, the lack of a clear API takes a few points (the chat commands can stay as they are).
The Lua version can be slightly improved regarding the hex conversion, the OOP style doesn't make much sense in this context but it isn't harmful either.

Approved
 
Top