Merge branch 'master' of mx.cogentleman.com:cogentleman/guitarHeroButBetter
This commit is contained in:
commit
19ee890687
@ -2,7 +2,8 @@
|
||||
"header": {
|
||||
"keySignatures": [],
|
||||
"meta": [],
|
||||
"name": "",
|
||||
"name": "Mary Had A Little Lamb",
|
||||
"artist": "Mrs Mary",
|
||||
"ppq": 480,
|
||||
"tempos": [
|
||||
{
|
||||
|
||||
@ -28,6 +28,7 @@ struct Track {
|
||||
|
||||
struct Header {
|
||||
std::string name;
|
||||
std::string artist;
|
||||
int ppq;
|
||||
float bpm;
|
||||
};
|
||||
@ -45,7 +46,8 @@ Song parseSong(const rapidjson::Document& doc) {
|
||||
if (doc.HasMember("header") && doc["header"].IsObject()) {
|
||||
const auto& h = doc["header"].GetObject();
|
||||
Header header;
|
||||
header.name = h["name"].GetString();
|
||||
header.name = h.HasMember("name") && h["name"].IsString() ? h["name"].GetString() : "";
|
||||
header.artist = h.HasMember("artist") && h["artist"].IsString() ? h["artist"].GetString() : "";
|
||||
header.ppq = h["ppq"].GetInt();
|
||||
printf("header has tempos: %d\n", h.HasMember("tempos"));
|
||||
if (h.HasMember("tempos") && h["tempos"].IsArray()) {
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
#include "samples/ghhb_game.h"
|
||||
#include "samples/instrument_select.h"
|
||||
#include "samples/song_select.h"
|
||||
#include "samples/title_screen.h"
|
||||
#include "entities/song.h"
|
||||
|
||||
@ -10,6 +11,7 @@
|
||||
|
||||
int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
||||
int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
||||
std::string SELECTED_SONG_PATH = "assets/songs/json/mary.json";
|
||||
|
||||
Game game;
|
||||
|
||||
@ -34,6 +36,7 @@ int main(int argc, char** argv)
|
||||
font_manager->set_texture_filter("Roboto", TEXTURE_FILTER_BILINEAR);
|
||||
|
||||
game.add_scene<TitleScreen>("title");
|
||||
game.add_scene<SongSelectScreen>("song_select");
|
||||
game.add_scene<InstrumentSelectScreen>("instrument_select");
|
||||
game.add_scene<GHHBScene>("ghhb");
|
||||
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
#include "background.h"
|
||||
#include <algorithm>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
@ -184,10 +185,9 @@ std::vector<Glyph> load_chart(const char* path)
|
||||
return empty;
|
||||
}
|
||||
|
||||
// const char* const GHHB_CHART_PATH = "assets/songs/json/tetris.json";
|
||||
const char* const GHHB_CHART_PATH = "assets/songs/json/mary.json";
|
||||
} // namespace
|
||||
|
||||
extern std::string SELECTED_SONG_PATH;
|
||||
extern int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES];
|
||||
extern int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES];
|
||||
|
||||
@ -222,6 +222,14 @@ public:
|
||||
|
||||
void on_enter() override
|
||||
{
|
||||
chart = load_chart(SELECTED_SONG_PATH.c_str());
|
||||
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;
|
||||
}
|
||||
song_time = 0.0f;
|
||||
score = 0;
|
||||
combo = 0;
|
||||
@ -293,14 +301,6 @@ 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>();
|
||||
background->add_tag("background");
|
||||
}
|
||||
|
||||
@ -79,7 +79,12 @@ public:
|
||||
int slot = BUTTON_SLOT[b];
|
||||
int gi = get_or_assign_gamepad_index(gp);
|
||||
if (gi >= 0)
|
||||
instrument_owner[slot] = gi;
|
||||
{
|
||||
if (instrument_owner[slot] == gi)
|
||||
instrument_owner[slot] = -1;
|
||||
else
|
||||
instrument_owner[slot] = gi;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
187
src/samples/song_select.h
Normal file
187
src/samples/song_select.h
Normal file
@ -0,0 +1,187 @@
|
||||
#pragma once
|
||||
|
||||
#include "engine/prefabs/includes.h"
|
||||
#include "entities/song.h"
|
||||
#include "rapidjson/filereadstream.h"
|
||||
#include <cstdio>
|
||||
#include <vector>
|
||||
|
||||
extern std::string SELECTED_SONG_PATH;
|
||||
|
||||
struct SongEntry
|
||||
{
|
||||
std::string name;
|
||||
std::string artist;
|
||||
std::string path;
|
||||
};
|
||||
|
||||
inline std::vector<SongEntry> build_song_list(const std::vector<std::string>& paths)
|
||||
{
|
||||
std::vector<SongEntry> entries;
|
||||
for (const std::string& path : paths)
|
||||
{
|
||||
std::FILE* fp = std::fopen(path.c_str(), "rb");
|
||||
if (!fp)
|
||||
continue;
|
||||
char read_buf[65536];
|
||||
rapidjson::FileReadStream is(fp, read_buf, sizeof(read_buf));
|
||||
rapidjson::Document doc;
|
||||
if (doc.ParseStream(is).HasParseError())
|
||||
{
|
||||
std::fclose(fp);
|
||||
continue;
|
||||
}
|
||||
std::fclose(fp);
|
||||
Song song = parseSong(doc);
|
||||
entries.push_back(
|
||||
{song.header.name.empty() ? path : song.header.name, song.header.artist, path});
|
||||
}
|
||||
while (entries.size() < 4)
|
||||
{
|
||||
for (size_t i = 0; i < entries.size() && entries.size() < 4; i++)
|
||||
entries.push_back(entries[i]);
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
class SongSelectScreen : public Scene
|
||||
{
|
||||
public:
|
||||
static constexpr int VISIBLE_SLOTS = 4;
|
||||
|
||||
Font font = {0};
|
||||
std::vector<SongEntry> songs;
|
||||
int selected_index = 0;
|
||||
int scroll_offset = 0;
|
||||
|
||||
void init() override
|
||||
{
|
||||
auto font_manager = game->get_manager<FontManager>();
|
||||
font = font_manager->get_font("Roboto");
|
||||
std::vector<std::string> paths = {"assets/songs/json/mary.json"};
|
||||
songs = build_song_list(paths);
|
||||
selected_index = 0;
|
||||
scroll_offset = 0;
|
||||
}
|
||||
|
||||
void update(float delta_time) override
|
||||
{
|
||||
bool up = IsKeyPressed(KEY_UP);
|
||||
bool down = IsKeyPressed(KEY_DOWN);
|
||||
for (int gp = 0; gp < 4; gp++)
|
||||
{
|
||||
if (IsGamepadAvailable(gp))
|
||||
{
|
||||
if (IsGamepadButtonPressed(gp, GAMEPAD_BUTTON_LEFT_FACE_UP))
|
||||
up = true;
|
||||
if (IsGamepadButtonPressed(gp, GAMEPAD_BUTTON_LEFT_FACE_DOWN))
|
||||
down = true;
|
||||
}
|
||||
}
|
||||
int n = static_cast<int>(songs.size());
|
||||
if (up)
|
||||
{
|
||||
if (selected_index > 0)
|
||||
selected_index--;
|
||||
else if (scroll_offset > 0)
|
||||
scroll_offset--;
|
||||
else
|
||||
{
|
||||
scroll_offset = n > VISIBLE_SLOTS ? n - VISIBLE_SLOTS : 0;
|
||||
selected_index = n > VISIBLE_SLOTS ? VISIBLE_SLOTS - 1 : n - 1;
|
||||
}
|
||||
}
|
||||
if (down)
|
||||
{
|
||||
if (selected_index < VISIBLE_SLOTS - 1 && scroll_offset + selected_index < n - 1)
|
||||
selected_index++;
|
||||
else if (scroll_offset + VISIBLE_SLOTS < n)
|
||||
scroll_offset++;
|
||||
else
|
||||
{
|
||||
scroll_offset = 0;
|
||||
selected_index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool confirm = IsKeyPressed(KEY_ENTER);
|
||||
for (int gp = 0; gp < 4; gp++)
|
||||
{
|
||||
if (IsGamepadAvailable(gp) && IsGamepadButtonPressed(gp, GAMEPAD_BUTTON_MIDDLE_RIGHT))
|
||||
confirm = true;
|
||||
}
|
||||
if (confirm && !songs.empty())
|
||||
{
|
||||
int idx = scroll_offset + selected_index;
|
||||
if (idx >= 0 && idx < static_cast<int>(songs.size()))
|
||||
{
|
||||
SELECTED_SONG_PATH = songs[idx].path;
|
||||
game->go_to_scene("instrument_select");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void draw() override
|
||||
{
|
||||
float w = static_cast<float>(GetScreenWidth());
|
||||
float h = static_cast<float>(GetScreenHeight());
|
||||
|
||||
ClearBackground(SKYBLUE);
|
||||
|
||||
const char* title_text = "Select Song";
|
||||
float title_size = 48.0f;
|
||||
float title_w = MeasureTextEx(font, title_text, title_size, 1.0f).x;
|
||||
DrawTextEx(font, title_text, Vector2{(w - title_w) * 0.5f, 60.0f}, title_size, 1.0f, WHITE);
|
||||
|
||||
float row_height = 80.0f;
|
||||
float start_y = 180.0f;
|
||||
float list_center_x = w * 0.5f;
|
||||
float name_size = 28.0f;
|
||||
float artist_size = 18.0f;
|
||||
float artist_offset_x = 0.0f;
|
||||
Color highlight_bg = {60, 80, 120, 220};
|
||||
Color highlight_border = {100, 130, 180, 255};
|
||||
float content_height = name_size + 4.0f + artist_size;
|
||||
float box_pad_v = 6.0f;
|
||||
float highlight_height = content_height + box_pad_v * 2.0f;
|
||||
|
||||
for (int i = 0; i < VISIBLE_SLOTS; i++)
|
||||
{
|
||||
int list_idx = scroll_offset + i;
|
||||
float y = start_y + i * row_height;
|
||||
bool selected = (i == selected_index);
|
||||
|
||||
if (list_idx < static_cast<int>(songs.size()))
|
||||
{
|
||||
const SongEntry& e = songs[list_idx];
|
||||
if (selected)
|
||||
{
|
||||
float box_w = w * 0.45f;
|
||||
float box_x = (w - box_w) * 0.5f;
|
||||
float box_y = y - box_pad_v;
|
||||
DrawRectangleRounded(
|
||||
Rectangle{box_x, box_y, box_w, highlight_height}, 0.2f, 12, highlight_bg);
|
||||
DrawRectangleRoundedLines(
|
||||
Rectangle{box_x, box_y, box_w, highlight_height}, 0.2f, 12,
|
||||
highlight_border);
|
||||
}
|
||||
float name_w = MeasureTextEx(font, e.name.c_str(), name_size, 1.0f).x;
|
||||
DrawTextEx(font, e.name.c_str(),
|
||||
Vector2{list_center_x - name_w * 0.5f, y}, name_size, 1.0f, WHITE);
|
||||
if (!e.artist.empty())
|
||||
{
|
||||
float artist_w = MeasureTextEx(font, e.artist.c_str(), artist_size, 1.0f).x;
|
||||
DrawTextEx(font, e.artist.c_str(),
|
||||
Vector2{list_center_x - artist_w * 0.5f + artist_offset_x,
|
||||
y + name_size + 4.0f},
|
||||
artist_size, 1.0f, Color{200, 200, 200, 255});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const char* hint = "D-Pad Up/Down | Start to confirm";
|
||||
float hint_size = 20.0f;
|
||||
float hint_w = MeasureTextEx(font, hint, hint_size, 1.0f).x;
|
||||
DrawTextEx(font, hint, Vector2{(w - hint_w) * 0.5f, h - 50.0f}, hint_size, 1.0f, WHITE);
|
||||
}
|
||||
};
|
||||
@ -18,7 +18,7 @@ public:
|
||||
// Trigger scene change on Enter key or gamepad start button.
|
||||
if (IsKeyPressed(KEY_ENTER) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_MIDDLE_RIGHT))
|
||||
{
|
||||
game->go_to_scene("instrument_select");
|
||||
game->go_to_scene("song_select");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user