diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml index 10a8f62a7b..d9c79eeab1 100644 --- a/Docs/Luadoc/Lua.xml +++ b/Docs/Luadoc/Lua.xml @@ -1289,6 +1289,7 @@ + diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml index 28f00cb28a..60637943ae 100644 --- a/Docs/Luadoc/LuaDocumentation.xml +++ b/Docs/Luadoc/LuaDocumentation.xml @@ -4125,6 +4125,7 @@ a,b = options:Boost(5) + If the player is using Incoming ((positive skew and negative tilt) or (negative skew and positive tilt)), returns the value of skew and its approach_speed.
Returns nil otherwise.
diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 9a5ac78344..76fb9fcad1 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -892,6 +892,7 @@ SuddenDeath=Sudden Death SuddenOffset=Sudden Offset SuperShuffle=Cement Mixer SoftShuffle=Soft Shuffle +HyperShuffle=Hyper Shuffle Swap Sides=Swap Sides Swap Up/Down=Swap Up/Down Sync Machine=Sync Machine diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index 6e650f8e27..3a8358aabe 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -2464,7 +2464,7 @@ Appearance,4="mod,blink;name,Blink" # Appearance,5="mod,blink;name,Blink" # Appearance,6="mod,randomvanish;name,R.Vanish" -Turn="7;selectmultiple" +Turn="8;selectmultiple" TurnDefault="mod,no turn" Turn,1="mod,mirror;name,Mirror" Turn,2="mod,backwards;name,Backwards" @@ -2473,6 +2473,7 @@ Turn,4="mod,right;name,Right" Turn,5="mod,shuffle;name,Shuffle" Turn,6="mod,supershuffle;name,SuperShuffle" Turn,7="mod,softshuffle;name,SoftShuffle" +Turn,8="mod,hypershuffle;name,HyperShuffle" # Turn="6" # TurnDefault="mod,no turn" diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp index 330b3f0b58..a10f9ace59 100644 --- a/src/NoteDataUtil.cpp +++ b/src/NoteDataUtil.cpp @@ -1855,6 +1855,142 @@ static void GetTrackMapping( StepsType st, NoteDataUtil::TrackMapping tt, int Nu } } +static void HyperShuffleNotes( NoteData &inout, int iStartIndex, int iEndIndex) +{ + int iNumTracks = inout.GetNumTracks(); + + std::vector viHoldEndRows(iNumTracks, -1); + + // A "free track" is a track that is not part of a hold and is therefore + // free to be shuffled into. + int iFreeTracks = iNumTracks; + + // If this is the first row, there cannot be any active holds yet. + if ( iStartIndex != 0 ) + { + // Search for any hold notes that might be present at the first row. + // Once the loop below gets going, it keeps track of hold notes itself. + for ( int track = 0; track < iNumTracks; track++ ) + { + int iTargetRow = iStartIndex; + + if ( inout.GetPrevTapNoteRowForTrack(track, iTargetRow) ) + { + const TapNote &tn = inout.GetTapNote(track, iTargetRow); + + // Check for a hold that ends on or after this row. + if ( tn.type == TapNoteType_HoldHead ) + { + int iHoldEndRow = iTargetRow + tn.iDuration; + + if ( iHoldEndRow >= iStartIndex ) + { + viHoldEndRows[track] = iHoldEndRow; + iFreeTracks--; + } + } + } + } + } + + // Stores the list of tracks that can have notes placed into them. + // This will be shuffled so that notes can be placed in different tracks. + std::vector viTargetTracks; + // Stores the list of tap notes that need to be placed. + std::vector vtnTargetTaps; + viTargetTracks.reserve(iNumTracks); + vtnTargetTaps.reserve(iNumTracks); + + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( inout, r, iStartIndex, iEndIndex ) + { + viTargetTracks.clear(); + vtnTargetTaps.clear(); + int iActualTaps = 0; + + for( int track=0; track iHoldEndRow) + { + iHoldEndRow = -1; + viHoldEndRows[track] = iHoldEndRow; + iFreeTracks++; + } + } + + const TapNote tn1 = inout.GetTapNote(track, r); + switch( tn1.type ) + { + case TapNoteType_HoldTail: + ASSERT_M(0, ssprintf("Saw a HoldTail during HyperShuffleNotes (row %d, track %d)", r, track)); + + // If asserts are off, treat this HoldTail as if it were an empty note. + // If they aren't off, this code can't be reached. + inout.SetTapNote(track, r, TAP_EMPTY); + + // Fallthrough + case TapNoteType_Empty: + // Empty tap notes don't get directly placed in the shuffle table. + // Instead, if they aren't in a hold, they get added to the list + // of selectable tracks. Later on, new empty tap notes will be + // created to pad the list of taps out to the required number. + if( iHoldEndRow == -1 ) + viTargetTracks.push_back(track); + break; + case TapNoteType_AutoKeysound: + case TapNoteType_Tap: + case TapNoteType_Lift: + case TapNoteType_Mine: + case TapNoteType_Attack: + case TapNoteType_Fake: + case TapNoteType_HoldHead: + vtnTargetTaps.push_back(tn1); + iActualTaps++; + + if (iHoldEndRow == -1 ) + viTargetTracks.push_back(track); + else + { + // This track won't be part of the shuffle table, so if it + // is not cleared now it will not get cleared later. + inout.SetTapNote(track, r, TAP_EMPTY); + } + break; + case TapNoteType_Invalid: + default: + ASSERT(0); + } + } + + // Do the padding described above. + for(int i = iActualTaps; i < iFreeTracks; i++) + vtnTargetTaps.push_back(TAP_EMPTY); + + std::random_shuffle(viTargetTracks.begin(), viTargetTracks.end(), g_RandomNumberGenerator); + + // Go through the tracks in their shuffled order and drop tap notes. + for(int i = 0; i < viTargetTracks.size(); i++) + { + const int targetTrack = viTargetTracks[i]; + const TapNote current_tn = vtnTargetTaps[i]; + inout.SetTapNote(targetTrack, r, current_tn); + + // If it's a hold, mark this track occupied by a hold until its end. + if( current_tn.type == TapNoteType_HoldHead ) + { + ASSERT_M(viHoldEndRows[targetTrack] == -1, + ssprintf("Tried to insert a hold into another hold (row %d, track %d)", r, targetTrack)); + iFreeTracks--; + viHoldEndRows[targetTrack] = r + current_tn.iDuration; + } + } + } +} + static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex ) { /* @@ -1935,14 +2071,28 @@ static void SuperShuffleTaps( NoteData &inout, int iStartIndex, int iEndIndex ) void NoteDataUtil::Turn( NoteData &inout, StepsType st, TrackMapping tt, int iStartIndex, int iEndIndex ) { - int iTakeFromTrack[MAX_NOTE_TRACKS]; // New track "t" will take from old track iTakeFromTrack[t] - GetTrackMapping( st, tt, inout.GetNumTracks(), iTakeFromTrack ); - NoteData tempNoteData; - tempNoteData.LoadTransformed( inout, inout.GetNumTracks(), iTakeFromTrack ); - if( tt == super_shuffle ) - SuperShuffleTaps( tempNoteData, iStartIndex, iEndIndex ); + if( tt == hyper_shuffle ) + { + // HyperShuffle doesn't require a column transformation beforehand, so there's no point + // in wasting time doing that. + tempNoteData.CopyAll(inout); + HyperShuffleNotes( tempNoteData, iStartIndex, iEndIndex ); + } + else + { + // Calculate how the columns should be rearranged, and then do it. + int iTakeFromTrack[MAX_NOTE_TRACKS]; // New track "t" will take from old track iTakeFromTrack[t] + GetTrackMapping( st, tt, inout.GetNumTracks(), iTakeFromTrack ); + + tempNoteData.LoadTransformed( inout, inout.GetNumTracks(), iTakeFromTrack ); + + // SuperShuffle doesn't affect holds, it instead relies on a regular shuffle to do them first. + // Then it SuperShuffles everything else. + if( tt == super_shuffle ) + SuperShuffleTaps( tempNoteData, iStartIndex, iEndIndex ); + } inout.CopyAll( tempNoteData ); inout.RevalidateATIs(vector(), false); @@ -2785,6 +2935,7 @@ void NoteDataUtil::TransformNoteData( NoteData &nd, TimingData const& timing_dat if( po.m_bTurns[PlayerOptions::TURN_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::shuffle, iStartIndex, iEndIndex ); if( po.m_bTurns[PlayerOptions::TURN_SOFT_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::soft_shuffle, iStartIndex, iEndIndex ); if( po.m_bTurns[PlayerOptions::TURN_SUPER_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::super_shuffle, iStartIndex, iEndIndex ); + if( po.m_bTurns[PlayerOptions::TURN_HYPER_SHUFFLE] ) NoteDataUtil::Turn( nd, st, NoteDataUtil::hyper_shuffle, iStartIndex, iEndIndex ); nd.RevalidateATIs(vector(), false); } diff --git a/src/NoteDataUtil.h b/src/NoteDataUtil.h index a855ddd2a6..3b09aeb059 100644 --- a/src/NoteDataUtil.h +++ b/src/NoteDataUtil.h @@ -107,6 +107,7 @@ namespace NoteDataUtil shuffle, soft_shuffle, super_shuffle, + hyper_shuffle, stomp, swap_up_down, NUM_TRACK_MAPPINGS diff --git a/src/PlayerOptions.cpp b/src/PlayerOptions.cpp index 128fb03c9d..f57b503247 100644 --- a/src/PlayerOptions.cpp +++ b/src/PlayerOptions.cpp @@ -492,6 +492,7 @@ void PlayerOptions::GetMods( vector &AddTo, bool bForceNoteSkin ) const if( m_bTurns[TURN_SHUFFLE] ) AddTo.push_back( "Shuffle" ); if( m_bTurns[TURN_SOFT_SHUFFLE] ) AddTo.push_back( "SoftShuffle" ); if( m_bTurns[TURN_SUPER_SHUFFLE] ) AddTo.push_back( "SuperShuffle" ); + if( m_bTurns[TURN_HYPER_SHUFFLE] ) AddTo.push_back( "HyperShuffle" ); if( m_bTransforms[TRANSFORM_NOHOLDS] ) AddTo.push_back( "NoHolds" ); if( m_bTransforms[TRANSFORM_NOROLLS] ) AddTo.push_back( "NoRolls" ); @@ -1027,6 +1028,7 @@ bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut else if( sBit == "shuffle" ) m_bTurns[TURN_SHUFFLE] = on; else if( sBit == "softshuffle" ) m_bTurns[TURN_SOFT_SHUFFLE] = on; else if( sBit == "supershuffle" ) m_bTurns[TURN_SUPER_SHUFFLE] = on; + else if( sBit == "hypershuffle" ) m_bTurns[TURN_HYPER_SHUFFLE] = on; else if( sBit == "little" ) m_bTransforms[TRANSFORM_LITTLE] = on; else if( sBit == "wide" ) m_bTransforms[TRANSFORM_WIDE] = on; else if( sBit == "big" ) m_bTransforms[TRANSFORM_BIG] = on; @@ -2009,6 +2011,7 @@ public: BOOL_INTERFACE(Shuffle, Turns[PlayerOptions::TURN_SHUFFLE]); BOOL_INTERFACE(SoftShuffle, Turns[PlayerOptions::TURN_SOFT_SHUFFLE]); BOOL_INTERFACE(SuperShuffle, Turns[PlayerOptions::TURN_SUPER_SHUFFLE]); + BOOL_INTERFACE(HyperShuffle, Turns[PlayerOptions::TURN_HYPER_SHUFFLE]); BOOL_INTERFACE(NoHolds, Transforms[PlayerOptions::TRANSFORM_NOHOLDS]); BOOL_INTERFACE(NoRolls, Transforms[PlayerOptions::TRANSFORM_NOROLLS]); BOOL_INTERFACE(NoMines, Transforms[PlayerOptions::TRANSFORM_NOMINES]); @@ -2554,6 +2557,7 @@ public: ADD_METHOD(Shuffle); ADD_METHOD(SoftShuffle); ADD_METHOD(SuperShuffle); + ADD_METHOD(HyperShuffle); ADD_METHOD(NoHolds); ADD_METHOD(NoRolls); ADD_METHOD(NoMines); diff --git a/src/PlayerOptions.h b/src/PlayerOptions.h index 6a0dfd9ad5..7e8729ecbe 100644 --- a/src/PlayerOptions.h +++ b/src/PlayerOptions.h @@ -292,6 +292,7 @@ public: TURN_SHUFFLE, /**< Some of the arrow columns are changed throughout the whole song. */ TURN_SOFT_SHUFFLE, /**< Only shuffle arrow columns on an axis of symmetry. */ TURN_SUPER_SHUFFLE, /**< Every arrow is placed on a random column. */ + TURN_HYPER_SHUFFLE, /**< Every arrow is placed on a random column, but more fairly than SuperShuffle. */ NUM_TURNS }; enum Transform { diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 12b252c6c5..ba5f905039 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -842,7 +842,7 @@ static MenuDef g_AlterMenu( EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), MenuRowDef(ScreenEdit::turn, "Turn", true, - EditMode_Practice, true, true, 0, "Left","Right","Mirror","Backwards","Shuffle","SuperShuffle" ), + EditMode_Practice, true, true, 0, "Left","Right","Mirror","Backwards","Shuffle","SuperShuffle","HyperShuffle" ), MenuRowDef(ScreenEdit::transform, "Transform", true, EditMode_Practice, true, true, 0, "NoHolds","NoMines","Little","Wide", "Big","Quick","Skippy","Mines","Echo","Stomp","Planted","Floored", @@ -5167,6 +5167,7 @@ void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &ans case turn_backwards: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::backwards ); break; case shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::shuffle ); break; case super_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::super_shuffle ); break; + case hyper_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::hyper_shuffle ); break; } HandleAreaMenuChoice( paste_at_begin_marker, false ); diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 1872bc9ecf..758a585f4a 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -487,6 +487,7 @@ public: turn_backwards, /**< Turn the notes as if you were facing away from the machine. */ shuffle, /**< Replace one column with another column. */ super_shuffle, /**< Replace each note individually. */ + hyper_shuffle, /**< Replace each note individually but more fairly. */ NUM_TURN_TYPES }; enum TransformType