diff --git a/src/MeasureInfo.h b/src/MeasureInfo.h index 2a3c449b90..a8fde1c092 100644 --- a/src/MeasureInfo.h +++ b/src/MeasureInfo.h @@ -4,11 +4,6 @@ #include "GameConstantsAndTypes.h" #include "NoteData.h" -/** This is a container for per-measure stats of a stepchart. - This data is calculated and saved to the song cache files as the #MEASUREINFO tag. - This data is provided to the theme via lua functions in Steps.cpp and Trail.cpp, - */ - struct MeasureInfo { int measureCount; diff --git a/src/NotesLoaderSSC.cpp b/src/NotesLoaderSSC.cpp index 2716b4affb..b1c79de157 100644 --- a/src/NotesLoaderSSC.cpp +++ b/src/NotesLoaderSSC.cpp @@ -408,19 +408,52 @@ void SetTechCounts(StepsTagInfo& info) info.ssc_format= true; } -void SetMeasureInfo(StepsTagInfo& info) +void SetNpsPerMeasure(StepsTagInfo& info) { if (info.from_cache || info.for_load_edit) { std::vector values; - split((*info.params)[1], "|", values, true); + split((*info.params)[1], ",", values, true); + std::size_t measures_per_player = values.size() / NUM_PlayerNumber; + std::vector> npsPerMeasure; + npsPerMeasure.resize(NUM_PLAYERS); - MeasureInfo v[NUM_PLAYERS]; FOREACH_PlayerNumber(pn) { - v[pn].FromString(values[pn]); + npsPerMeasure[pn].resize(measures_per_player, 0); + for(std::size_t i= 0; i < measures_per_player; ++i) + { + npsPerMeasure[pn][i]= StringToFloat(values[pn * measures_per_player + i]); + } } - info.steps->SetCachedMeasureInfo(v); + info.steps->SetCachedNpsPerMeasure(npsPerMeasure); + } + else + { + // just recalc at time. + } + info.ssc_format= true; +} + +void SetNotesPerMeasure(StepsTagInfo& info) +{ + if (info.from_cache || info.for_load_edit) + { + std::vector values; + split((*info.params)[1], ",", values, true); + std::size_t measures_per_player = values.size() / NUM_PlayerNumber; + std::vector> notesPerMeasure; + notesPerMeasure.resize(NUM_PLAYERS); + + FOREACH_PlayerNumber(pn) + { + notesPerMeasure[pn].resize(measures_per_player, 0); + for(std::size_t i= 0; i < measures_per_player; ++i) + { + notesPerMeasure[pn][i]= StringToInt(values[pn * measures_per_player + i]); + } + } + info.steps->SetCachedNotesPerMeasure(notesPerMeasure); } else { @@ -670,7 +703,8 @@ struct ssc_parser_helper_t steps_tag_handlers["FAKES"]= &SetStepsFakes; steps_tag_handlers["LABELS"]= &SetStepsLabels; steps_tag_handlers["TECHCOUNTS"] = &SetTechCounts; - steps_tag_handlers["MEASUREINFO"] = &SetMeasureInfo; + steps_tag_handlers["NPSPERMEASURE"] = &SetNpsPerMeasure; + steps_tag_handlers["NOTESPERMEASURE"] = &SetNotesPerMeasure; /* If this is called, the chart does not use the same attacks * as the Song's timing. No other changes are required. */ diff --git a/src/NotesWriterSSC.cpp b/src/NotesWriterSSC.cpp index 7b16b694b9..5d46279a23 100644 --- a/src/NotesWriterSSC.cpp +++ b/src/NotesWriterSSC.cpp @@ -439,16 +439,31 @@ static RString GetSSCNoteData( const Song &song, const Steps &in, bool bSavingCa } } lines.push_back(ssprintf("#TECHCOUNTS:%s;", join(",", asTechCounts).c_str())); - - std::vector asMeasureInfo; - FOREACH_PlayerNumber( pn ) + + std::vector asNpsPerMeasure; + FOREACH_PlayerNumber(pn) { - const MeasureInfo &ms = in.GetMeasureInfo(pn); - asMeasureInfo.push_back(ms.ToString()); + const std::vector &npsPerMeasure = in.GetNpsPerMeasure(pn); + for(unsigned i = 0; i < npsPerMeasure.size(); i++) + { + asNpsPerMeasure.push_back(ssprintf("%.3f", npsPerMeasure[i])); + } } - RString allMeasureInfo = "#MEASUREINFO:" + join("|", asMeasureInfo) + ";"; - lines.push_back(allMeasureInfo); + + lines.push_back( ssprintf( "#NPSPERMEASURE:%s;", join(",",asNpsPerMeasure).c_str() ) ); + std::vector asNotesPerMeasure; + FOREACH_PlayerNumber(pn) + { + const std::vector ¬esPerMeasure = in.GetNotesPerMeasure(pn); + for(unsigned i = 0; i < notesPerMeasure.size(); i++) + { + asNotesPerMeasure.push_back(ssprintf("%d", notesPerMeasure[i])); + } + } + + lines.push_back( ssprintf( "#NOTESPERMEASURE:%s;", join(",",asNotesPerMeasure).c_str() ) ); + // NOTE(MV): #STEPFILENAME has to be at the end of the cache tags, // because it's used in SSCLoader::LoadFromSimfile to determine when // to switch the state back to GETTING_SONG_INFO, which means any tags diff --git a/src/Song.cpp b/src/Song.cpp index 49d632a93d..4cdce33d39 100644 --- a/src/Song.cpp +++ b/src/Song.cpp @@ -51,7 +51,7 @@ * @brief The internal version of the cache for StepMania. * * Increment this value to invalidate the current cache. */ -const int FILE_CACHE_VERSION = 228; +const int FILE_CACHE_VERSION = 229; /** @brief How long does a song sample last by default? */ const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f; diff --git a/src/Steps.cpp b/src/Steps.cpp index 36a640908e..03dc5627aa 100644 --- a/src/Steps.cpp +++ b/src/Steps.cpp @@ -59,9 +59,16 @@ Steps::Steps(Song *song): m_StepsType(StepsType_Invalid), m_pSong(song), m_Difficulty(Difficulty_Invalid), m_iMeter(0), m_bAreCachedRadarValuesJustLoaded(false), m_bAreCachedTechCountsValuesJustLoaded(false), - m_bAreCachedMeasureInfoJustLoaded(false), + m_AreCachedNpsPerMeasureJustLoaded(false), + m_AreCachedNotesPerMeasureJustLoaded(false), m_sCredit(""), displayBPMType(DISPLAY_BPM_ACTUAL), - specifiedBPMMin(0), specifiedBPMMax(0) {} + specifiedBPMMin(0), specifiedBPMMax(0) { + m_CachedNpsPerMeasure.resize(NUM_PLAYERS); + m_CachedNotesPerMeasure.resize(NUM_PLAYERS); + FOREACH_PlayerNumber(pn) { + m_PeakNps[pn] = 0; + } + } Steps::~Steps() { @@ -411,17 +418,19 @@ void Steps::CalculateMeasureInfo() return; } - if( m_bAreCachedMeasureInfoJustLoaded ) + if( m_AreCachedNpsPerMeasureJustLoaded ) { - m_bAreCachedMeasureInfoJustLoaded = false; + m_AreCachedNpsPerMeasureJustLoaded = false; return; } NoteData tempNoteData; this->GetNoteData( tempNoteData ); + MeasureInfo measureInfo[NUM_PLAYERS]; + FOREACH_PlayerNumber(pn) - m_CachedMeasureInfo[pn] + measureInfo[pn] .Zero(); GAMESTATE->SetProcessedTimingData(this->GetTimingData()); @@ -432,7 +441,7 @@ void Steps::CalculateMeasureInfo() NoteDataUtil::SplitCompositeNoteData( tempNoteData, vParts ); for( std::size_t pn = 0; pn < std::min(vParts.size(), std::size_t(NUM_PLAYERS)); ++pn ) { - MeasureInfo::CalculateMeasureInfo(vParts[pn], m_CachedMeasureInfo[pn]); + MeasureInfo::CalculateMeasureInfo(vParts[pn], measureInfo[pn]); } } else if (GAMEMAN->GetStepsTypeInfo(this->m_StepsType).m_StepsTypeCategory == StepsTypeCategory_Couple) @@ -441,16 +450,25 @@ void Steps::CalculateMeasureInfo() // XXX: Assumption that couple will always have an even number of notes. const int tracks = tempNoteData.GetNumTracks() / 2; p1.SetNumTracks(tracks); - MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[PLAYER_1]); + MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[PLAYER_1]); NoteDataUtil::ShiftTracks(tempNoteData, tracks); tempNoteData.SetNumTracks(tracks); - MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[PLAYER_2]); + MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[PLAYER_2]); } else { - MeasureInfo::CalculateMeasureInfo(tempNoteData, m_CachedMeasureInfo[0]); - std::fill_n( m_CachedMeasureInfo + 1, NUM_PLAYERS-1, m_CachedMeasureInfo[0] ); + MeasureInfo::CalculateMeasureInfo(tempNoteData, measureInfo[0]); + std::fill_n( measureInfo + 1, NUM_PLAYERS-1, measureInfo[0] ); } + + FOREACH_PlayerNumber(pn) + { + m_CachedNpsPerMeasure[pn].assign(measureInfo[pn].npsPerMeasure.begin(), measureInfo[pn].npsPerMeasure.end()); + + m_CachedNotesPerMeasure[pn].assign(measureInfo[pn].notesPerMeasure.begin(), measureInfo[pn].notesPerMeasure.end()); + m_PeakNps[pn] = measureInfo[pn].peakNps; + } + GAMESTATE->SetProcessedTimingData(nullptr); } @@ -604,7 +622,11 @@ void Steps::DeAutogen( bool bCopyNoteData ) m_iMeter = Real()->m_iMeter; std::copy( Real()->m_CachedRadarValues, Real()->m_CachedRadarValues + NUM_PLAYERS, m_CachedRadarValues ); std::copy( Real()->m_CachedTechCounts, Real()->m_CachedTechCounts + NUM_PLAYERS, m_CachedTechCounts ); - std::copy( Real()->m_CachedMeasureInfo, Real()->m_CachedMeasureInfo + NUM_PLAYERS, m_CachedMeasureInfo ); + + m_CachedNpsPerMeasure.assign(Real()->m_CachedNpsPerMeasure.begin(), Real()->m_CachedNpsPerMeasure.end()); + m_CachedNotesPerMeasure.assign(Real()->m_CachedNotesPerMeasure.begin(), Real()->m_CachedNotesPerMeasure.end()); + + m_sCredit = Real()->m_sCredit; parent = nullptr; @@ -740,11 +762,40 @@ void Steps::SetCachedTechCounts( const TechCounts ts[NUM_PLAYERS] ) m_bAreCachedTechCountsValuesJustLoaded = true; } -void Steps::SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS]) +void Steps::SetCachedNpsPerMeasure(std::vector>& npsPerMeasure) { DeAutogen(); - std::copy(ms, ms + NUM_PLAYERS, m_CachedMeasureInfo); - m_bAreCachedMeasureInfoJustLoaded = true; + if(npsPerMeasure.size() != NUM_PLAYERS) + { + return; + } + + FOREACH_PlayerNumber(pn) + { + m_CachedNpsPerMeasure[pn].assign(npsPerMeasure[pn].begin(), npsPerMeasure[pn].end()); + std::vector::iterator peakNps = std::max_element(npsPerMeasure[pn].begin(), npsPerMeasure[pn].end()); + if(peakNps != npsPerMeasure[pn].end()) + { + m_PeakNps[pn] = *peakNps; + } + } + m_AreCachedNpsPerMeasureJustLoaded = true; +} + +void Steps::SetCachedNotesPerMeasure(std::vector>& notesPerMeasure) +{ + DeAutogen(); + + if(notesPerMeasure.size() != NUM_PLAYERS) + { + return; + } + + FOREACH_PlayerNumber(pn) + { + m_CachedNotesPerMeasure[pn].assign(notesPerMeasure[pn].begin(), notesPerMeasure[pn].end()); + } + m_AreCachedNotesPerMeasureJustLoaded = true; } RString Steps::GenerateChartKey() @@ -900,8 +951,8 @@ public: if (!lua_isnil(L, 1)) { pn = Enum::Check(L, 1); } - MeasureInfo &ts = const_cast(p->GetMeasureInfo(pn)); - LuaHelpers::CreateTableFromArray(ts.npsPerMeasure, L); + std::vector &ts = const_cast &>(p->GetNpsPerMeasure(pn)); + LuaHelpers::CreateTableFromArray(ts, L); return 1; } @@ -911,9 +962,8 @@ public: if (!lua_isnil(L, 1)) { pn = Enum::Check(L, 1); } - MeasureInfo &ts = const_cast(p->GetMeasureInfo(pn)); - LuaHelpers::CreateTableFromArray(ts.notesPerMeasure, L); - + std::vector &ts = const_cast &>(p->GetNotesPerMeasure(pn)); + LuaHelpers::CreateTableFromArray(ts, L); return 1; } @@ -923,8 +973,7 @@ public: if (!lua_isnil(L, 1)) { pn = Enum::Check(L, 1); } - MeasureInfo &ts = const_cast(p->GetMeasureInfo(pn)); - lua_pushnumber(L, ts.peakNps); + lua_pushnumber(L, p->GetPeakNps(pn)); return 1; } diff --git a/src/Steps.h b/src/Steps.h index 7cbbc27532..e1ce4b572c 100644 --- a/src/Steps.h +++ b/src/Steps.h @@ -145,7 +145,8 @@ public: void SetMeter( int meter ); void SetCachedRadarValues( const RadarValues v[NUM_PLAYERS] ); void SetCachedTechCounts(const TechCounts ts[NUM_PLAYERS]); - void SetCachedMeasureInfo(const MeasureInfo ms[NUM_PLAYERS]); + void SetCachedNpsPerMeasure(std::vector>& npsPerMeasure); + void SetCachedNotesPerMeasure(std::vector>& notesPerMeasure); float PredictMeter() const; unsigned GetHash() const; @@ -178,7 +179,10 @@ public: const TechCounts &GetTechCounts(PlayerNumber pn) const { return Real()->m_CachedTechCounts[pn]; } void CalculateMeasureInfo(); - const MeasureInfo &GetMeasureInfo(PlayerNumber pn) const { return Real()->m_CachedMeasureInfo[pn]; } + + const std::vector &GetNpsPerMeasure(PlayerNumber pn) const { return Real()->m_CachedNpsPerMeasure[pn]; } + const std::vector &GetNotesPerMeasure(PlayerNumber pn) const { return Real()->m_CachedNotesPerMeasure[pn]; } + float GetPeakNps(PlayerNumber pn) const { return Real()->m_PeakNps[pn]; } /** * @brief The TimingData used by the Steps. @@ -276,9 +280,17 @@ private: /** @brief The tech stats used for each player */ mutable TechCounts m_CachedTechCounts[NUM_PLAYERS]; bool m_bAreCachedTechCountsValuesJustLoaded; - - mutable MeasureInfo m_CachedMeasureInfo[NUM_PLAYERS]; - bool m_bAreCachedMeasureInfoJustLoaded; + + std::vector> m_CachedNpsPerMeasure; + bool m_AreCachedNpsPerMeasureJustLoaded; + + std::vector> m_CachedNotesPerMeasure; + bool m_AreCachedNotesPerMeasureJustLoaded; + + float m_PeakNps[NUM_PLAYERS]; + + + /** @brief The name of the person who created the Steps. */ RString m_sCredit;