diff --git a/src/game_scene_manager.odin b/src/game_scene_manager.odin deleted file mode 100644 index c80fe75..0000000 --- a/src/game_scene_manager.odin +++ /dev/null @@ -1,132 +0,0 @@ -package game - -import rl "vendor:raylib" - -Scene :: union { - Scene_Menu, - Scene_Gameplay, -} - -Scene_Menu :: struct { - selected_option: int, -} - -Scene_Gameplay :: struct { - level: Level, -} - -// ------------------------------------------------------------------------------ -// Scene Dispatchers -// ------------------------------------------------------------------------------ -scene_update :: proc(game: ^Game, dt: f32) { - // Must mutate the actual union via pointer - switch &s in game.current_scene { - case Scene_Menu: // update_menu(&s, game, dt) - case Scene_Gameplay: update_gameplay(&s, game, dt) - } -} - -scene_render :: proc(game: ^Game) { - // Value inspect is fine for rendering - switch &s in game.current_scene { - case Scene_Menu: // render_menu(s) - case Scene_Gameplay: render_gameplay(s, game.is_paused) - } -} - -// ------------------------------------------------------------------------------ -// Gameplay Scene -// ------------------------------------------------------------------------------ -init_gameplay_scene :: proc() -> Scene_Gameplay { - return Scene_Gameplay{ - level = init_level(), - } -} - -update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, dt: f32) { - if rl.IsKeyPressed(.P) { - game.is_paused = !game.is_paused - } - - if game.is_paused { - if rl.IsKeyPressed(.Q) { - game.current_scene = Scene_Menu{selected_option = 0} - } - return - } - - // Delegate world updates to level logic - update_level(&scene.level, dt) -} - -render_gameplay :: proc(scene: Scene_Gameplay, is_paused: bool) { - // Delegate world rendering to level logic - render_level(scene.level) - - // Scene UI Overlays - if is_paused { - rl.DrawText("PAUSED", 1200, 700, 40, rl.WHITE) - rl.DrawText("Press Q to Quit to Menu", 1150, 760, 20, rl.LIGHTGRAY) - } -} - -// -Level :: struct { - field_bounds: rl.Rectangle, - player: Entity, - enemy: Entity, - ball: Entity, -} - -init_level :: proc() -> Level { - pad_x := f32(WINDOW_WIDTH) * 0.10 - pad_y := f32(WINDOW_HEIGHT) * 0.10 - - bounds := rl.Rectangle{ - x = pad_x, - y = pad_y, - width = f32(WINDOW_WIDTH) - (pad_x * 2), - height = f32(WINDOW_HEIGHT) - (pad_y * 2), - } - - // player entity - player_ent := init_player() - player_ent.pos.x = bounds.x + 3 - player_ent.pos.y = bounds.y + (bounds.height / 2) - (player_ent.shape_size.y / 2) - - // enemy entity - enemy_ent := init_enemy() - enemy_ent.pos.x = (bounds.width + pad_x) - (enemy_ent.shape_size.x) - 3 - enemy_ent.pos.y = bounds.y + (bounds.height / 2) - (enemy_ent.shape_size.y / 2) - - - return Level{ - field_bounds = bounds, - player = player_ent, - enemy = enemy_ent, - ball = init_ball() - } -} - -update_level :: proc(level: ^Level, dt: f32) { - update_player(dt, &level.player, level) - // update_enemy(...) - // update_ball(...) -} - -render_level :: proc(level: Level) { - // Field Background & Outer Border - rl.DrawRectangleRec(level.field_bounds, rl.DARKGRAY) - rl.DrawRectangleLinesEx(level.field_bounds, 3, rl.RAYWHITE) - - // Center Divide Line - center_x := f32(WINDOW_WIDTH) / 2.0 - start_pos := rl.Vector2{center_x, level.field_bounds.y + 3} - end_pos := rl.Vector2{center_x, level.field_bounds.y + level.field_bounds.height - 3} - rl.DrawLineEx(start_pos, end_pos, 3.0, rl.WHITE) - - // Entities - render_player(level.player, level.field_bounds) - render_enemy(level.enemy, level.field_bounds) - render_ball(level.ball, level.field_bounds) -} \ No newline at end of file diff --git a/src/scene_manager.odin b/src/scene_manager.odin new file mode 100644 index 0000000..e5585a9 --- /dev/null +++ b/src/scene_manager.odin @@ -0,0 +1,155 @@ +package game + +import rl "vendor:raylib" + +Scene :: union { + Scene_Menu, + Scene_Gameplay, +} + +Scene_Menu :: struct { + selected_option: int, +} + +Scene_Gameplay :: struct { + level: Level, +} + +// ------------------------------------------------------------------------------ +// Scene Dispatchers +// ------------------------------------------------------------------------------ +scene_update :: proc(game: ^Game, input: Input_Event, dt: f32) { + switch &s in game.scene { + case Scene_Menu: // update_menu(&s, game, dt) + case Scene_Gameplay: update_gameplay(&s, game, input, dt) + } +} + +scene_render :: proc(game: ^Game) { + switch &s in game.scene { + case Scene_Menu: // render_menu(s) + case Scene_Gameplay: render_gameplay(s, game.viewport, game.paused) + } +} + +// ------------------------------------------------------------------------------ +// Menu Scene +// ------------------------------------------------------------------------------ +// @TODO: +// update_menu :: proc + // if input.move_dir.y < 0 { // UP + // menu.selected_option = max(0, menu.selected_option - 1) + // } else if input.move_dir.y > 0 { // DOWN + // menu.selected_option = min(MAX_OPTIONS - 1, menu.selected_option + 1) + // } + + // if input.confirm_pressed { + // // Execute menu selection + // } + +// ------------------------------------------------------------------------------ +// Gameplay Scene +// ------------------------------------------------------------------------------ +init_gameplay_scene :: proc() -> Scene_Gameplay { + return Scene_Gameplay{ + level = init_level(), + } +} + +update_gameplay :: proc(scene: ^Scene_Gameplay, game: ^Game, input: Input_Event, dt: f32) { + if input.pause_pressed { + game.paused = !game.paused + } + if game.paused do return + + update_level(&scene.level, input, dt) +} + +render_gameplay :: proc(scene: Scene_Gameplay, viewport_texture: rl.RenderTexture2D, is_paused: bool) { + // Offscreen Pass: Render all game objects at low/fixed virtual resolution (1280x720) + rl.BeginTextureMode(viewport_texture) + rl.ClearBackground(rl.BLACK) + + render_level(scene.level) + + if is_paused { + // UI coordinates aligned relative to internal 1280x720 viewport space + rl.DrawRectangleRec(rl.Rectangle{0,0, VIEWPORT_WIDTH, VIEWPORT_HEIGHT}, rl.Color{0,0,0,140}) + rl.DrawText("PAUSED", VIEWPORT_WIDTH / 2 - 80, VIEWPORT_HEIGHT / 2 - 40, 40, rl.WHITE) + rl.DrawText("Press Q to Quit to Menu", VIEWPORT_WIDTH / 2 - 110, VIEWPORT_HEIGHT / 2 + 10, 20, rl.LIGHTGRAY) + } + rl.EndTextureMode() + + // Upscale Pass: Stretch render buffer onto actual window display + render_game_to_window(viewport_texture.texture) +} + +render_game_to_window :: proc(texture: rl.Texture) { + rl.DrawTexturePro( + texture, + // Source rect: OpenGL render targets have inverted Y coordinates (-height flips it right side up) + rl.Rectangle{0, 0, f32(texture.width), -f32(texture.height)}, + // Dest rect: Scales 1280x720 canvas to fill window resolution (2560x1440) + rl.Rectangle{0, 0, f32(WINDOW_WIDTH), f32(WINDOW_HEIGHT)}, + rl.Vector2{}, + 0, + rl.WHITE + ) +} + +// ------------------------------------------------------------------------------ +// LEVEL specifics +// ------------------------------------------------------------------------------ +Level :: struct { + field_bounds: rl.Rectangle, + player: Entity, + enemy: Entity, + ball: Entity, +} + +init_level :: proc() -> Level { + // Positioning based strictly on internal viewport resolution, independent of target display size + pad_x := f32(VIEWPORT_WIDTH) * 0.10 + pad_y := f32(VIEWPORT_HEIGHT) * 0.10 + + bounds := rl.Rectangle{ + x = pad_x, + y = pad_y, + width = f32(VIEWPORT_WIDTH) - (pad_x * 2), + height = f32(VIEWPORT_HEIGHT) - (pad_y * 2), + } + + player_ent := init_player() + player_ent.pos.x = bounds.x + 3 + player_ent.pos.y = bounds.y + (bounds.height / 2) - (player_ent.size.y / 2) + + enemy_ent := init_enemy() + enemy_ent.pos.x = (bounds.x + bounds.width) - (enemy_ent.size.x) - 3 + enemy_ent.pos.y = bounds.y + (bounds.height / 2) - (enemy_ent.size.y / 2) + + return Level{ + field_bounds = bounds, + player = player_ent, + enemy = enemy_ent, + ball = init_ball(), + } +} + +update_level :: proc(level: ^Level, input: Input_Event, dt: f32) { + update_player(&level.player, input, level.field_bounds, dt) + update_ball(&level.ball, level.field_bounds, dt) +} + +render_level :: proc(level: Level) { + rl.DrawRectangleRec(level.field_bounds, rl.DARKGRAY) + rl.DrawRectangleLinesEx(level.field_bounds, 3, rl.RAYWHITE) + + center_x := f32(VIEWPORT_WIDTH) / 2.0 + start_pos := rl.Vector2{center_x, level.field_bounds.y + 3} + end_pos := rl.Vector2{center_x, level.field_bounds.y + level.field_bounds.height - 3} + rl.DrawLineEx(start_pos, end_pos, 3.0, rl.WHITE) + + render_player(level.player, level.field_bounds) + render_enemy(level.enemy, level.field_bounds) + render_ball(level.ball, level.field_bounds) +} \ No newline at end of file