diff --git a/.gitignore b/.gitignore index fab7372..71f32ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,10 @@ # This file is used to ignore files which are generated # ---------------------------------------------------------------------------- + +SFML* +CMake* + *~ *.autosave *.a diff --git a/application.cpp b/application.cpp index c9b6e76..343d3e8 100644 --- a/application.cpp +++ b/application.cpp @@ -20,16 +20,26 @@ void Application::run() // BPM of METEOR is 170. // Length is 1:14 // I calculated that the time between beats is about 1412162 microseconds - sf::Int64 iter = 1412162 * 25; + + std::string song_filename = "/home/naiji/METEOR.flac"; + microsec starting_beat_offset = 372162; + int amount_of_beats = 209; + microsec time_between_beats = 1412162; + microsec note_input_offset = 412162; + sf::Int64 iter = starting_beat_offset + (time_between_beats * amount_of_beats); + + Note::setPrecisionQualifier(note_input_offset / 2); + while (iter > 0) { - Note note(iter, iter + 412162); + Note note(iter, note_input_offset); _timeline.push(note); - iter -= 1412162; + iter -= time_between_beats; } + // // // // // // // // - _music.openFromFile("/home/naiji/METEOR.flac"); + _music.openFromFile(song_filename); _music.play(); _music.setVolume(5); @@ -154,7 +164,7 @@ void Application::onKeyPressed(const sf::Keyboard::Key &key) if (arrow != Note::Arrow::NONE) { - _debug.onUserTap(); + _debug.spawnGreenPulse(); if (!_timeline.empty()) { @@ -169,17 +179,29 @@ void Application::update() { const auto microseconds = _music.getPlayingOffset().asMicroseconds(); - if (!_timeline.empty() && _timeline.top().offset() <= microseconds) + // To Do: Here we notice when next note becomes active and ready for user input. + // Here I explicitly calculate its birth time for now. + if (!_timeline.empty() && _timeline.top().offset() - 412162 <= microseconds) { - _debug.onBeat(); + _debug.spawnBluePulse(); } - if (!_timeline.empty() && _timeline.top().deathOffset() <= microseconds) + // To do: Actual note offset should pulse only once and the note shouldn't die right after it, + // because there is also "after pulse" offset, like, you know, player can be a little late + if (!_timeline.empty() && _timeline.top().offset() <= microseconds) { _timeline.pop(); - _debug.onDeath(); + _debug.spawnRedPulse(); } + // To do: Here should be the end of "after pulse" time. When user fucked up all the time and the + // note dies with "Missed" grade. + // + // if ( . . . _timeline.top().offset() + 412162 <= microseconds) + // { + // + // } + _debug.update(microseconds); if (_grade.getFillColor().a > 0) diff --git a/debughelper.cpp b/debughelper.cpp index 55a8077..480c441 100644 --- a/debughelper.cpp +++ b/debughelper.cpp @@ -1,18 +1,11 @@ #include "debughelper.h" DebugHelper::DebugHelper(bool init) : - _toggled(init) + _toggled(init), + _red_pulse({0.f, 0.f}, sf::Color(255, 0, 0)), + _green_pulse({460.f, 0.f}, sf::Color(0, 255, 0)), + _blue_pulse({460.f, 360.f}, sf::Color(0, 100, 255)) { - _bpm_pulse.setSize({460, 360}); - _bpm_pulse.setOrigin(0.f, 0.f); - _bpm_pulse.setFillColor(sf::Color(255, 0, 0, 0)); - _tap_pulse.setSize({460, 360}); - _tap_pulse.move(460.f, 0.f); - _tap_pulse.setFillColor(sf::Color(0, 255, 0, 0)); - _death_pulse.setSize({460, 360}); - _death_pulse.move(460.f, 360.f); - _death_pulse.setFillColor(sf::Color(0, 100, 255, 0)); - _font.loadFromFile("VeraMono.ttf"); _time_print.setFont(_font); @@ -26,59 +19,71 @@ void DebugHelper::toggle() _toggled = !_toggled; } -static bool isVisible(const sf::Shape* shape) -{ - return shape->getFillColor().a > 0; -} - -void DebugHelper::update(const sf::Int64 µseconds) +void DebugHelper::update(const microsec µseconds) { _time_print.setString(std::to_string(microseconds)); - if (isVisible(&_bpm_pulse)) - { - const auto new_alpha = _bpm_pulse.getFillColor().a - 25; - _bpm_pulse.setFillColor(sf::Color(255, 0, 0, (new_alpha < 0) ? 0 : new_alpha)); - } - - if (isVisible(&_tap_pulse)) - { - const auto new_alpha = _tap_pulse.getFillColor().a - 25; - _tap_pulse.setFillColor(sf::Color(0, 255, 0, (new_alpha < 0) ? 0 : new_alpha)); - } - - if (isVisible(&_death_pulse)) - { - const auto new_alpha = _death_pulse.getFillColor().a - 25; - _death_pulse.setFillColor(sf::Color(0, 100, 255, (new_alpha < 0) ? 0 : new_alpha)); - } + _red_pulse.fade(); + _green_pulse.fade(); + _blue_pulse.fade(); } void DebugHelper::drawOn(sf::RenderWindow &game_window) const { if (_toggled) { - game_window.draw(_bpm_pulse); - game_window.draw(_tap_pulse); - game_window.draw(_death_pulse); + _red_pulse.drawOn(game_window); + _green_pulse.drawOn(game_window); + _blue_pulse.drawOn(game_window); game_window.draw(_time_print); } } -void DebugHelper::onUserTap() +void DebugHelper::spawnGreenPulse() +{ + _green_pulse.appear(); +} + +void DebugHelper::spawnRedPulse() +{ + _red_pulse.appear(); +} + +void DebugHelper::spawnBluePulse() +{ + _blue_pulse.appear(); +} + +DebugHelper::Pulse::Pulse(sf::Vector2f position, sf::Color fill_color) { - const sf::Uint8 alpha = 255; - _tap_pulse.setFillColor(sf::Color(255, 0, 0, alpha)); + _pulse_shape.setSize({480, 360}); + _pulse_shape.move(position.x, position.y); + + fill_color.a = 0; + _pulse_shape.setFillColor(fill_color); +} + +void DebugHelper::Pulse::appear() +{ + auto fill_color = _pulse_shape.getFillColor(); + fill_color.a = 255; + _pulse_shape.setFillColor(fill_color); } -void DebugHelper::onBeat() +void DebugHelper::Pulse::fade() { - const sf::Uint8 alpha = 255; - _bpm_pulse.setFillColor(sf::Color(0, 255, 0, alpha)); + auto fill_color = _pulse_shape.getFillColor(); + + if (fill_color.a == 0) + return; + + const auto new_alpha = fill_color.a - 25; + fill_color.a = new_alpha < 0 ? 0 : new_alpha; + + _pulse_shape.setFillColor(fill_color); } -void DebugHelper::onDeath() +void DebugHelper::Pulse::drawOn(sf::RenderWindow &game_window) const { - const sf::Uint8 alpha = 255; - _death_pulse.setFillColor(sf::Color(0, 100, 255, alpha)); + game_window.draw(_pulse_shape); } diff --git a/debughelper.h b/debughelper.h index 348105f..f0f0b0a 100644 --- a/debughelper.h +++ b/debughelper.h @@ -6,25 +6,42 @@ #include #include +using microsec = sf::Int64; + class DebugHelper { public: DebugHelper(bool init = true); void toggle(); - void update(const sf::Int64& microseconds); + void update(const microsec& microseconds); void drawOn(sf::RenderWindow &game_window) const; - void onUserTap(); - void onBeat(); - void onDeath(); + + void spawnGreenPulse(); + void spawnRedPulse(); + void spawnBluePulse(); private: bool _toggled; - sf::RectangleShape _bpm_pulse; - sf::RectangleShape _tap_pulse; - sf::RectangleShape _death_pulse; sf::Font _font; sf::Text _time_print; + + class Pulse + { + public: + Pulse(sf::Vector2f position, sf::Color fill_color); + void appear(); + void fade(); + + void drawOn(sf::RenderWindow &game_window) const; + + private: + sf::RectangleShape _pulse_shape; + }; + + Pulse _red_pulse; + Pulse _green_pulse; + Pulse _blue_pulse; }; #endif // DEBUGHELPER_H diff --git a/note.cpp b/note.cpp index ce865b0..922d438 100644 --- a/note.cpp +++ b/note.cpp @@ -1,20 +1,26 @@ #include "note.h" #include -Note::Note(microsec offset, microsec death_offset, Note::Arrow type) : +Note::Note(microsec offset, microsec life_span_offset, Note::Arrow type) : _offset(offset), - _death_offset(death_offset), + _start_handling_offset(_offset + life_span_offset), + _end_handling_offset(_offset - life_span_offset), _type(type) {} -microsec Note::offset() const noexcept +void Note::setPosition(coordinates position) { - return _offset; + _position = position; +} + +coordinates Note::position() const noexcept +{ + return _position; } -microsec Note::deathOffset() const noexcept +microsec Note::offset() const noexcept { - return _death_offset; + return _offset; } NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const @@ -28,12 +34,25 @@ NoteGrade Note::onTap(Arrow arrow_type, microsec tap_time_stamp) const NoteGrade Note::calculatePrecision(microsec odds) const { - NoteGrade ret; - if (odds < 412162) + NoteGrade ret(0, NoteGrade::Rating::BAD); + + if (odds < _precision_qualifier) { - ret.score = 50; - ret.rating = NoteGrade::Rating::GREAT; + ret = {50, NoteGrade::Rating::GREAT}; } return ret; } + +bool Note::isActive(microsec music_play_offset) const noexcept +{ + return music_play_offset > _start_handling_offset + && music_play_offset < _end_handling_offset; +} + +void Note::setPrecisionQualifier(microsec qualifier) +{ + _precision_qualifier = qualifier; +} + +microsec Note::_precision_qualifier = 500000; // Default initialization as 0.5 second. diff --git a/note.h b/note.h index 0f24dcc..a0484f9 100644 --- a/note.h +++ b/note.h @@ -2,21 +2,29 @@ #define NOTE_H #include +#include + +//////////////////////////////// using microsec = sf::Int64; +using coordinates = sf::Vector2i; struct NoteGrade { - int score = 0; + int score; enum class Rating { WRONG, BAD, GOOD, GREAT - } rating = Rating::BAD; + } rating; + + NoteGrade(int s, Rating r) : score(s), rating(r) {} }; +//////////////////////////////// + class Note { public: @@ -30,17 +38,25 @@ public: NONE }; - Note(microsec offset, microsec death_offset, Note::Arrow type = Note::Arrow::UP); + Note(microsec offset, microsec life_span_offset, Note::Arrow type = Note::Arrow::UP); - NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const; + void setPosition(coordinates position); + coordinates position() const noexcept; microsec offset() const noexcept; - microsec deathOffset() const noexcept; + + NoteGrade onTap(Arrow arrow_type, microsec tap_time_stamp) const; + bool isActive(microsec music_play_offset) const noexcept; + + static void setPrecisionQualifier(microsec qualifier); private: + coordinates _position; microsec _offset; - microsec _death_offset; + microsec _start_handling_offset; + microsec _end_handling_offset; Arrow _type = Arrow::UP; + static microsec _precision_qualifier; NoteGrade calculatePrecision(microsec odds) const; };