Arkadodge/game.odin

153 lines
3.4 KiB
Odin

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) * 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 }
screen_pos := rl.GetWorldToScreen2D(ball.position, camera)
fmt.println(screen_pos)
if !rl.CheckCollisionCircleRec(ball.position, ball.radius, rl.Rectangle{0, 0, WINDOWF.x, 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)
}
game_free :: proc(state: ^GameState) {
game := transmute(^Game)state
delete(game.bricks)
delete(game.balls)
free(state)
}