diff --git a/Docs/Changelog_sm5.txt b/Docs/Changelog_sm5.txt index fbcda87154..2dca08f66d 100644 --- a/Docs/Changelog_sm5.txt +++ b/Docs/Changelog_sm5.txt @@ -4,11 +4,18 @@ The StepMania 5 Changelog covers all post-sm-ssc changes. For a list of changes from StepMania 4 alpha 5 to sm-ssc v1.2.5, see Changelog_sm-ssc.txt. ________________________________________________________________________________ +================================================================================ +StepMania 5.0 ???? ?? | 20120??? +-------------------------------------------------------------------------------- + 2012/05/21 ---------- * [NotesLoaderBMS] Support for #BMP based background changes. [theDtTvB] * [SongManager] Add song to the list of songs before loading it, this fixes crashes in some BMS files. [theDtTvB] +* [ScreenEdit] Allow a new way of creating and modifying attacks using the + Area Menu. Note: Using the C or V keys is no longer allowed for attacks. + It will simplify things for later. [Wolfman2000] 2012/05/19 ---------- diff --git a/Themes/_fallback/Languages/en.ini b/Themes/_fallback/Languages/en.ini index 2489e45e35..2802ad224b 100644 --- a/Themes/_fallback/Languages/en.ini +++ b/Themes/_fallback/Languages/en.ini @@ -803,6 +803,8 @@ Off=Off Edit Courses/Mods=Edit Courses/Mods Accel=Acceleration Add/Edit Background Change=Add/Edit Background Change +Add Attack=Add Attack +Add Mod=Add Mod Air=Air AllowExtraStage=Allow Extra Stage AllowW1=Fantastic Timing @@ -976,6 +978,7 @@ Mines=Mines Mirror Player 1 to 2=Mirror P1's notes to P2 (routine only) Mirror Player 2 to 1=Mirror P2's notes to P1 (routine only) Modify Keysounds at current beat=Modify keysounds at current beat +Modify Attacks at current beat=Modify attacks at current beat MoveRandomToEnd=Random At End More Options=More Options MovieColorDepth=Movie Color @@ -1041,6 +1044,7 @@ ScoringType=Scoring Type ScreenFilter=Screen Filter Scroll=Scroll Secret=Secret +Secs Remaining=Seconds Remaining Server=Server Servers=Servers Set P1=Set P1 @@ -1064,6 +1068,7 @@ ShowSongOptions=Show Song Options ShowStats=Show Stats SmoothLines=SmoothLines SoundEffect=Sound Effect +Starting Time=Starting Time StepMania Credits=StepMania Credits Center1Player=Center 1 Player Song=Song @@ -1308,6 +1313,11 @@ or=or Zoom In Camera=Zoom In Camera [ScreenEdit] +Adding New Attack=Enter the new modifier for this attack.\nThe length can be adjusted later.\nLeave it blank to cancel. +Adding New Mod=Please enter the new modifier for this attack.\n\nLeave it blank to cancel. +Edit Existing Mod=Please adjust the present modifier below.\n\nLeave it blank to erase the mod. +Edit Attack Start=Enter the time when this attack starts. +Edit Attack Length=Enter how long it takes for this attack to finish. %s notes=%s notes Can't undo - no undo data.=Can't undo - no undo data. Do you want to revert from disk?=Do you want to revert from disk? diff --git a/Themes/_fallback/metrics.ini b/Themes/_fallback/metrics.ini index e445c49c40..ea71b0fe17 100644 --- a/Themes/_fallback/metrics.ini +++ b/Themes/_fallback/metrics.ini @@ -3941,6 +3941,14 @@ ColorDisabled=color("1,1,1,1") RowInitCommand=halign,0.5;valign,0.5;zoom,0.8;x,75;y,45;shadowlength,1 OptionRowNormalMetricsGroup="OptionRowMiniMenuEditHelp" +[ScreenMiniMenuAttackAtTimeMenu] +Fallback="ScreenMiniMenu" +ShowFooter=false + +[ScreenMiniMenuIndividualAttack] +Fallback="ScreenMiniMenu" +ShowFooter=false + [ScreenMiniMenuKeysoundTrack] Fallback="ScreenMiniMenu" ShowFooter=false diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index e29522a32f..474cb0269b 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -69,7 +69,13 @@ AutoScreenMessage( SM_BackFromSongInformation ); AutoScreenMessage( SM_BackFromBGChange ); AutoScreenMessage( SM_BackFromInsertTapAttack ); AutoScreenMessage( SM_BackFromInsertTapAttackPlayerOptions ); +AutoScreenMessage( SM_BackFromAttackAtTime ); AutoScreenMessage( SM_BackFromInsertStepAttack ); +AutoScreenMessage( SM_BackFromAddingModToExistingAttack ); +AutoScreenMessage( SM_BackFromEditingModToExistingAttack ); +AutoScreenMessage( SM_BackFromEditingAttackStart ); +AutoScreenMessage( SM_BackFromEditingAttackLength ); +AutoScreenMessage( SM_BackFromAddingAttackToChart ); AutoScreenMessage( SM_BackFromInsertStepAttackPlayerOptions ); AutoScreenMessage( SM_BackFromInsertCourseAttack ); AutoScreenMessage( SM_BackFromInsertCourseAttackPlayerOptions ); @@ -254,7 +260,7 @@ void ScreenEdit::InitEditMappings() m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); m_EditMappingsDeviceInput.hold[EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); m_EditMappingsDeviceInput.hold[EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADD_STEP_MODS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cc); + // m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADD_STEP_MODS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cc); // m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_STEP_ATTACK_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_INSERT); @@ -522,6 +528,16 @@ static MenuDef g_EditHelp( // fill this in dynamically ); +static MenuDef g_AttackAtTimeMenu( + "ScreenMiniMenuAttackAtTimeMenu" + // fill this in dynamically +); + +static MenuDef g_IndividualAttack( + "ScreenMiniMenuIndividualAttack" + // fill this in dynamically +); + static MenuDef g_KeysoundTrack( "ScreenMiniMenuKeysoundTrack" ); // fill this in dynamically @@ -692,6 +708,9 @@ static MenuDef g_AreaMenu( "Clear clipboard", true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::modify_attacks_at_row, + "Modify Attacks at current beat", + true, EditMode_CourseMods, true, true, 0, NULL), MenuRowDef(ScreenEdit::modify_keysounds_at_row, "Modify Keysounds at current beat", true, EditMode_Full, true, true, 0, NULL) @@ -1140,7 +1159,10 @@ void ScreenEdit::Init() m_iShiftAnchor = -1; m_iStartPlayingAt = -1; m_iStopPlayingAt = -1; - + + attackInProcess = -1; + modInProcess = -1; + this->AddChild( &m_Background ); m_SnapDisplay.SetXY( EDIT_X, PLAYER_Y_STANDARD ); @@ -1432,6 +1454,29 @@ void ScreenEdit::Update( float fDeltaTime ) PlayTicks(); } +static vector FindAllAttacksAtTime(const AttackArray& attacks, float fStartTime) +{ + vector ret; + for (unsigned i = 0; i < attacks.size(); ++i) + { + if (fabs(attacks[i].fStartSecond - fStartTime) < 0.001f) + { + ret.push_back(i); + } + } + return ret; +} + +static int FindAttackAtTime( const AttackArray& attacks, float fStartTime ) +{ + for( unsigned i = 0; i < attacks.size(); ++i ) + { + if( fabs(attacks[i].fStartSecond - fStartTime) < 0.001f ) + return i; + } + return -1; +} + static LocalizedString CURRENT_BEAT("ScreenEdit", "Current beat"); static LocalizedString CURRENT_SECOND("ScreenEdit", "Current second"); static LocalizedString SNAP_TO("ScreenEdit", "Snap to"); @@ -1554,6 +1599,11 @@ void ScreenEdit::UpdateTextInfo() sText += ssprintf( SEGMENT_TYPE_FORMAT.GetValue(), SEGMENT_TYPE.GetValue().c_str(), TimingSegmentTypeToString(currentCycleSegment).c_str() ); const RString tapnoteType = TapNoteTypeToString( m_selectedTap.type ); sText += ssprintf( TAP_NOTE_TYPE_FORMAT.GetValue(), TAP_NOTE_TYPE.GetValue().c_str(), tapnoteType.c_str() ); + + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + float beat = GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()); + sText += ssprintf("Attack here?: %s\n", FindAttackAtTime(attacks, beat) > -1 ? "YES" : "NO"); } GAMESTATE->SetProcessedTimingData(&m_pSteps->m_Timing); @@ -1729,16 +1779,6 @@ static void ShiftToRightSide( int &iCol, int iNumTracks ) } } -static int FindAttackAtTime( const AttackArray& attacks, float fStartTime ) -{ - for( unsigned i = 0; i < attacks.size(); ++i ) - { - if( fabs(attacks[i].fStartSecond - fStartTime) < 0.001f ) - return i; - } - return -1; -} - static LocalizedString BG_CHANGE_STEP_TIMING ( "ScreenEdit", "You must be in Song Timing Mode to edit BG Changes." ); static LocalizedString ALTER_MENU_NO_SELECTION ( "ScreenEdit", "You must have an area selected to enter the Alter Menu." ); static LocalizedString SWITCHED_TO ( "ScreenEdit", "Switched to" ); @@ -2471,31 +2511,7 @@ void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) break; case EDIT_BUTTON_OPEN_STEP_ATTACK_MENU: { - TimingData &timing = GetAppropriateTiming(); - float startTime = timing.GetElapsedTimeFromBeat(GetBeat()); - AttackArray &attacks = - (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); - int index = FindAttackAtTime(attacks, startTime); - - if (index >= 0) - { - const RString sDuration = ssprintf( "%.6f", attacks[index].fSecsRemaining ); - - g_InsertStepAttack.rows[sa_remove].bEnabled = true; - if( g_InsertStepAttack.rows[sa_duration].choices.size() == 9 ) - g_InsertStepAttack.rows[sa_duration].choices.push_back( sDuration ); - else - g_InsertStepAttack.rows[sa_duration].choices.back() = sDuration; - g_InsertStepAttack.rows[sa_duration].iDefaultChoice = 9; - } - else - { - if( g_InsertStepAttack.rows[sa_duration].choices.size() == 10 ) - g_InsertStepAttack.rows[sa_duration].choices.pop_back(); - g_InsertStepAttack.rows[sa_duration].iDefaultChoice = 3; - } - EditMiniMenu( &g_InsertStepAttack, SM_BackFromInsertStepAttack ); - + this->DoStepAttackMenu(); break; } case EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU: @@ -3245,6 +3261,12 @@ void ScreenEdit::HandleMessage( const Message &msg ) static LocalizedString SAVE_SUCCESSFUL ( "ScreenEdit", "Save successful." ); +static LocalizedString ADD_NEW_MOD ("ScreenEdit", "Adding New Mod"); +static LocalizedString ADD_NEW_ATTACK ("ScreenEdit", "Adding New Attack"); +static LocalizedString EDIT_EXISTING_MOD ("ScreenEdit", "Edit Existing Mod"); +static LocalizedString EDIT_ATTACK_START ("ScreenEdit", "Edit Attack Start"); +static LocalizedString EDIT_ATTACK_LENGTH ("ScreenEdit", "Edit Attack Length"); + void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) { if( SM != SM_UpdateTextInfo ) @@ -3576,6 +3598,196 @@ void ScreenEdit::HandleScreenMessage( const ScreenMessage SM ) m_NoteDataEdit.SetTapNote( g_iLastInsertTapAttackTrack, row, tn ); CheckNumberOfNotesAndUndo(); } + else if (SM == SM_BackFromEditingAttackStart && !ScreenTextEntry::s_bCancelledLast) + { + float time = StringToFloat(ScreenTextEntry::s_sLastAnswer); + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack &attack = attacks[attackInProcess]; + attack.fStartSecond = time; + SetDirty(true); + } + else if (SM == SM_BackFromEditingAttackLength && !ScreenTextEntry::s_bCancelledLast) + { + float time = StringToFloat(ScreenTextEntry::s_sLastAnswer); + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack &attack = attacks[attackInProcess]; + attack.fSecsRemaining = time; + SetDirty(true); + } + else if (SM == SM_BackFromAddingModToExistingAttack && !ScreenTextEntry::s_bCancelledLast) + { + RString mod = ScreenTextEntry::s_sLastAnswer; + Trim(mod); + if (mod.length() > 0) + { + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack &attack = attacks[attackInProcess]; + attack.sModifiers += "," + mod; + SetDirty(true); + } + + } + else if (SM == SM_BackFromEditingModToExistingAttack && !ScreenTextEntry::s_bCancelledLast) + { + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack &attack = attacks[attackInProcess]; + vector mods; + split(attack.sModifiers, ",", mods); + RString mod = ScreenTextEntry::s_sLastAnswer; + Trim(mod); + if (mod.length() > 0) + { + mods[modInProcess - 2] = mod; + } + else + { + mods.erase(mods.begin() + (modInProcess - 2)); + } + if (mods.size() > 0) + { + attack.sModifiers = join(",", mods); + } + else + { + attacks.erase(attacks.begin() + attackInProcess); + } + SetDirty(true); + } + else if (SM == SM_BackFromInsertStepAttack) + { + unsigned option = ScreenMiniMenu::s_iLastRowCode; + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack &attack = attacks[attackInProcess]; + vector mods; + split(attack.sModifiers, ",", mods); + modInProcess = option; + if (option == 0) // adjusting the starting time + { + ScreenTextEntry::TextEntry(SM_BackFromEditingAttackStart, + EDIT_ATTACK_START, + ssprintf("%.5f", attack.fStartSecond), + 10); + } + else if (option == 1) // adjusting the length of the attack + { + ScreenTextEntry::TextEntry(SM_BackFromEditingAttackLength, + EDIT_ATTACK_LENGTH, + ssprintf("%.5f", attack.fSecsRemaining), + 10); + } + else if (option >= 2 + mods.size()) // adding a new mod + { + ScreenTextEntry::TextEntry(SM_BackFromAddingModToExistingAttack, + ADD_NEW_MOD, + "", + 64); + } + else // modifying existing mod. + { + ScreenTextEntry::TextEntry(SM_BackFromEditingModToExistingAttack, + EDIT_EXISTING_MOD, + mods[option - 2], + 64); + } + } + else if (SM == SM_BackFromAddingAttackToChart && !ScreenTextEntry::s_bCancelledLast) + { + RString mod = ScreenTextEntry::s_sLastAnswer; + Trim(mod); + if (mod.length() > 0) + { + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + Attack attack; + attack.fStartSecond = GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()); + attack.fSecsRemaining = 5; // Users can change later. + attack.sModifiers = mod; + attacks.push_back(attack); + SetDirty(true); + } + } + else if (SM == SM_BackFromAttackAtTime) + { + int attackChoice = ScreenMiniMenu::s_iLastRowCode; + int attackDecision = ScreenMiniMenu::s_viLastAnswers[attackChoice]; + TimingData &timing = GetAppropriateTiming(); + float startTime = timing.GetElapsedTimeFromBeat(GetBeat()); + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + vector points = FindAllAttacksAtTime(attacks, startTime); + if (attackChoice == (int)points.size()) + { + // TODO: Add attack code. + ScreenTextEntry::TextEntry(SM_BackFromAddingAttackToChart, + ADD_NEW_ATTACK, + "", + 64); + } + else + { + attackInProcess = points[attackChoice]; + if (attackDecision == 1) // remove + { + attacks.erase(attacks.begin() + attackInProcess); + SetDirty(true); + } + else + { + Attack &attack = attacks[attackInProcess]; + g_IndividualAttack.rows.clear(); + + g_IndividualAttack.rows.push_back(MenuRowDef(0, + "Starting Time", + true, + EditMode_CourseMods, + true, + true, + 0, + NULL)); + g_IndividualAttack.rows[0].SetOneUnthemedChoice(ssprintf("%.5f", attack.fStartSecond)); + g_IndividualAttack.rows.push_back(MenuRowDef(1, + "Secs Remaining", + true, + EditMode_CourseMods, + true, + true, + 0, + NULL)); + g_IndividualAttack.rows[1].SetOneUnthemedChoice(ssprintf("%.5f", attack.fSecsRemaining)); + vector mods; + split(attack.sModifiers, ",", mods); + for (unsigned i = 0; i < mods.size(); ++i) + { + unsigned col = i + 2; + g_IndividualAttack.rows.push_back(MenuRowDef(col, + ssprintf("Attack %d", i + 1), + true, + EditMode_CourseMods, + false, + true, + 0, + NULL)); + g_IndividualAttack.rows[col].SetOneUnthemedChoice(mods[i].c_str()); + } + + g_IndividualAttack.rows.push_back(MenuRowDef(mods.size() + 2, + "Add Mod", + true, + EditMode_CourseMods, + true, + true, + 0, + NULL)); + + EditMiniMenu(&g_IndividualAttack, SM_BackFromInsertStepAttack); + } + } + } else if (SM == SM_BackFromInsertStepAttack) { int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0]; @@ -4832,6 +5044,11 @@ void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAns m_Clipboard.ClearAll(); break; } + case modify_attacks_at_row: + { + this->DoStepAttackMenu(); + break; + } case modify_keysounds_at_row: { this->DoKeyboardTrackMenu(); @@ -5589,6 +5806,46 @@ static RString GetDeviceButtonsLocalized( const vector &veb, const M return s; } +void ScreenEdit::DoStepAttackMenu() +{ + TimingData &timing = GetAppropriateTiming(); + float startTime = timing.GetElapsedTimeFromBeat(GetBeat()); + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + vector points = FindAllAttacksAtTime(attacks, startTime); + + g_AttackAtTimeMenu.rows.clear(); + unsigned index = 0; + + FOREACH(int, points, i) + { + const Attack &attack = attacks[*i]; + RString desc = ssprintf("%.5f -> %.5f (%d mod[s])", + startTime, startTime + attack.fSecsRemaining, + attack.GetNumAttacks()); + + g_AttackAtTimeMenu.rows.push_back(MenuRowDef(index++, + desc, + true, + EditMode_CourseMods, + false, + false, + 0, + "Modify", + "Delete")); + } + g_AttackAtTimeMenu.rows.push_back(MenuRowDef(index, + "Add Attack", + true, + EditMode_CourseMods, + true, + true, + 0, + NULL)); + + EditMiniMenu(&g_AttackAtTimeMenu, SM_BackFromAttackAtTime); +} + static LocalizedString TRACK_NUM("ScreenEdit", "Track %d"); static LocalizedString NO_KEYSND("ScreenEdit", "None"); static LocalizedString NEWKEYSND("ScreenEdit", "New Sound"); diff --git a/src/ScreenEdit.h b/src/ScreenEdit.h index 0de36ea16a..1e01b10775 100644 --- a/src/ScreenEdit.h +++ b/src/ScreenEdit.h @@ -250,6 +250,9 @@ protected: /** @brief Display the keyboard track menu for the current row. */ void DoKeyboardTrackMenu(); + + /** @brief Display the step attack menu for the current row. */ + void DoStepAttackMenu(); void DoHelp(); @@ -438,6 +441,7 @@ public: last_second_at_beat, undo, clear_clipboard, /**< Clear the clipboards. */ + modify_attacks_at_row, /**< Modify the chart attacks at this row. */ modify_keysounds_at_row, /**< Modify the keysounds at this row. */ NUM_AREA_MENU_CHOICES }; @@ -674,6 +678,9 @@ public: void EditMiniMenu(const MenuDef* pDef, ScreenMessage SM_SendOnOK = SM_None, ScreenMessage SM_SendOnCancel = SM_None ); + + int attackInProcess; + int modInProcess; private: /** * @brief Retrieve the appropriate TimingData based on GAMESTATE.