error appears only if I repeat a couple of steps for several times very fast

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
MarsOdin
Prole
Posts: 12
Joined: Tue Apr 02, 2019 11:46 pm

error appears only if I repeat a couple of steps for several times very fast

Post by MarsOdin »

I'm getting the following error:

Error

main.lua:638: attempt to compare nil with number


Traceback

main.lua:638: in function 'mouseHoverMenu'
main.lua:909: in function 'update'
[C]: in function 'xpcall'

I believe it is because my code is not efficient. But I hope the community can help me figure it out and maybe improve my code.

How to reproduce this error:
I have a main menu, if I click on "Continue" the map with locations opens and I can click on a location and enter the location view. I enter a location with a list of units in it. In here I have a button which if clicked sends you back to the main menu screen (this is just placeholder). If I repeat the steps very fast, after approximate the 3rd or 4th time when I click on the button to get back to the main menu the error appears.

Here is my code:

Code: Select all


function love.load()
    math.randomseed( os.time() )
    dt = love.timer.getDelta()
    -- Debug
        debugMsg = "-"
        debugN = 0
        function drawDebug()
            love.graphics.setFont(debug.font)
            love.graphics.setColor( 0.2, 0.1, 0.8)
            for i = 1, #debug do
                love.graphics.print(debug[i].name..": "..debug[i].data,0,i*22)
            end
        end
    -- Load images
        img = {}
        img.map = {}
        img.map.terrain = {
            [1] = love.graphics.newImage("textures/map/ground1.png"),
            [2] = love.graphics.newImage("textures/map/ground2.png"),
            [3] = love.graphics.newImage("textures/map/ground3.png"),
            [4] = love.graphics.newImage("textures/map/ground4.png"),
            [5] = love.graphics.newImage("textures/map/ground5.png"),
            [6] = love.graphics.newImage("textures/map/ground6.png"),
            [7] = love.graphics.newImage("textures/map/ground7.png"),
            [8] = love.graphics.newImage("textures/map/ground8.png"),
            [9] = love.graphics.newImage("textures/map/ground9.png"),

        }
        img.map.selection = {
            [1] = love.graphics.newImage("textures/map/selection.png"),
        }
        img.map.entity = {
            [1] = love.graphics.newImage("textures/map/hq.png"),
        }
        img.ui = {}
        img.ui.box = {
            [1] = love.graphics.newImage("textures/ui/box1.png"),
        }
        img.button ={}
        img.button.passive = {
            [1] = love.graphics.newImage("textures/ui/button_passive.png"),
        }
        img.button.active = {
            [1] = love.graphics.newImage("textures/ui/button_active.png"),
        }
    -- Cursors
        cursor = {
            [1] = love.mouse.newCursor("textures/ui/pointerA.png", 0,0),
            [2] = love.mouse.newCursor("textures/ui/pointerCrosshair.png", 32/2, 32/2),
        }
        love.mouse.setCursor(cursor[1])

    -- Map
        map = {}
        map.width = love.graphics.getWidth( ) -- get window width
        map.height = love.graphics.getHeight( ) -- get window height
        
        map.player = {}
        map.player.moveSpeed = 0.2
        map.player.coordX = 0
        map.player.coordY = 0
        
        map.tile = {}
        map.tile.width = img.map.terrain[1]:getWidth()
        map.tile.centerOffsetX = (map.width - map.tile.width) /2
        map.tile.centerOffsetY = (map.height - map.tile.width) /2
        map.tile.startRadius = 27
        --[[
        if math.floor(map.width / map.tile.width)%2 == 0 then
            map.tile.startRadius = math.floor(map.width / map.tile.width) +1
        else
            map.tile.startRadius = math.floor(map.width / map.tile.width)
        end]]
        map.tile.nextNewTile = 0

    -- Tiles creation
        function createTile(coordX,coordY,posX,posY)
            map.tile.nextNewTile = map.tile.nextNewTile + 1
            map.tile[map.tile.nextNewTile] = {}
            map.tile[map.tile.nextNewTile].id = map.tile.nextNewTile
            map.tile[map.tile.nextNewTile].coordX = coordX
            map.tile[map.tile.nextNewTile].coordY = coordY
            map.tile[map.tile.nextNewTile].posX = posX
            map.tile[map.tile.nextNewTile].posY = posY
            map.tile[map.tile.nextNewTile].texture = img.map.terrain[math.random(1,#img.map.terrain)]
            map.tile[map.tile.nextNewTile].tag = {}
        end
        
        for i = -(map.tile.startRadius-1)/2, (map.tile.startRadius-1)/2 do
            for j = -(map.tile.startRadius-1)/2, (map.tile.startRadius-1)/2 do
                createTile(i, j, i, j)
            end
        end

    -- Check for need of new tiles to create
        checkForNewTile = {}
        checkForNewTile.n = 0
        checkForNewTile.r = 2
        checkForNewTile.switchCreate = {}
        checkForNewTile.switchDontCreate = {}
        checkForNewTile.coordOffsetX = {}
        checkForNewTile.coordOffsetY = {}

        for i = -1*checkForNewTile.r, checkForNewTile.r do 
            for j = -1*checkForNewTile.r, checkForNewTile.r do
                if j == 0 and k == 0 then
                else
                    checkForNewTile.n = checkForNewTile.n + 1 
                    checkForNewTile.switchCreate[checkForNewTile.n] = false
                    checkForNewTile.switchDontCreate[checkForNewTile.n] = false
                    checkForNewTile.coordOffsetX[checkForNewTile.n] = i
                    checkForNewTile.coordOffsetY[checkForNewTile.n] = j
                end
            end
        end

    
    -- Entities
        entity = {
            n = 4,
            [1] = {id = 1,
                name = "hq",
                img = img.map.entity[1],
                posX = 0,
                posY = 0,
                destinationX = 0,
                destinationY = 0,
                distanceToDestination = 0,
                speed = 0,
                speedX = 0,
                speedY = 0,
                tag = {"location"},
                mapEntity = true,
            },
            [2] = {id = 2,
                name = "a random location",
                img = img.map.entity[1],
                posX = -5,
                posY = -5,
                destinationX = 0,
                destinationY = 0,
                distanceToDestination = 0,
                speed = 0,
                speedX = 0,
                speedY = 0,
                tag = {"location"},
                mapEntity = true,
            },
            [3] = {id = 3,
                name = "entity Group 1",
                img = img.map.entity[1],
                posX = 0,
                posY = 0,
                destinationX = 0,
                destinationY = 0,
                distanceToDestination = 0,
                speed = 0.02,
                speedX = 0,
                speedY = 0,
                tag = {"group"},
                mapEntity = false,
            },
            [4] = {id = 4,
            name = "a random location",
            img = img.map.entity[1],
            posX = 6,
            posY = -6,
            destinationX = 0,
            destinationY = 0,
            distanceToDestination = 0,
            speed = 0,
            speedX = 0,
            speedY = 0,
            tag = {"location"},
            mapEntity = true,
            },
        }

        for i = 1,37 do
            entity.n = entity.n + 1
            entity[entity.n] = {}
            entity[entity.n].id = entity.n
            entity[entity.n].name = tostring("unit "..(entity.n - 3))
            entity[entity.n].posX = 0
            entity[entity.n].posY = 0
            entity[entity.n].destinationX = 0
            entity[entity.n].destinationY = 0
            entity[entity.n].distanceToDestination = 0
            entity[entity.n].speed = 0
            entity[entity.n].speedX = 0
            entity[entity.n].speedY = 0
            entity[entity.n].tag = {"unit","friendly"}
            entity[entity.n].mapEntity = false
        end
    -- Game Mechanics
        gm = {}
            gm.screen = {
                [1] = "Title Screen",
                [2] = "Map Screen",
                [3] = "Location Screen",
            }
            gm.screen.active = 1
            gm.navi = {}
            gm.navi.mode = 1
            --[[ gm.navi.mode: 
                1: normal mouse navigation
                2: select target with mouse navigation
            ]]
            gm.navi.selection = {}
            gm.navi.selection.n = 0
            gm.navi.selection.id = 0

            gm.origin = 0
            gm.target = 0

            gm.entityMove = {}
            gm.entityMove.n = 0

        function saveSelectedLocationId()
            if gm.screen.active == 2 and gm.navi.selection.n ~= 0 then
                gm.navi.selection.id = gm.navi.selection.n
            else
                gm.navi.selection.id = 0
            end
        end
        function clickOnTarget()
            if gm.screen.active == 2 and
                gm.navi.selection.n ~= 0
            then
                gm.target = gm.navi.selection.n
                love.mouse.setCursor(cursor[1])
                entitySpeedAndDistanceCalculation(
                    3,
                    entity[gm.target].posX,
                    entity[gm.target].posY
                    
                )      
                gm.navi.mode = 1                          
            end
        end

    -- Map Navigation and Map Render
        function mapKeyboardNavigation(key) -- w,a,s,d
            if love.keyboard.isDown("w") == true then
                for i = 1, #map.tile do
                    map.tile[i].posY = map.tile[i].posY + map.player.moveSpeed
                end
                for i = 1, #entity do
                    entity[i].posY = entity[i].posY + map.player.moveSpeed
                    entity[i].destinationY = entity[i].destinationY + map.player.moveSpeed
                end
            elseif love.keyboard.isDown("s") == true then
                for i = 1, #map.tile do
                    map.tile[i].posY = map.tile[i].posY - map.player.moveSpeed
                end
                for i = 1, #entity do
                    entity[i].posY = entity[i].posY - map.player.moveSpeed
                    entity[i].destinationY = entity[i].destinationY - map.player.moveSpeed
                end
            elseif love.keyboard.isDown("a") == true then
                for i = 1, #map.tile do
                    map.tile[i].posX = map.tile[i].posX + map.player.moveSpeed
                end
                for i = 1, #entity do
                    entity[i].posX = entity[i].posX + map.player.moveSpeed
                    entity[i].destinationX = entity[i].destinationX + map.player.moveSpeed
                end
            elseif love.keyboard.isDown("d") == true then
                for i = 1, #map.tile do
                    map.tile[i].posX = map.tile[i].posX - map.player.moveSpeed
                end
                for i = 1, #entity do
                    entity[i].posX = entity[i].posX - map.player.moveSpeed
                    entity[i].destinationX = entity[i].destinationX - map.player.moveSpeed
                end
            end
            updateTilesToRender()
        end
        -- >>
            function updateTilesToRender()
                --[[
                -- Check for creation of new tiles
                    for i = 1, #checkForNewTile.switchCreate do
                        checkForNewTile.switchDontCreate[i] = false
                    end
                    for i = 1, #map.tile do
                        for j = 1, #checkForNewTile.switchCreate do
                            if map.tile[i].coordX == map.player.coordX + checkForNewTile.coordOffsetX[j] and
                                map.tile[i].coordY == map.player.coordY + checkForNewTile.coordOffsetY[j]
                                then
                                checkForNewTile.switchDontCreate[j] = true
                            else
                                checkForNewTile.switchCreate[j] = true
                            end
                        end
                    end
                ]]
                -- Sort tiles to be rendered
                    gm.navi.tilesOnScreen = {}
                    gm.navi.tilesOnScreen.n = 0
                    gm.navi.tilesOnScreen.r = 27 --math.floor(map.width/map.tile.width)
                    for i = 1, #map.tile do
                        if math.abs(map.tile[i].posX) < gm.navi.tilesOnScreen.r and
                            math.abs(map.tile[i].posY) < gm.navi.tilesOnScreen.r
                            then
                            gm.navi.tilesOnScreen.n = gm.navi.tilesOnScreen.n + 1
                            gm.navi.tilesOnScreen[gm.navi.tilesOnScreen.n] = map.tile[i].id
                        end
                    end
            end
            updateTilesToRender()

        mouse = {}
            mouse.wheel = {}
            mouse.wheel.scroll = 0
            mouse.wheel.scrollSum = 0

        --function love.mousepressed(x, y, button)
        function love.mousereleased(x, y, button)
            if gm.navi.mode == 1 then -- normal mode
                if button == 1 then
                    executeHandler()
                    handlerViewLocation() --for map
                elseif button == 2 then
                    openOptionsMenu()
                elseif button == 4 then
                    handlerBack()
                end
            elseif gm.navi.mode == 2 then -- select target with mouse mode
                if button == 1 then
                    clickOnTarget()
                elseif button == 2 then
                elseif button == 4 then
                    gm.navi.mode = 1
                    love.mouse.setCursor(cursor[1])                    
                end
            end
        end
        -- >>
            function executeHandler()
                for i = 1, #button do
                    if button[i].highlight == true then
                        button[i].active = true
                        button[i].highlight = false
                        button[i].handler()
                    end
                end
                for i = 1, #menu do
                    if menu[i].active == true then
                        for j = 1, #menu[i].options do
                            if menu[i].options[j].highlight == true then
                                menu[i].options[j].highlight = false
                                menu[i].active = false
                                menu[i].options[j].handler()
                            end
                        end
                    end
                end
            end

 
        function love.wheelmoved(x, y)
            if gm.screen.active == 3 then
                if y > 0 then
                    mouse.wheel.scroll = mouse.wheel.scroll + 20
                    mouse.wheel.scrollSum = mouse.wheel.scrollSum + 20
                elseif y < 0 then
                    mouse.wheel.scroll = mouse.wheel.scroll - 20
                    mouse.wheel.scrollSum = mouse.wheel.scrollSum - 20
                end
            end
        end
        -- >>
            function stopMouseScroll()
                mouse.wheel.scroll = 0 
            end

        function mouseHoverMap()
            if menu[2].active == false then
                gm.navi.selection.n = 0
                for i = 1, #entity do
                    if entity[i].mapEntity == true then
                        if love.mouse.getX() >= entity[i].posX * map.tile.width + map.tile.centerOffsetX and
                        love.mouse.getX() <= (entity[i].posX * map.tile.width + map.tile.centerOffsetX) + map.tile.width and
                        love.mouse.getY() >= entity[i].posY * map.tile.width + map.tile.centerOffsetY and
                        love.mouse.getY() <= entity[i].posY * map.tile.width + map.tile.centerOffsetY + map.tile.width
                        then
                            gm.navi.selection.n = i
                        end
                    end
                end
            end
        end

        render = {}
            render.distance = 27
            render.entity = {}
            render.entity.n = 0
        function updateGraphicsToRender()
            for i = 1, #entity do
                if entity[i].mapEntity == true then
                    if math.abs(entity[i].posX) < render.distance and
                        math.abs(entity[i].posY) < render.distance
                    then 
                        render.entity.n = render.entity.n + 1
                        render.entity[render.entity.n] = entity[i].id
                    end
                end
            end
        end
        updateGraphicsToRender()

        function entitySpeedAndDistanceCalculation(entityId
            ,destinationPosX,destinationPosY
            )
            entity[entityId].mapEntity = true
            entity[entityId].destinationX = destinationPosX 
            entity[entityId].destinationY = destinationPosY
            entity[entityId].distanceToDestination = math.sqrt((entity[entityId].destinationX - entity[entityId].posX)^2 + (entity[entityId].destinationY - entity[entityId].posY)^2)
            entity[entityId].speedX = entity[entityId].speed * ((entity[entityId].destinationX - entity[entityId].posX) / entity[entityId].distanceToDestination)
            entity[entityId].speedY = entity[entityId].speed * ((entity[entityId].destinationY - entity[entityId].posY) / entity[entityId].distanceToDestination)
            
        end

        function updateEntityPosition()
            for i = 1, #entity do
                if entity[i].mapEntity == true and
                    entity[i].speed ~= 0
                then
                    if math.abs(entity[i].distanceToDestination) > entity[i].speed then
                        debugMsg = entity[3].speed
                        entity[i].posX = entity[i].posX + entity[i].speedX
                        entity[i].posY = entity[i].posY + entity[i].speedY
                        entity[i].distanceToDestination = math.sqrt((entity[i].destinationX - entity[i].posX)^2 + (entity[i].destinationY - entity[i].posY)^2)
                    else
                        entity[i].posX = entity[i].destinationX
                        entity[i].posY = entity[i].destinationY
                        entity[i].destinationX = 0
                        entity[i].destinationY = 0  
                        entity[i].distanceToDestination = 0
                        entity[i].speed = 0
                        entity[i].speedX = 0
                        entity[i].speedY = 0
                    end
                end
            end
        end

    -- Handlers
        function hanlderMainMenu()
            gm.screen.active = 1
        end
        function handlerContinue()
            gm.screen.active = 2
        end
        function handlerViewLocation()
            saveSelectedLocationId()
            if gm.navi.selection.id ~= 0 and
                menu[2].active == false and
                gm.navi.mode == 1
            then
                gm.screen.active = 3
                menu[3].options = {}
                for i = 1, #entity do
                    for j = 1, #entity[i].tag do
                        if entity[i].tag[j] == "unit" then
                            for k = 1, #entity[i].tag do
                                if entity[i].tag[k] == "friendly" then
                                    if entity[i].posX == entity[gm.navi.selection.id].posX and
                                    entity[i].posY == entity[gm.navi.selection.id].posY then
                                        menu[3].n = menu[3].n + 1
                                        menu[3].options[menu[3].n] = {}
                                        menu[3].options[menu[3].n].text = entity[i].name
                                        menu[3].options[menu[3].n].highlight = false
                                        menu[3].options[menu[3].n].handler = handlerEmpty
                                    end
                                end
                            end
                        end
                    end
                end
            end
        end
        function handlerSelectTarget()
            gm.navi.mode = 2
            love.mouse.setCursor(cursor[2])
        end
        function handlerBack()
            if gm.screen.active == 2 then
                if menu[2].active == false then
                    gm.screen.active = 1
                elseif menu[2].active == true then
                    menu[2].active = false
                end
            elseif gm.screen.active == 3 then
                mouse.wheel.scroll = -mouse.wheel.scrollSum 
                mouse.wheel.scrollSum = 0
                gm.screen.active = 2
                menu[3].active = false
            end
        end
        function handlerExitGame()
            love.event.quit()
        end
        function handlerEmpty()
        end
    
    -- Menus
        menu = {}
            menu.buttonOffsetX = 0.2
            menu.buttonOffsetY = 0.1
            menu.buttonOffsetHeight = 0.8
            menu[1] = {} -- Title Screen Menu
                menu[1].active = false
                menu[1].scrollable = false
                menu[1].hasBackground = false
                menu[1].isList = true
                menu[1].n = 0
                menu[1].fontSize = 40
                menu[1].font = love.graphics.newFont(menu[1].fontSize)
                menu[1].lineDistance = menu[1].fontSize * 1.5
                menu[1].backgroundColor = {0,0,0}
                menu[1].textColor = {1,1,1}
                menu[1].textHighlightColor = {0.8, 0.6, 0.4}
                menu[1].posX = 600
                menu[1].posY = 200
                menu[1].posYScroll = 200
                menu[1].width = 250
                menu[1].options = {
                    [1] = { posX = 0, posY = 0, width = 0, height = 0, text = "Continue", handler = handlerContinue},
                    [2] = { posX = 0, posY = 0, width = 0, height = 0, text = "New Game", handler = handlerEmpty},
                    [3] = { posX = 0, posY = 0, width = 0, height = 0, text = "Settings", handler = handlerEmpty},
                    [4] = { posX = 0, posY = 0, width = 0, height = 0, text = "Exit", handler = handlerExitGame},
                }
                for i = 1, #menu[1].options do
                    menu[1].options[i].highlight = false
                end
                menu[1].height = #menu[1].options * menu[1].lineDistance

            menu[2] = {} -- Location Options (Right Click)
                menu[2].active = false
                menu[2].scrollable = false
                menu[2].hasBackground = true
                menu[2].isList = true
                menu[2].n = 0
                menu[2].fontSize = 20
                menu[2].font = love.graphics.newFont(menu[2].fontSize)
                menu[2].lineDistance = menu[2].fontSize * 1.5
                menu[2].backgroundColor = {0.2, 0.2, 0.2}
                menu[2].textColor = {1,1,1}
                menu[2].textHighlightColor = {0.8, 0.6, 0.4}
                menu[2].posX = 0
                menu[2].posY = 0
                menu[2].posYScroll = 0
                menu[2].width = 160
                menu[2].options = {
                    [1] = { posX = 0, posY = 0, width = 0, height = 0, text = "View Location", handler = handlerViewLocation},
                    [2] = { posX = 0, posY = 0, width = 0, height = 0, text = "Group 1", handler = handlerSelectTarget},
                }
                for i = 1, #menu[2].options do
                    menu[2].options[i].highlight = false
                end
                menu[2].height = #menu[2].options * menu[2].lineDistance
            menu[3] = {} -- Units
                menu[3].active = false
                menu[3].scrollable = true
                menu[3].hasBackground = true
                menu[3].isList = true
                menu[3].n = 0
                menu[3].fontSize = 20
                menu[3].font = love.graphics.newFont(menu[3].fontSize)
                menu[3].lineDistance = menu[3].fontSize * 1.5
                menu[3].backgroundColor = {0.2, 0.2, 0.2}
                menu[3].textColor = {1,1,1}
                menu[3].textHighlightColor = {0.8, 0.6, 0.4}
                menu[3].posX = love.graphics.getWidth()/4+50
                menu[3].posY = love.graphics.getHeight()/8+100
                menu[3].posYScroll = love.graphics.getHeight()/8+100
                menu[3].width = img.ui.box[1]:getWidth()-100
                menu[3].options = {}
                menu[3].height = 400

        function drawMenu(scrollable
            ,font,posX,posY,posYScroll
            ,width,height
            ,backColor1,backColor2,backColor3
            ,textColor1,textColor2,textColor3
            ,menuOptions,lineDist
            ,textHighlightColor1,textHighlightColor2,textHighlightColor3
            ,hasBackground,isList
            )
            if scrollable == true then
                height = height                    
            elseif scrollable == false then
                height = #menuOptions*lineDist
            end
            love.graphics.setFont(font)
            love.graphics.setColor(backColor1,backColor2,backColor3)
            if hasBackground == true then
                love.graphics.rectangle("fill",posX,posY,width,height)
            end
            if isList == true then
                love.graphics.setScissor(posX,posY,width,height)
                for i = 1, #menuOptions do
                    menuOptions[i].posX = posX + lineDist*menu.buttonOffsetX
                    menuOptions[i].posY = posY + lineDist*menu.buttonOffsetY + (i-1)*lineDist
                    menuOptions[i].width = width
                    menuOptions[i].height = lineDist * menu.buttonOffsetHeight
                    if menuOptions[i].highlight == true then
                        love.graphics.setColor(textHighlightColor1,textHighlightColor2,textHighlightColor3)
                    else
                        love.graphics.setColor(textColor1,textColor2,textColor3)
                    end
                    love.graphics.print(menuOptions[i].text,posX+lineDist*0.2,posYScroll+lineDist*0.1 + (i-1)*lineDist)
                end
                love.graphics.setScissor()
            else
                --empty
            end
        end
        function mouseHoverMenu()
            for i = 1, #menu do
                if menu[i].active == true then
                    if love.mouse.getX() >= menu[i].posX and
                        love.mouse.getX() <= menu[i].posX + menu[i].width and
                        love.mouse.getY() >= menu[i].posY and
                        love.mouse.getY() <= menu[i].posY + menu[i].height
                    then
                        for j = 1, #menu[i].options do
                            if menu[i].scrollable == false and
                                love.mouse.getX() >= menu[i].options[j].posX and
                                love.mouse.getX() <= menu[i].options[j].posX + menu[i].options[j].width and
                                love.mouse.getY() >= menu[i].options[j].posY and
                                love.mouse.getY() <= menu[i].options[j].posY + menu[i].options[j].height
                                or
                                menu[i].scrollable == true and
                                love.mouse.getX() >= menu[i].options[j].posX and
                                love.mouse.getX() <= menu[i].options[j].posX + menu[i].options[j].width and
                                love.mouse.getY() >= menu[i].options[j].posY + mouse.wheel.scrollSum and
                                love.mouse.getY() <= menu[i].options[j].posY + menu[i].options[j].height + mouse.wheel.scrollSum
                            then
                                menu[i].options[j].highlight = true
                            else
                                menu[i].options[j].highlight = false
                            end
                        end
                    else
                        for j = 1, #menu[i].options do
                            menu[i].options[j].highlight = false
                        end
                    end
                end
            end
        end
        function openOptionsMenu()
            saveSelectedLocationId()
            if gm.navi.selection.n ~= 0 and
                menu[2].active == false
            then
                menu[2].posX = love.mouse.getX()
                menu[2].posY = love.mouse.getY()
                menu[2].posYScroll = love.mouse.getY()
                menu[2].active = true
                gm.origin = gm.navi.selection.n
            end
        end
        function updateMenuScrollPosition()
            for i = 1, #menu do
                if menu[i].active == true then
                    menu[i].posYScroll = menu[i].posYScroll + mouse.wheel.scroll
                end
            end
        end

    -- Buttons
        button = {
            fontSize = 20,
            [1] = {
                text = "Create Group",
                handler = hanlderMainMenu,
                screen = 3,
                imgPassive = img.button.passive[1],
                imgActive = img.button.active[1],
                imgUsed = imgPassive,
                posX = 450,
                posY = 650,
                width = 0,
                height = 0,
                textWidth = 0,
                textColor = {1,1,1},
                textHighlightColor = {0.9,0.7,0.4},
                highlight = false,
                active = false,
            },
        }
        button.font = love.graphics.newFont("fonts/kenvector_future_thin.ttf", button.fontSize)
        for i = 1, #button do
            button[i].textWidth = button.font:getWidth(button[i].text)
            button[i].width = button[i].imgPassive:getWidth()
            button[i].height = button[i].imgPassive:getHeight()
        end

        function drawButtons()
            love.graphics.setFont(button.font)
            for i = 1, #button do
                if button[i].screen == gm.screen.active then
                    love.graphics.setColor(1,1,1)
                    --if button[i].active == true then
                    if love.mouse.isDown(1) and
                        button[i].highlight == true
                    then
                        button[i].imgUsed = button[i].imgActive
                    else
                        button[i].imgUsed = button[i].imgPassive
                    end
                    love.graphics.draw(button[i].imgUsed,
                    button[i].posX,button[i].posY,
                    0,1,1)
                    if button[i].highlight == true then
                        love.graphics.setColor(button[i].textHighlightColor[1],button[i].textHighlightColor[2],button[i].textHighlightColor[3])
                    else
                        love.graphics.setColor(button[i].textColor[1],button[i].textColor[2],button[i].textColor[3])
                    end
                    if love.mouse.isDown(1) and
                        button[i].highlight == true
                    then
                        love.graphics.print(button[i].text,
                        button[i].posX + (button[i].imgPassive:getWidth() - button[i].textWidth)/2,
                        button[i].posY + button.fontSize/1.5)
                    else
                        love.graphics.print(button[i].text,
                        button[i].posX + (button[i].imgPassive:getWidth() - button[i].textWidth)/2,
                        button[i].posY + button.fontSize/2)
                    end
                end
            end
        end
        function mouseHoverButton()
            for i = 1, #button do
                if button[i].screen == gm.screen.active then
                    if love.mouse.getX() >= button[i].posX and
                        love.mouse.getX() <= button[i].posX + button[i].width and
                        love.mouse.getY() >= button[i].posY and
                        love.mouse.getY() <= button[i].posY + button[i].height
                    then
                        button[i].highlight = true
                    else
                        button[i].highlight = false
                    end
                end
            end
        end


    -- Screen 1
        function drawTitleScreen()
            menu[1].active = true
            drawMenu(menu[1].scrollable
            ,menu[1].font
            ,menu[1].posX
            ,menu[1].posY
            ,menu[1].posYScroll
            ,menu[1].width
            ,menu[1].height
            ,menu[1].backgroundColor[1]
            ,menu[1].backgroundColor[2]
            ,menu[1].backgroundColor[3]
            ,menu[1].textColor[1]
            ,menu[1].textColor[2]
            ,menu[1].textColor[3]
            ,menu[1].options
            ,menu[1].lineDistance
            ,menu[1].textHighlightColor[1]
            ,menu[1].textHighlightColor[2]
            ,menu[1].textHighlightColor[3]
            ,menu[1].hasBackground
            ,menu[1].isList
            )
        end
    -- Screen 2
        function drawMap()
            love.graphics.setColor( 1, 1, 1)
            for i = 1, #gm.navi.tilesOnScreen do
                love.graphics.draw(map.tile[gm.navi.tilesOnScreen[i]].texture, 
                    map.tile[gm.navi.tilesOnScreen[i]].posX * map.tile.width + map.tile.centerOffsetX,
                    map.tile[gm.navi.tilesOnScreen[i]].posY * map.tile.width + map.tile.centerOffsetY,
                    0, 
                    1, 
                    1)
                
                --love.graphics.print(map.tile[navi.tilesOnScreen[i]].coordX.."/"..map.tile[navi.tilesOnScreen[i]].coordY,
                --map.tile[navi.tilesOnScreen[i]].posX * map.tile.width + map.tile.centerOffsetX,
                --map.tile[navi.tilesOnScreen[i]].posY * map.tile.width + map.tile.centerOffsetY)
                
                --love.graphics.print(map.tile[navi.tilesOnScreen[i]].posX.."/"..map.tile[navi.tilesOnScreen[i]].posY,
                --map.tile[navi.tilesOnScreen[i]].posX * map.tile.width + map.tile.centerOffsetX,
                --map.tile[navi.tilesOnScreen[i]].posY * map.tile.width + map.tile.centerOffsetY+20)
                
            end
        end
        function drawEntities()
            for i = 1, #entity do
                if entity[i].mapEntity == true then
                    love.graphics.setColor(1, 1, 1)
                    love.graphics.draw(entity[i].img, 
                    entity[i].posX * map.tile.width + map.tile.centerOffsetX,
                    entity[i].posY * map.tile.width + map.tile.centerOffsetY,
                    0, 
                    1, 
                    1)
                end
            end
        end
        function drawSelection()
            if gm.navi.selection.n ~= 0 then
                love.graphics.setColor(1, 1, 1)
                love.graphics.draw(img.map.selection[1], 
                entity[gm.navi.selection.n].posX * map.tile.width + map.tile.centerOffsetX,
                entity[gm.navi.selection.n].posY * map.tile.width + map.tile.centerOffsetY,
                0, 
                1, 
                1)
            end
        end
        function rightClickOnLocation()
            if menu[2].active == true then
                drawMenu(menu[2].scrollable
                ,menu[2].font
                ,menu[2].posX
                ,menu[2].posY
                ,menu[2].posYScroll
                ,menu[2].width
                ,menu[2].height
                ,menu[2].backgroundColor[1]
                ,menu[2].backgroundColor[2]
                ,menu[2].backgroundColor[3]
                ,menu[2].textColor[1]
                ,menu[2].textColor[2]
                ,menu[2].textColor[3]
                ,menu[2].options
                ,menu[2].lineDistance
                ,menu[2].textHighlightColor[1]
                ,menu[2].textHighlightColor[2]
                ,menu[2].textHighlightColor[3]
                ,menu[2].hasBackground
                ,menu[2].isList
                )
            end
        end
    -- Screen 3
        function drawLocationScreen()
            if gm.screen.active == 3 then
                love.graphics.setColor( 1, 1, 1)
                love.graphics.draw(img.ui.box[1],love.graphics.getWidth()/4, love.graphics.getHeight()/8,0,1,1)
                menu[3].active = true
                drawMenu(menu[3].scrollable
                ,menu[3].font
                ,menu[3].posX
                ,menu[3].posY
                ,menu[3].posYScroll
                ,menu[3].width
                ,menu[3].height
                ,menu[3].backgroundColor[1]
                ,menu[3].backgroundColor[2]
                ,menu[3].backgroundColor[3]
                ,menu[3].textColor[1]
                ,menu[3].textColor[2]
                ,menu[3].textColor[3]
                ,menu[3].options
                ,menu[3].lineDistance
                ,menu[3].textHighlightColor[1]
                ,menu[3].textHighlightColor[2]
                ,menu[3].textHighlightColor[3]
                ,menu[3].hasBackground
                ,menu[3].isList
                )

                menu[3].n = 0
            
            end
        end       
end

function love.update(dt)
    -- Debug
        --debugN = debugN +1
        
        debug = {
            font = love.graphics.newFont("fonts/kenvector_future_thin.ttf", 18),
            [1] = {name = "gm.navi.selection.n", data = gm.navi.selection.n},
            [2] = {name = "gm.navi.tilesOnScreen.r", data = gm.navi.tilesOnScreen.r},
            [3] = {name = "menu[1].active", data = tostring(menu[1].active)},
            [4] = {name = "menu[2].active", data = tostring(menu[2].active)},
            [5] = {name = "debugMsg", data = debugMsg},
            [6] = {name = "debugN", data = debugN},
            [7] = {name = "gm.navi.tilesOnScreen.n", data = gm.navi.tilesOnScreen.n},
            [8] = {name = "mouse.wheel.scroll", data = mouse.wheel.scroll},
            [9] = {name = "mouse.wheel.scrollSum", data = mouse.wheel.scrollSum},
            [10] = {name = "#entity", data = #entity},
        }
    
    -- Updating Map Visuals
        updateEntityPosition()    
        updateGraphicsToRender()
    -- Keyboard
        mapKeyboardNavigation(key)
    -- Mouse
        mouseHoverMenu()
        mouseHoverMap()
        updateMenuScrollPosition()
        stopMouseScroll()
        mouseHoverButton()
    -- Discovering New Tiles
        for i = 1, #checkForNewTile.switchCreate do
            if checkForNewTile.switchCreate[i] == true and checkForNewTile.switchDontCreate[i] == false then
                createTile(
                    map.player.coordX + checkForNewTile.coordOffsetX[i], 
                    map.player.coordY + checkForNewTile.coordOffsetY[i], 
                    checkForNewTile.coordOffsetX[i], 
                    checkForNewTile.coordOffsetY[i])
                navi.tilesOnScreen.n = navi.tilesOnScreen.n + 1
                navi.tilesOnScreen[navi.tilesOnScreen.n] = map.tile[map.tile.nextNewTile].id
                checkForNewTile.switchCreate[i] = false
            end
        end

end

function love.draw()
    if gm.screen.active == 1 then
        drawTitleScreen()
    elseif gm.screen.active == 2 then
        drawMap()
        drawEntities()
        drawSelection()
        rightClickOnLocation()
    elseif gm.screen.active == 3 then
        drawLocationScreen()
    end
    drawButtons()
    drawDebug()
end







MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: error appears only if I repeat a couple of steps for several times very fast

Post by MrFariator »

Well, on line 638 you try to compare value posX to mouse position, and this value is somehow nil. You could potentially check that the relevant variables exist in the inner loop like this:

Code: Select all

for j = 1, #menu[i].options do
  -- check that the relevant variables exist
  if menu[i].options[j] and menu[i].options[j].posX and menu[i].options[j].posY then
    -- store the mouseX and mouseY instead of repeatedly calling the function
    -- ideally outside the loop for good measure
    local mouseX, mouseY = love.mouse.getX(), love.mouse.getY()
    if menu[i].scrollable == false and
      mouseX >= menu[i].options[j].posX and
      mouseX <= menu[i].options[j].posX + menu[i].options[j].width and
      mouseY >= menu[i].options[j].posY and
      mouseY <= menu[i].options[j].posY + menu[i].options[j].height
      or
      menu[i].scrollable == true and
      mouseX >= menu[i].options[j].posX and
      mouseX <= menu[i].options[j].posX + menu[i].options[j].width and
      mouseY >= menu[i].options[j].posY + mouse.wheel.scrollSum and
      mouseY <= menu[i].options[j].posY + menu[i].options[j].height + mouse.wheel.scrollSum
    then
      menu[i].options[j].highlight = true
    else
      menu[i].options[j].highlight = false
    end
  end
end
However, this does not fix your underlying problem with posX and posY suddenly being nil. That issue probably lies elsewhere, and at a glance it may be menu[3].options, because from quickly skimming your code I don't see you assigning posX or posY inside handlerViewLocation, specifically the lines 472-476. As such, in certain situations it's entirely possible mouseHoverMenu will loop through menu[3].options, and then encounters this crash you're reporting.

Additionally as a side note: you do not need to wrap your global functions inside love.load; use love.load to load and setup things, not to define the entire code base your game uses. Putting everything inside love.load makes organization a mess (particularly as you add more stuff), and you're indenting the code more than you need to.

Furthermore, for future reference, attaching a .love would be more beneficial as it makes it easier to debug your code for others. This code you posted requires additional assets to work, and so I can't even run it without modification.
MarsOdin
Prole
Posts: 12
Joined: Tue Apr 02, 2019 11:46 pm

Re: error appears only if I repeat a couple of steps for several times very fast

Post by MarsOdin »

Thank you for your response, MrFariator!
As you can see I'm a beginner with coding and I'm learning empirically, so I appreciate the valuable feedback.
MrFariator wrote: Fri Mar 27, 2020 2:51 pm Well, on line 638 you try to compare value posX to mouse position, and this value is somehow nil. You could potentially check that the relevant variables exist in the inner loop like this:

Code: Select all

for j = 1, #menu[i].options do
  -- check that the relevant variables exist
  if menu[i].options[j] and menu[i].options[j].posX and menu[i].options[j].posY then
    -- store the mouseX and mouseY instead of repeatedly calling the function
    -- ideally outside the loop for good measure
    local mouseX, mouseY = love.mouse.getX(), love.mouse.getY()
    if menu[i].scrollable == false and
      mouseX >= menu[i].options[j].posX and
      mouseX <= menu[i].options[j].posX + menu[i].options[j].width and
      mouseY >= menu[i].options[j].posY and
      mouseY <= menu[i].options[j].posY + menu[i].options[j].height
      or
      menu[i].scrollable == true and
      mouseX >= menu[i].options[j].posX and
      mouseX <= menu[i].options[j].posX + menu[i].options[j].width and
      mouseY >= menu[i].options[j].posY + mouse.wheel.scrollSum and
      mouseY <= menu[i].options[j].posY + menu[i].options[j].height + mouse.wheel.scrollSum
    then
      menu[i].options[j].highlight = true
    else
      menu[i].options[j].highlight = false
    end
  end
end
However, this does not fix your underlying problem with posX and posY suddenly being nil. That issue probably lies elsewhere, and at a glance it may be menu[3].options, because from quickly skimming your code I don't see you assigning posX or posY inside handlerViewLocation, specifically the lines 472-476. As such, in certain situations it's entirely possible mouseHoverMenu will loop through menu[3].options, and then encounters this crash you're reporting.
I changed the way my drawMenu function works and now I can't recreate the problem. It seems to be fixed, yet I didn't touch the mouseHoverMenu function.

To your suggestion of storing mouseX and mouseY: When I call these variables, will it reflect the position of the mouse the moment it was stored or the moment it was called?
Additionally as a side note: you do not need to wrap your global functions inside love.load; use love.load to load and setup things, not to define the entire code base your game uses. Putting everything inside love.load makes organization a mess (particularly as you add more stuff), and you're indenting the code more than you need to.
I also don't like putting everything in love.load, but somewhere (tutorials or forums) I got the impression that I have to put everything that isn't drawn or updated into love.load. Is there maybe a guide or tutorial where I can learn to organize my code better?
Furthermore, for future reference, attaching a .love would be more beneficial as it makes it easier to debug your code for others. This code you posted requires additional assets to work, and so I can't even run it without modification.
Thanks for this! I wasn't aware how to make a .love out of my code. After this I searched it and now I know.
MrFariator
Party member
Posts: 548
Joined: Wed Oct 05, 2016 11:53 am

Re: error appears only if I repeat a couple of steps for several times very fast

Post by MrFariator »

MarsOdin wrote: Fri Mar 27, 2020 8:48 pm I changed the way my drawMenu function works and now I can't recreate the problem. It seems to be fixed, yet I didn't touch the mouseHoverMenu function.
Glad you got it working. The error you posted pointed towards mouseHoverMenu, but perhaps you did some other subtle changes that prevented the error from occurring.
MarsOdin wrote: Fri Mar 27, 2020 8:48 pm To your suggestion of storing mouseX and mouseY: When I call these variables, will it reflect the position of the mouse the moment it was stored or the moment it was called?
The stored values will reflect the moment when love.mouse.getX() and love.mouse.getY() were called. It's mostly to just save the little overhead that you get from calling the same function multiple times, when there will be no discernible difference in between the calls. Not the biggest issue in this case, but something to keep in mind for future. If you were calling any more expensive functions repeatedly in a row like that, it could start tanking performance.
MarsOdin wrote: Fri Mar 27, 2020 8:48 pm I also don't like putting everything in love.load, but somewhere (tutorials or forums) I got the impression that I have to put everything that isn't drawn or updated into love.load. Is there maybe a guide or tutorial where I can learn to organize my code better?
There isn't really any hard rules as to what you should and shouldn't put in love.load, but it's a bit of a code smell to have gigantic function bodies like the one your love.load is. Just have the love.load do all it must (eq. load images or other files, and do some configurations like setting window mode), and put the rest elsewhere.

Don't really know any good resources for better code organization, as it's a skill you slowly pick up along the way, but generally you'd want to try to split code into files or functions that do one thing, and not much else. Have a file for an unit class that only handles the functionality to said unit, collect related functions (eq. helper math functions) into a single file, and so and so forth.

Finally: try to avoid using globals. It becomes increasingly harder to debug and troubleshoot issues when all your variables are in the global scope (which, in lua's case, is a table you can access with the global variable _G). A typo or conflicting variable names can have a lot of consequences, making it that much harder to reason with your code. Love variables are global by default, unfortunately, which makes typos doubly devastating. My usual advice against globals is to use strict.lua in your lua projects, and force yourself to start using locals that way. Just copy-paste that code into its own file, and require it at the end of, say, love.load. Any accidental or undeclared globals from that point on will raise an error. You can then remove it at the end of the development without any issue.
MarsOdin
Prole
Posts: 12
Joined: Tue Apr 02, 2019 11:46 pm

Re: error appears only if I repeat a couple of steps for several times very fast

Post by MarsOdin »

Thanks for the help. I'll try out strict.lua
Post Reply

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 3 guests