package game import "core:fmt" import rl "vendor:raylib" // ------------------------------------------------------------------------------ // LEVEL Specifics // ------------------------------------------------------------------------------ FIELD_BORDER :: 2 SERVE_WAIT_TIME :: 1.0 // ------------------------------------------------------------------------------ // HUD Styling Constants // ------------------------------------------------------------------------------ HUD_FONT_SIZE :: 10.0 HUD_FONT_SPACING :: 1.0 HUD_PADDING :: 4.0 Level :: struct { field_bounds: rl.Rectangle, player: Entity, opponent: Entity, ball: Entity, serve_dir: u8, serve_timer: f32, wait_time: f32, score_player: int, score_enemy: int, score_win: int, particles: [dynamic]Particle, } init_level :: proc() -> Level { pad_x := f32(VIEWPORT_W) * 0.10 pad_y := f32(VIEWPORT_H) * 0.10 bounds := rl.Rectangle{ x = pad_x, y = pad_y, width = f32(VIEWPORT_W) - (pad_x * 2), height = f32(VIEWPORT_H) - (pad_y * 2), } player_ent := init_player() player_ent.pos.x = bounds.x + FIELD_BORDER player_ent.pos.y = bounds.y + (bounds.height / 2) - (player_ent.size.y / 2) opponent_ent := init_opponent() opponent_ent.pos.x = (bounds.x + bounds.width) - (opponent_ent.size.x) - FIELD_BORDER opponent_ent.pos.y = bounds.y + (bounds.height / 2) - (opponent_ent.size.y / 2) return Level{ field_bounds = bounds, player = player_ent, opponent = opponent_ent, ball = init_ball(), serve_timer = SERVE_WAIT_TIME, wait_time = SERVE_WAIT_TIME, score_win = 3, score_player = 0, score_enemy = 0, particles = make([dynamic]Particle), } } // Free memory allocated by dynamic slice fields in the level struct cleanup_level :: proc(level: ^Level) { delete(level.particles) } update_level :: proc(level: ^Level, input: Input_Event, dt: f32) { update_ball(&level.ball, level, dt) update_player(&level.player, input, level.field_bounds, dt) update_opponent(&level.opponent, level.ball, level.field_bounds, dt) update_particles(&level.particles, dt) } render_level :: proc(level: Level) { rl.BeginBlendMode(.ADDITIVE) // Draw outward field glow render_field_outward_glow(level.field_bounds, FIELD_BORDER, rl.WHITE) render_particles(level.particles) rl.EndBlendMode() center_x := f32(VIEWPORT_W) / 2.0 start_pos := rl.Vector2{center_x, level.field_bounds.y + FIELD_BORDER} end_pos := rl.Vector2{center_x, level.field_bounds.y + level.field_bounds.height - FIELD_BORDER} rl.DrawLineEx(start_pos, end_pos, FIELD_BORDER, rl.WHITE) render_player(level.player, level.field_bounds) render_opponent(level.opponent, level.field_bounds) render_ball(level.ball, level.field_bounds) } render_field_outward_glow :: proc(bounds: rl.Rectangle, line_thickness: f32, glow_color: rl.Color) { rl.DrawRectangleLinesEx(bounds, line_thickness, glow_color) glow_layers := 24 glow_step := f32(1.0) for i in 1 ..= glow_layers { offset := f32(i) * glow_step outer_rec := rl.Rectangle{ x = bounds.x - offset, y = bounds.y - offset, width = bounds.width + (offset * 2.0), height = bounds.height + (offset * 2.0), } alpha := u8(155.0 / f32(i * i)) layer_color := rl.Color{glow_color.r, glow_color.g, glow_color.b, alpha} rl.DrawRectangleLinesEx(outer_rec, line_thickness, layer_color) } } render_hud :: proc(level: Level) { font := rl.GetFontDefault() p1_size := rl.MeasureTextEx(font, "PLAYER 1", HUD_FONT_SIZE, HUD_FONT_SPACING) p1_pos := rl.Vector2{HUD_PADDING, HUD_PADDING} rl.DrawTextEx(font, "PLAYER", p1_pos, HUD_FONT_SIZE, HUD_FONT_SPACING, rl.WHITE) opp_size := rl.MeasureTextEx(font, "COMPUTER", HUD_FONT_SIZE, HUD_FONT_SPACING) opp_pos := rl.Vector2{f32(VIEWPORT_W) - HUD_PADDING - opp_size.x, HUD_PADDING} rl.DrawTextEx(font, "COMPUTER", opp_pos, HUD_FONT_SIZE, HUD_FONT_SPACING, rl.WHITE) score_text := fmt.ctprintf("%d : %d", level.score_player, level.score_enemy) score_size := rl.MeasureTextEx(font, score_text, HUD_FONT_SIZE, HUD_FONT_SPACING) score_pos := rl.Vector2{(f32(VIEWPORT_W) * 0.5) - (score_size.x * 0.5), HUD_PADDING} rl.DrawTextEx(font, score_text, score_pos, HUD_FONT_SIZE, HUD_FONT_SPACING, rl.WHITE) }