mirror of
https://gitlab.com/square-game-liberation-front/F.E.I.S.git
synced 2024-11-15 03:27:41 +01:00
New features :
- Note collisions are now highlighted in red, sometimes highlights weird spots but otherwise totally serviceable as is Small fixes : - The timeline now correctly scrubs to before the song starts - Claps now do happen after rewinding with the arrow keys
This commit is contained in:
parent
ddc6ff8bb3
commit
a314f1efe5
220
EditorState.cpp
220
EditorState.cpp
@ -88,10 +88,13 @@ void EditorState::setPlaybackAndMusicPosition(sf::Time newPosition) {
|
||||
} else if (newPosition > chartRuntime) {
|
||||
newPosition = chartRuntime;
|
||||
}
|
||||
previousPos = sf::seconds(newPosition.asSeconds() - 1.f/60.f);
|
||||
playbackPosition = newPosition;
|
||||
if (music) {
|
||||
if (playbackPosition.asSeconds() >= 0 and playbackPosition < music->getDuration()) {
|
||||
music->setPlayingOffset(playbackPosition);
|
||||
} else {
|
||||
music->stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -105,48 +108,56 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
|
||||
float squareSize = ImGui::GetWindowSize().x / 4.f;
|
||||
float TitlebarHeight = ImGui::GetWindowSize().y - ImGui::GetWindowSize().x;
|
||||
int ImGuiIndex = 0;
|
||||
|
||||
if (chart) {
|
||||
|
||||
int ImGuiIndex = 0;
|
||||
|
||||
for (auto note : visibleNotes) {
|
||||
for (auto const& note : visibleNotes) {
|
||||
|
||||
float note_offset = (playbackPosition.asSeconds() - getSecondsAt(note.getTiming()));
|
||||
auto frame = static_cast<long long int>(std::floor(note_offset * 30.f));
|
||||
int x = note.getPos()%4;
|
||||
int y = note.getPos()/4;
|
||||
int x = note.getPos() % 4;
|
||||
int y = note.getPos() / 4;
|
||||
|
||||
|
||||
if (note.getLength() == 0) {
|
||||
|
||||
auto t = marker.getSprite(markerEndingState,note_offset);
|
||||
// Display normal notes
|
||||
|
||||
auto t = marker.getSprite(markerEndingState, note_offset);
|
||||
|
||||
if (t) {
|
||||
ImGui::SetCursorPos({x*squareSize,TitlebarHeight + y*squareSize});
|
||||
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(*t,{squareSize,squareSize});
|
||||
ImGui::Image(*t, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
float tail_end_in_seconds = getSecondsAt(note.getTiming()+note.getLength());
|
||||
// Display long notes
|
||||
|
||||
float tail_end_in_seconds = getSecondsAt(note.getTiming() + note.getLength());
|
||||
float tail_end_offset = playbackPosition.asSeconds() - tail_end_in_seconds;
|
||||
|
||||
if (playbackPosition.asSeconds() < tail_end_in_seconds) {
|
||||
|
||||
// Before or During the long note
|
||||
|
||||
int triangle = note.getTail_pos_as_note_pos();
|
||||
|
||||
auto triangle_x = static_cast<float>(triangle%4);
|
||||
auto triangle_y = static_cast<float>(triangle/4);
|
||||
auto triangle_x = static_cast<float>(triangle % 4);
|
||||
auto triangle_y = static_cast<float>(triangle / 4);
|
||||
|
||||
AffineTransform<float> x_trans(0.0f,ticksToSeconds(note.getLength()),triangle_x, static_cast<float>(x));
|
||||
AffineTransform<float> y_trans(0.0f,ticksToSeconds(note.getLength()),triangle_y, static_cast<float>(y));
|
||||
AffineTransform<float> x_trans(0.0f, ticksToSeconds(note.getLength()), triangle_x,
|
||||
static_cast<float>(x));
|
||||
AffineTransform<float> y_trans(0.0f, ticksToSeconds(note.getLength()), triangle_y,
|
||||
static_cast<float>(y));
|
||||
triangle_x = x_trans.clampedTransform(note_offset);
|
||||
triangle_y = y_trans.clampedTransform(note_offset);
|
||||
|
||||
auto tail_tex = playfield.longNoteMarker.getTailTexture(note_offset,note.getTail_pos());
|
||||
auto tail_tex = playfield.longNoteMarker.getTailTexture(note_offset, note.getTail_pos());
|
||||
if (tail_tex) {
|
||||
|
||||
ImVec2 cursorPos;
|
||||
@ -156,37 +167,37 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
|
||||
// Before the note : tail goes from triangle tip to note edge
|
||||
|
||||
switch (note.getTail_pos()%4) {
|
||||
switch (note.getTail_pos() % 4) {
|
||||
|
||||
// going down
|
||||
case 0:
|
||||
cursorPos.x = x*squareSize;
|
||||
cursorPos.y = (triangle_y+1)*squareSize;
|
||||
cursorPos.x = x * squareSize;
|
||||
cursorPos.y = (triangle_y + 1) * squareSize;
|
||||
texSize.x = squareSize;
|
||||
texSize.y = (y - triangle_y - 1)*squareSize;
|
||||
texSize.y = (y - triangle_y - 1) * squareSize;
|
||||
break;
|
||||
|
||||
// going left (to the left, to the left ...)
|
||||
// going left (to the left, to the left ...)
|
||||
case 1:
|
||||
cursorPos.x = (x+1)*squareSize;
|
||||
cursorPos.y = y*squareSize;
|
||||
texSize.x = (triangle_x - x - 1)*squareSize;
|
||||
cursorPos.x = (x + 1) * squareSize;
|
||||
cursorPos.y = y * squareSize;
|
||||
texSize.x = (triangle_x - x - 1) * squareSize;
|
||||
texSize.y = squareSize;
|
||||
break;
|
||||
|
||||
// going up
|
||||
// going up
|
||||
case 2:
|
||||
cursorPos.x = x*squareSize;
|
||||
cursorPos.y = (y+1)*squareSize;
|
||||
cursorPos.x = x * squareSize;
|
||||
cursorPos.y = (y + 1) * squareSize;
|
||||
texSize.x = squareSize;
|
||||
texSize.y = (triangle_y - y - 1)*squareSize;
|
||||
texSize.y = (triangle_y - y - 1) * squareSize;
|
||||
break;
|
||||
|
||||
// going right
|
||||
// going right
|
||||
case 3:
|
||||
cursorPos.x = (triangle_x+1)*squareSize;
|
||||
cursorPos.y = y*squareSize;
|
||||
texSize.x = (x - triangle_x - 1)*squareSize;
|
||||
cursorPos.x = (triangle_x + 1) * squareSize;
|
||||
cursorPos.y = y * squareSize;
|
||||
texSize.x = (x - triangle_x - 1) * squareSize;
|
||||
texSize.y = squareSize;
|
||||
break;
|
||||
|
||||
@ -198,37 +209,37 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
|
||||
// During the note : tail goes from triangle base to note edge
|
||||
|
||||
switch (note.getTail_pos()%4) {
|
||||
switch (note.getTail_pos() % 4) {
|
||||
|
||||
// going down
|
||||
case 0:
|
||||
cursorPos.x = x*squareSize;
|
||||
cursorPos.y = (triangle_y + 0.9f)*squareSize;
|
||||
cursorPos.x = x * squareSize;
|
||||
cursorPos.y = (triangle_y + 0.9f) * squareSize;
|
||||
texSize.x = squareSize;
|
||||
texSize.y = (y - triangle_y - 0.9f)*squareSize;
|
||||
texSize.y = (y - triangle_y - 0.9f) * squareSize;
|
||||
break;
|
||||
|
||||
// going left (to the left, to the left ...)
|
||||
// going left (to the left, to the left ...)
|
||||
case 1:
|
||||
cursorPos.x = (x+1)*squareSize;
|
||||
cursorPos.y = y*squareSize;
|
||||
texSize.x = (triangle_x - x - 0.9f)*squareSize;
|
||||
cursorPos.x = (x + 1) * squareSize;
|
||||
cursorPos.y = y * squareSize;
|
||||
texSize.x = (triangle_x - x - 0.9f) * squareSize;
|
||||
texSize.y = squareSize;
|
||||
break;
|
||||
|
||||
// going up
|
||||
// going up
|
||||
case 2:
|
||||
cursorPos.x = x*squareSize;
|
||||
cursorPos.y = (y+1)*squareSize;
|
||||
cursorPos.x = x * squareSize;
|
||||
cursorPos.y = (y + 1) * squareSize;
|
||||
texSize.x = squareSize;
|
||||
texSize.y = (triangle_y - y - 0.9f)*squareSize;
|
||||
texSize.y = (triangle_y - y - 0.9f) * squareSize;
|
||||
break;
|
||||
|
||||
// going right
|
||||
// going right
|
||||
case 3:
|
||||
cursorPos.x = (triangle_x + 0.9f)*squareSize;
|
||||
cursorPos.y = y*squareSize;
|
||||
texSize.x = (x - triangle_x - 0.9f)*squareSize;
|
||||
cursorPos.x = (triangle_x + 0.9f) * squareSize;
|
||||
cursorPos.y = y * squareSize;
|
||||
texSize.x = (x - triangle_x - 0.9f) * squareSize;
|
||||
texSize.y = squareSize;
|
||||
break;
|
||||
|
||||
@ -242,44 +253,45 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
|
||||
ImGui::SetCursorPos(cursorPos);
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(*tail_tex,texSize);
|
||||
ImGui::Image(*tail_tex, texSize);
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
|
||||
Toolbox::displayIfHasValue(
|
||||
playfield.longNoteMarker.getSquareBackgroundTexture(note_offset, note.getTail_pos()),
|
||||
{x*squareSize,TitlebarHeight + y*squareSize},
|
||||
{squareSize,squareSize},
|
||||
playfield.longNoteMarker.getSquareBackgroundTexture(note_offset,
|
||||
note.getTail_pos()),
|
||||
{x * squareSize, TitlebarHeight + y * squareSize},
|
||||
{squareSize, squareSize},
|
||||
ImGuiIndex
|
||||
);
|
||||
);
|
||||
|
||||
Toolbox::displayIfHasValue(
|
||||
playfield.longNoteMarker.getSquareOutlineTexture(note_offset, note.getTail_pos()),
|
||||
{x*squareSize,TitlebarHeight + y*squareSize},
|
||||
{squareSize,squareSize},
|
||||
{x * squareSize, TitlebarHeight + y * squareSize},
|
||||
{squareSize, squareSize},
|
||||
ImGuiIndex
|
||||
);
|
||||
|
||||
Toolbox::displayIfHasValue(
|
||||
playfield.longNoteMarker.getTriangleTexture(note_offset, note.getTail_pos()),
|
||||
{triangle_x*squareSize,TitlebarHeight + triangle_y*squareSize},
|
||||
{squareSize,squareSize},
|
||||
{triangle_x * squareSize, TitlebarHeight + triangle_y * squareSize},
|
||||
{squareSize, squareSize},
|
||||
ImGuiIndex
|
||||
);
|
||||
|
||||
Toolbox::displayIfHasValue(
|
||||
playfield.longNoteMarker.getSquareHighlightTexture(note_offset, note.getTail_pos()),
|
||||
{x*squareSize,TitlebarHeight + y*squareSize},
|
||||
{squareSize,squareSize},
|
||||
{x * squareSize, TitlebarHeight + y * squareSize},
|
||||
{squareSize, squareSize},
|
||||
ImGuiIndex
|
||||
);
|
||||
|
||||
// Display the beginning marker
|
||||
auto t = marker.getSprite(markerEndingState,note_offset);
|
||||
auto t = marker.getSprite(markerEndingState, note_offset);
|
||||
if (t) {
|
||||
ImGui::SetCursorPos({x*squareSize,TitlebarHeight + y*squareSize});
|
||||
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(*t,{squareSize,squareSize});
|
||||
ImGui::Image(*t, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
@ -287,13 +299,13 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
|
||||
} else {
|
||||
|
||||
// Display the ending marker
|
||||
// After long note end : Display the ending marker
|
||||
if (tail_end_offset > 0.0f) {
|
||||
auto t = marker.getSprite(markerEndingState,tail_end_offset);
|
||||
auto t = marker.getSprite(markerEndingState, tail_end_offset);
|
||||
if (t) {
|
||||
ImGui::SetCursorPos({x*squareSize,TitlebarHeight + y*squareSize});
|
||||
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(*t,{squareSize,squareSize});
|
||||
ImGui::Image(*t, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
@ -303,7 +315,7 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
}
|
||||
}
|
||||
|
||||
// display buttons over
|
||||
// Display button grid
|
||||
for (int y = 0; y < 4; ++y) {
|
||||
for (int x = 0; x < 4; ++x) {
|
||||
ImGui::PushID(x+4*y);
|
||||
@ -318,6 +330,83 @@ void EditorState::displayPlayfield(Marker& marker, MarkerEndingState markerEndin
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
// Check for collisions then display them
|
||||
if (chart) {
|
||||
|
||||
std::array<std::vector<Note>, 16> notes_per_position = {};
|
||||
std::array<bool, 16> collisions = {};
|
||||
|
||||
for (auto const& note : visibleNotes) {
|
||||
notes_per_position[note.getPos()].push_back(note);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
|
||||
int size = notes_per_position.at(i).size();
|
||||
|
||||
if (size > 1) {
|
||||
|
||||
collisions.at(i) = true;
|
||||
|
||||
} else if (size == 1) {
|
||||
|
||||
auto note = notes_per_position.at(i).at(0);
|
||||
|
||||
int lower_bound = static_cast<int>(getTicksAt(getSecondsAt(note.getTiming())-1.f));
|
||||
int upper_bound = static_cast<int>(getTicksAt(getSecondsAt(note.getTiming())+1.f));
|
||||
|
||||
auto current_note_it = chart->ref.Notes.find(note);
|
||||
if (current_note_it != chart->ref.Notes.end()) {
|
||||
// backwards
|
||||
if (current_note_it != chart->ref.Notes.begin()) {
|
||||
auto other_note_it = current_note_it;
|
||||
--other_note_it;
|
||||
while (other_note_it != chart->ref.Notes.begin()) {
|
||||
if (other_note_it->getPos() == note.getPos()) {
|
||||
if (other_note_it->getTiming() > lower_bound) {
|
||||
collisions.at(i) = true;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
--other_note_it;
|
||||
}
|
||||
}
|
||||
|
||||
// forwards
|
||||
auto other_note_it = current_note_it;
|
||||
++other_note_it;
|
||||
while (other_note_it != chart->ref.Notes.end()) {
|
||||
if (other_note_it->getPos() == note.getPos()) {
|
||||
if (other_note_it->getTiming() < upper_bound) {
|
||||
collisions.at(i) = true;
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
++other_note_it;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
collisions.at(i) = false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 16; ++i) {
|
||||
if (collisions.at(i)) {
|
||||
int x = i%4;
|
||||
int y = i/4;
|
||||
ImGui::SetCursorPos({x * squareSize, TitlebarHeight + y * squareSize});
|
||||
ImGui::PushID(ImGuiIndex);
|
||||
ImGui::Image(playfield.note_collision, {squareSize, squareSize});
|
||||
ImGui::PopID();
|
||||
++ImGuiIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ImGui::End();
|
||||
|
||||
@ -446,7 +535,6 @@ void EditorState::displayTimeline() {
|
||||
ImGui::SetCursorPos({0,0});
|
||||
if(ImGui::VSliderFloat("",ImGui::GetContentRegionMax(),&slider_pos,0.f,1.f,"")) {
|
||||
setPlaybackAndMusicPosition(sf::seconds(scroll.backwards_transform(slider_pos)));
|
||||
this->lastTimingTicked = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,4 +38,9 @@ Widgets::Playfield::Playfield() {
|
||||
throw std::runtime_error("Unable to load texture " + button_path);
|
||||
}
|
||||
button_pressed.setSmooth(true);
|
||||
if (!note_collision.loadFromFile(button_path,{576,0,192,192})) {
|
||||
std::cerr << "Unable to load texture " << button_path;
|
||||
throw std::runtime_error("Unable to load texture " + button_path);
|
||||
}
|
||||
note_collision.setSmooth(true);
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ namespace Widgets {
|
||||
Playfield();
|
||||
sf::Texture button;
|
||||
sf::Texture button_pressed;
|
||||
sf::Texture note_collision;
|
||||
LNMarker longNoteMarker;
|
||||
|
||||
private:
|
||||
|
32
main.cpp
32
main.cpp
@ -10,7 +10,6 @@
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
// TODO : Highlight crossing notes
|
||||
// TODO : Different noise for chords
|
||||
// TODO : Density graph on the timeline
|
||||
// TODO : Pitch control (playback speed factor)
|
||||
@ -96,9 +95,13 @@ int main(int argc, char** argv) {
|
||||
if (editorState->musicVolume < 10) {
|
||||
editorState->musicVolume++;
|
||||
editorState->updateMusicVolume();
|
||||
std::stringstream ss;
|
||||
ss << "Music Volume : " << editorState->musicVolume*10 << "%";
|
||||
notificationsQueue.push(std::make_shared<TextNotification>(ss.str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO : there is something weird with the way I'm doing this, going back with the key often makes it go back twice
|
||||
if (editorState and editorState->chart) {
|
||||
float floatTicks = editorState->getTicks();
|
||||
int prevTick = static_cast<int>(floorf(floatTicks));
|
||||
@ -119,6 +122,9 @@ int main(int argc, char** argv) {
|
||||
if (editorState->musicVolume > 0) {
|
||||
editorState->musicVolume--;
|
||||
editorState->updateMusicVolume();
|
||||
std::stringstream ss;
|
||||
ss << "Music Volume : " << editorState->musicVolume*10 << "%";
|
||||
notificationsQueue.push(std::make_shared<TextNotification>(ss.str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@ -134,27 +140,33 @@ int main(int argc, char** argv) {
|
||||
case sf::Keyboard::Left:
|
||||
if (editorState and editorState->chart) {
|
||||
editorState->snap = Toolbox::getPreviousDivisor(editorState->chart->ref.getResolution(),editorState->snap);
|
||||
std::stringstream ss;
|
||||
ss << "Snap : " << Toolbox::toOrdinal(4*editorState->snap);
|
||||
notificationsQueue.push(std::make_shared<TextNotification>(ss.str()));
|
||||
}
|
||||
break;
|
||||
case sf::Keyboard::Right:
|
||||
if (editorState and editorState->chart) {
|
||||
editorState->snap = Toolbox::getNextDivisor(editorState->chart->ref.getResolution(),editorState->snap);
|
||||
std::stringstream ss;
|
||||
ss << "Snap : " << Toolbox::toOrdinal(4*editorState->snap);
|
||||
notificationsQueue.push(std::make_shared<TextNotification>(ss.str()));
|
||||
}
|
||||
break;
|
||||
case sf::Keyboard::F3:
|
||||
playBeatTick = not playBeatTick;
|
||||
if (playBeatTick) {
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Beat tick: on"));
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Beat tick : on"));
|
||||
} else {
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Beat tick: off"));
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Beat tick : off"));
|
||||
}
|
||||
break;
|
||||
case sf::Keyboard::F4:
|
||||
playNoteTick = not playNoteTick;
|
||||
if (playNoteTick) {
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Note tick: on"));
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Note tick : on"));
|
||||
} else {
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Note tick: off"));
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Note tick : off"));
|
||||
}
|
||||
break;
|
||||
case sf::Keyboard::Space:
|
||||
@ -177,6 +189,7 @@ int main(int argc, char** argv) {
|
||||
case sf::Keyboard::S:
|
||||
if (event.key.control) {
|
||||
ESHelper::save(*editorState);
|
||||
notificationsQueue.push(std::make_shared<TextNotification>("Saved file"));
|
||||
}
|
||||
break;
|
||||
case sf::Keyboard::Y:
|
||||
@ -249,14 +262,11 @@ int main(int argc, char** argv) {
|
||||
for (auto note : editorState->visibleNotes) {
|
||||
float noteTiming = editorState->getSecondsAt(note.getTiming());
|
||||
if (noteTiming >= editorState->previousPos.asSeconds()
|
||||
and noteTiming <= editorState->playbackPosition.asSeconds()
|
||||
and note.getTiming() > editorState->lastTimingTicked) {
|
||||
and noteTiming <= editorState->playbackPosition.asSeconds()) {
|
||||
noteTickSound.play();
|
||||
editorState->lastTimingTicked = note.getTiming();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
editorState->lastTimingTicked = -1;
|
||||
}
|
||||
if (editorState->playbackPosition >= editorState->chartRuntime) {
|
||||
editorState->playing = false;
|
||||
@ -314,7 +324,7 @@ int main(int argc, char** argv) {
|
||||
if (c) {
|
||||
editorState->showNewChartDialog = false;
|
||||
if(editorState->fumen.Charts.try_emplace(c->dif_name,*c).second) {
|
||||
editorState->chart->ref = editorState->fumen.Charts[c->dif_name];
|
||||
editorState->chart.emplace(editorState->fumen.Charts.at(c->dif_name));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user