From 4311158735d12e381a4efc88859f6dc38730566c Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Mon, 20 Aug 2007 02:57:05 +0000 Subject: [PATCH] Split *Options::FromString into FromString and FromOneModString. Check for invalid mod names when loading a course by checking whether the mod is a valid PlayerOption or a valid SongOption. Previously, SongOption mods were being flagged as errors by PlayerOptions. --- stepmania/src/ActiveAttackList.cpp | 2 +- stepmania/src/CatalogXml.cpp | 2 +- stepmania/src/CourseLoaderCRS.cpp | 6 +- stepmania/src/CourseUtil.cpp | 25 +++ stepmania/src/CourseUtil.h | 2 + stepmania/src/PlayerOptions.cpp | 321 ++++++++++++++--------------- stepmania/src/PlayerOptions.h | 3 +- stepmania/src/SongOptions.cpp | 129 ++++++------ stepmania/src/SongOptions.h | 1 + 9 files changed, 265 insertions(+), 226 deletions(-) diff --git a/stepmania/src/ActiveAttackList.cpp b/stepmania/src/ActiveAttackList.cpp index ddaa5df788..30ececae04 100644 --- a/stepmania/src/ActiveAttackList.cpp +++ b/stepmania/src/ActiveAttackList.cpp @@ -45,7 +45,7 @@ void ActiveAttackList::Refresh() continue; PlayerOptions po; - po.FromString( attack.sModifiers, true ); + po.FromString( attack.sModifiers ); po.GetLocalizedMods( vsThemedMods ); } diff --git a/stepmania/src/CatalogXml.cpp b/stepmania/src/CatalogXml.cpp index d6ec209b41..a57fc555bd 100644 --- a/stepmania/src/CatalogXml.cpp +++ b/stepmania/src/CatalogXml.cpp @@ -381,7 +381,7 @@ void CatalogXml::Save( LoadingWindow *loading_window ) { PlayerOptions po; RString s = *iter; - po.FromString( s, false ); + po.FromString( s ); vector v; po.GetLocalizedMods( v ); if( v.empty() ) diff --git a/stepmania/src/CourseLoaderCRS.cpp b/stepmania/src/CourseLoaderCRS.cpp index e288674a7d..ac62ea2242 100644 --- a/stepmania/src/CourseLoaderCRS.cpp +++ b/stepmania/src/CourseLoaderCRS.cpp @@ -13,6 +13,7 @@ #include "RageFileManager.h" #include "CourseWriterCRS.h" #include "RageUtil.h" +#include "CourseUtil.h" #include const int MAX_EDIT_COURSE_SIZE_BYTES = 30*1024; // 30KB @@ -130,9 +131,8 @@ bool CourseLoaderCRS::LoadFromMsd( const RString &sPath, const MsdFile &msd, Cou attack.fSecsRemaining = 0.0f; } - // warn on invalid so we catch bogus mods on load - PlayerOptions po; - po.FromString( attack.sModifiers, true ); + // warn on invalid so we catch typos on load + CourseUtil::WarnOnInvalidMods( attack.sModifiers ); attacks.push_back( attack ); } diff --git a/stepmania/src/CourseUtil.cpp b/stepmania/src/CourseUtil.cpp index d1aea7fdd9..8eab98321c 100644 --- a/stepmania/src/CourseUtil.cpp +++ b/stepmania/src/CourseUtil.cpp @@ -11,6 +11,8 @@ #include "Foreach.h" #include "GameState.h" #include "LocalizedString.h" +#include "RageLog.h" +#include "arch/Dialog/Dialog.h" // @@ -315,6 +317,29 @@ bool CourseUtil::ValidateEditCourseName( const RString &sAnswer, RString &sError return true; } +void CourseUtil::WarnOnInvalidMods( RString sMods ) +{ + PlayerOptions po; + SongOptions so; + vector vs; + split( sMods, ",", vs, true ); + FOREACH_CONST( RString, vs, s ) + { + bool bValid = false; + RString sErrorDetail; + bValid |= po.FromOneModString( *s, sErrorDetail ); + bValid |= so.FromOneModString( *s, sErrorDetail ); + if( !bValid ) + { + RString sFullError = ssprintf("Error processing '%s' in '%s'", (*s).c_str(), sMods.c_str() ); + if( !sErrorDetail.empty() ) + sFullError += ": " + sErrorDetail; + LOG->UserLog( "", "", "%s", sFullError.c_str() ); + Dialog::OK( sFullError, "INVALID_PLAYER_OPTION_WARNING" ); + } + } +} + ////////////////////////////////// // CourseID diff --git a/stepmania/src/CourseUtil.h b/stepmania/src/CourseUtil.h index e8caeb4d95..c8f82178ab 100644 --- a/stepmania/src/CourseUtil.h +++ b/stepmania/src/CourseUtil.h @@ -34,6 +34,8 @@ namespace CourseUtil void AutogenOniFromArtist( const RString &sArtistName, RString sArtistNameTranslit, vector aSongs, Difficulty dc, Course &out ); bool ValidateEditCourseName( const RString &sAnswer, RString &sErrorOut ); + + void WarnOnInvalidMods( RString sMods ); }; class CourseID diff --git a/stepmania/src/PlayerOptions.cpp b/stepmania/src/PlayerOptions.cpp index 61a71b5c17..a59b478c24 100644 --- a/stepmania/src/PlayerOptions.cpp +++ b/stepmania/src/PlayerOptions.cpp @@ -1,7 +1,6 @@ #include "global.h" #include "PlayerOptions.h" #include "RageUtil.h" -#include "RageLog.h" #include "GameState.h" #include "NoteSkinManager.h" #include "song.h" @@ -11,7 +10,6 @@ #include "Foreach.h" #include "Style.h" #include "CommonMetrics.h" -#include "arch/Dialog/Dialog.h" #include #define ONE( arr ) { for( unsigned Z = 0; Z < ARRAYLEN(arr); ++Z ) arr[Z]=1.0f; } @@ -232,179 +230,178 @@ void PlayerOptions::GetMods( vector &AddTo, bool bForceNoteSkin ) const /* Options are added to the current settings; call Init() beforehand if * you don't want this. */ -void PlayerOptions::FromString( const RString &sOptions, bool bWarnOnInvalid ) +void PlayerOptions::FromString( const RString &sMultipleMods ) +{ + RString sTemp = sMultipleMods; + vector vs; + split( sTemp, ",", vs, true ); + RString sThrowAway; + FOREACH( RString, vs, s ) + { + FromOneModString( *s, sThrowAway ); + } +} + +bool PlayerOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut ) { ASSERT( NOTESKIN ); -// Init(); - RString sTemp = sOptions; - sTemp.MakeLower(); - vector asBits; - split( sTemp, ",", asBits, true ); - FOREACH( RString, asBits, bit ) + RString sBit = sOneMod; + sBit.MakeLower(); + TrimLeft(sBit); + TrimRight(sBit); + + /* "drunk" + * "no drunk" + * "150% drunk" + * "*2 100% drunk": approach at 2x normal speed */ + + float level = 1; + float speed = 1; + vector asParts; + split( sBit, " ", asParts, true ); + + FOREACH_CONST( RString, asParts, s ) { - RString& sBit = *bit; - TrimLeft(sBit); - TrimRight(sBit); - - /* "drunk" - * "no drunk" - * "150% drunk" - * "*2 100% drunk": approach at 2x normal speed */ - - float level = 1; - float speed = 1; - vector asParts; - split( sBit, " ", asParts, true ); - - FOREACH_CONST( RString, asParts, s ) + if( *s == "no" ) { - if( *s == "no" ) + level = 0; + } + else if( isdigit((*s)[0]) || (*s)[0] == '-' ) + { + /* If the last character is a *, they probably said "123*" when + * they meant "*123". */ + if( s->Right(1) == "*" ) { - level = 0; + /* XXX We know what they want, is there any reason not to handle it? */ + /* Yes. We should be strict in handling the format. -Chris */ + sErrorOut = ssprintf("Invalid player options \"%s\"; did you mean '*%d'?", s->c_str(), atoi(*s) ); + return false; } - else if( isdigit((*s)[0]) || (*s)[0] == '-' ) + else { - /* If the last character is a *, they probably said "123*" when - * they meant "*123". */ - if( s->Right(1) == "*" ) - { - if( bWarnOnInvalid ) - { - LOG->UserLog( "", "", "Invalid player options \"%s\"; did you mean '*%d'?", - s->c_str(), atoi(*s) ); - } - // XXX We know what they want, is there any reason not to handle it? - speed = StringToFloat( *s ); - } - else - { - level = StringToFloat( *s ) / 100.0f; - } - } - else if( *s[0]=='*' ) - { - sscanf( *s, "*%f", &speed ); - if( !isfinite(speed) ) - speed = 1.0f; + level = StringToFloat( *s ) / 100.0f; } } - - - sBit = asParts.back(); - -#define SET_FLOAT( opt ) { m_ ## opt = level; m_Speed ## opt = speed; } - const bool on = (level > 0.5f); - - static Regex mult("^([0-9]+(\\.[0-9]+)?)x$"); - vector matches; - if( mult.Compare(sBit, matches) ) + else if( *s[0]=='*' ) { - StringConversion::FromString( matches[0], level ); - SET_FLOAT( fScrollSpeed ) - SET_FLOAT( fTimeSpacing ) - m_fTimeSpacing = 0; - } - else if( sscanf( sBit, "c%f", &level ) == 1 ) - { - if( !isfinite(level) || level <= 0.0f ) - level = 200.0f; // Just pick some value. - SET_FLOAT( fScrollBPM ) - SET_FLOAT( fTimeSpacing ) - m_fTimeSpacing = 1; - } - else if( sBit == "clearall" ) Init(); - else if( sBit == "boost" ) SET_FLOAT( fAccels[ACCEL_BOOST] ) - else if( sBit == "brake" || sBit == "land" ) SET_FLOAT( fAccels[ACCEL_BRAKE] ) - else if( sBit == "wave" ) SET_FLOAT( fAccels[ACCEL_WAVE] ) - else if( sBit == "expand" || sBit == "dwiwave" ) SET_FLOAT( fAccels[ACCEL_EXPAND] ) - else if( sBit == "boomerang" ) SET_FLOAT( fAccels[ACCEL_BOOMERANG] ) - else if( sBit == "drunk" ) SET_FLOAT( fEffects[EFFECT_DRUNK] ) - else if( sBit == "dizzy" ) SET_FLOAT( fEffects[EFFECT_DIZZY] ) - else if( sBit == "mini" ) SET_FLOAT( fEffects[EFFECT_MINI] ) - else if( sBit == "tiny" ) SET_FLOAT( fEffects[EFFECT_TINY] ) - else if( sBit == "flip" ) SET_FLOAT( fEffects[EFFECT_FLIP] ) - else if( sBit == "invert" ) SET_FLOAT( fEffects[EFFECT_INVERT] ) - else if( sBit == "tornado" ) SET_FLOAT( fEffects[EFFECT_TORNADO] ) - else if( sBit == "tipsy" ) SET_FLOAT( fEffects[EFFECT_TIPSY] ) - else if( sBit == "bumpy" ) SET_FLOAT( fEffects[EFFECT_BUMPY] ) - else if( sBit == "beat" ) SET_FLOAT( fEffects[EFFECT_BEAT] ) - else if( sBit == "hidden" ) SET_FLOAT( fAppearances[APPEARANCE_HIDDEN] ) - else if( sBit == "hiddenoffset" ) SET_FLOAT( fAppearances[APPEARANCE_HIDDEN_OFFSET] ) - else if( sBit == "sudden" ) SET_FLOAT( fAppearances[APPEARANCE_SUDDEN] ) - else if( sBit == "suddenoffset" ) SET_FLOAT( fAppearances[APPEARANCE_SUDDEN_OFFSET] ) - else if( sBit == "stealth" ) SET_FLOAT( fAppearances[APPEARANCE_STEALTH] ) - else if( sBit == "blink" ) SET_FLOAT( fAppearances[APPEARANCE_BLINK] ) - else if( sBit == "randomvanish" ) SET_FLOAT( fAppearances[APPEARANCE_RANDOMVANISH] ) - else if( sBit == "turn" && !on ) ZERO( m_bTurns ); /* "no turn" */ - else if( sBit == "mirror" ) m_bTurns[TURN_MIRROR] = on; - else if( sBit == "left" ) m_bTurns[TURN_LEFT] = on; - else if( sBit == "right" ) m_bTurns[TURN_RIGHT] = on; - else if( sBit == "shuffle" ) m_bTurns[TURN_SHUFFLE] = on; - else if( sBit == "supershuffle" ) m_bTurns[TURN_SUPER_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; - else if( sBit == "quick" ) m_bTransforms[TRANSFORM_QUICK] = on; - else if( sBit == "bmrize" ) m_bTransforms[TRANSFORM_BMRIZE] = on; - else if( sBit == "skippy" ) m_bTransforms[TRANSFORM_SKIPPY] = on; - else if( sBit == "mines" ) m_bTransforms[TRANSFORM_MINES] = on; - else if( sBit == "echo" ) m_bTransforms[TRANSFORM_ECHO] = on; - else if( sBit == "stomp" ) m_bTransforms[TRANSFORM_STOMP] = on; - else if( sBit == "planted" ) m_bTransforms[TRANSFORM_PLANTED] = on; - else if( sBit == "floored" ) m_bTransforms[TRANSFORM_FLOORED] = on; - else if( sBit == "twister" ) m_bTransforms[TRANSFORM_TWISTER] = on; - else if( sBit == "nojumps" ) m_bTransforms[TRANSFORM_NOJUMPS] = on; - else if( sBit == "nohands" ) m_bTransforms[TRANSFORM_NOHANDS] = on; - else if( sBit == "noquads" ) m_bTransforms[TRANSFORM_NOQUADS] = on; - else if( sBit == "reverse" ) SET_FLOAT( fScrolls[SCROLL_REVERSE] ) - else if( sBit == "split" ) SET_FLOAT( fScrolls[SCROLL_SPLIT] ) - else if( sBit == "alternate" ) SET_FLOAT( fScrolls[SCROLL_ALTERNATE] ) - else if( sBit == "cross" ) SET_FLOAT( fScrolls[SCROLL_CROSS] ) - else if( sBit == "centered" ) SET_FLOAT( fScrolls[SCROLL_CENTERED] ) - else if( sBit == "noholds" ) m_bTransforms[TRANSFORM_NOHOLDS] = on; - else if( sBit == "norolls" ) m_bTransforms[TRANSFORM_NOROLLS] = on; - else if( sBit == "nomines" ) m_bTransforms[TRANSFORM_NOMINES] = on; - else if( sBit == "nostretch" ) m_bTransforms[TRANSFORM_NOSTRETCH] = on; - else if( sBit == "dark" ) SET_FLOAT( fDark ) - else if( sBit == "blind" ) SET_FLOAT( fBlind ) - else if( sBit == "cover" ) SET_FLOAT( fCover ) - else if( sBit == "passmark" ) SET_FLOAT( fPassmark ) - else if( sBit == "overhead" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = 0; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } - else if( sBit == "incoming" ) { m_bSetTiltOrSkew = true; m_fSkew = level; m_fPerspectiveTilt = -level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } - else if( sBit == "space" ) { m_bSetTiltOrSkew = true; m_fSkew = level; m_fPerspectiveTilt = +level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } - else if( sBit == "hallway" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = -level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } - else if( sBit == "distant" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = +level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } - else if( NOTESKIN && NOTESKIN->DoesNoteSkinExist(sBit) ) m_sNoteSkin = sBit; - else if( sBit == "noteskin" && !on ) /* "no noteskin" */ m_sNoteSkin = CommonMetrics::DEFAULT_NOTESKIN_NAME; - else if( sBit == "randomspeed" ) SET_FLOAT( fRandomSpeed ) - else if( sBit == "failarcade" || - sBit == "failimmediate" ) m_FailType = FAIL_IMMEDIATE; - else if( sBit == "failendofsong" || - sBit == "failimmediatecontinue" ) m_FailType = FAIL_IMMEDIATE_CONTINUE; - else if( sBit == "failatend" ) m_FailType = FAIL_AT_END; - else if( sBit == "failoff" ) m_FailType = FAIL_OFF; - else if( sBit == "faildefault" ) - { - PlayerOptions po; - GAMESTATE->GetDefaultPlayerOptions( po ); - m_FailType = po.m_FailType; - } - else if( sBit == "addscore" ) m_ScoreDisplay = SCORING_ADD; - else if( sBit == "subtractscore" ) m_ScoreDisplay = SCORING_SUBTRACT; - else if( sBit == "averagescore" ) m_ScoreDisplay = SCORING_AVERAGE; - else if( sBit == "muteonerror" ) m_bMuteOnError = on; - else if( sBit == "random" ) ChooseRandomModifiers(); - else - { - if( bWarnOnInvalid ) - { - RString sWarning = ssprintf( "The options string \"%s\" contains an invalid mod name \"%s\".", sOptions.c_str(), sBit.c_str() ); - LOG->UserLog( "", "", "%s", sWarning.c_str() ); - Dialog::OK( sWarning, "INVALID_PLAYER_OPTION_WARNING" ); - } + sscanf( *s, "*%f", &speed ); + if( !isfinite(speed) ) + speed = 1.0f; } } + + + sBit = asParts.back(); + +#define SET_FLOAT( opt ) { m_ ## opt = level; m_Speed ## opt = speed; } + const bool on = (level > 0.5f); + + static Regex mult("^([0-9]+(\\.[0-9]+)?)x$"); + vector matches; + if( mult.Compare(sBit, matches) ) + { + StringConversion::FromString( matches[0], level ); + SET_FLOAT( fScrollSpeed ) + SET_FLOAT( fTimeSpacing ) + m_fTimeSpacing = 0; + } + else if( sscanf( sBit, "c%f", &level ) == 1 ) + { + if( !isfinite(level) || level <= 0.0f ) + level = 200.0f; // Just pick some value. + SET_FLOAT( fScrollBPM ) + SET_FLOAT( fTimeSpacing ) + m_fTimeSpacing = 1; + } + else if( sBit == "clearall" ) Init(); + else if( sBit == "boost" ) SET_FLOAT( fAccels[ACCEL_BOOST] ) + else if( sBit == "brake" || sBit == "land" ) SET_FLOAT( fAccels[ACCEL_BRAKE] ) + else if( sBit == "wave" ) SET_FLOAT( fAccels[ACCEL_WAVE] ) + else if( sBit == "expand" || sBit == "dwiwave" ) SET_FLOAT( fAccels[ACCEL_EXPAND] ) + else if( sBit == "boomerang" ) SET_FLOAT( fAccels[ACCEL_BOOMERANG] ) + else if( sBit == "drunk" ) SET_FLOAT( fEffects[EFFECT_DRUNK] ) + else if( sBit == "dizzy" ) SET_FLOAT( fEffects[EFFECT_DIZZY] ) + else if( sBit == "mini" ) SET_FLOAT( fEffects[EFFECT_MINI] ) + else if( sBit == "tiny" ) SET_FLOAT( fEffects[EFFECT_TINY] ) + else if( sBit == "flip" ) SET_FLOAT( fEffects[EFFECT_FLIP] ) + else if( sBit == "invert" ) SET_FLOAT( fEffects[EFFECT_INVERT] ) + else if( sBit == "tornado" ) SET_FLOAT( fEffects[EFFECT_TORNADO] ) + else if( sBit == "tipsy" ) SET_FLOAT( fEffects[EFFECT_TIPSY] ) + else if( sBit == "bumpy" ) SET_FLOAT( fEffects[EFFECT_BUMPY] ) + else if( sBit == "beat" ) SET_FLOAT( fEffects[EFFECT_BEAT] ) + else if( sBit == "hidden" ) SET_FLOAT( fAppearances[APPEARANCE_HIDDEN] ) + else if( sBit == "hiddenoffset" ) SET_FLOAT( fAppearances[APPEARANCE_HIDDEN_OFFSET] ) + else if( sBit == "sudden" ) SET_FLOAT( fAppearances[APPEARANCE_SUDDEN] ) + else if( sBit == "suddenoffset" ) SET_FLOAT( fAppearances[APPEARANCE_SUDDEN_OFFSET] ) + else if( sBit == "stealth" ) SET_FLOAT( fAppearances[APPEARANCE_STEALTH] ) + else if( sBit == "blink" ) SET_FLOAT( fAppearances[APPEARANCE_BLINK] ) + else if( sBit == "randomvanish" ) SET_FLOAT( fAppearances[APPEARANCE_RANDOMVANISH] ) + else if( sBit == "turn" && !on ) ZERO( m_bTurns ); /* "no turn" */ + else if( sBit == "mirror" ) m_bTurns[TURN_MIRROR] = on; + else if( sBit == "left" ) m_bTurns[TURN_LEFT] = on; + else if( sBit == "right" ) m_bTurns[TURN_RIGHT] = on; + else if( sBit == "shuffle" ) m_bTurns[TURN_SHUFFLE] = on; + else if( sBit == "supershuffle" ) m_bTurns[TURN_SUPER_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; + else if( sBit == "quick" ) m_bTransforms[TRANSFORM_QUICK] = on; + else if( sBit == "bmrize" ) m_bTransforms[TRANSFORM_BMRIZE] = on; + else if( sBit == "skippy" ) m_bTransforms[TRANSFORM_SKIPPY] = on; + else if( sBit == "mines" ) m_bTransforms[TRANSFORM_MINES] = on; + else if( sBit == "echo" ) m_bTransforms[TRANSFORM_ECHO] = on; + else if( sBit == "stomp" ) m_bTransforms[TRANSFORM_STOMP] = on; + else if( sBit == "planted" ) m_bTransforms[TRANSFORM_PLANTED] = on; + else if( sBit == "floored" ) m_bTransforms[TRANSFORM_FLOORED] = on; + else if( sBit == "twister" ) m_bTransforms[TRANSFORM_TWISTER] = on; + else if( sBit == "nojumps" ) m_bTransforms[TRANSFORM_NOJUMPS] = on; + else if( sBit == "nohands" ) m_bTransforms[TRANSFORM_NOHANDS] = on; + else if( sBit == "noquads" ) m_bTransforms[TRANSFORM_NOQUADS] = on; + else if( sBit == "reverse" ) SET_FLOAT( fScrolls[SCROLL_REVERSE] ) + else if( sBit == "split" ) SET_FLOAT( fScrolls[SCROLL_SPLIT] ) + else if( sBit == "alternate" ) SET_FLOAT( fScrolls[SCROLL_ALTERNATE] ) + else if( sBit == "cross" ) SET_FLOAT( fScrolls[SCROLL_CROSS] ) + else if( sBit == "centered" ) SET_FLOAT( fScrolls[SCROLL_CENTERED] ) + else if( sBit == "noholds" ) m_bTransforms[TRANSFORM_NOHOLDS] = on; + else if( sBit == "norolls" ) m_bTransforms[TRANSFORM_NOROLLS] = on; + else if( sBit == "nomines" ) m_bTransforms[TRANSFORM_NOMINES] = on; + else if( sBit == "nostretch" ) m_bTransforms[TRANSFORM_NOSTRETCH] = on; + else if( sBit == "dark" ) SET_FLOAT( fDark ) + else if( sBit == "blind" ) SET_FLOAT( fBlind ) + else if( sBit == "cover" ) SET_FLOAT( fCover ) + else if( sBit == "passmark" ) SET_FLOAT( fPassmark ) + else if( sBit == "overhead" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = 0; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } + else if( sBit == "incoming" ) { m_bSetTiltOrSkew = true; m_fSkew = level; m_fPerspectiveTilt = -level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } + else if( sBit == "space" ) { m_bSetTiltOrSkew = true; m_fSkew = level; m_fPerspectiveTilt = +level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } + else if( sBit == "hallway" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = -level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } + else if( sBit == "distant" ) { m_bSetTiltOrSkew = true; m_fSkew = 0; m_fPerspectiveTilt = +level; m_SpeedfSkew = m_SpeedfPerspectiveTilt = speed; } + else if( NOTESKIN && NOTESKIN->DoesNoteSkinExist(sBit) ) m_sNoteSkin = sBit; + else if( sBit == "noteskin" && !on ) /* "no noteskin" */ m_sNoteSkin = CommonMetrics::DEFAULT_NOTESKIN_NAME; + else if( sBit == "randomspeed" ) SET_FLOAT( fRandomSpeed ) + else if( sBit == "failarcade" || + sBit == "failimmediate" ) m_FailType = FAIL_IMMEDIATE; + else if( sBit == "failendofsong" || + sBit == "failimmediatecontinue" ) m_FailType = FAIL_IMMEDIATE_CONTINUE; + else if( sBit == "failatend" ) m_FailType = FAIL_AT_END; + else if( sBit == "failoff" ) m_FailType = FAIL_OFF; + else if( sBit == "faildefault" ) + { + PlayerOptions po; + GAMESTATE->GetDefaultPlayerOptions( po ); + m_FailType = po.m_FailType; + } + else if( sBit == "addscore" ) m_ScoreDisplay = SCORING_ADD; + else if( sBit == "subtractscore" ) m_ScoreDisplay = SCORING_SUBTRACT; + else if( sBit == "averagescore" ) m_ScoreDisplay = SCORING_AVERAGE; + else if( sBit == "muteonerror" ) m_bMuteOnError = on; + else if( sBit == "random" ) ChooseRandomModifiers(); + else + { + return false; + } + + return true; } void NextFloat( float fValues[], int size ) diff --git a/stepmania/src/PlayerOptions.h b/stepmania/src/PlayerOptions.h index dbcc8439f3..6cb5492cef 100644 --- a/stepmania/src/PlayerOptions.h +++ b/stepmania/src/PlayerOptions.h @@ -22,7 +22,8 @@ public: void ResetSavedPrefs(); void GetMods( vector &AddTo, bool bForceNoteSkin = false ) const; void GetLocalizedMods( vector &AddTo ) const; - void FromString( const RString &sOptions, bool bWarnOnInvalid = false ); + void FromString( const RString &sMultipleMods ); + bool FromOneModString( const RString &sOneMod, RString &sErrorDetailOut ); // On error, return false and optionally set sErrorDetailOut void ChooseRandomModifiers(); bool ContainsTransformOrTurn() const; diff --git a/stepmania/src/SongOptions.cpp b/stepmania/src/SongOptions.cpp index 88b274192e..98e80fbbe8 100644 --- a/stepmania/src/SongOptions.cpp +++ b/stepmania/src/SongOptions.cpp @@ -132,69 +132,82 @@ RString SongOptions::GetLocalizedString() const /* Options are added to the current settings; call Init() beforehand if * you don't want this. */ -void SongOptions::FromString( const RString &sOptions ) +void SongOptions::FromString( const RString &sMultipleMods ) { -// Init(); - RString sTemp = sOptions; - sTemp.MakeLower(); - vector asBits; - split( sTemp, ",", asBits, true ); - - for( unsigned i=0; i vs; + split( sTemp, ",", vs, true ); + RString sThrowAway; + FOREACH( RString, vs, s ) { - RString& sBit = asBits[i]; - TrimLeft(sBit); - TrimRight(sBit); - - Regex mult("^([0-9]+(\\.[0-9]+)?)xmusic$"); - vector matches; - if( mult.Compare(sBit, matches) ) - m_fMusicRate = StringToFloat( matches[0] ); - - matches.clear(); - Regex lives("^([0-9]+) ?(lives|life)$"); - if( lives.Compare(sBit, matches) ) - m_iBatteryLives = atoi( matches[0] ); - - vector asParts; - split( sBit, " ", asParts, true ); - bool on = true; - if( asParts.size() > 1 ) - { - sBit = asParts[1]; - - if( asParts[0] == "no" ) - on = false; - } - - if( sBit == "norecover" ) m_DrainType = DRAIN_NO_RECOVER; - else if( sBit == "suddendeath" || - sBit == "death" ) m_DrainType = DRAIN_SUDDEN_DEATH; - else if( sBit == "power-drop" ) m_DrainType = DRAIN_NO_RECOVER; - else if( sBit == "normal-drain" ) m_DrainType = DRAIN_NORMAL; - - else if( sBit == "clap" ) m_bAssistClap = on; - else if( sBit == "metronome" ) m_bAssistMetronome = on; - else if( sBit == "autosync" || sBit == "autosyncsong" ) - m_AutosyncType = on ? AUTOSYNC_SONG : AUTOSYNC_OFF; - else if( sBit == "autosyncmachine" ) - m_AutosyncType = on ? AUTOSYNC_MACHINE : AUTOSYNC_OFF; - else if( sBit == "autosynctempo" ) - m_AutosyncType = on ? AUTOSYNC_TEMPO : AUTOSYNC_OFF; - else if( sBit == "effect" && !on ) - m_SoundEffectType = SOUNDEFFECT_OFF; - else if( sBit == "effectspeed" ) - m_SoundEffectType = on ? SOUNDEFFECT_SPEED : SOUNDEFFECT_OFF; - else if( sBit == "effectpitch" ) - m_SoundEffectType = on ? SOUNDEFFECT_PITCH : SOUNDEFFECT_OFF; - else if( sBit == "savescore" ) m_bSaveScore = on; - else if( sBit == "bar" ) m_LifeType = LIFE_BAR; - else if( sBit == "battery" ) m_LifeType = LIFE_BATTERY; - else if( sBit == "lifetime" ) m_LifeType = LIFE_TIME; - else if( sBit == "haste" ) m_fHaste = on? 1.0f:0.0f; + FromOneModString( *s, sThrowAway ); } } +bool SongOptions::FromOneModString( const RString &sOneMod, RString &sErrorOut ) +{ + RString sBit = sOneMod; + sBit.MakeLower(); + TrimLeft(sBit); + TrimRight(sBit); + + Regex mult("^([0-9]+(\\.[0-9]+)?)xmusic$"); + vector matches; + if( mult.Compare(sBit, matches) ) + { + m_fMusicRate = StringToFloat( matches[0] ); + return true; + } + + matches.clear(); + Regex lives("^([0-9]+) ?(lives|life)$"); + if( lives.Compare(sBit, matches) ) + { + m_iBatteryLives = atoi( matches[0] ); + return true; + } + + vector asParts; + split( sBit, " ", asParts, true ); + bool on = true; + if( asParts.size() > 1 ) + { + sBit = asParts[1]; + if( asParts[0] == "no" ) + on = false; + } + + if( sBit == "norecover" ) m_DrainType = DRAIN_NO_RECOVER; + else if( sBit == "suddendeath" || + sBit == "death" ) m_DrainType = DRAIN_SUDDEN_DEATH; + else if( sBit == "power-drop" ) m_DrainType = DRAIN_NO_RECOVER; + else if( sBit == "normal-drain" ) m_DrainType = DRAIN_NORMAL; + + else if( sBit == "clap" ) m_bAssistClap = on; + else if( sBit == "metronome" ) m_bAssistMetronome = on; + else if( sBit == "autosync" || sBit == "autosyncsong" ) + m_AutosyncType = on ? AUTOSYNC_SONG : AUTOSYNC_OFF; + else if( sBit == "autosyncmachine" ) + m_AutosyncType = on ? AUTOSYNC_MACHINE : AUTOSYNC_OFF; + else if( sBit == "autosynctempo" ) + m_AutosyncType = on ? AUTOSYNC_TEMPO : AUTOSYNC_OFF; + else if( sBit == "effect" && !on ) + m_SoundEffectType = SOUNDEFFECT_OFF; + else if( sBit == "effectspeed" ) + m_SoundEffectType = on ? SOUNDEFFECT_SPEED : SOUNDEFFECT_OFF; + else if( sBit == "effectpitch" ) + m_SoundEffectType = on ? SOUNDEFFECT_PITCH : SOUNDEFFECT_OFF; + else if( sBit == "savescore" ) m_bSaveScore = on; + else if( sBit == "bar" ) m_LifeType = LIFE_BAR; + else if( sBit == "battery" ) m_LifeType = LIFE_BATTERY; + else if( sBit == "lifetime" ) m_LifeType = LIFE_TIME; + else if( sBit == "haste" ) m_fHaste = on? 1.0f:0.0f; + else + return false; + + return true; +} + bool SongOptions::operator==( const SongOptions &other ) const { #define COMPARE(x) { if( x != other.x ) return false; } diff --git a/stepmania/src/SongOptions.h b/stepmania/src/SongOptions.h index 6b1088c42a..a775fb4522 100644 --- a/stepmania/src/SongOptions.h +++ b/stepmania/src/SongOptions.h @@ -52,6 +52,7 @@ public: RString GetString() const; RString GetLocalizedString() const; void FromString( const RString &sOptions ); + bool FromOneModString( const RString &sOneMod, RString &sErrorDetailOut ); // On error, return false and optionally set sErrorDetailOut bool operator==( const SongOptions &other ) const; bool operator!=( const SongOptions &other ) const { return !operator==(other); }