2003-02-16 04:01:45 +00:00
# include "global.h"
2003-04-10 05:46:31 +00:00
# include "Player.h"
2002-05-01 19:14:55 +00:00
# include "GameConstantsAndTypes.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-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-11-11 07:36:28 +00:00
# include "CombinedLifeMeter.h"
2003-04-07 22:07:44 +00:00
# include "PlayerAI.h"
2003-04-14 22:32:08 +00:00
# include "NoteFieldPositioning.h"
2003-08-07 06:36:34 +00:00
# include "NoteDataUtil.h"
2003-09-06 02:20:09 +00:00
# include "ScreenGameplay.h" /* for SM_ComboStopped */
# include "ScreenManager.h"
2003-12-23 00:26:00 +00:00
# include "StageStats.h"
2004-02-25 07:30:12 +00:00
# include "ArrowEffects.h"
2001-11-03 10:52:42 +00:00
2003-12-22 10:30:10 +00:00
CachedThemeMetricF GRAY_ARROWS_Y_STANDARD ( " Player " , " ReceptorArrowsYStandard " ) ;
CachedThemeMetricF GRAY_ARROWS_Y_REVERSE ( " Player " , " ReceptorArrowsYReverse " ) ;
2004-03-24 07:05:50 +00:00
# define JUDGMENT_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? "JudgmentXOffsetBothSides" : ssprintf("JudgmentXOffsetOneSideP%d",p+1))
# define JUDGMENT_Y THEME->GetMetricF("Player","JudgmentY")
# define JUDGMENT_Y_REVERSE THEME->GetMetricF("Player","JudgmentYReverse")
# define COMBO_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? "ComboXOffsetBothSides" : ssprintf("ComboXOffsetOneSideP%d",p+1))
# define COMBO_Y THEME->GetMetricF("Player","ComboY")
# define COMBO_Y_REVERSE THEME->GetMetricF("Player","ComboYReverse")
# define ATTACK_DISPLAY_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? "AttackDisplayXOffsetBothSides" : ssprintf("AttackDisplayXOffsetOneSideP%d",p+1))
# define ATTACK_DISPLAY_Y THEME->GetMetricF("Player","AttackDisplayY")
# define ATTACK_DISPLAY_Y_REVERSE THEME->GetMetricF("Player","AttackDisplayYReverse")
2003-08-14 08:13:08 +00:00
CachedThemeMetricF HOLD_JUDGMENT_Y_STANDARD ( " Player " , " HoldJudgmentYStandard " ) ;
CachedThemeMetricF HOLD_JUDGMENT_Y_REVERSE ( " Player " , " HoldJudgmentYReverse " ) ;
CachedThemeMetricI BRIGHT_GHOST_COMBO_THRESHOLD ( " Player " , " BrightGhostComboThreshold " ) ;
2003-11-25 06:45:06 +00:00
CachedThemeMetricB TAP_JUDGMENTS_UNDER_FIELD ( " Player " , " TapJudgmentsUnderField " ) ;
CachedThemeMetricB HOLD_JUDGMENTS_UNDER_FIELD ( " Player " , " HoldJudgmentsUnderField " ) ;
2004-03-24 07:05:50 +00:00
# define START_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StartDrawingAtPixels")
# define STOP_DRAWING_AT_PIXELS THEME->GetMetricI("Player","StopDrawingAtPixels")
# 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
2004-06-27 00:03:06 +00:00
# define ADJUSTED_WINDOW( judge ) ((PREFSMAN->m_fJudgeWindowSeconds##judge * PREFSMAN->m_fJudgeWindowScale) + PREFSMAN->m_fJudgeWindowAdd)
2003-04-07 21:24:14 +00:00
2003-08-24 17:04:21 +00:00
PlayerMinus : : PlayerMinus ( )
2001-11-03 10:52:42 +00:00
{
2003-08-14 08:13:08 +00:00
GRAY_ARROWS_Y_STANDARD . Refresh ( ) ;
GRAY_ARROWS_Y_REVERSE . Refresh ( ) ;
HOLD_JUDGMENT_Y_STANDARD . Refresh ( ) ;
HOLD_JUDGMENT_Y_REVERSE . Refresh ( ) ;
2003-02-17 12:19:42 +00:00
BRIGHT_GHOST_COMBO_THRESHOLD . Refresh ( ) ;
2003-11-25 06:45:06 +00:00
TAP_JUDGMENTS_UNDER_FIELD . Refresh ( ) ;
HOLD_JUDGMENTS_UNDER_FIELD . Refresh ( ) ;
2002-06-24 22:04:31 +00:00
m_PlayerNumber = PLAYER_INVALID ;
2004-02-25 07:30:12 +00:00
m_fNoteFieldHeight = 0 ;
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 ;
2003-11-26 06:40:03 +00:00
m_pScoreDisplay = NULL ;
m_pSecondaryScoreDisplay = 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
2004-04-15 05:41:57 +00:00
m_iOffsetSample = 0 ;
2003-04-14 23:09:35 +00:00
this - > AddChild ( & m_ArrowBackdrop ) ;
2003-02-17 12:19:42 +00:00
this - > AddChild ( & m_Judgment ) ;
2003-08-01 01:18:08 +00:00
this - > AddChild ( & m_ProTimingDisplay ) ;
2003-02-17 12:19:42 +00:00
this - > AddChild ( & m_Combo ) ;
2003-11-26 08:25:45 +00:00
this - > AddChild ( & m_AttackDisplay ) ;
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
2003-08-20 09:58:11 +00:00
2003-04-21 02:41:10 +00:00
PlayerAI : : InitFromDisk ( ) ;
2001-11-03 10:52:42 +00:00
}
2003-08-24 17:04:21 +00:00
PlayerMinus : : ~ PlayerMinus ( )
2002-11-03 07:17:03 +00:00
{
}
2003-11-27 05:23:33 +00:00
void PlayerMinus : : Load ( PlayerNumber pn , const NoteData * pNoteData , LifeMeter * pLM , CombinedLifeMeter * pCombinedLM , ScoreDisplay * pScoreDisplay , ScoreDisplay * pSecondaryScoreDisplay , Inventory * pInventory , ScoreKeeper * pPrimaryScoreKeeper , ScoreKeeper * pSecondaryScoreKeeper , NoteField * pNoteField )
2002-02-02 05:11:12 +00:00
{
2003-11-10 23:25:44 +00:00
m_iDCState = AS2D_IDLE ;
2003-08-24 17:04:21 +00:00
//LOG->Trace( "PlayerMinus::Load()", );
2002-07-28 20:28:37 +00:00
2003-10-26 03:02:30 +00:00
GAMESTATE - > ResetNoteSkinsForPlayer ( pn ) ;
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 ;
2003-11-26 06:40:03 +00:00
m_pScoreDisplay = pScoreDisplay ;
m_pSecondaryScoreDisplay = pSecondaryScoreDisplay ;
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-08-24 17:04:21 +00:00
m_pNoteField = pNoteField ;
2004-02-25 05:49:54 +00:00
m_iRowLastCrossed = BeatToNoteRowNotRounded ( GAMESTATE - > m_fSongBeat ) - 1 ; // why this?
m_iMineRowLastCrossed = BeatToNoteRowNotRounded ( GAMESTATE - > m_fSongBeat ) - 1 ; // why this?
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
2004-06-28 07:26:00 +00:00
const Style * pStyle = GAMESTATE - > GetCurrentStyle ( ) ;
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-12-23 00:26:00 +00:00
if ( GAMESTATE - > m_SongOptions . m_LifeType = = SongOptions : : LIFE_BATTERY & & g_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-12-23 00:26:00 +00:00
m_Combo . SetCombo ( g_CurStageStats . iCurCombo [ m_PlayerNumber ] ) ; // combo can persist between songs and games
2003-11-26 08:25:45 +00:00
m_AttackDisplay . Init ( pn ) ;
2003-02-17 12:19:42 +00:00
m_Judgment . Reset ( ) ;
2002-07-28 20:28:37 +00:00
2003-10-10 23:44:17 +00:00
/* Don't re-init this; that'll reload graphics. Add a separate Reset() call
* if some ScoreDisplays need it. */
// if( m_pScore )
// m_pScore->Init( pn );
2002-04-28 20:42:32 +00:00
2003-08-13 19:17:28 +00:00
/* Apply transforms. */
2004-06-28 07:26:00 +00:00
NoteDataUtil : : TransformNoteData ( * this , GAMESTATE - > m_PlayerOptions [ pn ] , GAMESTATE - > GetCurrentStyle ( ) - > m_StepsType ) ;
2003-11-20 06:50:05 +00:00
switch ( GAMESTATE - > m_PlayMode )
{
2003-11-26 06:40:03 +00:00
case PLAY_MODE_RAVE :
2003-11-20 06:50:05 +00:00
case PLAY_MODE_BATTLE :
{
// ugly, ugly, ugly. Works only w/ dance.
2004-06-28 07:26:00 +00:00
NoteDataUtil : : TransformNoteData ( * this , GAMESTATE - > m_PlayerOptions [ pn ] , GAMESTATE - > GetCurrentStyle ( ) - > m_StepsType ) ;
2003-11-20 06:50:05 +00:00
// shuffle either p1 or p2
static int count = 0 ;
switch ( count )
{
case 0 :
case 3 :
2004-05-16 02:51:55 +00:00
NoteDataUtil : : Turn ( * this , STEPS_TYPE_DANCE_SINGLE , NoteDataUtil : : left ) ;
2003-11-20 06:50:05 +00:00
break ;
case 1 :
case 2 :
2004-05-16 02:51:55 +00:00
NoteDataUtil : : Turn ( * this , STEPS_TYPE_DANCE_SINGLE , NoteDataUtil : : right ) ;
2003-11-20 06:50:05 +00:00
break ;
default :
ASSERT ( 0 ) ;
}
count + + ;
count % = 4 ;
}
break ;
}
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-09-21 18:07:29 +00:00
float fNoteFieldMidde = ( GRAY_ARROWS_Y_STANDARD + GRAY_ARROWS_Y_REVERSE ) / 2 ;
m_pNoteField - > SetY ( fNoteFieldMidde ) ;
2004-02-25 07:30:12 +00:00
m_fNoteFieldHeight = GRAY_ARROWS_Y_REVERSE - GRAY_ARROWS_Y_STANDARD ;
m_pNoteField - > Load ( this , pn , iStartDrawingAtPixels , iStopDrawingAtPixels , m_fNoteFieldHeight ) ;
2003-04-14 22:32:08 +00:00
m_ArrowBackdrop . SetPlayer ( pn ) ;
2001-12-20 12:37:38 +00:00
2003-08-17 00:15:54 +00:00
const bool bReverse = GAMESTATE - > m_PlayerOptions [ pn ] . GetReversePercentForColumn ( 0 ) = = 1 ;
2004-06-28 07:26:00 +00:00
bool bPlayerUsingBothSides = GAMESTATE - > GetCurrentStyle ( ) - > m_StyleType = = Style : : ONE_PLAYER_TWO_CREDITS ;
2003-04-14 22:12:54 +00:00
m_Combo . SetX ( COMBO_X ( m_PlayerNumber , bPlayerUsingBothSides ) ) ;
2003-10-16 09:21:12 +00:00
m_Combo . SetY ( bReverse ? COMBO_Y_REVERSE : COMBO_Y ) ;
2003-11-26 08:25:45 +00:00
m_AttackDisplay . SetX ( ATTACK_DISPLAY_X ( m_PlayerNumber , bPlayerUsingBothSides ) ) ;
m_AttackDisplay . SetY ( bReverse ? ATTACK_DISPLAY_Y_REVERSE : ATTACK_DISPLAY_Y ) ;
2003-04-14 22:12:54 +00:00
m_Judgment . SetX ( JUDGMENT_X ( m_PlayerNumber , bPlayerUsingBothSides ) ) ;
2003-10-16 09:21:12 +00:00
m_Judgment . SetY ( bReverse ? JUDGMENT_Y_REVERSE : JUDGMENT_Y ) ;
2003-08-01 01:18:08 +00:00
m_ProTimingDisplay . SetX ( JUDGMENT_X ( m_PlayerNumber , bPlayerUsingBothSides ) ) ;
m_ProTimingDisplay . 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 ) ;
2003-08-01 01:18:08 +00:00
m_ProTimingDisplay . Command ( g_NoteFieldMode [ pn ] . m_JudgmentCmd ) ;
2003-04-17 22:53:36 +00:00
m_Combo . Command ( g_NoteFieldMode [ pn ] . m_ComboCmd ) ;
2003-11-26 08:25:45 +00:00
m_AttackDisplay . Command ( g_NoteFieldMode [ pn ] . m_AttackDisplayCmd ) ;
2003-04-17 22:53:36 +00:00
2002-11-16 09:12:55 +00:00
int c ;
2004-06-28 07:26:00 +00:00
for ( c = 0 ; c < pStyle - > m_iColsPerPlayer ; c + + )
2003-02-17 12:19:42 +00:00
{
2004-06-19 23:04:10 +00:00
NoteFieldMode & mode = g_NoteFieldMode [ pn ] ;
m_HoldJudgment [ c ] . Command ( mode . m_HoldJudgmentCmd [ c ] ) ;
2003-02-17 12:19:42 +00:00
}
2001-12-28 10:15:59 +00:00
2003-08-14 08:13:08 +00:00
// Need to set Y positions of all these elements in Update since
// they change depending on PlayerOptions.
2003-04-22 04:54:04 +00:00
2004-02-28 04:14:13 +00:00
m_soundMine . Load ( THEME - > GetPathToS ( " Player mine " ) , true ) ;
if ( GAMESTATE - > GetNumPlayersEnabled ( ) = = 2 )
{
/* Two players are active. Play mines on the player's side. */
RageSoundParams p ;
p . m_Balance = ( m_PlayerNumber = = PLAYER_1 ) ? - 1.0f : 1.0f ;
m_soundMine . SetParams ( p ) ;
}
2004-04-08 03:40:28 +00:00
/* Attacks can be launched in course modes and in battle modes. They both come
* here to play, but allow loading a different sound for different modes. */
switch ( GAMESTATE - > m_PlayMode )
{
case PLAY_MODE_RAVE :
case PLAY_MODE_BATTLE :
m_soundAttackLaunch . Load ( THEME - > GetPathToS ( ssprintf ( " Player battle attack launch p%d " , pn + 1 ) ) , true ) ;
m_soundAttackEnding . Load ( THEME - > GetPathToS ( ssprintf ( " Player battle attack ending p%d " , pn + 1 ) ) , true ) ;
break ;
default :
m_soundAttackLaunch . Load ( THEME - > GetPathToS ( ssprintf ( " Player course attack launch p%d " , pn + 1 ) ) , true ) ;
m_soundAttackEnding . Load ( THEME - > GetPathToS ( ssprintf ( " Player course attack ending p%d " , pn + 1 ) ) , true ) ;
break ;
}
2001-11-04 19:34:28 +00:00
}
2001-11-03 10:52:42 +00:00
2003-08-24 17:04:21 +00:00
void PlayerMinus : : Update ( float fDeltaTime )
2001-11-04 19:34:28 +00:00
{
2003-08-24 17:04:21 +00:00
//LOG->Trace( "PlayerMinus::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 ;
2004-03-24 07:37:35 +00:00
if ( GAMESTATE - > m_bAttackBeganThisUpdate [ m_PlayerNumber ] )
m_soundAttackLaunch . Play ( ) ;
2003-11-27 02:30:54 +00:00
if ( GAMESTATE - > m_bAttackEndedThisUpdate [ m_PlayerNumber ] )
2003-11-20 06:50:05 +00:00
m_soundAttackEnding . Play ( ) ;
2002-07-28 20:28:37 +00:00
const float fSongBeat = GAMESTATE - > m_fSongBeat ;
2003-12-16 04:00:39 +00:00
const int iSongRow = BeatToNoteRow ( fSongBeat ) ;
2002-06-23 11:43:53 +00:00
2003-08-24 17:04:21 +00:00
m_pNoteField - > Update ( fDeltaTime ) ;
2003-08-14 08:13:08 +00:00
//
// Update Y positions
//
2004-02-25 07:30:12 +00:00
{
2004-06-28 07:26:00 +00:00
for ( int c = 0 ; c < GAMESTATE - > GetCurrentStyle ( ) - > m_iColsPerPlayer ; c + + )
2004-02-25 07:30:12 +00:00
{
float fPercentReverse = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . GetReversePercentForColumn ( c ) ;
float fHoldJudgeYPos = SCALE ( fPercentReverse , 0.f , 1.f , HOLD_JUDGMENT_Y_STANDARD , HOLD_JUDGMENT_Y_REVERSE ) ;
2004-02-26 03:18:51 +00:00
// float fGrayYPos = SCALE( fPercentReverse, 0.f, 1.f, GRAY_ARROWS_Y_STANDARD, GRAY_ARROWS_Y_REVERSE );
2004-02-25 07:30:12 +00:00
const float fX = ArrowGetXPos ( m_PlayerNumber , c , 0 ) ;
const float fZ = ArrowGetZPos ( m_PlayerNumber , c , 0 ) ;
m_HoldJudgment [ c ] . SetX ( fX ) ;
m_HoldJudgment [ c ] . SetY ( fHoldJudgeYPos ) ;
m_HoldJudgment [ c ] . SetZ ( fZ ) ;
}
}
2003-08-17 00:15:54 +00:00
float fPercentReverse = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . GetReversePercentForColumn ( 0 ) ;
2003-08-14 08:13:08 +00:00
float fGrayYPos = SCALE ( fPercentReverse , 0.f , 1.f , GRAY_ARROWS_Y_STANDARD , GRAY_ARROWS_Y_REVERSE ) ;
m_ArrowBackdrop . SetY ( fGrayYPos ) ;
2003-08-16 23:34:47 +00:00
// NoteField accounts for reverse on its own now.
2003-08-24 17:04:21 +00:00
// m_pNoteField->SetY( fGrayYPos );
2003-08-14 08:13:08 +00:00
float fMiniPercent = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . m_fEffects [ PlayerOptions : : EFFECT_MINI ] ;
2004-03-25 09:31:38 +00:00
float fNoteFieldZoom = 1 - fMiniPercent * 0.5f ;
float fJudgmentZoom = 1 - fMiniPercent * 0.25f ;
m_pNoteField - > SetZoom ( fNoteFieldZoom ) ;
m_Judgment . SetZoom ( fJudgmentZoom ) ;
2003-08-14 08:13:08 +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
//
2004-05-24 04:26:54 +00:00
UpdateTapNotesMissedOlderThan ( GetMaxStepDistanceSeconds ( ) ) ;
2001-11-04 19:34:28 +00:00
2003-12-22 10:30:10 +00:00
//
// update pressed flag
//
2004-06-28 07:26:00 +00:00
const int iNumCols = GAMESTATE - > GetCurrentStyle ( ) - > m_iColsPerPlayer ;
2004-05-31 01:03:45 +00:00
ASSERT_M ( iNumCols < MAX_COLS_PER_PLAYER , ssprintf ( " %i >= %i " , iNumCols , MAX_COLS_PER_PLAYER ) ) ;
for ( int col = 0 ; col < iNumCols ; + + col )
2003-10-09 03:50:44 +00:00
{
2004-05-31 01:03:45 +00:00
CHECKPOINT_M ( ssprintf ( " %i %i " , col , iNumCols ) ) ;
const StyleInput StyleI ( m_PlayerNumber , col ) ;
2004-06-28 07:26:00 +00:00
const GameInput GameI = GAMESTATE - > GetCurrentStyle ( ) - > StyleInputToGameInput ( StyleI ) ;
2003-10-09 03:50:44 +00:00
bool bIsHoldingButton = INPUTMAPPER - > IsButtonDown ( GameI ) ;
2004-01-01 04:06:37 +00:00
// TODO: Make this work for non-human-controlled players
if ( bIsHoldingButton & & ! GAMESTATE - > m_bDemonstrationOrJukebox & & GAMESTATE - > m_PlayerController [ m_PlayerNumber ] = = PC_HUMAN )
2004-05-31 01:03:45 +00:00
m_pNoteField - > SetPressed ( col ) ;
2003-10-09 03:50:44 +00:00
}
2004-05-30 22:55:05 +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 ) ;
2003-12-16 06:10:50 +00:00
HoldNoteScore hns = GetHoldNoteScore ( hn ) ;
2002-08-13 23:26:46 +00:00
2003-12-18 02:34:59 +00:00
m_pNoteField - > m_HeldHoldNotes [ hn ] = false ; // set hold flag so NoteField can do intelligent drawing
2004-01-01 01:53:25 +00:00
m_pNoteField - > m_ActiveHoldNotes [ hn ] = false ; // set hold flag so NoteField can do intelligent drawing
2002-08-13 23:26:46 +00:00
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-12-16 06:10:50 +00:00
if ( iSongRow < hn . iStartRow )
continue ; // hold hasn't happened yet
2003-02-16 20:27:53 +00:00
const StyleInput StyleI ( m_PlayerNumber , hn . iTrack ) ;
2004-06-28 07:26:00 +00:00
const GameInput GameI = GAMESTATE - > GetCurrentStyle ( ) - > 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-12-16 06:10:50 +00:00
const TapNoteScore tns = GetTapNoteScore ( hn . iTrack , hn . iStartRow ) ;
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-12-16 06:10:50 +00:00
float fLife = GetHoldNoteLife ( hn ) ;
2003-07-17 18:50:01 +00:00
// If the song beat is in the range of this hold:
2003-12-16 04:00:39 +00:00
if ( hn . iStartRow < = iSongRow & & iSongRow < = hn . iEndRow )
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
2003-12-18 02:34:59 +00:00
m_pNoteField - > m_HeldHoldNotes [ hn ] = bIsHoldingButton & & bSteppedOnTapNote ;
2004-01-01 01:53:25 +00:00
m_pNoteField - > m_ActiveHoldNotes [ hn ] = bSteppedOnTapNote ;
2002-04-16 17:31:00 +00:00
2004-05-07 04:57:29 +00:00
if ( bSteppedOnTapNote )
2003-08-18 04:20:51 +00:00
{
2004-05-07 04:57:29 +00:00
/* This hold note is not judged and we stepped on its head. Update
* iLastHeldRow. */
HoldNoteResult * hnr = m_pNoteField - > CreateHoldNoteResult ( hn ) ;
hnr - > iLastHeldRow = min ( iSongRow , hn . iEndRow ) ;
hnr = this - > CreateHoldNoteResult ( hn ) ;
hnr - > iLastHeldRow = min ( iSongRow , hn . iEndRow ) ;
2003-08-18 04:20:51 +00:00
}
2002-09-15 19:38:44 +00:00
2004-01-01 01:53:25 +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-11-27 04:25:52 +00:00
m_pNoteField - > DidHoldNote ( 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
2004-06-27 00:03:06 +00:00
fLife - = fDeltaTime / ADJUSTED_WINDOW ( OK ) ;
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-12-16 04:00:39 +00:00
if ( iSongRow > = hn . iEndRow & & 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 ;
2003-11-27 04:25:52 +00:00
m_pNoteField - > DidTapNote ( 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 ) ;
2003-08-01 01:18:08 +00:00
int ms_error = ( hns = = HNS_OK ) ? 0 : MAX_PRO_TIMING_ERROR ;
2003-12-23 00:26:00 +00:00
g_CurStageStats . iTotalError [ m_PlayerNumber ] + = ms_error ;
2003-08-01 01:18:08 +00:00
if ( hns = = HNS_NG ) /* don't show a 0 for an OK */
m_ProTimingDisplay . SetJudgment ( ms_error , TNS_MISS ) ;
2002-12-18 22:25:24 +00:00
}
2003-12-16 06:10:50 +00:00
m_pNoteField - > SetHoldNoteLife ( hn , fLife ) ; // update the NoteField display
m_pNoteField - > SetHoldNoteScore ( hn , hns ) ; // update the NoteField display
2002-12-18 22:25:24 +00:00
2003-12-16 06:10:50 +00:00
SetHoldNoteLife ( hn , fLife ) ;
SetHoldNoteScore ( hn , hns ) ;
2001-12-19 01:50:57 +00:00
}
2003-06-10 19:53:33 +00:00
{
2004-02-25 05:49:54 +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 a very slow song (around 2 BPM) as a test case: without
* rounding, autoplay steps early. -glenn */
const int iRowNow = BeatToNoteRowNotRounded ( GAMESTATE - > m_fSongBeat ) ;
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 ) ;
}
}
{
// TRICKY:
float fPositionSeconds = GAMESTATE - > m_fMusicSeconds ;
fPositionSeconds - = PREFSMAN - > m_fPadStickSeconds ;
const float fSongBeat = GAMESTATE - > m_pCurSong ? GAMESTATE - > m_pCurSong - > GetBeatFromElapsedTime ( fPositionSeconds ) : 0 ;
const int iRowNow = BeatToNoteRowNotRounded ( fSongBeat ) ;
if ( iRowNow > = 0 )
{
for ( ; m_iMineRowLastCrossed < = iRowNow ; m_iMineRowLastCrossed + + ) // for each index we crossed since the last update
if ( GAMESTATE - > IsPlayerEnabled ( m_PlayerNumber ) )
CrossedMineRow ( m_iMineRowLastCrossed ) ;
}
2003-06-10 19:53:33 +00:00
}
2003-08-09 19:00:52 +00:00
2003-08-18 02:37:43 +00:00
// process transforms that are waiting to be applied
2003-12-23 02:17:28 +00:00
ApplyWaitingTransforms ( ) ;
/* Cache any newly-used note skins. Normally, the only new skins cached now are
* when we're adding course modifiers at the start of a song. If this is spending
* time loading skins in the middle of a song, something is wrong. */
m_pNoteField - > CacheAllUsedNoteSkins ( ) ;
ActorFrame : : Update ( fDeltaTime ) ;
}
void PlayerMinus : : ApplyWaitingTransforms ( )
{
2003-10-25 07:58:10 +00:00
for ( unsigned j = 0 ; j < GAMESTATE - > m_ModsToApply [ m_PlayerNumber ] . size ( ) ; j + + )
2003-08-18 02:37:43 +00:00
{
2003-10-25 22:00:58 +00:00
const Attack & mod = GAMESTATE - > m_ModsToApply [ m_PlayerNumber ] [ j ] ;
2003-10-25 07:58:10 +00:00
PlayerOptions po ;
/* Should this default to "" always? need it blank so we know if mod.sModifier
* changes the note skin. */
po . m_sNoteSkin = " " ;
po . FromString ( mod . sModifier ) ;
2003-08-18 02:37:43 +00:00
2003-10-25 07:58:10 +00:00
float fStartBeat , fEndBeat ;
mod . GetAttackBeats ( GAMESTATE - > m_pCurSong , m_PlayerNumber , fStartBeat , fEndBeat ) ;
2003-11-18 01:33:35 +00:00
fEndBeat = min ( fEndBeat , GetMaxBeat ( ) ) ;
2003-10-25 07:58:10 +00:00
2003-10-27 22:26:34 +00:00
LOG - > Trace ( " Applying transform '%s' from %f to %f to '%s' " , mod . sModifier . c_str ( ) , fStartBeat , fEndBeat ,
GAMESTATE - > m_pCurSong - > GetTranslitMainTitle ( ) . c_str ( ) ) ;
2003-10-25 07:58:10 +00:00
if ( po . m_sNoteSkin ! = " " )
GAMESTATE - > SetNoteSkinForBeatRange ( m_PlayerNumber , po . m_sNoteSkin , fStartBeat , fEndBeat ) ;
2003-08-18 04:20:51 +00:00
2004-06-28 07:26:00 +00:00
NoteDataUtil : : TransformNoteData ( * this , po , GAMESTATE - > GetCurrentStyle ( ) - > m_StepsType , fStartBeat , fEndBeat ) ;
2003-10-25 07:58:10 +00:00
m_pNoteField - > CopyRange ( this , BeatToNoteRow ( fStartBeat ) , BeatToNoteRow ( fEndBeat ) , BeatToNoteRow ( fStartBeat ) ) ;
2003-08-18 02:37:43 +00:00
}
2003-10-25 07:58:10 +00:00
GAMESTATE - > m_ModsToApply [ m_PlayerNumber ] . clear ( ) ;
2001-11-03 10:52:42 +00:00
}
2003-08-24 17:04:21 +00:00
void PlayerMinus : : 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.
2004-06-28 07:26:00 +00:00
if ( GAMESTATE - > GetCurrentStyle ( ) - > m_StyleType = = Style : : 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 ( ) ;
2003-09-17 01:58:27 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind = = 0 )
m_Combo . Draw ( ) ;
2002-07-31 19:40:40 +00:00
2003-11-26 08:25:45 +00:00
m_AttackDisplay . Draw ( ) ;
2003-11-25 06:45:06 +00:00
if ( TAP_JUDGMENTS_UNDER_FIELD )
2004-01-15 03:19:41 +00:00
DrawTapJudgments ( ) ;
2003-11-25 06:45:06 +00:00
if ( HOLD_JUDGMENTS_UNDER_FIELD )
2004-01-15 03:19:41 +00:00
DrawHoldJudgments ( ) ;
2003-11-25 06:45:06 +00:00
2003-04-01 19:31:27 +00:00
float fTilt = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . m_fPerspectiveTilt ;
2003-09-21 02:36:28 +00:00
float fSkew = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . m_fSkew ;
2003-08-17 00:15:54 +00:00
bool bReverse = GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . GetReversePercentForColumn ( 0 ) > 0.5 ;
2003-09-21 18:07:29 +00:00
2003-04-01 19:31:27 +00:00
2003-09-21 02:36:28 +00:00
DISPLAY - > CameraPushMatrix ( ) ;
2003-09-21 18:07:29 +00:00
DISPLAY - > PushMatrix ( ) ;
float fCenterY = ( GRAY_ARROWS_Y_STANDARD + GRAY_ARROWS_Y_REVERSE ) / 2 ;
2003-09-21 21:07:30 +00:00
// float fHeight = GRAY_ARROWS_Y_REVERSE-GRAY_ARROWS_Y_STANDARD;
2002-11-15 08:02:27 +00:00
2003-09-21 18:07:29 +00:00
DISPLAY - > LoadMenuPerspective ( 45 , SCALE ( fSkew , 0.f , 1.f , this - > GetX ( ) , CENTER_X ) , fCenterY ) ;
2002-11-15 08:02:27 +00:00
2003-09-21 02:36:28 +00:00
float fOriginalY = m_pNoteField - > GetY ( ) ;
2002-02-24 10:31:20 +00:00
2003-09-21 18:07:29 +00:00
float fTiltDegrees = SCALE ( fTilt , - 1.f , + 1.f , + 30 , - 30 ) * ( bReverse ? - 1 : 1 ) ;
float fZoom = SCALE ( GAMESTATE - > m_CurrentPlayerOptions [ m_PlayerNumber ] . m_fEffects [ PlayerOptions : : EFFECT_MINI ] , 0.f , 1.f , 1.f , 0.5f ) ;
if ( fTilt > 0 )
fZoom * = SCALE ( fTilt , 0.f , 1.f , 1.f , 0.9f ) ;
else
fZoom * = SCALE ( fTilt , 0.f , - 1.f , 1.f , 0.9f ) ;
float fYOffset ;
if ( fTilt > 0 )
fYOffset = SCALE ( fTilt , 0.f , 1.f , 0.f , - 45.f ) * ( bReverse ? - 1 : 1 ) ;
else
fYOffset = SCALE ( fTilt , 0.f , - 1.f , 0.f , - 20.f ) * ( bReverse ? - 1 : 1 ) ;
m_pNoteField - > SetY ( fOriginalY + fYOffset ) ;
m_pNoteField - > SetZoom ( fZoom ) ;
m_pNoteField - > SetRotationX ( fTiltDegrees ) ;
2003-08-24 17:04:21 +00:00
m_pNoteField - > Draw ( ) ;
2002-02-24 10:31:20 +00:00
2003-09-21 02:36:28 +00:00
m_pNoteField - > SetY ( fOriginalY ) ;
DISPLAY - > CameraPopMatrix ( ) ;
2003-09-21 18:07:29 +00:00
DISPLAY - > PopMatrix ( ) ;
2002-02-24 10:31:20 +00:00
2003-11-25 06:45:06 +00:00
if ( ! TAP_JUDGMENTS_UNDER_FIELD )
DrawTapJudgments ( ) ;
if ( ! TAP_JUDGMENTS_UNDER_FIELD )
DrawHoldJudgments ( ) ;
}
void PlayerMinus : : DrawTapJudgments ( )
{
2004-01-15 03:19:41 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind > 0 )
return ;
2003-11-25 06:45:06 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_bProTiming )
m_ProTimingDisplay . Draw ( ) ;
else
m_Judgment . Draw ( ) ;
}
void PlayerMinus : : DrawHoldJudgments ( )
{
2004-01-15 03:19:41 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind > 0 )
return ;
2003-02-01 05:16:38 +00:00
for ( int c = 0 ; c < GetNumTracks ( ) ; c + + )
2004-02-25 07:30:12 +00:00
{
g_NoteFieldMode [ m_PlayerNumber ] . BeginDrawTrack ( c ) ;
2003-02-17 12:19:42 +00:00
m_HoldJudgment [ c ] . Draw ( ) ;
2004-02-25 07:30:12 +00:00
g_NoteFieldMode [ m_PlayerNumber ] . EndDrawTrack ( c ) ;
}
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. */
2004-02-11 05:57:51 +00:00
int PlayerMinus : : GetClosestNoteDirectional ( int col , float fBeat , float fMaxBeatsDistance , int iDirection ) const
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 ;
}
2004-02-11 05:57:51 +00:00
int PlayerMinus : : GetClosestNote ( int col , float fBeat , float fMaxBeatsAhead , float fMaxBeatsBehind ) const
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
2003-08-24 17:04:21 +00:00
void PlayerMinus : : Step ( int col , RageTimer tm )
2003-02-09 02:07:30 +00:00
{
2003-12-23 00:26:00 +00:00
if ( GAMESTATE - > m_SongOptions . m_LifeType = = SongOptions : : LIFE_BATTERY & & g_CurStageStats . bFailed [ m_PlayerNumber ] ) // Oni dead
2003-02-09 02:07:30 +00:00
return ; // do nothing
2003-08-24 17:04:21 +00:00
//LOG->Trace( "PlayerMinus::HandlePlayerStep()" );
2003-02-09 02:07:30 +00:00
ASSERT ( col > = 0 & & col < = GetNumTracks ( ) ) ;
2004-02-25 05:49:54 +00:00
float fPositionSeconds = GAMESTATE - > m_fMusicSeconds ;
fPositionSeconds - = tm . Ago ( ) ;
const float fSongBeat = GAMESTATE - > m_pCurSong ? GAMESTATE - > m_pCurSong - > GetBeatFromElapsedTime ( fPositionSeconds ) : GAMESTATE - > m_fSongBeat ;
2003-11-15 23:19:13 +00:00
//
// Check for step on a TapNote
//
2004-02-25 05:49:54 +00:00
int iIndexOverlappingNote = GetClosestNote ( col , 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 );
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
/* We actually stepped on the note this long ago: */
2004-01-18 06:53:25 +00:00
const float fTimeSinceStep = tm . Ago ( ) ;
/* GAMESTATE->m_fMusicSeconds is the music time as of GAMESTATE->m_LastBeatUpdate. Figure
* out what the music time is as of now. */
2004-02-27 03:32:11 +00:00
const float fCurrentMusicSeconds = GAMESTATE - > m_fMusicSeconds + ( GAMESTATE - > m_LastBeatUpdate . Ago ( ) * GAMESTATE - > m_SongOptions . m_fMusicRate ) ;
2004-01-18 06:53:25 +00:00
2003-07-13 00:34:30 +00:00
/* ... which means it happened at this point in the music: */
2004-02-27 03:32:11 +00:00
const float fMusicSeconds = fCurrentMusicSeconds - fTimeSinceStep * 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
2004-01-18 06:53:25 +00:00
// LOG->Trace("step was %.3f ago, music is off by %f: %f vs %f, step was %f off",
// fTimeSinceStep, GAMESTATE->m_LastBeatUpdate.Ago()/GAMESTATE->m_SongOptions.m_fMusicRate,
// fStepSeconds, fMusicSeconds, fNoteOffset );
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-08-10 21:27:54 +00:00
TapNote tn = GetTapNote ( col , iIndexOverlappingNote ) ;
2003-04-07 21:24:14 +00:00
// calculate TapNoteScore
2004-01-02 08:43:14 +00:00
TapNoteScore score = TNS_NONE ;
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 : {
2003-11-10 01:03:47 +00:00
2003-11-15 23:19:13 +00:00
switch ( tn )
2003-09-08 15:30:26 +00:00
{
2003-11-15 23:19:13 +00:00
case TAP_MINE :
2003-11-10 01:03:47 +00:00
// stepped too close to mine?
2004-06-27 00:03:06 +00:00
if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Mine ) )
2003-11-10 01:03:47 +00:00
{
2003-11-12 05:21:51 +00:00
m_soundMine . Play ( ) ;
2004-01-02 08:43:14 +00:00
score = TNS_HIT_MINE ;
2003-11-11 07:36:28 +00:00
if ( m_pLifeMeter )
m_pLifeMeter - > ChangeLifeMine ( ) ;
if ( m_pCombinedLifeMeter )
m_pCombinedLifeMeter - > ChangeLifeMine ( m_PlayerNumber ) ;
2003-11-17 05:26:29 +00:00
m_pNoteField - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
2004-01-02 08:43:14 +00:00
m_pNoteField - > DidTapNote ( col , score , false ) ;
2003-11-10 01:03:47 +00:00
}
2003-11-15 23:19:13 +00:00
break ;
default : // not a mine
2003-11-17 05:26:29 +00:00
if ( IsTapAttack ( tn ) )
2003-11-10 01:03:47 +00:00
{
2003-11-17 15:44:51 +00:00
score = TNS_NONE ; // don't score this as anything
2004-06-27 00:03:06 +00:00
if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Attack ) )
2003-11-17 05:26:29 +00:00
{
2003-11-20 06:50:05 +00:00
m_soundAttackLaunch . Play ( ) ;
2003-11-17 07:14:41 +00:00
// put attack in effect
Attack attack = this - > GetAttackAt ( col , iIndexOverlappingNote ) ;
GAMESTATE - > LaunchAttack ( OPPOSITE_PLAYER [ m_PlayerNumber ] , attack ) ;
// remove all TapAttacks on this row
for ( int t = 0 ; t < this - > GetNumTracks ( ) ; t + + )
{
TapNote tn = this - > GetTapNote ( t , iIndexOverlappingNote ) ;
if ( IsTapAttack ( tn ) )
{
this - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
m_pNoteField - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
}
}
2003-11-17 05:26:29 +00:00
}
2003-11-10 01:03:47 +00:00
}
2003-11-17 05:26:29 +00:00
else // !IsTapAttack
2003-11-10 01:03:47 +00:00
{
2004-06-27 01:16:00 +00:00
if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Marvelous ) ) score = TNS_MARVELOUS ;
else if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Perfect ) ) score = TNS_PERFECT ;
else if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Great ) ) score = TNS_GREAT ;
else if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Good ) ) score = TNS_GOOD ;
else if ( fSecondsFromPerfect < = ADJUSTED_WINDOW ( Boo ) ) score = TNS_BOO ;
else score = TNS_NONE ;
2003-11-10 01:03:47 +00:00
}
2003-11-15 23:19:13 +00:00
break ;
2003-08-10 21:27:54 +00:00
}
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 :
2004-01-12 01:48:50 +00:00
case PC_AUTOPLAY :
switch ( GAMESTATE - > m_PlayerController [ m_PlayerNumber ] )
{
case PC_CPU :
2004-01-12 03:47:55 +00:00
score = PlayerAI : : GetTapNoteScore ( m_PlayerNumber ) ;
2004-01-12 01:48:50 +00:00
break ;
case PC_AUTOPLAY :
score = TNS_MARVELOUS ;
break ;
}
2003-09-08 15:30:26 +00:00
2004-01-11 22:29:18 +00:00
// TRICKY: We're asking the AI to judge mines. consider TNS_GOOD and below
// as "mine was hit" and everything else as "mine was avoided"
if ( tn = = TAP_MINE )
{
2004-01-11 23:31:37 +00:00
// The CPU hits a lot of mines. Only consider hitting the
// first mine for a row. We know we're the first mine if
// there are are no mines to the left of us.
for ( int t = 0 ; t < col ; t + + )
{
if ( TAP_MINE = = GetTapNote ( t , iIndexOverlappingNote ) ) // there's a mine to the left of us
return ; // avoid
}
// The CPU hits a lot of mines. Make it less likely to hit
// mines that don't have a tap note on the same row.
bool bTapsOnRow = IsThereATapOrHoldHeadAtRow ( iIndexOverlappingNote ) ;
TapNoteScore get_to_avoid = bTapsOnRow ? TNS_GREAT : TNS_GOOD ;
if ( score > = get_to_avoid )
2004-01-11 22:29:18 +00:00
{
return ; // avoided
}
else
{
score = TNS_HIT_MINE ;
m_soundMine . Play ( ) ;
if ( m_pLifeMeter )
m_pLifeMeter - > ChangeLifeMine ( ) ;
if ( m_pCombinedLifeMeter )
m_pCombinedLifeMeter - > ChangeLifeMine ( m_PlayerNumber ) ;
m_pNoteField - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
m_pNoteField - > DidTapNote ( col , score , false ) ;
}
}
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
2004-05-24 04:26:54 +00:00
* AI generated a miss, and let UpdateMissedTapNotesOlderThan() detect and handle the
2003-04-10 05:46:31 +00:00
* misses. */
if ( score = = TNS_MISS )
return ;
2003-08-10 21:27:54 +00:00
2004-01-11 22:29:18 +00:00
2003-11-17 05:26:29 +00:00
if ( IsTapAttack ( tn ) & & score > TNS_GOOD )
{
2003-11-20 06:50:05 +00:00
m_soundAttackLaunch . Play ( ) ;
2003-11-17 05:26:29 +00:00
score = TNS_NONE ; // don't score this as anything
2003-11-17 07:14:41 +00:00
// put attack in effect
Attack attack = this - > GetAttackAt ( col , iIndexOverlappingNote ) ;
GAMESTATE - > LaunchAttack ( OPPOSITE_PLAYER [ m_PlayerNumber ] , attack ) ;
// remove all TapAttacks on this row
for ( int t = 0 ; t < this - > GetNumTracks ( ) ; t + + )
{
TapNote tn = this - > GetTapNote ( t , iIndexOverlappingNote ) ;
if ( IsTapAttack ( tn ) )
{
this - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
m_pNoteField - > SetTapNote ( col , iIndexOverlappingNote , TAP_EMPTY ) ; // remove from NoteField
}
}
2003-08-10 21:27:54 +00:00
}
2003-11-17 05:26:29 +00:00
2003-04-21 02:41:10 +00:00
break ;
2003-11-17 07:14:41 +00:00
2003-04-21 02:41:10 +00:00
default :
ASSERT ( 0 ) ;
2003-04-22 04:54:04 +00:00
score = TNS_NONE ;
break ;
2003-04-07 21:24:14 +00:00
}
2003-11-10 23:25:44 +00:00
2003-08-10 19:07:54 +00:00
2004-06-27 01:16:00 +00:00
// Do game-specific score mapping.
const GameDef * pGameDef = GAMESTATE - > GetCurrentGameDef ( ) ;
if ( score = = TNS_MARVELOUS ) score = pGameDef - > m_mapMarvelousTo ;
if ( score = = TNS_PERFECT ) score = pGameDef - > m_mapPerfectTo ;
if ( score = = TNS_GREAT ) score = pGameDef - > m_mapGreatTo ;
if ( score = = TNS_GOOD ) score = pGameDef - > m_mapGoodTo ;
if ( score = = TNS_BOO ) score = pGameDef - > m_mapBooTo ;
2003-08-10 19:07:54 +00:00
if ( score ! = TNS_NONE & & score ! = TNS_MISS )
2003-08-01 00:12:10 +00:00
{
int ms_error = ( int ) roundf ( fSecondsFromPerfect * 1000 ) ;
ms_error = min ( ms_error , MAX_PRO_TIMING_ERROR ) ;
2003-12-23 00:26:00 +00:00
g_CurStageStats . iTotalError [ m_PlayerNumber ] + = ms_error ;
2003-09-14 14:05:15 +00:00
if ( ! GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind )
m_ProTimingDisplay . SetJudgment ( ms_error , score ) ;
2003-08-01 00:12:10 +00:00
}
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 ;
2004-03-28 09:49:05 +00:00
LOG - > Trace ( " XXX: %i col %i, at %f, music at %f, step was at %f, off by %f " ,
score , col , fStepSeconds , fCurrentMusicSeconds , fMusicSeconds , fNoteOffset ) ;
2003-09-03 08:01:48 +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
2004-04-15 05:41:57 +00:00
if ( GAMESTATE - > m_PlayerController [ m_PlayerNumber ] = = PC_HUMAN & &
score > = TNS_GREAT )
HandleAutosync ( fNoteOffset ) ;
2004-01-02 08:43:14 +00:00
if ( m_pPrimaryScoreKeeper )
m_pPrimaryScoreKeeper - > HandleTapScore ( score ) ;
if ( m_pSecondaryScoreKeeper )
m_pSecondaryScoreKeeper - > HandleTapScore ( score ) ;
2003-11-11 07:36:28 +00:00
2004-01-24 19:11:52 +00:00
switch ( tn )
2003-11-11 07:36:28 +00:00
{
2004-01-24 19:11:52 +00:00
case TAP_TAP :
case TAP_HOLD_HEAD :
case TAP_ADDITION :
// don't the row if this note is a mine or tap attack
2003-11-11 07:36:28 +00:00
if ( IsRowCompletelyJudged ( iIndexOverlappingNote ) )
OnRowCompletelyJudged ( iIndexOverlappingNote ) ;
}
2003-11-10 23:25:44 +00:00
if ( score = = TNS_MISS | | score = = TNS_BOO )
{
m_iDCState = AS2D_MISS ;
}
if ( score = = TNS_GOOD | | score = = TNS_GREAT )
{
m_iDCState = AS2D_GOOD ;
}
if ( score = = TNS_PERFECT | | score = = TNS_MARVELOUS )
{
m_iDCState = AS2D_GREAT ;
2003-11-17 05:26:29 +00:00
if ( m_pLifeMeter & & m_pLifeMeter - > GetLife ( ) = = 1.0f ) // full life
2003-11-10 23:25:44 +00:00
{
m_iDCState = AS2D_FEVER ; // super celebrate time :)
}
}
2001-11-04 19:34:28 +00:00
}
2003-04-13 00:44:50 +00:00
2003-11-10 23:25:44 +00:00
2004-01-12 01:54:30 +00:00
m_pNoteField - > Step ( col ) ;
2001-11-04 19:34:28 +00:00
}
2001-11-03 10:52:42 +00:00
2004-04-15 05:41:57 +00:00
void PlayerMinus : : 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_Timing . 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-08-24 17:04:21 +00:00
void PlayerMinus : : OnRowCompletelyJudged ( int iIndexThatWasSteppedOn )
2001-11-04 19:34:28 +00:00
{
2003-09-03 08:01:48 +00:00
// LOG->Trace( "PlayerMinus::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. */
2003-08-10 19:07:54 +00:00
/* Instead, use the last tap score (ala DDR). Using the minimum results in
* slightly more harsh scoring than DDR */
2003-09-19 20:07:58 +00:00
/* I'm not sure this is right, either. Can you really jump a boo and a perfect
* and get scored for a perfect? (That's so loose, you can gallop jumps.) -glenn */
2004-01-02 08:43:14 +00:00
/* Instead of grading individual columns, DDR sets a "was pressed recently"
* countdown every time you step on a column. When you step on the first note of
* the jump, it sets the first "was pressed recently" timer. Then, when you do
* the 2nd step of the jump, it sets another column's timer then AND's the jump
* columns with the "was pressed recently" columns to see whether or not you hit
* all the columns of the jump. -Chris */
2003-03-26 19:34:59 +00:00
// TapNoteScore score = MinTapNoteScore(iIndexThatWasSteppedOn);
TapNoteScore score = LastTapNoteScore ( iIndexThatWasSteppedOn ) ;
2003-03-26 22:22:10 +00:00
ASSERT ( score ! = TNS_NONE ) ;
2004-01-02 08:43:14 +00:00
ASSERT ( score ! = TNS_HIT_MINE ) ;
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. */
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-08-10 19:07:54 +00:00
TapNote tn = GetTapNote ( c , iIndexThatWasSteppedOn ) ;
if ( tn = = TAP_EMPTY ) continue ; /* no note in this col */
if ( tn = = TAP_MINE ) continue ; /* don't flash on mines b/c they're supposed to be missed */
// If the score is great or better, remove the note from the screen to
2003-09-14 14:05:15 +00:00
// indicate success. (Or always if blind is on.)
2003-09-17 01:50:54 +00:00
if ( score > = TNS_GREAT | | GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind )
2003-08-24 17:04:21 +00:00
m_pNoteField - > SetTapNote ( c , iIndexThatWasSteppedOn , TAP_EMPTY ) ;
2003-03-26 17:38:19 +00:00
// show the ghost arrow for this column
2003-09-14 14:05:15 +00:00
if ( GAMESTATE - > m_PlayerOptions [ m_PlayerNumber ] . m_fBlind )
2003-11-27 04:25:52 +00:00
m_pNoteField - > DidTapNote ( c , TNS_MARVELOUS , false ) ;
2003-09-14 14:05:15 +00:00
else
2003-03-26 17:38:19 +00:00
{
2003-09-14 14:05:15 +00:00
switch ( score )
2003-03-16 17:45:32 +00:00
{
2003-09-14 14:05:15 +00:00
case TNS_GREAT :
case TNS_PERFECT :
case TNS_MARVELOUS :
{
2003-12-23 00:26:00 +00:00
bool bBright = g_CurStageStats . iCurCombo [ m_PlayerNumber ] > ( int ) BRIGHT_GHOST_COMBO_THRESHOLD ;
2003-11-27 04:25:52 +00:00
m_pNoteField - > DidTapNote ( c , score , bBright ) ;
2003-09-14 14:05:15 +00:00
}
break ;
2003-03-16 17:45:32 +00:00
}
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
2002-05-19 01:59:48 +00:00
2003-09-17 01:58:27 +00:00
m_Judgment . SetJudgment ( score ) ;
2001-11-03 10:52:42 +00:00
}
2001-11-04 19:34:28 +00:00
2004-05-24 04:26:54 +00:00
void PlayerMinus : : UpdateTapNotesMissedOlderThan ( float fMissIfOlderThanSeconds )
2001-11-04 19:34:28 +00:00
{
2004-05-24 04:26:54 +00:00
//LOG->Trace( "Steps::UpdateTapNotesMissedOlderThan(%f)", fMissIfOlderThanThisBeat );
2003-09-19 20:07:58 +00:00
int iMissIfOlderThanThisIndex ;
2003-06-13 00:24:33 +00:00
{
2003-09-19 20:07:58 +00:00
const float fEarliestTime = GAMESTATE - > m_fMusicSeconds - fMissIfOlderThanSeconds ;
bool bFreeze ;
float fMissIfOlderThanThisBeat ;
float fThrowAway ;
GAMESTATE - > m_pCurSong - > GetBeatAndBPSFromElapsedTime ( fEarliestTime , fMissIfOlderThanThisBeat , fThrowAway , bFreeze ) ;
iMissIfOlderThanThisIndex = BeatToNoteRow ( fMissIfOlderThanThisBeat ) ;
if ( bFreeze )
{
2004-01-24 19:11:52 +00:00
/* If there is a freeze on iMissIfOlderThanThisIndex, include this index too.
* Otherwise we won't show misses for tap notes on freezes until the
2003-09-19 20:07:58 +00:00
* freeze finishes. */
iMissIfOlderThanThisIndex + + ;
}
2003-06-13 00:24:33 +00:00
}
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.
2003-09-19 20:07:58 +00:00
const 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 */
2003-08-10 19:07:54 +00:00
if ( GetTapNoteScore ( t , r ) ! = TNS_NONE ) continue ; /* note here is already hit */
2003-11-11 07:36:28 +00:00
if ( GetTapNote ( t , r ) ! = TAP_MINE )
2003-08-10 19:07:54 +00:00
{
// A normal note. Penalize for not stepping on it.
MissedNoteOnThisRow = true ;
SetTapNoteScore ( t , r , TNS_MISS ) ;
2003-12-23 00:26:00 +00:00
g_CurStageStats . iTotalError [ m_PlayerNumber ] + = MAX_PRO_TIMING_ERROR ;
2003-08-10 19:07:54 +00:00
m_ProTimingDisplay . SetJudgment ( MAX_PRO_TIMING_ERROR , TNS_MISS ) ;
}
2002-10-02 05:42:59 +00:00
}
2003-03-26 21:35:15 +00:00
2003-08-10 19:07:54 +00:00
if ( MissedNoteOnThisRow )
{
iNumMissesFound + + ;
HandleTapRowScore ( r ) ;
}
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
}
2003-08-24 17:04:21 +00:00
void PlayerMinus : : 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 )
2003-08-09 18:42:21 +00:00
if ( GetTapNoteScore ( t , iNoteRow ) = = TNS_NONE )
Step ( t , now ) ;
2003-07-13 00:34:30 +00:00
}
2004-02-25 05:49:54 +00:00
}
2003-08-31 22:39:05 +00:00
2004-02-25 05:49:54 +00:00
void PlayerMinus : : CrossedMineRow ( int iNoteRow )
{
2003-08-31 22:39:05 +00:00
// Hold the panel while crossing a mine will cause the mine to explode
2004-02-25 05:49:54 +00:00
RageTimer now ;
2003-08-31 22:39:05 +00:00
for ( int t = 0 ; t < GetNumTracks ( ) ; t + + )
{
2004-01-02 08:43:14 +00:00
if ( GetTapNote ( t , iNoteRow ) = = TAP_MINE )
2003-08-31 22:39:05 +00:00
{
const StyleInput StyleI ( m_PlayerNumber , t ) ;
2004-06-28 07:26:00 +00:00
const GameInput GameI = GAMESTATE - > GetCurrentStyle ( ) - > StyleInputToGameInput ( StyleI ) ;
2004-02-26 07:27:54 +00:00
if ( PREFSMAN - > m_fPadStickSeconds > 0 )
{
float fSecsHeld = INPUTMAPPER - > GetSecsHeld ( GameI ) ;
if ( fSecsHeld > = PREFSMAN - > m_fPadStickSeconds )
Step ( t , now + ( - PREFSMAN - > m_fPadStickSeconds ) ) ;
}
else
{
bool bIsDown = INPUTMAPPER - > IsButtonDown ( GameI ) ;
if ( bIsDown )
Step ( t , now ) ;
}
2003-08-31 22:39:05 +00:00
}
}
2002-06-24 22:04:31 +00:00
}
2003-08-24 17:04:21 +00:00
void PlayerMinus : : RandomiseNotes ( int iNoteRow )
2003-05-04 13:58:45 +00:00
{
2003-10-04 04:35:22 +00:00
const 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
2003-10-25 03:17:39 +00:00
bool UpdateNoteField = false ;
2003-05-04 13:58:45 +00:00
int iNumOfTracks = GetNumTracks ( ) ;
2003-10-24 22:57:47 +00:00
for ( int t = 0 ; t + 1 < iNumOfTracks ; t + + )
2003-05-04 13:58:45 +00:00
{
2003-10-24 22:57:47 +00:00
int iRandomTrackToSwapWith = RandomInt ( 0 , iNumOfTracks - 1 ) ;
const TapNote t1 = GetTapNote ( t , NewNoteRow ) ;
const TapNote t2 = GetTapNote ( iRandomTrackToSwapWith , NewNoteRow ) ;
if ( ( t1 = = TAP_TAP | | t1 = = TAP_EMPTY ) & & ( t2 = = TAP_TAP | | t2 = = TAP_EMPTY ) )
2003-05-04 13:58:45 +00:00
{
2003-10-24 22:57:47 +00:00
SetTapNote ( t , NewNoteRow , t2 ) ;
SetTapNote ( iRandomTrackToSwapWith , NewNoteRow , t1 ) ;
2003-10-25 03:17:39 +00:00
UpdateNoteField = true ;
2003-05-04 13:58:45 +00:00
}
}
2003-10-25 03:17:39 +00:00
if ( UpdateNoteField )
m_pNoteField - > CopyRange ( this , NewNoteRow , NewNoteRow , NewNoteRow ) ;
2003-05-04 13:58:45 +00:00
}
2002-06-24 22:04:31 +00:00
2003-08-24 17:04:21 +00:00
void PlayerMinus : : HandleTapRowScore ( unsigned row )
2002-06-24 22:04:31 +00:00
{
2003-03-26 21:35:15 +00:00
TapNoteScore scoreOfLastTap = LastTapNoteScore ( row ) ;
2004-01-02 08:43:14 +00:00
int iNumTapsInRow = this - > GetNumTracksWithTapOrHoldHead ( row ) ;
2003-03-26 21:35:15 +00:00
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-09-07 01:01:20 +00:00
/* Update miss combo, and handle "combo stopped" messages. */
2003-12-23 00:26:00 +00:00
int & iCurCombo = g_CurStageStats . iCurCombo [ m_PlayerNumber ] ;
2003-09-06 02:20:09 +00:00
switch ( scoreOfLastTap )
{
2003-09-06 02:26:06 +00:00
case TNS_MARVELOUS :
case TNS_PERFECT :
case TNS_GREAT :
2003-12-23 00:26:00 +00:00
g_CurStageStats . iCurMissCombo [ m_PlayerNumber ] = 0 ;
2003-09-06 03:25:45 +00:00
SCREENMAN - > PostMessageToTopScreen ( SM_MissComboAborted , 0 ) ;
2003-09-06 02:26:06 +00:00
break ;
2003-09-06 03:25:45 +00:00
2003-09-06 02:26:06 +00:00
case TNS_MISS :
2003-12-23 00:26:00 +00:00
+ + g_CurStageStats . iCurMissCombo [ m_PlayerNumber ] ;
2003-11-10 23:25:44 +00:00
m_iDCState = AS2D_MISS ; // update dancing 2d characters that may have missed a note
2003-09-06 02:20:09 +00:00
case TNS_GOOD :
case TNS_BOO :
if ( iCurCombo > 50 )
SCREENMAN - > PostMessageToTopScreen ( SM_ComboStopped , 0 ) ;
iCurCombo = 0 ;
break ;
2004-01-02 08:43:14 +00:00
default :
ASSERT ( 0 ) ;
2003-09-06 02:20:09 +00:00
}
2003-09-07 01:01:20 +00:00
/* The score keeper updates the hit combo. Remember the old combo for handling announcers. */
2003-12-23 00:26:00 +00:00
const int iOldCombo = g_CurStageStats . iCurCombo [ m_PlayerNumber ] ;
2003-09-07 01:01:20 +00:00
if ( m_pPrimaryScoreKeeper )
2004-01-02 08:43:14 +00:00
m_pPrimaryScoreKeeper - > HandleTapRowScore ( scoreOfLastTap , iNumTapsInRow ) ;
2003-09-07 01:01:20 +00:00
if ( m_pSecondaryScoreKeeper )
2004-01-02 08:43:14 +00:00
m_pSecondaryScoreKeeper - > HandleTapRowScore ( scoreOfLastTap , iNumTapsInRow ) ;
2003-09-07 01:01:20 +00:00
2003-12-23 00:26:00 +00:00
m_Combo . SetCombo ( g_CurStageStats . iCurCombo [ m_PlayerNumber ] ) ;
2003-09-19 20:15:15 +00:00
2003-09-07 01:01:20 +00:00
# define CROSSED( x ) (iOldCombo<x && iCurCombo>=x)
if ( CROSSED ( 100 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_100Combo , 0 ) ;
else if ( CROSSED ( 200 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_200Combo , 0 ) ;
else if ( CROSSED ( 300 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_300Combo , 0 ) ;
else if ( CROSSED ( 400 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_400Combo , 0 ) ;
else if ( CROSSED ( 500 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_500Combo , 0 ) ;
else if ( CROSSED ( 600 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_600Combo , 0 ) ;
else if ( CROSSED ( 700 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_700Combo , 0 ) ;
else if ( CROSSED ( 800 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_800Combo , 0 ) ;
else if ( CROSSED ( 900 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_900Combo , 0 ) ;
else if ( CROSSED ( 1000 ) )
SCREENMAN - > PostMessageToTopScreen ( SM_1000Combo , 0 ) ;
else if ( ( iOldCombo / 100 ) < ( iCurCombo / 100 ) & & iCurCombo > 1000 )
SCREENMAN - > PostMessageToTopScreen ( SM_ComboContinuing , 0 ) ;
# undef CROSSED
2003-09-06 02:20:09 +00:00
// new max combo
2003-12-23 00:26:00 +00:00
g_CurStageStats . iMaxCombo [ m_PlayerNumber ] = max ( g_CurStageStats . iMaxCombo [ m_PlayerNumber ] , iCurCombo ) ;
2003-09-06 02:20:09 +00:00
2003-10-24 05:19:08 +00:00
/* Use the real current beat, not the beat we've been passed. That's because we
* want to record the current life/combo to the current time; eg. if it's a MISS,
* the beat we're registering is in the past, but the life is changing now. */
2003-12-23 04:44:34 +00:00
g_CurStageStats . UpdateComboList ( m_PlayerNumber , GAMESTATE - > m_fSongBeat , false ) ;
2003-10-24 05:19:08 +00:00
2003-10-26 08:37:25 +00:00
float life = - 1 ;
2003-10-24 05:19:08 +00:00
if ( m_pLifeMeter )
life = m_pLifeMeter - > GetLife ( ) ;
2003-12-17 03:59:08 +00:00
else if ( m_pCombinedLifeMeter )
{
life = GAMESTATE - > m_fTugLifePercentP1 ;
if ( m_PlayerNumber = = PLAYER_2 )
life = 1.0f - life ;
}
2003-10-26 08:37:25 +00:00
if ( life ! = - 1 )
2003-12-23 00:26:00 +00:00
g_CurStageStats . SetLifeRecord ( m_PlayerNumber , life , GAMESTATE - > m_fSongBeat ) ;
2003-10-23 06:17:40 +00:00
2003-11-26 06:40:03 +00:00
if ( m_pScoreDisplay )
2003-12-23 00:26:00 +00:00
m_pScoreDisplay - > SetScore ( g_CurStageStats . iScore [ m_PlayerNumber ] ) ;
2003-11-26 06:40:03 +00:00
if ( m_pSecondaryScoreDisplay )
2003-12-23 00:26:00 +00:00
m_pSecondaryScoreDisplay - > SetScore ( g_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-08-24 17:04:21 +00:00
void PlayerMinus : : 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
2003-11-26 06:40:03 +00:00
if ( m_pScoreDisplay )
2003-12-23 00:26:00 +00:00
m_pScoreDisplay - > SetScore ( g_CurStageStats . iScore [ m_PlayerNumber ] ) ;
2003-11-26 06:40:03 +00:00
if ( m_pSecondaryScoreDisplay )
2003-12-23 00:26:00 +00:00
m_pSecondaryScoreDisplay - > SetScore ( g_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-08-24 17:04:21 +00:00
float PlayerMinus : : GetMaxStepDistanceSeconds ( )
2002-07-28 20:28:37 +00:00
{
2004-06-27 00:03:06 +00:00
return GAMESTATE - > m_SongOptions . m_fMusicRate * ADJUSTED_WINDOW ( Boo ) ;
2002-09-05 03:45:07 +00:00
}
2003-08-24 17:04:21 +00:00
void PlayerMinus : : FadeToFail ( )
2002-09-05 03:45:07 +00:00
{
2003-08-24 17:04:21 +00:00
m_pNoteField - > FadeToFail ( ) ;
}
2004-02-11 05:57:51 +00:00
/* XXX: Why's m_NoteField in a separate class, again? Is that still needed? */
2003-11-26 06:40:03 +00:00
void Player : : Load ( PlayerNumber player_no , const NoteData * pNoteData , LifeMeter * pLM , CombinedLifeMeter * pCombinedLM , ScoreDisplay * pScoreDisplay , ScoreDisplay * pSecondaryScoreDisplay , Inventory * pInventory , ScoreKeeper * pPrimaryScoreKeeper , ScoreKeeper * pSecondaryScoreKeeper )
2003-08-24 17:04:21 +00:00
{
2003-11-26 06:40:03 +00:00
PlayerMinus : : Load ( player_no , pNoteData , pLM , pCombinedLM , pScoreDisplay , pSecondaryScoreDisplay , pInventory , pPrimaryScoreKeeper , pSecondaryScoreKeeper , & m_NoteField ) ;
2002-09-05 03:45:07 +00:00
}
2004-06-08 00:08:04 +00:00
/*
* (c) 2001-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/