From ad7c67b0ce2a8fe3a67ad5500acf1832b725a20b Mon Sep 17 00:00:00 2001 From: Andrew Livy Date: Mon, 10 Nov 2003 23:25:44 +0000 Subject: [PATCH] Added: 2D DancingCharacter System, Ability to Show (or not show) Evaluation Screen on Fail. Evaluation Screen Fail / Passed BGA Overlays. --- stepmania/src/Background.h | 2 + stepmania/src/DancingCharacters.cpp | 142 +++++++++++++++++++++++++++- stepmania/src/DancingCharacters.h | 42 +++++++- stepmania/src/Player.cpp | 63 +++++++++++- stepmania/src/Player.h | 6 +- stepmania/src/ScreenEvaluation.cpp | 55 ++++++++++- stepmania/src/ScreenEvaluation.h | 7 +- stepmania/src/ScreenGameplay.cpp | 56 ++++++++++- 8 files changed, 357 insertions(+), 16 deletions(-) diff --git a/stepmania/src/Background.h b/stepmania/src/Background.h index 5c9ebdf62f..aa0a4c011d 100644 --- a/stepmania/src/Background.h +++ b/stepmania/src/Background.h @@ -41,6 +41,8 @@ public: virtual bool IsInDanger() { return m_bInDanger; }; + DancingCharacters* GetDancingCharacters() { return m_pDancingCharacters; }; + protected: bool IsDangerVisible(); void UpdateCurBGChange(); diff --git a/stepmania/src/DancingCharacters.cpp b/stepmania/src/DancingCharacters.cpp index 4af70cf34c..2387dd00e9 100644 --- a/stepmania/src/DancingCharacters.cpp +++ b/stepmania/src/DancingCharacters.cpp @@ -19,6 +19,8 @@ #include "song.h" #include "Character.h" +#define DC_X( choice ) THEME->GetMetricF("DancingCharacters",ssprintf("2DCharacterXP%d",choice+1)) +#define DC_Y( choice ) THEME->GetMetricF("DancingCharacters",ssprintf("2DCharacterYP%d",choice+1)) const float CAMERA_REST_DISTANCE = 32.f; const float CAMERA_REST_LOOK_AT_HEIGHT = -9.f; @@ -44,15 +46,86 @@ const float MODEL_ROTATIONY_TWO_PLAYERS[NUM_PLAYERS] = { -90, 90 }; DancingCharacters::DancingCharacters() { m_bDrawDangerLight = false; + for( int p=0; pIsPlayerEnabled(p) ) continue; Character* pChar = GAMESTATE->m_pCurCharacters[p]; if( !pChar ) continue; - + + // load in any potential 2D stuff + CString sCharacterDirectory = pChar->m_sCharDir; + CString sCurrentAnim; + sCurrentAnim = sCharacterDirectory + "2DIdle"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasIdleAnim[p] = m_bHas2DElements[p] = true; + m_bgIdle[p].LoadFromAniDir( sCurrentAnim ); + m_bgIdle[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DMiss"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasMissAnim[p] = m_bHas2DElements[p] = true; + m_bgMiss[p].LoadFromAniDir( sCurrentAnim ); + m_bgMiss[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DGood"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasGoodAnim[p] = m_bHas2DElements[p] = true; + m_bgGood[p].LoadFromAniDir( sCurrentAnim ); + m_bgGood[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DGreat"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasGreatAnim[p] = m_bHas2DElements[p] = true; + m_bgGreat[p].LoadFromAniDir( sCurrentAnim ); + m_bgGreat[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DFever"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasFeverAnim[p] = m_bHas2DElements[p] = true; + m_bgFever[p].LoadFromAniDir( sCurrentAnim ); + m_bgFever[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DFail"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasFailAnim[p] = m_bHas2DElements[p] = true; + m_bgFail[p].LoadFromAniDir( sCurrentAnim ); + m_bgFail[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DWin"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasWinAnim[p] = m_bHas2DElements[p] = true; + m_bgWin[p].LoadFromAniDir( sCurrentAnim ); + m_bgWin[p].SetXY(DC_X(p),DC_Y(p)); + } + + sCurrentAnim = sCharacterDirectory + "2DWinFever"; + if( DoesFileExist(sCurrentAnim + SLASH + "BGAnimation.ini") ) // check 2D Idle BGAnim exists + { + m_bHasWinFeverAnim[p] = m_bHas2DElements[p] = true; + m_bgWinFever[p].LoadFromAniDir( sCurrentAnim ); + m_bgWinFever[p].SetXY(DC_X(p),DC_Y(p)); + } + if( pChar->GetModelPath().empty() ) continue; @@ -182,6 +255,53 @@ void DancingCharacters::Update( float fDelta ) m_fThisCameraStartBeat = (float) iCurBeat; m_fThisCameraEndBeat = float(iCurBeat + 8); } + + // update any 2D stuff + for( int p=0; p 1.0f) + { + m_2DIdleTimer[p].SetZero(); + m_i2DAnimState[p] = AS2D_IDLE; + } + } + } + } + } +} + +void DancingCharacters::Change2DAnimState(int iPlayerNum, int iState) +{ + if(iPlayerNum >= NUM_PLAYERS) { ASSERT(0); } // player out of bounds? + if(iState >= AS2D_MAXSTATES) { ASSERT(0); } // invalid state? + + m_i2DAnimState[iPlayerNum] = iState; } void DancingCharacters::DrawPrimitives() @@ -235,4 +355,24 @@ void DancingCharacters::DrawPrimitives() DISPLAY->CameraPopMatrix(); + // now draw any potential 2D stuff + for( p=0; pTrace( "PlayerMinus::Load()", ); GAMESTATE->ResetNoteSkinsForPlayer( pn ); @@ -530,7 +531,33 @@ void PlayerMinus::Step( int col, RageTimer tm ) { case PC_HUMAN: { + if(GAMESTATE->m_CurGame == GAME_EZ2) + { + /* 1 is normal. 2 means scoring is half as hard; .5 means it's twice as hard. */ + /* Ez2 is only perfect / good / miss */ + float fScaledSecondsFromPerfect = fSecondsFromPerfect / PREFSMAN->m_fJudgeWindowScale; + if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowMarvelousSeconds ) score = TNS_PERFECT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowPerfectSeconds ) score = TNS_PERFECT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowGreatSeconds ) score = TNS_PERFECT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowGoodSeconds ) score = TNS_GOOD; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowBooSeconds ) score = TNS_MISS; + else score = TNS_NONE; + } + else if(GAMESTATE->m_CurGame == GAME_PNM) + { + /* PNM Goods = Great / Boo = Miss */ + float fScaledSecondsFromPerfect = fSecondsFromPerfect / PREFSMAN->m_fJudgeWindowScale; + if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowMarvelousSeconds ) score = TNS_MARVELOUS; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowPerfectSeconds ) score = TNS_PERFECT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowGreatSeconds ) score = TNS_GREAT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowGoodSeconds ) score = TNS_GREAT; + else if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowBooSeconds ) score = TNS_MISS; + else score = TNS_NONE; + } + + if( tn == TAP_MINE ) + { // stepped too close to mine? if( fScaledSecondsFromPerfect <= PREFSMAN->m_fJudgeWindowMineSeconds ) @@ -581,6 +608,17 @@ void PlayerMinus::Step( int col, RageTimer tm ) if(score == TNS_BOO) score = TNS_MISS; } + if ( GAMESTATE->m_CurGame == GAME_PNM ) // a boo is really miss on pnm, a good is really a great! + { + if(score == TNS_MARVELOUS) // in demo mode PC shouldnt hit marvelous + score = TNS_PERFECT; + + if(score == TNS_GOOD) + score = TNS_GREAT; + + if(score == TNS_BOO) + score = TNS_MISS; + } /* AI will generate misses here. Don't handle a miss like a regular note because * we want the judgment animation to appear delayed. Instead, return early if @@ -598,7 +636,7 @@ void PlayerMinus::Step( int col, RageTimer tm ) } break; case PC_AUTOPLAY: - if(GAMESTATE->m_CurGame == GAME_EZ2) + if(GAMESTATE->m_CurGame == GAME_EZ2 || GAMESTATE->m_CurGame == GAME_PNM) // these gametypes never hit marvelous on autoplay { score = TNS_PERFECT; } @@ -615,7 +653,7 @@ void PlayerMinus::Step( int col, RageTimer tm ) score = TNS_NONE; break; } - + if( score != TNS_NONE && score != TNS_MISS ) { @@ -645,8 +683,27 @@ void PlayerMinus::Step( int col, RageTimer tm ) if( IsRowCompletelyJudged(iIndexOverlappingNote) ) OnRowCompletelyJudged( iIndexOverlappingNote ); + + if( score == TNS_MISS || score == TNS_BOO ) + { + m_iDCState = AS2D_MISS; + } + if( score == TNS_GOOD || score == TNS_GREAT ) + { + m_iDCState = AS2D_GOOD; + } + if( score == TNS_PERFECT || score == TNS_MARVELOUS ) + { + m_iDCState = AS2D_GREAT; + if(m_pLifeMeter->GetLife() == 1.0f) // full life + { + m_iDCState = AS2D_FEVER; // super celebrate time :) + } + } } + + if( bGrayArrowStep ) m_pNoteField->Step( col ); @@ -896,7 +953,7 @@ void PlayerMinus::HandleTapRowScore( unsigned row ) case TNS_MISS: ++GAMESTATE->m_CurStageStats.iCurMissCombo[m_PlayerNumber]; - + m_iDCState = AS2D_MISS; // update dancing 2d characters that may have missed a note case TNS_GOOD: case TNS_BOO: if( iCurCombo > 50 ) diff --git a/stepmania/src/Player.h b/stepmania/src/Player.h index a1333c4577..ba27ee7110 100644 --- a/stepmania/src/Player.h +++ b/stepmania/src/Player.h @@ -27,6 +27,7 @@ #include "RageTimer.h" #include "ProTimingDisplay.h" #include "RageSound.h" +#include "DancingCharacters.h" class ScoreDisplay; class LifeMeter; @@ -51,7 +52,8 @@ public: void Step( int col, RageTimer tm ); void RandomiseNotes( int iNoteRow ); void FadeToFail(); - + int GetDancingCharacterState() { return m_iDCState; }; + void SetCharacterState(int iDCState) { m_iDCState = iDCState; }; protected: void UpdateTapNotesMissedOlderThan( float fMissIfOlderThanThisBeat ); void OnRowCompletelyJudged( int iStepIndex ); @@ -79,6 +81,8 @@ protected: Combo m_Combo; + int m_iDCState; +// DancingCharacters* m_pDancingCharacters; // used to adjust the 2D anims dancing states LifeMeter* m_pLifeMeter; CombinedLifeMeter* m_pCombinedLifeMeter; ScoreDisplay* m_pScore; diff --git a/stepmania/src/ScreenEvaluation.cpp b/stepmania/src/ScreenEvaluation.cpp index cc3b8b04d2..a7a9524244 100644 --- a/stepmania/src/ScreenEvaluation.cpp +++ b/stepmania/src/ScreenEvaluation.cpp @@ -45,6 +45,7 @@ const char* JUDGE_STRING[NUM_JUDGE_LINES] = // #define JUDGE_SOUND_TIME( name ) THEME->GetMetricF( m_sName, ssprintf("JudgeSoundTime%s", name) ); // #define SOUND_ON_FULL_ALPHA THEME->GetMetricB( m_sName, "JudgeSoundIfJudgeGraphicsFullAlpha" ) // metrics that are specific to classes derived from ScreenEvaluation +#define FAILED_SCREEN THEME->GetMetric (m_sName, "FailedScreen") #define NEXT_SCREEN THEME->GetMetric (m_sName,"NextScreen") #define END_SCREEN THEME->GetMetric (m_sName,"EndScreen") #define SHOW_BANNER_AREA THEME->GetMetricB(m_sName,"ShowBannerArea") @@ -91,6 +92,8 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) LOG->Trace( "ScreenEvaluation::ScreenEvaluation()" ); + m_bFailed = false; // the evaluation is not showing failed results by default + m_sName = sClassName; if( !TYPE.CompareNoCase("stage") ) m_Type = stage; @@ -107,6 +110,10 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) // Figure out which statistics and songs we're going to display // StageStats stageStats; + + if(stageStats.AllFailed() ) // if everyone failed + m_bFailed = true; // flag it for this screen + vector vSongsToShow; switch( m_Type ) { @@ -189,6 +196,8 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) continue; // skip if( !GAMESTATE->m_SongOptions.m_bSaveScore ) continue; // skip + if( m_bFailed ) + continue; // skip switch( m_Type ) { @@ -274,7 +283,6 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) } } - // If both players get a machine high score, a player // whose score is added later may bump the players who were // added earlier. Adjust for this. @@ -284,6 +292,9 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) continue; // skip if( iMachineHighScoreIndex[p] == -1 ) // no record continue; // skip + if( m_bFailed ) // both players failed + continue; // skip + for( int p2=0; p2IsHumanPlayer(p2) ) @@ -321,8 +332,16 @@ ScreenEvaluation::ScreenEvaluation( CString sClassName ) : Screen(sClassName) m_Menu.Load( m_sName ); this->AddChild( &m_Menu ); - - + if(m_bFailed && m_Type==stage) + { + m_bgFailedBack.LoadFromAniDir( THEME->GetPathToB("ScreenEvaluationStage Failed Background") ); + m_bgFailedOverlay.LoadFromAniDir( THEME->GetPathToB("ScreenEvaluationStage Failed Overlay") ); + } + else if( m_Type == stage ) + { + // the themer can use the regular background for passed background + m_bgPassedOverlay.LoadFromAniDir( THEME->GetPathToB("ScreenEvaluationStage Passed Overlay") ); + } // // init banner area // @@ -971,6 +990,14 @@ void ScreenEvaluation::Update( float fDeltaTime ) { Screen::Update( fDeltaTime ); + if(m_bFailed && m_Type == stage) + { + m_bgFailedBack.Update( fDeltaTime ); + m_bgFailedOverlay.Update( fDeltaTime ); + } + else if (m_Type == stage) + m_bgPassedOverlay.Update( fDeltaTime ); + for( int p=0; pm_CurStageStats.iBonus[p] == 0) @@ -1021,7 +1048,19 @@ void ScreenEvaluation::Update( float fDeltaTime ) void ScreenEvaluation::DrawPrimitives() { m_Menu.DrawBottomLayer(); + + // draw the failed background here if the player(s) failed + if(m_bFailed && m_Type == stage) + m_bgFailedBack.Draw(); + Screen::DrawPrimitives(); + + // draw the pass / failed overlays here respectively + if(m_bFailed && m_Type == stage) + m_bgFailedOverlay.Draw(); + else if (m_Type == stage) + m_bgPassedOverlay.Draw(); + m_Menu.DrawTopLayer(); } @@ -1043,13 +1082,19 @@ void ScreenEvaluation::HandleScreenMessage( const ScreenMessage SM ) MenuStart( PLAYER_INVALID ); break; case SM_GoToNextScreen: - SCREENMAN->SetNewScreen( NEXT_SCREEN ); + if(m_bFailed && !PREFSMAN->m_bEventMode) // if failed and not in event mode go to gameover screen + SCREENMAN->SetNewScreen( FAILED_SCREEN ); + else + SCREENMAN->SetNewScreen( NEXT_SCREEN ); break; case SM_GoToSelectCourse: SCREENMAN->SetNewScreen( "ScreenSelectCourse" ); break; case SM_GoToEndScreen: - SCREENMAN->SetNewScreen( END_SCREEN ); + if(m_bFailed && !PREFSMAN->m_bEventMode) // if failed and not in event mode go to gameover screen + SCREENMAN->SetNewScreen( FAILED_SCREEN ); + else + SCREENMAN->SetNewScreen( END_SCREEN ); break; // case SM_GoToEvaluationSummary: // SCREENMAN->SetNewScreen( "ScreenEvaluationSummary" ); diff --git a/stepmania/src/ScreenEvaluation.h b/stepmania/src/ScreenEvaluation.h index 094ed621bc..1ac00e91f6 100644 --- a/stepmania/src/ScreenEvaluation.h +++ b/stepmania/src/ScreenEvaluation.h @@ -56,7 +56,11 @@ protected: Type m_Type; float m_fScreenCreateTime; - + + BGAnimation m_bgFailedBack; + BGAnimation m_bgFailedOverlay; + BGAnimation m_bgPassedOverlay; + MenuElements m_Menu; // banner area @@ -115,6 +119,7 @@ protected: bool m_bTryExtraStage; Sprite m_sprTryExtraStage; AutoActor m_FullCombo[NUM_PLAYERS]; + bool m_bFailed; }; #endif diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index be39794965..df7335eab0 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -54,6 +54,7 @@ #define SHOW_LIFE_METER_FOR_DISABLED_PLAYERS THEME->GetMetricB("ScreenGameplay","ShowLifeMeterForDisabledPlayers") #define STATICBG_X THEME->GetMetricI ("ScreenGameplay","StaticBGX") #define STATICBG_Y THEME->GetMetricI ("ScreenGameplay","StaticBGY") +#define EVAL_ON_FAIL THEME->GetMetricB ("ScreenGameplay", "ShowEvaluationOnFail") CachedThemeMetricF SECONDS_BETWEEN_COMMENTS ("ScreenGameplay","SecondsBetweenComments"); CachedThemeMetricF G_TICK_EARLY_SECONDS ("ScreenGameplay","TickEarlySeconds"); @@ -1094,7 +1095,22 @@ void ScreenGameplay::Update( float fDeltaTime ) // check for fail // UpdateCheckFail(); - + + // + // update 2d dancing characters + // + for( int p=0; pIsPlayerEnabled(p) ) + continue; + if(m_Background.GetDancingCharacters() != NULL) + { + if(m_Player[p].GetDancingCharacterState() != AS2D_IGNORE) // grab the state of play from player and update the character + m_Background.GetDancingCharacters()->Change2DAnimState(p,m_Player[p].GetDancingCharacterState()); + m_Player[p].SetCharacterState(AS2D_IGNORE); // set to ignore as we've already grabbed the latest change + } + } + // // Check for enemy death in enemy battle // @@ -1662,6 +1678,21 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) m_NextSongOut.StartTransitioning( SM_LoadNextSong ); return; } + + // update dancing characters for win / lose + for(p=0; pIsPlayerEnabled(p) && GAMESTATE->m_CurStageStats.bFailed[p] ) + m_Background.GetDancingCharacters()->Change2DAnimState(p,AS2D_FAIL); // fail anim + else + { + if(m_pLifeMeter[p]->GetLife() == 1.0f) // full life + m_Background.GetDancingCharacters()->Change2DAnimState(p,AS2D_WINFEVER); // full life pass anim + else + m_Background.GetDancingCharacters()->Change2DAnimState(p,AS2D_WIN); // pass anim + } + + } /* End round. */ if( m_DancingState == STATE_OUTRO ) // ScreenGameplay already ended @@ -1712,6 +1743,7 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) SOUND->PlayOnceFromDir( ANNOUNCER->GetPathTo("gameplay cleared") ); } } + break; case SM_LoadNextSong: @@ -1945,11 +1977,29 @@ void ScreenGameplay::HandleScreenMessage( const ScreenMessage SM ) case PLAY_MODE_BATTLE: case PLAY_MODE_RAVE: if( PREFSMAN->m_bEventMode ) - HandleScreenMessage( SM_GoToScreenAfterBack ); + { + if(EVAL_ON_FAIL) // go to the eval screen if we fail + { + SCREENMAN->SetNewScreen( "ScreenEvaluationStage" ); + } + else // the theme says just fail and go back to the song select for event mode + { + HandleScreenMessage( SM_GoToScreenAfterBack ); + } + } else if( GAMESTATE->IsExtraStage() || GAMESTATE->IsExtraStage2() ) SCREENMAN->SetNewScreen( "ScreenEvaluationStage" ); else - SCREENMAN->SetNewScreen( "ScreenGameOver" ); + { + if(EVAL_ON_FAIL) // go to the eval screen if we fail + { + SCREENMAN->SetNewScreen( "ScreenEvaluationStage" ); + } + else // if not just game over now + { + SCREENMAN->SetNewScreen( "ScreenGameOver" ); + } + } break; case PLAY_MODE_NONSTOP: SCREENMAN->SetNewScreen( "ScreenEvaluationNonstop" );