diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index a3bdd21eba..42c3069b6c 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -2599,6 +2599,10 @@ Pump_Halfdouble=Half-Double Pump_Couple=Couple Pump_Routine=Routine Kb7_Single=KB7 +Kickbox_Human=Human +Kickbox_Quadarm=Quadarm +Kickbox_Insect=Insect +Kickbox_Arachnid=Arachnid Ez2_Single=Single Ez2_Double=Double Ez2_Real=Real @@ -2634,6 +2638,10 @@ Pump_Halfdouble=Half-Double Pump_Couple=Couple Pump_Routine=Routine Kb7_Single=KB7 +Kickbox_Human=Human +Kickbox_Quadarm=Quadarm +Kickbox_Insect=Insect +Kickbox_Arachnid=Arachnid Ez2_Single=Single Ez2_Double=Double Ez2_Real=Real diff --git a/src/GameState.cpp b/src/GameState.cpp index c673779f02..a6a0c6b530 100644 --- a/src/GameState.cpp +++ b/src/GameState.cpp @@ -739,7 +739,12 @@ void GameState::BeginStage() // only do this check with human players, assume CPU players (Rave) // always have tokens. -aj (this could probably be moved below, even.) if( !IsEventMode() && !IsCpuPlayer(p) ) - ASSERT( m_iPlayerStageTokens[p] >= m_iNumStagesOfThisSong ); + { + if(m_iPlayerStageTokens[p] < m_iNumStagesOfThisSong) + { + LuaHelpers::ReportScriptErrorFmt("Player %d only has %d stage tokens, but needs %d.", p, m_iPlayerStageTokens[p], m_iNumStagesOfThisSong); + } + } m_iPlayerStageTokens[p] -= m_iNumStagesOfThisSong; } FOREACH_HumanPlayer( pn ) @@ -979,6 +984,101 @@ bool GameState::CanSafelyEnterGameplay(RString& reason) return true; } +void GameState::SetCompatibleStylesForPlayers() +{ + bool style_set= false; + if(IsCourseMode()) + { + if(m_pCurCourse != NULL) + { + const Style* style= m_pCurCourse->GetCourseStyle(m_pCurGame, GetNumSidesJoined()); + if(style != NULL) + { + style_set= true; + SetCurrentStyle(style, PLAYER_INVALID); + } + } + else + { + vector vst; + GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst); + const Style *style = GAMEMAN->GetFirstCompatibleStyle( + m_pCurGame, GetNumSidesJoined(), vst[0]); + SetCurrentStyle(style, PLAYER_INVALID); + } + } + if(!style_set) + { + if(GetCurrentGame()->m_PlayersHaveSeparateStyles) + { + FOREACH_EnabledPlayer(pn) + { + StepsType st= StepsType_Invalid; + if(m_pCurSteps[pn] != NULL) + { + st= m_pCurSteps[pn]->m_StepsType; + } + else if(m_pCurTrail[pn] != NULL) + { + st= m_pCurTrail[pn]->m_StepsType; + } + else + { + vector vst; + GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst); + st= vst[0]; + } + const Style *style = GAMEMAN->GetFirstCompatibleStyle( + m_pCurGame, GetNumSidesJoined(), st); + SetCurrentStyle(style, pn); + } + } + else + { + vector vst; + GAMEMAN->GetStepsTypesForGame(m_pCurGame, vst); + const Style *style = GAMEMAN->GetFirstCompatibleStyle( + m_pCurGame, GetNumSidesJoined(), vst[0]); + SetCurrentStyle(style, PLAYER_INVALID); + } + } +} + +void GameState::ForceSharedSidesMatch() +{ + PlayerNumber pn_with_shared= PLAYER_INVALID; + const Style* shared_style= NULL; + FOREACH_EnabledPlayer(pn) + { + const Style* style= GetCurrentStyle(pn); + ASSERT_M(style != NULL, "Style being null should not be possible."); + if(style->m_StyleType == StyleType_TwoPlayersSharedSides) + { + pn_with_shared= pn; + shared_style= style; + } + } + if(pn_with_shared != PLAYER_INVALID) + { + ASSERT_M(GetNumPlayersEnabled() == 2, "2 players must be enabled for shared sides."); + PlayerNumber other_pn= OPPOSITE_PLAYER[pn_with_shared]; + const Style* other_style= GetCurrentStyle(other_pn); + ASSERT_M(other_style != NULL, "Other player's style being null should not be possible."); + if(other_style->m_StyleType != StyleType_TwoPlayersSharedSides) + { + SetCurrentStyle(shared_style, other_pn); + if(IsCourseMode()) + { + m_pCurTrail[other_pn].Set(m_pCurTrail[pn_with_shared]); + } + else + { + m_pCurSteps[other_pn].Set(m_pCurSteps[pn_with_shared]); + } + } + } +} + void GameState::Update( float fDelta ) { m_SongOptions.Update( fDelta ); @@ -1333,7 +1433,6 @@ void GameState::SetCurrentStyle(const Style *style, PlayerNumber pn) } else { - m_SeparatedStyles[pn]= style; if(pn == PLAYER_INVALID) { FOREACH_PlayerNumber(rpn) @@ -1341,6 +1440,10 @@ void GameState::SetCurrentStyle(const Style *style, PlayerNumber pn) m_SeparatedStyles[rpn]= style; } } + else + { + m_SeparatedStyles[pn]= style; + } } if(INPUTMAPPER) { @@ -1415,6 +1518,28 @@ bool GameState::IsHumanPlayer( PlayerNumber pn ) const if( pn == PLAYER_INVALID ) return false; + if(GetCurrentGame()->m_PlayersHaveSeparateStyles) + { + if( GetCurrentStyle(pn) == NULL ) // no style chosen + { + return m_bSideIsJoined[pn]; + } + else + { + StyleType type = GetCurrentStyle(pn)->m_StyleType; + switch( type ) + { + case StyleType_TwoPlayersTwoSides: + case StyleType_TwoPlayersSharedSides: + return true; + case StyleType_OnePlayerOneSide: + case StyleType_OnePlayerTwoSides: + return pn == this->GetMasterPlayerNumber(); + default: + FAIL_M(ssprintf("Invalid style type: %i", type)); + } + } + } if( GetCurrentStyle(pn) == NULL ) // no style chosen { if( PlayersCanJoin() ) diff --git a/src/GameState.h b/src/GameState.h index 09e6f5803d..d449f17e69 100644 --- a/src/GameState.h +++ b/src/GameState.h @@ -74,6 +74,8 @@ public: Song* GetDefaultSong() const; bool CanSafelyEnterGameplay(RString& reason); + void SetCompatibleStylesForPlayers(); + void ForceSharedSidesMatch(); void Update( float fDelta ); diff --git a/src/GhostArrowRow.cpp b/src/GhostArrowRow.cpp index dd023937db..457ff524d3 100644 --- a/src/GhostArrowRow.cpp +++ b/src/GhostArrowRow.cpp @@ -24,8 +24,9 @@ void GhostArrowRow::Load( const PlayerState* pPlayerState, float fYReverseOffset { const RString &sButton = GAMESTATE->GetCurrentStyle(pn)->ColToButtonName( c ); - const GameInput GameI = GAMESTATE->GetCurrentStyle(pn)->StyleInputToGameInput( c, pn ); - NOTESKIN->SetGameController( GameI.controller ); + vector GameI; + GAMESTATE->GetCurrentStyle(pn)->StyleInputToGameInput( c, pn, GameI ); + NOTESKIN->SetGameController( GameI[0].controller ); m_bHoldShowing.push_back( TapNoteSubType_Invalid ); m_bLastHoldShowing.push_back( TapNoteSubType_Invalid ); diff --git a/src/InputMapper.cpp b/src/InputMapper.cpp index 70da57665a..290b8304f2 100644 --- a/src/InputMapper.cpp +++ b/src/InputMapper.cpp @@ -910,6 +910,16 @@ bool InputMapper::IsBeingPressed( GameButton MenuI, PlayerNumber pn ) const return false; } +bool InputMapper::IsBeingPressed(const vector& GameI, MultiPlayer mp, const DeviceInputList *pButtonState ) const +{ + bool pressed= false; + for(size_t i= 0; i < GameI.size(); ++i) + { + pressed |= IsBeingPressed(GameI[i], mp, pButtonState); + } + return pressed; +} + void InputMapper::RepeatStopKey( const GameInput &GameI ) { for( int i=0; i& GameI, MultiPlayer mp = MultiPlayer_Invalid, const DeviceInputList *pButtonState = NULL ) const; void ResetKeyRepeat( const GameInput &GameI ); void ResetKeyRepeat( GameButton MenuI, PlayerNumber pn ); diff --git a/src/LightsManager.cpp b/src/LightsManager.cpp index 84bad22410..9b0d11258b 100644 --- a/src/LightsManager.cpp +++ b/src/LightsManager.cpp @@ -80,10 +80,14 @@ static void GetUsedGameInputs( vector &vGameInputsOut ) { for( int iCol=0; iCol<(*style)->m_iColsPerPlayer; ++iCol ) { - GameInput gi = (*style)->StyleInputToGameInput( iCol, pn ); - if( gi.IsValid() ) + vector gi; + (*style)->StyleInputToGameInput( iCol, pn, gi ); + for(size_t i= 0; i < gi.size(); ++i) { - vGIs.insert( gi ); + if(gi[i].IsValid()) + { + vGIs.insert(gi[i]); + } } } } diff --git a/src/NoteDisplay.cpp b/src/NoteDisplay.cpp index f0bca2c546..5c9189ac78 100644 --- a/src/NoteDisplay.cpp +++ b/src/NoteDisplay.cpp @@ -290,29 +290,30 @@ void NoteDisplay::Load( int iColNum, const PlayerState* pPlayerState, float fYRe m_fYReverseOffsetPixels = fYReverseOffsetPixels; const PlayerNumber pn = m_pPlayerState->m_PlayerNumber; - const GameInput GameI = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber)->StyleInputToGameInput( iColNum, pn ); + vector GameI; + GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber)->StyleInputToGameInput( iColNum, pn, GameI ); const RString &sButton = GAMESTATE->GetCurrentStyle(pPlayerState->m_PlayerNumber)->ColToButtonName( iColNum ); cache->Load( sButton ); // "normal" note types - m_TapNote.Load( sButton, "Tap Note", pn, GameI.controller ); + m_TapNote.Load( sButton, "Tap Note", pn, GameI[0].controller ); //m_TapAdd.Load( sButton, "Tap Addition", pn, GameI.controller ); - m_TapMine.Load( sButton, "Tap Mine", pn, GameI.controller ); - m_TapLift.Load( sButton, "Tap Lift", pn, GameI.controller ); - m_TapFake.Load( sButton, "Tap Fake", pn, GameI.controller ); + m_TapMine.Load( sButton, "Tap Mine", pn, GameI[0].controller ); + m_TapLift.Load( sButton, "Tap Lift", pn, GameI[0].controller ); + m_TapFake.Load( sButton, "Tap Fake", pn, GameI[0].controller ); // hold types FOREACH_HoldType( ht ) { FOREACH_ActiveType( at ) { - m_HoldHead[ht][at].Load( sButton, HoldTypeToString(ht)+" Head "+ActiveTypeToString(at), pn, GameI.controller ); - m_HoldTopCap[ht][at].Load( sButton, HoldTypeToString(ht)+" Topcap "+ActiveTypeToString(at), pn, GameI.controller ); - m_HoldBody[ht][at].Load( sButton, HoldTypeToString(ht)+" Body "+ActiveTypeToString(at), pn, GameI.controller ); - m_HoldBottomCap[ht][at].Load( sButton, HoldTypeToString(ht)+" Bottomcap "+ActiveTypeToString(at), pn, GameI.controller ); - m_HoldTail[ht][at].Load( sButton, HoldTypeToString(ht)+" Tail "+ActiveTypeToString(at), pn, GameI.controller ); + m_HoldHead[ht][at].Load( sButton, HoldTypeToString(ht)+" Head "+ActiveTypeToString(at), pn, GameI[0].controller ); + m_HoldTopCap[ht][at].Load( sButton, HoldTypeToString(ht)+" Topcap "+ActiveTypeToString(at), pn, GameI[0].controller ); + m_HoldBody[ht][at].Load( sButton, HoldTypeToString(ht)+" Body "+ActiveTypeToString(at), pn, GameI[0].controller ); + m_HoldBottomCap[ht][at].Load( sButton, HoldTypeToString(ht)+" Bottomcap "+ActiveTypeToString(at), pn, GameI[0].controller ); + m_HoldTail[ht][at].Load( sButton, HoldTypeToString(ht)+" Tail "+ActiveTypeToString(at), pn, GameI[0].controller ); } } } diff --git a/src/Player.cpp b/src/Player.cpp index 439a005b0b..2ecbad08dd 100644 --- a/src/Player.cpp +++ b/src/Player.cpp @@ -927,9 +927,10 @@ void Player::Update( float fDeltaTime ) ASSERT( m_pPlayerState != NULL ); // TODO: Remove use of PlayerNumber. - GameInput GameI = GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( col, m_pPlayerState->m_PlayerNumber ); + vector GameI; + GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( col, m_pPlayerState->m_PlayerNumber, GameI ); - bool bIsHoldingButton = INPUTMAPPER->IsBeingPressed( GameI ); + bool bIsHoldingButton= INPUTMAPPER->IsBeingPressed(GameI); // TODO: Make this work for non-human-controlled players if( bIsHoldingButton && !GAMESTATE->m_bDemonstrationOrJukebox && m_pPlayerState->m_PlayerController==PC_HUMAN ) @@ -1221,10 +1222,13 @@ void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vectorGetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn ); - // this previously read as bIsHoldingButton &= - // was there a specific reason for this? - Friez - bIsHoldingButton &= INPUTMAPPER->IsBeingPressed( GameI, m_pPlayerState->m_mp ); + vector GameI; + GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn, GameI ); + + bool is_holding= false; + // this previously read as bIsHoldingButton &= + // was there a specific reason for this? - Friez + is_holding &= INPUTMAPPER->IsBeingPressed(GameI, m_pPlayerState->m_mp); } } } @@ -2141,9 +2145,14 @@ void Player::StepStrumHopo( int col, int row, const RageTimer &tm, bool bHeld, b int iNumTracksHeld = 0; for( int t=0; tGetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( t, pn ); - const float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); - if( fSecsHeld > 0 && fSecsHeld < m_fTimingWindowJump ) + vector GameI; + GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( t, pn, GameI ); + float secs_held= 0.0f; + for(size_t i= 0; i < GameI.size(); ++i) + { + secs_held= max(secs_held, INPUTMAPPER->GetSecsHeld( GameI[i] )); + } + if( secs_held > 0 && secs_held < m_fTimingWindowJump ) iNumTracksHeld++; } @@ -2950,16 +2959,25 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) if( !REQUIRE_STEP_ON_HOLD_HEADS ) { PlayerNumber pn = m_pPlayerState->m_PlayerNumber; - GameInput GameI = GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn ); + vector GameI; + GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn, GameI ); if( PREFSMAN->m_fPadStickSeconds > 0.f ) { - float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI, m_pPlayerState->m_mp ); - if( fSecsHeld >= PREFSMAN->m_fPadStickSeconds ) - Step( iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false ); + for(size_t i= 0; i < GameI.size(); ++i) + { + float fSecsHeld = INPUTMAPPER->GetSecsHeld(GameI[i], m_pPlayerState->m_mp); + if(fSecsHeld >= PREFSMAN->m_fPadStickSeconds) + { + Step(iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false); + } + } } - else if( INPUTMAPPER->IsBeingPressed(GameI, m_pPlayerState->m_mp) ) + else { - Step( iTrack, -1, now, true, false ); + if(INPUTMAPPER->IsBeingPressed(GameI, m_pPlayerState->m_mp)) + { + Step(iTrack, -1, now, true, false); + } } } break; @@ -2969,17 +2987,22 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now ) // Hold the panel while crossing a mine will cause the mine to explode // TODO: Remove use of PlayerNumber. PlayerNumber pn = m_pPlayerState->m_PlayerNumber; - GameInput GameI = GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn ); - if( PREFSMAN->m_fPadStickSeconds > 0 ) + vector GameI; + GAMESTATE->GetCurrentStyle(GetPlayerState()->m_PlayerNumber)->StyleInputToGameInput( iTrack, pn, GameI ); + if( PREFSMAN->m_fPadStickSeconds > 0.0f ) { - float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI, m_pPlayerState->m_mp ); - if( fSecsHeld >= PREFSMAN->m_fPadStickSeconds ) - Step( iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false ); + for(size_t i= 0; i < GameI.size(); ++i) + { + float fSecsHeld = INPUTMAPPER->GetSecsHeld(GameI[i], m_pPlayerState->m_mp); + if(fSecsHeld >= PREFSMAN->m_fPadStickSeconds) + { + Step( iTrack, -1, now - PREFSMAN->m_fPadStickSeconds, true, false ); + } + } } - else + else if(INPUTMAPPER->IsBeingPressed(GameI, m_pPlayerState->m_mp)) { - if( INPUTMAPPER->IsBeingPressed(GameI, m_pPlayerState->m_mp) ) - Step( iTrack, iRow, now, true, false ); + Step( iTrack, iRow, now, true, false ); } break; } diff --git a/src/ReceptorArrow.cpp b/src/ReceptorArrow.cpp index 4437df69e0..eb744769ba 100644 --- a/src/ReceptorArrow.cpp +++ b/src/ReceptorArrow.cpp @@ -20,9 +20,14 @@ void ReceptorArrow::Load( const PlayerState* pPlayerState, int iColNo ) m_iColNo = iColNo; const PlayerNumber pn = m_pPlayerState->m_PlayerNumber; - const GameInput GameI = GAMESTATE->GetCurrentStyle(pn)->StyleInputToGameInput( iColNo, pn ); + vector GameI; + GAMESTATE->GetCurrentStyle(pn)->StyleInputToGameInput(iColNo, pn, GameI); NOTESKIN->SetPlayerNumber( pn ); - NOTESKIN->SetGameController( GameI.controller ); + // FIXME? Does this cause a problem when game inputs on different + // controllers are mapped to the same column? Such a thing could be set + // up in a style that uses two controllers and has a mapping that fits the + // requirements. -Kyz + NOTESKIN->SetGameController( GameI[0].controller ); RString sButton = GAMESTATE->GetCurrentStyle(pn)->ColToButtonName( iColNo ); m_pReceptor.Load( NOTESKIN->LoadActor(sButton, "Receptor") ); diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index fa8b9e4b05..8f5d5221fe 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -1514,8 +1514,13 @@ void ScreenEdit::Update( float fDeltaTime ) for( int t=0; tGetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->m_iColsPerPlayer; t++ ) // for each track { - GameInput GameI = GAMESTATE->GetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->StyleInputToGameInput( t, PLAYER_1 ); - float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); + vector GameI; + GAMESTATE->GetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->StyleInputToGameInput( t, PLAYER_1, GameI ); + float fSecsHeld= 0.0f; + for(size_t i= 0; i < GameI.size(); ++i) + { + fSecsHeld= max(fSecsHeld, INPUTMAPPER->GetSecsHeld(GameI[i])); + } fSecsHeld = min( fSecsHeld, m_RemoveNoteButtonLastChanged.Ago() ); if( fSecsHeld == 0 ) continue; @@ -1568,7 +1573,8 @@ void ScreenEdit::Update( float fDeltaTime ) bool bButtonIsBeingPressed = false; for( int t=0; tGetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->m_iColsPerPlayer; t++ ) // for each track { - GameInput GameI = GAMESTATE->GetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->StyleInputToGameInput( t, PLAYER_1 ); + vector GameI; + GAMESTATE->GetCurrentStyle(GAMESTATE->GetMasterPlayerNumber())->StyleInputToGameInput( t, PLAYER_1, GameI ); if( INPUTMAPPER->IsBeingPressed(GameI) ) bButtonIsBeingPressed = true; } diff --git a/src/ScreenGameplay.cpp b/src/ScreenGameplay.cpp index 42498c0434..8ef4e722c8 100644 --- a/src/ScreenGameplay.cpp +++ b/src/ScreenGameplay.cpp @@ -2179,8 +2179,12 @@ void ScreenGameplay::UpdateLights() if( bBlink ) { - GameInput gi = pStyle->StyleInputToGameInput( t, pi->m_pn ); - bBlinkGameButton[gi.controller][gi.button] = true; + vector gi; + pStyle->StyleInputToGameInput( t, pi->m_pn, gi ); + for(size_t i= 0; i < gi.size(); ++i) + { + bBlinkGameButton[gi[i].controller][gi[i].button] = true; + } } } } diff --git a/src/ScreenNameEntry.cpp b/src/ScreenNameEntry.cpp index 63a97deced..a9ecccddc2 100644 --- a/src/ScreenNameEntry.cpp +++ b/src/ScreenNameEntry.cpp @@ -270,9 +270,15 @@ void ScreenNameEntry::Init() break; // We have enough columns. // Find out if this column is associated with the START menu button. - GameInput gi = GAMESTATE->GetCurrentStyle(p)->StyleInputToGameInput( iCol, p ); - GameButton mb = INPUTMAPPER->GameButtonToMenuButton( gi.button ); - if( mb == GAME_BUTTON_START ) + vector gi; + GAMESTATE->GetCurrentStyle(p)->StyleInputToGameInput( iCol, p, gi ); + bool gi_is_start= false; + for(size_t i= 0; i < gi.size(); ++i) + { + gi_is_start|= (INPUTMAPPER->GameButtonToMenuButton(gi[i].button) + == GAME_BUTTON_START); + } + if(gi_is_start) continue; m_ColToStringIndex[p][iCol] = CurrentStringIndex++; diff --git a/src/ScreenSelectMusic.cpp b/src/ScreenSelectMusic.cpp index a5358c3c49..77d3714dbc 100644 --- a/src/ScreenSelectMusic.cpp +++ b/src/ScreenSelectMusic.cpp @@ -232,10 +232,7 @@ void ScreenSelectMusic::BeginScreen() if( CommonMetrics::AUTO_SET_STYLE ) { - vector vst; - GAMEMAN->GetStepsTypesForGame( GAMESTATE->m_pCurGame, vst ); - const Style *pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), vst[0] ); - GAMESTATE->SetCurrentStyle( pStyle, PLAYER_INVALID ); + GAMESTATE->SetCompatibleStylesForPlayers(); } if( GAMESTATE->GetCurrentStyle(PLAYER_INVALID) == NULL ) @@ -1434,31 +1431,9 @@ bool ScreenSelectMusic::MenuStart( const InputEventPlus &input ) } } - if( CommonMetrics::AUTO_SET_STYLE ) - { - // Now that Steps have been chosen, set a Style that can play them. - const Style *pStyle = NULL; - if( GAMESTATE->IsCourseMode() ) - pStyle = GAMESTATE->m_pCurCourse->GetCourseStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined() ); - if( pStyle == NULL ) - { - StepsType stCurrent; - PlayerNumber pn = GAMESTATE->GetMasterPlayerNumber(); - if( GAMESTATE->IsCourseMode() ) - { - ASSERT( GAMESTATE->m_pCurTrail[pn] != NULL ); - stCurrent = GAMESTATE->m_pCurTrail[pn]->m_StepsType; - } - else - { - ASSERT( GAMESTATE->m_pCurSteps[pn] != NULL ); - stCurrent = GAMESTATE->m_pCurSteps[pn]->m_StepsType; - } - vector vst; - pStyle = GAMEMAN->GetFirstCompatibleStyle( GAMESTATE->m_pCurGame, GAMESTATE->GetNumSidesJoined(), stCurrent ); - } - GAMESTATE->SetCurrentStyle( pStyle, PLAYER_INVALID ); - } + // Now that Steps have been chosen, set a Style that can play them. + GAMESTATE->SetCompatibleStylesForPlayers(); + GAMESTATE->ForceSharedSidesMatch(); /* If we're currently waiting on song assets, abort all except the music * and start the music, so if we make a choice quickly before background @@ -1829,6 +1804,10 @@ void ScreenSelectMusic::AfterMusicChange() } SongUtil::GetPlayableSteps( pSong, m_vpSteps ); + if(m_vpSteps.empty()) + { + //LuaHelpers::ReportScriptError("GetPlayableSteps returned nothing."); + } if ( PREFSMAN->m_bShowBanners ) g_sBannerPath = pSong->GetBannerPath(); diff --git a/src/Style.cpp b/src/Style.cpp index 7226095342..a628307574 100644 --- a/src/Style.cpp +++ b/src/Style.cpp @@ -47,7 +47,7 @@ void Style::GetTransformedNoteDataForStyle( PlayerNumber pn, const NoteData& ori noteDataOut.LoadTransformed( original, m_iColsPerPlayer, iNewToOriginalTrack ); } -GameInput Style::StyleInputToGameInput( int iCol, PlayerNumber pn ) const +void Style::StyleInputToGameInput( int iCol, PlayerNumber pn, vector& ret ) const { ASSERT_M( pn < NUM_PLAYERS && iCol < MAX_COLS_PER_PLAYER, ssprintf("P%i C%i", pn, iCol) ); @@ -65,12 +65,20 @@ GameInput Style::StyleInputToGameInput( int iCol, PlayerNumber pn ) const if( iThisInputCol == END_MAPPING ) break; + // A style can have multiple game inputs mapped to a single column, so + // we have to return all the game inputs that are valid. If only the + // first is returned, then holds will drop on other inputs that should + // be valid. -Kyz if( iThisInputCol == iCol ) - return GameInput( gc, gb ); + { + ret.push_back(GameInput( gc, gb )); + } } } - - FAIL_M( ssprintf("Invalid column number %i for player %i in the style %s", iCol, pn, m_szName) ); + if(unlikely(ret.empty())) + { + FAIL_M( ssprintf("Invalid column number %i for player %i in the style %s", iCol, pn, m_szName) ); + } }; int Style::GameInputToColumn( const GameInput &GameI ) const @@ -122,8 +130,9 @@ RString Style::ColToButtonName( int iCol ) const if( pzColumnName != NULL ) return pzColumnName; - GameInput GI = StyleInputToGameInput( iCol, PLAYER_1 ); - return INPUTMAPPER->GetInputScheme()->GetGameButtonName(GI.button); + vector GI; + StyleInputToGameInput( iCol, PLAYER_1, GI ); + return INPUTMAPPER->GetInputScheme()->GetGameButtonName(GI[0].button); } // Lua bindings diff --git a/src/Style.h b/src/Style.h index d7a5ab371f..2562d2bb8d 100644 --- a/src/Style.h +++ b/src/Style.h @@ -80,7 +80,7 @@ public: * This is primarily for Couple and Routine styles. */ bool m_bLockDifficulties; - GameInput StyleInputToGameInput( int iCol, PlayerNumber pn ) const; + void StyleInputToGameInput( int iCol, PlayerNumber pn, vector& ret ) const; /** * @brief Retrieve the column based on the game input. * @param GameI the game input.