Final
This commit is contained in:
parent
09b0f28735
commit
d568031b82
BIN
assets/ball.png
BIN
assets/ball.png
Binary file not shown.
Before Width: | Height: | Size: 141 B |
BIN
assets/jet.png
BIN
assets/jet.png
Binary file not shown.
Before Width: | Height: | Size: 268 B |
BIN
assets/pad.png
BIN
assets/pad.png
Binary file not shown.
Before Width: | Height: | Size: 653 B |
18
ball.odin
18
ball.odin
|
@ -22,7 +22,7 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
|
||||||
} else {
|
} else {
|
||||||
ball_position_prev := ball.position
|
ball_position_prev := ball.position
|
||||||
ball.velocity = rl.Vector2Rotate(ball.velocity, ball.angular_velocity * delta)
|
ball.velocity = rl.Vector2Rotate(ball.velocity, ball.angular_velocity * delta)
|
||||||
if abs(ball.velocity.y) < 50 {
|
if abs(ball.velocity.y) < 100 && ball.angular_velocity == 0 {
|
||||||
ball.velocity.y += math.sign(ball.velocity.y) * 100 * delta
|
ball.velocity.y += math.sign(ball.velocity.y) * 100 * delta
|
||||||
}
|
}
|
||||||
ball.position += ball.velocity * delta
|
ball.position += ball.velocity * delta
|
||||||
|
@ -55,7 +55,7 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
|
||||||
offset_x := ball.position.x - pad.position.x
|
offset_x := ball.position.x - pad.position.x
|
||||||
dir.x = offset_x / (pad.size.x / 2)
|
dir.x = offset_x / (pad.size.x / 2)
|
||||||
ball.velocity = rl.Vector2Normalize(dir) * rl.Vector2Length(ball.velocity)
|
ball.velocity = rl.Vector2Normalize(dir) * rl.Vector2Length(ball.velocity)
|
||||||
ball.angular_velocity = -pad.velocity.x / PAD_MAX_SPEED * math.PI / 2
|
ball.angular_velocity = -pad.velocity.x / PAD_MAX_SPEED * math.PI / 3
|
||||||
}
|
}
|
||||||
|
|
||||||
for &brick, i in bricks {
|
for &brick, i in bricks {
|
||||||
|
@ -77,11 +77,17 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
|
||||||
diff := clamped - ball.position
|
diff := clamped - ball.position
|
||||||
normal := rl.Vector2Normalize(-diff)
|
normal := rl.Vector2Normalize(-diff)
|
||||||
if rl.Vector2LengthSqr(diff) < ball.radius * ball.radius {
|
if rl.Vector2LengthSqr(diff) < ball.radius * ball.radius {
|
||||||
|
if brick.type != .SOLID{
|
||||||
brick.hits -= 1
|
brick.hits -= 1
|
||||||
|
}
|
||||||
if brick.hits <= 0 && !brick.is_flying {
|
if brick.hits <= 0 && !brick.is_flying {
|
||||||
brick.is_flying = true
|
brick.is_flying = true
|
||||||
brick.velocity = ball.velocity / 2
|
brick.velocity = ball.velocity / 2
|
||||||
brick.angular_velocity = brick.velocity.x
|
brick.angular_velocity = brick.velocity.x
|
||||||
|
game.score += brick.score
|
||||||
|
}
|
||||||
|
if brick.hit_callback != nil {
|
||||||
|
brick.hit_callback(&brick, game)
|
||||||
}
|
}
|
||||||
ball.position = clamped + normal * ball.radius
|
ball.position = clamped + normal * ball.radius
|
||||||
ball.velocity = linalg.reflect(ball.velocity, normal)
|
ball.velocity = linalg.reflect(ball.velocity, normal)
|
||||||
|
@ -92,3 +98,11 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ball_draw :: proc(ball: ^Ball) {
|
||||||
|
draw_ball_virtual(ball.position, ball.radius)
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_ball_virtual :: proc(position: Vec2, radius: f32) {
|
||||||
|
rl.DrawCircleV(position, radius, rl.Color{80, 120, 255, 255})
|
||||||
|
rl.DrawCircleV(position - radius / 3, radius / 5, rl.Color{80, 160, 255, 255})
|
||||||
|
}
|
||||||
|
|
150
brick.odin
150
brick.odin
|
@ -1,6 +1,58 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
import "core:fmt"
|
||||||
|
|
||||||
|
Brick_Type :: enum {
|
||||||
|
NORMAL,
|
||||||
|
EXPLOSIVE,
|
||||||
|
BONUS,
|
||||||
|
HARD,
|
||||||
|
SOLID
|
||||||
|
}
|
||||||
|
|
||||||
|
brick_names := [Brick_Type]cstring {
|
||||||
|
.NORMAL = "обычный",
|
||||||
|
.EXPLOSIVE = "взрывной",
|
||||||
|
.BONUS = "восстанавливает здоровье",
|
||||||
|
.HARD = "крепкий",
|
||||||
|
.SOLID = "нерушимый"
|
||||||
|
}
|
||||||
|
|
||||||
|
brick_colors := [Brick_Type]rl.Color {
|
||||||
|
.NORMAL = rl.Color{190, 190, 190, 255},
|
||||||
|
.EXPLOSIVE = rl.Color{250, 30, 30, 255},
|
||||||
|
.BONUS = rl.Color{30, 250, 30, 255},
|
||||||
|
.HARD = rl.Color{190, 190, 255, 255},
|
||||||
|
.SOLID = rl.Color{80, 80, 100, 255}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
brick_scores := [Brick_Type]u32 {
|
||||||
|
.NORMAL = 100,
|
||||||
|
.HARD = 500,
|
||||||
|
.BONUS = 1000,
|
||||||
|
.EXPLOSIVE = 1000,
|
||||||
|
.SOLID = 0,
|
||||||
|
}
|
||||||
|
|
||||||
|
brick_hits := [Brick_Type]i32 {
|
||||||
|
.NORMAL = 1,
|
||||||
|
.EXPLOSIVE = 1,
|
||||||
|
.BONUS = 1,
|
||||||
|
.HARD = 2,
|
||||||
|
.SOLID = 10000000
|
||||||
|
}
|
||||||
|
|
||||||
|
BrickHitCallback :: proc(brick: ^Brick, game: ^Game)
|
||||||
|
|
||||||
|
brick_callbacks := [Brick_Type]BrickHitCallback {
|
||||||
|
.NORMAL = nil,
|
||||||
|
.EXPLOSIVE = brick_explode,
|
||||||
|
.BONUS = nil,
|
||||||
|
.HARD = nil,
|
||||||
|
.SOLID = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -13,32 +65,114 @@ Brick :: struct {
|
||||||
is_flying: bool,
|
is_flying: bool,
|
||||||
is_fixed: bool,
|
is_fixed: bool,
|
||||||
color: rl.Color,
|
color: rl.Color,
|
||||||
hits: u8,
|
hits: i32,
|
||||||
is_to_free: bool,
|
is_to_free: bool,
|
||||||
|
type: Brick_Type,
|
||||||
|
score: u32,
|
||||||
|
hit_callback: proc(brick: ^Brick, game: ^Game)
|
||||||
}
|
}
|
||||||
|
|
||||||
spawn_brick :: proc(position: Vec2, size: Vec2, is_fixed: bool = true, color: rl.Color = rl.RED, hits : u8 = 1) -> Brick {
|
spawn_brick :: proc(position: Vec2, size: Vec2, type: Brick_Type = Brick_Type.NORMAL) -> Brick {
|
||||||
return Brick {
|
return Brick {
|
||||||
position = position,
|
position = position,
|
||||||
velocity = Vec2{},
|
velocity = Vec2{},
|
||||||
size = Vec2{30,20},
|
size = size,
|
||||||
is_flying = false,
|
is_flying = false,
|
||||||
is_fixed = is_fixed,
|
is_fixed = true,
|
||||||
color = color,
|
type = type,
|
||||||
hits = hits,
|
color = brick_colors[type],
|
||||||
|
hits = brick_hits[type],
|
||||||
|
score = brick_scores[type],
|
||||||
|
hit_callback = brick_callbacks[type],
|
||||||
angle = 0
|
angle = 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
brick_update :: proc(brick: ^Brick, camera: rl.Camera2D, delta: f32) {
|
brick_update :: proc(brick: ^Brick, game: ^Game, delta: f32) {
|
||||||
if !brick.is_flying { return }
|
if !brick.is_flying { return }
|
||||||
brick.velocity.y += 500 * delta
|
brick.velocity.y += 500 * delta
|
||||||
brick.position += brick.velocity * delta
|
brick.position += brick.velocity * delta
|
||||||
brick.angle += brick.angular_velocity * delta
|
brick.angle += brick.angular_velocity * delta
|
||||||
screen_pos := rl.GetWorldToScreen2D(brick.position, camera)
|
screen_pos := rl.GetWorldToScreen2D(brick.position, game.camera)
|
||||||
|
|
||||||
|
if brick.position.x < brick.size.x / 2 {
|
||||||
|
brick.position.x = brick.size.x / 2
|
||||||
|
brick.velocity.x = -brick.velocity.x
|
||||||
|
}
|
||||||
|
if brick.position.x > GameField.x - brick.size.x / 2 {
|
||||||
|
brick.position.x = GameField.x - brick.size.x / 2
|
||||||
|
brick.velocity.x = -brick.velocity.x
|
||||||
|
}
|
||||||
|
|
||||||
|
if rl.CheckCollisionCircleRec(brick.position, brick.size.x / 2, rl.Rectangle{
|
||||||
|
game.pad.position.x - game.pad.size.x / 2,
|
||||||
|
game.pad.position.y - game.pad.size.y / 2,
|
||||||
|
game.pad.size.x,
|
||||||
|
game.pad.size.y
|
||||||
|
}) {
|
||||||
|
if brick.type != .BONUS {
|
||||||
|
game.pad.health -= 30
|
||||||
|
} else {
|
||||||
|
game.pad.health = min(game.pad.health + 30, 100)
|
||||||
|
}
|
||||||
|
brick.is_to_free = true
|
||||||
|
}
|
||||||
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},
|
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}
|
rl.Rectangle{0, 0, WINDOWF.x, WINDOWF.y}
|
||||||
) {
|
) {
|
||||||
brick.is_to_free = true
|
brick.is_to_free = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
brick_draw :: proc(brick: ^Brick) {
|
||||||
|
rl.DrawRectanglePro(
|
||||||
|
rl.Rectangle{brick.position.x, brick.position.y, brick.size.x, brick.size.y},
|
||||||
|
brick.size / 2,
|
||||||
|
brick.angle,
|
||||||
|
brick.color
|
||||||
|
)
|
||||||
|
if brick.type == .HARD && brick.hits <= 1 {
|
||||||
|
rl.DrawRectanglePro(
|
||||||
|
rl.Rectangle{brick.position.x, brick.position.y, brick.size.x / 1.5, brick.size.y / 1.5},
|
||||||
|
brick.size / 3,
|
||||||
|
brick.angle,
|
||||||
|
rl.ColorTint(brick.color, rl.Color{180, 180, 180, 255})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
brick_explode :: proc(brick: ^Brick, game: ^Game) {
|
||||||
|
radius := brick.size.x * 2
|
||||||
|
brick.is_to_free = true
|
||||||
|
|
||||||
|
explode(brick.position, radius, 0.4)
|
||||||
|
for &other, i in game.bricks {
|
||||||
|
if other.is_to_free {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if &other == brick {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
diff := other.position - brick.position
|
||||||
|
dir := rl.Vector2Normalize(diff)
|
||||||
|
dis := rl.Vector2Length(diff)
|
||||||
|
if dis > radius {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch other.type {
|
||||||
|
case .NORMAL, .HARD, .SOLID:
|
||||||
|
other.hits = 0
|
||||||
|
other.is_flying = true
|
||||||
|
other.velocity += dir * (radius - dis + 50) * 3
|
||||||
|
other.angular_velocity = other.velocity.x * 2
|
||||||
|
game.score += other.score
|
||||||
|
case .EXPLOSIVE, .BONUS:
|
||||||
|
if other.hit_callback != nil {
|
||||||
|
other.hit_callback(&other, game)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
import "core:slice"
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
|
Explosion :: struct{
|
||||||
|
position: Vec2,
|
||||||
|
radius: f32,
|
||||||
|
max_radius: f32,
|
||||||
|
time: f32,
|
||||||
|
duration: f32,
|
||||||
|
to_free: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
explosions_buf : [256]Explosion
|
||||||
|
explosions : [dynamic]Explosion
|
||||||
|
|
||||||
|
explosions_init :: proc() {
|
||||||
|
explosions = slice.into_dynamic(explosions_buf[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
explode :: proc(position: Vec2, max_radius: f32, duration: f32) {
|
||||||
|
append(&explosions, Explosion {
|
||||||
|
position = position,
|
||||||
|
max_radius = max_radius,
|
||||||
|
duration = duration
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
explosions_process :: proc(delta: f32) {
|
||||||
|
#reverse for &e, i in explosions {
|
||||||
|
e.time += delta
|
||||||
|
e.radius = ease.back_out(e.time / e.duration) * e.max_radius
|
||||||
|
if e.time > e.duration {
|
||||||
|
unordered_remove(&explosions, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
explosions_draw :: proc() {
|
||||||
|
#reverse for &e, i in explosions {
|
||||||
|
color := rl.ColorAlpha(rl.YELLOW, 1 - ease.exponential_in(e.time / e.duration))
|
||||||
|
rl.DrawCircleV(e.position, e.radius, color)
|
||||||
|
}
|
||||||
|
}
|
158
game.odin
158
game.odin
|
@ -5,6 +5,9 @@ import "vendor:raylib/rlgl"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:math"
|
import "core:math"
|
||||||
import "core:math/ease"
|
import "core:math/ease"
|
||||||
|
import "core:math/rand"
|
||||||
|
import "core:strings"
|
||||||
|
import "core:strconv"
|
||||||
|
|
||||||
// Virtual game field dimensions
|
// Virtual game field dimensions
|
||||||
GameField := Vec2{800, 600}
|
GameField := Vec2{800, 600}
|
||||||
|
@ -16,42 +19,93 @@ Game :: struct {
|
||||||
balls: [dynamic]Ball,
|
balls: [dynamic]Ball,
|
||||||
bricks: [dynamic]Brick,
|
bricks: [dynamic]Brick,
|
||||||
camera: rl.Camera2D,
|
camera: rl.Camera2D,
|
||||||
|
score: u32,
|
||||||
|
levels_done: u32,
|
||||||
|
|
||||||
|
levelsize: Vec2i,
|
||||||
|
|
||||||
|
background: rl.Texture
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
game_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
game_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
state := new(Game)
|
state := new(Game)
|
||||||
|
state.previous = prev
|
||||||
state.variant = state
|
state.variant = state
|
||||||
state.draw = game_draw
|
state.draw = game_draw
|
||||||
state.update = game_update
|
state.update = game_update
|
||||||
state.free = game_free
|
state.free = game_free
|
||||||
state.lives = 3
|
state.levelsize = Vec2i{6, 4}
|
||||||
state.previous = prev
|
|
||||||
state.camera = rl.Camera2D{
|
backimage := rl.GenImageChecked(800, 600, 80, 60, rl.Color{70, 20, 120, 255}, rl.Color{80, 30, 130, 255})
|
||||||
|
defer rl.UnloadImage(backimage)
|
||||||
|
state.background = rl.LoadTextureFromImage(backimage)
|
||||||
|
|
||||||
|
game_setup(state)
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
game_setup :: proc(game: ^Game) {
|
||||||
|
#reverse for ball, i in game.balls {
|
||||||
|
unordered_remove(&game.balls, i)
|
||||||
|
}
|
||||||
|
#reverse for brick, i in game.bricks {
|
||||||
|
unordered_remove(&game.bricks, i)
|
||||||
|
}
|
||||||
|
game.lives = 3
|
||||||
|
game.camera = rl.Camera2D{
|
||||||
zoom = 1,
|
zoom = 1,
|
||||||
target = GameField / 2,
|
target = GameField / 2,
|
||||||
}
|
}
|
||||||
state.pad = Pad{
|
game.pad = Pad{
|
||||||
position = Vec2{GameField.x / 2, GameField.y - 40},
|
position = Vec2{GameField.x / 2, GameField.y - 40},
|
||||||
size = {80, 10},
|
size = {80, 20},
|
||||||
|
health = 100,
|
||||||
}
|
}
|
||||||
append(&state.balls, Ball{
|
game_gen_level(game)
|
||||||
|
}
|
||||||
|
|
||||||
|
game_init_ball :: proc(game: ^Game) {
|
||||||
|
game_clear_balls(game)
|
||||||
|
append(&game.balls, Ball{
|
||||||
radius = 8,
|
radius = 8,
|
||||||
position = Vec2{GameField.x / 2, GameField.y - 40 - 8},
|
position = Vec2{GameField.x / 2, -100},
|
||||||
is_on_pad = true,
|
is_on_pad = true,
|
||||||
pad_offset = Vec2{0, -16},
|
pad_offset = Vec2{0, -18},
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
game_clear_balls :: proc(game: ^Game) {
|
||||||
|
#reverse for &ball, i in game.balls {
|
||||||
|
unordered_remove(&game.balls, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game_clear_bricks :: proc(game: ^Game) {
|
||||||
|
#reverse for &brick, i in game.bricks {
|
||||||
|
unordered_remove(&game.bricks, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
game_gen_level :: proc(game: ^Game) {
|
||||||
|
explosions_init()
|
||||||
|
game_init_ball(game)
|
||||||
|
game_clear_bricks(game)
|
||||||
|
|
||||||
|
brick_size := Vec2{40, 30}
|
||||||
|
brick_margin := brick_size + 10
|
||||||
|
center := Vec2{GameField.x / 2, 100}
|
||||||
|
|
||||||
offset : f32 = 0
|
offset : f32 = 0
|
||||||
for y : f32 = 40; y < GameField.y / 2; y += 35 {
|
for y : i32 = 0; y < game.levelsize.y; y += 1 {
|
||||||
for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 {
|
for x : i32 = 0; x < game.levelsize.x / 2; x += 1{
|
||||||
append(&state.bricks, spawn_brick(Vec2{x, y}, Vec2{30, 20}, color = rl.YELLOW))
|
choice := rand.choice([]Brick_Type{.NORMAL, .NORMAL, .NORMAL, .HARD, .HARD, .SOLID, .EXPLOSIVE, .BONUS})
|
||||||
|
append(&game.bricks, spawn_brick(center + {-brick_margin.x / 2 - f32(x) * brick_margin.x, f32(y) * brick_margin.y}, brick_size, choice))
|
||||||
|
append(&game.bricks, spawn_brick(center + {+brick_margin.x / 2 + f32(x) * brick_margin.x, f32(y) * brick_margin.y}, brick_size, choice))
|
||||||
}
|
}
|
||||||
if offset == 0 { offset = 20 } else { offset = 0 }
|
|
||||||
}
|
}
|
||||||
return state
|
|
||||||
}
|
}
|
||||||
|
|
||||||
game_update :: proc(state: ^GameState, delta: f32) {
|
game_update :: proc(state: ^GameState, delta: f32) {
|
||||||
|
@ -63,6 +117,11 @@ game_update :: proc(state: ^GameState, delta: f32) {
|
||||||
pause := pause_init(game)
|
pause := pause_init(game)
|
||||||
stack_push(pause)
|
stack_push(pause)
|
||||||
}
|
}
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.P) {
|
||||||
|
for &brick in bricks {
|
||||||
|
brick.is_flying = true
|
||||||
|
}
|
||||||
|
}
|
||||||
pad_update(&game.pad, delta)
|
pad_update(&game.pad, delta)
|
||||||
if rl.IsKeyPressed(rl.KeyboardKey.SPACE) {
|
if rl.IsKeyPressed(rl.KeyboardKey.SPACE) {
|
||||||
for &ball, i in balls {
|
for &ball, i in balls {
|
||||||
|
@ -86,62 +145,97 @@ game_update :: proc(state: ^GameState, delta: f32) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if pad.health <= 0 {
|
||||||
|
|
||||||
|
go := gameover_init(game)
|
||||||
|
stack_push(go)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if all_balls_outside {
|
if all_balls_outside {
|
||||||
|
if lives == 0 {
|
||||||
|
go := gameover_init(game)
|
||||||
|
stack_push(go)
|
||||||
|
return
|
||||||
|
}
|
||||||
lives -= 1
|
lives -= 1
|
||||||
append(&balls, Ball{
|
append(&balls, Ball{
|
||||||
radius = 8,
|
radius = 8,
|
||||||
position = Vec2{GameField.x / 2, GameField.y - 40 - 8},
|
position = Vec2{GameField.x / 2, GameField.y - 40 - 8},
|
||||||
is_on_pad = true,
|
is_on_pad = true,
|
||||||
pad_offset = Vec2{0, -16},
|
pad_offset = Vec2{0, -18},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
only_solid := true
|
||||||
#reverse for &brick, i in bricks {
|
#reverse for &brick, i in bricks {
|
||||||
brick_update(&brick, camera, delta)
|
brick_update(&brick, game, delta)
|
||||||
|
if brick.type != .SOLID {
|
||||||
|
only_solid = false
|
||||||
|
}
|
||||||
if brick.is_to_free {
|
if brick.is_to_free {
|
||||||
unordered_remove(&bricks, i)
|
unordered_remove(&bricks, i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if only_solid {
|
||||||
|
leveldone := leveldone_init(game)
|
||||||
|
stack_push(leveldone)
|
||||||
|
}
|
||||||
|
|
||||||
|
explosions_process(delta)
|
||||||
|
|
||||||
camera.offset = WINDOWF / 2
|
camera.offset = WINDOWF / 2
|
||||||
camera.zoom = (WINDOWF.y / GameField.y) / 1.1
|
camera.zoom = (WINDOWF.y / GameField.y) / 1.1
|
||||||
}
|
}
|
||||||
|
|
||||||
game_draw :: proc(state: ^GameState) {
|
game_draw :: proc(state: ^GameState) {
|
||||||
game := transmute(^Game)state
|
game := transmute(^Game)state
|
||||||
using game
|
|
||||||
|
|
||||||
rl.BeginMode2D(camera)
|
rl.BeginMode2D(game.camera)
|
||||||
|
|
||||||
|
|
||||||
|
rl.DrawTextureV(game.background, {}, rl.WHITE)
|
||||||
|
// rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE)
|
||||||
|
for &ball, i in game.balls {
|
||||||
|
ball_draw(&ball)
|
||||||
|
}
|
||||||
|
pad_draw(&game.pad)
|
||||||
|
|
||||||
rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE)
|
for &brick, i in game.bricks {
|
||||||
rl.DrawRectanglePro(rl.Rectangle{pad.position.x, pad.position.y, pad.size.x, pad.size.y}, pad.size / 2, 0, rl.GREEN)
|
brick_draw(&brick)
|
||||||
|
|
||||||
for ball, i in balls {
|
|
||||||
rl.DrawCircleV(ball.position, ball.radius, rl.BLUE)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
explosions_draw()
|
||||||
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()
|
rl.EndMode2D()
|
||||||
|
|
||||||
for i : u8 = 0; i < lives; i += 1 {
|
game_field_zoomed := GameField * game.camera.zoom
|
||||||
rl.DrawCircleV(Vec2{20 + 20 * f32(i), 20}, 10, rl.BLUE)
|
|
||||||
|
buf : [12]byte
|
||||||
|
score_string := strconv.itoa(buf[:], int(game.score))
|
||||||
|
score_ui := strings.right_justify(score_string, 8, "0")
|
||||||
|
score_ui_c := strings.unsafe_string_to_cstring(score_ui)
|
||||||
|
|
||||||
|
size := rl.MeasureTextEx(FontUI, score_ui_c, 48, 2)
|
||||||
|
rl.DrawTextPro(FontUI, score_ui_c, {WINDOWF.x / 2, WINDOWF.y - 28}, size / 2, 0, 48, 2, rl.WHITE)
|
||||||
|
|
||||||
|
level_string := rl.TextFormat("Уровень %d", game.levels_done + 1)
|
||||||
|
size = rl.MeasureTextEx(FontUI, level_string, 48, 2)
|
||||||
|
rl.DrawTextPro(FontUI, level_string, {WINDOWF.x / 2 + game_field_zoomed.x / 2, WINDOWF.y - 28}, {size.x, size.y / 2}, 0, 48, 2, rl.WHITE)
|
||||||
|
|
||||||
|
for i : u8 = 0; i < game.lives; i += 1 {
|
||||||
|
draw_ball_virtual({WINDOWF.x / 2 - game_field_zoomed.x / 2 + 16 + f32(i * 40), WINDOWF.y - 28}, 16)
|
||||||
}
|
}
|
||||||
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_free :: proc(state: ^GameState) {
|
||||||
game := transmute(^Game)state
|
game := transmute(^Game)state
|
||||||
|
|
||||||
|
rl.UnloadTexture(game.background)
|
||||||
|
|
||||||
delete(game.bricks)
|
delete(game.bricks)
|
||||||
delete(game.balls)
|
delete(game.balls)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
|
|
||||||
|
GameOver :: struct {
|
||||||
|
using state: GameState,
|
||||||
|
position: Vec2,
|
||||||
|
size: Vec2,
|
||||||
|
ready_to_go: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
gameover_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
|
state := new(GameOver)
|
||||||
|
state.variant = state
|
||||||
|
state.position = Vec2{WINDOWF.x / 2, WINDOWF.y + 300}
|
||||||
|
state.size = {700, 500}
|
||||||
|
state.update = gameover_update
|
||||||
|
state.draw = gameover_draw
|
||||||
|
state.free = gameover_free
|
||||||
|
|
||||||
|
state.previous = prev
|
||||||
|
tween_to(&state.position.y, WINDOWF.y / 2, 1, ease.Ease.Back_Out, state, gameover_ready)
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
gameover_update :: proc(state: ^GameState, delta: f32) {
|
||||||
|
gameover := transmute(^GameOver)state
|
||||||
|
if gameover.ready_to_go {
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) {
|
||||||
|
stack_pop()
|
||||||
|
stack_pop()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.ENTER) {
|
||||||
|
stack_pop()
|
||||||
|
game := transmute(^Game)stack_top()
|
||||||
|
game_setup(game)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
gameover_draw :: proc(state: ^GameState) {
|
||||||
|
gameover := transmute(^GameOver)state
|
||||||
|
|
||||||
|
if state.previous != nil {
|
||||||
|
state.previous->draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleFontSize :: 96
|
||||||
|
TitleSpacing :: 3
|
||||||
|
TitleText :: "ИГРА ОКОНЧЕНА"
|
||||||
|
TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing)
|
||||||
|
|
||||||
|
SubtitleText := [3]cstring{"Нажмите Enter", "чтобы начать сначала", "Или Escape для выхода"}
|
||||||
|
SubtitleFontSize :: 48
|
||||||
|
SubtitleSpacing :: 2
|
||||||
|
SubtitleSizes := [3]Vec2{}
|
||||||
|
for c, i in SubtitleText {
|
||||||
|
SubtitleSizes = rl.MeasureTextEx(FontUI, c, SubtitleFontSize, SubtitleSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
rl.DrawRectangleV(gameover.position - gameover.size / 2, gameover.size, rl.Color{90, 30, 150, 255})
|
||||||
|
|
||||||
|
rl.DrawTextPro(FontTitle, TitleText, gameover.position - {0, 100}, TitleSize / 2, 0, TitleFontSize, TitleSpacing, rl.WHITE)
|
||||||
|
for c, i in SubtitleText {
|
||||||
|
rl.DrawTextPro(FontUI, c, gameover.position - {0, f32(10 - i * 50)}, SubtitleSizes[i] / 2, 0, SubtitleFontSize, SubtitleSpacing, rl.WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
gameover_free :: proc(state: ^GameState) {
|
||||||
|
free(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
gameover_ready :: proc(state: rawptr) {
|
||||||
|
gameover := transmute(^GameOver)state
|
||||||
|
gameover.ready_to_go = true
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
|
|
||||||
|
Howtoplay :: struct {
|
||||||
|
using state: GameState,
|
||||||
|
}
|
||||||
|
|
||||||
|
howtoplay_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
|
state := new(Howtoplay)
|
||||||
|
state.variant = state
|
||||||
|
state.update = howtoplay_update
|
||||||
|
state.draw = howtoplay_draw
|
||||||
|
state.free = howtoplay_free
|
||||||
|
state.previous = prev
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
howtoplay_update :: proc(state: ^GameState, delta: f32) {
|
||||||
|
howtoplay := transmute(^Howtoplay)state
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) {
|
||||||
|
stack_pop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
howtoplay_draw :: proc(state: ^GameState) {
|
||||||
|
howtoplay := transmute(^Howtoplay)state
|
||||||
|
|
||||||
|
TitleFontSize :: 96
|
||||||
|
TitleSpacing :: 3
|
||||||
|
TitleText :: "ARKADODGE"
|
||||||
|
TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing)
|
||||||
|
rl.DrawTextPro(FontTitle, "ARKADODGE", {WINDOWF.x - 50, 50}, {TitleSize.x, 0}, 0, 96, 3, rl.WHITE)
|
||||||
|
|
||||||
|
Text :: [?]cstring{
|
||||||
|
"Используй Пробел для запуска шара",
|
||||||
|
"Стрелки для перемещения биты",
|
||||||
|
"Не дай шарику упасть вниз!",
|
||||||
|
"",
|
||||||
|
"Также блоки, падающие на биту, уменьшают её здоровье!"
|
||||||
|
}
|
||||||
|
|
||||||
|
for t, y in Text {
|
||||||
|
rl.DrawTextPro(FontUI, t, {10, 100 + f32(y) * 38}, {0, 0}, 0, 32, 1, rl.WHITE)
|
||||||
|
}
|
||||||
|
|
||||||
|
for c, t in brick_names {
|
||||||
|
y := cast(i32)t
|
||||||
|
rl.DrawRectangleV({WINDOWF.x / 2, 100 + f32(y) * 40}, {30, 20}, brick_colors[t])
|
||||||
|
rl.DrawTextPro(FontUI, c, {WINDOWF.x / 2 + 40, 100 + f32(y) * 40}, {0, 14}, 0, 32, 2, rl.WHITE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
howtoplay_free :: proc(state: ^GameState) {
|
||||||
|
free(state)
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import rl "vendor:raylib"
|
||||||
|
import "core:math/ease"
|
||||||
|
|
||||||
|
|
||||||
|
LevelDone :: struct {
|
||||||
|
using state: GameState,
|
||||||
|
position: Vec2,
|
||||||
|
size: Vec2,
|
||||||
|
ready_to_go: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldone_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
|
state := new(LevelDone)
|
||||||
|
state.variant = state
|
||||||
|
state.position = Vec2{WINDOWF.x / 2, WINDOWF.y + 300}
|
||||||
|
state.size = {800, 400}
|
||||||
|
state.update = leveldone_update
|
||||||
|
state.draw = leveldone_draw
|
||||||
|
state.free = leveldone_free
|
||||||
|
|
||||||
|
state.previous = prev
|
||||||
|
tween_to(&state.position.y, WINDOWF.y / 2, 1, ease.Ease.Back_Out, state, leveldone_ready)
|
||||||
|
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldone_update :: proc(state: ^GameState, delta: f32) {
|
||||||
|
leveldone := transmute(^LevelDone)state
|
||||||
|
if leveldone.ready_to_go {
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.ENTER) {
|
||||||
|
stack_pop()
|
||||||
|
game := transmute(^Game)stack_top()
|
||||||
|
game.levels_done += 1
|
||||||
|
if game.levels_done > 0 && game.levels_done % 2 == 0 {
|
||||||
|
game.levelsize += {2, 1}
|
||||||
|
game.levelsize.x = min(game.levelsize.x, 13)
|
||||||
|
game.levelsize.y = min(game.levelsize.y, 8)
|
||||||
|
}
|
||||||
|
game_gen_level(game)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
leveldone_draw :: proc(state: ^GameState) {
|
||||||
|
leveldone := transmute(^LevelDone)state
|
||||||
|
|
||||||
|
if state.previous != nil {
|
||||||
|
state.previous->draw()
|
||||||
|
}
|
||||||
|
|
||||||
|
TitleFontSize :: 96
|
||||||
|
TitleSpacing :: 3
|
||||||
|
TitleText :: "УРОВЕНЬ ПРОЙДЕН"
|
||||||
|
TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing)
|
||||||
|
|
||||||
|
SubtitleText :: "Нажмите Enter чтобы продолжить"
|
||||||
|
SubtitleFontSize :: 48
|
||||||
|
SubtitleSpacing :: 2
|
||||||
|
SubtitleSize := rl.MeasureTextEx(FontUI, SubtitleText, SubtitleFontSize, SubtitleSpacing)
|
||||||
|
|
||||||
|
|
||||||
|
rl.DrawRectangleV(leveldone.position - leveldone.size / 2, leveldone.size, rl.Color{90, 30, 150, 255})
|
||||||
|
|
||||||
|
rl.DrawTextPro(FontTitle, TitleText, leveldone.position - {0, 50}, TitleSize / 2, 0, TitleFontSize, TitleSpacing, rl.WHITE)
|
||||||
|
rl.DrawTextPro(FontUI, SubtitleText, leveldone.position + {0, 20}, SubtitleSize / 2, 0, SubtitleFontSize, SubtitleSpacing, rl.WHITE)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldone_free :: proc(state: ^GameState) {
|
||||||
|
free(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
leveldone_ready :: proc(state: rawptr) {
|
||||||
|
leveldone := transmute(^LevelDone)state
|
||||||
|
leveldone.ready_to_go = true
|
||||||
|
}
|
|
@ -19,7 +19,7 @@ FontUI: rl.Font
|
||||||
WindowShouldExit := false
|
WindowShouldExit := false
|
||||||
|
|
||||||
main :: proc() {
|
main :: proc() {
|
||||||
rl.SetConfigFlags(rl.ConfigFlags{.FULLSCREEN_MODE, .VSYNC_HINT, .WINDOW_MAXIMIZED, .WINDOW_UNDECORATED})
|
rl.SetConfigFlags(rl.ConfigFlags{.FULLSCREEN_MODE, .VSYNC_HINT, })
|
||||||
monitor := rl.GetCurrentMonitor()
|
monitor := rl.GetCurrentMonitor()
|
||||||
rl.InitWindow(0, 0, "SinePong")
|
rl.InitWindow(0, 0, "SinePong")
|
||||||
rl.SetTargetFPS(9999)
|
rl.SetTargetFPS(9999)
|
||||||
|
@ -76,6 +76,7 @@ main :: proc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
TODO:
|
TODO:
|
||||||
|
@ -83,7 +84,7 @@ main :: proc() {
|
||||||
[ ] Проверка завершения уровня
|
[ ] Проверка завершения уровня
|
||||||
[ ] Генерация уровней
|
[ ] Генерация уровней
|
||||||
[ ] (Опционально) редактор уровней с сохранением в файл
|
[ ] (Опционально) редактор уровней с сохранением в файл
|
||||||
[ ] Меню
|
[x] Меню
|
||||||
[ ] Гейм овер
|
[ ] Гейм овер
|
||||||
[ ] Бонусы
|
[ ] Бонусы
|
||||||
[ ] Визуальное оформление
|
[ ] Визуальное оформление
|
||||||
|
|
|
@ -7,14 +7,12 @@ import "core:math/ease"
|
||||||
Menu_Buttons :: enum {
|
Menu_Buttons :: enum {
|
||||||
START,
|
START,
|
||||||
HOW_TO_PLAY,
|
HOW_TO_PLAY,
|
||||||
FULLSCREEN,
|
|
||||||
EXIT
|
EXIT
|
||||||
}
|
}
|
||||||
|
|
||||||
menu_strings := [Menu_Buttons]cstring {
|
menu_strings := [Menu_Buttons]cstring {
|
||||||
.START = "Старт",
|
.START = "Старт",
|
||||||
.HOW_TO_PLAY = "Как играть?",
|
.HOW_TO_PLAY = "Как играть?",
|
||||||
.FULLSCREEN = "Полный экран",
|
|
||||||
.EXIT = "Выход"
|
.EXIT = "Выход"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +28,7 @@ menu_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
state.variant = state
|
state.variant = state
|
||||||
state.list = MenuList(Menu_Buttons){
|
state.list = MenuList(Menu_Buttons){
|
||||||
state = state,
|
state = state,
|
||||||
position = {300, 300},
|
position = {100, WINDOWF.y / 2},
|
||||||
line_size = 60,
|
line_size = 60,
|
||||||
font_size = 48,
|
font_size = 48,
|
||||||
elements = &menu_strings,
|
elements = &menu_strings,
|
||||||
|
@ -47,6 +45,7 @@ menu_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
|
|
||||||
menu_update :: proc(state: ^GameState, delta: f32) {
|
menu_update :: proc(state: ^GameState, delta: f32) {
|
||||||
menu := transmute(^Menu)state
|
menu := transmute(^Menu)state
|
||||||
|
menu.list.position.y = WINDOWF.y / 2
|
||||||
|
|
||||||
menu_list_update(&menu.list)
|
menu_list_update(&menu.list)
|
||||||
}
|
}
|
||||||
|
@ -56,8 +55,9 @@ menu_button_pressed :: proc(state: ^GameState, el: Menu_Buttons) {
|
||||||
case .START:
|
case .START:
|
||||||
game := game_init(state)
|
game := game_init(state)
|
||||||
stack_push(game)
|
stack_push(game)
|
||||||
case .FULLSCREEN:
|
|
||||||
case .HOW_TO_PLAY:
|
case .HOW_TO_PLAY:
|
||||||
|
howtoplay := howtoplay_init(state)
|
||||||
|
stack_push(howtoplay)
|
||||||
case .EXIT:
|
case .EXIT:
|
||||||
WindowShouldExit = true
|
WindowShouldExit = true
|
||||||
return
|
return
|
||||||
|
|
9
pad.odin
9
pad.odin
|
@ -13,10 +13,12 @@ Pad :: struct {
|
||||||
position: Vec2,
|
position: Vec2,
|
||||||
velocity: Vec2,
|
velocity: Vec2,
|
||||||
size: Vec2,
|
size: Vec2,
|
||||||
|
health: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
pad_update :: proc(pad: ^Pad, delta: f32) {
|
pad_update :: proc(pad: ^Pad, delta: f32) {
|
||||||
|
pad.health = min(pad.health + delta, 100)
|
||||||
target : f32 = 0
|
target : f32 = 0
|
||||||
if rl.IsKeyDown(rl.KeyboardKey.LEFT) {
|
if rl.IsKeyDown(rl.KeyboardKey.LEFT) {
|
||||||
target = -PAD_MAX_SPEED
|
target = -PAD_MAX_SPEED
|
||||||
|
@ -53,4 +55,11 @@ pad_update :: proc(pad: ^Pad, delta: f32) {
|
||||||
pad.position.x = GameField.x - pad_half_width
|
pad.position.x = GameField.x - pad_half_width
|
||||||
pad.velocity.x = 0
|
pad.velocity.x = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
pad_draw :: proc(pad: ^Pad) {
|
||||||
|
rl.DrawRectanglePro(rl.Rectangle{pad.position.x, pad.position.y, pad.size.x, pad.size.y}, pad.size / 2, 0, rl.RED)
|
||||||
|
hbar_x := pad.health / 100 * pad.size.x
|
||||||
|
rl.DrawRectanglePro(rl.Rectangle{pad.position.x, pad.position.y, hbar_x, pad.size.y/2}, {hbar_x / 2, pad.size.y / 4}, 0, rl.GREEN)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,13 +6,11 @@ import "core:math/ease"
|
||||||
|
|
||||||
Pause_Buttons :: enum {
|
Pause_Buttons :: enum {
|
||||||
RESUME,
|
RESUME,
|
||||||
FULLSCREEN,
|
|
||||||
EXIT
|
EXIT
|
||||||
}
|
}
|
||||||
|
|
||||||
pause_strings := [Pause_Buttons]cstring {
|
pause_strings := [Pause_Buttons]cstring {
|
||||||
.RESUME = "Продолжить",
|
.RESUME = "Продолжить",
|
||||||
.FULLSCREEN = "Полный экран",
|
|
||||||
.EXIT = "Выход"
|
.EXIT = "Выход"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +44,10 @@ pause_init :: proc(prev: ^GameState = nil) -> ^GameState {
|
||||||
pause_update :: proc(state: ^GameState, delta: f32) {
|
pause_update :: proc(state: ^GameState, delta: f32) {
|
||||||
menu := transmute(^Pause)state
|
menu := transmute(^Pause)state
|
||||||
|
|
||||||
|
if rl.IsKeyPressed(rl.KeyboardKey.ESCAPE) {
|
||||||
|
stack_pop()
|
||||||
|
return
|
||||||
|
}
|
||||||
menu_list_update(&menu.list)
|
menu_list_update(&menu.list)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,7 +55,6 @@ pause_button_pressed :: proc(state: ^GameState, el: Pause_Buttons) {
|
||||||
switch el {
|
switch el {
|
||||||
case .RESUME:
|
case .RESUME:
|
||||||
stack_pop()
|
stack_pop()
|
||||||
case .FULLSCREEN:
|
|
||||||
case .EXIT:
|
case .EXIT:
|
||||||
stack_pop()
|
stack_pop()
|
||||||
stack_pop()
|
stack_pop()
|
||||||
|
@ -77,5 +78,6 @@ pause_draw :: proc(state: ^GameState) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pause_free :: proc(state: ^GameState) {
|
pause_free :: proc(state: ^GameState) {
|
||||||
|
pause := transmute(^Pause)state
|
||||||
free(state)
|
free(state)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ package main
|
||||||
|
|
||||||
import "core:slice"
|
import "core:slice"
|
||||||
|
|
||||||
StateVariant :: union{^Game, ^Menu, ^Pause}
|
StateVariant :: union{^Game, ^Menu, ^Pause, ^GameOver, ^LevelDone, ^Howtoplay}
|
||||||
|
|
||||||
GameState :: struct {
|
GameState :: struct {
|
||||||
update: proc(state: ^GameState, delta: f32),
|
update: proc(state: ^GameState, delta: f32),
|
||||||
|
@ -46,3 +46,7 @@ stack_pop :: proc() -> (bool) {
|
||||||
state->free()
|
state->free()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stack_top :: proc() -> ^GameState {
|
||||||
|
return state_stack[len(state_stack)-1]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue