Add pagination to game launcher - 4 games per page
- GameLauncher now displays only 4 games per page to keep menu in bounds - Added page navigation with page indicator (Page X/Y) - KEY0 navigates between pages and within page - KEY1 selects the highlighted game - Touch selection works on current page only - Helper methods: get_total_pages(), get_page_start_index(), get_page_end_index() - Updated both lib/ and emulator/ versions for consistency
This commit is contained in:
219
emulator/games/lua_examples/snake.lua
Normal file
219
emulator/games/lua_examples/snake.lua
Normal file
@@ -0,0 +1,219 @@
|
||||
-- NAME: Snake Game
|
||||
-- DESC: Classic snake with state machine
|
||||
|
||||
-- Game states
|
||||
local STATE_MENU = 0
|
||||
local STATE_PLAYING = 1
|
||||
local STATE_GAME_OVER = 2
|
||||
|
||||
-- Game constants
|
||||
local CELL_SIZE = 10
|
||||
local GRID_W = 40
|
||||
local GRID_H = 28
|
||||
|
||||
-- Initialize game
|
||||
function init()
|
||||
game.vars.state = STATE_MENU
|
||||
game.vars.score = 0
|
||||
game.vars.high_score = 0
|
||||
|
||||
-- Snake as array of {x, y} positions
|
||||
game.vars.snake = {}
|
||||
game.vars.snake[1] = {x = 20, y = 14}
|
||||
game.vars.snake[2] = {x = 19, y = 14}
|
||||
game.vars.snake[3] = {x = 18, y = 14}
|
||||
|
||||
game.vars.dir_x = 1
|
||||
game.vars.dir_y = 0
|
||||
|
||||
game.vars.food_x = 30
|
||||
game.vars.food_y = 14
|
||||
|
||||
game.vars.frame_count = 0
|
||||
game.vars.move_speed = 10 -- Frames between moves
|
||||
|
||||
-- Enable continuous frame updates
|
||||
game.set_frame_updates(true)
|
||||
|
||||
print("Snake Game initialized")
|
||||
end
|
||||
|
||||
-- Update game logic
|
||||
function update(event)
|
||||
local state = game.vars.state
|
||||
|
||||
-- State: MENU
|
||||
if state == STATE_MENU then
|
||||
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||
game.vars.state = STATE_PLAYING
|
||||
game.vars.score = 0
|
||||
init_snake()
|
||||
spawn_food()
|
||||
return true
|
||||
end
|
||||
|
||||
-- State: PLAYING
|
||||
elseif state == STATE_PLAYING then
|
||||
-- Handle input for direction change
|
||||
if event.type == INPUT.TOUCH_DOWN then
|
||||
local head = game.vars.snake[1]
|
||||
local head_screen_x = head.x * CELL_SIZE
|
||||
local head_screen_y = head.y * CELL_SIZE
|
||||
|
||||
local dx = event.x - head_screen_x
|
||||
local dy = event.y - head_screen_y
|
||||
|
||||
-- Change direction based on touch relative to head
|
||||
if math.abs(dx) > math.abs(dy) then
|
||||
if dx > 0 and game.vars.dir_x ~= -1 then
|
||||
game.vars.dir_x = 1
|
||||
game.vars.dir_y = 0
|
||||
elseif dx < 0 and game.vars.dir_x ~= 1 then
|
||||
game.vars.dir_x = -1
|
||||
game.vars.dir_y = 0
|
||||
end
|
||||
else
|
||||
if dy > 0 and game.vars.dir_y ~= -1 then
|
||||
game.vars.dir_x = 0
|
||||
game.vars.dir_y = 1
|
||||
elseif dy < 0 and game.vars.dir_y ~= 1 then
|
||||
game.vars.dir_x = 0
|
||||
game.vars.dir_y = -1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Move snake every N frames
|
||||
game.vars.frame_count = game.vars.frame_count + 1
|
||||
if game.vars.frame_count >= game.vars.move_speed then
|
||||
game.vars.frame_count = 0
|
||||
move_snake()
|
||||
return true
|
||||
end
|
||||
|
||||
-- State: GAME_OVER
|
||||
elseif state == STATE_GAME_OVER then
|
||||
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
||||
game.vars.state = STATE_MENU
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
-- Draw game
|
||||
function draw()
|
||||
renderer.clear(false)
|
||||
|
||||
local state = game.vars.state
|
||||
|
||||
-- Draw: MENU
|
||||
if state == STATE_MENU then
|
||||
renderer.text(game.width() / 2 - 30, game.height() / 2 - 20, "SNAKE", true)
|
||||
renderer.text(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true)
|
||||
|
||||
if game.vars.high_score > 0 then
|
||||
local hs_text = "High: " .. tostring(game.vars.high_score)
|
||||
renderer.text(game.width() / 2 - 30, game.height() / 2 + 20, hs_text, true)
|
||||
end
|
||||
|
||||
-- Draw: PLAYING
|
||||
elseif state == STATE_PLAYING then
|
||||
-- Draw snake
|
||||
for i = 1, #game.vars.snake do
|
||||
local seg = game.vars.snake[i]
|
||||
local filled = (i == 1) -- Head filled, body outline
|
||||
renderer.rect(seg.x * CELL_SIZE, seg.y * CELL_SIZE, CELL_SIZE, CELL_SIZE, true, filled)
|
||||
end
|
||||
|
||||
-- Draw food
|
||||
renderer.circle(game.vars.food_x * CELL_SIZE + CELL_SIZE / 2,
|
||||
game.vars.food_y * CELL_SIZE + CELL_SIZE / 2,
|
||||
CELL_SIZE / 2, true, true)
|
||||
|
||||
-- Draw score
|
||||
local score_text = "Score: " .. tostring(game.vars.score)
|
||||
renderer.text(5, 5, score_text, true)
|
||||
|
||||
-- Draw: GAME_OVER
|
||||
elseif state == STATE_GAME_OVER then
|
||||
renderer.text(game.width() / 2 - 40, game.height() / 2 - 20, "GAME OVER", true)
|
||||
|
||||
local score_text = "Score: " .. tostring(game.vars.score)
|
||||
renderer.text(game.width() / 2 - 40, game.height() / 2, score_text, true)
|
||||
|
||||
renderer.text(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Continue", true)
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper: Initialize snake
|
||||
function init_snake()
|
||||
game.vars.snake = {}
|
||||
game.vars.snake[1] = {x = 20, y = 14}
|
||||
game.vars.snake[2] = {x = 19, y = 14}
|
||||
game.vars.snake[3] = {x = 18, y = 14}
|
||||
game.vars.dir_x = 1
|
||||
game.vars.dir_y = 0
|
||||
end
|
||||
|
||||
-- Helper: Spawn food at random position
|
||||
function spawn_food()
|
||||
game.vars.food_x = math.random(0, GRID_W - 1)
|
||||
game.vars.food_y = math.random(0, GRID_H - 1)
|
||||
end
|
||||
|
||||
-- Helper: Move snake
|
||||
function move_snake()
|
||||
local head = game.vars.snake[1]
|
||||
local new_head = {
|
||||
x = head.x + game.vars.dir_x,
|
||||
y = head.y + game.vars.dir_y
|
||||
}
|
||||
|
||||
-- Check wall collision
|
||||
if new_head.x < 0 or new_head.x >= GRID_W or new_head.y < 0 or new_head.y >= GRID_H then
|
||||
game_over()
|
||||
return
|
||||
end
|
||||
|
||||
-- Check self collision
|
||||
for i = 1, #game.vars.snake do
|
||||
local seg = game.vars.snake[i]
|
||||
if new_head.x == seg.x and new_head.y == seg.y then
|
||||
game_over()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- Check food collision
|
||||
local ate_food = false
|
||||
if new_head.x == game.vars.food_x and new_head.y == game.vars.food_y then
|
||||
ate_food = true
|
||||
game.vars.score = game.vars.score + 10
|
||||
spawn_food()
|
||||
|
||||
-- Increase speed slightly
|
||||
if game.vars.move_speed > 3 then
|
||||
game.vars.move_speed = game.vars.move_speed - 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Move snake
|
||||
table.insert(game.vars.snake, 1, new_head)
|
||||
|
||||
if not ate_food then
|
||||
table.remove(game.vars.snake) -- Remove tail
|
||||
end
|
||||
end
|
||||
|
||||
-- Helper: Game over
|
||||
function game_over()
|
||||
game.vars.state = STATE_GAME_OVER
|
||||
|
||||
if game.vars.score > game.vars.high_score then
|
||||
game.vars.high_score = game.vars.score
|
||||
end
|
||||
|
||||
print("Game Over! Score: " .. game.vars.score)
|
||||
end
|
||||
Reference in New Issue
Block a user