Module DataPath
    Public Function Path() As String
        Path = Application.StartupPath.ToLower().Replace("\enigma\bin\debug\net8.0-windows", "\data\")
        Return path
    End Function
End Module


' Direction.vb
Public Enum Direction
    Up
    Down
    Left
    Right
End Enum


' frmGame.vb
Imports System.Threading
Imports System.Windows.Forms

Public Class frmGame
    Inherits Form

    Private plannedMoves As New List(Of PlannedMove)()
    Private moveLock As New Object()
    Private moveThread As Thread
    Private gameRunning As Boolean = True

    Private currentPlayer As Player
    Private gameMap As GameMap
    Private gameObjects As List(Of GameObject)

    ' Constructor accepting parameters
    Public Sub New(clonedGridRenderer As GridRenderer, levelName As String, clonedGameObjects As List(Of GameObject))
        ' This call is required by the designer.
        InitializeComponent()

        ' Assign the cloned grid renderer and game objects
        Me.gameMap = New GameMap(clonedGridRenderer)
        Me.gameObjects = clonedGameObjects

        ' Initialize the current player at a starting position (you can modify the position as needed)
        currentPlayer = New Player(New Point(1, 1)) ' Starting at position (1,1)

        ' Set the form's title to the level name
        Me.Text = "Game - " & levelName

        ' Start the move processing thread
        moveThread = New Thread(AddressOf ProcessMoves)
        moveThread.IsBackground = True
        moveThread.Start()

        ' Attach the KeyDown event handler to capture key presses
        AddHandler Me.KeyDown, AddressOf frmGame_KeyDown

        ' Ensure the form is focused to receive keyboard input
        Me.KeyPreview = True

        ' Optionally, initialize any other UI elements or controls here
    End Sub

    ' KeyDown event handler
    Private Sub frmGame_KeyDown(sender As Object, e As KeyEventArgs)
        Dim direction As Direction

        Select Case e.KeyCode
            Case Keys.W
                direction = Direction.Up
            Case Keys.A
                direction = Direction.Left
            Case Keys.S
                direction = Direction.Down
            Case Keys.D
                direction = Direction.Right
            Case Else
                Exit Sub ' Exit if another key is pressed
        End Select

        ' Queue the planned move
        Dim plannedMove As New PlannedMove(currentPlayer, direction)
        SyncLock moveLock
            plannedMoves.Add(plannedMove)
        End SyncLock
    End Sub

    ' ProcessMoves method
    Private Sub ProcessMoves()
        While gameRunning
            Dim movesToProcess As List(Of PlannedMove)

            ' Copy and clear the planned moves safely
            SyncLock moveLock
                movesToProcess = New List(Of PlannedMove)(plannedMoves)
                plannedMoves.Clear()
            End SyncLock

            ' Process each move
            For Each plannedMove In movesToProcess
                plannedMove.Player.MovePlayer(plannedMove.Direction, gameMap)
            Next

            ' Update the game UI after processing moves
            If Me.IsHandleCreated AndAlso Not Me.IsDisposed Then
                Me.Invoke(Sub()
                              ' Update any UI elements to reflect the new game state
                              ' For example, if you're using PictureBoxes or Panels to represent the game
                          End Sub)
            End If

            ' Sleep to control the processing rate
            Thread.Sleep(50) ' Adjust as needed
        End While
    End Sub

    ' FormClosing event handler
    Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
        ' Stop the game loop when the form is closing
        gameRunning = False
        If moveThread IsNot Nothing AndAlso moveThread.IsAlive Then
            moveThread.Join()
        End If

        MyBase.OnFormClosing(e)
    End Sub

End Class


' GameMap.vb
Public Class GameMap
    Public Property Width As Integer
    Public Property Height As Integer
    Public Tiles(,) As Tile

    ' Parameterless constructor
    Public Sub New()
        ' Set default dimensions
        Width = 15
        Height = 11
        ReDim Tiles(Width - 1, Height - 1)
        InitializeTiles()
    End Sub

    ' Constructor accepting a GridRenderer
    Public Sub New(gridRenderer As GridRenderer)
        ' Initialize dimensions based on the gridRenderer
        Width = gridRenderer.WallX.GetLength(0) - 1
        Height = gridRenderer.WallY.GetLength(1) - 1
        ReDim Tiles(Width - 1, Height - 1)
        InitializeTiles(gridRenderer)
    End Sub

    ' Method to initialize tiles
    Private Sub InitializeTiles(Optional gridRenderer As GridRenderer = Nothing)
        For x As Integer = 0 To Width - 1
            For y As Integer = 0 To Height - 1
                If gridRenderer IsNot Nothing Then
                    If gridRenderer.IsCellBlocked(x, y) Then
                        Tiles(x, y) = New Tile(TileType.Wall)
                    Else
                        Tiles(x, y) = New Tile(TileType.Floor)
                    End If
                Else
                    ' Default to floor if no gridRenderer
                    Tiles(x, y) = New Tile(TileType.Floor)
                End If
            Next
        Next
    End Sub
End Class


' GameObject.vb
Imports System.Drawing

Public Class GameObject
    Public Property Image As Image
    Public Property CellX As Integer
    Public Property CellY As Integer
    Public Property ImageTag As String

    Public Sub New(img As Image, x As Integer, y As Integer, imgTag As String)
        Me.Image = img
        Me.CellX = x
        Me.CellY = y
        Me.ImageTag = imgTag
    End Sub

    ' Method to clone the GameObject
    Public Function Clone() As GameObject
        Return New GameObject(Me.Image, Me.CellX, Me.CellY, Me.ImageTag)
    End Function
End Class


' GridRenderer.vb
Imports System.Drawing
Imports System.Drawing.Drawing2D

Public Class GridRenderer
    ' Fields for wall arrays
    Public WallX(,) As Boolean
    Public WallY(,) As Boolean

    ' Properties for hatch styles and colors
    Public Property wallsHatchStyle As HatchStyle = HatchStyle.Cross
    Public Property wallsColor1 As Color = Color.Gray
    Public Property wallsColor2 As Color = Color.DarkGray
    Public Property floorsHatchStyle As HatchStyle = HatchStyle.SmallCheckerBoard
    Public Property floorsColor1 As Color = Color.LightGray
    Public Property floorsColor2 As Color = Color.White

    ' Calculated square sizes
    Public SQSizeX As Integer
    Public SQSizeY As Integer

    ' Constructor
    Public Sub New()
        ' Initialize the wall arrays
        WallX = New Boolean(15, 11) {}
        WallY = New Boolean(15, 11) {}

        ' Set all walls to true (walls exist everywhere by default)
        For i As Integer = 0 To WallX.GetLength(0) - 1
            For j As Integer = 0 To WallX.GetLength(1) - 1
                WallX(i, j) = True
            Next
        Next

        For i As Integer = 0 To WallY.GetLength(0) - 1
            For j As Integer = 0 To WallY.GetLength(1) - 1
                WallY(i, j) = True
            Next
        Next
    End Sub

    ' Method to calculate square sizes
    Public Sub CalculateSquareSizes(clientWidth As Integer, clientHeight As Integer, SQCOUNTX As Integer, SQCOUNTY As Integer, WALL_THICKNESS As Integer)
        SQSizeX = (clientWidth - 2 * 20 - (SQCOUNTX + 1) * WALL_THICKNESS) \ SQCOUNTX
        SQSizeY = (clientHeight - 2 * 20 - (SQCOUNTY + 1) * WALL_THICKNESS) \ SQCOUNTY
    End Sub

    ' Method to draw the grid
    Public Sub DrawGrid(g As Graphics, clientWidth As Integer, clientHeight As Integer, INDENT As Integer, SQCOUNTX As Integer, SQCOUNTY As Integer, WALL_THICKNESS As Integer)
        ' Drawing logic for walls and floors
        Dim xOrigin As Integer = INDENT
        Dim yOrigin As Integer = INDENT

        ' Draw floors
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY - 1
                Dim floorRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                               yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                               SQSizeX,
                                               SQSizeY)

                Using floorBrush As New HatchBrush(floorsHatchStyle, floorsColor1, floorsColor2)
                    g.FillRectangle(floorBrush, floorRect)
                End Using
            Next
        Next

        ' Draw walls (vertical)
        For x As Integer = 0 To SQCOUNTX
            For y As Integer = 0 To SQCOUNTY - 1
                If WallX(x, y) Then
                    Dim wallRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                                  yOrigin + y * (SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                                  WALL_THICKNESS,
                                                  SQSizeY)

                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                End If
            Next
        Next

        ' Draw walls (horizontal)
        For x As Integer = 0 To SQCOUNTX - 1
            For y As Integer = 0 To SQCOUNTY
                If WallY(x, y) Then
                    Dim wallRect As New Rectangle(xOrigin + x * (SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                                  yOrigin + y * (SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2,
                                                  SQSizeX,
                                                  WALL_THICKNESS)

                    Using wallBrush As New HatchBrush(wallsHatchStyle, wallsColor1, wallsColor2)
                        g.FillRectangle(wallBrush, wallRect)
                    End Using
                End If
            Next
        Next
    End Sub

    ' Method to determine if a cell is blocked
    Public Function IsCellBlocked(x As Integer, y As Integer) As Boolean
        If WallX(x, y) OrElse WallX(x + 1, y) OrElse WallY(x, y) OrElse WallY(x, y + 1) Then
            Return True
        End If
        Return False
    End Function

    ' Method to clone the GridRenderer
    Public Function Clone() As GridRenderer
        Dim newRenderer As New GridRenderer()
        newRenderer.WallX = CType(Me.WallX.Clone(), Boolean(,))
        newRenderer.WallY = CType(Me.WallY.Clone(), Boolean(,))
        newRenderer.wallsHatchStyle = Me.wallsHatchStyle
        newRenderer.wallsColor1 = Me.wallsColor1
        newRenderer.wallsColor2 = Me.wallsColor2
        newRenderer.floorsHatchStyle = Me.floorsHatchStyle
        newRenderer.floorsColor1 = Me.floorsColor1
        newRenderer.floorsColor2 = Me.floorsColor2
        Return newRenderer
    End Function
End Class


' PlannedMove.vb
Public Class PlannedMove
    Public Property Player As Player
    Public Property Direction As Direction

    Public Sub New(player As Player, direction As Direction)
        Me.Player = player
        Me.Direction = direction
    End Sub
End Class


' Player.vb
Imports System.Drawing

Public Class Player
    Public Property Position As Point

    Public Sub New(startPosition As Point)
        Position = startPosition
    End Sub

    Public Sub MovePlayer(direction As Direction, gameMap As GameMap)
        Dim newPosition As Point = Position

        Select Case direction
            Case Direction.Up
                newPosition.Y -= 1
            Case Direction.Down
                newPosition.Y += 1
            Case Direction.Left
                newPosition.X -= 1
            Case Direction.Right
                newPosition.X += 1
        End Select

        If IsValidMove(newPosition, gameMap) Then
            Position = newPosition
        End If
    End Sub

    Private Function IsValidMove(newPosition As Point, gameMap As GameMap) As Boolean
        If newPosition.X < 0 OrElse newPosition.X >= gameMap.Width OrElse
           newPosition.Y < 0 OrElse newPosition.Y >= gameMap.Height Then
            Return False
        End If

        If gameMap.Tiles(newPosition.X, newPosition.Y).Type = TileType.Wall Then
            Return False
        End If

        Return True
    End Function
End Class


' RoomView.vb
Imports System.Drawing
Imports System.Drawing.Drawing2D
Imports System.Windows.Forms
Imports System.Collections.Generic
Imports System.Linq

Public Class RoomView
    Inherits UserControl

    ' Constants for grid dimensions and indentation
    Private Const INDENT As Integer = 20
    Private Const SQCOUNTX As Integer = 15
    Private Const SQCOUNTY As Integer = 11
    Private Const WALL_THICKNESS As Integer = 10

    ' Instance of GridRenderer
    Private gridRenderer As New GridRenderer()

    ' Properties to hold game data
    Public Property GameMap As GameMap
    Public Property Players As List(Of Player)
    Public Property GameObjects As List(Of GameObject)

    ' Mouse state variables
    Private isRightMouseDown As Boolean = False
    Private isLeftMouseDown As Boolean = False
    Private isMouseInsideGrid As Boolean = False

    ' Variables for highlighting
    Private highlightedX As Integer
    Private highlightedY As Integer
    Private highlightedWallX As Boolean = False
    Private highlightedWallY As Boolean = False
    Private highlightColor As Color = Color.Black
    Private highlightTimer As Timer

    ' Variable to track the cell being hovered over during drag-and-drop
    Private dragOverCell As Point? = Nothing

    ' List to hold animated images
    Private animatedImages As New List(Of Image)

    ' Constructor
    Public Sub New()
        ' This call is required by the designer.
        InitializeComponent()

        ' Set control styles for better rendering performance
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint Or ControlStyles.UserPaint Or ControlStyles.DoubleBuffer, True)
        Me.UpdateStyles()

        ' Initialize properties
        GameMap = New GameMap()
        Players = New List(Of Player)()
        GameObjects = New List(Of GameObject)()

        ' Initialize the highlight timer
        highlightTimer = New Timer()
        highlightTimer.Interval = 500 ' Flash every 500 milliseconds
        AddHandler highlightTimer.Tick, AddressOf HighlightTimer_Tick
        highlightTimer.Start()

        ' Use Application.Idle event to update frames
        AddHandler Application.Idle, AddressOf OnApplicationIdle
    End Sub
    ' Method to update the view
    Public Sub UpdateView()
        ' Redraw the control
        Me.Invalidate()
    End Sub

    ' Event handler for the highlight timer tick
    Private Sub HighlightTimer_Tick(sender As Object, e As EventArgs)
        ' Toggle the highlight color between black and white
        If highlightColor = Color.Black Then
            highlightColor = Color.White
        Else
            highlightColor = Color.Black
        End If
        Me.Invalidate()
    End Sub

    ' Event handler for Application.Idle event
    Private Sub OnApplicationIdle(sender As Object, e As EventArgs)
        For Each img In animatedImages
            ImageAnimator.UpdateFrames(img)
        Next
        Me.Invalidate()
    End Sub

    ' Override the OnPaint method to draw the game elements
    Protected Overrides Sub OnPaint(e As PaintEventArgs)
        MyBase.OnPaint(e)

        ' Calculate the square size based on the size of the control
        gridRenderer.CalculateSquareSizes(Me.ClientSize.Width, Me.ClientSize.Height, SQCOUNTX, SQCOUNTY, WALL_THICKNESS)

        ' Set SmoothingMode to None to prevent gaps due to anti-aliasing
        e.Graphics.SmoothingMode = SmoothingMode.None

        ' Draw the grid
        gridRenderer.DrawGrid(e.Graphics, Me.ClientSize.Width, Me.ClientSize.Height, INDENT, SQCOUNTX, SQCOUNTY, WALL_THICKNESS)

        ' Draw the game map
        If GameMap IsNot Nothing Then
            DrawMap(e.Graphics, GameMap)
        End If

        ' Draw game objects
        If GameObjects IsNot Nothing Then
            For Each obj In GameObjects
                DrawGameObject(e.Graphics, obj)
            Next
        End If

        ' Draw players
        If Players IsNot Nothing Then
            For Each player In Players
                DrawPlayer(e.Graphics, player)
            Next
        End If

        ' Draw highlights
        DrawHighlight(e.Graphics)

        ' Draw drag-over highlight
        If dragOverCell.HasValue Then
            DrawDragOverHighlight(e.Graphics, dragOverCell.Value)
        End If
    End Sub

    ' Method to draw the game map
    Private Sub DrawMap(g As Graphics, map As GameMap)
        If map.Tiles Is Nothing Then Exit Sub

        Dim tileSize As Integer = gridRenderer.SQSizeX
        For y As Integer = 0 To map.Height - 1
            For x As Integer = 0 To map.Width - 1
                Dim tile As Tile = map.Tiles(x, y)
                Dim destRect As New Rectangle(INDENT + x * (tileSize + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                              INDENT + y * (tileSize + WALL_THICKNESS) + WALL_THICKNESS \ 2,
                                              tileSize,
                                              tileSize)

                Select Case tile.Type
                    Case TileType.Floor
                        g.FillRectangle(Brushes.LightGray, destRect)
                    Case TileType.Wall
                        g.FillRectangle(Brushes.DarkGray, destRect)
                End Select

                ' Optional: Draw grid lines
                g.DrawRectangle(Pens.Black, destRect)
            Next
        Next
    End Sub

    ' Method to draw a game object on the grid
    Private Sub DrawGameObject(g As Graphics, obj As GameObject)
        ' Update the frame if the image is animated
        If ImageAnimator.CanAnimate(obj.Image) Then
            ImageAnimator.UpdateFrames(obj.Image)
        End If

        Dim x As Integer = INDENT + obj.CellX * (gridRenderer.SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim y As Integer = INDENT + obj.CellY * (gridRenderer.SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2

        Dim destRect As New Rectangle(x, y, gridRenderer.SQSizeX, gridRenderer.SQSizeY)
        g.DrawImage(obj.Image, destRect)
    End Sub

    ' Method to draw a player
    Private Sub DrawPlayer(g As Graphics, player As Player)
        Dim tileSize As Integer = gridRenderer.SQSizeX
        Dim x As Integer = INDENT + player.Position.X * (tileSize + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim y As Integer = INDENT + player.Position.Y * (tileSize + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim playerRect As New Rectangle(x, y, tileSize, tileSize)
        g.FillEllipse(Brushes.Blue, playerRect)
        g.DrawEllipse(Pens.White, playerRect)
    End Sub

    ' Method to draw the highlight
    Private Sub DrawHighlight(g As Graphics)
        If Not isMouseInsideGrid Then
            Return
        End If

        Using highlightPen As New Pen(highlightColor, 2)
            highlightPen.DashStyle = DashStyle.Dash

            If highlightedWallX Then
                ' Vertical wall
                Dim x As Integer = INDENT + highlightedX * (gridRenderer.SQSizeX + WALL_THICKNESS) - WALL_THICKNESS \ 2
                Dim y As Integer = INDENT + highlightedY * (gridRenderer.SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
                Dim highlightRect As New Rectangle(x, y, WALL_THICKNESS, gridRenderer.SQSizeY)
                g.DrawRectangle(highlightPen, highlightRect)
            ElseIf highlightedWallY Then
                ' Horizontal wall
                Dim x As Integer = INDENT + highlightedX * (gridRenderer.SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
                Dim y As Integer = INDENT + highlightedY * (gridRenderer.SQSizeY + WALL_THICKNESS) - WALL_THICKNESS \ 2
                Dim highlightRect As New Rectangle(x, y, gridRenderer.SQSizeX, WALL_THICKNESS)
                g.DrawRectangle(highlightPen, highlightRect)
            Else
                ' Square
                Dim x As Integer = INDENT + highlightedX * (gridRenderer.SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
                Dim y As Integer = INDENT + highlightedY * (gridRenderer.SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2
                Dim highlightRect As New Rectangle(x, y, gridRenderer.SQSizeX, gridRenderer.SQSizeY)
                g.DrawRectangle(highlightPen, highlightRect)
            End If
        End Using
    End Sub

    ' Method to draw drag-over highlight
    Private Sub DrawDragOverHighlight(g As Graphics, cell As Point)
        Dim x As Integer = INDENT + cell.X * (gridRenderer.SQSizeX + WALL_THICKNESS) + WALL_THICKNESS \ 2
        Dim y As Integer = INDENT + cell.Y * (gridRenderer.SQSizeY + WALL_THICKNESS) + WALL_THICKNESS \ 2

        Dim highlightRect As New Rectangle(x, y, gridRenderer.SQSizeX, gridRenderer.SQSizeY)

        Using highlightPen As New Pen(Color.Red, 2)
            highlightPen.DashStyle = DashStyle.Dash
            g.DrawRectangle(highlightPen, highlightRect)
        End Using
    End Sub

    ' Event handlers for mouse events
    Protected Overrides Sub OnMouseMove(e As MouseEventArgs)
        MyBase.OnMouseMove(e)

        ' Initialize as False at the start of the method
        isMouseInsideGrid = False

        ' Adjust for indentation
        Dim mouseX As Integer = e.X - INDENT
        Dim mouseY As Integer = e.Y - INDENT

        ' Early exit if the mouse is outside the grid area
        If mouseX < 0 OrElse mouseY < 0 Then
            Exit Sub
        End If

        Dim cellSizeX As Integer = gridRenderer.SQSizeX + WALL_THICKNESS
        Dim cellSizeY As Integer = gridRenderer.SQSizeY + WALL_THICKNESS

        Dim cellX As Integer = mouseX \ cellSizeX
        Dim cellY As Integer = mouseY \ cellSizeY

        If cellX >= SQCOUNTX OrElse cellY >= SQCOUNTY Then
            Exit Sub
        End If

        Dim offsetX As Integer = mouseX Mod cellSizeX
        Dim offsetY As Integer = mouseY Mod cellSizeY

        highlightedWallX = False
        highlightedWallY = False

        If offsetX < WALL_THICKNESS Then
            ' Vertical wall
            highlightedWallX = True
            highlightedX = cellX
            highlightedY = cellY
            isMouseInsideGrid = True

            ' Wall modification code
            If isRightMouseDown OrElse isLeftMouseDown Then
                If isRightMouseDown Then
                    If gridRenderer.WallX(cellX, cellY) Then
                        gridRenderer.WallX(cellX, cellY) = False
                        Me.Invalidate()
                    End If
                ElseIf isLeftMouseDown Then
                    If Not gridRenderer.WallX(cellX, cellY) Then
                        gridRenderer.WallX(cellX, cellY) = True
                        Me.Invalidate()
                    End If
                End If
            End If

        ElseIf offsetY < WALL_THICKNESS Then
            ' Horizontal wall
            highlightedWallY = True
            highlightedX = cellX
            highlightedY = cellY
            isMouseInsideGrid = True

            ' Wall modification code
            If isRightMouseDown OrElse isLeftMouseDown Then
                If isRightMouseDown Then
                    If gridRenderer.WallY(cellX, cellY) Then
                        gridRenderer.WallY(cellX, cellY) = False
                        Me.Invalidate()
                    End If
                ElseIf isLeftMouseDown Then
                    If Not gridRenderer.WallY(cellX, cellY) Then
                        gridRenderer.WallY(cellX, cellY) = True
                        Me.Invalidate()
                    End If
                End If
            End If

        ElseIf offsetX >= WALL_THICKNESS AndAlso offsetX < cellSizeX AndAlso offsetY >= WALL_THICKNESS AndAlso offsetY < cellSizeY Then
            ' Inside the square
            highlightedX = cellX
            highlightedY = cellY
            isMouseInsideGrid = True

            ' Handle right-click dragging over game objects to remove them
            If isRightMouseDown Then
                Dim objToRemove As GameObject = GameObjects.FirstOrDefault(Function(obj) obj.CellX = cellX AndAlso obj.CellY = cellY)
                If objToRemove IsNot Nothing Then
                    ' Remove the object from the GameObjects list
                    GameObjects.Remove(objToRemove)

                    ' Remove from animatedImages if necessary
                    If animatedImages.Contains(objToRemove.Image) Then
                        animatedImages.Remove(objToRemove.Image)
                        ImageAnimator.StopAnimate(objToRemove.Image, AddressOf OnFrameChanged)
                    End If

                    Me.Invalidate()
                End If
            End If

        Else
            Exit Sub
        End If

        ' Redraw to update the highlight
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnMouseDown(e As MouseEventArgs)
        MyBase.OnMouseDown(e)

        If e.Button = MouseButtons.Right Then
            isRightMouseDown = True
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = True
        End If
    End Sub

    Protected Overrides Sub OnMouseUp(e As MouseEventArgs)
        MyBase.OnMouseUp(e)

        If e.Button = MouseButtons.Right Then
            isRightMouseDown = False
        ElseIf e.Button = MouseButtons.Left Then
            isLeftMouseDown = False
        End If
    End Sub

    ' Drag-and-drop event handlers
    Protected Overrides Sub OnDragEnter(e As DragEventArgs)
        MyBase.OnDragEnter(e)
        If e.Data.GetDataPresent(GetType(Bitmap)) OrElse e.Data.GetDataPresent(GetType(Image)) Then
            e.Effect = DragDropEffects.Copy
        Else
            e.Effect = DragDropEffects.None
        End If
    End Sub

    Protected Overrides Sub OnDragOver(e As DragEventArgs)
        MyBase.OnDragOver(e)
        Dim clientPoint As Point = Me.PointToClient(New Point(e.X, e.Y))
        Dim cell = GetCellFromPoint(clientPoint)
        If cell.HasValue AndAlso IsFloorTile(cell.Value.X, cell.Value.Y) Then
            dragOverCell = cell
            e.Effect = DragDropEffects.Copy
        Else
            dragOverCell = Nothing
            e.Effect = DragDropEffects.None
        End If
        Me.Invalidate()
    End Sub

    Protected Overrides Sub OnDragDrop(e As DragEventArgs)
        MyBase.OnDragDrop(e)
        Dim clientPoint As Point = Me.PointToClient(New Point(e.X, e.Y))
        Dim cell = GetCellFromPoint(clientPoint)
        If cell.HasValue AndAlso IsFloorTile(cell.Value.X, cell.Value.Y) Then
            If Not IsCellOccupied(cell.Value.X, cell.Value.Y) Then
                Dim img As Image = CType(e.Data.GetData(GetType(Bitmap)), Image)
                AddGameObject(img, cell.Value.X, cell.Value.Y)
                Me.Invalidate()
            Else
                MessageBox.Show("This tile is already occupied.")
            End If
        Else
            MessageBox.Show("You can only place objects on floor tiles.")
        End If
        dragOverCell = Nothing
    End Sub

    Protected Overrides Sub OnDragLeave(e As EventArgs)
        MyBase.OnDragLeave(e)
        dragOverCell = Nothing
        Me.Invalidate()
    End Sub

    ' Helper methods
    Private Function GetCellFromPoint(point As Point) As Point?
        Dim mouseX As Integer = point.X - INDENT
        Dim mouseY As Integer = point.Y - INDENT

        If mouseX < 0 OrElse mouseY < 0 Then Return Nothing

        Dim cellSizeX As Integer = gridRenderer.SQSizeX + WALL_THICKNESS
        Dim cellSizeY As Integer = gridRenderer.SQSizeY + WALL_THICKNESS

        Dim cellX As Integer = mouseX \ cellSizeX
        Dim cellY As Integer = mouseY \ cellSizeY

        If cellX >= SQCOUNTX OrElse cellY >= SQCOUNTY Then Return Nothing

        Dim offsetX As Integer = mouseX Mod cellSizeX
        Dim offsetY As Integer = mouseY Mod cellSizeY

        If offsetX >= WALL_THICKNESS AndAlso offsetY >= WALL_THICKNESS Then
            Return New Point(cellX, cellY)
        End If

        Return Nothing
    End Function

    Private Function IsFloorTile(cellX As Integer, cellY As Integer) As Boolean
        ' Check if the cell is a floor tile in the GameMap
        If GameMap IsNot Nothing AndAlso GameMap.Tiles IsNot Nothing Then
            If GameMap.Tiles(cellX, cellY).Type = TileType.Floor Then
                Return True
            End If
        End If
        Return False
    End Function

    Private Function IsCellOccupied(cellX As Integer, cellY As Integer) As Boolean
        For Each obj As GameObject In GameObjects
            If obj.CellX = cellX AndAlso obj.CellY = cellY Then
                Return True
            End If
        Next
        Return False
    End Function

    Private Sub AddGameObject(img As Image, cellX As Integer, cellY As Integer)
        Dim imageTag As String = If(img.Tag IsNot Nothing, img.Tag.ToString(), "Unknown")
        Dim obj As New GameObject(img, cellX, cellY, imageTag)
        GameObjects.Add(obj)

        ' If the image is animated, and not already being animated
        If ImageAnimator.CanAnimate(img) AndAlso Not animatedImages.Contains(img) Then
            animatedImages.Add(img)
            ImageAnimator.Animate(img, AddressOf OnFrameChanged)
        End If
    End Sub

    ' Event handler for animation frame changes
    Private Sub OnFrameChanged(sender As Object, e As EventArgs)
        Me.Invalidate()
    End Sub

    ' Override Dispose to clean up any resources
    'Protected Overrides Sub Dispose(disposing As Boolean)
    '    Try
    '        If disposing Then
    '            If highlightTimer IsNot Nothing Then
    '                highlightTimer.Stop()
    '                RemoveHandler highlightTimer.Tick, AddressOf HighlightTimer_Tick
    '                highlightTimer.Dispose()
    '                highlightTimer = Nothing
    '            End If

    '            ' Remove Application.Idle event handler
    '            RemoveHandler Application.Idle, AddressOf OnApplicationIdle
    '        End If
    '    Finally
    '        MyBase.Dispose(disposing)
    '    End Try
    'End Sub

End Class


' Tile.vb
Public Class Tile
    Public Property Type As TileType

    Public Sub New(type As TileType)
        Me.Type = type
    End Sub
End Class

Public Enum TileType
    Floor
    Wall
End Enum


Please help. Can you make RoomView a form again please! PLEEEEEAAASE I'm losing my mind!