From 9b307b198cf65ddd020f8b8e078b3742cf39db32 Mon Sep 17 00:00:00 2001 From: Chris Danford Date: Fri, 26 Aug 2005 17:39:53 +0000 Subject: [PATCH] add list of scores and judgments to multiplayer gameplay --- stepmania/src/Player.cpp | 287 +++++++++++++++----- stepmania/src/Player.h | 26 +- stepmania/src/PlayerScoreList.cpp | 145 ++++++++++ stepmania/src/PlayerScoreList.h | 68 +++++ stepmania/src/RageUtil.cpp | 10 +- stepmania/src/ScreenGameplay.cpp | 109 +++++--- stepmania/src/ScreenGameplay.h | 7 +- stepmania/src/ScreenGameplayMultiplayer.cpp | 4 - 8 files changed, 523 insertions(+), 133 deletions(-) create mode 100644 stepmania/src/PlayerScoreList.cpp create mode 100644 stepmania/src/PlayerScoreList.h diff --git a/stepmania/src/Player.cpp b/stepmania/src/Player.cpp index 1d64046efa..3f702a8fbe 100644 --- a/stepmania/src/Player.cpp +++ b/stepmania/src/Player.cpp @@ -39,8 +39,8 @@ #include "Profile.h" #include "StatsManager.h" #include "Steps.h" +#include "GameCommand.h" -CString JUDGMENT_X_NAME( size_t p, size_t both_sides ) { return "JudgmentXOffset" + (both_sides ? CString("BothSides") : ssprintf("OneSideP%d",int(p+1)) ); } CString COMBO_X_NAME( size_t p, size_t both_sides ) { return "ComboXOffset" + (both_sides ? CString("BothSides") : ssprintf("OneSideP%d",int(p+1)) ); } CString ATTACK_DISPLAY_X_NAME( size_t p, size_t both_sides ){ return "AttackDisplayXOffset" + (both_sides ? CString("BothSides") : ssprintf("OneSideP%d",int(p+1)) ); } @@ -90,7 +90,7 @@ float AdjustedWindowHold( HoldWindow hw, bool bIsPlayingBeginner ) #define ADJUSTED_WINDOW_HOLD( hw ) AdjustedWindowHold( hw, IsPlayingBeginner() ) -Player::Player( bool bShowNoteField ) +Player::Player( bool bShowNoteField, bool bShowJudgment ) { m_bLoaded = false; @@ -109,23 +109,43 @@ Player::Player( bool bShowNoteField ) m_bPaused = false; m_iOffsetSample = 0; - this->AddChild( &m_Judgment ); - this->AddChild( &m_Combo ); - this->AddChild( &m_AttackDisplay ); - for( int c=0; cAddChild( &m_HoldJudgment[c] ); + m_pJudgment = NULL; + if( bShowJudgment ) + { + m_pJudgment = new Judgment; + this->AddChild( m_pJudgment ); + } + + m_pCombo = NULL; + if( bShowNoteField ) + { + m_pCombo = new Combo; + this->AddChild( m_pCombo ); + } + + m_pAttackDisplay = NULL; + if( bShowNoteField ) + { + m_pAttackDisplay = new AttackDisplay; + this->AddChild( m_pAttackDisplay ); + } PlayerAI::InitFromDisk(); m_pNoteField = NULL; if( bShowNoteField ) + { m_pNoteField = new NoteField; + } this->SubscribeToMessage( Message_AutosyncChanged ); } Player::~Player() { + SAFE_DELETE( m_pJudgment ); + SAFE_DELETE( m_pCombo ); + SAFE_DELETE( m_pAttackDisplay ); SAFE_DELETE( m_pNoteField ); } @@ -144,11 +164,6 @@ void Player::Init( { GRAY_ARROWS_Y_STANDARD.Load(sType,"ReceptorArrowsYStandard"); GRAY_ARROWS_Y_REVERSE.Load(sType,"ReceptorArrowsYReverse"); - JUDGMENT_X.Load(sType,JUDGMENT_X_NAME,NUM_PLAYERS,2); - JUDGMENT_Y.Load(sType,"JudgmentY"); - JUDGMENT_Y_REVERSE.Load(sType,"JudgmentYReverse"); - JUDGMENT_CENTERED_ADDY.Load(sType,"JudgmentCenteredAddY"); - JUDGMENT_CENTERED_ADDY_REVERSE.Load(sType,"JudgmentCenteredAddYReverse"); COMBO_X.Load(sType,COMBO_X_NAME,NUM_PLAYERS,2); COMBO_Y.Load(sType,"ComboY"); COMBO_Y_REVERSE.Load(sType,"ComboYReverse"); @@ -166,6 +181,80 @@ void Player::Init( STOP_DRAWING_AT_PIXELS.Load(sType,"StopDrawingAtPixels"); MAX_PRO_TIMING_ERROR.Load(sType,"MaxProTimingError"); + if( m_pJudgment ) + { + // + // Init judgment positions + // + LuaExpression expr; + expr.SetFromExpression( THEME->GetMetric(sType,"JudgmentTransformCommandFunction") ); + + bool bPlayerUsingBothSides = GAMESTATE->GetCurrentStyle()->m_StyleType==ONE_PLAYER_TWO_SIDES; + Actor temp; + + int iEnabledPlayerIndex = -1; + int iNumEnabledPlayers = 0; + if( GAMESTATE->m_bMultiplayer ) + { + FOREACH_EnabledMultiPlayer( p ) + { + if( p == pPlayerState->m_mp ) + iEnabledPlayerIndex = iNumEnabledPlayers; + iNumEnabledPlayers++; + } + } + else + { + FOREACH_EnabledPlayer( p ) + { + if( p == pPlayerState->m_PlayerNumber ) + iEnabledPlayerIndex = iNumEnabledPlayers; + iNumEnabledPlayers++; + } + } + ASSERT( iEnabledPlayerIndex != -1 ); + ASSERT( iNumEnabledPlayers > 0 ); + + for( int i=0; iGet(); + expr.PushSelf( L ); + ASSERT( !lua_isnil(L, -1) ); + temp.PushSelf( L ); + LuaHelpers::Push( pPlayerState->m_PlayerNumber, L ); + LuaHelpers::Push( pPlayerState->m_mp, L ); + LuaHelpers::Push( iEnabledPlayerIndex, L ); + LuaHelpers::Push( iNumEnabledPlayers, L ); + LuaHelpers::Push( bPlayerUsingBothSides, L ); + LuaHelpers::Push( !!i, L ); + LuaHelpers::Push( !!j, L ); + lua_call( L, 8, 0 ); // 8 args, 0 results + LUA->Release(L); + + m_tsJudgment[i][j] = temp.DestTweenState(); + } + } + + // Load Judgment frame + if( GAMESTATE->m_bMultiplayer && !m_sprJudgmentFrame.IsLoaded() ) // only load the first time + { + GameCommand gc; + ASSERT( pPlayerState->m_mp != MultiPlayer_INVALID ); + gc.m_MultiPlayer = pPlayerState->m_mp; + Lua *L = LUA->Get(); + gc.PushSelf( L ); + lua_setglobal( L, "ThisGameCommand" ); + LUA->Release( L ); + + m_sprJudgmentFrame.Load( THEME->GetPathG(sType,"JudgmentFrame") ); + + LUA->UnsetGlobal( "ThisGameCommand" ); + this->AddChild( m_sprJudgmentFrame ); + } + } + m_pPlayerState = pPlayerState; m_pPlayerStageStats = pPlayerStageStats; m_pLifeMeter = pLM; @@ -207,13 +296,19 @@ void Player::Init( m_soundAttackLaunch.SetParams( p ); m_soundAttackEnding.SetParams( p ); - m_Combo.SetName( "Combo" ); - m_Combo.Load( m_pPlayerState, m_pPlayerStageStats ); - ActorUtil::OnCommand( m_Combo, sType ); + if( m_pCombo ) + { + m_pCombo->SetName( "Combo" ); + m_pCombo->Load( m_pPlayerState, m_pPlayerStageStats ); + ActorUtil::OnCommand( m_pCombo, sType ); + } - m_Judgment.SetName( "Judgment" ); - m_Judgment.Load( IsPlayingBeginner() ); - ActorUtil::OnCommand( m_Judgment, sType ); + if( m_pJudgment ) + { + m_pJudgment->SetName( "Judgment" ); + m_pJudgment->Load( IsPlayingBeginner() ); + ActorUtil::OnCommand( m_pJudgment, sType ); + } m_fNoteFieldHeight = GRAY_ARROWS_Y_REVERSE-GRAY_ARROWS_Y_STANDARD; if( m_pNoteField ) @@ -244,12 +339,18 @@ void Player::Load( const NoteData& noteData ) /* The editor reuses Players ... so we really need to make sure everything * is reset and not tweening. Perhaps ActorFrame should recurse to subactors; * then we could just this->StopTweening()? -glenn */ - m_Judgment.StopTweening(); -// m_Combo.Reset(); // don't reset combos between songs in a course! + if( m_pJudgment ) + m_pJudgment->StopTweening(); +// m_pCombo->Reset(); // don't reset combos between songs in a course! if( m_pPlayerStageStats ) - m_Combo.SetCombo( m_pPlayerStageStats->iCurCombo, m_pPlayerStageStats->iCurMissCombo ); // combo can persist between songs and games - m_AttackDisplay.Init( m_pPlayerState ); - m_Judgment.Reset(); + { + if( m_pCombo ) + m_pCombo->SetCombo( m_pPlayerStageStats->iCurCombo, m_pPlayerStageStats->iCurMissCombo ); // combo can persist between songs and games + } + if( m_pAttackDisplay ) + m_pAttackDisplay->Init( m_pPlayerState ); + if( m_pJudgment ) + m_pJudgment->Reset(); /* Don't re-init this; that'll reload graphics. Add a separate Reset() call * if some ScoreDisplays need it. */ @@ -301,16 +402,31 @@ void Player::Load( const NoteData& noteData ) const bool bReverse = GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions.GetReversePercentForColumn(0) == 1; bool bPlayerUsingBothSides = GAMESTATE->GetCurrentStyle()->m_StyleType==ONE_PLAYER_TWO_SIDES; - m_Combo.SetX( COMBO_X.GetValue(pn,bPlayerUsingBothSides) ); - m_Combo.SetY( bReverse ? COMBO_Y_REVERSE : COMBO_Y ); - m_AttackDisplay.SetX( ATTACK_DISPLAY_X.GetValue(pn,bPlayerUsingBothSides) - 40 ); - // set this in Update //m_AttackDisplay.SetY( bReverse ? ATTACK_DISPLAY_Y_REVERSE : ATTACK_DISPLAY_Y ); - m_Judgment.SetX( JUDGMENT_X.GetValue(pn,bPlayerUsingBothSides) ); - // set this in Update //m_Judgment.SetY( bReverse ? JUDGMENT_Y_REVERSE : JUDGMENT_Y ); + if( m_pCombo ) + { + m_pCombo->SetX( COMBO_X.GetValue(pn,bPlayerUsingBothSides) ); + m_pCombo->SetY( bReverse ? COMBO_Y_REVERSE : COMBO_Y ); + } + if( m_pAttackDisplay ) + m_pAttackDisplay->SetX( ATTACK_DISPLAY_X.GetValue(pn,bPlayerUsingBothSides) - 40 ); + // set this in Update //m_pAttackDisplay->SetY( bReverse ? ATTACK_DISPLAY_Y_REVERSE : ATTACK_DISPLAY_Y ); + + // set this in Update + //m_pJudgment->SetX( JUDGMENT_X.GetValue(pn,bPlayerUsingBothSides) ); + //m_pJudgment->SetY( bReverse ? JUDGMENT_Y_REVERSE : JUDGMENT_Y ); // Need to set Y positions of all these elements in Update since // they change depending on PlayerOptions. + + // Load HoldJudgments + FOREACH( HoldJudgment, m_vHoldJudgment, i ) + this->RemoveChild( i ); + m_vHoldJudgment.resize( GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer ); + FOREACH( HoldJudgment, m_vHoldJudgment, i ) + this->AddChild( i ); + + // // Load keysounds. If sounds are already loaded (as in the editor), don't reload them. // XXX: the editor will load several duplicate copies (in each NoteField), and each @@ -373,9 +489,9 @@ void Player::Update( float fDeltaTime ) const float fX = ArrowEffects::GetXPos( m_pPlayerState, c, 0 ); const float fZ = ArrowEffects::GetZPos( m_pPlayerState, c, 0 ); - m_HoldJudgment[c].SetX( fX ); - m_HoldJudgment[c].SetY( fHoldJudgeYPos ); - m_HoldJudgment[c].SetZ( fZ ); + m_vHoldJudgment[c].SetX( fX ); + m_vHoldJudgment[c].SetY( fHoldJudgeYPos ); + m_vHoldJudgment[c].SetZ( fZ ); } } @@ -385,14 +501,21 @@ void Player::Update( float fDeltaTime ) const bool bReverse = m_pPlayerState->m_PlayerOptions.GetReversePercentForColumn(0) == 1; float fPercentCentered = m_pPlayerState->m_CurrentPlayerOptions.m_fScrolls[PlayerOptions::SCROLL_CENTERED]; - m_Combo.SetY( - bReverse ? - COMBO_Y_REVERSE + fPercentCentered * COMBO_CENTERED_ADDY_REVERSE : - COMBO_Y + fPercentCentered * COMBO_CENTERED_ADDY ); - m_Judgment.SetY( - bReverse ? - JUDGMENT_Y_REVERSE + fPercentCentered * JUDGMENT_CENTERED_ADDY_REVERSE : - JUDGMENT_Y + fPercentCentered * JUDGMENT_CENTERED_ADDY ); + if( m_pCombo ) + { + m_pCombo->SetY( + bReverse ? + COMBO_Y_REVERSE + fPercentCentered * COMBO_CENTERED_ADDY_REVERSE : + COMBO_Y + fPercentCentered * COMBO_CENTERED_ADDY ); + } + if( m_pJudgment ) + { + const Actor::TweenState &ts1 = m_tsJudgment[bReverse?1:0][0]; + const Actor::TweenState &ts2 = m_tsJudgment[bReverse?1:0][1]; + Actor::TweenState::MakeWeightedAverage( m_pJudgment->DestTweenState(), ts1, ts2, fPercentCentered ); + if( m_sprJudgmentFrame.IsLoaded() ) + Actor::TweenState::MakeWeightedAverage( m_sprJudgmentFrame->DestTweenState(), ts1, ts2, fPercentCentered ); + } float fMiniPercent = m_pPlayerState->m_CurrentPlayerOptions.m_fEffects[PlayerOptions::EFFECT_MINI]; @@ -400,7 +523,10 @@ void Player::Update( float fDeltaTime ) float fJudgmentZoom = 1 - fMiniPercent*0.25f; if( m_pNoteField ) m_pNoteField->SetZoom( fNoteFieldZoom ); - m_Judgment.SetZoom( fJudgmentZoom ); + if( m_pJudgment ) + m_pJudgment->SetZoom( m_pJudgment->GetZoom() * fJudgmentZoom ); + if( m_sprJudgmentFrame.IsLoaded() ) + m_sprJudgmentFrame->SetZoom( m_sprJudgmentFrame->GetZoom() * fJudgmentZoom ); // If we're paused, don't update tap or hold note logic, so hold notes can be released // during pause. @@ -570,7 +696,7 @@ void Player::Update( float fDeltaTime ) { /* this note has been judged */ HandleHoldScore( hns, tns ); - m_HoldJudgment[iTrack].SetHoldJudgment( hns ); + m_vHoldJudgment[iTrack].SetHoldJudgment( hns ); int ms_error = (hns == HNS_OK)? 0:MAX_PRO_TIMING_ERROR; @@ -653,9 +779,6 @@ void Player::ApplyWaitingTransforms() void Player::DrawPrimitives() { - if( m_pNoteField == NULL ) - return; - // TODO: Remove use of PlayerNumber. PlayerNumber pn = m_pPlayerState->m_PlayerNumber; @@ -667,9 +790,13 @@ void Player::DrawPrimitives() // Draw these below everything else. if( m_pPlayerState->m_PlayerOptions.m_fBlind == 0 ) - m_Combo.Draw(); + { + if( m_pCombo ) + m_pCombo->Draw(); + } - m_AttackDisplay.Draw(); + if( m_pAttackDisplay ) + m_pAttackDisplay->Draw(); if( TAP_JUDGMENTS_UNDER_FIELD ) DrawTapJudgments(); @@ -689,29 +816,32 @@ void Player::DrawPrimitives() DISPLAY->LoadMenuPerspective( 45, SCALE(fSkew,0.f,1.f,this->GetX(),SCREEN_CENTER_X), fCenterY ); - float fOriginalY = m_pNoteField->GetY(); + if( m_pNoteField ) + { + float fOriginalY = m_pNoteField->GetY(); - float fTiltDegrees = SCALE(fTilt,-1.f,+1.f,+30,-30) * (bReverse?-1:1); + float fTiltDegrees = SCALE(fTilt,-1.f,+1.f,+30,-30) * (bReverse?-1:1); - float fZoom = SCALE( m_pPlayerState->m_CurrentPlayerOptions.m_fEffects[PlayerOptions::EFFECT_MINI], 0.f, 1.f, 1.f, 0.5f ); - if( fTilt > 0 ) - fZoom *= SCALE( fTilt, 0.f, 1.f, 1.f, 0.9f ); - else - fZoom *= SCALE( fTilt, 0.f, -1.f, 1.f, 0.9f ); + float fZoom = SCALE( m_pPlayerState->m_CurrentPlayerOptions.m_fEffects[PlayerOptions::EFFECT_MINI], 0.f, 1.f, 1.f, 0.5f ); + if( fTilt > 0 ) + fZoom *= SCALE( fTilt, 0.f, 1.f, 1.f, 0.9f ); + else + fZoom *= SCALE( fTilt, 0.f, -1.f, 1.f, 0.9f ); - float fYOffset; - if( fTilt > 0 ) - fYOffset = SCALE( fTilt, 0.f, 1.f, 0.f, -45.f ) * (bReverse?-1:1); - else - fYOffset = SCALE( fTilt, 0.f, -1.f, 0.f, -20.f ) * (bReverse?-1:1); + float fYOffset; + if( fTilt > 0 ) + fYOffset = SCALE( fTilt, 0.f, 1.f, 0.f, -45.f ) * (bReverse?-1:1); + else + fYOffset = SCALE( fTilt, 0.f, -1.f, 0.f, -20.f ) * (bReverse?-1:1); - m_pNoteField->SetY( fOriginalY + fYOffset ); - m_pNoteField->SetZoom( fZoom ); - m_pNoteField->SetRotationX( fTiltDegrees ); - m_pNoteField->Draw(); + m_pNoteField->SetY( fOriginalY + fYOffset ); + m_pNoteField->SetZoom( fZoom ); + m_pNoteField->SetRotationX( fTiltDegrees ); + m_pNoteField->Draw(); - m_pNoteField->SetY( fOriginalY ); + m_pNoteField->SetY( fOriginalY ); + } DISPLAY->CameraPopMatrix(); DISPLAY->PopMatrix(); @@ -729,7 +859,10 @@ void Player::DrawTapJudgments() if( m_pPlayerState->m_PlayerOptions.m_fBlind > 0 ) return; - m_Judgment.Draw(); + if( m_sprJudgmentFrame.IsLoaded() ) + m_sprJudgmentFrame->Draw(); + if( m_pJudgment ) + m_pJudgment->Draw(); } void Player::DrawHoldJudgments() @@ -744,7 +877,7 @@ void Player::DrawHoldJudgments() { NoteFieldMode::BeginDrawTrack( pn, c ); - m_HoldJudgment[c].Draw(); + m_vHoldJudgment[c].Draw(); NoteFieldMode::EndDrawTrack(c); } @@ -1062,7 +1195,8 @@ void Player::HandleStep( int col, const RageTimer &tm, bool bHeld ) bool bSteppedEarly = -fNoteOffset < 0; if( IsPlayingBeginner() && PREFSMAN->m_bMercifulBeginner && score==TNS_BOO && bSteppedEarly ) { - m_Judgment.SetJudgment( score, bSteppedEarly ); + if( m_pJudgment ) + m_pJudgment->SetJudgment( score, bSteppedEarly ); } else { @@ -1313,7 +1447,8 @@ void Player::OnRowCompletelyJudged( int iIndexThatWasSteppedOn ) DisplayJudgedRow( iIndexThatWasSteppedOn, score, c ); } - m_Judgment.SetJudgment( score, tnr.fTapNoteOffset < 0 ); + if( m_pJudgment ) + m_pJudgment->SetJudgment( score, tnr.fTapNoteOffset < 0 ); } else { @@ -1328,7 +1463,8 @@ void Player::OnRowCompletelyJudged( int iIndexThatWasSteppedOn ) DisplayJudgedRow( iIndexThatWasSteppedOn, score, c ); - m_Judgment.SetJudgment( score, tnr.fTapNoteOffset < 0 ); + if( m_pJudgment ) + m_pJudgment->SetJudgment( score, tnr.fTapNoteOffset < 0 ); } } @@ -1406,7 +1542,10 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds ) } if( iNumMissesFound > 0 ) - m_Judgment.SetJudgment( TNS_MISS, false ); + { + if( m_pJudgment ) + m_pJudgment->SetJudgment( TNS_MISS, false ); + } } @@ -1546,7 +1685,10 @@ void Player::HandleTapRowScore( unsigned row ) m_pSecondaryScoreKeeper->HandleTapRowScore( scoreOfLastTap, iNumTapsInRow ); if( m_pPlayerStageStats ) - m_Combo.SetCombo( m_pPlayerStageStats->iCurCombo, m_pPlayerStageStats->iCurMissCombo ); + { + if( m_pCombo ) + m_pCombo->SetCombo( m_pPlayerStageStats->iCurCombo, m_pPlayerStageStats->iCurMissCombo ); + } #define CROSSED( x ) (iOldCombo=x) if ( CROSSED(100) ) @@ -1689,7 +1831,8 @@ void Player::FadeToFail() m_pNoteField->FadeToFail(); // clear miss combo - m_Combo.SetCombo( 0, 0 ); + if( m_pCombo ) + m_pCombo->SetCombo( 0, 0 ); } bool Player::IsPlayingBeginner() const diff --git a/stepmania/src/Player.h b/stepmania/src/Player.h index 334274b855..6c52477ef1 100644 --- a/stepmania/src/Player.h +++ b/stepmania/src/Player.h @@ -26,7 +26,7 @@ class PlayerStageStats; class Player: public ActorFrame { public: - Player( bool bShowNoteField = true ); + Player( bool bShowNoteField = true, bool bShowJudgment = true ); ~Player(); virtual void Update( float fDeltaTime ); @@ -78,23 +78,24 @@ protected: bool m_bLoaded; - PlayerState* m_pPlayerState; - PlayerStageStats* m_pPlayerStageStats; + PlayerState *m_pPlayerState; + PlayerStageStats*m_pPlayerStageStats; float m_fNoteFieldHeight; bool m_bPaused; float m_fOffset[SAMPLE_COUNT]; // for AutoSync int m_iOffsetSample; - NoteField* m_pNoteField; + NoteField *m_pNoteField; - HoldJudgment m_HoldJudgment[MAX_NOTE_TRACKS]; + vector m_vHoldJudgment; - Judgment m_Judgment; + Judgment *m_pJudgment; + AutoActor m_sprJudgmentFrame; - Combo m_Combo; + Combo *m_pCombo; - AttackDisplay m_AttackDisplay; + AttackDisplay *m_pAttackDisplay; TapNoteScore m_LastTapNoteScore; LifeMeter* m_pLifeMeter; @@ -118,11 +119,6 @@ protected: ThemeMetric GRAY_ARROWS_Y_STANDARD; ThemeMetric GRAY_ARROWS_Y_REVERSE; - ThemeMetric2D JUDGMENT_X; - ThemeMetric JUDGMENT_Y; - ThemeMetric JUDGMENT_Y_REVERSE; - ThemeMetric JUDGMENT_CENTERED_ADDY; - ThemeMetric JUDGMENT_CENTERED_ADDY_REVERSE; ThemeMetric2D COMBO_X; ThemeMetric COMBO_Y; ThemeMetric COMBO_Y_REVERSE; @@ -139,6 +135,10 @@ protected: ThemeMetric START_DRAWING_AT_PIXELS; ThemeMetric STOP_DRAWING_AT_PIXELS; ThemeMetric MAX_PRO_TIMING_ERROR; + +#define NUM_REVERSE 2 +#define NUM_CENTERED 2 + TweenState m_tsJudgment[NUM_REVERSE][NUM_CENTERED]; }; #endif diff --git a/stepmania/src/PlayerScoreList.cpp b/stepmania/src/PlayerScoreList.cpp new file mode 100644 index 0000000000..9a475421f6 --- /dev/null +++ b/stepmania/src/PlayerScoreList.cpp @@ -0,0 +1,145 @@ +#include "global.h" +#include "PlayerScoreList.h" +#include "GameCommand.h" +#include "PlayerState.h" + + +// +// PlayerScoreItem +// +void PlayerScoreItem::Init( const PlayerState *pPlayerState, const PlayerStageStats *pPlayerStageStats ) +{ + m_sprFrame.Load( THEME->GetPathG("PlayerScoreItem","Frame") ); + this->AddChild( m_sprFrame ); + + m_score.Load( pPlayerState, pPlayerStageStats, "PlayerScoreItem Percent", true ); + this->AddChild( &m_score ); +} + + +// +// PlayerScoreList +// +PlayerScoreList::PlayerScoreList() +{ + m_timerRefreshCountdown.Touch(); +} + +static void PositionItem( LuaExpression &expr, Actor *pSelf, int iItemIndex, int iNumItems ) +{ + Lua *L = LUA->Get(); + expr.PushSelf( L ); + ASSERT( !lua_isnil(L, -1) ); + pSelf->PushSelf( L ); + LuaHelpers::Push( iItemIndex, L ); + LuaHelpers::Push( iNumItems, L ); + lua_call( L, 3, 0 ); // 3 args, 0 results + LUA->Release(L); +} + +void PlayerScoreList::Init( vector vpPlayerState, vector vpPlayerStageStats ) +{ + ASSERT( !vpPlayerState.empty() ); + ASSERT( !vpPlayerStageStats.empty() ); + + m_vpPlayerState = vpPlayerState; + m_vpPlayerStageStats = vpPlayerStageStats; + + m_vsprBullet.resize( vpPlayerState.size() ); + m_vScoreItem.resize( vpPlayerState.size() ); + + m_exprItemPositionCommandFunction.SetFromExpression( THEME->GetMetric("PlayerScoreList","ItemPositionCommandFunction") ); + + GameCommand gc; + FOREACH( const PlayerState*, m_vpPlayerState, iter ) + { + int i = iter - m_vpPlayerState.begin(); + + gc.m_iIndex = i; + gc.m_MultiPlayer = (*iter)->m_mp; + + Lua *L = LUA->Get(); + gc.PushSelf( L ); + lua_setglobal( L, "ThisGameCommand" ); + LUA->Release( L ); + + m_vsprBullet[i].Load( THEME->GetPathG("PlayerScoreList","Bullet") ); + PositionItem( m_exprItemPositionCommandFunction, m_vsprBullet[i], i, vpPlayerState.size() ); + this->AddChild( m_vsprBullet[i] ); + + m_vScoreItem[i].Init( vpPlayerState[i], vpPlayerStageStats[i] ); + this->AddChild( &m_vScoreItem[i] ); + + LUA->UnsetGlobal( "ThisGameCommand" ); + } +} + +void PlayerScoreList::Update( float fDelta ) +{ + ActorFrame::Update( fDelta ); + + bool bTimeToRefresh = m_timerRefreshCountdown.PeekDeltaTime() > 0.5f; + if( bTimeToRefresh ) + m_timerRefreshCountdown.Touch(); + + if( bTimeToRefresh ) + Refresh(); +} + +struct EnabledPlayerIndexAndScore +{ + int iEnabledPlayerIndex; + float fScore; + + bool operator<( const EnabledPlayerIndexAndScore &other ) const { return fScore > other.fScore; } +}; + +void PlayerScoreList::Refresh() +{ + vector v; + + FOREACH( const PlayerStageStats*, m_vpPlayerStageStats, iter ) + { + int i = iter - m_vpPlayerStageStats.begin(); + EnabledPlayerIndexAndScore t = { i, (*iter)->GetPercentDancePoints() }; + v.push_back( t ); + } + + stable_sort( v.begin(), v.end() ); + + FOREACH( EnabledPlayerIndexAndScore, v, iter ) + { + int i = iter - v.begin(); + PlayerScoreItem &psi = m_vScoreItem[iter->iEnabledPlayerIndex]; + psi.SetDrawOrder( -i ); + psi.BeginTweening( 0.5f, Actor::TWEEN_DECELERATE ); + PositionItem( m_exprItemPositionCommandFunction, &psi, i, m_vpPlayerStageStats.size() ); + } + + this->SortByDrawOrder(); +} + +/* + * (c) 2004 Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/PlayerScoreList.h b/stepmania/src/PlayerScoreList.h new file mode 100644 index 0000000000..1e2b15dbef --- /dev/null +++ b/stepmania/src/PlayerScoreList.h @@ -0,0 +1,68 @@ +#ifndef PlayerScoreList_H +#define PlayerScoreList_H + +#include "ActorFrame.h" +#include "AutoActor.h" +#include "PercentageDisplay.h" +class PlayerState; +class PlayerStageStats; + +class PlayerScoreItem : public ActorFrame +{ +public: + void Init( const PlayerState *pPlayerState, const PlayerStageStats *pPlayerStageStats ); + +protected: + AutoActor m_sprFrame; + PercentageDisplay m_score; +}; + +class PlayerScoreList : public ActorFrame +{ +public: + PlayerScoreList(); + + void Init( vector vpPlayerState, vector vpPlayerStageStats ); + + virtual void Update( float fDelta ); + + void Refresh(); + +protected: + vector m_vpPlayerState; + vector m_vpPlayerStageStats; + + LuaExpression m_exprItemPositionCommandFunction; // params: self,itemIndex,numItems + + RageTimer m_timerRefreshCountdown; + + vector m_vsprBullet; + vector m_vScoreItem; +}; + +#endif + +/* + * (c) 2004 Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ diff --git a/stepmania/src/RageUtil.cpp b/stepmania/src/RageUtil.cpp index c66d4aa013..7e7b4521cf 100644 --- a/stepmania/src/RageUtil.cpp +++ b/stepmania/src/RageUtil.cpp @@ -179,15 +179,17 @@ CString Commify( int iNum ) return sReturn; } +LuaFunction( FormatNumberAndSuffix, FormatNumberAndSuffix( IArg(1) ) ) + CString FormatNumberAndSuffix( int i ) { CString sSuffix; switch( i%10 ) { - case 1: sSuffix = "st"; break; - case 2: sSuffix = "nd"; break; - case 3: sSuffix = "rd"; break; - default: sSuffix = "th"; break; + case 1: sSuffix = "st"; break; + case 2: sSuffix = "nd"; break; + case 3: sSuffix = "rd"; break; + default: sSuffix = "th"; break; } // "11th", "113th", etc. diff --git a/stepmania/src/ScreenGameplay.cpp b/stepmania/src/ScreenGameplay.cpp index 77dd3be5a4..860ff998c4 100644 --- a/stepmania/src/ScreenGameplay.cpp +++ b/stepmania/src/ScreenGameplay.cpp @@ -56,6 +56,7 @@ #include "Player.h" #include "DifficultyIcon.h" #include "DifficultyMeter.h" +#include "PlayerScoreList.h" // // Defines @@ -120,29 +121,33 @@ void PlayerInfo::Load( PlayerNumber pn, MultiPlayer mp, bool bShowNoteField ) m_ptextCourseSongNumber = NULL; m_ptextStepsDescription = NULL; - switch( GAMESTATE->m_PlayMode ) + if( !IsMultiPlayer() ) { - case PLAY_MODE_REGULAR: - case PLAY_MODE_NONSTOP: - case PLAY_MODE_BATTLE: - case PLAY_MODE_RAVE: - if( PREFSMAN->m_bPercentageScoring ) - m_pPrimaryScoreDisplay = new ScoreDisplayPercentage; - else - m_pPrimaryScoreDisplay = new ScoreDisplayNormal; - break; - case PLAY_MODE_ONI: - case PLAY_MODE_ENDLESS: - if( GAMESTATE->m_SongOptions.m_LifeType == SongOptions::LIFE_TIME ) - m_pPrimaryScoreDisplay = new ScoreDisplayLifeTime; - else - m_pPrimaryScoreDisplay = new ScoreDisplayOni; - break; - default: - ASSERT(0); + switch( GAMESTATE->m_PlayMode ) + { + case PLAY_MODE_REGULAR: + case PLAY_MODE_NONSTOP: + case PLAY_MODE_BATTLE: + case PLAY_MODE_RAVE: + if( PREFSMAN->m_bPercentageScoring ) + m_pPrimaryScoreDisplay = new ScoreDisplayPercentage; + else + m_pPrimaryScoreDisplay = new ScoreDisplayNormal; + break; + case PLAY_MODE_ONI: + case PLAY_MODE_ENDLESS: + if( GAMESTATE->m_SongOptions.m_LifeType == SongOptions::LIFE_TIME ) + m_pPrimaryScoreDisplay = new ScoreDisplayLifeTime; + else + m_pPrimaryScoreDisplay = new ScoreDisplayOni; + break; + default: + ASSERT(0); + } } - m_pPrimaryScoreDisplay->Init( GetPlayerState(), GetPlayerStageStats() ); + if( m_pPrimaryScoreDisplay ) + m_pPrimaryScoreDisplay->Init( GetPlayerState(), GetPlayerStageStats() ); switch( GAMESTATE->m_PlayMode ) { @@ -179,14 +184,16 @@ void PlayerInfo::Load( PlayerNumber pn, MultiPlayer mp, bool bShowNoteField ) m_ptextPlayerOptions = NULL; m_pActiveAttackList = NULL; m_pWin = NULL; - m_pPlayer = new Player( bShowNoteField || mp == MultiPlayer_1 ); + m_pPlayer = new Player( bShowNoteField, true ); m_pInventory = NULL; m_pDifficultyIcon = NULL; m_pDifficultyMeter = NULL; if( IsMultiPlayer() ) { - *GetPlayerState() = *GAMESTATE->m_pPlayerState[PLAYER_1]; + GetPlayerState()->m_CurrentPlayerOptions = GAMESTATE->m_pPlayerState[PLAYER_1]->m_CurrentPlayerOptions; + GetPlayerState()->m_PlayerOptions = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; + GetPlayerState()->m_StoredPlayerOptions = GAMESTATE->m_pPlayerState[PLAYER_1]->m_StoredPlayerOptions; } } @@ -196,7 +203,7 @@ void PlayerInfo::LoadDummyP1() m_bIsDummy = true; // don't init any of the scoring objects - m_pPlayer = new Player( true ); // show NoteField + m_pPlayer = new Player( true, false ); m_PlayerStateDummy = *GAMESTATE->m_pPlayerState[PLAYER_1]; m_PlayerStateDummy.m_PlayerController = PC_AUTOPLAY; @@ -331,6 +338,8 @@ GetNextVisiblePlayerInfo( vector::iterator iter, vector ScreenGameplay::ScreenGameplay( CString sName ) : ScreenWithMenuElements(sName) { + m_pPlayerScoreList = NULL; + PLAYER_TYPE.Load( sName, "PlayerType" ); GIVE_UP_TEXT.Load( sName, "GiveUpText" ); GIVE_UP_ABORTED_TEXT.Load( sName, "GiveUpAbortedText" ); @@ -454,22 +463,20 @@ void ScreenGameplay::Init() pi->m_pPlayer->SetName( ssprintf("Player%s", pi->GetName().c_str()) ); // If pi->m_pn is set, then the player will be visible. If not, then it's not // visible and don't bother setting its position. - if( pi->m_pPlayer->HasNoteField() ) - { - float fPlayerX = PLAYER_X( pi->GetName(), GAMESTATE->GetCurrentStyle()->m_StyleType ); - /* Perhaps this should be handled better by defining a new - * StyleType for ONE_PLAYER_ONE_CREDIT_AND_ONE_COMPUTER, - * but for now just ignore SoloSingles when it's Battle or Rave - * Mode. This doesn't begin to address two-player solo (6 arrows) */ - if( PREFSMAN->m_bSoloSingle && - GAMESTATE->m_PlayMode != PLAY_MODE_BATTLE && - GAMESTATE->m_PlayMode != PLAY_MODE_RAVE && - GAMESTATE->GetCurrentStyle()->m_StyleType == ONE_PLAYER_ONE_SIDE ) - fPlayerX = SCREEN_CENTER_X; + float fPlayerX = PLAYER_X( pi->GetName(), GAMESTATE->GetCurrentStyle()->m_StyleType ); - pi->m_pPlayer->SetXY( fPlayerX, SCREEN_CENTER_Y ); - } + /* Perhaps this should be handled better by defining a new + * StyleType for ONE_PLAYER_ONE_CREDIT_AND_ONE_COMPUTER, + * but for now just ignore SoloSingles when it's Battle or Rave + * Mode. This doesn't begin to address two-player solo (6 arrows) */ + if( PREFSMAN->m_bSoloSingle && + GAMESTATE->m_PlayMode != PLAY_MODE_BATTLE && + GAMESTATE->m_PlayMode != PLAY_MODE_RAVE && + GAMESTATE->GetCurrentStyle()->m_StyleType == ONE_PLAYER_ONE_SIDE ) + fPlayerX = SCREEN_CENTER_X; + + pi->m_pPlayer->SetXY( fPlayerX, SCREEN_CENTER_Y ); this->AddChild( pi->m_pPlayer ); } @@ -605,6 +612,26 @@ void ScreenGameplay::Init() m_MaxCombo.SetText( ssprintf("%d", m_vPlayerInfo[0].GetPlayerStageStats()->iMaxCombo) ); // TODO: Make this work for both players this->AddChild( &m_MaxCombo ); + + if( GAMESTATE->m_bMultiplayer ) + { + vector vpPlayerState; + vector vpPlayerStageStats; + + FOREACH_EnabledMultiPlayer( p ) + { + vpPlayerState.push_back( m_vPlayerInfo[p].GetPlayerState() ); + vpPlayerStageStats.push_back( m_vPlayerInfo[p].GetPlayerStageStats() ); + } + + m_pPlayerScoreList = new PlayerScoreList; + m_pPlayerScoreList->Init( vpPlayerState, vpPlayerStageStats ); + m_pPlayerScoreList->SetName( "PlayerScoreList" ); + SET_XY( m_pPlayerScoreList ); + this->AddChild( m_pPlayerScoreList ); + } + + FOREACH_EnabledPlayerInfo( m_vPlayerInfo, pi ) { // primary score display @@ -924,6 +951,8 @@ ScreenGameplay::~ScreenGameplay() } LOG->Trace( "ScreenGameplay::~ScreenGameplay()" ); + + SAFE_DELETE( m_pPlayerScoreList ); if( !GAMESTATE->m_bDemonstrationOrJukebox ) MEMCARDMAN->UnPauseMountingThread(); @@ -2155,6 +2184,8 @@ void ScreenGameplay::Input( const DeviceInput& DeviceI, const InputEventType typ if( GAMESTATE->m_bMultiplayer ) { + MultiPlayer mp = MultiPlayer_INVALID; + // Translate input and sent to the appropriate player. Assume that all // joystick devices are mapped the same as the master player. if( DeviceI.IsJoystick() ) @@ -2169,7 +2200,7 @@ void ScreenGameplay::Input( const DeviceInput& DeviceI, const InputEventType typ StyleInput _StyleI; INPUTMAPPER->GameToStyle( _GameI, _StyleI ); - MultiPlayer mp = (MultiPlayer)(DeviceI.device - DEVICE_JOY1); + mp = (MultiPlayer)(DeviceI.device - DEVICE_JOY1); if( mp>=0 && mpStep( _StyleI.col, DeviceI.ts ); @@ -2582,6 +2613,7 @@ void ScreenGameplay::TweenOursOnScreen() ON_COMMAND( m_sprScoreFrame ); ON_COMMAND( m_BPMDisplay ); ON_COMMAND( m_MaxCombo ); + ON_COMMAND( m_pPlayerScoreList ); if( m_pCombinedLifeMeter ) ON_COMMAND( *m_pCombinedLifeMeter ); @@ -2622,6 +2654,7 @@ void ScreenGameplay::TweenOursOffScreen() OFF_COMMAND( m_sprScoreFrame ); OFF_COMMAND( m_BPMDisplay ); OFF_COMMAND( m_MaxCombo ); + OFF_COMMAND( m_pPlayerScoreList ); if( m_pCombinedLifeMeter ) OFF_COMMAND( *m_pCombinedLifeMeter ); diff --git a/stepmania/src/ScreenGameplay.h b/stepmania/src/ScreenGameplay.h index 40a7049ee3..cb9de37154 100644 --- a/stepmania/src/ScreenGameplay.h +++ b/stepmania/src/ScreenGameplay.h @@ -31,6 +31,7 @@ class DifficultyIcon; class DifficultyMeter; class Inventory; class ScoreKeeper; +class PlayerScoreList; AutoScreenMessage( SM_NotesEnded ) @@ -190,7 +191,9 @@ protected: RageTimer m_GiveUpTimer; void AbortGiveUp( bool bShowText ); - BitmapText m_MaxCombo; + BitmapText m_MaxCombo; // TODO: move this into an overlay + + PlayerScoreList *m_pPlayerScoreList; Transition m_Ready; Transition m_Go; @@ -200,7 +203,7 @@ protected: Transition m_Toasty; // easter egg Transition m_Draw; - BitmapText m_textSurviveTime; // used in extra stage + BitmapText m_textSurviveTime; // used in extra stage. TODO: Move this into a BGA AutoKeysounds m_AutoKeysounds; diff --git a/stepmania/src/ScreenGameplayMultiplayer.cpp b/stepmania/src/ScreenGameplayMultiplayer.cpp index aa755dc741..ccc7347ab5 100644 --- a/stepmania/src/ScreenGameplayMultiplayer.cpp +++ b/stepmania/src/ScreenGameplayMultiplayer.cpp @@ -12,10 +12,6 @@ ScreenGameplayMultiplayer::ScreenGameplayMultiplayer( CString sName ) : ScreenGa void ScreenGameplayMultiplayer::Init() { ScreenGameplay::Init(); - - // force the dummy player into autoplay - PlayerInfo &pi = m_vPlayerInfo.back(); - pi.LoadDummyP1(); // dummy autoplay NoteField } void ScreenGameplayMultiplayer::FillPlayerInfo( vector &vPlayerInfoOut )