2003-02-16 04:01:45 +00:00
#include "global.h"
2001-11-04 19:34:28 +00:00
/*
-----------------------------------------------------------------------------
2002-06-24 22:04:31 +00:00
Class: Player
2001-11-03 10:52:42 +00:00
2002-03-19 07:09:49 +00:00
Desc: See header.
2001-11-03 10:52:42 +00:00
2002-05-19 01:59:48 +00:00
Copyright (c) 2001-2002 by the person(s) listed below. All rights reserved.
2002-06-24 22:04:31 +00:00
Chris Danford
2001-11-04 19:34:28 +00:00
-----------------------------------------------------------------------------
*/
2003-04-10 05:46:31 +00:00
#include "Player.h"
2002-05-01 19:14:55 +00:00
#include "GameConstantsAndTypes.h"
2003-02-14 06:42:08 +00:00
#include <math.h> // for fabs()
2001-11-04 19:34:28 +00:00
#include "RageUtil.h"
2002-07-23 01:41:40 +00:00
#include "PrefsManager.h"
2002-05-01 19:14:55 +00:00
#include "GameConstantsAndTypes.h"
2002-02-24 10:31:20 +00:00
#include "ArrowEffects.h"
2002-04-16 17:31:00 +00:00
#include "GameManager.h"
#include "InputMapper.h"
2002-06-14 22:25:22 +00:00
#include "SongManager.h"
2002-07-23 01:41:40 +00:00
#include "GameState.h"
2002-11-03 07:17:03 +00:00
#include "ScoreKeeperMAX2.h"
2002-07-28 20:28:37 +00:00
#include "RageLog.h"
2002-10-28 05:30:45 +00:00
#include "RageMath.h"
2002-11-11 04:53:31 +00:00
#include "RageDisplay.h"
#include "ThemeManager.h"
2002-11-07 19:42:39 +00:00
#include "Combo.h"
2003-02-25 02:51:04 +00:00
#include "ScoreDisplay.h"
#include "LifeMeter.h"
2003-04-07 22:07:44 +00:00
#include "PlayerAI.h"
2003-04-14 22:32:08 +00:00
#include "NoteFieldPositioning.h"
2001-11-03 10:52:42 +00:00
2003-02-19 06:42:29 +00:00
#define GRAY_ARROWS_Y THEME->GetMetricF("Player","GrayArrowsY")
2003-04-14 22:12:54 +00:00
#define JUDGMENT_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? "JudgmentXOffsetBothSides" : ssprintf("JudgmentXOffsetOneSideP%d",p+1))
2003-02-19 06:42:29 +00:00
#define JUDGMENT_Y THEME->GetMetricF("Player","JudgmentY")
2003-04-14 22:12:54 +00:00
#define COMBO_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? "ComboXOffsetBothSides" : ssprintf("ComboXOffsetOneSideP%d",p+1))
2003-02-19 06:42:29 +00:00
#define COMBO_Y THEME->GetMetricF("Player","ComboY")
#define HOLD_JUDGMENT_Y THEME->GetMetricF("Player","HoldJudgmentY")
2003-03-26 19:15:38 +00:00
CachedThemeMetricI BRIGHT_GHOST_COMBO_THRESHOLD ( "Player" , "BrightGhostComboThreshold" );
2003-02-19 06:42:29 +00:00
#define START_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StartDrawingAtPixels")
#define STOP_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StopDrawingAtPixels")
2001-11-04 19:34:28 +00:00
2003-03-26 22:22:10 +00:00
/* Distance to search for a note in Step(). */
2003-04-07 22:07:44 +00:00
/* Units? */
2003-03-26 22:22:10 +00:00
static const float StepSearchDistanceBackwards = 1.0f ;
static const float StepSearchDistanceForwards = 1.0f ;
2002-02-24 01:43:11 +00:00
2003-04-07 21:24:14 +00:00
2002-04-03 21:33:51 +00:00
Player :: Player ()
2001-11-03 10:52:42 +00:00
{
2003-02-17 12:19:42 +00:00
BRIGHT_GHOST_COMBO_THRESHOLD . Refresh ();
2002-08-28 22:42:40 +00:00
2002-06-24 22:04:31 +00:00
m_PlayerNumber = PLAYER_INVALID ;
2003-05-19 08:18:21 +00:00
m_bShowJudgment = true ;
2001-11-03 10:52:42 +00:00
2002-04-28 20:42:32 +00:00
m_pLifeMeter = NULL ;
2003-06-30 18:08:27 +00:00
m_pCombinedLifeMeter = NULL ;
2002-04-28 20:42:32 +00:00
m_pScore = NULL ;
2003-06-30 18:08:27 +00:00
m_pPrimaryScoreKeeper = NULL ;
m_pSecondaryScoreKeeper = NULL ;
2003-02-25 02:51:04 +00:00
m_pInventory = NULL ;
2002-11-03 07:17:03 +00:00
2002-10-01 01:53:56 +00:00
m_iOffsetSample = 0 ;
2003-04-14 23:09:35 +00:00
this -> AddChild ( & m_ArrowBackdrop );
this -> AddChild ( & m_NoteField );
this -> AddChild ( & m_GrayArrowRow );
this -> AddChild ( & m_GhostArrowRow );
2003-02-17 12:19:42 +00:00
this -> AddChild ( & m_Judgment );
this -> AddChild ( & m_Combo );
2002-04-16 17:31:00 +00:00
for ( int c = 0 ; c < MAX_NOTE_TRACKS ; c ++ )
2003-02-17 12:19:42 +00:00
this -> AddChild ( & m_HoldJudgment [ c ] );
2003-04-21 02:41:10 +00:00
PlayerAI :: InitFromDisk ();
2001-11-03 10:52:42 +00:00
}
2002-11-03 07:17:03 +00:00
Player ::~ Player ()
{
}
2003-06-30 18:08:27 +00:00
void Player :: Load ( PlayerNumber pn , NoteData * pNoteData , LifeMeter * pLM , CombinedLifeMeter * pCombinedLM , ScoreDisplay * pScore , Inventory * pInventory , ScoreKeeper * pPrimaryScoreKeeper , ScoreKeeper * pSecondaryScoreKeeper )
2002-02-02 05:11:12 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Player::Load()", );
2002-07-28 20:28:37 +00:00
m_PlayerNumber = pn ;
m_pLifeMeter = pLM ;
2003-06-30 18:08:27 +00:00
m_pCombinedLifeMeter = pCombinedLM ;
2002-07-28 20:28:37 +00:00
m_pScore = pScore ;
2003-02-25 02:51:04 +00:00
m_pInventory = pInventory ;
2003-06-30 18:08:27 +00:00
m_pPrimaryScoreKeeper = pPrimaryScoreKeeper ;
m_pSecondaryScoreKeeper = pSecondaryScoreKeeper ;
2003-06-25 05:35:14 +00:00
m_iRowLastCrossed = BeatToNoteRowNotRounded ( GAMESTATE -> m_fSongBeat ) - 1 ;
// m_iRowLastCrossed = -1;
2002-07-28 20:28:37 +00:00
2003-04-15 02:52:58 +00:00
/* Ensure that this is up-to-date. */
2003-04-21 23:43:51 +00:00
GAMESTATE -> m_pPosition -> Load ( pn );
2003-04-15 02:52:58 +00:00
2002-08-22 23:03:39 +00:00
const StyleDef * pStyleDef = GAMESTATE -> GetCurrentStyleDef ();
2002-07-28 20:28:37 +00:00
2002-08-01 21:55:40 +00:00
// init scoring
2002-12-18 22:25:24 +00:00
NoteDataWithScoring :: Init ();
2002-08-01 21:55:40 +00:00
2002-07-28 20:28:37 +00:00
// copy note data
2002-04-16 17:31:00 +00:00
this -> CopyAll ( pNoteData );
2003-01-27 06:18:13 +00:00
if ( GAMESTATE -> m_SongOptions . m_LifeType == SongOptions :: LIFE_BATTERY && GAMESTATE -> m_CurStageStats . bFailed [ pn ] ) // Oni dead
2002-08-01 21:55:40 +00:00
this -> ClearAll ();
2002-07-28 20:28:37 +00:00
2002-08-26 08:11:52 +00:00
/* 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 */
2003-02-17 12:19:42 +00:00
m_Judgment . StopTweening ();
2003-05-19 08:55:33 +00:00
// m_Combo.Reset(); // don't reset combos between songs in a course!
2003-02-25 00:33:42 +00:00
m_Combo . Init ( pn );
2003-02-17 12:19:42 +00:00
m_Judgment . Reset ();
2002-07-28 20:28:37 +00:00
2002-06-14 22:25:22 +00:00
if ( m_pScore )
2002-07-28 20:28:37 +00:00
m_pScore -> Init ( pn );
2002-04-28 20:42:32 +00:00
2002-07-28 20:28:37 +00:00
if ( ! GAMESTATE -> m_PlayerOptions [ pn ]. m_bHoldNotes )
2002-12-13 23:34:37 +00:00
NoteDataUtil :: RemoveHoldNotes ( * this );
2001-12-20 12:37:38 +00:00
2003-02-26 23:26:57 +00:00
switch ( GAMESTATE -> m_PlayerOptions [ pn ]. m_Turn )
2003-02-18 23:15:38 +00:00
{
case PlayerOptions :: TURN_NONE : break ;
case PlayerOptions :: TURN_MIRROR : NoteDataUtil :: Turn ( * this , NoteDataUtil :: mirror ); break ;
case PlayerOptions :: TURN_LEFT : NoteDataUtil :: Turn ( * this , NoteDataUtil :: left ); break ;
case PlayerOptions :: TURN_RIGHT : NoteDataUtil :: Turn ( * this , NoteDataUtil :: right ); break ;
case PlayerOptions :: TURN_SHUFFLE : NoteDataUtil :: Turn ( * this , NoteDataUtil :: shuffle ); break ;
case PlayerOptions :: TURN_SUPER_SHUFFLE : NoteDataUtil :: Turn ( * this , NoteDataUtil :: super_shuffle ); break ;
default : ASSERT ( 0 );
}
2001-11-04 19:34:28 +00:00
2003-02-16 20:27:53 +00:00
switch ( GAMESTATE -> m_PlayerOptions [ pn ]. m_Transform )
{
2003-02-20 00:57:52 +00:00
case PlayerOptions :: TRANSFORM_NONE : break ;
case PlayerOptions :: TRANSFORM_LITTLE : NoteDataUtil :: Little ( * this ); break ;
case PlayerOptions :: TRANSFORM_WIDE : NoteDataUtil :: Wide ( * this ); break ;
case PlayerOptions :: TRANSFORM_BIG : NoteDataUtil :: Big ( * this ); break ;
case PlayerOptions :: TRANSFORM_QUICK : NoteDataUtil :: Quick ( * this ); break ;
2003-03-13 09:25:57 +00:00
case PlayerOptions :: TRANSFORM_SKIPPY : NoteDataUtil :: Skippy ( * this ); break ;
2003-02-18 23:15:38 +00:00
default : ASSERT ( 0 );
2003-02-16 20:27:53 +00:00
}
2001-11-04 19:34:28 +00:00
2003-02-19 06:42:29 +00:00
int iStartDrawingAtPixels = GAMESTATE -> m_bEditing ? - 100 : START_DRAWING_AT_PIXELS ;
int iStopDrawingAtPixels = GAMESTATE -> m_bEditing ? 400 : STOP_DRAWING_AT_PIXELS ;
2002-08-31 03:04:38 +00:00
2003-04-14 22:32:08 +00:00
m_ArrowBackdrop . Unload ();
2003-04-17 21:16:56 +00:00
CString BackdropName = g_NoteFieldMode [ pn ]. m_Backdrop ;
2003-04-14 22:32:08 +00:00
if ( ! BackdropName . empty () )
m_ArrowBackdrop . LoadFromAniDir ( THEME -> GetPathToB ( BackdropName ) );
2003-05-04 13:58:45 +00:00
2003-02-19 06:42:29 +00:00
m_NoteField . Load ( ( NoteData * ) this , pn , iStartDrawingAtPixels , iStopDrawingAtPixels );
2002-05-19 01:59:48 +00:00
2003-04-14 22:32:08 +00:00
m_ArrowBackdrop . SetPlayer ( pn );
2002-07-28 20:28:37 +00:00
m_GrayArrowRow . Load ( pn );
m_GhostArrowRow . Load ( pn );
2002-02-24 01:43:11 +00:00
2003-04-14 22:32:08 +00:00
const bool bReverse = GAMESTATE -> m_PlayerOptions [ pn ]. m_fReverseScroll == 1 ;
2003-04-14 22:12:54 +00:00
bool bPlayerUsingBothSides = GAMESTATE -> GetCurrentStyleDef () -> m_StyleType == StyleDef :: ONE_PLAYER_TWO_CREDITS ;
m_Combo . SetX ( COMBO_X ( m_PlayerNumber , bPlayerUsingBothSides ) );
2003-04-15 18:32:34 +00:00
m_Combo . SetY ( bReverse ? SCREEN_BOTTOM - COMBO_Y : SCREEN_TOP + COMBO_Y );
2003-04-14 22:12:54 +00:00
m_Judgment . SetX ( JUDGMENT_X ( m_PlayerNumber , bPlayerUsingBothSides ) );
2003-02-17 12:19:42 +00:00
m_Judgment . SetY ( bReverse ? SCREEN_BOTTOM - JUDGMENT_Y : SCREEN_TOP + JUDGMENT_Y );
2002-02-24 01:43:11 +00:00
2003-04-17 22:53:36 +00:00
/* These commands add to the above positioning, and are usually empty. */
m_Judgment . Command ( g_NoteFieldMode [ pn ]. m_JudgmentCmd );
m_Combo . Command ( g_NoteFieldMode [ pn ]. m_ComboCmd );
2002-11-16 09:12:55 +00:00
int c ;
for ( c = 0 ; c < pStyleDef -> m_iColsPerPlayer ; c ++ )
2003-02-17 12:19:42 +00:00
{
m_HoldJudgment [ c ]. SetY ( bReverse ? SCREEN_BOTTOM - HOLD_JUDGMENT_Y : SCREEN_TOP + HOLD_JUDGMENT_Y );
m_HoldJudgment [ c ]. SetX ( ( float ) pStyleDef -> m_ColumnInfo [ pn ][ c ]. fXOffset );
2003-04-17 23:21:22 +00:00
m_HoldJudgment [ c ]. Command ( g_NoteFieldMode [ pn ]. m_HoldJudgmentCmd [ c ] );
2003-02-17 12:19:42 +00:00
}
2002-02-24 01:43:11 +00:00
2003-04-14 23:09:35 +00:00
m_ArrowBackdrop . SetY ( bReverse ? SCREEN_BOTTOM - GRAY_ARROWS_Y : SCREEN_TOP + GRAY_ARROWS_Y );
m_NoteField . SetY ( bReverse ? SCREEN_BOTTOM - GRAY_ARROWS_Y : SCREEN_TOP + GRAY_ARROWS_Y );
m_GrayArrowRow . SetY ( bReverse ? SCREEN_BOTTOM - GRAY_ARROWS_Y : SCREEN_TOP + GRAY_ARROWS_Y );
m_GhostArrowRow . SetY ( bReverse ? SCREEN_BOTTOM - GRAY_ARROWS_Y : SCREEN_TOP + GRAY_ARROWS_Y );
2002-07-27 19:29:51 +00:00
2003-02-26 23:26:57 +00:00
if ( GAMESTATE -> m_PlayerOptions [ pn ]. m_fEffects [ PlayerOptions :: EFFECT_MINI ] == 1 )
2002-07-27 19:29:51 +00:00
{
m_NoteField . SetZoom ( 0.5f );
m_GrayArrowRow . SetZoom ( 0.5f );
m_GhostArrowRow . SetZoom ( 0.5f );
}
2003-04-22 04:54:04 +00:00
m_sLastSeenNoteSkin = GAMESTATE -> m_PlayerOptions [ m_PlayerNumber ]. m_sNoteSkin ;
2001-11-04 19:34:28 +00:00
}
2002-07-28 20:28:37 +00:00
void Player :: Update ( float fDeltaTime )
2001-11-03 10:52:42 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Player::Update(%f)", fDeltaTime );
2001-11-03 10:52:42 +00:00
2003-04-13 04:50:08 +00:00
if ( GAMESTATE -> m_pCurSong == NULL )
return ;
2002-07-28 20:28:37 +00:00
const float fSongBeat = GAMESTATE -> m_fSongBeat ;
2002-06-23 11:43:53 +00:00
2002-02-24 01:43:11 +00:00
//
2002-03-19 07:09:49 +00:00
// Check for TapNote misses
2002-02-24 01:43:11 +00:00
//
2003-02-09 04:05:31 +00:00
UpdateTapNotesMissedOlderThan ( GetMaxStepDistanceSeconds () );
2001-11-04 19:34:28 +00:00
2002-02-24 01:43:11 +00:00
//
2002-03-19 07:09:49 +00:00
// update HoldNotes logic
2002-02-24 01:43:11 +00:00
//
2002-11-02 22:46:15 +00:00
for ( int i = 0 ; i < GetNumHoldNotes (); i ++ ) // for each HoldNote
2001-12-19 01:50:57 +00:00
{
2002-11-02 22:46:15 +00:00
const HoldNote & hn = GetHoldNote ( i );
2002-12-18 22:25:24 +00:00
HoldNoteScore hns = GetHoldNoteScore ( i );
float fLife = GetHoldNoteLife ( i );
2003-02-16 20:27:53 +00:00
int iHoldStartIndex = BeatToNoteRow ( hn . fStartBeat );
2002-08-13 23:26:46 +00:00
m_NoteField . m_bIsHoldingHoldNote [ i ] = false ; // set host flag so NoteField can do intelligent drawing
2002-02-03 20:45:08 +00:00
2002-06-14 22:25:22 +00:00
if ( hns != HNS_NONE ) // if this HoldNote already has a result
2002-02-24 01:43:11 +00:00
continue ; // we don't need to update the logic for this one
2003-02-16 20:27:53 +00:00
const StyleInput StyleI ( m_PlayerNumber , hn . iTrack );
2002-07-23 01:41:40 +00:00
const GameInput GameI = GAMESTATE -> GetCurrentStyleDef () -> StyleInputToGameInput ( StyleI );
2002-02-24 01:43:11 +00:00
2002-10-12 22:04:44 +00:00
// if they got a bad score or haven't stepped on the corresponding tap yet
2003-02-16 20:27:53 +00:00
const TapNoteScore tns = GetTapNoteScore ( hn . iTrack , iHoldStartIndex );
2002-10-12 22:17:16 +00:00
const bool bSteppedOnTapNote = tns != TNS_NONE && tns != TNS_MISS ; // did they step on the start of this hold?
2002-10-12 22:04:44 +00:00
2003-02-16 20:27:53 +00:00
if ( hn . fStartBeat < fSongBeat && fSongBeat < hn . fEndBeat ) // if the song beat is in the range of this hold
2001-12-19 01:50:57 +00:00
{
2002-10-20 17:18:05 +00:00
bool bIsHoldingButton = INPUTMAPPER -> IsButtonDown ( GameI );
2003-04-10 05:46:31 +00:00
// TODO: Make the CPU miss sometimes.
2003-04-21 02:41:10 +00:00
if ( GAMESTATE -> m_PlayerController [ m_PlayerNumber ] != PC_HUMAN )
2002-10-20 17:18:05 +00:00
bIsHoldingButton = true ;
2002-04-16 17:31:00 +00:00
2002-08-13 23:26:46 +00:00
m_NoteField . m_bIsHoldingHoldNote [ i ] = bIsHoldingButton && bSteppedOnTapNote ; // set host flag so NoteField can do intelligent drawing
2002-06-14 22:25:22 +00:00
2002-09-15 19:38:44 +00:00
if ( bSteppedOnTapNote ) // this note is not judged and we stepped on its head
2003-02-16 20:27:53 +00:00
m_NoteField . GetHoldNote ( i ). fStartBeat = fSongBeat ; // move the start of this Hold
2002-09-15 19:38:44 +00:00
2002-10-12 22:21:08 +00:00
if ( bSteppedOnTapNote && bIsHoldingButton )
2001-12-19 01:50:57 +00:00
{
2002-06-14 22:25:22 +00:00
// Increase life
2003-03-27 00:06:16 +00:00
fLife = 1 ;
2002-06-14 22:25:22 +00:00
2003-02-16 20:27:53 +00:00
m_GhostArrowRow . HoldNote ( hn . iTrack ); // update the "electric ghost" effect
2001-12-19 01:50:57 +00:00
}
2002-08-01 03:15:27 +00:00
else
2002-02-24 01:43:11 +00:00
{
2003-02-16 20:27:53 +00:00
if ( fSongBeat - hn . fStartBeat > GAMESTATE -> m_fCurBPS * GetMaxStepDistanceSeconds () )
2002-06-14 22:25:22 +00:00
{
// Decrease life
2003-02-17 05:33:57 +00:00
fLife -= fDeltaTime / PREFSMAN -> m_fJudgeWindowOKSeconds ;
2002-06-14 22:25:22 +00:00
fLife = max ( fLife , 0 ); // clamp
}
2002-02-24 01:43:11 +00:00
}
2001-12-20 21:08:45 +00:00
}
2002-10-12 22:17:16 +00:00
/* check for NG. If the head was missed completely, don't count
* an NG. */
if ( bSteppedOnTapNote && fLife == 0 ) // the player has not pressed the button for a long time!
2002-06-14 22:25:22 +00:00
hns = HNS_NG ;
2001-12-19 01:50:57 +00:00
2002-02-24 01:43:11 +00:00
// check for OK
2003-02-16 20:27:53 +00:00
if ( fSongBeat >= hn . fEndBeat && bSteppedOnTapNote && fLife > 0 ) // if this HoldNote is in the past
2001-12-19 01:50:57 +00:00
{
2002-06-14 22:25:22 +00:00
fLife = 1 ;
hns = HNS_OK ;
2002-09-05 03:45:07 +00:00
m_GhostArrowRow . TapNote ( StyleI . col , TNS_PERFECT , true ); // bright ghost flash
2001-12-19 01:50:57 +00:00
}
2002-12-18 22:25:24 +00:00
if ( hns != HNS_NONE )
{
2003-04-10 05:46:31 +00:00
/* this note has been judged */
2003-03-16 17:45:32 +00:00
HandleHoldScore ( hns , tns );
2003-02-17 12:19:42 +00:00
m_HoldJudgment [ hn . iTrack ]. SetHoldJudgment ( hns );
2002-12-18 22:25:24 +00:00
}
m_NoteField . SetHoldNoteLife ( i , fLife ); // update the NoteField display
m_NoteField . SetHoldNoteScore ( i , hns ); // update the NoteField display
SetHoldNoteLife ( i , fLife );
SetHoldNoteScore ( i , hns );
2001-12-19 01:50:57 +00:00
}
2003-05-30 20:23:57 +00:00
/* Is there a reason this wasn't done within NoteField to begin with? */
/* if( m_sLastSeenNoteSkin != GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_sNoteSkin )
2003-04-22 04:54:04 +00:00
{
m_NoteField.ReloadNoteSkin();
m_sLastSeenNoteSkin = GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_sNoteSkin;
2003-05-30 20:23:57 +00:00
} */
2003-04-22 04:54:04 +00:00
2003-06-13 00:50:24 +00:00
// Why was this originally "BeatToNoteRowNotRounded"? It should be rounded. -Chris
/* We want to send the crossed row message exactly when we cross the row--not
* .5 before the row. Use MAX3 as a test case: without rounding, autoplay steps
* early. -glenn */
const int iRowNow = BeatToNoteRowNotRounded ( GAMESTATE -> m_fSongBeat );
2003-06-10 19:53:33 +00:00
if ( iRowNow >= 0 )
{
for ( ; m_iRowLastCrossed <= iRowNow ; m_iRowLastCrossed ++ ) // for each index we crossed since the last update
if ( GAMESTATE -> IsPlayerEnabled ( m_PlayerNumber ) )
CrossedRow ( m_iRowLastCrossed );
}
2002-02-24 01:43:11 +00:00
ActorFrame :: Update ( fDeltaTime );
2001-11-03 10:52:42 +00:00
}
2002-05-19 01:59:48 +00:00
void Player :: DrawPrimitives ()
2002-02-24 10:31:20 +00:00
{
2003-06-13 04:31:01 +00:00
// May have both players in doubles (for battle play); only draw primary player.
2003-05-05 06:48:20 +00:00
if ( GAMESTATE -> GetCurrentStyleDef () -> m_StyleType == StyleDef :: ONE_PLAYER_TWO_CREDITS &&
2003-06-13 04:31:01 +00:00
m_PlayerNumber != GAMESTATE -> m_MasterPlayerNumber )
2003-05-05 06:48:20 +00:00
return ;
2003-04-14 23:09:35 +00:00
// Draw these below everything else.
m_ArrowBackdrop . Draw ();
m_Combo . Draw ();
2002-07-31 19:40:40 +00:00
2003-04-01 19:31:27 +00:00
float fTilt = GAMESTATE -> m_CurrentPlayerOptions [ m_PlayerNumber ]. m_fPerspectiveTilt ;
bool bReverse = GAMESTATE -> m_CurrentPlayerOptions [ m_PlayerNumber ]. m_fReverseScroll == 1 ;
2003-04-02 18:24:00 +00:00
float fReverseScale = bReverse ? - 1.0f : 1.0f ;
2003-04-01 19:31:27 +00:00
if ( fTilt != 0 )
2002-02-24 10:31:20 +00:00
{
2002-11-15 08:02:27 +00:00
DISPLAY -> EnterPerspective ( 45 , false );
2002-02-24 10:31:20 +00:00
2002-11-15 08:02:27 +00:00
// construct view and project matrix
2003-04-02 04:41:49 +00:00
RageVector3 Up ( 0.0f , 1.0f , 0.0f );
RageVector3 Eye ( CENTER_X , CENTER_Y + SCALE ( fTilt * fReverseScale , - 1 , 1 , - 350 , 350 ), 500 );
2003-04-01 19:31:27 +00:00
// give a push the receptors toward the edge of the screen so they aren't so far in the middle
float fYOffset = SCALE ( fTilt , - 1 , + 1 , 10 * fReverseScale , 60 * fReverseScale );
2003-04-02 04:41:49 +00:00
RageVector3 At ( CENTER_X , CENTER_Y + fYOffset , 0 );
2002-11-15 08:02:27 +00:00
DISPLAY -> LookAt ( Eye , At , Up );
2002-02-24 10:31:20 +00:00
}
2003-04-14 23:09:35 +00:00
m_GrayArrowRow . Draw ();
m_NoteField . Draw ();
m_GhostArrowRow . Draw ();
2002-02-24 10:31:20 +00:00
2003-04-02 18:24:00 +00:00
if ( fTilt != 0 )
2002-11-15 08:02:27 +00:00
DISPLAY -> ExitPerspective ();
2002-02-24 10:31:20 +00:00
2003-05-19 08:50:58 +00:00
if ( m_bShowJudgment )
m_Judgment . Draw ();
2002-02-24 10:31:20 +00:00
2003-02-01 05:16:38 +00:00
for ( int c = 0 ; c < GetNumTracks (); c ++ )
2003-02-17 12:19:42 +00:00
m_HoldJudgment [ c ]. Draw ();
2001-12-28 10:15:59 +00:00
}
2003-06-13 00:50:24 +00:00
/* It's OK for this function to search a little more than was requested. */
2003-06-13 00:24:33 +00:00
int Player :: GetClosestNoteDirectional ( int col , float fBeat , float fMaxBeatsDistance , int iDirection )
2001-11-03 10:52:42 +00:00
{
2001-11-04 19:34:28 +00:00
// look for the closest matching step
2003-02-09 03:33:36 +00:00
const int iIndexStartLookingAt = BeatToNoteRow ( fBeat );
2003-02-09 02:07:30 +00:00
2003-06-13 00:50:24 +00:00
/* Number of elements to examine on either end of iIndexStartLookingAt. Make
* sure we always round up. */
const int iNumElementsToExamine = BeatToNoteRow ( fMaxBeatsDistance + 1 );
2003-02-09 03:33:36 +00:00
// Start at iIndexStartLookingAt and search outward.
for ( int delta = 0 ; delta < iNumElementsToExamine ; delta ++ )
2001-11-04 19:34:28 +00:00
{
2003-02-09 03:33:36 +00:00
int iCurrentIndex = iIndexStartLookingAt + ( iDirection * delta );
2001-11-03 10:52:42 +00:00
2003-02-09 03:33:36 +00:00
if ( iCurrentIndex < 0 ) continue ;
if ( GetTapNote ( col , iCurrentIndex ) == TAP_EMPTY ) continue ; /* no note here */
if ( GetTapNoteScore ( col , iCurrentIndex ) != TNS_NONE ) continue ; /* this note has a score already */
2001-11-03 10:52:42 +00:00
2003-02-09 03:33:36 +00:00
return iCurrentIndex ;
2001-11-04 19:34:28 +00:00
}
2002-05-29 09:47:24 +00:00
2003-02-09 03:33:36 +00:00
return - 1 ;
}
2003-02-17 05:25:20 +00:00
int Player :: GetClosestNote ( int col , float fBeat , float fMaxBeatsAhead , float fMaxBeatsBehind )
2003-02-09 03:33:36 +00:00
{
2003-02-17 05:25:20 +00:00
int Fwd = GetClosestNoteDirectional ( col , fBeat , fMaxBeatsAhead , 1 );
int Back = GetClosestNoteDirectional ( col , fBeat , fMaxBeatsBehind , - 1 );
2003-02-09 03:33:36 +00:00
if ( Fwd == - 1 && Back == - 1 ) return - 1 ;
if ( Fwd == - 1 ) return Back ;
if ( Back == - 1 ) return Fwd ;
/* Figure out which row is closer. */
const float DistToFwd = fabsf ( fBeat - NoteRowToBeat ( Fwd ));
const float DistToBack = fabsf ( fBeat - NoteRowToBeat ( Back ));
if ( DistToFwd > DistToBack ) return Back ;
return Fwd ;
2003-02-09 02:07:30 +00:00
}
void Player :: Step ( int col )
{
if ( GAMESTATE -> m_SongOptions . m_LifeType == SongOptions :: LIFE_BATTERY && GAMESTATE -> m_CurStageStats . bFailed [ m_PlayerNumber ] ) // Oni dead
return ; // do nothing
//LOG->Trace( "Player::HandlePlayerStep()" );
ASSERT ( col >= 0 && col <= GetNumTracks () );
2003-02-17 05:25:20 +00:00
int iIndexOverlappingNote = GetClosestNote ( col , GAMESTATE -> m_fSongBeat ,
2003-03-26 22:22:10 +00:00
StepSearchDistanceForwards * GAMESTATE -> m_fCurBPS * GAMESTATE -> m_SongOptions . m_fMusicRate ,
StepSearchDistanceBackwards * GAMESTATE -> m_fCurBPS * GAMESTATE -> m_SongOptions . m_fMusicRate );
2003-02-09 02:07:30 +00:00
//LOG->Trace( "iIndexStartLookingAt = %d, iNumElementsToExamine = %d", iIndexStartLookingAt, iNumElementsToExamine );
2003-04-13 00:44:50 +00:00
bool bGrayArrowStep = true ;
2002-05-29 09:47:24 +00:00
if ( iIndexOverlappingNote != - 1 )
{
2002-06-24 22:04:31 +00:00
// compute the score for this hit
2003-04-07 21:24:14 +00:00
float fStepBeat = NoteRowToBeat ( ( float ) iIndexOverlappingNote );
2003-02-17 05:25:20 +00:00
2003-04-07 21:24:14 +00:00
float fStepSeconds = GAMESTATE -> m_pCurSong -> GetElapsedTimeFromBeat ( fStepBeat );
2003-02-17 05:25:20 +00:00
2003-02-09 03:33:36 +00:00
// The offset from the actual step in seconds:
2003-04-07 21:24:14 +00:00
float fNoteOffset = fStepSeconds - GAMESTATE -> m_fMusicSeconds ;
2002-10-01 01:53:56 +00:00
2003-04-07 21:24:14 +00:00
float fSecondsFromPerfect = fabsf ( fNoteOffset ) / GAMESTATE -> m_SongOptions . m_fMusicRate ; // account for music rate
2002-06-24 22:04:31 +00:00
2003-04-07 21:24:14 +00:00
// calculate TapNoteScore
2002-12-17 22:07:59 +00:00
TapNoteScore score ;
2002-06-24 22:04:31 +00:00
2003-04-21 02:41:10 +00:00
switch ( GAMESTATE -> m_PlayerController [ m_PlayerNumber ] )
2003-04-07 21:24:14 +00:00
{
2003-04-21 03:24:42 +00:00
case PC_HUMAN : {
/* 1 is normal. 2 means scoring is half as hard; .5 means it's twice as hard. */
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_GOOD ;
else if ( fScaledSecondsFromPerfect <= PREFSMAN -> m_fJudgeWindowBooSeconds ) score = TNS_BOO ;
2003-04-11 00:12:07 +00:00
else score = TNS_NONE ;
2003-04-21 02:41:10 +00:00
break ;
2003-04-21 03:24:42 +00:00
}
2003-04-21 02:41:10 +00:00
case PC_CPU :
score = PlayerAI :: GetTapNoteScore ( GAMESTATE -> m_iCpuSkill [ m_PlayerNumber ], GAMESTATE -> GetSumOfActiveAttackLevels ( m_PlayerNumber ) );
2003-04-10 05:46:31 +00:00
/* 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
* AI generated a miss, and let UpdateMissedTapNotesOlderThan() detect and handle the
* misses. */
if ( score == TNS_MISS )
return ;
2003-04-21 02:41:10 +00:00
break ;
case PC_AUTOPLAY :
score = TNS_MARVELOUS ;
break ;
default :
ASSERT ( 0 );
2003-04-22 04:54:04 +00:00
score = TNS_NONE ;
break ;
2003-04-07 21:24:14 +00:00
}
2003-01-11 08:55:21 +00:00
if ( score == TNS_MARVELOUS && ! PREFSMAN -> m_bMarvelousTiming )
2002-08-27 16:53:25 +00:00
score = TNS_PERFECT ;
2003-04-13 00:44:50 +00:00
bGrayArrowStep = score < TNS_GOOD ;
2002-07-11 19:02:26 +00:00
2003-03-26 22:22:10 +00:00
LOG -> Trace ( "Note offset: %f (fSecondsFromPerfect = %f), Score: %i" , fNoteOffset , fSecondsFromPerfect , score );
2003-04-11 00:12:07 +00:00
2002-12-18 22:25:24 +00:00
SetTapNoteScore ( col , iIndexOverlappingNote , score );
2003-04-11 00:12:07 +00:00
if ( score != TNS_NONE )
SetTapNoteOffset ( col , iIndexOverlappingNote , - fNoteOffset );
2002-12-17 22:07:59 +00:00
2003-04-21 02:41:10 +00:00
if ( GAMESTATE -> m_PlayerController [ m_PlayerNumber ] == PC_HUMAN &&
2003-04-10 05:46:31 +00:00
score >= TNS_GREAT )
2003-03-26 18:48:19 +00:00
HandleAutosync ( fNoteOffset );
2002-10-01 01:53:56 +00:00
2003-04-10 05:46:31 +00:00
if ( IsRowCompletelyJudged ( iIndexOverlappingNote ) )
OnRowCompletelyJudged ( iIndexOverlappingNote );
2002-05-29 09:47:24 +00:00
}
2003-04-13 00:44:50 +00:00
if ( bGrayArrowStep )
m_GrayArrowRow . Step ( col );
2001-11-04 19:34:28 +00:00
}
2001-11-03 10:52:42 +00:00
2003-03-26 18:48:19 +00:00
void Player :: HandleAutosync ( float fNoteOffset )
{
if ( ! GAMESTATE -> m_SongOptions . m_bAutoSync )
return ;
m_fOffset [ m_iOffsetSample ++ ] = fNoteOffset ;
if ( m_iOffsetSample < SAMPLE_COUNT )
return ; /* need more */
const float mean = calc_mean ( m_fOffset , m_fOffset + SAMPLE_COUNT );
const float stddev = calc_stddev ( m_fOffset , m_fOffset + SAMPLE_COUNT );
if ( stddev < .03 && stddev < fabsf ( mean )) { //If they stepped with less than .03 error
GAMESTATE -> m_pCurSong -> m_fBeat0OffsetInSeconds += mean ;
LOG -> Trace ( "Offset corrected by %f. Error in steps: %f seconds." , mean , stddev );
} else
LOG -> Trace ( "Offset NOT corrected. Average offset: %f seconds. Error: %f seconds." , mean , stddev );
m_iOffsetSample = 0 ;
}
2003-03-26 19:34:59 +00:00
2003-04-10 05:46:31 +00:00
void Player :: OnRowCompletelyJudged ( int iIndexThatWasSteppedOn )
2001-11-04 19:34:28 +00:00
{
2003-04-10 05:46:31 +00:00
LOG -> Trace ( "Player::OnRowCompletelyJudged" );
2002-06-24 22:04:31 +00:00
2003-03-26 19:34:59 +00:00
/* Find the minimum score of the row. This will never be TNS_NONE, since this
* function is only called when a row is completed. */
// TapNoteScore score = MinTapNoteScore(iIndexThatWasSteppedOn);
TapNoteScore score = LastTapNoteScore ( iIndexThatWasSteppedOn );
2003-03-26 22:22:10 +00:00
ASSERT ( score != TNS_NONE );
2001-11-03 10:52:42 +00:00
2002-10-24 05:24:56 +00:00
/* If the whole row was hit with perfects or greats, remove the row
* from the NoteField, so it disappears. */
2003-01-11 08:55:21 +00:00
switch ( score )
{
case TNS_MARVELOUS :
case TNS_PERFECT :
case TNS_GREAT :
2002-04-28 20:42:32 +00:00
m_NoteField . RemoveTapNoteRow ( iIndexThatWasSteppedOn );
2003-01-11 08:55:21 +00:00
break ;
}
2002-02-24 01:43:11 +00:00
2003-02-01 05:16:38 +00:00
for ( int c = 0 ; c < GetNumTracks (); c ++ ) // for each column
2002-02-24 01:43:11 +00:00
{
2003-03-26 17:38:19 +00:00
if ( GetTapNote ( c , iIndexThatWasSteppedOn ) == TAP_EMPTY )
continue ; /* no note in this col */
2002-10-24 05:24:56 +00:00
2003-03-26 17:38:19 +00:00
// show the ghost arrow for this column
switch ( score )
{
case TNS_GREAT :
case TNS_PERFECT :
case TNS_MARVELOUS :
2003-03-16 17:45:32 +00:00
{
2003-03-26 17:38:19 +00:00
bool bBright = GAMESTATE -> m_CurStageStats . iCurCombo [ m_PlayerNumber ] > ( int ) BRIGHT_GHOST_COMBO_THRESHOLD ;
m_GhostArrowRow . TapNote ( c , score , bBright );
2003-03-16 17:45:32 +00:00
}
2003-03-26 17:38:19 +00:00
break ;
2002-10-02 05:42:59 +00:00
}
2002-10-24 05:24:56 +00:00
}
2002-10-02 05:42:59 +00:00
2003-03-26 21:35:15 +00:00
HandleTapRowScore ( iIndexThatWasSteppedOn ); // update score
m_Combo . SetCombo ( GAMESTATE -> m_CurStageStats . iCurCombo [ m_PlayerNumber ] );
2002-05-19 01:59:48 +00:00
2003-02-17 12:19:42 +00:00
m_Judgment . SetJudgment ( score );
2001-11-03 10:52:42 +00:00
}
2003-03-26 21:35:15 +00:00
void Player :: UpdateTapNotesMissedOlderThan ( float fMissIfOlderThanSeconds )
2001-11-04 19:34:28 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Notes::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
2003-02-09 04:05:31 +00:00
const float fEarliestTime = GAMESTATE -> m_fMusicSeconds - fMissIfOlderThanSeconds ;
2003-06-13 00:24:33 +00:00
bool bFreeze ;
float fMissIfOlderThanThisBeat ;
float fThrowAway ;
GAMESTATE -> m_pCurSong -> GetBeatAndBPSFromElapsedTime ( fEarliestTime , fMissIfOlderThanThisBeat , fThrowAway , bFreeze );
2001-11-04 19:34:28 +00:00
2002-05-19 01:59:48 +00:00
int iMissIfOlderThanThisIndex = BeatToNoteRow ( fMissIfOlderThanThisBeat );
2003-06-13 00:24:33 +00:00
if ( bFreeze )
{
/* iMissIfOlderThanThisIndex is a freeze. Include the index of the freeze,
* too. Otherwise we won't show misses for tap notes on freezes until the
* freeze finishes. */
iMissIfOlderThanThisIndex ++ ;
}
2001-11-03 10:52:42 +00:00
2002-04-16 17:31:00 +00:00
// Since this is being called every frame, let's not check the whole array every time.
2001-11-04 19:34:28 +00:00
// Instead, only check 10 elements back. Even 10 is overkill.
int iStartCheckingAt = max ( 0 , iMissIfOlderThanThisIndex - 10 );
2001-11-03 10:52:42 +00:00
2002-07-31 19:40:40 +00:00
//LOG->Trace( "iStartCheckingAt: %d iMissIfOlderThanThisIndex: %d", iStartCheckingAt, iMissIfOlderThanThisIndex );
2003-02-09 04:05:31 +00:00
int iNumMissesFound = 0 ;
2003-06-13 00:24:33 +00:00
for ( int r = iStartCheckingAt ; r < iMissIfOlderThanThisIndex ; r ++ )
2001-11-03 10:52:42 +00:00
{
2003-03-26 21:35:15 +00:00
bool MissedNoteOnThisRow = false ;
2003-02-01 05:16:38 +00:00
for ( int t = 0 ; t < GetNumTracks (); t ++ )
2001-11-03 10:52:42 +00:00
{
2003-03-26 21:35:15 +00:00
if ( GetTapNote ( t , r ) == TAP_EMPTY ) continue ; /* no note here */
if ( GetTapNoteScore ( t , r ) != TNS_NONE ) continue ; /* note here is hit */
MissedNoteOnThisRow = true ;
SetTapNoteScore ( t , r , TNS_MISS );
2002-10-02 05:42:59 +00:00
}
2003-03-26 21:35:15 +00:00
if ( ! MissedNoteOnThisRow ) continue ;
2003-04-22 03:55:23 +00:00
iNumMissesFound ++ ;
2003-03-26 21:35:15 +00:00
HandleTapRowScore ( r );
m_Combo . SetCombo ( GAMESTATE -> m_CurStageStats . iCurCombo [ m_PlayerNumber ] );
2001-11-03 10:52:42 +00:00
}
2002-05-29 09:47:24 +00:00
if ( iNumMissesFound > 0 )
2003-02-17 12:19:42 +00:00
m_Judgment . SetJudgment ( TNS_MISS );
2001-11-03 10:52:42 +00:00
}
2002-07-28 20:28:37 +00:00
void Player :: CrossedRow ( int iNoteRow )
2002-06-24 22:04:31 +00:00
{
2003-06-10 19:53:33 +00:00
// If we're doing random vanish, randomise notes on the fly.
if ( GAMESTATE -> m_CurrentPlayerOptions [ m_PlayerNumber ]. m_fAppearances [ PlayerOptions :: APPEARANCE_RANDOMVANISH ] == 1 )
RandomiseNotes ( iNoteRow );
2003-04-07 21:24:14 +00:00
// check to see if there's at the crossed row
for ( int t = 0 ; t < GetNumTracks (); t ++ )
if ( GetTapNote ( t , iNoteRow ) != TAP_EMPTY )
2003-04-21 02:41:10 +00:00
if ( GAMESTATE -> m_PlayerController [ m_PlayerNumber ] != PC_HUMAN )
2003-04-10 05:46:31 +00:00
Step ( t );
2002-06-24 22:04:31 +00:00
}
2001-11-03 10:52:42 +00:00
2003-05-04 13:58:45 +00:00
void Player :: RandomiseNotes ( int iNoteRow )
{
2003-05-11 08:19:24 +00:00
int NewNoteRow = ( int )( iNoteRow + 50 / GAMESTATE -> m_PlayerOptions [ m_PlayerNumber ]. m_fScrollSpeed ); // change the row to look ahead from based upon their speed mod
2003-05-04 13:58:45 +00:00
// check to see if they're at the crossed row
2003-05-11 08:19:24 +00:00
// int EmptyNoteCol = -1;
// int WaitingForEmptyColumn = -1;
2003-05-04 13:58:45 +00:00
bool b_updatenotedata = false ;
int iNumOfTracks = GetNumTracks ();
for ( int t = 0 ; t < iNumOfTracks ; t ++ )
{
if ( t + 1 < iNumOfTracks )
{
int iRandomTrackToSwapWith = RandomInt ( 0 , iNumOfTracks - 1 );
TapNote t1 = GetTapNote ( t , NewNoteRow );
TapNote t2 = GetTapNote ( iRandomTrackToSwapWith , NewNoteRow );
if (( t1 == TAP_TAP || t1 == TAP_EMPTY ) && ( t2 == TAP_TAP || t2 == TAP_EMPTY ) && ( ! ( t1 == TAP_EMPTY && t2 == TAP_EMPTY ) && ! ( t1 == TAP_TAP && t2 == TAP_TAP )))
{
SetTapNote ( t , NewNoteRow , t2 );
SetTapNote ( iRandomTrackToSwapWith , NewNoteRow , t1 );
b_updatenotedata = true ;
}
}
}
if ( b_updatenotedata )
{
m_NoteField . CopyAll (( NoteData * ) this );
}
}
2001-11-04 19:34:28 +00:00
2003-03-26 21:35:15 +00:00
void Player :: HandleTapRowScore ( unsigned row )
2002-06-24 22:04:31 +00:00
{
2003-03-26 21:35:15 +00:00
TapNoteScore scoreOfLastTap = LastTapNoteScore ( row );
int iNumTapsInRow = 0 ;
for ( int c = 0 ; c < GetNumTracks (); c ++ ) // for each column
if ( GetTapNote ( c , row ) != TAP_EMPTY )
iNumTapsInRow ++ ;
ASSERT ( iNumTapsInRow > 0 );
2002-10-02 05:42:59 +00:00
2003-04-21 04:11:01 +00:00
bool NoCheating = true ;
2003-04-22 04:54:04 +00:00
#ifdef DEBUG
NoCheating = false ;
#endif
2002-08-23 20:18:29 +00:00
2003-04-21 04:11:01 +00:00
if ( GAMESTATE -> m_bDemonstrationOrJukebox )
NoCheating = false ;
// don't accumulate points if AutoPlay is on.
2003-04-22 04:54:04 +00:00
if ( NoCheating && GAMESTATE -> m_PlayerController [ m_PlayerNumber ] == PC_AUTOPLAY )
2003-04-21 04:11:01 +00:00
return ;
2003-06-30 18:08:27 +00:00
if ( m_pPrimaryScoreKeeper )
m_pPrimaryScoreKeeper -> HandleTapRowScore ( scoreOfLastTap , iNumTapsInRow );
if ( m_pSecondaryScoreKeeper )
m_pSecondaryScoreKeeper -> HandleTapRowScore ( scoreOfLastTap , iNumTapsInRow );
2002-10-24 23:22:43 +00:00
2002-10-24 19:21:05 +00:00
if ( m_pScore )
2003-06-18 20:08:39 +00:00
m_pScore -> SetScore ( GAMESTATE -> m_CurStageStats . iScore [ m_PlayerNumber ]);
2002-11-03 20:10:17 +00:00
if ( m_pLifeMeter ) {
2003-03-16 17:45:32 +00:00
m_pLifeMeter -> ChangeLife ( scoreOfLastTap );
2002-11-03 20:10:17 +00:00
m_pLifeMeter -> OnDancePointsChange (); // update oni life meter
}
2003-06-30 18:08:27 +00:00
if ( m_pCombinedLifeMeter ) {
m_pCombinedLifeMeter -> ChangeLife ( m_PlayerNumber , scoreOfLastTap );
m_pCombinedLifeMeter -> OnDancePointsChange ( m_PlayerNumber ); // update oni life meter
}
2002-06-24 22:04:31 +00:00
}
2002-07-28 20:28:37 +00:00
2002-10-24 19:21:05 +00:00
2003-03-16 17:45:32 +00:00
void Player :: HandleHoldScore ( HoldNoteScore holdScore , TapNoteScore tapScore )
2002-07-28 20:28:37 +00:00
{
2003-04-21 04:11:01 +00:00
bool NoCheating = true ;
2003-06-18 04:25:49 +00:00
#ifdef DEBUG
NoCheating = false ;
#endif
2003-04-21 04:11:01 +00:00
if ( GAMESTATE -> m_bDemonstrationOrJukebox )
NoCheating = false ;
// don't accumulate points if AutoPlay is on.
2003-05-17 21:19:00 +00:00
if ( NoCheating && GAMESTATE -> m_PlayerController [ m_PlayerNumber ] == PC_AUTOPLAY )
2003-04-21 04:11:01 +00:00
return ;
2003-06-30 18:08:27 +00:00
if ( m_pPrimaryScoreKeeper )
m_pPrimaryScoreKeeper -> HandleHoldScore ( holdScore , tapScore );
if ( m_pSecondaryScoreKeeper )
m_pSecondaryScoreKeeper -> HandleHoldScore ( holdScore , tapScore );
2002-08-23 20:18:29 +00:00
2002-11-03 20:52:13 +00:00
if ( m_pScore )
2003-06-18 20:08:39 +00:00
m_pScore -> SetScore ( GAMESTATE -> m_CurStageStats . iScore [ m_PlayerNumber ]);
2002-11-03 20:52:13 +00:00
2002-10-12 22:04:44 +00:00
if ( m_pLifeMeter ) {
2003-03-16 17:45:32 +00:00
m_pLifeMeter -> ChangeLife ( holdScore , tapScore );
2003-02-17 03:45:52 +00:00
m_pLifeMeter -> OnDancePointsChange ();
2002-10-12 22:04:44 +00:00
}
2003-06-30 18:08:27 +00:00
if ( m_pCombinedLifeMeter ) {
m_pCombinedLifeMeter -> ChangeLife ( m_PlayerNumber , holdScore , tapScore );
m_pCombinedLifeMeter -> OnDancePointsChange ( m_PlayerNumber );
}
2002-07-28 20:28:37 +00:00
}
2002-09-05 03:45:07 +00:00
2003-02-09 04:05:31 +00:00
float Player :: GetMaxStepDistanceSeconds ()
2002-07-28 20:28:37 +00:00
{
2003-02-09 04:05:31 +00:00
return GAMESTATE -> m_SongOptions . m_fMusicRate * PREFSMAN -> m_fJudgeWindowBooSeconds * PREFSMAN -> m_fJudgeWindowScale ;
2002-09-05 03:45:07 +00:00
}
void Player :: FadeToFail ()
{
m_NoteField . FadeToFail ();
}