diff --git a/src/editor_state.cpp b/src/editor_state.cpp index 10b9f84..3141dc4 100644 --- a/src/editor_state.cpp +++ b/src/editor_state.cpp @@ -427,7 +427,7 @@ Fraction EditorState::get_snap_step() const { return Fraction{1, snap}; }; -void EditorState::display_playfield(OldMarker& marker, Judgement markerEndingState) { +void EditorState::display_playfield(const Marker& marker, Judgement markerEndingState) { ImGui::SetNextWindowSize(ImVec2(400, 400), ImGuiCond_Once); ImGui::SetNextWindowSizeConstraints( ImVec2(0, 0), @@ -467,7 +467,7 @@ void EditorState::display_playfield(OldMarker& marker, Judgement markerEndingSta auto display = VariantVisitor { [&, this](const better::TapNote& tap_note){ auto note_offset = (this->current_time() - this->time_at(tap_note.get_time())); - auto t = marker.at(markerEndingState, note_offset); + const auto t = marker.at(markerEndingState, note_offset); if (t) { ImGui::SetCursorPos({ tap_note.get_position().get_x() * squareSize, @@ -478,6 +478,7 @@ void EditorState::display_playfield(OldMarker& marker, Judgement markerEndingSta ImGui::PopID(); ++ImGuiIndex; } + }, [&, this](const better::LongNote& long_note){ this->playfield.draw_long_note( diff --git a/src/editor_state.hpp b/src/editor_state.hpp index d78e752..f605461 100644 --- a/src/editor_state.hpp +++ b/src/editor_state.hpp @@ -144,7 +144,7 @@ public: Fraction get_snap_step() const; bool show_playfield = true; - void display_playfield(OldMarker& marker, Judgement markerEndingState); + void display_playfield(const Marker& marker, Judgement markerEndingState); bool show_file_properties = false; void display_file_properties(); diff --git a/src/main.cpp b/src/main.cpp index f4a6e0d..3a35d2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,34 +1,34 @@ -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include -#include "imgui_extras.hpp" -#include "src/custom_sfml_audio/synced_sound_streams.hpp" -#include "widgets/blank_screen.hpp" #include "chart_state.hpp" #include "config.hpp" #include "editor_state.hpp" #include "file_dialogs.hpp" #include "history_item.hpp" +#include "imgui_extras.hpp" #include "marker.hpp" #include "mp3_reader.hpp" #include "notifications_queue.hpp" +#include "src/custom_sfml_audio/synced_sound_streams.hpp" #include "utf8_sfml.hpp" #include "utf8_strings.hpp" +#include "widgets/blank_screen.hpp" int main() { // TODO : Make the playfield not appear when there's no chart selected @@ -40,7 +40,7 @@ int main() { auto assets_folder = executable_folder / "assets"; auto settings_folder = executable_folder / "settings"; - config::Config config{settings_folder}; + config::Config config {settings_folder}; sf::RenderWindow window(sf::VideoMode(800, 600), "FEIS"); window.setVerticalSyncEnabled(true); @@ -56,64 +56,61 @@ int main() { if (not std::filesystem::exists(font_path)) { tinyfd_messageBox( "Error", - ("Could not open "+path_to_utf8_encoded_string(font_path)).c_str(), + ("Could not open " + path_to_utf8_encoded_string(font_path)).c_str(), "ok", "error", - 1 - ); + 1); return -1; } ImGuiIO& IO = ImGui::GetIO(); IO.Fonts->Clear(); IO.Fonts->AddFontFromFileTTF( - path_to_utf8_encoded_string(assets_folder / "fonts" / "NotoSans-Medium.ttf").c_str(), - 16.f - ); + path_to_utf8_encoded_string(assets_folder / "fonts" / "NotoSans-Medium.ttf") + .c_str(), + 16.f); ImGui::SFML::UpdateFontTexture(); IO.ConfigWindowsMoveFromTitleBarOnly = true; // Loading markers preview - std::map markerPreviews; + std::map> markers; for (const auto& folder : std::filesystem::directory_iterator(assets_folder / "textures" / "markers")) { - if (folder.is_directory()) { - feis::Texture markerPreview; - markerPreview.load_from_path(folder.path() / "ma15.png"); - markerPreview.setSmooth(true); - markerPreviews.insert({folder, markerPreview}); + try { + const auto marker = load_marker_from(folder); + markers.emplace(folder, marker); + } catch (const std::exception& e) { } } - std::optional default_marker_opt; - try { - default_marker_opt = first_available_marker_in(assets_folder); - } catch (const std::exception& e) { - fmt::print("Couldn't load any marker folder, aborting"); + if (markers.size() == 0) { + fmt::print("Couldn't load any markers, aborting"); return -1; } - OldMarker& default_marker = *default_marker_opt; - std::optional marker_opt; + auto marker = markers.begin()->second; if (config.marker.folder) { - try { - marker_opt = OldMarker(*config.marker.folder); - } catch (const std::exception& e) { - fmt::print("Failed to load marker from preferences"); - marker_opt = default_marker_opt; - config.marker.folder = marker_opt->get_folder(); + const auto folder = *config.marker.folder; + const auto it = markers.find(folder); + if (it != markers.end()) { + marker = it->second; + } else { + try { + const auto new_marker = load_marker_from(folder); + auto [it, _] = markers.emplace(folder, new_marker); + marker = it->second; + } catch (const std::exception& e) { + fmt::print("Failed to load marker from preferences"); + } } - } else { - marker_opt = default_marker_opt; - config.marker.folder = marker_opt->get_folder(); } - OldMarker& marker = *marker_opt; + if (not config.marker.ending_state) { config.marker.ending_state = Judgement::Perfect; } Judgement& markerEndingState = *config.marker.ending_state; - BlankScreen bg{assets_folder}; + BlankScreen bg {assets_folder}; std::optional editor_state; NotificationsQueue notificationsQueue; feis::NewChartDialog newChartDialog; @@ -132,7 +129,8 @@ int main() { switch (event.type) { case sf::Event::Closed: if (editor_state) { - if (editor_state->save_if_needed_and_user_wants_to() != EditorState::SaveOutcome::UserCanceled) { + if (editor_state->save_if_needed_and_user_wants_to() + != EditorState::SaveOutcome::UserCanceled) { window.close(); } } else { @@ -205,8 +203,7 @@ int main() { case sf::Keyboard::Tab: if (editor_state and editor_state->chart_state) { editor_state->chart_state->handle_time_selection_tab( - editor_state->current_exact_beats() - ); + editor_state->current_exact_beats()); } break; @@ -226,9 +223,7 @@ int main() { notificationsQueue.push( std::make_shared(fmt::format( "Music Volume : {}%", - editor_state->get_volume() * 10 - )) - ); + editor_state->get_volume() * 10))); } } else { if (editor_state) { @@ -243,9 +238,7 @@ int main() { notificationsQueue.push( std::make_shared(fmt::format( "Music Volume : {}%", - editor_state->get_volume() * 10 - )) - ); + editor_state->get_volume() * 10))); } } else { if (editor_state) { @@ -258,20 +251,19 @@ int main() { if (event.key.shift) { if (editor_state) { editor_state->speed_down(); - notificationsQueue.push(std::make_shared(fmt::format( - "Speed : {}%", - editor_state->get_speed() * 10 - ))); + notificationsQueue.push( + std::make_shared(fmt::format( + "Speed : {}%", + editor_state->get_speed() * 10))); } } else { if (editor_state and editor_state->chart_state) { - editor_state->snap = Toolbox::getPreviousDivisor(240, editor_state->snap); - notificationsQueue.push( - std::make_shared(fmt::format( - "Snap : {}", - Toolbox::toOrdinal(4 * editor_state->snap) - )) - ); + editor_state->snap = Toolbox::getPreviousDivisor( + 240, + editor_state->snap); + notificationsQueue.push(std::make_shared(fmt::format( + "Snap : {}", + Toolbox::toOrdinal(4 * editor_state->snap)))); } } } @@ -281,20 +273,19 @@ int main() { if (event.key.shift) { if (editor_state) { editor_state->speed_up(); - notificationsQueue.push(std::make_shared(fmt::format( - "Speed : {}%", - editor_state->get_speed() * 10 - ))); + notificationsQueue.push( + std::make_shared(fmt::format( + "Speed : {}%", + editor_state->get_speed() * 10))); } } else { if (editor_state and editor_state->chart_state) { - editor_state->snap = Toolbox::getNextDivisor(240, editor_state->snap); - notificationsQueue.push( - std::make_shared(fmt::format( - "Snap : {}", - Toolbox::toOrdinal(4 * editor_state->snap) - )) - ); + editor_state->snap = Toolbox::getNextDivisor( + 240, + editor_state->snap); + notificationsQueue.push(std::make_shared(fmt::format( + "Snap : {}", + Toolbox::toOrdinal(4 * editor_state->snap)))); } } } @@ -350,13 +341,15 @@ int main() { case sf::Keyboard::Add: if (editor_state) { editor_state->linear_view.zoom_in(); - notificationsQueue.push(std::make_shared("Zoom in")); + notificationsQueue.push(std::make_shared( + "Zoom in")); } break; case sf::Keyboard::Subtract: if (editor_state) { editor_state->linear_view.zoom_out(); - notificationsQueue.push(std::make_shared("Zoom out")); + notificationsQueue.push(std::make_shared( + "Zoom out")); } break; /* @@ -374,7 +367,8 @@ int main() { break; case sf::Keyboard::F: if (editor_state) { - config.editor.show_free_buttons = not config.editor.show_free_buttons; + config.editor.show_free_buttons = + not config.editor.show_free_buttons; } case sf::Keyboard::O: if (event.key.control) { @@ -433,7 +427,8 @@ int main() { switch (event.key.code) { case sf::Keyboard::F: if (editor_state) { - config.editor.show_free_buttons = not config.editor.show_free_buttons; + config.editor.show_free_buttons = + not config.editor.show_free_buttons; } default: break; @@ -454,11 +449,14 @@ int main() { if (editor_state->get_status() == sf::SoundSource::Playing) { editor_state->previous_playback_position = editor_state->playback_position; if (editor_state->has_any_audio()) { - editor_state->playback_position = editor_state->get_precise_playback_position(); + editor_state->playback_position = + editor_state->get_precise_playback_position(); } else { - editor_state->playback_position = editor_state->current_time() + delta * editor_state->get_pitch(); + editor_state->playback_position = editor_state->current_time() + + delta * editor_state->get_pitch(); } - if (editor_state->current_time() > editor_state->get_editable_range().end) { + if (editor_state->current_time() + > editor_state->get_editable_range().end) { editor_state->pause(); } } @@ -472,7 +470,7 @@ int main() { editor_state->display_history(); } if (editor_state->show_playfield) { - editor_state->display_playfield(marker, markerEndingState); + editor_state->display_playfield(*marker, markerEndingState); } if (editor_state->show_linear_view) { editor_state->display_linear_view(); @@ -543,7 +541,8 @@ int main() { if (not editor_state) { editor_state.emplace(assets_folder, config); } else { - if (editor_state->save_if_needed_and_user_wants_to() != EditorState::SaveOutcome::UserCanceled) { + if (editor_state->save_if_needed_and_user_wants_to() + != EditorState::SaveOutcome::UserCanceled) { editor_state.emplace(assets_folder, config); } } @@ -639,7 +638,8 @@ int main() { nullptr, false, editor_state->chart_state.has_value())) { - editor_state->erase_chart_and_push_history(editor_state->chart_state->difficulty_name); + editor_state->erase_chart_and_push_history( + editor_state->chart_state->difficulty_name); } ImGui::EndMenu(); } @@ -674,7 +674,8 @@ int main() { } if (ImGui::MenuItem("90° Counter-Clockwise")) { if (editor_state->chart_state.has_value()) { - editor_state->chart_state->rotate_selection_90_counter_clockwise(notificationsQueue); + editor_state->chart_state->rotate_selection_90_counter_clockwise( + notificationsQueue); } } if (ImGui::MenuItem("180°")) { @@ -685,15 +686,23 @@ int main() { ImGui::EndMenu(); } if (ImGui::BeginMenu("Quantize")) { - if (ImGui::MenuItem(fmt::format("To snap ({})", Toolbox::toOrdinal(4 * editor_state->snap)).c_str())) { + if (ImGui::MenuItem(fmt::format( + "To snap ({})", + Toolbox::toOrdinal(4 * editor_state->snap)) + .c_str())) { if (editor_state->chart_state.has_value()) { - editor_state->chart_state->quantize_selection(editor_state->snap, notificationsQueue); + editor_state->chart_state->quantize_selection( + editor_state->snap, + notificationsQueue); } } - for (const auto& [snap, color]: config.linear_view.quantization_colors.palette) { + for (const auto& [snap, color] : + config.linear_view.quantization_colors.palette) { feis::ColorSquare(color); ImGui::SameLine(); - if (ImGui::MenuItem(fmt::format("To {}##Notes Quantize", Toolbox::toOrdinal(4 * snap)).c_str())) { + if (ImGui::MenuItem( + fmt::format("To {}##Notes Quantize", Toolbox::toOrdinal(4 * snap)) + .c_str())) { if (editor_state->chart_state.has_value()) { editor_state->chart_state->quantize_selection(snap, notificationsQueue); } @@ -752,22 +761,12 @@ int main() { } if (ImGui::BeginMenu("Marker")) { int i = 0; - for (auto& tuple : markerPreviews) { - ImGui::PushID(tuple.first.c_str()); - if (ImGui::ImageButton(tuple.second, {100, 100})) { - try { - marker = OldMarker(tuple.first); - config.marker.folder = marker.get_folder(); - } catch (const std::exception& e) { - tinyfd_messageBox( - "Error", - e.what(), - "ok", - "error", - 1); - marker = default_marker; - config.marker.folder = marker.get_folder(); - } + for (const auto& [path, marker_ptr] : markers) { + ImGui::PushID(path.c_str()); + const auto preview = marker_ptr->approach_preview(); + if (ImGui::ImageButton(preview, {100, 100})) { + marker = marker_ptr; + config.marker.folder = path; } ImGui::PopID(); i++; @@ -779,7 +778,8 @@ int main() { } if (ImGui::BeginMenu("Marker Ending State")) { for (const auto& [judgement, name] : judgement_to_name) { - if (ImGui::ImageButton(marker.preview(judgement), {100, 100})) { + const auto preview = marker->judgement_preview(judgement); + if (ImGui::ImageButton(preview, {100, 100})) { markerEndingState = judgement; } ImGui::SameLine(); diff --git a/src/marker.cpp b/src/marker.cpp index 9ab7186..c42e85c 100644 --- a/src/marker.cpp +++ b/src/marker.cpp @@ -1,20 +1,15 @@ #include "marker.hpp" #include +#include #include +#include #include +#include "sprite_sheet.hpp" #include "utf8_sfml.hpp" #include "utf8_strings.hpp" - -OldMarker first_available_marker_in(const std::filesystem::path& assets_folder) { - for (auto& folder : std::filesystem::directory_iterator(assets_folder / "textures" / "markers")) { - try { - return OldMarker{folder}; - } catch (const std::runtime_error&) {} - } - throw std::runtime_error("No valid marker found"); -} +#include "variant_visitor.hpp" OldMarker::OldMarker(const std::filesystem::path& folder_): fps(30), @@ -59,13 +54,13 @@ OldMarker::OldMarker(const std::filesystem::path& folder_): } }; -OldMarker::optional_texture_reference OldMarker::at(Judgement state, sf::Time offset) const { +std::optional OldMarker::at(Judgement state, sf::Time offset) const { const auto frame = static_cast(std::floor(offset.asSeconds() * fps)); if (frame < 0) { const auto approach_frames = static_cast(approach.size()); const auto index = approach_frames + frame; if (index >= 0 and index < approach_frames) { - return approach.at(index); + return sf::Sprite{approach.at(index)}; } else { return {}; } @@ -73,17 +68,21 @@ OldMarker::optional_texture_reference OldMarker::at(Judgement state, sf::Time of auto& vec = texture_vector_of(state); if (frame < static_cast(vec.size())) { - return std::ref(vec.at(frame)); + return sf::Sprite{vec.at(frame)}; } else { return {}; } } -OldMarker::texture_reference OldMarker::preview(Judgement state) const { - return texture_vector_of(state).at(2); +sf::Sprite OldMarker::judgement_preview(Judgement state) const { + return sf::Sprite{texture_vector_of(state).at(0)}; } -std::filesystem::path OldMarker::get_folder() const { +sf::Sprite OldMarker::approach_preview() const { + return sf::Sprite{approach.back()}; +} + +const std::filesystem::path& OldMarker::get_folder() const { return folder; } @@ -102,4 +101,106 @@ const OldMarker::texture_vector_type& OldMarker::texture_vector_of(Judgement sta default: throw std::invalid_argument("Unexpected judgement value"); } +} + +JujubeMarker::JujubeMarker( + const std::size_t _fps, + const SpriteSheet& _approach, + const SpriteSheet& _perfect, + const SpriteSheet& _great, + const SpriteSheet& _good, + const SpriteSheet& _poor, + const SpriteSheet& _miss, + const std::filesystem::path& _folder +) : + fps(_fps), + approach(_approach), + perfect(_perfect), + great(_great), + good(_good), + poor(_poor), + miss(_miss), + folder(_folder) +{} + + +JujubeMarker JujubeMarker::load_from_folder(const std::filesystem::path& folder) { + nowide::ifstream f{path_to_utf8_encoded_string(folder / "marker.json")}; + const auto j = nlohmann::json::parse(f); + return JujubeMarker{ + j.at("fps").get(), + SpriteSheet::load_from_json(j.at("approach"), folder), + SpriteSheet::load_from_json(j.at("perfect"), folder), + SpriteSheet::load_from_json(j.at("great"), folder), + SpriteSheet::load_from_json(j.at("good"), folder), + SpriteSheet::load_from_json(j.at("poor"), folder), + SpriteSheet::load_from_json(j.at("miss"), folder), + folder + }; +} + +std::optional JujubeMarker::at(Judgement state, sf::Time offset) const { + const auto frame = static_cast(std::floor(offset.asSeconds() * fps)); + if (frame < 0) { + const auto approach_frames = static_cast(approach.size()); + const auto index = approach_frames + frame; + if (index >= 0 and index < approach_frames) { + return approach.at(index); + } else { + return {}; + } + } + + auto& sheet = sprite_sheet_of(state); + if (frame < static_cast(sheet.size())) { + return sheet.at(frame); + } else { + return {}; + } +} + +sf::Sprite JujubeMarker::judgement_preview(Judgement state) const { + return sprite_sheet_of(state).at(0); +} + +sf::Sprite JujubeMarker::approach_preview() const { + return approach.at(approach.size() - 1); +} + +const std::filesystem::path& JujubeMarker::get_folder() const { + return folder; +} + +const SpriteSheet& JujubeMarker::sprite_sheet_of(Judgement state) const { + switch (state) { + case Judgement::Perfect: + return perfect; + case Judgement::Great: + return great; + case Judgement::Good: + return good; + case Judgement::Poor: + return poor; + case Judgement::Miss: + return miss; + default: + throw std::invalid_argument("Unexpected judgement value"); + } +} + +std::shared_ptr load_marker_from(const std::filesystem::path& folder) { + try { + return std::make_shared(folder); + } catch (const std::exception&) {} + + return std::make_shared(std::move(JujubeMarker::load_from_folder(folder))); +} + +std::shared_ptr first_available_marker_in(const std::filesystem::path& assets_folder) { + for (auto& folder : std::filesystem::directory_iterator(assets_folder / "textures" / "markers")) { + try { + return load_marker_from(folder); + } catch (const std::exception&) {} + } + throw std::runtime_error("No valid marker found"); } \ No newline at end of file diff --git a/src/marker.hpp b/src/marker.hpp index 1da9e9a..6a98461 100644 --- a/src/marker.hpp +++ b/src/marker.hpp @@ -1,7 +1,5 @@ #pragma once -#include -#include #include #include #include @@ -9,9 +7,15 @@ #include #include #include +#include #include #include #include +#include + +#include +#include + #include "sprite_sheet.hpp" #include "utf8_sfml_redefinitions.hpp" @@ -43,21 +47,31 @@ const static std::unordered_map name_to_judgement = { * Holds the textures associated with a given marker folder from the assets * folder */ -class OldMarker { +class Marker { public: - using texture_type = feis::Texture; - using texture_vector_type = std::vector; - using texture_reference = std::reference_wrapper; - using optional_texture_reference = std::optional; - - explicit OldMarker(const std::filesystem::path& folder); - optional_texture_reference at(Judgement state, sf::Time offset) const; - texture_reference preview(Judgement state) const; + virtual std::optional at(Judgement state, sf::Time offset) const = 0; + virtual sf::Sprite judgement_preview(Judgement state) const = 0; + virtual sf::Sprite approach_preview() const = 0; + virtual const std::filesystem::path& get_folder() const = 0; - std::filesystem::path get_folder() const; + virtual ~Marker() = default; +}; + +class OldMarker : public Marker { +public: + explicit OldMarker(const std::filesystem::path& folder); + + std::optional at(Judgement state, sf::Time offset) const override; + sf::Sprite judgement_preview(Judgement state) const override; + sf::Sprite approach_preview() const; + + const std::filesystem::path& get_folder() const override; private: unsigned int fps = 30; + using texture_type = feis::Texture; + using texture_vector_type = std::vector; + texture_vector_type approach; texture_vector_type perfect; texture_vector_type great; @@ -66,16 +80,29 @@ private: texture_vector_type miss; const texture_vector_type& texture_vector_of(Judgement state) const; + std::filesystem::path folder; }; -class JujubeMarker { +class JujubeMarker : public Marker { public: - explicit JujubeMarker(const std::filesystem::path& folder); - std::optional at(Judgement state, sf::Time offset) const; - sf::Sprite preview(Judgement state) const; - - std::filesystem::path get_folder() const; + explicit JujubeMarker( + const std::size_t fps, + const SpriteSheet& approach, + const SpriteSheet& perfect, + const SpriteSheet& great, + const SpriteSheet& good, + const SpriteSheet& poor, + const SpriteSheet& miss, + const std::filesystem::path& folder + ); + static JujubeMarker load_from_folder(const std::filesystem::path& folder); + + std::optional at(Judgement state, sf::Time offset) const override; + sf::Sprite judgement_preview(Judgement state) const override; + sf::Sprite approach_preview() const; + + const std::filesystem::path& get_folder() const override; private: std::size_t fps; @@ -85,6 +112,11 @@ private: SpriteSheet good; SpriteSheet poor; SpriteSheet miss; + + const SpriteSheet& sprite_sheet_of(Judgement state) const; + + std::filesystem::path folder; }; -OldMarker first_available_marker_in(const std::filesystem::path& assets_folder); +std::shared_ptr load_marker_from(const std::filesystem::path& folder); +std::shared_ptr first_available_marker_in(const std::filesystem::path& assets_folder); diff --git a/src/playfield.cpp b/src/playfield.cpp index 0fde40b..a25e45d 100644 --- a/src/playfield.cpp +++ b/src/playfield.cpp @@ -195,8 +195,8 @@ void Playfield::draw_long_note( const better::LongNote& note, const sf::Time& playback_position, const better::Timing& timing, - OldMarker& marker, - Judgement& markerEndingState + const Marker& marker, + const Judgement& markerEndingState ) { draw_tail_and_receptor(note, playback_position, timing); @@ -210,28 +210,28 @@ void Playfield::draw_long_note( // Display the beginning marker auto t = marker.at(markerEndingState, note_offset); if (t) { - const float scale = square_size / t->get().getSize().x; - marker_sprite.setTexture(*t, true); - marker_sprite.setScale(scale, scale); - marker_sprite.setPosition( + const float x_scale = square_size / t->getTextureRect().width; + const float y_scale = square_size / t->getTextureRect().height; + t->setScale(x_scale, y_scale); + t->setPosition( note.get_position().get_x() * square_size, note.get_position().get_y() * square_size ); - marker_layer.draw(marker_sprite); + marker_layer.draw(*t); } } else { const auto tail_end_offset = playback_position - tail_end; auto t = marker.at(markerEndingState, tail_end_offset); if (t) { - const float scale = square_size / t->get().getSize().x; - marker_sprite.setTexture(*t, true); - marker_sprite.setScale(scale, scale); - marker_sprite.setPosition( + const float x_scale = square_size / t->getTextureRect().width; + const float y_scale = square_size / t->getTextureRect().height; + t->setScale(x_scale, y_scale); + t->setPosition( note.get_position().get_x() * square_size, note.get_position().get_y() * square_size ); - marker_layer.draw(marker_sprite); + marker_layer.draw(*t); } } } \ No newline at end of file diff --git a/src/playfield.hpp b/src/playfield.hpp index 0714a52..16dfda7 100644 --- a/src/playfield.hpp +++ b/src/playfield.hpp @@ -52,8 +52,8 @@ public: const better::LongNote& note, const sf::Time& playbackPosition, const better::Timing& timing, - OldMarker& marker, - Judgement& markerEndingState + const Marker& marker, + const Judgement& markerEndingState ); private: diff --git a/src/sprite_sheet.cpp b/src/sprite_sheet.cpp index 5bff812..b464af8 100644 --- a/src/sprite_sheet.cpp +++ b/src/sprite_sheet.cpp @@ -4,6 +4,8 @@ #include "fmt/core.h" +#include "utf8_strings.hpp" + SpriteSheet::SpriteSheet( const std::filesystem::path& texture_path, std::size_t count, @@ -61,17 +63,38 @@ SpriteSheet::SpriteSheet( } } - +/* + { + "sprite_sheet": "approach.png", + "count": 16, + "columns": 4, + "rows": 4 + } +*/ SpriteSheet SpriteSheet::load_from_json( - const nlohmann::json& dict, + const nlohmann::json& obj, const std::filesystem::path& parent_folder ) { - + auto texture_path = to_path(obj.at("sprite_sheet").get()); + if (texture_path.is_relative()) { + texture_path = parent_folder / texture_path; + } + + const auto count = obj.at("count").get(); + const auto columns = obj.at("columns").get(); + const auto rows = obj.at("rows").get(); + + return SpriteSheet{ + texture_path, + count, + columns, + rows + }; } -std::optional SpriteSheet::at(std::size_t frame) const { +sf::Sprite SpriteSheet::at(std::size_t frame) const { if (frame >= count) { - return {}; + throw std::out_of_range(fmt::format("frame {} is outside of the SpriteSheet range ({})", frame, count)); } sf::Sprite sprite{tex}; @@ -84,4 +107,8 @@ std::optional SpriteSheet::at(std::size_t frame) const { }; sprite.setTextureRect(rect); return sprite; +} + +std::size_t SpriteSheet::size() const { + return count; } \ No newline at end of file diff --git a/src/sprite_sheet.hpp b/src/sprite_sheet.hpp index 5433741..7fe0b47 100644 --- a/src/sprite_sheet.hpp +++ b/src/sprite_sheet.hpp @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -23,7 +24,8 @@ public: const std::filesystem::path& parent_folder ); - std::optional at(std::size_t frame) const; + sf::Sprite at(std::size_t frame) const; + std::size_t size() const; private: feis::Texture tex; diff --git a/src/utf8_sfml.hpp b/src/utf8_sfml.hpp index 757dbfe..003beab 100644 --- a/src/utf8_sfml.hpp +++ b/src/utf8_sfml.hpp @@ -2,14 +2,12 @@ uses nowide under the hood */ #pragma once -#include #include +#include #include +#include #include -#include -#include -#include #include "utf8_file_input_stream.hpp" @@ -19,7 +17,7 @@ namespace feis { load_from_file() and thus don't need the file stream to remain available after the call to load_from_file() */ template - class LoadFromPathMixin : public T { + class UTF8Loader : public T { public: bool load_from_path(const std::filesystem::path& file) { UTF8FileInputStream f; @@ -30,12 +28,21 @@ namespace feis { } }; + class HoldsFileStream { + public: + virtual ~HoldsFileStream() = default; + protected: + UTF8FileInputStream file_stream; + }; + /* UTF8-aware wrapper around SFML resource classes that "open" files, i.e. resource classes that just store the file stream when open_from_path() is called and stream the file contents on demand and hence require the file stream to remain available for the whole lifetime of the resource */ template - class HoldFileStreamMixin : public T { + class UTF8Streamer : public HoldsFileStream, public T { + /* The file_stream is kept in a base class to make sure it is destroyed + AFTER the SFML class that uses it */ public: bool open_from_path(const std::filesystem::path& file) { if (not file_stream.open(file)) { @@ -43,7 +50,5 @@ namespace feis { }; return this->openFromStream(file_stream); } - protected: - UTF8FileInputStream file_stream; }; } \ No newline at end of file diff --git a/src/utf8_sfml_redefinitions.hpp b/src/utf8_sfml_redefinitions.hpp index 35cb5bf..6491c7a 100644 --- a/src/utf8_sfml_redefinitions.hpp +++ b/src/utf8_sfml_redefinitions.hpp @@ -3,12 +3,13 @@ #include #include #include +#include #include "utf8_sfml.hpp" namespace feis { - using Music = feis::HoldFileStreamMixin; - using InputSoundFile = feis::HoldFileStreamMixin; - using SoundBuffer = feis::LoadFromPathMixin; - using Texture = feis::LoadFromPathMixin; + using Music = feis::UTF8Streamer; + using InputSoundFile = feis::UTF8Streamer; + using SoundBuffer = feis::UTF8Loader; + using Texture = feis::UTF8Loader; } \ No newline at end of file