Menu, tweens, fixed ball bugs
This commit is contained in:
		
							
								
								
									
										
											BIN
										
									
								
								assets/monogram-extended.ttf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/monogram-extended.ttf
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							@ -37,13 +37,14 @@ ball_update :: proc(ball: ^Ball, game: ^Game, delta: f32) -> bool {
 | 
				
			|||||||
				ball.velocity.x = -ball.velocity.x
 | 
									ball.velocity.x = -ball.velocity.x
 | 
				
			||||||
				ball.angular_velocity = 0
 | 
									ball.angular_velocity = 0
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ball.position.y < ball.radius - 20 {
 | 
							if ball.position.y < ball.radius {
 | 
				
			||||||
			ball.position.y = ball.radius
 | 
								ball.position.y = ball.radius
 | 
				
			||||||
			ball.velocity.y = -ball.velocity.y
 | 
								ball.velocity.y = -ball.velocity.y
 | 
				
			||||||
			ball.angular_velocity = 0
 | 
								ball.angular_velocity = 0
 | 
				
			||||||
			return false
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ball.position.y + ball.radius > pad.position.y - pad.size.y / 2 &&
 | 
							if ball.position.y + ball.radius > pad.position.y - pad.size.y / 2 &&
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										44
									
								
								brick.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								brick.odin
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,44 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import rl "vendor:raylib"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Brick :: struct {
 | 
				
			||||||
 | 
						position: Vec2,
 | 
				
			||||||
 | 
						velocity: Vec2,
 | 
				
			||||||
 | 
						size: Vec2,
 | 
				
			||||||
 | 
						angle: f32,
 | 
				
			||||||
 | 
						angular_velocity: f32,
 | 
				
			||||||
 | 
						is_flying: bool,
 | 
				
			||||||
 | 
						is_fixed: bool,
 | 
				
			||||||
 | 
						color: rl.Color,
 | 
				
			||||||
 | 
						hits: u8,
 | 
				
			||||||
 | 
						is_to_free: bool,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					brick_update :: proc(brick: ^Brick, camera: rl.Camera2D, delta: f32) {
 | 
				
			||||||
 | 
						if !brick.is_flying { return }
 | 
				
			||||||
 | 
						brick.velocity.y += 500 * delta
 | 
				
			||||||
 | 
						brick.position += brick.velocity * delta
 | 
				
			||||||
 | 
						brick.angle += brick.angular_velocity * delta
 | 
				
			||||||
 | 
						screen_pos := rl.GetWorldToScreen2D(brick.position, camera) 
 | 
				
			||||||
 | 
						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}
 | 
				
			||||||
 | 
						) {
 | 
				
			||||||
 | 
							brick.is_to_free = true
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										67
									
								
								game.odin
									
									
									
									
									
								
							
							
						
						
									
										67
									
								
								game.odin
									
									
									
									
									
								
							@ -4,53 +4,29 @@ import rl "vendor:raylib"
 | 
				
			|||||||
import "vendor:raylib/rlgl"
 | 
					import "vendor:raylib/rlgl"
 | 
				
			||||||
import "core:fmt"
 | 
					import "core:fmt"
 | 
				
			||||||
import "core:math"
 | 
					import "core:math"
 | 
				
			||||||
 | 
					import "core:math/ease"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Virtual game field dimensions
 | 
					// Virtual game field dimensions
 | 
				
			||||||
GameField := Vec2{800, 600}
 | 
					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 {
 | 
					Game :: struct {
 | 
				
			||||||
	using state: GameState,
 | 
						using state: GameState,
 | 
				
			||||||
	lives: u8,
 | 
						lives: u8,
 | 
				
			||||||
	pad: Pad,
 | 
						pad: Pad,
 | 
				
			||||||
	balls: [dynamic]Ball,
 | 
						balls: [dynamic]Ball,
 | 
				
			||||||
	bricks: [dynamic]Brick,
 | 
						bricks: [dynamic]Brick,
 | 
				
			||||||
	camera: rl.Camera2D
 | 
						camera: rl.Camera2D,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
game_init :: proc() -> ^GameState {
 | 
					game_init :: proc(prev: ^GameState = nil) -> ^GameState {
 | 
				
			||||||
	state := new(Game)
 | 
						state := new(Game)
 | 
				
			||||||
	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.lives = 3
 | 
						state.lives = 3
 | 
				
			||||||
 | 
						state.previous = prev
 | 
				
			||||||
	state.camera = rl.Camera2D{
 | 
						state.camera = rl.Camera2D{
 | 
				
			||||||
		zoom = 1,
 | 
							zoom = 1,
 | 
				
			||||||
		target = GameField / 2,
 | 
							target = GameField / 2,
 | 
				
			||||||
@ -67,6 +43,7 @@ game_init :: proc() -> ^GameState {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	offset : f32 = 0
 | 
						offset : f32 = 0
 | 
				
			||||||
	for y : f32 = 40; y < GameField.y / 2; y += 35 {
 | 
						for y : f32 = 40; y < GameField.y / 2; y += 35 {
 | 
				
			||||||
		for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 {
 | 
							for x : f32 = 40 + offset; x < GameField.x - 40; x += 40 {
 | 
				
			||||||
@ -99,7 +76,10 @@ game_update :: proc(state: ^GameState, delta: f32) {
 | 
				
			|||||||
		is_inside := ball_update(&ball, game, delta)
 | 
							is_inside := ball_update(&ball, game, delta)
 | 
				
			||||||
		if is_inside { all_balls_outside = false }
 | 
							if is_inside { all_balls_outside = false }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if ball.position.y > GameField.y + 200 {
 | 
							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)
 | 
								unordered_remove(&balls, i)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -115,15 +95,8 @@ game_update :: proc(state: ^GameState, delta: f32) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	#reverse for &brick, i in bricks {
 | 
						#reverse for &brick, i in bricks {
 | 
				
			||||||
		if !brick.is_flying { continue }
 | 
							brick_update(&brick, camera, delta)
 | 
				
			||||||
		brick.velocity.y += 500 * delta
 | 
							if brick.is_to_free {
 | 
				
			||||||
		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)
 | 
								unordered_remove(&bricks, i)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@ -138,7 +111,6 @@ game_draw :: proc(state: ^GameState) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	rl.BeginMode2D(camera)
 | 
						rl.BeginMode2D(camera)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rl.ClearBackground(rl.RAYWHITE)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE)
 | 
						rl.DrawRectangleGradientV(0, 0, i32(GameField.x), i32(GameField.y), rl.RED, rl.RAYWHITE)
 | 
				
			||||||
@ -154,12 +126,23 @@ game_draw :: proc(state: ^GameState) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//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("%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()
 | 
						rl.EndMode2D()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i : u8 = 0; i < lives; i += 1 {
 | 
						for i : u8 = 0; i < lives; i += 1 {
 | 
				
			||||||
		rl.DrawCircleV(Vec2{20 + 20 * f32(i), 20}, 10, rl.BLUE)
 | 
							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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										34
									
								
								main.odin
									
									
									
									
									
								
							
							
						
						
									
										34
									
								
								main.odin
									
									
									
									
									
								
							@ -1,15 +1,22 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import rl "vendor:raylib"
 | 
					import rl "vendor:raylib"
 | 
				
			||||||
 | 
					import rlgl "vendor:raylib/rlgl"
 | 
				
			||||||
import "core:slice"
 | 
					import "core:slice"
 | 
				
			||||||
import "core:fmt"
 | 
					import "core:fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "core:math/ease"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Vec2 :: [2]f32
 | 
					Vec2 :: [2]f32
 | 
				
			||||||
Vec2i :: [2]i32
 | 
					Vec2i :: [2]i32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
WINDOW : Vec2i
 | 
					WINDOW : Vec2i
 | 
				
			||||||
WINDOWF : Vec2
 | 
					WINDOWF : Vec2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					FontTitle : rl.Font
 | 
				
			||||||
 | 
					FontUI: rl.Font
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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, .WINDOW_MAXIMIZED,  .WINDOW_UNDECORATED})
 | 
				
			||||||
@ -17,6 +24,18 @@ main :: proc() {
 | 
				
			|||||||
	rl.InitWindow(0, 0, "SinePong")
 | 
						rl.InitWindow(0, 0, "SinePong")
 | 
				
			||||||
	rl.SetTargetFPS(9999)
 | 
						rl.SetTargetFPS(9999)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tween_init()
 | 
				
			||||||
 | 
						defer tween_clean()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						FontUI = rl.LoadFontEx("assets/monogram-extended.ttf", 96, nil, 2048)
 | 
				
			||||||
 | 
					//	FontUI = rl.LoadFont("assets/monogram-extended.ttf")
 | 
				
			||||||
 | 
						FontTitle = rl.LoadFontEx("assets/monogram-extended.ttf", 96*2, nil, 2048)
 | 
				
			||||||
 | 
						defer rl.UnloadFont(FontTitle)
 | 
				
			||||||
 | 
						defer rl.UnloadFont(FontUI)
 | 
				
			||||||
 | 
						rl.SetTextureFilter(FontUI.texture, rl.TextureFilter.POINT)
 | 
				
			||||||
 | 
						rl.SetTextureFilter(FontTitle.texture, rl.TextureFilter.POINT)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	WINDOW.x = rl.GetScreenWidth()
 | 
						WINDOW.x = rl.GetScreenWidth()
 | 
				
			||||||
	WINDOW.y = rl.GetScreenHeight()
 | 
						WINDOW.y = rl.GetScreenHeight()
 | 
				
			||||||
	WINDOWF = Vec2{f32(WINDOW.x), f32(WINDOW.y)}
 | 
						WINDOWF = Vec2{f32(WINDOW.x), f32(WINDOW.y)}
 | 
				
			||||||
@ -24,15 +43,16 @@ main :: proc() {
 | 
				
			|||||||
	stack_init()
 | 
						stack_init()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	defer {
 | 
						defer {
 | 
				
			||||||
		for s, i in state_stack {
 | 
							for state, i in state_stack {
 | 
				
			||||||
			free(s)
 | 
								state->free()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	game := game_init()
 | 
						game := game_init()
 | 
				
			||||||
	append(&state_stack, game)
 | 
						menu := menu_init()
 | 
				
			||||||
 | 
						append(&state_stack, menu)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (!rl.WindowShouldClose()) {
 | 
						for (!WindowShouldExit) {
 | 
				
			||||||
		if rl.IsWindowResized() {
 | 
							if rl.IsWindowResized() {
 | 
				
			||||||
			WINDOW.x = rl.GetScreenWidth()
 | 
								WINDOW.x = rl.GetScreenWidth()
 | 
				
			||||||
			WINDOW.y = rl.GetScreenHeight()
 | 
								WINDOW.y = rl.GetScreenHeight()
 | 
				
			||||||
@ -42,17 +62,17 @@ main :: proc() {
 | 
				
			|||||||
		current_state := state_stack[len(state_stack)-1]
 | 
							current_state := state_stack[len(state_stack)-1]
 | 
				
			||||||
		
 | 
							
 | 
				
			||||||
		delta := rl.GetFrameTime()
 | 
							delta := rl.GetFrameTime()
 | 
				
			||||||
 | 
							tweens_process(delta)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		current_state->update(delta)
 | 
							current_state->update(delta)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		{
 | 
							{
 | 
				
			||||||
			rl.BeginDrawing()
 | 
								rl.BeginDrawing()
 | 
				
			||||||
			defer rl.EndDrawing()
 | 
								defer rl.EndDrawing()
 | 
				
			||||||
 | 
								rl.ClearBackground(rl.Color{40, 10, 90, 255})
 | 
				
			||||||
			current_state->draw()
 | 
								current_state->draw()
 | 
				
			||||||
 | 
								rlgl.PopMatrix()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										95
									
								
								menu.odin
									
									
									
									
									
								
							
							
						
						
									
										95
									
								
								menu.odin
									
									
									
									
									
								
							@ -1,8 +1,103 @@
 | 
				
			|||||||
package main
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import rl "vendor:raylib"
 | 
					import rl "vendor:raylib"
 | 
				
			||||||
 | 
					import "core:math/ease"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Menu_Buttons :: enum {
 | 
				
			||||||
 | 
						START,
 | 
				
			||||||
 | 
						HOW_TO_PLAY,
 | 
				
			||||||
 | 
						FULLSCREEN,
 | 
				
			||||||
 | 
						EXIT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu_strings := [Menu_Buttons]cstring {
 | 
				
			||||||
 | 
						.START = "Старт",
 | 
				
			||||||
 | 
						.HOW_TO_PLAY = "Как играть?",
 | 
				
			||||||
 | 
						.FULLSCREEN = "Полный экран",
 | 
				
			||||||
 | 
						.EXIT = "Выход"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Menu :: struct {
 | 
					Menu :: struct {
 | 
				
			||||||
	using state: GameState,
 | 
						using state: GameState,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						menu_pos: Vec2,
 | 
				
			||||||
 | 
						menu_element_offset: f32,
 | 
				
			||||||
 | 
						active_element: Menu_Buttons,
 | 
				
			||||||
 | 
						active_marker: Vec2,
 | 
				
			||||||
 | 
						marker_tween: ^Tween,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu_init :: proc(prev: ^GameState = nil) -> ^GameState {
 | 
				
			||||||
 | 
						state := new(Menu)
 | 
				
			||||||
 | 
						state.variant = state
 | 
				
			||||||
 | 
						state.menu_pos = {300, 300}
 | 
				
			||||||
 | 
						state.menu_element_offset = 60
 | 
				
			||||||
 | 
						state.active_marker = state.menu_pos + Vec2{0, f32(state.active_element) * state.menu_element_offset}
 | 
				
			||||||
 | 
						state.update = menu_update
 | 
				
			||||||
 | 
						state.draw = menu_draw 
 | 
				
			||||||
 | 
						state.free = menu_free
 | 
				
			||||||
 | 
						state.previous = prev
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return state
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu_update :: proc(state: ^GameState, delta: f32) {
 | 
				
			||||||
 | 
						menu := transmute(^Menu)state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prev_element := cast(i8)menu.active_element
 | 
				
			||||||
 | 
						cur_element := prev_element
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.DOWN) {
 | 
				
			||||||
 | 
							cur_element += 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.UP) {
 | 
				
			||||||
 | 
							cur_element -= 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if prev_element != cur_element {
 | 
				
			||||||
 | 
							if cur_element < 0 { cur_element = len(Menu_Buttons) -1 }
 | 
				
			||||||
 | 
							if cur_element == len(Menu_Buttons) { cur_element = 0 }
 | 
				
			||||||
 | 
							menu.active_element = cast(Menu_Buttons)cur_element
 | 
				
			||||||
 | 
							if menu.marker_tween != nil {
 | 
				
			||||||
 | 
								tween_cancel(menu.marker_tween)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							menu.marker_tween = tween_to(
 | 
				
			||||||
 | 
								&menu.active_marker.y,
 | 
				
			||||||
 | 
								menu.menu_pos.y + f32(menu.active_element) * menu.menu_element_offset,
 | 
				
			||||||
 | 
								0.5,
 | 
				
			||||||
 | 
								ease.Ease.Back_Out,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.ENTER) || rl.IsKeyPressed(rl.KeyboardKey.SPACE) {
 | 
				
			||||||
 | 
							switch menu.active_element {
 | 
				
			||||||
 | 
								case .START:
 | 
				
			||||||
 | 
									game := game_init(state)
 | 
				
			||||||
 | 
									stack_push(game)
 | 
				
			||||||
 | 
								case .FULLSCREEN:
 | 
				
			||||||
 | 
								case .HOW_TO_PLAY:
 | 
				
			||||||
 | 
								case .EXIT:
 | 
				
			||||||
 | 
									WindowShouldExit = true
 | 
				
			||||||
 | 
									return
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu_draw :: proc(state: ^GameState) {
 | 
				
			||||||
 | 
						menu := transmute(^Menu)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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rl.DrawTextEx(FontUI, ">", menu.active_marker + {-30, 0}, 48, 2, rl.WHITE)
 | 
				
			||||||
 | 
						for el, i in menu_strings {
 | 
				
			||||||
 | 
							pos := menu.menu_pos + {0, f32(i) * menu.menu_element_offset}
 | 
				
			||||||
 | 
							rl.DrawTextEx(FontUI, el, pos, 48, 2, rl.WHITE)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					menu_free :: proc(state: ^GameState) {
 | 
				
			||||||
 | 
						free(state)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										100
									
								
								pause.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								pause.odin
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,100 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import rl "vendor:raylib"
 | 
				
			||||||
 | 
					import "core:math/ease"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pause_Buttons :: enum {
 | 
				
			||||||
 | 
						RESUME,
 | 
				
			||||||
 | 
						FULLSCREEN,
 | 
				
			||||||
 | 
						EXIT
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pause_strings := [Pause_Buttons]cstring {
 | 
				
			||||||
 | 
						.RESUME = "Старт",
 | 
				
			||||||
 | 
						.FULLSCREEN = "Полный экран",
 | 
				
			||||||
 | 
						.EXIT = "Выход"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Pause :: struct {
 | 
				
			||||||
 | 
						using state: GameState,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						menu_pos: Vec2,
 | 
				
			||||||
 | 
						menu_element_offset: f32,
 | 
				
			||||||
 | 
						active_element: Pause_Buttons,
 | 
				
			||||||
 | 
						active_marker: Vec2,
 | 
				
			||||||
 | 
						marker_tween: ^Tween,
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pause_init :: proc(prev: ^GameState = nil) -> ^GameState {
 | 
				
			||||||
 | 
						state := new(Pause)
 | 
				
			||||||
 | 
						state.variant = state
 | 
				
			||||||
 | 
						state.menu_pos = {300, 300}
 | 
				
			||||||
 | 
						state.menu_element_offset = 60
 | 
				
			||||||
 | 
						state.active_marker = state.menu_pos + Vec2{0, f32(state.active_element) * state.menu_element_offset}
 | 
				
			||||||
 | 
						state.update = menu_update
 | 
				
			||||||
 | 
						state.draw = menu_draw 
 | 
				
			||||||
 | 
						state.free = menu_free
 | 
				
			||||||
 | 
						state.previous = prev
 | 
				
			||||||
 | 
						
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return state
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pause_update :: proc(state: ^GameState, delta: f32) {
 | 
				
			||||||
 | 
						menu := transmute(^Pause)state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						prev_element := cast(i8)menu.active_element
 | 
				
			||||||
 | 
						cur_element := prev_element
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.DOWN) {
 | 
				
			||||||
 | 
							cur_element += 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.UP) {
 | 
				
			||||||
 | 
							cur_element -= 1
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if prev_element != cur_element {
 | 
				
			||||||
 | 
							if cur_element < 0 { cur_element = len(Pause_Buttons) -1 }
 | 
				
			||||||
 | 
							if cur_element == len(Pause_Buttons) { cur_element = 0 }
 | 
				
			||||||
 | 
							menu.active_element = cast(Pause_Buttons)cur_element
 | 
				
			||||||
 | 
							if menu.marker_tween != nil {
 | 
				
			||||||
 | 
								tween_cancel(menu.marker_tween)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							menu.marker_tween = tween_to(
 | 
				
			||||||
 | 
								&menu.active_marker.y,
 | 
				
			||||||
 | 
								menu.menu_pos.y + f32(menu.active_element) * menu.menu_element_offset,
 | 
				
			||||||
 | 
								0.5,
 | 
				
			||||||
 | 
								ease.Ease.Back_Out,
 | 
				
			||||||
 | 
							)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if rl.IsKeyPressed(rl.KeyboardKey.ENTER) || rl.IsKeyPressed(rl.KeyboardKey.SPACE) {
 | 
				
			||||||
 | 
							switch menu.active_element {
 | 
				
			||||||
 | 
								case .RESUME:
 | 
				
			||||||
 | 
									stack_pop()
 | 
				
			||||||
 | 
								case .FULLSCREEN:
 | 
				
			||||||
 | 
								case .EXIT:
 | 
				
			||||||
 | 
									stack_pop()
 | 
				
			||||||
 | 
									stack_pop()
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pause_draw :: proc(state: ^GameState) {
 | 
				
			||||||
 | 
						menu := transmute(^Pause)state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TitleFontSize :: 96
 | 
				
			||||||
 | 
						TitleSpacing :: 3
 | 
				
			||||||
 | 
						TitleText :: "ПАУЗА"
 | 
				
			||||||
 | 
						TitleSize := rl.MeasureTextEx(FontTitle, TitleText, TitleFontSize, TitleSpacing)
 | 
				
			||||||
 | 
						rl.DrawTextPro(FontTitle, "ARKADODGE", {WINDOWF.x - 50, 50}, {TitleSize.x, 0}, 0, 96, 3, rl.WHITE)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rl.DrawTextEx(FontUI, ">", menu.active_marker + {-30, 0}, 48, 2, rl.WHITE)
 | 
				
			||||||
 | 
						for el, i in menu_strings {
 | 
				
			||||||
 | 
							pos := menu.menu_pos + {0, f32(i) * menu.menu_element_offset}
 | 
				
			||||||
 | 
							rl.DrawTextEx(FontUI, el, pos, 48, 2, rl.WHITE)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					pause_free :: proc(state: ^GameState) {
 | 
				
			||||||
 | 
						free(state)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,11 +2,13 @@ package main
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import "core:slice"
 | 
					import "core:slice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
StateVariant :: union{^Game, ^Menu}
 | 
					StateVariant :: union{^Game, ^Menu, ^Pause}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
GameState :: struct {
 | 
					GameState :: struct {
 | 
				
			||||||
	update: proc(state: ^GameState, delta: f32),
 | 
						update: proc(state: ^GameState, delta: f32),
 | 
				
			||||||
	draw: proc(state: ^GameState),
 | 
						draw: proc(state: ^GameState),
 | 
				
			||||||
 | 
						free: proc(state: ^GameState),
 | 
				
			||||||
 | 
						previous: ^GameState,
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
	variant: StateVariant
 | 
						variant: StateVariant
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -41,6 +43,6 @@ stack_pop :: proc() -> (bool) {
 | 
				
			|||||||
		return false
 | 
							return false
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	state := pop(&state_stack)
 | 
						state := pop(&state_stack)
 | 
				
			||||||
	free(state)
 | 
						state->free()
 | 
				
			||||||
	return true
 | 
						return true
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										81
									
								
								tween.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								tween.odin
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,81 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import "core:math"
 | 
				
			||||||
 | 
					import "core:math/ease"
 | 
				
			||||||
 | 
					import "core:math/linalg"
 | 
				
			||||||
 | 
					import "core:slice"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tween :: struct {
 | 
				
			||||||
 | 
						ptr: ^f32,
 | 
				
			||||||
 | 
						from: f32,
 | 
				
			||||||
 | 
						to: f32,
 | 
				
			||||||
 | 
						time: f32,
 | 
				
			||||||
 | 
						duration: f32,
 | 
				
			||||||
 | 
						ease_type: ease.Ease,
 | 
				
			||||||
 | 
						active: bool,
 | 
				
			||||||
 | 
						finished: proc(data: rawptr),
 | 
				
			||||||
 | 
						data: rawptr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TWEEN_SIZE :: 128
 | 
				
			||||||
 | 
					tweens_buf: [TWEEN_SIZE]^Tween
 | 
				
			||||||
 | 
					tweens : [dynamic]^Tween
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tween_init :: proc() {
 | 
				
			||||||
 | 
						tweens = slice.into_dynamic(tweens_buf[:])
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tween_clean :: proc() {
 | 
				
			||||||
 | 
						for tween, i in tweens {
 | 
				
			||||||
 | 
							free(tween)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tween_to :: proc(
 | 
				
			||||||
 | 
							value: ^f32, to: f32, duration: f32,
 | 
				
			||||||
 | 
							ease: ease.Ease = ease.Ease.Quartic_In_Out,
 | 
				
			||||||
 | 
							data: rawptr = nil,
 | 
				
			||||||
 | 
							callback: proc(data: rawptr) = nil
 | 
				
			||||||
 | 
							) -> ^Tween {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tween := new(Tween) 
 | 
				
			||||||
 | 
						tween.ptr = value
 | 
				
			||||||
 | 
						tween.from = value^
 | 
				
			||||||
 | 
						tween.to = to
 | 
				
			||||||
 | 
						tween.duration = duration
 | 
				
			||||||
 | 
						tween.ease_type = ease
 | 
				
			||||||
 | 
						tween.active = true
 | 
				
			||||||
 | 
						tween.data = data
 | 
				
			||||||
 | 
						tween.finished = callback
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						append(&tweens, tween)
 | 
				
			||||||
 | 
						return tween
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tween_cancel :: proc(t: ^Tween) {
 | 
				
			||||||
 | 
						t.active = false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tweens_process :: proc(delta: f32) {
 | 
				
			||||||
 | 
						#reverse for tween, i in tweens {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							tween.time += delta
 | 
				
			||||||
 | 
							p := clamp(tween.time / tween.duration, 0, 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							val := ease.ease(tween.ease_type, p)
 | 
				
			||||||
 | 
							tween.ptr^ = math.lerp(tween.from, tween.to, val)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if tween.time >= tween.duration {
 | 
				
			||||||
 | 
								tween.active = false
 | 
				
			||||||
 | 
								if tween.finished != nil {
 | 
				
			||||||
 | 
									tween.finished(tween.data)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if !tween.active {
 | 
				
			||||||
 | 
								free(tween)
 | 
				
			||||||
 | 
								unordered_remove(&tweens, i)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user