1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//! The Event Loop module
//!
//! ## Overview
//!
//! This is the code that handles beginning each frame and ending it. Do not try to add your own game logic in here.
//! The event loop function has its own statemachine (`core_renderer_sm.rs`) that handles the current action.
//!
//! You can think of this as a bit of bootstrap code for the game. All that happens directly here is rendering of the loading screen and a bit of error handling.

use std::cell::RefCell;

use crate::discord::DiscordChannel;
use crate::persistent::save_state::GameSaveState;
use crate::persistent::settings::PersistentGameSettings;
use crate::project_constants::ProjectConstants;
use crate::rendering::core_renderer_sm::{PreloadState, RenderBackendStates};
use crate::rendering::screens::sm_failure_screen;
use crate::scenes::SceneRenderDelegate;
use raylib::audio::RaylibAudio;
use raylib::consts::KeyboardKey;
use raylib::prelude::RaylibDraw;
use raylib::RaylibBuilder;

/// Will begin rendering graphics. Returns when the window closes
pub async fn handle_graphics_blocking<ConfigBuilder>(
    config: ConfigBuilder,
    target_frames_per_second: u32,
    constants: &ProjectConstants,
    discord_signaling: DiscordChannel,
    game_settings: &mut PersistentGameSettings,
    save_state: &mut GameSaveState
) where
    ConfigBuilder: FnOnce(&mut RaylibBuilder),
{
    // Set up the backend rendering state machine
    let mut backend_sm = RenderBackendStates::preload();

    // Let the caller configure Raylib's internal window stuff
    let (mut raylib_handle, raylib_thread) = {
        log::trace!("Configuring Raylib");
        let mut builder = raylib::init();
        config(&mut builder);
        builder.build()
    };

    // Set some important settings on the window
    raylib_handle.set_exit_key(None);
    raylib_handle.set_target_fps(target_frames_per_second);

    // Set up audio
    debug!("Set up Audio");
    let audio_subsystem = RaylibAudio::init_audio_device();
    audio_subsystem.set_master_volume(0.4);

    // Set up the internal screens
    let mut loading_screen = crate::rendering::screens::loading_screen::LoadingScreen::new();
    let mut sm_failure_screen = sm_failure_screen::SmFailureScreen::new();

    // Set up the main render delegate
    let mut render_delegate = SceneRenderDelegate::on_game_start(
        &mut raylib_handle,
        &raylib_thread,
        constants,
        audio_subsystem,
        game_settings,
        save_state,
    );

    // Handle loading the resources and rendering the loading screen
    log::trace!("Running event loop");
    while !raylib_handle.window_should_close() {
        // Handle state machine updates
        match backend_sm {
            RenderBackendStates::Preload(m @ PreloadState::FromInit) => {
                backend_sm = m.finish_preload();
            }
            RenderBackendStates::Loading(ref m) => {
                if loading_screen
                    .render(
                        &mut raylib_handle,
                        &raylib_thread,
                        &discord_signaling,
                        &constants,
                    )
                    .await
                {
                    backend_sm = m.finish_loading();
                }
            }
            _ => break,
        };

        // Tell the profiler that we ended the frame
        profiling::finish_frame!();
    }
    log::info!("Finished loading game");

    // Get access to the global resources
    let global_resources = loading_screen
        .resources
        .expect("Failed to get global resources");

    // Tracker for if we are showing the FPS counter
    let mut show_fps_counter = false;

    // Run the event loop
    while !raylib_handle.window_should_close() {
        // Handle state machine updates
        match backend_sm {
            RenderBackendStates::SmFailed(ref m) => {
                sm_failure_screen
                    .render(
                        &mut raylib_handle,
                        &raylib_thread,
                        &discord_signaling,
                        &constants,
                    )
                    .await;
            }
            RenderBackendStates::RenderGame(ref m) => {
                render_delegate
                    .process_ingame_frame(
                        &mut raylib_handle,
                        &raylib_thread,
                        &discord_signaling,
                        &global_resources,
                        constants,
                        game_settings,
                        save_state,
                    )
                    .await;

                // Handle exiting the game
                if render_delegate.needs_exit {
                    break;
                }
            }
            _ => backend_sm = RenderBackendStates::sm_failed(),
        };

        // Check for F3 being pressed
        if raylib_handle.is_key_pressed(KeyboardKey::KEY_F3) {
            show_fps_counter = !show_fps_counter;
        }

        // Show the FPS counter
        if show_fps_counter {
            raylib_handle.begin_drawing(&raylib_thread).draw_fps(10, 10);
        }

        // Tell the profiler that we ended the frame
        profiling::finish_frame!();
    }
    log::trace!("Event loop ended");
}