This commit is contained in:
2025-01-22 00:16:29 +01:00
commit af2f573d60
38 changed files with 5840 additions and 0 deletions

133
lua/Flappy.lua Normal file
View File

@@ -0,0 +1,133 @@
-- Flappy Bird Clone using GameEngine
local vector2 = require("vector2")
local utils = require("utils")
-- Game Variables
local bird = {
position = vector2.new(100, 200),
width = 30,
height = 30,
velocity = 0,
jumpStrength = -8
}
local gravity = 0.5
local pipes = {}
local pipeWidth = 60
local pipeGap = 120
local pipeSpeed = 2
local score = 0
local gameRunning = true
local gameStarted = false
local screenWidth = GameEngine:getWidth()
local screenHeight = GameEngine:getHeight()
-- Function to spawn new pipes
local function spawnPipe()
--GameEngine:messageBox(tostring(screenHeight - pipeGap - 50))
local pipeHeight = math.random(50, screenHeight - pipeGap - 50)
--local pipeHeight =50
table.insert(pipes, { x = screenWidth, y = pipeHeight })
end
function setup_window()
GameEngine:setTitle("BreakOut")
GameEngine:setWidth(800)
GameEngine:setHeight(600)
GameEngine:setFrameRate(60)
end
--- the set_keylist function
--- @return string
function set_keylist()
return "R "
end
--- the start function
--- @return nil
function start()
screenWidth = GameEngine:getWidth()
screenHeight = GameEngine:getHeight()
-- print(GameEngine:getRequest("https://dummyjson.com/c/3029-d29f-4014-9fb4"))
end
-- Update game state
function update()
if not gameRunning then return end
-- Apply gravity
bird.velocity = bird.velocity + gravity
bird.position.y = bird.position.y + bird.velocity
-- Jumping mechanic
if GameEngine:isKeyDown(" ") then
bird.velocity = bird.jumpStrength
end
-- Move pipes
for i = #pipes, 1, -1 do
pipes[i].x = pipes[i].x - pipeSpeed
-- Remove off-screen pipes
if pipes[i].x + pipeWidth < 0 then
table.remove(pipes, i)
score = score + 1
end
end
-- Collision detection
for _, pipe in ipairs(pipes) do
if bird.position.x < pipe.x + pipeWidth and bird.position.x + bird.width > pipe.x then
if bird.position.y < pipe.y or bird.position.y + bird.height > pipe.y + pipeGap then
gameRunning = false
end
end
end
-- Check if bird hits ground or ceiling
if bird.position.y + bird.height >= screenHeight or bird.position.y <= 0 then
gameRunning = false
end
-- Spawn pipes periodically
if #pipes == 0 or pipes[#pipes].x < screenWidth - 200 then
spawnPipe()
end
end
-- Draw game elements
function draw()
-- Clear screen
GameEngine:fillScreen(Color.new(135, 206, 250)) -- Sky Blue background
-- Draw bird
GameEngine:setColor(Color.new(255, 255, 0)) -- Yellow
GameEngine:fillOval(bird.position.x, bird.position.y, bird.width, bird.height)
-- Draw pipes
GameEngine:setColor(Color.new(0, 255, 0)) -- Green
for _, pipe in ipairs(pipes) do
GameEngine:fillRect(pipe.x, 0, pipeWidth, pipe.y)
GameEngine:fillRect(pipe.x, pipe.y + pipeGap, pipeWidth, screenHeight - pipe.y - pipeGap)
end
-- Draw score
GameEngine:setColor(Color.new(255, 255, 255)) -- White
GameEngine:drawText(tostring(score), 10, 10)
-- Game over message
if not gameRunning then
GameEngine:drawText("Game Over! Press R to restart", screenWidth / 2 - 80, screenHeight / 2)
end
end
-- Restart function
function restart()
bird.position.y = 200
bird.velocity = 0
pipes = {}
score = 0
gameRunning = true
end

182
lua/Pong.lua Normal file
View File

@@ -0,0 +1,182 @@
-- annotation.lua contains EmmyLua annotations for cpp_function & cpp_variable
package.path = package.path .. ";../?.lua"
local utils = require("utils")
local vector2 = require("vector2")
local player = {
position = vector2.new(0, 0),
height = 100,
speed = 5
}
local ball = {
x = 0,
y = 0,
size = 20,
xspeed = 5,
yspeed = 5,
color = Color.new(0,255,255)
}
local AI = {
y = 200,
height = 100,
speed = 4
}
local player_score = 0
local ai_score = 0
local start_timer = 0
local start_timer_max = 60
local next_round = true
local countdown_text = 3
function update_player(player)
if GameEngine:isKeyDown("W") then
if (player.position.y >= 0) then
player.position.y = player.position.y - player.speed
end
end
if GameEngine:isKeyDown("S") then
if player.position.y + player.height < GameEngine:getHeight() then
player.position.y = player.position.y + player.speed
end
end
end
function update_ai()
if ball.y < AI.y then
if(AI.y > 0) then
AI.y = AI.y - AI.speed
end
end
if ball.y > AI.y then
if AI.y + AI.height < GameEngine:getHeight() then
AI.y = AI.y + AI.speed
end
end
end
function update_ball()
ball.x = ball.x + ball.xspeed
ball.y = ball.y + ball.yspeed
if utils.check_collision(ball.x, ball.y, ball.size, ball.size, player.position.x, player.position.y, 10, player.height) then
ball.xspeed = -ball.xspeed * 1.1
end
if utils.check_collision(ball.x, ball.y, ball.size, ball.size, GameEngine:getWidth() - 10, AI.y, 10, AI.height) then
ball.xspeed = -ball.xspeed * 1.1
end
if ball.x <= 0 then
ai_score = ai_score + 1
ball.x = math.floor(GameEngine:getWidth() / 2 - ball.size / 2)
ball.y = math.floor(GameEngine:getHeight() / 2 - ball.size / 2)
next_round = true
start_timer = 0
countdown_text = 3
end
if ball.x >= GameEngine:getWidth() - ball.size then
player_score = player_score + 1
ball.x = math.floor(GameEngine:getWidth() / 2 - ball.size / 2)
ball.y = math.floor(GameEngine:getHeight() / 2 - ball.size / 2)
next_round = true
start_timer = 0
countdown_text = 3
end
if ball.y <= 0 or ball.y >= GameEngine:getHeight() - ball.size then
ball.yspeed = -ball.yspeed
end
end
function draw_player()
GameEngine:setColor(Color.new(255,255,255))
GameEngine:fillRect(0, math.floor(player.position.y), 10, player.height)
end
function draw_ai()
GameEngine:setColor(Color.new(255,255,0))
GameEngine:fillRect(GameEngine:getWidth() - 10, AI.y, 10, AI.height)
end
function draw_ball()
GameEngine:setColor(ball.color)
GameEngine:fillOval(ball.x, ball.y, ball.size, ball.size)
end
--- the setup function
--- @return nil
function setup_window()
GameEngine:setTitle("BreakOut")
GameEngine:setWidth(800)
GameEngine:setHeight(600)
GameEngine:setFrameRate(60)
end
--- the set_keylist function
--- @return string
function set_keylist()
return "WASD"
end
--- the start function
--- @return nil
function start()
ball.x = GameEngine:getWidth() / 2 - ball.size / 2
ball.y = GameEngine:getHeight() / 2 - ball.size / 2
end
--- the update function
--- @param Engine GameEngine # The GameEngine instance.
--- @return nil
function update()
if (start_timer < start_timer_max and next_round) then
start_timer = start_timer + 1
if start_timer % 20 == 0 then
countdown_text = countdown_text - 1
end
return
end
if(next_round) then
ball.xspeed = 5
ball.yspeed = 5
next_round = false
end
update_player(player)
update_ball()
update_ai()
end
--- the draw function
--- @return nil
function draw()
GameEngine:fillScreen(Color.new(0, 0,0))
-- draw the score
GameEngine:setColor(Color.new(255,255,255))
GameEngine:drawText(tostring(player_score), 10, 10)
GameEngine:drawText(tostring(ai_score), GameEngine:getWidth() - 20, 10)
if next_round then
GameEngine:drawText(tostring(countdown_text), GameEngine:getWidth() / 2,GameEngine:getHeight() / 2 - 30)
end
draw_player()
draw_ball()
draw_ai()
end

236
lua/annotation.lua Normal file
View File

@@ -0,0 +1,236 @@
---@meta
--- This file provides type annotations for Lua scripts interacting with C++ via SOL2.
--- The Color class exposed from C++
--- @class Color
--- @field r number # The red component of the color.
--- @field g number # The green component of the color.
--- @field b number # The blue component of the color.
Color = {}
--- constructor
--- @param r number # The red component of the color.
--- @param g number # The green component of the color.
--- @param b number # The blue component of the color.
--- @return Color
function Color.new(r, g, b) end
--- The Bitmap class exposed from C++
--- @class Bitmap
--- @field filename string # The filename of the bitmap.
Bitmap = {}
--- constructor
--- @param filename string # The filename of the bitmap.
--- @return Bitmap
function Bitmap.new(filename) end
--- Sets the transparency color
--- @param color Color # The color to set as transparent.
--- @return nil
function Bitmap:SetTransparencyColor(color) end
--- Sets the Opacity
--- @param opacity number # The opacity to set.
--- @return nil
function Bitmap:SetOpacity(opacity) end
--- Gets if the bitmap exists
--- @return boolean # True if the bitmap exists, false otherwise.
function Bitmap:Exists() end
--- Gets the width of the bitmap
--- @return number # The width of the bitmap.
function Bitmap:GetWidth() end
--- Gets the height of the bitmap
--- @return number # The height of the bitmap.
function Bitmap:GetHeight() end
--- The Textbox class exposed from C++
--- @class Textbox
--- @field text string # The text of the textbox.
Textbox = {}
--- constructor
--- @param text string # The text of the textbox.
--- @return Textbox
function Textbox.new(text) end
--- Set the bounds of the textbox
--- @param x number # The x coordinate of the textbox.
--- @param y number # The y coordinate of the textbox.
--- @param width number # The width of the textbox.
--- @param height number # The height of the textbox.
--- @return nil
function Textbox:SetBounds(x, y, width, height) end
--- Set the text of the textbox
--- @param text string # The text of the textbox.
--- @return nil
function Textbox:SetText(text) end
--- Sets the background color of the textbox
--- @param color Color # The color to set as the background.
--- @return nil
function Textbox:SetBackgroundColor(color) end
--- Sets the foreground color of the textbox
--- @param color Color # The color to set as the foreground.
--- @return nil
function Textbox:SetForegroundColor(color) end
--- Sets the textbox to be enabled
--- @param enabled boolean # True if the textbox is enabled, false otherwise.
--- @return nil
function Textbox:SetEnabled(enabled) end
--- Shows the textbox
--- @return nil
function Textbox:Show() end
--- Hides the textbox
--- @return nil
function Textbox:Hide() end
--- Gets the text in the textbox
--- @return string # The text in the textbox.
function Textbox:GetText() end
--- The Game engine class exposed from C++
--- @class Engine
GameEngine = {}
--- @type
GameEngine = GameEngine
---Sets the title of the window.
---@param title string # The new title.
---@return nil
function GameEngine:setTitle(title) end
--- Sets the width of the window.
--- @param width number # The new width.
--- @return nil
function GameEngine:setWidth(width) end
--- Gets the window width.
--- @return number # The width of the window.
function GameEngine:getWidth() end
--- Gets the window height.
--- @return number # The height of the window.
function GameEngine:getHeight() end
--- Sets the height of the window.
--- @param height number # The new height.
--- @return nil
function GameEngine:setHeight(height) end
--- Sets the frame rate of the window.
--- @param frameRate number # The new frame rate.
--- @return nil
function GameEngine:setFrameRate(frameRate) end
--- Sets the drawing color
--- @param color Color # The new color.
--- @return nil
function GameEngine:setColor(color) end
--- Fills the screen
--- @param color Color # The color to fill the screen with.
--- @return nil
function GameEngine:fillScreen(color) end
--- message box
--- @param message string # The message to display.
--- @return nil
function GameEngine:messageBox(message) end
--- Draw a bitmap
--- @param bitmap Bitmap # The bitmap to draw.
--- @param x number # The x coordinate of the bitmap.
--- @param y number # The y coordinate of the bitmap.
function GameEngine:drawBitmap(bitmap, x, y) end
--- Draws a rectangle
--- @param x number # The x coordinate of the rectangle.
--- @param y number # The y coordinate of the rectangle.
--- @param width number # The width of the rectangle.
--- @param height number # The height of the rectangle.
--- @return nil
function GameEngine:drawRect(x, y, width, height) end
--- fills a rectangle
--- @param x number # The x coordinate of the rectangle.
--- @param y number # The y coordinate of the rectangle.
--- @param width number # The width of the rectangle.
--- @param height number # The height of the rectangle.
--- @return nil
function GameEngine:fillRect(x, y, width, height) end
--- draws an oval
--- @param x number # The x coordinate of the oval.
--- @param y number # The y coordinate of the oval.
--- @param width number # The width of the oval.
--- @param height number # The height of the oval.
function GameEngine:drawOval(x, y, width, height) end
--- fills an oval
--- @param x number # The x coordinate of the oval.
--- @param y number # The y coordinate of the oval.
--- @param width number # The width of the oval.
--- @param height number # The height of the oval.
function GameEngine:fillOval(x, y, width, height) end
--- draws text
--- @param text string # The text to draw.
--- @param x number # The x coordinate of the text.
--- @param y number # The y coordinate of the text.
--- @return nil
function GameEngine:drawText(text, x, y,) end
--- Checks if a key is pressed
--- @param key string # The key to check.
--- @return boolean # True if the key is pressed, false otherwise.
function GameEngine:isKeyDown(key) end
--- Gets the mouse Position X
--- @return number # The x coordinate of the mouse.
function GameEngine:getMouseX() end
--- Gets the mouse Position Y
--- @return number # The y coordinate of the mouse.
function GameEngine:getMouseY() end
--- Get if the left mouse button isKeyDown
--- @return boolean # True if the left mouse button is pressed, false otherwise.
function GameEngine:isMouseLeftDown() end;
--- Get if the right mouse button isKeyDown
--- @return boolean # True if the right mouse button is pressed, false otherwise.
function GameEngine:isMouseRightDown() end;
--- Preform a Get Request
--- @param url string # The url to get.
--- @return string # The response from the server.
function GameEngine:getRequest(url) end
--- Preform a Post Request
--- @param url string # The url to post.
--- @param data string # The data to post.
function GameEngine:postRequest(url, data) end
--- Opens a dialog to enter a string
--- @return string # The string entered by the user.
function GameEngine:GetString() end

68
lua/button.lua Normal file
View File

@@ -0,0 +1,68 @@
local Button = {}
Button.__index = Button
--- Constructor
--- @param x number
--- @param y number
--- @param width number
--- @param height number
--- @param text string
--- @param color Color
--- @param callback function | nil
--- @return Button
function Button.new(x, y, width, height, text, color, callback)
local self = setmetatable({}, Button)
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
self.color = color
self.callback = callback or function() end
self.clicked = false
return self
end
--- Update function
--- @param engine Engine
function Button:update(engine)
local mouseX = engine:getMouseX()
local mouseY = engine:getMouseY()
local isHovered = mouseX >= self.x and mouseX <= (self.x + self.width) and
mouseY >= self.y and mouseY <= (self.y + self.height)
if isHovered and engine:isMouseLeftDown() then
if not self.clicked then
self.callback()
self.clicked = true
end
else
self.clicked = false
end
end
--- Draw function
--- @param engine Engine
function Button:draw(engine)
local mouseX = engine:getMouseX()
local mouseY = engine:getMouseY()
local isHovered = mouseX >= self.x and mouseX <= (self.x + self.width) and
mouseY >= self.y and mouseY <= (self.y + self.height)
local drawColor = isHovered and Color.new(self.color.r + 50, self.color.g + 50, self.color.b + 50) or self.color
engine:setColor(drawColor)
engine:fillRect(self.x, self.y, self.width, self.height)
engine:setColor(Color.new(0, 0, 0)) -- Black text
local charWidth = 8
local textWidth = #self.text * charWidth
local textX = self.x + (self.width - textWidth) / 2
local textY = self.y + (self.height - 8) / 2
engine:drawText(self.text, textX, textY)
end
return Button

82
lua/script copy.lua Normal file
View File

@@ -0,0 +1,82 @@
-- annotation.lua contains EmmyLua annotations for cpp_function & cpp_variable\
function create_button(x, y, width, height, text)
return {
x = x,
y = y,
width = width,
height = height,
text = text,
hovered = false,
was_pressed = false,
onpressed = function() end
}
end
function draw_button(button)
if button.hovered then
Engine:setColor(Color.new(0, 255, 0))
else
Engine:setColor(Color.new(255, 0, 0))
end
Engine:fillRect(button.x, button.y, button.width, button.height)
Engine:setColor(Color.new(0, 0, 0))
Engine:drawText(button.text, button.x + 10, button.y + 10)
end
function is_mouse_in_button(button, x, y)
return x >= button.x and x <= button.x + button.width and y >= button.y and y <= button.y + button.height
end
testButton = create_button(100, 100, 100, 50, "Test Button")
function update_button(button)
if is_mouse_in_button(button, Engine:getMouseX(), Engine:getMouseY()) then
button.hovered = true
if Engine:isMouseLeftDown() and not button.was_pressed then
button.onpressed()
button.was_pressed = true
elseif not Engine:isMouseLeftDown() then
button.was_pressed = false
end
else
button.hovered = false
button.was_pressed = false
end
end
--- the setup function
--- @return nil
function setup_window()
Engine:setTitle("Hello World new")
Engine:setWidth(800)
Engine:setHeight(600)
Engine:setFrameRate(60)
testButton.onpressed = function()
Engine:messageBox("Button Pressed")
end
end
--- the set_keylist function
--- @return string
function set_keylist()
return "WASD"
end
--- the update function
--- @param Engine GameEngine # The GameEngine instance.
--- @return nil
function update()
update_button( testButton)
end
--- the draw function
--- @return nil
function draw()
Engine:setColor(Color.new(0, 0, 0))
Engine:fillScreen(Color.new(255, 255, 255))
draw_button(testButton)
end

501
lua/script.lua Normal file
View File

@@ -0,0 +1,501 @@
-- Tetris Game using GameEngine
local vector2 = require("vector2")
local button = require("button")
-- Constants
local GRID_WIDTH = 10
local GRID_HEIGHT = 19
local BLOCK_SIZE = 30
local FALL_SPEED = 0.5
local screenWidth = 800
local screenHeight = 600
-- Tetromino shapes and colors
local tetrominoes = {
{ shape = {{1, 1, 1, 1}}, color = Color.new(0, 255, 255) }, -- I
{ shape = {{1, 1}, {1, 1}}, color = Color.new(255, 255, 0) }, -- O
{ shape = {{0, 1, 0}, {1, 1, 1}}, color = Color.new(128, 0, 128) }, -- T
{ shape = {{1, 1, 0}, {0, 1, 1}}, color = Color.new(0, 255, 0) }, -- S
{ shape = {{0, 1, 1}, {1, 1, 0}}, color = Color.new(255, 0, 0) }, -- Z
{ shape = {{1, 1, 1}, {1, 0, 0}}, color = Color.new(255, 165, 0) }, -- L
{ shape = {{1, 1, 1}, {0, 0, 1}}, color = Color.new(0, 0, 255) } -- J
}
-- Game State
local grid = {}
local currentPiece = {}
local pieceX, pieceY = 4, 0
local fallTimer = 0
local lastKeyState = { Left = false, Right = false, Down = false, Rotate = false, Space = true }
-- enum of gameState, Main Menu, Playing, Submitting Name, Game Over
local MAIN_MENU = 0
local PLAYING = 1
local GAME_OVER = 2
local SUBMITTING_NAME = 3
local gameState = MAIN_MENU
local titleScreenBitmap
local nameTextBox
local score = 0
-- list of bitmap frames that should be animated
local leaderboardFrames = {}
local leaderboardFrameTimer = 0
local leaderboardFrameIndex = 1
local leaderboardFreamSpeed = 1
local yesButton = button.new(300, 400, 100, 50, "Yes", Color.new(0, 255, 0), function()
print("Sending Post")
local playerName = nameTextBox:GetText()
if playerName == "" then playerName = "Anonymous" end
local data = "{ \"player\": \"" .. playerName .. "\", \"score\": " .. score .. " }"
local response = GameEngine:postRequest("https://api.brammie15.dev/leaderboard/tetris", data)
if(response == "Error: Request timed out") then
netError = true
end
print(response)
gameState = GAME_OVER
end)
local noButton = button.new(300, 500, 100, 50, "No", Color.new(255, 0, 0), function() gameState = GAME_OVER end)
local hasGottenLeaderBoard = false
local leaderboard = {}
local netError = false
local nextPiece = {}
local pieceBag = {}
--- region BagStuff
local function shuffleBag()
pieceBag = {}
local indices = {1, 2, 3, 4, 5, 6, 7}
for i = #indices, 2, -1 do
local j = math.random(i)
indices[i], indices[j] = indices[j], indices[i]
end
for _, index in ipairs(indices) do
table.insert(pieceBag, tetrominoes[index])
end
end
local function getNextPiece()
if #pieceBag == 0 then
shuffleBag()
end
local pieceData = table.remove(pieceBag, 1)
return { shape = pieceData.shape, color = pieceData.color }
end
local function getRandomPiece()
local pieceData = tetrominoes[math.random(#tetrominoes)]
return { shape = pieceData.shape, color = pieceData.color }
end
--- endregion
local function checkCollision(px, py, piece)
for y = 1, #piece.shape do
for x = 1, #piece.shape[y] do
if piece.shape[y][x] == 1 then
if px + x < 1 or px + x > GRID_WIDTH or py + y > GRID_HEIGHT or (grid[py + y] and grid[py + y][px + x].value ~= 0) then
return true
end
end
end
end
return false
end
local function newPiece()
currentPiece = nextPiece or getNextPiece()
nextPiece = getNextPiece()
pieceX, pieceY = 4, 0
if checkCollision(pieceX, pieceY, currentPiece) then
gameState = SUBMITTING_NAME
end
end
--- region GhostPieces
local function getGhostPieceY()
local ghostY = pieceY
while not checkCollision(pieceX, ghostY + 1, currentPiece) do
ghostY = ghostY + 1
end
return ghostY
end
local function drawGhostPiece()
local ghostY = getGhostPieceY()
GameEngine:setColor(Color.new(200, 200, 200)) -- Semi-transparent gray
for y = 1, #currentPiece.shape do
for x = 1, #currentPiece.shape[y] do
if currentPiece.shape[y][x] == 1 then
GameEngine:fillRect((pieceX + x) * BLOCK_SIZE, (ghostY + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
end
end
end
end
--- endregion
local function drawNextPiece()
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawText("Next:", 650, 100)
if nextPiece then
for y = 1, #nextPiece.shape do
for x = 1, #nextPiece.shape[y] do
if nextPiece.shape[y][x] == 1 then
GameEngine:setColor(nextPiece.color)
GameEngine:fillRect(650 + x * BLOCK_SIZE, 120 + y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
end
end
end
end
end
local function clearLines()
local linesCleared = 0
for y = GRID_HEIGHT, 1, -1 do
local full = true
for x = 1, GRID_WIDTH do
if grid[y][x].value == 0 then
full = false
break
end
end
if full then
table.remove(grid, y)
table.insert(grid, 1, {})
for x = 1, GRID_WIDTH do
grid[1][x] = { value = 0, color = Color.new(255, 255, 255) }
end
linesCleared = linesCleared + 1
end
end
-- Score calculation based on cleared lines
local scoreTable = { 100, 300, 500, 800 }
if linesCleared > 0 then
score = score + scoreTable[linesCleared] or 0
end
end
local function freezePiece()
for y = 1, #currentPiece.shape do
for x = 1, #currentPiece.shape[y] do
if currentPiece.shape[y][x] == 1 then
grid[pieceY + y][pieceX + x] = { value = 1, color = currentPiece.color }
end
end
end
clearLines()
newPiece()
end
local function rotatePiece()
local newShape = {}
for x = 1, #currentPiece.shape[1] do
newShape[x] = {}
for y = 1, #currentPiece.shape do
newShape[x][#currentPiece.shape - y + 1] = currentPiece.shape[y][x]
end
end
if not checkCollision(pieceX, pieceY, { shape = newShape }) then
currentPiece.shape = newShape
end
end
local function drawGrid()
for y = 1, GRID_HEIGHT do
for x = 1, GRID_WIDTH do
if grid[y][x].value ~= 0 then
GameEngine:setColor(grid[y][x].color)
GameEngine:fillRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
end
GameEngine:setColor(Color.new(50, 50, 50))
GameEngine:drawRect(x * BLOCK_SIZE, y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
end
end
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawRect(BLOCK_SIZE, BLOCK_SIZE, GRID_WIDTH * BLOCK_SIZE, GRID_HEIGHT * BLOCK_SIZE)
end
local function drawPiece()
GameEngine:setColor(currentPiece.color)
for y = 1, #currentPiece.shape do
for x = 1, #currentPiece.shape[y] do
if currentPiece.shape[y][x] == 1 then
GameEngine:fillRect((pieceX + x) * BLOCK_SIZE, (pieceY + y) * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE)
end
end
end
end
function setup_window()
GameEngine:setTitle("Tetris")
GameEngine:setWidth(screenWidth)
GameEngine:setHeight(screenHeight)
GameEngine:setFrameRate(60)
end
--- the set_keylist function
--- @return string
function set_keylist()
return "WASD "
end
function start()
-- Initialize grid
for y = 1, GRID_HEIGHT do
grid[y] = {}
for x = 1, GRID_WIDTH do
grid[y][x] = { value = 0, color = Color.new(255, 255, 255) }
end
end
nextPiece = getRandomPiece()
newPiece()
nameTextBox = Textbox.new("")
nameTextBox:SetBounds(250, 200, 200, 25)
nameTextBox:Hide()
titleScreenBitmap = Bitmap.new("resources/tetrisLogo.bmp", true)
-- load frame0 - 4
for i = 0, 4 do
print("loading frame" .. i)
local bmp = Bitmap.new("resources/leaderboard/frame" .. i .. ".bmp", true)
bmp:SetTransparencyColor(Color.new(255, 0, 255))
table.insert(leaderboardFrames, bmp)
end
end
function update()
-- print(GameEngine:getMouseX(), GameEngine:getMouseY())
if gameState == PLAYING then
fallTimer = fallTimer + 1 / 60
if fallTimer >= FALL_SPEED then
if not checkCollision(pieceX, pieceY + 1, currentPiece) then
pieceY = pieceY + 1
else
freezePiece()
end
fallTimer = 0
end
local leftPressed = GameEngine:isKeyDown("A")
local rightPressed = GameEngine:isKeyDown("D")
local downPressed = GameEngine:isKeyDown("S")
local rotatePressed = GameEngine:isKeyDown("W")
local spacePressed = GameEngine:isKeyDown(" ")
if leftPressed and not lastKeyState.Left and not checkCollision(pieceX - 1, pieceY, currentPiece) then
pieceX = pieceX - 1
end
if rightPressed and not lastKeyState.Right and not checkCollision(pieceX + 1, pieceY, currentPiece) then
pieceX = pieceX + 1
end
if downPressed and not lastKeyState.Down and not checkCollision(pieceX, pieceY + 1, currentPiece) then
pieceY = pieceY + 1
end
if spacePressed and not lastKeyState.Space then
while not checkCollision(pieceX, pieceY + 1, currentPiece) do
pieceY = pieceY + 1
end
freezePiece()
end
if rotatePressed and not lastKeyState.Rotate then
rotatePiece()
end
lastKeyState.Left = leftPressed
lastKeyState.Right = rightPressed
lastKeyState.Down = downPressed
lastKeyState.Rotate = rotatePressed
lastKeyState.Space = spacePressed
end
if gameState == GAME_OVER then
--Update leaderboard gif
leaderboardFrameTimer = leaderboardFrameTimer + 1 / 60
if leaderboardFrameTimer >= leaderboardFreamSpeed then
leaderboardFrameIndex = leaderboardFrameIndex + 1
if leaderboardFrameIndex > #leaderboardFrames then
leaderboardFrameIndex = 1
end
leaderboardFrameTimer = 0
end
if GameEngine:isKeyDown("R") then
gameState = PLAYING
score = 0
netError = false
hasGottenLeaderBoard = false
leaderboard = {}
nameTextBox:SetText("")
grid = {}
for y = 1, GRID_HEIGHT do
grid[y] = {}
for x = 1, GRID_WIDTH do
grid[y][x] = { value = 0, color = Color.new(255, 255, 255) }
end
end
newPiece()
end
end
if gameState == SUBMITTING_NAME then
yesButton:update(GameEngine)
noButton:update(GameEngine)
end
if gameState == MAIN_MENU then
if GameEngine:isKeyDown(" ") then
gameState = PLAYING
end
end
end
function drawScoreBoard()
GameEngine:fillScreen(Color.new(0, 0, 0))
if not hasGottenLeaderBoard then
local response = GameEngine:getRequest("https://api.brammie15.dev/leaderboard/tetris")
if response == "Error: Request timed out" then
netError = true
end
print(response)
-- format is
-- NAME SCORE
if not netError then
for line in response:gmatch("[^\r\n]+") do
local name, score = line:match("([^%s]+)%s+(%d+)")
table.insert(leaderboard, { name = name, score = score })
end
table.sort(leaderboard, function(a, b) return a.score > b.score end)
end
hasGottenLeaderBoard = true
end
GameEngine:setColor(Color.new(255, 0, 0))
GameEngine:drawText("Score: " .. score, 350, 250)
if hasGottenLeaderBoard then
local NamesX = 600
local ScoresX = 700
local Y = 100
GameEngine:setColor(Color.new(255, 255, 0))
-- GameEngine:drawText("Leaderboard", 600, 60)
GameEngine:drawBitmap(leaderboardFrames[leaderboardFrameIndex], 450, 60)
if netError then
GameEngine:setColor(Color.new(255, 0, 0))
GameEngine:drawText("Error: Request timed out", 600, 100)
GameEngine:drawText("Or server down", 600, 120)
GameEngine:drawText("Not gonna check :p", 600, 140)
return
end
for i = 1, math.min(#leaderboard, 20) do
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawText(leaderboard[i].name, NamesX, Y)
GameEngine:drawText(leaderboard[i].score, ScoresX, Y)
Y = Y + 20
end
end
end
function drawGame()
GameEngine:fillScreen(Color.new(0, 0, 0))
drawGrid()
drawGhostPiece()
drawPiece()
drawNextPiece() -- Draw the next piece preview
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawText("Score: " .. score, 650, 50)
end
function drawGameOver()
nameTextBox:Hide()
GameEngine:setColor(Color.new(255, 0, 0))
GameEngine:fillScreen(Color.new(0, 0, 0))
GameEngine:drawText("Game Over", 350, 300)
GameEngine:drawText("Press R to restart", 350, 350)
drawScoreBoard()
end
function drawSubmitName()
GameEngine:fillScreen(Color.new(0, 0, 0))
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawText("Name:", 200, 200)
local charwidth = 8
local submitText = "Would you like to submit to the leaderboard"
local submitTextWidth = #submitText * charwidth
GameEngine:drawText(submitText, 400 - submitTextWidth / 2, 50)
nameTextBox:Show()
yesButton:draw(GameEngine)
noButton:draw(GameEngine)
end
function drawMainMenu()
GameEngine:drawBitmap(titleScreenBitmap, 0, 0)
--Press space to start
GameEngine:setColor(Color.new(255, 255, 255))
GameEngine:drawText("Press Space to Start", 350, 500)
end
function draw()
if gameState == PLAYING then
drawGame()
end
if gameState == GAME_OVER then
drawGameOver()
end
if gameState == SUBMITTING_NAME then
drawSubmitName()
end
if gameState == MAIN_MENU then
drawMainMenu()
end
end

131
lua/script_old.lua Normal file
View File

@@ -0,0 +1,131 @@
-- annotation.lua contains EmmyLua annotations for cpp_function & cpp_variable\
-- ball player
player = {
x = 0,
y = 0,
size = 20
}
avoid_cubes = {}
timer = 0
max_timer = 10
function create_cube(x, y, size, timer_to_explode, exploded, death_timer)
return {
x = x,
y = y,
size = size,
timer_to_explode = timer_to_explode,
death_timer = death_timer,
exploded = exploded
}
end
function create_random_cube()
local x = math.random(0, 800)
local y = math.random(0, 600)
local size = math.random(10, 50)
local timer_to_explode = math.random(50, 200)
local exploded = false
local death_timer = math.random(50, 200)
return create_cube(x, y, size, timer_to_explode, exploded, death_timer)
end
--- @param Engine GameEngine
--- @param cube table
function draw_cube(Engine, cube)
if cube.exploded then
Engine:setColor(Color.new(255, 0, 0))
Engine:fillRect(cube.x, cube.y, cube.size, cube.size)
else
Engine:setColor(Color.new(0, 255, 0))
Engine:drawRect(cube.x, cube.y, cube.size, cube.size)
end
end
function update_cubes()
for i, cube in ipairs(avoid_cubes) do
cube.timer_to_explode = cube.timer_to_explode - 1
if cube.timer_to_explode < 0 then
cube.exploded = true
end
if cube.exploded then
cube.death_timer = cube.death_timer - 1
if cube.death_timer < 0 then
table.remove(avoid_cubes, i)
end
end
end
end
function draw_player(Engine)
Engine:setColor(Color.new(255, 0, 0))
Engine:fillRect(player.x, player.y, player.size, player.size)
end
--- the setup function
--- @param Engine GameEngine # The GameEngine instance.
--- @return nil
function setup_window(Engine)
Engine:setTitle("Hello World new")
Engine:setWidth(800)
Engine:setHeight(600)
Engine:setFrameRate(60)
end
--- the set_keylist function
--- @return string
function set_keylist()
return "WASD"
end
--- the update function
--- @param Engine GameEngine # The GameEngine instance.
--- @return nil
function update(Engine)
if Engine:isKeyDown("W") then
player.y = player.y - 5
end
if Engine:isKeyDown("A") then
player.x = player.x - 5
end
if Engine:isKeyDown("S") then
player.y = player.y + 5
end
if Engine:isKeyDown("D") then
player.x = player.x + 5
end
timer = timer + 1
if timer > max_timer then
table.insert(avoid_cubes, create_random_cube())
timer = 0
end
update_cubes()
player.x = Engine:getMouseX()
player.y = Engine:getMouseY()
end
--- the draw function
--- @param Engine GameEngine # The GameEngine instance.
--- @return nil
function draw(Engine)
Engine:fillScreen(Color.new(0, 0, 0))
draw_player(Engine)
for i, cube in ipairs(avoid_cubes) do
draw_cube(Engine, cube)
end
end

21
lua/utils.lua Normal file
View File

@@ -0,0 +1,21 @@
utils = {}
--- @param x1 number
--- @param x2 number
--- @param w1 number
--- @param h1 number
--- @param y1 number
--- @param y2 number
--- @param w2 number
--- @param h2 number
--- @return boolean
function utils.check_collision(x1, y1, w1, h1, x2, y2, w2, h2)
return x1 < x2 + w2 and
x2 < x1 + w1 and
y1 < y2 + h2 and
y2 < y1 + h1
end
return utils

68
lua/vector2.lua Normal file
View File

@@ -0,0 +1,68 @@
-- Define 'Vector2' class with shared methods and metamethods
--- @class Vector2
--- @field x number
--- @field y number
local Vector2 = {} -- Declare Vector2 as a local table first
--- Calculate the dot product of two vectors
--- @param v0 Vector2
--- @param v1 Vector2
--- @return number
Vector2.dot = function(v0, v1)
return v0.x * v1.x + v0.y * v1.y
end
--- Calculate the add of two vectors
--- @param v0 Vector2
--- @param v1 Vector2
--- @return Vector2
Vector2.__add = function(v0, v1)
return Vector2.new(v0.x + v1.x, v0.y + v1.y)
end
--- Calculate the sub of two vectors
--- @param v0 Vector2
--- @param v1 Vector2
--- @return Vector2
Vector2.__sub = function(v0, v1)
return Vector2.new(v0.x - v1.x, v0.y - v1.y)
end
--- Calcuate the length of a vector2
--- @param v Vector2
--- @return number
Vector2.__len = function(v)
return math.sqrt(v.x * v.x + v.y * v.y)
end
--- Vector2 to string
--- @param v Vector2
--- @return string
Vector2.__tostring = function(v)
return string.format("Vector2(%g, %g)", v.x, v.y)
end
Vector2.add = function(self, other_vec)
self.x = self.x + other_vec.x
self.y = self.y + other_vec.y
return self
end
Vector2.__index = Vector2 -- Enable shared methods via __index
--- Constructor
--- @param x number
--- @param y number
--- @return Vector2
function Vector2.new(x, y)
return setmetatable(
{x = x or 0, y = y or 0}, -- Initialize x and y with defaults
Vector2 -- Assign metatable
)
end
return Vector2 -- Return the Vector2 table