diff --git a/src/samples/ghhb_game.h b/src/samples/ghhb_game.h index 0e23bcb..4f127aa 100644 --- a/src/samples/ghhb_game.h +++ b/src/samples/ghhb_game.h @@ -14,23 +14,27 @@ namespace constexpr int LANE_COUNT = 12; constexpr int MAX_INSTRUMENT_TYPES = 8; constexpr int MAX_GAMEPADS = 4; -constexpr float HIT_WINDOW_PX = 80.0f; -constexpr float RECEPTOR_HEIGHT = 100.0f; +constexpr float RECEPTOR_HEIGHT = 150.0f; constexpr float SCROLL_PX_PER_SEC = 350.0f; +constexpr float LEAD_OFFSET_SECONDS = 3.0f; const int GAMEPAD_BUTTONS[LANE_COUNT] = { - GAMEPAD_BUTTON_LEFT_FACE_LEFT, - GAMEPAD_BUTTON_LEFT_FACE_UP, - GAMEPAD_BUTTON_LEFT_FACE_RIGHT, - GAMEPAD_BUTTON_LEFT_FACE_DOWN, - GAMEPAD_BUTTON_RIGHT_FACE_LEFT, - GAMEPAD_BUTTON_RIGHT_FACE_UP, - GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, - GAMEPAD_BUTTON_RIGHT_FACE_DOWN, - GAMEPAD_BUTTON_LEFT_TRIGGER_2, - GAMEPAD_BUTTON_LEFT_TRIGGER_1, - GAMEPAD_BUTTON_RIGHT_TRIGGER_1, - GAMEPAD_BUTTON_RIGHT_TRIGGER_2, + GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Left + GAMEPAD_BUTTON_LEFT_FACE_UP, // Up + GAMEPAD_BUTTON_LEFT_FACE_RIGHT, // Right + GAMEPAD_BUTTON_LEFT_FACE_DOWN, // Down + GAMEPAD_BUTTON_RIGHT_FACE_LEFT, // X + GAMEPAD_BUTTON_RIGHT_FACE_UP, // Y + GAMEPAD_BUTTON_RIGHT_FACE_RIGHT, // B + GAMEPAD_BUTTON_RIGHT_FACE_DOWN, // A + GAMEPAD_BUTTON_LEFT_TRIGGER_2, // LT + GAMEPAD_BUTTON_LEFT_TRIGGER_1, // LB + GAMEPAD_BUTTON_RIGHT_TRIGGER_1, // RB + GAMEPAD_BUTTON_RIGHT_TRIGGER_2, // RT +}; + +const char* const GAMEPAD_BUTTON_LABELS[LANE_COUNT] = { + "<", "^", ">", "v", "X", "Y", "B", "A", "LT", "LB", "RB", "RT", }; const int KEY_KEYS[LANE_COUNT] = { @@ -187,8 +191,10 @@ public: std::vector spawned; std::unordered_set completed_notes; float song_time = 0.0f; + float chart_time_offset = 0.0f; int score = 0; int combo = 0; + float upper_bar_y = 0.0f; float hit_line_y = 0.0f; float lane_width = 0.0f; float screen_width = 0.0f; @@ -222,6 +228,10 @@ public: { StopMusicStream(music); PlayMusicStream(music); + if (chart_time_offset < 0.0f) + { + SeekMusicStream(music, -chart_time_offset); + } } } @@ -262,6 +272,7 @@ public: font = font_manager->get_font("Roboto"); screen_width = static_cast(GetScreenWidth()); screen_height = static_cast(GetScreenHeight()); + upper_bar_y = screen_height - RECEPTOR_HEIGHT; hit_line_y = screen_height - RECEPTOR_HEIGHT / 2.0f; lane_width = screen_width / LANE_COUNT; for (int slot = 0; slot < MAX_INSTRUMENT_TYPES; slot++) @@ -277,6 +288,13 @@ public: } } chart = load_chart(GHHB_CHART_PATH); + float first_note_time = 0.0f; + if (!chart.empty()) + { + first_note_time = chart.front().time; + float lead_seconds = (hit_line_y - 60.0f) / SCROLL_PX_PER_SEC; + chart_time_offset = lead_seconds + LEAD_OFFSET_SECONDS - first_note_time; + } background = add_game_object(); background->add_tag("background"); } @@ -318,10 +336,15 @@ public: return false; } + float glyph_y(const Glyph& n) const + { + return hit_line_y - (n.time + chart_time_offset - song_time) * SCROLL_PX_PER_SEC; + } + 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; + float y = glyph_y(n); + return y >= upper_bar_y && y <= hit_line_y; } void consume_note(Glyph* n) @@ -363,12 +386,13 @@ public: float last_note_time = 0.0f; for (const auto& n : chart) { - if (n.time > last_note_time) + float t = n.time + chart_time_offset; + if (t > last_note_time) { - last_note_time = n.time; + last_note_time = t; } } - float note_exit_seconds = HIT_WINDOW_PX / SCROLL_PX_PER_SEC; + float note_exit_seconds = (hit_line_y - upper_bar_y) / SCROLL_PX_PER_SEC; if (song_time >= last_note_time + note_exit_seconds + RESULTS_DELAY_AFTER_LAST_NOTE) { game_ended = true; @@ -384,7 +408,7 @@ public: } bool already = std::find_if(spawned.begin(), spawned.end(), [&n](Glyph* p) { return p == &n; }) != spawned.end(); - if (!already && song_time >= n.time - lead_seconds) + if (!already && song_time >= n.time + chart_time_offset - lead_seconds) { spawned.push_back(&n); } @@ -393,7 +417,7 @@ public: for (auto it = spawned.begin(); it != spawned.end();) { Glyph* n = *it; - float y = n->y_position(song_time, hit_line_y); + float y = glyph_y(*n); if (y > hit_line_y) { miss_flash_timer[n->lane] = MISS_FLASH_DURATION; @@ -443,7 +467,7 @@ public: { continue; } - float y = n->y_position(song_time, hit_line_y); + float y = glyph_y(*n); float d = fabsf(y - hit_line_y); if (d < best_dist) { @@ -483,9 +507,8 @@ public: DrawLineEx(Vector2{cx, 0}, Vector2{cx, screen_height}, 2.0f, Color{70, 70, 90, 255}); } - float receptor_top = screen_height - RECEPTOR_HEIGHT; DrawRectangle(0, - static_cast(receptor_top), + static_cast(upper_bar_y), static_cast(screen_width), static_cast(RECEPTOR_HEIGHT), Color{60, 60, 100, 255}); @@ -495,7 +518,7 @@ public: { float alpha = 180.0f * (hit_flash_timer[lane] / PRESS_FLASH_DURATION); DrawRectangle(static_cast(lane * lane_width), - static_cast(receptor_top), + static_cast(upper_bar_y), static_cast(lane_width), static_cast(RECEPTOR_HEIGHT), Color{80, 255, 120, static_cast(alpha)}); @@ -504,7 +527,7 @@ public: { float alpha = 180.0f * (press_flash_timer[lane] / PRESS_FLASH_DURATION); DrawRectangle(static_cast(lane * lane_width), - static_cast(receptor_top), + static_cast(upper_bar_y), static_cast(lane_width), static_cast(RECEPTOR_HEIGHT), Color{255, 255, 255, static_cast(alpha)}); @@ -513,17 +536,30 @@ public: { float alpha = 200.0f * (miss_flash_timer[lane] / MISS_FLASH_DURATION); DrawRectangle(static_cast(lane * lane_width), - static_cast(receptor_top), + static_cast(upper_bar_y), static_cast(lane_width), static_cast(RECEPTOR_HEIGHT), Color{255, 80, 80, static_cast(alpha)}); } } + DrawLineEx(Vector2{0, upper_bar_y}, Vector2{screen_width, upper_bar_y}, 3.0f, WHITE); DrawLineEx(Vector2{0, hit_line_y}, Vector2{screen_width, hit_line_y}, 3.0f, WHITE); + const float button_label_font_size = 28.0f; + const float button_label_y = hit_line_y + 18.0f; + for (int lane = 0; lane < LANE_COUNT; lane++) + { + const char* label = GAMEPAD_BUTTON_LABELS[lane]; + float label_w = MeasureTextEx(font, label, button_label_font_size, 1).x; + float cx = lane_center_x(lane); + DrawTextEx(font, label, + {cx - label_w / 2.0f, button_label_y}, + button_label_font_size, 1, Color{220, 220, 240, 255}); + } + for (Glyph* n : spawned) { - float y = n->y_position(song_time, hit_line_y); + float y = glyph_y(*n); if (y < -40.0f || y > screen_height + 40.0f) { continue; @@ -539,8 +575,8 @@ public: std::string score_text = "Score: " + std::to_string(score) + " Combo: " + std::to_string(combo); DrawTextEx(font, score_text.c_str(), {20, 16}, 28, 1, WHITE); - DrawTextEx(font, "Arrows / D-pad / X Y A B / LB LT RB RT: hit when note reaches white line", - {20, receptor_top - 28}, 18, 1, Color{200, 200, 200, 255}); + DrawTextEx(font, "Arrows / D-pad / X Y A B / LB LT RB RT: hit when note is between the two white lines", + {20, upper_bar_y - 28}, 18, 1, Color{200, 200, 200, 255}); if (game_ended) {