Merge branch 'Tracks'
# Conflicts: # src/samples/ghhb_game.h
This commit is contained in:
commit
7654d1323c
@ -49,7 +49,6 @@ Song parseSong(const rapidjson::Document& doc) {
|
|||||||
header.name = h.HasMember("name") && h["name"].IsString() ? 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.artist = h.HasMember("artist") && h["artist"].IsString() ? h["artist"].GetString() : "";
|
||||||
header.ppq = h["ppq"].GetInt();
|
header.ppq = h["ppq"].GetInt();
|
||||||
printf("header has tempos: %d\n", h.HasMember("tempos"));
|
|
||||||
if (h.HasMember("tempos") && h["tempos"].IsArray()) {
|
if (h.HasMember("tempos") && h["tempos"].IsArray()) {
|
||||||
const auto& tempos = h["tempos"].GetArray();
|
const auto& tempos = h["tempos"].GetArray();
|
||||||
header.bpm = tempos[0]["bpm"].GetFloat();
|
header.bpm = tempos[0]["bpm"].GetFloat();
|
||||||
|
|||||||
@ -1,20 +1,30 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
inline std::vector<std::string> get_song_catalog()
|
/** -1 = pick track by most notes; 0-based index = use that track for the chart. */
|
||||||
|
using SongCatalogEntry = std::pair<std::string, int>;
|
||||||
|
|
||||||
|
inline std::vector<SongCatalogEntry> get_song_catalog()
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
"assets/songs/json/mary.json",
|
{"assets/songs/json/mary.json", -1},
|
||||||
"assets/songs/json/pallettown.json",
|
{"assets/songs/json/pallettown.json", -1},
|
||||||
"assets/songs/json/tetris.json",
|
{"assets/songs/json/tetris.json", -1},
|
||||||
"assets/songs/json/undertale.json",
|
{"assets/songs/json/undertale.json", 0},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
inline std::string get_default_song_path()
|
inline std::string get_default_song_path()
|
||||||
{
|
{
|
||||||
auto catalog = get_song_catalog();
|
auto catalog = get_song_catalog();
|
||||||
return catalog.empty() ? "" : catalog.front();
|
return catalog.empty() ? "" : catalog.front().first;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline int get_default_track_override()
|
||||||
|
{
|
||||||
|
auto catalog = get_song_catalog();
|
||||||
|
return catalog.empty() ? -1 : catalog.front().second;
|
||||||
}
|
}
|
||||||
|
|||||||
14
src/main.cpp
14
src/main.cpp
@ -13,6 +13,7 @@
|
|||||||
int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
||||||
int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES] = {-1, -1, -1, -1};
|
||||||
std::string SELECTED_SONG_PATH = get_default_song_path();
|
std::string SELECTED_SONG_PATH = get_default_song_path();
|
||||||
|
int SELECTED_TRACK_OVERRIDE = get_default_track_override();
|
||||||
|
|
||||||
Game game;
|
Game game;
|
||||||
|
|
||||||
@ -42,18 +43,9 @@ int main(int argc, char** argv)
|
|||||||
game.add_scene<GHHBScene>("ghhb");
|
game.add_scene<GHHBScene>("ghhb");
|
||||||
|
|
||||||
auto catalog = get_song_catalog();
|
auto catalog = get_song_catalog();
|
||||||
for (const auto& path : catalog)
|
for (const auto& entry : catalog)
|
||||||
{
|
{
|
||||||
song_manager->load_song(path, path);
|
song_manager->load_song(entry.first, entry.first);
|
||||||
}
|
|
||||||
std::string default_path = get_default_song_path();
|
|
||||||
Song& song = song_manager->get_song(default_path);
|
|
||||||
if (!default_path.empty() && song_manager->has_song(default_path))
|
|
||||||
{
|
|
||||||
printf("Song name: %s\n", song.header.name.c_str());
|
|
||||||
printf("Song bpm: %f\n", song.header.bpm);
|
|
||||||
if (!song.tracks.empty() && !song.tracks[0].notes.empty())
|
|
||||||
printf("First note duration: %d\n", song.tracks[0].notes[0].duration_ms);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -23,6 +23,8 @@ constexpr float HIT_ZONE_MARGIN = 20.0f;
|
|||||||
constexpr float SIMULTANEOUS_NOTE_Y_TOLERANCE = 2.0f;
|
constexpr float SIMULTANEOUS_NOTE_Y_TOLERANCE = 2.0f;
|
||||||
constexpr float SCROLL_PX_PER_SEC = 350.0f;
|
constexpr float SCROLL_PX_PER_SEC = 350.0f;
|
||||||
constexpr float LEAD_OFFSET_SECONDS = 3.0f;
|
constexpr float LEAD_OFFSET_SECONDS = 3.0f;
|
||||||
|
constexpr float GLYPH_HEIGHT_FRACTION_OF_LANE = 0.5f;
|
||||||
|
constexpr float MIN_SUSTAIN_FALLBACK_SEC = 0.05f;
|
||||||
|
|
||||||
const int GAMEPAD_BUTTONS[LANE_COUNT] = {
|
const int GAMEPAD_BUTTONS[LANE_COUNT] = {
|
||||||
GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Left
|
GAMEPAD_BUTTON_LEFT_FACE_LEFT, // Left
|
||||||
@ -138,6 +140,7 @@ const char* const INSTRUMENT_LANE_WAV[MAX_INSTRUMENT_TYPES][LANE_COUNT * OCTAVE_
|
|||||||
struct Glyph
|
struct Glyph
|
||||||
{
|
{
|
||||||
float time = 0.0f;
|
float time = 0.0f;
|
||||||
|
float duration_sec = 0.0f;
|
||||||
int lane = 0;
|
int lane = 0;
|
||||||
int instrument_slot = 0;
|
int instrument_slot = 0;
|
||||||
int octave = 0;
|
int octave = 0;
|
||||||
@ -148,50 +151,89 @@ struct Glyph
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Glyph> chart_from_song(const Song& song)
|
struct PendingSound
|
||||||
|
{
|
||||||
|
float play_time = 0.0f;
|
||||||
|
float duration_sec = 0.0f;
|
||||||
|
int lane = 0;
|
||||||
|
int instrument_slot = 0;
|
||||||
|
int octave = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ActiveSustainedSound
|
||||||
|
{
|
||||||
|
float end_time = 0.0f;
|
||||||
|
int lane = 0;
|
||||||
|
int instrument_slot = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
static size_t pick_track_by_note_count(const Song& song)
|
||||||
|
{
|
||||||
|
size_t best = 0;
|
||||||
|
size_t best_count = 0;
|
||||||
|
for (size_t i = 0; i < song.tracks.size(); i++)
|
||||||
|
{
|
||||||
|
size_t n = song.tracks[i].notes.size();
|
||||||
|
if (n > best_count)
|
||||||
|
{
|
||||||
|
best_count = n;
|
||||||
|
best = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return best;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Glyph> chart_from_song(const Song& song, int track_override)
|
||||||
{
|
{
|
||||||
std::vector<Glyph> glyphs;
|
std::vector<Glyph> glyphs;
|
||||||
|
if (song.tracks.empty())
|
||||||
|
return glyphs;
|
||||||
|
|
||||||
|
size_t track_idx;
|
||||||
|
if (track_override >= 0 &&
|
||||||
|
static_cast<size_t>(track_override) < song.tracks.size())
|
||||||
|
{
|
||||||
|
track_idx = static_cast<size_t>(track_override);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
track_idx = pick_track_by_note_count(song);
|
||||||
|
}
|
||||||
|
const Track& track = song.tracks[track_idx];
|
||||||
int ppq = song.header.ppq > 0 ? song.header.ppq : 480;
|
int ppq = song.header.ppq > 0 ? song.header.ppq : 480;
|
||||||
float bpm = song.header.bpm > 0.0f ? song.header.bpm : 120.0f;
|
float bpm = song.header.bpm > 0.0f ? song.header.bpm : 120.0f;
|
||||||
float ticks_per_sec = ppq * (bpm / 60.0f);
|
float ticks_per_sec = ppq * (bpm / 60.0f);
|
||||||
|
|
||||||
std::vector<std::pair<size_t, size_t>> track_note_counts;
|
std::vector<std::pair<int, const Note*>> timed_notes;
|
||||||
for (size_t i = 0; i < song.tracks.size(); i++)
|
|
||||||
track_note_counts.push_back({i, song.tracks[i].notes.size()});
|
|
||||||
|
|
||||||
std::sort(track_note_counts.begin(), track_note_counts.end(),
|
|
||||||
[](const auto& a, const auto& b) { return a.second > b.second; });
|
|
||||||
|
|
||||||
size_t n_used = std::min(static_cast<size_t>(MAX_INSTRUMENT_TYPES), track_note_counts.size());
|
|
||||||
for (size_t slot = 0; slot < n_used; slot++)
|
|
||||||
{
|
|
||||||
size_t track_idx = track_note_counts[slot].first;
|
|
||||||
size_t note_count = track_note_counts[slot].second;
|
|
||||||
const Track& track = song.tracks[track_idx];
|
|
||||||
std::printf("Instrument %zu: \"%s\" (family=%s number=%d) %zu notes\n",
|
|
||||||
slot, track.name.c_str(), track.instrument.family.c_str(),
|
|
||||||
track.instrument.number, note_count);
|
|
||||||
int instrument_slot = static_cast<int>(slot);
|
|
||||||
for (const Note& note : track.notes)
|
for (const Note& note : track.notes)
|
||||||
{
|
{
|
||||||
if (note.midi < 0 || note.midi > 127)
|
if (note.midi >= 0 && note.midi <= 127)
|
||||||
continue;
|
timed_notes.push_back({note.ticks, ¬e});
|
||||||
|
}
|
||||||
|
std::sort(timed_notes.begin(), timed_notes.end(),
|
||||||
|
[](const auto& a, const auto& b) {
|
||||||
|
if (a.first != b.first)
|
||||||
|
return a.first < b.first;
|
||||||
|
return a.second->midi < b.second->midi;
|
||||||
|
});
|
||||||
|
|
||||||
|
int note_index = 0;
|
||||||
|
for (const auto& pair : timed_notes)
|
||||||
|
{
|
||||||
|
const Note& note = *pair.second;
|
||||||
float time_sec = note.ticks / ticks_per_sec;
|
float time_sec = note.ticks / ticks_per_sec;
|
||||||
|
float duration_sec = note.duration_ticks / ticks_per_sec;
|
||||||
int lane = note.midi % LANE_COUNT;
|
int lane = note.midi % LANE_COUNT;
|
||||||
int octave = note.midi % (LANE_COUNT * OCTAVE_COUNT);
|
int octave = note.midi % (LANE_COUNT * OCTAVE_COUNT);
|
||||||
glyphs.push_back(Glyph{time_sec, lane, instrument_slot, octave});
|
int instrument_slot = note_index % MAX_INSTRUMENT_TYPES;
|
||||||
|
glyphs.push_back(Glyph{time_sec, duration_sec, lane, instrument_slot, octave});
|
||||||
|
note_index++;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
std::sort(glyphs.begin(), glyphs.end(),
|
|
||||||
[](const Glyph& a, const Glyph& b) {
|
|
||||||
if (a.time != b.time)
|
|
||||||
return a.time < b.time;
|
|
||||||
return a.lane < b.lane;
|
|
||||||
});
|
|
||||||
return glyphs;
|
return glyphs;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Glyph> load_chart(const char* path)
|
std::vector<Glyph> load_chart(const char* path, int track_override)
|
||||||
{
|
{
|
||||||
std::vector<Glyph> empty;
|
std::vector<Glyph> empty;
|
||||||
std::FILE* fp = std::fopen(path, "rb");
|
std::FILE* fp = std::fopen(path, "rb");
|
||||||
@ -204,7 +246,7 @@ std::vector<Glyph> load_chart(const char* path)
|
|||||||
{
|
{
|
||||||
Song song = parseSong(doc);
|
Song song = parseSong(doc);
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
return chart_from_song(song);
|
return chart_from_song(song, track_override);
|
||||||
}
|
}
|
||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
return empty;
|
return empty;
|
||||||
@ -213,6 +255,7 @@ std::vector<Glyph> load_chart(const char* path)
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
extern std::string SELECTED_SONG_PATH;
|
extern std::string SELECTED_SONG_PATH;
|
||||||
|
extern int SELECTED_TRACK_OVERRIDE;
|
||||||
extern int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES];
|
extern int INSTRUMENT_GAMEPAD_INDEX[MAX_INSTRUMENT_TYPES];
|
||||||
extern int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES];
|
extern int INSTRUMENT_PHYSICAL_GAMEPAD[MAX_INSTRUMENT_TYPES];
|
||||||
|
|
||||||
@ -225,6 +268,8 @@ public:
|
|||||||
std::vector<Glyph> chart;
|
std::vector<Glyph> chart;
|
||||||
std::vector<Glyph*> spawned;
|
std::vector<Glyph*> spawned;
|
||||||
std::unordered_set<Glyph*> completed_notes;
|
std::unordered_set<Glyph*> completed_notes;
|
||||||
|
std::vector<PendingSound> pending_sounds;
|
||||||
|
std::vector<ActiveSustainedSound> active_sustained;
|
||||||
float song_time = 0.0f;
|
float song_time = 0.0f;
|
||||||
float chart_time_offset = 0.0f;
|
float chart_time_offset = 0.0f;
|
||||||
int score = 0;
|
int score = 0;
|
||||||
@ -249,7 +294,7 @@ public:
|
|||||||
|
|
||||||
void on_enter() override
|
void on_enter() override
|
||||||
{
|
{
|
||||||
chart = load_chart(SELECTED_SONG_PATH.c_str());
|
chart = load_chart(SELECTED_SONG_PATH.c_str(), SELECTED_TRACK_OVERRIDE);
|
||||||
float first_note_time = 0.0f;
|
float first_note_time = 0.0f;
|
||||||
if (!chart.empty())
|
if (!chart.empty())
|
||||||
{
|
{
|
||||||
@ -264,6 +309,8 @@ public:
|
|||||||
dev_auto_hit_mode = false;
|
dev_auto_hit_mode = false;
|
||||||
spawned.clear();
|
spawned.clear();
|
||||||
completed_notes.clear();
|
completed_notes.clear();
|
||||||
|
pending_sounds.clear();
|
||||||
|
active_sustained.clear();
|
||||||
for (int i = 0; i < LANE_COUNT; i++)
|
for (int i = 0; i < LANE_COUNT; i++)
|
||||||
{
|
{
|
||||||
press_flash_timer[i] = 0.0f;
|
press_flash_timer[i] = 0.0f;
|
||||||
@ -354,21 +401,40 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void stop_playing_released_notes(int lane)
|
bool is_lane_held_by_instrument(int lane, int instrument_slot) const
|
||||||
{
|
{
|
||||||
|
int physical_id = INSTRUMENT_PHYSICAL_GAMEPAD[instrument_slot];
|
||||||
|
if (physical_id < 0)
|
||||||
|
{
|
||||||
|
if (IsKeyDown(KEY_KEYS[lane]))
|
||||||
|
return true;
|
||||||
for (int i = 0; i < MAX_GAMEPADS; i++)
|
for (int i = 0; i < MAX_GAMEPADS; i++)
|
||||||
{
|
{
|
||||||
if (IsGamepadAvailable(i) && IsGamepadButtonDown(i, GAMEPAD_BUTTONS[lane]))
|
if (IsGamepadAvailable(i) && IsGamepadButtonDown(i, GAMEPAD_BUTTONS[lane]))
|
||||||
{
|
return true;
|
||||||
printf("Button held: [%d][%d]\n", lane, i);
|
}
|
||||||
continue;
|
return false;
|
||||||
|
}
|
||||||
|
return IsGamepadAvailable(physical_id) &&
|
||||||
|
IsGamepadButtonDown(physical_id, GAMEPAD_BUTTONS[lane]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!note_sounds_playing[lane][i].empty())
|
void stop_playing_released_notes(int lane)
|
||||||
{
|
{
|
||||||
printf("Stopping sound: [%d][%d]\n", lane, i);
|
for (int slot = 0; slot < MAX_INSTRUMENT_TYPES; slot++)
|
||||||
StopSound(note_sounds_playing[lane][i].front());
|
{
|
||||||
note_sounds_playing[lane][i].pop_front();
|
if (is_lane_held_by_instrument(lane, slot))
|
||||||
|
continue;
|
||||||
|
if (!note_sounds_playing[lane][slot].empty())
|
||||||
|
{
|
||||||
|
StopSound(note_sounds_playing[lane][slot].front());
|
||||||
|
note_sounds_playing[lane][slot].pop_front();
|
||||||
|
auto it = std::find_if(active_sustained.begin(), active_sustained.end(),
|
||||||
|
[lane, slot](const ActiveSustainedSound& a) {
|
||||||
|
return a.lane == lane && a.instrument_slot == slot;
|
||||||
|
});
|
||||||
|
if (it != active_sustained.end())
|
||||||
|
active_sustained.erase(it);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -436,12 +502,10 @@ public:
|
|||||||
hit_flash_timer[n->lane] = PRESS_FLASH_DURATION;
|
hit_flash_timer[n->lane] = PRESS_FLASH_DURATION;
|
||||||
spawned.erase(it);
|
spawned.erase(it);
|
||||||
completed_notes.insert(n);
|
completed_notes.insert(n);
|
||||||
printf("note lane: %d, note octave: %d\n", n->lane, n->octave);
|
float hit_line_time = n->time + chart_time_offset;
|
||||||
if (note_sounds_loaded[n->instrument_slot][n->octave]) {
|
if (note_sounds_loaded[n->instrument_slot][n->octave])
|
||||||
Sound sound = note_sounds[n->instrument_slot][n->octave];
|
pending_sounds.push_back(
|
||||||
PlaySound(sound);
|
{hit_line_time, n->duration_sec, n->lane, n->instrument_slot, n->octave});
|
||||||
note_sounds_playing[n->lane][n->instrument_slot].push_back(sound);
|
|
||||||
}
|
|
||||||
float y_n = glyph_y(*n);
|
float y_n = glyph_y(*n);
|
||||||
for (auto it2 = spawned.begin(); it2 != spawned.end();)
|
for (auto it2 = spawned.begin(); it2 != spawned.end();)
|
||||||
{
|
{
|
||||||
@ -450,6 +514,10 @@ public:
|
|||||||
fabsf(glyph_y(*other) - y_n) <= SIMULTANEOUS_NOTE_Y_TOLERANCE)
|
fabsf(glyph_y(*other) - y_n) <= SIMULTANEOUS_NOTE_Y_TOLERANCE)
|
||||||
{
|
{
|
||||||
completed_notes.insert(other);
|
completed_notes.insert(other);
|
||||||
|
float other_hit_line_time = other->time + chart_time_offset;
|
||||||
|
if (note_sounds_loaded[other->instrument_slot][other->octave])
|
||||||
|
pending_sounds.push_back({other_hit_line_time, other->duration_sec,
|
||||||
|
other->lane, other->instrument_slot, other->octave});
|
||||||
it2 = spawned.erase(it2);
|
it2 = spawned.erase(it2);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -497,6 +565,44 @@ public:
|
|||||||
song_time += delta_time;
|
song_time += delta_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float glyph_height_px = lane_width * GLYPH_HEIGHT_FRACTION_OF_LANE;
|
||||||
|
float time_per_glyph_height =
|
||||||
|
glyph_height_px > 0.f ? glyph_height_px / SCROLL_PX_PER_SEC : MIN_SUSTAIN_FALLBACK_SEC;
|
||||||
|
for (auto it = pending_sounds.begin(); it != pending_sounds.end();)
|
||||||
|
{
|
||||||
|
if (song_time >= it->play_time)
|
||||||
|
{
|
||||||
|
Sound s = note_sounds[it->instrument_slot][it->octave];
|
||||||
|
PlaySound(s);
|
||||||
|
note_sounds_playing[it->lane][it->instrument_slot].push_back(s);
|
||||||
|
float sustain_sec = std::max(it->duration_sec, time_per_glyph_height);
|
||||||
|
active_sustained.push_back(
|
||||||
|
{it->play_time + sustain_sec, it->lane, it->instrument_slot});
|
||||||
|
it = pending_sounds.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto it = active_sustained.begin(); it != active_sustained.end();)
|
||||||
|
{
|
||||||
|
if (song_time >= it->end_time)
|
||||||
|
{
|
||||||
|
if (!note_sounds_playing[it->lane][it->instrument_slot].empty())
|
||||||
|
{
|
||||||
|
StopSound(note_sounds_playing[it->lane][it->instrument_slot].front());
|
||||||
|
note_sounds_playing[it->lane][it->instrument_slot].pop_front();
|
||||||
|
}
|
||||||
|
it = active_sustained.erase(it);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
++it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float last_note_time = 0.0f;
|
float last_note_time = 0.0f;
|
||||||
for (const auto& n : chart)
|
for (const auto& n : chart)
|
||||||
{
|
{
|
||||||
@ -738,7 +844,7 @@ public:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
float slice_width = lane_width / static_cast<float>(instrument_count);
|
float slice_width = lane_width / static_cast<float>(instrument_count);
|
||||||
float glyph_height = lane_width / 2.0f;
|
float glyph_height = lane_width * GLYPH_HEIGHT_FRACTION_OF_LANE;
|
||||||
int column = 0;
|
int column = 0;
|
||||||
for (size_t g = 0; g < group.size();)
|
for (size_t g = 0; g < group.size();)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -8,19 +8,23 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
extern std::string SELECTED_SONG_PATH;
|
extern std::string SELECTED_SONG_PATH;
|
||||||
|
extern int SELECTED_TRACK_OVERRIDE;
|
||||||
|
|
||||||
struct SongEntry
|
struct SongEntry
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string artist;
|
std::string artist;
|
||||||
std::string path;
|
std::string path;
|
||||||
|
int track_override;
|
||||||
};
|
};
|
||||||
|
|
||||||
inline std::vector<SongEntry> build_song_list(const std::vector<std::string>& paths)
|
inline std::vector<SongEntry> build_song_list(const std::vector<SongCatalogEntry>& catalog)
|
||||||
{
|
{
|
||||||
std::vector<SongEntry> entries;
|
std::vector<SongEntry> entries;
|
||||||
for (const std::string& path : paths)
|
for (const auto& entry : catalog)
|
||||||
{
|
{
|
||||||
|
const std::string& path = entry.first;
|
||||||
|
int track_override = entry.second;
|
||||||
std::FILE* fp = std::fopen(path.c_str(), "rb");
|
std::FILE* fp = std::fopen(path.c_str(), "rb");
|
||||||
if (!fp)
|
if (!fp)
|
||||||
continue;
|
continue;
|
||||||
@ -35,7 +39,8 @@ inline std::vector<SongEntry> build_song_list(const std::vector<std::string>& pa
|
|||||||
std::fclose(fp);
|
std::fclose(fp);
|
||||||
Song song = parseSong(doc);
|
Song song = parseSong(doc);
|
||||||
entries.push_back(
|
entries.push_back(
|
||||||
{song.header.name.empty() ? path : song.header.name, song.header.artist, path});
|
{song.header.name.empty() ? path : song.header.name, song.header.artist, path,
|
||||||
|
track_override});
|
||||||
}
|
}
|
||||||
while (entries.size() < 4)
|
while (entries.size() < 4)
|
||||||
{
|
{
|
||||||
@ -116,6 +121,7 @@ public:
|
|||||||
if (idx >= 0 && idx < static_cast<int>(songs.size()))
|
if (idx >= 0 && idx < static_cast<int>(songs.size()))
|
||||||
{
|
{
|
||||||
SELECTED_SONG_PATH = songs[idx].path;
|
SELECTED_SONG_PATH = songs[idx].path;
|
||||||
|
SELECTED_TRACK_OVERRIDE = songs[idx].track_override;
|
||||||
game->go_to_scene("instrument_select");
|
game->go_to_scene("instrument_select");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user