diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt
index 874e230a6e..254e40a4ce 100644
--- a/Docs/Changelog_sm5.txt
+++ b/Docs/Changelog_sm5.txt
@@ -5,9 +5,25 @@ from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt.
________________________________________________________________________________
================================================================================
-StepMania 5.0 Preview 2 | 20110???
+StepMania 5.0 Preview 2 | 201106??
--------------------------------------------------------------------------------
+2011/06/19
+* [EditMenu] Allow all songs to work in Practice Mode, regardless of folder
+ location. This fixes a crash if everyone had songs in AdditionalSongs.
+ [Wolfman2000]
+
+2011/06/17
+----------
+* [LifeMeterBattery] Fixed a bug where MinesSubtractLives was not being honored.
+ Also added LifeChanged message to changing of life on holds. [AJ]
+
+2011/06/15
+----------
+* [TimingData] Added many lua bindings for the other timing segments. GetWarps,
+ GetCombos, GetTimeSignatures, GetTickcounts, GetFakes, GetScrolls, GetSpeeds.
+ This should cover all of them now. [Wolfman2000]
+
2011/06/13
----------
* [InputHandler_DirectInput] Fixed MouseWheel input not resetting. [AJ]
diff --git a/Docs/Luadoc/Lua.xml b/Docs/Luadoc/Lua.xml
index f88325bed1..521b2c5e68 100644
--- a/Docs/Luadoc/Lua.xml
+++ b/Docs/Luadoc/Lua.xml
@@ -1430,10 +1430,17 @@
+
+
+
+
+
+
+
diff --git a/Docs/Luadoc/LuaDocumentation.xml b/Docs/Luadoc/LuaDocumentation.xml
index 30e4ff0436..659e951332 100644
--- a/Docs/Luadoc/LuaDocumentation.xml
+++ b/Docs/Luadoc/LuaDocumentation.xml
@@ -3611,6 +3611,27 @@
Returns a table of the Labels and the times they happen as strings with the format "beat=label name."
+
+ Returns a table of the Warps and the times they happen as strings with the format "beat=number of beats warped over."
+
+
+ Returns a table of the Combos and the times they happen as strings with the format "beat=combo value."
+
+
+ Returns a table of the Time Signatures and the times they happen as strings with the format "beat=beats per measure (numerator)=denominator."
+
+
+ Returns a table of the Tickcountss and the times they happen as strings with the format "beat=number of ticks per beat."
+
+
+ Returns a table of the Fakes and the times they happen as strings with the format "beat=number of beats to not judge."
+
+
+ Returns a table of the Scrolls and the times they happen as strings with the format "beat=scroll rate ratio."
+
+
+ Returns a table of the Speeds and the times they happen as strings with the format "beat=scroll rate ratio=length of time to fully activate=unit of activation (0 for beats, 1 for seconds)."
+
Returns true if the TimingData contains BPM changes.
diff --git a/Themes/_fallback/Scripts/03 HSV.lua b/Themes/_fallback/Scripts/03 HSV.lua
index a4b56baa5d..c2fd988ea7 100644
--- a/Themes/_fallback/Scripts/03 HSV.lua
+++ b/Themes/_fallback/Scripts/03 HSV.lua
@@ -246,4 +246,16 @@ function Hue(color,newHue)
end
c.Hue = newHue
return HSVToColor(c)
+end;
+
+function Alpha(color,percent)
+ local c = ColorToHSV(color);
+ -- error checking
+ if percent < 0 then
+ percent = 0.0;
+ elseif percent > 1 then
+ percent = 1.0;
+ end;
+ c.Alpha = percent;
+ return HSVToColor(c);
end;
\ No newline at end of file
diff --git a/Themes/_fallback/Scripts/04 Scoring.lua b/Themes/_fallback/Scripts/04 Scoring.lua
index fd579db996..c918bb60d7 100644
--- a/Themes/_fallback/Scripts/04 Scoring.lua
+++ b/Themes/_fallback/Scripts/04 Scoring.lua
@@ -84,14 +84,19 @@ r['DDR Extreme'] = function(params, pss)
setmetatable(judgmentBase, ZeroIfNotFound);
local steps = GAMESTATE:GetCurrentSteps(params.Player);
local radarValues = steps:GetRadarValues(params.Player);
- local baseScore = (steps:IsAnEdit() and
- 5 or steps:GetMeter()) * 1000000;
+ local meter = steps:GetMeter();
+ if (steps:IsAnEdit()) then
+ meter = 5;
+ elseif (meter < 1) then
+ meter = 1;
+ elseif (meter > 10) then
+ meter = 10;
+ end;
+ local baseScore = meter * 1000000;
if (GAMESTATE:GetCurrentSong():IsMarathon()) then
baseScore = baseScore * 3;
- else
- if (GAMESTATE:GetCurrentSong():IsLong()) then
- baseScore = baseScore * 2;
- end;
+ elseif (GAMESTATE:GetCurrentSong():IsLong()) then
+ baseScore = baseScore * 2;
end;
local totalItems = GetTotalItems(radarValues);
local singleStep = (1 + totalItems) * totalItems / 2;
diff --git a/sm-ssc.nsi b/sm-ssc.nsi
index a7560744b1..653fe7458e 100644
--- a/sm-ssc.nsi
+++ b/sm-ssc.nsi
@@ -42,7 +42,7 @@
; don't forget to change this before releasing a new verson.
; wish this could be automated, but it requires "X.Y.Z.a" format. -aj
- VIProductVersion "1.2.3.0"
+ VIProductVersion "5.0.0.3"
VIAddVersionKey "ProductName" "${PRODUCT_ID}"
VIAddVersionKey "FileVersion" "${PRODUCT_VER}"
VIAddVersionKey "FileDescription" "${PRODUCT_ID} Installer"
diff --git a/src/AutoKeysounds.cpp b/src/AutoKeysounds.cpp
index 068036bd69..b536a3aa95 100644
--- a/src/AutoKeysounds.cpp
+++ b/src/AutoKeysounds.cpp
@@ -63,6 +63,10 @@ void AutoKeysounds::LoadAutoplaySoundsInto( RageSoundReader_Chain *pChain )
if( t >= m_ndAutoKeysoundsOnly[pn].GetNumTracks() )
continue;
int iNextRowForPlayer = iRow;
+ /* XXX: If a BMS file only has one tap note per track,
+ * this will prevent any keysounds from loading.
+ * This leads to failure later on.
+ * We need a better way to prevent this. */
if( m_ndAutoKeysoundsOnly[pn].GetNextTapNoteRowForTrack( t, iNextRowForPlayer ) )
iNextRow = min( iNextRow, iNextRowForPlayer );
}
@@ -274,7 +278,7 @@ void AutoKeysounds::FinishLoading()
delete pChain;
}
}
- ASSERT( m_pSharedSound );
+ ASSERT_M( m_pSharedSound, ssprintf("No keysounds were loaded for the song %s!", pSong->m_sMainTitle.c_str() ));
m_pSharedSound = new RageSoundReader_PitchChange( m_pSharedSound );
m_pSharedSound = new RageSoundReader_PostBuffering( m_pSharedSound );
diff --git a/src/EditMenu.cpp b/src/EditMenu.cpp
index 8b39585144..274ff975b8 100644
--- a/src/EditMenu.cpp
+++ b/src/EditMenu.cpp
@@ -60,7 +60,7 @@ void EditMenu::GetSongsToShowForGroup( const RString &sGroup, vector &vpS
for( int i=vpSongsOut.size()-1; i>=0; i-- )
{
const Song* pSong = vpSongsOut[i];
- if( !pSong->NormallyDisplayed() || pSong->IsTutorial() || SONGMAN->WasLoadedFromAdditionalSongs(pSong) )
+ if( !pSong->NormallyDisplayed() || pSong->IsTutorial() )
vpSongsOut.erase( vpSongsOut.begin()+i );
}
break;
diff --git a/src/EditMenu.h b/src/EditMenu.h
index 28c9730d81..dac6767899 100644
--- a/src/EditMenu.h
+++ b/src/EditMenu.h
@@ -10,6 +10,7 @@
#include "EnumHelper.h"
#include "ThemeMetric.h"
+/** @brief What type of row is needed for the EditMenu? */
enum EditMenuRow
{
ROW_GROUP,
@@ -19,11 +20,19 @@ enum EditMenuRow
ROW_SOURCE_STEPS_TYPE,
ROW_SOURCE_STEPS,
ROW_ACTION,
- NUM_EditMenuRow
+ NUM_EditMenuRow /**< The number of EditMenuRows available. */
};
/** @brief Loop through each EditMenuRow. */
#define FOREACH_EditMenuRow( r ) FOREACH_ENUM( EditMenuRow, r )
+/**
+ * @brief Turn the EditMenuRow into a string.
+ * @param r the row.
+ * @return the string. */
const RString& EditMenuRowToString( EditMenuRow r );
+/**
+ * @brief Turn the EditMenuRow into a localized string.
+ * @param r the row.
+ * @return the localized string. */
const RString& EditMenuRowToLocalizedString( EditMenuRow r );
/** @brief The different actions one can take on a step. */
@@ -38,9 +47,18 @@ enum EditMenuAction
};
/** @brief Loop through each EditMenuAction. */
#define FOREACH_EditMenuAction( ema ) FOREACH_ENUM( EditMenuAction, ema )
+/**
+ * @brief Turn the EditMenuAction into a string.
+ * @param ema the action.
+ * @return the string. */
const RString& EditMenuActionToString( EditMenuAction ema );
+/**
+ * @brief Turn the EditMenuAction into a localized string.
+ * @param ema the action.
+ * @return the localized string. */
const RString& EditMenuActionToLocalizedString( EditMenuAction ema );
+/** @brief How many arrows are used for the EditMenu? */
const int NUM_ARROWS = 2;
/**
@@ -50,39 +68,110 @@ const int NUM_ARROWS = 2;
class EditMenu: public ActorFrame
{
public:
+ /** @brief Set up the EditMenu. */
EditMenu();
+ /** @brief Destroy the EditMenu. */
~EditMenu();
void Load( const RString &sType );
+ /** @brief Determine if we can move up.
+ * @return true if we can, false otherwise. */
bool CanGoUp();
+ /** @brief Determine if we can move down.
+ * @return true if we can, false otherwise. */
bool CanGoDown();
+ /** @brief Determine if we can move left.
+ * @return true if we can, false otherwise. */
bool CanGoLeft();
+ /** @brief Determine if we can move right.
+ * @return true if we can, false otherwise. */
bool CanGoRight();
+ /** @brief Determine if the EditMenuRow is selectable.
+ * @param row the row in question.
+ * @return true if it can be selected, false otherwise. */
bool RowIsSelectable( EditMenuRow row );
+ /** @brief Move up to the next selection. */
void Up();
+ /** @brief Move down to the next selection. */
void Down();
+ /** @brief Move left to the next selection. */
void Left();
+ /** @brief Move right to the next selection. */
void Right();
void RefreshAll();
- RString GetSelectedGroup() const
+ /** @brief Retrieve the currently selected group.
+ * @return the current group. */
+ RString GetSelectedGroup() const
{
if( !SHOW_GROUPS.GetValue() ) return GROUP_ALL;
- ASSERT_M((int)m_iSelection[ROW_GROUP] < (int)m_sGroups.size(),
- ssprintf("Group selection %d < Number of groups %d", m_iSelection[ROW_GROUP], (int)m_sGroups.size()));
+ int groups = static_cast(m_sGroups.size());
+ ASSERT_M(m_iSelection[ROW_GROUP] < groups,
+ ssprintf("Group selection %d < Number of groups %d",
+ m_iSelection[ROW_GROUP],
+ groups));
return m_sGroups[m_iSelection[ROW_GROUP]];
}
- Song* GetSelectedSong() const { ASSERT(m_iSelection[ROW_SONG] < (int)m_pSongs.size()); return m_pSongs[m_iSelection[ROW_SONG]]; }
- StepsType GetSelectedStepsType() const { ASSERT(m_iSelection[ROW_STEPS_TYPE] < (int)m_StepsTypes.size()); return m_StepsTypes[m_iSelection[ROW_STEPS_TYPE]]; }
- Steps* GetSelectedSteps() const { ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size()); return m_vpSteps[m_iSelection[ROW_STEPS]].pSteps; }
- Difficulty GetSelectedDifficulty() const { ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size()); return m_vpSteps[m_iSelection[ROW_STEPS]].dc; }
- StepsType GetSelectedSourceStepsType() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS_TYPE] < (int)m_StepsTypes.size()); return m_StepsTypes[m_iSelection[ROW_SOURCE_STEPS_TYPE]]; }
- Steps* GetSelectedSourceSteps() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size()); return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].pSteps; }
- Difficulty GetSelectedSourceDifficulty() const { ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size()); return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].dc; }
- EditMenuAction GetSelectedAction() const { ASSERT(m_iSelection[ROW_ACTION] < (int)m_Actions.size()); return m_Actions[m_iSelection[ROW_ACTION]]; }
-
+ /** @brief Retrieve the currently selected song.
+ * @return the current song. */
+ Song* GetSelectedSong() const
+ {
+ ASSERT(m_iSelection[ROW_SONG] < (int)m_pSongs.size());
+ return m_pSongs[m_iSelection[ROW_SONG]];
+ }
+ /** @brief Retrieve the currently selected steps type.
+ * @return the current steps type. */
+ StepsType GetSelectedStepsType() const
+ {
+ ASSERT(m_iSelection[ROW_STEPS_TYPE] < (int)m_StepsTypes.size());
+ return m_StepsTypes[m_iSelection[ROW_STEPS_TYPE]];
+ }
+ /** @brief Retrieve the currently selected steps.
+ * @return the current steps. */
+ Steps* GetSelectedSteps() const
+ {
+ ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size());
+ return m_vpSteps[m_iSelection[ROW_STEPS]].pSteps;
+ }
+ /** @brief Retrieve the currently selected difficulty.
+ * @return the current difficulty. */
+ Difficulty GetSelectedDifficulty() const
+ {
+ ASSERT(m_iSelection[ROW_STEPS] < (int)m_vpSteps.size());
+ return m_vpSteps[m_iSelection[ROW_STEPS]].dc;
+ }
+ /** @brief Retrieve the currently selected source steps type.
+ * @return the current source steps type. */
+ StepsType GetSelectedSourceStepsType() const
+ {
+ ASSERT(m_iSelection[ROW_SOURCE_STEPS_TYPE] < (int)m_StepsTypes.size());
+ return m_StepsTypes[m_iSelection[ROW_SOURCE_STEPS_TYPE]];
+ }
+ /** @brief Retrieve the currently selected source steps.
+ * @return the current source steps. */
+ Steps* GetSelectedSourceSteps() const
+ {
+ ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size());
+ return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].pSteps;
+ }
+ /** @brief Retrieve the currently selected difficulty.
+ * @return the current difficulty. */
+ Difficulty GetSelectedSourceDifficulty() const
+ {
+ ASSERT(m_iSelection[ROW_SOURCE_STEPS] < (int)m_vpSourceSteps.size());
+ return m_vpSourceSteps[m_iSelection[ROW_SOURCE_STEPS]].dc;
+ }
+ /** @brief Retrieve the currently selected action.
+ * @return the current action. */
+ EditMenuAction GetSelectedAction() const
+ {
+ ASSERT(m_iSelection[ROW_ACTION] < (int)m_Actions.size());
+ return m_Actions[m_iSelection[ROW_ACTION]];
+ }
+ /** @brief Retrieve the currently selected row.
+ * @return the current row. */
EditMenuRow GetSelectedRow() const { return m_SelectedRow; }
private:
diff --git a/src/EnumHelper.h b/src/EnumHelper.h
index 3f9c60c295..dc1049cc0c 100644
--- a/src/EnumHelper.h
+++ b/src/EnumHelper.h
@@ -15,7 +15,12 @@ extern "C"
/** @brief A general foreach loop for enumerators. */
#define FOREACH_ENUM( e, var ) for( e var=(e)0; var( var, +1 ) )
-int CheckEnum( lua_State *L, LuaReference &table, int iPos, int iInvalid, const char *szType, bool bAllowInvalid );
+int CheckEnum(lua_State *L,
+ LuaReference &table,
+ int iPos,
+ int iInvalid,
+ const char *szType,
+ bool bAllowInvalid);
template
struct EnumTraits
@@ -33,7 +38,12 @@ namespace Enum
template
static T Check( lua_State *L, int iPos, bool bAllowInvalid = false )
{
- return (T) CheckEnum( L, EnumTraits::StringToEnum, iPos, EnumTraits::Invalid, EnumTraits::szName, bAllowInvalid );
+ return (T) CheckEnum(L,
+ EnumTraits::StringToEnum,
+ iPos,
+ EnumTraits::Invalid,
+ EnumTraits::szName,
+ bAllowInvalid);
}
template
static void Push( lua_State *L, T iVal )
@@ -57,40 +67,47 @@ namespace Enum
const RString &EnumToString( int iVal, int iMax, const char **szNameArray, auto_ptr *pNameCache ); // XToString helper
#define XToString(X) \
- const RString& X##ToString(X x); \
- COMPILE_ASSERT( NUM_##X == ARRAYLEN(X##Names) ); \
- const RString& X##ToString( X x ) \
- { \
- static auto_ptr as_##X##Name[NUM_##X+2]; \
- return EnumToString( x, NUM_##X, X##Names, as_##X##Name ); \
- } \
- namespace StringConversion { template<> RString ToString( const X &value ) { return X##ToString(value); } }
+const RString& X##ToString(X x); \
+COMPILE_ASSERT( NUM_##X == ARRAYLEN(X##Names) ); \
+const RString& X##ToString( X x ) \
+{ \
+ static auto_ptr as_##X##Name[NUM_##X+2]; \
+ return EnumToString( x, NUM_##X, X##Names, as_##X##Name ); \
+} \
+namespace StringConversion { template<> RString ToString( const X &value ) { return X##ToString(value); } }
#define XToLocalizedString(X) \
- const RString &X##ToLocalizedString(X x); \
- const RString &X##ToLocalizedString( X x ) \
- { \
- static auto_ptr g_##X##Name[NUM_##X]; \
- if( g_##X##Name[0].get() == NULL ) { \
- for( unsigned i = 0; i < NUM_##X; ++i ) \
- { \
- auto_ptr ap( new LocalizedString(#X, X##ToString((X)i)) ); \
- g_##X##Name[i] = ap; \
- } \
+const RString &X##ToLocalizedString(X x); \
+const RString &X##ToLocalizedString( X x ) \
+{ \
+ static auto_ptr g_##X##Name[NUM_##X]; \
+ if( g_##X##Name[0].get() == NULL ) { \
+ for( unsigned i = 0; i < NUM_##X; ++i ) \
+ { \
+ auto_ptr ap( new LocalizedString(#X, X##ToString((X)i)) ); \
+ g_##X##Name[i] = ap; \
} \
- return g_##X##Name[x]->GetValue(); \
- }
+ } \
+ return g_##X##Name[x]->GetValue(); \
+}
#define StringToX(X) \
- X StringTo##X(const RString&); \
- X StringTo##X( const RString& s ) \
- { \
- for( unsigned i = 0; i < ARRAYLEN(X##Names); ++i ) \
- if( !s.CompareNoCase(X##Names[i]) ) \
- return (X)i; \
- return X##_Invalid; \
+X StringTo##X(const RString&); \
+X StringTo##X( const RString& s ) \
+{ \
+ for( unsigned i = 0; i < ARRAYLEN(X##Names); ++i ) \
+ if( !s.CompareNoCase(X##Names[i]) ) \
+ return (X)i; \
+ return X##_Invalid; \
+} \
+namespace StringConversion \
+{ \
+ template<> bool FromString( const RString &sValue, X &out ) \
+ { \
+ out = StringTo##X(sValue); \
+ return out != X##_Invalid; \
} \
- namespace StringConversion { template<> bool FromString( const RString &sValue, X &out ) { out = StringTo##X(sValue); return out != X##_Invalid; } }
+}
// currently unused
#define LuaDeclareType(X)
@@ -126,8 +143,21 @@ static void Lua##X(lua_State* L) \
REGISTER_WITH_LUA_FUNCTION( Lua##X ); \
template<> X EnumTraits::Invalid = X##_Invalid; \
template<> const char *EnumTraits::szName = #X; \
-namespace LuaHelpers { template<> bool FromStack( lua_State *L, X &Object, int iOffset ) { Object = Enum::Check( L, iOffset, true ); return Object != EnumTraits::Invalid; } } \
-namespace LuaHelpers { template<> void Push( lua_State *L, const X &Object ) { Enum::Push( L, Object ); } }
+namespace LuaHelpers \
+{ \
+ template<> bool FromStack( lua_State *L, X &Object, int iOffset ) \
+ { \
+ Object = Enum::Check( L, iOffset, true ); \
+ return Object != EnumTraits::Invalid; \
+ } \
+} \
+namespace LuaHelpers \
+{ \
+ template<> void Push( lua_State *L, const X &Object ) \
+ { \
+ Enum::Push( L, Object ); \
+ } \
+}
#endif
diff --git a/src/LifeMeterBattery.cpp b/src/LifeMeterBattery.cpp
index 20f42ff4e7..3f49487a17 100644
--- a/src/LifeMeterBattery.cpp
+++ b/src/LifeMeterBattery.cpp
@@ -155,7 +155,7 @@ void LifeMeterBattery::ChangeLife( TapNoteScore score )
SubtractLives(MINES_SUBTRACT_LIVES);
else
{
- if( score < MIN_SCORE_TO_KEEP_LIFE && SUBTRACT_LIVES > 0 )
+ if( score < MIN_SCORE_TO_KEEP_LIFE && score > TNS_CheckpointMiss && SUBTRACT_LIVES > 0 )
SubtractLives(SUBTRACT_LIVES);
}
@@ -175,6 +175,12 @@ void LifeMeterBattery::ChangeLife( HoldNoteScore score, TapNoteScore tscore )
AddLives(HELD_ADD_LIVES);
if( score == HNS_LetGo && LET_GO_SUBTRACT_LIVES > 0 )
SubtractLives(LET_GO_SUBTRACT_LIVES);
+
+ Message msg( "LifeChanged" );
+ msg.SetParam( "Player", m_pPlayerState->m_PlayerNumber );
+ msg.SetParam( "LifeMeter", LuaReference::CreateFromPush(*this) );
+ msg.SetParam( "LivesLeft", GetLivesLeft() );
+ MESSAGEMAN->Broadcast( msg );
}
void LifeMeterBattery::HandleTapScoreNone()
diff --git a/src/NoteDataUtil.cpp b/src/NoteDataUtil.cpp
index 68151e4cae..eff74fb0ae 100644
--- a/src/NoteDataUtil.cpp
+++ b/src/NoteDataUtil.cpp
@@ -803,6 +803,7 @@ RadarStats CalculateRadarStatsFast( const NoteData &in, RadarStats &out )
case TapNote::mine:
case TapNote::empty:
case TapNote::fake:
+ case TapNote::autoKeysound:
continue; // skip these types - they don't count
}
diff --git a/src/NotesLoaderBMS.cpp b/src/NotesLoaderBMS.cpp
index 4f53c0e152..c6bab7c292 100644
--- a/src/NotesLoaderBMS.cpp
+++ b/src/NotesLoaderBMS.cpp
@@ -696,6 +696,8 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
}
NameToData_t::const_iterator it;
+
+ bool hasBGM = false;
for( it = mapNameToData.lower_bound("#00000"); it != mapNameToData.end(); ++it )
{
const RString &sName = it->first;
@@ -745,6 +747,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
{
if( bmsTrack == BMS_AUTO_KEYSOUND_1 )
{
+ hasBGM = true;
// shift the auto keysound as far right as possible
int iLastEmptyTrack = -1;
if( ndNotes.GetTapLastEmptyTrack(row, iLastEmptyTrack) &&
@@ -772,6 +775,12 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
}
}
+ if (!hasBGM)
+ {
+ LOG->Warn("The song at %s is missing a #XXX01 tag! We're unable to load.", sPath.c_str());
+ return false;
+ }
+
/* Handles hold notes like uBMPlay.
* Different BMS simulators support hold notes differently.
* See http://nvyu.net/rdm/ex.php for more info.
@@ -964,7 +973,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
iTransformNewToOld[15] = BMS_P2_TURN;
break;
default:
- ASSERT(0);
+ ASSERT_M(0, ssprintf("Invalid StepsType when parsing BMS file %s!", sPath.c_str()));
}
// shift all of the autokeysound tracks onto the main tracks
@@ -990,7 +999,7 @@ static bool LoadFromBMSFile( const RString &sPath, const NameToData_t &mapNameTo
}
else
{
- LOG->UserLog( "Song file", sPath, "has too much simultenous autokeysound tracks." );
+ LOG->UserLog( "Song file", sPath, "has too much simultaneous autokeysound tracks." );
}
}
}
diff --git a/src/NotesLoaderKSF.cpp b/src/NotesLoaderKSF.cpp
index c283ce7b53..3ad22bfbc4 100644
--- a/src/NotesLoaderKSF.cpp
+++ b/src/NotesLoaderKSF.cpp
@@ -20,66 +20,6 @@ static void HandleBunki( TimingData &timing, const float fEarlyBPM,
timing.AddBPMSegment( BPMSegment(BeatToNoteRow(beat), fCurBPM) );
}
-static bool HandlePipeChars( TimingData &timing, const RString sNoteRow,
- const float fCurBeat, int &iTickCount )
-{
- // gotta do something tricky here: if the bpm is below one then a couple of calculations
- // for scrollsegments will be made, example, bpm 0.2, tick 4000, the scrollsegment will
- // be 0. if the tickcount is non a stepmania standard then it will be adapted, a scroll
- // segment will then be added based on approximations.
-
- RString temp = sNoteRow.substr(2,sNoteRow.size()-3);
- float numTemp = StringToFloat(temp);
- if (BeginsWith(sNoteRow, "|T"))
- {
- iTickCount = static_cast(numTemp);
- timing.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
- return true;
- }
- else if (BeginsWith(sNoteRow, "|B"))
- {
- timing.SetBPMAtBeat( fCurBeat, numTemp );
- return true;
- }
- else if (BeginsWith(sNoteRow, "|E"))
- {
- // Finally! the |E| tag is working as it should. I can die happy now -DaisuMaster
- float fCurDelay = 60 / timing.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount;
- fCurDelay += timing.GetDelayAtRow(BeatToNoteRow(fCurBeat) );
- timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
- return true;
- }
- else if (BeginsWith(sNoteRow, "|D"))
- {
- float fCurDelay = timing.GetStopAtRow(BeatToNoteRow(fCurBeat) );
- fCurDelay += numTemp / 1000;
- timing.SetStopAtBeat( fCurBeat, fCurDelay, true );
- return true;
- }
- else if (BeginsWith(sNoteRow, "|M") || BeginsWith(sNoteRow, "|C"))
- {
- // multipliers/combo
- timing.SetComboAtBeat( fCurBeat, static_cast(numTemp) );
- return true;
- }
- else if (BeginsWith(sNoteRow, "|S"))
- {
- return false;
- }
- else if (BeginsWith(sNoteRow, "|F"))
- {
- //
- return false;
- }
-
- else if (BeginsWith(sNoteRow, "|X"))
- {
- timing.SetScrollAtBeat( fCurBeat, numTemp );
- return true;
- }
- return false;
-}
-
static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song, bool bKIUCompliant )
{
LOG->Trace( "Steps::LoadFromKSFFile( '%s' )", sPath.c_str() );
@@ -91,7 +31,10 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
return false;
}
- int iTickCount = -1; // this is the value we read for TICKCOUNT
+ // this is the value we read for TICKCOUNT
+ int iTickCount = -1;
+ // used to adapt weird tickcounts
+ float fScrollRatio = 1.0f;
vector vNoteRows;
// According to Aldo_MX, there is a default BPM and it's 60. -aj
@@ -350,21 +293,109 @@ static bool LoadFromKSFFile( const RString &sPath, Steps &out, const Song &song,
// Log an error, ignore the line.
continue;
}
- if ( !HandlePipeChars( stepsTiming, sRowString, fCurBeat, iTickCount ) )
- {
- // LOG it first.
- }
- continue;
+ // gotta do something tricky here: if the bpm is below one then a couple of calculations
+ // for scrollsegments will be made, example, bpm 0.2, tick 4000, the scrollsegment will
+ // be 0. if the tickcount is non a stepmania standard then it will be adapted, a scroll
+ // segment will then be added based on approximations. -DaisuMaster
+ // eh better do it considering the tickcount (high tickcounts)
- }
-
- else
- {
- // Is this why improper ksf or some kiucompilant ksf mixed with dm05 ksf are ignored?? -DaisuMaster
- //LOG->UserLog( "Song file", sPath, "has a RowString with an improper length \"%s\"; corrupt notes ignored.",
- // sRowString.c_str() );
- //return false;
- //continue;
+ // I'm making some experiments, please spare me...
+
+ RString temp = sRowString.substr(2,sRowString.size()-3);
+ float numTemp = StringToFloat(temp);
+ if (BeginsWith(sRowString, "|T"))
+ {
+ // duh
+ iTickCount = static_cast(numTemp);
+ //if( iTickCount > ROWS_PER_BEAT )
+
+ /* adapt tickcounts //
+ // valid tickcounts for SM: 1, 2, 3, 4, 6, 8, 12, 16, 24, 32, 64, ROWS_PER_BEAT
+ // put this inside the tickcount handling condition yes/no
+
+ if( iTickCount > ROWS_PER_BEAT ) // beyond 48
+ {
+ // clamp/scale/whatever and use scroll segments
+ }
+ else if( iTickCount > 32 || iTickCount < ROWS_PER_BEAT ) // ranging from 33 to 48, approximate to 32
+ {
+ fScrollRatio = 32;
+ iTickCount = 32;
+ }
+ else if( iTickCount > 24 || iTickCount < 32 ) // ranging from 25 to 31, approximate to 24
+ {
+ fScrollRatio = 24;
+ iTickCount = 24;
+ }
+ else if( iTickCount > 16 || iTickCount < 24 ) // ranging from 17 to 23, approximate to 16
+ {
+ fScrollRatio = 16;
+ iTickCount = 16;
+ }
+ else if( iTickCount > 12 || iTickCount < 16 ) // ranging from 13 to 15, approximate to 12
+ {
+ fScrollRatio = 12;
+ iTickCount = 12;
+ }
+ else if( iTickCount > 8 || iTickCount < 12 ) // ranging from 9 to 11, approximate to 8
+ {
+ fScrollRatio = 8;
+ iTickCount = 8;
+ }
+ else if( iTickCount > 6 || iTickCount < 8 ) // 7, approximate to 6
+ {
+ fScrollRatio = 6 / iTickCountt;
+ iTickCount = 6;
+ }
+ else if( iTickCount > 4 || iTickCount < 6 ) // 5, approximate to 4
+ {
+ fScrollRatio = iTickCount / 4;
+ iTickCount = 4;
+ }
+ //*/
+
+ stepsTiming.SetTickcountAtBeat( fCurBeat, clamp(iTickCount, 0, ROWS_PER_BEAT) );
+ }
+ else if (BeginsWith(sRowString, "|B"))
+ {
+ // BPM
+ stepsTiming.SetBPMAtBeat( fCurBeat, numTemp );
+ }
+ else if (BeginsWith(sRowString, "|E"))
+ {
+ // DelayBeat
+ float fCurDelay = 60 / stepsTiming.GetBPMAtBeat(fCurBeat) * numTemp / iTickCount;
+ fCurDelay += stepsTiming.GetDelayAtRow(BeatToNoteRow(fCurBeat) );
+ stepsTiming.SetStopAtBeat( fCurBeat, fCurDelay, true );
+ }
+ else if (BeginsWith(sRowString, "|D"))
+ {
+ // Delays
+ float fCurDelay = stepsTiming.GetStopAtRow(BeatToNoteRow(fCurBeat) );
+ fCurDelay += numTemp / 1000;
+ stepsTiming.SetStopAtBeat( fCurBeat, fCurDelay, true );
+ }
+ else if (BeginsWith(sRowString, "|M") || BeginsWith(sRowString, "|C"))
+ {
+ // multipliers/combo
+ stepsTiming.SetComboAtBeat( fCurBeat, static_cast(numTemp) );
+ }
+ else if (BeginsWith(sRowString, "|S"))
+ {
+ // speed segments
+ }
+ else if (BeginsWith(sRowString, "|F"))
+ {
+ // fakes
+ }
+ else if (BeginsWith(sRowString, "|X"))
+ {
+ // scroll segments
+ stepsTiming.SetScrollAtBeat( fCurBeat, numTemp );
+ return true;
+ }
+
+ continue;
}
// Half-doubles is offset; "0011111100000".
@@ -485,7 +516,7 @@ static void LoadTags( const RString &str, Song &out )
out.m_sArtist = artist;
}
-static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant )
+static bool LoadGlobalData( const RString &sPath, Song &out, bool bKIUCompliant )
{
MsdFile msd;
if( !msd.ReadFile( sPath, false ) ) // don't unescape
@@ -658,14 +689,11 @@ static bool LoadGlobalData( const RString &sPath, Song &out, bool &bKIUCompliant
}
// This is where the DMRequired test will take place.
- if (BeginsWith(NoteRowString, "|T") || BeginsWith(NoteRowString, "|B") ||
- BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") )
+ //if (BeginsWith(NoteRowString, "|T") || BeginsWith(NoteRowString, "|B") || BeginsWith(NoteRowString, "|D") || BeginsWith(NoteRowString, "|E") )
+ if ( BeginsWith( NoteRowString, "|" ) )
{
+ // have a static timing for everything
bDMRequired = true;
- if ( !HandlePipeChars( out.m_SongTiming, NoteRowString, fCurBeat, iTickCount ) )
- {
- // LOG it first.
- }
continue;
}
else
@@ -705,21 +733,32 @@ bool KSFLoader::LoadFromDir( const RString &sDir, Song &out )
// We shouldn't have been called to begin with if there were no KSFs.
ASSERT( arrayKSFFileNames.size() );
- bool bKIUCompliant = false;
+ //bool bKIUCompliant = false;
/* With Split Timing, there has to be a backup Song Timing in case
* anything goes wrong. As these files are kept in alphabetical
* order (hopefully), it is best to use the LAST file for timing
* purposes, for that is the "normal", or easiest difficulty.
* Usually. */
+ // Nevermind, kiu compilancy is screwing things up:
+ // IE, I have two simfiles, oh wich each have four ksf files, the first one has
+ // the first ksf with directmove timing changes, and the rest are not, everything
+ // goes fine. In the other hand I have my second simfile with the first ksf file
+ // without directmove timing changes and the rest have changes, changes are not
+ // loaded due to kiucompilancy in the first ksf file.
+ // About the "normal" thing, my simfiles' ksfs uses non-standard naming so
+ // the last chart is usually nightmare or normal, I use easy and normal
+ // indistinctly for SM so it shouldn't matter, I use piu fiesta/ex naming
+ // for directmove though, and we're just gathering basic info anyway, and
+ // most of the time all the KSF files have the same info in the #TITLE:; section
unsigned files = arrayKSFFileNames.size();
- if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[files - 1], out, bKIUCompliant) )
+ if( !LoadGlobalData(out.GetSongDir() + arrayKSFFileNames[0], out, false) )
return false;
// load the Steps from the rest of the KSF files
for( unsigned i=0; i apDriverList;
+ ReferenceAllDrivers( apDriverList );
+
+ for( unsigned i = 0; i < apDriverList.size(); ++i )
+ {
+ LoadedDriver *pDriver = apDriverList[i];
+ const RString driverPath = pDriver->GetPath( tmpPath );
+
+ if ( driverPath.empty() || pDriver->m_sRoot.empty() )
+ continue;
+
+ if ( pDriver->m_sType != "dir" && pDriver->m_sType != "dirro" )
+ continue;
+
+ int iMountPointLen = pDriver->m_sMountPoint.length();
+ if( tmpPath.substr(0, iMountPointLen) != pDriver->m_sMountPoint )
+ continue;
+
+ resolvedPath = pDriver->m_sRoot + "/" + RString(tmpPath.substr(iMountPointLen));
+ break;
+ }
+
+ UnreferenceAllDrivers( apDriverList );
+
+ NormalizePath( resolvedPath );
+
+ return resolvedPath;
+}
+
static bool SortBySecond( const pair &a, const pair &b )
{
return a.second < b.second;
diff --git a/src/RageFileManager.h b/src/RageFileManager.h
index 12c6ca1fe0..7eaa445852 100644
--- a/src/RageFileManager.h
+++ b/src/RageFileManager.h
@@ -37,6 +37,12 @@ public:
int GetFileSizeInBytes( const RString &sPath );
int GetFileHash( const RString &sPath );
+
+ /**
+ * @brief Get the absolte path from the VPS.
+ * @param path the VPS path.
+ * @return the absolute path. */
+ RString ResolvePath(const RString &path);
bool Mount( const RString &sType, const RString &sRealPath, const RString &sMountPoint, bool bAddToEnd = true );
void Mount( RageFileDriver *pDriver, const RString &sMountPoint, bool bAddToEnd = true );
diff --git a/src/Song.cpp b/src/Song.cpp
index 8cf6d352a7..ba1069f731 100644
--- a/src/Song.cpp
+++ b/src/Song.cpp
@@ -41,7 +41,7 @@
* @brief The internal version of the cache for StepMania.
*
* Increment this value to invalidate the current cache. */
-const int FILE_CACHE_VERSION = 180;
+const int FILE_CACHE_VERSION = 182;
/** @brief How long does a song sample last by default? */
const float DEFAULT_MUSIC_SAMPLE_LENGTH = 12.f;
@@ -190,7 +190,9 @@ RString Song::GetCacheFilePath() const
// Get a path to the SM containing data for this song. It might be a cache file.
const RString &Song::GetSongFilePath() const
{
- ASSERT( !m_sSongFileName.empty() );
+ ASSERT_M( !m_sSongFileName.empty(),
+ ssprintf("The song %s has no filename associated with it!",
+ this->m_sMainTitle.c_str()));
return m_sSongFileName;
}
diff --git a/src/SongManager.cpp b/src/SongManager.cpp
index 56afe8f93e..9550ae3c75 100644
--- a/src/SongManager.cpp
+++ b/src/SongManager.cpp
@@ -999,11 +999,14 @@ void SongManager::Cleanup()
for( unsigned i=0; i& vpSteps = pSong->GetAllSteps();
- for( unsigned n=0; nCompress();
+ const vector& vpSteps = pSong->GetAllSteps();
+ for( unsigned n=0; nCompress();
+ }
}
}
}
diff --git a/src/TimingData.cpp b/src/TimingData.cpp
index 660b5e50c0..579d11c635 100644
--- a/src/TimingData.cpp
+++ b/src/TimingData.cpp
@@ -1670,6 +1670,93 @@ public:
static int HasFakes( T* p, lua_State *L ) { lua_pushboolean(L, p->HasFakes()); return 1; }
static int HasSpeedChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasSpeedChanges()); return 1; }
static int HasScrollChanges( T* p, lua_State *L ) { lua_pushboolean(L, p->HasScrollChanges()); return 1; }
+ static int GetWarps( T* p, lua_State *L )
+ {
+ vector vWarps;
+ FOREACH_CONST( WarpSegment, p->m_WarpSegments, seg )
+ {
+ const float length = seg->GetLength();
+ const float beat = seg->GetBeat();
+ vWarps.push_back( ssprintf("%f=%f", beat, length) );
+ }
+ LuaHelpers::CreateTableFromArray(vWarps, L);
+ return 1;
+ }
+ static int GetFakes( T* p, lua_State *L )
+ {
+ vector vFakes;
+ FOREACH_CONST( FakeSegment, p->m_FakeSegments, seg )
+ {
+ const float length = seg->GetLength();
+ const float beat = seg->GetBeat();
+ vFakes.push_back( ssprintf("%f=%f", beat, length) );
+ }
+ LuaHelpers::CreateTableFromArray(vFakes, L);
+ return 1;
+ }
+ static int GetScrolls( T* p, lua_State *L )
+ {
+ vector vScrolls;
+ FOREACH_CONST( ScrollSegment, p->m_ScrollSegments, seg )
+ {
+ const float ratio = seg->GetRatio();
+ const float beat = seg->GetBeat();
+ vScrolls.push_back( ssprintf("%f=%f", beat, ratio) );
+ }
+ LuaHelpers::CreateTableFromArray(vScrolls, L);
+ return 1;
+ }
+ static int GetSpeeds( T* p, lua_State *L )
+ {
+ vector vSpeeds;
+ FOREACH_CONST( SpeedSegment, p->m_SpeedSegments, seg )
+ {
+ const float length = seg->GetLength();
+ const float ratio = seg->GetRatio();
+ const unsigned short unit = seg->GetUnit();
+ const float beat = seg->GetBeat();
+ vSpeeds.push_back( ssprintf("%f=%f=%f=%uh", beat, ratio, length, unit) );
+ }
+ LuaHelpers::CreateTableFromArray(vSpeeds, L);
+ return 1;
+ }
+ static int GetTimeSignatures( T* p, lua_State *L )
+ {
+ vector vTimes;
+ FOREACH_CONST( TimeSignatureSegment, p->m_vTimeSignatureSegments, seg )
+ {
+ const int numerator = seg->GetNum();
+ const int denominator = seg->GetDen();
+ const float beat = seg->GetBeat();
+ vTimes.push_back( ssprintf("%f=%d=%d", beat, numerator, denominator) );
+ }
+ LuaHelpers::CreateTableFromArray(vTimes, L);
+ return 1;
+ }
+ static int GetCombos( T* p, lua_State *L )
+ {
+ vector vCombos;
+ FOREACH_CONST( ComboSegment, p->m_ComboSegments, seg )
+ {
+ const int combo = seg->GetCombo();
+ const float beat = seg->GetBeat();
+ vCombos.push_back( ssprintf("%f=%d", beat, combo) );
+ }
+ LuaHelpers::CreateTableFromArray(vCombos, L);
+ return 1;
+ }
+ static int GetTickcounts( T* p, lua_State *L )
+ {
+ vector vTicks;
+ FOREACH_CONST( TickcountSegment, p->m_TickcountSegments, seg )
+ {
+ const int ticks = seg->GetTicks();
+ const float beat = seg->GetBeat();
+ vTicks.push_back( ssprintf("%f=%d", beat, ticks) );
+ }
+ LuaHelpers::CreateTableFromArray(vTicks, L);
+ return 1;
+ }
static int GetStops( T* p, lua_State *L )
{
vector vStops;
@@ -1763,6 +1850,13 @@ public:
ADD_METHOD( GetStops );
ADD_METHOD( GetDelays );
ADD_METHOD( GetBPMs );
+ ADD_METHOD( GetWarps );
+ ADD_METHOD( GetFakes );
+ ADD_METHOD( GetTimeSignatures );
+ ADD_METHOD( GetTickcounts );
+ ADD_METHOD( GetSpeeds );
+ ADD_METHOD( GetScrolls );
+ ADD_METHOD( GetCombos );
ADD_METHOD( GetLabels );
ADD_METHOD( GetBPMsAndTimes );
ADD_METHOD( GetActualBPM );
diff --git a/src/UnlockManager.cpp b/src/UnlockManager.cpp
index 47af088551..18cee4407f 100644
--- a/src/UnlockManager.cpp
+++ b/src/UnlockManager.cpp
@@ -295,7 +295,7 @@ void UnlockManager::GetPoints( const Profile *pProfile, float fScores[NUM_Unlock
fScores[UnlockRequirement_SongPoints] = GetSongPoints( pProfile );
fScores[UnlockRequirement_DancePoints] = (float) pProfile->m_iTotalDancePoints;
fScores[UnlockRequirement_StagesCleared] = (float) pProfile->GetTotalNumSongsPassed();
- fScores[UnlockRequirement_NumUnlocked] = (float) GetNumUnlocked();
+ //fScores[UnlockRequirement_NumUnlocked] = (float) GetNumUnlocked();
}
/* Return true if all songs and/or courses referenced by an unlock are available. */
@@ -330,8 +330,13 @@ UnlockEntryStatus UnlockEntry::GetUnlockEntryStatus() const
UNLOCKMAN->GetPoints( PROFILEMAN->GetMachineProfile(), fScores );
for( int i = 0; i < NUM_UnlockRequirement; ++i )
+ {
+ if( i == UnlockRequirement_NumUnlocked )
+ continue;
+
if( m_fRequirement[i] && fScores[i] >= m_fRequirement[i] )
return UnlockEntryStatus_RequirementsMet;
+ }
if( m_bRequirePassHardSteps && m_Song.IsValid() )
{
@@ -348,7 +353,6 @@ UnlockEntryStatus UnlockEntry::GetUnlockEntryStatus() const
return UnlockEntryStatus_RequirementsMet;
}
-
return UnlockEntryStatus_RequrementsNotMet;
}
@@ -601,7 +605,7 @@ int UnlockManager::GetNumUnlocked() const
int count = 0;
FOREACH_CONST( UnlockEntry, m_UnlockEntries, ue )
{
- if( ue->GetUnlockEntryStatus() == UnlockEntryStatus_Unlocked )
+ if( !ue->IsLocked() )
count++;
}
return count;