|
|
|
@ -3,6 +3,7 @@
|
|
|
|
|
#include <set>
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include <algorithm>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
|
|
#include "tools/mathutils.h"
|
|
|
|
|
#include "core/note.h"
|
|
|
|
@ -18,15 +19,46 @@ public:
|
|
|
|
|
|
|
|
|
|
typedef typename std::set<TNote*>::const_iterator Iterator;
|
|
|
|
|
|
|
|
|
|
void recalculate(const microsec& offset)
|
|
|
|
|
{
|
|
|
|
|
_current_offset = offset;
|
|
|
|
|
expire(_first_visible_note);
|
|
|
|
|
expire(_last_visible_note);
|
|
|
|
|
expire(_top_note);
|
|
|
|
|
|
|
|
|
|
if (!_timeline.empty())
|
|
|
|
|
{
|
|
|
|
|
Iterator head_iterator = _timeline.begin();
|
|
|
|
|
|
|
|
|
|
while (!isExpired(head_iterator))
|
|
|
|
|
{
|
|
|
|
|
if ((*head_iterator)->offset() >= offset)
|
|
|
|
|
{
|
|
|
|
|
Iterator pre_head = head_iterator;
|
|
|
|
|
--pre_head;
|
|
|
|
|
|
|
|
|
|
_top_note = !isExpired(pre_head) && (*pre_head)->isActive(offset)
|
|
|
|
|
? pre_head
|
|
|
|
|
: head_iterator;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++head_iterator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (isExpired(_top_note))
|
|
|
|
|
_top_note = _timeline.begin();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fetchVisibleNotes();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void setNotes(const std::set<TNote*, NotePtrCompt>& notes, const microsec& visibility)
|
|
|
|
|
{
|
|
|
|
|
_visibility_offset = visibility;
|
|
|
|
|
_timeline = std::move(notes);
|
|
|
|
|
|
|
|
|
|
_top_note = _timeline.begin();
|
|
|
|
|
expire(_first_visible_note);
|
|
|
|
|
expire(_last_visible_note);
|
|
|
|
|
expire(_active_note);
|
|
|
|
|
recalculate(_current_offset);
|
|
|
|
|
|
|
|
|
|
if (isExpired(_top_note))
|
|
|
|
|
return;
|
|
|
|
@ -37,13 +69,16 @@ public:
|
|
|
|
|
void insertNote(TNote* note)
|
|
|
|
|
{
|
|
|
|
|
_top_note = _timeline.insert(note).first;
|
|
|
|
|
recalculate(_current_offset);
|
|
|
|
|
update(_current_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void insertNotes(const std::set<TNote*, NotePtrCompt>& notes)
|
|
|
|
|
{
|
|
|
|
|
_timeline.insert(notes.begin(), notes.end());
|
|
|
|
|
recalculate(_current_offset);
|
|
|
|
|
update(_current_offset);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void clear()
|
|
|
|
@ -57,13 +92,7 @@ public:
|
|
|
|
|
void update(const microsec& offset)
|
|
|
|
|
{
|
|
|
|
|
_current_offset = offset;
|
|
|
|
|
|
|
|
|
|
if (isExpired(_top_note))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
checkTopNote(_current_offset);
|
|
|
|
|
checkCurrentActiveNote();
|
|
|
|
|
checkForNextActiveNote();
|
|
|
|
|
updateTopNote(_current_offset);
|
|
|
|
|
updateVisibleSprites(_current_offset);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -83,9 +112,6 @@ public:
|
|
|
|
|
|
|
|
|
|
void findLastVisibleNote(const microsec& music_offset)
|
|
|
|
|
{
|
|
|
|
|
if (isExpired(_top_note))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
Iterator note_iterator = _top_note;
|
|
|
|
|
while (!isExpired(note_iterator) && isVisiblyClose(note_iterator, music_offset))
|
|
|
|
|
{
|
|
|
|
@ -113,15 +139,34 @@ public:
|
|
|
|
|
{
|
|
|
|
|
auto note = *note_iterator;
|
|
|
|
|
if (note->shouldRemove())
|
|
|
|
|
{
|
|
|
|
|
++_first_visible_note;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
++note_iterator;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Iterator getActiveNote() noexcept
|
|
|
|
|
Iterator getActiveNote(const microsec& music_offset) noexcept
|
|
|
|
|
{
|
|
|
|
|
return _active_note;
|
|
|
|
|
Iterator return_note = _timeline.end();
|
|
|
|
|
|
|
|
|
|
auto note_iterator = _top_note;
|
|
|
|
|
while (!isExpired(note_iterator))
|
|
|
|
|
{
|
|
|
|
|
const auto& note = *note_iterator;
|
|
|
|
|
if (note->isActive(music_offset))
|
|
|
|
|
{
|
|
|
|
|
return_note = note_iterator;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else if (note->offset() > music_offset)
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
++note_iterator;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return return_note;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline Iterator getNoteBy(const microsec& music_offset) noexcept
|
|
|
|
@ -133,12 +178,12 @@ public:
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool isExpired(const Iterator& iterator) const
|
|
|
|
|
inline bool isExpired(const Iterator& iterator) const noexcept
|
|
|
|
|
{
|
|
|
|
|
return iterator == _timeline.end();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline void expire(Iterator& iterator)
|
|
|
|
|
inline void expire(Iterator& iterator) noexcept
|
|
|
|
|
{
|
|
|
|
|
iterator = _timeline.end();
|
|
|
|
|
}
|
|
|
|
@ -148,6 +193,16 @@ private:
|
|
|
|
|
microsec _visibility_offset;
|
|
|
|
|
microsec _current_offset;
|
|
|
|
|
|
|
|
|
|
inline void updateTopNote(const microsec& music_offset) noexcept
|
|
|
|
|
{
|
|
|
|
|
if ((*_top_note)->offset() < music_offset //
|
|
|
|
|
&& _top_note == _first_visible_note // Maybe simplify
|
|
|
|
|
&& !(*_top_note)->isActive(music_offset)) //
|
|
|
|
|
{
|
|
|
|
|
++_top_note;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void updateVisibleSprites(const microsec& music_offset)
|
|
|
|
|
{
|
|
|
|
|
if (nothingToDraw())
|
|
|
|
@ -160,40 +215,7 @@ private:
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkCurrentActiveNote()
|
|
|
|
|
{
|
|
|
|
|
if (isExpired(_active_note))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto note = *_active_note;
|
|
|
|
|
|
|
|
|
|
if (!note->isActive())
|
|
|
|
|
{
|
|
|
|
|
expire(_active_note);
|
|
|
|
|
++_top_note;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkTopNote(const microsec& offset)
|
|
|
|
|
{
|
|
|
|
|
if (isExpired(_top_note) || !isExpired(_active_note))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
while ((*_top_note)->offset() < offset)
|
|
|
|
|
++_top_note;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void checkForNextActiveNote()
|
|
|
|
|
{
|
|
|
|
|
if (!isExpired(_active_note))
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto top_note = *_top_note;
|
|
|
|
|
if (top_note->isActive())
|
|
|
|
|
_active_note = _top_note;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
inline bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const
|
|
|
|
|
inline bool isVisiblyClose(const Iterator& iterator, const microsec& music_offset) const noexcept
|
|
|
|
|
{
|
|
|
|
|
return ((*iterator)->offset() - _visibility_offset) <= music_offset;
|
|
|
|
|
}
|
|
|
|
@ -203,22 +225,7 @@ private:
|
|
|
|
|
return isExpired(_first_visible_note);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Difference between top and active note is that
|
|
|
|
|
* top note is the note handling input right now
|
|
|
|
|
* OR it's the closest note from current music offset
|
|
|
|
|
* position, not necessarily active. A note stops being top only
|
|
|
|
|
* after dying or being tapped by player, even if it's already
|
|
|
|
|
* past her perfect offset.
|
|
|
|
|
*
|
|
|
|
|
* Meanwhile active note is the note which is currently handling
|
|
|
|
|
* player input for grade.
|
|
|
|
|
*
|
|
|
|
|
* An active note is always top note but a top note
|
|
|
|
|
* is not always active note.
|
|
|
|
|
* */
|
|
|
|
|
|
|
|
|
|
Iterator _top_note;
|
|
|
|
|
Iterator _active_note;
|
|
|
|
|
Iterator _last_visible_note;
|
|
|
|
|
Iterator _first_visible_note;
|
|
|
|
|
};
|
|
|
|
|