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
use bones_framework::prelude::kira::{
    sound::{static_sound::StaticSoundSettings, PlaybackState, Region},
    tween::Tween,
};

use crate::{prelude::*, ui::main_menu::MenuPage};

/// The music playback state.
#[derive(HasSchema, Default, PartialEq, Eq)]
#[schema(no_clone)]
pub enum MusicState {
    /// Music is not playing.
    #[default]
    None,
    /// Playing the main menu music.
    MainMenu,
    /// Playing the character select music.
    CharacterSelect,
    /// Playing the credits music.
    Credits,
    /// Playing the fight music.
    Fight {
        /// The index of the song in the shuffled playlist.
        idx: usize,
    },
}

/// Bevy resource containing the in-game music playlist shuffled.
#[derive(HasSchema, Deref, DerefMut, Clone, Default)]
#[repr(C)]
pub struct ShuffledPlaylist(pub SVec<Handle<AudioSource>>);

/// The amount of time to spend fading the music in and out.
pub const MUSIC_FADE_DURATION: Duration = Duration::from_millis(500);

pub const MUSIC_VOLUME: f64 = 0.1;

/// System that plays music according to the game mode.
pub(super) fn music_system(
    meta: Root<GameMeta>,
    mut audio: ResMut<AudioCenter>,
    mut shuffled_fight_music: ResMutInit<ShuffledPlaylist>,
    mut music_state: ResMutInit<MusicState>,
    ctx: Res<EguiCtx>,
    sessions: Res<Sessions>,
) {
    if shuffled_fight_music.is_empty() {
        let mut songs = meta.music.fight.clone();
        THREAD_RNG.with(|rng| rng.shuffle(&mut songs));
        **shuffled_fight_music = songs;
    }

    let tween = Tween {
        start_time: kira::StartTime::Immediate,
        duration: MUSIC_FADE_DURATION,
        easing: kira::tween::Easing::Linear,
    };
    let play_settings = StaticSoundSettings::default()
        .volume(MUSIC_VOLUME)
        .fade_in_tween(tween);

    // If we are in a game
    if sessions.get(SessionNames::GAME).is_some() {
        if let MusicState::Fight { idx } = &mut *music_state {
            if let Some(PlaybackState::Stopped) = audio.music_state() {
                *idx = (*idx + 1) % shuffled_fight_music.len();
                audio.play_music_from_settings(shuffled_fight_music[*idx], play_settings, true);
            }
        } else if let Some(song) = shuffled_fight_music.get(0) {
            audio.play_music_from_settings(*song, play_settings, false);
            *music_state = MusicState::Fight { idx: 0 };
        }

    // If we are on a menu page
    } else if sessions.get(SessionNames::MAIN_MENU).is_some() {
        let menu_page = ctx.get_state::<MenuPage>();
        match menu_page {
            MenuPage::PlayerSelect | MenuPage::MapSelect { .. } | MenuPage::NetworkGame => {
                if *music_state != MusicState::CharacterSelect {
                    audio.play_music_from_settings(
                        meta.music.title_screen,
                        play_settings.loop_region(Region::default()),
                        false,
                    );
                    *music_state = MusicState::CharacterSelect;
                }
            }
            MenuPage::Home | MenuPage::Settings => {
                if *music_state != MusicState::MainMenu {
                    audio.play_music_from_settings(meta.music.title_screen, play_settings, false);
                    *music_state = MusicState::MainMenu;
                }
            }
            MenuPage::Credits => {
                if *music_state != MusicState::Credits {
                    audio.play_music_from_settings(meta.music.credits, play_settings, false);
                    *music_state = MusicState::Credits;
                }
            }
        }
    }
}