190 lines
6.8 KiB
Lua
190 lines
6.8 KiB
Lua
-- NAME: Air Hockey
|
|
-- DESC: Fast-paced 2-player hockey
|
|
|
|
-- Game states
|
|
local STATE_MENU = 0
|
|
local STATE_PLAYING = 1
|
|
local STATE_GAME_OVER = 2
|
|
|
|
-- Game constants
|
|
local PADDLE_WIDTH = 6
|
|
local PADDLE_HEIGHT = 35
|
|
local PUCK_RADIUS = 3
|
|
local MAX_SCORE = 7
|
|
local PUCK_SPEED = 4
|
|
|
|
function init()
|
|
game.vars.state = STATE_MENU
|
|
|
|
-- Left paddle (player 1)
|
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
|
game.vars.paddle_left_score = 0
|
|
|
|
-- Right paddle (player 2)
|
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
|
game.vars.paddle_right_score = 0
|
|
|
|
-- Puck
|
|
game.vars.puck_x = game.width() / 2
|
|
game.vars.puck_y = game.height() / 2
|
|
game.vars.puck_vel_x = PUCK_SPEED
|
|
game.vars.puck_vel_y = 1
|
|
|
|
-- Enable continuous updates
|
|
game.set_frame_updates(true)
|
|
|
|
print("Air Hockey initialized")
|
|
end
|
|
|
|
function update(event)
|
|
local state = game.vars.state
|
|
|
|
if state == STATE_MENU then
|
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.BUTTON_0 or event.type == INPUT.BUTTON_1 then
|
|
reset_game()
|
|
game.vars.state = STATE_PLAYING
|
|
return true
|
|
end
|
|
|
|
elseif state == STATE_PLAYING then
|
|
-- Handle paddle input via touch
|
|
if event.type == INPUT.TOUCH_DOWN or event.type == INPUT.TOUCH_MOVE then
|
|
if event.x < game.width() / 2 then
|
|
-- Left paddle
|
|
game.vars.paddle_left_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
|
else
|
|
-- Right paddle
|
|
game.vars.paddle_right_y = math.max(0, math.min(game.height() - PADDLE_HEIGHT, event.y - PADDLE_HEIGHT / 2))
|
|
end
|
|
end
|
|
|
|
-- Update physics on frame tick
|
|
if event.type == INPUT.FRAME_TICK then
|
|
update_puck()
|
|
check_collisions()
|
|
|
|
-- Check win
|
|
if game.vars.paddle_left_score >= MAX_SCORE or game.vars.paddle_right_score >= MAX_SCORE then
|
|
game.vars.state = STATE_GAME_OVER
|
|
end
|
|
|
|
return true
|
|
end
|
|
|
|
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
|
|
|
|
function draw()
|
|
renderer.clear(false) -- Black background
|
|
|
|
local state = game.vars.state
|
|
|
|
if state == STATE_MENU then
|
|
renderer.text_scaled(game.width() / 2 - 35, game.height() / 2 - 30, "AIR HOCKEY", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2, "Tap to Start", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2 + 20, "First to " .. tostring(MAX_SCORE), true, 2)
|
|
|
|
elseif state == STATE_PLAYING or state == STATE_GAME_OVER then
|
|
-- Draw center line
|
|
for y = 0, game.height(), 4 do
|
|
renderer.pixel(game.width() / 2, y, true)
|
|
end
|
|
|
|
-- Draw goal areas (top/bottom highlights)
|
|
renderer.line(0, 5, game.width(), 5, true, 1)
|
|
renderer.line(0, game.height() - 5, game.width(), game.height() - 5, true, 1)
|
|
|
|
-- Draw paddles
|
|
renderer.rect(5, game.vars.paddle_left_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
|
renderer.rect(game.width() - 5 - PADDLE_WIDTH, game.vars.paddle_right_y, PADDLE_WIDTH, PADDLE_HEIGHT, true, true)
|
|
|
|
-- Draw puck (convert to integers)
|
|
renderer.circle(math.floor(game.vars.puck_x + 0.5), math.floor(game.vars.puck_y + 0.5), PUCK_RADIUS, true, true)
|
|
|
|
-- Draw scores
|
|
renderer.text_scaled(game.width() / 2 - 30, 5, tostring(game.vars.paddle_left_score), true, 2)
|
|
renderer.text_scaled(game.width() / 2 + 20, 5, tostring(game.vars.paddle_right_score), true, 2)
|
|
|
|
if state == STATE_GAME_OVER then
|
|
local winner = game.vars.paddle_left_score > game.vars.paddle_right_score and "Player 1" or "Player 2"
|
|
renderer.text_scaled(game.width() / 2 - 50, game.height() / 2 - 20, "GAME OVER", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 40, game.height() / 2, winner .. " Wins!", true, 2)
|
|
renderer.text_scaled(game.width() / 2 - 60, game.height() / 2 + 20, "Tap to Menu", true, 2)
|
|
end
|
|
end
|
|
end
|
|
|
|
function update_puck()
|
|
-- Move puck
|
|
game.vars.puck_x = game.vars.puck_x + game.vars.puck_vel_x
|
|
game.vars.puck_y = game.vars.puck_y + game.vars.puck_vel_y
|
|
|
|
-- Bounce off top/bottom
|
|
if game.vars.puck_y - PUCK_RADIUS < 0 or game.vars.puck_y + PUCK_RADIUS > game.height() then
|
|
game.vars.puck_vel_y = -game.vars.puck_vel_y
|
|
game.vars.puck_y = math.max(PUCK_RADIUS, math.min(game.height() - PUCK_RADIUS, game.vars.puck_y))
|
|
end
|
|
|
|
-- Goal: left side
|
|
if game.vars.puck_x < 0 then
|
|
game.vars.paddle_right_score = game.vars.paddle_right_score + 1
|
|
reset_puck()
|
|
end
|
|
|
|
-- Goal: right side
|
|
if game.vars.puck_x > game.width() then
|
|
game.vars.paddle_left_score = game.vars.paddle_left_score + 1
|
|
reset_puck()
|
|
end
|
|
end
|
|
|
|
function check_collisions()
|
|
-- Left paddle collision
|
|
if game.vars.puck_x - PUCK_RADIUS < 5 + PADDLE_WIDTH then
|
|
if game.vars.puck_y > game.vars.paddle_left_y and game.vars.puck_y < game.vars.paddle_left_y + PADDLE_HEIGHT then
|
|
if game.vars.puck_vel_x < 0 then
|
|
game.vars.puck_vel_x = -game.vars.puck_vel_x + 0.5 -- Speed up slightly
|
|
|
|
-- Add spin
|
|
local hit_pos = (game.vars.puck_y - game.vars.paddle_left_y) / PADDLE_HEIGHT
|
|
game.vars.puck_vel_y = (hit_pos - 0.5) * 6
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Right paddle collision
|
|
if game.vars.puck_x + PUCK_RADIUS > game.width() - 5 - PADDLE_WIDTH then
|
|
if game.vars.puck_y > game.vars.paddle_right_y and game.vars.puck_y < game.vars.paddle_right_y + PADDLE_HEIGHT then
|
|
if game.vars.puck_vel_x > 0 then
|
|
game.vars.puck_vel_x = -game.vars.puck_vel_x - 0.5 -- Speed up slightly
|
|
|
|
-- Add spin
|
|
local hit_pos = (game.vars.puck_y - game.vars.paddle_right_y) / PADDLE_HEIGHT
|
|
game.vars.puck_vel_y = (hit_pos - 0.5) * 6
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function reset_puck()
|
|
game.vars.puck_x = game.width() / 2
|
|
game.vars.puck_y = game.height() / 2
|
|
game.vars.puck_vel_x = PUCK_SPEED * (math.random() > 0.5 and 1 or -1)
|
|
game.vars.puck_vel_y = (math.random() - 0.5) * 2
|
|
end
|
|
|
|
function reset_game()
|
|
game.vars.paddle_left_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
|
game.vars.paddle_left_score = 0
|
|
game.vars.paddle_right_y = (game.height() / 2) - (PADDLE_HEIGHT / 2)
|
|
game.vars.paddle_right_score = 0
|
|
reset_puck()
|
|
end
|