forked from NaiJi/project-kyoku
Separate ClassicNote from its logic to ClassicNoteManager
parent
cf1119c742
commit
a223b7253c
@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/inputtype.h"
|
||||
|
||||
class Note
|
||||
{
|
||||
public:
|
||||
explicit Note(microsec perfect_offset) :
|
||||
_perfect_offset(perfect_offset) {}
|
||||
virtual ~Note() = default;
|
||||
|
||||
virtual bool isActive() const = 0;
|
||||
virtual void update(const microsec& music_offset) = 0;
|
||||
virtual void input(PlayerInput&& inputdata) = 0;
|
||||
virtual void draw() const = 0;
|
||||
|
||||
virtual void putToGame(const microsec &offset) = 0;
|
||||
virtual bool isInGame() const = 0;
|
||||
virtual bool shouldRemove() const = 0;
|
||||
|
||||
microsec offset() const
|
||||
{
|
||||
return _perfect_offset;
|
||||
}
|
||||
|
||||
protected:
|
||||
microsec _perfect_offset;
|
||||
};
|
@ -1,162 +0,0 @@
|
||||
#include "classicnote.h"
|
||||
#include "classicsprite.h"
|
||||
#include "classicgraphicsmanager.h"
|
||||
|
||||
// Replace with interface by dependency injection
|
||||
#include "classicflyinganimationscenario.h"
|
||||
#include "classicdyinganimationscenario.h"
|
||||
//
|
||||
|
||||
ClassicNote::ClassicNote(ClassicNoteInitializer &&init, const std::unique_ptr<ClassicGraphicsManager> &manager) :
|
||||
Note(init.perfect_offset),
|
||||
_evaluator(init.intervals, _perfect_offset),
|
||||
_graphics_manager(manager),
|
||||
_state(State::NONE)
|
||||
{
|
||||
_elements.resize(init.elements.size());
|
||||
_isHold = init.hold;
|
||||
|
||||
for (std::size_t i = 0; i < _elements.size(); ++i)
|
||||
{
|
||||
_elements[i].keys = init.elements[i].keys;
|
||||
_elements[i].coordinates = init.elements[i].coordinates;
|
||||
_elements[i].type = init.elements[i].type;
|
||||
|
||||
// Animations will be injected into note.
|
||||
_elements[i].animations[State::NONE] = nullptr;
|
||||
_elements[i].animations[State::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
|
||||
_elements[i].animations[State::ACTIVE] = _elements[i].animations[State::FLYING];
|
||||
_elements[i].animations[State::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
|
||||
_elements[i].animations[State::DEAD] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicNote::isActive() const
|
||||
{
|
||||
return _state == State::ACTIVE;
|
||||
}
|
||||
|
||||
void ClassicNote::putToGame(const microsec &music_offset)
|
||||
{
|
||||
_state = State::FLYING;
|
||||
|
||||
for (auto& element : _elements)
|
||||
{
|
||||
element.sprite = _graphics_manager->getSprite(element.type);
|
||||
element.sprite->setCoordinates(element.coordinates, 0., 9.);
|
||||
element.animations[_state]->launch(element.sprite, music_offset, offset());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicNote::isInGame() const
|
||||
{
|
||||
return _state == State::FLYING
|
||||
|| _state == State::ACTIVE
|
||||
|| _state == State::DYING;
|
||||
}
|
||||
|
||||
bool ClassicNote::shouldRemove() const
|
||||
{
|
||||
return _state == State::DEAD;
|
||||
}
|
||||
|
||||
void ClassicNote::update(const microsec& music_offset)
|
||||
{
|
||||
switch (_state)
|
||||
{
|
||||
default: return;
|
||||
break;
|
||||
|
||||
case State::FLYING:
|
||||
if (_evaluator.isActive(music_offset))
|
||||
_state = State::ACTIVE;
|
||||
break;
|
||||
|
||||
case State::DYING:
|
||||
if (_elements[0].animations[_state]->isDone())
|
||||
_state = State::DEAD;
|
||||
break;
|
||||
|
||||
case State::ACTIVE:
|
||||
if (!_evaluator.isActive(music_offset))
|
||||
{
|
||||
_state = State::DYING;
|
||||
for (auto& element : _elements)
|
||||
element.animations[_state]->launch(element.sprite, music_offset, offset());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& element : _elements)
|
||||
if (element.animations[_state])
|
||||
element.animations[_state]->update(music_offset);
|
||||
}
|
||||
|
||||
void ClassicNote::input(PlayerInput&& inputdata)
|
||||
{
|
||||
auto grade = ClassicNote::Grade::BAD;
|
||||
|
||||
bool input_valid = std::any_of(_elements.begin(), _elements.end(),
|
||||
[inputdata=inputdata](auto& element)
|
||||
{
|
||||
if (element.pressed)
|
||||
return false;
|
||||
|
||||
auto key_iterator = std::find(element.keys.begin(), element.keys.end(), inputdata.event.key.code);
|
||||
bool found_key = key_iterator != element.keys.end();
|
||||
if (found_key)
|
||||
{
|
||||
element.pressed = true;
|
||||
element.pressed_as = inputdata.event.key.code;
|
||||
}
|
||||
|
||||
return found_key;
|
||||
});
|
||||
|
||||
bool all_pressed = allElementsPressed();
|
||||
|
||||
if (all_pressed)
|
||||
grade = _evaluator.calculatePrecision(inputdata.timestamp);
|
||||
|
||||
if (all_pressed || !input_valid)
|
||||
{
|
||||
_state = State::DYING;
|
||||
for (auto& element : _elements)
|
||||
element.animations[_state]->launch(element.sprite, inputdata.timestamp, offset());
|
||||
}
|
||||
|
||||
std::cout << "User input: " << static_cast<int>(grade) << "\n";
|
||||
}
|
||||
|
||||
void ClassicNote::draw() const
|
||||
{
|
||||
for (std::size_t i = 0; i < _elements.size(); ++i)
|
||||
{
|
||||
if (i >= 1)
|
||||
_graphics_manager->drawLine(_elements[i-1].sprite->trailCoordinates(), _elements[i].sprite->trailCoordinates());
|
||||
_graphics_manager->draw(_elements[i].sprite);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicNote::isHold() const
|
||||
{
|
||||
return _isHold;
|
||||
}
|
||||
|
||||
bool ClassicNote::allElementsPressed() const
|
||||
{
|
||||
return std::all_of(_elements.begin(), _elements.end(),
|
||||
[](const auto& element)
|
||||
{
|
||||
return element.pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool ClassicNote::isPressedAs(sf::Keyboard::Key key) const
|
||||
{
|
||||
return std::any_of(_elements.begin(), _elements.end(),
|
||||
[key=key](const auto& element)
|
||||
{
|
||||
return key == element.pressed_as;
|
||||
});
|
||||
}
|
@ -1,89 +1,56 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/note.h"
|
||||
#include "game/precisionevaluator.h"
|
||||
#include "classicactions.h"
|
||||
#include "game/precisionevaluator.h"
|
||||
|
||||
#include "SFML/Window/Keyboard.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
class ClassicSprite;
|
||||
class ClassicGraphicsManager;
|
||||
class ClassicAnimationScenario;
|
||||
|
||||
class ClassicNote : public Note
|
||||
enum class ClassicGrade
|
||||
{
|
||||
public:
|
||||
|
||||
enum class Grade
|
||||
{
|
||||
PERFECT,
|
||||
GOOD,
|
||||
BAD
|
||||
};
|
||||
|
||||
enum State
|
||||
{
|
||||
NONE,
|
||||
|
||||
FLYING,
|
||||
ACTIVE,
|
||||
DYING,
|
||||
DEAD
|
||||
};
|
||||
|
||||
struct ClassicNoteInitializer
|
||||
{
|
||||
std::vector<microsec> intervals;
|
||||
microsec perfect_offset;
|
||||
bool hold;
|
||||
|
||||
struct Element
|
||||
{
|
||||
Type type;
|
||||
Coordinates coordinates;
|
||||
std::vector<Coordinates> falling_curve_interpolation;
|
||||
std::array<sf::Keyboard::Key, 2> keys;
|
||||
};
|
||||
PERFECT,
|
||||
GOOD,
|
||||
BAD
|
||||
};
|
||||
|
||||
std::vector<Element> elements;
|
||||
};
|
||||
enum ClassicNoteState
|
||||
{
|
||||
NONE,
|
||||
|
||||
explicit ClassicNote(ClassicNoteInitializer&& init, const std::unique_ptr<ClassicGraphicsManager>& manager);
|
||||
virtual ~ClassicNote() = default;
|
||||
FLYING,
|
||||
ACTIVE,
|
||||
DYING,
|
||||
DEAD
|
||||
};
|
||||
|
||||
virtual bool isActive() const override;
|
||||
virtual void update(const microsec &music_offset) override;
|
||||
virtual void input(PlayerInput&& inputdata) override;
|
||||
virtual void putToGame(const microsec &music_offset) override;
|
||||
virtual bool isInGame() const override;
|
||||
virtual bool shouldRemove() const override;
|
||||
virtual void draw() const override;
|
||||
class ClassicSprite;
|
||||
class ClassicAnimationScenario;
|
||||
|
||||
bool isHold() const;
|
||||
bool allElementsPressed() const;
|
||||
bool isPressedAs(sf::Keyboard::Key key) const;
|
||||
struct ClassicNote
|
||||
{
|
||||
PrecisionEvaluator<ClassicGrade> evaluator;
|
||||
std::vector<microsec> intervals;
|
||||
bool hold = false;
|
||||
|
||||
private:
|
||||
ClassicNoteState state = ClassicNoteState::NONE;
|
||||
|
||||
struct NoteElement
|
||||
struct Element
|
||||
{
|
||||
std::shared_ptr<ClassicSprite> sprite;
|
||||
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
|
||||
|
||||
std::array<sf::Keyboard::Key, 2> keys;
|
||||
Coordinates coordinates; // Each note may consist of several buttons.
|
||||
Type type; // For example, ↑ → or ↓ → ←
|
||||
// For example, ↑ → or ↓ → ←
|
||||
// Note Element represents this idea.
|
||||
bool pressed = false; // Each ending button in such sequence
|
||||
sf::Keyboard::Key pressed_as; // is an element.
|
||||
};
|
||||
|
||||
std::vector<NoteElement> _elements;
|
||||
|
||||
const PrecisionEvaluator<Grade> _evaluator;
|
||||
const std::unique_ptr<ClassicGraphicsManager>& _graphics_manager;
|
||||
Type type = Type::NONE;
|
||||
std::vector<Coordinates> falling_curve_interpolation;
|
||||
std::array<sf::Keyboard::Key, 2> available_keys;
|
||||
std::shared_ptr<ClassicSprite> sprite;
|
||||
std::array<std::shared_ptr<ClassicAnimationScenario>, 5> animations;
|
||||
};
|
||||
|
||||
State _state;
|
||||
bool _isHold;
|
||||
std::vector<Element> elements;
|
||||
};
|
||||
|
@ -0,0 +1,149 @@
|
||||
#include "classicnotemanager.h"
|
||||
#include "classicsprite.h"
|
||||
#include "classicgraphicsmanager.h"
|
||||
|
||||
// Replace with interface by dependency injection
|
||||
#include "classicflyinganimationscenario.h"
|
||||
#include "classicdyinganimationscenario.h"
|
||||
//
|
||||
|
||||
ClassicNoteManager::ClassicNoteManager(const std::shared_ptr<ClassicGraphicsManager>& manager) :
|
||||
_graphics_manager(manager)
|
||||
{}
|
||||
|
||||
ClassicNoteManager::~ClassicNoteManager()
|
||||
{}
|
||||
|
||||
bool ClassicNoteManager::isActive(const ClassicNote* note) const
|
||||
{
|
||||
return note->state == ClassicNoteState::ACTIVE;
|
||||
}
|
||||
|
||||
void ClassicNoteManager::putToGame(ClassicNote* note, const microsec &music_offset)
|
||||
{
|
||||
note->state = ClassicNoteState::FLYING;
|
||||
|
||||
for (auto& element : note->elements)
|
||||
{
|
||||
element.sprite = _graphics_manager->getSprite(element.type);
|
||||
element.sprite->setCoordinates(element.coordinates, 0., 9.);
|
||||
element.animations[note->state]->launch(element.sprite, music_offset, note->evaluator.offset());
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicNoteManager::isInGame(const ClassicNote* note) const
|
||||
{
|
||||
return note->state == ClassicNoteState::FLYING
|
||||
|| note->state == ClassicNoteState::ACTIVE
|
||||
|| note->state == ClassicNoteState::DYING;
|
||||
}
|
||||
|
||||
bool ClassicNoteManager::shouldRemove(const ClassicNote* note) const
|
||||
{
|
||||
return note->state == ClassicNoteState::DEAD;
|
||||
}
|
||||
|
||||
void ClassicNoteManager::update(ClassicNote* note, const microsec& music_offset)
|
||||
{
|
||||
switch (note->state)
|
||||
{
|
||||
default: return;
|
||||
break;
|
||||
|
||||
case ClassicNoteState::FLYING:
|
||||
if (note->evaluator.isActive(music_offset))
|
||||
note->state = ClassicNoteState::ACTIVE;
|
||||
break;
|
||||
|
||||
case ClassicNoteState::DYING:
|
||||
if (note->elements[0].animations[note->state]->isDone())
|
||||
note->state = ClassicNoteState::DEAD;
|
||||
break;
|
||||
|
||||
case ClassicNoteState::ACTIVE:
|
||||
if (!note->evaluator.isActive(music_offset))
|
||||
{
|
||||
note->state = ClassicNoteState::DYING;
|
||||
for (auto& element : note->elements)
|
||||
element.animations[note->state]->launch(element.sprite, music_offset, note->evaluator.offset());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (auto& element : note->elements)
|
||||
if (element.animations[note->state])
|
||||
element.animations[note->state]->update(music_offset);
|
||||
}
|
||||
|
||||
void ClassicNoteManager::input(ClassicNote* note, PlayerInput&& inputdata)
|
||||
{
|
||||
auto grade = ClassicGrade::BAD;
|
||||
|
||||
bool input_valid = std::any_of(note->elements.begin(), note->elements.end(),
|
||||
[inputdata=inputdata](auto& element)
|
||||
{
|
||||
if (element.pressed)
|
||||
return false;
|
||||
|
||||
auto key_iterator = std::find(element.keys.begin(), element.keys.end(), inputdata.event.key.code);
|
||||
bool found_key = key_iterator != element.keys.end();
|
||||
if (found_key)
|
||||
{
|
||||
element.pressed = true;
|
||||
element.pressed_as = inputdata.event.key.code;
|
||||
}
|
||||
|
||||
return found_key;
|
||||
});
|
||||
|
||||
bool all_pressed = allElementsPressed(note);
|
||||
|
||||
if (all_pressed)
|
||||
grade = note->evaluator.calculatePrecision(inputdata.timestamp);
|
||||
|
||||
if (all_pressed || !input_valid)
|
||||
{
|
||||
note->state = ClassicNoteState::DYING;
|
||||
for (auto& element : note->elements)
|
||||
element.animations[note->state]->launch(element.sprite, inputdata.timestamp, note->evaluator.offset());
|
||||
}
|
||||
|
||||
std::cout << "User input: " << static_cast<int>(grade) << "\n";
|
||||
}
|
||||
|
||||
void ClassicNoteManager::draw(const ClassicNote* note) const
|
||||
{
|
||||
for (std::size_t i = 0; i < note->elements.size(); ++i)
|
||||
{
|
||||
if (i >= 1)
|
||||
_graphics_manager->drawLine(note->elements[i-1].sprite->trailCoordinates(), note->elements[i].sprite->trailCoordinates());
|
||||
_graphics_manager->draw(note->elements[i].sprite);
|
||||
}
|
||||
}
|
||||
|
||||
bool ClassicNoteManager::allElementsPressed(const ClassicNote* note) const
|
||||
{
|
||||
return std::all_of(note->elements.begin(), note->elements.end(),
|
||||
[](const auto& element)
|
||||
{
|
||||
return element.pressed;
|
||||
});
|
||||
}
|
||||
|
||||
bool ClassicNoteManager::isPressedAs(const ClassicNote* note, sf::Keyboard::Key key) const
|
||||
{
|
||||
return std::any_of(note->elements.begin(), note->elements.end(),
|
||||
[key=key](const auto& element)
|
||||
{
|
||||
return key == element.pressed_as;
|
||||
});
|
||||
}
|
||||
|
||||
void ClassicNoteManager::setDefaultAnimations(std::array<std::shared_ptr<ClassicAnimationScenario>, 5>& animations) const
|
||||
{
|
||||
animations[ClassicNoteState::NONE] = nullptr;
|
||||
animations[ClassicNoteState::FLYING] = std::make_shared<ClassicFlyingAnimationScenario>();
|
||||
animations[ClassicNoteState::ACTIVE] = animations[ClassicNoteState::FLYING];
|
||||
animations[ClassicNoteState::DYING] = std::make_shared<ClassicDyingAnimationScenario>();
|
||||
animations[ClassicNoteState::DEAD] = nullptr;
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "game/inputtype.h"
|
||||
#include "classicnote.h"
|
||||
#include <memory>
|
||||
|
||||
class ClassicGraphicsManager;
|
||||
|
||||
class ClassicNoteManager
|
||||
{
|
||||
public:
|
||||
explicit ClassicNoteManager(const std::shared_ptr<ClassicGraphicsManager>& manager);
|
||||
~ClassicNoteManager();
|
||||
|
||||
bool isActive(const ClassicNote* note) const;
|
||||
void update(ClassicNote* note, const microsec &music_offset);
|
||||
void input(ClassicNote* note, PlayerInput&& inputdata);
|
||||
void putToGame(ClassicNote* note, const microsec &music_offset);
|
||||
bool isInGame(const ClassicNote* note) const;
|
||||
bool shouldRemove(const ClassicNote* note) const;
|
||||
void draw(const ClassicNote* note) const;
|
||||
|
||||
bool allElementsPressed(const ClassicNote* note) const;
|
||||
bool isPressedAs(const ClassicNote* note, sf::Keyboard::Key key) const;
|
||||
|
||||
void setDefaultAnimations(std::array<std::shared_ptr<ClassicAnimationScenario>, 5>& animations) const;
|
||||
|
||||
private:
|
||||
const std::shared_ptr<ClassicGraphicsManager> _graphics_manager;
|
||||
};
|
Loading…
Reference in New Issue