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
-----------------------------------------------------------------------------
*/
2001-11-03 10:52:42 +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")
2003-08-01 00:12:10 +00:00
# define MAX_PRO_TIMING_ERROR THEME->GetMetricI("Player","MaxProTimingError")
2001-11-03 10:52:42 +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-20 11:49:10 +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-11-04 19:34:28 +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-12-20 12:37:38 +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-30 19:08:44 +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 ) ;
2001-12-20 12:37:38 +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 ) ;
2001-12-20 12:37:38 +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
}
2001-12-28 10:15:59 +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
}
2001-11-03 10:52:42 +00:00
2002-07-28 20:28:37 +00:00
void Player : : Update ( float fDeltaTime )
2001-11-04 19:34:28 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Player::Update(%f)", fDeltaTime );
2001-11-04 19:34:28 +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 ) ;
2001-12-19 01:50:57 +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-07-17 18:50:01 +00:00
// If the song beat is in the range of this hold:
if ( hn . fStartBeat < = fSongBeat & & fSongBeat < = hn . fEndBeat )
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-06-14 22:25:22 +00:00
2003-07-15 23:24:48 +00:00
// set hold flag so NoteField can do intelligent drawing
m_NoteField . m_bIsHoldingHoldNote [ i ] = bIsHoldingButton & & bSteppedOnTapNote ;
2002-04-16 17:31:00 +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
2001-12-19 01:50:57 +00:00
{
2003-07-17 18:50:01 +00:00
/* What is this conditional for? It causes a problem: if a hold note
* begins on a freeze, you can tap it and then release it for the
* duration of the freeze; life doesn't count down until we're
* past the first beat. */
// 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
2003-07-17 18:50:01 +00:00
// }
2001-12-19 01:50:57 +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-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 ) ;
// 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-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 ) ;
2001-11-03 10:52:42 +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 */
2002-05-29 09:47:24 +00:00
2003-02-09 03:33:36 +00:00
return iCurrentIndex ;
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
}
2003-07-13 00:34:30 +00:00
void Player : : Step ( int col , RageTimer tm )
2003-02-09 02:07:30 +00:00
{
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-07-13 00:34:30 +00:00
const float fStepBeat = NoteRowToBeat ( ( float ) iIndexOverlappingNote ) ;
const float fStepSeconds = GAMESTATE - > m_pCurSong - > GetElapsedTimeFromBeat ( fStepBeat ) ;
2003-02-17 05:25:20 +00:00
2003-07-13 00:34:30 +00:00
/* XXX: Double-check that we're handling m_fMusicRate correctly. */
/* We actually stepped on the note this long ago: */
const float fAgo = tm . Ago ( ) ;
/* ... which means it happened at this point in the music: */
const float fMusicSeconds = GAMESTATE - > m_fMusicSeconds - ( fAgo / GAMESTATE - > m_SongOptions . m_fMusicRate ) ;
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-07-22 05:03:38 +00:00
const float fNoteOffset = ( fStepSeconds - fMusicSeconds ) / GAMESTATE - > m_SongOptions . m_fMusicRate ; // account for music rate
2002-10-01 01:53:56 +00:00
2003-07-22 05:03:38 +00:00
const float fSecondsFromPerfect = fabsf ( fNoteOffset ) ;
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
2003-08-01 00:12:10 +00:00
if ( score ! = TNS_NONE )
{
int ms_error = ( int ) roundf ( fSecondsFromPerfect * 1000 ) ;
ms_error = min ( ms_error , MAX_PRO_TIMING_ERROR ) ;
GAMESTATE - > m_CurStageStats . iTotalError [ m_PlayerNumber ] + = ms_error ;
}
2003-07-25 08:05:29 +00:00
if ( score = = TNS_MARVELOUS & & ! GAMESTATE - > ShowMarvelous ( ) )
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-06-24 22:04:31 +00:00
2003-04-10 05:46:31 +00:00
if ( IsRowCompletelyJudged ( iIndexOverlappingNote ) )
OnRowCompletelyJudged ( iIndexOverlappingNote ) ;
2001-11-04 19:34:28 +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-04 19:34:28 +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 */
// 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
}
2001-11-04 19:34:28 +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-20 11:49:10 +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-20 11:49:10 +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.
2002-02-24 01:43:11 +00:00
// Instead, only check 10 elements back. Even 10 is overkill.
int iStartCheckingAt = max ( 0 , iMissIfOlderThanThisIndex - 10 ) ;
2001-12-19 01:50:57 +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-12-19 01:50:57 +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 + + )
2002-02-03 20:45:08 +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 ) ;
2003-08-01 00:12:10 +00:00
GAMESTATE - > m_CurStageStats . iTotalError [ m_PlayerNumber ] + = MAX_PRO_TIMING_ERROR ;
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-12-19 01:50:57 +00:00
}
2001-11-04 19:34:28 +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-04 19:34:28 +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
2003-07-13 00:34:30 +00:00
RageTimer now ;
if ( GAMESTATE - > m_PlayerController [ m_PlayerNumber ] ! = PC_HUMAN )
{
for ( int t = 0 ; t < GetNumTracks ( ) ; t + + )
if ( GetTapNote ( t , iNoteRow ) ! = TAP_EMPTY )
Step ( t , now ) ;
}
2002-06-24 22:04:31 +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 ) ;
}
}
2002-06-24 22:04:31 +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
2003-04-21 04:11:01 +00:00
if ( GAMESTATE - > m_bDemonstrationOrJukebox )
NoCheating = false ;
2002-08-23 20:18:29 +00:00
// 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 )
2002-08-23 20:18:29 +00:00
return ;
2003-06-30 18:08:27 +00:00
if ( m_pPrimaryScoreKeeper )
2003-07-08 20:56:25 +00:00
m_pPrimaryScoreKeeper - > HandleTapRowScore ( scoreOfLastTap , iNumTapsInRow ) ;
2003-06-30 18:08:27 +00:00
if ( m_pSecondaryScoreKeeper )
2003-07-08 20:56:25 +00:00
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 ;
2002-08-23 20:18:29 +00:00
// 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 )
2002-08-23 20:18:29 +00:00
return ;
2003-06-30 18:08:27 +00:00
if ( m_pPrimaryScoreKeeper )
2003-07-08 20:56:25 +00:00
m_pPrimaryScoreKeeper - > HandleHoldScore ( holdScore , tapScore ) ;
2003-06-30 18:08:27 +00:00
if ( m_pSecondaryScoreKeeper )
2003-07-08 20:56:25 +00:00
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 ( ) ;
}