Merge branch 'master' of mx.cogentleman.com:cogentleman/guitarHeroButBetter

This commit is contained in:
Joseph DiMaria 2026-01-31 00:55:07 -08:00
commit b2ad69fadf
13 changed files with 99 additions and 19 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -47,7 +47,23 @@ const int KEY_KEYS[LANE_COUNT] = {
KEY_V, KEY_V,
}; };
struct Note /* Chromatic scale C4B4: 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; float time = 0.0f;
int lane = 0; int lane = 0;
@ -58,22 +74,22 @@ struct Note
} }
}; };
std::vector<Note> default_chart() std::vector<Glyph> default_chart()
{ {
std::vector<Note> notes; std::vector<Glyph> glyphs;
float t = 2.0f; float t = 2.0f;
for (int i = 0; i < 4; i++) for (int i = 0; i < 4; i++)
{ {
for (int lane = 0; lane < LANE_COUNT; lane++) 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.4f;
} }
t += 0.6f; t += 0.6f;
} }
for (int lane = 0; lane < LANE_COUNT; lane++) 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.2f;
} }
t += 0.5f; t += 0.5f;
@ -81,21 +97,25 @@ std::vector<Note> default_chart()
{ {
for (int lane = 0; lane < LANE_COUNT; lane++) 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; t += 0.8f;
} }
return notes; return glyphs;
} }
} // namespace } // namespace
static const char* const GHHB_MUSIC_PATHS[] = {"assets/music/background.ogg", "assets/music/background.mp3"};
class GHHBScene : public Scene class GHHBScene : public Scene
{ {
public: public:
Font font = {0}; Font font = {0};
std::vector<Note> chart = default_chart(); Music music = {0};
std::vector<Note*> spawned; bool music_loaded = false;
std::unordered_set<Note*> completed_notes; std::vector<Glyph> chart = default_chart();
std::vector<Glyph*> spawned;
std::unordered_set<Glyph*> completed_notes;
float song_time = 0.0f; float song_time = 0.0f;
int score = 0; int score = 0;
int combo = 0; int combo = 0;
@ -104,6 +124,8 @@ public:
float screen_width = 0.0f; float screen_width = 0.0f;
float screen_height = 0.0f; float screen_height = 0.0f;
int gamepad_id = 0; 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 PRESS_FLASH_DURATION = 0.12f;
static constexpr float MISS_FLASH_DURATION = 0.15f; static constexpr float MISS_FLASH_DURATION = 0.15f;
float press_flash_timer[LANE_COUNT] = {0}; float press_flash_timer[LANE_COUNT] = {0};
@ -126,6 +148,35 @@ public:
hit_flash_timer[i] = 0.0f; hit_flash_timer[i] = 0.0f;
miss_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 void init() override
@ -136,6 +187,23 @@ public:
screen_height = static_cast<float>(GetScreenHeight()); screen_height = static_cast<float>(GetScreenHeight());
hit_line_y = screen_height - RECEPTOR_HEIGHT / 2.0f; hit_line_y = screen_height - RECEPTOR_HEIGHT / 2.0f;
lane_width = screen_width / LANE_COUNT; 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 float lane_center_x(int lane) const
@ -143,15 +211,15 @@ public:
return (lane + 0.5f) * lane_width; 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); float y = n.y_position(song_time, hit_line_y);
return fabsf(y - hit_line_y) <= HIT_WINDOW_PX; 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()) if (it != spawned.end())
{ {
hit_flash_timer[n->lane] = PRESS_FLASH_DURATION; hit_flash_timer[n->lane] = PRESS_FLASH_DURATION;
@ -173,7 +241,15 @@ public:
return; 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; float last_note_time = 0.0f;
for (const auto& n : chart) for (const auto& n : chart)
@ -198,7 +274,7 @@ public:
continue; continue;
} }
bool already = std::find_if(spawned.begin(), spawned.end(), 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) if (!already && song_time >= n.time - lead_seconds)
{ {
spawned.push_back(&n); spawned.push_back(&n);
@ -207,7 +283,7 @@ public:
for (auto it = spawned.begin(); it != spawned.end();) for (auto it = spawned.begin(); it != spawned.end();)
{ {
Note* n = *it; Glyph* n = *it;
float y = n->y_position(song_time, hit_line_y); float y = n->y_position(song_time, hit_line_y);
if (y > hit_line_y) if (y > hit_line_y)
{ {
@ -244,14 +320,18 @@ public:
if (pressed) if (pressed)
{ {
press_flash_timer[lane] = PRESS_FLASH_DURATION; press_flash_timer[lane] = PRESS_FLASH_DURATION;
if (note_sounds_loaded[lane])
{
PlaySound(note_sounds[lane]);
}
} }
if (!pressed) if (!pressed)
{ {
continue; continue;
} }
Note* best = nullptr; Glyph* best = nullptr;
float best_dist = 1e9f; float best_dist = 1e9f;
for (Note* n : spawned) for (Glyph* n : spawned)
{ {
if (n->lane != lane) if (n->lane != lane)
{ {
@ -334,7 +414,7 @@ public:
} }
DrawLineEx(Vector2{0, hit_line_y}, Vector2{screen_width, hit_line_y}, 3.0f, WHITE); 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); float y = n->y_position(song_time, hit_line_y);
if (y < -40.0f || y > screen_height + 40.0f) if (y < -40.0f || y > screen_height + 40.0f)