sync up hg with the svn code in preparation for sm-ssc v1.0 beta 4; this adds at least one new Lua binding and the WIP warps code (partially-working in quirks mode).
This commit is contained in:
+3
-2
@@ -702,7 +702,8 @@ void BackgroundImpl::Layer::UpdateCurBGChange( const Song *pSong, float fLastMus
|
|||||||
|
|
||||||
float fBeat, fBPS;
|
float fBeat, fBPS;
|
||||||
bool bFreeze;
|
bool bFreeze;
|
||||||
pSong->m_Timing.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze, bFreeze );
|
int iThrowAway1, iThrowAway2;
|
||||||
|
pSong->m_Timing.GetBeatAndBPSFromElapsedTime( fCurrentTime, fBeat, fBPS, bFreeze, bFreeze, iThrowAway1, iThrowAway2 );
|
||||||
|
|
||||||
/* Calls to Update() should *not* be scaled by music rate; fCurrentTime is. Undo it. */
|
/* Calls to Update() should *not* be scaled by music rate; fCurrentTime is. Undo it. */
|
||||||
const float fRate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
const float fRate = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate;
|
||||||
@@ -850,7 +851,7 @@ void BackgroundImpl::GetLoadedBackgroundChanges( vector<BackgroundChange> *pBack
|
|||||||
|
|
||||||
bool BackgroundImpl::IsDangerAllVisible()
|
bool BackgroundImpl::IsDangerAllVisible()
|
||||||
{
|
{
|
||||||
/* The players are never in danger in FAIL_OFF. */
|
// The players are never in danger in FAIL_OFF.
|
||||||
FOREACH_PlayerNumber( p )
|
FOREACH_PlayerNumber( p )
|
||||||
if( GAMESTATE->GetPlayerFailType(GAMESTATE->m_pPlayerState[p]) == PlayerOptions::FAIL_OFF )
|
if( GAMESTATE->GetPlayerFailType(GAMESTATE->m_pPlayerState[p]) == PlayerOptions::FAIL_OFF )
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+4
-3
@@ -240,7 +240,6 @@ void GameState::ResetPlayer( PlayerNumber pn )
|
|||||||
m_pPlayerState[pn]->m_PlayerOptions.Assign( ModsLevel_Preferred, po );
|
m_pPlayerState[pn]->m_PlayerOptions.Assign( ModsLevel_Preferred, po );
|
||||||
}
|
}
|
||||||
|
|
||||||
//static Preference<bool> g_bMultiplayer( "Multiplayer", false );
|
|
||||||
void GameState::Reset()
|
void GameState::Reset()
|
||||||
{
|
{
|
||||||
m_MasterPlayerNumber = PLAYER_INVALID; // must initialize for UnjoinPlayer
|
m_MasterPlayerNumber = PLAYER_INVALID; // must initialize for UnjoinPlayer
|
||||||
@@ -895,8 +894,8 @@ void GameState::ResetMusicStatistics()
|
|||||||
//m_bStop = false;
|
//m_bStop = false;
|
||||||
m_bFreeze = false;
|
m_bFreeze = false;
|
||||||
m_bDelay = false;
|
m_bDelay = false;
|
||||||
//m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
|
m_iWarpBeginRow = -1; // Set to -1 because some song may want to warp to row 0. -aj
|
||||||
//m_iWarpEndRow = -1; // Set when a warp is encountered. also see above. -aj
|
m_iWarpEndRow = -1; // Set when a warp is encountered. also see above. -aj
|
||||||
m_fMusicSecondsVisible = 0;
|
m_fMusicSecondsVisible = 0;
|
||||||
m_fSongBeatVisible = 0;
|
m_fSongBeatVisible = 0;
|
||||||
Actor::SetBGMTime( 0, 0, 0, 0 );
|
Actor::SetBGMTime( 0, 0, 0, 0 );
|
||||||
@@ -951,8 +950,10 @@ void GameState::UpdateSongPosition( float fPositionSeconds, const TimingData &ti
|
|||||||
m_LastBeatUpdate.Touch();
|
m_LastBeatUpdate.Touch();
|
||||||
|
|
||||||
// xxx testing: only do this on monotune survivor
|
// xxx testing: only do this on monotune survivor
|
||||||
|
/*
|
||||||
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
if( m_pCurSong && m_pCurSong->GetDisplayFullTitle() == "monotune survivor" )
|
||||||
LOG->Trace( ssprintf("[GameState::UpdateSongPosition] cur BPS = %f, fPositionSeconds = %f",m_fCurBPS,fPositionSeconds) );
|
LOG->Trace( ssprintf("[GameState::UpdateSongPosition] cur BPS = %f, fPositionSeconds = %f",m_fCurBPS,fPositionSeconds) );
|
||||||
|
*/
|
||||||
|
|
||||||
//timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_iWarpEndRow );
|
//timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay, m_iWarpBeginRow, m_iWarpEndRow );
|
||||||
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay );
|
timing.GetBeatAndBPSFromElapsedTime( fPositionSeconds, m_fSongBeat, m_fCurBPS, m_bFreeze, m_bDelay );
|
||||||
|
|||||||
+1
-1
@@ -184,7 +184,7 @@ public:
|
|||||||
//bool m_bStop; // in the middle of a stop (freeze or delay)
|
//bool m_bStop; // in the middle of a stop (freeze or delay)
|
||||||
bool m_bFreeze; // in the middle of a freeze
|
bool m_bFreeze; // in the middle of a freeze
|
||||||
bool m_bDelay; // in the middle of a delay
|
bool m_bDelay; // in the middle of a delay
|
||||||
//int m_iWarpToRow, m_iWarpFromRow; // used for warping
|
int m_iWarpBeginRow, m_iWarpEndRow; // used for warping
|
||||||
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
RageTimer m_LastBeatUpdate; // time of last m_fSongBeat, etc. update
|
||||||
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
BroadcastOnChange<bool> m_bGameplayLeadIn;
|
||||||
|
|
||||||
|
|||||||
+27
-16
@@ -135,7 +135,7 @@ ThemeMetric<bool> CHECKPOINTS_USE_TIME_SIGNATURES ( "Player", "CheckpointsUseTim
|
|||||||
ThemeMetric<bool> CHECKPOINTS_FLASH_ON_HOLD ( "Player", "CheckpointsFlashOnHold" ); // sm-ssc addition
|
ThemeMetric<bool> CHECKPOINTS_FLASH_ON_HOLD ( "Player", "CheckpointsFlashOnHold" ); // sm-ssc addition
|
||||||
ThemeMetric<bool> IMMEDIATE_HOLD_LET_GO ( "Player", "ImmediateHoldLetGo" );
|
ThemeMetric<bool> IMMEDIATE_HOLD_LET_GO ( "Player", "ImmediateHoldLetGo" );
|
||||||
ThemeMetric<bool> REQUIRE_STEP_ON_HOLD_HEADS ( "Player", "RequireStepOnHoldHeads" );
|
ThemeMetric<bool> REQUIRE_STEP_ON_HOLD_HEADS ( "Player", "RequireStepOnHoldHeads" );
|
||||||
//ThemeMetric<bool> REQUIRE_STEP_ON_TAP_NOTES ( "Player", "RequireStepOnTapNotes" ); // parastar stuff; leave in though
|
//ThemeMetric<bool> HOLD_TRIGGERS_TAP_NOTES ( "Player", "HoldTriggersTapNotes" ); // parastar stuff; leave in though
|
||||||
ThemeMetric<bool> ROLL_BODY_INCREMENTS_COMBO ( "Player", "RollBodyIncrementsCombo" );
|
ThemeMetric<bool> ROLL_BODY_INCREMENTS_COMBO ( "Player", "RollBodyIncrementsCombo" );
|
||||||
ThemeMetric<bool> CHECKPOINTS_TAPS_SEPARATE_JUDGMENT ( "Player", "CheckpointsTapsSeparateJudgment" );
|
ThemeMetric<bool> CHECKPOINTS_TAPS_SEPARATE_JUDGMENT ( "Player", "CheckpointsTapsSeparateJudgment" );
|
||||||
ThemeMetric<bool> SCORE_MISSED_HOLDS_AND_ROLLS ( "Player", "ScoreMissedHoldsAndRolls" ); // sm-ssc addition
|
ThemeMetric<bool> SCORE_MISSED_HOLDS_AND_ROLLS ( "Player", "ScoreMissedHoldsAndRolls" ); // sm-ssc addition
|
||||||
@@ -355,6 +355,9 @@ void Player::Init(
|
|||||||
m_pActorWithComboPosition = &*m_sprCombo;
|
m_pActorWithComboPosition = &*m_sprCombo;
|
||||||
this->AddChild( m_sprCombo );
|
this->AddChild( m_sprCombo );
|
||||||
|
|
||||||
|
// todo: allow for judgments to be loaded per-column a la pop'n?
|
||||||
|
// see how HoldJudgments are handled below for an example, though
|
||||||
|
// it would need more work. -aj
|
||||||
m_sprJudgment.Load( THEME->GetPathG(sType,"judgment") );
|
m_sprJudgment.Load( THEME->GetPathG(sType,"judgment") );
|
||||||
m_sprJudgment->SetName( "Judgment" );
|
m_sprJudgment->SetName( "Judgment" );
|
||||||
m_pActorWithJudgmentPosition = &*m_sprJudgment;
|
m_pActorWithJudgmentPosition = &*m_sprJudgment;
|
||||||
@@ -481,6 +484,9 @@ void Player::Load()
|
|||||||
{
|
{
|
||||||
// ugly, ugly, ugly. Works only w/ dance.
|
// ugly, ugly, ugly. Works only w/ dance.
|
||||||
// Why does this work only with dance? - Steve
|
// Why does this work only with dance? - Steve
|
||||||
|
// it has to do with there only being four cases. This is a lame
|
||||||
|
// workaround, but since only DDR has ever really implemented those
|
||||||
|
// modes, it's stayed like this. -aj
|
||||||
StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType;
|
StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType;
|
||||||
NoteDataUtil::TransformNoteData( m_NoteData, m_pPlayerState->m_PlayerOptions.GetStage(), st );
|
NoteDataUtil::TransformNoteData( m_NoteData, m_pPlayerState->m_PlayerOptions.GetStage(), st );
|
||||||
|
|
||||||
@@ -675,7 +681,6 @@ void Player::Update( float fDeltaTime )
|
|||||||
|
|
||||||
// Update Y positions
|
// Update Y positions
|
||||||
{
|
{
|
||||||
// todo: unhardcode hold judgment cmds -aj
|
|
||||||
for( int c=0; c<GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer; c++ )
|
for( int c=0; c<GAMESTATE->GetCurrentStyle()->m_iColsPerPlayer; c++ )
|
||||||
{
|
{
|
||||||
float fPercentReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(c);
|
float fPercentReverse = m_pPlayerState->m_PlayerOptions.GetCurrent().GetReversePercentForColumn(c);
|
||||||
@@ -767,7 +772,6 @@ void Player::Update( float fDeltaTime )
|
|||||||
m_pNoteField->SetPressed( col );
|
m_pNoteField->SetPressed( col );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// handle Autoplay for rolls
|
// handle Autoplay for rolls
|
||||||
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
if( m_pPlayerState->m_PlayerController != PC_HUMAN )
|
||||||
{
|
{
|
||||||
@@ -821,8 +825,8 @@ void Player::Update( float fDeltaTime )
|
|||||||
TrackRowTapNote trtn = { iTrack, iRow, &tn };
|
TrackRowTapNote trtn = { iTrack, iRow, &tn };
|
||||||
|
|
||||||
/* All holds must be of the same subType because fLife is handled
|
/* All holds must be of the same subType because fLife is handled
|
||||||
* in different ways depending on the SubType. Handle Rolls one at a time
|
* in different ways depending on the SubType. Handle Rolls one at
|
||||||
* and don't mix with holds. */
|
* a time and don't mix with holds. */
|
||||||
switch( tn.subType )
|
switch( tn.subType )
|
||||||
{
|
{
|
||||||
DEFAULT_FAIL( tn.subType );
|
DEFAULT_FAIL( tn.subType );
|
||||||
@@ -901,7 +905,7 @@ void Player::Update( float fDeltaTime )
|
|||||||
ApplyWaitingTransforms();
|
ApplyWaitingTransforms();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update a group of holds with shared scoring/life. All of these holds will have the same start row. */
|
// Update a group of holds with shared scoring/life. All of these holds will have the same start row.
|
||||||
void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vector<TrackRowTapNote> &vTN )
|
void Player::UpdateHoldNotes( int iSongRow, float fDeltaTime, vector<TrackRowTapNote> &vTN )
|
||||||
{
|
{
|
||||||
ASSERT( !vTN.empty() );
|
ASSERT( !vTN.empty() );
|
||||||
@@ -2321,7 +2325,8 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
|||||||
bool bFreeze, bDelay;
|
bool bFreeze, bDelay;
|
||||||
float fMissIfOlderThanThisBeat;
|
float fMissIfOlderThanThisBeat;
|
||||||
float fThrowAway;
|
float fThrowAway;
|
||||||
GAMESTATE->m_pCurSong->m_Timing.GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze, bDelay );
|
int iWarpBeginRow, iWarpEndRow;
|
||||||
|
GAMESTATE->m_pCurSong->m_Timing.GetBeatAndBPSFromElapsedTime( fEarliestTime, fMissIfOlderThanThisBeat, fThrowAway, bFreeze, bDelay, iWarpBeginRow, iWarpEndRow );
|
||||||
|
|
||||||
iMissIfOlderThanThisRow = BeatToNoteRow( fMissIfOlderThanThisBeat );
|
iMissIfOlderThanThisRow = BeatToNoteRow( fMissIfOlderThanThisBeat );
|
||||||
if( bFreeze || bDelay )
|
if( bFreeze || bDelay )
|
||||||
@@ -2356,6 +2361,11 @@ void Player::UpdateTapNotesMissedOlderThan( float fMissIfOlderThanSeconds )
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// warp hackery: don't score notes within the warp region.
|
||||||
|
// (Only useful when QuirksMode is enabled.) -aj
|
||||||
|
if( iter.Row() >= GAMESTATE->m_iWarpBeginRow && iter.Row() <= GAMESTATE->m_iWarpEndRow )
|
||||||
|
continue;
|
||||||
|
|
||||||
tn.result.tns = TNS_Miss;
|
tn.result.tns = TNS_Miss;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2367,7 +2377,6 @@ void Player::UpdateJudgedRows()
|
|||||||
bool bAllJudged = true;
|
bool bAllJudged = true;
|
||||||
const bool bSeparately = GAMESTATE->GetCurrentGame()->m_bCountNotesSeparately;
|
const bool bSeparately = GAMESTATE->GetCurrentGame()->m_bCountNotesSeparately;
|
||||||
|
|
||||||
// todo: modify function for warps? -aj
|
|
||||||
{
|
{
|
||||||
NoteData::all_tracks_iterator iter = *m_pIterUnjudgedRows;
|
NoteData::all_tracks_iterator iter = *m_pIterUnjudgedRows;
|
||||||
int iLastSeenRow = -1;
|
int iLastSeenRow = -1;
|
||||||
@@ -2375,16 +2384,14 @@ void Player::UpdateJudgedRows()
|
|||||||
{
|
{
|
||||||
int iRow = iter.Row();
|
int iRow = iter.Row();
|
||||||
|
|
||||||
// if row is in a skip section, ignore it? -aj
|
|
||||||
//if(iRow)
|
|
||||||
//{
|
|
||||||
// // stuff!!
|
|
||||||
//}
|
|
||||||
|
|
||||||
if( iLastSeenRow != iRow )
|
if( iLastSeenRow != iRow )
|
||||||
{
|
{
|
||||||
iLastSeenRow = iRow;
|
iLastSeenRow = iRow;
|
||||||
|
|
||||||
|
// if row is within a warp section, ignore it. -aj
|
||||||
|
if(iRow >= GAMESTATE->m_iWarpBeginRow && iRow <= GAMESTATE->m_iWarpEndRow)
|
||||||
|
continue;
|
||||||
|
|
||||||
// crossed a nonempty row
|
// crossed a nonempty row
|
||||||
if( !NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRow) )
|
if( !NoteDataWithScoring::IsRowCompletelyJudged(m_NoteData, iRow) )
|
||||||
{
|
{
|
||||||
@@ -2526,7 +2533,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
|||||||
int iLastSeenRow = -1;
|
int iLastSeenRow = -1;
|
||||||
for( ; !iter.IsAtEnd() && iter.Row() <= iLastRowCrossed; ++iter )
|
for( ; !iter.IsAtEnd() && iter.Row() <= iLastRowCrossed; ++iter )
|
||||||
{
|
{
|
||||||
/* Apply InitialHoldLife. */
|
// Apply InitialHoldLife.
|
||||||
TapNote &tn = *iter;
|
TapNote &tn = *iter;
|
||||||
int iRow = iter.Row();
|
int iRow = iter.Row();
|
||||||
int iTrack = iter.Track();
|
int iTrack = iter.Track();
|
||||||
@@ -2630,7 +2637,7 @@ void Player::CrossedRows( int iLastRowCrossed, const RageTimer &now )
|
|||||||
{
|
{
|
||||||
TimeSignatureSegment tSignature = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) );
|
TimeSignatureSegment tSignature = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( NoteRowToBeat( iLastRowCrossed ) );
|
||||||
|
|
||||||
// Most songs are in 4/4 time. The frequency for checking tick counts should reflect that.
|
// Most songs are in 4/4 time. The frequency for checking tick counts should reflect that.
|
||||||
iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature.m_iDenominator / (tSignature.m_iNumerator * 4);
|
iCheckpointFrequencyRows = ROWS_PER_BEAT * tSignature.m_iDenominator / (tSignature.m_iNumerator * 4);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2744,6 +2751,10 @@ void Player::HandleTapRowScore( unsigned row )
|
|||||||
bNoCheating = false;
|
bNoCheating = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// more warp hackery. -aj
|
||||||
|
if(row >= (unsigned)GAMESTATE->m_iWarpBeginRow && row <= (unsigned)GAMESTATE->m_iWarpEndRow)
|
||||||
|
return;
|
||||||
|
|
||||||
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
if( GAMESTATE->m_bDemonstrationOrJukebox )
|
||||||
bNoCheating = false;
|
bNoCheating = false;
|
||||||
// don't accumulate points if AutoPlay is on.
|
// don't accumulate points if AutoPlay is on.
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
#include <set>
|
#include <set>
|
||||||
#include <float.h>
|
#include <float.h>
|
||||||
|
|
||||||
const int FILE_CACHE_VERSION = 159; // increment this to invalidate cache
|
const int FILE_CACHE_VERSION = 160; // increment this to invalidate cache
|
||||||
|
|
||||||
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
|
||||||
|
|
||||||
|
|||||||
+45
-20
@@ -11,6 +11,7 @@
|
|||||||
TimingData::TimingData()
|
TimingData::TimingData()
|
||||||
{
|
{
|
||||||
m_fBeat0OffsetInSeconds = 0;
|
m_fBeat0OffsetInSeconds = 0;
|
||||||
|
m_bHasNegativeBpms = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut ) const
|
void TimingData::GetActualBPM( float &fMinBPMOut, float &fMaxBPMOut ) const
|
||||||
@@ -41,12 +42,10 @@ void TimingData::AddTimeSignatureSegment( const TimeSignatureSegment &seg )
|
|||||||
m_vTimeSignatureSegments.insert( upper_bound(m_vTimeSignatureSegments.begin(), m_vTimeSignatureSegments.end(), seg), seg );
|
m_vTimeSignatureSegments.insert( upper_bound(m_vTimeSignatureSegments.begin(), m_vTimeSignatureSegments.end(), seg), seg );
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
void TimingData::AddWarpSegment( const WarpSegment &seg )
|
void TimingData::AddWarpSegment( const WarpSegment &seg )
|
||||||
{
|
{
|
||||||
m_WarpSegments.insert( upper_bound(m_WarpSegments.begin(), m_WarpSegments.end(), seg), seg );
|
m_WarpSegments.insert( upper_bound(m_WarpSegments.begin(), m_WarpSegments.end(), seg), seg );
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
/* Change an existing BPM segment, merge identical segments together or insert a new one. */
|
/* Change an existing BPM segment, merge identical segments together or insert a new one. */
|
||||||
void TimingData::SetBPMAtRow( int iNoteRow, float fBPM )
|
void TimingData::SetBPMAtRow( int iNoteRow, float fBPM )
|
||||||
@@ -110,6 +109,13 @@ void TimingData::SetDelayAtRow( int iRow, float fSeconds )
|
|||||||
SetStopAtRow(iRow,fSeconds,true);
|
SetStopAtRow(iRow,fSeconds,true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
void TimingData::SetWarpAtRow( int iRowAt, int iRowTo )
|
||||||
|
{
|
||||||
|
// todo: code this -aj
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
float TimingData::GetStopAtRow( int iNoteRow, bool &bDelayOut ) const
|
float TimingData::GetStopAtRow( int iNoteRow, bool &bDelayOut ) const
|
||||||
{
|
{
|
||||||
bDelayOut = false; // not a delay by default
|
bDelayOut = false; // not a delay by default
|
||||||
@@ -125,6 +131,18 @@ float TimingData::GetStopAtRow( int iNoteRow, bool &bDelayOut ) const
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int TimingData::GetWarpToRow( int iWarpBeginRow ) const
|
||||||
|
{
|
||||||
|
for( unsigned i=0; i<m_WarpSegments.size(); i++ )
|
||||||
|
{
|
||||||
|
if( m_WarpSegments[i].m_iStartRow == iWarpBeginRow )
|
||||||
|
{
|
||||||
|
return m_WarpSegments[i].m_iEndRow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Multiply the BPM in the range [fStartBeat,fEndBeat) by fFactor.
|
// Multiply the BPM in the range [fStartBeat,fEndBeat) by fFactor.
|
||||||
void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor )
|
void TimingData::MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor )
|
||||||
{
|
{
|
||||||
@@ -205,15 +223,14 @@ BPMSegment& TimingData::GetBPMSegmentAtBeat( float fBeat )
|
|||||||
return m_BPMSegments[i];
|
return m_BPMSegments[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut ) const
|
void TimingData::GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, int &iWarpEndOut ) const
|
||||||
{
|
{
|
||||||
fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds;
|
fElapsedTime += PREFSMAN->m_fGlobalOffsetSeconds;
|
||||||
|
|
||||||
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut );
|
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeatOut, fBPSOut, bFreezeOut, bDelayOut, iWarpBeginOut, iWarpEndOut );
|
||||||
}
|
}
|
||||||
|
|
||||||
//, int &iWarpOut
|
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, int &iWarpEndOut ) const
|
||||||
void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut ) const
|
|
||||||
{
|
{
|
||||||
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
|
// LOG->Trace( "GetBeatAndBPSFromElapsedTime( fElapsedTime = %f )", fElapsedTime );
|
||||||
const float fTime = fElapsedTime;
|
const float fTime = fElapsedTime;
|
||||||
@@ -254,18 +271,18 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float
|
|||||||
if( (fFreezeStartSecond >= fElapsedTime && !bIsDelay) ||
|
if( (fFreezeStartSecond >= fElapsedTime && !bIsDelay) ||
|
||||||
(fFreezeStartSecond > fElapsedTime && bIsDelay) )
|
(fFreezeStartSecond > fElapsedTime && bIsDelay) )
|
||||||
{
|
{
|
||||||
/* The time lies within the stop. */
|
// The time lies within the stop.
|
||||||
fBeatOut = NoteRowToBeat(m_StopSegments[j].m_iStartRow);
|
fBeatOut = NoteRowToBeat(m_StopSegments[j].m_iStartRow);
|
||||||
fBPSOut = fBPS;
|
fBPSOut = fBPS;
|
||||||
bFreezeOut = !bIsDelay;
|
bFreezeOut = !bIsDelay;
|
||||||
bDelayOut = bIsDelay;
|
bDelayOut = bIsDelay;
|
||||||
//bWarpOut = false;
|
//iWarpBeginOut = -1;
|
||||||
|
//iWarpEndOut = -1;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// by this point we should have the warps in their own place.
|
// by this point we should have the warps in their own place.
|
||||||
/*
|
|
||||||
for( unsigned j=0; j<m_WarpSegments.size(); j++ ) // foreach warp
|
for( unsigned j=0; j<m_WarpSegments.size(); j++ ) // foreach warp
|
||||||
{
|
{
|
||||||
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_WarpSegments[j].m_iStartRow )
|
if( !bIsFirstBPMSegment && iStartRowThisSegment >= m_WarpSegments[j].m_iStartRow )
|
||||||
@@ -273,12 +290,18 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float
|
|||||||
if( !bIsLastBPMSegment && m_WarpSegments[j].m_iStartRow > iStartRowNextSegment )
|
if( !bIsLastBPMSegment && m_WarpSegments[j].m_iStartRow > iStartRowNextSegment )
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
// this warp lies within this BPMSegment.
|
||||||
|
iWarpBeginOut = m_WarpSegments[j].m_iStartRow;
|
||||||
|
iWarpEndOut = m_WarpSegments[j].m_iEndRow;
|
||||||
|
|
||||||
// this warp lies within this BPMSegment, and these are wrong
|
// this warp lies within this BPMSegment, and these are wrong
|
||||||
|
// huh? -aj
|
||||||
|
/*
|
||||||
const int iRowsBeatsSinceStartOfSegment = m_WarpSegments[j].m_iStartRow - iStartRowThisSegment;
|
const int iRowsBeatsSinceStartOfSegment = m_WarpSegments[j].m_iStartRow - iStartRowThisSegment;
|
||||||
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
|
const float fBeatsSinceStartOfSegment = NoteRowToBeat(iRowsBeatsSinceStartOfSegment);
|
||||||
const float fWarpStartSecond = fBeatsSinceStartOfSegment / fBPS;
|
const float fWarpStartSecond = fBeatsSinceStartOfSegment / fBPS;
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
|
const float fBeatsInThisSegment = fStartBeatNextSegment - fStartBeatThisSegment;
|
||||||
const float fSecondsInThisSegment = fBeatsInThisSegment / fBPS;
|
const float fSecondsInThisSegment = fBeatsInThisSegment / fBPS;
|
||||||
@@ -291,17 +314,17 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float
|
|||||||
*/
|
*/
|
||||||
if( bIsLastBPMSegment || fElapsedTime <= fSecondsInThisSegment )
|
if( bIsLastBPMSegment || fElapsedTime <= fSecondsInThisSegment )
|
||||||
{
|
{
|
||||||
// this BPMSegment IS the current segment
|
// this BPMSegment IS the current segment.
|
||||||
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
|
fBeatOut = fStartBeatThisSegment + fElapsedTime*fBPS;
|
||||||
fBPSOut = fBPS;
|
fBPSOut = fBPS;
|
||||||
bFreezeOut = false;
|
bFreezeOut = false;
|
||||||
bDelayOut = false;
|
bDelayOut = false;
|
||||||
//bWarpOut = false;
|
//iWarpBeginOut;
|
||||||
|
//iWarpEndOut;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// this BPMSegment is NOT the current segment
|
// this BPMSegment is NOT the current segment.
|
||||||
fElapsedTime -= fSecondsInThisSegment;
|
fElapsedTime -= fSecondsInThisSegment;
|
||||||
// xxx: negative testing
|
// xxx: negative testing
|
||||||
/*
|
/*
|
||||||
@@ -317,13 +340,13 @@ void TimingData::GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float
|
|||||||
// If we get here, something has gone wrong. Is everything sorted?
|
// If we get here, something has gone wrong. Is everything sorted?
|
||||||
vector<BPMSegment> vBPMS = m_BPMSegments;
|
vector<BPMSegment> vBPMS = m_BPMSegments;
|
||||||
vector<StopSegment> vSS = m_StopSegments;
|
vector<StopSegment> vSS = m_StopSegments;
|
||||||
//vector<WarpSegment> vWS = m_WarpSegments;
|
vector<WarpSegment> vWS = m_WarpSegments;
|
||||||
sort( vBPMS.begin(), vBPMS.end() );
|
sort( vBPMS.begin(), vBPMS.end() );
|
||||||
sort( vSS.begin(), vSS.end() );
|
sort( vSS.begin(), vSS.end() );
|
||||||
//sort( vWS.begin(), vWS.end() );
|
sort( vWS.begin(), vWS.end() );
|
||||||
ASSERT_M( vBPMS == m_BPMSegments, "The BPM segments were not sorted!" );
|
ASSERT_M( vBPMS == m_BPMSegments, "The BPM segments were not sorted!" );
|
||||||
ASSERT_M( vSS == m_StopSegments, "The Stop segments were not sorted!" );
|
ASSERT_M( vSS == m_StopSegments, "The Stop segments were not sorted!" );
|
||||||
//ASSERT_M( vWS == m_WarpSegments, "The Warp segments were not sorted!" );
|
ASSERT_M( vWS == m_WarpSegments, "The Warp segments were not sorted!" );
|
||||||
FAIL_M( ssprintf("Failed to find the appropriate segment for elapsed time %f.", fTime) );
|
FAIL_M( ssprintf("Failed to find the appropriate segment for elapsed time %f.", fTime) );
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -375,7 +398,9 @@ float TimingData::GetElapsedTimeFromBeatNoOffset( float fBeat ) const
|
|||||||
/*
|
/*
|
||||||
for( unsigned i=0; i<m_WarpSegments.size(); i++ ) // foreach WarpSegment
|
for( unsigned i=0; i<m_WarpSegments.size(); i++ ) // foreach WarpSegment
|
||||||
{
|
{
|
||||||
// do some crap with warps here
|
// todo: is this correct? -aj
|
||||||
|
const int iWarpToRow = m_WarpSegments[i].m_iEndRow;
|
||||||
|
fElapsedTime += NoteRowToBeat( iWarpToRow );
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -499,12 +524,10 @@ bool TimingData::HasStops() const
|
|||||||
return m_StopSegments.size()>0;
|
return m_StopSegments.size()>0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
bool TimingData::HasWarps() const
|
bool TimingData::HasWarps() const
|
||||||
{
|
{
|
||||||
return m_WarpSegments.size()>0;
|
return m_WarpSegments.size()>0;
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
|
void TimingData::NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const
|
||||||
{
|
{
|
||||||
@@ -614,6 +637,7 @@ public:
|
|||||||
LuaHelpers::CreateTableFromArray(fBPMs, L);
|
LuaHelpers::CreateTableFromArray(fBPMs, L);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
static int HasNegativeBPMs( T* p, lua_State *L ) { lua_pushboolean(L, p->m_bHasNegativeBpms); return 1; }
|
||||||
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
||||||
static int GetBPMAtBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBPMAtBeat(FArg(1))); return 1; }
|
static int GetBPMAtBeat( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBPMAtBeat(FArg(1))); return 1; }
|
||||||
static int GetBeatFromElapsedTime( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBeatFromElapsedTime(FArg(1))); return 1; }
|
static int GetBeatFromElapsedTime( T* p, lua_State *L ) { lua_pushnumber(L, p->GetBeatFromElapsedTime(FArg(1))); return 1; }
|
||||||
@@ -628,6 +652,7 @@ public:
|
|||||||
ADD_METHOD( GetBPMs );
|
ADD_METHOD( GetBPMs );
|
||||||
ADD_METHOD( GetBPMsAndTimes );
|
ADD_METHOD( GetBPMsAndTimes );
|
||||||
ADD_METHOD( GetActualBPM );
|
ADD_METHOD( GetActualBPM );
|
||||||
|
ADD_METHOD( HasNegativeBPMs );
|
||||||
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
// formerly in Song.cpp in sm-ssc private beta 1.x:
|
||||||
ADD_METHOD( GetBPMAtBeat );
|
ADD_METHOD( GetBPMAtBeat );
|
||||||
ADD_METHOD( GetBeatFromElapsedTime );
|
ADD_METHOD( GetBeatFromElapsedTime );
|
||||||
|
|||||||
+15
-14
@@ -86,9 +86,9 @@ struct TimeSignatureSegment
|
|||||||
};
|
};
|
||||||
|
|
||||||
/* A warp segment is used to replicate the effects of Negative BPMs without
|
/* A warp segment is used to replicate the effects of Negative BPMs without
|
||||||
* ausing negative BPMs. Negative BPMs should be converted to warp segments.
|
* abusing negative BPMs. Negative BPMs should be converted to warp segments.
|
||||||
* WarpAt=WarpTo is the format, where both are in beats. */
|
* WarpAt=WarpTo is the format, where both are in beats. (Technically they're
|
||||||
/*
|
* both rows though.) */
|
||||||
struct WarpSegment
|
struct WarpSegment
|
||||||
{
|
{
|
||||||
WarpSegment() : m_iStartRow(-1), m_iEndRow(-1) { }
|
WarpSegment() : m_iStartRow(-1), m_iEndRow(-1) { }
|
||||||
@@ -105,7 +105,6 @@ struct WarpSegment
|
|||||||
bool operator!=( const WarpSegment &other ) const { return !operator==(other); }
|
bool operator!=( const WarpSegment &other ) const { return !operator==(other); }
|
||||||
bool operator<( const WarpSegment &other ) const { return m_iStartRow < other.m_iStartRow; }
|
bool operator<( const WarpSegment &other ) const { return m_iStartRow < other.m_iStartRow; }
|
||||||
};
|
};
|
||||||
*/
|
|
||||||
|
|
||||||
class TimingData
|
class TimingData
|
||||||
{
|
{
|
||||||
@@ -121,40 +120,43 @@ public:
|
|||||||
void SetStopAtBeat( float fBeat, float fSeconds ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds ); }
|
void SetStopAtBeat( float fBeat, float fSeconds ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds ); }
|
||||||
void SetStopAtBeat( float fBeat, float fSeconds, bool bDelay ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds, bDelay ); } // (sm-ssc)
|
void SetStopAtBeat( float fBeat, float fSeconds, bool bDelay ) { SetStopAtRow( BeatToNoteRow(fBeat), fSeconds, bDelay ); } // (sm-ssc)
|
||||||
float GetStopAtRow( int iNoteRow, bool &bDelayOut ) const;
|
float GetStopAtRow( int iNoteRow, bool &bDelayOut ) const;
|
||||||
|
int GetWarpToRow( int iWarpBeginRow ) const;
|
||||||
void SetDelayAtRow( int iNoteRow, float fSeconds ); // sm-ssc
|
void SetDelayAtRow( int iNoteRow, float fSeconds ); // sm-ssc
|
||||||
void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor );
|
void MultiplyBPMInBeatRange( int iStartIndex, int iEndIndex, float fFactor );
|
||||||
void AddBPMSegment( const BPMSegment &seg );
|
void AddBPMSegment( const BPMSegment &seg );
|
||||||
void AddStopSegment( const StopSegment &seg );
|
void AddStopSegment( const StopSegment &seg );
|
||||||
void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
|
void AddTimeSignatureSegment( const TimeSignatureSegment &seg );
|
||||||
//void AddWarpSegment( const WarpSegment &seg );
|
void AddWarpSegment( const WarpSegment &seg );
|
||||||
int GetBPMSegmentIndexAtBeat( float fBeat );
|
int GetBPMSegmentIndexAtBeat( float fBeat );
|
||||||
const TimeSignatureSegment& GetTimeSignatureSegmentAtBeat( float fBeat ) const;
|
const TimeSignatureSegment& GetTimeSignatureSegmentAtBeat( float fBeat ) const;
|
||||||
BPMSegment& GetBPMSegmentAtBeat( float fBeat );
|
BPMSegment& GetBPMSegmentAtBeat( float fBeat );
|
||||||
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
|
void NoteRowToMeasureAndBeat( int iNoteRow, int &iMeasureIndexOut, int &iBeatIndexOut, int &iRowsRemainder ) const;
|
||||||
|
|
||||||
void GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut ) const;
|
void GetBeatAndBPSFromElapsedTime( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, int &iWarpEndOut ) const;
|
||||||
float GetBeatFromElapsedTime( float fElapsedTime ) const // shortcut for places that care only about the beat
|
float GetBeatFromElapsedTime( float fElapsedTime ) const // shortcut for places that care only about the beat
|
||||||
{
|
{
|
||||||
float fBeat, fThrowAway;
|
float fBeat, fThrowAway;
|
||||||
bool bThrowAway, bThrowAway2;
|
bool bThrowAway, bThrowAway2;
|
||||||
GetBeatAndBPSFromElapsedTime( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2 );
|
int iThrowAway, iThrowAway2;
|
||||||
|
GetBeatAndBPSFromElapsedTime( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2, iThrowAway, iThrowAway2 );
|
||||||
return fBeat;
|
return fBeat;
|
||||||
}
|
}
|
||||||
float GetElapsedTimeFromBeat( float fBeat ) const;
|
float GetElapsedTimeFromBeat( float fBeat ) const;
|
||||||
|
|
||||||
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut ) const;
|
void GetBeatAndBPSFromElapsedTimeNoOffset( float fElapsedTime, float &fBeatOut, float &fBPSOut, bool &bFreezeOut, bool &bDelayOut, int &iWarpBeginOut, int &iWarpEndOut ) const;
|
||||||
float GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
|
float GetBeatFromElapsedTimeNoOffset( float fElapsedTime ) const // shortcut for places that care only about the beat
|
||||||
{
|
{
|
||||||
float fBeat, fThrowAway;
|
float fBeat, fThrowAway;
|
||||||
bool bThrowAway, bThrowAway2;
|
bool bThrowAway, bThrowAway2;
|
||||||
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2 );
|
int iThrowAway, iThrowAway2;
|
||||||
|
GetBeatAndBPSFromElapsedTimeNoOffset( fElapsedTime, fBeat, fThrowAway, bThrowAway, bThrowAway2, iThrowAway, iThrowAway2 );
|
||||||
return fBeat;
|
return fBeat;
|
||||||
}
|
}
|
||||||
float GetElapsedTimeFromBeatNoOffset( float fBeat ) const;
|
float GetElapsedTimeFromBeatNoOffset( float fBeat ) const;
|
||||||
|
|
||||||
bool HasBpmChanges() const;
|
bool HasBpmChanges() const;
|
||||||
bool HasStops() const;
|
bool HasStops() const;
|
||||||
// bool HasWarps() const;
|
bool HasWarps() const;
|
||||||
|
|
||||||
bool operator==( const TimingData &other )
|
bool operator==( const TimingData &other )
|
||||||
{
|
{
|
||||||
@@ -164,11 +166,9 @@ public:
|
|||||||
COMPARE( m_StopSegments.size() );
|
COMPARE( m_StopSegments.size() );
|
||||||
for( unsigned i=0; i<m_StopSegments.size(); i++ )
|
for( unsigned i=0; i<m_StopSegments.size(); i++ )
|
||||||
COMPARE( m_StopSegments[i] );
|
COMPARE( m_StopSegments[i] );
|
||||||
/*
|
|
||||||
COMPARE( m_WarpSegments.size() );
|
COMPARE( m_WarpSegments.size() );
|
||||||
for( unsigned i=0; i<m_WarpSegments.size(); i++ )
|
for( unsigned i=0; i<m_WarpSegments.size(); i++ )
|
||||||
COMPARE( m_WarpSegments[i] );
|
COMPARE( m_WarpSegments[i] );
|
||||||
*/
|
|
||||||
COMPARE( m_fBeat0OffsetInSeconds );
|
COMPARE( m_fBeat0OffsetInSeconds );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -181,12 +181,13 @@ public:
|
|||||||
// Lua
|
// Lua
|
||||||
void PushSelf( lua_State *L );
|
void PushSelf( lua_State *L );
|
||||||
|
|
||||||
RString m_sFile; // informational only
|
RString m_sFile; // informational only
|
||||||
vector<BPMSegment> m_BPMSegments; // this must be sorted before gameplay
|
vector<BPMSegment> m_BPMSegments; // this must be sorted before gameplay
|
||||||
vector<StopSegment> m_StopSegments; // this must be sorted before gameplay
|
vector<StopSegment> m_StopSegments; // this must be sorted before gameplay
|
||||||
vector<TimeSignatureSegment> m_vTimeSignatureSegments; // this must be sorted before gameplay
|
vector<TimeSignatureSegment> m_vTimeSignatureSegments; // this must be sorted before gameplay
|
||||||
// vector<WarpSegment> m_vWarpSegments; // this must be sorted before gameplay
|
vector<WarpSegment> m_WarpSegments; // this must be sorted before gameplay
|
||||||
float m_fBeat0OffsetInSeconds;
|
float m_fBeat0OffsetInSeconds;
|
||||||
|
bool m_bHasNegativeBpms; // only used for Lua bindings in Song (to be moved to TimingData later)
|
||||||
};
|
};
|
||||||
|
|
||||||
#undef COMPARE
|
#undef COMPARE
|
||||||
|
|||||||
Reference in New Issue
Block a user