0,]']±>Sï}WÿÍu§ðp8·d%qy)6o;]ÉA¦ösï÷ôôô”°ÂVl†3 128 then player_1.y = 128 - player_1.h end end function update_player_2() if btn(0) then player_2.y = player_2.y - 2 end if btn(1) then player_2.y = player_2.y + 2 end -- Keep the player within the screen bounds if player_2.y < 8 then player_2.y = 8 elseif player_2.y + player_2.h > 128 then player_2.y = 128 - player_2.h end end -- ================================ -- BALL'S FUNCTIONS -- ================================ function calculate_rebound_angle(paddle, ball, is_left_player) -- Calculate the relative position of the ball to the paddle local relative_y = ball.y - paddle.y local angles = {-60, -45, -30, -15, 0, 15, 30, 45, 60} -- Calculate the angle based on the paddle's position local zone if relative_y < 0 then zone = 1 elseif relative_y < 4 then zone = 2 elseif relative_y < 8 then zone = 3 elseif relative_y < 12 then zone = 4 elseif relative_y < 13 then zone = 5 elseif relative_y < 17 then zone = 6 elseif relative_y < 21 then zone = 7 elseif relative_y < 25 then zone = 8 else zone = 9 end -- Get the angle in degrees local angle_deg = angles[zone] if not is_left_player then angle_deg = 180 - angle_deg -- Invert the angle for Player 2 end -- Convert the angle to radians local angle_rad = math.rad(angle_deg) -- Calculate the new velocity components (gains +0.5 speed by 10 rebounds) local vel_x = (ball.speed + (ball.rebound//10)*0.5) * math.cos(angle_rad) local vel_y = (ball.speed + (ball.rebound//10)*0.5) * math.sin(angle_rad) return vel_x, vel_y end function reset_ball() -- Reset the ball's position to the center of the screen ball.x = 116 ball.y = 64 -- Randomly choose an angle for the ball's initial velocity local angles_deg = {45, 0, -45, 135, 180, -135} local angle_deg = angles_deg[math.random(1, #angles_deg)] local angle_rad = math.rad(angle_deg) -- Calculate the new velocity components ball.vel_x = ball.speed * math.cos(angle_rad) ball.vel_y = ball.speed * math.sin(angle_rad) end function update_ball() -- Update ball trail (last 3 positions) table.remove(ball_trail, 1) -- remove the oldest position table.insert(ball_trail, {x = ball.x, y = ball.y}) -- add the current position -- Update the ball's position based on its velocity ball.x = ball.x + ball.vel_x ball.y = ball.y + ball.vel_y -- Check for collisions with the top and bottom walls if ball.y < 8 then -- Logic ball.y = 8 ball.vel_y = -ball.vel_y -- Effects spawn_effect(ball.x, ball.y, {277,278,279,280}) sfx(16, 'A-3', 30) elseif ball.y + ball.h > 128 then -- Logic ball.y = 120 ball.vel_y = -ball.vel_y -- Effects spawn_effect(ball.x, ball.y, {309,310,311,312}) sfx(16, 'A-3', 30) end -- Check for collisions with player_1 if ball.x < player_1.x + player_1.w and ball.y + ball.h - 4 > player_1.y and ball.y < player_1.y + player_1.h - 4 then -- Logic ball.x = player_1.x + player_1.w ball.vel_x, ball.vel_y = calculate_rebound_angle(player_1, ball, true) -- Ball rebound count ball.rebound = ball.rebound + 1 -- Effects spawn_effect(ball.x, ball.y, {261,262,263,264}) sfx(16, 'D-4', 30) end -- Check for collisions with player_2 if ball.x + ball.w > player_2.x and ball.y + ball.h - 4 > player_2.y and ball.y < player_2.y + player_2.h - 4 then -- Logic ball.x = player_2.x - ball.w ball.vel_x, ball.vel_y = calculate_rebound_angle(player_2, ball, false) -- Ball rebound count ball.rebound = ball.rebound + 1 -- Effects spawn_effect(ball.x, ball.y, {293,294,295,296}) sfx(16, 'E-4', 30) end -- Check for scoring if ball.x < -8 then -- Effects sfx(17, 'A-4', 50) -- Clean ball trail ball_trail = { { x = ball.x, y = ball.y }, { x = ball.x, y = ball.y }, { x = ball.x, y = ball.y } } --Clean ball rebound ball.rebound = 0 -- Logic game.p2_goal = game.p2_goal + 1 mapping.timer = 60 change_state("goal") elseif ball.x > 240 then -- Effects sfx(17, 'A-4', 50) -- Clean ball trail ball_trail = { { x = ball.x, y = ball.y }, { x = ball.x, y = ball.y }, { x = ball.x, y = ball.y } } -- Clean ball rebound ball.rebound = 0 -- Logic game.p1_goal = game.p1_goal + 1 mapping.timer = 60 change_state("goal") end end -- ================================ -- VISUAL EFFECTS -- ================================ effects = {} function spawn_effect(x, y, sprite_ids) table.insert(effects, { x = x, y = y, sprites = sprite_ids, frame = 1, timer = 0, delay = 3 -- frames entre cada sprite }) end function update_effects() for i = #effects, 1, -1 do local e = effects[i] e.timer = e.timer + 1 if e.timer >= e.delay then e.timer = 0 e.frame = e.frame + 1 if e.frame > #e.sprites then table.remove(effects, i) -- eliminar efecto terminado end end end end function draw_effects() for _, e in ipairs(effects) do spr(e.sprites[e.frame], e.x, e.y, 0) end end -- ================================ -- GAME STATES -- ================================ function change_state(new_state) current_state = new_state end -- ==========START UP========== function update_startup_1() mapping.sy = mapping.sy - 1 if mapping.sy <= 0 then mapping.sy = 0 mapping.timer = 3*60 music(0) change_state("startup_2") end end function update_startup_2() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 0 mapping.x = 30 mapping.sy = 130 music() change_state("menu_anim") end end function update_menu_anim() mapping.sy = mapping.sy - 1 if mapping.sy <= 0 then mapping.sy = 0 music(1) mapping.timer = 10*60 change_state("menu") end end function draw_map_only() map(mapping.x, mapping.y, 30, 17, mapping.sx, mapping.sy) end -- ============MENU============ function update_menu() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 0 music() end if btnp(0) then menu.selection = menu.selection + 1 if menu.selection > menu.selection_max then menu.selection = menu.selection_min end end if btnp(1) then menu.selection = menu.selection - 1 if menu.selection < menu.selection_min then menu.selection = menu.selection_max end end if btnp(6) then music() mapping.timer = 60 game.goal = menu.goal_options[menu.selection] change_state("starting") end end function update_starting() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 3*60 music(2) mapping.x = 0 change_state("rules") end end function draw_menu() map(mapping.x, mapping.y, 30, 17, mapping.sx, mapping.sy) print("GOALS:", 93, 75, 4) print(menu.goal_options_str[menu.selection], 130, 75, 12) print("PRESS OR TO CHANGE GOALS", 22, 85, 4) if (current_state == "menu") or (current_state == "starting" and mapping.timer % 8 <= 3) then print("PRESS TO START", 72, 95, 4) end print("PLAYER 1:", 0, 10, 3) print(" and ", 0, 20, 3) print("PLAYER 2:", 193, 10, 10) print(" and ", 163, 20, 10) print(menu.company_desc, 55, 115, 12) print(menu.country_desc, 84, 125, 12) end -- ============RULES============ function update_rules() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 60 music() change_state("start_game_1") end end function draw_rules() cls() print("THE PLAYER WHO REACHES THE", 47, 45, 4) if menu.goal_options[menu.selection] < 10 then print(menu.goal_options[menu.selection], 114, 55, 12, false, 2) else print(menu.goal_options[menu.selection], 108, 55, 12, false, 2) end print("GOALS FIRST WINS", 75, 70, 4) print("READY!", 104, 90, 12) end -- ============STARTING GAME============ function update_start_game_1() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 60 change_state("start_game_2") end end function update_start_game_2() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 60 reset_ball() change_state("start_game_3") end end function update_start_game_3() update_player_1() update_player_2() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then mapping.timer = 60 change_state("game") end end function draw_start_game() map(mapping.x, mapping.y, 30, 17, mapping.sx, mapping.sy) if game.p1_goal < 10 then print(game.p1_goal, 90, 18, 2, false, 4) print(game.p1_goal, 90, 16, 3, false, 4) else print(game.p1_goal, 70, 18, 2, false, 4) print(game.p1_goal, 70, 16, 3, false, 4) end print(game.p2_goal, 130, 18, 9, false, 4) print(game.p2_goal, 130, 16, 10, false, 4) if current_state == "start_game_2" or current_state == "start_game_3" or current_state == "game" then spr(player_1.sprite, player_1.x, player_1.y, 0, 1, 0, 0, player_1.sprite_w, player_1.sprite_h) spr(player_2.sprite, player_2.x, player_2.y, 0, 1, 0, 0, player_2.sprite_w, player_2.sprite_h) end if current_state == "start_game_3" or current_state == "game" then spr(ball.sprite, ball.x, ball.y, 0, 1, 0, 0, ball.sprite_w, ball.sprite_h) end end -- ============GAME============ function update_game() update_player_1() update_player_2() update_ball() end function draw_game() -- MAP map(mapping.x, mapping.y, 30, 17, mapping.sx, mapping.sy) -- SCORE if game.p1_goal < 10 then print(game.p1_goal, 90, 18, 2, false, 4) print(game.p1_goal, 90, 16, 3, false, 4) else print(game.p1_goal, 70, 18, 2, false, 4) print(game.p1_goal, 70, 16, 3, false, 4) end print(game.p2_goal, 130, 18, 9, false, 4) print(game.p2_goal, 130, 16, 10, false, 4) -- PLAYERS spr(player_1.sprite, player_1.x, player_1.y, 0, 1, 0, 0, player_1.sprite_w, player_1.sprite_h) spr(player_2.sprite, player_2.x, player_2.y, 0, 1, 0, 0, player_2.sprite_w, player_2.sprite_h) -- BALL TRAIL if current_state == "game" then spr(305, ball_trail[1].x, ball_trail[1].y, 0) -- Oldest spr(289, ball_trail[2].x, ball_trail[2].y, 0) -- Middle spr(273, ball_trail[3].x, ball_trail[3].y, 0) -- Newest end -- BALL spr(ball.sprite, ball.x, ball.y, 0, 1, 0, 0, ball.sprite_w, ball.sprite_h) end function update_goal() update_player_1() update_player_2() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then if game.p1_goal >= game.goal or game.p2_goal >= game.goal then music(3) mapping.timer = 300 change_state("player_wins") else mapping.timer = 60 reset_ball() change_state("start_game_3") end end end function update_win() mapping.timer = mapping.timer - 1 if mapping.timer <= 0 then music() mapping.timer = 0 end if btnp(6) then music() mapping.timer = 0 -- Reset players player_1.y = 52 player_2.y = 52 -- Reset ball ball.x = 116 ball.y = 64 ball.vel_x = 0 ball.vel_y = 0 -- Reset scores game.p1_goal = 0 game.p2_goal = 0 game.goal = 0 -- Reset screen mapping.x = 30 mapping.sy = 130 -- Volver al menu change_state("menu_anim") end end function draw_win() cls() print("THE WINNER IS:", 83, 40, 4) if game.p1_goal >= game.goal then print("PLAYER 1", 32, 50, 3, false, 4) else print("PLAYER 2", 30, 50, 10, false, 4) end print("GAME OVER", 94, 85, 4) print("PRESS TO RETURN TO MENU", 47, 100, 12) end -- ============STATE TABLE============ state_logic = { startup_1 = update_startup_1, startup_2 = update_startup_2, menu_anim = update_menu_anim, menu = update_menu, starting = update_starting, rules = update_rules, start_game_1 = update_start_game_1, start_game_2 = update_start_game_2, start_game_3 = update_start_game_3, game = update_game, goal = update_goal, player_wins = update_win, } state_draw = { startup_1 = draw_map_only, startup_2 = draw_map_only, menu_anim = draw_map_only, menu = draw_menu, starting = draw_menu, rules = draw_rules, start_game_1 = draw_start_game, start_game_2 = draw_start_game, start_game_3 = draw_start_game, game = draw_game, goal = draw_game, player_wins = draw_win, } -- ================================ -- MAIN LOOP -- ================================ function TIC() cls(0) -- GAME -- UPDATE if state_logic[current_state] then state_logic[current_state]() end update_effects() -- DRAW if state_draw[current_state] then state_draw[current_state]() end draw_effects() -- DEBUG -- print("STATE: " .. current_state, 0, 0, 12) -- print("TIMER: " .. mapping.timer, 0, 10, 12) -- print("X: " .. mapping.x, 0, 20, 12) -- print("Y: " .. mapping.y, 0, 30, 12) -- print("SX: " .. mapping.sx, 0, 40, 12) -- print("SY: " .. mapping.sy, 0, 50, 12) -- DEBUG -- if btnp(0) then SCREEN_Y = SCREEN_Y-1 end -- if btnp(1) then SCREEN_Y = SCREEN_Y+1 end -- if btnp(2) then SCREEN_X = SCREEN_X-1 end -- if btnp(3) then SCREEN_X = SCREEN_X+1 end -- if btnp(4) then SCREEN_SY = SCREEN_SY-1 end -- if btnp(5) then SCREEN_SY = SCREEN_SY+1 end -- if btnp(6) then SCREEN_SX = SCREEN_SX-1 end -- if btnp(7) then SCREEN_SX = SCREEN_SX+1 end -- map(SCREEN_X,SCREEN_Y,SCREEN_WIDTH,SCREEN_HEIGHT,SCREEN_SX,SCREEN_SY) -- print("X:"..SCREEN_X,10,10,4) -- print("Y:"..SCREEN_Y,10,20,4) -- print("SX:"..SCREEN_SX,10,30,4) -- print("SY:"..SCREEN_SY,10,40,4) end