2021-04-03 19:14:31 +02:00
|
|
|
#include "application.h"
|
|
|
|
#include <SFML/Graphics/Color.hpp>
|
|
|
|
#include <SFML/Window/Event.hpp>
|
2021-04-04 22:43:12 +02:00
|
|
|
|
|
|
|
const sf::Time TIME_PER_FRAME = sf::seconds(1.f / 60.f);
|
2021-04-03 19:14:31 +02:00
|
|
|
|
|
|
|
Application::Application() :
|
2021-04-05 16:17:57 +02:00
|
|
|
_game_window({1280, 720}, "Test"),
|
|
|
|
_debug(true)
|
2021-04-03 19:14:31 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
_font.loadFromFile("VeraMono.ttf");
|
|
|
|
_grade.setFont(_font);
|
|
|
|
_grade.setPosition(160, 160);
|
|
|
|
_grade.setFillColor(sf::Color(255, 255, 255, 0));
|
|
|
|
_grade.setCharacterSize(35);
|
2021-04-03 19:14:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Application::run()
|
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
// BPM of METEOR is 170.
|
|
|
|
// Length is 1:14
|
|
|
|
// I calculated that the time between beats is about 1412162 microseconds
|
2021-04-06 19:39:29 +02:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
while (iter > 0)
|
2021-04-04 22:43:12 +02:00
|
|
|
{
|
2021-04-06 19:39:29 +02:00
|
|
|
Note note(iter, note_input_offset);
|
2021-04-05 16:17:57 +02:00
|
|
|
_timeline.push(note);
|
2021-04-06 19:39:29 +02:00
|
|
|
iter -= time_between_beats;
|
2021-04-04 22:43:12 +02:00
|
|
|
}
|
2021-04-06 19:39:29 +02:00
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
// // // // // // // //
|
2021-04-04 22:43:12 +02:00
|
|
|
|
2021-04-06 19:39:29 +02:00
|
|
|
_music.openFromFile(song_filename);
|
2021-04-05 16:17:57 +02:00
|
|
|
_music.play();
|
|
|
|
_music.setVolume(5);
|
|
|
|
|
|
|
|
_game_window.display();
|
|
|
|
|
|
|
|
startGameLoop();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static bool isOneFramePassed(const sf::Time& time_since_last_update)
|
|
|
|
{
|
|
|
|
return time_since_last_update >= TIME_PER_FRAME;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::startGameLoop()
|
|
|
|
{
|
2021-04-04 22:43:12 +02:00
|
|
|
sf::Clock timer;
|
|
|
|
sf::Time time_since_last_update = sf::Time::Zero;
|
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
while (_game_window.isOpen())
|
|
|
|
{
|
|
|
|
input();
|
2021-04-03 19:14:31 +02:00
|
|
|
|
2021-04-04 22:43:12 +02:00
|
|
|
time_since_last_update += timer.restart();
|
2021-04-05 16:17:57 +02:00
|
|
|
if (isOneFramePassed(time_since_last_update))
|
2021-04-04 22:43:12 +02:00
|
|
|
{
|
|
|
|
time_since_last_update -= TIME_PER_FRAME;
|
|
|
|
update();
|
|
|
|
draw();
|
|
|
|
}
|
2021-04-03 19:14:31 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
static sf::Text makeGradeString(const NoteGrade::Rating& rating)
|
2021-04-03 19:14:31 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
sf::Text ret;
|
|
|
|
switch (rating)
|
2021-04-03 19:14:31 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
case (NoteGrade::Rating::BAD):
|
|
|
|
ret.setString("BAD");
|
|
|
|
ret.setFillColor(sf::Color(255, 255, 255, 255));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (NoteGrade::Rating::GREAT):
|
|
|
|
ret.setString("GREAT");
|
|
|
|
ret.setFillColor(sf::Color(255, 255, 0, 255));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (NoteGrade::Rating::WRONG):
|
|
|
|
ret.setString("WRONG");
|
|
|
|
ret.setFillColor(sf::Color(120, 120, 120, 255));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (NoteGrade::Rating::GOOD):
|
|
|
|
ret.setString("GOOD");
|
|
|
|
ret.setFillColor(sf::Color(255, 100, 120, 255));
|
|
|
|
break;
|
2021-04-03 19:14:31 +02:00
|
|
|
}
|
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
return ret;
|
|
|
|
}
|
2021-04-04 22:43:12 +02:00
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
void Application::input()
|
|
|
|
{
|
|
|
|
sf::Event event;
|
|
|
|
while (_game_window.pollEvent(event))
|
2021-04-03 19:14:31 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
switch (event.type)
|
|
|
|
{
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (sf::Event::Closed):
|
|
|
|
_game_window.close();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case (sf::Event::KeyPressed):
|
|
|
|
onKeyPressed(event.key.code);
|
|
|
|
break;
|
|
|
|
}
|
2021-04-04 22:43:12 +02:00
|
|
|
}
|
2021-04-05 16:17:57 +02:00
|
|
|
}
|
2021-04-04 22:43:12 +02:00
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
static Note::Arrow keyToArrow(const sf::Keyboard::Key &key)
|
|
|
|
{
|
|
|
|
switch (key)
|
2021-04-04 22:43:12 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
case sf::Keyboard::A:
|
|
|
|
case sf::Keyboard::Left:
|
|
|
|
case sf::Keyboard::Num4:
|
|
|
|
return Note::Arrow::LEFT;
|
|
|
|
|
|
|
|
case sf::Keyboard::W:
|
|
|
|
case sf::Keyboard::Up:
|
|
|
|
case sf::Keyboard::Num8:
|
|
|
|
return Note::Arrow::UP;
|
|
|
|
|
|
|
|
case sf::Keyboard::D:
|
|
|
|
case sf::Keyboard::Right:
|
|
|
|
case sf::Keyboard::Num6:
|
|
|
|
return Note::Arrow::RIGHT;
|
|
|
|
|
|
|
|
case sf::Keyboard::S:
|
|
|
|
case sf::Keyboard::Down:
|
|
|
|
case sf::Keyboard::Num2:
|
|
|
|
return Note::Arrow::DOWN;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return Note::Arrow::NONE;
|
2021-04-04 22:43:12 +02:00
|
|
|
}
|
2021-04-05 16:17:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void Application::onKeyPressed(const sf::Keyboard::Key &key)
|
|
|
|
{
|
|
|
|
if (key == sf::Keyboard::D)
|
|
|
|
{
|
|
|
|
_debug.toggle();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto arrow = keyToArrow(key);
|
|
|
|
|
|
|
|
if (arrow != Note::Arrow::NONE)
|
|
|
|
{
|
2021-04-06 19:39:29 +02:00
|
|
|
_debug.spawnGreenPulse();
|
2021-04-05 16:17:57 +02:00
|
|
|
|
|
|
|
if (!_timeline.empty())
|
|
|
|
{
|
|
|
|
const auto current_note = _timeline.top();
|
|
|
|
const auto grade_result = current_note.onTap(arrow, _music.getPlayingOffset().asMicroseconds());
|
|
|
|
_grade = makeGradeString(grade_result.rating);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::update()
|
|
|
|
{
|
|
|
|
const auto microseconds = _music.getPlayingOffset().asMicroseconds();
|
|
|
|
|
2021-04-06 19:39:29 +02:00
|
|
|
// 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)
|
2021-04-05 16:17:57 +02:00
|
|
|
{
|
2021-04-06 19:39:29 +02:00
|
|
|
_debug.spawnBluePulse();
|
2021-04-05 16:17:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-06 19:39:29 +02:00
|
|
|
// 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)
|
2021-04-05 16:17:57 +02:00
|
|
|
{
|
|
|
|
_timeline.pop();
|
2021-04-06 19:39:29 +02:00
|
|
|
_debug.spawnRedPulse();
|
2021-04-05 16:17:57 +02:00
|
|
|
}
|
|
|
|
|
2021-04-06 19:39:29 +02:00
|
|
|
// 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)
|
|
|
|
// {
|
|
|
|
//
|
|
|
|
// }
|
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
_debug.update(microseconds);
|
2021-04-04 22:43:12 +02:00
|
|
|
|
2021-04-05 16:17:57 +02:00
|
|
|
if (_grade.getFillColor().a > 0)
|
2021-04-04 22:43:12 +02:00
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
const auto alpha = _grade.getFillColor().a - 20;
|
|
|
|
_grade.setFillColor(sf::Color(255, 255, 255, alpha < 0 ? 0 : alpha));
|
2021-04-03 19:14:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void Application::draw()
|
|
|
|
{
|
2021-04-05 16:17:57 +02:00
|
|
|
_game_window.clear();
|
|
|
|
_debug.drawOn(_game_window);
|
|
|
|
_game_window.draw(_grade);
|
|
|
|
_game_window.display();
|
2021-04-03 19:14:31 +02:00
|
|
|
}
|