#include "global.h" #include "ScreenHowToPlay.h" #include "ThemeManager.h" #include "GameState.h" #include "Steps.h" #include "GameManager.h" #include "NotesLoaderSM.h" #include "NotesLoaderSSC.h" #include "GameSoundManager.h" #include "Model.h" #include "ThemeMetric.h" #include "PlayerState.h" #include "Style.h" #include "PrefsManager.h" #include "CharacterManager.h" #include "StatsManager.h" #include "RageDisplay.h" #include "SongUtil.h" #include "Character.h" #include "LifeMeterBar.h" static const ThemeMetric NUM_W2S ("ScreenHowToPlay","NumW2s"); static const ThemeMetric NUM_MISSES ("ScreenHowToPlay","NumMisses"); static const ThemeMetric USE_CHARACTER ("ScreenHowToPlay","UseCharacter"); static const ThemeMetric USE_PAD ("ScreenHowToPlay","UsePad"); static const ThemeMetric USE_PLAYER ("ScreenHowToPlay","UsePlayer"); static const ThemeMetric CHARACTER_NAME("ScreenHowToPlay","CharacterName"); enum Animation { ANIM_DANCE_PAD, ANIM_DANCE_PADS, ANIM_UP, ANIM_DOWN, ANIM_LEFT, ANIM_RIGHT, ANIM_JUMPLR, NUM_ANIMATIONS }; static const RString anims[NUM_ANIMATIONS] = { "DancePad.txt", "DancePads.txt", "BeginnerHelper_step-up.bones.txt", "BeginnerHelper_step-down.bones.txt", "BeginnerHelper_step-left.bones.txt", "BeginnerHelper_step-right.bones.txt", "BeginnerHelper_step-jumplr.bones.txt" }; static RString GetAnimPath( Animation a ) { return RString("Characters/") + anims[a]; } static bool HaveAllCharAnimations() { for( int i = ANIM_UP; i < NUM_ANIMATIONS; ++i ) if( !DoesFileExist( GetAnimPath( (Animation) i ) ) ) return false; return true; } REGISTER_SCREEN_CLASS( ScreenHowToPlay ); ScreenHowToPlay::ScreenHowToPlay() { m_iW2s = 0; m_iNumW2s = NUM_W2S; // initialize these because they might not be used. m_pLifeMeterBar = NULL; m_pmCharacter = NULL; m_pmDancePad = NULL; } void ScreenHowToPlay::Init() { ScreenAttract::Init(); if( (bool)USE_PAD && DoesFileExist( GetAnimPath(ANIM_DANCE_PAD) ) ) { m_pmDancePad = new Model; m_pmDancePad->SetName( "Pad" ); m_pmDancePad->LoadMilkshapeAscii( GetAnimPath(ANIM_DANCE_PAD) ); // xxx: hardcoded rotation. can be undone, but still. -freem m_pmDancePad->SetRotationX( 35 ); ActorUtil::LoadAllCommandsAndSetXY( m_pmDancePad, m_sName ); } // Display a character vector vpCharacters; CHARMAN->GetCharacters( vpCharacters ); if( (bool)USE_CHARACTER && vpCharacters.size() && HaveAllCharAnimations() ) { Character* displayChar; if( !CHARACTER_NAME.GetValue().empty() && CHARMAN->GetCharacterFromID(CHARACTER_NAME) ) displayChar = CHARMAN->GetCharacterFromID(CHARACTER_NAME); else displayChar = CHARMAN->GetRandomCharacter(); RString sModelPath = displayChar->GetModelPath(); if( sModelPath != "" ) { m_pmCharacter = new Model; m_pmCharacter->SetName( "Character" ); m_pmCharacter->LoadMilkshapeAscii( displayChar->GetModelPath() ); m_pmCharacter->LoadMilkshapeAsciiBones( "Step-LEFT", GetAnimPath( ANIM_LEFT ) ); m_pmCharacter->LoadMilkshapeAsciiBones( "Step-DOWN", GetAnimPath( ANIM_DOWN ) ); m_pmCharacter->LoadMilkshapeAsciiBones( "Step-UP", GetAnimPath( ANIM_UP ) ); m_pmCharacter->LoadMilkshapeAsciiBones( "Step-RIGHT", GetAnimPath( ANIM_RIGHT ) ); m_pmCharacter->LoadMilkshapeAsciiBones( "Step-JUMPLR", GetAnimPath( ANIM_JUMPLR ) ); RString sRestFile = displayChar->GetRestAnimationPath(); ASSERT( !sRestFile.empty() ); m_pmCharacter->LoadMilkshapeAsciiBones( "rest",displayChar->GetRestAnimationPath() ); m_pmCharacter->SetDefaultAnimation( "rest" ); m_pmCharacter->PlayAnimation( "rest" ); // Stay bouncing after a step has finished animating. // xxx: hardcoded rotation. can be undone, but still. -freem m_pmCharacter->SetRotationX( 40 ); m_pmCharacter->SetCullMode( CULL_NONE ); // many of the models floating around have the vertex order flipped ActorUtil::LoadAllCommandsAndSetXY( m_pmCharacter, m_sName ); } } GAMESTATE->SetCurrentStyle( GAMEMAN->GetHowToPlayStyleForGame(GAMESTATE->m_pCurGame) ); if( USE_PLAYER ) { m_pLifeMeterBar = new LifeMeterBar; m_pLifeMeterBar->SetName("LifeMeterBar"); m_pLifeMeterBar->Load( GAMESTATE->m_pPlayerState[PLAYER_1], &STATSMAN->m_CurStageStats.m_player[PLAYER_1] ); ActorUtil::LoadAllCommandsAndSetXY( m_pLifeMeterBar, m_sName ); m_pLifeMeterBar->FillForHowToPlay( NUM_W2S, NUM_MISSES ); // Allow themers to use either a .ssc or .sm file for this. -aj RString sStepsPath = THEME->GetPathO(m_sName, "steps"); if( sStepsPath.Right(4) == ".ssc" ) SSCLoader::LoadFromSSCFile( sStepsPath, m_Song, false ); else SMLoader::LoadFromSMFile( sStepsPath, m_Song, false ); m_Song.AddAutoGenNotes(); const Style* pStyle = GAMESTATE->GetCurrentStyle(); Steps *pSteps = SongUtil::GetStepsByDescription( &m_Song, pStyle->m_StepsType, "" ); // todo: make StepsType human readable. -aj ASSERT_M( pSteps != NULL, "No playable steps for ScreenHowToPlay" ); NoteData tempNoteData; pSteps->GetNoteData( tempNoteData ); pStyle->GetTransformedNoteDataForStyle( PLAYER_1, tempNoteData, m_NoteData ); GAMESTATE->m_pCurSong.Set( &m_Song ); GAMESTATE->m_bGameplayLeadIn.Set( false ); GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_AUTOPLAY; m_Player->Init( "Player", GAMESTATE->m_pPlayerState[PLAYER_1], NULL, m_pLifeMeterBar, NULL, NULL, NULL, NULL, NULL, NULL ); m_Player.Load( m_NoteData ); m_Player->SetName( "Player" ); this->AddChild( m_Player ); ActorUtil::LoadAllCommandsAndSetXY( m_Player, m_sName ); // Don't show judgment PO_GROUP_ASSIGN( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions, ModsLevel_Stage, m_fBlind, 1.0f ); GAMESTATE->m_MasterPlayerNumber = PLAYER_1; GAMESTATE->m_bDemonstrationOrJukebox = true; } // deferred until after the player, so the notes go under it if( m_pLifeMeterBar ) this->AddChild( m_pLifeMeterBar ); if( m_pmDancePad ) this->AddChild( m_pmDancePad ); if( m_pmCharacter ) this->AddChild( m_pmCharacter ); m_fFakeSecondsIntoSong = 0; this->MoveToTail( &m_In ); this->MoveToTail( &m_Out ); } ScreenHowToPlay::~ScreenHowToPlay() { delete m_pLifeMeterBar; delete m_pmCharacter; delete m_pmDancePad; } void ScreenHowToPlay::Step() { // xxx: assumes dance. -freem #define ST_LEFT 0x01 #define ST_DOWN 0x02 #define ST_UP 0x04 #define ST_RIGHT 0x08 #define ST_JUMPLR (ST_LEFT | ST_RIGHT) #define ST_JUMPUD (ST_UP | ST_DOWN) int iStep = 0; const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat + 0.6f ); // if we want to miss from here on out, don't process steps. if( m_iW2s < m_iNumW2s && m_NoteData.IsThereATapAtRow( iNoteRow ) ) { const int iNumTracks = m_NoteData.GetNumTracks(); for( int k=0; kPlayAnimation( "Step-LEFT", 1.8f ); break; case ST_RIGHT: m_pmCharacter->PlayAnimation( "Step-RIGHT", 1.8f ); break; case ST_UP: m_pmCharacter->PlayAnimation( "Step-UP", 1.8f ); break; case ST_DOWN: m_pmCharacter->PlayAnimation( "Step-DOWN", 1.8f ); break; case ST_JUMPLR: m_pmCharacter->PlayAnimation( "Step-JUMPLR", 1.8f ); break; case ST_JUMPUD: // Until I can get an UP+DOWN jump animation, this will have to do. m_pmCharacter->PlayAnimation( "Step-JUMPLR", 1.8f ); m_pmCharacter->StopTweening(); m_pmCharacter->BeginTweening( GAMESTATE->m_fCurBPS /8, TWEEN_LINEAR ); m_pmCharacter->SetRotationY( 90 ); m_pmCharacter->BeginTweening( (1/(GAMESTATE->m_fCurBPS * 2) ) ); //sleep between jump-frames m_pmCharacter->BeginTweening( GAMESTATE->m_fCurBPS /6, TWEEN_LINEAR ); m_pmCharacter->SetRotationY( 0 ); break; } } } void ScreenHowToPlay::Update( float fDelta ) { if( GAMESTATE->m_pCurSong != NULL ) { GAMESTATE->UpdateSongPosition( m_fFakeSecondsIntoSong, GAMESTATE->m_pCurSong->m_Timing ); m_fFakeSecondsIntoSong += fDelta; static int iLastNoteRowCounted = 0; int iCurNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat ); if( iCurNoteRow != iLastNoteRowCounted &&m_NoteData.IsThereATapAtRow(iCurNoteRow) ) { if( m_pLifeMeterBar && !m_Player ) { if ( m_iW2s < m_iNumW2s ) m_pLifeMeterBar->ChangeLife( TNS_W2 ); else m_pLifeMeterBar->ChangeLife( TNS_Miss ); } m_iW2s++; iLastNoteRowCounted = iCurNoteRow; } // Once we hit the number of perfects we want, we want to fail. Switch // the controller to HUMAN. Since we aren't taking input, the steps will // always be misses. if( m_iW2s > m_iNumW2s ) GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_HUMAN; // Per the above code, we don't always want the character stepping. // If they try to make all of the steps in the miss part, they look // silly. Have then stand still instead. - freem if ( m_pmCharacter ) { if( m_iW2s <= m_iNumW2s ) Step(); } } ScreenAttract::Update( fDelta ); } void ScreenHowToPlay::HandleScreenMessage( const ScreenMessage SM ) { if( SM == SM_GainFocus ) { // We do this ourself. SOUND->HandleSongTimer( false ); } else if( SM == SM_LoseFocus ) { SOUND->HandleSongTimer( true ); } else if( SM == SM_GoToNextScreen ) { GAMESTATE->Reset(); } ScreenAttract::HandleScreenMessage( SM ); } // lua start #include "LuaBinding.h" /** @brief Allow Lua to have access to the ScreenHowToPlay. */ class LunaScreenHowToPlay: public Luna { public: static int GetLifeMeter( T* p, lua_State *L ) { //PlayerNumber pn = Enum::Check( L, 1 ); p->m_pLifeMeterBar->PushSelf( L ); return 1; } LunaScreenHowToPlay() { ADD_METHOD( GetLifeMeter ); } }; LUA_REGISTER_DERIVED_CLASS( ScreenHowToPlay, ScreenAttract ) // lua end /* * (c) 2001-2004 Chris Danford, Thad Ward * 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. */