Return select grid selection back to calling function

New to Playdate and LUA development.
I'm currently developing a puzzle game, with the help of this forum and some YouTube vids, got most things to work. Using a grid view I got a level select screen to work, but I can't feature out how to pass back the selected level value to the calling method/function on A-Button click. Any help is appreciated. Here is my code:

class("GameScene").extends()

function GameScene:init()
    self:goToLevel("Level_24")
           
    self.spawnX = 4 * 28
    self.spawnY = 4 * 28
    puzzleStoneCount = 8

    self:drawLevelNumber("24")
    
 	local systemMenu = pd.getSystemMenu()
	systemMenu:removeAllMenuItems()
        systemMenu:addMenuItem("Level select", function()
        self:drawLevelScreen()
        self:goToLevel(self.levelname)
	end) 
       systemMenu:addMenuItem("Restart level", function()
        self:resetLevel()
	end)
        systemMenu:addMenuItem("Reset player", function()
        self:resetPlayer()
	end)
    
    self.player = Player(self.spawnX, self.spawnY, self)
end
function GameScene:drawLevelScreen()
	gfx.sprite.removeAll()
    
    --grid view --
    oxydianFontInstance:setTracking(1)

    local gridview = playdate.ui.gridview.new(28, 28)

    gridview.backgroundImage = playdate.graphics.nineSlice.new('images/LevelSelect_Background', 4, 4, 28, 28)
    gridview:setNumberOfColumns(10)
    gridview:setNumberOfRows(5) -- number of sections is set automatically
    gridview:setSectionHeaderHeight(0)
    gridview:setContentInset(58, 4, 58, 4)
    gridview:setCellPadding(0, 0, 0, 0)
    gridview.changeRowOnColumnWrap = false


    function gridview:drawCell(section, row, column, selected, x, y, width, height)

        if selected then
            gfx.setLineWidth(0)
            gfx.fillRect(x, y, 23, 23)
            gfx.setImageDrawMode(gfx.kDrawModeFillWhite)
        else
            gfx.setLineWidth(0)
            gfx.drawRect(x, y, 24, 24)
            gfx.setImageDrawMode(gfx.kDrawModeCopy)
        end
        if row == 1 then
            cellText = ""..row * column
        elseif row == 2 then
            cellText = ""..((row - 1) * column) + 10
        elseif row == 3 then
            cellText = ""..((row - 2) * column) + 20
        elseif row == 4 then
            cellText = ""..((row - 3) * column) + 30
        elseif row == 5 then
            cellText = ""..((row - 4) * column) + 40
        end
        gfx.setFont(oxydianFontInstance)
        gfx.drawTextInRect(cellText, x - 2, y + 6, width, height, nil, nil, kTextAlignment.center)

    end

    -- buttons --
    function pd.AButtonUp()
        local section, row, column = gridview:getSelection()
        if row == 1 then
            levelNumber = row * column
        elseif row == 2 then
            levelNumber = ((row - 1) * column) + 10
        elseif row == 3 then
            levelNumber = ((row - 2) * column) + 20
        elseif row == 4 then
            levelNumber = ((row - 3) * column) + 30
        elseif row == 5 then
            levelNumber = ((row - 4) * column) + 40
        end

        self.levelname = "Level_" .. levelNumber

    end
        -- main update function --
    function pd.update() 
        if gridview.needsDisplay == true then
            gridview:drawInRect(0, 0, 400, 240)
        end        
    end
    
end

Hello, I’m not sure if I understand what you want to achieve, but looking at your code, you could try to invoke self:goToLevel(…) directly in the button press callback and remove the self:goToLevel(self.levelname) in the systemMenu:addMenuItem callback. Something like this:

systemMenu:addMenuItem("Level select", function()
    self:drawLevelScreen()
end)

...

function pd.AButtonUp()
    ...
    local levelname <const> = "Level_" .. levelNumber
    self:goToLevel(levelname)
end

Hello Jakub,

thank you very much for your replay.
Thanks for your solution. I had something similar in mind, but it doesn't work.

What I try to achieve is on my game's main screen and/or on my game's gameplay screen, pressing the home button, I want to give the player a level select option. I decided to use a grid view, because I like the movement with the D-pad and the 'selected' parameter. I've seen other developers using the grid view as a sprite, which I didn't get to work either.

Oxydian 2024-07-30 10.10.20

From the game screen I press home-button and select 'select level' option and the level select screen is loaded.

Oxydian 2024-07-30 11.15.11

After selecting a level pressing the A-Button nothing seems to happen and the program is staying on level select screen.
Debugging the code, I can see that the level number is correctly selected, but it looks like the code is stuck in the A-button up function.
Could be that I do something fundamentally wrong in coding.

local pd <const> = playdate
local gfx <const> = playdate.graphics
local ldtk <const> = LDtk

oxydianFontInstance = gfx.font.new("font/font-full-circle")
gfx.setFont(oxydianFontInstance)

TAGS = {
    Solid = 1,
    WallBounce = 2,
    circleStone = 3,
    triangleStone = 4,
    BounceStone = 5,
    Player = 6,
    Interactable = 7,
	Pickup = 8,
    MeshStone = 9,
    Hazard = 10,
    ArrowLeft = 11,
    ArrowRight = 12,
    ArrowUp = 13,
    ArrowDown = 14,
    WormHole = 15
    }

Z_INDEXES = {
    Player = 100,
    WallBounce = 120,
    PuzzleStone = 110,
    MeshStone = 121,
    Pickup = 90,
    Gate = 0
}

Hit_DIRECTIONS = {
    Up = 8,
    Down = 8,
    Left = 8,
    Right = 8
}

Bounce_DIRECTIONS = {
    Up = 10,
    Down = 10,
    Left = 10,
    Right = 10
}

COLLISION_GROUPS = {
    player = 1
}

ldtk.load("levels/oxydian.ldtk", false)

class("GameScene").extends()

function GameScene:init()
    
    self:goToLevel("Level_1")
    self.spawnX = 7 * 28
    self.spawnY = 4 * 28    
    puzzleStoneCount = 4
    self:drawLevelNumber("1")
    
 	local systemMenu = pd.getSystemMenu()
	systemMenu:removeAllMenuItems()
    systemMenu:addMenuItem("Level select", function()
        self:drawLevelScreen()
	end) 
    systemMenu:addMenuItem("Restart level", function()
        self:resetLevel()
	end)
    systemMenu:addMenuItem("Reset player", function()
        self:resetPlayer()
	end)
    
    self.player = Player(self.spawnX, self.spawnY, self)
end

function GameScene:resetPlayer()
    self.player:moveTo(self.spawnX, self.spawnY)
end

function GameScene:resetLevel()

    local resetLevelNumber = string.match(self.levelname, "_(.*)")

    if self.levelname == "Level_1" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28    
    elseif self.levelname == "Level_2" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
    elseif self.levelname == "Level_3" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_4" then
        self.spawnX = 7 * 28
        self.spawnY = 5 * 28
    elseif self.levelname == "Level_5" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_6" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
    elseif self.levelname == "Level_7" then
        self.spawnX = 3 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_8" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_9" then
        self.spawnX = 8 * 28
        self.spawnY = 6 * 28
    elseif self.levelname == "Level_10" then
        self.spawnX = 10 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_11" then
        self.spawnX = 8.5 * 28
        self.spawnY = 4.5 * 28
    elseif self.levelname == "Level_12" then
        self.spawnX = 8.5 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_13" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28    
    elseif self.levelname == "Level_14" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_15" then
        self.spawnX = 2 * 28
        self.spawnY = 3.5 * 28
    elseif self.levelname == "Level_16" then
        self.spawnX = 7 * 28
        self.spawnY = 3.5 * 28                
    elseif self.levelname == "Level_17" then
        self.spawnX = 1.5 * 28
        self.spawnY = 2.5 * 28
    elseif self.levelname == "Level_18" then
        self.spawnX = 9 * 28
        self.spawnY = 7 * 28
    elseif self.levelname == "Level_19" then
        self.spawnX = 7 * 28
        self.spawnY = 3.5 * 28
    elseif self.levelname == "Level_20" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
    elseif self.levelname == "Level_21" then
        self.spawnX = 10.5 * 28
        self.spawnY = 3.5 * 28              
    elseif self.levelname == "Level_22" then
        self.spawnX = 2 * 28
        self.spawnY = 2.5 * 28
    elseif self.levelname == "Level_23" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_24" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28
    elseif self.levelname == "Level_25" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28              
    end    
    self:goToLevel(self.levelname)
    self:drawLevelNumber(resetLevelNumber)
    self.player = Player(self.spawnX, self.spawnY, self)
end

function GameScene:nextLevel()

    local nextLevelNumber = string.match(self.levelname, "_(.*)")
    nextLevelNumber = nextLevelNumber + 1
    local nextLevelName = "Level_" .. nextLevelNumber

    if nextLevelName == "Level_2" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
        puzzleStoneCount = 6
    elseif nextLevelName == "Level_3" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 12
    elseif nextLevelName == "Level_4" then
        self.spawnX = 7 * 28
        self.spawnY = 5 * 28
        puzzleStoneCount = 8            
    elseif nextLevelName == "Level_5" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 12
    elseif nextLevelName == "Level_6" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
        puzzleStoneCount = 10
    elseif nextLevelName == "Level_7" then
        self.spawnX = 3 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 4
    elseif nextLevelName == "Level_8" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 8        
    elseif nextLevelName == "Level_9" then
        self.spawnX = 8 * 28
        self.spawnY = 6 * 28
        puzzleStoneCount = 6
    elseif nextLevelName == "Level_10" then
        self.spawnX = 10 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_11" then
        self.spawnX = 8.5 * 28
        self.spawnY = 4.5 * 28
        puzzleStoneCount = 4
    elseif nextLevelName == "Level_12" then
        self.spawnX = 8.5 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_13" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28    
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_14" then
        self.spawnX = 7 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_15" then
        self.spawnX = 2 * 28
        self.spawnY = 3.5 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_16" then
        self.spawnX = 7 * 28
        self.spawnY = 3.5 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_17" then
        self.spawnX = 1.5 * 28
        self.spawnY = 2.5 * 28
        puzzleStoneCount = 6
    elseif nextLevelName == "Level_18" then
        self.spawnX = 9 * 28
        self.spawnY = 7 * 28
        puzzleStoneCount = 6
    elseif nextLevelName == "Level_19" then
        self.spawnX = 7 * 28
        self.spawnY = 3.5 * 28
        puzzleStoneCount = 10
    elseif nextLevelName == "Level_20" then
        self.spawnX = 7 * 28
        self.spawnY = 6 * 28
        puzzleStoneCount = 16
    elseif nextLevelName == "Level_21" then
        self.spawnX = 10.5 * 28
        self.spawnY = 3.5 * 28
        puzzleStoneCount = 4
    elseif nextLevelName == "Level_22" then
        self.spawnX = 2 * 28
        self.spawnY = 2.5 * 28
        puzzleStoneCount = 6
    elseif nextLevelName == "Level_23" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 12            
    elseif nextLevelName == "Level_24" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 8
    elseif nextLevelName == "Level_25" then
        self.spawnX = 4 * 28
        self.spawnY = 4 * 28
        puzzleStoneCount = 6       
    end    
    self:goToLevel(nextLevelName)
    self:drawLevelNumber(nextLevelNumber)
    self.player = Player(self.spawnX, self.spawnY, self)
end

function GameScene:goToLevel(levelName)
    if not levelName then return end

    self.levelname = levelName
	gfx.sprite.removeAll()
    PuzzleStoneSet = {}
    
    for layerName, layer in pairs(ldtk.get_layers(levelName)) do
        if layer.tiles then
            local tilemap = ldtk.create_tilemap(levelName, layerName)

            local layerSprite = gfx.sprite.new()
                
            layerSprite:setTilemap(tilemap)
            layerSprite:setCenter(0, 0)
            layerSprite:moveTo(0, 0)
            layerSprite:setZIndex(layer.zIndex)
            layerSprite:add()

			for enum, tag in pairs(TAGS) do
				local emptyTiles = ldtk.get_empty_tileIDs(levelName, enum, layerName)
				if emptyTiles then
					local tileSprites = gfx.sprite.addWallSprites(tilemap, emptyTiles)
					for i=1,#tileSprites do
						local tileSprite = tileSprites[i]
						tileSprite:setTag(tag)
					end
				end
			end
        end
    end

    for _, entity in ipairs(ldtk.get_entities(levelName)) do
        local entityX, entityY = entity.position.x, entity.position.y
        local entityName = entity.name
        if entityName == "PuzzleStone" then
            PuzzleStone(entityX, entityY, entity)
        elseif entityName == "BlackPuzzleStone" then
            BlackPuzzleStone(entityX, entityY, entity)     
        elseif entityName == "MeshStone" then
            MeshStone(entityX, entityY)
        elseif entityName == "MeshStone2" then
            MeshStone2(entityX, entityY)
        elseif entityName == "MeshStone3" then
            MeshStone3(entityX, entityY)                        
        elseif entityName == "MeshStone4" then
            MeshStone4(entityX, entityY)
        elseif entityName == "Memo" then
            Memo(entityX, entityY, entity)
        elseif entityName == "Skull" then
            Skull(entityX, entityY, entity)
        elseif entityName == "ArrowLeft" then
            ArrowLeft(entityX, entityY)
        elseif entityName == "ArrowRight" then
            ArrowRight(entityX, entityY)
        elseif entityName == "BouncerStone" then
            BouncerStone(entityX, entityY)            
        elseif entityName == "Umbrella" then
            Umbrella(entityX, entityY, entity)
        elseif entityName == "SwitchStone" then
            SwitchStone(entityX, entityY, entity)
        elseif entityName == "MeshTimerStone" then
            MeshTimerStone(entityX, entityY, entity)
        elseif entityName == "WormHole" then
            WormHole(entityX, entityY, entity)
        elseif entityName == "ArrowUp" then
            ArrowUp(entityX, entityY)
        elseif entityName == "ArrowDown" then
            ArrowDown(entityX, entityY)
        elseif entityName == "SpaceTile" then
            SpaceTile(entityX, entityY)                                                      
        end
    end
end

function GameScene:drawLevelNumber(levelNumber)
    LevelSprite = gfx.sprite.new()
    local textWidth, textHeight = gfx.getTextSize(levelNumber)
    local textImage = gfx.image.new(25, textHeight)
    local x = 2

    gfx.pushContext(textImage)
    gfx.drawText("L"..levelNumber, 0, 0)
    gfx.popContext()
    LevelSprite:setImage(textImage)
    LevelSprite:setCenter(0, 0)
    LevelSprite:moveTo(x, 226)
    LevelSprite:add()    
end

function GameScene:drawLevelScreen()
    
    --grid view --
    oxydianFontInstance:setTracking(1)

    local gridview = playdate.ui.gridview.new(28, 28)

    gridview.backgroundImage = playdate.graphics.nineSlice.new('images/LevelSelect_Background', 4, 4, 28, 28)
    gridview:setNumberOfColumns(10)
    gridview:setNumberOfRows(5) -- number of sections is set automatically
    gridview:setSectionHeaderHeight(0)
    gridview:setContentInset(58, 4, 58, 4)
    gridview:setCellPadding(0, 0, 0, 0)
    gridview.changeRowOnColumnWrap = false


    function gridview:drawCell(section, row, column, selected, x, y, width, height)

        if selected then
            gfx.setLineWidth(0)
            gfx.fillRect(x, y, 23, 23)
            gfx.setImageDrawMode(gfx.kDrawModeFillWhite)
        else
            gfx.setLineWidth(0)
            gfx.drawRect(x, y, 24, 24)
            gfx.setImageDrawMode(gfx.kDrawModeCopy)
        end
        if row == 1 then
            cellText = ""..row * column
        elseif row == 2 then
            cellText = ""..((row - 1) * column) + 10
        elseif row == 3 then
            cellText = ""..((row - 2) * column) + 20
        elseif row == 4 then
            cellText = ""..((row - 3) * column) + 30
        elseif row == 5 then
            cellText = ""..((row - 4) * column) + 40
        end
        gfx.setFont(oxydianFontInstance)
        gfx.drawTextInRect(cellText, x - 2, y + 6, width, height, nil, nil, kTextAlignment.center)

    end

    -- buttons --
    function pd.AButtonUp()
        local section, row, column = gridview:getSelection()
        if row == 1 then
            levelNumber = row * column
        elseif row == 2 then
            levelNumber = ((row - 1) * column) + 10
        elseif row == 3 then
            levelNumber = ((row - 2) * column) + 20
        elseif row == 4 then
            levelNumber = ((row - 3) * column) + 30
        elseif row == 5 then
            levelNumber = ((row - 4) * column) + 40
        end
        local levelname <const> = "Level_" .. levelNumber
        self:goToLevel(levelname)
    end

    function pd.upButtonUp()
        gridview:selectPreviousRow(true)
    end

    function pd.downButtonUp()
        gridview:selectNextRow(true)	
    end

    function pd.leftButtonUp()
        gridview:selectPreviousColumn(true)
    end

    function pd.rightButtonUp()
        gridview:selectNextColumn(true)
    end

    function pd.update() 
        if gridview.needsDisplay == true then
            gridview:drawInRect(0, 0, 400, 240)
        end
        
    end
    
end

Hello, OK I understand now. I would probably need to have access to the repo and run the project locally so I can help you debug it. But on a first look, there are some improvements that can be done, but they are unrelated to the problem you have.

  1. You can easily get rid of the elseif statements by creating and indexing maps, so instead of doing this:
if self.levelname == "Level_1" then
    self.spawnX = 7 * 28
    self.spawnY = 4 * 28
elseif self.levelname == "Level_2" then
    self.spawnX = 7 * 28
    self.spawnY = 6 * 28
elseif self.levelname == "Level_3" then
    self.spawnX = 7 * 28
    self.spawnY = 4 * 28
elseif self.levelname == "Level_4" then
    self.spawnX = 7 * 28
    self.spawnY = 5 * 28
end

You can do this:

local multiplier <const> = 28

local levelSpawnPositions <const> = {
    ["Level_1"] = { x = 7 * multiplier, y = 4 * multiplier }, -- or as an array-style { 7 * multiplier, 4 * multiplier }
    ["Level_2"] = { x = 7 * multiplier, y = 6 * multiplier },
    ["Level_3"] = { x = 7 * multiplier, y = 4 * multiplier },
    ["Level_4"] = { x = 7 * multiplier, y = 5 * multiplier },
}

local levelname <const> = "Level_4" -- Replaced with self.levelname in your code
local spawnPosition <const> = levelSpawnPositions[levelname]

self.spawnX = spawnPosition.x
self.spawnY = spawnPosition.y

This can be done for every instance of elseif related to the level spawning.

  1. In the block where you creating entities based on entityName, you could index the global table directly, since it seems that entityName and the names of the Sprites are identical. So instead of doing this:
local entityName = entity.name
if entityName == "PuzzleStone" then
    PuzzleStone(entityX, entityY, entity)
elseif entityName == "BlackPuzzleStone" then
    BlackPuzzleStone(entityX, entityY, entity)
elseif entityName == "MeshStone" then
    MeshStone(entityX, entityY)
end

You can do this:

local entityName <const> = entity.name
_G[entityName](entityX, entityY, entity)
  1. Frequent string concatenation with .. can be quite expensive, it's recommended to use table.concat instead, so instead of this:
if row == 1 then
    cellText = "" .. row * column
elseif row == 2 then
    cellText = "" .. ((row - 1) * column) + 10
elseif row == 3 then
    cellText = "" .. ((row - 2) * column) + 20
end

You can do this:

local table_concat <const> = table.concat
if row == 1 then
    cellText = table_concat({ "", row * column })
elseif row == 2 then
    cellText = table_concat({ "", ((row - 1) * column) + 10 })
elseif row == 3 then
    cellText = table_concat({ "", ((row - 2) * column) + 20 })
end

There are also few recommendations that can improve overall performance, such as creating locals for frequently used functions etc.

Maybe try:

function pd.AButtonUp()
        local section, row, column = gridview:getSelection()
        local levelname = "Level_" .. (row-1) * 10 + column
        print(‘Going to level ’ .. levelname)
        self:goToLevel(levelname)
    end

You can replace other long ifs in a similar way.
Check that the print is firing as expected and that the levelname is what you need. If it is, then throw some print statements into :goToLevel and see where it’s getting to.

Having function pd.update() inside gamescene:drawLevelScreen() seems odd?

Hi Jakub,

sorry for the late answer! Find some time to work on the project again.
First I removed the "Select Level" menu entry from the game's play screen because it made no sense there either. Replace it with a save game entry.

playdate-20240826-113408

Next I added the "Select Level" entry to the games Start/Menu screen, where it is actually has more sense, UI wise.

Oxydian 2024-08-26 11.12.27

In the game's menu screen, I'm using now something called as a "SceneManager", which lets me switch between different screens.

function Menu:update()
    if self.active then
        if pd.buttonJustPressed(pd.kButtonLeft) then
            self.gridview:selectPreviousColumn(true)
        elseif pd.buttonJustPressed(pd.kButtonRight) then
            self.gridview:selectNextColumn(true)
        end

        if pd.buttonJustPressed(pd.kButtonA) then
            local _, _, selectedColumn = self.gridview:getSelection()
            if #self.elements == 3 then
                if selectedColumn == 1 then
                    SCENE_MANAGER:switchScene(GameScene, CUR_LEVEL, CUR_X, CUR_Y, STONES, LEVELS)
                elseif selectedColumn == 2 then
                    SCENE_MANAGER:switchScene(GameScene, nil)
                else
                    SCENE_MANAGER:switchScene(LevelSelectView)
                end
            else
                if selectedColumn == 1 then
                    SCENE_MANAGER:switchScene(GameScene, nil)
                else    
                    SCENE_MANAGER:switchScene(LevelSelectView)
                end    
            end
        end        

        -- if pd.buttonJustPressed(pd.kButtonA) then
        --     GameScene()
        -- end
    end

Using the SceneManger code module, I can now simply passing the select level from the "Select Level screen" back to the Game Scene as a parameter.

Oxydian 2024-08-26 12.21.07

Oxydian 2024-08-26 12.22.33

Everything now working fine and the game is mostly in a playable state. Also thanks for the advice for optimizing the code. Before adding new level(s) and other features, I will do the optimizing first. But I have to say that I not really completely understand some of code yet.
I might create a repo for you, maybe you just have look at it, if you have some time. Anyway thanks for the great help....

1 Like

@ TheMediocritist
Sorry for the late answer, but thanks for the coding advise!
You are right, pdupdate() along other coding mistake were odd!

1 Like

This looks great, keep at it!

I'm a big fan of Oxyd from my Atari ST days.