-- 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