Files
itgmania212121/src/TimingSegments.h
T
Jason Felds 4f07a0ea8b war on -Werror, part 7: start informal params.
I'm preserving our numbering Pohly. ;)
2012-12-26 23:23:59 -05:00

744 lines
22 KiB
C++

#ifndef TIMING_SEGMENTS_H
#define TIMING_SEGMENTS_H
#include "NoteTypes.h" // Converting rows to beats and vice~versa.
enum TimingSegmentType
{
SEGMENT_BPM,
SEGMENT_STOP,
SEGMENT_DELAY,
SEGMENT_TIME_SIG,
SEGMENT_WARP,
SEGMENT_LABEL,
SEGMENT_TICKCOUNT,
SEGMENT_COMBO,
SEGMENT_SPEED,
SEGMENT_SCROLL,
SEGMENT_FAKE,
NUM_TimingSegmentType,
TimingSegmentType_Invalid,
};
// XXX: dumb names
enum SegmentEffectType
{
SegmentEffectType_Row, // takes effect on a single row
SegmentEffectType_Range, // takes effect for a definite amount of rows
SegmentEffectType_Indefinite, // takes effect until the next segment of its type
NUM_SegmentEffectType,
SegmentEffectType_Invalid,
};
#define FOREACH_TimingSegmentType(tst) FOREACH_ENUM(TimingSegmentType, tst)
const RString& TimingSegmentTypeToString( TimingSegmentType tst );
const int ROW_INVALID = -1;
#define COMPARE(x) if( this->x!=other.x ) return false
#define COMPARE_FLOAT(x) if( fabsf(this->x - other.x) > EPSILON ) return false
/**
* @brief The base timing segment for make glorious benefit wolfman
* XXX: this should be an abstract class.
*/
struct TimingSegment
{
virtual TimingSegmentType GetType() const { return TimingSegmentType_Invalid; }
virtual SegmentEffectType GetEffectType() const { return SegmentEffectType_Invalid; }
virtual TimingSegment* Copy() const = 0;
virtual bool IsNotable() const = 0;
virtual void DebugPrint() const;
// don't allow base TimingSegments to be instantiated directly
TimingSegment( int iRow = ROW_INVALID ) : m_iStartRow(iRow) { }
TimingSegment( float fBeat ) : m_iStartRow(ToNoteRow(fBeat)) { }
TimingSegment(const TimingSegment &other) :
m_iStartRow( other.GetRow() ) { }
// for our purposes, two floats within this level of error are equal
static const double EPSILON;
virtual ~TimingSegment() { }
/**
* @brief Scales itself.
* @param start Starting row
* @param length Length in rows
* @param newLength The new length in rows
*/
virtual void Scale( int start, int length, int newLength );
int GetRow() const { return m_iStartRow; }
void SetRow( int iRow ) { m_iStartRow = iRow; }
float GetBeat() const { return NoteRowToBeat(m_iStartRow); }
void SetBeat( float fBeat ) { SetRow( BeatToNoteRow(fBeat) ); }
virtual RString ToString(int /* dec */) const
{
return FloatToString(GetBeat());
}
bool operator<( const TimingSegment &other ) const
{
return GetRow() < other.GetRow();
}
// overloads should not call this base version; derived classes
// should only compare contents, and this compares position.
virtual bool operator==( const TimingSegment &other ) const
{
return GetRow() == other.GetRow();
}
virtual bool operator!=( const TimingSegment &other ) const
{
return !this->operator==(other);
}
private:
/** @brief The row in which this segment activates. */
int m_iStartRow;
};
/**
* @brief Identifies when a whole region of arrows is to be ignored.
*
* FakeSegments are similar to the Fake Tap Notes in that the contents
* inside are neither for nor against the player. They can be useful for
* mission modes, in conjunction with WarpSegments, or perhaps other
* uses not thought up at the time of this comment. Unlike the Warp
* Segments, these are not magically jumped over: instead, these are
* drawn normally.
*
* These were inspired by the Pump It Up series. */
struct FakeSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_FAKE; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Range; }
TimingSegment* Copy() const { return new FakeSegment(*this); }
bool IsNotable() const { return m_iLengthRows > 0; }
void DebugPrint() const;
FakeSegment() : TimingSegment(), m_iLengthRows(-1) { }
FakeSegment( int iStartRow, int iLengthRows ) :
TimingSegment(iStartRow), m_iLengthRows(iLengthRows) { }
FakeSegment( int iStartRow, float fBeats ) :
TimingSegment(iStartRow), m_iLengthRows(ToNoteRow(fBeats)) { }
FakeSegment( const FakeSegment &other ) :
TimingSegment( other.GetRow() ),
m_iLengthRows( other.GetLengthRows() ) { }
int GetLengthRows() const { return m_iLengthRows; }
float GetLengthBeats() const { return ToBeat(m_iLengthRows); }
float GetLength() const { return GetLengthBeats(); } // compatibility
void SetLength( int iRows ) { m_iLengthRows = ToNoteRow(iRows); }
void SetLength( float fBeats ) { m_iLengthRows = ToNoteRow(fBeats); }
void Scale( int start, int length, int newLength );
RString ToString( int dec ) const;
bool operator==( const FakeSegment &other ) const
{
COMPARE( m_iLengthRows );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const FakeSegment&>(other) );
}
private:
/** @brief The number of rows the FakeSegment is alive for. */
int m_iLengthRows;
};
/**
* @brief Identifies when a song needs to warp to a new beat.
*
* A warp segment is used to replicate the effects of Negative BPMs without
* abusing negative BPMs. Negative BPMs should be converted to warp segments.
* WarpAt=WarpToRelative is the format, where both are in beats.
* (Technically they're both rows though.) */
struct WarpSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_WARP; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Range; }
TimingSegment* Copy() const { return new WarpSegment(*this); }
bool IsNotable() const { return m_iLengthRows > 0; }
void DebugPrint() const;
WarpSegment() : TimingSegment(), m_iLengthRows(0) { }
WarpSegment( const WarpSegment &other ) :
TimingSegment( other.GetRow() ),
m_iLengthRows( other.GetLengthRows() ) { }
WarpSegment( int iStartRow, int iLengthRows ) :
TimingSegment(iStartRow), m_iLengthRows(iLengthRows) { }
WarpSegment( int iStartRow, float fBeats ) :
TimingSegment(iStartRow), m_iLengthRows(ToNoteRow(fBeats)) { }
int GetLengthRows() const { return m_iLengthRows; }
float GetLengthBeats() const { return ToBeat(m_iLengthRows); }
float GetLength() const { return GetLengthBeats(); } // compatibility
void SetLength( int iRows ) { m_iLengthRows = ToNoteRow(iRows); }
void SetLength( float fBeats ) { m_iLengthRows = ToNoteRow(fBeats); }
void Scale( int start, int length, int newLength );
RString ToString( int dec ) const;
bool operator==( const WarpSegment &other ) const
{
COMPARE( m_iLengthRows );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const WarpSegment&>(other) );
}
private:
/** @brief The number of rows the WarpSegment will warp past. */
int m_iLengthRows;
};
/**
* @brief Identifies when a chart is to have a different tickcount value
* for hold notes.
*
* A tickcount segment is used to better replicate the checkpoint hold
* system used by various based video games. The number is used to
* represent how many ticks can be counted in one beat.
*/
struct TickcountSegment : public TimingSegment
{
/** @brief The default amount of ticks per beat. */
static const unsigned DEFAULT_TICK_COUNT = 4;
TimingSegmentType GetType() const { return SEGMENT_TICKCOUNT; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new TickcountSegment(*this); }
TickcountSegment( int iStartRow = ROW_INVALID, int iTicks = DEFAULT_TICK_COUNT ) :
TimingSegment(iStartRow), m_iTicksPerBeat(iTicks) { }
TickcountSegment( const TickcountSegment &other ) :
TimingSegment( other.GetRow() ),
m_iTicksPerBeat( other.GetTicks() ) { }
int GetTicks() const { return m_iTicksPerBeat; }
void SetTicks( int iTicks ) { m_iTicksPerBeat = iTicks; }
RString ToString( int dec ) const;
bool operator==( const TickcountSegment &other ) const
{
COMPARE( m_iTicksPerBeat );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const TickcountSegment&>(other) );
}
private:
/** @brief The amount of hold checkpoints counted per beat */
int m_iTicksPerBeat;
};
/**
* @brief Identifies when a chart is to have a different combo multiplier value.
*
* Admitedly, this would primarily be used for mission mode style charts. However,
* it can have its place during normal gameplay.
*/
struct ComboSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_COMBO; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new ComboSegment(*this); }
ComboSegment( int iStartRow = ROW_INVALID, int iCombo = 1, int iMissCombo = 1 ) :
TimingSegment(iStartRow), m_iCombo(iCombo),
m_iMissCombo(iMissCombo) { }
ComboSegment(const ComboSegment &other) :
TimingSegment( other.GetRow() ),
m_iCombo( other.GetCombo() ),
m_iMissCombo( other.GetMissCombo() ) { }
int GetCombo() const { return m_iCombo; }
int GetMissCombo() const { return m_iMissCombo; }
void SetCombo( int iCombo ) { m_iCombo = iCombo; }
void SetMissCombo( int iCombo ) { m_iMissCombo = iCombo; }
RString ToString( int dec ) const;
bool operator==( const ComboSegment &other ) const
{
COMPARE( m_iCombo );
COMPARE( m_iMissCombo );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const ComboSegment&>(other) );
}
private:
/** @brief The amount the combo increases at this point. */
int m_iCombo;
/** @brief The amount of miss combos given at this point. */
int m_iMissCombo;
};
/**
* @brief Identifies when a chart is entering a different section.
*
* This is meant for helping to identify different sections of a chart
* versus relying on measures and beats alone.
*/
struct LabelSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_LABEL; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new LabelSegment(*this); }
LabelSegment( int iStartRow = ROW_INVALID, const RString& sLabel = RString() ) :
TimingSegment(iStartRow), m_sLabel(sLabel) { }
LabelSegment(const LabelSegment &other) :
TimingSegment( other.GetRow() ),
m_sLabel( other.GetLabel() ) { }
const RString& GetLabel() const { return m_sLabel; }
void SetLabel( const RString& sLabel ) { m_sLabel.assign(sLabel); }
RString ToString( int dec ) const;
bool operator==( const LabelSegment &other ) const
{
COMPARE( m_sLabel );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const LabelSegment&>(other) );
}
private:
/** @brief The label/section name for this point. */
RString m_sLabel;
};
/**
* @brief Identifies when a song changes its BPM.
*/
struct BPMSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_BPM; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new BPMSegment(*this); }
// note that this takes a BPM, not a BPS (compatibility)
BPMSegment( int iStartRow = ROW_INVALID, float fBPM = 0.0f ) :
TimingSegment(iStartRow) { SetBPM(fBPM); }
BPMSegment( const BPMSegment &other ) :
TimingSegment( other.GetRow() ),
m_fBPS( other.GetBPS() ) { }
float GetBPS() const { return m_fBPS; }
float GetBPM() const { return m_fBPS * 60.0f; }
void SetBPS( float fBPS ) { m_fBPS = fBPS; }
void SetBPM( float fBPM ) { m_fBPS = fBPM / 60.0f; }
RString ToString( int dec ) const;
bool operator==( const BPMSegment &other ) const
{
COMPARE_FLOAT( m_fBPS );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const BPMSegment&>(other) );
}
private:
/** @brief The number of beats per second within this BPMSegment. */
float m_fBPS;
};
/**
* @brief Identifies when a song changes its time signature.
*
* This only supports simple time signatures. The upper number
* (called the numerator here, though this isn't properly a
* fraction) is the number of beats per measure. The lower number
* (denominator here) is the note value representing one beat. */
struct TimeSignatureSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_TIME_SIG; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new TimeSignatureSegment(*this); }
TimeSignatureSegment( int iStartRow = ROW_INVALID,
int iNum = 4, int iDenom = 4 ) :
TimingSegment(iStartRow), m_iNumerator(iNum),
m_iDenominator(iDenom) { }
TimeSignatureSegment( const TimeSignatureSegment &other ) :
TimingSegment( other.GetRow() ),
m_iNumerator( other.GetNum() ),
m_iDenominator( other.GetDen() ) { }
int GetNum() const { return m_iNumerator; }
void SetNum( int num ) { m_iNumerator = num; }
int GetDen() const { return m_iDenominator; }
void SetDen( int den ) { m_iDenominator = den; }
void Set( int num, int den ) { m_iNumerator = num; m_iDenominator = den; }
RString ToString( int dec ) const;
/**
* @brief Retrieve the number of note rows per measure within the TimeSignatureSegment.
*
* With BeatToNoteRow(1) rows per beat, then we should have BeatToNoteRow(1)*m_iNumerator
* beats per measure. But if we assume that every BeatToNoteRow(1) rows is a quarter note,
* and we want the beats to be 1/m_iDenominator notes, then we should have
* BeatToNoteRow(1)*4 is rows per whole note and thus BeatToNoteRow(1)*4/m_iDenominator is
* rows per beat. Multiplying by m_iNumerator gives rows per measure.
* @returns the number of note rows per measure.
*/
int GetNoteRowsPerMeasure() const
{
return BeatToNoteRow(1) * 4 * m_iNumerator / m_iDenominator;
}
bool operator==( const TimeSignatureSegment &other ) const
{
COMPARE( m_iNumerator );
COMPARE( m_iDenominator );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const TimeSignatureSegment&>(other) );
}
private:
int m_iNumerator, m_iDenominator;
};
/**
* @brief Identifies when the arrow scroll changes.
*
* SpeedSegments take a Player's scrolling BPM (Step's BPM * speed mod),
* and then multiplies it with the percentage value. No matter the player's
* speed mod, the ratio will be the same. Unlike forced attacks, these
* cannot be turned off at a set time: reset it by setting the percentage
* back to 1.
*
* These were inspired by the Pump It Up series. */
struct SpeedSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_SPEED; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new SpeedSegment(*this); }
/** @brief The type of unit used for segment scaling. */
enum BaseUnit { UNIT_BEATS, UNIT_SECONDS };
SpeedSegment( int iStartRow = ROW_INVALID, float fRatio = 1.0f,
float fDelay = 0.0f, BaseUnit unit = UNIT_BEATS ) :
TimingSegment(iStartRow), m_fRatio(fRatio), m_fDelay(fDelay),
m_Unit(unit) { }
SpeedSegment(const SpeedSegment &other) :
TimingSegment( other.GetRow() ),
m_fRatio( other.GetRatio() ),
m_fDelay( other.GetDelay() ),
m_Unit( other.GetUnit() ) { }
float GetRatio() const { return m_fRatio; }
void SetRatio( float fRatio ) { m_fRatio = fRatio; }
float GetDelay() const { return m_fDelay; }
void SetDelay( float fDelay ) { m_fDelay = fDelay; }
BaseUnit GetUnit() const { return m_Unit; }
void SetUnit( BaseUnit unit ) { m_Unit = unit; }
void Scale( int start, int length, int newLength );
RString ToString( int dec ) const;
bool operator==( const SpeedSegment &other ) const
{
COMPARE_FLOAT( m_fRatio );
COMPARE_FLOAT( m_fDelay );
COMPARE( m_Unit );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const SpeedSegment&>(other) );
}
private:
/** @brief The percentage by which the Player's BPM is multiplied. */
float m_fRatio;
/**
* @brief The number of beats or seconds to wait before applying.
* A value of 0 means this is immediate. */
float m_fDelay;
/** @brief The mode that this segment uses for the math. */
BaseUnit m_Unit;
};
/**
* @brief Identifies when the chart scroll changes.
*
* ScrollSegments adjusts the scrolling speed of the note field.
* Unlike forced attacks, these cannot be turned off at a set time:
* reset it by setting the precentage back to 1.
*
* These were inspired by the Pump It Up series. */
struct ScrollSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_SCROLL; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Indefinite; }
bool IsNotable() const { return true; } // indefinite segments are always true
void DebugPrint() const;
TimingSegment* Copy() const { return new ScrollSegment(*this); }
ScrollSegment( int iStartRow = ROW_INVALID, float fRatio = 1.0f ) :
TimingSegment(iStartRow), m_fRatio(fRatio) { }
ScrollSegment(const ScrollSegment &other) :
TimingSegment( other.GetRow() ),
m_fRatio( other.GetRatio() ) { }
float GetRatio() const { return m_fRatio; }
void SetRatio( float fRatio ) { m_fRatio = fRatio; }
RString ToString( int dec ) const;
bool operator==( const ScrollSegment &other ) const
{
COMPARE_FLOAT( m_fRatio );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const ScrollSegment&>(other) );
}
private:
/** @brief The percentage by which the chart's scroll rate is multiplied. */
float m_fRatio;
};
/**
* @brief Identifies when a song has a stop, DDR/ITG style.
*/
struct StopSegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_STOP; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Row; }
bool IsNotable() const { return m_fSeconds > 0; }
void DebugPrint() const;
TimingSegment* Copy() const { return new StopSegment(*this); }
StopSegment( int iStartRow = ROW_INVALID, float fSeconds = 0.0f ) :
TimingSegment(iStartRow), m_fSeconds(fSeconds) { }
StopSegment (const StopSegment &other) :
TimingSegment( other.GetRow() ),
m_fSeconds( other.GetPause() ) { }
float GetPause() const { return m_fSeconds; }
void SetPause( float fSeconds ) { m_fSeconds = fSeconds; }
RString ToString( int dec ) const;
bool operator==( const StopSegment &other ) const
{
COMPARE_FLOAT( m_fSeconds );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const StopSegment&>(other) );
}
private:
/** @brief The number of seconds to pause at the segment's row. */
float m_fSeconds;
};
/**
* @brief Identifies when a song has a delay, or pump style stop.
*/
struct DelaySegment : public TimingSegment
{
TimingSegmentType GetType() const { return SEGMENT_DELAY; }
SegmentEffectType GetEffectType() const { return SegmentEffectType_Row; }
bool IsNotable() const { return m_fSeconds > 0; }
void DebugPrint() const;
TimingSegment* Copy() const { return new DelaySegment(*this); }
DelaySegment( int iStartRow = ROW_INVALID, float fSeconds = 0 ) :
TimingSegment(iStartRow), m_fSeconds(fSeconds) { }
DelaySegment( const DelaySegment &other ) :
TimingSegment( other.GetRow() ),
m_fSeconds( other.GetPause() ) { }
float GetPause() const { return m_fSeconds; }
void SetPause( float fSeconds ) { m_fSeconds = fSeconds; }
RString ToString( int dec ) const;
bool operator==( const DelaySegment &other ) const
{
COMPARE_FLOAT( m_fSeconds );
return true;
}
bool operator==( const TimingSegment &other ) const
{
if( GetType() != other.GetType() )
return false;
return operator==( static_cast<const DelaySegment&>(other) );
}
private:
/** @brief The number of seconds to pause at the segment's row. */
float m_fSeconds;
};
#undef COMPARE
#undef COMPARE_FLOAT
#endif
/**
* @file
* @author Jason Felds (c) 2011
* @section LICENSE
* 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.
*/