Final
This commit is contained in:
		
							
								
								
									
										
											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 | 
							
								
								
									
										20
									
								
								ball.odin
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								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 { | ||||||
| 				brick.hits -= 1 | 				if brick.type != .SOLID{ | ||||||
|  | 					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) | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										46
									
								
								explosion.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								explosion.odin
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
|  |  | ||||||
|  | |||||||
							
								
								
									
										84
									
								
								gameover.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										84
									
								
								gameover.odin
									
									
									
									
									
										Normal file
									
								
							| @ -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 | ||||||
|  | } | ||||||
							
								
								
									
										62
									
								
								howtoplay.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								howtoplay.odin
									
									
									
									
									
										Normal file
									
								
							| @ -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) | ||||||
|  | } | ||||||
							
								
								
									
										79
									
								
								leveldone.odin
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								leveldone.odin
									
									
									
									
									
										Normal file
									
								
							| @ -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] | ||||||
|  | } | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user