package main import rl "vendor:raylib" import "vendor:raylib/rlgl" import "core:fmt" import "core:math" import "core:math/ease" // Virtual game field dimensions GameField := Vec2{800, 600} Game :: struct { using state: GameState, lives: u8, pad: Pad, balls: [dynamic]Ball, bricks: [dynamic]Brick, camera: rl.Camera2D, } game_init :: proc(prev: ^GameState = nil) -> ^GameState { state := new(Game) state.variant = state state.draw = game_draw state.update = game_update state.free = game_free state.lives = 3 state.previous = prev state.camera = rl.Camera2D{ zoom = 1, target = GameField / 2, } state.pad = Pad{ position = Vec2{GameField.x / 2, GameField.y - 40}, size = {80, 10}, } append(&state.balls, Ball{ radius = 8, position = Vec2{GameField.x / 2, GameField.y - 40 - 8}, is_on_pad = true, pad_offset = Vec2{0, -16}, }) offset : f32 = 0 for y : f32 = 40; y < GameField.y / 2; y += 35 { for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 { append(&state.bricks, spawn_brick(Vec2{x, y}, Vec2{30, 20}, color = rl.YELLOW)) } if offset == 0 { offset = 20 } else { offset = 0 } } return state } game_update :: proc(state: ^GameState, delta: f32) { game := transmute(^Game)state using game if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) { pause := pause_init(game) stack_push(pause) } pad_update(&game.pad, delta) if rl.IsKeyPressed(rl.KeyboardKey.SPACE) { for &ball, i in balls { if !ball.is_on_pad { continue } direction := Vec2{pad.velocity.x / PAD_MAX_SPEED, -1} ball.is_on_pad = false ball.velocity = rl.Vector2Normalize(direction) * 300 } } all_balls_outside := true #reverse for &ball, i in balls { is_inside := ball_update(&ball, game, delta) if is_inside { all_balls_outside = false } screen_pos := rl.GetWorldToScreen2D(ball.position, camera) if screen_pos.y > WINDOWF.y{ unordered_remove(&balls, i) } } if all_balls_outside { lives -= 1 append(&balls, Ball{ radius = 8, position = Vec2{GameField.x / 2, GameField.y - 40 - 8}, is_on_pad = true, pad_offset = Vec2{0, -16}, }) } #reverse for &brick, i in bricks { brick_update(&brick, camera, delta) if brick.is_to_free { unordered_remove(&bricks, i) } } camera.offset = WINDOWF / 2 camera.zoom = (WINDOWF.y / GameField.y) / 1.1 } game_draw :: proc(state: ^GameState) { game := transmute(^Game)state using game rl.BeginMode2D(camera) rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE) rl.DrawRectanglePro(rl.Rectangle{pad.position.x, pad.position.y, pad.size.x, pad.size.y}, pad.size / 2, 0, rl.GREEN) for ball, i in balls { rl.DrawCircleV(ball.position, ball.radius, rl.BLUE) } for brick, i in bricks { rl.DrawRectanglePro(rl.Rectangle{brick.position.x, brick.position.y, brick.size.x, brick.size.y}, brick.size / 2, brick.angle, brick.color) } //rl.DrawText(rl.TextFormat("%f\n%f\n%f\n%f", pad.position.x, pad.position.y, pad.velocity.x, pad.velocity.y), 0, 0, 20, rl.BLACK) rl.EndMode2D() for i : u8 = 0; i < lives; i += 1 { rl.DrawCircleV(Vec2{20 + 20 * f32(i), 20}, 10, rl.BLUE) } rl.DrawTextEx(FontUI, rl.TextFormat("%d", len(bricks)), {}, f32(48), 2, rl.BLACK) rl.DrawTextEx(FontUI, rl.TextFormat("%d", len(balls)), {0, 100}, f32(48), 2, rl.BLACK) } game_free :: proc(state: ^GameState) { game := transmute(^Game)state delete(game.bricks) delete(game.balls) free(state) }