2012-07-02 16:57:22 -05:00
#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 < int > NUM_W2S ( "ScreenHowToPlay" , "NumW2s" );
static const ThemeMetric < int > NUM_MISSES ( "ScreenHowToPlay" , "NumMisses" );
static const ThemeMetric < bool > USE_CHARACTER ( "ScreenHowToPlay" , "UseCharacter" );
static const ThemeMetric < bool > USE_PAD ( "ScreenHowToPlay" , "UsePad" );
static const ThemeMetric < bool > USE_PLAYER ( "ScreenHowToPlay" , "UsePlayer" );
static const ThemeMetric < RString > 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 < Character *> vpCharacters ;
CHARMAN -> GetCharacters ( vpCharacters );
if ( ( bool ) USE_CHARACTER && vpCharacters . size () && HaveAllCharAnimations () )
{
Character * displayChar ;
2014-07-25 01:39:33 -05:00
if ( ! CHARACTER_NAME . GetValue (). empty () && CHARMAN -> GetCharacterFromID ( CHARACTER_NAME . GetValue ()) )
displayChar = CHARMAN -> GetCharacterFromID ( CHARACTER_NAME . GetValue ());
2012-07-02 16:57:22 -05:00
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
m_pmCharacter -> RunCommands ( displayChar -> m_cmdInit ); // run InitCommand from file
ActorUtil :: LoadAllCommandsAndSetXY ( m_pmCharacter , m_sName );
}
}
2014-12-07 01:53:17 -07:00
GAMESTATE -> SetCurrentStyle ( GAMEMAN -> GetHowToPlayStyleForGame ( GAMESTATE -> m_pCurGame ), PLAYER_INVALID );
2012-07-02 16:57:22 -05:00
if ( USE_PLAYER )
{
2012-07-03 01:40:15 -05:00
GAMESTATE -> SetMasterPlayerNumber ( PLAYER_1 );
2012-07-02 16:57:22 -05:00
m_pLifeMeterBar = new LifeMeterBar ;
m_pLifeMeterBar -> Load ( GAMESTATE -> m_pPlayerState [ PLAYER_1 ], & STATSMAN -> m_CurStageStats . m_player [ PLAYER_1 ] );
2012-07-03 01:40:15 -05:00
m_pLifeMeterBar -> SetName ( "LifeMeterBar" );
2012-07-02 16:57:22 -05:00
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" );
SSCLoader loaderSSC ;
SMLoader loaderSM ;
if ( sStepsPath . Right ( 4 ) == ".ssc" )
loaderSSC . LoadFromSimfile ( sStepsPath , m_Song , false );
else
loaderSM . LoadFromSimfile ( sStepsPath , m_Song , false );
m_Song . AddAutoGenNotes ();
2014-12-07 01:53:17 -07:00
const Style * pStyle = GAMESTATE -> GetCurrentStyle ( PLAYER_INVALID );
2012-07-02 16:57:22 -05:00
Steps * pSteps = SongUtil :: GetClosestNotes ( & m_Song , pStyle -> m_StepsType , Difficulty_Beginner );
2014-12-22 07:09:21 -07:00
if ( pSteps == NULL )
{
LuaHelpers :: ReportScriptErrorFmt ( "No playable steps of StepsType '%s' for ScreenHowToPlay in file %s" , StringConversion :: ToString ( pStyle -> m_StepsType ). c_str (), sStepsPath . c_str ());
}
else
{
m_Song . m_SongTiming . TidyUpData ( false );
pSteps -> m_Timing . TidyUpData ( true );
NoteData tempNoteData ;
pSteps -> GetNoteData ( tempNoteData );
pStyle -> GetTransformedNoteDataForStyle ( PLAYER_1 , tempNoteData , m_NoteData );
2012-07-02 16:57:22 -05:00
2014-12-22 07:09:21 -07:00
GAMESTATE -> m_pCurSong . Set ( & m_Song );
GAMESTATE -> m_pCurSteps [ PLAYER_1 ]. Set ( pSteps );
GAMESTATE -> m_bGameplayLeadIn . Set ( false );
GAMESTATE -> m_pPlayerState [ PLAYER_1 ] -> m_PlayerController = PC_AUTOPLAY ;
2012-07-02 16:57:22 -05:00
2014-12-22 07:09:21 -07:00
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 );
2012-07-02 16:57:22 -05:00
2014-12-22 07:09:21 -07:00
// Don't show judgment
PO_GROUP_ASSIGN ( GAMESTATE -> m_pPlayerState [ PLAYER_1 ] -> m_PlayerOptions , ModsLevel_Stage , m_fBlind , 1.0f );
GAMESTATE -> m_bDemonstrationOrJukebox = true ;
}
2012-07-02 16:57:22 -05:00
}
// 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_Position . 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 ; k < iNumTracks ; k ++ )
2014-07-25 18:45:02 -05:00
if ( m_NoteData . GetTapNote ( k , iNoteRow ). type == TapNoteType_Tap )
2012-07-02 16:57:22 -05:00
iStep |= 1 << k ;
switch ( iStep )
{
case ST_LEFT : m_pmCharacter -> PlayAnimation ( "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_Position . m_fCurBPS / 8 , TWEEN_LINEAR );
m_pmCharacter -> SetRotationY ( 90 );
m_pmCharacter -> BeginTweening ( ( 1 / ( GAMESTATE -> m_Position . m_fCurBPS * 2 ) ) ); //sleep between jump-frames
m_pmCharacter -> BeginTweening ( GAMESTATE -> m_Position . m_fCurBPS / 6 , TWEEN_LINEAR );
m_pmCharacter -> SetRotationY ( 0 );
break ;
}
}
}
void ScreenHowToPlay :: Update ( float fDelta )
{
if ( GAMESTATE -> m_pCurSong != NULL )
{
2012-07-03 01:40:15 -05:00
RageTimer tm ;
2014-07-24 19:45:17 -05:00
GAMESTATE -> UpdateSongPosition ( m_fFakeSecondsIntoSong , GAMESTATE -> m_pCurSong -> m_SongTiming , tm );
2012-07-02 16:57:22 -05:00
m_fFakeSecondsIntoSong += fDelta ;
static int iLastNoteRowCounted = 0 ;
int iCurNoteRow = BeatToNoteRowNotRounded ( GAMESTATE -> m_Position . 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 < ScreenHowToPlay >
{
public :
static int GetLifeMeter ( T * p , lua_State * L )
{
//PlayerNumber pn = Enum::Check<PlayerNumber>( 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.
*/