#include "global.h" #include "ActorUtil.h" #include "BeginnerHelper.h" #include "GameState.h" #include "PrefsManager.h" #include "RageLog.h" #include "RageDisplay.h" #include "Steps.h" #include "Style.h" #include "Model.h" #include "ScreenDimensions.h" #include "ThemeManager.h" // "PLAYER_X" offsets are relative to the pad. // ex: Setting this to 10, and the HELPER to 300, will put the dancer at 310. #define PLAYER_X( px ) THEME->GetMetricF("BeginnerHelper",ssprintf("Player%dX",px+1)) // "HELPER" offsets effect the pad/dancer as a whole. // Their relative Y cooridinates are hard-coded for each other. #define HELPER_X THEME->GetMetricF("BeginnerHelper","HelperX") #define HELPER_Y THEME->GetMetricF("BeginnerHelper","HelperY") #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) enum Animation { ANIM_DANCE_PAD, ANIM_DANCE_PADS, ANIM_UP, ANIM_DOWN, ANIM_LEFT, ANIM_RIGHT, ANIM_JUMPLR, NUM_ANIMATIONS }; static const char *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]; } BeginnerHelper::BeginnerHelper() { m_bShowBackground = true; m_bInitialized = false; m_iLastRowChecked = m_iLastRowFlashed = 0; FOREACH_PlayerNumber( pn ) m_bPlayerEnabled[pn] = false; FOREACH_PlayerNumber( pn ) m_pDancer[pn] = new Model; m_pDancePad = new Model; } BeginnerHelper::~BeginnerHelper() { FOREACH_PlayerNumber( pn ) delete m_pDancer[pn]; delete m_pDancePad; } bool BeginnerHelper::Init( int iDancePadType ) { ASSERT( !m_bInitialized ); if( !CanUse(PLAYER_INVALID) ) return false; // If no players were successfully added, bail. { bool bAnyLoaded = false; for( int pn=0; pnGetPathG("BeginnerHelper","background") ); this->AddChild( m_sBackground ); //m_sBackground.SetXY( 1, 1 ); m_sFlash.Load( THEME->GetPathG("BeginnerHelper","flash") ); m_sFlash.SetXY( 0, 0 ); m_sFlash.SetDiffuseAlpha( 0 ); } // Load StepCircle graphics for( int lsc=0; lscGetPathG("BeginnerHelper","stepcircle") ); m_sStepCircle[lsc][lsce].SetZoom( 0 ); // Hide until needed. this->AddChild(&m_sStepCircle[lsc][lsce]); // Set StepCircle coordinates switch( lsce ) { case 0: m_sStepCircle[lsc][lsce].SetXY((HELPER_X+PLAYER_X(lsc)-80),HELPER_Y); break; // Left case 1: m_sStepCircle[lsc][lsce].SetXY((HELPER_X+PLAYER_X(lsc)+80),HELPER_Y); break; // Right case 2: m_sStepCircle[lsc][lsce].SetXY((HELPER_X+PLAYER_X(lsc)),(HELPER_Y-60)); break; // Up case 3: m_sStepCircle[lsc][lsce].SetXY((HELPER_X+PLAYER_X(lsc)),(HELPER_Y+60)); break; // Down } } } SHOW_DANCE_PAD.Load( "BeginnerHelper","ShowDancePad" ); // Load the DancePad if( SHOW_DANCE_PAD ) { switch( iDancePadType ) { case 0: break; // No pad case 1: m_pDancePad->LoadMilkshapeAscii(GetAnimPath(ANIM_DANCE_PAD)); break; case 2: m_pDancePad->LoadMilkshapeAscii(GetAnimPath(ANIM_DANCE_PADS)); break; } m_pDancePad->SetName("DancePad"); m_pDancePad->SetX( HELPER_X ); m_pDancePad->SetY( HELPER_Y ); ActorUtil::LoadAllCommands( m_pDancePad, "BeginnerHelper" ); } for( int pl=0; plm_pCurCharacters[pl]; ASSERT( Character != nullptr ); m_pDancer[pl]->SetName( ssprintf("PlayerP%d",pl+1) ); // Load textures m_pDancer[pl]->LoadMilkshapeAscii( Character->GetModelPath() ); // Load needed animations m_pDancer[pl]->LoadMilkshapeAsciiBones( "Step-LEFT", GetAnimPath(ANIM_LEFT) ); m_pDancer[pl]->LoadMilkshapeAsciiBones( "Step-DOWN", GetAnimPath(ANIM_DOWN) ); m_pDancer[pl]->LoadMilkshapeAsciiBones( "Step-UP", GetAnimPath(ANIM_UP) ); m_pDancer[pl]->LoadMilkshapeAsciiBones( "Step-RIGHT", GetAnimPath(ANIM_RIGHT) ); m_pDancer[pl]->LoadMilkshapeAsciiBones( "Step-JUMPLR", GetAnimPath(ANIM_JUMPLR) ); m_pDancer[pl]->LoadMilkshapeAsciiBones( "rest", Character->GetRestAnimationPath() ); m_pDancer[pl]->SetDefaultAnimation( "rest" ); // Stay bouncing after a step has finished animating m_pDancer[pl]->PlayAnimation( "rest" ); m_pDancer[pl]->SetX( HELPER_X+PLAYER_X(pl) ); m_pDancer[pl]->SetY( HELPER_Y+10 ); ActorUtil::LoadAllCommandsAndOnCommand( m_pDancer[pl], "BeginnerHelper" ); // many of the models floating around have the vertex order flipped, so force this. m_pDancer[pl]->SetCullMode( CULL_NONE ); } m_bInitialized = true; return true; } void BeginnerHelper::ShowStepCircle( PlayerNumber pn, int CSTEP ) { int isc=0; // Save OR issues within array boundries.. it's worth the extra few bytes of memory. switch(CSTEP) { case ST_LEFT: isc=0; break; case ST_RIGHT: isc=1; break; case ST_UP: isc=2; break; case ST_DOWN: isc=3; break; } m_sStepCircle[pn][isc].StopEffect(); m_sStepCircle[pn][isc].SetZoom( 2 ); m_sStepCircle[pn][isc].StopTweening(); m_sStepCircle[pn][isc].BeginTweening( GAMESTATE->m_Position.m_fCurBPS/3, TWEEN_LINEAR ); m_sStepCircle[pn][isc].SetZoom( 0 ); } void BeginnerHelper::AddPlayer( PlayerNumber pn, const NoteData &ns ) { ASSERT( !m_bInitialized ); ASSERT( pn >= 0 && pn < NUM_PLAYERS ); ASSERT( GAMESTATE->IsHumanPlayer(pn) ); if( !CanUse(pn) ) return; const Character *Character = GAMESTATE->m_pCurCharacters[pn]; ASSERT( Character != nullptr ); if( !DoesFileExist(Character->GetModelPath()) ) return; m_NoteData[pn].CopyAll( ns ); m_bPlayerEnabled[pn] = true; } bool BeginnerHelper::CanUse(PlayerNumber pn) { for (int i=0; iGetCurrentStyle(PLAYER_1)->m_bCanUseBeginnerHelper || GAMESTATE->GetCurrentStyle(PLAYER_2)->m_bCanUseBeginnerHelper; } return GAMESTATE->GetCurrentStyle(pn)->m_bCanUseBeginnerHelper; } void BeginnerHelper::DrawPrimitives() { // If not initialized, don't bother with this if( !m_bInitialized ) return; ActorFrame::DrawPrimitives(); m_sFlash.Draw(); bool DrawCelShaded = PREFSMAN->m_bCelShadeModels; // Draw Pad if( SHOW_DANCE_PAD ) { if( DrawCelShaded ) m_pDancePad->DrawCelShaded(); else { DISPLAY->SetLighting( true ); DISPLAY->SetLightDirectional( 0, RageColor(0.5f,0.5f,0.5f,1), RageColor(1,1,1,1), RageColor(0,0,0,1), RageVector3(0, 0, 1) ); m_pDancePad->Draw(); DISPLAY->ClearZBuffer(); // So character doesn't step "into" the dance pad. DISPLAY->SetLightOff( 0 ); DISPLAY->SetLighting( false ); } } // Draw StepCircles for(int scd=0; scdIsHumanPlayer(pn) ) m_pDancer[pn]->DrawCelShaded(); } else { DISPLAY->SetLighting( true ); DISPLAY->SetLightDirectional( 0, RageColor(0.5f,0.5f,0.5f,1), RageColor(1,1,1,1), RageColor(0,0,0,1), RageVector3(0, 0, 1) ); FOREACH_PlayerNumber( pn ) if( GAMESTATE->IsHumanPlayer(pn) ) m_pDancer[pn]->Draw(); DISPLAY->SetLightOff( 0 ); DISPLAY->SetLighting( false ); } } void BeginnerHelper::Step( PlayerNumber pn, int CSTEP ) { m_pDancer[pn]->StopTweening(); m_pDancer[pn]->SetRotationY( 0 ); // Make sure we're not still inside of a JUMPUD tween. switch(CSTEP) { case ST_LEFT: ShowStepCircle( pn, ST_LEFT ); m_pDancer[pn]->PlayAnimation( "Step-LEFT", 1.5f ); break; case ST_RIGHT: ShowStepCircle( pn, ST_RIGHT ); m_pDancer[pn]->PlayAnimation( "Step-RIGHT", 1.5f ); break; case ST_UP: ShowStepCircle( pn, ST_UP ); m_pDancer[pn]->PlayAnimation( "Step-UP", 1.5f ); break; case ST_DOWN: ShowStepCircle( pn, ST_DOWN ); m_pDancer[pn]->PlayAnimation( "Step-DOWN", 1.5f ); break; case ST_JUMPLR: ShowStepCircle( pn, ST_LEFT ); ShowStepCircle( pn, ST_RIGHT ); m_pDancer[pn]->PlayAnimation( "Step-JUMPLR", 1.5f ); break; case ST_JUMPUD: ShowStepCircle( pn, ST_UP ); ShowStepCircle( pn, ST_DOWN ); m_pDancer[pn]->StopTweening(); m_pDancer[pn]->PlayAnimation( "Step-JUMPLR", 1.5f ); m_pDancer[pn]->BeginTweening( GAMESTATE->m_Position.m_fCurBPS/8, TWEEN_LINEAR ); m_pDancer[pn]->SetRotationY( 90 ); m_pDancer[pn]->BeginTweening( 1/(GAMESTATE->m_Position.m_fCurBPS * 2) ); //sleep between jump-frames m_pDancer[pn]->BeginTweening( GAMESTATE->m_Position.m_fCurBPS /6, TWEEN_LINEAR ); m_pDancer[pn]->SetRotationY( 0 ); break; } m_sFlash.StopEffect(); m_sFlash.StopTweening(); m_sFlash.Sleep( GAMESTATE->m_Position.m_fCurBPS/16 ); m_sFlash.SetDiffuseAlpha( 1 ); m_sFlash.BeginTweening( 1/GAMESTATE->m_Position.m_fCurBPS * 0.5f ); m_sFlash.SetDiffuseAlpha( 0 ); } void BeginnerHelper::Update( float fDeltaTime ) { if( !m_bInitialized ) return; // the row we want to check on this update int iCurRow = BeatToNoteRowNotRounded( GAMESTATE->m_Position.m_fSongBeat + 0.4f ); FOREACH_EnabledPlayer( pn ) { for( int iRow=m_iLastRowChecked; iRowStep( pn, iStep ); } } // Make sure we don't accidentally scan a row 2x m_iLastRowChecked = iCurRow; // Update animations ActorFrame::Update( fDeltaTime ); m_pDancePad->Update( fDeltaTime ); m_sFlash.Update( fDeltaTime ); float beat = fDeltaTime*GAMESTATE->m_Position.m_fCurBPS; // If this is not a human player, the dancer is not shown FOREACH_HumanPlayer( pu ) { // Update dancer's animation and StepCircles m_pDancer[pu]->Update( beat ); for( int scu=0; scu