Arkadodge/game.odin

166 lines
3.8 KiB
Odin

package main
import rl "vendor:raylib"
import "vendor:raylib/rlgl"
import "core:fmt"
import "core:math"
// Virtual game field dimensions
GameField := Vec2{800, 600}
Brick :: struct {
position: Vec2,
velocity: Vec2,
size: Vec2,
angle: f32,
angular_velocity: f32,
is_flying: bool,
is_fixed: bool,
color: rl.Color,
hits: u8,
}
spawn_brick :: proc(position: Vec2, size: Vec2, is_fixed: bool = true, color: rl.Color = rl.RED, hits : u8 = 1) -> Brick {
return Brick {
position = position,
velocity = Vec2{},
size = Vec2{30,20},
is_flying = false,
is_fixed = is_fixed,
color = color,
hits = hits,
angle = 0
}
}
Game :: struct {
using state: GameState,
lives: u8,
pad: Pad,
balls: [dynamic]Ball,
bricks: [dynamic]Brick,
camera: rl.Camera2D
}
game_init :: proc() -> ^GameState {
state := new(Game)
state.variant = state
state.draw = game_draw
state.update = game_update
state.lives = 3
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
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) * 400
}
}
all_balls_outside := true
#reverse for &ball, i in balls {
is_inside := ball_update(&ball, game, delta)
if is_inside { all_balls_outside = false }
if ball.position.y > GameField.y + 200 {
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 {
if !brick.is_flying { continue }
brick.velocity.y += 500 * delta
brick.position += brick.velocity * delta
brick.angle += brick.angular_velocity * delta
screen_pos := rl.GetWorldToScreen2D(brick.position, camera)
fmt.println(screen_pos)
if !rl.CheckCollisionRecs(rl.Rectangle{screen_pos.x - brick.size.x / 2, screen_pos.y - brick.size.y / 2, brick.size.x, brick.size.y},
rl.Rectangle{0, 0, WINDOWF.x, WINDOWF.y}
) {
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.ClearBackground(rl.RAYWHITE)
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.DrawText(rl.TextFormat("%d", len(bricks)), 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)
}
}