diff --git a/assets/sounds/nes_harp/nes_harp_A4.wav b/assets/sounds/nes_harp/nes_harp_A4.wav new file mode 100644 index 0000000..7df5c8b Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_A4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_As4.wav b/assets/sounds/nes_harp/nes_harp_As4.wav new file mode 100644 index 0000000..cbde0e4 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_As4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_B4.wav b/assets/sounds/nes_harp/nes_harp_B4.wav new file mode 100644 index 0000000..6d6bb79 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_B4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_C4.wav b/assets/sounds/nes_harp/nes_harp_C4.wav new file mode 100644 index 0000000..6ff4a6b Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_C4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_Cs4.wav b/assets/sounds/nes_harp/nes_harp_Cs4.wav new file mode 100644 index 0000000..a11a3f3 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_Cs4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_D4.wav b/assets/sounds/nes_harp/nes_harp_D4.wav new file mode 100644 index 0000000..befee8e Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_D4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_Ds4.wav b/assets/sounds/nes_harp/nes_harp_Ds4.wav new file mode 100644 index 0000000..0239da2 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_Ds4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_E4.wav b/assets/sounds/nes_harp/nes_harp_E4.wav new file mode 100644 index 0000000..19733ec Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_E4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_F4.wav b/assets/sounds/nes_harp/nes_harp_F4.wav new file mode 100644 index 0000000..ca929c3 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_F4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_Fs4.wav b/assets/sounds/nes_harp/nes_harp_Fs4.wav new file mode 100644 index 0000000..311dcb3 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_Fs4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_G4.wav b/assets/sounds/nes_harp/nes_harp_G4.wav new file mode 100644 index 0000000..b105d89 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_G4.wav differ diff --git a/assets/sounds/nes_harp/nes_harp_Gs4.wav b/assets/sounds/nes_harp/nes_harp_Gs4.wav new file mode 100644 index 0000000..9321050 Binary files /dev/null and b/assets/sounds/nes_harp/nes_harp_Gs4.wav differ diff --git a/src/samples/ghhb_game.h b/src/samples/ghhb_game.h index ae8aafd..86fb8e5 100644 --- a/src/samples/ghhb_game.h +++ b/src/samples/ghhb_game.h @@ -47,7 +47,23 @@ const int KEY_KEYS[LANE_COUNT] = { KEY_V, }; -struct Note +/* Chromatic scale C4–B4: one .wav per lane/button (assets/sounds/nes_harp, copied to build output). */ +const char* const LANE_NOTE_WAV[LANE_COUNT] = { + "assets/sounds/nes_harp/nes_harp_C4.wav", + "assets/sounds/nes_harp/nes_harp_Cs4.wav", + "assets/sounds/nes_harp/nes_harp_D4.wav", + "assets/sounds/nes_harp/nes_harp_Ds4.wav", + "assets/sounds/nes_harp/nes_harp_E4.wav", + "assets/sounds/nes_harp/nes_harp_F4.wav", + "assets/sounds/nes_harp/nes_harp_Fs4.wav", + "assets/sounds/nes_harp/nes_harp_G4.wav", + "assets/sounds/nes_harp/nes_harp_Gs4.wav", + "assets/sounds/nes_harp/nes_harp_A4.wav", + "assets/sounds/nes_harp/nes_harp_As4.wav", + "assets/sounds/nes_harp/nes_harp_B4.wav", +}; + +struct Glyph { float time = 0.0f; int lane = 0; @@ -58,22 +74,22 @@ struct Note } }; -std::vector default_chart() +std::vector default_chart() { - std::vector notes; + std::vector glyphs; float t = 2.0f; for (int i = 0; i < 4; i++) { for (int lane = 0; lane < LANE_COUNT; lane++) { - notes.push_back(Note{t, lane}); + glyphs.push_back(Glyph{t, lane}); t += 0.4f; } t += 0.6f; } for (int lane = 0; lane < LANE_COUNT; lane++) { - notes.push_back(Note{t, lane}); + glyphs.push_back(Glyph{t, lane}); t += 0.2f; } t += 0.5f; @@ -81,21 +97,25 @@ std::vector default_chart() { for (int lane = 0; lane < LANE_COUNT; lane++) { - notes.push_back(Note{t + lane * 0.08f, lane}); + glyphs.push_back(Glyph{t + lane * 0.08f, lane}); } t += 0.8f; } - return notes; + return glyphs; } } // namespace +static const char* const GHHB_MUSIC_PATHS[] = {"assets/music/background.ogg", "assets/music/background.mp3"}; + class GHHBScene : public Scene { public: Font font = {0}; - std::vector chart = default_chart(); - std::vector spawned; - std::unordered_set completed_notes; + Music music = {0}; + bool music_loaded = false; + std::vector chart = default_chart(); + std::vector spawned; + std::unordered_set completed_notes; float song_time = 0.0f; int score = 0; int combo = 0; @@ -104,6 +124,8 @@ public: float screen_width = 0.0f; float screen_height = 0.0f; int gamepad_id = 0; + Sound note_sounds[LANE_COUNT] = {0}; + bool note_sounds_loaded[LANE_COUNT] = {false}; static constexpr float PRESS_FLASH_DURATION = 0.12f; static constexpr float MISS_FLASH_DURATION = 0.15f; float press_flash_timer[LANE_COUNT] = {0}; @@ -126,6 +148,35 @@ public: hit_flash_timer[i] = 0.0f; miss_flash_timer[i] = 0.0f; } + if (music_loaded) + { + StopMusicStream(music); + PlayMusicStream(music); + } + } + + void on_exit() override + { + if (music_loaded) + { + StopMusicStream(music); + } + } + + ~GHHBScene() override + { + if (music_loaded) + { + StopMusicStream(music); + UnloadMusicStream(music); + } + for (int i = 0; i < LANE_COUNT; i++) + { + if (note_sounds_loaded[i]) + { + UnloadSound(note_sounds[i]); + } + } } void init() override @@ -136,6 +187,23 @@ public: screen_height = static_cast(GetScreenHeight()); hit_line_y = screen_height - RECEPTOR_HEIGHT / 2.0f; lane_width = screen_width / LANE_COUNT; + for (const char* path : GHHB_MUSIC_PATHS) + { + if (FileExists(path)) + { + music = LoadMusicStream(path); + music_loaded = true; + break; + } + } + for (int i = 0; i < LANE_COUNT; i++) + { + if (FileExists(LANE_NOTE_WAV[i])) + { + note_sounds[i] = LoadSound(LANE_NOTE_WAV[i]); + note_sounds_loaded[i] = true; + } + } } float lane_center_x(int lane) const @@ -143,15 +211,15 @@ public: return (lane + 0.5f) * lane_width; } - bool is_note_hittable(const Note& n) const + bool is_note_hittable(const Glyph& n) const { float y = n.y_position(song_time, hit_line_y); return fabsf(y - hit_line_y) <= HIT_WINDOW_PX; } - void consume_note(Note* n) + void consume_note(Glyph* n) { - auto it = std::find_if(spawned.begin(), spawned.end(), [n](Note* p) { return p == n; }); + auto it = std::find_if(spawned.begin(), spawned.end(), [n](Glyph* p) { return p == n; }); if (it != spawned.end()) { hit_flash_timer[n->lane] = PRESS_FLASH_DURATION; @@ -173,7 +241,15 @@ public: return; } - song_time += delta_time; + if (music_loaded) + { + UpdateMusicStream(music); + song_time = GetMusicTimePlayed(music); + } + else + { + song_time += delta_time; + } float last_note_time = 0.0f; for (const auto& n : chart) @@ -198,7 +274,7 @@ public: continue; } bool already = std::find_if(spawned.begin(), spawned.end(), - [&n](Note* p) { return p == &n; }) != spawned.end(); + [&n](Glyph* p) { return p == &n; }) != spawned.end(); if (!already && song_time >= n.time - lead_seconds) { spawned.push_back(&n); @@ -207,7 +283,7 @@ public: for (auto it = spawned.begin(); it != spawned.end();) { - Note* n = *it; + Glyph* n = *it; float y = n->y_position(song_time, hit_line_y); if (y > hit_line_y) { @@ -244,14 +320,18 @@ public: if (pressed) { press_flash_timer[lane] = PRESS_FLASH_DURATION; + if (note_sounds_loaded[lane]) + { + PlaySound(note_sounds[lane]); + } } if (!pressed) { continue; } - Note* best = nullptr; + Glyph* best = nullptr; float best_dist = 1e9f; - for (Note* n : spawned) + for (Glyph* n : spawned) { if (n->lane != lane) { @@ -334,7 +414,7 @@ public: } DrawLineEx(Vector2{0, hit_line_y}, Vector2{screen_width, hit_line_y}, 3.0f, WHITE); - for (Note* n : spawned) + for (Glyph* n : spawned) { float y = n->y_position(song_time, hit_line_y); if (y < -40.0f || y > screen_height + 40.0f)