diff --git a/EditorState.cpp b/EditorState.cpp index db9e4bb..cb26539 100644 --- a/EditorState.cpp +++ b/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(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(triangle%4); - auto triangle_y = static_cast(triangle/4); + auto triangle_x = static_cast(triangle % 4); + auto triangle_y = static_cast(triangle / 4); - AffineTransform x_trans(0.0f,ticksToSeconds(note.getLength()),triangle_x, static_cast(x)); - AffineTransform y_trans(0.0f,ticksToSeconds(note.getLength()),triangle_y, static_cast(y)); + AffineTransform x_trans(0.0f, ticksToSeconds(note.getLength()), triangle_x, + static_cast(x)); + AffineTransform y_trans(0.0f, ticksToSeconds(note.getLength()), triangle_y, + static_cast(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, 16> notes_per_position = {}; + std::array 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(getTicksAt(getSecondsAt(note.getTiming())-1.f)); + int upper_bound = static_cast(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; } } } diff --git a/Widgets.cpp b/Widgets.cpp index 5f7ff2a..421420f 100644 --- a/Widgets.cpp +++ b/Widgets.cpp @@ -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); } diff --git a/Widgets.h b/Widgets.h index 7f2409b..43ac3e3 100644 --- a/Widgets.h +++ b/Widgets.h @@ -33,6 +33,7 @@ namespace Widgets { Playfield(); sf::Texture button; sf::Texture button_pressed; + sf::Texture note_collision; LNMarker longNoteMarker; private: diff --git a/main.cpp b/main.cpp index b22b5f1..29b3953 100644 --- a/main.cpp +++ b/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(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(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(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(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(ss.str())); } break; case sf::Keyboard::F3: playBeatTick = not playBeatTick; if (playBeatTick) { - notificationsQueue.push(std::make_shared("Beat tick: on")); + notificationsQueue.push(std::make_shared("Beat tick : on")); } else { - notificationsQueue.push(std::make_shared("Beat tick: off")); + notificationsQueue.push(std::make_shared("Beat tick : off")); } break; case sf::Keyboard::F4: playNoteTick = not playNoteTick; if (playNoteTick) { - notificationsQueue.push(std::make_shared("Note tick: on")); + notificationsQueue.push(std::make_shared("Note tick : on")); } else { - notificationsQueue.push(std::make_shared("Note tick: off")); + notificationsQueue.push(std::make_shared("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("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 {