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"
|
2004-07-25 17:07:32 +00:00
|
|
|
#include "Game.h"
|
2004-08-10 06:39:25 +00:00
|
|
|
#include "NetworkSyncManager.h" //used for sending timing offset
|
2004-08-19 01:18:11 +00:00
|
|
|
#include "DancingCharacters.h"
|
2004-09-21 06:07:12 +00:00
|
|
|
#include "ScreenDimensions.h"
|
2004-10-02 05:08:59 +00:00
|
|
|
#include "RageSoundManager.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-08-07 02:13:52 +00:00
|
|
|
#define JUDGMENT_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? CString("JudgmentXOffsetBothSides") : ssprintf("JudgmentXOffsetOneSideP%d",p+1))
|
2004-03-24 07:05:50 +00:00
|
|
|
#define JUDGMENT_Y THEME->GetMetricF("Player","JudgmentY")
|
|
|
|
|
#define JUDGMENT_Y_REVERSE THEME->GetMetricF("Player","JudgmentYReverse")
|
2004-08-07 02:13:52 +00:00
|
|
|
#define COMBO_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? CString("ComboXOffsetBothSides") : ssprintf("ComboXOffsetOneSideP%d",p+1))
|
2004-03-24 07:05:50 +00:00
|
|
|
#define COMBO_Y THEME->GetMetricF("Player","ComboY")
|
|
|
|
|
#define COMBO_Y_REVERSE THEME->GetMetricF("Player","ComboYReverse")
|
2004-08-07 02:13:52 +00:00
|
|
|
#define ATTACK_DISPLAY_X( p, both_sides ) THEME->GetMetricF("Player",both_sides ? CString("AttackDisplayXOffsetBothSides") : ssprintf("AttackDisplayXOffsetOneSideP%d",p+1))
|
2004-03-24 07:05:50 +00:00
|
|
|
#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();
|
2004-07-02 19:06:11 +00:00
|
|
|
|
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
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
void PlayerMinus::Load(
|
|
|
|
|
PlayerNumber pn,
|
|
|
|
|
const NoteData& noteData,
|
|
|
|
|
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;
|
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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
// init steps
|
|
|
|
|
m_NoteData.Init();
|
|
|
|
|
bool bOniDead = GAMESTATE->m_SongOptions.m_LifeType == SongOptions::LIFE_BATTERY && g_CurStageStats.bFailed[pn];
|
|
|
|
|
if( !bOniDead )
|
|
|
|
|
m_NoteData.CopyAll( noteData );
|
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 );
|
2004-07-31 21:26:11 +00:00
|
|
|
m_Combo.SetCombo( g_CurStageStats.iCurCombo[m_PlayerNumber], g_CurStageStats.iCurMissCombo[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-10-23 17:43:49 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, 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-10-23 17:43:49 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, 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-10-23 17:43:49 +00:00
|
|
|
NoteDataUtil::Turn( m_NoteData, STEPS_TYPE_DANCE_SINGLE, NoteDataUtil::left);
|
2003-11-20 06:50:05 +00:00
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
case 2:
|
2004-10-23 17:43:49 +00:00
|
|
|
NoteDataUtil::Turn( m_NoteData, 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;
|
2004-10-01 02:11:52 +00:00
|
|
|
|
2003-09-21 18:07:29 +00:00
|
|
|
m_pNoteField->SetY( fNoteFieldMidde );
|
2004-02-25 07:30:12 +00:00
|
|
|
m_fNoteFieldHeight = GRAY_ARROWS_Y_REVERSE-GRAY_ARROWS_Y_STANDARD;
|
2004-10-23 17:43:49 +00:00
|
|
|
m_pNoteField->Load( &m_NoteData, 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 );
|
2004-10-01 02:11:52 +00:00
|
|
|
m_AttackDisplay.SetX( ATTACK_DISPLAY_X(m_PlayerNumber,bPlayerUsingBothSides) - 40 );
|
2003-11-26 08:25:45 +00:00
|
|
|
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
|
|
|
|
2004-10-02 05:08:59 +00:00
|
|
|
for( int 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-07-27 05:12:13 +00:00
|
|
|
RageSoundParams p;
|
2004-02-28 04:14:13 +00:00
|
|
|
m_soundMine.Load( THEME->GetPathToS("Player mine"), true );
|
|
|
|
|
|
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:
|
2004-07-27 05:12:13 +00:00
|
|
|
m_soundAttackLaunch.Load( THEME->GetPathToS("Player battle attack launch"), true );
|
|
|
|
|
m_soundAttackEnding.Load( THEME->GetPathToS("Player battle attack ending"), true );
|
2004-04-08 03:40:28 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2004-07-27 05:12:13 +00:00
|
|
|
m_soundAttackLaunch.Load( THEME->GetPathToS("Player course attack launch"), true );
|
|
|
|
|
m_soundAttackEnding.Load( THEME->GetPathToS("Player course attack ending"), true );
|
2004-04-08 03:40:28 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2004-07-27 05:12:13 +00:00
|
|
|
|
|
|
|
|
if( GAMESTATE->GetNumPlayersEnabled() == 2 )
|
|
|
|
|
{
|
|
|
|
|
/* Two players are active. Play sounds on this player's side. */
|
|
|
|
|
p.m_Balance = (m_PlayerNumber == PLAYER_1)? -1.0f:1.0f;
|
|
|
|
|
}
|
|
|
|
|
m_soundMine.SetParams( p );
|
|
|
|
|
m_soundAttackLaunch.SetParams( p );
|
|
|
|
|
m_soundAttackEnding.SetParams( p );
|
2004-10-23 23:41:49 +00:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// Load keysounds
|
|
|
|
|
//
|
|
|
|
|
Song* pSong = GAMESTATE->m_pCurSong;
|
|
|
|
|
CString sSongDir = pSong->GetSongDir();
|
|
|
|
|
m_vKeysounds.clear();
|
|
|
|
|
m_vKeysounds.resize( pSong->m_vsKeysoundFile.size() );
|
|
|
|
|
for( unsigned i=0; i<m_vKeysounds.size(); i++ )
|
|
|
|
|
{
|
|
|
|
|
CString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[i];
|
|
|
|
|
RageSound& sound = m_vKeysounds[i];
|
|
|
|
|
sound.Load( sKeysoundFilePath );
|
|
|
|
|
sound.SetParams( p );
|
|
|
|
|
}
|
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
|
|
|
//
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int i=0; i < m_NoteData.GetNumHoldNotes(); i++ ) // for each HoldNote
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
const HoldNote &hn = m_NoteData.GetHoldNote(i);
|
|
|
|
|
HoldNoteScore hns = m_NoteData.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
|
2004-10-23 17:43:49 +00:00
|
|
|
const TapNoteScore tns = m_NoteData.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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
float fLife = m_NoteData.GetHoldNoteLife(hn);
|
2003-12-16 06:10:50 +00:00
|
|
|
|
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 );
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
hnr = m_NoteData.CreateHoldNoteResult( hn );
|
2004-05-07 04:57:29 +00:00
|
|
|
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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetHoldNoteLife(hn, fLife);
|
|
|
|
|
m_NoteData.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 );
|
2004-10-23 17:43:49 +00:00
|
|
|
fEndBeat = min( fEndBeat, m_NoteData.GetLastBeat() );
|
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-10-23 17:43:49 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, po, GAMESTATE->GetCurrentStyle()->m_StepsType, fStartBeat, fEndBeat );
|
|
|
|
|
m_pNoteField->CopyRange( m_NoteData, 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;
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int c=0; c<m_NoteData.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
|
|
|
}
|
|
|
|
|
|
2004-10-25 00:29:34 +00:00
|
|
|
int PlayerMinus::GetClosestNoteDirectional( int col, int iStartRow, int iMaxRowsAhead, bool bAllowGraded, bool bForward ) const
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2004-10-25 00:29:34 +00:00
|
|
|
/* Be sure to check iStartRow itself, too. */
|
|
|
|
|
int iRow = iStartRow;
|
|
|
|
|
while( abs(iStartRow-iRow) <= iMaxRowsAhead )
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2004-10-25 00:29:34 +00:00
|
|
|
/* Is iRow the row we want? */
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if( m_NoteData.GetTapNote(col, iRow).type == TapNote::empty )
|
|
|
|
|
break;
|
|
|
|
|
if( !bAllowGraded && m_NoteData.GetTapNoteScore(col, iRow) != TNS_NONE )
|
|
|
|
|
break;
|
|
|
|
|
return iRow;
|
|
|
|
|
}
|
|
|
|
|
while(0);
|
2002-05-29 09:47:24 +00:00
|
|
|
|
2004-10-25 00:29:34 +00:00
|
|
|
if( bForward && !m_NoteData.GetNextTapNoteRowForTrack( col, iRow ) )
|
|
|
|
|
return -1;
|
|
|
|
|
if( !bForward && !m_NoteData.GetPrevTapNoteRowForTrack( col, iRow ) )
|
|
|
|
|
return -1;
|
2002-05-29 09:47:24 +00:00
|
|
|
}
|
2003-02-09 03:33:36 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2004-10-25 00:29:34 +00:00
|
|
|
/* Find the closest note to fBeat. */
|
|
|
|
|
int PlayerMinus::GetClosestNote( int col, float fBeat, float fMaxBeatsAhead, float fMaxBeatsBehind, bool bAllowGraded ) const
|
2003-02-09 03:33:36 +00:00
|
|
|
{
|
2004-10-25 00:29:34 +00:00
|
|
|
// look for the closest matching step
|
|
|
|
|
const int iRow = BeatToNoteRow( fBeat );
|
|
|
|
|
|
|
|
|
|
// Start at iIndexStartLookingAt and search outward.
|
|
|
|
|
int iNextIndex = GetClosestNoteDirectional( col, iRow, BeatToNoteRow(fMaxBeatsAhead), bAllowGraded, true );
|
|
|
|
|
int iPrevIndex = GetClosestNoteDirectional( col, iRow, BeatToNoteRow(fMaxBeatsBehind), bAllowGraded, false );
|
2003-02-09 03:33:36 +00:00
|
|
|
|
2004-10-25 00:29:34 +00:00
|
|
|
if( iNextIndex == -1 && iPrevIndex == -1 )
|
|
|
|
|
return -1;
|
|
|
|
|
if( iNextIndex == -1 )
|
|
|
|
|
return iPrevIndex;
|
|
|
|
|
if( iPrevIndex == -1 )
|
|
|
|
|
return iNextIndex;
|
2003-02-09 03:33:36 +00:00
|
|
|
|
|
|
|
|
/* Figure out which row is closer. */
|
2004-10-25 00:29:34 +00:00
|
|
|
const float DistToFwd = fabsf( fBeat-NoteRowToBeat(iNextIndex) );
|
|
|
|
|
const float DistToBack = fabsf( fBeat-NoteRowToBeat(iPrevIndex) );
|
2003-02-09 03:33:36 +00:00
|
|
|
|
2004-10-25 00:29:34 +00:00
|
|
|
if( DistToFwd > DistToBack )
|
|
|
|
|
return iPrevIndex;
|
|
|
|
|
return iNextIndex;
|
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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
ASSERT( col >= 0 && col <= m_NoteData.GetNumTracks() );
|
2003-02-09 02:07:30 +00:00
|
|
|
|
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,
|
2004-10-25 00:29:34 +00:00
|
|
|
StepSearchDistanceBackwards * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.m_fMusicRate,
|
|
|
|
|
false );
|
2003-02-09 02:07:30 +00:00
|
|
|
|
|
|
|
|
//LOG->Trace( "iIndexStartLookingAt = %d, iNumElementsToExamine = %d", iIndexStartLookingAt, iNumElementsToExamine );
|
|
|
|
|
|
2004-07-02 01:24:11 +00:00
|
|
|
// calculate TapNoteScore
|
|
|
|
|
TapNoteScore score = TNS_NONE;
|
|
|
|
|
|
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
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
TapNote tn = m_NoteData.GetTapNote( col, iIndexOverlappingNote );
|
2003-08-10 21:27:54 +00:00
|
|
|
|
2003-04-21 02:41:10 +00:00
|
|
|
switch( GAMESTATE->m_PlayerController[m_PlayerNumber] )
|
2003-04-07 21:24:14 +00:00
|
|
|
{
|
2004-09-12 05:56:24 +00:00
|
|
|
case PC_HUMAN:
|
2003-11-10 01:03:47 +00:00
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
switch( tn.type )
|
2003-09-08 15:30:26 +00:00
|
|
|
{
|
2004-09-12 05:56:24 +00:00
|
|
|
case TapNote::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;
|
|
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
case TapNote::attack:
|
|
|
|
|
score = TNS_NONE; // don't score this as anything
|
|
|
|
|
if( fSecondsFromPerfect <= ADJUSTED_WINDOW(Attack) )
|
2003-11-10 01:03:47 +00:00
|
|
|
{
|
2004-09-12 05:56:24 +00:00
|
|
|
m_soundAttackLaunch.Play();
|
2004-10-23 23:41:49 +00:00
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
// put attack in effect
|
2004-10-23 23:41:49 +00:00
|
|
|
Attack attack(
|
|
|
|
|
ATTACK_LEVEL_1,
|
|
|
|
|
-1, // now
|
|
|
|
|
tn.fAttackDurationSeconds,
|
|
|
|
|
tn.sAttackModifiers,
|
|
|
|
|
true,
|
|
|
|
|
false
|
|
|
|
|
);
|
2004-09-12 05:56:24 +00:00
|
|
|
GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
|
2003-11-17 07:14:41 +00:00
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
// remove all TapAttacks on this row
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
2004-09-12 05:56:24 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
TapNote tn = m_NoteData.GetTapNote(t, iIndexOverlappingNote);
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::attack )
|
2003-11-17 07:14:41 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetTapNote(col, iIndexOverlappingNote, TAP_EMPTY); // remove from NoteField
|
2004-09-12 05:56:24 +00:00
|
|
|
m_pNoteField->SetTapNote(col, iIndexOverlappingNote, TAP_EMPTY); // remove from NoteField
|
2003-11-17 07:14:41 +00:00
|
|
|
}
|
2003-11-17 05:26:29 +00:00
|
|
|
}
|
2003-11-10 01:03:47 +00:00
|
|
|
}
|
2004-09-12 05:56:24 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
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-15 23:19:13 +00:00
|
|
|
break;
|
2003-08-10 21:27:54 +00:00
|
|
|
}
|
2003-04-21 02:41:10 +00:00
|
|
|
break;
|
2004-09-12 05:56:24 +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"
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::mine )
|
2004-01-11 22:29:18 +00:00
|
|
|
{
|
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++ )
|
|
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.GetTapNote(t,iIndexOverlappingNote).type == TapNote::mine ) // there's a mine to the left of us
|
2004-01-11 23:31:37 +00:00
|
|
|
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.
|
2004-10-23 17:43:49 +00:00
|
|
|
bool bTapsOnRow = m_NoteData.IsThereATapOrHoldHeadAtRow( iIndexOverlappingNote );
|
2004-01-11 23:31:37 +00:00
|
|
|
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
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::attack && score > TNS_GOOD )
|
2003-11-17 05:26:29 +00:00
|
|
|
{
|
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
|
2004-10-23 23:41:49 +00:00
|
|
|
Attack attack(
|
|
|
|
|
ATTACK_LEVEL_1,
|
|
|
|
|
-1, // now
|
|
|
|
|
tn.fAttackDurationSeconds,
|
|
|
|
|
tn.sAttackModifiers,
|
|
|
|
|
true,
|
|
|
|
|
false
|
|
|
|
|
);
|
2003-11-17 07:14:41 +00:00
|
|
|
GAMESTATE->LaunchAttack( OPPOSITE_PLAYER[m_PlayerNumber], attack );
|
|
|
|
|
|
|
|
|
|
// remove all TapAttacks on this row
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
2003-11-17 07:14:41 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
TapNote tn = m_NoteData.GetTapNote(t, iIndexOverlappingNote);
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::attack )
|
2003-11-17 07:14:41 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetTapNote(col, iIndexOverlappingNote, TAP_EMPTY); // remove from NoteField
|
2003-11-17 07:14:41 +00:00
|
|
|
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.
|
2004-07-25 17:07:32 +00:00
|
|
|
const Game* pGame = GAMESTATE->GetCurrentGame();
|
|
|
|
|
if( score == TNS_MARVELOUS ) score = pGame->m_mapMarvelousTo;
|
|
|
|
|
if( score == TNS_PERFECT ) score = pGame->m_mapPerfectTo;
|
|
|
|
|
if( score == TNS_GREAT ) score = pGame->m_mapGreatTo;
|
|
|
|
|
if( score == TNS_GOOD ) score = pGame->m_mapGoodTo;
|
|
|
|
|
if( score == TNS_BOO ) score = pGame->m_mapBooTo;
|
2004-06-27 01:16:00 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetTapNoteScore(col, iIndexOverlappingNote, score);
|
2003-04-11 00:12:07 +00:00
|
|
|
|
|
|
|
|
if( score != TNS_NONE )
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetTapNoteOffset(col, iIndexOverlappingNote, -fNoteOffset);
|
2002-12-17 22:07:59 +00:00
|
|
|
|
2004-10-24 23:41:02 +00:00
|
|
|
if( tn.bKeysound )
|
2004-10-23 23:41:49 +00:00
|
|
|
{
|
|
|
|
|
m_vKeysounds[tn.iKeysoundIndex].Play();
|
|
|
|
|
}
|
|
|
|
|
|
2004-04-15 05:41:57 +00:00
|
|
|
if( GAMESTATE->m_PlayerController[m_PlayerNumber] == PC_HUMAN &&
|
|
|
|
|
score >= TNS_GREAT )
|
|
|
|
|
HandleAutosync(fNoteOffset);
|
|
|
|
|
|
2004-08-10 06:39:25 +00:00
|
|
|
//Keep this here so we get the same data as Autosync
|
|
|
|
|
NSMAN->ReportTiming(fNoteOffset,m_PlayerNumber);
|
|
|
|
|
|
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-09-12 05:56:24 +00:00
|
|
|
switch( tn.type )
|
2003-11-11 07:36:28 +00:00
|
|
|
{
|
2004-09-12 05:56:24 +00:00
|
|
|
case TapNote::tap:
|
|
|
|
|
case TapNote::hold_head:
|
2004-01-24 19:11:52 +00:00
|
|
|
// don't the row if this note is a mine or tap attack
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.IsRowCompletelyJudged(iIndexOverlappingNote) )
|
2003-11-11 07:36:28 +00:00
|
|
|
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 :)
|
|
|
|
|
}
|
|
|
|
|
}
|
2004-07-02 20:08:17 +00:00
|
|
|
m_pNoteField->Step( col, score );
|
2001-11-04 19:34:28 +00:00
|
|
|
}
|
2004-07-02 20:08:17 +00:00
|
|
|
else
|
2004-10-23 17:43:49 +00:00
|
|
|
{
|
2004-07-02 20:08:17 +00:00
|
|
|
m_pNoteField->Step( col, TNS_NONE );
|
2004-10-23 17:43:49 +00:00
|
|
|
}
|
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);
|
2004-10-23 17:43:49 +00:00
|
|
|
TapNoteScore score = m_NoteData.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
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int c=0; c<m_NoteData.GetNumTracks(); c++ ) // for each column
|
2002-02-24 01:43:11 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
TapNote tn = m_NoteData.GetTapNote(c, iIndexThatWasSteppedOn);
|
2003-08-10 19:07:54 +00:00
|
|
|
|
2004-09-12 05:56:24 +00:00
|
|
|
if( tn.type == TapNote::empty ) continue; /* no note in this col */
|
|
|
|
|
if( tn.type == TapNote::mine ) continue; /* don't flash on mines b/c they're supposed to be missed */
|
2003-08-10 19:07:54 +00:00
|
|
|
|
|
|
|
|
// 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;
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
2002-02-03 20:45:08 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.GetTapNote(t, r).type == TapNote::empty) /* no note here */
|
2004-09-12 05:56:24 +00:00
|
|
|
continue;
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.GetTapNoteScore(t, r) != TNS_NONE ) /* note here is already hit */
|
2004-09-12 05:56:24 +00:00
|
|
|
continue;
|
2003-08-10 19:07:54 +00:00
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.GetTapNote(t, r).type != TapNote::mine )
|
2003-08-10 19:07:54 +00:00
|
|
|
{
|
|
|
|
|
// A normal note. Penalize for not stepping on it.
|
|
|
|
|
MissedNoteOnThisRow = true;
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.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)
|
2004-10-23 17:43:49 +00:00
|
|
|
RandomizeNotes( iNoteRow );
|
2003-06-10 19:53:33 +00:00
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
// check to see if there's a note at the crossed row
|
2003-07-13 00:34:30 +00:00
|
|
|
RageTimer now;
|
|
|
|
|
if( GAMESTATE->m_PlayerController[m_PlayerNumber] != PC_HUMAN )
|
|
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
|
|
|
|
{
|
|
|
|
|
if( m_NoteData.GetTapNote(t, iNoteRow).type != TapNote::empty )
|
|
|
|
|
if( m_NoteData.GetTapNoteScore(t, iNoteRow) == TNS_NONE )
|
2003-08-09 18:42:21 +00:00
|
|
|
Step( t, now );
|
2004-10-23 17:43:49 +00:00
|
|
|
}
|
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;
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
2003-08-31 22:39:05 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
if( m_NoteData.GetTapNote(t,iNoteRow).type == TapNote::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
|
|
|
}
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
void PlayerMinus::RandomizeNotes( int iNoteRow )
|
2003-05-04 13:58:45 +00:00
|
|
|
{
|
2004-10-07 04:20:13 +00:00
|
|
|
// change the row to look ahead from based upon their speed mod
|
|
|
|
|
/* This is incorrect: if m_fScrollSpeed is 0.5, we'll never change
|
|
|
|
|
* any odd rows, and if it's 2, we'll shuffle each row twice. */
|
|
|
|
|
int iNewNoteRow = iNoteRow + ROWS_PER_BEAT*2;
|
|
|
|
|
iNewNoteRow = int( iNewNoteRow / GAMESTATE->m_PlayerOptions[m_PlayerNumber].m_fScrollSpeed );
|
2003-05-04 13:58:45 +00:00
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
int iNumOfTracks = m_NoteData.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
|
|
|
{
|
2004-10-07 04:20:13 +00:00
|
|
|
const int iSwapWith = RandomInt( 0, iNumOfTracks-1 );
|
2003-10-24 22:57:47 +00:00
|
|
|
|
2004-10-07 04:20:13 +00:00
|
|
|
/* Only swap a tap and an empty. */
|
2004-10-23 17:43:49 +00:00
|
|
|
const TapNote t1 = m_NoteData.GetTapNote(t, iNewNoteRow);
|
2004-10-07 04:20:13 +00:00
|
|
|
if( t1.type != TapNote::tap )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
const TapNote t2 = m_NoteData.GetTapNote( iSwapWith, iNewNoteRow );
|
2004-10-07 04:20:13 +00:00
|
|
|
if( t2.type != TapNote::empty )
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Make sure the destination row isn't in the middle of a hold. */
|
|
|
|
|
bool bSkip = false;
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int i = 0; !bSkip && i < m_NoteData.GetNumHoldNotes(); ++i )
|
2003-05-04 13:58:45 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
const HoldNote &hn = m_NoteData.GetHoldNote(i);
|
2004-10-07 04:20:13 +00:00
|
|
|
if( hn.iTrack == iSwapWith && hn.RowIsInRange(iNewNoteRow) )
|
|
|
|
|
bSkip = true;
|
2003-05-04 13:58:45 +00:00
|
|
|
}
|
2004-10-07 04:20:13 +00:00
|
|
|
if( bSkip )
|
|
|
|
|
continue;
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
m_NoteData.SetTapNote( t, iNewNoteRow, t2 );
|
|
|
|
|
m_NoteData.SetTapNote( iSwapWith, iNewNoteRow, t1 );
|
2004-10-07 04:20:13 +00:00
|
|
|
|
|
|
|
|
const TapNote nft1 = m_pNoteField->GetTapNote( t, iNewNoteRow );
|
|
|
|
|
const TapNote nft2 = m_pNoteField->GetTapNote( iSwapWith, iNewNoteRow );
|
|
|
|
|
m_pNoteField->SetTapNote( t, iNewNoteRow, nft2 );
|
|
|
|
|
m_pNoteField->SetTapNote( iSwapWith, iNewNoteRow, nft1 );
|
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
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
TapNoteScore scoreOfLastTap = m_NoteData.LastTapNoteScore(row);
|
|
|
|
|
int iNumTapsInRow = m_NoteData.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
|
|
|
|
2004-07-31 21:26:11 +00:00
|
|
|
m_Combo.SetCombo( g_CurStageStats.iCurCombo[m_PlayerNumber], g_CurStageStats.iCurMissCombo[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. */
|
2004-07-24 18:11:04 +00:00
|
|
|
g_CurStageStats.UpdateComboList( m_PlayerNumber, g_CurStageStats.fAliveSeconds[m_PlayerNumber], 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 )
|
2004-07-24 18:11:04 +00:00
|
|
|
g_CurStageStats.SetLifeRecordAt( m_PlayerNumber, life, g_CurStageStats.fAliveSeconds[m_PlayerNumber] );
|
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? */
|
2004-10-23 17:43:49 +00:00
|
|
|
/* It was used to have an invisible computer player in PLAY_MODE_BATTLE. -Chris */
|
|
|
|
|
void Player::Load(
|
|
|
|
|
PlayerNumber player_no,
|
|
|
|
|
const NoteData& noteData,
|
|
|
|
|
LifeMeter* pLM,
|
|
|
|
|
CombinedLifeMeter* pCombinedLM,
|
|
|
|
|
ScoreDisplay* pScoreDisplay,
|
|
|
|
|
ScoreDisplay* pSecondaryScoreDisplay,
|
|
|
|
|
Inventory* pInventory,
|
|
|
|
|
ScoreKeeper* pPrimaryScoreKeeper,
|
|
|
|
|
ScoreKeeper* pSecondaryScoreKeeper )
|
2003-08-24 17:04:21 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
PlayerMinus::Load(
|
|
|
|
|
player_no,
|
|
|
|
|
noteData,
|
|
|
|
|
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.
|
|
|
|
|
*/
|