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"
|
2005-01-25 18:29:42 +00:00
|
|
|
#include "RageTimer.h"
|
2002-07-23 01:41:40 +00:00
|
|
|
#include "PrefsManager.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"
|
2005-10-08 02:10:32 +00:00
|
|
|
#include "ScoreKeeperNormal.h"
|
2002-07-28 20:28:37 +00:00
|
|
|
#include "RageLog.h"
|
2002-11-11 04:53:31 +00:00
|
|
|
#include "RageDisplay.h"
|
|
|
|
|
#include "ThemeManager.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"
|
2005-01-25 18:29:42 +00:00
|
|
|
#include "NoteField.h"
|
2003-08-07 06:36:34 +00:00
|
|
|
#include "NoteDataUtil.h"
|
2005-12-30 04:25:41 +00:00
|
|
|
#include "ScreenMessage.h"
|
2003-09-06 02:20:09 +00:00
|
|
|
#include "ScreenManager.h"
|
2003-12-23 00:26:00 +00:00
|
|
|
#include "StageStats.h"
|
2005-12-30 04:25:41 +00:00
|
|
|
#include "ActorUtil.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"
|
2004-11-06 20:36:04 +00:00
|
|
|
#include "ThemeMetric.h"
|
2004-12-20 06:25:59 +00:00
|
|
|
#include "PlayerState.h"
|
2005-01-15 01:28:29 +00:00
|
|
|
#include "GameSoundManager.h"
|
2005-01-15 02:01:26 +00:00
|
|
|
#include "Style.h"
|
2005-02-15 07:09:07 +00:00
|
|
|
#include "MessageManager.h"
|
2005-02-15 09:24:48 +00:00
|
|
|
#include "ProfileManager.h"
|
2005-07-01 05:07:22 +00:00
|
|
|
#include "Profile.h"
|
2005-04-05 04:56:39 +00:00
|
|
|
#include "StatsManager.h"
|
2005-12-30 04:25:41 +00:00
|
|
|
#include "song.h"
|
2005-07-03 02:49:42 +00:00
|
|
|
#include "Steps.h"
|
2005-08-26 17:39:53 +00:00
|
|
|
#include "GameCommand.h"
|
2005-12-22 03:10:04 +00:00
|
|
|
#include "LocalizedString.h"
|
2006-02-06 01:23:25 +00:00
|
|
|
#include "AdjustSync.h"
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2006-07-20 08:50:16 +00:00
|
|
|
RString COMBO_X_NAME( size_t p, size_t both_sides ) { return "ComboXOffset" + (both_sides ? RString("BothSides") : ssprintf("OneSideP%d",int(p+1)) ); }
|
|
|
|
|
RString ATTACK_DISPLAY_X_NAME( size_t p, size_t both_sides ) { return "AttackDisplayXOffset" + (both_sides ? RString("BothSides") : ssprintf("OneSideP%d",int(p+1)) ); }
|
2005-02-06 19:15:38 +00:00
|
|
|
|
2004-10-25 00:31:56 +00:00
|
|
|
/* Distance to search for a note in Step(), in seconds. */
|
|
|
|
|
static const float StepSearchDistance = 1.0f;
|
2006-03-01 17:41:29 +00:00
|
|
|
static const float JUMP_WINDOW_SECONDS = 0.25f;
|
2002-02-24 01:43:11 +00:00
|
|
|
|
2006-07-23 23:49:52 +00:00
|
|
|
static float ADJUSTED_WINDOW_SECONDS( TimingWindow tw )
|
2005-05-23 05:21:40 +00:00
|
|
|
{
|
2005-10-26 23:09:56 +00:00
|
|
|
float fSecs = PREFSMAN->m_fTimingWindowSeconds[tw];
|
2005-10-09 04:30:59 +00:00
|
|
|
fSecs *= PREFSMAN->m_fTimingWindowScale;
|
|
|
|
|
fSecs += PREFSMAN->m_fTimingWindowAdd;
|
2005-05-23 05:21:40 +00:00
|
|
|
return fSecs;
|
|
|
|
|
}
|
2003-04-07 21:24:14 +00:00
|
|
|
|
2006-07-17 00:54:19 +00:00
|
|
|
Player::Player( NoteData &nd, bool bShowNoteField, bool bShowJudgment ) : m_NoteData(nd)
|
2001-11-03 10:52:42 +00:00
|
|
|
{
|
2005-07-22 21:04:27 +00:00
|
|
|
m_bLoaded = false;
|
|
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
m_pPlayerState = NULL;
|
2004-12-22 07:31:27 +00:00
|
|
|
m_pPlayerStageStats = NULL;
|
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
|
|
|
|
2005-03-11 03:54:40 +00:00
|
|
|
m_bPaused = false;
|
2004-04-15 05:41:57 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
m_pJudgment = NULL;
|
|
|
|
|
if( bShowJudgment )
|
|
|
|
|
{
|
|
|
|
|
m_pJudgment = new Judgment;
|
|
|
|
|
this->AddChild( m_pJudgment );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pCombo = NULL;
|
2006-07-23 23:48:14 +00:00
|
|
|
if( bShowJudgment )
|
2005-08-26 17:39:53 +00:00
|
|
|
{
|
|
|
|
|
m_pCombo = new Combo;
|
|
|
|
|
this->AddChild( m_pCombo );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_pAttackDisplay = NULL;
|
|
|
|
|
if( bShowNoteField )
|
|
|
|
|
{
|
|
|
|
|
m_pAttackDisplay = new AttackDisplay;
|
|
|
|
|
this->AddChild( m_pAttackDisplay );
|
|
|
|
|
}
|
2003-04-21 02:41:10 +00:00
|
|
|
|
|
|
|
|
PlayerAI::InitFromDisk();
|
2005-01-15 01:03:48 +00:00
|
|
|
|
2005-08-23 21:07:36 +00:00
|
|
|
m_pNoteField = NULL;
|
|
|
|
|
if( bShowNoteField )
|
2005-08-26 17:39:53 +00:00
|
|
|
{
|
2005-08-23 21:07:36 +00:00
|
|
|
m_pNoteField = new NoteField;
|
2005-08-26 17:39:53 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
Player::~Player()
|
2002-11-03 07:17:03 +00:00
|
|
|
{
|
2005-08-26 17:39:53 +00:00
|
|
|
SAFE_DELETE( m_pJudgment );
|
|
|
|
|
SAFE_DELETE( m_pCombo );
|
|
|
|
|
SAFE_DELETE( m_pAttackDisplay );
|
2005-08-23 21:07:36 +00:00
|
|
|
SAFE_DELETE( m_pNoteField );
|
2005-10-05 22:21:27 +00:00
|
|
|
for( unsigned i = 0; i < m_vHoldJudgment.size(); ++i )
|
|
|
|
|
SAFE_DELETE( m_vHoldJudgment[i] );
|
2002-11-03 07:17:03 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:28:29 +00:00
|
|
|
/* Init() does the expensive stuff: load sounds and note skins. Load() just loads a NoteData. */
|
|
|
|
|
void Player::Init(
|
2006-01-22 01:00:06 +00:00
|
|
|
const RString &sType,
|
2004-12-20 06:25:59 +00:00
|
|
|
PlayerState* pPlayerState,
|
2004-12-22 07:31:27 +00:00
|
|
|
PlayerStageStats* pPlayerStageStats,
|
2004-10-23 17:43:49 +00:00
|
|
|
LifeMeter* pLM,
|
|
|
|
|
CombinedLifeMeter* pCombinedLM,
|
|
|
|
|
ScoreDisplay* pScoreDisplay,
|
|
|
|
|
ScoreDisplay* pSecondaryScoreDisplay,
|
|
|
|
|
Inventory* pInventory,
|
|
|
|
|
ScoreKeeper* pPrimaryScoreKeeper,
|
2005-01-15 01:03:48 +00:00
|
|
|
ScoreKeeper* pSecondaryScoreKeeper )
|
2002-02-02 05:11:12 +00:00
|
|
|
{
|
2006-07-23 23:55:16 +00:00
|
|
|
GRAY_ARROWS_Y_STANDARD.Load( sType, "ReceptorArrowsYStandard" );
|
|
|
|
|
GRAY_ARROWS_Y_REVERSE.Load( sType, "ReceptorArrowsYReverse" );
|
2006-07-24 00:14:04 +00:00
|
|
|
COMBO_X.Load( sType, COMBO_X_NAME, NUM_PLAYERS, 2 );
|
2006-07-23 23:55:16 +00:00
|
|
|
COMBO_Y.Load( sType, "ComboY" );
|
|
|
|
|
COMBO_Y_REVERSE.Load( sType, "ComboYReverse" );
|
|
|
|
|
COMBO_CENTERED_ADDY.Load( sType, "ComboCenteredAddY" );
|
|
|
|
|
COMBO_CENTERED_ADDY_REVERSE.Load( sType, "ComboCenteredAddYReverse" );
|
|
|
|
|
ATTACK_DISPLAY_X.Load( sType, ATTACK_DISPLAY_X_NAME, NUM_PLAYERS, 2 );
|
|
|
|
|
ATTACK_DISPLAY_Y.Load( sType, "AttackDisplayY" );
|
|
|
|
|
ATTACK_DISPLAY_Y_REVERSE.Load( sType, "AttackDisplayYReverse" );
|
|
|
|
|
HOLD_JUDGMENT_Y_STANDARD.Load( sType, "HoldJudgmentYStandard" );
|
|
|
|
|
HOLD_JUDGMENT_Y_REVERSE.Load( sType, "HoldJudgmentYReverse" );
|
|
|
|
|
BRIGHT_GHOST_COMBO_THRESHOLD.Load( sType, "BrightGhostComboThreshold" );
|
|
|
|
|
TAP_JUDGMENTS_UNDER_FIELD.Load( sType, "TapJudgmentsUnderField" );
|
|
|
|
|
HOLD_JUDGMENTS_UNDER_FIELD.Load( sType, "HoldJudgmentsUnderField" );
|
|
|
|
|
START_DRAWING_AT_PIXELS.Load( sType, "StartDrawingAtPixels" );
|
|
|
|
|
STOP_DRAWING_AT_PIXELS.Load( sType, "StopDrawingAtPixels" );
|
2005-02-26 05:59:12 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pJudgment )
|
|
|
|
|
{
|
|
|
|
|
//
|
|
|
|
|
// Init judgment positions
|
|
|
|
|
//
|
2006-09-21 02:09:44 +00:00
|
|
|
LuaReference expr;
|
2005-08-26 17:39:53 +00:00
|
|
|
expr.SetFromExpression( THEME->GetMetric(sType,"JudgmentTransformCommandFunction") );
|
|
|
|
|
|
|
|
|
|
bool bPlayerUsingBothSides = GAMESTATE->GetCurrentStyle()->m_StyleType==ONE_PLAYER_TWO_SIDES;
|
|
|
|
|
Actor temp;
|
|
|
|
|
|
|
|
|
|
int iEnabledPlayerIndex = -1;
|
|
|
|
|
int iNumEnabledPlayers = 0;
|
|
|
|
|
if( GAMESTATE->m_bMultiplayer )
|
|
|
|
|
{
|
|
|
|
|
FOREACH_EnabledMultiPlayer( p )
|
|
|
|
|
{
|
|
|
|
|
if( p == pPlayerState->m_mp )
|
|
|
|
|
iEnabledPlayerIndex = iNumEnabledPlayers;
|
|
|
|
|
iNumEnabledPlayers++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
FOREACH_EnabledPlayer( p )
|
|
|
|
|
{
|
|
|
|
|
if( p == pPlayerState->m_PlayerNumber )
|
|
|
|
|
iEnabledPlayerIndex = iNumEnabledPlayers;
|
|
|
|
|
iNumEnabledPlayers++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2005-08-28 08:58:21 +00:00
|
|
|
|
|
|
|
|
if( iNumEnabledPlayers == 0 ) // hack for ScreenHowToPlay where no players are joined
|
|
|
|
|
{
|
|
|
|
|
iEnabledPlayerIndex = 0;
|
|
|
|
|
iNumEnabledPlayers = 1;
|
|
|
|
|
}
|
2005-08-26 17:39:53 +00:00
|
|
|
|
|
|
|
|
for( int i=0; i<NUM_REVERSE; i++ )
|
|
|
|
|
{
|
|
|
|
|
for( int j=0; j<NUM_CENTERED; j++ )
|
|
|
|
|
{
|
|
|
|
|
Lua *L = LUA->Get();
|
|
|
|
|
expr.PushSelf( L );
|
|
|
|
|
ASSERT( !lua_isnil(L, -1) );
|
|
|
|
|
temp.PushSelf( L );
|
|
|
|
|
LuaHelpers::Push( pPlayerState->m_PlayerNumber, L );
|
|
|
|
|
LuaHelpers::Push( pPlayerState->m_mp, L );
|
|
|
|
|
LuaHelpers::Push( iEnabledPlayerIndex, L );
|
|
|
|
|
LuaHelpers::Push( iNumEnabledPlayers, L );
|
|
|
|
|
LuaHelpers::Push( bPlayerUsingBothSides, L );
|
|
|
|
|
LuaHelpers::Push( !!i, L );
|
|
|
|
|
LuaHelpers::Push( !!j, L );
|
|
|
|
|
lua_call( L, 8, 0 ); // 8 args, 0 results
|
|
|
|
|
LUA->Release(L);
|
|
|
|
|
|
|
|
|
|
m_tsJudgment[i][j] = temp.DestTweenState();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Load Judgment frame
|
|
|
|
|
if( GAMESTATE->m_bMultiplayer && !m_sprJudgmentFrame.IsLoaded() ) // only load the first time
|
|
|
|
|
{
|
|
|
|
|
GameCommand gc;
|
|
|
|
|
ASSERT( pPlayerState->m_mp != MultiPlayer_INVALID );
|
|
|
|
|
gc.m_MultiPlayer = pPlayerState->m_mp;
|
2006-04-21 00:37:27 +00:00
|
|
|
|
|
|
|
|
{
|
|
|
|
|
Lua *L = LUA->Get();
|
|
|
|
|
gc.PushSelf( L );
|
|
|
|
|
lua_setglobal( L, "ThisGameCommand" );
|
|
|
|
|
LUA->Release( L );
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
m_sprJudgmentFrame.Load( THEME->GetPathG(sType,"JudgmentFrame") );
|
|
|
|
|
|
2006-04-21 00:37:27 +00:00
|
|
|
{
|
|
|
|
|
Lua *L = LUA->Get();
|
|
|
|
|
expr.PushSelf( L );
|
|
|
|
|
ASSERT( !lua_isnil(L, -1) );
|
|
|
|
|
m_sprJudgmentFrame->PushSelf( L );
|
|
|
|
|
LuaHelpers::Push( pPlayerState->m_PlayerNumber, L );
|
|
|
|
|
LuaHelpers::Push( pPlayerState->m_mp, L );
|
|
|
|
|
LuaHelpers::Push( iEnabledPlayerIndex, L );
|
|
|
|
|
LuaHelpers::Push( iNumEnabledPlayers, L );
|
|
|
|
|
LuaHelpers::Push( bPlayerUsingBothSides, L );
|
|
|
|
|
LuaHelpers::Push( false, L );
|
|
|
|
|
LuaHelpers::Push( false, L );
|
|
|
|
|
lua_call( L, 8, 0 ); // 8 args, 0 results
|
|
|
|
|
LUA->Release( L );
|
|
|
|
|
}
|
|
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
LUA->UnsetGlobal( "ThisGameCommand" );
|
|
|
|
|
this->AddChild( m_sprJudgmentFrame );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-01-11 01:02:46 +00:00
|
|
|
this->SortByDrawOrder();
|
|
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
m_pPlayerState = pPlayerState;
|
2004-12-22 07:31:27 +00:00
|
|
|
m_pPlayerStageStats = pPlayerStageStats;
|
2002-07-28 20:28:37 +00:00
|
|
|
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;
|
2005-01-15 01:28:29 +00:00
|
|
|
|
2005-02-15 07:09:07 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
|
|
|
|
|
|
|
|
|
m_sMessageToSendOnStep = ssprintf("StepP%d",pn+1);
|
|
|
|
|
|
|
|
|
|
|
2005-02-26 05:59:12 +00:00
|
|
|
m_soundMine.Load( THEME->GetPathS(sType,"mine"), true );
|
2005-01-15 01:28:29 +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:
|
2005-02-26 05:59:12 +00:00
|
|
|
m_soundAttackLaunch.Load( THEME->GetPathS(sType,"battle attack launch"), true );
|
|
|
|
|
m_soundAttackEnding.Load( THEME->GetPathS(sType,"battle attack ending"), true );
|
2005-01-15 01:28:29 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
2005-02-26 05:59:12 +00:00
|
|
|
m_soundAttackLaunch.Load( THEME->GetPathS(sType,"course attack launch"), true );
|
|
|
|
|
m_soundAttackEnding.Load( THEME->GetPathS(sType,"course attack ending"), true );
|
2005-01-15 01:28:29 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
RageSoundParams p;
|
|
|
|
|
GameSoundManager::SetPlayerBalance( pn, p );
|
|
|
|
|
|
|
|
|
|
m_soundMine.SetParams( p );
|
|
|
|
|
m_soundAttackLaunch.SetParams( p );
|
|
|
|
|
m_soundAttackEnding.SetParams( p );
|
2005-02-26 05:45:51 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pCombo )
|
|
|
|
|
{
|
|
|
|
|
m_pCombo->SetName( "Combo" );
|
|
|
|
|
m_pCombo->Load( m_pPlayerState, m_pPlayerStageStats );
|
|
|
|
|
ActorUtil::OnCommand( m_pCombo, sType );
|
|
|
|
|
}
|
2005-04-14 04:50:20 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pJudgment )
|
|
|
|
|
{
|
|
|
|
|
m_pJudgment->SetName( "Judgment" );
|
2006-07-11 16:55:38 +00:00
|
|
|
m_pJudgment->LoadNormal();
|
2005-08-26 17:39:53 +00:00
|
|
|
ActorUtil::OnCommand( m_pJudgment, sType );
|
|
|
|
|
}
|
2005-02-26 06:31:56 +00:00
|
|
|
|
2005-10-05 22:21:27 +00:00
|
|
|
// Load HoldJudgments
|
|
|
|
|
for( int i = 0; i < GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer; ++i )
|
|
|
|
|
{
|
|
|
|
|
HoldJudgment *pJudgment = new HoldJudgment;
|
2006-07-13 08:07:11 +00:00
|
|
|
pJudgment->Load( THEME->GetPathG("HoldJudgment","label 1x2") );
|
2005-10-05 22:21:27 +00:00
|
|
|
m_vHoldJudgment.push_back( pJudgment );
|
|
|
|
|
this->AddChild( m_vHoldJudgment[i] );
|
|
|
|
|
}
|
|
|
|
|
|
2005-03-18 04:43:59 +00:00
|
|
|
m_fNoteFieldHeight = GRAY_ARROWS_Y_REVERSE-GRAY_ARROWS_Y_STANDARD;
|
2005-08-23 21:07:36 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->Init( m_pPlayerState, m_fNoteFieldHeight );
|
2005-01-15 01:28:29 +00:00
|
|
|
}
|
|
|
|
|
|
2006-07-17 00:54:19 +00:00
|
|
|
void Player::Load()
|
2005-01-15 01:28:29 +00:00
|
|
|
{
|
2005-07-22 21:04:27 +00:00
|
|
|
m_bLoaded = true;
|
|
|
|
|
|
2005-10-08 00:57:40 +00:00
|
|
|
m_LastTapNoteScore = TNS_None;
|
2006-08-01 20:58:30 +00:00
|
|
|
// The editor can start playing in the middle of the song.
|
|
|
|
|
const int iNoteRow = BeatToNoteRowNotRounded( GAMESTATE->m_fSongBeat );
|
|
|
|
|
m_iRowLastCrossed = iNoteRow - 1;
|
|
|
|
|
m_iMineRowLastCrossed = iNoteRow - 1;
|
|
|
|
|
m_iRowLastJudged = iNoteRow - 1;
|
|
|
|
|
m_iMineRowLastJudged = iNoteRow - 1;
|
|
|
|
|
m_JudgedRows.Reset( iNoteRow );
|
2002-07-28 20:28:37 +00:00
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
2005-01-15 01:28:29 +00:00
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
2004-12-20 06:25:59 +00:00
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
bool bOniDead = GAMESTATE->m_SongOptions.GetStage().m_LifeType == SongOptions::LIFE_BATTERY &&
|
2004-12-22 07:31:27 +00:00
|
|
|
(m_pPlayerStageStats == NULL || m_pPlayerStageStats->bFailed);
|
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 */
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pJudgment )
|
|
|
|
|
m_pJudgment->StopTweening();
|
|
|
|
|
// m_pCombo->Reset(); // don't reset combos between songs in a course!
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
2005-08-26 17:39:53 +00:00
|
|
|
{
|
|
|
|
|
if( m_pCombo )
|
|
|
|
|
m_pCombo->SetCombo( m_pPlayerStageStats->iCurCombo, m_pPlayerStageStats->iCurMissCombo ); // combo can persist between songs and games
|
|
|
|
|
}
|
|
|
|
|
if( m_pAttackDisplay )
|
|
|
|
|
m_pAttackDisplay->Init( m_pPlayerState );
|
|
|
|
|
if( m_pJudgment )
|
|
|
|
|
m_pJudgment->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. */
|
2006-08-05 02:38:05 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, m_pPlayerState->m_PlayerOptions.GetStage(), GAMESTATE->GetCurrentStyle()->m_StepsType );
|
2003-11-20 06:50:05 +00:00
|
|
|
|
|
|
|
|
switch( GAMESTATE->m_PlayMode )
|
|
|
|
|
{
|
2005-01-15 03:10:08 +00:00
|
|
|
case PLAY_MODE_RAVE:
|
|
|
|
|
case PLAY_MODE_BATTLE:
|
|
|
|
|
{
|
|
|
|
|
// ugly, ugly, ugly. Works only w/ dance.
|
2006-08-05 02:38:05 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, m_pPlayerState->m_PlayerOptions.GetStage(), GAMESTATE->GetCurrentStyle()->m_StepsType );
|
2005-01-15 03:10:08 +00:00
|
|
|
|
|
|
|
|
// shuffle either p1 or p2
|
|
|
|
|
static int count = 0;
|
|
|
|
|
switch( count )
|
2003-11-20 06:50:05 +00:00
|
|
|
{
|
2005-01-15 03:10:08 +00:00
|
|
|
case 0:
|
|
|
|
|
case 3:
|
|
|
|
|
NoteDataUtil::Turn( m_NoteData, STEPS_TYPE_DANCE_SINGLE, NoteDataUtil::left);
|
|
|
|
|
break;
|
|
|
|
|
case 1:
|
|
|
|
|
case 2:
|
|
|
|
|
NoteDataUtil::Turn( m_NoteData, STEPS_TYPE_DANCE_SINGLE, NoteDataUtil::right);
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
2003-11-20 06:50:05 +00:00
|
|
|
}
|
2005-01-15 03:10:08 +00:00
|
|
|
count++;
|
|
|
|
|
count %= 4;
|
|
|
|
|
}
|
|
|
|
|
break;
|
2003-11-20 06:50:05 +00:00
|
|
|
}
|
2001-11-30 19:08:44 +00:00
|
|
|
|
2005-07-14 05:12:56 +00:00
|
|
|
int iStartDrawingAtPixels = GAMESTATE->IsEditing() ? -100 : START_DRAWING_AT_PIXELS;
|
|
|
|
|
int iStopDrawingAtPixels = GAMESTATE->IsEditing() ? 400 : STOP_DRAWING_AT_PIXELS;
|
2002-08-31 03:04:38 +00:00
|
|
|
|
2005-01-15 01:09:08 +00:00
|
|
|
float fNoteFieldMiddle = (GRAY_ARROWS_Y_STANDARD+GRAY_ARROWS_Y_REVERSE)/2;
|
2004-10-01 02:11:52 +00:00
|
|
|
|
2005-10-02 19:23:08 +00:00
|
|
|
if( m_pNoteField && !bOniDead )
|
2005-08-23 21:07:36 +00:00
|
|
|
{
|
2005-01-15 01:09:08 +00:00
|
|
|
m_pNoteField->SetY( fNoteFieldMiddle );
|
2005-08-23 21:07:36 +00:00
|
|
|
m_pNoteField->Load( &m_NoteData, iStartDrawingAtPixels, iStopDrawingAtPixels );
|
|
|
|
|
}
|
2001-12-20 12:37:38 +00:00
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
const bool bReverse = m_pPlayerState->m_PlayerOptions.GetStage().GetReversePercentForColumn( 0 ) == 1;
|
2004-11-07 05:49:06 +00:00
|
|
|
bool bPlayerUsingBothSides = GAMESTATE->GetCurrentStyle()->m_StyleType==ONE_PLAYER_TWO_SIDES;
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pCombo )
|
|
|
|
|
{
|
2006-07-24 00:14:04 +00:00
|
|
|
m_pCombo->SetX( COMBO_X.GetValue(pn, bPlayerUsingBothSides) );
|
2005-08-26 17:39:53 +00:00
|
|
|
m_pCombo->SetY( bReverse ? COMBO_Y_REVERSE : COMBO_Y );
|
|
|
|
|
}
|
|
|
|
|
if( m_pAttackDisplay )
|
2006-07-24 00:14:04 +00:00
|
|
|
m_pAttackDisplay->SetX( ATTACK_DISPLAY_X.GetValue(pn, bPlayerUsingBothSides) - 40 );
|
2005-08-26 17:39:53 +00:00
|
|
|
// set this in Update //m_pAttackDisplay->SetY( bReverse ? ATTACK_DISPLAY_Y_REVERSE : ATTACK_DISPLAY_Y );
|
|
|
|
|
|
|
|
|
|
// set this in Update
|
|
|
|
|
//m_pJudgment->SetX( JUDGMENT_X.GetValue(pn,bPlayerUsingBothSides) );
|
|
|
|
|
//m_pJudgment->SetY( bReverse ? JUDGMENT_Y_REVERSE : JUDGMENT_Y );
|
2001-12-20 12:37:38 +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
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
|
2004-10-23 23:41:49 +00:00
|
|
|
//
|
2005-01-15 03:10:08 +00:00
|
|
|
// Load keysounds. If sounds are already loaded (as in the editor), don't reload them.
|
|
|
|
|
// XXX: the editor will load several duplicate copies (in each NoteField), and each
|
|
|
|
|
// player will load duplicate sounds. Does this belong somewhere else (perhaps in
|
|
|
|
|
// a separate object, used alongside ScreenGameplay::m_pSoundMusic and ScreenEdit::m_pSoundMusic?)
|
|
|
|
|
// We don't have to load separate copies to set player fade: always make a copy, and set the
|
|
|
|
|
// fade on the copy.
|
2004-10-23 23:41:49 +00:00
|
|
|
//
|
2005-01-15 01:28:29 +00:00
|
|
|
const Song* pSong = GAMESTATE->m_pCurSong;
|
2006-01-22 01:00:06 +00:00
|
|
|
RString sSongDir = pSong->GetSongDir();
|
2004-10-23 23:41:49 +00:00
|
|
|
m_vKeysounds.resize( pSong->m_vsKeysoundFile.size() );
|
2005-01-15 01:28:29 +00:00
|
|
|
|
|
|
|
|
RageSoundParams p;
|
|
|
|
|
GameSoundManager::SetPlayerBalance( pn, p );
|
2004-10-23 23:41:49 +00:00
|
|
|
for( unsigned i=0; i<m_vKeysounds.size(); i++ )
|
|
|
|
|
{
|
2006-01-22 01:00:06 +00:00
|
|
|
RString sKeysoundFilePath = sSongDir + pSong->m_vsKeysoundFile[i];
|
2004-10-23 23:41:49 +00:00
|
|
|
RageSound& sound = m_vKeysounds[i];
|
2005-01-15 03:10:08 +00:00
|
|
|
if( sound.GetLoadedFilePath() != sKeysoundFilePath )
|
|
|
|
|
sound.Load( sKeysoundFilePath, true );
|
2004-10-23 23:41:49 +00:00
|
|
|
sound.SetParams( p );
|
|
|
|
|
}
|
2001-11-04 19:34:28 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::Update( float fDeltaTime )
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2006-07-25 05:31:22 +00:00
|
|
|
const RageTimer now;
|
2005-07-22 21:04:27 +00:00
|
|
|
// Don't update if we havn't been loaded yet.
|
|
|
|
|
if( !m_bLoaded )
|
|
|
|
|
return;
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
//LOG->Trace( "Player::Update(%f)", fDeltaTime );
|
2001-11-04 19:34:28 +00:00
|
|
|
|
2003-04-13 04:50:08 +00:00
|
|
|
if( GAMESTATE->m_pCurSong==NULL )
|
|
|
|
|
return;
|
|
|
|
|
|
2005-03-11 03:54:40 +00:00
|
|
|
ActorFrame::Update( fDeltaTime );
|
|
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
if( m_pPlayerState->m_bAttackBeganThisUpdate )
|
2004-03-24 07:37:35 +00:00
|
|
|
m_soundAttackLaunch.Play();
|
2004-12-20 06:25:59 +00:00
|
|
|
if( m_pPlayerState->m_bAttackEndedThisUpdate )
|
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
|
|
|
|
2004-12-20 10:47:41 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->Update( fDeltaTime );
|
2003-08-14 08:13:08 +00:00
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
float fMiniPercent = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_MINI];
|
|
|
|
|
float fTinyPercent = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY];
|
2006-06-11 21:26:38 +00:00
|
|
|
float fJudgmentZoom = min( powf(0.5f, fMiniPercent+fTinyPercent), 1.0f );
|
|
|
|
|
|
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
|
|
|
{
|
2006-08-05 02:38:05 +00:00
|
|
|
float fPercentReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(c);
|
2004-02-25 07:30:12 +00:00
|
|
|
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
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
const float fX = ArrowEffects::GetXPos( m_pPlayerState, c, 0 );
|
|
|
|
|
const float fZ = ArrowEffects::GetZPos( m_pPlayerState, c, 0 );
|
2004-02-25 07:30:12 +00:00
|
|
|
|
2005-10-05 22:21:27 +00:00
|
|
|
m_vHoldJudgment[c]->SetX( fX );
|
|
|
|
|
m_vHoldJudgment[c]->SetY( fHoldJudgeYPos );
|
|
|
|
|
m_vHoldJudgment[c]->SetZ( fZ );
|
2006-06-11 21:26:38 +00:00
|
|
|
m_vHoldJudgment[c]->SetZoom( fJudgmentZoom );
|
2004-02-25 07:30:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2003-08-16 23:34:47 +00:00
|
|
|
// NoteField accounts for reverse on its own now.
|
2004-12-20 10:47:41 +00:00
|
|
|
//if( m_pNoteField )
|
|
|
|
|
// m_pNoteField->SetY( fGrayYPos );
|
2003-08-14 08:13:08 +00:00
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
const bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(0) == 1;
|
|
|
|
|
float fPercentCentered = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fScrolls[PlayerOptions::SCROLL_CENTERED];
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pCombo )
|
|
|
|
|
{
|
|
|
|
|
m_pCombo->SetY(
|
|
|
|
|
bReverse ?
|
|
|
|
|
COMBO_Y_REVERSE + fPercentCentered * COMBO_CENTERED_ADDY_REVERSE :
|
|
|
|
|
COMBO_Y + fPercentCentered * COMBO_CENTERED_ADDY );
|
|
|
|
|
}
|
|
|
|
|
if( m_pJudgment )
|
|
|
|
|
{
|
|
|
|
|
const Actor::TweenState &ts1 = m_tsJudgment[bReverse?1:0][0];
|
|
|
|
|
const Actor::TweenState &ts2 = m_tsJudgment[bReverse?1:0][1];
|
|
|
|
|
Actor::TweenState::MakeWeightedAverage( m_pJudgment->DestTweenState(), ts1, ts2, fPercentCentered );
|
|
|
|
|
if( m_sprJudgmentFrame.IsLoaded() )
|
|
|
|
|
Actor::TweenState::MakeWeightedAverage( m_sprJudgmentFrame->DestTweenState(), ts1, ts2, fPercentCentered );
|
|
|
|
|
}
|
2005-04-05 21:40:30 +00:00
|
|
|
|
|
|
|
|
|
2006-04-05 02:59:16 +00:00
|
|
|
float fNoteFieldZoom = 1 - fTinyPercent*0.5f;
|
|
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->SetZoom( fNoteFieldZoom );
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pJudgment )
|
|
|
|
|
m_pJudgment->SetZoom( m_pJudgment->GetZoom() * fJudgmentZoom );
|
|
|
|
|
if( m_sprJudgmentFrame.IsLoaded() )
|
|
|
|
|
m_sprJudgmentFrame->SetZoom( m_sprJudgmentFrame->GetZoom() * fJudgmentZoom );
|
2003-08-14 08:13:08 +00:00
|
|
|
|
2005-03-11 03:54:40 +00:00
|
|
|
// If we're paused, don't update tap or hold note logic, so hold notes can be released
|
|
|
|
|
// during pause.
|
|
|
|
|
if( m_bPaused )
|
|
|
|
|
return;
|
2006-07-20 07:16:06 +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;
|
2005-10-14 00:16:24 +00:00
|
|
|
ASSERT_M( iNumCols <= MAX_COLS_PER_PLAYER, ssprintf("%i > %i", iNumCols, MAX_COLS_PER_PLAYER) );
|
2004-05-31 01:03:45 +00:00
|
|
|
for( int col=0; col < iNumCols; ++col )
|
2003-10-09 03:50:44 +00:00
|
|
|
{
|
2005-10-15 10:11:45 +00:00
|
|
|
ASSERT( m_pPlayerState );
|
|
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
2006-09-13 01:54:16 +00:00
|
|
|
GameInput GameI = GAMESTATE->m_pCurStyle->StyleInputToGameInput( col, m_pPlayerState->m_PlayerNumber );
|
2004-12-20 06:25:59 +00:00
|
|
|
|
2006-09-13 01:54:16 +00:00
|
|
|
bool bIsHoldingButton = INPUTMAPPER->IsBeingPressed( GameI );
|
2004-01-01 04:06:37 +00:00
|
|
|
// TODO: Make this work for non-human-controlled players
|
2004-12-20 06:25:59 +00:00
|
|
|
if( bIsHoldingButton && !GAMESTATE->m_bDemonstrationOrJukebox && m_pPlayerState->m_PlayerController==PC_HUMAN )
|
2004-12-20 10:47:41 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->SetPressed( col );
|
2003-10-09 03:50:44 +00:00
|
|
|
}
|
2004-05-30 22:55:05 +00:00
|
|
|
|
2005-06-16 21:36:58 +00:00
|
|
|
|
|
|
|
|
//
|
|
|
|
|
// handle Autoplay for rolls
|
|
|
|
|
//
|
2005-06-16 21:41:51 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
2005-06-16 21:36:58 +00:00
|
|
|
{
|
|
|
|
|
for( int iTrack=0; iTrack<m_NoteData.GetNumTracks(); ++iTrack )
|
|
|
|
|
{
|
2005-06-16 21:41:51 +00:00
|
|
|
// TODO: Make the CPU miss sometimes.
|
|
|
|
|
int iHeadRow;
|
2005-07-22 00:14:24 +00:00
|
|
|
if( !m_NoteData.IsHoldNoteAtRow(iTrack, iSongRow, &iHeadRow) )
|
2005-06-16 21:41:51 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( iTrack, iHeadRow );
|
|
|
|
|
if( tn.type != TapNote::hold_head || tn.subType != TapNote::hold_head_roll )
|
|
|
|
|
continue;
|
2005-10-08 00:57:40 +00:00
|
|
|
if( tn.HoldResult.hns != HNS_None )
|
2005-06-16 21:41:51 +00:00
|
|
|
continue;
|
|
|
|
|
if( tn.HoldResult.fLife >= 0.5f )
|
|
|
|
|
continue;
|
|
|
|
|
|
2006-08-14 09:09:24 +00:00
|
|
|
Step( iTrack, iSongRow, now, false, false );
|
2006-04-25 02:35:55 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY )
|
|
|
|
|
STATSMAN->m_CurStageStats.bUsedAutoplay = true;
|
2005-06-16 21:36:58 +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
|
|
|
//
|
2005-01-25 05:02:35 +00:00
|
|
|
for( int iTrack=0; iTrack<m_NoteData.GetNumTracks(); ++iTrack )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
// Since this is being called every frame, let's not check the whole array every time.
|
|
|
|
|
// Instead, only check 1 beat back. Even 1 is overkill.
|
|
|
|
|
const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) );
|
|
|
|
|
NoteData::iterator begin, end;
|
2005-03-12 02:30:28 +00:00
|
|
|
m_NoteData.GetTapNoteRangeInclusive( iTrack, iStartCheckingAt, iSongRow+1, begin, end );
|
2005-01-25 05:02:35 +00:00
|
|
|
for( ; begin != end; ++begin )
|
|
|
|
|
{
|
|
|
|
|
TapNote &tn = begin->second;
|
|
|
|
|
if( tn.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
|
|
|
|
int iRow = begin->first;
|
2002-02-03 20:45:08 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// set hold flags so NoteField can do intelligent drawing
|
|
|
|
|
tn.HoldResult.bHeld = false;
|
|
|
|
|
tn.HoldResult.bActive = false;
|
2003-12-16 06:10:50 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
HoldNoteScore hns = tn.HoldResult.hns;
|
2005-10-08 00:57:40 +00:00
|
|
|
if( hns != HNS_None ) // if this HoldNote already has a result
|
2005-01-25 05:02:35 +00:00
|
|
|
continue; // we don't need to update the logic for this one
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
2003-12-16 06:10:50 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// if they got a bad score or haven't stepped on the corresponding tap yet
|
|
|
|
|
const TapNoteScore tns = tn.result.tns;
|
2005-10-08 00:57:40 +00:00
|
|
|
const bool bSteppedOnTapNote = tns != TNS_None && tns != TNS_Miss; // did they step on the start of this hold?
|
2005-01-18 08:20:14 +00:00
|
|
|
|
2005-04-26 20:21:13 +00:00
|
|
|
bool bIsHoldingButton = false;
|
2005-01-25 05:02:35 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
|
|
|
|
{
|
|
|
|
|
// TODO: Make the CPU miss sometimes.
|
2005-06-16 21:36:58 +00:00
|
|
|
bIsHoldingButton = true;
|
2006-04-25 02:35:55 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY )
|
|
|
|
|
STATSMAN->m_CurStageStats.bUsedAutoplay = true;
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-09-13 01:54:16 +00:00
|
|
|
GameInput GameI = GAMESTATE->m_pCurStyle->StyleInputToGameInput( iTrack, pn );
|
2006-09-13 01:45:10 +00:00
|
|
|
bIsHoldingButton = INPUTMAPPER->IsBeingPressed( GameI, m_pPlayerState->m_mp );
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
2005-01-18 08:20:14 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
int iEndRow = iRow + tn.iDuration;
|
2002-04-16 17:31:00 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
float fLife = tn.HoldResult.fLife;
|
|
|
|
|
if( bSteppedOnTapNote && fLife != 0 )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
2005-01-25 05:02:35 +00:00
|
|
|
/* This hold note is not judged and we stepped on its head. Update iLastHeldRow.
|
|
|
|
|
* Do this even if we're a little beyond the end of the hold note, to make sure
|
|
|
|
|
* iLastHeldRow is clamped to iEndRow if the hold note is held all the way. */
|
|
|
|
|
tn.HoldResult.iLastHeldRow = min( iSongRow, iEndRow );
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
2005-01-25 05:02:35 +00:00
|
|
|
|
|
|
|
|
// If the song beat is in the range of this hold:
|
|
|
|
|
if( iRow <= iSongRow && iRow <= iEndRow )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
2005-04-20 01:52:55 +00:00
|
|
|
switch( tn.subType )
|
2005-01-25 05:02:35 +00:00
|
|
|
{
|
2005-04-20 01:52:55 +00:00
|
|
|
case TapNote::hold_head_hold:
|
|
|
|
|
// set hold flag so NoteField can do intelligent drawing
|
|
|
|
|
tn.HoldResult.bHeld = bIsHoldingButton && bSteppedOnTapNote;
|
|
|
|
|
tn.HoldResult.bActive = bSteppedOnTapNote;
|
|
|
|
|
|
|
|
|
|
if( bSteppedOnTapNote && bIsHoldingButton )
|
|
|
|
|
{
|
|
|
|
|
// Increase life
|
|
|
|
|
fLife = 1;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Decrease life
|
2005-10-09 04:30:59 +00:00
|
|
|
fLife -= fDeltaTime/ADJUSTED_WINDOW_SECONDS(TW_Hold);
|
2005-04-20 01:52:55 +00:00
|
|
|
fLife = max( fLife, 0 ); // clamp
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
case TapNote::hold_head_roll:
|
|
|
|
|
tn.HoldResult.bHeld = true;
|
|
|
|
|
tn.HoldResult.bActive = bSteppedOnTapNote;
|
|
|
|
|
|
|
|
|
|
// give positive life in Step(), not here.
|
2005-01-25 05:02:35 +00:00
|
|
|
|
2002-06-14 22:25:22 +00:00
|
|
|
// Decrease life
|
2005-10-08 17:37:04 +00:00
|
|
|
fLife -= fDeltaTime/ADJUSTED_WINDOW_SECONDS(TW_Roll);
|
2002-06-14 22:25:22 +00:00
|
|
|
fLife = max( fLife, 0 ); // clamp
|
2005-04-20 01:52:55 +00:00
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
|
|
|
|
|
2005-10-08 02:10:32 +00:00
|
|
|
/* check for LetGo. If the head was missed completely, don't count an LetGo. */
|
2005-01-25 05:02:35 +00:00
|
|
|
if( bSteppedOnTapNote && fLife == 0 ) // the player has not pressed the button for a long time!
|
2005-10-08 00:57:40 +00:00
|
|
|
hns = HNS_LetGo;
|
2001-12-19 01:50:57 +00:00
|
|
|
|
2005-01-25 05:02:35 +00:00
|
|
|
// check for OK
|
|
|
|
|
if( iSongRow >= iEndRow && bSteppedOnTapNote && fLife > 0 ) // if this HoldNote is in the past
|
|
|
|
|
{
|
|
|
|
|
fLife = 1;
|
2005-10-08 00:57:40 +00:00
|
|
|
hns = HNS_Held;
|
2005-04-26 05:28:32 +00:00
|
|
|
bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD;
|
2005-01-25 05:02:35 +00:00
|
|
|
if( m_pNoteField )
|
2005-10-08 00:57:40 +00:00
|
|
|
m_pNoteField->DidHoldNote( iTrack, HNS_Held, bBright ); // bright ghost flash
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
2006-07-10 05:15:52 +00:00
|
|
|
|
|
|
|
|
tn.HoldResult.fLife = fLife;
|
|
|
|
|
tn.HoldResult.hns = hns;
|
|
|
|
|
|
2005-10-08 00:57:40 +00:00
|
|
|
if( hns != HNS_None )
|
2005-01-25 05:02:35 +00:00
|
|
|
{
|
|
|
|
|
/* this note has been judged */
|
2006-07-10 05:15:52 +00:00
|
|
|
HandleHoldScore( tn );
|
2006-04-21 00:37:27 +00:00
|
|
|
|
2006-05-30 17:44:50 +00:00
|
|
|
if( m_pPlayerStageStats != NULL )
|
|
|
|
|
m_pPlayerStageStats->hnsLast = hns;
|
2006-04-21 00:37:27 +00:00
|
|
|
if( m_pPlayerState->m_mp != MultiPlayer_INVALID )
|
|
|
|
|
MESSAGEMAN->Broadcast( enum_add2(Message_ShowHoldJudgmentMuliPlayerP1,m_pPlayerState->m_mp) );
|
|
|
|
|
|
2005-10-05 22:21:27 +00:00
|
|
|
m_vHoldJudgment[iTrack]->SetHoldJudgment( hns );
|
2005-01-25 05:02:35 +00:00
|
|
|
}
|
|
|
|
|
}
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
2006-07-20 07:16:06 +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 )
|
|
|
|
|
{
|
2005-01-22 20:41:50 +00:00
|
|
|
// for each index we crossed since the last update
|
2006-09-12 23:57:56 +00:00
|
|
|
if( GAMESTATE->IsPlayerEnabled(m_pPlayerState) )
|
2005-01-28 04:37:48 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iRowLastCrossed, iRowNow+1 )
|
2006-07-25 05:31:22 +00:00
|
|
|
CrossedRow( r, now );
|
2005-01-28 04:37:48 +00:00
|
|
|
m_iRowLastCrossed = iRowNow+1;
|
2004-02-25 05:49:54 +00:00
|
|
|
}
|
|
|
|
|
}
|
2006-07-20 07:16:06 +00:00
|
|
|
|
2004-02-25 05:49:54 +00:00
|
|
|
{
|
|
|
|
|
// 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 )
|
|
|
|
|
{
|
2005-01-22 20:41:50 +00:00
|
|
|
// for each index we crossed since the last update
|
2006-09-12 23:57:56 +00:00
|
|
|
if( GAMESTATE->IsPlayerEnabled(m_pPlayerState) )
|
2005-01-28 04:37:48 +00:00
|
|
|
FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteData, r, m_iMineRowLastCrossed, iRowNow+1 )
|
2006-07-25 05:31:22 +00:00
|
|
|
CrossedMineRow( r, now );
|
2005-01-28 04:37:48 +00:00
|
|
|
m_iMineRowLastCrossed = iRowNow+1;
|
2004-02-25 05:49:54 +00:00
|
|
|
}
|
2003-06-10 19:53:33 +00:00
|
|
|
}
|
2006-07-20 07:16:06 +00:00
|
|
|
|
|
|
|
|
// Check for completely judged rows.
|
|
|
|
|
UpdateJudgedRows();
|
2003-06-10 19:53:33 +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();
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-10 04:06:37 +00:00
|
|
|
void Player::ProcessMessages( float fDeltaTime )
|
|
|
|
|
{
|
|
|
|
|
ActorFrame::ProcessMessages( fDeltaTime );
|
|
|
|
|
|
|
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->ProcessMessages( fDeltaTime );
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::ApplyWaitingTransforms()
|
2003-12-23 02:17:28 +00:00
|
|
|
{
|
2004-12-20 06:25:59 +00:00
|
|
|
for( unsigned j=0; j<m_pPlayerState->m_ModsToApply.size(); j++ )
|
2003-08-18 02:37:43 +00:00
|
|
|
{
|
2004-12-20 06:25:59 +00:00
|
|
|
const Attack &mod = m_pPlayerState->m_ModsToApply[j];
|
2003-10-25 07:58:10 +00:00
|
|
|
PlayerOptions po;
|
2005-05-20 08:57:59 +00:00
|
|
|
po.FromString( mod.sModifiers );
|
2003-08-18 02:37:43 +00:00
|
|
|
|
2003-10-25 07:58:10 +00:00
|
|
|
float fStartBeat, fEndBeat;
|
2006-02-01 04:39:19 +00:00
|
|
|
mod.GetRealtimeAttackBeats( GAMESTATE->m_pCurSong, m_pPlayerState, fStartBeat, fEndBeat );
|
2004-10-23 17:43:49 +00:00
|
|
|
fEndBeat = min( fEndBeat, m_NoteData.GetLastBeat() );
|
2003-10-25 07:58:10 +00:00
|
|
|
|
2005-05-20 08:57:59 +00:00
|
|
|
LOG->Trace( "Applying transform '%s' from %f to %f to '%s'", mod.sModifiers.c_str(), fStartBeat, fEndBeat,
|
2003-10-27 22:26:34 +00:00
|
|
|
GAMESTATE->m_pCurSong->GetTranslitMainTitle().c_str() );
|
2003-08-18 04:20:51 +00:00
|
|
|
|
2005-01-22 17:34:10 +00:00
|
|
|
NoteDataUtil::TransformNoteData( m_NoteData, po, GAMESTATE->GetCurrentStyle()->m_StepsType, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat) );
|
2003-08-18 02:37:43 +00:00
|
|
|
}
|
2004-12-20 06:25:59 +00:00
|
|
|
m_pPlayerState->m_ModsToApply.clear();
|
2001-11-03 10:52:42 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::DrawPrimitives()
|
2002-02-24 10:31:20 +00:00
|
|
|
{
|
2004-12-20 06:25:59 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
|
|
|
|
|
2003-06-13 04:31:01 +00:00
|
|
|
// May have both players in doubles (for battle play); only draw primary player.
|
2004-11-07 05:49:06 +00:00
|
|
|
if( GAMESTATE->GetCurrentStyle()->m_StyleType == ONE_PLAYER_TWO_SIDES &&
|
2004-12-20 06:25:59 +00:00
|
|
|
pn != 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.
|
2006-08-05 02:38:05 +00:00
|
|
|
if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind == 0 )
|
2005-08-26 17:39:53 +00:00
|
|
|
{
|
|
|
|
|
if( m_pCombo )
|
|
|
|
|
m_pCombo->Draw();
|
|
|
|
|
}
|
2002-07-31 19:40:40 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pAttackDisplay )
|
|
|
|
|
m_pAttackDisplay->Draw();
|
2003-11-26 08:25:45 +00:00
|
|
|
|
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
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
float fTilt = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fPerspectiveTilt;
|
|
|
|
|
float fSkew = m_pPlayerState->m_PlayerOptions.GetCurrent().m_fSkew;
|
|
|
|
|
bool bReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().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();
|
|
|
|
|
|
2004-11-07 10:38:20 +00:00
|
|
|
float fCenterY = this->GetY()+(GRAY_ARROWS_Y_STANDARD+GRAY_ARROWS_Y_REVERSE)/2;
|
2002-11-15 08:02:27 +00:00
|
|
|
|
2005-12-29 18:33:03 +00:00
|
|
|
DISPLAY->LoadMenuPerspective( 45, SCREEN_WIDTH, SCREEN_HEIGHT, SCALE(fSkew,0.f,1.f,this->GetX(),SCREEN_CENTER_X), fCenterY );
|
2002-11-15 08:02:27 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
{
|
|
|
|
|
float fOriginalY = m_pNoteField->GetY();
|
2002-02-24 10:31:20 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
float fTiltDegrees = SCALE(fTilt,-1.f,+1.f,+30,-30) * (bReverse?-1:1);
|
2003-09-21 18:07:29 +00:00
|
|
|
|
2006-08-05 02:38:05 +00:00
|
|
|
float fZoom = SCALE( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fEffects[PlayerOptions::EFFECT_TINY], 0.f, 1.f, 1.f, 0.5f );
|
2006-04-05 02:59:16 +00:00
|
|
|
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 );
|
|
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
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);
|
2003-09-21 18:07:29 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
m_pNoteField->SetY( fOriginalY + fYOffset );
|
2006-04-05 02:59:16 +00:00
|
|
|
m_pNoteField->SetZoom( fZoom );
|
2005-08-26 17:39:53 +00:00
|
|
|
m_pNoteField->SetRotationX( fTiltDegrees );
|
|
|
|
|
m_pNoteField->Draw();
|
2002-02-24 10:31:20 +00:00
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
m_pNoteField->SetY( fOriginalY );
|
|
|
|
|
}
|
2003-09-21 02:36:28 +00:00
|
|
|
|
|
|
|
|
DISPLAY->CameraPopMatrix();
|
2003-09-21 18:07:29 +00:00
|
|
|
DISPLAY->PopMatrix();
|
|
|
|
|
|
2002-02-24 10:31:20 +00:00
|
|
|
|
2004-11-06 20:36:04 +00:00
|
|
|
if( !(bool)TAP_JUDGMENTS_UNDER_FIELD )
|
2003-11-25 06:45:06 +00:00
|
|
|
DrawTapJudgments();
|
|
|
|
|
|
2005-10-05 22:05:47 +00:00
|
|
|
if( !(bool)HOLD_JUDGMENTS_UNDER_FIELD )
|
2003-11-25 06:45:06 +00:00
|
|
|
DrawHoldJudgments();
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::DrawTapJudgments()
|
2003-11-25 06:45:06 +00:00
|
|
|
{
|
2006-08-05 02:38:05 +00:00
|
|
|
if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind > 0 )
|
2004-01-15 03:19:41 +00:00
|
|
|
return;
|
|
|
|
|
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_sprJudgmentFrame.IsLoaded() )
|
|
|
|
|
m_sprJudgmentFrame->Draw();
|
|
|
|
|
if( m_pJudgment )
|
|
|
|
|
m_pJudgment->Draw();
|
2003-11-25 06:45:06 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::DrawHoldJudgments()
|
2003-11-25 06:45:06 +00:00
|
|
|
{
|
2006-08-05 02:38:05 +00:00
|
|
|
if( m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind > 0 )
|
2004-01-15 03:19:41 +00:00
|
|
|
return;
|
|
|
|
|
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int c=0; c<m_NoteData.GetNumTracks(); c++ )
|
2005-10-05 22:21:27 +00:00
|
|
|
m_vHoldJudgment[c]->Draw();
|
2001-12-28 10:15:59 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-25 19:39:31 +00:00
|
|
|
int Player::GetClosestNoteDirectional( int col, int iStartRow, int iEndRow, bool bAllowGraded, bool bForward ) const
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2005-01-25 19:39:31 +00:00
|
|
|
NoteData::const_iterator begin, end;
|
|
|
|
|
m_NoteData.GetTapNoteRange( col, iStartRow, iEndRow, begin, end );
|
|
|
|
|
|
|
|
|
|
if( !bForward )
|
|
|
|
|
swap( begin, end );
|
|
|
|
|
|
|
|
|
|
while( begin != end )
|
2001-11-04 19:34:28 +00:00
|
|
|
{
|
2005-01-25 19:39:31 +00:00
|
|
|
if( !bForward )
|
|
|
|
|
--begin;
|
|
|
|
|
|
|
|
|
|
/* Is this the row we want? */
|
|
|
|
|
do {
|
|
|
|
|
const TapNote &tn = begin->second;
|
2005-01-22 02:10:54 +00:00
|
|
|
if( tn.type == TapNote::empty )
|
2004-10-25 00:29:34 +00:00
|
|
|
break;
|
2005-10-08 00:57:40 +00:00
|
|
|
if( !bAllowGraded && tn.result.tns != TNS_None )
|
2004-10-25 00:29:34 +00:00
|
|
|
break;
|
2005-01-25 19:39:31 +00:00
|
|
|
|
|
|
|
|
return begin->first;
|
2004-11-08 07:14:47 +00:00
|
|
|
} while(0);
|
2002-05-29 09:47:24 +00:00
|
|
|
|
2005-01-25 19:39:31 +00:00
|
|
|
if( bForward )
|
|
|
|
|
++begin;
|
2002-05-29 09:47:24 +00:00
|
|
|
}
|
2005-01-25 19:39:31 +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. */
|
2005-01-25 19:39:31 +00:00
|
|
|
int Player::GetClosestNote( int col, int iNoteRow, int iMaxRowsAhead, int iMaxRowsBehind, bool bAllowGraded ) const
|
2003-02-09 03:33:36 +00:00
|
|
|
{
|
2004-10-25 00:29:34 +00:00
|
|
|
// Start at iIndexStartLookingAt and search outward.
|
2005-01-25 19:39:31 +00:00
|
|
|
int iNextIndex = GetClosestNoteDirectional( col, iNoteRow, iNoteRow+iMaxRowsAhead, bAllowGraded, true );
|
|
|
|
|
int iPrevIndex = GetClosestNoteDirectional( col, iNoteRow-iMaxRowsBehind, iNoteRow, 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. */
|
2005-01-25 19:39:31 +00:00
|
|
|
if( abs(iNoteRow-iNextIndex) > abs(iNoteRow-iPrevIndex) )
|
2004-10-25 00:29:34 +00:00
|
|
|
return iPrevIndex;
|
2005-01-25 19:39:31 +00:00
|
|
|
else
|
|
|
|
|
return iNextIndex;
|
2003-02-09 02:07:30 +00:00
|
|
|
}
|
|
|
|
|
|
2006-08-14 09:09:24 +00:00
|
|
|
void Player::Step( int col, int row, const RageTimer &tm, bool bHeld, bool bRelease )
|
2003-02-09 02:07:30 +00:00
|
|
|
{
|
2006-07-28 09:05:58 +00:00
|
|
|
// If we're playing on oni and we've died, do nothing.
|
2006-08-05 02:38:05 +00:00
|
|
|
if( GAMESTATE->m_SongOptions.GetCurrent().m_LifeType == SongOptions::LIFE_BATTERY && m_pPlayerStageStats && m_pPlayerStageStats->bFailed )
|
2006-07-28 09:05:58 +00:00
|
|
|
return;
|
2006-07-28 07:12:27 +00:00
|
|
|
|
2006-07-25 06:17:14 +00:00
|
|
|
DEBUG_ASSERT_M( col >= 0 && col <= m_NoteData.GetNumTracks(), ssprintf("%i, %i", col, m_NoteData.GetNumTracks()) );
|
2004-12-22 07:31:27 +00:00
|
|
|
|
2005-10-02 17:55:47 +00:00
|
|
|
|
2006-07-28 09:05:58 +00:00
|
|
|
const float fPositionSeconds = GAMESTATE->m_fMusicSeconds - tm.Ago();
|
2005-10-02 17:55:47 +00:00
|
|
|
const float fSongBeat = GAMESTATE->m_pCurSong ? GAMESTATE->m_pCurSong->GetBeatFromElapsedTime( fPositionSeconds ) : GAMESTATE->m_fSongBeat;
|
2006-07-28 09:05:58 +00:00
|
|
|
const int iSongRow = row == -1 ? BeatToNoteRow( fSongBeat ) : row;
|
|
|
|
|
|
2006-08-14 09:57:18 +00:00
|
|
|
if( !bRelease )
|
2005-10-02 17:55:47 +00:00
|
|
|
{
|
|
|
|
|
// Update roll life
|
|
|
|
|
// Let's not check the whole array every time.
|
|
|
|
|
// Instead, only check 1 beat back. Even 1 is overkill.
|
|
|
|
|
// Just update the life here and let Update judge the roll.
|
|
|
|
|
const int iStartCheckingAt = max( 0, iSongRow-BeatToNoteRow(1) );
|
|
|
|
|
NoteData::iterator begin, end;
|
|
|
|
|
m_NoteData.GetTapNoteRangeInclusive( col, iStartCheckingAt, iSongRow+1, begin, end );
|
|
|
|
|
for( ; begin != end; ++begin )
|
|
|
|
|
{
|
|
|
|
|
TapNote &tn = begin->second;
|
|
|
|
|
if( tn.type != TapNote::hold_head )
|
|
|
|
|
continue;
|
2006-07-28 09:05:58 +00:00
|
|
|
const int iRow = begin->first;
|
2005-10-02 17:55:47 +00:00
|
|
|
|
|
|
|
|
HoldNoteScore hns = tn.HoldResult.hns;
|
2005-10-08 00:57:40 +00:00
|
|
|
if( hns != HNS_None ) // if this HoldNote already has a result
|
2005-10-02 17:55:47 +00:00
|
|
|
continue; // we don't need to update the logic for this one
|
|
|
|
|
|
|
|
|
|
// if they got a bad score or haven't stepped on the corresponding tap yet
|
|
|
|
|
const TapNoteScore tns = tn.result.tns;
|
2005-10-08 00:57:40 +00:00
|
|
|
const bool bSteppedOnTapNote = tns != TNS_None && tns != TNS_Miss; // did they step on the start of this roll?
|
2006-07-28 09:05:58 +00:00
|
|
|
const int iEndRow = iRow + tn.iDuration;
|
2005-10-02 17:55:47 +00:00
|
|
|
|
|
|
|
|
if( bSteppedOnTapNote && tn.HoldResult.fLife != 0 )
|
|
|
|
|
{
|
|
|
|
|
/* This hold note is not judged and we stepped on its head. Update iLastHeldRow.
|
|
|
|
|
* Do this even if we're a little beyond the end of the hold note, to make sure
|
|
|
|
|
* iLastHeldRow is clamped to iEndRow if the hold note is held all the way. */
|
|
|
|
|
tn.HoldResult.iLastHeldRow = min( iSongRow, iEndRow );
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If the song beat is in the range of this hold:
|
|
|
|
|
if( iRow <= iSongRow && iRow <= iEndRow )
|
|
|
|
|
{
|
|
|
|
|
switch( tn.subType )
|
|
|
|
|
{
|
|
|
|
|
case TapNote::hold_head_hold:
|
|
|
|
|
// this is handled in Update
|
|
|
|
|
break;
|
|
|
|
|
case TapNote::hold_head_roll:
|
2006-03-29 01:27:04 +00:00
|
|
|
if( bSteppedOnTapNote )
|
2005-10-02 17:55:47 +00:00
|
|
|
{
|
|
|
|
|
// Increase life
|
|
|
|
|
tn.HoldResult.fLife = 1;
|
|
|
|
|
|
|
|
|
|
bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->iCurCombo>(int)BRIGHT_GHOST_COMBO_THRESHOLD;
|
|
|
|
|
if( m_pNoteField )
|
2005-10-08 00:57:40 +00:00
|
|
|
m_pNoteField->DidHoldNote( col, HNS_Held, bBright );
|
2005-10-02 17:55:47 +00:00
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2005-02-15 07:09:07 +00:00
|
|
|
//
|
2005-04-06 01:28:49 +00:00
|
|
|
// Count calories for this step, unless we're being called because a button is
|
2006-08-14 09:57:18 +00:00
|
|
|
// held over a mine or being released.
|
2005-02-15 07:09:07 +00:00
|
|
|
//
|
2006-08-14 09:57:18 +00:00
|
|
|
if( m_pPlayerStageStats && m_pPlayerState && !bHeld && !bRelease )
|
2005-02-15 07:09:07 +00:00
|
|
|
{
|
2005-02-15 09:24:48 +00:00
|
|
|
// TODO: remove use of PlayerNumber
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
2006-07-28 09:05:58 +00:00
|
|
|
Profile *pProfile = PROFILEMAN->GetProfile( pn );
|
2005-02-15 09:24:48 +00:00
|
|
|
if( pProfile )
|
|
|
|
|
{
|
2005-06-15 01:59:40 +00:00
|
|
|
int iNumTracksHeld = 0;
|
|
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
2005-02-15 09:24:48 +00:00
|
|
|
{
|
2006-09-13 01:54:16 +00:00
|
|
|
GameInput GameI = GAMESTATE->m_pCurStyle->StyleInputToGameInput( t, pn );
|
2006-09-13 01:38:10 +00:00
|
|
|
const float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI );
|
2006-03-01 17:41:29 +00:00
|
|
|
if( fSecsHeld > 0 && fSecsHeld < JUMP_WINDOW_SECONDS )
|
2005-06-15 01:59:40 +00:00
|
|
|
iNumTracksHeld++;
|
|
|
|
|
}
|
2005-02-15 09:24:48 +00:00
|
|
|
|
2005-06-15 01:59:40 +00:00
|
|
|
float fCals = 0;
|
|
|
|
|
switch( iNumTracksHeld )
|
|
|
|
|
{
|
|
|
|
|
case 0:
|
|
|
|
|
// autoplay is on, or this is a computer player
|
|
|
|
|
iNumTracksHeld = 1;
|
|
|
|
|
// fall through
|
|
|
|
|
default:
|
2005-02-15 09:24:48 +00:00
|
|
|
{
|
2006-04-05 02:59:16 +00:00
|
|
|
float fCalsFor100Lbs = SCALE( iNumTracksHeld, 1, 2, 0.023f, 0.077f );
|
|
|
|
|
float fCalsFor200Lbs = SCALE( iNumTracksHeld, 1, 2, 0.041f, 0.133f );
|
2005-06-15 01:59:40 +00:00
|
|
|
fCals = SCALE( pProfile->GetCalculatedWeightPounds(), 100.f, 200.f, fCalsFor100Lbs, fCalsFor200Lbs );
|
2005-02-15 09:24:48 +00:00
|
|
|
}
|
2005-06-15 01:59:40 +00:00
|
|
|
break;
|
2005-02-15 09:24:48 +00:00
|
|
|
}
|
2005-06-15 01:59:40 +00:00
|
|
|
|
|
|
|
|
m_pPlayerStageStats->fCaloriesBurned += fCals;
|
2005-02-15 09:24:48 +00:00
|
|
|
}
|
2005-02-15 07:09:07 +00:00
|
|
|
}
|
|
|
|
|
|
2003-02-09 02:07:30 +00:00
|
|
|
|
2005-06-16 21:36:58 +00:00
|
|
|
|
|
|
|
|
|
2003-11-15 23:19:13 +00:00
|
|
|
//
|
|
|
|
|
// Check for step on a TapNote
|
|
|
|
|
//
|
2006-07-28 09:05:58 +00:00
|
|
|
/* XXX: This seems wrong. If a player steps twice quickly and two notes are close together in the same column then
|
|
|
|
|
* it is possible for the two notes to be graded out of order. Two possible fixes:
|
|
|
|
|
* 1. Adjust the fSongBeat (or the resulting note row) backward by iStepSearchRows and search forward two iStepSearchRows
|
|
|
|
|
* lengths, disallowing graded. This doesn't seem right because if a second note has passed, an earlier one should not
|
|
|
|
|
* be graded.
|
|
|
|
|
* 2. Clamp the distance searched backward to the previous row graded.
|
|
|
|
|
* Either option would fundamentally change the grading of two quick notes "jack hammers." Hmm.
|
|
|
|
|
*/
|
2006-08-05 02:38:05 +00:00
|
|
|
const int iStepSearchRows = BeatToNoteRow( StepSearchDistance * GAMESTATE->m_fCurBPS * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate );
|
2006-07-28 09:05:58 +00:00
|
|
|
int iIndexOverlappingNote = row == -1 ? GetClosestNote( col, BeatToNoteRow(fSongBeat), iStepSearchRows, iStepSearchRows, false ) : row;
|
2003-02-09 02:07:30 +00:00
|
|
|
|
2004-07-02 01:24:11 +00:00
|
|
|
// calculate TapNoteScore
|
2005-10-08 00:57:40 +00:00
|
|
|
TapNoteScore score = TNS_None;
|
2004-07-02 01:24:11 +00:00
|
|
|
|
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
|
2006-07-28 09:05:58 +00:00
|
|
|
float fNoteOffset = 0.0f;
|
|
|
|
|
|
2006-07-28 19:40:14 +00:00
|
|
|
if( row == -1 )
|
2005-02-07 06:02:15 +00:00
|
|
|
{
|
|
|
|
|
const float fStepBeat = NoteRowToBeat( iIndexOverlappingNote );
|
|
|
|
|
const float fStepSeconds = GAMESTATE->m_pCurSong->GetElapsedTimeFromBeat(fStepBeat);
|
2003-02-17 05:25:20 +00:00
|
|
|
|
2005-02-07 06:02:15 +00:00
|
|
|
/* We actually stepped on the note this long ago: */
|
|
|
|
|
const float fTimeSinceStep = tm.Ago();
|
2004-01-18 06:53:25 +00:00
|
|
|
|
2005-02-07 06:02:15 +00:00
|
|
|
/* GAMESTATE->m_fMusicSeconds is the music time as of GAMESTATE->m_LastBeatUpdate. Figure
|
|
|
|
|
* out what the music time is as of now. */
|
2006-08-05 02:38:05 +00:00
|
|
|
const float fCurrentMusicSeconds = GAMESTATE->m_fMusicSeconds + (GAMESTATE->m_LastBeatUpdate.Ago()*GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate);
|
2004-01-18 06:53:25 +00:00
|
|
|
|
2005-02-07 06:02:15 +00:00
|
|
|
/* ... which means it happened at this point in the music: */
|
2006-08-05 02:38:05 +00:00
|
|
|
const float fMusicSeconds = fCurrentMusicSeconds - fTimeSinceStep * GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
2003-02-17 05:25:20 +00:00
|
|
|
|
2005-02-07 06:02:15 +00:00
|
|
|
// The offset from the actual step in seconds:
|
2006-08-05 02:38:05 +00:00
|
|
|
fNoteOffset = (fStepSeconds - fMusicSeconds) / GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; // account for music rate
|
2005-02-07 06:02:15 +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
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
const float fSecondsFromExact = fabsf( fNoteOffset );
|
2002-06-24 22:04:31 +00:00
|
|
|
|
2006-07-28 09:05:58 +00:00
|
|
|
NoteData::iterator iter = m_NoteData.FindTapNote( col, iIndexOverlappingNote );
|
|
|
|
|
|
|
|
|
|
DEBUG_ASSERT( iter!= m_NoteData.end(col) );
|
|
|
|
|
TapNote &tn = iter->second;
|
2003-08-10 21:27:54 +00:00
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
switch( m_pPlayerState->m_PlayerController )
|
2003-04-07 21:24:14 +00:00
|
|
|
{
|
2004-09-12 05:56:24 +00:00
|
|
|
case PC_HUMAN:
|
2006-08-14 12:16:31 +00:00
|
|
|
switch( tn.type )
|
2003-09-08 15:30:26 +00:00
|
|
|
{
|
2006-08-14 12:16:31 +00:00
|
|
|
case TapNote::mine:
|
|
|
|
|
// Stepped too close to mine?
|
|
|
|
|
if( !bRelease && fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_Mine) )
|
|
|
|
|
score = TNS_HitMine;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case TapNote::attack:
|
|
|
|
|
if( !bRelease && fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_Attack) && !tn.result.bHidden )
|
|
|
|
|
score = TNS_W2; /* sentinel */
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
if( (tn.type == TapNote::lift) == bRelease )
|
2006-08-14 09:57:18 +00:00
|
|
|
{
|
|
|
|
|
if( fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_W1) ) score = TNS_W1;
|
|
|
|
|
else if( fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_W2) ) score = TNS_W2;
|
|
|
|
|
else if( fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_W3) ) score = TNS_W3;
|
|
|
|
|
else if( fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_W4) ) score = TNS_W4;
|
|
|
|
|
else if( fSecondsFromExact <= ADJUSTED_WINDOW_SECONDS(TW_W5) ) score = TNS_W5;
|
|
|
|
|
}
|
2006-08-14 12:16:31 +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:
|
2005-02-07 04:43:39 +00:00
|
|
|
score = PlayerAI::GetTapNoteScore( m_pPlayerState );
|
2003-09-08 15:30:26 +00:00
|
|
|
|
2006-07-28 09:05:58 +00:00
|
|
|
/* XXX: This doesn't make sense. Step should only be called in autoplay for hit notes */
|
|
|
|
|
#if 0
|
2005-10-09 04:30:59 +00:00
|
|
|
// GetTapNoteScore always returns TNS_W1 in autoplay.
|
2005-06-16 21:41:51 +00:00
|
|
|
// If the step is far away, don't judge it.
|
|
|
|
|
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY &&
|
2005-10-09 04:30:59 +00:00
|
|
|
fSecondsFromExact > ADJUSTED_WINDOW_SECONDS(TW_W5) )
|
2005-10-02 17:55:47 +00:00
|
|
|
{
|
2005-10-08 00:57:40 +00:00
|
|
|
score = TNS_None;
|
2005-10-02 17:55:47 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2006-07-28 09:05:58 +00:00
|
|
|
#endif
|
2005-06-16 21:41:51 +00:00
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
// TRICKY: We're asking the AI to judge mines. consider TNS_W4 and below
|
2004-01-11 22:29:18 +00:00
|
|
|
// 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 );
|
2005-10-09 04:30:59 +00:00
|
|
|
TapNoteScore get_to_avoid = bTapsOnRow ? TNS_W3 : TNS_W4;
|
2004-01-11 23:31:37 +00:00
|
|
|
|
|
|
|
|
if( score >= get_to_avoid )
|
2004-01-11 22:29:18 +00:00
|
|
|
return; // avoided
|
|
|
|
|
else
|
2005-10-08 00:57:40 +00:00
|
|
|
score = TNS_HitMine;
|
2004-01-11 22:29:18 +00:00
|
|
|
}
|
2005-02-07 03:54:45 +00:00
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
if( tn.type == TapNote::attack && score > TNS_W4 )
|
|
|
|
|
score = TNS_W2; /* sentinel */
|
2005-02-07 03:54:45 +00:00
|
|
|
|
|
|
|
|
/* AI will generate misses here. Don't handle a miss like a regular note because
|
|
|
|
|
* we want the judgment animation to appear delayed. Instead, return early if
|
|
|
|
|
* AI generated a miss, and let UpdateMissedTapNotesOlderThan() detect and handle the
|
|
|
|
|
* misses. */
|
2005-10-08 00:57:40 +00:00
|
|
|
if( score == TNS_Miss )
|
2005-02-07 03:54:45 +00:00
|
|
|
return;
|
|
|
|
|
|
2005-05-12 07:52:53 +00:00
|
|
|
// Put some small, random amount in fNoteOffset so that demonstration
|
2006-07-28 09:05:58 +00:00
|
|
|
// show a mix of late and early.
|
2005-05-12 07:52:53 +00:00
|
|
|
fNoteOffset = randomf( -0.1f, 0.1f );
|
|
|
|
|
|
2005-02-07 03:45:25 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
ASSERT(0);
|
2005-10-08 00:57:40 +00:00
|
|
|
score = TNS_None;
|
2005-02-07 03:45:25 +00:00
|
|
|
break;
|
|
|
|
|
}
|
2004-01-11 22:29:18 +00:00
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
if( tn.type == TapNote::attack && score == TNS_W2 )
|
2005-02-07 03:54:45 +00:00
|
|
|
{
|
2005-10-08 00:57:40 +00:00
|
|
|
score = TNS_None; // don't score this as anything
|
2005-02-07 03:54:45 +00:00
|
|
|
|
|
|
|
|
m_soundAttackLaunch.Play();
|
|
|
|
|
|
|
|
|
|
// put attack in effect
|
|
|
|
|
Attack attack(
|
|
|
|
|
ATTACK_LEVEL_1,
|
|
|
|
|
-1, // now
|
|
|
|
|
tn.fAttackDurationSeconds,
|
|
|
|
|
tn.sAttackModifiers,
|
|
|
|
|
true,
|
|
|
|
|
false
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// TODO: Remove use of PlayerNumber
|
2006-01-13 05:47:44 +00:00
|
|
|
PlayerNumber pnToAttack = OPPOSITE_PLAYER[m_pPlayerState->m_PlayerNumber];
|
|
|
|
|
PlayerState *pPlayerStateToAttack = GAMESTATE->m_pPlayerState[pnToAttack];
|
|
|
|
|
pPlayerStateToAttack->LaunchAttack( attack );
|
2005-02-07 03:54:45 +00:00
|
|
|
|
|
|
|
|
// remove all TapAttacks on this row
|
|
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
|
|
|
|
{
|
2006-07-23 21:44:35 +00:00
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( t, iIndexOverlappingNote );
|
2005-02-07 03:54:45 +00:00
|
|
|
if( tn.type == TapNote::attack )
|
2006-07-23 21:35:41 +00:00
|
|
|
HideNote( t, iIndexOverlappingNote );
|
2005-02-07 03:54:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController == PC_HUMAN && score >= TNS_W3 )
|
2006-02-06 01:23:25 +00:00
|
|
|
AdjustSync::HandleAutosync( fNoteOffset );
|
2005-02-07 06:19:43 +00:00
|
|
|
|
2005-02-07 06:20:22 +00:00
|
|
|
// Do game-specific and mode-specific score mapping.
|
2005-02-07 06:05:42 +00:00
|
|
|
score = GAMESTATE->GetCurrentGame()->MapTapNoteScore( score );
|
2005-10-09 04:30:59 +00:00
|
|
|
if( score == TNS_W1 && !GAMESTATE->ShowW1() )
|
|
|
|
|
score = TNS_W2;
|
2004-06-27 01:16:00 +00:00
|
|
|
|
2006-03-17 02:23:45 +00:00
|
|
|
tn.result.tns = score;
|
2003-08-01 00:12:10 +00:00
|
|
|
|
2006-03-17 02:23:45 +00:00
|
|
|
if( score != TNS_None )
|
|
|
|
|
tn.result.fTapNoteOffset = -fNoteOffset;
|
2005-05-23 05:21:40 +00:00
|
|
|
|
2006-07-22 09:01:19 +00:00
|
|
|
PlayerNumber pn = tn.pn == PLAYER_INVALID ? m_pPlayerState->m_PlayerNumber : tn.pn;
|
2006-03-17 02:23:45 +00:00
|
|
|
m_LastTapNoteScore = score;
|
2006-07-23 21:12:39 +00:00
|
|
|
if( GAMESTATE->GetCurrentGame()->m_bCountNotesSeparately )
|
|
|
|
|
{
|
|
|
|
|
if( tn.type != TapNote::mine )
|
|
|
|
|
{
|
2006-08-05 02:38:05 +00:00
|
|
|
const bool bBlind = (m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind != 0);
|
2006-07-23 21:12:39 +00:00
|
|
|
// XXX This is the wrong combo for shared players. STATSMAN->m_CurStageStats.m_Player[pn] might work but could be wrong.
|
|
|
|
|
const bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->iCurCombo > int(BRIGHT_GHOST_COMBO_THRESHOLD) || bBlind;
|
|
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->DidTapNote( col, score, bBright );
|
|
|
|
|
if( score >= TNS_W3 || bBlind )
|
2006-07-23 21:35:41 +00:00
|
|
|
HideNote( col, iIndexOverlappingNote );
|
2006-07-23 21:12:39 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if( NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iIndexOverlappingNote, pn) )
|
|
|
|
|
{
|
2006-07-22 09:01:19 +00:00
|
|
|
FlashGhostRow( iIndexOverlappingNote, pn );
|
2006-07-23 21:12:39 +00:00
|
|
|
}
|
2004-10-23 17:43:49 +00:00
|
|
|
}
|
2004-10-25 00:46:08 +00:00
|
|
|
|
2006-08-14 09:57:18 +00:00
|
|
|
if( score != TNS_None )
|
2004-10-25 00:46:08 +00:00
|
|
|
{
|
2005-06-16 21:36:58 +00:00
|
|
|
/* Search for keyed sounds separately. If we can't find a nearby note, search
|
|
|
|
|
* backwards indefinitely, and ignore grading. */
|
2006-07-28 09:05:58 +00:00
|
|
|
/* XXX: This isn't quite right. As per the above XXX for iIndexOverlappingNote, if iIndexOverlappingNote
|
|
|
|
|
* is set to a previous note, the keysound could have changed and this would cause the wrong one to play,
|
|
|
|
|
* in essence playing two sounds in the opposite order. Maybe this should always perform the search. Still,
|
|
|
|
|
* even that doesn't seem quite right since it would then play the same (new) keysound twice which would
|
|
|
|
|
* sound wrong even though the notes were judged as being correct, above. Fixing the above problem would
|
|
|
|
|
* fix this one as well. */
|
2005-06-16 21:36:58 +00:00
|
|
|
if( iIndexOverlappingNote == -1 )
|
|
|
|
|
iIndexOverlappingNote = GetClosestNote( col, BeatToNoteRow(fSongBeat),
|
2006-07-17 00:54:19 +00:00
|
|
|
iStepSearchRows, MAX_NOTE_ROW, true );
|
2005-06-16 21:36:58 +00:00
|
|
|
if( iIndexOverlappingNote != -1 )
|
|
|
|
|
{
|
2006-07-23 21:44:35 +00:00
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( col, iIndexOverlappingNote );
|
2006-08-14 10:17:48 +00:00
|
|
|
if( tn.iKeysoundIndex >= 0 && tn.iKeysoundIndex < (int) m_vKeysounds.size() )
|
2005-06-16 21:36:58 +00:00
|
|
|
m_vKeysounds[tn.iKeysoundIndex].Play();
|
|
|
|
|
}
|
2004-10-25 00:46:08 +00:00
|
|
|
}
|
2006-08-14 09:57:18 +00:00
|
|
|
// XXX:
|
|
|
|
|
if( !bRelease )
|
|
|
|
|
{
|
|
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->Step( col, score );
|
|
|
|
|
MESSAGEMAN->Broadcast( m_sMessageToSendOnStep );
|
|
|
|
|
}
|
2001-11-04 19:34:28 +00:00
|
|
|
}
|
2001-11-03 10:52:42 +00:00
|
|
|
|
2006-07-21 08:01:28 +00:00
|
|
|
static bool Unjudged( const TapNote &tn )
|
|
|
|
|
{
|
2006-08-16 13:27:59 +00:00
|
|
|
if( tn.result.tns != TNS_None )
|
|
|
|
|
return false;
|
|
|
|
|
switch( tn.type )
|
|
|
|
|
{
|
|
|
|
|
case TapNote::tap:
|
|
|
|
|
case TapNote::hold_head:
|
|
|
|
|
case TapNote::mine:
|
|
|
|
|
case TapNote::lift:
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
2006-07-21 08:01:28 +00:00
|
|
|
}
|
2001-11-04 19:34:28 +00:00
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::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;
|
2006-03-28 08:58:30 +00:00
|
|
|
GAMESTATE->m_pCurSong->m_Timing.GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze );
|
2003-09-19 20:07:58 +00:00
|
|
|
|
|
|
|
|
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
|
|
|
|
2003-02-09 04:05:31 +00:00
|
|
|
|
2006-07-21 04:13:31 +00:00
|
|
|
// m_iRowLastJudged and m_iMineRowLastJudged have already been judged.
|
|
|
|
|
const int iStartIndex = min( m_iRowLastJudged, m_iMineRowLastJudged ) + 1;
|
2006-07-18 00:26:37 +00:00
|
|
|
bool bMisses = false;
|
2006-07-21 04:07:19 +00:00
|
|
|
|
2006-07-21 08:01:28 +00:00
|
|
|
NoteData::all_tracks_iterator iter = m_NoteData.GetTapNoteRangeAllTracks( iStartIndex, iMissIfOlderThanThisIndex-1, Unjudged );
|
|
|
|
|
|
|
|
|
|
for( ; !iter.IsAtEnd(); ++iter )
|
2001-12-19 01:50:57 +00:00
|
|
|
{
|
2006-07-21 08:01:28 +00:00
|
|
|
TapNote &tn = *iter;
|
|
|
|
|
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != m_pPlayerState->m_PlayerNumber )
|
|
|
|
|
continue;
|
|
|
|
|
if( tn.type == TapNote::mine )
|
2002-02-03 20:45:08 +00:00
|
|
|
{
|
2006-07-21 08:01:28 +00:00
|
|
|
tn.result.tns = TNS_AvoidMine;
|
2006-07-21 04:13:31 +00:00
|
|
|
|
2006-07-21 08:01:28 +00:00
|
|
|
/* The only real way to tell if a mine has been scored is if it has disappeared
|
|
|
|
|
* but this only works for hit mines so update the scores for avoided mines here. */
|
|
|
|
|
if( m_pPrimaryScoreKeeper )
|
|
|
|
|
m_pPrimaryScoreKeeper->HandleTapScore( tn );
|
|
|
|
|
if( m_pSecondaryScoreKeeper )
|
|
|
|
|
m_pSecondaryScoreKeeper->HandleTapScore( tn );
|
2002-10-02 05:42:59 +00:00
|
|
|
}
|
2006-07-21 08:01:28 +00:00
|
|
|
else
|
|
|
|
|
{
|
2006-07-18 00:26:37 +00:00
|
|
|
bMisses = true;
|
2006-07-21 08:01:28 +00:00
|
|
|
tn.result.tns = TNS_Miss;
|
|
|
|
|
}
|
2001-12-19 01:50:57 +00:00
|
|
|
}
|
2006-07-21 08:01:28 +00:00
|
|
|
|
2006-07-18 00:26:37 +00:00
|
|
|
if( bMisses )
|
2006-04-21 00:37:27 +00:00
|
|
|
SetJudgment( TNS_Miss, false );
|
2001-11-04 19:34:28 +00:00
|
|
|
}
|
|
|
|
|
|
2006-07-21 08:01:28 +00:00
|
|
|
static bool MinesNotHidden( const TapNote &tn )
|
|
|
|
|
{
|
|
|
|
|
return tn.type == TapNote::mine && !tn.result.bHidden;
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-17 00:54:19 +00:00
|
|
|
void Player::UpdateJudgedRows()
|
|
|
|
|
{
|
2006-07-18 00:26:37 +00:00
|
|
|
const int iEndRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
|
2006-07-17 00:54:19 +00:00
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
2006-07-18 00:26:37 +00:00
|
|
|
bool bAllJudged = true;
|
2006-07-22 09:01:19 +00:00
|
|
|
const bool bSeparately = GAMESTATE->GetCurrentGame()->m_bCountNotesSeparately;
|
2006-07-17 00:54:19 +00:00
|
|
|
|
2006-07-18 00:26:37 +00:00
|
|
|
for( int iRow = m_iRowLastJudged+1; iRow <= iEndRow; ++iRow )
|
2006-07-17 00:54:19 +00:00
|
|
|
{
|
2006-07-18 00:26:37 +00:00
|
|
|
if( !NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRow, pn) )
|
|
|
|
|
{
|
|
|
|
|
bAllJudged = false;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if( bAllJudged )
|
|
|
|
|
++m_iRowLastJudged;
|
2006-07-22 09:01:19 +00:00
|
|
|
if( m_JudgedRows[iRow] )
|
2006-07-17 00:54:19 +00:00
|
|
|
continue;
|
2006-07-22 09:01:19 +00:00
|
|
|
const TapNoteResult &lastTNR = NoteDataWithScoring::LastTapNoteWithResult( m_NoteData, iRow, pn ).result;
|
|
|
|
|
|
|
|
|
|
if( lastTNR.tns < TNS_Miss )
|
|
|
|
|
continue;
|
|
|
|
|
if( bSeparately )
|
|
|
|
|
{
|
|
|
|
|
for( int iTrack = 0; iTrack < m_NoteData.GetNumTracks(); ++iTrack )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( iTrack, iRow );
|
|
|
|
|
if( tn.type == TapNote::empty || tn.type == TapNote::mine ) continue;
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn ) continue;
|
|
|
|
|
SetJudgment( tn.result.tns, tn.result.fTapNoteOffset < 0.0f );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
SetJudgment( lastTNR.tns, lastTNR.fTapNoteOffset < 0.0f );
|
|
|
|
|
}
|
|
|
|
|
HandleTapRowScore( iRow );
|
2006-07-17 00:54:19 +00:00
|
|
|
}
|
2006-07-20 08:15:10 +00:00
|
|
|
|
2006-07-21 08:01:28 +00:00
|
|
|
NoteData::all_tracks_iterator iter = m_NoteData.GetTapNoteRangeAllTracks( m_iMineRowLastJudged+1, iEndRow, MinesNotHidden );
|
|
|
|
|
|
|
|
|
|
bAllJudged = true;
|
|
|
|
|
for( ; !iter.IsAtEnd(); ++iter )
|
2006-07-20 08:15:10 +00:00
|
|
|
{
|
2006-07-21 08:01:28 +00:00
|
|
|
TapNote &tn = *iter;
|
|
|
|
|
|
|
|
|
|
switch( tn.result.tns )
|
2006-07-20 08:15:10 +00:00
|
|
|
{
|
2006-07-21 08:01:28 +00:00
|
|
|
case TNS_None: bAllJudged = false;
|
|
|
|
|
case TNS_AvoidMine: continue;
|
|
|
|
|
case TNS_HitMine: break;
|
|
|
|
|
DEFAULT_FAIL( tn.result.tns );
|
2006-07-20 08:15:10 +00:00
|
|
|
}
|
2006-07-21 08:01:28 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->DidTapNote( iter.Track(), tn.result.tns, false );
|
|
|
|
|
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn )
|
|
|
|
|
continue;
|
2006-08-14 10:17:48 +00:00
|
|
|
if( tn.iKeysoundIndex >= 0 && tn.iKeysoundIndex < (int) m_vKeysounds.size() )
|
2006-07-21 08:01:28 +00:00
|
|
|
m_vKeysounds[tn.iKeysoundIndex].Play();
|
|
|
|
|
else
|
|
|
|
|
m_soundMine.Play();
|
|
|
|
|
|
|
|
|
|
if( m_pLifeMeter )
|
|
|
|
|
m_pLifeMeter->ChangeLife( tn.result.tns );
|
|
|
|
|
if( m_pScoreDisplay )
|
|
|
|
|
m_pScoreDisplay->OnJudgment( tn.result.tns );
|
|
|
|
|
if( m_pSecondaryScoreDisplay )
|
|
|
|
|
m_pSecondaryScoreDisplay->OnJudgment( tn.result.tns );
|
|
|
|
|
if( m_pCombinedLifeMeter )
|
|
|
|
|
m_pCombinedLifeMeter->ChangeLife( pn, tn.result.tns );
|
|
|
|
|
|
2006-08-14 12:16:31 +00:00
|
|
|
// Make sure hit mines affect the dance points.
|
2006-07-21 08:01:28 +00:00
|
|
|
if( m_pPrimaryScoreKeeper )
|
|
|
|
|
m_pPrimaryScoreKeeper->HandleTapScore( tn );
|
|
|
|
|
if( m_pSecondaryScoreKeeper )
|
|
|
|
|
m_pSecondaryScoreKeeper->HandleTapScore( tn );
|
|
|
|
|
tn.result.bHidden = true;
|
|
|
|
|
// Subtract one to ensure that we have actually completed the row.
|
|
|
|
|
if( bAllJudged && iter.Row() - 1 > m_iMineRowLastJudged )
|
|
|
|
|
m_iMineRowLastJudged = iter.Row() - 1;
|
2006-07-20 08:15:10 +00:00
|
|
|
}
|
2006-07-21 08:01:28 +00:00
|
|
|
if( bAllJudged )
|
|
|
|
|
m_iMineRowLastJudged = iEndRow;
|
2006-07-17 00:54:19 +00:00
|
|
|
}
|
2001-11-04 19:34:28 +00:00
|
|
|
|
2006-07-22 09:01:19 +00:00
|
|
|
void Player::FlashGhostRow( int iRow, PlayerNumber pn )
|
|
|
|
|
{
|
|
|
|
|
TapNoteScore lastTNS = NoteDataWithScoring::LastTapNoteWithResult( m_NoteData, iRow, pn ).result.tns;
|
2006-08-05 02:38:05 +00:00
|
|
|
const bool bBlind = (m_pPlayerState->m_PlayerOptions.GetCurrent().m_fBlind != 0);
|
2006-07-22 09:01:19 +00:00
|
|
|
// XXX This is the wrong combo for shared players. STATSMAN->m_CurStageStats.m_Player[pn] might work but could be wrong.
|
|
|
|
|
const bool bBright = m_pPlayerStageStats && m_pPlayerStageStats->iCurCombo > int(BRIGHT_GHOST_COMBO_THRESHOLD) || bBlind;
|
|
|
|
|
|
|
|
|
|
for( int iTrack = 0; iTrack < m_NoteData.GetNumTracks(); ++iTrack )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( iTrack, iRow );
|
|
|
|
|
|
|
|
|
|
if( tn.type == TapNote::empty || tn.type == TapNote::mine )
|
|
|
|
|
continue;
|
|
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn )
|
|
|
|
|
continue;
|
|
|
|
|
if( m_pNoteField )
|
2006-07-23 21:12:39 +00:00
|
|
|
m_pNoteField->DidTapNote( iTrack, lastTNS, bBright );
|
|
|
|
|
if( lastTNS >= TNS_W3 || bBlind )
|
2006-07-23 21:35:41 +00:00
|
|
|
HideNote( iTrack, iRow );
|
2006-07-22 09:01:19 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2006-07-25 05:31:22 +00:00
|
|
|
void Player::CrossedRow( int iNoteRow, const RageTimer &now )
|
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.
|
2006-08-05 02:38:05 +00:00
|
|
|
if( m_pPlayerState->m_PlayerOptions.GetCurrent().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
|
2004-12-20 06:25:59 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
2003-07-13 00:34:30 +00:00
|
|
|
{
|
2004-10-23 17:43:49 +00:00
|
|
|
for( int t=0; t<m_NoteData.GetNumTracks(); t++ )
|
|
|
|
|
{
|
2006-07-23 21:44:35 +00:00
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( t, iNoteRow );
|
2005-10-08 00:57:40 +00:00
|
|
|
if( tn.type != TapNote::empty && tn.result.tns == TNS_None )
|
2005-10-01 00:18:13 +00:00
|
|
|
{
|
2006-08-14 09:09:24 +00:00
|
|
|
Step( t, iNoteRow, now, false, false );
|
2006-04-25 02:35:55 +00:00
|
|
|
if( m_pPlayerState->m_PlayerController == PC_AUTOPLAY )
|
|
|
|
|
STATSMAN->m_CurStageStats.bUsedAutoplay = true;
|
2005-10-01 00:18:13 +00:00
|
|
|
}
|
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
|
|
|
|
2006-07-25 05:31:22 +00:00
|
|
|
void Player::CrossedMineRow( int iNoteRow, const RageTimer &now )
|
2004-02-25 05:49:54 +00:00
|
|
|
{
|
2003-08-31 22:39:05 +00:00
|
|
|
// Hold the panel while crossing a mine will cause the mine to explode
|
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
|
|
|
{
|
2004-12-20 06:25:59 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
|
|
|
|
|
2006-09-13 01:54:16 +00:00
|
|
|
GameInput GameI = GAMESTATE->m_pCurStyle->StyleInputToGameInput( t, pn );
|
2004-02-26 07:27:54 +00:00
|
|
|
if( PREFSMAN->m_fPadStickSeconds > 0 )
|
|
|
|
|
{
|
2006-09-13 02:31:48 +00:00
|
|
|
float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI, m_pPlayerState->m_mp );
|
2004-02-26 07:27:54 +00:00
|
|
|
if( fSecsHeld >= PREFSMAN->m_fPadStickSeconds )
|
2006-09-13 08:17:56 +00:00
|
|
|
Step( t, -1, now - PREFSMAN->m_fPadStickSeconds, true, false );
|
2004-02-26 07:27:54 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2006-09-13 01:45:10 +00:00
|
|
|
bool bIsDown = INPUTMAPPER->IsBeingPressed( GameI, m_pPlayerState->m_mp );
|
2004-02-26 07:27:54 +00:00
|
|
|
if( bIsDown )
|
2006-08-14 09:09:24 +00:00
|
|
|
Step( t, iNoteRow, now, true, false );
|
2004-02-26 07:27:54 +00:00
|
|
|
}
|
2003-08-31 22:39:05 +00:00
|
|
|
}
|
|
|
|
|
}
|
2002-06-24 22:04:31 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::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;
|
2006-08-05 02:38:05 +00:00
|
|
|
iNewNoteRow = int( iNewNoteRow / m_pPlayerState->m_PlayerOptions.GetCurrent().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
|
|
|
{
|
2006-07-23 21:44:35 +00:00
|
|
|
const int iSwapWith = RandomInt( iNumOfTracks );
|
2003-10-24 22:57:47 +00:00
|
|
|
|
2004-10-07 04:20:13 +00:00
|
|
|
/* Only swap a tap and an empty. */
|
2006-07-24 04:42:08 +00:00
|
|
|
NoteData::iterator iter = m_NoteData.FindTapNote( t, iNewNoteRow );
|
|
|
|
|
if( iter == m_NoteData.end(t) || iter->second.type != TapNote::tap )
|
2004-10-07 04:20:13 +00:00
|
|
|
continue;
|
|
|
|
|
|
2006-07-24 04:42:08 +00:00
|
|
|
// Make sure this is empty.
|
|
|
|
|
if( m_NoteData.FindTapNote(iSwapWith, iNewNoteRow) != m_NoteData.end(iSwapWith) )
|
2004-10-07 04:20:13 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Make sure the destination row isn't in the middle of a hold. */
|
2005-07-22 00:14:24 +00:00
|
|
|
if( m_NoteData.IsHoldNoteAtRow(iSwapWith, iNoteRow) )
|
2004-10-07 04:20:13 +00:00
|
|
|
continue;
|
|
|
|
|
|
2006-07-24 04:42:08 +00:00
|
|
|
m_NoteData.SetTapNote( iSwapWith, iNewNoteRow, iter->second );
|
|
|
|
|
m_NoteData.RemoveTapNote( t, iter );
|
2003-05-04 13:58:45 +00:00
|
|
|
}
|
|
|
|
|
}
|
2002-06-24 22:04:31 +00:00
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::HandleTapRowScore( unsigned row )
|
2002-06-24 22:04:31 +00:00
|
|
|
{
|
2006-07-17 00:54:19 +00:00
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
|
|
|
|
const TapNote &lastTN = NoteDataWithScoring::LastTapNoteWithResult( m_NoteData, row, pn );
|
2006-07-10 05:15:52 +00:00
|
|
|
TapNoteScore scoreOfLastTap = lastTN.result.tns;
|
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
|
|
|
|
2006-07-14 04:19:30 +00:00
|
|
|
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
2003-04-21 04:11:01 +00:00
|
|
|
NoCheating = false;
|
2002-08-23 20:18:29 +00:00
|
|
|
// don't accumulate points if AutoPlay is on.
|
2004-12-20 06:25:59 +00:00
|
|
|
if( NoCheating && m_pPlayerState->m_PlayerController == 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. */
|
2005-01-08 15:34:22 +00:00
|
|
|
/* When is m_pPlayerStageStats NULL? Would it be cleaner to pass Player a dummy
|
|
|
|
|
* PlayerStageStats in this case, instead of having to carefully check for NULL
|
|
|
|
|
* every time we use it? -glenn */
|
2004-12-22 07:31:27 +00:00
|
|
|
int iDummy = 0;
|
2005-01-08 15:34:22 +00:00
|
|
|
int &iCurCombo = m_pPlayerStageStats ? m_pPlayerStageStats->iCurCombo : iDummy;
|
2004-12-29 19:12:25 +00:00
|
|
|
int &iCurMissCombo = m_pPlayerStageStats ? m_pPlayerStageStats->iCurMissCombo : iDummy;
|
2003-09-06 02:20:09 +00:00
|
|
|
switch( scoreOfLastTap )
|
|
|
|
|
{
|
2005-10-09 04:30:59 +00:00
|
|
|
case TNS_W1:
|
|
|
|
|
case TNS_W2:
|
|
|
|
|
case TNS_W3:
|
2004-12-29 19:12:25 +00:00
|
|
|
iCurMissCombo = 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
|
|
|
|
2005-10-08 00:57:40 +00:00
|
|
|
case TNS_Miss:
|
2004-12-29 19:12:25 +00:00
|
|
|
++iCurMissCombo;
|
2005-10-08 00:57:40 +00:00
|
|
|
m_LastTapNoteScore = TNS_Miss;
|
2005-02-07 04:12:51 +00:00
|
|
|
|
2005-10-09 04:30:59 +00:00
|
|
|
case TNS_W4:
|
|
|
|
|
case TNS_W5:
|
2005-01-08 15:34:22 +00:00
|
|
|
if( iCurCombo > 50 )
|
2003-09-06 02:20:09 +00:00
|
|
|
SCREENMAN->PostMessageToTopScreen( SM_ComboStopped, 0 );
|
2005-01-09 05:24:46 +00:00
|
|
|
iCurCombo = 0;
|
2003-09-06 02:20:09 +00:00
|
|
|
break;
|
2004-12-22 07:31:27 +00:00
|
|
|
|
2004-01-02 08:43:14 +00:00
|
|
|
default:
|
|
|
|
|
ASSERT( 0 );
|
2003-09-06 02:20:09 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-08 15:34:22 +00:00
|
|
|
/* The score keeper updates the hit combo. Remember the old combo for handling announcers. */
|
|
|
|
|
const int iOldCombo = iCurCombo;
|
2006-07-17 00:54:19 +00:00
|
|
|
|
|
|
|
|
for( int track = 0; track < m_NoteData.GetNumTracks(); ++track )
|
|
|
|
|
{
|
|
|
|
|
const TapNote &tn = m_NoteData.GetTapNote( track, row );
|
2006-07-20 07:31:39 +00:00
|
|
|
// Mines cannot be handled here.
|
|
|
|
|
if( tn.type == TapNote::empty || tn.type == TapNote::mine )
|
|
|
|
|
continue;
|
2006-07-17 00:54:19 +00:00
|
|
|
if( tn.pn != PLAYER_INVALID && tn.pn != pn )
|
|
|
|
|
continue;
|
|
|
|
|
if( m_pPrimaryScoreKeeper )
|
|
|
|
|
m_pPrimaryScoreKeeper->HandleTapScore( tn );
|
|
|
|
|
if( m_pSecondaryScoreKeeper )
|
|
|
|
|
m_pSecondaryScoreKeeper->HandleTapScore( tn );
|
|
|
|
|
}
|
|
|
|
|
|
2005-01-08 15:31:55 +00:00
|
|
|
if( m_pPrimaryScoreKeeper != NULL )
|
2006-07-14 04:19:30 +00:00
|
|
|
m_pPrimaryScoreKeeper->HandleTapRowScore( m_NoteData, row );
|
2005-01-08 15:31:55 +00:00
|
|
|
if( m_pSecondaryScoreKeeper != NULL )
|
2006-07-14 04:19:30 +00:00
|
|
|
m_pSecondaryScoreKeeper->HandleTapRowScore( m_NoteData, row );
|
2003-09-07 01:01:20 +00:00
|
|
|
|
2006-08-01 06:29:11 +00:00
|
|
|
if( m_pPlayerStageStats && m_pCombo )
|
2005-08-26 17:39:53 +00:00
|
|
|
{
|
2006-08-01 06:29:11 +00:00
|
|
|
m_pCombo->SetCombo( iCurCombo, iCurMissCombo );
|
2005-08-26 17:39:53 +00:00
|
|
|
}
|
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
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
|
|
|
|
m_pPlayerStageStats->iMaxCombo = max(m_pPlayerStageStats->iMaxCombo, iCurCombo);
|
2003-09-06 02:20:09 +00:00
|
|
|
|
2005-04-05 04:56:39 +00:00
|
|
|
/*
|
|
|
|
|
* Use the real current beat, not the beat we've been passed. That's because we
|
2003-10-24 05:19:08 +00:00
|
|
|
* want to record the current life/combo to the current time; eg. if it's a MISS,
|
2005-04-05 04:56:39 +00:00
|
|
|
* the beat we're registering is in the past, but the life is changing now.
|
|
|
|
|
*
|
|
|
|
|
* We need to include time from previous songs in a course, so we can't use
|
|
|
|
|
* GAMESTATE->m_fMusicSeconds. Use fGameplaySeconds instead.
|
|
|
|
|
*/
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
2005-04-23 02:50:26 +00:00
|
|
|
m_pPlayerStageStats->UpdateComboList( STATSMAN->m_CurStageStats.fStepsSeconds, 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 )
|
2004-12-20 10:47:41 +00:00
|
|
|
{
|
2003-10-24 05:19:08 +00:00
|
|
|
life = m_pLifeMeter->GetLife();
|
2004-12-20 10:47:41 +00:00
|
|
|
}
|
2003-12-17 03:59:08 +00:00
|
|
|
else if( m_pCombinedLifeMeter )
|
|
|
|
|
{
|
|
|
|
|
life = GAMESTATE->m_fTugLifePercentP1;
|
2004-12-20 06:25:59 +00:00
|
|
|
if( pn == PLAYER_2 )
|
2003-12-17 03:59:08 +00:00
|
|
|
life = 1.0f - life;
|
|
|
|
|
}
|
2003-10-26 08:37:25 +00:00
|
|
|
if( life != -1 )
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
2005-04-23 02:50:26 +00:00
|
|
|
m_pPlayerStageStats->SetLifeRecordAt( life, STATSMAN->m_CurStageStats.fStepsSeconds );
|
2003-10-23 06:17:40 +00:00
|
|
|
|
2005-04-21 11:16:01 +00:00
|
|
|
if( m_pScoreDisplay )
|
|
|
|
|
{
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
2005-04-21 11:16:01 +00:00
|
|
|
m_pScoreDisplay->SetScore( m_pPlayerStageStats->iScore );
|
|
|
|
|
m_pScoreDisplay->OnJudgment( scoreOfLastTap );
|
|
|
|
|
}
|
|
|
|
|
if( m_pSecondaryScoreDisplay )
|
|
|
|
|
{
|
2004-12-22 07:31:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
2005-04-21 11:16:01 +00:00
|
|
|
m_pSecondaryScoreDisplay->SetScore( m_pPlayerStageStats->iScore );
|
|
|
|
|
m_pSecondaryScoreDisplay->OnJudgment( scoreOfLastTap );
|
|
|
|
|
}
|
2002-11-03 20:10:17 +00:00
|
|
|
|
2005-04-21 11:16:01 +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
|
|
|
|
|
}
|
2005-04-21 11:16:01 +00:00
|
|
|
if( m_pCombinedLifeMeter )
|
|
|
|
|
{
|
2004-12-20 06:25:59 +00:00
|
|
|
m_pCombinedLifeMeter->ChangeLife( pn, scoreOfLastTap );
|
2006-07-17 00:54:19 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
2004-12-20 06:25:59 +00:00
|
|
|
m_pCombinedLifeMeter->OnDancePointsChange( pn ); // update oni life meter
|
2003-06-30 18:08:27 +00:00
|
|
|
}
|
2002-06-24 22:04:31 +00:00
|
|
|
}
|
2002-07-28 20:28:37 +00:00
|
|
|
|
2002-10-24 19:21:05 +00:00
|
|
|
|
2006-07-10 05:15:52 +00:00
|
|
|
void Player::HandleHoldScore( const TapNote &tn )
|
2002-07-28 20:28:37 +00:00
|
|
|
{
|
2006-07-10 05:15:52 +00:00
|
|
|
HoldNoteScore holdScore = tn.HoldResult.hns;
|
|
|
|
|
TapNoteScore tapScore = tn.result.tns;
|
2003-04-21 04:11:01 +00:00
|
|
|
bool NoCheating = true;
|
2003-06-18 04:25:49 +00:00
|
|
|
#ifdef DEBUG
|
|
|
|
|
NoCheating = false;
|
|
|
|
|
#endif
|
|
|
|
|
|
2005-04-21 11:16:01 +00:00
|
|
|
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
2003-04-21 04:11:01 +00:00
|
|
|
NoCheating = false;
|
2002-08-23 20:18:29 +00:00
|
|
|
// don't accumulate points if AutoPlay is on.
|
2004-12-20 06:25:59 +00:00
|
|
|
if( NoCheating && m_pPlayerState->m_PlayerController == PC_AUTOPLAY )
|
2002-08-23 20:18:29 +00:00
|
|
|
return;
|
|
|
|
|
|
2006-07-10 05:15:52 +00:00
|
|
|
if( m_pPrimaryScoreKeeper )
|
|
|
|
|
m_pPrimaryScoreKeeper->HandleHoldScore( tn );
|
|
|
|
|
if( m_pSecondaryScoreKeeper )
|
|
|
|
|
m_pSecondaryScoreKeeper->HandleHoldScore( tn );
|
2002-08-23 20:18:29 +00:00
|
|
|
|
2004-12-20 06:25:59 +00:00
|
|
|
// TODO: Remove use of PlayerNumber.
|
|
|
|
|
PlayerNumber pn = m_pPlayerState->m_PlayerNumber;
|
|
|
|
|
|
2005-04-21 11:16:01 +00:00
|
|
|
if( m_pScoreDisplay )
|
|
|
|
|
{
|
|
|
|
|
if( m_pPlayerStageStats )
|
|
|
|
|
m_pScoreDisplay->SetScore( m_pPlayerStageStats->iScore );
|
|
|
|
|
m_pScoreDisplay->OnJudgment( holdScore, tapScore );
|
|
|
|
|
}
|
|
|
|
|
if( m_pSecondaryScoreDisplay )
|
|
|
|
|
{
|
|
|
|
|
if( m_pPlayerStageStats )
|
|
|
|
|
m_pSecondaryScoreDisplay->SetScore( m_pPlayerStageStats->iScore );
|
|
|
|
|
m_pSecondaryScoreDisplay->OnJudgment( holdScore, tapScore );
|
|
|
|
|
}
|
2002-11-03 20:52:13 +00:00
|
|
|
|
2004-12-20 06:25:59 +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
|
|
|
}
|
2004-12-20 06:25:59 +00:00
|
|
|
if( m_pCombinedLifeMeter )
|
|
|
|
|
{
|
|
|
|
|
m_pCombinedLifeMeter->ChangeLife( pn, holdScore, tapScore );
|
|
|
|
|
m_pCombinedLifeMeter->OnDancePointsChange( pn );
|
2003-06-30 18:08:27 +00:00
|
|
|
}
|
2002-07-28 20:28:37 +00:00
|
|
|
}
|
2002-09-05 03:45:07 +00:00
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
float Player::GetMaxStepDistanceSeconds()
|
2002-07-28 20:28:37 +00:00
|
|
|
{
|
2006-08-05 02:38:05 +00:00
|
|
|
return GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate * ADJUSTED_WINDOW_SECONDS(TW_W5);
|
2002-09-05 03:45:07 +00:00
|
|
|
}
|
|
|
|
|
|
2005-01-15 01:03:48 +00:00
|
|
|
void Player::FadeToFail()
|
2002-09-05 03:45:07 +00:00
|
|
|
{
|
2004-12-20 10:47:41 +00:00
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->FadeToFail();
|
2005-05-09 02:22:54 +00:00
|
|
|
|
|
|
|
|
// clear miss combo
|
2005-08-26 17:39:53 +00:00
|
|
|
if( m_pCombo )
|
|
|
|
|
m_pCombo->SetCombo( 0, 0 );
|
2003-08-24 17:04:21 +00:00
|
|
|
}
|
|
|
|
|
|
2006-02-01 06:18:09 +00:00
|
|
|
void Player::CacheAllUsedNoteSkins()
|
|
|
|
|
{
|
|
|
|
|
if( m_pNoteField )
|
|
|
|
|
m_pNoteField->CacheAllUsedNoteSkins();
|
|
|
|
|
}
|
|
|
|
|
|
2005-04-28 00:13:01 +00:00
|
|
|
bool Player::IsPlayingBeginner() const
|
|
|
|
|
{
|
2005-04-28 05:08:15 +00:00
|
|
|
if( m_pPlayerStageStats != NULL && !m_pPlayerStageStats->vpPossibleSteps.empty() )
|
2005-04-28 00:13:01 +00:00
|
|
|
{
|
|
|
|
|
Steps *pSteps = m_pPlayerStageStats->vpPossibleSteps[0];
|
|
|
|
|
return pSteps->GetDifficulty() == DIFFICULTY_BEGINNER;
|
|
|
|
|
}
|
2005-08-23 21:07:36 +00:00
|
|
|
|
|
|
|
|
if( m_pPlayerState == NULL )
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
if( m_pPlayerState->m_PlayerNumber == PLAYER_INVALID )
|
|
|
|
|
return false;
|
|
|
|
|
Steps *pSteps = GAMESTATE->m_pCurSteps[ m_pPlayerState->m_PlayerNumber ];
|
|
|
|
|
return pSteps && pSteps->GetDifficulty() == DIFFICULTY_BEGINNER;
|
2005-04-28 00:13:01 +00:00
|
|
|
}
|
|
|
|
|
|
2006-04-21 00:37:27 +00:00
|
|
|
void Player::SetJudgment( TapNoteScore tns, bool bEarly )
|
|
|
|
|
{
|
2006-04-24 21:28:27 +00:00
|
|
|
if( m_pPlayerStageStats )
|
|
|
|
|
m_pPlayerStageStats->tnsLast = tns;
|
2006-04-21 00:37:27 +00:00
|
|
|
if( m_pPlayerState->m_mp != MultiPlayer_INVALID )
|
|
|
|
|
MESSAGEMAN->Broadcast( enum_add2(Message_ShowJudgmentMuliPlayerP1,m_pPlayerState->m_mp) );
|
|
|
|
|
|
|
|
|
|
if( m_pJudgment )
|
|
|
|
|
m_pJudgment->SetJudgment( tns, bEarly );
|
|
|
|
|
}
|
2005-05-19 23:29:39 +00:00
|
|
|
|
2004-06-08 00:08:04 +00:00
|
|
|
/*
|
2006-07-20 08:15:10 +00:00
|
|
|
* (c) 2001-2006 Chris Danford, Steve Checkoway
|
2004-06-08 00:08:04 +00:00
|
|
|
* 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.
|
|
|
|
|
*/
|