2001-11-03 10:52:42 +00:00
# include "stdafx.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
2002-05-01 19:14:55 +00:00
# include "GameConstantsAndTypes.h"
2001-11-04 19:34:28 +00:00
# include "Math.h" // for fabs()
2001-11-03 10:52:42 +00:00
# include "Player.h"
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-07-28 20:28:37 +00:00
# include "RageLog.h"
2001-11-03 10:52:42 +00:00
2002-05-19 01:59:48 +00:00
// these two items are in the
const float FRAME_JUDGE_AND_COMBO_Y = CENTER_Y ;
const float JUDGEMENT_Y_OFFSET = - 26 ;
const float COMBO_Y_OFFSET = + 26 ;
2002-07-28 20:28:37 +00:00
const float FRAME_JUDGE_AND_COMBO_BEAT_TIME = 0.2f ;
2002-02-24 01:43:11 +00:00
const float ARROWS_Y = SCREEN_TOP + ARROW_SIZE * 1.5f ;
const float HOLD_JUDGEMENT_Y = ARROWS_Y + 80 ;
2001-11-03 10:52:42 +00:00
2002-06-24 22:04:31 +00:00
const float HOLD_ARROW_NG_TIME = 0.18f ;
2001-11-04 19:34:28 +00:00
2002-02-24 01:43:11 +00:00
2002-04-03 21:33:51 +00:00
Player : : Player ( )
2001-11-03 10:52:42 +00:00
{
2002-06-24 22:04:31 +00:00
m_PlayerNumber = PLAYER_INVALID ;
2001-11-20 11:49:10 +00:00
2002-04-28 20:42:32 +00:00
m_pLifeMeter = NULL ;
m_pScore = NULL ;
2001-11-04 19:34:28 +00:00
2002-07-23 01:41:40 +00:00
this - > AddSubActor ( & m_GrayArrowRow ) ;
this - > AddSubActor ( & m_NoteField ) ;
this - > AddSubActor ( & m_GhostArrowRow ) ;
2002-05-19 01:59:48 +00:00
2002-07-23 01:41:40 +00:00
m_frameJudgement . AddSubActor ( & m_Judgement ) ;
this - > AddSubActor ( & m_frameJudgement ) ;
m_frameCombo . AddSubActor ( & m_Combo ) ;
this - > AddSubActor ( & m_frameCombo ) ;
2002-05-19 01:59:48 +00:00
2002-04-16 17:31:00 +00:00
for ( int c = 0 ; c < MAX_NOTE_TRACKS ; c + + )
2002-07-23 01:41:40 +00:00
this - > AddSubActor ( & m_HoldJudgement [ c ] ) ;
2001-11-03 10:52:42 +00:00
}
2002-07-28 20:28:37 +00:00
void Player : : Load ( PlayerNumber pn , NoteData * pNoteData , LifeMeter * pLM , ScoreDisplay * pScore )
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 ;
m_pScore = pScore ;
StyleDef * pStyleDef = GAMESTATE - > GetCurrentStyleDef ( ) ;
2002-08-01 21:55:40 +00:00
// init scoring
NoteDataWithScoring : : Init ( ) ;
2002-07-28 20:28:37 +00:00
// copy note data
2002-04-16 17:31:00 +00:00
this - > CopyAll ( pNoteData ) ;
2002-08-01 21:55:40 +00:00
if ( GAMESTATE - > m_SongOptions . m_LifeType = = SongOptions : : LIFE_BATTERY & & GAMESTATE - > m_fSecondsBeforeFail [ m_PlayerNumber ] ! = - 1 ) // Oni dead
this - > ClearAll ( ) ;
2002-07-28 20:28:37 +00:00
m_iNumTapNotes = pNoteData - > GetNumTapNotes ( ) ;
m_iTapNotesHit = 0 ;
2002-08-13 23:26:46 +00:00
m_iMeter = GAMESTATE - > m_pCurNotes [ m_PlayerNumber ] ? GAMESTATE - > m_pCurNotes [ m_PlayerNumber ] - > m_iMeter : 5 ;
2002-07-28 20:28:37 +00:00
2002-02-02 05:11:12 +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-04-16 17:31:00 +00:00
this - > RemoveHoldNotes ( ) ;
2001-11-04 19:34:28 +00:00
2002-07-28 20:28:37 +00:00
this - > Turn ( GAMESTATE - > m_PlayerOptions [ pn ] . m_TurnType ) ;
2001-12-20 12:37:38 +00:00
2002-07-28 20:28:37 +00:00
if ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bLittle )
2002-04-16 17:31:00 +00:00
this - > MakeLittle ( ) ;
2001-11-30 19:08:44 +00:00
2002-08-13 23:26:46 +00:00
int iPixelsToDrawBefore = 96 ;
int iPixelsToDrawAfter = 384 ;
2002-07-28 20:28:37 +00:00
switch ( GAMESTATE - > m_PlayerOptions [ pn ] . m_EffectType )
2002-07-27 19:29:51 +00:00
{
case PlayerOptions : : EFFECT_MINI : iPixelsToDrawBefore * = 2 ; iPixelsToDrawAfter * = 2 ; break ;
case PlayerOptions : : EFFECT_SPACE : iPixelsToDrawBefore * = 2 ; iPixelsToDrawAfter * = 2 ; break ;
}
2002-07-28 20:28:37 +00:00
m_NoteField . Load ( ( NoteData * ) this , pn , iPixelsToDrawBefore , iPixelsToDrawAfter ) ;
2002-05-19 01:59:48 +00:00
2002-07-28 20:28:37 +00:00
m_GrayArrowRow . Load ( pn ) ;
m_GhostArrowRow . Load ( pn ) ;
2001-12-20 12:37:38 +00:00
2002-07-23 01:41:40 +00:00
m_frameJudgement . SetY ( FRAME_JUDGE_AND_COMBO_Y ) ;
m_frameCombo . SetY ( FRAME_JUDGE_AND_COMBO_Y ) ;
2002-07-28 20:28:37 +00:00
m_Combo . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? - COMBO_Y_OFFSET : COMBO_Y_OFFSET ) ;
m_Judgement . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? - JUDGEMENT_Y_OFFSET : JUDGEMENT_Y_OFFSET ) ;
2001-12-20 12:37:38 +00:00
2002-06-24 22:04:31 +00:00
for ( int c = 0 ; c < pStyleDef - > m_iColsPerPlayer ; c + + )
2002-07-28 20:28:37 +00:00
m_HoldJudgement [ c ] . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? SCREEN_HEIGHT - HOLD_JUDGEMENT_Y : HOLD_JUDGEMENT_Y ) ;
2002-06-24 22:04:31 +00:00
for ( c = 0 ; c < pStyleDef - > m_iColsPerPlayer ; c + + )
2002-07-28 20:28:37 +00:00
m_HoldJudgement [ c ] . SetX ( ( float ) pStyleDef - > m_ColumnInfo [ pn ] [ c ] . fXOffset ) ;
2001-12-28 10:15:59 +00:00
2002-07-28 20:28:37 +00:00
m_NoteField . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y ) ;
m_GrayArrowRow . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y ) ;
m_GhostArrowRow . SetY ( GAMESTATE - > m_PlayerOptions [ pn ] . m_bReverseScroll ? SCREEN_HEIGHT - ARROWS_Y : ARROWS_Y ) ;
2002-07-27 19:29:51 +00:00
2002-07-28 20:28:37 +00:00
if ( GAMESTATE - > m_PlayerOptions [ pn ] . m_EffectType = = PlayerOptions : : EFFECT_MINI )
2002-07-27 19:29:51 +00:00
{
m_NoteField . SetZoom ( 0.5f ) ;
m_GrayArrowRow . SetZoom ( 0.5f ) ;
m_GhostArrowRow . SetZoom ( 0.5f ) ;
}
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
2002-08-01 21:55:40 +00:00
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
//
2002-08-02 09:31:06 +00:00
int iNumMisses = UpdateTapNotesMissedOlderThan ( GAMESTATE - > m_fSongBeat - GetMaxBeatDifference ( ) ) ;
if ( iNumMisses > 0 )
int kjsdfsd = 0 ;
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-03-19 07:09:49 +00:00
for ( int i = 0 ; i < m_iNumHoldNotes ; i + + ) // for each HoldNote
2001-12-19 01:50:57 +00:00
{
2002-03-19 07:09:49 +00:00
HoldNote & hn = m_HoldNotes [ i ] ;
HoldNoteScore & hns = m_HoldNoteScores [ i ] ;
2002-06-14 22:25:22 +00:00
float & fLife = m_fHoldNoteLife [ i ] ;
2002-08-13 23:26:46 +00:00
int iHoldStartIndex = BeatToNoteRow ( hn . m_fStartBeat ) ;
int iHoldEndIndex = BeatToNoteRow ( hn . m_fEndBeat ) ;
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
2002-04-16 17:31:00 +00:00
const StyleInput StyleI ( m_PlayerNumber , hn . m_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-08-13 23:26:46 +00:00
if ( hn . m_fStartBeat < fSongBeat & & fSongBeat < hn . m_fEndBeat ) // if the song beat is in the range of this hold
2001-12-19 01:50:57 +00:00
{
2002-08-03 18:40:09 +00:00
const bool bIsHoldingButton = INPUTMAPPER - > IsButtonDown ( GameI ) | | PREFSMAN - > m_bAutoPlay | | GAMESTATE - > m_bDemonstration ;
2002-06-14 22:25:22 +00:00
// if they got a bad score or haven't stepped on the corresponding tap yet
2002-08-13 23:26:46 +00:00
const TapNoteScore tns = m_TapNoteScores [ hn . m_iTrack ] [ iHoldStartIndex ] ;
const bool bSteppedOnTapNote = tns ! = TNS_NONE & & tns ! = TNS_MISS ; // did they step on the start of this hold?
2002-06-14 22:25:22 +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-04-16 17:31:00 +00:00
2002-08-13 23:26:46 +00:00
if ( bSteppedOnTapNote & & bIsHoldingButton )
2001-12-19 01:50:57 +00:00
{
2002-06-14 22:25:22 +00:00
// Increase life
fLife + = fDeltaTime / HOLD_ARROW_NG_TIME ;
fLife = min ( fLife , 1 ) ; // clamp
2002-08-13 23:26:46 +00:00
m_NoteField . m_HoldNotes [ i ] . m_fStartBeat = fSongBeat ; // move the start of this Hold
2002-06-14 22:25:22 +00:00
2002-06-24 22:04:31 +00:00
m_GhostArrowRow . HoldNote ( hn . m_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
{
2002-08-13 23:26:46 +00:00
if ( fSongBeat - hn . m_fStartBeat > GetMaxBeatDifference ( ) )
2002-06-14 22:25:22 +00:00
{
// Decrease life
fLife - = fDeltaTime / HOLD_ARROW_NG_TIME ;
fLife = max ( fLife , 0 ) ; // clamp
}
2001-12-19 01:50:57 +00:00
}
2002-08-01 03:15:27 +00:00
m_NoteField . m_fHoldNoteLife [ i ] = fLife ; // update the NoteField display
2001-12-19 01:50:57 +00:00
}
2002-02-24 01:43:11 +00:00
// check for NG
2002-06-14 22:25:22 +00:00
if ( fLife = = 0 ) // the player has not pressed the button for a long time!
2002-02-24 01:43:11 +00:00
{
2002-06-14 22:25:22 +00:00
hns = HNS_NG ;
2002-07-28 20:28:37 +00:00
HandleNoteScore ( hns ) ;
2002-06-24 22:04:31 +00:00
m_Combo . EndCombo ( ) ;
m_HoldJudgement [ hn . m_iTrack ] . SetHoldJudgement ( HNS_NG ) ;
2002-08-01 03:15:27 +00:00
m_NoteField . m_HoldNoteScores [ i ] = HNS_NG ; // update the NoteField display
2002-02-24 01:43:11 +00:00
}
2001-12-19 01:50:57 +00:00
2002-02-24 01:43:11 +00:00
// check for OK
2002-08-13 23:26:46 +00:00
if ( fSongBeat > hn . m_fEndBeat ) // if this HoldNote is in the past
2001-12-19 01:50:57 +00:00
{
2002-06-14 22:25:22 +00:00
// At this point fLife > 0, or else we would have marked it NG above
fLife = 1 ;
hns = HNS_OK ;
2002-07-28 20:28:37 +00:00
HandleNoteScore ( hns ) ;
2002-06-24 22:04:31 +00:00
m_HoldJudgement [ hn . m_iTrack ] . SetHoldJudgement ( HNS_OK ) ;
2002-08-01 03:15:27 +00:00
m_NoteField . m_fHoldNoteLife [ i ] = fLife ; // update the NoteField display
m_NoteField . m_HoldNoteScores [ i ] = HNS_OK ; // update the NoteField display
2001-12-19 01:50:57 +00:00
}
}
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
{
2002-07-31 19:40:40 +00:00
m_frameCombo . Draw ( ) ; // draw this below everything else
2002-02-24 10:31:20 +00:00
D3DXMATRIX matOldView , matOldProj ;
2002-07-28 20:28:37 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_EffectType = = PlayerOptions : : EFFECT_SPACE )
2002-02-24 10:31:20 +00:00
{
// save old view and projection
2002-08-18 16:19:26 +00:00
DISPLAY - > GetViewTransform ( & matOldView ) ;
DISPLAY - > GetProjectionTransform ( & matOldProj ) ;
2002-02-24 10:31:20 +00:00
// construct view and project matrix
D3DXMATRIX matNewView ;
2002-07-28 20:28:37 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_bReverseScroll )
2002-07-27 19:29:51 +00:00
D3DXMatrixLookAtLH (
& matNewView ,
& D3DXVECTOR3 ( CENTER_X , GetY ( ) - 300.0f , 400.0f ) ,
& D3DXVECTOR3 ( CENTER_X , GetY ( ) + 100.0f , 0.0f ) ,
& D3DXVECTOR3 ( 0.0f , - 1.0f , 0.0f )
) ;
else
D3DXMatrixLookAtLH (
& matNewView ,
& D3DXVECTOR3 ( CENTER_X , GetY ( ) + 800.0f , 400.0f ) ,
& D3DXVECTOR3 ( CENTER_X , GetY ( ) + 400.0f , 0.0f ) ,
& D3DXVECTOR3 ( 0.0f , - 1.0f , 0.0f )
) ;
2002-08-18 16:19:26 +00:00
DISPLAY - > SetViewTransform ( & matNewView ) ;
2002-02-24 10:31:20 +00:00
D3DXMATRIX matNewProj ;
D3DXMatrixPerspectiveFovLH ( & matNewProj , D3DX_PI / 4.0f , SCREEN_WIDTH / ( float ) SCREEN_HEIGHT , 0.0f , 1000.0f ) ;
2002-08-18 16:19:26 +00:00
DISPLAY - > SetProjectionTransform ( & matNewProj ) ;
2002-02-24 10:31:20 +00:00
}
2002-04-16 17:31:00 +00:00
m_GrayArrowRow . Draw ( ) ;
m_NoteField . Draw ( ) ;
m_GhostArrowRow . Draw ( ) ;
2002-02-24 10:31:20 +00:00
2002-07-28 20:28:37 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_EffectType = = PlayerOptions : : EFFECT_SPACE )
2002-02-24 10:31:20 +00:00
{
// restire old view and projection
2002-08-18 16:19:26 +00:00
DISPLAY - > SetViewTransform ( & matOldView ) ;
DISPLAY - > SetProjectionTransform ( & matOldProj ) ;
2002-02-24 10:31:20 +00:00
}
2002-07-23 01:41:40 +00:00
m_frameJudgement . Draw ( ) ;
2002-02-24 10:31:20 +00:00
2002-04-16 17:31:00 +00:00
for ( int c = 0 ; c < m_iNumTracks ; c + + )
2002-02-24 10:31:20 +00:00
m_HoldJudgement [ c ] . Draw ( ) ;
2001-12-28 10:15:59 +00:00
}
2002-07-28 20:28:37 +00:00
void Player : : Step ( int col )
2001-11-04 19:34:28 +00:00
{
2002-08-01 21:55:40 +00:00
if ( GAMESTATE - > m_SongOptions . m_LifeType = = SongOptions : : LIFE_BATTERY & & GAMESTATE - > m_fSecondsBeforeFail [ m_PlayerNumber ] ! = - 1 ) // Oni dead
return ; // do nothing
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Player::HandlePlayerStep()" );
2001-11-03 10:52:42 +00:00
2002-04-16 17:31:00 +00:00
ASSERT ( col > = 0 & & col < = m_iNumTracks ) ;
2002-03-09 06:19:40 +00:00
2002-07-28 20:28:37 +00:00
const float fSongBeat = GAMESTATE - > m_fSongBeat ;
2001-11-03 10:52:42 +00:00
2001-11-04 19:34:28 +00:00
// look for the closest matching step
2002-07-28 20:28:37 +00:00
int iIndexStartLookingAt = BeatToNoteRow ( GAMESTATE - > m_fSongBeat ) ;
int iNumElementsToExamine = BeatToNoteRow ( GetMaxBeatDifference ( ) ) ; // number of elements to examine on either end of iIndexStartLookingAt
2001-11-03 10:52:42 +00:00
2002-07-31 19:40:40 +00:00
//LOG->Trace( "iIndexStartLookingAt = %d, iNumElementsToExamine = %d", iIndexStartLookingAt, iNumElementsToExamine );
2001-11-03 10:52:42 +00:00
2002-05-29 09:47:24 +00:00
int iIndexOverlappingNote = - 1 ; // leave as -1 if we don't find any
2002-06-24 22:04:31 +00:00
// Start at iIndexStartLookingAt and search outward. The first one note overlaps the player's hit (this is the closest match).
2001-11-04 19:34:28 +00:00
for ( int delta = 0 ; delta < = iNumElementsToExamine ; delta + + )
{
int iCurrentIndexEarlier = iIndexStartLookingAt - delta ;
int iCurrentIndexLater = iIndexStartLookingAt + delta ;
////////////////////////////
// check the step to the left of iIndexStartLookingAt
////////////////////////////
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Checking Notes[%d]", iCurrentIndexEarlier );
2002-08-17 06:44:04 +00:00
if ( iCurrentIndexEarlier > = 0 & &
m_TapNotes [ col ] [ iCurrentIndexEarlier ] ! = ' 0 ' & & // there is a note here
2002-06-24 22:04:31 +00:00
m_TapNoteScores [ col ] [ iCurrentIndexEarlier ] = = TNS_NONE ) // this note doesn't have a score
2001-11-04 19:34:28 +00:00
{
2002-05-29 09:47:24 +00:00
iIndexOverlappingNote = iCurrentIndexEarlier ;
break ;
2001-11-04 19:34:28 +00:00
}
2001-11-03 10:52:42 +00:00
2002-05-29 09:47:24 +00:00
2001-11-04 19:34:28 +00:00
////////////////////////////
// check the step to the right of iIndexStartLookingAt
////////////////////////////
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Checking Notes[%d]", iCurrentIndexLater );
2002-08-17 06:44:04 +00:00
if ( iCurrentIndexLater > = 0 & &
m_TapNotes [ col ] [ iCurrentIndexLater ] ! = ' 0 ' & & // there is a note here
2002-06-24 22:04:31 +00:00
m_TapNoteScores [ col ] [ iCurrentIndexLater ] = = TNS_NONE ) // this note doesn't have a score
2001-11-04 19:34:28 +00:00
{
2002-05-29 09:47:24 +00:00
iIndexOverlappingNote = iCurrentIndexLater ;
break ;
}
}
2002-07-11 19:02:26 +00:00
bool bDestroyedNote = false ;
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
const float fStepBeat = NoteRowToBeat ( ( float ) iIndexOverlappingNote ) ;
const float fBeatsUntilStep = fStepBeat - fSongBeat ;
2002-07-28 20:28:37 +00:00
const float fPercentFromPerfect = fabsf ( fBeatsUntilStep / GetMaxBeatDifference ( ) ) ;
2002-06-24 22:04:31 +00:00
TapNoteScore & score = m_TapNoteScores [ col ] [ iIndexOverlappingNote ] ;
2002-07-11 19:02:26 +00:00
if ( fPercentFromPerfect < 0.25f ) score = TNS_PERFECT ;
else if ( fPercentFromPerfect < 0.50f ) score = TNS_GREAT ;
else if ( fPercentFromPerfect < 0.75f ) score = TNS_GOOD ;
2002-06-24 22:04:31 +00:00
else score = TNS_BOO ;
2002-07-11 19:02:26 +00:00
bDestroyedNote = score > = TNS_GOOD ;
2002-06-24 22:04:31 +00:00
2002-05-29 09:47:24 +00:00
bool bRowDestroyed = true ;
for ( int t = 0 ; t < m_iNumTracks ; t + + ) // did this complete the elminiation of the row?
{
2002-06-24 22:04:31 +00:00
if ( m_TapNotes [ t ] [ iIndexOverlappingNote ] ! = ' 0 ' & & // there is a note here
m_TapNoteScores [ t ] [ iIndexOverlappingNote ] = = TNS_NONE ) // and it doesn't have a score
{
2002-05-29 09:47:24 +00:00
bRowDestroyed = false ;
2002-06-24 22:04:31 +00:00
break ; // stop searching
}
2001-11-04 19:34:28 +00:00
}
2002-05-29 09:47:24 +00:00
if ( bRowDestroyed )
2002-07-28 20:28:37 +00:00
OnRowDestroyed ( col , iIndexOverlappingNote ) ;
2001-11-04 19:34:28 +00:00
}
2002-07-11 19:02:26 +00:00
if ( ! bDestroyedNote )
m_GrayArrowRow . Step ( col ) ;
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 : : OnRowDestroyed ( int col , int iIndexThatWasSteppedOn )
2001-11-04 19:34:28 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "fBeatsUntilStep: %f, fPercentFromPerfect: %f",
2001-11-06 02:31:28 +00:00
// fBeatsUntilStep, fPercentFromPerfect );
2002-06-24 22:04:31 +00:00
// find the minimum score of the row
TapNoteScore score = TNS_PERFECT ;
for ( int t = 0 ; t < m_iNumTracks ; t + + )
if ( m_TapNoteScores [ t ] [ iIndexThatWasSteppedOn ] > = TNS_BOO )
score = min ( score , m_TapNoteScores [ t ] [ iIndexThatWasSteppedOn ] ) ;
2001-11-04 19:34:28 +00:00
2002-04-16 17:31:00 +00:00
// remove this row from the NoteField
2002-08-13 23:26:46 +00:00
// bool bHoldNoteOnThisBeat = false;
// for( int j=0; j<m_iNumHoldNotes; j++ )
// {
// if( m_HoldNotes[j].m_iStartIndex == iIndexThatWasSteppedOn )
// {
// bHoldNoteOnThisBeat = true;
// break;
// }
// }
// if ( score==TNS_PERFECT || score == TNS_GREAT || bHoldNoteOnThisBeat )
if ( score = = TNS_PERFECT | | score = = TNS_GREAT )
2002-04-28 20:42:32 +00:00
m_NoteField . RemoveTapNoteRow ( iIndexThatWasSteppedOn ) ;
2002-02-24 01:43:11 +00:00
2002-04-16 17:31:00 +00:00
for ( int c = 0 ; c < m_iNumTracks ; c + + ) // for each column
2002-02-24 01:43:11 +00:00
{
2002-06-24 22:04:31 +00:00
if ( m_TapNotes [ c ] [ iIndexThatWasSteppedOn ] ! = ' 0 ' ) // if there is a note in this col
{
2002-04-16 17:31:00 +00:00
m_GhostArrowRow . TapNote ( c , score , m_Combo . GetCurrentCombo ( ) > 100 ) ; // show the ghost arrow for this column
2002-06-24 22:04:31 +00:00
2002-07-28 20:28:37 +00:00
HandleNoteScore ( score ) ; // update score - called once per note in this row
2002-07-23 01:41:40 +00:00
2002-06-24 22:04:31 +00:00
// update combo - called once per note in this row
switch ( score )
{
case TNS_PERFECT :
case TNS_GREAT :
m_Combo . ContinueCombo ( ) ;
2002-07-28 20:28:37 +00:00
GAMESTATE - > m_iMaxCombo [ m_PlayerNumber ] = max ( GAMESTATE - > m_iMaxCombo [ m_PlayerNumber ] , m_Combo . GetCurrentCombo ( ) ) ;
2002-06-24 22:04:31 +00:00
break ;
case TNS_GOOD :
case TNS_BOO :
m_Combo . EndCombo ( ) ;
break ;
}
}
2001-11-03 10:52:42 +00:00
}
2002-05-19 01:59:48 +00:00
2002-07-28 20:28:37 +00:00
// update the judgement, score, and life
m_Judgement . SetJudgement ( score ) ;
if ( m_pLifeMeter )
m_pLifeMeter - > ChangeLife ( score ) ;
2002-05-19 01:59:48 +00:00
// zoom the judgement and combo like a heart beat
float fStartZoom ;
switch ( score )
{
2002-07-28 20:28:37 +00:00
case TNS_PERFECT : fStartZoom = 1.3f ; break ;
case TNS_GREAT : fStartZoom = 1.2f ; break ;
case TNS_GOOD : fStartZoom = 1.1f ; break ;
2002-05-19 01:59:48 +00:00
case TNS_BOO : fStartZoom = 1.0f ; break ;
}
2002-07-23 01:41:40 +00:00
m_frameJudgement . SetZoom ( fStartZoom ) ;
2002-07-28 20:28:37 +00:00
m_frameJudgement . BeginTweening ( 0.1f ) ;
2002-07-23 01:41:40 +00:00
m_frameJudgement . SetTweenZoom ( 1 ) ;
m_frameCombo . SetZoom ( fStartZoom ) ;
2002-07-28 20:28:37 +00:00
m_frameCombo . BeginTweening ( 0.1f ) ;
2002-07-23 01:41:40 +00:00
m_frameCombo . SetTweenZoom ( 1 ) ;
2001-11-03 10:52:42 +00:00
}
2001-11-04 19:34:28 +00:00
2002-04-16 17:31:00 +00:00
int Player : : UpdateTapNotesMissedOlderThan ( float fMissIfOlderThanThisBeat )
2001-11-04 19:34:28 +00:00
{
2002-07-31 19:40:40 +00:00
//LOG->Trace( "Notes::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
2001-11-20 11:49:10 +00:00
2002-05-19 01:59:48 +00:00
int iMissIfOlderThanThisIndex = BeatToNoteRow ( fMissIfOlderThanThisBeat ) ;
2001-11-20 11:49:10 +00:00
2002-02-24 01:43:11 +00:00
int iNumMissesFound = 0 ;
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 );
2002-06-24 22:04:31 +00:00
for ( int t = 0 ; t < m_iNumTracks ; t + + )
2001-12-19 01:50:57 +00:00
{
2002-06-24 22:04:31 +00:00
for ( int r = iStartCheckingAt ; r < iMissIfOlderThanThisIndex ; r + + )
2002-02-03 20:45:08 +00:00
{
2002-06-24 22:04:31 +00:00
bool bFoundAMissInThisRow = false ;
if ( m_TapNotes [ t ] [ r ] ! = ' 0 ' & & m_TapNoteScores [ t ] [ r ] = = TNS_NONE )
{
m_TapNoteScores [ t ] [ r ] = TNS_MISS ;
iNumMissesFound + + ;
bFoundAMissInThisRow = true ;
2002-07-28 20:28:37 +00:00
HandleNoteScore ( TNS_MISS ) ;
2002-06-24 22:04:31 +00:00
}
if ( bFoundAMissInThisRow )
2002-07-02 00:27:58 +00:00
if ( m_pLifeMeter )
m_pLifeMeter - > ChangeLife ( TNS_MISS ) ;
2002-02-03 20:45:08 +00:00
}
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 )
{
m_Judgement . SetJudgement ( TNS_MISS ) ;
m_Combo . EndCombo ( ) ;
}
2002-02-24 01:43:11 +00:00
return iNumMissesFound ;
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
{
2002-08-02 09:31:06 +00:00
if ( PREFSMAN - > m_bAutoPlay | | GAMESTATE - > m_bDemonstration )
2002-06-24 22:04:31 +00:00
{
// check to see if there's at the crossed row
for ( int t = 0 ; t < m_iNumTracks ; t + + )
{
if ( m_TapNotes [ t ] [ iNoteRow ] ! = ' 0 ' )
2002-07-28 20:28:37 +00:00
this - > Step ( t ) ;
2002-06-24 22:04:31 +00:00
}
}
}
2001-11-04 19:34:28 +00:00
2002-07-28 20:28:37 +00:00
void Player : : HandleNoteScore ( TapNoteScore score )
2002-06-24 22:04:31 +00:00
{
2002-07-28 20:28:37 +00:00
// update dance points for Oni lifemeter
GAMESTATE - > m_iActualDancePoints [ m_PlayerNumber ] + = TapNoteScoreToDancePoints ( score ) ;
GAMESTATE - > m_TapNoteScores [ m_PlayerNumber ] [ score ] + + ;
if ( m_pLifeMeter )
m_pLifeMeter - > OnDancePointsChange ( ) ;
//A single step's points are calculated as follows:
//
//Let p = score multiplier (Perfect = 10, Great = 5, other = 0)
//N = total number of steps and freeze steps
//n = number of the current step or freeze step (varies from 1 to N)
//B = Base value of the song (1,000,000 X the number of feet difficulty) - All edit data is rated as 5 feet
//So, the score for one step is:
//one_step_score = p * (B/S) * n
//Where S = The sum of all integers from 1 to N (the total number of steps/freeze steps)
//
//*IMPORTANT* : Double steps (U+L, D+R, etc.) count as two steps instead of one, so if you get a double L+R on the 112th step of a song, you score is calculated with a Perfect/Great/whatever for both the 112th and 113th steps. Got it? Now, through simple algebraic manipulation
//S = 1+...+N = (1+N)*N/2 (1 through N added together)
//Okay, time for an example:
//
//So, for example, suppose we wanted to calculate the step score of a "Great" on the 57th step of a 441 step, 8-foot difficulty song (I'm just making this one up):
//
//S = (1 + 441)*441 / 2
//= 194,222 / 2
//= 97,461
//StepScore = p * (B/S) * n
//= 5 * (8,000,000 / 97,461) * 57
//= 5 * (82) * 57 (The 82 is rounded down from 82.08411...)
//= 23,370
//Remember this is just the score for the step, not the cumulative score up to the 57th step. Also, please note that I am currently checking into rounding errors with the system and if there are any, how they are resolved in the system.
//
//Note: if you got all Perfect on this song, you would get (p=10)*B, which is 80,000,000. In fact, the maximum possible score for any song is the number of feet difficulty X 10,000,000.
int p ; // score multiplier
switch ( score )
{
case TNS_PERFECT : p = 10 ; break ;
case TNS_GREAT : p = 5 ; break ;
default : p = 0 ; break ;
}
int N = m_iNumTapNotes ;
int n = m_iTapNotesHit + 1 ;
int B = m_iMeter * 1000000 ;
float S = ( 1 + N ) * N / 2.0f ;
2001-11-04 19:34:28 +00:00
2002-08-13 23:26:46 +00:00
// printf( "m_iNumTapNotes %d, m_iTapNotesHit %d\n", m_iNumTapNotes, m_iTapNotesHit );
2001-11-04 19:34:28 +00:00
2002-07-28 20:28:37 +00:00
float one_step_score = p * ( B / S ) * n ;
2002-06-24 22:04:31 +00:00
2002-07-28 20:28:37 +00:00
float & fScore = GAMESTATE - > m_fScore [ m_PlayerNumber ] ;
ASSERT ( fScore > = 0 ) ;
2002-06-24 22:04:31 +00:00
2002-07-28 20:28:37 +00:00
fScore + = one_step_score ;
2001-11-04 19:34:28 +00:00
2002-07-28 20:28:37 +00:00
m_iTapNotesHit + + ;
ASSERT ( m_iTapNotesHit < = m_iNumTapNotes ) ;
// HACK: Correct for rounding errors that cause a 100% perfect score to be slightly off
if ( m_iTapNotesHit = = m_iNumTapNotes & &
fabsf ( fScore - froundf ( fScore , 1000000 ) ) < 50.0f ) // close to a multiple of 1,000,000
fScore = froundf ( fScore , 1000000 ) ;
if ( m_pScore )
m_pScore - > SetScore ( fScore ) ;
2002-06-24 22:04:31 +00:00
}
2002-07-28 20:28:37 +00:00
void Player : : HandleNoteScore ( HoldNoteScore score )
{
// update dance points for Oni lifemeter
GAMESTATE - > m_iActualDancePoints [ m_PlayerNumber ] + = HoldNoteScoreToDancePoints ( score ) ;
GAMESTATE - > m_HoldNoteScores [ m_PlayerNumber ] [ score ] + + ;
if ( m_pLifeMeter )
m_pLifeMeter - > OnDancePointsChange ( ) ;
// HoldNoteScores don't effect m_fScore
}
float Player : : GetMaxBeatDifference ( )
{
return GAMESTATE - > m_fCurBPS * PREFSMAN - > m_fJudgeWindow * GAMESTATE - > m_SongOptions . m_fMusicRate ;
}