Files
itgmania212121/stepmania/src/LifeMeterBar.cpp
T

412 lines
12 KiB
C++
Raw Normal View History

2003-02-16 04:01:45 +00:00
#include "global.h"
2002-04-28 20:42:32 +00:00
#include "LifeMeterBar.h"
2002-07-23 01:41:40 +00:00
#include "PrefsManager.h"
2006-11-09 04:59:17 +00:00
#include "RageLog.h"
2002-07-27 19:29:51 +00:00
#include "RageTimer.h"
#include "GameState.h"
2003-07-04 18:03:20 +00:00
#include "RageMath.h"
#include "ThemeManager.h"
#include "Song.h"
2005-02-16 03:25:45 +00:00
#include "StatsManager.h"
#include "ThemeMetric.h"
#include "PlayerState.h"
2005-04-10 01:24:00 +00:00
#include "Quad.h"
#include "ActorUtil.h"
2005-04-21 11:16:01 +00:00
#include "StreamDisplay.h"
2005-06-20 18:37:08 +00:00
#include "Steps.h"
2006-11-09 04:59:17 +00:00
#include "Course.h"
2002-04-28 20:42:32 +00:00
2006-12-12 10:31:16 +00:00
static RString LIFE_PERCENT_CHANGE_NAME( size_t i ) { return "LifePercentChange" + ScoreEventToString( (ScoreEvent)i ); }
2002-04-28 20:42:32 +00:00
LifeMeterBar::LifeMeterBar()
{
2007-02-12 22:38:09 +00:00
DANGER_THRESHOLD.Load ("LifeMeterBar","DangerThreshold");
INITIAL_VALUE.Load ("LifeMeterBar","InitialValue");
HOT_VALUE.Load ("LifeMeterBar","HotValue");
2007-02-12 22:38:09 +00:00
MIN_STAY_ALIVE.Load ("LifeMeterBar","MinStayAlive");
m_fLifePercentChange.Load( "LifeMeterBar", LIFE_PERCENT_CHANGE_NAME, NUM_ScoreEvent );
2006-11-14 04:18:07 +00:00
m_pPlayerState = NULL;
switch( GAMESTATE->m_SongOptions.GetStage().m_DrainType )
2002-07-29 03:06:55 +00:00
{
case SongOptions::DRAIN_NORMAL:
m_fLifePercentage = INITIAL_VALUE;
break;
/* These types only go down, so they always start at full. */
case SongOptions::DRAIN_NO_RECOVER:
case SongOptions::DRAIN_SUDDEN_DEATH:
m_fLifePercentage = 1.0f; break;
2002-07-29 03:06:55 +00:00
default: ASSERT(0);
}
2006-01-22 01:00:06 +00:00
const RString sType = "LifeMeterBar";
2005-04-21 11:16:01 +00:00
m_fPassingAlpha = 0;
2002-07-23 01:41:40 +00:00
m_fHotAlpha = 0;
m_fBaseLifeDifficulty = PREFSMAN->m_fLifeDifficultyScale;
m_fLifeDifficulty = m_fBaseLifeDifficulty;
2003-07-29 12:42:38 +00:00
// set up progressive lifebar
m_iProgressiveLifebar = PREFSMAN->m_iProgressiveLifebar;
m_iMissCombo = 0;
// set up combotoregainlife
m_iComboToRegainLife = 0;
2008-03-19 11:38:14 +00:00
bool bExtra = GAMESTATE->IsAnExtraStage();
RString sExtra = bExtra ? "extra " : "";
m_sprUnder.Load( THEME->GetPathG(sType,sExtra+"Under") );
m_sprUnder->SetName( "Under" );
ActorUtil::LoadAllCommandsAndSetXY( m_sprUnder, sType );
this->AddChild( m_sprUnder );
2005-04-10 01:24:00 +00:00
2008-03-19 11:38:14 +00:00
m_sprDanger.Load( THEME->GetPathG(sType,sExtra+"Danger") );
m_sprDanger->SetName( "Danger" );
ActorUtil::LoadAllCommandsAndSetXY( m_sprDanger, sType );
this->AddChild( m_sprDanger );
2005-04-10 01:24:00 +00:00
2005-04-21 11:16:01 +00:00
m_pStream = new StreamDisplay;
m_pStream->Load( bExtra ? "StreamDisplayExtra" : "StreamDisplay" );
2008-03-19 11:38:14 +00:00
m_pStream->SetName( "Stream" );
ActorUtil::LoadAllCommandsAndSetXY( m_pStream, sType );
2005-04-10 01:24:00 +00:00
this->AddChild( m_pStream );
2008-03-19 11:38:14 +00:00
m_sprOver.Load( THEME->GetPathG(sType,sExtra+"Over") );
m_sprOver->SetName( "Over" );
ActorUtil::LoadAllCommandsAndSetXY( m_sprOver, sType );
this->AddChild( m_sprOver );
}
2002-05-27 08:23:27 +00:00
LifeMeterBar::~LifeMeterBar()
{
2005-04-21 11:16:01 +00:00
SAFE_DELETE( m_pStream );
2002-04-28 20:42:32 +00:00
}
void LifeMeterBar::Load( const PlayerState *pPlayerState, PlayerStageStats *pPlayerStageStats )
2002-07-23 01:41:40 +00:00
{
LifeMeter::Load( pPlayerState, pPlayerStageStats );
PlayerNumber pn = pPlayerState->m_PlayerNumber;
2005-06-20 18:37:08 +00:00
// Change life difficulty to really easy if merciful beginner on
bool bMercifulBeginnerInEffect =
GAMESTATE->m_PlayMode == PLAY_MODE_REGULAR &&
GAMESTATE->IsPlayerEnabled( pPlayerState ) &&
GAMESTATE->m_pCurSteps[pn]->GetDifficulty() == Difficulty_Beginner &&
2005-06-20 18:37:08 +00:00
PREFSMAN->m_bMercifulBeginner;
if( bMercifulBeginnerInEffect )
{
2008-07-07 20:06:06 +00:00
m_fBaseLifeDifficulty = 1.5f;
2005-06-20 18:37:08 +00:00
m_fLifeDifficulty = m_fBaseLifeDifficulty;
}
2006-11-14 20:45:29 +00:00
AfterLifeChanged();
2002-07-23 01:41:40 +00:00
}
2002-04-28 20:42:32 +00:00
void LifeMeterBar::ChangeLife( TapNoteScore score )
{
2002-09-07 10:22:21 +00:00
float fDeltaLife=0.f;
switch( score )
{
2006-07-03 04:05:32 +00:00
DEFAULT_FAIL( score );
2007-02-12 22:38:09 +00:00
case TNS_W1: fDeltaLife = m_fLifePercentChange.GetValue(SE_W1); break;
case TNS_W2: fDeltaLife = m_fLifePercentChange.GetValue(SE_W2); break;
case TNS_W3: fDeltaLife = m_fLifePercentChange.GetValue(SE_W3); break;
case TNS_W4: fDeltaLife = m_fLifePercentChange.GetValue(SE_W4); break;
case TNS_W5: fDeltaLife = m_fLifePercentChange.GetValue(SE_W5); break;
case TNS_Miss: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break;
case TNS_HitMine: fDeltaLife = m_fLifePercentChange.GetValue(SE_HitMine); break;
case TNS_None: fDeltaLife = m_fLifePercentChange.GetValue(SE_Miss); break;
2007-04-06 23:25:32 +00:00
case TNS_CheckpointHit: fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointHit); break;
case TNS_CheckpointMiss:fDeltaLife = m_fLifePercentChange.GetValue(SE_CheckpointMiss); break;
}
2007-04-06 23:25:32 +00:00
if( IsHot() && fDeltaLife < 0 )
fDeltaLife = min( fDeltaLife, -0.10f ); // make it take a while to get back to "hot"
switch( GAMESTATE->m_SongOptions.GetSong().m_DrainType )
2002-04-28 20:42:32 +00:00
{
DEFAULT_FAIL( GAMESTATE->m_SongOptions.GetSong().m_DrainType );
2002-07-27 19:29:51 +00:00
case SongOptions::DRAIN_NORMAL:
2002-07-23 01:41:40 +00:00
break;
2002-07-27 19:29:51 +00:00
case SongOptions::DRAIN_NO_RECOVER:
fDeltaLife = min( fDeltaLife, 0 );
2002-07-23 01:41:40 +00:00
break;
2002-07-27 19:29:51 +00:00
case SongOptions::DRAIN_SUDDEN_DEATH:
if( score < MIN_STAY_ALIVE )
fDeltaLife = -1.0f;
else
fDeltaLife = 0;
2002-07-23 01:41:40 +00:00
break;
2002-04-28 20:42:32 +00:00
}
2003-11-11 07:36:28 +00:00
ChangeLife( fDeltaLife );
}
void LifeMeterBar::ChangeLife( HoldNoteScore score, TapNoteScore tscore )
{
float fDeltaLife=0.f;
switch( GAMESTATE->m_SongOptions.GetSong().m_DrainType )
2003-11-11 07:36:28 +00:00
{
case SongOptions::DRAIN_NORMAL:
switch( score )
{
2007-02-12 22:38:09 +00:00
case HNS_Held: fDeltaLife = m_fLifePercentChange.GetValue(SE_Held); break;
case HNS_LetGo: fDeltaLife = m_fLifePercentChange.GetValue(SE_LetGo); break;
2003-11-11 07:36:28 +00:00
default:
ASSERT(0);
}
if( IsHot() && score == HNS_LetGo )
fDeltaLife = -0.10f; // make it take a while to get back to "hot"
2003-11-11 07:36:28 +00:00
break;
case SongOptions::DRAIN_NO_RECOVER:
switch( score )
{
case HNS_Held: fDeltaLife = +0.000f; break;
2007-02-12 22:38:09 +00:00
case HNS_LetGo: fDeltaLife = m_fLifePercentChange.GetValue(SE_LetGo); break;
2003-11-11 07:36:28 +00:00
default:
ASSERT(0);
}
break;
case SongOptions::DRAIN_SUDDEN_DEATH:
switch( score )
{
case HNS_Held: fDeltaLife = +0; break;
case HNS_LetGo: fDeltaLife = -1.0; break;
2003-11-11 07:36:28 +00:00
default:
ASSERT(0);
}
break;
default:
ASSERT(0);
}
ChangeLife( fDeltaLife );
}
void LifeMeterBar::ChangeLife( float fDeltaLife )
{
2004-01-11 23:33:56 +00:00
if( PREFSMAN->m_bMercifulDrain && fDeltaLife < 0 )
fDeltaLife *= SCALE( m_fLifePercentage, 0.f, 1.f, 0.5f, 1.f);
// handle progressiveness and ComboToRegainLife here
2003-11-11 07:36:28 +00:00
if( fDeltaLife >= 0 )
2003-07-29 12:42:38 +00:00
{
m_iMissCombo = 0;
2003-10-13 00:06:49 +00:00
m_iComboToRegainLife = max( m_iComboToRegainLife-1, 0 );
if ( m_iComboToRegainLife > 0 )
fDeltaLife = 0.0f;
2003-11-11 07:36:28 +00:00
}
else
{
2003-07-29 12:42:38 +00:00
fDeltaLife *= 1 + (float)m_iProgressiveLifebar/8 * m_iMissCombo;
// do this after; only successive W5/miss will
2003-07-29 12:42:38 +00:00
// increase the amount of life lost.
m_iMissCombo++;
m_iComboToRegainLife = PREFSMAN->m_iRegenComboAfterMiss;
2003-07-29 12:42:38 +00:00
}
/* If we've already failed, there's no point in letting them fill up the bar again. */
if( m_pPlayerStageStats->m_bFailed )
2006-11-09 09:48:51 +00:00
return;
2003-10-13 03:24:28 +00:00
switch( GAMESTATE->m_SongOptions.GetSong().m_DrainType )
{
case SongOptions::DRAIN_NORMAL:
case SongOptions::DRAIN_NO_RECOVER:
if( fDeltaLife > 0 )
fDeltaLife *= m_fLifeDifficulty;
else
fDeltaLife /= m_fLifeDifficulty;
break;
}
2002-05-29 09:47:24 +00:00
m_fLifePercentage += fDeltaLife;
2002-07-23 01:41:40 +00:00
CLAMP( m_fLifePercentage, 0, 1 );
2005-04-21 11:16:01 +00:00
AfterLifeChanged();
2002-04-28 20:42:32 +00:00
}
extern ThemeMetric<bool> PENALIZE_TAP_SCORE_NONE;
void LifeMeterBar::HandleTapScoreNone()
{
if( PENALIZE_TAP_SCORE_NONE )
ChangeLife( TNS_None );
}
void LifeMeterBar::AfterLifeChanged()
2002-04-28 20:42:32 +00:00
{
2005-04-21 11:16:01 +00:00
m_pStream->SetPercent( m_fLifePercentage );
2006-11-14 20:45:29 +00:00
Message msg( "LifeChanged" );
msg.SetParam( "Player", m_pPlayerState->m_PlayerNumber );
msg.SetParam( "LifeMeter", LuaReference::CreateFromPush(*this) );
MESSAGEMAN->Broadcast( msg );
2002-05-27 08:23:27 +00:00
}
2003-11-03 18:24:13 +00:00
bool LifeMeterBar::IsHot() const
2002-05-27 08:23:27 +00:00
{
return m_fLifePercentage >= HOT_VALUE;
2002-05-27 08:23:27 +00:00
}
2002-04-28 20:42:32 +00:00
2003-11-03 18:24:13 +00:00
bool LifeMeterBar::IsInDanger() const
2002-05-27 08:23:27 +00:00
{
2004-01-03 21:30:20 +00:00
return m_fLifePercentage < DANGER_THRESHOLD;
2002-05-27 08:23:27 +00:00
}
2002-04-28 20:42:32 +00:00
2003-11-03 18:24:13 +00:00
bool LifeMeterBar::IsFailing() const
2002-05-27 08:23:27 +00:00
{
2006-11-09 09:21:52 +00:00
return m_fLifePercentage <= m_pPlayerState->m_PlayerOptions.GetCurrent().m_fPassmark;
2002-07-23 01:41:40 +00:00
}
2002-04-28 20:42:32 +00:00
2002-05-27 08:23:27 +00:00
void LifeMeterBar::Update( float fDeltaTime )
{
2002-07-28 20:28:37 +00:00
LifeMeter::Update( fDeltaTime );
2006-11-09 09:47:44 +00:00
m_fPassingAlpha += !IsFailing() ? +fDeltaTime*2 : -fDeltaTime*2;
CLAMP( m_fPassingAlpha, 0, 1 );
m_fHotAlpha += IsHot() ? + fDeltaTime*2 : -fDeltaTime*2;
2002-08-19 20:02:30 +00:00
CLAMP( m_fHotAlpha, 0, 1 );
2005-04-21 11:16:01 +00:00
m_pStream->SetPassingAlpha( m_fPassingAlpha );
m_pStream->SetHotAlpha( m_fHotAlpha );
2007-02-25 08:23:50 +00:00
if( m_pPlayerState->m_HealthState == HealthState_Danger )
2008-03-19 11:38:14 +00:00
m_sprDanger->SetVisible( true );
2005-04-21 11:16:01 +00:00
else
2008-03-19 11:38:14 +00:00
m_sprDanger->SetVisible( false );
2002-08-19 20:02:30 +00:00
}
2002-07-23 01:41:40 +00:00
void LifeMeterBar::UpdateNonstopLifebar()
{
2006-11-09 04:59:17 +00:00
int iCleared, iTotal, iProgressiveLifebarDifficulty;
switch( GAMESTATE->m_PlayMode )
{
case PLAY_MODE_REGULAR:
if( GAMESTATE->IsEventMode() || GAMESTATE->m_bDemonstrationOrJukebox )
return;
2007-04-25 20:01:11 +00:00
iCleared = GAMESTATE->m_iCurrentStageIndex;
2006-11-09 04:59:17 +00:00
iTotal = PREFSMAN->m_iSongsPerPlay;
iProgressiveLifebarDifficulty = PREFSMAN->m_iProgressiveStageLifebar;
break;
case PLAY_MODE_NONSTOP:
2006-11-09 04:59:17 +00:00
iCleared = GAMESTATE->GetCourseSongIndex();
iTotal = GAMESTATE->m_pCurCourse->GetEstimatedNumStages();
iProgressiveLifebarDifficulty = PREFSMAN->m_iProgressiveNonstopLifebar;
break;
default:
return;
}
2006-11-09 04:59:17 +00:00
// if (iCleared > iTotal) iCleared = iTotal; // clear/iTotal <= 1
// if (iTotal == 0) iTotal = 1; // no division by 0
2006-08-06 02:42:40 +00:00
if( GAMESTATE->IsAnExtraStage() )
{ // extra stage is its own thing, should not be progressive
// and it should be as difficult as life 4
// (e.g. it should not depend on life settings)
m_iProgressiveLifebar = 0;
m_fLifeDifficulty = 1.0f;
return;
}
2006-11-09 04:59:17 +00:00
if( iTotal > 1 )
m_fLifeDifficulty = m_fBaseLifeDifficulty - 0.2f * (int)(iProgressiveLifebarDifficulty * iCleared / (iTotal - 1));
else
2006-11-09 04:59:17 +00:00
m_fLifeDifficulty = m_fBaseLifeDifficulty - 0.2f * iProgressiveLifebarDifficulty;
2006-08-06 02:42:40 +00:00
if( m_fLifeDifficulty >= 0.4f )
return;
2006-02-14 11:16:39 +00:00
/*
* Approximate deductions for a miss
* Life 1 : 5 %
* Life 2 : 5.7 %
* Life 3 : 6.6 %
* Life 4 : 8 %
* Life 5 : 10 %
* Life 6 : 13.3 %
* Life 7 : 20 %
* Life 8 : 26.6 %
* Life 9 : 32 %
* Life 10: 40 %
* Life 11: 50 %
* Life 12: 57.1 %
* Life 13: 66.6 %
* Life 14: 80 %
* Life 15: 100 %
* Life 16+: 200 %
*
* Note there is 200%, because boos take off 1/2 as much as
* a miss, and a W5 would suck up half of your lifebar.
*
* Everything past 7 is intended mainly for nonstop mode.
2006-02-14 11:16:39 +00:00
*/
// the lifebar is pretty harsh at 0.4 already (you lose
// about 20% of your lifebar); at 0.2 it would be 40%, which
// is too harsh at one difficulty level higher. Override.
2003-09-04 10:37:48 +00:00
2006-11-09 05:05:08 +00:00
int iLifeDifficulty = int( (1.8f - m_fLifeDifficulty)/0.2f );
2003-09-04 10:37:48 +00:00
// first eight values don't matter
2006-11-09 05:05:08 +00:00
float fDifficultyValues[16] = {0,0,0,0,0,0,0,0,
2003-09-04 10:37:48 +00:00
0.3f, 0.25f, 0.2f, 0.16f, 0.14f, 0.12f, 0.10f, 0.08f};
2006-11-09 05:05:08 +00:00
if( iLifeDifficulty >= 16 )
{
2003-09-04 10:37:48 +00:00
// judge 16 or higher
m_fLifeDifficulty = 0.04f;
return;
}
2006-11-09 05:05:08 +00:00
m_fLifeDifficulty = fDifficultyValues[iLifeDifficulty];
return;
}
2006-08-06 02:42:40 +00:00
void LifeMeterBar::FillForHowToPlay( int NumW2s, int NumMisses )
{
m_iProgressiveLifebar = 0; // disable progressive lifebar
float AmountForW2 = NumW2s * m_fLifeDifficulty * 0.008f;
float AmountForMiss = NumMisses / m_fLifeDifficulty * 0.08f;
m_fLifePercentage = AmountForMiss - AmountForW2;
CLAMP( m_fLifePercentage, 0.0f, 1.0f );
AfterLifeChanged();
}
2004-06-08 00:08:04 +00:00
/*
* (c) 2001-2004 Chris Danford
* All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, and/or sell copies of the Software, and to permit persons to
* whom the Software is furnished to do so, provided that the above
* copyright notice(s) and this permission notice appear in all copies of
* the Software and that both the above copyright notice(s) and this
* permission notice appear in supporting documentation.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
* OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
* THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS
* INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT
* OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*/