From bb634d57ac385384b34ae03c8c927c609b3e0f76 Mon Sep 17 00:00:00 2001 From: Jason Felds Date: Tue, 30 Apr 2013 22:46:14 -0400 Subject: [PATCH] Another batch of loops. --- src/ScreenEdit.cpp | 11879 ++++++++++++++++++++++--------------------- 1 file changed, 5940 insertions(+), 5939 deletions(-) diff --git a/src/ScreenEdit.cpp b/src/ScreenEdit.cpp index 8a31f4e610..70a2db0e8e 100644 --- a/src/ScreenEdit.cpp +++ b/src/ScreenEdit.cpp @@ -1,5939 +1,5940 @@ -#include "global.h" -#include -#include -#include "ScreenEdit.h" -#include "ActorUtil.h" -#include "AdjustSync.h" -#include "BackgroundUtil.h" -#include "CommonMetrics.h" -#include "Foreach.h" -#include "GameManager.h" -#include "GamePreferences.h" -#include "GameSoundManager.h" -#include "GameState.h" -#include "InputEventPlus.h" -#include "InputMapper.h" -#include "LocalizedString.h" -#include "NoteDataUtil.h" -#include "NoteSkinManager.h" -#include "NoteTypes.h" -#include "NotesWriterSM.h" -#include "PrefsManager.h" -#include "RageSoundManager.h" -#include "RageInput.h" -#include "RageLog.h" -#include "ScreenDimensions.h" -#include "ScreenManager.h" -#include "ScreenMiniMenu.h" -#include "ScreenPrompt.h" -#include "ScreenSaveSync.h" -#include "ScreenTextEntry.h" -#include "SongManager.h" -#include "SongUtil.h" -#include "StepsUtil.h" -#include "Style.h" -#include "ThemeManager.h" -#include "ThemeMetric.h" -#include "TimingData.h" -#include "Game.h" -#include "RageSoundReader.h" - -static Preference g_iDefaultRecordLength( "DefaultRecordLength", 4 ); -static Preference g_bEditorShowBGChangesPlay( "EditorShowBGChangesPlay", true ); - -/** @brief How long must the button be held to generate a hold in record mode? */ -const float RECORD_HOLD_SECONDS = 0.3f; - -#define PLAYER_X (SCREEN_CENTER_X) -#define PLAYER_Y (SCREEN_CENTER_Y) -#define PLAYER_HEIGHT (360) -#define PLAYER_Y_STANDARD (PLAYER_Y-PLAYER_HEIGHT/2) - -#define EDIT_X (SCREEN_CENTER_X) -#define EDIT_Y (PLAYER_Y) - -#define RECORD_X (SCREEN_CENTER_X) -#define RECORD_Y (SCREEN_CENTER_Y) - -#define PLAY_RECORD_HELP_TEXT THEME->GetString(m_sName,"PlayRecordHelpText") -#define EDIT_HELP_TEXT THEME->GetString(m_sName,"EditHelpText") - -AutoScreenMessage( SM_UpdateTextInfo ); -AutoScreenMessage( SM_BackFromMainMenu ); -AutoScreenMessage( SM_BackFromAreaMenu ); -AutoScreenMessage( SM_BackFromAlterMenu ); -AutoScreenMessage( SM_BackFromStepsInformation ); -AutoScreenMessage( SM_BackFromStepsData ); -AutoScreenMessage( SM_BackFromOptions ); -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 ); -AutoScreenMessage( SM_BackFromCourseModeMenu ); -AutoScreenMessage( SM_BackFromKeysoundTrack ); -AutoScreenMessage( SM_BackFromNewKeysound ); -AutoScreenMessage( SM_DoRevertToLastSave ); -AutoScreenMessage( SM_DoRevertFromDisk ); -AutoScreenMessage( SM_BackFromTimingDataInformation ); -AutoScreenMessage( SM_BackFromDifficultyMeterChange ); -AutoScreenMessage( SM_BackFromBeat0Change ); -AutoScreenMessage( SM_BackFromBPMChange ); -AutoScreenMessage( SM_BackFromStopChange ); -AutoScreenMessage( SM_BackFromDelayChange ); -AutoScreenMessage( SM_BackFromTimeSignatureChange ); -AutoScreenMessage( SM_BackFromTickcountChange ); -AutoScreenMessage( SM_BackFromComboChange ); -AutoScreenMessage( SM_BackFromLabelChange ); -AutoScreenMessage( SM_BackFromWarpChange ); -AutoScreenMessage( SM_BackFromSpeedPercentChange ); -AutoScreenMessage( SM_BackFromSpeedWaitChange ); -AutoScreenMessage( SM_BackFromSpeedModeChange ); -AutoScreenMessage( SM_BackFromScrollChange ); -AutoScreenMessage( SM_BackFromFakeChange ); -AutoScreenMessage( SM_DoEraseStepTiming ); -AutoScreenMessage( SM_DoSaveAndExit ); -AutoScreenMessage( SM_DoExit ); -AutoScreenMessage( SM_SaveSuccessful ); -AutoScreenMessage( SM_SaveFailed ); - -static const char *EditStateNames[] = { - "Edit", - "Record", - "RecordPaused", - "Playing" -}; -XToString( EditState ); -LuaXType( EditState ); - -void ScreenEdit::InitEditMappings() -{ - m_EditMappingsDeviceInput.Clear(); - - // Common mappings: - switch( EDIT_MODE.GetValue() ) - { - case EditMode_Practice: - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_PREV_MEASURE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); - m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_PREV_MEASURE][0] = GAME_BUTTON_UP; - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_NEXT_MEASURE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); - m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_NEXT_MEASURE][0] = GAME_BUTTON_DOWN; - break; - default: - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_LINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); - m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_UP_LINE][0] = GAME_BUTTON_UP; - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_LINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); - m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_DOWN_LINE][0] = GAME_BUTTON_DOWN; - break; - } - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_PAGE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGUP); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_PAGE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SEMICOLON); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_PAGE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGDN); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_PAGE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SQUOTE); - - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_UP_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_UP_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGUP); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SEMICOLON); - - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_DOWN_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_DOWN_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGDN); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SQUOTE); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_HOME][0] = DeviceInput(DEVICE_KEYBOARD, KEY_HOME); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_END][0] = DeviceInput(DEVICE_KEYBOARD, KEY_END); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PERIOD); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_COMMA); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SEGMENT_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PERIOD); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_NEXT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SEGMENT_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_COMMA); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_PREV][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_SPACE); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_FROM_START][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_START][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_START][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_CURSOR][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); - - // EditMode-specific mappings - switch( EDIT_MODE.GetValue() ) - { - case EditMode_Practice: - // Left = Zoom out (1x, 2x, 4x) - // Right = Zoom in - m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); - m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); - - // F1 = Show help popup - m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); - - // Esc = Show Edit Menu - m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; - m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; - - - // Escape, Enter = exit play/record - m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); - m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_PlayMappingsMenuButton.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK; - return; - case EditMode_CourseMods: - // Left/Right = Snap to Next/Prev - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); - - // v = course playback menu - // m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADD_COURSE_MODS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Co); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_COURSE_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); - - // F1 = Show help popup - m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); - - // Esc = Show Edit Menu - m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; - m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; - - // Escape, Enter = exit play/record - m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); - m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_PlayMappingsMenuButton.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_START; - return; - - case EditMode_Full: - // F4 = Show timing menu - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_TIMING_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F4); - - /* Don't allow F5/F6 in home mode. It breaks the "delay creation until first save" logic. */ - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_PREV_STEPS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F5); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_NEXT_STEPS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F6); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_BPM_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F7); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_BPM_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F8); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_STOP_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F9); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_STOP_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F10); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELAY_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F9); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELAY_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F10); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OFFSET_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F11); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OFFSET_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F12); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_START_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RBRACKET); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_START_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LBRACKET); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_LENGTH_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RBRACKET); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LBRACKET); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SAMPLE_MUSIC][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cl); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); - 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_OPEN_STEP_ATTACK_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_INSERT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_INSERT_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_INSERT_SHIFT_PAUSES][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELETE_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DEL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELETE_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELETE_SHIFT_PAUSES][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - break; - default: break; - } - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_0][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C1); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_1][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C2); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_2][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C3); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_3][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C4); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_4][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C5); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_5][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C6); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_6][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C7); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_7][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C8); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_8][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C9); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_9][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C0); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_RIGHT_SIDE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_RIGHT_SIDE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_TAP_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cn); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_TAP_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cm); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cn); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cm); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; - m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_AREA_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_ALTER_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Ca); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_INSERT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_BACKSLASH); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELETE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DEL); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADJUST_FINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); - m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADJUST_FINE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_Cs); - #if defined(MACOSX) - /* use cmd */ - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LMETA); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RMETA); - #else - /* use ctrl */ - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - #endif - - m_EditMappingsDeviceInput.button[EDIT_BUTTON_UNDO][1] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); - - // Switch players, if it makes sense to do so. - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SWITCH_PLAYERS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_SLASH); - - // Allow song and step timing to be swapped. - m_EditMappingsDeviceInput.button[EDIT_BUTTON_SWITCH_TIMINGS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Ct); - - m_PlayMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); - m_PlayMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_PlayMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_BACK; - m_PlayMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_START; - - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_REMOVE_NOTE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_REMOVE_NOTE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_RecordMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); - m_RecordMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_BACK; - m_RecordMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_START; - - m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); - m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); - m_RecordPausedMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); - m_RecordPausedMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); - m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RECORD_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); - m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); - m_RecordPausedMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK; - m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_UNDO][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); -} - -/* Given a DeviceInput that was just depressed, return an active edit function. */ -EditButton ScreenEdit::DeviceToEdit( const DeviceInput &DeviceI ) const -{ - ASSERT( DeviceI.IsValid() ); - - const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); - - /* First, search to see if a key that requires a modifier is pressed. */ - FOREACH_EditButton( e ) - { - for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) - { - if( pCurrentMap->button[e][slot] == DeviceI && pCurrentMap->hold[e][0].IsValid() ) - { - /* The button maps to this function, and has one or more shift modifiers attached. */ - for( int holdslot = 0; holdslot < NUM_EDIT_TO_DEVICE_SLOTS; ++holdslot ) - { - DeviceInput hDI = pCurrentMap->hold[e][holdslot]; - if( hDI.IsValid() && INPUTFILTER->IsBeingPressed(hDI) ) - return e; - } - } - } - } - - /* No shifted keys matched. See if any unshifted inputs are bound to this key. */ - FOREACH_EditButton(e) - { - for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) - { - if( pCurrentMap->button[e][slot] == DeviceI && !pCurrentMap->hold[e][0].IsValid() ) - { - /* The button maps to this function. */ - return e; - } - } - } - - return EditButton_Invalid; -} - -/* Given a DeviceInput that was just depressed, return an active edit function. */ -EditButton ScreenEdit::MenuButtonToEditButton( GameButton MenuI ) const -{ - const MapEditButtonToMenuButton *pCurrentMap = GetCurrentMenuButtonMap(); - - FOREACH_EditButton(e) - { - for( int slot = 0; slot < NUM_EDIT_TO_MENU_SLOTS; ++slot ) - { - if( pCurrentMap->button[e][slot] == MenuI ) - { - /* The button maps to this function. */ - return e; - } - } - } - - return EditButton_Invalid; -} - -/* If DeviceI was just pressed, return true if button is triggered. (More than one - * function may be mapped to a key.) */ -bool ScreenEdit::EditPressed( EditButton button, const DeviceInput &DeviceI ) -{ - ASSERT( DeviceI.IsValid() ); - - const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); - - /* First, search to see if a key that requires a modifier is pressed. */ - bool bPrimaryButtonPressed = false; - for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) - { - if( pCurrentMap->button[button][slot] == DeviceI ) - bPrimaryButtonPressed = true; - } - - if( !bPrimaryButtonPressed ) - return false; - - /* The button maps to this function. Does the function has one or more shift modifiers attached? */ - if( !pCurrentMap->hold[button][0].IsValid() ) - return true; - - for( int holdslot = 0; holdslot < NUM_EDIT_TO_DEVICE_SLOTS; ++holdslot ) - { - DeviceInput hDI = pCurrentMap->hold[button][holdslot]; - if( INPUTFILTER->IsBeingPressed(hDI) ) - return true; - } - - /* No shifted keys matched. */ - return false; -} - -bool ScreenEdit::EditToDevice( EditButton button, int iSlotNum, DeviceInput &DeviceI ) const -{ - ASSERT( iSlotNum < NUM_EDIT_TO_DEVICE_SLOTS ); - const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); - if( pCurrentMap->button[button][iSlotNum].IsValid() ) - DeviceI = pCurrentMap->button[button][iSlotNum]; - else if( pCurrentMap->hold[button][iSlotNum].IsValid() ) - DeviceI = pCurrentMap->hold[button][iSlotNum]; - return DeviceI.IsValid(); -} - -bool ScreenEdit::EditIsBeingPressed( EditButton button ) const -{ - for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) - { - DeviceInput DeviceI; - if( EditToDevice( button, slot, DeviceI ) && INPUTFILTER->IsBeingPressed(DeviceI) ) - return true; - } - - return false; -} - -const MapEditToDI *ScreenEdit::GetCurrentDeviceInputMap() const -{ - switch( m_EditState ) - { - DEFAULT_FAIL( m_EditState ); - case STATE_EDITING: return &m_EditMappingsDeviceInput; - case STATE_PLAYING: return &m_PlayMappingsDeviceInput; - case STATE_RECORDING: return &m_RecordMappingsDeviceInput; - case STATE_RECORDING_PAUSED: return &m_RecordPausedMappingsDeviceInput; - } -} - -const MapEditButtonToMenuButton *ScreenEdit::GetCurrentMenuButtonMap() const -{ - switch( m_EditState ) - { - DEFAULT_FAIL( m_EditState ); - case STATE_EDITING: return &m_EditMappingsMenuButton; - case STATE_PLAYING: return &m_PlayMappingsMenuButton; - case STATE_RECORDING: return &m_RecordMappingsMenuButton; - case STATE_RECORDING_PAUSED: return &m_RecordPausedMappingsMenuButton; - } -} - - -static MenuDef g_EditHelp( - "ScreenMiniMenuEditHelp" - // 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 - -static MenuDef g_MainMenu( - "ScreenMiniMenuMainMenu", - MenuRowDef(ScreenEdit::play_whole_song, - "Play whole song", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::play_current_beat_to_end, - "Play current beat to end", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::save, - "Save", - true, EditMode_Home, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::play_selection, - "Play selection", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::set_selection_start, - "Set selection start", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::set_selection_end, - "Set selection end", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::revert_to_last_save, - "Revert to last save", - true, EditMode_Home, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::revert_from_disk, - "Revert from disk", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::options, - "Editor options", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::edit_song_info, - "Edit song info", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::edit_steps_information, - "Edit steps information", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::edit_timing_data, - "Edit Timing Data", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::view_steps_data, - "View steps data", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::play_preview_music, - "Play preview music", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::exit, - "Exit Edit Mode", - true, EditMode_Practice, true, true, 0, NULL ) -); - -static MenuDef g_AlterMenu( - "ScreenMiniMenuAlterMenu", - MenuRowDef(ScreenEdit::cut, - "Cut", - true, - EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::copy, - "Copy", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::clear, "Clear area", true, - EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::quantize, "Quantize", true, - EditMode_Practice, true, true, 0, - "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), - MenuRowDef(ScreenEdit::turn, "Turn", true, - EditMode_Practice, true, true, 0, "Left","Right","Mirror","Backwards","Shuffle","SuperShuffle" ), - MenuRowDef(ScreenEdit::transform, "Transform", true, - EditMode_Practice, true, true, 0, "NoHolds","NoMines","Little","Wide", - "Big","Quick","Skippy","Mines","Echo","Stomp","Planted","Floored", - "Twister","NoJumps","NoHands","NoQuads","NoStretch" ), - MenuRowDef(ScreenEdit::alter, "Alter", true, - EditMode_Practice, true, true, 0, "Autogen To Fill Width","Backwards","Swap Sides", - "Copy Left To Right","Copy Right To Left","Clear Left","Clear Right", - "Collapse To One","Collapse Left","Shift Left","Shift Right" ), - MenuRowDef(ScreenEdit::tempo, "Tempo", true, - EditMode_Full, true, true, 0, "Compress 2x","Compress 3->2", - "Compress 4->3","Expand 3->4","Expand 2->3","Expand 2x" ), - MenuRowDef(ScreenEdit::play, - "Play selection", - true, - EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::record, - "Record in selection", - true, - EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::preview_designation, - "Designate as Music Preview", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_to_pause, - "Convert selection to pause", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_to_delay, - "Convert selection to delay", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_to_warp, - "Convert selection to warp", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_to_fake, - "Convert selection to fake", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_to_attack, - "Convert selection to attack", - true, - EditMode_Full, true, true, 0, NULL), - MenuRowDef(ScreenEdit::routine_invert_notes, - "Invert notes' player", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::routine_mirror_1_to_2, - "Mirror Player 1 to 2", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::routine_mirror_2_to_1, - "Mirror Player 2 to 1", - true, - EditMode_Full, true, true, 0, NULL ) -); - -static MenuDef g_AreaMenu( - "ScreenMiniMenuAreaMenu", - MenuRowDef(ScreenEdit::paste_at_current_beat, - "Paste at current beat", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::paste_at_begin_marker, - "Paste at begin marker", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::insert_and_shift, - "Insert beat and shift down", - true, EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), - MenuRowDef(ScreenEdit::delete_and_shift, - "Delete beat and shift up", - true, EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), - MenuRowDef(ScreenEdit::shift_pauses_forward, - "Shift all timing changes down", - true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), - MenuRowDef(ScreenEdit::shift_pauses_backward, - "Shift all timing changes up", - true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), - MenuRowDef(ScreenEdit::convert_pause_to_beat, - "Convert pause to beats", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::convert_delay_to_beat, - "Convert delay to beats", - true, - EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::last_second_at_beat, - "Designate last second at current beat", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::undo, - "Undo", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::clear_clipboard, - "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) - -); - -static MenuDef g_StepsInformation( - "ScreenMiniMenuStepsInformation", - MenuRowDef(ScreenEdit::difficulty, - "Difficulty", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::meter, - "Meter", - true, EditMode_Practice, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::predict_meter, - "Predicted Meter", - false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::chartname, - "Chart Name", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::description, - "Description", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::chartstyle, - "Chart Style", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::step_credit, - "Step Author", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::step_display_bpm, - "Display BPM", - true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), - MenuRowDef(ScreenEdit::step_min_bpm, - "Min BPM", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::step_max_bpm, - "Max BPM", - true, EditMode_Full, true, true, 0, NULL ) -); - -static MenuDef g_StepsData( - "ScreenMiniMenuStepsData", - MenuRowDef( ScreenEdit::tap_notes, "Tap Steps", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::jumps, "Jumps", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::hands, "Hands", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::quads, "Quads", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::holds, "Holds", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::mines, "Mines", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::rolls, "Rolls", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::lifts, "Lifts", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::fakes, "Fakes", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::stream, "Stream", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::voltage, "Voltage", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::air, "Air", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::freeze, "Freeze", false, EditMode_Full, true, true, 0, NULL ), - MenuRowDef( ScreenEdit::chaos, "Chaos", false, EditMode_Full, true, true, 0, NULL ) -); - -static MenuDef g_SongInformation( - "ScreenMiniMenuSongInformation", - MenuRowDef(ScreenEdit::main_title, - "Main title", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::sub_title, - "Sub title", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::artist, - "Artist", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::genre, - "Genre", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::credit, - "Credit", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::main_title_transliteration, - "Main title transliteration", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::sub_title_transliteration, - "Sub title transliteration", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::artist_transliteration, - "Artist transliteration", - true, EditMode_Practice, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::last_second_hint, - "Last second hint", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::preview_start, - "Preview Start", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::preview_length, - "Preview Length", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::display_bpm, - "Display BPM", - true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), - MenuRowDef(ScreenEdit::min_bpm, - "Min BPM", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::max_bpm, - "Max BPM", - true, EditMode_Full, true, true, 0, NULL ) -); - - -static MenuDef g_TimingDataInformation( - "ScreenMiniMenuTimingDataInformation", - MenuRowDef(ScreenEdit::beat_0_offset, - "Beat 0 Offset", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::bpm, - "Edit BPM change", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::stop, - "Edit stop", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::delay, - "Edit delay", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::time_signature, - "Edit time signature", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::label, - "Edit label", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::tickcount, - "Edit tickcount", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::combo, - "Edit combo", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::warp, - "Edit warp", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::speed_percent, - "Edit speed (percent)", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::speed_wait, - "Edit speed (wait)", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::speed_mode, - "Edit speed (mode)", - true, EditMode_Full, true, true, 0, "Beats", "Seconds" ), - MenuRowDef(ScreenEdit::scroll, - "Edit scrolling factor", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::fake, - "Edit fake", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::copy_full_timing, - "Copy timing data", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::paste_full_timing, - "Paste timing data", - true, EditMode_Full, true, true, 0, NULL ), - MenuRowDef(ScreenEdit::erase_step_timing, - "Erase step timing", - true, EditMode_Full, true, true, 0, NULL ) -); - -// XXX: What are these enums used for? -enum -{ - song_bganimation, - song_movie, - song_bitmap, - global_bganimation, - global_movie, - global_movie_song_group, - global_movie_song_group_and_genre, - dynamic_random, - baked_random, - none -}; -static bool EnabledIfSet1SongBGAnimation(); -static bool EnabledIfSet1SongMovie(); -static bool EnabledIfSet1SongBitmap(); -static bool EnabledIfSet1GlobalBGAnimation(); -static bool EnabledIfSet1GlobalMovie(); -static bool EnabledIfSet1GlobalMovieSongGroup(); -static bool EnabledIfSet1GlobalMovieSongGroupAndGenre(); -static bool EnabledIfSet2SongBGAnimation(); -static bool EnabledIfSet2SongMovie(); -static bool EnabledIfSet2SongBitmap(); -static bool EnabledIfSet2GlobalBGAnimation(); -static bool EnabledIfSet2GlobalMovie(); -static bool EnabledIfSet2GlobalMovieSongGroup(); -static bool EnabledIfSet2GlobalMovieSongGroupAndGenre(); -static MenuDef g_BackgroundChange( - "ScreenMiniMenuBackgroundChange", - MenuRowDef(ScreenEdit::layer, - "Layer", - false, - EditMode_Full, true, false, 0, "" ), - MenuRowDef(ScreenEdit::rate, - "Rate", - true, - EditMode_Full, true, false, 10, "0%","10%","20%","30%","40%","50%", - "60%","70%","80%","90%","100%","120%","140%","160%","180%","200%", - "220%","240%","260%","280%","300%","350%","400%" ), - MenuRowDef(ScreenEdit::transition, - "Force Transition", - true, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::effect, - "Force Effect", - true, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::color1, - "Force Color 1", - true, - EditMode_Full, true, false, 0, "-","#FFFFFF","#808080","#FFFFFF80", - "#000000","#FF0000","#00FF00","#0000FF","#FFFF00","#00FFFF","#FF00FF" ), - MenuRowDef(ScreenEdit::color2, - "Force Color 2", - true, - EditMode_Full, true, false, 0, "-","#FFFFFF","#808080","#FFFFFF80", - "#000000","#FF0000","#00FF00","#0000FF","#FFFF00","#00FFFF","#FF00FF" ), - MenuRowDef(ScreenEdit::file1_type, - "File1 Type", - true, - EditMode_Full, true, true, 0, "Song BGAnimation", "Song Movie", - "Song Bitmap", "Global BGAnimation", "Global Movie", - "Global Movie from Song Group", "Global Movie from Song Group and Genre", - "Dynamic Random", "Baked Random", "None" ), - MenuRowDef(ScreenEdit::file1_song_bganimation, - "File1 Song BGAnimation", - EnabledIfSet1SongBGAnimation, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_song_movie, - "File1 Song Movie", - EnabledIfSet1SongMovie, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_song_still, - "File1 Song Still", - EnabledIfSet1SongBitmap, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_global_bganimation, - "File1 Global BGAnimation", - EnabledIfSet1GlobalBGAnimation, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_global_movie, - "File1 Global Movie", - EnabledIfSet1GlobalMovie, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_global_movie_song_group, - "File1 Global Movie (Group)", - EnabledIfSet1GlobalMovieSongGroup, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file1_global_movie_song_group_and_genre, - "File1 Global Movie (Group + Genre)", - EnabledIfSet1GlobalMovieSongGroupAndGenre, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_type, - "File2 Type", - true, - EditMode_Full, true, true, 0, "Song BGAnimation", "Song Movie", - "Song Bitmap", "Global BGAnimation", "Global Movie", - "Global Movie from Song Group", "Global Movie from Song Group and Genre", - "Dynamic Random", "Baked Random", "None" ), - MenuRowDef(ScreenEdit::file2_song_bganimation, - "File2 Song BGAnimation", - EnabledIfSet2SongBGAnimation, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_song_movie, - "File2 Song Movie", - EnabledIfSet2SongMovie, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_song_still, - "File2 Song Still", - EnabledIfSet2SongBitmap, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_global_bganimation, - "File2 Global BGAnimation", - EnabledIfSet2GlobalBGAnimation, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_global_movie, - "File2 Global Movie", - EnabledIfSet2GlobalMovie, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_global_movie_song_group, - "File2 Global Movie (Group)", - EnabledIfSet2GlobalMovieSongGroup, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::file2_global_movie_song_group_and_genre, - "File2 Global Movie (Group + Genre)", - EnabledIfSet2GlobalMovieSongGroupAndGenre, - EditMode_Full, true, false, 0, NULL ), - MenuRowDef(ScreenEdit::delete_change, - "Remove Change", - true, - EditMode_Full, true, true, 0, NULL ) -); -static bool EnabledIfSet1SongBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_bganimation && !g_BackgroundChange.rows[ScreenEdit::file1_song_bganimation].choices.empty(); } -static bool EnabledIfSet1SongMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_movie && !g_BackgroundChange.rows[ScreenEdit::file1_song_movie].choices.empty(); } -static bool EnabledIfSet1SongBitmap() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_bitmap && !g_BackgroundChange.rows[ScreenEdit::file1_song_still].choices.empty(); } -static bool EnabledIfSet1GlobalBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_bganimation && !g_BackgroundChange.rows[ScreenEdit::file1_global_bganimation].choices.empty(); } -static bool EnabledIfSet1GlobalMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie].choices.empty(); } -static bool EnabledIfSet1GlobalMovieSongGroup() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie_song_group && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie_song_group].choices.empty(); } -static bool EnabledIfSet1GlobalMovieSongGroupAndGenre() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie_song_group_and_genre && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie_song_group_and_genre].choices.empty(); } -static bool EnabledIfSet2SongBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_bganimation && !g_BackgroundChange.rows[ScreenEdit::file2_song_bganimation].choices.empty(); } -static bool EnabledIfSet2SongMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_movie && !g_BackgroundChange.rows[ScreenEdit::file2_song_movie].choices.empty(); } -static bool EnabledIfSet2SongBitmap() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_bitmap && !g_BackgroundChange.rows[ScreenEdit::file2_song_still].choices.empty(); } -static bool EnabledIfSet2GlobalBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_bganimation && !g_BackgroundChange.rows[ScreenEdit::file2_global_bganimation].choices.empty(); } -static bool EnabledIfSet2GlobalMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie].choices.empty(); } -static bool EnabledIfSet2GlobalMovieSongGroup() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie_song_group && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie_song_group].choices.empty(); } -static bool EnabledIfSet2GlobalMovieSongGroupAndGenre() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie_song_group_and_genre && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie_song_group_and_genre].choices.empty(); } - -static RString GetOneBakedRandomFile( Song *pSong, bool bTryGenre = true ) -{ - vector vsPaths; - vector vsNames; - BackgroundUtil::GetGlobalRandomMovies( - pSong, - "", - vsPaths, - vsNames, - bTryGenre ); - - return vsNames.empty() ? RString() : vsNames[RandomInt(vsNames.size())]; -} - -static MenuDef g_InsertTapAttack( - "ScreenMiniMenuInsertTapAttack", - MenuRowDef(-1, - "Duration seconds", - true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), // TODO: Replace - MenuRowDef(-1, - "Set modifiers", - true, EditMode_Practice, true, true, 0, "Press Start" ) -); - -static MenuDef g_InsertCourseAttack( - "ScreenMiniMenuInsertCourseAttack", - MenuRowDef(ScreenEdit::duration, - "Duration seconds", - true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), - MenuRowDef(ScreenEdit::set_mods, - "Set modifiers", - true, EditMode_Practice, true, true, 0, "Press Start" ), - MenuRowDef(ScreenEdit::remove, - "Remove", - true, EditMode_Practice, true, true, 0, "Press Start" ) -); - -static MenuDef g_InsertStepAttack( - "ScreenMiniMenuInsertCourseAttack", - MenuRowDef(ScreenEdit::sa_duration, - "Duration seconds", - true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), - MenuRowDef(ScreenEdit::sa_set_mods, - "Set modifiers", - true, EditMode_Practice, true, true, 0, "Press Start" ), - MenuRowDef(ScreenEdit::sa_remove, - "Remove", - true, EditMode_Practice, true, true, 0, "Press Start" )); - -static MenuDef g_CourseMode( - "ScreenMiniMenuCourseDisplay", - MenuRowDef( -1, "Play mods from course", true, EditMode_Practice, true, false, 0, NULL ) -); - -// HACK: need to remember the track we're inserting on so that we can lay the -// attack note after coming back from menus. -static int g_iLastInsertTapAttackTrack = -1; -static float g_fLastInsertAttackDurationSeconds = -1; -static float g_fLastInsertAttackPositionSeconds = -1; -static BackgroundLayer g_CurrentBGChangeLayer = BACKGROUND_LAYER_Invalid; - -static void SetDefaultEditorNoteSkin( size_t num, RString &sNameOut, RString &defaultValueOut ) -{ - sNameOut = ssprintf( "EditorNoteSkinP%d", int(num + 1) ); - - // XXX: We need more supported noteskins. - defaultValueOut = "default"; -} - -static Preference1D EDITOR_NOTE_SKINS( SetDefaultEditorNoteSkin, NUM_PLAYERS ); - -static ThemeMetric EDIT_MODIFIERS ("ScreenEdit","EditModifiers"); - -static ThemeMetric LOOP_ON_CHART_END ("ScreenEdit","LoopOnChartEnd"); - -REGISTER_SCREEN_CLASS( ScreenEdit ); - -void ScreenEdit::Init() -{ - m_pSoundMusic = NULL; - - GAMESTATE->m_bIsUsingStepTiming = false; - GAMESTATE->m_bInStepEditor = true; - - SubscribeToMessage( "Judgment" ); - - ASSERT( GAMESTATE->m_pCurSong != NULL ); - ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); - - EDIT_MODE.Load( m_sName, "EditMode" ); - ScreenWithMenuElements::Init(); - - InitEditMappings(); - - currentCycleSegment = SEGMENT_LABEL; - - // save the originals for reverting later - CopyToLastSave(); - - m_CurrentAction = MAIN_MENU_CHOICE_INVALID; - m_InputPlayerNumber = PLAYER_INVALID; - - if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides ) - m_InputPlayerNumber = PLAYER_1; - - FOREACH_PlayerNumber( p ) - GAMESTATE->m_bSideIsJoined[p] = false; - GAMESTATE->m_bSideIsJoined[PLAYER_1] = true; - - m_pSong = GAMESTATE->m_pCurSong; - m_pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - - /* The user will most likely switch into Step Timing after laying down - some initial notes. It also throws off many people at first glance. - */ - if( m_pSong->IsStepsUsingDifferentTiming(m_pSteps) ) - GAMESTATE->m_bIsUsingStepTiming = true; - - m_bReturnToRecordMenuAfterPlay = false; - m_fBeatToReturnTo = 0; - - m_selectedTap = TAP_ORIGINAL_TAP; - - GAMESTATE->m_bGameplayLeadIn.Set( true ); - GAMESTATE->m_EditMode = EDIT_MODE.GetValue(); - SetBeat(0); - m_fTrailingBeat = 0; - - 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 ); - m_SnapDisplay.Load(); - // xxx: hardcoded command -aj - m_SnapDisplay.SetZoom( 0.5f ); - this->AddChild( &m_SnapDisplay ); - - // We keep track of this bit of state so that when the user is in Edit/Sync Songs and makes a change to the NoteSkins, - // that change is "sticky" across multiple ScreenEdits. That is the way the rest of the options work. - // TODO: It would be cleaner to do this by making it possible to set an option in metrics.ini. - if( !GAMESTATE->m_bDidModeChangeNoteSkin ) - { - GAMESTATE->m_bDidModeChangeNoteSkin = true; - FOREACH_PlayerNumber( pn ) - { - const RString &sNoteSkin = EDITOR_NOTE_SKINS[pn].Get(); - if( NOTESKIN->DoesNoteSkinExist( sNoteSkin ) ) - PO_GROUP_ASSIGN( GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions, - ModsLevel_Preferred, m_sNoteSkin, sNoteSkin ); - } - } - m_PlayerStateEdit.m_PlayerNumber = PLAYER_1; - // If we always go with the GAMESTATE NoteSkin, we will have fun effects - // like Vivid or Flat in the editor notefield. This is not conducive to - // productive editing. - // todo: We should allow certain noteskins (note-colored/rhythm) to be - // displayed. (Perhaps this should be a noteskin metric.) -aj - if( NOTESKIN->DoesNoteSkinExist( EDITOR_NOTE_SKINS[PLAYER_1].Get() ) ) - { - PO_GROUP_ASSIGN( m_PlayerStateEdit.m_PlayerOptions, ModsLevel_Stage, m_sNoteSkin, EDITOR_NOTE_SKINS[PLAYER_1].Get() ); - } - else - { - PO_GROUP_ASSIGN( m_PlayerStateEdit.m_PlayerOptions, ModsLevel_Stage, m_sNoteSkin, GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetStage().m_sNoteSkin ); - } - m_PlayerStateEdit.m_PlayerOptions.FromString( ModsLevel_Stage, EDIT_MODIFIERS ); - - this->originalPlayerOptions.FromString(ModsLevel_Stage, EDIT_MODIFIERS); - - m_pSteps->GetNoteData( m_NoteDataEdit ); - m_NoteFieldEdit.SetXY( EDIT_X, EDIT_Y ); - m_NoteFieldEdit.SetZoom( 0.5f ); - m_NoteFieldEdit.Init( &m_PlayerStateEdit, PLAYER_HEIGHT*2 ); - m_NoteFieldEdit.Load( &m_NoteDataEdit, -240, 850 ); - this->AddChild( &m_NoteFieldEdit ); - - m_NoteDataRecord.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); - m_NoteFieldRecord.SetXY( RECORD_X, RECORD_Y ); - m_NoteFieldRecord.Init( GAMESTATE->m_pPlayerState[PLAYER_1], PLAYER_HEIGHT ); - m_NoteFieldRecord.Load( &m_NoteDataRecord, -120, 425 ); - this->AddChild( &m_NoteFieldRecord ); - - m_EditState = EditState_Invalid; - TransitionEditState( STATE_EDITING ); - - m_bRemoveNoteButtonDown = false; - - m_Clipboard.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); - - clipboardFullTiming = GAMESTATE->m_pCurSong->m_SongTiming; // always have a backup. - - m_bHasUndo = false; - m_Undo.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); - - m_bDirty = m_NoteDataEdit.IsEmpty(); // require the usage of saving if empty. - - m_Player->Init( "Player", GAMESTATE->m_pPlayerState[PLAYER_1], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); - m_Player->CacheAllUsedNoteSkins(); - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_HUMAN; - m_Player->SetXY( PLAYER_X, PLAYER_Y ); - this->AddChild( m_Player ); - - this->AddChild( &m_Foreground ); - - m_textInputTips.SetName( "EditHelp" ); - m_textInputTips.LoadFromFont( THEME->GetPathF("ScreenEdit","EditHelp") ); - m_textInputTips.SetText( EDIT_HELP_TEXT ); - LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textInputTips ); - this->AddChild( &m_textInputTips ); - - m_textInfo.SetName( "Info" ); - m_textInfo.LoadFromFont( THEME->GetPathF("ScreenEdit","Info") ); - LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textInfo ); - this->AddChild( &m_textInfo ); - - m_textPlayRecordHelp.SetName( "PlayRecordHelp" ); - m_textPlayRecordHelp.LoadFromFont( THEME->GetPathF("ScreenEdit","PlayRecordHelp") ); - m_textPlayRecordHelp.SetText( PLAY_RECORD_HELP_TEXT ); - LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textPlayRecordHelp ); - this->AddChild( &m_textPlayRecordHelp ); - - m_soundAddNote.Load( THEME->GetPathS("ScreenEdit","AddNote"), true ); - m_soundRemoveNote.Load( THEME->GetPathS("ScreenEdit","RemoveNote"), true ); - m_soundChangeLine.Load( THEME->GetPathS("ScreenEdit","line"), true ); - m_soundChangeSnap.Load( THEME->GetPathS("ScreenEdit","snap"), true ); - m_soundMarker.Load( THEME->GetPathS("ScreenEdit","marker"), true ); - m_soundValueIncrease.Load( THEME->GetPathS("ScreenEdit","value increase"), true ); - m_soundValueDecrease.Load( THEME->GetPathS("ScreenEdit","value decrease"), true ); - m_soundSwitchPlayer.Load( THEME->GetPathS("ScreenEdit","switch player"), true ); - m_soundSwitchTiming.Load( THEME->GetPathS("ScreenEdit","switch timing"), true ); - m_soundSwitchSteps.Load( THEME->GetPathS("ScreenEdit","switch steps") ); - m_soundSave.Load( THEME->GetPathS("ScreenEdit","save") ); - m_GameplayAssist.Init(); - - m_AutoKeysounds.FinishLoading(); - m_pSoundMusic = m_AutoKeysounds.GetSound(); - - this->HandleScreenMessage( SM_UpdateTextInfo ); - m_bTextInfoNeedsUpdate = true; - - SubscribeToMessage( Message_SongModified ); -} - -ScreenEdit::~ScreenEdit() -{ - // UGLY: Don't delete the Song's steps. - m_SongLastSave.DetachSteps(); - - LOG->Trace( "ScreenEdit::~ScreenEdit()" ); - m_pSoundMusic->StopPlaying(); - - // Go back to Step Timing on leave. - GAMESTATE->m_bIsUsingStepTiming = true; - // DEFINITELY reset the InStepEditor variable. - GAMESTATE->m_bInStepEditor = false; -} - -void ScreenEdit::BeginScreen() -{ - ScreenWithMenuElements::BeginScreen(); - - /* We do this ourself. */ - SOUND->HandleSongTimer( false ); -} - -void ScreenEdit::EndScreen() -{ - ScreenWithMenuElements::EndScreen(); - - SOUND->HandleSongTimer( true ); -} - -// play assist ticks -void ScreenEdit::PlayTicks() -{ - if( m_EditState != STATE_PLAYING ) - return; - - m_GameplayAssist.PlayTicks( m_Player->GetNoteData(), m_Player->GetPlayerState() ); -} - -static ThemeMetric FADE_IN_PREVIEW("ScreenEdit", "FadeInPreview"); -static ThemeMetric FADE_OUT_PREVIEW("ScreenEdit", "FadeOutPreview"); - -void ScreenEdit::PlayPreviewMusic() -{ - SOUND->StopMusic(); - SOUND->PlayMusic( - m_pSong->GetMusicPath(), - NULL, - false, - m_pSong->m_fMusicSampleStartSeconds, - m_pSong->m_fMusicSampleLengthSeconds, - FADE_IN_PREVIEW, - FADE_OUT_PREVIEW ); -} - -void ScreenEdit::MakeFilteredMenuDef( const MenuDef* pDef, MenuDef &menu ) -{ - menu = *pDef; - menu.rows.clear(); - - vector aRows; - FOREACH_CONST( MenuRowDef, pDef->rows, r ) - { - // Don't add rows that aren't applicable to this edit mode. - if( EDIT_MODE >= r->emShowIn ) - menu.rows.push_back( *r ); - } -} - -void ScreenEdit::EditMiniMenu( const MenuDef* pDef, ScreenMessage SM_SendOnOK, ScreenMessage SM_SendOnCancel ) -{ - // Reload options. - MenuDef menu(""); - MakeFilteredMenuDef( pDef, menu ); - ScreenMiniMenu::MiniMenu( &menu, SM_SendOnOK, SM_SendOnCancel ); -} - -void ScreenEdit::Update( float fDeltaTime ) -{ - m_PlayerStateEdit.Update( fDeltaTime ); - - if( m_pSoundMusic->IsPlaying() ) - { - RageTimer tm; - const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm ); - GAMESTATE->UpdateSongPosition( fSeconds, GAMESTATE->m_pCurSong->m_SongTiming, tm, true ); - } - - if( m_EditState == STATE_RECORDING ) - { - // TODO: Find a way to prevent STATE_RECORDING when in Song Timing. - - for( int t=0; tGetCurrentStyle()->m_iColsPerPlayer; t++ ) // for each track - { - GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( t, PLAYER_1 ); - float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); - fSecsHeld = min( fSecsHeld, m_RemoveNoteButtonLastChanged.Ago() ); - if( fSecsHeld == 0 ) - continue; - - // TODO: Currently, this will ignore a hold note that's - // held past the end of selection. Ideally the hold - // note should be created, ending right at the end of - // selection (or appropriate quantization). - float fStartPlayingAtBeat = NoteRowToBeat(m_iStartPlayingAt); - float fStopPlayingAtBeat = NoteRowToBeat(m_iStopPlayingAt); - if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat <= fStartPlayingAtBeat || - GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat >= fStopPlayingAtBeat ) - continue; - - float fStartedHoldingSeconds = m_pSoundMusic->GetPositionSeconds() - fSecsHeld; - float fStartBeat = max( fStartPlayingAtBeat, m_pSteps->GetTimingData()->GetBeatFromElapsedTime(fStartedHoldingSeconds) ); - float fEndBeat = max( fStartBeat, GetBeat() ); - fEndBeat = min( fEndBeat, fStopPlayingAtBeat ); - - // Round start and end to the nearest snap interval - fStartBeat = Quantize( fStartBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); - fEndBeat = Quantize( fEndBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); - - if( m_bRemoveNoteButtonDown ) - { - m_NoteDataRecord.ClearRangeForTrack( BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat), t ); - } - else if( fSecsHeld > RECORD_HOLD_SECONDS ) - { - // create or extend a hold or roll note - TapNote tn = EditIsBeingPressed(EDIT_BUTTON_LAY_ROLL) ? TAP_ORIGINAL_ROLL_HEAD: TAP_ORIGINAL_HOLD_HEAD; - - tn.pn = m_InputPlayerNumber; - m_NoteDataRecord.AddHoldNote( t, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat), tn ); - } - } - } - - // - // check for end of playback/record - // - if( m_EditState == STATE_RECORDING || m_EditState == STATE_PLAYING ) - { - /* - * If any arrow is being held, continue for up to half a second after - * the end marker. This makes it possible to start a hold note near - * the end of the range. We won't allow placing anything outside of the - * range. - */ - bool bButtonIsBeingPressed = false; - for( int t=0; tGetCurrentStyle()->m_iColsPerPlayer; t++ ) // for each track - { - GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( t, PLAYER_1 ); - if( INPUTMAPPER->IsBeingPressed(GameI) ) - bButtonIsBeingPressed = true; - } - - float fLastBeat = NoteRowToBeat(m_iStopPlayingAt); - if( bButtonIsBeingPressed && m_EditState == STATE_RECORDING ) - { - float fSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( fLastBeat ); - fLastBeat = m_pSteps->GetTimingData()->GetBeatFromElapsedTime( fSeconds + 0.5f ); - } - - float fStopAtSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( NoteRowToBeat(m_iStopPlayingAt) ) + 1; - if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fMusicSeconds > fStopAtSeconds ) - { - TransitionEditState( ( LOOP_ON_CHART_END ? STATE_PLAYING : STATE_EDITING ) ); - } - } - - - //LOG->Trace( "ScreenEdit::Update(%f)", fDeltaTime ); - ScreenWithMenuElements::Update( fDeltaTime ); - - - // Update trailing beat - float fDelta = GetBeat() - m_fTrailingBeat; - if( fabsf(fDelta) < 10 ) - fapproach( m_fTrailingBeat, GetBeat(), - fDeltaTime*40 / m_NoteFieldEdit.GetPlayerState()->m_PlayerOptions.GetCurrent().m_fScrollSpeed ); - else - fapproach( m_fTrailingBeat, GetBeat(), - fabsf(fDelta) * fDeltaTime*5 ); - - 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; -} - -// TODO: Fix this mess. -Colby -static LocalizedString CURRENT_BEAT("ScreenEdit", "Current beat"); -static LocalizedString CURRENT_SECOND("ScreenEdit", "Current second"); -static LocalizedString SNAP_TO("ScreenEdit", "Snap to"); -static LocalizedString NOTES("ScreenEdit", "%s notes"); -static LocalizedString SELECTION_BEAT("ScreenEdit", "Selection beat"); -static LocalizedString DIFFICULTY("ScreenEdit", "Difficulty"); -static LocalizedString ROUTINE_PLAYER("ScreenEdit", "Routine Player"); -static LocalizedString CHART_NAME("ScreenEdit", "Chart Name"); -static LocalizedString DESCRIPTION("ScreenEdit", "Description"); -static LocalizedString CHART_STYLE("ScreenEdit", "Chart Style"); -static LocalizedString STEP_AUTHOR("ScreenEdit", "Step Author"); -static LocalizedString MAIN_TITLE("ScreenEdit", "Main title"); -static LocalizedString SUBTITLE("ScreenEdit", "Subtitle"); -static LocalizedString TAP_NOTE_TYPE("ScreenEdit", "Tap Note"); -static LocalizedString SEGMENT_TYPE("ScreenEdit", "Segment"); -static LocalizedString TAP_STEPS("ScreenEdit", "Tap Steps"); -static LocalizedString JUMPS("ScreenEdit", "Jumps"); -static LocalizedString HANDS("ScreenEdit", "Hands"); -static LocalizedString HOLDS("ScreenEdit", "Holds"); -static LocalizedString MINES("ScreenEdit", "Mines"); -static LocalizedString ROLLS("ScreenEdit", "Rolls"); -static LocalizedString LIFTS("ScreenEdit", "Lifts"); -static LocalizedString FAKES("ScreenEdit", "Fakes"); -static LocalizedString TIMING_MODE("ScreenEdit", "Timing Mode"); -static LocalizedString STEP_TIMING("ScreenEdit", "Step Timing"); -static LocalizedString SONG_TIMING("ScreenEdit", "Song Timing"); -static LocalizedString BEAT_0_OFFSET("ScreenEdit", "Beat 0 offset"); -static LocalizedString PREVIEW_START("ScreenEdit", "Preview Start"); -static LocalizedString PREVIEW_LENGTH("ScreenEdit", "Preview Length"); - -static ThemeMetric CURRENT_BEAT_FORMAT("ScreenEdit", "CurrentBeatFormat"); -static ThemeMetric CURRENT_SECOND_FORMAT("ScreenEdit", "CurrentSecondFormat"); -static ThemeMetric SNAP_TO_FORMAT("ScreenEdit", "SnapToFormat"); -static ThemeMetric SELECTION_BEAT_BEGIN_FORMAT("ScreenEdit", "SelectionBeatBeginFormat"); -static ThemeMetric SELECTION_BEAT_UNFINISHED_FORMAT("ScreenEdit", "SelectionBeatUnfinishedFormat"); -static ThemeMetric SELECTION_BEAT_END_FORMAT("ScreenEdit", "SelectionBeatEndFormat"); -static ThemeMetric DIFFICULTY_FORMAT("ScreenEdit", "DifficultyFormat"); -static ThemeMetric ROUTINE_PLAYER_FORMAT("ScreenEdit", "RoutinePlayerFormat"); -static ThemeMetric CHART_NAME_FORMAT("ScreenEdit", "ChartNameFormat"); -static ThemeMetric DESCRIPTION_FORMAT("ScreenEdit", "DescriptionFormat"); -static ThemeMetric CHART_STYLE_FORMAT("ScreenEdit", "ChartStyleFormat"); -static ThemeMetric STEP_AUTHOR_FORMAT("ScreenEdit", "StepAuthorFormat"); -static ThemeMetric MAIN_TITLE_FORMAT("ScreenEdit", "MainTitleFormat"); -static ThemeMetric SUBTITLE_FORMAT("ScreenEdit", "SubtitleFormat"); -static ThemeMetric TAP_NOTE_TYPE_FORMAT("ScreenEdit", "TapNoteTypeFormat"); -static ThemeMetric SEGMENT_TYPE_FORMAT("ScreenEdit", "SegmentTypeFormat"); -static ThemeMetric NUM_STEPS_FORMAT("ScreenEdit", "NumStepsFormat"); -static ThemeMetric NUM_JUMPS_FORMAT("ScreenEdit", "NumJumpsFormat"); -static ThemeMetric NUM_HOLDS_FORMAT("ScreenEdit", "NumHoldsFormat"); -static ThemeMetric NUM_MINES_FORMAT("ScreenEdit", "NumMinesFormat"); -static ThemeMetric NUM_HANDS_FORMAT("ScreenEdit", "NumHandsFormat"); -static ThemeMetric NUM_ROLLS_FORMAT("ScreenEdit", "NumRollsFormat"); -static ThemeMetric NUM_LIFTS_FORMAT("ScreenEdit", "NumLiftsFormat"); -static ThemeMetric NUM_FAKES_FORMAT("ScreenEdit", "NumFakesFormat"); -static ThemeMetric NUM_STEPS_FORMAT_TWO_PLAYER("ScreenEdit", "NumStepsFormatTwoPlayer"); -static ThemeMetric NUM_JUMPS_FORMAT_TWO_PLAYER("ScreenEdit", "NumJumpsFormatTwoPlayer"); -static ThemeMetric NUM_HOLDS_FORMAT_TWO_PLAYER("ScreenEdit", "NumHoldsFormatTwoPlayer"); -static ThemeMetric NUM_MINES_FORMAT_TWO_PLAYER("ScreenEdit", "NumMinesFormatTwoPlayer"); -static ThemeMetric NUM_HANDS_FORMAT_TWO_PLAYER("ScreenEdit", "NumHandsFormatTwoPlayer"); -static ThemeMetric NUM_ROLLS_FORMAT_TWO_PLAYER("ScreenEdit", "NumRollsFormatTwoPlayer"); -static ThemeMetric NUM_LIFTS_FORMAT_TWO_PLAYER("ScreenEdit", "NumLiftsFormatTwoPlayer"); -static ThemeMetric NUM_FAKES_FORMAT_TWO_PLAYER("ScreenEdit", "NumFakesFormatTwoPlayer"); -static ThemeMetric TIMING_MODE_FORMAT("ScreenEdit", "TimingModeFormat"); -static ThemeMetric BEAT_0_OFFSET_FORMAT("ScreenEdit", "Beat0OffsetFormat"); -static ThemeMetric PREVIEW_START_FORMAT("ScreenEdit", "PreviewStartFormat"); -static ThemeMetric PREVIEW_LENGTH_FORMAT("ScreenEdit", "PreviewLengthFormat"); -void ScreenEdit::UpdateTextInfo() -{ - if( m_pSteps == NULL ) - return; - - // Don't update the text during playback or record. It causes skips. - if( m_EditState != STATE_EDITING ) - return; - - if( !m_bTextInfoNeedsUpdate ) - return; - - m_bTextInfoNeedsUpdate = false; - - RString sNoteType = ssprintf( NOTES.GetValue(), NoteTypeToLocalizedString(m_SnapDisplay.GetNoteType()).c_str() ); - - RString sText; - sText += ssprintf( CURRENT_BEAT_FORMAT.GetValue(), CURRENT_BEAT.GetValue().c_str(), GetBeat() ); - sText += ssprintf( CURRENT_SECOND_FORMAT.GetValue(), CURRENT_SECOND.GetValue().c_str(), GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()) ); - switch( EDIT_MODE.GetValue() ) - { - DEFAULT_FAIL( EDIT_MODE.GetValue() ); - case EditMode_Practice: - break; - case EditMode_CourseMods: - case EditMode_Home: - case EditMode_Full: - sText += ssprintf( SNAP_TO_FORMAT.GetValue(), SNAP_TO.GetValue().c_str(), sNoteType.c_str() ); - break; - } - - if( m_NoteFieldEdit.m_iBeginMarker != -1 ) - { - sText += ssprintf( SELECTION_BEAT_BEGIN_FORMAT.GetValue(), SELECTION_BEAT.GetValue().c_str(), NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - - if( m_NoteFieldEdit.m_iEndMarker != -1 ) - sText += ssprintf( SELECTION_BEAT_END_FORMAT.GetValue(), NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - else - sText += SELECTION_BEAT_UNFINISHED_FORMAT; - } - - if (EDIT_MODE.GetValue() == EditMode_Full) - { - sText += ssprintf( DIFFICULTY_FORMAT.GetValue(), DIFFICULTY.GetValue().c_str(), DifficultyToString( m_pSteps->GetDifficulty() ).c_str() ); - if ( m_InputPlayerNumber != PLAYER_INVALID ) - sText += ssprintf( ROUTINE_PLAYER_FORMAT.GetValue(), ROUTINE_PLAYER.GetValue().c_str(), m_InputPlayerNumber + 1 ); - //sText += ssprintf( DESCRIPTION_FORMAT.GetValue(), DESCRIPTION.GetValue().c_str(), m_pSteps->GetDescription().c_str() ); - sText += ssprintf( CHART_NAME_FORMAT.GetValue(), CHART_NAME.GetValue().c_str(), m_pSteps->GetChartName().c_str() ); - sText += ssprintf( STEP_AUTHOR_FORMAT.GetValue(), STEP_AUTHOR.GetValue().c_str(), m_pSteps->GetCredit().c_str() ); - //sText += ssprintf( CHART_STYLE_FORMAT.GetValue(), CHART_STYLE.GetValue().c_str(), m_pSteps->GetChartStyle().c_str() ); - sText += ssprintf( MAIN_TITLE_FORMAT.GetValue(), MAIN_TITLE.GetValue().c_str(), m_pSong->m_sMainTitle.c_str() ); - if( m_pSong->m_sSubTitle.size() ) - sText += ssprintf( SUBTITLE_FORMAT.GetValue(), SUBTITLE.GetValue().c_str(), m_pSong->m_sSubTitle.c_str() ); - 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->GetTimingData()); - const StepsTypeCategory &cat = GAMEMAN->GetStepsTypeInfo(m_pSteps->m_StepsType).m_StepsTypeCategory; - if (cat == StepsTypeCategory_Couple || cat == StepsTypeCategory_Routine) - { - pair tmp = m_NoteDataEdit.GetNumTapNotesTwoPlayer(); - sText += ssprintf(NUM_STEPS_FORMAT_TWO_PLAYER.GetValue(), - TAP_STEPS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumJumpsTwoPlayer(); - sText += ssprintf(NUM_JUMPS_FORMAT_TWO_PLAYER.GetValue(), - JUMPS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumHandsTwoPlayer(); - sText += ssprintf(NUM_HANDS_FORMAT_TWO_PLAYER.GetValue(), - HANDS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumHoldNotesTwoPlayer(); - sText += ssprintf(NUM_HOLDS_FORMAT_TWO_PLAYER.GetValue(), - HOLDS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumMinesTwoPlayer(); - sText += ssprintf(NUM_MINES_FORMAT_TWO_PLAYER.GetValue(), - MINES.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumRollsTwoPlayer(); - sText += ssprintf(NUM_ROLLS_FORMAT_TWO_PLAYER.GetValue(), - ROLLS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumLiftsTwoPlayer(); - sText += ssprintf(NUM_LIFTS_FORMAT_TWO_PLAYER.GetValue(), - LIFTS.GetValue().c_str(), - tmp.first, tmp.second); - tmp = m_NoteDataEdit.GetNumFakesTwoPlayer(); - sText += ssprintf(NUM_FAKES_FORMAT_TWO_PLAYER.GetValue(), - FAKES.GetValue().c_str(), - tmp.first, tmp.second); - } - else - { - sText += ssprintf( NUM_STEPS_FORMAT.GetValue(), TAP_STEPS.GetValue().c_str(), m_NoteDataEdit.GetNumTapNotes() ); - sText += ssprintf( NUM_JUMPS_FORMAT.GetValue(), JUMPS.GetValue().c_str(), m_NoteDataEdit.GetNumJumps() ); - sText += ssprintf( NUM_HANDS_FORMAT.GetValue(), HANDS.GetValue().c_str(), m_NoteDataEdit.GetNumHands() ); - sText += ssprintf( NUM_HOLDS_FORMAT.GetValue(), HOLDS.GetValue().c_str(), m_NoteDataEdit.GetNumHoldNotes() ); - sText += ssprintf( NUM_MINES_FORMAT.GetValue(), MINES.GetValue().c_str(), m_NoteDataEdit.GetNumMines() ); - sText += ssprintf( NUM_ROLLS_FORMAT.GetValue(), ROLLS.GetValue().c_str(), m_NoteDataEdit.GetNumRolls() ); - sText += ssprintf( NUM_LIFTS_FORMAT.GetValue(), LIFTS.GetValue().c_str(), m_NoteDataEdit.GetNumLifts() ); - sText += ssprintf( NUM_FAKES_FORMAT.GetValue(), FAKES.GetValue().c_str(), m_NoteDataEdit.GetNumFakes() ); - } - switch( EDIT_MODE.GetValue() ) - { - DEFAULT_FAIL( EDIT_MODE.GetValue() ); - case EditMode_Practice: - case EditMode_CourseMods: - case EditMode_Home: - break; - case EditMode_Full: - sText += ssprintf( TIMING_MODE_FORMAT.GetValue(), - TIMING_MODE.GetValue().c_str(), - ( GAMESTATE->m_bIsUsingStepTiming ? - STEP_TIMING.GetValue().c_str() : - SONG_TIMING.GetValue().c_str() ) ); - sText += ssprintf( BEAT_0_OFFSET_FORMAT.GetValue(), - BEAT_0_OFFSET.GetValue().c_str(), - GetAppropriateTiming().m_fBeat0OffsetInSeconds ); - sText += ssprintf( PREVIEW_START_FORMAT.GetValue(), PREVIEW_START.GetValue().c_str(), m_pSong->m_fMusicSampleStartSeconds ); - sText += ssprintf( PREVIEW_LENGTH_FORMAT.GetValue(), PREVIEW_LENGTH.GetValue().c_str(), m_pSong->m_fMusicSampleLengthSeconds ); - break; - } - - m_textInfo.SetText( sText ); - - GAMESTATE->SetProcessedTimingData(NULL); -} - -void ScreenEdit::DrawPrimitives() -{ - - if( m_pSoundMusic->IsPlaying() ) - { - ScreenWithMenuElements::DrawPrimitives(); - return; - } - - // HACK: Draw using the trailing beat - PlayerState *pPlayerState = const_cast (m_NoteFieldEdit.GetPlayerState()); - - float fPlayerSongBeat = pPlayerState->m_Position.m_fSongBeat; // save song beat - float fPlayerSongBeatNoOffset = pPlayerState->m_Position.m_fSongBeatNoOffset; - float fPlayerSongBeatVisible = pPlayerState->m_Position.m_fSongBeatVisible; - - float fGameSongBeat = GAMESTATE->m_Position.m_fSongBeat; // save song beat - float fGameSongBeatNoOffset = GAMESTATE->m_Position.m_fSongBeatNoOffset; - float fGameSongBeatVisible = GAMESTATE->m_Position.m_fSongBeatVisible; - - pPlayerState->m_Position.m_fSongBeat = m_fTrailingBeat; // put trailing beat in effect - pPlayerState->m_Position.m_fSongBeatNoOffset = m_fTrailingBeat; // put trailing beat in effect - pPlayerState->m_Position.m_fSongBeatVisible = m_fTrailingBeat; // put trailing beat in effect - - GAMESTATE->m_Position.m_fSongBeat = m_fTrailingBeat; // put trailing beat in effect - GAMESTATE->m_Position.m_fSongBeatNoOffset = m_fTrailingBeat; // put trailing beat in effect - GAMESTATE->m_Position.m_fSongBeatVisible = m_fTrailingBeat; // put trailing beat in effect - - ScreenWithMenuElements::DrawPrimitives(); - - pPlayerState->m_Position.m_fSongBeat = fPlayerSongBeat; // restore real song beat - pPlayerState->m_Position.m_fSongBeatNoOffset = fPlayerSongBeatNoOffset; - pPlayerState->m_Position.m_fSongBeatVisible = fPlayerSongBeatVisible; - - GAMESTATE->m_Position.m_fSongBeat = fGameSongBeat; // restore real song beat - GAMESTATE->m_Position.m_fSongBeatNoOffset = fGameSongBeatNoOffset; - GAMESTATE->m_Position.m_fSongBeatVisible = fGameSongBeatVisible; - -} - -bool ScreenEdit::Input( const InputEventPlus &input ) -{ -// LOG->Trace( "ScreenEdit::Input()" ); - - // invalidate input if cmd/meta is being held. - if( INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LMETA)) || - INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RMETA)) ) - return false; - - if( m_In.IsTransitioning() || m_Out.IsTransitioning() ) - return false; - - EditButton EditB = DeviceToEdit( input.DeviceI ); - if( EditB == EditButton_Invalid ) - EditB = MenuButtonToEditButton( input.MenuI ); - - if( EditB == EDIT_BUTTON_REMOVE_NOTE ) - { - /* Ugly: we need to know when the button was pressed or released, so we - * can clamp operations to that time. Should InputFilter keep track of - * last release, too? */ - m_bRemoveNoteButtonDown = (input.type != IET_RELEASE); - m_RemoveNoteButtonLastChanged.Touch(); - } - - switch( m_EditState ) - { - DEFAULT_FAIL( m_EditState ); - case STATE_EDITING: - m_bTextInfoNeedsUpdate = true; - return InputEdit( input, EditB ); - case STATE_RECORDING: - return InputRecord( input, EditB ); - case STATE_RECORDING_PAUSED: - return InputRecordPaused( input, EditB ); - case STATE_PLAYING: - return InputPlay( input, EditB ); - } -} - -static void ShiftToRightSide( int &iCol, int iNumTracks ) -{ - switch( GAMESTATE->GetCurrentStyle()->m_StyleType ) - { - DEFAULT_FAIL( GAMESTATE->GetCurrentStyle()->m_StyleType ); - case StyleType_OnePlayerOneSide: - break; - case StyleType_TwoPlayersTwoSides: - case StyleType_OnePlayerTwoSides: - case StyleType_TwoPlayersSharedSides: - iCol += iNumTracks/2; - break; - } -} - -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" ); -static LocalizedString NO_BACKGROUNDS_AVAILABLE ( "ScreenEdit", "No backgrounds available" ); -static ThemeMetric INVERT_SCROLL_BUTTONS ( "ScreenEdit", "InvertScrollSpeedButtons" ); - -bool ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) -{ - if( input.type == IET_RELEASE ) - { - if( EditPressed( EDIT_BUTTON_SCROLL_SELECT, input.DeviceI ) ) - m_iShiftAnchor = -1; - // XXX Key releases usually don't count as "handled," but what - // does it mean in this case? - return false; - } - const TimingData &sTiming = GetAppropriateTiming(); - float playerBeat = GetAppropriatePosition().m_fSongBeat; - int beatsPerMeasure = sTiming.GetTimeSignatureSegmentAtBeat( playerBeat )->GetNum(); - - switch( EditB ) - { - case EDIT_BUTTON_COLUMN_0: - case EDIT_BUTTON_COLUMN_1: - case EDIT_BUTTON_COLUMN_2: - case EDIT_BUTTON_COLUMN_3: - case EDIT_BUTTON_COLUMN_4: - case EDIT_BUTTON_COLUMN_5: - case EDIT_BUTTON_COLUMN_6: - case EDIT_BUTTON_COLUMN_7: - case EDIT_BUTTON_COLUMN_8: - case EDIT_BUTTON_COLUMN_9: - { - if( input.type != IET_FIRST_PRESS ) - return false; // We only care about first presses - - int iCol = EditB - EDIT_BUTTON_COLUMN_0; - - - // Alt + number = input to right half - if( EditIsBeingPressed(EDIT_BUTTON_RIGHT_SIDE) ) - ShiftToRightSide( iCol, m_NoteDataEdit.GetNumTracks() ); - - if( iCol >= m_NoteDataEdit.GetNumTracks() ) - return false; // this button is not in the range of columns for this Style - - const float fSongBeat = GetBeat(); - const int iSongIndex = BeatToNoteRow( fSongBeat ); - - // check for to see if the user intended to remove a HoldNote - int iHeadRow; - if( m_NoteDataEdit.IsHoldNoteAtRow( iCol, iSongIndex, &iHeadRow ) ) - { - m_soundRemoveNote.Play(); - SetDirty( true ); - SaveUndo(); - m_NoteDataEdit.SetTapNote( iCol, iHeadRow, TAP_EMPTY ); - // Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes. - } - else if( m_NoteDataEdit.GetTapNote(iCol, iSongIndex).type != TapNote::empty ) - { - m_soundRemoveNote.Play(); - SetDirty( true ); - SaveUndo(); - m_NoteDataEdit.SetTapNote( iCol, iSongIndex, TAP_EMPTY ); - // Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes. - } - else if( EditIsBeingPressed(EDIT_BUTTON_LAY_TAP_ATTACK) ) - { - g_iLastInsertTapAttackTrack = iCol; - EditMiniMenu( &g_InsertTapAttack, SM_BackFromInsertTapAttack ); - } - else - { - m_soundAddNote.Play(); - SetDirty( true ); - SaveUndo(); - TapNote tn = m_selectedTap; - tn.pn = m_InputPlayerNumber; - m_NoteDataEdit.SetTapNote(iCol, iSongIndex, tn ); - CheckNumberOfNotesAndUndo(); - } - } - return true; - - case EDIT_BUTTON_CYCLE_TAP_LEFT: - { - switch ( m_selectedTap.type ) - { - case TapNote::tap: m_selectedTap = TAP_ORIGINAL_FAKE; break; - case TapNote::mine: m_selectedTap = TAP_ORIGINAL_TAP; break; - case TapNote::lift: m_selectedTap = TAP_ORIGINAL_MINE; break; - case TapNote::fake: m_selectedTap = TAP_ORIGINAL_LIFT; break; - DEFAULT_FAIL( m_selectedTap.type ); - } - return true; - } - case EDIT_BUTTON_CYCLE_TAP_RIGHT: - { - switch ( m_selectedTap.type ) - { - case TapNote::tap: m_selectedTap = TAP_ORIGINAL_MINE; break; - case TapNote::mine: m_selectedTap = TAP_ORIGINAL_LIFT; break; - case TapNote::lift: m_selectedTap = TAP_ORIGINAL_FAKE; break; - case TapNote::fake: m_selectedTap = TAP_ORIGINAL_TAP; break; - DEFAULT_FAIL( m_selectedTap.type ); - } - return true; - } - case EDIT_BUTTON_CYCLE_SEGMENT_LEFT: - { - int tmp = enum_add2( this->currentCycleSegment, -1 ); - wrap( *ConvertValue(&tmp), NUM_TimingSegmentType ); - currentCycleSegment = (TimingSegmentType)tmp; - return true; - } - case EDIT_BUTTON_CYCLE_SEGMENT_RIGHT: - { - int tmp = enum_add2( this->currentCycleSegment, +1 ); - wrap( *ConvertValue(&tmp), NUM_TimingSegmentType ); - currentCycleSegment = (TimingSegmentType)tmp; - return true; - } - case EDIT_BUTTON_SCROLL_SPEED_UP: - case EDIT_BUTTON_SCROLL_SPEED_DOWN: - { - PlayerState *pPlayerState = const_cast (m_NoteFieldEdit.GetPlayerState()); - float fScrollSpeed = pPlayerState->m_PlayerOptions.GetSong().m_fScrollSpeed; - - const float fSpeeds[] = { 1.0f, 1.5f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f }; - int iSpeed = 0; - for( unsigned i = 0; i < ARRAYLEN(fSpeeds); ++i ) - { - if( fSpeeds[i] == fScrollSpeed ) - { - iSpeed = i; - break; - } - } - - switch( EditB ) - { - DEFAULT_FAIL(EditB); - case EDIT_BUTTON_SCROLL_SPEED_DOWN: - INVERT_SCROLL_BUTTONS ? ++iSpeed : --iSpeed; - break; - case EDIT_BUTTON_SCROLL_SPEED_UP: - INVERT_SCROLL_BUTTONS ? --iSpeed : ++iSpeed; - break; - } - iSpeed = clamp( iSpeed, 0, (int) ARRAYLEN(fSpeeds)-1 ); - - if( fSpeeds[iSpeed] != fScrollSpeed ) - { - m_soundMarker.Play(); - fScrollSpeed = fSpeeds[iSpeed]; - } - - PO_GROUP_ASSIGN( pPlayerState->m_PlayerOptions, ModsLevel_Song, m_fScrollSpeed, fScrollSpeed ); - } - - return true; - case EDIT_BUTTON_SCROLL_HOME: - ScrollTo( 0 ); - return true; - case EDIT_BUTTON_SCROLL_END: - ScrollTo( m_NoteDataEdit.GetLastBeat() ); - return true; - case EDIT_BUTTON_SCROLL_UP_LINE: - case EDIT_BUTTON_SCROLL_UP_PAGE: - case EDIT_BUTTON_SCROLL_UP_TS: - case EDIT_BUTTON_SCROLL_DOWN_LINE: - case EDIT_BUTTON_SCROLL_DOWN_PAGE: - case EDIT_BUTTON_SCROLL_DOWN_TS: - { - float fBeatsToMove=0.f; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_SCROLL_UP_LINE: - case EDIT_BUTTON_SCROLL_DOWN_LINE: - fBeatsToMove = NoteTypeToBeat( m_SnapDisplay.GetNoteType() ); - if( EditB == EDIT_BUTTON_SCROLL_UP_LINE ) - fBeatsToMove *= -1; - break; - case EDIT_BUTTON_SCROLL_UP_PAGE: - case EDIT_BUTTON_SCROLL_DOWN_PAGE: - fBeatsToMove = 4; - if( EditB == EDIT_BUTTON_SCROLL_UP_PAGE ) - fBeatsToMove *= -1; - break; - case EDIT_BUTTON_SCROLL_UP_TS: - case EDIT_BUTTON_SCROLL_DOWN_TS: - fBeatsToMove = beatsPerMeasure; - if( EditB == EDIT_BUTTON_SCROLL_UP_TS ) - fBeatsToMove *= -1; - break; - } - - if( m_PlayerStateEdit.m_PlayerOptions.GetSong().m_fScrolls[PlayerOptions::SCROLL_REVERSE] > 0.5 ) - fBeatsToMove *= -1; - - float fDestinationBeat = GetBeat() + fBeatsToMove; - fDestinationBeat = Quantize( fDestinationBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); - - ScrollTo( fDestinationBeat ); - } - return true; - case EDIT_BUTTON_SCROLL_NEXT_MEASURE: - { - float fDestinationBeat = GetBeat() + beatsPerMeasure; - fDestinationBeat = ftruncf( fDestinationBeat, (float)beatsPerMeasure ); - ScrollTo( fDestinationBeat ); - return true; - } - case EDIT_BUTTON_SCROLL_PREV_MEASURE: - { - float fDestinationBeat = QuantizeUp( GetBeat(), (float)beatsPerMeasure ); - fDestinationBeat -= (float)beatsPerMeasure; - ScrollTo( fDestinationBeat ); - return true; - } - case EDIT_BUTTON_SCROLL_NEXT: - { - int iRow = BeatToNoteRow( GetBeat() ); - NoteDataUtil::GetNextEditorPosition( m_NoteDataEdit, iRow ); - ScrollTo( NoteRowToBeat(iRow) ); - } - return true; - case EDIT_BUTTON_SCROLL_PREV: - { - int iRow = BeatToNoteRow( GetBeat() ); - NoteDataUtil::GetPrevEditorPosition( m_NoteDataEdit, iRow ); - ScrollTo( NoteRowToBeat(iRow) ); - } - return true; - case EDIT_BUTTON_SEGMENT_NEXT: - { - // TODO: Work around Stops and Delays. We MAY have to separate them. - const TimingData &timing = GetAppropriateTiming(); - ScrollTo(timing.GetNextSegmentBeatAtBeat(this->currentCycleSegment, - GetBeat())); - } - return true; - case EDIT_BUTTON_SEGMENT_PREV: - { - // TODO: Work around Stops and Delays. We MAY have to separate them. - const TimingData &timing = GetAppropriateTiming(); - ScrollTo(timing.GetPreviousSegmentBeatAtBeat(this->currentCycleSegment, - GetBeat())); - } - return true; - case EDIT_BUTTON_SNAP_NEXT: - if( m_SnapDisplay.PrevSnapMode() ) - OnSnapModeChange(); - return true; - case EDIT_BUTTON_SNAP_PREV: - if( m_SnapDisplay.NextSnapMode() ) - OnSnapModeChange(); - return true; - case EDIT_BUTTON_LAY_SELECT: - { - const int iCurrentRow = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); - if( m_NoteFieldEdit.m_iBeginMarker==-1 && m_NoteFieldEdit.m_iEndMarker==-1 ) - { - // lay begin marker - m_NoteFieldEdit.m_iBeginMarker = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); - } - else if( m_NoteFieldEdit.m_iEndMarker==-1 ) // only begin marker is laid - { - if( iCurrentRow == m_NoteFieldEdit.m_iBeginMarker ) - { - m_NoteFieldEdit.m_iBeginMarker = -1; - } - else - { - m_NoteFieldEdit.m_iEndMarker = max( m_NoteFieldEdit.m_iBeginMarker, iCurrentRow ); - m_NoteFieldEdit.m_iBeginMarker = min( m_NoteFieldEdit.m_iBeginMarker, iCurrentRow ); - } - } - else // both markers are laid - { - m_NoteFieldEdit.m_iBeginMarker = iCurrentRow; - m_NoteFieldEdit.m_iEndMarker = -1; - } - m_soundMarker.Play(); - } - return true; - case EDIT_BUTTON_OPEN_AREA_MENU: - { - // TODO: Improve behavior if timing changes are shifted down on beat 0. - g_AreaMenu.rows[shift_pauses_forward].bEnabled = (GetBeat() != 0); - g_AreaMenu.rows[paste_at_current_beat].bEnabled = !m_Clipboard.IsEmpty(); - g_AreaMenu.rows[paste_at_begin_marker].bEnabled = !m_Clipboard.IsEmpty() != 0 && m_NoteFieldEdit.m_iBeginMarker!=-1; - g_AreaMenu.rows[undo].bEnabled = m_bHasUndo; - EditMiniMenu( &g_AreaMenu, SM_BackFromAreaMenu ); - } - return true; - case EDIT_BUTTON_OPEN_ALTER_MENU: - { - bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1; - if (!bAreaSelected) - { - SCREENMAN->SystemMessage( ALTER_MENU_NO_SELECTION ); - SCREENMAN->PlayInvalidSound(); - } - else - { - // TODO: Improve interaction when field is 0. - g_AlterMenu.rows[convert_to_delay].bEnabled = (m_NoteFieldEdit.m_iBeginMarker > 0); - g_AlterMenu.rows[convert_to_pause].bEnabled = (m_NoteFieldEdit.m_iBeginMarker > 0); - bool isRoutine = (m_InputPlayerNumber != PLAYER_INVALID); - g_AlterMenu.rows[routine_invert_notes].bEnabled = isRoutine; - g_AlterMenu.rows[routine_mirror_1_to_2].bEnabled = isRoutine; - g_AlterMenu.rows[routine_mirror_2_to_1].bEnabled = isRoutine; - EditMiniMenu(&g_AlterMenu, SM_BackFromAlterMenu); - } - return true; - - } - case EDIT_BUTTON_OPEN_EDIT_MENU: - EditMiniMenu( &g_MainMenu, SM_BackFromMainMenu ); - return true; - case EDIT_BUTTON_OPEN_INPUT_HELP: - DoHelp(); - return true; - case EDIT_BUTTON_OPEN_TIMING_MENU: - { - DisplayTimingMenu(); - return true; - } - case EDIT_BUTTON_OPEN_NEXT_STEPS: - case EDIT_BUTTON_OPEN_PREV_STEPS: - { - // don't keep undo when changing Steps - ClearUndo(); - - // get the second of the current step. - float curSecond = GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()); - - // save current steps - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - ASSERT( pSteps != NULL ); - pSteps->SetNoteData( m_NoteDataEdit ); - - // Get all Steps of this StepsType - const StepsType st = pSteps->m_StepsType; - vector vSteps; - SongUtil::GetSteps( GAMESTATE->m_pCurSong, vSteps, st ); - - // Sort them by difficulty. - StepsUtil::SortStepsByTypeAndDifficulty( vSteps ); - - // Find out what index the current Steps are - vector::iterator it = find( vSteps.begin(), vSteps.end(), pSteps ); - ASSERT( it != vSteps.end() ); - - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_OPEN_PREV_STEPS: - if( it==vSteps.begin() ) - { - SCREENMAN->PlayInvalidSound(); - return true; - } - it--; - break; - case EDIT_BUTTON_OPEN_NEXT_STEPS: - it++; - if( it==vSteps.end() ) - { - SCREENMAN->PlayInvalidSound(); - return true; - } - break; - } - - pSteps = *it; - GAMESTATE->m_pCurSteps[PLAYER_1].Set( pSteps ); - m_pSteps = pSteps; - pSteps->GetNoteData( m_NoteDataEdit ); - - RString s = ssprintf( - SWITCHED_TO.GetValue() + " %s %s '%s' (%d of %d)", - GAMEMAN->GetStepsTypeInfo( st ).szName, - DifficultyToString( pSteps->GetDifficulty() ).c_str(), - pSteps->GetChartName().c_str(), - it - vSteps.begin() + 1, - int(vSteps.size()) ); - SCREENMAN->SystemMessage( s ); - m_soundSwitchSteps.Play(); - - ScrollTo( GetAppropriateTiming().GetBeatFromElapsedTime(curSecond) ); - } - return true; - case EDIT_BUTTON_BPM_UP: - case EDIT_BUTTON_BPM_DOWN: - { - float fBPM = GetAppropriateTiming().GetBPMAtBeat( GetBeat() ); - float fDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_BPM_UP: fDelta = +0.020f; break; - case EDIT_BUTTON_BPM_DOWN: fDelta = -0.020f; break; - } - if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) - { - fDelta /= 20; // .001 bpm - } - else if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fDelta *= 10; - else - fDelta *= 40; - } - - float fNewBPM = fBPM + fDelta; - GetAppropriateTimingForUpdate().SetBPMAtBeat( GetBeat(), fNewBPM ); - (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); - SetDirty( true ); - } - return true; - case EDIT_BUTTON_STOP_UP: - case EDIT_BUTTON_STOP_DOWN: - { - float fDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_STOP_UP: fDelta = +0.020f; break; - case EDIT_BUTTON_STOP_DOWN: fDelta = -0.020f; break; - } - if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) - { - fDelta /= 20; // 1ms - } - else if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fDelta *= 10; - else - fDelta *= 40; - } - - // is there a StopSegment on the current row? - TimingData & timing = GetAppropriateTimingForUpdate(); - StopSegment *seg = timing.GetStopSegmentAtRow( GetRow() ); - int i = timing.GetSegmentIndexAtRow(SEGMENT_STOP, GetRow()); - if (i == -1 || seg->GetRow() != GetRow()) // invalid - { - if( fDelta > 0 ) - timing.AddSegment( StopSegment(GetRow(), fDelta) ); - else - return false; - } - else - { - vector &stops = timing.GetTimingSegments(SEGMENT_STOP); - seg->SetPause(seg->GetPause() + fDelta); - if( seg->GetPause() <= 0 ) - stops.erase( stops.begin()+i, stops.begin()+i+1); - } - - (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); - SetDirty( true ); - } - return true; - // TODO: Combine the stop and delay call somehow? - case EDIT_BUTTON_DELAY_UP: - case EDIT_BUTTON_DELAY_DOWN: - { - float fDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_DELAY_UP: fDelta = +0.020f; break; - case EDIT_BUTTON_DELAY_DOWN: fDelta = -0.020f; break; - } - if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) - { - fDelta /= 20; // 1ms - } - else if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fDelta *= 10; - else - fDelta *= 40; - } - - // is there a StopSegment on the current row? - TimingData & timing = GetAppropriateTimingForUpdate(); - DelaySegment *seg = timing.GetDelaySegmentAtRow( GetRow() ); - int i = timing.GetSegmentIndexAtRow(SEGMENT_DELAY, GetRow()); - if (i == -1 || seg->GetRow() != GetRow()) // invalid - { - if( fDelta > 0 ) - timing.AddSegment( DelaySegment(GetRow(), fDelta) ); - else - return false; - } - else - { - vector &stops = timing.GetTimingSegments(SEGMENT_DELAY); - seg->SetPause(seg->GetPause() + fDelta); - if( seg->GetPause() <= 0 ) - stops.erase( stops.begin()+i, stops.begin()+i+1); - } - - (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); - SetDirty( true ); - } - return true; - case EDIT_BUTTON_OFFSET_UP: - case EDIT_BUTTON_OFFSET_DOWN: - { - float fDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_OFFSET_DOWN: fDelta = -0.02f; break; - case EDIT_BUTTON_OFFSET_UP: fDelta = +0.02f; break; - } - if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) - { - fDelta /= 20; /* 1ms */ - } - else if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fDelta *= 10; - else - fDelta *= 40; - } - GetAppropriateTimingForUpdate().m_fBeat0OffsetInSeconds += fDelta; - (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); - if (GAMESTATE->m_bIsUsingStepTiming) - { - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(fDelta); - } - else - { - GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(fDelta); - GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += fDelta; - } - SetDirty( true ); - } - return true; - case EDIT_BUTTON_SAMPLE_START_UP: - case EDIT_BUTTON_SAMPLE_START_DOWN: - case EDIT_BUTTON_SAMPLE_LENGTH_DOWN: - case EDIT_BUTTON_SAMPLE_LENGTH_UP: - { - float fDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_SAMPLE_START_DOWN: fDelta = -0.02f; break; - case EDIT_BUTTON_SAMPLE_START_UP: fDelta = +0.02f; break; - case EDIT_BUTTON_SAMPLE_LENGTH_DOWN: fDelta = -0.02f; break; - case EDIT_BUTTON_SAMPLE_LENGTH_UP: fDelta = +0.02f; break; - } - - if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fDelta *= 10; - else - fDelta *= 40; - } - - if( EditB == EDIT_BUTTON_SAMPLE_LENGTH_DOWN || EditB == EDIT_BUTTON_SAMPLE_LENGTH_UP ) - { - m_pSong->m_fMusicSampleLengthSeconds += fDelta; - m_pSong->m_fMusicSampleLengthSeconds = max(m_pSong->m_fMusicSampleLengthSeconds,0); - } - else - { - m_pSong->m_fMusicSampleStartSeconds += fDelta; - m_pSong->m_fMusicSampleStartSeconds = max(m_pSong->m_fMusicSampleStartSeconds,0); - } - (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); - SetDirty( true ); - } - return true; - case EDIT_BUTTON_PLAY_SAMPLE_MUSIC: - PlayPreviewMusic(); - return true; - case EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU: - case EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU: - if( !GAMESTATE->m_bIsUsingStepTiming ) - { - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU: g_CurrentBGChangeLayer = BACKGROUND_LAYER_1; break; - case EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU: g_CurrentBGChangeLayer = BACKGROUND_LAYER_2; break; - } - - { - // Fill in option names - vector vThrowAway; - - MenuDef &menu = g_BackgroundChange; - - menu.rows[layer].choices[0] = ssprintf("%d",g_CurrentBGChangeLayer); - BackgroundUtil::GetBackgroundTransitions( "", vThrowAway, menu.rows[transition].choices ); - g_BackgroundChange.rows[transition].choices.insert( g_BackgroundChange.rows[transition].choices.begin(), "None" ); // add "no transition" - BackgroundUtil::GetBackgroundEffects( "", vThrowAway, menu.rows[effect].choices ); - menu.rows[effect].choices.insert( menu.rows[effect].choices.begin(), "Default" ); // add "default effect" - - BackgroundUtil::GetSongBGAnimations( m_pSong, "", vThrowAway, menu.rows[file1_song_bganimation].choices ); - BackgroundUtil::GetSongMovies( m_pSong, "", vThrowAway, menu.rows[file1_song_movie].choices ); - BackgroundUtil::GetSongBitmaps( m_pSong, "", vThrowAway, menu.rows[file1_song_still].choices ); - BackgroundUtil::GetGlobalBGAnimations( m_pSong, "", vThrowAway, menu.rows[file1_global_bganimation].choices ); // NULL to get all background files - BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie].choices, false, false ); // all backgrounds - BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie_song_group].choices, false, true ); // song group's backgrounds - BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie_song_group_and_genre].choices, true, true ); // song group and genre's backgrounds - - menu.rows[file2_type].choices = menu.rows[file1_type].choices; - menu.rows[file2_song_bganimation].choices = menu.rows[file1_song_bganimation].choices; - menu.rows[file2_song_movie].choices = menu.rows[file1_song_movie].choices; - menu.rows[file2_song_still].choices = menu.rows[file1_song_still].choices; - menu.rows[file2_global_bganimation].choices = menu.rows[file1_global_bganimation].choices; - menu.rows[file2_global_movie].choices = menu.rows[file1_global_movie].choices; - menu.rows[file2_global_movie_song_group].choices = menu.rows[file1_global_movie_song_group].choices; - menu.rows[file2_global_movie_song_group_and_genre].choices = menu.rows[file1_global_movie_song_group_and_genre].choices; - - - // Fill in lines enabled/disabled - bool bAlreadyBGChangeHere = false; - BackgroundChange bgChange; - FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer), bgc ) - { - if( bgc->m_fStartBeat == GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat ) - { - bAlreadyBGChangeHere = true; - bgChange = *bgc; - } - } - -#define FILL_ENABLED( x ) menu.rows[x].bEnabled = menu.rows[x].choices.size() > 0; - FILL_ENABLED( transition ); - FILL_ENABLED( effect ); - FILL_ENABLED( file1_song_bganimation ); - FILL_ENABLED( file1_song_movie ); - FILL_ENABLED( file1_song_still ); - FILL_ENABLED( file1_global_bganimation ); - FILL_ENABLED( file1_global_movie ); - FILL_ENABLED( file1_global_movie_song_group ); - FILL_ENABLED( file1_global_movie_song_group_and_genre ); - FILL_ENABLED( file2_song_bganimation ); - FILL_ENABLED( file2_song_movie ); - FILL_ENABLED( file2_song_still ); - FILL_ENABLED( file2_global_bganimation ); - FILL_ENABLED( file2_global_movie ); - FILL_ENABLED( file2_global_movie_song_group ); - FILL_ENABLED( file2_global_movie_song_group_and_genre ); -#undef FILL_ENABLED - menu.rows[delete_change].bEnabled = bAlreadyBGChangeHere; - - // set default choices - menu.rows[rate]. SetDefaultChoiceIfPresent( ssprintf("%2.0f%%",bgChange.m_fRate*100) ); - menu.rows[transition]. SetDefaultChoiceIfPresent( bgChange.m_sTransition ); - menu.rows[effect]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sEffect ); - menu.rows[file1_type].iDefaultChoice = none; - if( bgChange.m_def.m_sFile1 == RANDOM_BACKGROUND_FILE ) menu.rows[file1_type].iDefaultChoice = dynamic_random; - if( menu.rows[file1_song_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_bganimation; - if( menu.rows[file1_song_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_movie; - if( menu.rows[file1_song_still]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_bitmap; - if( menu.rows[file1_global_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_bganimation; - if( menu.rows[file1_global_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie; - if( menu.rows[file1_global_movie_song_group]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie_song_group; - if( menu.rows[file1_global_movie_song_group_and_genre]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie_song_group_and_genre; - menu.rows[file2_type].iDefaultChoice = none; - if( bgChange.m_def.m_sFile2 == RANDOM_BACKGROUND_FILE ) menu.rows[file2_type].iDefaultChoice = dynamic_random; - if( menu.rows[file2_song_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_bganimation; - if( menu.rows[file2_song_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_movie; - if( menu.rows[file2_song_still]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_bitmap; - if( menu.rows[file2_global_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_bganimation; - if( menu.rows[file2_global_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie; - if( menu.rows[file2_global_movie_song_group]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie_song_group; - if( menu.rows[file2_global_movie_song_group_and_genre]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie_song_group_and_genre; - menu.rows[color1]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor1 ); - menu.rows[color2]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor2 ); - - EditMiniMenu( &g_BackgroundChange, SM_BackFromBGChange ); - } - } - else - { - SCREENMAN->SystemMessage( BG_CHANGE_STEP_TIMING ); - SCREENMAN->PlayInvalidSound(); - } - return true; - - case EDIT_BUTTON_OPEN_COURSE_MENU: - { - g_CourseMode.rows[0].choices.clear(); - g_CourseMode.rows[0].choices.push_back( CommonMetrics::LocalizeOptionItem("Off",false) ); - g_CourseMode.rows[0].iDefaultChoice = 0; - - vector courses; - SONGMAN->GetAllCourses( courses, false ); - for( unsigned i = 0; i < courses.size(); ++i ) - { - const Course *crs = courses[i]; - - bool bUsesThisSong = false; - for( unsigned e = 0; e < crs->m_vEntries.size(); ++e ) - { - if( crs->m_vEntries[e].songID.ToSong() != m_pSong ) - continue; - bUsesThisSong = true; - } - - if( bUsesThisSong ) - { - g_CourseMode.rows[0].choices.push_back( crs->GetDisplayFullTitle() ); - if( crs == GAMESTATE->m_pCurCourse ) - g_CourseMode.rows[0].iDefaultChoice = g_CourseMode.rows[0].choices.size()-1; - } - } - - EditMiniMenu( &g_CourseMode, SM_BackFromCourseModeMenu ); - } - return true; - case EDIT_BUTTON_OPEN_STEP_ATTACK_MENU: - { - this->DoStepAttackMenu(); - return true; - } - case EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU: - { - // TODO: Give Song/Step Timing switches/functions here? - Course *pCourse = GAMESTATE->m_pCurCourse; - if( pCourse == NULL ) - return false; - CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; - float fStartTime = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat ); - int iAttack = FindAttackAtTime( ce.attacks, fStartTime ); - - if( iAttack >= 0 ) - { - const RString sDuration = FloatToString(ce.attacks[iAttack].fSecsRemaining ); - - g_InsertCourseAttack.rows[remove].bEnabled = true; - if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) - g_InsertCourseAttack.rows[duration].choices.push_back( sDuration ); - else - g_InsertCourseAttack.rows[duration].choices.back() = sDuration; - g_InsertCourseAttack.rows[duration].iDefaultChoice = 9; - } - else - { - if( g_InsertCourseAttack.rows[duration].choices.size() == 10 ) - g_InsertCourseAttack.rows[duration].choices.pop_back(); - g_InsertCourseAttack.rows[duration].iDefaultChoice = 3; - } - - EditMiniMenu( &g_InsertCourseAttack, SM_BackFromInsertCourseAttack ); - } - return true; - case EDIT_BUTTON_ADD_STEP_MODS: - { - float start = -1; - float end = -1; - PlayerOptions po; - - if (m_NoteFieldEdit.m_iBeginMarker == -1) // not highlighted - { - po.FromString(""); - } - else - { - const TimingData &timing = GetAppropriateTiming(); - start = timing.GetElapsedTimeFromBeat(NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker)); - AttackArray &attacks = - (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); - int index = FindAttackAtTime(attacks, start); - - if (index >= 0) - { - po.FromString(""); - } - if (m_NoteFieldEdit.m_iEndMarker == -1) - { - end = m_pSong->m_fMusicLengthSeconds; - } - else - { - end = timing.GetElapsedTimeFromBeat(NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker)); - } - - } - ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; - this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); - g_fLastInsertAttackPositionSeconds = start; - g_fLastInsertAttackDurationSeconds = end - start; - toEdit.Assign( ModsLevel_Stage, po ); - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); - return true; - } - case EDIT_BUTTON_ADD_COURSE_MODS: - { - float fStart, fEnd; - PlayerOptions po; - const Course *pCourse = GAMESTATE->m_pCurCourse; - if( pCourse == NULL ) - return false; - const CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; - - if( m_NoteFieldEdit.m_iBeginMarker == -1 ) - { - fStart = -1; - fEnd = -1; - po.FromString( ce.sModifiers ); - } - else - { - // TODO: Give Song/Step Timing switches/functions here? - TimingData *timing = m_pSteps->GetTimingData(); - fStart = timing->GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - int iAttack = FindAttackAtTime( ce.attacks, fStart ); - - if( iAttack >= 0 ) - po.FromString( ce.attacks[iAttack].sModifiers ); - - if( m_NoteFieldEdit.m_iEndMarker == -1 ) - fEnd = m_pSong->m_fMusicLengthSeconds; - else - fEnd = timing->GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - } - g_fLastInsertAttackPositionSeconds = fStart; - g_fLastInsertAttackDurationSeconds = fEnd - fStart; - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.Assign( ModsLevel_Stage, po ); - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertCourseAttackPlayerOptions ); - - } - return true; - case EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP: - case EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE: - { - bool bTryGenre = EditB == EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE; - RString sName = GetOneBakedRandomFile(m_pSong, bTryGenre); - if( sName.empty() ) - { - SCREENMAN->PlayInvalidSound(); - SCREENMAN->SystemMessage( NO_BACKGROUNDS_AVAILABLE ); - } - else - { - BackgroundLayer iLayer = BACKGROUND_LAYER_1; - BackgroundChange bgChange; - bgChange.m_fStartBeat = GAMESTATE->m_Position.m_fSongBeat; - FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(iLayer), bgc ) - { - if( bgc->m_fStartBeat == GAMESTATE->m_Position.m_fSongBeat ) - { - bgChange = *bgc; - m_pSong->GetBackgroundChanges(iLayer).erase( bgc ); - break; - } - } - bgChange.m_def.m_sFile1 = sName; - m_pSong->AddBackgroundChange( iLayer, bgChange ); - m_soundMarker.Play(); - } - } - return true; - - case EDIT_BUTTON_PLAY_FROM_START: - HandleMainMenuChoice( play_whole_song ); - return true; - - case EDIT_BUTTON_PLAY_FROM_CURSOR: - HandleMainMenuChoice( play_current_beat_to_end ); - return true; - - case EDIT_BUTTON_PLAY_SELECTION: - HandleMainMenuChoice( play_selection ); - return true; - case EDIT_BUTTON_RECORD_SELECTION: - if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) - { - HandleAlterMenuChoice( record ); - } - else - { - if( g_iDefaultRecordLength.Get() == -1 ) - { - m_iStartPlayingAt = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); - m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() + 1 ); - } - else - { - m_iStartPlayingAt = BeatToNoteRow( ftruncf(GetAppropriatePosition().m_fSongBeat, g_iDefaultRecordLength.Get()) ); - m_iStopPlayingAt = m_iStartPlayingAt + BeatToNoteRow( g_iDefaultRecordLength.Get() ); - } - - if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) - m_iStopPlayingAt = min( m_iStopPlayingAt, BeatToNoteRow(GetMaximumBeatForNewNote()) ); - - if( m_iStartPlayingAt >= m_iStopPlayingAt ) - { - SCREENMAN->PlayInvalidSound(); - return true; - } - - TransitionEditState( STATE_RECORDING ); - } - return true; - case EDIT_BUTTON_RECORD_FROM_CURSOR: - m_iStartPlayingAt = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); - m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); - TransitionEditState( STATE_RECORDING ); - return true; - - case EDIT_BUTTON_INSERT: - HandleAreaMenuChoice( insert_and_shift ); - SCREENMAN->PlayInvalidSound(); - return true; - - case EDIT_BUTTON_INSERT_SHIFT_PAUSES: - HandleAreaMenuChoice( shift_pauses_forward ); - SCREENMAN->PlayInvalidSound(); - return true; - - case EDIT_BUTTON_DELETE: - HandleAreaMenuChoice( delete_and_shift ); - SCREENMAN->PlayInvalidSound(); - return true; - - case EDIT_BUTTON_DELETE_SHIFT_PAUSES: - HandleAreaMenuChoice( shift_pauses_backward ); - SCREENMAN->PlayInvalidSound(); - return true; - - case EDIT_BUTTON_SAVE: - HandleMainMenuChoice( ScreenEdit::save ); - return true; - - case EDIT_BUTTON_UNDO: - Undo(); - return true; - - case EDIT_BUTTON_SWITCH_PLAYERS: - if( m_InputPlayerNumber == PLAYER_INVALID ) - return false; - enum_add( m_InputPlayerNumber, 1 ); - if( m_InputPlayerNumber == NUM_PLAYERS ) - m_InputPlayerNumber = PLAYER_1; - m_soundSwitchPlayer.Play(); - return true; - - case EDIT_BUTTON_SWITCH_TIMINGS: - GAMESTATE->m_bIsUsingStepTiming = !GAMESTATE->m_bIsUsingStepTiming; - m_soundSwitchTiming.Play(); - return true; - default: - return false; - } -} - -bool ScreenEdit::InputRecord( const InputEventPlus &input, EditButton EditB ) -{ - if( input.type == IET_FIRST_PRESS && EditB == EDIT_BUTTON_RETURN_TO_EDIT ) - { - TransitionEditState( STATE_EDITING ); - return true; - } - - if( input.pn != PLAYER_1 ) - return false; // ignore - - const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI ); - - //Is this actually a column? If not, ignore the input. - if( iCol == -1 ) - return false; - - switch( input.type ) - { - DEFAULT_FAIL( input.type ); - case IET_FIRST_PRESS: - { - if( EditIsBeingPressed(EDIT_BUTTON_REMOVE_NOTE) ) - { - // Remove notes in Update. - return false; - } - - // Add a tap - float fBeat = GetBeat(); - fBeat = Quantize( fBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); - - const int iRow = BeatToNoteRow( fBeat ); - if( iRow < 0 ) - return false; - - // Don't add outside of the range. - if( iRow < m_iStartPlayingAt || iRow >= m_iStopPlayingAt ) - return false; - - // Remove hold if any so that we don't have taps inside of a hold. - int iHeadRow; - if( m_NoteDataRecord.IsHoldNoteAtRow( iCol, iRow, &iHeadRow ) ) - m_NoteDataRecord.SetTapNote( iCol, iHeadRow, TAP_EMPTY ); - - TapNote tn = TAP_ORIGINAL_TAP; - tn.pn = m_InputPlayerNumber; - m_NoteDataRecord.SetTapNote( iCol, iRow, tn ); - m_NoteFieldRecord.Step( iCol, TNS_W1 ); - } - return true; - case IET_REPEAT: - case IET_RELEASE: - // don't add or extend holds here; we do it in Update() - return false; - } -} - -bool ScreenEdit::InputRecordPaused( const InputEventPlus &input, EditButton EditB ) -{ - if( input.type != IET_FIRST_PRESS ) - return false; // don't care - - switch( EditB ) - { - case EDIT_BUTTON_UNDO: - /* We've already actually committed changes to m_NoteDataEdit, so all we have - * to do to undo is Undo() as usual, and copy the note data back in. */ - Undo(); - m_NoteDataRecord.CopyAll( m_NoteDataEdit ); - return true; - - case EDIT_BUTTON_PLAY_SELECTION: - TransitionEditState( STATE_PLAYING ); - return true; - - case EDIT_BUTTON_RECORD_SELECTION: - TransitionEditState( STATE_RECORDING ); - return true; - - case EDIT_BUTTON_RECORD_FROM_CURSOR: - if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) - { - float fMaximumBeat = GetMaximumBeatForNewNote(); - if( NoteRowToBeat(m_iStopPlayingAt) >= fMaximumBeat ) - { - // We're already at the end. - SCREENMAN->PlayInvalidSound(); - return true; - } - } - - // Pick up where we left off. - { - int iSize = m_iStopPlayingAt - m_iStartPlayingAt; - m_iStartPlayingAt = m_iStopPlayingAt; - m_iStopPlayingAt += iSize; - - if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) - m_iStopPlayingAt = min( m_iStopPlayingAt, BeatToNoteRow(GetMaximumBeatForNewNote()) ); - } - - TransitionEditState( STATE_RECORDING ); - return true; - - case EDIT_BUTTON_RETURN_TO_EDIT: - TransitionEditState( STATE_EDITING ); - return true; - default: - return false; - } -} - -bool ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB ) -{ - switch( input.type ) - { - case IET_RELEASE: - case IET_FIRST_PRESS: - break; - default: - return false; - } - - GameButtonType gbt = GAMESTATE->m_pCurGame->GetPerButtonInfo(input.GameI.button)->m_gbt; - - if( GamePreferences::m_AutoPlay == PC_HUMAN && GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay == 0 ) - { - const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI ); - bool bRelease = input.type == IET_RELEASE; - switch( input.pn ) - { - case PLAYER_2: - // ignore player 2 input unless this mode requires it - if( GAMESTATE->GetCurrentStyle()->m_StyleType != StyleType_TwoPlayersSharedSides ) - break; - - // fall through to input handling logic: - case PLAYER_1: - { - switch( gbt ) - { - case GameButtonType_Step: - if( iCol != -1 ) - m_Player->Step( iCol, -1, input.DeviceI.ts, false, bRelease ); - return true; - case GameButtonType_Fret: - if( iCol != -1 ) - m_Player->Fret( iCol, -1, input.DeviceI.ts, false, bRelease ); - return true; - case GameButtonType_Strum: - m_Player->Strum( iCol, -1, input.DeviceI.ts, false, bRelease ); - return true; - default: - break; - } - } - default: break; - } - } - - if( gbt == GameButtonType_INVALID && input.type == IET_FIRST_PRESS ) - { - switch( EditB ) - { - case EDIT_BUTTON_RETURN_TO_EDIT: - /* When exiting play mode manually, leave the cursor where it is. */ - m_fBeatToReturnTo = GetAppropriatePosition().m_fSongBeat; - TransitionEditState( STATE_EDITING ); - return true; - case EDIT_BUTTON_OFFSET_UP: - case EDIT_BUTTON_OFFSET_DOWN: - { - float fOffsetDelta; - switch( EditB ) - { - DEFAULT_FAIL( EditB ); - case EDIT_BUTTON_OFFSET_DOWN: fOffsetDelta = -0.020f; break; - case EDIT_BUTTON_OFFSET_UP: fOffsetDelta = +0.020f; break; - } - - if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) - { - fOffsetDelta /= 20; /* 1ms */ - } - else if( input.type == IET_REPEAT ) - { - if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) - fOffsetDelta *= 10; - else - fOffsetDelta *= 40; - } - - GetAppropriateTimingForUpdate().m_fBeat0OffsetInSeconds += fOffsetDelta; - if (!GAMESTATE->m_bIsUsingStepTiming) - { - GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += fOffsetDelta; - } - } - return true; - default: break; - } - } - return false; -} - -void ScreenEdit::TransitionEditState( EditState em ) -{ - EditState old = m_EditState; - - // If we're going from recording to paused, come back when we're done. - if( old == STATE_RECORDING_PAUSED && em == STATE_PLAYING ) - m_bReturnToRecordMenuAfterPlay = true; - - const bool bStateChanging = em != old; - -#if 0 - // If switching out of record, open the menu. - { - bool bGoToRecordMenu = (old == STATE_RECORDING); - if( m_bReturnToRecordMenuAfterPlay && old == STATE_PLAYING ) - { - bGoToRecordMenu = true; - m_bReturnToRecordMenuAfterPlay = false; - } - - if( bGoToRecordMenu ) - em = STATE_RECORDING_PAUSED; - } -#endif - - // If we're playing music or assist ticks when changing modes, stop. - SOUND->StopMusic(); - if( m_pSoundMusic ) - m_pSoundMusic->StopPlaying(); - m_GameplayAssist.StopPlaying(); - GAMESTATE->m_bGameplayLeadIn.Set( true ); - - if( bStateChanging ) - { - switch( old ) - { - case STATE_EDITING: - // If exiting EDIT mode, save the cursor position. - m_fBeatToReturnTo = GetAppropriatePosition().m_fSongBeat; - break; - - case STATE_PLAYING: - AdjustSync::HandleSongEnd(); - if (!GAMESTATE->m_bIsUsingStepTiming) - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = backupStepTiming; - if( AdjustSync::IsSyncDataChanged() ) - ScreenSaveSync::PromptSaveSync(); - break; - - case STATE_RECORDING: - SetDirty( true ); - if (!GAMESTATE->m_bIsUsingStepTiming) - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = backupStepTiming; - SaveUndo(); - - // delete old TapNotes in the range - m_NoteDataEdit.ClearRange( m_iStartPlayingAt, m_iStopPlayingAt ); - m_NoteDataEdit.CopyRange( m_NoteDataRecord, m_iStartPlayingAt, m_iStopPlayingAt, m_iStartPlayingAt ); - m_NoteDataRecord.ClearAll(); - - CheckNumberOfNotesAndUndo(); - break; - default: break; - } - } - - // Set up player options for this mode. (EDITING uses m_PlayerStateEdit, - // which we don't need to change.) - if( em != STATE_EDITING ) - { - // Stop displaying course attacks, if any. - GAMESTATE->m_pPlayerState[PLAYER_1]->RemoveActiveAttacks(); - // Load the player's default PlayerOptions. - GAMESTATE->m_pPlayerState[PLAYER_1]->RebuildPlayerOptionsFromActiveAttacks(); - - // Snap to current options. - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.SetCurrentToLevel( ModsLevel_Stage ); - } - - switch( em ) - { - DEFAULT_FAIL( em ); - case STATE_EDITING: - // Important: people will stop playing, change the BG and start again; make sure we reload - m_Background.Unload(); - m_Foreground.Unload(); - - // Restore the cursor position + Quantize + Clamp - SetBeat( max( 0, Quantize( m_fBeatToReturnTo, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ) ) ); - GAMESTATE->m_bInStepEditor = true; - break; - - case STATE_PLAYING: - case STATE_RECORDING: - { - if( bStateChanging ) - AdjustSync::ResetOriginalSyncData(); - - /* Give a 1 second lead-in. If we're loading Player, this must be done first. - * Also be sure to get the right timing. */ - float fSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1; - GAMESTATE->UpdateSongPosition( fSeconds, GetAppropriateTiming(), RageZeroTimer, true ); - - GAMESTATE->m_bGameplayLeadIn.Set( false ); - - if (!GAMESTATE->m_bIsUsingStepTiming) - { - // Substitute the song timing for the step timing during - // previuw if we're in song mode - backupStepTiming = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing; - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing.Clear(); - } - - /* Reset the note skin, in case preferences have changed. */ - // XXX - // GAMESTATE->ResetNoteSkins(); - //GAMESTATE->res - GAMESTATE->m_bInStepEditor = false; - break; - } - case STATE_RECORDING_PAUSED: - GAMESTATE->m_bInStepEditor = false; - break; - } - - switch( em ) - { - case STATE_PLAYING: - // If we're in course display mode, set that up. - SetupCourseAttacks(); - - m_Player.Load( m_NoteDataEdit ); - - if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay != 0 ) - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_AUTOPLAY; - else - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = GamePreferences::m_AutoPlay; - - if( g_bEditorShowBGChangesPlay ) - { - /* FirstBeat affects backgrounds, so commit changes to memory (not to disk) - * and recalc it. */ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - ASSERT( pSteps != NULL ); - pSteps->SetNoteData( m_NoteDataEdit ); - m_pSong->ReCalculateRadarValuesAndLastSecond(); - - m_Background.Unload(); - m_Background.LoadFromSong( m_pSong ); - - m_Foreground.Unload(); - m_Foreground.LoadFromSong( m_pSong ); - } - - break; - case STATE_RECORDING: - case STATE_RECORDING_PAUSED: - // initialize m_NoteFieldRecord - m_NoteDataRecord.CopyAll( m_NoteDataEdit ); - - // highlight the section being recorded - m_NoteFieldRecord.m_iBeginMarker = m_iStartPlayingAt; - m_NoteFieldRecord.m_iEndMarker = m_iStopPlayingAt; - - break; - default: break; - } - - // Show/hide depending on edit state (em) - m_sprOverlay->PlayCommand( EditStateToString(em) ); - m_sprUnderlay->PlayCommand( EditStateToString(em) ); - - m_Background.SetVisible( g_bEditorShowBGChangesPlay && em != STATE_EDITING ); - m_textInputTips.SetVisible( em == STATE_EDITING ); - m_textInfo.SetVisible( em == STATE_EDITING ); - // Play the OnCommands again so that these will be re-hidden if the OnCommand hides them. - if( em == STATE_EDITING ) - { - m_textInputTips.PlayCommand( "On" ); - m_textInfo.PlayCommand( "On" ); - } - m_textPlayRecordHelp.SetVisible( em != STATE_EDITING ); - m_SnapDisplay.SetVisible( em == STATE_EDITING ); - m_NoteFieldEdit.SetVisible( em == STATE_EDITING ); - m_NoteFieldRecord.SetVisible( em == STATE_RECORDING || em == STATE_RECORDING_PAUSED ); - m_Player->SetVisible( em == STATE_PLAYING ); - m_Foreground.SetVisible( g_bEditorShowBGChangesPlay && em != STATE_EDITING ); - - switch( em ) - { - case STATE_PLAYING: - case STATE_RECORDING: - { - const float fStartSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( GetBeat() ); - LOG->Trace( "Starting playback at %f", fStartSeconds ); - - RageSoundParams p; - p.m_fSpeed = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; - p.m_StartSecond = fStartSeconds; - p.StopMode = RageSoundParams::M_CONTINUE; - m_pSoundMusic->SetProperty( "AccurateSync", true ); - m_pSoundMusic->Play( &p ); - break; - } - default: break; - } - - m_EditState = em; -} - -void ScreenEdit::ScrollTo( float fDestinationBeat ) -{ - CLAMP( fDestinationBeat, 0, GetMaximumBeatForMoving() ); - - // Don't play the sound and do the hold note logic below if our position didn't change. - const float fOriginalBeat = GetAppropriatePosition().m_fSongBeat; - if( fOriginalBeat == fDestinationBeat ) - return; - - SetBeat(fDestinationBeat); - - // check to see if they're holding a button - for( int n=0; n= m_NoteDataEdit.GetNumTracks() ) - continue; // skip - - EditButton b = EditButton(EDIT_BUTTON_COLUMN_0+n); - if( !EditIsBeingPressed(b) ) - continue; - - // create a new hold note - int iStartRow = BeatToNoteRow( min(fOriginalBeat, fDestinationBeat) ); - int iEndRow = BeatToNoteRow( max(fOriginalBeat, fDestinationBeat) ); - - // Don't SaveUndo. We want to undo the whole hold, not just the last segment - // that the user made. Dragging the hold bigger can only absorb and remove - // other taps, so dragging won't cause us to exceed the note limit. - TapNote tn = EditIsBeingPressed(EDIT_BUTTON_LAY_ROLL) ? TAP_ORIGINAL_ROLL_HEAD : TAP_ORIGINAL_HOLD_HEAD; - - tn.pn = m_InputPlayerNumber; - m_NoteDataEdit.AddHoldNote( iCol, iStartRow, iEndRow, tn ); - } - - if( EditIsBeingPressed(EDIT_BUTTON_SCROLL_SELECT) ) - { - /* Shift is being held. - * If this is the first time we've moved since shift was depressed, - * the old position (before this move) becomes the start pos: */ - int iDestinationRow = BeatToNoteRow( fDestinationBeat ); - if( m_iShiftAnchor == -1 ) - m_iShiftAnchor = BeatToNoteRow(fOriginalBeat); - - if( iDestinationRow == m_iShiftAnchor ) - { - // We're back at the anchor, so we have nothing selected. - m_NoteFieldEdit.m_iBeginMarker = m_NoteFieldEdit.m_iEndMarker = -1; - } - else - { - m_NoteFieldEdit.m_iBeginMarker = m_iShiftAnchor; - m_NoteFieldEdit.m_iEndMarker = iDestinationRow; - if( m_NoteFieldEdit.m_iBeginMarker > m_NoteFieldEdit.m_iEndMarker ) - swap( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - } - - m_soundChangeLine.Play(); -} - -static LocalizedString NEW_KEYSOUND_FILE("ScreenEdit", "Enter New Keysound File"); - -void ScreenEdit::HandleMessage( const Message &msg ) -{ - if( msg == "Judgment" ) - { - PlayerNumber pn; - msg.GetParam( "Player", pn ); - - if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_bMuteOnError ) - { - RageSoundReader *pSoundReader = m_AutoKeysounds.GetPlayerSound( pn ); - if( pSoundReader == NULL ) - pSoundReader = m_AutoKeysounds.GetSharedSound(); - - HoldNoteScore hns; - msg.GetParam( "HoldNoteScore", hns ); - TapNoteScore tns; - msg.GetParam( "TapNoteScore", tns ); - - bool bOn = false; - if( hns != HoldNoteScore_Invalid ) - bOn = hns != HNS_LetGo; - else - bOn = tns != TNS_Miss; - - if( pSoundReader ) - pSoundReader->SetProperty( "Volume", bOn? 1.0f:0.0f ); - } - } - if( msg == Message_SongModified ) - { - SetDirty( true ); - } - Screen::HandleMessage( 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 ) - m_bTextInfoNeedsUpdate = true; - - if( SM == SM_UpdateTextInfo ) - { - UpdateTextInfo(); - this->PostScreenMessage( SM_UpdateTextInfo, 0.1f ); - } - else if( SM == SM_GoToNextScreen ) - { - GAMESTATE->m_EditMode = EditMode_Invalid; - } - else if( SM == SM_BackFromMainMenu ) - { - HandleMainMenuChoice( (MainMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - } - else if( SM == SM_BackFromAreaMenu ) - { - AreaMenuChoice amc = static_cast(ScreenMiniMenu::s_iLastRowCode); - const vector &answers = ScreenMiniMenu::s_viLastAnswers; - HandleAreaMenuChoice( amc, answers ); - } - else if( SM == SM_BackFromAlterMenu ) - { - HandleAlterMenuChoice( (AlterMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - } - else if( SM == SM_BackFromStepsInformation ) - { - HandleStepsInformationChoice( (StepsInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - } - else if (SM == SM_BackFromStepsData) - { - HandleStepsDataChoice((StepsDataChoice)ScreenMiniMenu::s_iLastRowCode, - ScreenMiniMenu::s_viLastAnswers); - } - - else if( SM == SM_BackFromSongInformation ) - { - HandleSongInformationChoice( (SongInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - } - else if( SM == SM_BackFromTimingDataInformation ) - { - HandleTimingDataInformationChoice( (TimingDataInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - } - else if( SM == SM_BackFromDifficultyMeterChange ) - { - int i = StringToInt( ScreenTextEntry::s_sLastAnswer ); - GAMESTATE->m_pCurSteps[PLAYER_1]->SetMeter(i); - SetDirty( true ); - } - else if( SM == SM_BackFromBeat0Change && !ScreenTextEntry::s_bCancelledLast ) - { - float fBeat0 = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - - TimingData &timing = GetAppropriateTimingForUpdate(); - float old = timing.m_fBeat0OffsetInSeconds; - timing.m_fBeat0OffsetInSeconds = fBeat0; - float delta = timing.m_fBeat0OffsetInSeconds - old; - - if (GAMESTATE->m_bIsUsingStepTiming) - { - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(delta); - } - else - { - GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(delta); - GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += delta; - } - - SetDirty( true ); - } - else if( SM == SM_BackFromBPMChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fBPM = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - - if( fBPM > 0 ) - GetAppropriateTimingForUpdate().AddSegment( BPMSegment(GetRow(), fBPM) ); - - SetDirty( true ); - } - else if( SM == SM_BackFromStopChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fStop = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - - if( fStop >= 0 ) - GetAppropriateTimingForUpdate().AddSegment( StopSegment(GetRow(), fStop) ); - - SetDirty( true ); - } - else if( SM == SM_BackFromDelayChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fDelay = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - - if( fDelay >= 0 ) - GetAppropriateTimingForUpdate().AddSegment( DelaySegment(GetRow(), fDelay) ); - - SetDirty( true ); - } - else if( SM == SM_BackFromTimeSignatureChange && !ScreenTextEntry::s_bCancelledLast ) - { - int iNum, iDen; - - if( sscanf( ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iNum, &iDen ) == 2 ) - GetAppropriateTimingForUpdate().AddSegment( TimeSignatureSegment(GetRow(), iNum, iDen) ); - - SetDirty( true ); - } - else if ( SM == SM_BackFromTickcountChange && !ScreenTextEntry::s_bCancelledLast ) - { - int iTick = StringToInt( ScreenTextEntry::s_sLastAnswer ); - - if ( iTick >= 0 && iTick <= ROWS_PER_BEAT ) - GetAppropriateTimingForUpdate().AddSegment( TickcountSegment( GetRow(), iTick) ); - - SetDirty( true ); - } - else if ( SM == SM_BackFromComboChange && !ScreenTextEntry::s_bCancelledLast ) - { - int iCombo, iMiss; - - if (sscanf(ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iCombo, &iMiss) == 2) - GetAppropriateTimingForUpdate().AddSegment( ComboSegment(GetRow(), iCombo, iMiss) ); - - SetDirty( true ); - } - else if ( SM == SM_BackFromLabelChange && !ScreenTextEntry::s_bCancelledLast ) - { - RString sLabel = ScreenTextEntry::s_sLastAnswer; - - if ( !GetAppropriateTiming().DoesLabelExist(sLabel) ) - { - // XXX: these should be in the NotesWriters where they're needed. - sLabel.Replace("=", "_"); - sLabel.Replace(",", "_"); - GetAppropriateTimingForUpdate().AddSegment( LabelSegment(GetRow(), sLabel) ); - SetDirty( true ); - } - } - else if ( SM == SM_BackFromWarpChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fWarp = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - if( fWarp >= 0 ) // allow 0 to kill a warp. - { - GetAppropriateTimingForUpdate().SetWarpAtBeat( GetBeat(), fWarp ); - SetDirty( true ); - } - } - else if( SM == SM_BackFromSpeedPercentChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fNum = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - GetAppropriateTimingForUpdate().SetSpeedPercentAtBeat( GetBeat(), fNum ); - SetDirty( true ); - } - else if ( SM == SM_BackFromSpeedWaitChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fDen = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - if( fDen >= 0) - { - GetAppropriateTimingForUpdate().SetSpeedWaitAtBeat( GetBeat(), fDen ); - } - SetDirty( true ); - } - else if ( SM == SM_BackFromSpeedModeChange && !ScreenTextEntry::s_bCancelledLast ) - { - if( ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "b" || ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "B" ) - { - GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), SpeedSegment::UNIT_BEATS ); - } - else if( ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "s" || ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "S" ) - { - GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), SpeedSegment::UNIT_SECONDS ); - } - else - { - int tmp = StringToInt(ScreenTextEntry::s_sLastAnswer ); - - SpeedSegment::BaseUnit unit = (tmp == 0 ) ? - SpeedSegment::UNIT_BEATS : SpeedSegment::UNIT_SECONDS; - - GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), unit ); - } - SetDirty( true ); - } - else if( SM == SM_BackFromScrollChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fNum = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - GetAppropriateTimingForUpdate().SetScrollAtBeat( GetBeat(), fNum ); - SetDirty( true ); - } - else if ( SM == SM_BackFromFakeChange && !ScreenTextEntry::s_bCancelledLast ) - { - float fFake = StringToFloat( ScreenTextEntry::s_sLastAnswer ); - if( fFake >= 0 ) // allow 0 to kill a fake. - { - GetAppropriateTimingForUpdate().AddSegment( FakeSegment(GetRow(), fFake) ); - SetDirty( true ); - } - } - - else if( SM == SM_BackFromBGChange ) - { - HandleBGChangeChoice( (BGChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); - SetDirty( true ); - } - else if( SM == SM_BackFromCourseModeMenu ) - { - const int num = ScreenMiniMenu::s_viLastAnswers[0]; - GAMESTATE->m_pCurCourse.Set( NULL ); - if( num != 0 ) - { - const RString name = g_CourseMode.rows[0].choices[num]; - Course *pCourse = SONGMAN->FindCourse( name ); - - int iCourseEntryIndex = -1; - FOREACH_CONST( CourseEntry, pCourse->m_vEntries, i ) - { - if( i->songID.ToSong() == GAMESTATE->m_pCurSong.Get() ) - iCourseEntryIndex = i - pCourse->m_vEntries.begin(); - } - - ASSERT( iCourseEntryIndex != -1 ); - - GAMESTATE->m_pCurCourse.Set( pCourse ); - GAMESTATE->m_iEditCourseEntryIndex.Set( iCourseEntryIndex ); - ASSERT( GAMESTATE->m_pCurCourse != NULL ); - } - } - else if (SM == SM_BackFromKeysoundTrack) - { - const int track = ScreenMiniMenu::s_iLastRowCode; - const int tracks = m_NoteDataEdit.GetNumTracks(); - const int row = this->GetRow(); - unsigned int sound = ScreenMiniMenu::s_viLastAnswers[track]; - vector &kses = m_pSong->m_vsKeysoundFile; - - if (track < tracks) - { - if (sound == kses.size()) - { - // create a new sound (filename), point it. - // if it's empty, make it an auto keysound. - ScreenTextEntry::TextEntry(SM_BackFromNewKeysound, NEW_KEYSOUND_FILE, "", 64); - return; - } - const TapNote &oldNote = m_NoteDataEdit.GetTapNote(track, row); - TapNote newNote = oldNote; // need to lose the const. not feeling like casting. - if (sound < kses.size()) - { - // set note at this row to use this keysound file. - // if it's empty, make it an auto keysound. - newNote.iKeysoundIndex = sound; - if (newNote.type == TapNote::empty) - { - newNote.type = TapNote::autoKeysound; // keysounds need something non empty. - } - } - else // sound > kses.size() - { - // remove the sound. if it's an auto keysound, make it empty. - newNote.iKeysoundIndex = -1; - if (newNote.type == TapNote::autoKeysound) - { - newNote.type = TapNote::empty; // autoKeysound with no sound is pointless. - } - } - m_NoteDataEdit.SetTapNote(track, row, newNote); - } - else if (track == tracks) - { - kses.erase(kses.begin() + sound); - // TODO: Make the following a part of NoteData? - for (int t = 0; t < tracks; ++t) - { - FOREACH_NONEMPTY_ROW_IN_TRACK(m_NoteDataEdit, t, r) - { - const TapNote &oldNote = m_NoteDataEdit.GetTapNote(t, r); - TapNote newNote = oldNote; // need to lose the const. not feeling like casting. - if (newNote.iKeysoundIndex == static_cast(sound)) - { - newNote.iKeysoundIndex = -1; - if (newNote.type == TapNote::autoKeysound) - newNote.type = TapNote::empty; - } - else if (newNote.iKeysoundIndex > static_cast(sound)) - newNote.iKeysoundIndex--; - - m_NoteDataEdit.SetTapNote(t, r, newNote); - } - } - } - SetDirty(true); - } - else if (SM == SM_BackFromNewKeysound && !ScreenTextEntry::s_bCancelledLast) - { - RString answer = ScreenTextEntry::s_sLastAnswer; - const int track = ScreenMiniMenu::s_iLastRowCode; // still keeps the same value. - const int row = this->GetRow(); - const TapNote &oldNote = m_NoteDataEdit.GetTapNote(track, row); - TapNote newNote = oldNote; // need to lose the const. not feeling like casting. - vector &kses = m_pSong->m_vsKeysoundFile; - unsigned pos = find(kses.begin(), kses.end(), answer) - kses.begin(); - if (pos == kses.size()) - { - newNote.iKeysoundIndex = kses.size(); - kses.push_back(answer); - } - else - { - newNote.iKeysoundIndex = pos; - } - if (newNote.type == TapNote::empty) - newNote.type = TapNote::autoKeysound; // keysounds need something non empty. - m_NoteDataEdit.SetTapNote(track, row, newNote); - SetDirty(true); - } - else if( SM == SM_BackFromOptions ) - { - // The options may have changed the note skin. - m_NoteFieldRecord.CacheAllUsedNoteSkins(); - m_Player->CacheAllUsedNoteSkins(); - - // stop any music that screen may have been playing - SOUND->StopMusic(); - } - else if( SM == SM_BackFromInsertTapAttack ) - { - int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0]; - g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertTapAttack.rows[0].choices[iDurationChoice] ); - - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertTapAttackPlayerOptions ); - } - else if( SM == SM_BackFromInsertTapAttackPlayerOptions ) - { - PlayerOptions poChosen = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetPreferred(); - RString sMods = poChosen.GetString(); - const int row = BeatToNoteRow( GAMESTATE->m_Position.m_fSongBeat ); - - TapNote tn( - TapNote::attack, - TapNote::SubType_Invalid, - TapNote::original, - sMods, - g_fLastInsertAttackDurationSeconds, - -1 ); - tn.pn = m_InputPlayerNumber; - SetDirty( true ); - SaveUndo(); - 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, - FloatToString(attack.fStartSecond), - 10); - } - else if (option == 1) // adjusting the length of the attack - { - ScreenTextEntry::TextEntry(SM_BackFromEditingAttackLength, - EDIT_ATTACK_LENGTH, - FloatToString(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]; - const 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(FloatToString(attack.fStartSecond)); - g_IndividualAttack.rows.push_back(MenuRowDef(1, - "Secs Remaining", - true, - EditMode_CourseMods, - true, - true, - 0, - NULL)); - g_IndividualAttack.rows[1].SetOneUnthemedChoice(FloatToString(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]; - const TimingData &timing = GetAppropriateTiming(); - g_fLastInsertAttackPositionSeconds = timing.GetElapsedTimeFromBeat( GetBeat() ); - g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertStepAttack.rows[0].choices[iDurationChoice] ); - AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks; - int iAttack = FindAttackAtTime(attacks, g_fLastInsertAttackPositionSeconds); - - if (ScreenMiniMenu::s_iLastRowCode == ScreenEdit::remove ) - { - if (iAttack > 0) - attacks.erase(attacks.begin() + iAttack); - } - else - { - ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; - this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); - PlayerOptions po; - if (iAttack >= 0) - po.FromString(attacks[iAttack].sModifiers); - - toEdit.Assign( ModsLevel_Preferred, po ); - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); - } - SetDirty(true); - } - else if( SM == SM_BackFromInsertCourseAttack ) - { - int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0]; - Course *pCourse = GAMESTATE->m_pCurCourse; - CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; - int iAttack; - - // TODO: Handle Song/Step Timing functions/switches here? - - g_fLastInsertAttackPositionSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( GAMESTATE->m_Position.m_fSongBeat ); - g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertCourseAttack.rows[0].choices[iDurationChoice] ); - iAttack = FindAttackAtTime( ce.attacks, g_fLastInsertAttackPositionSeconds ); - - if( ScreenMiniMenu::s_iLastRowCode == ScreenEdit::remove ) - { - ASSERT( iAttack >= 0 ); - ce.attacks.erase( ce.attacks.begin() + iAttack ); - } - else - { - PlayerOptions po; - - if( iAttack >= 0 ) - po.FromString( ce.attacks[iAttack].sModifiers ); - - GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.Assign( ModsLevel_Preferred, po ); - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertCourseAttackPlayerOptions ); - } - } - else if (SM == SM_BackFromInsertStepAttackPlayerOptions) - { - ModsGroup &toRestore = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; - PlayerOptions poChosen = toRestore.GetPreferred(); - RString mods = poChosen.GetString(); - - if (g_fLastInsertAttackPositionSeconds >= 0) - { - Attack a(ATTACK_LEVEL_1, - g_fLastInsertAttackPositionSeconds, - g_fLastInsertAttackDurationSeconds, - mods, - false, - false); - AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks; - int index = FindAttackAtTime(attacks, g_fLastInsertAttackPositionSeconds); - if (index >= 0) - attacks[index] = a; - else - attacks.push_back(a); - } - toRestore.Assign(ModsLevel_Preferred, this->originalPlayerOptions.GetPreferred()); - } - else if( SM == SM_BackFromInsertCourseAttackPlayerOptions ) - { - PlayerOptions poChosen = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetPreferred(); - RString sMods = poChosen.GetString(); - - Course *pCourse = GAMESTATE->m_pCurCourse; - CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; - if( g_fLastInsertAttackPositionSeconds < 0 ) - { - ce.sModifiers = sMods; - } - else - { - Attack a( - ATTACK_LEVEL_1, - g_fLastInsertAttackPositionSeconds, - g_fLastInsertAttackDurationSeconds, - sMods, - false, - false ); - int iAttack = FindAttackAtTime( ce.attacks, g_fLastInsertAttackPositionSeconds ); - - if( iAttack >= 0 ) - ce.attacks[iAttack] = a; - else - ce.attacks.push_back( a ); - } - } - else if( SM == SM_DoRevertToLastSave ) - { - if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) - { - SaveUndo(); - CopyFromLastSave(); - m_pSteps->GetNoteData( m_NoteDataEdit ); - SetDirty( false ); - } - } - else if( SM == SM_DoRevertFromDisk ) - { - if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) - { - SaveUndo(); - RevertFromDisk(); - m_pSteps->GetNoteData( m_NoteDataEdit ); - SetDirty( false ); - } - } - else if( SM == SM_DoEraseStepTiming ) - { - if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) - { - SaveUndo(); - m_pSteps->m_Timing.Clear(); - SetDirty( true ); - } - } - else if( SM == SM_DoSaveAndExit ) // just asked "save before exiting? yes, no, cancel" - { - switch( ScreenPrompt::s_LastAnswer ) - { - DEFAULT_FAIL( ScreenPrompt::s_LastAnswer ); - case ANSWER_YES: - // This will send SM_SaveSuccessful or SM_SaveFailed. - HandleMainMenuChoice( ScreenEdit::save_on_exit ); - return; - case ANSWER_NO: - // Don't save; just exit. - SCREENMAN->SendMessageToTopScreen( SM_DoExit ); - return; - case ANSWER_CANCEL: - break; // do nothing - } - } - else if( SM == SM_SaveSuccessful ) - { - LOG->Trace( "Save successful." ); - CopyToLastSave(); - SetDirty( false ); - SONGMAN->Invalidate( GAMESTATE->m_pCurSong ); - - if( m_CurrentAction == save_on_exit ) - ScreenPrompt::Prompt( SM_DoExit, SAVE_SUCCESSFUL ); - else - SCREENMAN->SystemMessage( SAVE_SUCCESSFUL ); - } - else if( SM == SM_SaveFailed ) // save failed; stay in the editor - { - /* We committed the steps to SongManager. Revert to the last save, and - * recommit the reversion to SongManager. */ - LOG->Trace( "Save failed. Changes uncommitted from memory." ); - CopyFromLastSave(); - m_pSteps->SetNoteData( m_NoteDataEdit ); - } - else if( SM == SM_DoExit ) - { - // IMPORTANT: CopyFromLastSave before deleting the Steps below - CopyFromLastSave(); - - /* The user has been given a choice to save. - * Delete all unsaved steps before exiting the editor. */ - /* FIXME: This code causes all the steps to be deleted if you quit - * without saving. However, without this code, any new steps will get - * saved on quit. -aj */ - - // At this point, the last good song copy is in use. - Song *pSong = GAMESTATE->m_pCurSong; - const vector &apSteps = pSong->GetAllSteps(); - vector apToDelete; - FOREACH_CONST( Steps *, apSteps, s ) - { - // If we're not on the same style, let it go. - if( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType != (*s)->m_StepsType ) - continue; - // If autogenned, it isn't being saved. - if( (*s)->IsAutogen() ) - continue; - // If the notedata has content, let it go. - if( !(*s)->GetNoteData().IsEmpty() ) - continue; - // It's hard to say if these steps were saved to disk or not. - /* - if( !(*s)->GetSavedToDisk() ) - continue; - */ - apToDelete.push_back( *s ); - } - FOREACH_CONST( Steps *, apToDelete, s ) - { - Steps *pSteps = *s; - pSong->DeleteSteps( pSteps ); - if( m_pSteps == pSteps ) - m_pSteps = NULL; - if( GAMESTATE->m_pCurSteps[PLAYER_1].Get() == pSteps ) - GAMESTATE->m_pCurSteps[PLAYER_1].Set( NULL ); - } - - - m_Out.StartTransitioning( SM_GoToNextScreen ); - } - else if( SM == SM_GainFocus ) - { - /* When another screen comes up, RageSounds takes over the sound timer. When we come - * back, put the timer back to where it was. */ - SetBeat(m_fTrailingBeat); - } - else if( SM == SM_LoseFocus ) - { - // Snap the trailing beat, in case we lose focus while tweening. - m_fTrailingBeat = GetBeat(); //GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; - } - - ScreenWithMenuElements::HandleScreenMessage( SM ); -} - -void ScreenEdit::OnSnapModeChange() -{ - m_soundChangeSnap.Play(); - - NoteType nt = m_SnapDisplay.GetNoteType(); - int iStepIndex = BeatToNoteRow( GetBeat() ); - int iElementsPerNoteType = BeatToNoteRow( NoteTypeToBeat(nt) ); - int iStepIndexHangover = iStepIndex % iElementsPerNoteType; - SetBeat( GetBeat() - NoteRowToBeat( iStepIndexHangover ) ); -} - - - -// Helper function for below - -// Begin helper functions for InputEdit - - -static void ChangeDescription( const RString &sNew ) -{ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - - // Don't erase edit descriptions. - if( sNew.empty() && pSteps->GetDifficulty() == Difficulty_Edit ) - return; - - pSteps->SetDescription(sNew); -} - -static void ChangeChartName( const RString &sNew ) -{ - Steps *pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - pSteps->SetChartName(sNew); -} - -static void ChangeChartStyle( const RString &sNew ) -{ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - pSteps->SetChartStyle(sNew); -} - -static void ChangeStepCredit( const RString &sNew ) -{ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - pSteps->SetCredit(sNew); -} - -static void ChangeStepMeter( const RString &sNew ) -{ - int diff = StringToInt(sNew); - GAMESTATE->m_pCurSteps[PLAYER_1]->SetMeter(max(diff, 1)); -} - -static void ChangeMainTitle( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sMainTitle = sNew; -} - -static void ChangeSubTitle( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sSubTitle = sNew; -} - -static void ChangeArtist( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sArtist = sNew; -} - -static void ChangeGenre( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sGenre = sNew; -} - -static void ChangeCredit( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sCredit = sNew; -} - -static void ChangeMainTitleTranslit( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sMainTitleTranslit = sNew; -} - -static void ChangeSubTitleTranslit( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sSubTitleTranslit = sNew; -} - -static void ChangeArtistTranslit( const RString &sNew ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_sArtistTranslit = sNew; -} - -static void ChangeLastSecondHint( const RString &sNew ) -{ - Song &s = *GAMESTATE->m_pCurSong; - s.SetSpecifiedLastSecond(StringToFloat(sNew)); -} - -static void ChangePreviewStart( const RString &sNew ) -{ - GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds = StringToFloat( sNew ); -} - -static void ChangePreviewLength( const RString &sNew ) -{ - GAMESTATE->m_pCurSong->m_fMusicSampleLengthSeconds = StringToFloat( sNew ); -} - -static void ChangeMinBPM( const RString &sNew ) -{ - GAMESTATE->m_pCurSong->m_fSpecifiedBPMMin = StringToFloat( sNew ); -} - -static void ChangeStepsMinBPM(const RString &sNew) -{ - Steps *step = GAMESTATE->m_pCurSteps[PLAYER_1]; - step->SetMinBPM(StringToFloat(sNew)); -} - -static void ChangeMaxBPM( const RString &sNew ) -{ - GAMESTATE->m_pCurSong->m_fSpecifiedBPMMax = StringToFloat( sNew ); -} - -static void ChangeStepsMaxBPM(const RString &sNew) -{ - Steps *step = GAMESTATE->m_pCurSteps[PLAYER_1]; - step->SetMaxBPM(StringToFloat(sNew)); -} - -const TimingData & ScreenEdit::GetAppropriateTiming() const -{ - if( GAMESTATE->m_bIsUsingStepTiming ) - { - return *m_pSteps->GetTimingData(); - } - return m_pSong->m_SongTiming; -} - -TimingData & ScreenEdit::GetAppropriateTimingForUpdate() -{ - if( GAMESTATE->m_bIsUsingStepTiming ) - { - // Copy from song if there is no step timing - if( m_pSteps->m_Timing.empty() ) - m_pSteps->m_Timing = m_pSong->m_SongTiming; - return m_pSteps->m_Timing; - } - return m_pSong->m_SongTiming; -} - -SongPosition & ScreenEdit::GetAppropriatePosition() const -{ - if (GAMESTATE->m_bIsUsingStepTiming) - { - return GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position; - } - return GAMESTATE->m_Position; -} - -inline void ScreenEdit::SetBeat(float fBeat) -{ - if( !GAMESTATE->m_bIsUsingStepTiming ) - { - GAMESTATE->m_Position.m_fSongBeat = fBeat; - GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat = m_pSteps->GetTimingData()->GetBeatFromElapsedTime(m_pSong->m_SongTiming.GetElapsedTimeFromBeat(fBeat)); - } - else - { - GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat = fBeat; - GAMESTATE->m_Position.m_fSongBeat = m_pSong->m_SongTiming.GetBeatFromElapsedTime(m_pSteps->GetTimingData()->GetElapsedTimeFromBeat(fBeat)); - } -} - -inline float ScreenEdit::GetBeat() -{ - if( !GAMESTATE->m_bIsUsingStepTiming ) - { - return GAMESTATE->m_Position.m_fSongBeat; - } - return GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; -} - -inline int ScreenEdit::GetRow() -{ - return BeatToNoteRow(GetBeat()); -} - -void ScreenEdit::DisplayTimingMenu() -{ - int row = GetRow(); - const TimingData &pTime = GetAppropriateTiming(); - bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtRow( row )->GetRow() == row; - // bool bIsSelecting = ( (m_NoteFieldEdit.m_iEndMarker != -1) && (m_NoteFieldEdit.m_iBeginMarker != -1) ); - - - g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( FloatToString(pTime.m_fBeat0OffsetInSeconds) ); - g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( FloatToString(pTime.GetBPMAtRow( row ) ) ); - g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( FloatToString(pTime.GetStopAtRow( row ) ) ) ; - g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( FloatToString(pTime.GetDelayAtRow( row ) ) ); - - g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice( - ssprintf( "%d / %d", - pTime.GetTimeSignatureSegmentAtRow(row)->GetNum(), - pTime.GetTimeSignatureSegmentAtRow(row)->GetDen() - ) - ); - - g_TimingDataInformation.rows[label].SetOneUnthemedChoice( pTime.GetLabelAtRow( row ).c_str() ); - g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtRow( row ) ) ); - g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d / %d", - pTime.GetComboAtRow( row ), - pTime.GetMissComboAtRow( row ) ) ); - g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( FloatToString(pTime.GetWarpAtRow( row ) ) ); - g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? FloatToString(pTime.GetSpeedPercentAtRow( row ) ) : "---" ); - g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? FloatToString(pTime.GetSpeedWaitAtRow( row ) ) : "---" ); - - RString starting = ( pTime.GetSpeedModeAtRow( row ) == 1 ? "Seconds" : "Beats" ); - g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); - - g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( FloatToString(pTime.GetScrollAtRow( row ) ) ); - g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( FloatToString(pTime.GetFakeAtRow( row ) ) ); - - // g_TimingDataInformation.rows[speed_percent].bEnabled = !bIsSelecting; - g_TimingDataInformation.rows[speed_wait].bEnabled = bHasSpeedOnThisRow; - g_TimingDataInformation.rows[speed_mode].bEnabled = bHasSpeedOnThisRow; - - EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation ); -} - -// End helper functions - -static LocalizedString REVERT_LAST_SAVE ( "ScreenEdit", "Do you want to revert to your last save?" ); -static LocalizedString DESTROY_ALL_UNSAVED_CHANGES ( "ScreenEdit", "This will destroy all unsaved changes." ); -static LocalizedString REVERT_FROM_DISK ( "ScreenEdit", "Do you want to revert from disk?" ); -static LocalizedString SAVE_CHANGES_BEFORE_EXITING ( "ScreenEdit", "Do you want to save changes before exiting?" ); - -void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAnswers ) -{ - GAMESTATE->SetProcessedTimingData(m_pSteps->GetTimingData()); - switch( c ) - { - DEFAULT_FAIL( c ); - case play_selection: - if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) - HandleAlterMenuChoice( play ); - else if( m_NoteFieldEdit.m_iBeginMarker!=-1 ) - HandleMainMenuChoice( play_selection_start_to_end ); - else - HandleMainMenuChoice( play_current_beat_to_end ); - break; - case play_whole_song: - { - m_iStartPlayingAt = 0; - m_iStopPlayingAt = m_NoteDataEdit.GetLastRow(); - TransitionEditState( STATE_PLAYING ); - } - break; - case play_selection_start_to_end: - { - m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; - m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); - TransitionEditState( STATE_PLAYING ); - } - break; - case play_current_beat_to_end: - { - m_iStartPlayingAt = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); - m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); - TransitionEditState( STATE_PLAYING ); - } - break; - case set_selection_start: - { - const int iCurrentRow = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); - if( m_NoteFieldEdit.m_iEndMarker!=-1 && iCurrentRow >= m_NoteFieldEdit.m_iEndMarker ) - { - SCREENMAN->PlayInvalidSound(); - } - else - { - m_NoteFieldEdit.m_iBeginMarker = iCurrentRow; - m_soundMarker.Play(); - } - } - break; - case set_selection_end: - { - const int iCurrentRow = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); - if( m_NoteFieldEdit.m_iBeginMarker!=-1 && iCurrentRow <= m_NoteFieldEdit.m_iBeginMarker ) - { - SCREENMAN->PlayInvalidSound(); - } - else - { - m_NoteFieldEdit.m_iEndMarker = iCurrentRow; - m_soundMarker.Play(); - } - } - break; - case edit_steps_information: - { - /* XXX: If the difficulty is changed from EDIT, and pSteps->WasLoadedFromProfile() - * is true, we should warn that the steps will no longer be saved to the profile. */ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - - g_StepsInformation.rows[difficulty].choices.clear(); - FOREACH_ENUM( Difficulty, dc ) - { - g_StepsInformation.rows[difficulty].choices.push_back( "|" + CustomDifficultyToLocalizedString( GetCustomDifficulty(pSteps->m_StepsType, dc, CourseType_Invalid) ) ); - } - g_StepsInformation.rows[difficulty].iDefaultChoice = pSteps->GetDifficulty(); - g_StepsInformation.rows[difficulty].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); - g_StepsInformation.rows[meter].SetOneUnthemedChoice( ssprintf("%d", pSteps->GetMeter()) ); - g_StepsInformation.rows[meter].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Home); - g_StepsInformation.rows[predict_meter].SetOneUnthemedChoice( ssprintf("%.2f",pSteps->PredictMeter()) ); - g_StepsInformation.rows[chartname].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); - g_StepsInformation.rows[chartname].SetOneUnthemedChoice(pSteps->GetChartName()); - g_StepsInformation.rows[description].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); - g_StepsInformation.rows[description].SetOneUnthemedChoice( pSteps->GetDescription() ); - g_StepsInformation.rows[chartstyle].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); - g_StepsInformation.rows[chartstyle].SetOneUnthemedChoice( pSteps->GetChartStyle() ); - g_StepsInformation.rows[step_credit].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); - g_StepsInformation.rows[step_credit].SetOneUnthemedChoice( pSteps->GetCredit() ); - g_StepsInformation.rows[step_display_bpm].iDefaultChoice = pSteps->GetDisplayBPM(); - g_StepsInformation.rows[step_min_bpm].SetOneUnthemedChoice( FloatToString(pSteps->GetMinBPM())); - g_StepsInformation.rows[step_max_bpm].SetOneUnthemedChoice( FloatToString(pSteps->GetMaxBPM())); - EditMiniMenu( &g_StepsInformation, SM_BackFromStepsInformation, SM_None ); - } - break; - case view_steps_data: - { - float fMusicSeconds = m_pSoundMusic->GetLengthSeconds(); - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - const StepsTypeCategory &cat = GAMEMAN->GetStepsTypeInfo(pSteps->m_StepsType).m_StepsTypeCategory; - if (cat == StepsTypeCategory_Couple || cat == StepsTypeCategory_Routine) - { - pair tmp = m_NoteDataEdit.GetNumTapNotesTwoPlayer(); - g_StepsData.rows[tap_notes].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumJumpsTwoPlayer(); - g_StepsData.rows[jumps].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumHandsTwoPlayer(); - g_StepsData.rows[hands].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumQuadsTwoPlayer(); - g_StepsData.rows[quads].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumHoldNotesTwoPlayer(); - g_StepsData.rows[holds].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumMinesTwoPlayer(); - g_StepsData.rows[mines].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumRollsTwoPlayer(); - g_StepsData.rows[rolls].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumLiftsTwoPlayer(); - g_StepsData.rows[lifts].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - tmp = m_NoteDataEdit.GetNumFakesTwoPlayer(); - g_StepsData.rows[fakes].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); - } - else - { - g_StepsData.rows[tap_notes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumTapNotes()) ); - g_StepsData.rows[jumps].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumJumps()) ); - g_StepsData.rows[hands].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHands()) ); - g_StepsData.rows[quads].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumQuads()) ); - g_StepsData.rows[holds].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHoldNotes()) ); - g_StepsData.rows[mines].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumMines()) ); - g_StepsData.rows[rolls].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumRolls()) ); - g_StepsData.rows[lifts].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumLifts()) ); - g_StepsData.rows[fakes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumFakes()) ); - } - g_StepsData.rows[stream].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetStreamRadarValue(m_NoteDataEdit,fMusicSeconds)) ); - g_StepsData.rows[voltage].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetVoltageRadarValue(m_NoteDataEdit,fMusicSeconds)) ); - g_StepsData.rows[air].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetAirRadarValue(m_NoteDataEdit,fMusicSeconds)) ); - g_StepsData.rows[freeze].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetFreezeRadarValue(m_NoteDataEdit,fMusicSeconds)) ); - g_StepsData.rows[chaos].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetChaosRadarValue(m_NoteDataEdit,fMusicSeconds)) ); - EditMiniMenu( &g_StepsData, SM_BackFromStepsData, SM_None ); - break; - } - case save: - case save_on_exit: - { - m_CurrentAction = c; - - // copy edit into current Steps - m_pSteps->SetNoteData( m_NoteDataEdit ); - - // don't forget the attacks. - m_pSong->m_Attacks = GAMESTATE->m_pCurSong->m_Attacks; - m_pSong->m_sAttackString = GAMESTATE->m_pCurSong->m_Attacks.ToVectorString(); - m_pSteps->m_Attacks = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks; - m_pSteps->m_sAttackString = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.ToVectorString(); - - switch( EDIT_MODE.GetValue() ) - { - DEFAULT_FAIL( EDIT_MODE.GetValue() ); - case EditMode_Home: - { - ASSERT( m_pSteps->IsAnEdit() ); - - RString sError; - - m_pSteps->CalculateRadarValues( m_pSong->m_fMusicLengthSeconds ); - if( !NotesWriterSM::WriteEditFileToMachine(m_pSong, m_pSteps, sError) ) - { - ScreenPrompt::Prompt( SM_None, sError ); - break; - } - - m_pSteps->SetSavedToDisk( true ); - - // HACK: clear undo, so "exit" below knows we don't need to save. - // This only works because important non-steps data can't be changed in - // home mode (BPMs, stops). - ClearUndo(); - - SCREENMAN->ZeroNextUpdate(); - - HandleScreenMessage( SM_SaveSuccessful ); - - /* FIXME - RString s; - switch( c ) - { - case save: s = "ScreenMemcardSaveEditsAfterSave"; break; - case save_on_exit: s = "ScreenMemcardSaveEditsAfterExit"; break; - default: FAIL_M(ssprintf("Invalid menu choice: %i", c)); - } - SCREENMAN->AddNewScreenToTop( s ); - */ - } - break; - case EditMode_Full: - { - // This will recalculate radar values. - m_pSong->Save(); - SCREENMAN->ZeroNextUpdate(); - - HandleScreenMessage( SM_SaveSuccessful ); - } - break; - case EditMode_CourseMods: - case EditMode_Practice: - break; - } - - m_soundSave.Play(); - } - break; - case revert_to_last_save: - ScreenPrompt::Prompt( SM_DoRevertToLastSave, REVERT_LAST_SAVE.GetValue() + "\n\n" + DESTROY_ALL_UNSAVED_CHANGES.GetValue(), PROMPT_YES_NO, ANSWER_NO ); - break; - case revert_from_disk: - ScreenPrompt::Prompt( SM_DoRevertFromDisk, REVERT_FROM_DISK.GetValue() + "\n\n" + DESTROY_ALL_UNSAVED_CHANGES.GetValue(), PROMPT_YES_NO, ANSWER_NO ); - break; - case options: - SCREENMAN->AddNewScreenToTop( "ScreenEditOptions", SM_BackFromOptions ); - break; - case edit_song_info: - { - const Song* pSong = GAMESTATE->m_pCurSong; - g_SongInformation.rows[main_title].SetOneUnthemedChoice( pSong->m_sMainTitle ); - g_SongInformation.rows[sub_title].SetOneUnthemedChoice( pSong->m_sSubTitle ); - g_SongInformation.rows[artist].SetOneUnthemedChoice( pSong->m_sArtist ); - g_SongInformation.rows[genre].SetOneUnthemedChoice( pSong->m_sGenre ); - g_SongInformation.rows[credit].SetOneUnthemedChoice( pSong->m_sCredit ); - g_SongInformation.rows[main_title_transliteration].SetOneUnthemedChoice( pSong->m_sMainTitleTranslit ); - g_SongInformation.rows[sub_title_transliteration].SetOneUnthemedChoice( pSong->m_sSubTitleTranslit ); - g_SongInformation.rows[artist_transliteration].SetOneUnthemedChoice( pSong->m_sArtistTranslit ); - g_SongInformation.rows[last_second_hint].SetOneUnthemedChoice( FloatToString(pSong->GetSpecifiedLastSecond()) ); - g_SongInformation.rows[preview_start].SetOneUnthemedChoice( FloatToString(pSong->m_fMusicSampleStartSeconds) ); - g_SongInformation.rows[preview_length].SetOneUnthemedChoice( FloatToString(pSong->m_fMusicSampleLengthSeconds) ); - g_SongInformation.rows[display_bpm].iDefaultChoice = pSong->m_DisplayBPMType; - g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( FloatToString(pSong->m_fSpecifiedBPMMin) ); - g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( FloatToString(pSong->m_fSpecifiedBPMMax) ); - - EditMiniMenu( &g_SongInformation, SM_BackFromSongInformation ); - } - break; - case edit_timing_data: - { - DisplayTimingMenu(); - } - break; - - case play_preview_music: - PlayPreviewMusic(); - break; - case exit: - switch( EDIT_MODE.GetValue() ) - { - DEFAULT_FAIL( EDIT_MODE.GetValue() ); - case EditMode_Full: - case EditMode_Home: - if( IsDirty() ) - ScreenPrompt::Prompt( SM_DoSaveAndExit, SAVE_CHANGES_BEFORE_EXITING, PROMPT_YES_NO_CANCEL, ANSWER_CANCEL ); - else - SCREENMAN->SendMessageToTopScreen( SM_DoExit ); - break; - case EditMode_Practice: - case EditMode_CourseMods: - SCREENMAN->SendMessageToTopScreen( SM_DoExit ); - break; - } - break; - }; - GAMESTATE->SetProcessedTimingData(NULL); -} - -void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAnswers, bool bAllowUndo) -{ - ASSERT_M(m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, - "You can only alter a selection of notes with a selection to begin with!"); - - bool bSaveUndo = true; - switch (c) - { - case play: - case record: - case cut: - case copy: - { - bSaveUndo = false; - } - default: - break; - } - - if( bSaveUndo ) - SetDirty( true ); - - /* We call HandleAreaMenuChoice recursively. Only the outermost - * HandleAreaMenuChoice should allow Undo so that the inner calls don't - * also save Undo and mess up the outermost */ - if( !bAllowUndo ) - bSaveUndo = false; - - if( bSaveUndo ) - SaveUndo(); - - switch(c) - { - case cut: - { - HandleAlterMenuChoice( copy ); - HandleAlterMenuChoice( clear ); - } - break; - case copy: - { - - m_Clipboard.ClearAll(); - m_Clipboard.CopyRange( m_NoteDataEdit, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - break; - case clear: - { - m_NoteDataEdit.ClearRange( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); - } - break; - case quantize: - { - NoteType nt = (NoteType)iAnswers[c]; - NoteDataUtil::SnapToNearestNoteType(m_NoteDataEdit, nt, nt, - m_NoteFieldEdit.m_iBeginMarker, - m_NoteFieldEdit.m_iEndMarker ); - break; - } - case turn: - { - const NoteData OldClipboard( m_Clipboard ); - HandleAlterMenuChoice( cut ); - - StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; - TurnType tt = (TurnType)iAnswers[c]; - switch( tt ) - { - DEFAULT_FAIL( tt ); - case left: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::left ); break; - case right: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::right ); break; - case mirror: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::mirror ); break; - case turn_backwards: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::backwards ); break; - case shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::shuffle ); break; - case super_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::super_shuffle ); break; - } - - HandleAreaMenuChoice( paste_at_begin_marker ); - m_Clipboard = OldClipboard; - } - break; - case transform: - { - int iBeginRow = m_NoteFieldEdit.m_iBeginMarker; - int iEndRow = m_NoteFieldEdit.m_iEndMarker; - TransformType tt = (TransformType)iAnswers[c]; - StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; - - switch( tt ) - { - DEFAULT_FAIL( tt ); - case noholds: NoteDataUtil::RemoveHoldNotes( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nomines: NoteDataUtil::RemoveMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case little: NoteDataUtil::Little( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case wide: NoteDataUtil::Wide( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case big: NoteDataUtil::Big( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case quick: NoteDataUtil::Quick( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case skippy: NoteDataUtil::Skippy( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case add_mines: NoteDataUtil::AddMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case echo: NoteDataUtil::Echo( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case stomp: NoteDataUtil::Stomp( m_NoteDataEdit, st, iBeginRow, iEndRow ); break; - case planted: NoteDataUtil::Planted( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case floored: NoteDataUtil::Floored( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case twister: NoteDataUtil::Twister( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nojumps: NoteDataUtil::RemoveJumps( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nohands: NoteDataUtil::RemoveHands( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case noquads: NoteDataUtil::RemoveQuads( m_NoteDataEdit, iBeginRow, iEndRow ); break; - case nostretch: NoteDataUtil::RemoveStretch( m_NoteDataEdit, st, iBeginRow, iBeginRow );break; - } - - // bake in the additions - NoteDataUtil::ConvertAdditionsToRegular( m_NoteDataEdit ); - break; - } - case alter: - { - const NoteData OldClipboard( m_Clipboard ); - HandleAlterMenuChoice( cut ); - - AlterType at = (AlterType)iAnswers[c]; - switch( at ) - { - DEFAULT_FAIL( at ); - case autogen_to_fill_width: - { - NoteData temp( m_Clipboard ); - int iMaxNonEmptyTrack = NoteDataUtil::GetMaxNonEmptyTrack( temp ); - if( iMaxNonEmptyTrack == -1 ) - break; - temp.SetNumTracks( iMaxNonEmptyTrack+1 ); - NoteDataUtil::LoadTransformedSlidingWindow( temp, m_Clipboard, m_Clipboard.GetNumTracks() ); - NoteDataUtil::RemoveStretch( m_Clipboard, GAMESTATE->m_pCurSteps[0]->m_StepsType ); - } - break; - case backwards: NoteDataUtil::Backwards( m_Clipboard ); break; - case swap_sides: NoteDataUtil::SwapSides( m_Clipboard ); break; - case copy_left_to_right: NoteDataUtil::CopyLeftToRight( m_Clipboard ); break; - case copy_right_to_left: NoteDataUtil::CopyRightToLeft( m_Clipboard ); break; - case clear_left: NoteDataUtil::ClearLeft( m_Clipboard ); break; - case clear_right: NoteDataUtil::ClearRight( m_Clipboard ); break; - case collapse_to_one: NoteDataUtil::CollapseToOne( m_Clipboard ); break; - case collapse_left: NoteDataUtil::CollapseLeft( m_Clipboard ); break; - case shift_left: NoteDataUtil::ShiftLeft( m_Clipboard ); break; - case shift_right: NoteDataUtil::ShiftRight( m_Clipboard ); break; - } - - HandleAreaMenuChoice( paste_at_begin_marker ); - m_Clipboard = OldClipboard; - break; - } - case tempo: - { - // This affects all steps. - AlterType at = (AlterType)iAnswers[c]; - float fScale = -1; - - switch( at ) - { - DEFAULT_FAIL( at ); - case compress_2x: fScale = 0.5f; break; - case compress_3_2: fScale = 2.0f/3; break; - case compress_4_3: fScale = 0.75f; break; - case expand_4_3: fScale = 4.0f/3; break; - case expand_3_2: fScale = 1.5f; break; - case expand_2x: fScale = 2; break; - } - - int iStartIndex = m_NoteFieldEdit.m_iBeginMarker; - int iEndIndex = m_NoteFieldEdit.m_iEndMarker; - int iNewEndIndex = iEndIndex + lrintf( (iEndIndex - iStartIndex) * (fScale - 1) ); - - // scale currently editing notes - NoteDataUtil::ScaleRegion( m_NoteDataEdit, fScale, iStartIndex, iEndIndex ); - - // scale timing data - GetAppropriateTimingForUpdate().ScaleRegion(fScale, - m_NoteFieldEdit.m_iBeginMarker, - m_NoteFieldEdit.m_iEndMarker, true ); - - m_NoteFieldEdit.m_iEndMarker = iNewEndIndex; - break; - - } - - case play: - m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; - m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; - TransitionEditState( STATE_PLAYING ); - break; - case record: - m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; - m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; - TransitionEditState( STATE_RECORDING ); - break; - case preview_designation: - { - float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds = fMarkerStart; - GAMESTATE->m_pCurSong->m_fMusicSampleLengthSeconds = fMarkerEnd - fMarkerStart; - break; - } - case convert_to_pause: - { - float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - - // The length of the stop segment we're going to create. This includes time spent in any - // stops in the selection, which will be deleted and subsumed into the new stop. - float fStopLength = fMarkerEnd - fMarkerStart; - - // be sure not to clobber the row at the start - a row at the end - // can be dropped safely, though - NoteDataUtil::DeleteRows( m_NoteDataEdit, - m_NoteFieldEdit.m_iBeginMarker + 1, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker - ); - GetAppropriateTimingForUpdate().DeleteRows( m_NoteFieldEdit.m_iBeginMarker + 1, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); - GetAppropriateTimingForUpdate().SetStopAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); - m_NoteFieldEdit.m_iBeginMarker = -1; - m_NoteFieldEdit.m_iEndMarker = -1; - break; - } - case convert_to_delay: - { - float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); - float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); - - // The length of the delay segment we're going to create. This includes time spent in any - // stops in the selection, which will be deleted and subsumed into the new stop. - float fStopLength = fMarkerEnd - fMarkerStart; - - NoteDataUtil::DeleteRows( m_NoteDataEdit, - m_NoteFieldEdit.m_iBeginMarker, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker - ); - GetAppropriateTimingForUpdate().DeleteRows( m_NoteFieldEdit.m_iBeginMarker, - m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); - GetAppropriateTimingForUpdate().SetDelayAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); - m_NoteFieldEdit.m_iBeginMarker = -1; - m_NoteFieldEdit.m_iEndMarker = -1; - break; - } - case convert_to_warp: - { - float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); - float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - startBeat; - GetAppropriateTimingForUpdate().SetWarpAtBeat(startBeat,lengthBeat); - SetDirty(true); - break; - } - case convert_to_attack: - { - float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); - float endBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker); - const TimingData &timing = GetAppropriateTiming(); - float &start = g_fLastInsertAttackPositionSeconds; - float &length = g_fLastInsertAttackDurationSeconds; - start = timing.GetElapsedTimeFromBeat(startBeat); - length = timing.GetElapsedTimeFromBeat(endBeat) - start; - - AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? - m_pSteps->m_Attacks : m_pSong->m_Attacks; - int iAttack = FindAttackAtTime(attacks, start); - - ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; - this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); - PlayerOptions po; - if (iAttack >= 0) - po.FromString(attacks[iAttack].sModifiers); - - toEdit.Assign( ModsLevel_Preferred, po ); - SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); - SetDirty(true); - break; - } - case convert_to_fake: - { - int startRow = m_NoteFieldEdit.m_iBeginMarker; - float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - NoteRowToBeat(startRow); - GetAppropriateTimingForUpdate().AddSegment( FakeSegment(startRow,lengthBeat) ); - SetDirty(true); - break; - } - case routine_invert_notes: - { - NoteData &nd = this->m_NoteDataEdit; - NoteField &nf = this->m_NoteFieldEdit; - FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE(nd, r, - nf.m_iBeginMarker, - nf.m_iEndMarker) - { - for (int t = 0; t < nd.GetNumTracks(); t++) - { - const TapNote &tn = nd.GetTapNote(t, r); - if (tn.type != TapNote::empty) - { - TapNote nTap = tn; - nTap.pn = (tn.pn == PLAYER_1 ? - PLAYER_2 : PLAYER_1); - m_NoteDataEdit.SetTapNote(t, r, nTap); - } - } - } - break; - } - case routine_mirror_1_to_2: - case routine_mirror_2_to_1: - { - PlayerNumber oPN = (c == routine_mirror_1_to_2 ? - PLAYER_1 : PLAYER_2); - PlayerNumber nPN = (c == routine_mirror_1_to_2 ? - PLAYER_2 : PLAYER_1); - int nTrack = -1; - NoteData &nd = this->m_NoteDataEdit; - NoteField &nf = this->m_NoteFieldEdit; - int tracks = nd.GetNumTracks(); - FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE(nd, r, - nf.m_iBeginMarker, - nf.m_iEndMarker) - { - for (int t = 0; t < tracks; t++) - { - const TapNote &tn = nd.GetTapNote(t, r); - if (tn.type != TapNote::empty && tn.pn == oPN) - { - TapNote nTap = tn; - nTap.pn = nPN; - StepsType curType = GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType; - // TODO: Find a better way to do this. - if (curType == StepsType_dance_routine) - { - nTrack = tracks - t - 1; - } - else if (curType == StepsType_pump_routine) - { - switch (t) - { - case 0: nTrack = 8; break; - case 1: nTrack = 9; break; - case 2: nTrack = 7; break; - case 3: nTrack = 5; break; - case 4: nTrack = 6; break; - case 5: nTrack = 3; break; - case 6: nTrack = 4; break; - case 7: nTrack = 2; break; - case 8: nTrack = 0; break; - case 9: nTrack = 1; break; - default: FAIL_M(ssprintf("Invalid column %d for pump-routine", t)); break; - } - } - m_NoteDataEdit.SetTapNote(nTrack, r, nTap); - } - } - } - break; - } - default: break; - } - -} - -void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAnswers, bool bAllowUndo ) -{ - bool bSaveUndo = true; - switch( c ) - { - case clear_clipboard: - case undo: - bSaveUndo = false; - break; - default: - break; - } - - if( bSaveUndo ) - SetDirty( true ); - - /* We call HandleAreaMenuChoice recursively. Only the outermost - * HandleAreaMenuChoice should allow Undo so that the inner calls don't - * also save Undo and mess up the outermost */ - if( !bAllowUndo ) - bSaveUndo = false; - - if( bSaveUndo ) - SaveUndo(); - - switch( c ) - { - DEFAULT_FAIL( c ); - - case paste_at_current_beat: - case paste_at_begin_marker: - { - int iDestFirstRow = -1; - switch( c ) - { - DEFAULT_FAIL( c ); - case paste_at_current_beat: - iDestFirstRow = BeatToNoteRow( GetAppropriatePosition().m_fSongBeat ); - break; - case paste_at_begin_marker: - ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 ); - iDestFirstRow = m_NoteFieldEdit.m_iBeginMarker; - break; - } - - int iRowsToCopy = m_Clipboard.GetLastRow()+1; - m_NoteDataEdit.CopyRange( m_Clipboard, 0, iRowsToCopy, iDestFirstRow ); - } - break; - - case insert_and_shift: - NoteDataUtil::InsertRows( m_NoteDataEdit, GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); - break; - case delete_and_shift: - NoteDataUtil::DeleteRows( m_NoteDataEdit, GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); - break; - case shift_pauses_forward: - GetAppropriateTimingForUpdate().InsertRows( GetRow(), - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); - break; - case shift_pauses_backward: - GetAppropriateTimingForUpdate().DeleteRows( GetRow() + 1, - iAnswers.size() > 0 ? - NoteTypeToRow((NoteType)iAnswers[c]) : 48); - break; - - case convert_pause_to_beat: - { - float fStopSeconds = GetAppropriateTiming().GetStopAtRow(GetRow()); - GetAppropriateTimingForUpdate().SetStopAtBeat( GetBeat() , 0 ); - - float fStopBeats = fStopSeconds * GetAppropriateTiming().GetBPMAtBeat( GetBeat() ) / 60; - - // don't move the step from where it is, just move everything later - NoteDataUtil::InsertRows( m_NoteDataEdit, GetRow() + 1, BeatToNoteRow(fStopBeats) ); - GetAppropriateTimingForUpdate().InsertRows( GetRow() + 1, BeatToNoteRow(fStopBeats) ); - } - break; - case convert_delay_to_beat: - { - TimingData &timing = GetAppropriateTimingForUpdate(); - float pause = timing.GetDelayAtRow(GetRow()); - timing.SetDelayAtRow(GetRow(), 0); - - float pauseBeats = pause * timing.GetBPMAtBeat(GetBeat()) / 60; - - NoteDataUtil::InsertRows(m_NoteDataEdit, GetRow(), BeatToNoteRow(pauseBeats)); - timing.InsertRows(GetRow(), BeatToNoteRow(pauseBeats)); - break; - } - case last_second_at_beat: - { - const TimingData &timing = GetAppropriateTiming(); - Song &s = *GAMESTATE->m_pCurSong; - s.SetSpecifiedLastSecond(timing.GetElapsedTimeFromBeat(GetBeat())); - break; - } - case undo: - Undo(); - break; - case clear_clipboard: - { - m_Clipboard.ClearAll(); - break; - } - case modify_attacks_at_row: - { - this->DoStepAttackMenu(); - break; - } - case modify_keysounds_at_row: - { - this->DoKeyboardTrackMenu(); - break; - } - }; - - if( bSaveUndo ) - CheckNumberOfNotesAndUndo(); -} - -void ScreenEdit::HandleStepsDataChoice( StepsDataChoice c, const vector &iAnswers ) -{ - return; // nothing is done with the choices. Yet. -} - -static LocalizedString ENTER_NEW_DESCRIPTION( "ScreenEdit", "Enter a new description." ); -static LocalizedString ENTER_NEW_CHART_NAME("ScreenEdit", "Enter a new chart name."); -static LocalizedString ENTER_NEW_CHART_STYLE( "ScreenEdit", "Enter a new chart style." ); -static LocalizedString ENTER_NEW_STEP_AUTHOR( "ScreenEdit", "Enter the author who made this step pattern." ); -static LocalizedString ENTER_NEW_METER( "ScreenEdit", "Enter a new meter." ); -static LocalizedString ENTER_MIN_BPM ("ScreenEdit","Enter a new min BPM."); -static LocalizedString ENTER_MAX_BPM ("ScreenEdit","Enter a new max BPM."); -void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const vector &iAnswers ) -{ - Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; - Difficulty dc = (Difficulty)iAnswers[difficulty]; - pSteps->SetDifficulty( dc ); - pSteps->SetDisplayBPM(static_cast(iAnswers[step_display_bpm])); - - switch( c ) - { - case chartname: - { - ScreenTextEntry::TextEntry(SM_None, - ENTER_NEW_CHART_NAME, - m_pSteps->GetChartName(), - MAX_STEPS_DESCRIPTION_LENGTH, - SongUtil::ValidateCurrentStepsChartName, - ChangeChartName, - NULL); - break; - } - case description: - { - ScreenTextEntry::TextEntry(SM_None, - ENTER_NEW_DESCRIPTION, - m_pSteps->GetDescription(), - MAX_STEPS_DESCRIPTION_LENGTH, - SongUtil::ValidateCurrentStepsDescription, - ChangeDescription, - NULL); - break; - } - case chartstyle: - { - ScreenTextEntry::TextEntry(SM_None, - ENTER_NEW_CHART_STYLE, - m_pSteps->GetChartStyle(), - 255, - NULL, - ChangeChartStyle, - NULL); - break; - } - case step_credit: - { - ScreenTextEntry::TextEntry(SM_None, - ENTER_NEW_STEP_AUTHOR, - m_pSteps->GetCredit(), - 255, - SongUtil::ValidateCurrentStepsCredit, - ChangeStepCredit, - NULL); - break; - } - case meter: - { - ScreenTextEntry::TextEntry(SM_BackFromDifficultyMeterChange, - ENTER_NEW_METER, - ssprintf("%d", m_pSteps->GetMeter()), - 4, - NULL, - ChangeStepMeter, - NULL); - break; - } - case step_min_bpm: - { - ScreenTextEntry::TextEntry(SM_None, ENTER_MIN_BPM, - FloatToString(pSteps->GetMinBPM()), 20, - ScreenTextEntry::FloatValidate, - ChangeStepsMinBPM, NULL); - break; - } - case step_max_bpm: - { - ScreenTextEntry::TextEntry(SM_None, ENTER_MAX_BPM, - FloatToString(pSteps->GetMaxBPM()), 20, - ScreenTextEntry::FloatValidate, - ChangeStepsMaxBPM, NULL); - break; - } - default: - break; - } - SetDirty(true); -} - -static LocalizedString ENTER_MAIN_TITLE ("ScreenEdit","Enter a new main title."); -static LocalizedString ENTER_SUB_TITLE ("ScreenEdit","Enter a new sub title."); -static LocalizedString ENTER_ARTIST ("ScreenEdit","Enter a new artist."); -static LocalizedString ENTER_GENRE ("ScreenEdit","Enter a new genre."); -static LocalizedString ENTER_CREDIT ("ScreenEdit","Enter a new credit."); -static LocalizedString ENTER_MAIN_TITLE_TRANSLIT ("ScreenEdit","Enter a new main title transliteration."); -static LocalizedString ENTER_SUB_TITLE_TRANSLIT ("ScreenEdit","Enter a new sub title transliteration."); -static LocalizedString ENTER_ARTIST_TRANSLIT ("ScreenEdit","Enter a new artist transliteration."); -static LocalizedString ENTER_LAST_SECOND_HINT ("ScreenEdit","Enter a new last second hint."); -static LocalizedString ENTER_PREVIEW_START ("ScreenEdit","Enter a new preview start."); -static LocalizedString ENTER_PREVIEW_LENGTH ("ScreenEdit","Enter a new preview length."); -void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vector &iAnswers ) -{ - Song* pSong = GAMESTATE->m_pCurSong; - pSong->m_DisplayBPMType = static_cast(iAnswers[display_bpm]); - - switch( c ) - { - case main_title: - ScreenTextEntry::TextEntry( SM_None, ENTER_MAIN_TITLE, pSong->m_sMainTitle, 100, NULL, ChangeMainTitle, NULL ); - break; - case sub_title: - ScreenTextEntry::TextEntry( SM_None, ENTER_SUB_TITLE, pSong->m_sSubTitle, 100, NULL, ChangeSubTitle, NULL ); - break; - case artist: - ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST, pSong->m_sArtist, 100, NULL, ChangeArtist, NULL ); - break; - case genre: - ScreenTextEntry::TextEntry( SM_None, ENTER_GENRE, pSong->m_sGenre, 100, NULL, ChangeGenre, NULL ); - break; - case credit: - ScreenTextEntry::TextEntry( SM_None, ENTER_CREDIT, pSong->m_sCredit, 100, NULL, ChangeCredit, NULL ); - break; - case main_title_transliteration: - ScreenTextEntry::TextEntry( SM_None, ENTER_MAIN_TITLE_TRANSLIT, pSong->m_sMainTitleTranslit, 100, NULL, ChangeMainTitleTranslit, NULL ); - break; - case sub_title_transliteration: - ScreenTextEntry::TextEntry( SM_None, ENTER_SUB_TITLE_TRANSLIT, pSong->m_sSubTitleTranslit, 100, NULL, ChangeSubTitleTranslit, NULL ); - break; - case artist_transliteration: - ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST_TRANSLIT, pSong->m_sArtistTranslit, 100, NULL, ChangeArtistTranslit, NULL ); - break; - case last_second_hint: - ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_SECOND_HINT, - FloatToString(pSong->GetSpecifiedLastSecond()), 20, - ScreenTextEntry::FloatValidate, ChangeLastSecondHint, NULL ); - break; - case preview_start: - ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_START, - FloatToString(pSong->m_fMusicSampleStartSeconds), 20, - ScreenTextEntry::FloatValidate, ChangePreviewStart, NULL ); - break; - case preview_length: - ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_LENGTH, - FloatToString(pSong->m_fMusicSampleLengthSeconds), 20, - ScreenTextEntry::FloatValidate, ChangePreviewLength, NULL ); - break; - case min_bpm: - ScreenTextEntry::TextEntry( SM_None, ENTER_MIN_BPM, - FloatToString(pSong->m_fSpecifiedBPMMin), 20, - ScreenTextEntry::FloatValidate, ChangeMinBPM, NULL ); - break; - case max_bpm: - ScreenTextEntry::TextEntry( SM_None, ENTER_MAX_BPM, - FloatToString(pSong->m_fSpecifiedBPMMax), 20, - ScreenTextEntry::FloatValidate, ChangeMaxBPM, NULL ); - break; - default: break; - }; - SetDirty(true); -} - - -static LocalizedString ENTER_BEAT_0_OFFSET ( "ScreenEdit", "Enter the offset for the song."); -static LocalizedString ENTER_BPM_VALUE ( "ScreenEdit", "Enter a new BPM value." ); -static LocalizedString ENTER_STOP_VALUE ( "ScreenEdit", "Enter a new Stop value." ); -static LocalizedString ENTER_DELAY_VALUE ( "ScreenEdit", "Enter a new Delay value." ); -static LocalizedString ENTER_TIME_SIGNATURE_VALUE ( "ScreenEdit", "Enter a new Time Signature." ); -static LocalizedString ENTER_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." ); -static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo value." ); -static LocalizedString ENTER_LABEL_VALUE ( "ScreenEdit", "Enter a new Label value." ); -static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp value." ); -static LocalizedString ENTER_SPEED_PERCENT_VALUE ( "ScreenEdit", "Enter a new Speed percent value." ); -static LocalizedString ENTER_SPEED_WAIT_VALUE ( "ScreenEdit", "Enter a new Speed wait value." ); -static LocalizedString ENTER_SPEED_MODE_VALUE ( "ScreenEdit", "Enter a new Speed mode value." ); -static LocalizedString ENTER_SCROLL_VALUE ( "ScreenEdit", "Enter a new Scroll value." ); -static LocalizedString ENTER_FAKE_VALUE ( "ScreenEdit", "Enter a new Fake value." ); -static LocalizedString CONFIRM_TIMING_ERASE ( "ScreenEdit", "Are you sure you want to erase this chart's timing data?" ); -void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector &iAnswers ) -{ - switch( c ) - { - DEFAULT_FAIL( c ); - case beat_0_offset: - ScreenTextEntry::TextEntry( - SM_BackFromBeat0Change, - ENTER_BEAT_0_OFFSET, - FloatToString(GetAppropriateTiming().m_fBeat0OffsetInSeconds), - 20 - ); - break; - case bpm: - ScreenTextEntry::TextEntry( - SM_BackFromBPMChange, - ENTER_BPM_VALUE, - FloatToString( GetAppropriateTiming().GetBPMAtBeat( GetBeat() ) ), - 10 - ); - break; - case stop: - ScreenTextEntry::TextEntry( - SM_BackFromStopChange, - ENTER_STOP_VALUE, - FloatToString( GetAppropriateTiming().GetStopAtBeat( GetBeat() ) ), - 10 - ); - break; - case delay: - ScreenTextEntry::TextEntry( - SM_BackFromDelayChange, - ENTER_DELAY_VALUE, - FloatToString( GetAppropriateTiming().GetDelayAtBeat( GetBeat() ) ), - 10 - ); - break; - case time_signature: - { - const TimeSignatureSegment *ts = GetAppropriateTiming().GetTimeSignatureSegmentAtBeat( GetBeat() ); - ScreenTextEntry::TextEntry( - SM_BackFromTimeSignatureChange, - ENTER_TIME_SIGNATURE_VALUE, - ssprintf( "%d/%d", ts->GetNum(), ts->GetDen() ), - 8 - ); - break; - } - case tickcount: - ScreenTextEntry::TextEntry( - SM_BackFromTickcountChange, - ENTER_TICKCOUNT_VALUE, - ssprintf( "%d", GetAppropriateTiming().GetTickcountAtBeat( GetBeat() ) ), - 2 - ); - break; - case combo: - { - const ComboSegment *cs = GetAppropriateTiming().GetComboSegmentAtBeat(GetBeat()); - ScreenTextEntry::TextEntry(SM_BackFromComboChange, - ENTER_COMBO_VALUE, - ssprintf( "%d/%d", - cs->GetCombo(), - cs->GetMissCombo()), - 7); - break; - } - case label: - ScreenTextEntry::TextEntry( - SM_BackFromLabelChange, - ENTER_LABEL_VALUE, - ssprintf( "%s", GetAppropriateTiming().GetLabelAtBeat( GetBeat() ).c_str() ), - 64 - ); - break; - case warp: - ScreenTextEntry::TextEntry( - SM_BackFromWarpChange, - ENTER_WARP_VALUE, - FloatToString( GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), - 10 - ); - break; - case speed_percent: - ScreenTextEntry::TextEntry( - SM_BackFromSpeedPercentChange, - ENTER_SPEED_PERCENT_VALUE, - FloatToString( GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() )->GetRatio() ), - 10 - ); - break; - case scroll: - ScreenTextEntry::TextEntry( - SM_BackFromScrollChange, - ENTER_SCROLL_VALUE, - FloatToString( GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() )->GetRatio() ), - 10 - ); - break; - case speed_wait: - ScreenTextEntry::TextEntry( - SM_BackFromSpeedWaitChange, - ENTER_SPEED_WAIT_VALUE, - FloatToString( GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() )->GetDelay() ), - 10 - ); - break; - case speed_mode: - { - ScreenTextEntry::TextEntry( - SM_BackFromSpeedModeChange, - ENTER_SPEED_MODE_VALUE, - "", - 3 - ); - - break; - } - case fake: - { - ScreenTextEntry::TextEntry( - SM_BackFromFakeChange, - ENTER_FAKE_VALUE, - FloatToString(GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), - 10 - ); - break; - } - case copy_full_timing: - { - clipboardFullTiming = GetAppropriateTiming(); - break; - } - case paste_full_timing: - { - if (GAMESTATE->m_bIsUsingStepTiming) - { - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = clipboardFullTiming; - } - else - { - GAMESTATE->m_pCurSong->m_SongTiming = clipboardFullTiming; - } - SetDirty(true); - break; - } - case erase_step_timing: - ScreenPrompt::Prompt( SM_DoEraseStepTiming, CONFIRM_TIMING_ERASE , PROMPT_YES_NO, ANSWER_NO ); - break; - - } -} - -void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector &iAnswers ) -{ - BackgroundChange newChange; - - FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer), iter ) - { - if( iter->m_fStartBeat == GAMESTATE->m_Position.m_fSongBeat ) - { - newChange = *iter; - // delete the old change. We'll add a new one below. - m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer).erase( iter ); - break; - } - } - - newChange.m_fStartBeat = GAMESTATE->m_Position.m_fSongBeat; - newChange.m_fRate = StringToFloat( g_BackgroundChange.rows[rate].choices[iAnswers[rate]] )/100.f; - newChange.m_sTransition = iAnswers[transition] ? g_BackgroundChange.rows[transition].choices[iAnswers[transition]] : RString(); - newChange.m_def.m_sEffect = iAnswers[effect] ? g_BackgroundChange.rows[effect].choices[iAnswers[effect]] : RString(); - newChange.m_def.m_sColor1 = iAnswers[color1] ? g_BackgroundChange.rows[color1].choices[iAnswers[color1]] : RString(); - newChange.m_def.m_sColor2 = iAnswers[color2] ? g_BackgroundChange.rows[color2].choices[iAnswers[color2]] : RString(); - switch( iAnswers[file1_type] ) - { - DEFAULT_FAIL( iAnswers[file1_type] ); - case none: newChange.m_def.m_sFile1 = ""; break; - case dynamic_random: newChange.m_def.m_sFile1 = RANDOM_BACKGROUND_FILE; break; - case baked_random: newChange.m_def.m_sFile1 = GetOneBakedRandomFile( m_pSong ); break; - case song_bganimation: - case song_movie: - case song_bitmap: - case global_bganimation: - case global_movie: - case global_movie_song_group: - case global_movie_song_group_and_genre: - { - BGChangeChoice row1 = (BGChangeChoice)(file1_song_bganimation + iAnswers[file1_type]); - newChange.m_def.m_sFile1 = g_BackgroundChange.rows[row1].choices.empty() ? "" : g_BackgroundChange.rows[row1].choices[iAnswers[row1]]; - } - break; - } - switch( iAnswers[file2_type] ) - { - DEFAULT_FAIL(iAnswers[file2_type]); - case none: newChange.m_def.m_sFile2 = ""; break; - case dynamic_random: newChange.m_def.m_sFile2 = RANDOM_BACKGROUND_FILE; break; - case baked_random: newChange.m_def.m_sFile2 = GetOneBakedRandomFile( m_pSong ); break; - case song_bganimation: - case song_movie: - case song_bitmap: - case global_bganimation: - case global_movie: - case global_movie_song_group: - case global_movie_song_group_and_genre: - { - BGChangeChoice row2 = (BGChangeChoice)(file2_song_bganimation + iAnswers[file2_type]); - newChange.m_def.m_sFile2 = g_BackgroundChange.rows[row2].choices.empty() ? "" : g_BackgroundChange.rows[row2].choices[iAnswers[row2]]; - } - break; - } - - - if( c == delete_change || newChange.m_def.m_sFile1.empty() ) - { - // don't add - } - else - { - m_pSong->AddBackgroundChange( g_CurrentBGChangeLayer, newChange ); - } - g_CurrentBGChangeLayer = BACKGROUND_LAYER_Invalid; -} - -void ScreenEdit::SetupCourseAttacks() -{ - /* This is the first beat that can be changed without it being visible. Until - * we draw for the first time, any beat can be changed. */ - GAMESTATE->m_pPlayerState[PLAYER_1]->m_fLastDrawnBeat = -100; - - // Put course options into effect. - GAMESTATE->m_pPlayerState[PLAYER_1]->m_ModsToApply.clear(); - GAMESTATE->m_pPlayerState[PLAYER_1]->RemoveActiveAttacks(); - - - if( GAMESTATE->m_pCurCourse ) - { - AttackArray Attacks; - - if( EDIT_MODE == EditMode_CourseMods ) - { - Attacks = GAMESTATE->m_pCurCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex].attacks; - } - else - { - GAMESTATE->m_pCurCourse->RevertFromDisk(); // Remove this and have a separate reload key? - - for( unsigned e = 0; e < GAMESTATE->m_pCurCourse->m_vEntries.size(); ++e ) - { - if( GAMESTATE->m_pCurCourse->m_vEntries[e].songID.ToSong() != m_pSong ) - continue; - - Attacks = GAMESTATE->m_pCurCourse->m_vEntries[e].attacks; - break; - } - } - - FOREACH( Attack, Attacks, attack ) - GAMESTATE->m_pPlayerState[PLAYER_1]->LaunchAttack( *attack ); - } - else - { - const PlayerOptions &p = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent(); - if (GAMESTATE->m_pCurSong && p.m_fNoAttack == 0 && p.m_fRandAttack == 0 ) - { - AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? - GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks : - GAMESTATE->m_pCurSong->m_Attacks; - - if (attacks.size() > 0) - { - FOREACH(Attack, attacks, attack) - { - float fBeat = GetAppropriateTiming().GetBeatFromElapsedTime(attack->fStartSecond); - if (fBeat >= GetBeat()) - GAMESTATE->m_pPlayerState[PLAYER_1]->LaunchAttack( *attack ); - } - } - } - } - - GAMESTATE->m_pPlayerState[PLAYER_1]->RebuildPlayerOptionsFromActiveAttacks(); -} - -void ScreenEdit::CopyToLastSave() -{ - ASSERT( GAMESTATE->m_pCurSong != NULL ); - ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); - m_SongLastSave = *GAMESTATE->m_pCurSong; - m_vStepsLastSave.clear(); - const vector &vSteps = GAMESTATE->m_pCurSong->GetStepsByStepsType( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType ); - FOREACH_CONST( Steps*, vSteps, it ) - m_vStepsLastSave.push_back( **it ); -} - -void ScreenEdit::CopyFromLastSave() -{ - // We are assuming two things here: - // 1) No steps can be created by ScreenEdit - // 2) No steps can be deleted by ScreenEdit (except possibly when we exit) - *GAMESTATE->m_pCurSong = m_SongLastSave; - const vector &vSteps = GAMESTATE->m_pCurSong->GetStepsByStepsType( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType ); - ASSERT_M( vSteps.size() == m_vStepsLastSave.size(), ssprintf("Step sizes don't match: %d, %d", int(vSteps.size()), int(m_vStepsLastSave.size())) ); - for( unsigned i = 0; i < vSteps.size(); ++i ) - *vSteps[i] = m_vStepsLastSave[i]; -} - -void ScreenEdit::RevertFromDisk() -{ - ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); - StepsID id; - id.FromSteps( GAMESTATE->m_pCurSteps[PLAYER_1] ); - ASSERT( id.IsValid() ); - - GAMESTATE->m_pCurSong->ReloadFromSongDir( GAMESTATE->m_pCurSong->GetSongDir() ); - - Steps *pNewSteps = id.ToSteps( GAMESTATE->m_pCurSong, true ); - if( !pNewSteps ) - { - // If the Steps we were currently editing vanished when we did the revert, - // put a blank Steps in its place. Note that this does not have to be the - // work of someone maliciously changing the simfile; it could happen to - // someone editing a new stepchart and reverting from disk, for example. - pNewSteps = GAMESTATE->m_pCurSong->CreateSteps(); - pNewSteps->CreateBlank( id.GetStepsType() ); - pNewSteps->SetDifficulty( id.GetDifficulty() ); - GAMESTATE->m_pCurSong->AddSteps( pNewSteps ); - } - GAMESTATE->m_pCurSteps[PLAYER_1].Set( pNewSteps ); - m_pSteps = pNewSteps; - - CopyToLastSave(); - SetDirty( false ); - SONGMAN->Invalidate( GAMESTATE->m_pCurSong ); -} - -void ScreenEdit::SaveUndo() -{ - m_bHasUndo = true; - m_Undo.CopyAll( m_NoteDataEdit ); -} - -static LocalizedString UNDO ("ScreenEdit", "Undo"); -static LocalizedString CANT_UNDO ("ScreenEdit", "Can't undo - no undo data."); -void ScreenEdit::Undo() -{ - if( m_bHasUndo ) - { - swap( m_Undo, m_NoteDataEdit ); - SCREENMAN->SystemMessage( UNDO ); - } - else - { - SCREENMAN->SystemMessage( CANT_UNDO ); - SCREENMAN->PlayInvalidSound(); - } -} - -void ScreenEdit::ClearUndo() -{ - m_bHasUndo = false; - m_Undo.ClearAll(); -} - -static LocalizedString CREATES_MORE_THAN_NOTES ( "ScreenEdit", "This change creates more than %d notes in a measure." ); -static LocalizedString CREATES_NOTES_PAST_END ( "ScreenEdit", "This change creates notes past the end of the music and is not allowed." ); -static LocalizedString CHANGE_REVERTED ( "ScreenEdit", "The change has been reverted." ); -void ScreenEdit::CheckNumberOfNotesAndUndo() -{ - if( EDIT_MODE.GetValue() != EditMode_Home ) - return; - - const float fBeat = GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; - const TimeSignatureSegment * curTime = GAMESTATE->m_pCurSong->m_SongTiming.GetTimeSignatureSegmentAtBeat( fBeat ); - int rowsPerMeasure = curTime->GetDen() * curTime->GetNum(); - - for( int row=0; row<=m_NoteDataEdit.GetLastRow(); row+=rowsPerMeasure ) - { - int iNumNotesThisMeasure = 0; - FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteDataEdit, r, row, row+rowsPerMeasure ) - iNumNotesThisMeasure += m_NoteDataEdit.GetNumTapNonEmptyTracks( r ); - } - - if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) - { - /* Check that the action didn't push notes any farther past the last - * measure. This blocks Insert Beat from pushing past the end, but allows - * Delete Beat to pull back the notes that are already past the end. - */ - float fNewLastBeat = m_NoteDataEdit.GetLastBeat(); - bool bLastBeatIncreased = fNewLastBeat > m_Undo.GetLastBeat(); - bool bPassedTheEnd = fNewLastBeat > GetMaximumBeatForNewNote(); - if( bLastBeatIncreased && bPassedTheEnd ) - { - Undo(); - m_bHasUndo = false; - RString sError = CREATES_NOTES_PAST_END.GetValue() + "\n\n" + CHANGE_REVERTED.GetValue(); - ScreenPrompt::Prompt( SM_None, sError ); - return; - } - } -} - -float ScreenEdit::GetMaximumBeatForNewNote() const -{ - switch( EDIT_MODE.GetValue() ) - { - DEFAULT_FAIL( EDIT_MODE.GetValue() ); - case EditMode_Practice: - case EditMode_CourseMods: - case EditMode_Home: - { - Song &s = *GAMESTATE->m_pCurSong; - float fEndBeat = s.GetLastBeat(); - - /* Round up to the next measure end. Some songs end on weird beats - * mid-measure, and it's odd to have movement capped to these weird - * beats. */ - TimingData &timing = s.m_SongTiming; - float playerBeat = GetAppropriatePosition().m_fSongBeat; - int beatsPerMeasure = timing.GetTimeSignatureSegmentAtBeat( playerBeat )->GetNum(); - fEndBeat += beatsPerMeasure; - fEndBeat = ftruncf( fEndBeat, (float)beatsPerMeasure ); - - return fEndBeat; - } - case EditMode_Full: - return NoteRowToBeat(MAX_NOTE_ROW); - } -} - -float ScreenEdit::GetMaximumBeatForMoving() const -{ - float fEndBeat = GetMaximumBeatForNewNote(); - - /* Jump to GetLastBeat even if it's past the song's last beat - * so that users can delete garbage steps past then end that they have - * have inserted in a text editor. Once they delete all steps on - * GetLastBeat() and move off of that beat, they won't be able to return. */ - fEndBeat = max( fEndBeat, m_NoteDataEdit.GetLastBeat() ); - - return fEndBeat; -} - -struct EditHelpLine -{ - const char *szEnglishDescription; - vector veb; - - EditHelpLine( - const char *_szEnglishDescription, - EditButton eb0, - EditButton eb1 = EditButton_Invalid, - EditButton eb2 = EditButton_Invalid, - EditButton eb3 = EditButton_Invalid, - EditButton eb4 = EditButton_Invalid, - EditButton eb5 = EditButton_Invalid, - EditButton eb6 = EditButton_Invalid, - EditButton eb7 = EditButton_Invalid, - EditButton eb8 = EditButton_Invalid, - EditButton eb9 = EditButton_Invalid ) - { - szEnglishDescription = _szEnglishDescription; -#define PUSH_IF_VALID( x ) if( x != EditButton_Invalid ) veb.push_back( x ); - PUSH_IF_VALID( eb0 ); - PUSH_IF_VALID( eb1 ); - PUSH_IF_VALID( eb2 ); - PUSH_IF_VALID( eb3 ); - PUSH_IF_VALID( eb4 ); - PUSH_IF_VALID( eb5 ); - PUSH_IF_VALID( eb6 ); - PUSH_IF_VALID( eb7 ); - PUSH_IF_VALID( eb8 ); - PUSH_IF_VALID( eb9 ); -#undef PUSH_IF_VALID - } -}; -// TODO: Identify which of these can be removed and sent to a readme. -static const EditHelpLine g_EditHelpLines[] = -{ - EditHelpLine( "Move cursor", EDIT_BUTTON_SCROLL_UP_LINE, EDIT_BUTTON_SCROLL_DOWN_LINE ), - EditHelpLine( "Jump measure", EDIT_BUTTON_SCROLL_UP_PAGE, EDIT_BUTTON_SCROLL_DOWN_PAGE ), - EditHelpLine( "Jump measure", EDIT_BUTTON_SCROLL_PREV_MEASURE, EDIT_BUTTON_SCROLL_NEXT_MEASURE ), - EditHelpLine( "Select region", EDIT_BUTTON_SCROLL_SELECT ), - EditHelpLine( "Jump to first/last beat", EDIT_BUTTON_SCROLL_HOME, EDIT_BUTTON_SCROLL_END ), - EditHelpLine( "Change zoom", EDIT_BUTTON_SCROLL_SPEED_UP, EDIT_BUTTON_SCROLL_SPEED_DOWN ), - EditHelpLine( "Play", EDIT_BUTTON_PLAY_SELECTION ), - EditHelpLine( "Play current beat to end", EDIT_BUTTON_PLAY_FROM_CURSOR ), - EditHelpLine( "Play whole song", EDIT_BUTTON_PLAY_FROM_START ), - EditHelpLine( "Record", EDIT_BUTTON_RECORD_SELECTION ), - EditHelpLine( "Set selection", EDIT_BUTTON_LAY_SELECT ), - EditHelpLine( "Next/prev steps of same StepsType", EDIT_BUTTON_OPEN_NEXT_STEPS, EDIT_BUTTON_OPEN_PREV_STEPS ), - EditHelpLine( "Decrease/increase BPM at cur beat", EDIT_BUTTON_BPM_DOWN, EDIT_BUTTON_BPM_UP ), - EditHelpLine( "Decrease/increase stop at cur beat", EDIT_BUTTON_STOP_DOWN, EDIT_BUTTON_STOP_UP ), - EditHelpLine( "Decrease/increase delay at cur beat", EDIT_BUTTON_DELAY_DOWN, EDIT_BUTTON_DELAY_UP ), - EditHelpLine( "Decrease/increase music offset", EDIT_BUTTON_OFFSET_DOWN, EDIT_BUTTON_OFFSET_UP ), - EditHelpLine( "Decrease/increase sample music start", EDIT_BUTTON_SAMPLE_START_DOWN, EDIT_BUTTON_SAMPLE_START_UP ), - EditHelpLine( "Decrease/increase sample music length", EDIT_BUTTON_SAMPLE_LENGTH_DOWN, EDIT_BUTTON_SAMPLE_LENGTH_UP ), - EditHelpLine( "Play sample music", EDIT_BUTTON_PLAY_SAMPLE_MUSIC ), - EditHelpLine( "Add/Edit Background Change", EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU ), - EditHelpLine( "Insert beat and shift down", EDIT_BUTTON_INSERT ), - EditHelpLine( "Shift BPM changes and stops down one beat", EDIT_BUTTON_INSERT_SHIFT_PAUSES ), - EditHelpLine( "Delete beat and shift up", EDIT_BUTTON_DELETE ), - EditHelpLine( "Shift BPM changes and stops up one beat", EDIT_BUTTON_DELETE_SHIFT_PAUSES ), - EditHelpLine( "Cycle between tap notes", EDIT_BUTTON_CYCLE_TAP_LEFT, EDIT_BUTTON_CYCLE_TAP_RIGHT ), - EditHelpLine( "Add to/remove from right half", EDIT_BUTTON_RIGHT_SIDE ), - EditHelpLine( "Switch Timing", EDIT_BUTTON_SWITCH_TIMINGS ), - EditHelpLine( "Switch player (Routine only)", EDIT_BUTTON_SWITCH_PLAYERS ), -}; - -static bool IsMapped( EditButton eb, const MapEditToDI &editmap ) -{ - for( int s=0; s &vs ) -{ - FOREACH( RString, vs, s ) - ProcessKeyName( *s ); - - sort( vs.begin(), vs.end() ); - vector::iterator toDelete = unique( vs.begin(), vs.end() ); - vs.erase(toDelete, vs.end()); -} - -static RString GetDeviceButtonsLocalized( const vector &veb, const MapEditToDI &editmap ) -{ - vector vsPress; - vector vsHold; - FOREACH_CONST( EditButton, veb, eb ) - { - if( !IsMapped( *eb, editmap ) ) - continue; - - for( int s=0; sGetLocalizedInputString(diPress)) ); - if( diHold.IsValid() ) - vsHold.push_back( Capitalize(INPUTMAN->GetLocalizedInputString(diHold)) ); - } - } - - ProcessKeyNames( vsPress ); - ProcessKeyNames( vsHold ); - - RString s = join("/",vsPress); - if( !vsHold.empty() ) - s = join("/",vsHold) + " + " + s; - return s; -} - -void ScreenEdit::DoStepAttackMenu() -{ - const 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("%g -> %g (%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"); - -void ScreenEdit::DoKeyboardTrackMenu() -{ - g_KeysoundTrack.rows.clear(); - vector &kses = m_pSong->m_vsKeysoundFile; - - vector choices; - FOREACH(RString, kses, ks) - { - choices.push_back(*ks); - } - choices.push_back(NEWKEYSND); - choices.push_back(NO_KEYSND); - int numKeysounds = kses.size(); - for (int i = 0; i < m_NoteDataEdit.GetNumTracks(); ++i) - { - const TapNote &tn = m_NoteDataEdit.GetTapNote(i, this->GetRow()); - int keyIndex = tn.iKeysoundIndex; - if (keyIndex == -1) - { - keyIndex = numKeysounds; - } - - g_KeysoundTrack.rows.push_back(MenuRowDef(i, ssprintf(TRACK_NUM.GetValue(), i + 1), - true, EditMode_Full, false, false, keyIndex, choices)); - } - g_KeysoundTrack.rows.push_back(MenuRowDef(m_NoteDataEdit.GetNumTracks(), "Remove Keysound", - true, EditMode_Full, false, false, 0, kses)); - - EditMiniMenu(&g_KeysoundTrack, SM_BackFromKeysoundTrack); -} - -void ScreenEdit::DoHelp() -{ - g_EditHelp.rows.clear(); - - for( unsigned i=0; iGetString( "EditHelpDescription", hl.szEnglishDescription ); - - // TODO: Better way of hiding routine only key on non-routine. - if( hl.veb[0] == EDIT_BUTTON_SWITCH_PLAYERS && m_InputPlayerNumber == PLAYER_INVALID ) - { - continue; - } - - g_EditHelp.rows.push_back( MenuRowDef( -1, sDescription, false, EditMode_Practice, false, false, 0, sButtons ) ); - } - - EditMiniMenu( &g_EditHelp ); -} - -// lua start -#include "LuaBinding.h" - -/** @brief Allow Lua to have access to ScreenEdit. */ -class LunaScreenEdit: public Luna -{ -public: - DEFINE_METHOD( GetEditState, GetEditState() ) - LunaScreenEdit() - { - ADD_METHOD( GetEditState ); - } -}; - -LUA_REGISTER_DERIVED_CLASS( ScreenEdit, ScreenWithMenuElements ) - -/* - * (c) 2001-2004 Chris Danford - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ +#include "global.h" +#include +#include +#include "ScreenEdit.h" +#include "ActorUtil.h" +#include "AdjustSync.h" +#include "BackgroundUtil.h" +#include "CommonMetrics.h" +#include "Foreach.h" +#include "GameManager.h" +#include "GamePreferences.h" +#include "GameSoundManager.h" +#include "GameState.h" +#include "InputEventPlus.h" +#include "InputMapper.h" +#include "LocalizedString.h" +#include "NoteDataUtil.h" +#include "NoteSkinManager.h" +#include "NoteTypes.h" +#include "NotesWriterSM.h" +#include "PrefsManager.h" +#include "RageSoundManager.h" +#include "RageInput.h" +#include "RageLog.h" +#include "ScreenDimensions.h" +#include "ScreenManager.h" +#include "ScreenMiniMenu.h" +#include "ScreenPrompt.h" +#include "ScreenSaveSync.h" +#include "ScreenTextEntry.h" +#include "SongManager.h" +#include "SongUtil.h" +#include "StepsUtil.h" +#include "Style.h" +#include "ThemeManager.h" +#include "ThemeMetric.h" +#include "TimingData.h" +#include "Game.h" +#include "RageSoundReader.h" + +static Preference g_iDefaultRecordLength( "DefaultRecordLength", 4 ); +static Preference g_bEditorShowBGChangesPlay( "EditorShowBGChangesPlay", true ); + +/** @brief How long must the button be held to generate a hold in record mode? */ +const float RECORD_HOLD_SECONDS = 0.3f; + +#define PLAYER_X (SCREEN_CENTER_X) +#define PLAYER_Y (SCREEN_CENTER_Y) +#define PLAYER_HEIGHT (360) +#define PLAYER_Y_STANDARD (PLAYER_Y-PLAYER_HEIGHT/2) + +#define EDIT_X (SCREEN_CENTER_X) +#define EDIT_Y (PLAYER_Y) + +#define RECORD_X (SCREEN_CENTER_X) +#define RECORD_Y (SCREEN_CENTER_Y) + +#define PLAY_RECORD_HELP_TEXT THEME->GetString(m_sName,"PlayRecordHelpText") +#define EDIT_HELP_TEXT THEME->GetString(m_sName,"EditHelpText") + +AutoScreenMessage( SM_UpdateTextInfo ); +AutoScreenMessage( SM_BackFromMainMenu ); +AutoScreenMessage( SM_BackFromAreaMenu ); +AutoScreenMessage( SM_BackFromAlterMenu ); +AutoScreenMessage( SM_BackFromStepsInformation ); +AutoScreenMessage( SM_BackFromStepsData ); +AutoScreenMessage( SM_BackFromOptions ); +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 ); +AutoScreenMessage( SM_BackFromCourseModeMenu ); +AutoScreenMessage( SM_BackFromKeysoundTrack ); +AutoScreenMessage( SM_BackFromNewKeysound ); +AutoScreenMessage( SM_DoRevertToLastSave ); +AutoScreenMessage( SM_DoRevertFromDisk ); +AutoScreenMessage( SM_BackFromTimingDataInformation ); +AutoScreenMessage( SM_BackFromDifficultyMeterChange ); +AutoScreenMessage( SM_BackFromBeat0Change ); +AutoScreenMessage( SM_BackFromBPMChange ); +AutoScreenMessage( SM_BackFromStopChange ); +AutoScreenMessage( SM_BackFromDelayChange ); +AutoScreenMessage( SM_BackFromTimeSignatureChange ); +AutoScreenMessage( SM_BackFromTickcountChange ); +AutoScreenMessage( SM_BackFromComboChange ); +AutoScreenMessage( SM_BackFromLabelChange ); +AutoScreenMessage( SM_BackFromWarpChange ); +AutoScreenMessage( SM_BackFromSpeedPercentChange ); +AutoScreenMessage( SM_BackFromSpeedWaitChange ); +AutoScreenMessage( SM_BackFromSpeedModeChange ); +AutoScreenMessage( SM_BackFromScrollChange ); +AutoScreenMessage( SM_BackFromFakeChange ); +AutoScreenMessage( SM_DoEraseStepTiming ); +AutoScreenMessage( SM_DoSaveAndExit ); +AutoScreenMessage( SM_DoExit ); +AutoScreenMessage( SM_SaveSuccessful ); +AutoScreenMessage( SM_SaveFailed ); + +static const char *EditStateNames[] = { + "Edit", + "Record", + "RecordPaused", + "Playing" +}; +XToString( EditState ); +LuaXType( EditState ); + +void ScreenEdit::InitEditMappings() +{ + m_EditMappingsDeviceInput.Clear(); + + // Common mappings: + switch( EDIT_MODE.GetValue() ) + { + case EditMode_Practice: + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_PREV_MEASURE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); + m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_PREV_MEASURE][0] = GAME_BUTTON_UP; + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_NEXT_MEASURE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); + m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_NEXT_MEASURE][0] = GAME_BUTTON_DOWN; + break; + default: + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_LINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); + m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_UP_LINE][0] = GAME_BUTTON_UP; + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_LINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); + m_EditMappingsMenuButton.button[EDIT_BUTTON_SCROLL_DOWN_LINE][0] = GAME_BUTTON_DOWN; + break; + } + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_PAGE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGUP); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_PAGE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SEMICOLON); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_PAGE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGDN); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_PAGE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SQUOTE); + + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_UP_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_UP_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGUP); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_UP_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SEMICOLON); + + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_DOWN_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_DOWN_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_TS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PGDN); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_DOWN_TS][1] = DeviceInput(DEVICE_KEYBOARD, KEY_SQUOTE); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_HOME][0] = DeviceInput(DEVICE_KEYBOARD, KEY_HOME); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_END][0] = DeviceInput(DEVICE_KEYBOARD, KEY_END); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PERIOD); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_COMMA); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SEGMENT_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_PERIOD); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_NEXT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SEGMENT_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_COMMA); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SEGMENT_PREV][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_SPACE); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_FROM_START][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_START][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_START][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_PLAY_FROM_CURSOR][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); + + // EditMode-specific mappings + switch( EDIT_MODE.GetValue() ) + { + case EditMode_Practice: + // Left = Zoom out (1x, 2x, 4x) + // Right = Zoom in + m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); + m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); + + // F1 = Show help popup + m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); + + // Esc = Show Edit Menu + m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; + m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; + + + // Escape, Enter = exit play/record + m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_PlayMappingsMenuButton.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK; + return; + case EditMode_CourseMods: + // Left/Right = Snap to Next/Prev + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); + + // v = course playback menu + // m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADD_COURSE_MODS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Co); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_COURSE_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); + + // F1 = Show help popup + m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); + + // Esc = Show Edit Menu + m_EditMappingsDeviceInput.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; + m_EditMappingsMenuButton.button [EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; + + // Escape, Enter = exit play/record + m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_PlayMappingsDeviceInput.button [EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_PlayMappingsMenuButton.button [EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_START; + return; + + case EditMode_Full: + // F4 = Show timing menu + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_TIMING_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F4); + + /* Don't allow F5/F6 in home mode. It breaks the "delay creation until first save" logic. */ + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_PREV_STEPS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F5); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_NEXT_STEPS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F6); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_BPM_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F7); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_BPM_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F8); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_STOP_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F9); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_STOP_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F10); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELAY_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F9); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELAY_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F10); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELAY_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OFFSET_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F11); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OFFSET_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F12); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_START_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RBRACKET); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_START_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LBRACKET); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_LENGTH_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RBRACKET); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LBRACKET); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAMPLE_LENGTH_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SAMPLE_MUSIC][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cl); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); + 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_OPEN_STEP_ATTACK_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cv); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_INSERT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_INSERT_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_INSERT_SHIFT_PAUSES][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELETE_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DEL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELETE_SHIFT_PAUSES][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_DELETE_SHIFT_PAUSES][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + break; + default: break; + } + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_0][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C1); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_1][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C2); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_2][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C3); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_3][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C4); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_4][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C5); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_5][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C6); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_6][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C7); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_7][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C8); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_8][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C9); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_COLUMN_9][0] = DeviceInput(DEVICE_KEYBOARD, KEY_C0); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_RIGHT_SIDE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_RIGHT_SIDE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_TAP_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cn); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_TAP_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cm); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cn); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_LEFT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cm); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_CYCLE_SEGMENT_RIGHT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_UP); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_UP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button [EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DOWN); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_DOWN][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SCROLL_SPEED_DOWN][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SCROLL_SELECT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_NEXT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LEFT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SNAP_PREV][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RIGHT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_EDIT_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][0] = GAME_BUTTON_START; + m_EditMappingsMenuButton.button[EDIT_BUTTON_OPEN_EDIT_MENU][1] = GAME_BUTTON_BACK; + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_AREA_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_ALTER_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Ca); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_INPUT_HELP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F1); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cb); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_INSERT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_INSERT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_BACKSLASH); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_DELETE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_DEL); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADJUST_FINE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); + m_EditMappingsDeviceInput.button[EDIT_BUTTON_ADJUST_FINE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_Cs); + #if defined(MACOSX) + /* use cmd */ + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LMETA); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RMETA); + #else + /* use ctrl */ + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_EditMappingsDeviceInput.hold[EDIT_BUTTON_SAVE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + #endif + + m_EditMappingsDeviceInput.button[EDIT_BUTTON_UNDO][1] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); + + // Switch players, if it makes sense to do so. + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SWITCH_PLAYERS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_SLASH); + + // Allow song and step timing to be swapped. + m_EditMappingsDeviceInput.button[EDIT_BUTTON_SWITCH_TIMINGS][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Ct); + + m_PlayMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_PlayMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_PlayMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_BACK; + m_PlayMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_START; + + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LSHIFT); + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_LAY_ROLL][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RSHIFT); + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_REMOVE_NOTE][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LALT); + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_REMOVE_NOTE][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RALT); + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_RecordMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = DeviceInput(DEVICE_KEYBOARD, KEY_ENTER); + m_RecordMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = GAME_BUTTON_BACK; + m_RecordMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_START; + + m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_PLAY_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cp); + m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); + m_RecordPausedMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][0] = DeviceInput(DEVICE_KEYBOARD, KEY_LCTRL); + m_RecordPausedMappingsDeviceInput.hold[EDIT_BUTTON_RECORD_SELECTION][1] = DeviceInput(DEVICE_KEYBOARD, KEY_RCTRL); + m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RECORD_FROM_CURSOR][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cr); + m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC); + m_RecordPausedMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK; + m_RecordPausedMappingsDeviceInput.button[EDIT_BUTTON_UNDO][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cu); +} + +/* Given a DeviceInput that was just depressed, return an active edit function. */ +EditButton ScreenEdit::DeviceToEdit( const DeviceInput &DeviceI ) const +{ + ASSERT( DeviceI.IsValid() ); + + const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); + + /* First, search to see if a key that requires a modifier is pressed. */ + FOREACH_EditButton( e ) + { + for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) + { + if( pCurrentMap->button[e][slot] == DeviceI && pCurrentMap->hold[e][0].IsValid() ) + { + /* The button maps to this function, and has one or more shift modifiers attached. */ + for( int holdslot = 0; holdslot < NUM_EDIT_TO_DEVICE_SLOTS; ++holdslot ) + { + DeviceInput hDI = pCurrentMap->hold[e][holdslot]; + if( hDI.IsValid() && INPUTFILTER->IsBeingPressed(hDI) ) + return e; + } + } + } + } + + /* No shifted keys matched. See if any unshifted inputs are bound to this key. */ + FOREACH_EditButton(e) + { + for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) + { + if( pCurrentMap->button[e][slot] == DeviceI && !pCurrentMap->hold[e][0].IsValid() ) + { + /* The button maps to this function. */ + return e; + } + } + } + + return EditButton_Invalid; +} + +/* Given a DeviceInput that was just depressed, return an active edit function. */ +EditButton ScreenEdit::MenuButtonToEditButton( GameButton MenuI ) const +{ + const MapEditButtonToMenuButton *pCurrentMap = GetCurrentMenuButtonMap(); + + FOREACH_EditButton(e) + { + for( int slot = 0; slot < NUM_EDIT_TO_MENU_SLOTS; ++slot ) + { + if( pCurrentMap->button[e][slot] == MenuI ) + { + /* The button maps to this function. */ + return e; + } + } + } + + return EditButton_Invalid; +} + +/* If DeviceI was just pressed, return true if button is triggered. (More than one + * function may be mapped to a key.) */ +bool ScreenEdit::EditPressed( EditButton button, const DeviceInput &DeviceI ) +{ + ASSERT( DeviceI.IsValid() ); + + const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); + + /* First, search to see if a key that requires a modifier is pressed. */ + bool bPrimaryButtonPressed = false; + for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) + { + if( pCurrentMap->button[button][slot] == DeviceI ) + bPrimaryButtonPressed = true; + } + + if( !bPrimaryButtonPressed ) + return false; + + /* The button maps to this function. Does the function has one or more shift modifiers attached? */ + if( !pCurrentMap->hold[button][0].IsValid() ) + return true; + + for( int holdslot = 0; holdslot < NUM_EDIT_TO_DEVICE_SLOTS; ++holdslot ) + { + DeviceInput hDI = pCurrentMap->hold[button][holdslot]; + if( INPUTFILTER->IsBeingPressed(hDI) ) + return true; + } + + /* No shifted keys matched. */ + return false; +} + +bool ScreenEdit::EditToDevice( EditButton button, int iSlotNum, DeviceInput &DeviceI ) const +{ + ASSERT( iSlotNum < NUM_EDIT_TO_DEVICE_SLOTS ); + const MapEditToDI *pCurrentMap = GetCurrentDeviceInputMap(); + if( pCurrentMap->button[button][iSlotNum].IsValid() ) + DeviceI = pCurrentMap->button[button][iSlotNum]; + else if( pCurrentMap->hold[button][iSlotNum].IsValid() ) + DeviceI = pCurrentMap->hold[button][iSlotNum]; + return DeviceI.IsValid(); +} + +bool ScreenEdit::EditIsBeingPressed( EditButton button ) const +{ + for( int slot = 0; slot < NUM_EDIT_TO_DEVICE_SLOTS; ++slot ) + { + DeviceInput DeviceI; + if( EditToDevice( button, slot, DeviceI ) && INPUTFILTER->IsBeingPressed(DeviceI) ) + return true; + } + + return false; +} + +const MapEditToDI *ScreenEdit::GetCurrentDeviceInputMap() const +{ + switch( m_EditState ) + { + DEFAULT_FAIL( m_EditState ); + case STATE_EDITING: return &m_EditMappingsDeviceInput; + case STATE_PLAYING: return &m_PlayMappingsDeviceInput; + case STATE_RECORDING: return &m_RecordMappingsDeviceInput; + case STATE_RECORDING_PAUSED: return &m_RecordPausedMappingsDeviceInput; + } +} + +const MapEditButtonToMenuButton *ScreenEdit::GetCurrentMenuButtonMap() const +{ + switch( m_EditState ) + { + DEFAULT_FAIL( m_EditState ); + case STATE_EDITING: return &m_EditMappingsMenuButton; + case STATE_PLAYING: return &m_PlayMappingsMenuButton; + case STATE_RECORDING: return &m_RecordMappingsMenuButton; + case STATE_RECORDING_PAUSED: return &m_RecordPausedMappingsMenuButton; + } +} + + +static MenuDef g_EditHelp( + "ScreenMiniMenuEditHelp" + // 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 + +static MenuDef g_MainMenu( + "ScreenMiniMenuMainMenu", + MenuRowDef(ScreenEdit::play_whole_song, + "Play whole song", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::play_current_beat_to_end, + "Play current beat to end", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::save, + "Save", + true, EditMode_Home, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::play_selection, + "Play selection", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::set_selection_start, + "Set selection start", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::set_selection_end, + "Set selection end", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::revert_to_last_save, + "Revert to last save", + true, EditMode_Home, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::revert_from_disk, + "Revert from disk", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::options, + "Editor options", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::edit_song_info, + "Edit song info", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::edit_steps_information, + "Edit steps information", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::edit_timing_data, + "Edit Timing Data", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::view_steps_data, + "View steps data", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::play_preview_music, + "Play preview music", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::exit, + "Exit Edit Mode", + true, EditMode_Practice, true, true, 0, NULL ) +); + +static MenuDef g_AlterMenu( + "ScreenMiniMenuAlterMenu", + MenuRowDef(ScreenEdit::cut, + "Cut", + true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::copy, + "Copy", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::clear, "Clear area", true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::quantize, "Quantize", true, + EditMode_Practice, true, true, 0, + "4th","8th","12th","16th","24th","32nd","48th","64th","192nd"), + MenuRowDef(ScreenEdit::turn, "Turn", true, + EditMode_Practice, true, true, 0, "Left","Right","Mirror","Backwards","Shuffle","SuperShuffle" ), + MenuRowDef(ScreenEdit::transform, "Transform", true, + EditMode_Practice, true, true, 0, "NoHolds","NoMines","Little","Wide", + "Big","Quick","Skippy","Mines","Echo","Stomp","Planted","Floored", + "Twister","NoJumps","NoHands","NoQuads","NoStretch" ), + MenuRowDef(ScreenEdit::alter, "Alter", true, + EditMode_Practice, true, true, 0, "Autogen To Fill Width","Backwards","Swap Sides", + "Copy Left To Right","Copy Right To Left","Clear Left","Clear Right", + "Collapse To One","Collapse Left","Shift Left","Shift Right" ), + MenuRowDef(ScreenEdit::tempo, "Tempo", true, + EditMode_Full, true, true, 0, "Compress 2x","Compress 3->2", + "Compress 4->3","Expand 3->4","Expand 2->3","Expand 2x" ), + MenuRowDef(ScreenEdit::play, + "Play selection", + true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::record, + "Record in selection", + true, + EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::preview_designation, + "Designate as Music Preview", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_pause, + "Convert selection to pause", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_delay, + "Convert selection to delay", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_warp, + "Convert selection to warp", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_fake, + "Convert selection to fake", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_to_attack, + "Convert selection to attack", + true, + EditMode_Full, true, true, 0, NULL), + MenuRowDef(ScreenEdit::routine_invert_notes, + "Invert notes' player", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::routine_mirror_1_to_2, + "Mirror Player 1 to 2", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::routine_mirror_2_to_1, + "Mirror Player 2 to 1", + true, + EditMode_Full, true, true, 0, NULL ) +); + +static MenuDef g_AreaMenu( + "ScreenMiniMenuAreaMenu", + MenuRowDef(ScreenEdit::paste_at_current_beat, + "Paste at current beat", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::paste_at_begin_marker, + "Paste at begin marker", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::insert_and_shift, + "Insert beat and shift down", + true, EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), + MenuRowDef(ScreenEdit::delete_and_shift, + "Delete beat and shift up", + true, EditMode_Practice, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), + MenuRowDef(ScreenEdit::shift_pauses_forward, + "Shift all timing changes down", + true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), + MenuRowDef(ScreenEdit::shift_pauses_backward, + "Shift all timing changes up", + true, EditMode_Full, true, true, 0, "4th","8th","12th","16th","24th","32nd","48th","64th","192nd" ), + MenuRowDef(ScreenEdit::convert_pause_to_beat, + "Convert pause to beats", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::convert_delay_to_beat, + "Convert delay to beats", + true, + EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::last_second_at_beat, + "Designate last second at current beat", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::undo, + "Undo", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::clear_clipboard, + "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) + +); + +static MenuDef g_StepsInformation( + "ScreenMiniMenuStepsInformation", + MenuRowDef(ScreenEdit::difficulty, + "Difficulty", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::meter, + "Meter", + true, EditMode_Practice, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::predict_meter, + "Predicted Meter", + false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::chartname, + "Chart Name", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::description, + "Description", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::chartstyle, + "Chart Style", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::step_credit, + "Step Author", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::step_display_bpm, + "Display BPM", + true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), + MenuRowDef(ScreenEdit::step_min_bpm, + "Min BPM", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::step_max_bpm, + "Max BPM", + true, EditMode_Full, true, true, 0, NULL ) +); + +static MenuDef g_StepsData( + "ScreenMiniMenuStepsData", + MenuRowDef( ScreenEdit::tap_notes, "Tap Steps", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::jumps, "Jumps", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::hands, "Hands", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::quads, "Quads", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::holds, "Holds", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::mines, "Mines", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::rolls, "Rolls", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::lifts, "Lifts", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::fakes, "Fakes", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::stream, "Stream", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::voltage, "Voltage", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::air, "Air", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::freeze, "Freeze", false, EditMode_Full, true, true, 0, NULL ), + MenuRowDef( ScreenEdit::chaos, "Chaos", false, EditMode_Full, true, true, 0, NULL ) +); + +static MenuDef g_SongInformation( + "ScreenMiniMenuSongInformation", + MenuRowDef(ScreenEdit::main_title, + "Main title", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::sub_title, + "Sub title", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::artist, + "Artist", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::genre, + "Genre", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::credit, + "Credit", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::main_title_transliteration, + "Main title transliteration", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::sub_title_transliteration, + "Sub title transliteration", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::artist_transliteration, + "Artist transliteration", + true, EditMode_Practice, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::last_second_hint, + "Last second hint", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::preview_start, + "Preview Start", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::preview_length, + "Preview Length", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::display_bpm, + "Display BPM", + true, EditMode_Full, true, true, 0, "Actual", "Specified", "Random" ), + MenuRowDef(ScreenEdit::min_bpm, + "Min BPM", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::max_bpm, + "Max BPM", + true, EditMode_Full, true, true, 0, NULL ) +); + + +static MenuDef g_TimingDataInformation( + "ScreenMiniMenuTimingDataInformation", + MenuRowDef(ScreenEdit::beat_0_offset, + "Beat 0 Offset", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::bpm, + "Edit BPM change", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::stop, + "Edit stop", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::delay, + "Edit delay", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::time_signature, + "Edit time signature", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::label, + "Edit label", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::tickcount, + "Edit tickcount", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::combo, + "Edit combo", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::warp, + "Edit warp", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::speed_percent, + "Edit speed (percent)", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::speed_wait, + "Edit speed (wait)", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::speed_mode, + "Edit speed (mode)", + true, EditMode_Full, true, true, 0, "Beats", "Seconds" ), + MenuRowDef(ScreenEdit::scroll, + "Edit scrolling factor", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::fake, + "Edit fake", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::copy_full_timing, + "Copy timing data", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::paste_full_timing, + "Paste timing data", + true, EditMode_Full, true, true, 0, NULL ), + MenuRowDef(ScreenEdit::erase_step_timing, + "Erase step timing", + true, EditMode_Full, true, true, 0, NULL ) +); + +// XXX: What are these enums used for? +enum +{ + song_bganimation, + song_movie, + song_bitmap, + global_bganimation, + global_movie, + global_movie_song_group, + global_movie_song_group_and_genre, + dynamic_random, + baked_random, + none +}; +static bool EnabledIfSet1SongBGAnimation(); +static bool EnabledIfSet1SongMovie(); +static bool EnabledIfSet1SongBitmap(); +static bool EnabledIfSet1GlobalBGAnimation(); +static bool EnabledIfSet1GlobalMovie(); +static bool EnabledIfSet1GlobalMovieSongGroup(); +static bool EnabledIfSet1GlobalMovieSongGroupAndGenre(); +static bool EnabledIfSet2SongBGAnimation(); +static bool EnabledIfSet2SongMovie(); +static bool EnabledIfSet2SongBitmap(); +static bool EnabledIfSet2GlobalBGAnimation(); +static bool EnabledIfSet2GlobalMovie(); +static bool EnabledIfSet2GlobalMovieSongGroup(); +static bool EnabledIfSet2GlobalMovieSongGroupAndGenre(); +static MenuDef g_BackgroundChange( + "ScreenMiniMenuBackgroundChange", + MenuRowDef(ScreenEdit::layer, + "Layer", + false, + EditMode_Full, true, false, 0, "" ), + MenuRowDef(ScreenEdit::rate, + "Rate", + true, + EditMode_Full, true, false, 10, "0%","10%","20%","30%","40%","50%", + "60%","70%","80%","90%","100%","120%","140%","160%","180%","200%", + "220%","240%","260%","280%","300%","350%","400%" ), + MenuRowDef(ScreenEdit::transition, + "Force Transition", + true, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::effect, + "Force Effect", + true, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::color1, + "Force Color 1", + true, + EditMode_Full, true, false, 0, "-","#FFFFFF","#808080","#FFFFFF80", + "#000000","#FF0000","#00FF00","#0000FF","#FFFF00","#00FFFF","#FF00FF" ), + MenuRowDef(ScreenEdit::color2, + "Force Color 2", + true, + EditMode_Full, true, false, 0, "-","#FFFFFF","#808080","#FFFFFF80", + "#000000","#FF0000","#00FF00","#0000FF","#FFFF00","#00FFFF","#FF00FF" ), + MenuRowDef(ScreenEdit::file1_type, + "File1 Type", + true, + EditMode_Full, true, true, 0, "Song BGAnimation", "Song Movie", + "Song Bitmap", "Global BGAnimation", "Global Movie", + "Global Movie from Song Group", "Global Movie from Song Group and Genre", + "Dynamic Random", "Baked Random", "None" ), + MenuRowDef(ScreenEdit::file1_song_bganimation, + "File1 Song BGAnimation", + EnabledIfSet1SongBGAnimation, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_song_movie, + "File1 Song Movie", + EnabledIfSet1SongMovie, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_song_still, + "File1 Song Still", + EnabledIfSet1SongBitmap, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_global_bganimation, + "File1 Global BGAnimation", + EnabledIfSet1GlobalBGAnimation, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_global_movie, + "File1 Global Movie", + EnabledIfSet1GlobalMovie, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_global_movie_song_group, + "File1 Global Movie (Group)", + EnabledIfSet1GlobalMovieSongGroup, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file1_global_movie_song_group_and_genre, + "File1 Global Movie (Group + Genre)", + EnabledIfSet1GlobalMovieSongGroupAndGenre, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_type, + "File2 Type", + true, + EditMode_Full, true, true, 0, "Song BGAnimation", "Song Movie", + "Song Bitmap", "Global BGAnimation", "Global Movie", + "Global Movie from Song Group", "Global Movie from Song Group and Genre", + "Dynamic Random", "Baked Random", "None" ), + MenuRowDef(ScreenEdit::file2_song_bganimation, + "File2 Song BGAnimation", + EnabledIfSet2SongBGAnimation, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_song_movie, + "File2 Song Movie", + EnabledIfSet2SongMovie, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_song_still, + "File2 Song Still", + EnabledIfSet2SongBitmap, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_global_bganimation, + "File2 Global BGAnimation", + EnabledIfSet2GlobalBGAnimation, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_global_movie, + "File2 Global Movie", + EnabledIfSet2GlobalMovie, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_global_movie_song_group, + "File2 Global Movie (Group)", + EnabledIfSet2GlobalMovieSongGroup, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::file2_global_movie_song_group_and_genre, + "File2 Global Movie (Group + Genre)", + EnabledIfSet2GlobalMovieSongGroupAndGenre, + EditMode_Full, true, false, 0, NULL ), + MenuRowDef(ScreenEdit::delete_change, + "Remove Change", + true, + EditMode_Full, true, true, 0, NULL ) +); +static bool EnabledIfSet1SongBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_bganimation && !g_BackgroundChange.rows[ScreenEdit::file1_song_bganimation].choices.empty(); } +static bool EnabledIfSet1SongMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_movie && !g_BackgroundChange.rows[ScreenEdit::file1_song_movie].choices.empty(); } +static bool EnabledIfSet1SongBitmap() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == song_bitmap && !g_BackgroundChange.rows[ScreenEdit::file1_song_still].choices.empty(); } +static bool EnabledIfSet1GlobalBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_bganimation && !g_BackgroundChange.rows[ScreenEdit::file1_global_bganimation].choices.empty(); } +static bool EnabledIfSet1GlobalMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie].choices.empty(); } +static bool EnabledIfSet1GlobalMovieSongGroup() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie_song_group && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie_song_group].choices.empty(); } +static bool EnabledIfSet1GlobalMovieSongGroupAndGenre() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file1_type] == global_movie_song_group_and_genre && !g_BackgroundChange.rows[ScreenEdit::file1_global_movie_song_group_and_genre].choices.empty(); } +static bool EnabledIfSet2SongBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_bganimation && !g_BackgroundChange.rows[ScreenEdit::file2_song_bganimation].choices.empty(); } +static bool EnabledIfSet2SongMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_movie && !g_BackgroundChange.rows[ScreenEdit::file2_song_movie].choices.empty(); } +static bool EnabledIfSet2SongBitmap() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == song_bitmap && !g_BackgroundChange.rows[ScreenEdit::file2_song_still].choices.empty(); } +static bool EnabledIfSet2GlobalBGAnimation() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_bganimation && !g_BackgroundChange.rows[ScreenEdit::file2_global_bganimation].choices.empty(); } +static bool EnabledIfSet2GlobalMovie() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie].choices.empty(); } +static bool EnabledIfSet2GlobalMovieSongGroup() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie_song_group && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie_song_group].choices.empty(); } +static bool EnabledIfSet2GlobalMovieSongGroupAndGenre() { return ScreenMiniMenu::s_viLastAnswers[ScreenEdit::file2_type] == global_movie_song_group_and_genre && !g_BackgroundChange.rows[ScreenEdit::file2_global_movie_song_group_and_genre].choices.empty(); } + +static RString GetOneBakedRandomFile( Song *pSong, bool bTryGenre = true ) +{ + vector vsPaths; + vector vsNames; + BackgroundUtil::GetGlobalRandomMovies( + pSong, + "", + vsPaths, + vsNames, + bTryGenre ); + + return vsNames.empty() ? RString() : vsNames[RandomInt(vsNames.size())]; +} + +static MenuDef g_InsertTapAttack( + "ScreenMiniMenuInsertTapAttack", + MenuRowDef(-1, + "Duration seconds", + true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), // TODO: Replace + MenuRowDef(-1, + "Set modifiers", + true, EditMode_Practice, true, true, 0, "Press Start" ) +); + +static MenuDef g_InsertCourseAttack( + "ScreenMiniMenuInsertCourseAttack", + MenuRowDef(ScreenEdit::duration, + "Duration seconds", + true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), + MenuRowDef(ScreenEdit::set_mods, + "Set modifiers", + true, EditMode_Practice, true, true, 0, "Press Start" ), + MenuRowDef(ScreenEdit::remove, + "Remove", + true, EditMode_Practice, true, true, 0, "Press Start" ) +); + +static MenuDef g_InsertStepAttack( + "ScreenMiniMenuInsertCourseAttack", + MenuRowDef(ScreenEdit::sa_duration, + "Duration seconds", + true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ), + MenuRowDef(ScreenEdit::sa_set_mods, + "Set modifiers", + true, EditMode_Practice, true, true, 0, "Press Start" ), + MenuRowDef(ScreenEdit::sa_remove, + "Remove", + true, EditMode_Practice, true, true, 0, "Press Start" )); + +static MenuDef g_CourseMode( + "ScreenMiniMenuCourseDisplay", + MenuRowDef( -1, "Play mods from course", true, EditMode_Practice, true, false, 0, NULL ) +); + +// HACK: need to remember the track we're inserting on so that we can lay the +// attack note after coming back from menus. +static int g_iLastInsertTapAttackTrack = -1; +static float g_fLastInsertAttackDurationSeconds = -1; +static float g_fLastInsertAttackPositionSeconds = -1; +static BackgroundLayer g_CurrentBGChangeLayer = BACKGROUND_LAYER_Invalid; + +static void SetDefaultEditorNoteSkin( size_t num, RString &sNameOut, RString &defaultValueOut ) +{ + sNameOut = ssprintf( "EditorNoteSkinP%d", int(num + 1) ); + + // XXX: We need more supported noteskins. + defaultValueOut = "default"; +} + +static Preference1D EDITOR_NOTE_SKINS( SetDefaultEditorNoteSkin, NUM_PLAYERS ); + +static ThemeMetric EDIT_MODIFIERS ("ScreenEdit","EditModifiers"); + +static ThemeMetric LOOP_ON_CHART_END ("ScreenEdit","LoopOnChartEnd"); + +REGISTER_SCREEN_CLASS( ScreenEdit ); + +void ScreenEdit::Init() +{ + m_pSoundMusic = NULL; + + GAMESTATE->m_bIsUsingStepTiming = false; + GAMESTATE->m_bInStepEditor = true; + + SubscribeToMessage( "Judgment" ); + + ASSERT( GAMESTATE->m_pCurSong != NULL ); + ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); + + EDIT_MODE.Load( m_sName, "EditMode" ); + ScreenWithMenuElements::Init(); + + InitEditMappings(); + + currentCycleSegment = SEGMENT_LABEL; + + // save the originals for reverting later + CopyToLastSave(); + + m_CurrentAction = MAIN_MENU_CHOICE_INVALID; + m_InputPlayerNumber = PLAYER_INVALID; + + if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides ) + m_InputPlayerNumber = PLAYER_1; + + FOREACH_PlayerNumber( p ) + GAMESTATE->m_bSideIsJoined[p] = false; + GAMESTATE->m_bSideIsJoined[PLAYER_1] = true; + + m_pSong = GAMESTATE->m_pCurSong; + m_pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + + /* The user will most likely switch into Step Timing after laying down + some initial notes. It also throws off many people at first glance. + */ + if( m_pSong->IsStepsUsingDifferentTiming(m_pSteps) ) + GAMESTATE->m_bIsUsingStepTiming = true; + + m_bReturnToRecordMenuAfterPlay = false; + m_fBeatToReturnTo = 0; + + m_selectedTap = TAP_ORIGINAL_TAP; + + GAMESTATE->m_bGameplayLeadIn.Set( true ); + GAMESTATE->m_EditMode = EDIT_MODE.GetValue(); + SetBeat(0); + m_fTrailingBeat = 0; + + 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 ); + m_SnapDisplay.Load(); + // xxx: hardcoded command -aj + m_SnapDisplay.SetZoom( 0.5f ); + this->AddChild( &m_SnapDisplay ); + + // We keep track of this bit of state so that when the user is in Edit/Sync Songs and makes a change to the NoteSkins, + // that change is "sticky" across multiple ScreenEdits. That is the way the rest of the options work. + // TODO: It would be cleaner to do this by making it possible to set an option in metrics.ini. + if( !GAMESTATE->m_bDidModeChangeNoteSkin ) + { + GAMESTATE->m_bDidModeChangeNoteSkin = true; + FOREACH_PlayerNumber( pn ) + { + const RString &sNoteSkin = EDITOR_NOTE_SKINS[pn].Get(); + if( NOTESKIN->DoesNoteSkinExist( sNoteSkin ) ) + PO_GROUP_ASSIGN( GAMESTATE->m_pPlayerState[pn]->m_PlayerOptions, + ModsLevel_Preferred, m_sNoteSkin, sNoteSkin ); + } + } + m_PlayerStateEdit.m_PlayerNumber = PLAYER_1; + // If we always go with the GAMESTATE NoteSkin, we will have fun effects + // like Vivid or Flat in the editor notefield. This is not conducive to + // productive editing. + // todo: We should allow certain noteskins (note-colored/rhythm) to be + // displayed. (Perhaps this should be a noteskin metric.) -aj + if( NOTESKIN->DoesNoteSkinExist( EDITOR_NOTE_SKINS[PLAYER_1].Get() ) ) + { + PO_GROUP_ASSIGN( m_PlayerStateEdit.m_PlayerOptions, ModsLevel_Stage, m_sNoteSkin, EDITOR_NOTE_SKINS[PLAYER_1].Get() ); + } + else + { + PO_GROUP_ASSIGN( m_PlayerStateEdit.m_PlayerOptions, ModsLevel_Stage, m_sNoteSkin, GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetStage().m_sNoteSkin ); + } + m_PlayerStateEdit.m_PlayerOptions.FromString( ModsLevel_Stage, EDIT_MODIFIERS ); + + this->originalPlayerOptions.FromString(ModsLevel_Stage, EDIT_MODIFIERS); + + m_pSteps->GetNoteData( m_NoteDataEdit ); + m_NoteFieldEdit.SetXY( EDIT_X, EDIT_Y ); + m_NoteFieldEdit.SetZoom( 0.5f ); + m_NoteFieldEdit.Init( &m_PlayerStateEdit, PLAYER_HEIGHT*2 ); + m_NoteFieldEdit.Load( &m_NoteDataEdit, -240, 850 ); + this->AddChild( &m_NoteFieldEdit ); + + m_NoteDataRecord.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); + m_NoteFieldRecord.SetXY( RECORD_X, RECORD_Y ); + m_NoteFieldRecord.Init( GAMESTATE->m_pPlayerState[PLAYER_1], PLAYER_HEIGHT ); + m_NoteFieldRecord.Load( &m_NoteDataRecord, -120, 425 ); + this->AddChild( &m_NoteFieldRecord ); + + m_EditState = EditState_Invalid; + TransitionEditState( STATE_EDITING ); + + m_bRemoveNoteButtonDown = false; + + m_Clipboard.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); + + clipboardFullTiming = GAMESTATE->m_pCurSong->m_SongTiming; // always have a backup. + + m_bHasUndo = false; + m_Undo.SetNumTracks( m_NoteDataEdit.GetNumTracks() ); + + m_bDirty = m_NoteDataEdit.IsEmpty(); // require the usage of saving if empty. + + m_Player->Init( "Player", GAMESTATE->m_pPlayerState[PLAYER_1], NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL ); + m_Player->CacheAllUsedNoteSkins(); + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_HUMAN; + m_Player->SetXY( PLAYER_X, PLAYER_Y ); + this->AddChild( m_Player ); + + this->AddChild( &m_Foreground ); + + m_textInputTips.SetName( "EditHelp" ); + m_textInputTips.LoadFromFont( THEME->GetPathF("ScreenEdit","EditHelp") ); + m_textInputTips.SetText( EDIT_HELP_TEXT ); + LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textInputTips ); + this->AddChild( &m_textInputTips ); + + m_textInfo.SetName( "Info" ); + m_textInfo.LoadFromFont( THEME->GetPathF("ScreenEdit","Info") ); + LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textInfo ); + this->AddChild( &m_textInfo ); + + m_textPlayRecordHelp.SetName( "PlayRecordHelp" ); + m_textPlayRecordHelp.LoadFromFont( THEME->GetPathF("ScreenEdit","PlayRecordHelp") ); + m_textPlayRecordHelp.SetText( PLAY_RECORD_HELP_TEXT ); + LOAD_ALL_COMMANDS_AND_SET_XY_AND_ON_COMMAND( m_textPlayRecordHelp ); + this->AddChild( &m_textPlayRecordHelp ); + + m_soundAddNote.Load( THEME->GetPathS("ScreenEdit","AddNote"), true ); + m_soundRemoveNote.Load( THEME->GetPathS("ScreenEdit","RemoveNote"), true ); + m_soundChangeLine.Load( THEME->GetPathS("ScreenEdit","line"), true ); + m_soundChangeSnap.Load( THEME->GetPathS("ScreenEdit","snap"), true ); + m_soundMarker.Load( THEME->GetPathS("ScreenEdit","marker"), true ); + m_soundValueIncrease.Load( THEME->GetPathS("ScreenEdit","value increase"), true ); + m_soundValueDecrease.Load( THEME->GetPathS("ScreenEdit","value decrease"), true ); + m_soundSwitchPlayer.Load( THEME->GetPathS("ScreenEdit","switch player"), true ); + m_soundSwitchTiming.Load( THEME->GetPathS("ScreenEdit","switch timing"), true ); + m_soundSwitchSteps.Load( THEME->GetPathS("ScreenEdit","switch steps") ); + m_soundSave.Load( THEME->GetPathS("ScreenEdit","save") ); + m_GameplayAssist.Init(); + + m_AutoKeysounds.FinishLoading(); + m_pSoundMusic = m_AutoKeysounds.GetSound(); + + this->HandleScreenMessage( SM_UpdateTextInfo ); + m_bTextInfoNeedsUpdate = true; + + SubscribeToMessage( Message_SongModified ); +} + +ScreenEdit::~ScreenEdit() +{ + // UGLY: Don't delete the Song's steps. + m_SongLastSave.DetachSteps(); + + LOG->Trace( "ScreenEdit::~ScreenEdit()" ); + m_pSoundMusic->StopPlaying(); + + // Go back to Step Timing on leave. + GAMESTATE->m_bIsUsingStepTiming = true; + // DEFINITELY reset the InStepEditor variable. + GAMESTATE->m_bInStepEditor = false; +} + +void ScreenEdit::BeginScreen() +{ + ScreenWithMenuElements::BeginScreen(); + + /* We do this ourself. */ + SOUND->HandleSongTimer( false ); +} + +void ScreenEdit::EndScreen() +{ + ScreenWithMenuElements::EndScreen(); + + SOUND->HandleSongTimer( true ); +} + +// play assist ticks +void ScreenEdit::PlayTicks() +{ + if( m_EditState != STATE_PLAYING ) + return; + + m_GameplayAssist.PlayTicks( m_Player->GetNoteData(), m_Player->GetPlayerState() ); +} + +static ThemeMetric FADE_IN_PREVIEW("ScreenEdit", "FadeInPreview"); +static ThemeMetric FADE_OUT_PREVIEW("ScreenEdit", "FadeOutPreview"); + +void ScreenEdit::PlayPreviewMusic() +{ + SOUND->StopMusic(); + SOUND->PlayMusic( + m_pSong->GetMusicPath(), + NULL, + false, + m_pSong->m_fMusicSampleStartSeconds, + m_pSong->m_fMusicSampleLengthSeconds, + FADE_IN_PREVIEW, + FADE_OUT_PREVIEW ); +} + +void ScreenEdit::MakeFilteredMenuDef( const MenuDef* pDef, MenuDef &menu ) +{ + menu = *pDef; + menu.rows.clear(); + + vector aRows; + for (MenuRowDef const &r : pDef->rows) + { + // Don't add rows that aren't applicable to this edit mode. + if( EDIT_MODE >= r.emShowIn ) + menu.rows.push_back( r ); + } +} + +void ScreenEdit::EditMiniMenu( const MenuDef* pDef, ScreenMessage SM_SendOnOK, ScreenMessage SM_SendOnCancel ) +{ + // Reload options. + MenuDef menu(""); + MakeFilteredMenuDef( pDef, menu ); + ScreenMiniMenu::MiniMenu( &menu, SM_SendOnOK, SM_SendOnCancel ); +} + +void ScreenEdit::Update( float fDeltaTime ) +{ + m_PlayerStateEdit.Update( fDeltaTime ); + + if( m_pSoundMusic->IsPlaying() ) + { + RageTimer tm; + const float fSeconds = m_pSoundMusic->GetPositionSeconds( NULL, &tm ); + GAMESTATE->UpdateSongPosition( fSeconds, GAMESTATE->m_pCurSong->m_SongTiming, tm, true ); + } + + if( m_EditState == STATE_RECORDING ) + { + // TODO: Find a way to prevent STATE_RECORDING when in Song Timing. + + for( int t=0; tGetCurrentStyle()->m_iColsPerPlayer; t++ ) // for each track + { + GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( t, PLAYER_1 ); + float fSecsHeld = INPUTMAPPER->GetSecsHeld( GameI ); + fSecsHeld = min( fSecsHeld, m_RemoveNoteButtonLastChanged.Ago() ); + if( fSecsHeld == 0 ) + continue; + + // TODO: Currently, this will ignore a hold note that's + // held past the end of selection. Ideally the hold + // note should be created, ending right at the end of + // selection (or appropriate quantization). + float fStartPlayingAtBeat = NoteRowToBeat(m_iStartPlayingAt); + float fStopPlayingAtBeat = NoteRowToBeat(m_iStopPlayingAt); + if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat <= fStartPlayingAtBeat || + GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat >= fStopPlayingAtBeat ) + continue; + + float fStartedHoldingSeconds = m_pSoundMusic->GetPositionSeconds() - fSecsHeld; + float fStartBeat = max( fStartPlayingAtBeat, m_pSteps->GetTimingData()->GetBeatFromElapsedTime(fStartedHoldingSeconds) ); + float fEndBeat = max( fStartBeat, GetBeat() ); + fEndBeat = min( fEndBeat, fStopPlayingAtBeat ); + + // Round start and end to the nearest snap interval + fStartBeat = Quantize( fStartBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); + fEndBeat = Quantize( fEndBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); + + if( m_bRemoveNoteButtonDown ) + { + m_NoteDataRecord.ClearRangeForTrack( BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat), t ); + } + else if( fSecsHeld > RECORD_HOLD_SECONDS ) + { + // create or extend a hold or roll note + TapNote tn = EditIsBeingPressed(EDIT_BUTTON_LAY_ROLL) ? TAP_ORIGINAL_ROLL_HEAD: TAP_ORIGINAL_HOLD_HEAD; + + tn.pn = m_InputPlayerNumber; + m_NoteDataRecord.AddHoldNote( t, BeatToNoteRow(fStartBeat), BeatToNoteRow(fEndBeat), tn ); + } + } + } + + // + // check for end of playback/record + // + if( m_EditState == STATE_RECORDING || m_EditState == STATE_PLAYING ) + { + /* + * If any arrow is being held, continue for up to half a second after + * the end marker. This makes it possible to start a hold note near + * the end of the range. We won't allow placing anything outside of the + * range. + */ + bool bButtonIsBeingPressed = false; + for( int t=0; tGetCurrentStyle()->m_iColsPerPlayer; t++ ) // for each track + { + GameInput GameI = GAMESTATE->GetCurrentStyle()->StyleInputToGameInput( t, PLAYER_1 ); + if( INPUTMAPPER->IsBeingPressed(GameI) ) + bButtonIsBeingPressed = true; + } + + float fLastBeat = NoteRowToBeat(m_iStopPlayingAt); + if( bButtonIsBeingPressed && m_EditState == STATE_RECORDING ) + { + float fSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( fLastBeat ); + fLastBeat = m_pSteps->GetTimingData()->GetBeatFromElapsedTime( fSeconds + 0.5f ); + } + + float fStopAtSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( NoteRowToBeat(m_iStopPlayingAt) ) + 1; + if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fMusicSeconds > fStopAtSeconds ) + { + TransitionEditState( ( LOOP_ON_CHART_END ? STATE_PLAYING : STATE_EDITING ) ); + } + } + + + //LOG->Trace( "ScreenEdit::Update(%f)", fDeltaTime ); + ScreenWithMenuElements::Update( fDeltaTime ); + + + // Update trailing beat + float fDelta = GetBeat() - m_fTrailingBeat; + if( fabsf(fDelta) < 10 ) + fapproach( m_fTrailingBeat, GetBeat(), + fDeltaTime*40 / m_NoteFieldEdit.GetPlayerState()->m_PlayerOptions.GetCurrent().m_fScrollSpeed ); + else + fapproach( m_fTrailingBeat, GetBeat(), + fabsf(fDelta) * fDeltaTime*5 ); + + 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; +} + +// TODO: Fix this mess. -Colby +static LocalizedString CURRENT_BEAT("ScreenEdit", "Current beat"); +static LocalizedString CURRENT_SECOND("ScreenEdit", "Current second"); +static LocalizedString SNAP_TO("ScreenEdit", "Snap to"); +static LocalizedString NOTES("ScreenEdit", "%s notes"); +static LocalizedString SELECTION_BEAT("ScreenEdit", "Selection beat"); +static LocalizedString DIFFICULTY("ScreenEdit", "Difficulty"); +static LocalizedString ROUTINE_PLAYER("ScreenEdit", "Routine Player"); +static LocalizedString CHART_NAME("ScreenEdit", "Chart Name"); +static LocalizedString DESCRIPTION("ScreenEdit", "Description"); +static LocalizedString CHART_STYLE("ScreenEdit", "Chart Style"); +static LocalizedString STEP_AUTHOR("ScreenEdit", "Step Author"); +static LocalizedString MAIN_TITLE("ScreenEdit", "Main title"); +static LocalizedString SUBTITLE("ScreenEdit", "Subtitle"); +static LocalizedString TAP_NOTE_TYPE("ScreenEdit", "Tap Note"); +static LocalizedString SEGMENT_TYPE("ScreenEdit", "Segment"); +static LocalizedString TAP_STEPS("ScreenEdit", "Tap Steps"); +static LocalizedString JUMPS("ScreenEdit", "Jumps"); +static LocalizedString HANDS("ScreenEdit", "Hands"); +static LocalizedString HOLDS("ScreenEdit", "Holds"); +static LocalizedString MINES("ScreenEdit", "Mines"); +static LocalizedString ROLLS("ScreenEdit", "Rolls"); +static LocalizedString LIFTS("ScreenEdit", "Lifts"); +static LocalizedString FAKES("ScreenEdit", "Fakes"); +static LocalizedString TIMING_MODE("ScreenEdit", "Timing Mode"); +static LocalizedString STEP_TIMING("ScreenEdit", "Step Timing"); +static LocalizedString SONG_TIMING("ScreenEdit", "Song Timing"); +static LocalizedString BEAT_0_OFFSET("ScreenEdit", "Beat 0 offset"); +static LocalizedString PREVIEW_START("ScreenEdit", "Preview Start"); +static LocalizedString PREVIEW_LENGTH("ScreenEdit", "Preview Length"); + +static ThemeMetric CURRENT_BEAT_FORMAT("ScreenEdit", "CurrentBeatFormat"); +static ThemeMetric CURRENT_SECOND_FORMAT("ScreenEdit", "CurrentSecondFormat"); +static ThemeMetric SNAP_TO_FORMAT("ScreenEdit", "SnapToFormat"); +static ThemeMetric SELECTION_BEAT_BEGIN_FORMAT("ScreenEdit", "SelectionBeatBeginFormat"); +static ThemeMetric SELECTION_BEAT_UNFINISHED_FORMAT("ScreenEdit", "SelectionBeatUnfinishedFormat"); +static ThemeMetric SELECTION_BEAT_END_FORMAT("ScreenEdit", "SelectionBeatEndFormat"); +static ThemeMetric DIFFICULTY_FORMAT("ScreenEdit", "DifficultyFormat"); +static ThemeMetric ROUTINE_PLAYER_FORMAT("ScreenEdit", "RoutinePlayerFormat"); +static ThemeMetric CHART_NAME_FORMAT("ScreenEdit", "ChartNameFormat"); +static ThemeMetric DESCRIPTION_FORMAT("ScreenEdit", "DescriptionFormat"); +static ThemeMetric CHART_STYLE_FORMAT("ScreenEdit", "ChartStyleFormat"); +static ThemeMetric STEP_AUTHOR_FORMAT("ScreenEdit", "StepAuthorFormat"); +static ThemeMetric MAIN_TITLE_FORMAT("ScreenEdit", "MainTitleFormat"); +static ThemeMetric SUBTITLE_FORMAT("ScreenEdit", "SubtitleFormat"); +static ThemeMetric TAP_NOTE_TYPE_FORMAT("ScreenEdit", "TapNoteTypeFormat"); +static ThemeMetric SEGMENT_TYPE_FORMAT("ScreenEdit", "SegmentTypeFormat"); +static ThemeMetric NUM_STEPS_FORMAT("ScreenEdit", "NumStepsFormat"); +static ThemeMetric NUM_JUMPS_FORMAT("ScreenEdit", "NumJumpsFormat"); +static ThemeMetric NUM_HOLDS_FORMAT("ScreenEdit", "NumHoldsFormat"); +static ThemeMetric NUM_MINES_FORMAT("ScreenEdit", "NumMinesFormat"); +static ThemeMetric NUM_HANDS_FORMAT("ScreenEdit", "NumHandsFormat"); +static ThemeMetric NUM_ROLLS_FORMAT("ScreenEdit", "NumRollsFormat"); +static ThemeMetric NUM_LIFTS_FORMAT("ScreenEdit", "NumLiftsFormat"); +static ThemeMetric NUM_FAKES_FORMAT("ScreenEdit", "NumFakesFormat"); +static ThemeMetric NUM_STEPS_FORMAT_TWO_PLAYER("ScreenEdit", "NumStepsFormatTwoPlayer"); +static ThemeMetric NUM_JUMPS_FORMAT_TWO_PLAYER("ScreenEdit", "NumJumpsFormatTwoPlayer"); +static ThemeMetric NUM_HOLDS_FORMAT_TWO_PLAYER("ScreenEdit", "NumHoldsFormatTwoPlayer"); +static ThemeMetric NUM_MINES_FORMAT_TWO_PLAYER("ScreenEdit", "NumMinesFormatTwoPlayer"); +static ThemeMetric NUM_HANDS_FORMAT_TWO_PLAYER("ScreenEdit", "NumHandsFormatTwoPlayer"); +static ThemeMetric NUM_ROLLS_FORMAT_TWO_PLAYER("ScreenEdit", "NumRollsFormatTwoPlayer"); +static ThemeMetric NUM_LIFTS_FORMAT_TWO_PLAYER("ScreenEdit", "NumLiftsFormatTwoPlayer"); +static ThemeMetric NUM_FAKES_FORMAT_TWO_PLAYER("ScreenEdit", "NumFakesFormatTwoPlayer"); +static ThemeMetric TIMING_MODE_FORMAT("ScreenEdit", "TimingModeFormat"); +static ThemeMetric BEAT_0_OFFSET_FORMAT("ScreenEdit", "Beat0OffsetFormat"); +static ThemeMetric PREVIEW_START_FORMAT("ScreenEdit", "PreviewStartFormat"); +static ThemeMetric PREVIEW_LENGTH_FORMAT("ScreenEdit", "PreviewLengthFormat"); +void ScreenEdit::UpdateTextInfo() +{ + if( m_pSteps == NULL ) + return; + + // Don't update the text during playback or record. It causes skips. + if( m_EditState != STATE_EDITING ) + return; + + if( !m_bTextInfoNeedsUpdate ) + return; + + m_bTextInfoNeedsUpdate = false; + + RString sNoteType = ssprintf( NOTES.GetValue(), NoteTypeToLocalizedString(m_SnapDisplay.GetNoteType()).c_str() ); + + RString sText; + sText += ssprintf( CURRENT_BEAT_FORMAT.GetValue(), CURRENT_BEAT.GetValue().c_str(), GetBeat() ); + sText += ssprintf( CURRENT_SECOND_FORMAT.GetValue(), CURRENT_SECOND.GetValue().c_str(), GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()) ); + switch( EDIT_MODE.GetValue() ) + { + DEFAULT_FAIL( EDIT_MODE.GetValue() ); + case EditMode_Practice: + break; + case EditMode_CourseMods: + case EditMode_Home: + case EditMode_Full: + sText += ssprintf( SNAP_TO_FORMAT.GetValue(), SNAP_TO.GetValue().c_str(), sNoteType.c_str() ); + break; + } + + if( m_NoteFieldEdit.m_iBeginMarker != -1 ) + { + sText += ssprintf( SELECTION_BEAT_BEGIN_FORMAT.GetValue(), SELECTION_BEAT.GetValue().c_str(), NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + + if( m_NoteFieldEdit.m_iEndMarker != -1 ) + sText += ssprintf( SELECTION_BEAT_END_FORMAT.GetValue(), NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + else + sText += SELECTION_BEAT_UNFINISHED_FORMAT; + } + + if (EDIT_MODE.GetValue() == EditMode_Full) + { + sText += ssprintf( DIFFICULTY_FORMAT.GetValue(), DIFFICULTY.GetValue().c_str(), DifficultyToString( m_pSteps->GetDifficulty() ).c_str() ); + if ( m_InputPlayerNumber != PLAYER_INVALID ) + sText += ssprintf( ROUTINE_PLAYER_FORMAT.GetValue(), ROUTINE_PLAYER.GetValue().c_str(), m_InputPlayerNumber + 1 ); + //sText += ssprintf( DESCRIPTION_FORMAT.GetValue(), DESCRIPTION.GetValue().c_str(), m_pSteps->GetDescription().c_str() ); + sText += ssprintf( CHART_NAME_FORMAT.GetValue(), CHART_NAME.GetValue().c_str(), m_pSteps->GetChartName().c_str() ); + sText += ssprintf( STEP_AUTHOR_FORMAT.GetValue(), STEP_AUTHOR.GetValue().c_str(), m_pSteps->GetCredit().c_str() ); + //sText += ssprintf( CHART_STYLE_FORMAT.GetValue(), CHART_STYLE.GetValue().c_str(), m_pSteps->GetChartStyle().c_str() ); + sText += ssprintf( MAIN_TITLE_FORMAT.GetValue(), MAIN_TITLE.GetValue().c_str(), m_pSong->m_sMainTitle.c_str() ); + if( m_pSong->m_sSubTitle.size() ) + sText += ssprintf( SUBTITLE_FORMAT.GetValue(), SUBTITLE.GetValue().c_str(), m_pSong->m_sSubTitle.c_str() ); + 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->GetTimingData()); + const StepsTypeCategory &cat = GAMEMAN->GetStepsTypeInfo(m_pSteps->m_StepsType).m_StepsTypeCategory; + if (cat == StepsTypeCategory_Couple || cat == StepsTypeCategory_Routine) + { + pair tmp = m_NoteDataEdit.GetNumTapNotesTwoPlayer(); + sText += ssprintf(NUM_STEPS_FORMAT_TWO_PLAYER.GetValue(), + TAP_STEPS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumJumpsTwoPlayer(); + sText += ssprintf(NUM_JUMPS_FORMAT_TWO_PLAYER.GetValue(), + JUMPS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumHandsTwoPlayer(); + sText += ssprintf(NUM_HANDS_FORMAT_TWO_PLAYER.GetValue(), + HANDS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumHoldNotesTwoPlayer(); + sText += ssprintf(NUM_HOLDS_FORMAT_TWO_PLAYER.GetValue(), + HOLDS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumMinesTwoPlayer(); + sText += ssprintf(NUM_MINES_FORMAT_TWO_PLAYER.GetValue(), + MINES.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumRollsTwoPlayer(); + sText += ssprintf(NUM_ROLLS_FORMAT_TWO_PLAYER.GetValue(), + ROLLS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumLiftsTwoPlayer(); + sText += ssprintf(NUM_LIFTS_FORMAT_TWO_PLAYER.GetValue(), + LIFTS.GetValue().c_str(), + tmp.first, tmp.second); + tmp = m_NoteDataEdit.GetNumFakesTwoPlayer(); + sText += ssprintf(NUM_FAKES_FORMAT_TWO_PLAYER.GetValue(), + FAKES.GetValue().c_str(), + tmp.first, tmp.second); + } + else + { + sText += ssprintf( NUM_STEPS_FORMAT.GetValue(), TAP_STEPS.GetValue().c_str(), m_NoteDataEdit.GetNumTapNotes() ); + sText += ssprintf( NUM_JUMPS_FORMAT.GetValue(), JUMPS.GetValue().c_str(), m_NoteDataEdit.GetNumJumps() ); + sText += ssprintf( NUM_HANDS_FORMAT.GetValue(), HANDS.GetValue().c_str(), m_NoteDataEdit.GetNumHands() ); + sText += ssprintf( NUM_HOLDS_FORMAT.GetValue(), HOLDS.GetValue().c_str(), m_NoteDataEdit.GetNumHoldNotes() ); + sText += ssprintf( NUM_MINES_FORMAT.GetValue(), MINES.GetValue().c_str(), m_NoteDataEdit.GetNumMines() ); + sText += ssprintf( NUM_ROLLS_FORMAT.GetValue(), ROLLS.GetValue().c_str(), m_NoteDataEdit.GetNumRolls() ); + sText += ssprintf( NUM_LIFTS_FORMAT.GetValue(), LIFTS.GetValue().c_str(), m_NoteDataEdit.GetNumLifts() ); + sText += ssprintf( NUM_FAKES_FORMAT.GetValue(), FAKES.GetValue().c_str(), m_NoteDataEdit.GetNumFakes() ); + } + switch( EDIT_MODE.GetValue() ) + { + DEFAULT_FAIL( EDIT_MODE.GetValue() ); + case EditMode_Practice: + case EditMode_CourseMods: + case EditMode_Home: + break; + case EditMode_Full: + sText += ssprintf( TIMING_MODE_FORMAT.GetValue(), + TIMING_MODE.GetValue().c_str(), + ( GAMESTATE->m_bIsUsingStepTiming ? + STEP_TIMING.GetValue().c_str() : + SONG_TIMING.GetValue().c_str() ) ); + sText += ssprintf( BEAT_0_OFFSET_FORMAT.GetValue(), + BEAT_0_OFFSET.GetValue().c_str(), + GetAppropriateTiming().m_fBeat0OffsetInSeconds ); + sText += ssprintf( PREVIEW_START_FORMAT.GetValue(), PREVIEW_START.GetValue().c_str(), m_pSong->m_fMusicSampleStartSeconds ); + sText += ssprintf( PREVIEW_LENGTH_FORMAT.GetValue(), PREVIEW_LENGTH.GetValue().c_str(), m_pSong->m_fMusicSampleLengthSeconds ); + break; + } + + m_textInfo.SetText( sText ); + + GAMESTATE->SetProcessedTimingData(NULL); +} + +void ScreenEdit::DrawPrimitives() +{ + + if( m_pSoundMusic->IsPlaying() ) + { + ScreenWithMenuElements::DrawPrimitives(); + return; + } + + // HACK: Draw using the trailing beat + PlayerState *pPlayerState = const_cast (m_NoteFieldEdit.GetPlayerState()); + + float fPlayerSongBeat = pPlayerState->m_Position.m_fSongBeat; // save song beat + float fPlayerSongBeatNoOffset = pPlayerState->m_Position.m_fSongBeatNoOffset; + float fPlayerSongBeatVisible = pPlayerState->m_Position.m_fSongBeatVisible; + + float fGameSongBeat = GAMESTATE->m_Position.m_fSongBeat; // save song beat + float fGameSongBeatNoOffset = GAMESTATE->m_Position.m_fSongBeatNoOffset; + float fGameSongBeatVisible = GAMESTATE->m_Position.m_fSongBeatVisible; + + pPlayerState->m_Position.m_fSongBeat = m_fTrailingBeat; // put trailing beat in effect + pPlayerState->m_Position.m_fSongBeatNoOffset = m_fTrailingBeat; // put trailing beat in effect + pPlayerState->m_Position.m_fSongBeatVisible = m_fTrailingBeat; // put trailing beat in effect + + GAMESTATE->m_Position.m_fSongBeat = m_fTrailingBeat; // put trailing beat in effect + GAMESTATE->m_Position.m_fSongBeatNoOffset = m_fTrailingBeat; // put trailing beat in effect + GAMESTATE->m_Position.m_fSongBeatVisible = m_fTrailingBeat; // put trailing beat in effect + + ScreenWithMenuElements::DrawPrimitives(); + + pPlayerState->m_Position.m_fSongBeat = fPlayerSongBeat; // restore real song beat + pPlayerState->m_Position.m_fSongBeatNoOffset = fPlayerSongBeatNoOffset; + pPlayerState->m_Position.m_fSongBeatVisible = fPlayerSongBeatVisible; + + GAMESTATE->m_Position.m_fSongBeat = fGameSongBeat; // restore real song beat + GAMESTATE->m_Position.m_fSongBeatNoOffset = fGameSongBeatNoOffset; + GAMESTATE->m_Position.m_fSongBeatVisible = fGameSongBeatVisible; + +} + +bool ScreenEdit::Input( const InputEventPlus &input ) +{ +// LOG->Trace( "ScreenEdit::Input()" ); + + // invalidate input if cmd/meta is being held. + if( INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_LMETA)) || + INPUTFILTER->IsBeingPressed(DeviceInput(DEVICE_KEYBOARD, KEY_RMETA)) ) + return false; + + if( m_In.IsTransitioning() || m_Out.IsTransitioning() ) + return false; + + EditButton EditB = DeviceToEdit( input.DeviceI ); + if( EditB == EditButton_Invalid ) + EditB = MenuButtonToEditButton( input.MenuI ); + + if( EditB == EDIT_BUTTON_REMOVE_NOTE ) + { + /* Ugly: we need to know when the button was pressed or released, so we + * can clamp operations to that time. Should InputFilter keep track of + * last release, too? */ + m_bRemoveNoteButtonDown = (input.type != IET_RELEASE); + m_RemoveNoteButtonLastChanged.Touch(); + } + + switch( m_EditState ) + { + DEFAULT_FAIL( m_EditState ); + case STATE_EDITING: + m_bTextInfoNeedsUpdate = true; + return InputEdit( input, EditB ); + case STATE_RECORDING: + return InputRecord( input, EditB ); + case STATE_RECORDING_PAUSED: + return InputRecordPaused( input, EditB ); + case STATE_PLAYING: + return InputPlay( input, EditB ); + } +} + +static void ShiftToRightSide( int &iCol, int iNumTracks ) +{ + switch( GAMESTATE->GetCurrentStyle()->m_StyleType ) + { + DEFAULT_FAIL( GAMESTATE->GetCurrentStyle()->m_StyleType ); + case StyleType_OnePlayerOneSide: + break; + case StyleType_TwoPlayersTwoSides: + case StyleType_OnePlayerTwoSides: + case StyleType_TwoPlayersSharedSides: + iCol += iNumTracks/2; + break; + } +} + +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" ); +static LocalizedString NO_BACKGROUNDS_AVAILABLE ( "ScreenEdit", "No backgrounds available" ); +static ThemeMetric INVERT_SCROLL_BUTTONS ( "ScreenEdit", "InvertScrollSpeedButtons" ); + +bool ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB ) +{ + if( input.type == IET_RELEASE ) + { + if( EditPressed( EDIT_BUTTON_SCROLL_SELECT, input.DeviceI ) ) + m_iShiftAnchor = -1; + // XXX Key releases usually don't count as "handled," but what + // does it mean in this case? + return false; + } + const TimingData &sTiming = GetAppropriateTiming(); + float playerBeat = GetAppropriatePosition().m_fSongBeat; + int beatsPerMeasure = sTiming.GetTimeSignatureSegmentAtBeat( playerBeat )->GetNum(); + + switch( EditB ) + { + case EDIT_BUTTON_COLUMN_0: + case EDIT_BUTTON_COLUMN_1: + case EDIT_BUTTON_COLUMN_2: + case EDIT_BUTTON_COLUMN_3: + case EDIT_BUTTON_COLUMN_4: + case EDIT_BUTTON_COLUMN_5: + case EDIT_BUTTON_COLUMN_6: + case EDIT_BUTTON_COLUMN_7: + case EDIT_BUTTON_COLUMN_8: + case EDIT_BUTTON_COLUMN_9: + { + if( input.type != IET_FIRST_PRESS ) + return false; // We only care about first presses + + int iCol = EditB - EDIT_BUTTON_COLUMN_0; + + + // Alt + number = input to right half + if( EditIsBeingPressed(EDIT_BUTTON_RIGHT_SIDE) ) + ShiftToRightSide( iCol, m_NoteDataEdit.GetNumTracks() ); + + if( iCol >= m_NoteDataEdit.GetNumTracks() ) + return false; // this button is not in the range of columns for this Style + + const float fSongBeat = GetBeat(); + const int iSongIndex = BeatToNoteRow( fSongBeat ); + + // check for to see if the user intended to remove a HoldNote + int iHeadRow; + if( m_NoteDataEdit.IsHoldNoteAtRow( iCol, iSongIndex, &iHeadRow ) ) + { + m_soundRemoveNote.Play(); + SetDirty( true ); + SaveUndo(); + m_NoteDataEdit.SetTapNote( iCol, iHeadRow, TAP_EMPTY ); + // Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes. + } + else if( m_NoteDataEdit.GetTapNote(iCol, iSongIndex).type != TapNote::empty ) + { + m_soundRemoveNote.Play(); + SetDirty( true ); + SaveUndo(); + m_NoteDataEdit.SetTapNote( iCol, iSongIndex, TAP_EMPTY ); + // Don't CheckNumberOfNotesAndUndo. We don't want to revert any change that removes notes. + } + else if( EditIsBeingPressed(EDIT_BUTTON_LAY_TAP_ATTACK) ) + { + g_iLastInsertTapAttackTrack = iCol; + EditMiniMenu( &g_InsertTapAttack, SM_BackFromInsertTapAttack ); + } + else + { + m_soundAddNote.Play(); + SetDirty( true ); + SaveUndo(); + TapNote tn = m_selectedTap; + tn.pn = m_InputPlayerNumber; + m_NoteDataEdit.SetTapNote(iCol, iSongIndex, tn ); + CheckNumberOfNotesAndUndo(); + } + } + return true; + + case EDIT_BUTTON_CYCLE_TAP_LEFT: + { + switch ( m_selectedTap.type ) + { + case TapNote::tap: m_selectedTap = TAP_ORIGINAL_FAKE; break; + case TapNote::mine: m_selectedTap = TAP_ORIGINAL_TAP; break; + case TapNote::lift: m_selectedTap = TAP_ORIGINAL_MINE; break; + case TapNote::fake: m_selectedTap = TAP_ORIGINAL_LIFT; break; + DEFAULT_FAIL( m_selectedTap.type ); + } + return true; + } + case EDIT_BUTTON_CYCLE_TAP_RIGHT: + { + switch ( m_selectedTap.type ) + { + case TapNote::tap: m_selectedTap = TAP_ORIGINAL_MINE; break; + case TapNote::mine: m_selectedTap = TAP_ORIGINAL_LIFT; break; + case TapNote::lift: m_selectedTap = TAP_ORIGINAL_FAKE; break; + case TapNote::fake: m_selectedTap = TAP_ORIGINAL_TAP; break; + DEFAULT_FAIL( m_selectedTap.type ); + } + return true; + } + case EDIT_BUTTON_CYCLE_SEGMENT_LEFT: + { + int tmp = enum_add2( this->currentCycleSegment, -1 ); + wrap( *ConvertValue(&tmp), NUM_TimingSegmentType ); + currentCycleSegment = (TimingSegmentType)tmp; + return true; + } + case EDIT_BUTTON_CYCLE_SEGMENT_RIGHT: + { + int tmp = enum_add2( this->currentCycleSegment, +1 ); + wrap( *ConvertValue(&tmp), NUM_TimingSegmentType ); + currentCycleSegment = (TimingSegmentType)tmp; + return true; + } + case EDIT_BUTTON_SCROLL_SPEED_UP: + case EDIT_BUTTON_SCROLL_SPEED_DOWN: + { + PlayerState *pPlayerState = const_cast (m_NoteFieldEdit.GetPlayerState()); + float fScrollSpeed = pPlayerState->m_PlayerOptions.GetSong().m_fScrollSpeed; + + const float fSpeeds[] = { 1.0f, 1.5f, 2.0f, 3.0f, 4.0f, 6.0f, 8.0f }; + int iSpeed = 0; + for( unsigned i = 0; i < ARRAYLEN(fSpeeds); ++i ) + { + if( fSpeeds[i] == fScrollSpeed ) + { + iSpeed = i; + break; + } + } + + switch( EditB ) + { + DEFAULT_FAIL(EditB); + case EDIT_BUTTON_SCROLL_SPEED_DOWN: + INVERT_SCROLL_BUTTONS ? ++iSpeed : --iSpeed; + break; + case EDIT_BUTTON_SCROLL_SPEED_UP: + INVERT_SCROLL_BUTTONS ? --iSpeed : ++iSpeed; + break; + } + iSpeed = clamp( iSpeed, 0, (int) ARRAYLEN(fSpeeds)-1 ); + + if( fSpeeds[iSpeed] != fScrollSpeed ) + { + m_soundMarker.Play(); + fScrollSpeed = fSpeeds[iSpeed]; + } + + PO_GROUP_ASSIGN( pPlayerState->m_PlayerOptions, ModsLevel_Song, m_fScrollSpeed, fScrollSpeed ); + } + + return true; + case EDIT_BUTTON_SCROLL_HOME: + ScrollTo( 0 ); + return true; + case EDIT_BUTTON_SCROLL_END: + ScrollTo( m_NoteDataEdit.GetLastBeat() ); + return true; + case EDIT_BUTTON_SCROLL_UP_LINE: + case EDIT_BUTTON_SCROLL_UP_PAGE: + case EDIT_BUTTON_SCROLL_UP_TS: + case EDIT_BUTTON_SCROLL_DOWN_LINE: + case EDIT_BUTTON_SCROLL_DOWN_PAGE: + case EDIT_BUTTON_SCROLL_DOWN_TS: + { + float fBeatsToMove=0.f; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_SCROLL_UP_LINE: + case EDIT_BUTTON_SCROLL_DOWN_LINE: + fBeatsToMove = NoteTypeToBeat( m_SnapDisplay.GetNoteType() ); + if( EditB == EDIT_BUTTON_SCROLL_UP_LINE ) + fBeatsToMove *= -1; + break; + case EDIT_BUTTON_SCROLL_UP_PAGE: + case EDIT_BUTTON_SCROLL_DOWN_PAGE: + fBeatsToMove = 4; + if( EditB == EDIT_BUTTON_SCROLL_UP_PAGE ) + fBeatsToMove *= -1; + break; + case EDIT_BUTTON_SCROLL_UP_TS: + case EDIT_BUTTON_SCROLL_DOWN_TS: + fBeatsToMove = beatsPerMeasure; + if( EditB == EDIT_BUTTON_SCROLL_UP_TS ) + fBeatsToMove *= -1; + break; + } + + if( m_PlayerStateEdit.m_PlayerOptions.GetSong().m_fScrolls[PlayerOptions::SCROLL_REVERSE] > 0.5 ) + fBeatsToMove *= -1; + + float fDestinationBeat = GetBeat() + fBeatsToMove; + fDestinationBeat = Quantize( fDestinationBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); + + ScrollTo( fDestinationBeat ); + } + return true; + case EDIT_BUTTON_SCROLL_NEXT_MEASURE: + { + float fDestinationBeat = GetBeat() + beatsPerMeasure; + fDestinationBeat = ftruncf( fDestinationBeat, (float)beatsPerMeasure ); + ScrollTo( fDestinationBeat ); + return true; + } + case EDIT_BUTTON_SCROLL_PREV_MEASURE: + { + float fDestinationBeat = QuantizeUp( GetBeat(), (float)beatsPerMeasure ); + fDestinationBeat -= (float)beatsPerMeasure; + ScrollTo( fDestinationBeat ); + return true; + } + case EDIT_BUTTON_SCROLL_NEXT: + { + int iRow = BeatToNoteRow( GetBeat() ); + NoteDataUtil::GetNextEditorPosition( m_NoteDataEdit, iRow ); + ScrollTo( NoteRowToBeat(iRow) ); + } + return true; + case EDIT_BUTTON_SCROLL_PREV: + { + int iRow = BeatToNoteRow( GetBeat() ); + NoteDataUtil::GetPrevEditorPosition( m_NoteDataEdit, iRow ); + ScrollTo( NoteRowToBeat(iRow) ); + } + return true; + case EDIT_BUTTON_SEGMENT_NEXT: + { + // TODO: Work around Stops and Delays. We MAY have to separate them. + const TimingData &timing = GetAppropriateTiming(); + ScrollTo(timing.GetNextSegmentBeatAtBeat(this->currentCycleSegment, + GetBeat())); + } + return true; + case EDIT_BUTTON_SEGMENT_PREV: + { + // TODO: Work around Stops and Delays. We MAY have to separate them. + const TimingData &timing = GetAppropriateTiming(); + ScrollTo(timing.GetPreviousSegmentBeatAtBeat(this->currentCycleSegment, + GetBeat())); + } + return true; + case EDIT_BUTTON_SNAP_NEXT: + if( m_SnapDisplay.PrevSnapMode() ) + OnSnapModeChange(); + return true; + case EDIT_BUTTON_SNAP_PREV: + if( m_SnapDisplay.NextSnapMode() ) + OnSnapModeChange(); + return true; + case EDIT_BUTTON_LAY_SELECT: + { + const int iCurrentRow = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); + if( m_NoteFieldEdit.m_iBeginMarker==-1 && m_NoteFieldEdit.m_iEndMarker==-1 ) + { + // lay begin marker + m_NoteFieldEdit.m_iBeginMarker = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); + } + else if( m_NoteFieldEdit.m_iEndMarker==-1 ) // only begin marker is laid + { + if( iCurrentRow == m_NoteFieldEdit.m_iBeginMarker ) + { + m_NoteFieldEdit.m_iBeginMarker = -1; + } + else + { + m_NoteFieldEdit.m_iEndMarker = max( m_NoteFieldEdit.m_iBeginMarker, iCurrentRow ); + m_NoteFieldEdit.m_iBeginMarker = min( m_NoteFieldEdit.m_iBeginMarker, iCurrentRow ); + } + } + else // both markers are laid + { + m_NoteFieldEdit.m_iBeginMarker = iCurrentRow; + m_NoteFieldEdit.m_iEndMarker = -1; + } + m_soundMarker.Play(); + } + return true; + case EDIT_BUTTON_OPEN_AREA_MENU: + { + // TODO: Improve behavior if timing changes are shifted down on beat 0. + g_AreaMenu.rows[shift_pauses_forward].bEnabled = (GetBeat() != 0); + g_AreaMenu.rows[paste_at_current_beat].bEnabled = !m_Clipboard.IsEmpty(); + g_AreaMenu.rows[paste_at_begin_marker].bEnabled = !m_Clipboard.IsEmpty() != 0 && m_NoteFieldEdit.m_iBeginMarker!=-1; + g_AreaMenu.rows[undo].bEnabled = m_bHasUndo; + EditMiniMenu( &g_AreaMenu, SM_BackFromAreaMenu ); + } + return true; + case EDIT_BUTTON_OPEN_ALTER_MENU: + { + bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1; + if (!bAreaSelected) + { + SCREENMAN->SystemMessage( ALTER_MENU_NO_SELECTION ); + SCREENMAN->PlayInvalidSound(); + } + else + { + // TODO: Improve interaction when field is 0. + g_AlterMenu.rows[convert_to_delay].bEnabled = (m_NoteFieldEdit.m_iBeginMarker > 0); + g_AlterMenu.rows[convert_to_pause].bEnabled = (m_NoteFieldEdit.m_iBeginMarker > 0); + bool isRoutine = (m_InputPlayerNumber != PLAYER_INVALID); + g_AlterMenu.rows[routine_invert_notes].bEnabled = isRoutine; + g_AlterMenu.rows[routine_mirror_1_to_2].bEnabled = isRoutine; + g_AlterMenu.rows[routine_mirror_2_to_1].bEnabled = isRoutine; + EditMiniMenu(&g_AlterMenu, SM_BackFromAlterMenu); + } + return true; + + } + case EDIT_BUTTON_OPEN_EDIT_MENU: + EditMiniMenu( &g_MainMenu, SM_BackFromMainMenu ); + return true; + case EDIT_BUTTON_OPEN_INPUT_HELP: + DoHelp(); + return true; + case EDIT_BUTTON_OPEN_TIMING_MENU: + { + DisplayTimingMenu(); + return true; + } + case EDIT_BUTTON_OPEN_NEXT_STEPS: + case EDIT_BUTTON_OPEN_PREV_STEPS: + { + // don't keep undo when changing Steps + ClearUndo(); + + // get the second of the current step. + float curSecond = GetAppropriateTiming().GetElapsedTimeFromBeat(GetBeat()); + + // save current steps + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + ASSERT( pSteps != NULL ); + pSteps->SetNoteData( m_NoteDataEdit ); + + // Get all Steps of this StepsType + const StepsType st = pSteps->m_StepsType; + vector vSteps; + SongUtil::GetSteps( GAMESTATE->m_pCurSong, vSteps, st ); + + // Sort them by difficulty. + StepsUtil::SortStepsByTypeAndDifficulty( vSteps ); + + // Find out what index the current Steps are + vector::iterator it = find( vSteps.begin(), vSteps.end(), pSteps ); + ASSERT( it != vSteps.end() ); + + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_OPEN_PREV_STEPS: + if( it==vSteps.begin() ) + { + SCREENMAN->PlayInvalidSound(); + return true; + } + it--; + break; + case EDIT_BUTTON_OPEN_NEXT_STEPS: + it++; + if( it==vSteps.end() ) + { + SCREENMAN->PlayInvalidSound(); + return true; + } + break; + } + + pSteps = *it; + GAMESTATE->m_pCurSteps[PLAYER_1].Set( pSteps ); + m_pSteps = pSteps; + pSteps->GetNoteData( m_NoteDataEdit ); + + RString s = ssprintf( + SWITCHED_TO.GetValue() + " %s %s '%s' (%d of %d)", + GAMEMAN->GetStepsTypeInfo( st ).szName, + DifficultyToString( pSteps->GetDifficulty() ).c_str(), + pSteps->GetChartName().c_str(), + it - vSteps.begin() + 1, + int(vSteps.size()) ); + SCREENMAN->SystemMessage( s ); + m_soundSwitchSteps.Play(); + + ScrollTo( GetAppropriateTiming().GetBeatFromElapsedTime(curSecond) ); + } + return true; + case EDIT_BUTTON_BPM_UP: + case EDIT_BUTTON_BPM_DOWN: + { + float fBPM = GetAppropriateTiming().GetBPMAtBeat( GetBeat() ); + float fDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_BPM_UP: fDelta = +0.020f; break; + case EDIT_BUTTON_BPM_DOWN: fDelta = -0.020f; break; + } + if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) + { + fDelta /= 20; // .001 bpm + } + else if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fDelta *= 10; + else + fDelta *= 40; + } + + float fNewBPM = fBPM + fDelta; + GetAppropriateTimingForUpdate().SetBPMAtBeat( GetBeat(), fNewBPM ); + (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + SetDirty( true ); + } + return true; + case EDIT_BUTTON_STOP_UP: + case EDIT_BUTTON_STOP_DOWN: + { + float fDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_STOP_UP: fDelta = +0.020f; break; + case EDIT_BUTTON_STOP_DOWN: fDelta = -0.020f; break; + } + if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) + { + fDelta /= 20; // 1ms + } + else if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fDelta *= 10; + else + fDelta *= 40; + } + + // is there a StopSegment on the current row? + TimingData & timing = GetAppropriateTimingForUpdate(); + StopSegment *seg = timing.GetStopSegmentAtRow( GetRow() ); + int i = timing.GetSegmentIndexAtRow(SEGMENT_STOP, GetRow()); + if (i == -1 || seg->GetRow() != GetRow()) // invalid + { + if( fDelta > 0 ) + timing.AddSegment( StopSegment(GetRow(), fDelta) ); + else + return false; + } + else + { + vector &stops = timing.GetTimingSegments(SEGMENT_STOP); + seg->SetPause(seg->GetPause() + fDelta); + if( seg->GetPause() <= 0 ) + stops.erase( stops.begin()+i, stops.begin()+i+1); + } + + (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + SetDirty( true ); + } + return true; + // TODO: Combine the stop and delay call somehow? + case EDIT_BUTTON_DELAY_UP: + case EDIT_BUTTON_DELAY_DOWN: + { + float fDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_DELAY_UP: fDelta = +0.020f; break; + case EDIT_BUTTON_DELAY_DOWN: fDelta = -0.020f; break; + } + if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) + { + fDelta /= 20; // 1ms + } + else if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fDelta *= 10; + else + fDelta *= 40; + } + + // is there a StopSegment on the current row? + TimingData & timing = GetAppropriateTimingForUpdate(); + DelaySegment *seg = timing.GetDelaySegmentAtRow( GetRow() ); + int i = timing.GetSegmentIndexAtRow(SEGMENT_DELAY, GetRow()); + if (i == -1 || seg->GetRow() != GetRow()) // invalid + { + if( fDelta > 0 ) + timing.AddSegment( DelaySegment(GetRow(), fDelta) ); + else + return false; + } + else + { + vector &stops = timing.GetTimingSegments(SEGMENT_DELAY); + seg->SetPause(seg->GetPause() + fDelta); + if( seg->GetPause() <= 0 ) + stops.erase( stops.begin()+i, stops.begin()+i+1); + } + + (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + SetDirty( true ); + } + return true; + case EDIT_BUTTON_OFFSET_UP: + case EDIT_BUTTON_OFFSET_DOWN: + { + float fDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_OFFSET_DOWN: fDelta = -0.02f; break; + case EDIT_BUTTON_OFFSET_UP: fDelta = +0.02f; break; + } + if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) + { + fDelta /= 20; /* 1ms */ + } + else if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fDelta *= 10; + else + fDelta *= 40; + } + GetAppropriateTimingForUpdate().m_fBeat0OffsetInSeconds += fDelta; + (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + if (GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(fDelta); + } + else + { + GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(fDelta); + GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += fDelta; + } + SetDirty( true ); + } + return true; + case EDIT_BUTTON_SAMPLE_START_UP: + case EDIT_BUTTON_SAMPLE_START_DOWN: + case EDIT_BUTTON_SAMPLE_LENGTH_DOWN: + case EDIT_BUTTON_SAMPLE_LENGTH_UP: + { + float fDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_SAMPLE_START_DOWN: fDelta = -0.02f; break; + case EDIT_BUTTON_SAMPLE_START_UP: fDelta = +0.02f; break; + case EDIT_BUTTON_SAMPLE_LENGTH_DOWN: fDelta = -0.02f; break; + case EDIT_BUTTON_SAMPLE_LENGTH_UP: fDelta = +0.02f; break; + } + + if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fDelta *= 10; + else + fDelta *= 40; + } + + if( EditB == EDIT_BUTTON_SAMPLE_LENGTH_DOWN || EditB == EDIT_BUTTON_SAMPLE_LENGTH_UP ) + { + m_pSong->m_fMusicSampleLengthSeconds += fDelta; + m_pSong->m_fMusicSampleLengthSeconds = max(m_pSong->m_fMusicSampleLengthSeconds,0); + } + else + { + m_pSong->m_fMusicSampleStartSeconds += fDelta; + m_pSong->m_fMusicSampleStartSeconds = max(m_pSong->m_fMusicSampleStartSeconds,0); + } + (fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play(); + SetDirty( true ); + } + return true; + case EDIT_BUTTON_PLAY_SAMPLE_MUSIC: + PlayPreviewMusic(); + return true; + case EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU: + case EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU: + if( !GAMESTATE->m_bIsUsingStepTiming ) + { + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU: g_CurrentBGChangeLayer = BACKGROUND_LAYER_1; break; + case EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU: g_CurrentBGChangeLayer = BACKGROUND_LAYER_2; break; + } + + { + // Fill in option names + vector vThrowAway; + + MenuDef &menu = g_BackgroundChange; + + menu.rows[layer].choices[0] = ssprintf("%d",g_CurrentBGChangeLayer); + BackgroundUtil::GetBackgroundTransitions( "", vThrowAway, menu.rows[transition].choices ); + g_BackgroundChange.rows[transition].choices.insert( g_BackgroundChange.rows[transition].choices.begin(), "None" ); // add "no transition" + BackgroundUtil::GetBackgroundEffects( "", vThrowAway, menu.rows[effect].choices ); + menu.rows[effect].choices.insert( menu.rows[effect].choices.begin(), "Default" ); // add "default effect" + + BackgroundUtil::GetSongBGAnimations( m_pSong, "", vThrowAway, menu.rows[file1_song_bganimation].choices ); + BackgroundUtil::GetSongMovies( m_pSong, "", vThrowAway, menu.rows[file1_song_movie].choices ); + BackgroundUtil::GetSongBitmaps( m_pSong, "", vThrowAway, menu.rows[file1_song_still].choices ); + BackgroundUtil::GetGlobalBGAnimations( m_pSong, "", vThrowAway, menu.rows[file1_global_bganimation].choices ); // NULL to get all background files + BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie].choices, false, false ); // all backgrounds + BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie_song_group].choices, false, true ); // song group's backgrounds + BackgroundUtil::GetGlobalRandomMovies( m_pSong, "", vThrowAway, menu.rows[file1_global_movie_song_group_and_genre].choices, true, true ); // song group and genre's backgrounds + + menu.rows[file2_type].choices = menu.rows[file1_type].choices; + menu.rows[file2_song_bganimation].choices = menu.rows[file1_song_bganimation].choices; + menu.rows[file2_song_movie].choices = menu.rows[file1_song_movie].choices; + menu.rows[file2_song_still].choices = menu.rows[file1_song_still].choices; + menu.rows[file2_global_bganimation].choices = menu.rows[file1_global_bganimation].choices; + menu.rows[file2_global_movie].choices = menu.rows[file1_global_movie].choices; + menu.rows[file2_global_movie_song_group].choices = menu.rows[file1_global_movie_song_group].choices; + menu.rows[file2_global_movie_song_group_and_genre].choices = menu.rows[file1_global_movie_song_group_and_genre].choices; + + + // Fill in lines enabled/disabled + bool bAlreadyBGChangeHere = false; + BackgroundChange bgChange; + FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer), bgc ) + { + if( bgc->m_fStartBeat == GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat ) + { + bAlreadyBGChangeHere = true; + bgChange = *bgc; + } + } + +#define FILL_ENABLED( x ) menu.rows[x].bEnabled = menu.rows[x].choices.size() > 0; + FILL_ENABLED( transition ); + FILL_ENABLED( effect ); + FILL_ENABLED( file1_song_bganimation ); + FILL_ENABLED( file1_song_movie ); + FILL_ENABLED( file1_song_still ); + FILL_ENABLED( file1_global_bganimation ); + FILL_ENABLED( file1_global_movie ); + FILL_ENABLED( file1_global_movie_song_group ); + FILL_ENABLED( file1_global_movie_song_group_and_genre ); + FILL_ENABLED( file2_song_bganimation ); + FILL_ENABLED( file2_song_movie ); + FILL_ENABLED( file2_song_still ); + FILL_ENABLED( file2_global_bganimation ); + FILL_ENABLED( file2_global_movie ); + FILL_ENABLED( file2_global_movie_song_group ); + FILL_ENABLED( file2_global_movie_song_group_and_genre ); +#undef FILL_ENABLED + menu.rows[delete_change].bEnabled = bAlreadyBGChangeHere; + + // set default choices + menu.rows[rate]. SetDefaultChoiceIfPresent( ssprintf("%2.0f%%",bgChange.m_fRate*100) ); + menu.rows[transition]. SetDefaultChoiceIfPresent( bgChange.m_sTransition ); + menu.rows[effect]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sEffect ); + menu.rows[file1_type].iDefaultChoice = none; + if( bgChange.m_def.m_sFile1 == RANDOM_BACKGROUND_FILE ) menu.rows[file1_type].iDefaultChoice = dynamic_random; + if( menu.rows[file1_song_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_bganimation; + if( menu.rows[file1_song_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_movie; + if( menu.rows[file1_song_still]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = song_bitmap; + if( menu.rows[file1_global_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_bganimation; + if( menu.rows[file1_global_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie; + if( menu.rows[file1_global_movie_song_group]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie_song_group; + if( menu.rows[file1_global_movie_song_group_and_genre]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile1 ) ) menu.rows[file1_type].iDefaultChoice = global_movie_song_group_and_genre; + menu.rows[file2_type].iDefaultChoice = none; + if( bgChange.m_def.m_sFile2 == RANDOM_BACKGROUND_FILE ) menu.rows[file2_type].iDefaultChoice = dynamic_random; + if( menu.rows[file2_song_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_bganimation; + if( menu.rows[file2_song_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_movie; + if( menu.rows[file2_song_still]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = song_bitmap; + if( menu.rows[file2_global_bganimation]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_bganimation; + if( menu.rows[file2_global_movie]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie; + if( menu.rows[file2_global_movie_song_group]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie_song_group; + if( menu.rows[file2_global_movie_song_group_and_genre]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sFile2 ) ) menu.rows[file2_type].iDefaultChoice = global_movie_song_group_and_genre; + menu.rows[color1]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor1 ); + menu.rows[color2]. SetDefaultChoiceIfPresent( bgChange.m_def.m_sColor2 ); + + EditMiniMenu( &g_BackgroundChange, SM_BackFromBGChange ); + } + } + else + { + SCREENMAN->SystemMessage( BG_CHANGE_STEP_TIMING ); + SCREENMAN->PlayInvalidSound(); + } + return true; + + case EDIT_BUTTON_OPEN_COURSE_MENU: + { + g_CourseMode.rows[0].choices.clear(); + g_CourseMode.rows[0].choices.push_back( CommonMetrics::LocalizeOptionItem("Off",false) ); + g_CourseMode.rows[0].iDefaultChoice = 0; + + vector courses; + SONGMAN->GetAllCourses( courses, false ); + for( unsigned i = 0; i < courses.size(); ++i ) + { + const Course *crs = courses[i]; + + bool bUsesThisSong = false; + for( unsigned e = 0; e < crs->m_vEntries.size(); ++e ) + { + if( crs->m_vEntries[e].songID.ToSong() != m_pSong ) + continue; + bUsesThisSong = true; + } + + if( bUsesThisSong ) + { + g_CourseMode.rows[0].choices.push_back( crs->GetDisplayFullTitle() ); + if( crs == GAMESTATE->m_pCurCourse ) + g_CourseMode.rows[0].iDefaultChoice = g_CourseMode.rows[0].choices.size()-1; + } + } + + EditMiniMenu( &g_CourseMode, SM_BackFromCourseModeMenu ); + } + return true; + case EDIT_BUTTON_OPEN_STEP_ATTACK_MENU: + { + this->DoStepAttackMenu(); + return true; + } + case EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU: + { + // TODO: Give Song/Step Timing switches/functions here? + Course *pCourse = GAMESTATE->m_pCurCourse; + if( pCourse == NULL ) + return false; + CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; + float fStartTime = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat ); + int iAttack = FindAttackAtTime( ce.attacks, fStartTime ); + + if( iAttack >= 0 ) + { + const RString sDuration = FloatToString(ce.attacks[iAttack].fSecsRemaining ); + + g_InsertCourseAttack.rows[remove].bEnabled = true; + if( g_InsertCourseAttack.rows[duration].choices.size() == 9 ) + g_InsertCourseAttack.rows[duration].choices.push_back( sDuration ); + else + g_InsertCourseAttack.rows[duration].choices.back() = sDuration; + g_InsertCourseAttack.rows[duration].iDefaultChoice = 9; + } + else + { + if( g_InsertCourseAttack.rows[duration].choices.size() == 10 ) + g_InsertCourseAttack.rows[duration].choices.pop_back(); + g_InsertCourseAttack.rows[duration].iDefaultChoice = 3; + } + + EditMiniMenu( &g_InsertCourseAttack, SM_BackFromInsertCourseAttack ); + } + return true; + case EDIT_BUTTON_ADD_STEP_MODS: + { + float start = -1; + float end = -1; + PlayerOptions po; + + if (m_NoteFieldEdit.m_iBeginMarker == -1) // not highlighted + { + po.FromString(""); + } + else + { + const TimingData &timing = GetAppropriateTiming(); + start = timing.GetElapsedTimeFromBeat(NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker)); + AttackArray &attacks = + (GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks); + int index = FindAttackAtTime(attacks, start); + + if (index >= 0) + { + po.FromString(""); + } + if (m_NoteFieldEdit.m_iEndMarker == -1) + { + end = m_pSong->m_fMusicLengthSeconds; + } + else + { + end = timing.GetElapsedTimeFromBeat(NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker)); + } + + } + ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; + this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); + g_fLastInsertAttackPositionSeconds = start; + g_fLastInsertAttackDurationSeconds = end - start; + toEdit.Assign( ModsLevel_Stage, po ); + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); + return true; + } + case EDIT_BUTTON_ADD_COURSE_MODS: + { + float fStart, fEnd; + PlayerOptions po; + const Course *pCourse = GAMESTATE->m_pCurCourse; + if( pCourse == NULL ) + return false; + const CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; + + if( m_NoteFieldEdit.m_iBeginMarker == -1 ) + { + fStart = -1; + fEnd = -1; + po.FromString( ce.sModifiers ); + } + else + { + // TODO: Give Song/Step Timing switches/functions here? + TimingData *timing = m_pSteps->GetTimingData(); + fStart = timing->GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + int iAttack = FindAttackAtTime( ce.attacks, fStart ); + + if( iAttack >= 0 ) + po.FromString( ce.attacks[iAttack].sModifiers ); + + if( m_NoteFieldEdit.m_iEndMarker == -1 ) + fEnd = m_pSong->m_fMusicLengthSeconds; + else + fEnd = timing->GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + } + g_fLastInsertAttackPositionSeconds = fStart; + g_fLastInsertAttackDurationSeconds = fEnd - fStart; + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.Assign( ModsLevel_Stage, po ); + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertCourseAttackPlayerOptions ); + + } + return true; + case EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP: + case EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE: + { + bool bTryGenre = EditB == EDIT_BUTTON_BAKE_RANDOM_FROM_SONG_GROUP_AND_GENRE; + RString sName = GetOneBakedRandomFile(m_pSong, bTryGenre); + if( sName.empty() ) + { + SCREENMAN->PlayInvalidSound(); + SCREENMAN->SystemMessage( NO_BACKGROUNDS_AVAILABLE ); + } + else + { + BackgroundLayer iLayer = BACKGROUND_LAYER_1; + BackgroundChange bgChange; + bgChange.m_fStartBeat = GAMESTATE->m_Position.m_fSongBeat; + FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(iLayer), bgc ) + { + if( bgc->m_fStartBeat == GAMESTATE->m_Position.m_fSongBeat ) + { + bgChange = *bgc; + m_pSong->GetBackgroundChanges(iLayer).erase( bgc ); + break; + } + } + bgChange.m_def.m_sFile1 = sName; + m_pSong->AddBackgroundChange( iLayer, bgChange ); + m_soundMarker.Play(); + } + } + return true; + + case EDIT_BUTTON_PLAY_FROM_START: + HandleMainMenuChoice( play_whole_song ); + return true; + + case EDIT_BUTTON_PLAY_FROM_CURSOR: + HandleMainMenuChoice( play_current_beat_to_end ); + return true; + + case EDIT_BUTTON_PLAY_SELECTION: + HandleMainMenuChoice( play_selection ); + return true; + case EDIT_BUTTON_RECORD_SELECTION: + if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) + { + HandleAlterMenuChoice( record ); + } + else + { + if( g_iDefaultRecordLength.Get() == -1 ) + { + m_iStartPlayingAt = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); + m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() + 1 ); + } + else + { + m_iStartPlayingAt = BeatToNoteRow( ftruncf(GetAppropriatePosition().m_fSongBeat, g_iDefaultRecordLength.Get()) ); + m_iStopPlayingAt = m_iStartPlayingAt + BeatToNoteRow( g_iDefaultRecordLength.Get() ); + } + + if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) + m_iStopPlayingAt = min( m_iStopPlayingAt, BeatToNoteRow(GetMaximumBeatForNewNote()) ); + + if( m_iStartPlayingAt >= m_iStopPlayingAt ) + { + SCREENMAN->PlayInvalidSound(); + return true; + } + + TransitionEditState( STATE_RECORDING ); + } + return true; + case EDIT_BUTTON_RECORD_FROM_CURSOR: + m_iStartPlayingAt = BeatToNoteRow(GetAppropriatePosition().m_fSongBeat); + m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); + TransitionEditState( STATE_RECORDING ); + return true; + + case EDIT_BUTTON_INSERT: + HandleAreaMenuChoice( insert_and_shift ); + SCREENMAN->PlayInvalidSound(); + return true; + + case EDIT_BUTTON_INSERT_SHIFT_PAUSES: + HandleAreaMenuChoice( shift_pauses_forward ); + SCREENMAN->PlayInvalidSound(); + return true; + + case EDIT_BUTTON_DELETE: + HandleAreaMenuChoice( delete_and_shift ); + SCREENMAN->PlayInvalidSound(); + return true; + + case EDIT_BUTTON_DELETE_SHIFT_PAUSES: + HandleAreaMenuChoice( shift_pauses_backward ); + SCREENMAN->PlayInvalidSound(); + return true; + + case EDIT_BUTTON_SAVE: + HandleMainMenuChoice( ScreenEdit::save ); + return true; + + case EDIT_BUTTON_UNDO: + Undo(); + return true; + + case EDIT_BUTTON_SWITCH_PLAYERS: + if( m_InputPlayerNumber == PLAYER_INVALID ) + return false; + enum_add( m_InputPlayerNumber, 1 ); + if( m_InputPlayerNumber == NUM_PLAYERS ) + m_InputPlayerNumber = PLAYER_1; + m_soundSwitchPlayer.Play(); + return true; + + case EDIT_BUTTON_SWITCH_TIMINGS: + GAMESTATE->m_bIsUsingStepTiming = !GAMESTATE->m_bIsUsingStepTiming; + m_soundSwitchTiming.Play(); + return true; + default: + return false; + } +} + +bool ScreenEdit::InputRecord( const InputEventPlus &input, EditButton EditB ) +{ + if( input.type == IET_FIRST_PRESS && EditB == EDIT_BUTTON_RETURN_TO_EDIT ) + { + TransitionEditState( STATE_EDITING ); + return true; + } + + if( input.pn != PLAYER_1 ) + return false; // ignore + + const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI ); + + //Is this actually a column? If not, ignore the input. + if( iCol == -1 ) + return false; + + switch( input.type ) + { + DEFAULT_FAIL( input.type ); + case IET_FIRST_PRESS: + { + if( EditIsBeingPressed(EDIT_BUTTON_REMOVE_NOTE) ) + { + // Remove notes in Update. + return false; + } + + // Add a tap + float fBeat = GetBeat(); + fBeat = Quantize( fBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ); + + const int iRow = BeatToNoteRow( fBeat ); + if( iRow < 0 ) + return false; + + // Don't add outside of the range. + if( iRow < m_iStartPlayingAt || iRow >= m_iStopPlayingAt ) + return false; + + // Remove hold if any so that we don't have taps inside of a hold. + int iHeadRow; + if( m_NoteDataRecord.IsHoldNoteAtRow( iCol, iRow, &iHeadRow ) ) + m_NoteDataRecord.SetTapNote( iCol, iHeadRow, TAP_EMPTY ); + + TapNote tn = TAP_ORIGINAL_TAP; + tn.pn = m_InputPlayerNumber; + m_NoteDataRecord.SetTapNote( iCol, iRow, tn ); + m_NoteFieldRecord.Step( iCol, TNS_W1 ); + } + return true; + case IET_REPEAT: + case IET_RELEASE: + // don't add or extend holds here; we do it in Update() + return false; + } +} + +bool ScreenEdit::InputRecordPaused( const InputEventPlus &input, EditButton EditB ) +{ + if( input.type != IET_FIRST_PRESS ) + return false; // don't care + + switch( EditB ) + { + case EDIT_BUTTON_UNDO: + /* We've already actually committed changes to m_NoteDataEdit, so all we have + * to do to undo is Undo() as usual, and copy the note data back in. */ + Undo(); + m_NoteDataRecord.CopyAll( m_NoteDataEdit ); + return true; + + case EDIT_BUTTON_PLAY_SELECTION: + TransitionEditState( STATE_PLAYING ); + return true; + + case EDIT_BUTTON_RECORD_SELECTION: + TransitionEditState( STATE_RECORDING ); + return true; + + case EDIT_BUTTON_RECORD_FROM_CURSOR: + if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) + { + float fMaximumBeat = GetMaximumBeatForNewNote(); + if( NoteRowToBeat(m_iStopPlayingAt) >= fMaximumBeat ) + { + // We're already at the end. + SCREENMAN->PlayInvalidSound(); + return true; + } + } + + // Pick up where we left off. + { + int iSize = m_iStopPlayingAt - m_iStartPlayingAt; + m_iStartPlayingAt = m_iStopPlayingAt; + m_iStopPlayingAt += iSize; + + if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) + m_iStopPlayingAt = min( m_iStopPlayingAt, BeatToNoteRow(GetMaximumBeatForNewNote()) ); + } + + TransitionEditState( STATE_RECORDING ); + return true; + + case EDIT_BUTTON_RETURN_TO_EDIT: + TransitionEditState( STATE_EDITING ); + return true; + default: + return false; + } +} + +bool ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB ) +{ + switch( input.type ) + { + case IET_RELEASE: + case IET_FIRST_PRESS: + break; + default: + return false; + } + + GameButtonType gbt = GAMESTATE->m_pCurGame->GetPerButtonInfo(input.GameI.button)->m_gbt; + + if( GamePreferences::m_AutoPlay == PC_HUMAN && GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay == 0 ) + { + const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI ); + bool bRelease = input.type == IET_RELEASE; + switch( input.pn ) + { + case PLAYER_2: + // ignore player 2 input unless this mode requires it + if( GAMESTATE->GetCurrentStyle()->m_StyleType != StyleType_TwoPlayersSharedSides ) + break; + + // fall through to input handling logic: + case PLAYER_1: + { + switch( gbt ) + { + case GameButtonType_Step: + if( iCol != -1 ) + m_Player->Step( iCol, -1, input.DeviceI.ts, false, bRelease ); + return true; + case GameButtonType_Fret: + if( iCol != -1 ) + m_Player->Fret( iCol, -1, input.DeviceI.ts, false, bRelease ); + return true; + case GameButtonType_Strum: + m_Player->Strum( iCol, -1, input.DeviceI.ts, false, bRelease ); + return true; + default: + break; + } + } + default: break; + } + } + + if( gbt == GameButtonType_INVALID && input.type == IET_FIRST_PRESS ) + { + switch( EditB ) + { + case EDIT_BUTTON_RETURN_TO_EDIT: + /* When exiting play mode manually, leave the cursor where it is. */ + m_fBeatToReturnTo = GetAppropriatePosition().m_fSongBeat; + TransitionEditState( STATE_EDITING ); + return true; + case EDIT_BUTTON_OFFSET_UP: + case EDIT_BUTTON_OFFSET_DOWN: + { + float fOffsetDelta; + switch( EditB ) + { + DEFAULT_FAIL( EditB ); + case EDIT_BUTTON_OFFSET_DOWN: fOffsetDelta = -0.020f; break; + case EDIT_BUTTON_OFFSET_UP: fOffsetDelta = +0.020f; break; + } + + if( EditIsBeingPressed( EDIT_BUTTON_ADJUST_FINE ) ) + { + fOffsetDelta /= 20; /* 1ms */ + } + else if( input.type == IET_REPEAT ) + { + if( INPUTFILTER->GetSecsHeld(input.DeviceI) < 1.0f ) + fOffsetDelta *= 10; + else + fOffsetDelta *= 40; + } + + GetAppropriateTimingForUpdate().m_fBeat0OffsetInSeconds += fOffsetDelta; + if (!GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += fOffsetDelta; + } + } + return true; + default: break; + } + } + return false; +} + +void ScreenEdit::TransitionEditState( EditState em ) +{ + EditState old = m_EditState; + + // If we're going from recording to paused, come back when we're done. + if( old == STATE_RECORDING_PAUSED && em == STATE_PLAYING ) + m_bReturnToRecordMenuAfterPlay = true; + + const bool bStateChanging = em != old; + +#if 0 + // If switching out of record, open the menu. + { + bool bGoToRecordMenu = (old == STATE_RECORDING); + if( m_bReturnToRecordMenuAfterPlay && old == STATE_PLAYING ) + { + bGoToRecordMenu = true; + m_bReturnToRecordMenuAfterPlay = false; + } + + if( bGoToRecordMenu ) + em = STATE_RECORDING_PAUSED; + } +#endif + + // If we're playing music or assist ticks when changing modes, stop. + SOUND->StopMusic(); + if( m_pSoundMusic ) + m_pSoundMusic->StopPlaying(); + m_GameplayAssist.StopPlaying(); + GAMESTATE->m_bGameplayLeadIn.Set( true ); + + if( bStateChanging ) + { + switch( old ) + { + case STATE_EDITING: + // If exiting EDIT mode, save the cursor position. + m_fBeatToReturnTo = GetAppropriatePosition().m_fSongBeat; + break; + + case STATE_PLAYING: + AdjustSync::HandleSongEnd(); + if (!GAMESTATE->m_bIsUsingStepTiming) + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = backupStepTiming; + if( AdjustSync::IsSyncDataChanged() ) + ScreenSaveSync::PromptSaveSync(); + break; + + case STATE_RECORDING: + SetDirty( true ); + if (!GAMESTATE->m_bIsUsingStepTiming) + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = backupStepTiming; + SaveUndo(); + + // delete old TapNotes in the range + m_NoteDataEdit.ClearRange( m_iStartPlayingAt, m_iStopPlayingAt ); + m_NoteDataEdit.CopyRange( m_NoteDataRecord, m_iStartPlayingAt, m_iStopPlayingAt, m_iStartPlayingAt ); + m_NoteDataRecord.ClearAll(); + + CheckNumberOfNotesAndUndo(); + break; + default: break; + } + } + + // Set up player options for this mode. (EDITING uses m_PlayerStateEdit, + // which we don't need to change.) + if( em != STATE_EDITING ) + { + // Stop displaying course attacks, if any. + GAMESTATE->m_pPlayerState[PLAYER_1]->RemoveActiveAttacks(); + // Load the player's default PlayerOptions. + GAMESTATE->m_pPlayerState[PLAYER_1]->RebuildPlayerOptionsFromActiveAttacks(); + + // Snap to current options. + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.SetCurrentToLevel( ModsLevel_Stage ); + } + + switch( em ) + { + DEFAULT_FAIL( em ); + case STATE_EDITING: + // Important: people will stop playing, change the BG and start again; make sure we reload + m_Background.Unload(); + m_Foreground.Unload(); + + // Restore the cursor position + Quantize + Clamp + SetBeat( max( 0, Quantize( m_fBeatToReturnTo, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) ) ) ); + GAMESTATE->m_bInStepEditor = true; + break; + + case STATE_PLAYING: + case STATE_RECORDING: + { + if( bStateChanging ) + AdjustSync::ResetOriginalSyncData(); + + /* Give a 1 second lead-in. If we're loading Player, this must be done first. + * Also be sure to get the right timing. */ + float fSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1; + GAMESTATE->UpdateSongPosition( fSeconds, GetAppropriateTiming(), RageZeroTimer, true ); + + GAMESTATE->m_bGameplayLeadIn.Set( false ); + + if (!GAMESTATE->m_bIsUsingStepTiming) + { + // Substitute the song timing for the step timing during + // previuw if we're in song mode + backupStepTiming = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing; + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing.Clear(); + } + + /* Reset the note skin, in case preferences have changed. */ + // XXX + // GAMESTATE->ResetNoteSkins(); + //GAMESTATE->res + GAMESTATE->m_bInStepEditor = false; + break; + } + case STATE_RECORDING_PAUSED: + GAMESTATE->m_bInStepEditor = false; + break; + } + + switch( em ) + { + case STATE_PLAYING: + // If we're in course display mode, set that up. + SetupCourseAttacks(); + + m_Player.Load( m_NoteDataEdit ); + + if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_fPlayerAutoPlay != 0 ) + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = PC_AUTOPLAY; + else + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerController = GamePreferences::m_AutoPlay; + + if( g_bEditorShowBGChangesPlay ) + { + /* FirstBeat affects backgrounds, so commit changes to memory (not to disk) + * and recalc it. */ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + ASSERT( pSteps != NULL ); + pSteps->SetNoteData( m_NoteDataEdit ); + m_pSong->ReCalculateRadarValuesAndLastSecond(); + + m_Background.Unload(); + m_Background.LoadFromSong( m_pSong ); + + m_Foreground.Unload(); + m_Foreground.LoadFromSong( m_pSong ); + } + + break; + case STATE_RECORDING: + case STATE_RECORDING_PAUSED: + // initialize m_NoteFieldRecord + m_NoteDataRecord.CopyAll( m_NoteDataEdit ); + + // highlight the section being recorded + m_NoteFieldRecord.m_iBeginMarker = m_iStartPlayingAt; + m_NoteFieldRecord.m_iEndMarker = m_iStopPlayingAt; + + break; + default: break; + } + + // Show/hide depending on edit state (em) + m_sprOverlay->PlayCommand( EditStateToString(em) ); + m_sprUnderlay->PlayCommand( EditStateToString(em) ); + + m_Background.SetVisible( g_bEditorShowBGChangesPlay && em != STATE_EDITING ); + m_textInputTips.SetVisible( em == STATE_EDITING ); + m_textInfo.SetVisible( em == STATE_EDITING ); + // Play the OnCommands again so that these will be re-hidden if the OnCommand hides them. + if( em == STATE_EDITING ) + { + m_textInputTips.PlayCommand( "On" ); + m_textInfo.PlayCommand( "On" ); + } + m_textPlayRecordHelp.SetVisible( em != STATE_EDITING ); + m_SnapDisplay.SetVisible( em == STATE_EDITING ); + m_NoteFieldEdit.SetVisible( em == STATE_EDITING ); + m_NoteFieldRecord.SetVisible( em == STATE_RECORDING || em == STATE_RECORDING_PAUSED ); + m_Player->SetVisible( em == STATE_PLAYING ); + m_Foreground.SetVisible( g_bEditorShowBGChangesPlay && em != STATE_EDITING ); + + switch( em ) + { + case STATE_PLAYING: + case STATE_RECORDING: + { + const float fStartSeconds = GetAppropriateTiming().GetElapsedTimeFromBeat( GetBeat() ); + LOG->Trace( "Starting playback at %f", fStartSeconds ); + + RageSoundParams p; + p.m_fSpeed = GAMESTATE->m_SongOptions.GetCurrent().m_fMusicRate; + p.m_StartSecond = fStartSeconds; + p.StopMode = RageSoundParams::M_CONTINUE; + m_pSoundMusic->SetProperty( "AccurateSync", true ); + m_pSoundMusic->Play( &p ); + break; + } + default: break; + } + + m_EditState = em; +} + +void ScreenEdit::ScrollTo( float fDestinationBeat ) +{ + CLAMP( fDestinationBeat, 0, GetMaximumBeatForMoving() ); + + // Don't play the sound and do the hold note logic below if our position didn't change. + const float fOriginalBeat = GetAppropriatePosition().m_fSongBeat; + if( fOriginalBeat == fDestinationBeat ) + return; + + SetBeat(fDestinationBeat); + + // check to see if they're holding a button + for( int n=0; n= m_NoteDataEdit.GetNumTracks() ) + continue; // skip + + EditButton b = EditButton(EDIT_BUTTON_COLUMN_0+n); + if( !EditIsBeingPressed(b) ) + continue; + + // create a new hold note + int iStartRow = BeatToNoteRow( min(fOriginalBeat, fDestinationBeat) ); + int iEndRow = BeatToNoteRow( max(fOriginalBeat, fDestinationBeat) ); + + // Don't SaveUndo. We want to undo the whole hold, not just the last segment + // that the user made. Dragging the hold bigger can only absorb and remove + // other taps, so dragging won't cause us to exceed the note limit. + TapNote tn = EditIsBeingPressed(EDIT_BUTTON_LAY_ROLL) ? TAP_ORIGINAL_ROLL_HEAD : TAP_ORIGINAL_HOLD_HEAD; + + tn.pn = m_InputPlayerNumber; + m_NoteDataEdit.AddHoldNote( iCol, iStartRow, iEndRow, tn ); + } + + if( EditIsBeingPressed(EDIT_BUTTON_SCROLL_SELECT) ) + { + /* Shift is being held. + * If this is the first time we've moved since shift was depressed, + * the old position (before this move) becomes the start pos: */ + int iDestinationRow = BeatToNoteRow( fDestinationBeat ); + if( m_iShiftAnchor == -1 ) + m_iShiftAnchor = BeatToNoteRow(fOriginalBeat); + + if( iDestinationRow == m_iShiftAnchor ) + { + // We're back at the anchor, so we have nothing selected. + m_NoteFieldEdit.m_iBeginMarker = m_NoteFieldEdit.m_iEndMarker = -1; + } + else + { + m_NoteFieldEdit.m_iBeginMarker = m_iShiftAnchor; + m_NoteFieldEdit.m_iEndMarker = iDestinationRow; + if( m_NoteFieldEdit.m_iBeginMarker > m_NoteFieldEdit.m_iEndMarker ) + swap( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); + } + } + + m_soundChangeLine.Play(); +} + +static LocalizedString NEW_KEYSOUND_FILE("ScreenEdit", "Enter New Keysound File"); + +void ScreenEdit::HandleMessage( const Message &msg ) +{ + if( msg == "Judgment" ) + { + PlayerNumber pn; + msg.GetParam( "Player", pn ); + + if( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent().m_bMuteOnError ) + { + RageSoundReader *pSoundReader = m_AutoKeysounds.GetPlayerSound( pn ); + if( pSoundReader == NULL ) + pSoundReader = m_AutoKeysounds.GetSharedSound(); + + HoldNoteScore hns; + msg.GetParam( "HoldNoteScore", hns ); + TapNoteScore tns; + msg.GetParam( "TapNoteScore", tns ); + + bool bOn = false; + if( hns != HoldNoteScore_Invalid ) + bOn = hns != HNS_LetGo; + else + bOn = tns != TNS_Miss; + + if( pSoundReader ) + pSoundReader->SetProperty( "Volume", bOn? 1.0f:0.0f ); + } + } + if( msg == Message_SongModified ) + { + SetDirty( true ); + } + Screen::HandleMessage( 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 ) + m_bTextInfoNeedsUpdate = true; + + if( SM == SM_UpdateTextInfo ) + { + UpdateTextInfo(); + this->PostScreenMessage( SM_UpdateTextInfo, 0.1f ); + } + else if( SM == SM_GoToNextScreen ) + { + GAMESTATE->m_EditMode = EditMode_Invalid; + } + else if( SM == SM_BackFromMainMenu ) + { + HandleMainMenuChoice( (MainMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } + else if( SM == SM_BackFromAreaMenu ) + { + AreaMenuChoice amc = static_cast(ScreenMiniMenu::s_iLastRowCode); + const vector &answers = ScreenMiniMenu::s_viLastAnswers; + HandleAreaMenuChoice( amc, answers ); + } + else if( SM == SM_BackFromAlterMenu ) + { + HandleAlterMenuChoice( (AlterMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } + else if( SM == SM_BackFromStepsInformation ) + { + HandleStepsInformationChoice( (StepsInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } + else if (SM == SM_BackFromStepsData) + { + HandleStepsDataChoice((StepsDataChoice)ScreenMiniMenu::s_iLastRowCode, + ScreenMiniMenu::s_viLastAnswers); + } + + else if( SM == SM_BackFromSongInformation ) + { + HandleSongInformationChoice( (SongInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } + else if( SM == SM_BackFromTimingDataInformation ) + { + HandleTimingDataInformationChoice( (TimingDataInformationChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + } + else if( SM == SM_BackFromDifficultyMeterChange ) + { + int i = StringToInt( ScreenTextEntry::s_sLastAnswer ); + GAMESTATE->m_pCurSteps[PLAYER_1]->SetMeter(i); + SetDirty( true ); + } + else if( SM == SM_BackFromBeat0Change && !ScreenTextEntry::s_bCancelledLast ) + { + float fBeat0 = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + + TimingData &timing = GetAppropriateTimingForUpdate(); + float old = timing.m_fBeat0OffsetInSeconds; + timing.m_fBeat0OffsetInSeconds = fBeat0; + float delta = timing.m_fBeat0OffsetInSeconds - old; + + if (GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.UpdateStartTimes(delta); + } + else + { + GAMESTATE->m_pCurSong->m_Attacks.UpdateStartTimes(delta); + GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds += delta; + } + + SetDirty( true ); + } + else if( SM == SM_BackFromBPMChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fBPM = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + + if( fBPM > 0 ) + GetAppropriateTimingForUpdate().AddSegment( BPMSegment(GetRow(), fBPM) ); + + SetDirty( true ); + } + else if( SM == SM_BackFromStopChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fStop = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + + if( fStop >= 0 ) + GetAppropriateTimingForUpdate().AddSegment( StopSegment(GetRow(), fStop) ); + + SetDirty( true ); + } + else if( SM == SM_BackFromDelayChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fDelay = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + + if( fDelay >= 0 ) + GetAppropriateTimingForUpdate().AddSegment( DelaySegment(GetRow(), fDelay) ); + + SetDirty( true ); + } + else if( SM == SM_BackFromTimeSignatureChange && !ScreenTextEntry::s_bCancelledLast ) + { + int iNum, iDen; + + if( sscanf( ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iNum, &iDen ) == 2 ) + GetAppropriateTimingForUpdate().AddSegment( TimeSignatureSegment(GetRow(), iNum, iDen) ); + + SetDirty( true ); + } + else if ( SM == SM_BackFromTickcountChange && !ScreenTextEntry::s_bCancelledLast ) + { + int iTick = StringToInt( ScreenTextEntry::s_sLastAnswer ); + + if ( iTick >= 0 && iTick <= ROWS_PER_BEAT ) + GetAppropriateTimingForUpdate().AddSegment( TickcountSegment( GetRow(), iTick) ); + + SetDirty( true ); + } + else if ( SM == SM_BackFromComboChange && !ScreenTextEntry::s_bCancelledLast ) + { + int iCombo, iMiss; + + if (sscanf(ScreenTextEntry::s_sLastAnswer.c_str(), " %d / %d ", &iCombo, &iMiss) == 2) + GetAppropriateTimingForUpdate().AddSegment( ComboSegment(GetRow(), iCombo, iMiss) ); + + SetDirty( true ); + } + else if ( SM == SM_BackFromLabelChange && !ScreenTextEntry::s_bCancelledLast ) + { + RString sLabel = ScreenTextEntry::s_sLastAnswer; + + if ( !GetAppropriateTiming().DoesLabelExist(sLabel) ) + { + // XXX: these should be in the NotesWriters where they're needed. + sLabel.Replace("=", "_"); + sLabel.Replace(",", "_"); + GetAppropriateTimingForUpdate().AddSegment( LabelSegment(GetRow(), sLabel) ); + SetDirty( true ); + } + } + else if ( SM == SM_BackFromWarpChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fWarp = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fWarp >= 0 ) // allow 0 to kill a warp. + { + GetAppropriateTimingForUpdate().SetWarpAtBeat( GetBeat(), fWarp ); + SetDirty( true ); + } + } + else if( SM == SM_BackFromSpeedPercentChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fNum = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + GetAppropriateTimingForUpdate().SetSpeedPercentAtBeat( GetBeat(), fNum ); + SetDirty( true ); + } + else if ( SM == SM_BackFromSpeedWaitChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fDen = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fDen >= 0) + { + GetAppropriateTimingForUpdate().SetSpeedWaitAtBeat( GetBeat(), fDen ); + } + SetDirty( true ); + } + else if ( SM == SM_BackFromSpeedModeChange && !ScreenTextEntry::s_bCancelledLast ) + { + if( ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "b" || ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "B" ) + { + GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), SpeedSegment::UNIT_BEATS ); + } + else if( ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "s" || ScreenTextEntry::s_sLastAnswer.substr(0, 1) == "S" ) + { + GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), SpeedSegment::UNIT_SECONDS ); + } + else + { + int tmp = StringToInt(ScreenTextEntry::s_sLastAnswer ); + + SpeedSegment::BaseUnit unit = (tmp == 0 ) ? + SpeedSegment::UNIT_BEATS : SpeedSegment::UNIT_SECONDS; + + GetAppropriateTimingForUpdate().SetSpeedModeAtBeat( GetBeat(), unit ); + } + SetDirty( true ); + } + else if( SM == SM_BackFromScrollChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fNum = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + GetAppropriateTimingForUpdate().SetScrollAtBeat( GetBeat(), fNum ); + SetDirty( true ); + } + else if ( SM == SM_BackFromFakeChange && !ScreenTextEntry::s_bCancelledLast ) + { + float fFake = StringToFloat( ScreenTextEntry::s_sLastAnswer ); + if( fFake >= 0 ) // allow 0 to kill a fake. + { + GetAppropriateTimingForUpdate().AddSegment( FakeSegment(GetRow(), fFake) ); + SetDirty( true ); + } + } + + else if( SM == SM_BackFromBGChange ) + { + HandleBGChangeChoice( (BGChangeChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers ); + SetDirty( true ); + } + else if( SM == SM_BackFromCourseModeMenu ) + { + const int num = ScreenMiniMenu::s_viLastAnswers[0]; + GAMESTATE->m_pCurCourse.Set( NULL ); + if( num != 0 ) + { + const RString name = g_CourseMode.rows[0].choices[num]; + Course *pCourse = SONGMAN->FindCourse( name ); + + int iCourseEntryIndex = -1; + int index = 0; + for (CourseEntry const &i : pCourse->m_vEntries) + { + if( i.songID.ToSong() == GAMESTATE->m_pCurSong.Get() ) + iCourseEntryIndex = index; + ++index; + } + + ASSERT( iCourseEntryIndex != -1 ); + + GAMESTATE->m_pCurCourse.Set( pCourse ); + GAMESTATE->m_iEditCourseEntryIndex.Set( iCourseEntryIndex ); + ASSERT( GAMESTATE->m_pCurCourse != NULL ); + } + } + else if (SM == SM_BackFromKeysoundTrack) + { + const int track = ScreenMiniMenu::s_iLastRowCode; + const int tracks = m_NoteDataEdit.GetNumTracks(); + const int row = this->GetRow(); + unsigned int sound = ScreenMiniMenu::s_viLastAnswers[track]; + vector &kses = m_pSong->m_vsKeysoundFile; + + if (track < tracks) + { + if (sound == kses.size()) + { + // create a new sound (filename), point it. + // if it's empty, make it an auto keysound. + ScreenTextEntry::TextEntry(SM_BackFromNewKeysound, NEW_KEYSOUND_FILE, "", 64); + return; + } + const TapNote &oldNote = m_NoteDataEdit.GetTapNote(track, row); + TapNote newNote = oldNote; // need to lose the const. not feeling like casting. + if (sound < kses.size()) + { + // set note at this row to use this keysound file. + // if it's empty, make it an auto keysound. + newNote.iKeysoundIndex = sound; + if (newNote.type == TapNote::empty) + { + newNote.type = TapNote::autoKeysound; // keysounds need something non empty. + } + } + else // sound > kses.size() + { + // remove the sound. if it's an auto keysound, make it empty. + newNote.iKeysoundIndex = -1; + if (newNote.type == TapNote::autoKeysound) + { + newNote.type = TapNote::empty; // autoKeysound with no sound is pointless. + } + } + m_NoteDataEdit.SetTapNote(track, row, newNote); + } + else if (track == tracks) + { + kses.erase(kses.begin() + sound); + // TODO: Make the following a part of NoteData? + for (int t = 0; t < tracks; ++t) + { + FOREACH_NONEMPTY_ROW_IN_TRACK(m_NoteDataEdit, t, r) + { + const TapNote &oldNote = m_NoteDataEdit.GetTapNote(t, r); + TapNote newNote = oldNote; // need to lose the const. not feeling like casting. + if (newNote.iKeysoundIndex == static_cast(sound)) + { + newNote.iKeysoundIndex = -1; + if (newNote.type == TapNote::autoKeysound) + newNote.type = TapNote::empty; + } + else if (newNote.iKeysoundIndex > static_cast(sound)) + newNote.iKeysoundIndex--; + + m_NoteDataEdit.SetTapNote(t, r, newNote); + } + } + } + SetDirty(true); + } + else if (SM == SM_BackFromNewKeysound && !ScreenTextEntry::s_bCancelledLast) + { + RString answer = ScreenTextEntry::s_sLastAnswer; + const int track = ScreenMiniMenu::s_iLastRowCode; // still keeps the same value. + const int row = this->GetRow(); + const TapNote &oldNote = m_NoteDataEdit.GetTapNote(track, row); + TapNote newNote = oldNote; // need to lose the const. not feeling like casting. + vector &kses = m_pSong->m_vsKeysoundFile; + unsigned pos = find(kses.begin(), kses.end(), answer) - kses.begin(); + if (pos == kses.size()) + { + newNote.iKeysoundIndex = kses.size(); + kses.push_back(answer); + } + else + { + newNote.iKeysoundIndex = pos; + } + if (newNote.type == TapNote::empty) + newNote.type = TapNote::autoKeysound; // keysounds need something non empty. + m_NoteDataEdit.SetTapNote(track, row, newNote); + SetDirty(true); + } + else if( SM == SM_BackFromOptions ) + { + // The options may have changed the note skin. + m_NoteFieldRecord.CacheAllUsedNoteSkins(); + m_Player->CacheAllUsedNoteSkins(); + + // stop any music that screen may have been playing + SOUND->StopMusic(); + } + else if( SM == SM_BackFromInsertTapAttack ) + { + int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0]; + g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertTapAttack.rows[0].choices[iDurationChoice] ); + + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertTapAttackPlayerOptions ); + } + else if( SM == SM_BackFromInsertTapAttackPlayerOptions ) + { + PlayerOptions poChosen = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetPreferred(); + RString sMods = poChosen.GetString(); + const int row = BeatToNoteRow( GAMESTATE->m_Position.m_fSongBeat ); + + TapNote tn( + TapNote::attack, + TapNote::SubType_Invalid, + TapNote::original, + sMods, + g_fLastInsertAttackDurationSeconds, + -1 ); + tn.pn = m_InputPlayerNumber; + SetDirty( true ); + SaveUndo(); + 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, + FloatToString(attack.fStartSecond), + 10); + } + else if (option == 1) // adjusting the length of the attack + { + ScreenTextEntry::TextEntry(SM_BackFromEditingAttackLength, + EDIT_ATTACK_LENGTH, + FloatToString(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]; + const 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(FloatToString(attack.fStartSecond)); + g_IndividualAttack.rows.push_back(MenuRowDef(1, + "Secs Remaining", + true, + EditMode_CourseMods, + true, + true, + 0, + NULL)); + g_IndividualAttack.rows[1].SetOneUnthemedChoice(FloatToString(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]; + const TimingData &timing = GetAppropriateTiming(); + g_fLastInsertAttackPositionSeconds = timing.GetElapsedTimeFromBeat( GetBeat() ); + g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertStepAttack.rows[0].choices[iDurationChoice] ); + AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks; + int iAttack = FindAttackAtTime(attacks, g_fLastInsertAttackPositionSeconds); + + if (ScreenMiniMenu::s_iLastRowCode == ScreenEdit::remove ) + { + if (iAttack > 0) + attacks.erase(attacks.begin() + iAttack); + } + else + { + ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; + this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); + PlayerOptions po; + if (iAttack >= 0) + po.FromString(attacks[iAttack].sModifiers); + + toEdit.Assign( ModsLevel_Preferred, po ); + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); + } + SetDirty(true); + } + else if( SM == SM_BackFromInsertCourseAttack ) + { + int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0]; + Course *pCourse = GAMESTATE->m_pCurCourse; + CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; + int iAttack; + + // TODO: Handle Song/Step Timing functions/switches here? + + g_fLastInsertAttackPositionSeconds = m_pSteps->GetTimingData()->GetElapsedTimeFromBeat( GAMESTATE->m_Position.m_fSongBeat ); + g_fLastInsertAttackDurationSeconds = StringToFloat( g_InsertCourseAttack.rows[0].choices[iDurationChoice] ); + iAttack = FindAttackAtTime( ce.attacks, g_fLastInsertAttackPositionSeconds ); + + if( ScreenMiniMenu::s_iLastRowCode == ScreenEdit::remove ) + { + ASSERT( iAttack >= 0 ); + ce.attacks.erase( ce.attacks.begin() + iAttack ); + } + else + { + PlayerOptions po; + + if( iAttack >= 0 ) + po.FromString( ce.attacks[iAttack].sModifiers ); + + GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.Assign( ModsLevel_Preferred, po ); + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertCourseAttackPlayerOptions ); + } + } + else if (SM == SM_BackFromInsertStepAttackPlayerOptions) + { + ModsGroup &toRestore = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; + PlayerOptions poChosen = toRestore.GetPreferred(); + RString mods = poChosen.GetString(); + + if (g_fLastInsertAttackPositionSeconds >= 0) + { + Attack a(ATTACK_LEVEL_1, + g_fLastInsertAttackPositionSeconds, + g_fLastInsertAttackDurationSeconds, + mods, + false, + false); + AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? m_pSteps->m_Attacks : m_pSong->m_Attacks; + int index = FindAttackAtTime(attacks, g_fLastInsertAttackPositionSeconds); + if (index >= 0) + attacks[index] = a; + else + attacks.push_back(a); + } + toRestore.Assign(ModsLevel_Preferred, this->originalPlayerOptions.GetPreferred()); + } + else if( SM == SM_BackFromInsertCourseAttackPlayerOptions ) + { + PlayerOptions poChosen = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetPreferred(); + RString sMods = poChosen.GetString(); + + Course *pCourse = GAMESTATE->m_pCurCourse; + CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex]; + if( g_fLastInsertAttackPositionSeconds < 0 ) + { + ce.sModifiers = sMods; + } + else + { + Attack a( + ATTACK_LEVEL_1, + g_fLastInsertAttackPositionSeconds, + g_fLastInsertAttackDurationSeconds, + sMods, + false, + false ); + int iAttack = FindAttackAtTime( ce.attacks, g_fLastInsertAttackPositionSeconds ); + + if( iAttack >= 0 ) + ce.attacks[iAttack] = a; + else + ce.attacks.push_back( a ); + } + } + else if( SM == SM_DoRevertToLastSave ) + { + if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) + { + SaveUndo(); + CopyFromLastSave(); + m_pSteps->GetNoteData( m_NoteDataEdit ); + SetDirty( false ); + } + } + else if( SM == SM_DoRevertFromDisk ) + { + if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) + { + SaveUndo(); + RevertFromDisk(); + m_pSteps->GetNoteData( m_NoteDataEdit ); + SetDirty( false ); + } + } + else if( SM == SM_DoEraseStepTiming ) + { + if( ScreenPrompt::s_LastAnswer == ANSWER_YES ) + { + SaveUndo(); + m_pSteps->m_Timing.Clear(); + SetDirty( true ); + } + } + else if( SM == SM_DoSaveAndExit ) // just asked "save before exiting? yes, no, cancel" + { + switch( ScreenPrompt::s_LastAnswer ) + { + DEFAULT_FAIL( ScreenPrompt::s_LastAnswer ); + case ANSWER_YES: + // This will send SM_SaveSuccessful or SM_SaveFailed. + HandleMainMenuChoice( ScreenEdit::save_on_exit ); + return; + case ANSWER_NO: + // Don't save; just exit. + SCREENMAN->SendMessageToTopScreen( SM_DoExit ); + return; + case ANSWER_CANCEL: + break; // do nothing + } + } + else if( SM == SM_SaveSuccessful ) + { + LOG->Trace( "Save successful." ); + CopyToLastSave(); + SetDirty( false ); + SONGMAN->Invalidate( GAMESTATE->m_pCurSong ); + + if( m_CurrentAction == save_on_exit ) + ScreenPrompt::Prompt( SM_DoExit, SAVE_SUCCESSFUL ); + else + SCREENMAN->SystemMessage( SAVE_SUCCESSFUL ); + } + else if( SM == SM_SaveFailed ) // save failed; stay in the editor + { + /* We committed the steps to SongManager. Revert to the last save, and + * recommit the reversion to SongManager. */ + LOG->Trace( "Save failed. Changes uncommitted from memory." ); + CopyFromLastSave(); + m_pSteps->SetNoteData( m_NoteDataEdit ); + } + else if( SM == SM_DoExit ) + { + // IMPORTANT: CopyFromLastSave before deleting the Steps below + CopyFromLastSave(); + + /* The user has been given a choice to save. + * Delete all unsaved steps before exiting the editor. */ + /* FIXME: This code causes all the steps to be deleted if you quit + * without saving. However, without this code, any new steps will get + * saved on quit. -aj */ + + // At this point, the last good song copy is in use. + Song *pSong = GAMESTATE->m_pCurSong; + const vector &apSteps = pSong->GetAllSteps(); + vector apToDelete; + for (Steps *s : apSteps) + { + // If we're not on the same style, let it go. + if( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType != s->m_StepsType ) + continue; + // If autogenned, it isn't being saved. + if( s->IsAutogen() ) + continue; + // If the notedata has content, let it go. + if( !s->GetNoteData().IsEmpty() ) + continue; + // It's hard to say if these steps were saved to disk or not. + /* + if( !(s->GetSavedToDisk() ) + continue; + */ + apToDelete.push_back( s ); + } + for (Steps *pSteps : apToDelete) + { + pSong->DeleteSteps( pSteps ); + if( m_pSteps == pSteps ) + m_pSteps = NULL; + if( GAMESTATE->m_pCurSteps[PLAYER_1].Get() == pSteps ) + GAMESTATE->m_pCurSteps[PLAYER_1].Set( NULL ); + } + + + m_Out.StartTransitioning( SM_GoToNextScreen ); + } + else if( SM == SM_GainFocus ) + { + /* When another screen comes up, RageSounds takes over the sound timer. When we come + * back, put the timer back to where it was. */ + SetBeat(m_fTrailingBeat); + } + else if( SM == SM_LoseFocus ) + { + // Snap the trailing beat, in case we lose focus while tweening. + m_fTrailingBeat = GetBeat(); //GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; + } + + ScreenWithMenuElements::HandleScreenMessage( SM ); +} + +void ScreenEdit::OnSnapModeChange() +{ + m_soundChangeSnap.Play(); + + NoteType nt = m_SnapDisplay.GetNoteType(); + int iStepIndex = BeatToNoteRow( GetBeat() ); + int iElementsPerNoteType = BeatToNoteRow( NoteTypeToBeat(nt) ); + int iStepIndexHangover = iStepIndex % iElementsPerNoteType; + SetBeat( GetBeat() - NoteRowToBeat( iStepIndexHangover ) ); +} + + + +// Helper function for below + +// Begin helper functions for InputEdit + + +static void ChangeDescription( const RString &sNew ) +{ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + + // Don't erase edit descriptions. + if( sNew.empty() && pSteps->GetDifficulty() == Difficulty_Edit ) + return; + + pSteps->SetDescription(sNew); +} + +static void ChangeChartName( const RString &sNew ) +{ + Steps *pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + pSteps->SetChartName(sNew); +} + +static void ChangeChartStyle( const RString &sNew ) +{ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + pSteps->SetChartStyle(sNew); +} + +static void ChangeStepCredit( const RString &sNew ) +{ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + pSteps->SetCredit(sNew); +} + +static void ChangeStepMeter( const RString &sNew ) +{ + int diff = StringToInt(sNew); + GAMESTATE->m_pCurSteps[PLAYER_1]->SetMeter(max(diff, 1)); +} + +static void ChangeMainTitle( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sMainTitle = sNew; +} + +static void ChangeSubTitle( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sSubTitle = sNew; +} + +static void ChangeArtist( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sArtist = sNew; +} + +static void ChangeGenre( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sGenre = sNew; +} + +static void ChangeCredit( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sCredit = sNew; +} + +static void ChangeMainTitleTranslit( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sMainTitleTranslit = sNew; +} + +static void ChangeSubTitleTranslit( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sSubTitleTranslit = sNew; +} + +static void ChangeArtistTranslit( const RString &sNew ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_sArtistTranslit = sNew; +} + +static void ChangeLastSecondHint( const RString &sNew ) +{ + Song &s = *GAMESTATE->m_pCurSong; + s.SetSpecifiedLastSecond(StringToFloat(sNew)); +} + +static void ChangePreviewStart( const RString &sNew ) +{ + GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds = StringToFloat( sNew ); +} + +static void ChangePreviewLength( const RString &sNew ) +{ + GAMESTATE->m_pCurSong->m_fMusicSampleLengthSeconds = StringToFloat( sNew ); +} + +static void ChangeMinBPM( const RString &sNew ) +{ + GAMESTATE->m_pCurSong->m_fSpecifiedBPMMin = StringToFloat( sNew ); +} + +static void ChangeStepsMinBPM(const RString &sNew) +{ + Steps *step = GAMESTATE->m_pCurSteps[PLAYER_1]; + step->SetMinBPM(StringToFloat(sNew)); +} + +static void ChangeMaxBPM( const RString &sNew ) +{ + GAMESTATE->m_pCurSong->m_fSpecifiedBPMMax = StringToFloat( sNew ); +} + +static void ChangeStepsMaxBPM(const RString &sNew) +{ + Steps *step = GAMESTATE->m_pCurSteps[PLAYER_1]; + step->SetMaxBPM(StringToFloat(sNew)); +} + +const TimingData & ScreenEdit::GetAppropriateTiming() const +{ + if( GAMESTATE->m_bIsUsingStepTiming ) + { + return *m_pSteps->GetTimingData(); + } + return m_pSong->m_SongTiming; +} + +TimingData & ScreenEdit::GetAppropriateTimingForUpdate() +{ + if( GAMESTATE->m_bIsUsingStepTiming ) + { + // Copy from song if there is no step timing + if( m_pSteps->m_Timing.empty() ) + m_pSteps->m_Timing = m_pSong->m_SongTiming; + return m_pSteps->m_Timing; + } + return m_pSong->m_SongTiming; +} + +SongPosition & ScreenEdit::GetAppropriatePosition() const +{ + if (GAMESTATE->m_bIsUsingStepTiming) + { + return GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position; + } + return GAMESTATE->m_Position; +} + +inline void ScreenEdit::SetBeat(float fBeat) +{ + if( !GAMESTATE->m_bIsUsingStepTiming ) + { + GAMESTATE->m_Position.m_fSongBeat = fBeat; + GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat = m_pSteps->GetTimingData()->GetBeatFromElapsedTime(m_pSong->m_SongTiming.GetElapsedTimeFromBeat(fBeat)); + } + else + { + GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat = fBeat; + GAMESTATE->m_Position.m_fSongBeat = m_pSong->m_SongTiming.GetBeatFromElapsedTime(m_pSteps->GetTimingData()->GetElapsedTimeFromBeat(fBeat)); + } +} + +inline float ScreenEdit::GetBeat() +{ + if( !GAMESTATE->m_bIsUsingStepTiming ) + { + return GAMESTATE->m_Position.m_fSongBeat; + } + return GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; +} + +inline int ScreenEdit::GetRow() +{ + return BeatToNoteRow(GetBeat()); +} + +void ScreenEdit::DisplayTimingMenu() +{ + int row = GetRow(); + const TimingData &pTime = GetAppropriateTiming(); + bool bHasSpeedOnThisRow = pTime.GetSpeedSegmentAtRow( row )->GetRow() == row; + // bool bIsSelecting = ( (m_NoteFieldEdit.m_iEndMarker != -1) && (m_NoteFieldEdit.m_iBeginMarker != -1) ); + + + g_TimingDataInformation.rows[beat_0_offset].SetOneUnthemedChoice( FloatToString(pTime.m_fBeat0OffsetInSeconds) ); + g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( FloatToString(pTime.GetBPMAtRow( row ) ) ); + g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( FloatToString(pTime.GetStopAtRow( row ) ) ) ; + g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( FloatToString(pTime.GetDelayAtRow( row ) ) ); + + g_TimingDataInformation.rows[time_signature].SetOneUnthemedChoice( + ssprintf( "%d / %d", + pTime.GetTimeSignatureSegmentAtRow(row)->GetNum(), + pTime.GetTimeSignatureSegmentAtRow(row)->GetDen() + ) + ); + + g_TimingDataInformation.rows[label].SetOneUnthemedChoice( pTime.GetLabelAtRow( row ).c_str() ); + g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtRow( row ) ) ); + g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d / %d", + pTime.GetComboAtRow( row ), + pTime.GetMissComboAtRow( row ) ) ); + g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( FloatToString(pTime.GetWarpAtRow( row ) ) ); + g_TimingDataInformation.rows[speed_percent].SetOneUnthemedChoice( bHasSpeedOnThisRow ? FloatToString(pTime.GetSpeedPercentAtRow( row ) ) : "---" ); + g_TimingDataInformation.rows[speed_wait].SetOneUnthemedChoice( bHasSpeedOnThisRow ? FloatToString(pTime.GetSpeedWaitAtRow( row ) ) : "---" ); + + RString starting = ( pTime.GetSpeedModeAtRow( row ) == 1 ? "Seconds" : "Beats" ); + g_TimingDataInformation.rows[speed_mode].SetOneUnthemedChoice( starting.c_str() ); + + g_TimingDataInformation.rows[scroll].SetOneUnthemedChoice( FloatToString(pTime.GetScrollAtRow( row ) ) ); + g_TimingDataInformation.rows[fake].SetOneUnthemedChoice( FloatToString(pTime.GetFakeAtRow( row ) ) ); + + // g_TimingDataInformation.rows[speed_percent].bEnabled = !bIsSelecting; + g_TimingDataInformation.rows[speed_wait].bEnabled = bHasSpeedOnThisRow; + g_TimingDataInformation.rows[speed_mode].bEnabled = bHasSpeedOnThisRow; + + EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation ); +} + +// End helper functions + +static LocalizedString REVERT_LAST_SAVE ( "ScreenEdit", "Do you want to revert to your last save?" ); +static LocalizedString DESTROY_ALL_UNSAVED_CHANGES ( "ScreenEdit", "This will destroy all unsaved changes." ); +static LocalizedString REVERT_FROM_DISK ( "ScreenEdit", "Do you want to revert from disk?" ); +static LocalizedString SAVE_CHANGES_BEFORE_EXITING ( "ScreenEdit", "Do you want to save changes before exiting?" ); + +void ScreenEdit::HandleMainMenuChoice( MainMenuChoice c, const vector &iAnswers ) +{ + GAMESTATE->SetProcessedTimingData(m_pSteps->GetTimingData()); + switch( c ) + { + DEFAULT_FAIL( c ); + case play_selection: + if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 ) + HandleAlterMenuChoice( play ); + else if( m_NoteFieldEdit.m_iBeginMarker!=-1 ) + HandleMainMenuChoice( play_selection_start_to_end ); + else + HandleMainMenuChoice( play_current_beat_to_end ); + break; + case play_whole_song: + { + m_iStartPlayingAt = 0; + m_iStopPlayingAt = m_NoteDataEdit.GetLastRow(); + TransitionEditState( STATE_PLAYING ); + } + break; + case play_selection_start_to_end: + { + m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; + m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); + TransitionEditState( STATE_PLAYING ); + } + break; + case play_current_beat_to_end: + { + m_iStartPlayingAt = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); + m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() ); + TransitionEditState( STATE_PLAYING ); + } + break; + case set_selection_start: + { + const int iCurrentRow = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); + if( m_NoteFieldEdit.m_iEndMarker!=-1 && iCurrentRow >= m_NoteFieldEdit.m_iEndMarker ) + { + SCREENMAN->PlayInvalidSound(); + } + else + { + m_NoteFieldEdit.m_iBeginMarker = iCurrentRow; + m_soundMarker.Play(); + } + } + break; + case set_selection_end: + { + const int iCurrentRow = BeatToNoteRow(GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat); + if( m_NoteFieldEdit.m_iBeginMarker!=-1 && iCurrentRow <= m_NoteFieldEdit.m_iBeginMarker ) + { + SCREENMAN->PlayInvalidSound(); + } + else + { + m_NoteFieldEdit.m_iEndMarker = iCurrentRow; + m_soundMarker.Play(); + } + } + break; + case edit_steps_information: + { + /* XXX: If the difficulty is changed from EDIT, and pSteps->WasLoadedFromProfile() + * is true, we should warn that the steps will no longer be saved to the profile. */ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + + g_StepsInformation.rows[difficulty].choices.clear(); + FOREACH_ENUM( Difficulty, dc ) + { + g_StepsInformation.rows[difficulty].choices.push_back( "|" + CustomDifficultyToLocalizedString( GetCustomDifficulty(pSteps->m_StepsType, dc, CourseType_Invalid) ) ); + } + g_StepsInformation.rows[difficulty].iDefaultChoice = pSteps->GetDifficulty(); + g_StepsInformation.rows[difficulty].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); + g_StepsInformation.rows[meter].SetOneUnthemedChoice( ssprintf("%d", pSteps->GetMeter()) ); + g_StepsInformation.rows[meter].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Home); + g_StepsInformation.rows[predict_meter].SetOneUnthemedChoice( ssprintf("%.2f",pSteps->PredictMeter()) ); + g_StepsInformation.rows[chartname].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); + g_StepsInformation.rows[chartname].SetOneUnthemedChoice(pSteps->GetChartName()); + g_StepsInformation.rows[description].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); + g_StepsInformation.rows[description].SetOneUnthemedChoice( pSteps->GetDescription() ); + g_StepsInformation.rows[chartstyle].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); + g_StepsInformation.rows[chartstyle].SetOneUnthemedChoice( pSteps->GetChartStyle() ); + g_StepsInformation.rows[step_credit].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Full); + g_StepsInformation.rows[step_credit].SetOneUnthemedChoice( pSteps->GetCredit() ); + g_StepsInformation.rows[step_display_bpm].iDefaultChoice = pSteps->GetDisplayBPM(); + g_StepsInformation.rows[step_min_bpm].SetOneUnthemedChoice( FloatToString(pSteps->GetMinBPM())); + g_StepsInformation.rows[step_max_bpm].SetOneUnthemedChoice( FloatToString(pSteps->GetMaxBPM())); + EditMiniMenu( &g_StepsInformation, SM_BackFromStepsInformation, SM_None ); + } + break; + case view_steps_data: + { + float fMusicSeconds = m_pSoundMusic->GetLengthSeconds(); + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + const StepsTypeCategory &cat = GAMEMAN->GetStepsTypeInfo(pSteps->m_StepsType).m_StepsTypeCategory; + if (cat == StepsTypeCategory_Couple || cat == StepsTypeCategory_Routine) + { + pair tmp = m_NoteDataEdit.GetNumTapNotesTwoPlayer(); + g_StepsData.rows[tap_notes].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumJumpsTwoPlayer(); + g_StepsData.rows[jumps].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumHandsTwoPlayer(); + g_StepsData.rows[hands].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumQuadsTwoPlayer(); + g_StepsData.rows[quads].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumHoldNotesTwoPlayer(); + g_StepsData.rows[holds].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumMinesTwoPlayer(); + g_StepsData.rows[mines].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumRollsTwoPlayer(); + g_StepsData.rows[rolls].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumLiftsTwoPlayer(); + g_StepsData.rows[lifts].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + tmp = m_NoteDataEdit.GetNumFakesTwoPlayer(); + g_StepsData.rows[fakes].SetOneUnthemedChoice( ssprintf("%d / %d", tmp.first, tmp.second) ); + } + else + { + g_StepsData.rows[tap_notes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumTapNotes()) ); + g_StepsData.rows[jumps].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumJumps()) ); + g_StepsData.rows[hands].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHands()) ); + g_StepsData.rows[quads].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumQuads()) ); + g_StepsData.rows[holds].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHoldNotes()) ); + g_StepsData.rows[mines].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumMines()) ); + g_StepsData.rows[rolls].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumRolls()) ); + g_StepsData.rows[lifts].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumLifts()) ); + g_StepsData.rows[fakes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumFakes()) ); + } + g_StepsData.rows[stream].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetStreamRadarValue(m_NoteDataEdit,fMusicSeconds)) ); + g_StepsData.rows[voltage].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetVoltageRadarValue(m_NoteDataEdit,fMusicSeconds)) ); + g_StepsData.rows[air].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetAirRadarValue(m_NoteDataEdit,fMusicSeconds)) ); + g_StepsData.rows[freeze].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetFreezeRadarValue(m_NoteDataEdit,fMusicSeconds)) ); + g_StepsData.rows[chaos].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetChaosRadarValue(m_NoteDataEdit,fMusicSeconds)) ); + EditMiniMenu( &g_StepsData, SM_BackFromStepsData, SM_None ); + break; + } + case save: + case save_on_exit: + { + m_CurrentAction = c; + + // copy edit into current Steps + m_pSteps->SetNoteData( m_NoteDataEdit ); + + // don't forget the attacks. + m_pSong->m_Attacks = GAMESTATE->m_pCurSong->m_Attacks; + m_pSong->m_sAttackString = GAMESTATE->m_pCurSong->m_Attacks.ToVectorString(); + m_pSteps->m_Attacks = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks; + m_pSteps->m_sAttackString = GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks.ToVectorString(); + + switch( EDIT_MODE.GetValue() ) + { + DEFAULT_FAIL( EDIT_MODE.GetValue() ); + case EditMode_Home: + { + ASSERT( m_pSteps->IsAnEdit() ); + + RString sError; + + m_pSteps->CalculateRadarValues( m_pSong->m_fMusicLengthSeconds ); + if( !NotesWriterSM::WriteEditFileToMachine(m_pSong, m_pSteps, sError) ) + { + ScreenPrompt::Prompt( SM_None, sError ); + break; + } + + m_pSteps->SetSavedToDisk( true ); + + // HACK: clear undo, so "exit" below knows we don't need to save. + // This only works because important non-steps data can't be changed in + // home mode (BPMs, stops). + ClearUndo(); + + SCREENMAN->ZeroNextUpdate(); + + HandleScreenMessage( SM_SaveSuccessful ); + + /* FIXME + RString s; + switch( c ) + { + case save: s = "ScreenMemcardSaveEditsAfterSave"; break; + case save_on_exit: s = "ScreenMemcardSaveEditsAfterExit"; break; + default: FAIL_M(ssprintf("Invalid menu choice: %i", c)); + } + SCREENMAN->AddNewScreenToTop( s ); + */ + } + break; + case EditMode_Full: + { + // This will recalculate radar values. + m_pSong->Save(); + SCREENMAN->ZeroNextUpdate(); + + HandleScreenMessage( SM_SaveSuccessful ); + } + break; + case EditMode_CourseMods: + case EditMode_Practice: + break; + } + + m_soundSave.Play(); + } + break; + case revert_to_last_save: + ScreenPrompt::Prompt( SM_DoRevertToLastSave, REVERT_LAST_SAVE.GetValue() + "\n\n" + DESTROY_ALL_UNSAVED_CHANGES.GetValue(), PROMPT_YES_NO, ANSWER_NO ); + break; + case revert_from_disk: + ScreenPrompt::Prompt( SM_DoRevertFromDisk, REVERT_FROM_DISK.GetValue() + "\n\n" + DESTROY_ALL_UNSAVED_CHANGES.GetValue(), PROMPT_YES_NO, ANSWER_NO ); + break; + case options: + SCREENMAN->AddNewScreenToTop( "ScreenEditOptions", SM_BackFromOptions ); + break; + case edit_song_info: + { + const Song* pSong = GAMESTATE->m_pCurSong; + g_SongInformation.rows[main_title].SetOneUnthemedChoice( pSong->m_sMainTitle ); + g_SongInformation.rows[sub_title].SetOneUnthemedChoice( pSong->m_sSubTitle ); + g_SongInformation.rows[artist].SetOneUnthemedChoice( pSong->m_sArtist ); + g_SongInformation.rows[genre].SetOneUnthemedChoice( pSong->m_sGenre ); + g_SongInformation.rows[credit].SetOneUnthemedChoice( pSong->m_sCredit ); + g_SongInformation.rows[main_title_transliteration].SetOneUnthemedChoice( pSong->m_sMainTitleTranslit ); + g_SongInformation.rows[sub_title_transliteration].SetOneUnthemedChoice( pSong->m_sSubTitleTranslit ); + g_SongInformation.rows[artist_transliteration].SetOneUnthemedChoice( pSong->m_sArtistTranslit ); + g_SongInformation.rows[last_second_hint].SetOneUnthemedChoice( FloatToString(pSong->GetSpecifiedLastSecond()) ); + g_SongInformation.rows[preview_start].SetOneUnthemedChoice( FloatToString(pSong->m_fMusicSampleStartSeconds) ); + g_SongInformation.rows[preview_length].SetOneUnthemedChoice( FloatToString(pSong->m_fMusicSampleLengthSeconds) ); + g_SongInformation.rows[display_bpm].iDefaultChoice = pSong->m_DisplayBPMType; + g_SongInformation.rows[min_bpm].SetOneUnthemedChoice( FloatToString(pSong->m_fSpecifiedBPMMin) ); + g_SongInformation.rows[max_bpm].SetOneUnthemedChoice( FloatToString(pSong->m_fSpecifiedBPMMax) ); + + EditMiniMenu( &g_SongInformation, SM_BackFromSongInformation ); + } + break; + case edit_timing_data: + { + DisplayTimingMenu(); + } + break; + + case play_preview_music: + PlayPreviewMusic(); + break; + case exit: + switch( EDIT_MODE.GetValue() ) + { + DEFAULT_FAIL( EDIT_MODE.GetValue() ); + case EditMode_Full: + case EditMode_Home: + if( IsDirty() ) + ScreenPrompt::Prompt( SM_DoSaveAndExit, SAVE_CHANGES_BEFORE_EXITING, PROMPT_YES_NO_CANCEL, ANSWER_CANCEL ); + else + SCREENMAN->SendMessageToTopScreen( SM_DoExit ); + break; + case EditMode_Practice: + case EditMode_CourseMods: + SCREENMAN->SendMessageToTopScreen( SM_DoExit ); + break; + } + break; + }; + GAMESTATE->SetProcessedTimingData(NULL); +} + +void ScreenEdit::HandleAlterMenuChoice(AlterMenuChoice c, const vector &iAnswers, bool bAllowUndo) +{ + ASSERT_M(m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1, + "You can only alter a selection of notes with a selection to begin with!"); + + bool bSaveUndo = true; + switch (c) + { + case play: + case record: + case cut: + case copy: + { + bSaveUndo = false; + } + default: + break; + } + + if( bSaveUndo ) + SetDirty( true ); + + /* We call HandleAreaMenuChoice recursively. Only the outermost + * HandleAreaMenuChoice should allow Undo so that the inner calls don't + * also save Undo and mess up the outermost */ + if( !bAllowUndo ) + bSaveUndo = false; + + if( bSaveUndo ) + SaveUndo(); + + switch(c) + { + case cut: + { + HandleAlterMenuChoice( copy ); + HandleAlterMenuChoice( clear ); + } + break; + case copy: + { + + m_Clipboard.ClearAll(); + m_Clipboard.CopyRange( m_NoteDataEdit, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); + } + break; + case clear: + { + m_NoteDataEdit.ClearRange( m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker ); + } + break; + case quantize: + { + NoteType nt = (NoteType)iAnswers[c]; + NoteDataUtil::SnapToNearestNoteType(m_NoteDataEdit, nt, nt, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker ); + break; + } + case turn: + { + const NoteData OldClipboard( m_Clipboard ); + HandleAlterMenuChoice( cut ); + + StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; + TurnType tt = (TurnType)iAnswers[c]; + switch( tt ) + { + DEFAULT_FAIL( tt ); + case left: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::left ); break; + case right: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::right ); break; + case mirror: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::mirror ); break; + case turn_backwards: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::backwards ); break; + case shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::shuffle ); break; + case super_shuffle: NoteDataUtil::Turn( m_Clipboard, st, NoteDataUtil::super_shuffle ); break; + } + + HandleAreaMenuChoice( paste_at_begin_marker ); + m_Clipboard = OldClipboard; + } + break; + case transform: + { + int iBeginRow = m_NoteFieldEdit.m_iBeginMarker; + int iEndRow = m_NoteFieldEdit.m_iEndMarker; + TransformType tt = (TransformType)iAnswers[c]; + StepsType st = GAMESTATE->GetCurrentStyle()->m_StepsType; + + switch( tt ) + { + DEFAULT_FAIL( tt ); + case noholds: NoteDataUtil::RemoveHoldNotes( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nomines: NoteDataUtil::RemoveMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case little: NoteDataUtil::Little( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case wide: NoteDataUtil::Wide( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case big: NoteDataUtil::Big( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case quick: NoteDataUtil::Quick( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case skippy: NoteDataUtil::Skippy( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case add_mines: NoteDataUtil::AddMines( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case echo: NoteDataUtil::Echo( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case stomp: NoteDataUtil::Stomp( m_NoteDataEdit, st, iBeginRow, iEndRow ); break; + case planted: NoteDataUtil::Planted( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case floored: NoteDataUtil::Floored( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case twister: NoteDataUtil::Twister( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nojumps: NoteDataUtil::RemoveJumps( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nohands: NoteDataUtil::RemoveHands( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case noquads: NoteDataUtil::RemoveQuads( m_NoteDataEdit, iBeginRow, iEndRow ); break; + case nostretch: NoteDataUtil::RemoveStretch( m_NoteDataEdit, st, iBeginRow, iBeginRow );break; + } + + // bake in the additions + NoteDataUtil::ConvertAdditionsToRegular( m_NoteDataEdit ); + break; + } + case alter: + { + const NoteData OldClipboard( m_Clipboard ); + HandleAlterMenuChoice( cut ); + + AlterType at = (AlterType)iAnswers[c]; + switch( at ) + { + DEFAULT_FAIL( at ); + case autogen_to_fill_width: + { + NoteData temp( m_Clipboard ); + int iMaxNonEmptyTrack = NoteDataUtil::GetMaxNonEmptyTrack( temp ); + if( iMaxNonEmptyTrack == -1 ) + break; + temp.SetNumTracks( iMaxNonEmptyTrack+1 ); + NoteDataUtil::LoadTransformedSlidingWindow( temp, m_Clipboard, m_Clipboard.GetNumTracks() ); + NoteDataUtil::RemoveStretch( m_Clipboard, GAMESTATE->m_pCurSteps[0]->m_StepsType ); + } + break; + case backwards: NoteDataUtil::Backwards( m_Clipboard ); break; + case swap_sides: NoteDataUtil::SwapSides( m_Clipboard ); break; + case copy_left_to_right: NoteDataUtil::CopyLeftToRight( m_Clipboard ); break; + case copy_right_to_left: NoteDataUtil::CopyRightToLeft( m_Clipboard ); break; + case clear_left: NoteDataUtil::ClearLeft( m_Clipboard ); break; + case clear_right: NoteDataUtil::ClearRight( m_Clipboard ); break; + case collapse_to_one: NoteDataUtil::CollapseToOne( m_Clipboard ); break; + case collapse_left: NoteDataUtil::CollapseLeft( m_Clipboard ); break; + case shift_left: NoteDataUtil::ShiftLeft( m_Clipboard ); break; + case shift_right: NoteDataUtil::ShiftRight( m_Clipboard ); break; + } + + HandleAreaMenuChoice( paste_at_begin_marker ); + m_Clipboard = OldClipboard; + break; + } + case tempo: + { + // This affects all steps. + AlterType at = (AlterType)iAnswers[c]; + float fScale = -1; + + switch( at ) + { + DEFAULT_FAIL( at ); + case compress_2x: fScale = 0.5f; break; + case compress_3_2: fScale = 2.0f/3; break; + case compress_4_3: fScale = 0.75f; break; + case expand_4_3: fScale = 4.0f/3; break; + case expand_3_2: fScale = 1.5f; break; + case expand_2x: fScale = 2; break; + } + + int iStartIndex = m_NoteFieldEdit.m_iBeginMarker; + int iEndIndex = m_NoteFieldEdit.m_iEndMarker; + int iNewEndIndex = iEndIndex + lrintf( (iEndIndex - iStartIndex) * (fScale - 1) ); + + // scale currently editing notes + NoteDataUtil::ScaleRegion( m_NoteDataEdit, fScale, iStartIndex, iEndIndex ); + + // scale timing data + GetAppropriateTimingForUpdate().ScaleRegion(fScale, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker, true ); + + m_NoteFieldEdit.m_iEndMarker = iNewEndIndex; + break; + + } + + case play: + m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; + m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; + TransitionEditState( STATE_PLAYING ); + break; + case record: + m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker; + m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker; + TransitionEditState( STATE_RECORDING ); + break; + case preview_designation: + { + float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + GAMESTATE->m_pCurSong->m_fMusicSampleStartSeconds = fMarkerStart; + GAMESTATE->m_pCurSong->m_fMusicSampleLengthSeconds = fMarkerEnd - fMarkerStart; + break; + } + case convert_to_pause: + { + float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + + // The length of the stop segment we're going to create. This includes time spent in any + // stops in the selection, which will be deleted and subsumed into the new stop. + float fStopLength = fMarkerEnd - fMarkerStart; + + // be sure not to clobber the row at the start - a row at the end + // can be dropped safely, though + NoteDataUtil::DeleteRows( m_NoteDataEdit, + m_NoteFieldEdit.m_iBeginMarker + 1, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker + ); + GetAppropriateTimingForUpdate().DeleteRows( m_NoteFieldEdit.m_iBeginMarker + 1, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); + GetAppropriateTimingForUpdate().SetStopAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); + m_NoteFieldEdit.m_iBeginMarker = -1; + m_NoteFieldEdit.m_iEndMarker = -1; + break; + } + case convert_to_delay: + { + float fMarkerStart = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) ); + float fMarkerEnd = GetAppropriateTiming().GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) ); + + // The length of the delay segment we're going to create. This includes time spent in any + // stops in the selection, which will be deleted and subsumed into the new stop. + float fStopLength = fMarkerEnd - fMarkerStart; + + NoteDataUtil::DeleteRows( m_NoteDataEdit, + m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker + ); + GetAppropriateTimingForUpdate().DeleteRows( m_NoteFieldEdit.m_iBeginMarker, + m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker ); + GetAppropriateTimingForUpdate().SetDelayAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength ); + m_NoteFieldEdit.m_iBeginMarker = -1; + m_NoteFieldEdit.m_iEndMarker = -1; + break; + } + case convert_to_warp: + { + float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); + float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - startBeat; + GetAppropriateTimingForUpdate().SetWarpAtBeat(startBeat,lengthBeat); + SetDirty(true); + break; + } + case convert_to_attack: + { + float startBeat = NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker); + float endBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker); + const TimingData &timing = GetAppropriateTiming(); + float &start = g_fLastInsertAttackPositionSeconds; + float &length = g_fLastInsertAttackDurationSeconds; + start = timing.GetElapsedTimeFromBeat(startBeat); + length = timing.GetElapsedTimeFromBeat(endBeat) - start; + + AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? + m_pSteps->m_Attacks : m_pSong->m_Attacks; + int iAttack = FindAttackAtTime(attacks, start); + + ModsGroup &toEdit = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions; + this->originalPlayerOptions.Assign(ModsLevel_Preferred, toEdit.GetPreferred()); + PlayerOptions po; + if (iAttack >= 0) + po.FromString(attacks[iAttack].sModifiers); + + toEdit.Assign( ModsLevel_Preferred, po ); + SCREENMAN->AddNewScreenToTop( "ScreenPlayerOptions", SM_BackFromInsertStepAttackPlayerOptions ); + SetDirty(true); + break; + } + case convert_to_fake: + { + int startRow = m_NoteFieldEdit.m_iBeginMarker; + float lengthBeat = NoteRowToBeat(m_NoteFieldEdit.m_iEndMarker) - NoteRowToBeat(startRow); + GetAppropriateTimingForUpdate().AddSegment( FakeSegment(startRow,lengthBeat) ); + SetDirty(true); + break; + } + case routine_invert_notes: + { + NoteData &nd = this->m_NoteDataEdit; + NoteField &nf = this->m_NoteFieldEdit; + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE(nd, r, + nf.m_iBeginMarker, + nf.m_iEndMarker) + { + for (int t = 0; t < nd.GetNumTracks(); t++) + { + const TapNote &tn = nd.GetTapNote(t, r); + if (tn.type != TapNote::empty) + { + TapNote nTap = tn; + nTap.pn = (tn.pn == PLAYER_1 ? + PLAYER_2 : PLAYER_1); + m_NoteDataEdit.SetTapNote(t, r, nTap); + } + } + } + break; + } + case routine_mirror_1_to_2: + case routine_mirror_2_to_1: + { + PlayerNumber oPN = (c == routine_mirror_1_to_2 ? + PLAYER_1 : PLAYER_2); + PlayerNumber nPN = (c == routine_mirror_1_to_2 ? + PLAYER_2 : PLAYER_1); + int nTrack = -1; + NoteData &nd = this->m_NoteDataEdit; + NoteField &nf = this->m_NoteFieldEdit; + int tracks = nd.GetNumTracks(); + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE(nd, r, + nf.m_iBeginMarker, + nf.m_iEndMarker) + { + for (int t = 0; t < tracks; t++) + { + const TapNote &tn = nd.GetTapNote(t, r); + if (tn.type != TapNote::empty && tn.pn == oPN) + { + TapNote nTap = tn; + nTap.pn = nPN; + StepsType curType = GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType; + // TODO: Find a better way to do this. + if (curType == StepsType_dance_routine) + { + nTrack = tracks - t - 1; + } + else if (curType == StepsType_pump_routine) + { + switch (t) + { + case 0: nTrack = 8; break; + case 1: nTrack = 9; break; + case 2: nTrack = 7; break; + case 3: nTrack = 5; break; + case 4: nTrack = 6; break; + case 5: nTrack = 3; break; + case 6: nTrack = 4; break; + case 7: nTrack = 2; break; + case 8: nTrack = 0; break; + case 9: nTrack = 1; break; + default: FAIL_M(ssprintf("Invalid column %d for pump-routine", t)); break; + } + } + m_NoteDataEdit.SetTapNote(nTrack, r, nTap); + } + } + } + break; + } + default: break; + } + +} + +void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector &iAnswers, bool bAllowUndo ) +{ + bool bSaveUndo = true; + switch( c ) + { + case clear_clipboard: + case undo: + bSaveUndo = false; + break; + default: + break; + } + + if( bSaveUndo ) + SetDirty( true ); + + /* We call HandleAreaMenuChoice recursively. Only the outermost + * HandleAreaMenuChoice should allow Undo so that the inner calls don't + * also save Undo and mess up the outermost */ + if( !bAllowUndo ) + bSaveUndo = false; + + if( bSaveUndo ) + SaveUndo(); + + switch( c ) + { + DEFAULT_FAIL( c ); + + case paste_at_current_beat: + case paste_at_begin_marker: + { + int iDestFirstRow = -1; + switch( c ) + { + DEFAULT_FAIL( c ); + case paste_at_current_beat: + iDestFirstRow = BeatToNoteRow( GetAppropriatePosition().m_fSongBeat ); + break; + case paste_at_begin_marker: + ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 ); + iDestFirstRow = m_NoteFieldEdit.m_iBeginMarker; + break; + } + + int iRowsToCopy = m_Clipboard.GetLastRow()+1; + m_NoteDataEdit.CopyRange( m_Clipboard, 0, iRowsToCopy, iDestFirstRow ); + } + break; + + case insert_and_shift: + NoteDataUtil::InsertRows( m_NoteDataEdit, GetRow(), + iAnswers.size() > 0 ? + NoteTypeToRow((NoteType)iAnswers[c]) : 48); + break; + case delete_and_shift: + NoteDataUtil::DeleteRows( m_NoteDataEdit, GetRow(), + iAnswers.size() > 0 ? + NoteTypeToRow((NoteType)iAnswers[c]) : 48); + break; + case shift_pauses_forward: + GetAppropriateTimingForUpdate().InsertRows( GetRow(), + iAnswers.size() > 0 ? + NoteTypeToRow((NoteType)iAnswers[c]) : 48); + break; + case shift_pauses_backward: + GetAppropriateTimingForUpdate().DeleteRows( GetRow() + 1, + iAnswers.size() > 0 ? + NoteTypeToRow((NoteType)iAnswers[c]) : 48); + break; + + case convert_pause_to_beat: + { + float fStopSeconds = GetAppropriateTiming().GetStopAtRow(GetRow()); + GetAppropriateTimingForUpdate().SetStopAtBeat( GetBeat() , 0 ); + + float fStopBeats = fStopSeconds * GetAppropriateTiming().GetBPMAtBeat( GetBeat() ) / 60; + + // don't move the step from where it is, just move everything later + NoteDataUtil::InsertRows( m_NoteDataEdit, GetRow() + 1, BeatToNoteRow(fStopBeats) ); + GetAppropriateTimingForUpdate().InsertRows( GetRow() + 1, BeatToNoteRow(fStopBeats) ); + } + break; + case convert_delay_to_beat: + { + TimingData &timing = GetAppropriateTimingForUpdate(); + float pause = timing.GetDelayAtRow(GetRow()); + timing.SetDelayAtRow(GetRow(), 0); + + float pauseBeats = pause * timing.GetBPMAtBeat(GetBeat()) / 60; + + NoteDataUtil::InsertRows(m_NoteDataEdit, GetRow(), BeatToNoteRow(pauseBeats)); + timing.InsertRows(GetRow(), BeatToNoteRow(pauseBeats)); + break; + } + case last_second_at_beat: + { + const TimingData &timing = GetAppropriateTiming(); + Song &s = *GAMESTATE->m_pCurSong; + s.SetSpecifiedLastSecond(timing.GetElapsedTimeFromBeat(GetBeat())); + break; + } + case undo: + Undo(); + break; + case clear_clipboard: + { + m_Clipboard.ClearAll(); + break; + } + case modify_attacks_at_row: + { + this->DoStepAttackMenu(); + break; + } + case modify_keysounds_at_row: + { + this->DoKeyboardTrackMenu(); + break; + } + }; + + if( bSaveUndo ) + CheckNumberOfNotesAndUndo(); +} + +void ScreenEdit::HandleStepsDataChoice( StepsDataChoice c, const vector &iAnswers ) +{ + return; // nothing is done with the choices. Yet. +} + +static LocalizedString ENTER_NEW_DESCRIPTION( "ScreenEdit", "Enter a new description." ); +static LocalizedString ENTER_NEW_CHART_NAME("ScreenEdit", "Enter a new chart name."); +static LocalizedString ENTER_NEW_CHART_STYLE( "ScreenEdit", "Enter a new chart style." ); +static LocalizedString ENTER_NEW_STEP_AUTHOR( "ScreenEdit", "Enter the author who made this step pattern." ); +static LocalizedString ENTER_NEW_METER( "ScreenEdit", "Enter a new meter." ); +static LocalizedString ENTER_MIN_BPM ("ScreenEdit","Enter a new min BPM."); +static LocalizedString ENTER_MAX_BPM ("ScreenEdit","Enter a new max BPM."); +void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const vector &iAnswers ) +{ + Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1]; + Difficulty dc = (Difficulty)iAnswers[difficulty]; + pSteps->SetDifficulty( dc ); + pSteps->SetDisplayBPM(static_cast(iAnswers[step_display_bpm])); + + switch( c ) + { + case chartname: + { + ScreenTextEntry::TextEntry(SM_None, + ENTER_NEW_CHART_NAME, + m_pSteps->GetChartName(), + MAX_STEPS_DESCRIPTION_LENGTH, + SongUtil::ValidateCurrentStepsChartName, + ChangeChartName, + NULL); + break; + } + case description: + { + ScreenTextEntry::TextEntry(SM_None, + ENTER_NEW_DESCRIPTION, + m_pSteps->GetDescription(), + MAX_STEPS_DESCRIPTION_LENGTH, + SongUtil::ValidateCurrentStepsDescription, + ChangeDescription, + NULL); + break; + } + case chartstyle: + { + ScreenTextEntry::TextEntry(SM_None, + ENTER_NEW_CHART_STYLE, + m_pSteps->GetChartStyle(), + 255, + NULL, + ChangeChartStyle, + NULL); + break; + } + case step_credit: + { + ScreenTextEntry::TextEntry(SM_None, + ENTER_NEW_STEP_AUTHOR, + m_pSteps->GetCredit(), + 255, + SongUtil::ValidateCurrentStepsCredit, + ChangeStepCredit, + NULL); + break; + } + case meter: + { + ScreenTextEntry::TextEntry(SM_BackFromDifficultyMeterChange, + ENTER_NEW_METER, + ssprintf("%d", m_pSteps->GetMeter()), + 4, + NULL, + ChangeStepMeter, + NULL); + break; + } + case step_min_bpm: + { + ScreenTextEntry::TextEntry(SM_None, ENTER_MIN_BPM, + FloatToString(pSteps->GetMinBPM()), 20, + ScreenTextEntry::FloatValidate, + ChangeStepsMinBPM, NULL); + break; + } + case step_max_bpm: + { + ScreenTextEntry::TextEntry(SM_None, ENTER_MAX_BPM, + FloatToString(pSteps->GetMaxBPM()), 20, + ScreenTextEntry::FloatValidate, + ChangeStepsMaxBPM, NULL); + break; + } + default: + break; + } + SetDirty(true); +} + +static LocalizedString ENTER_MAIN_TITLE ("ScreenEdit","Enter a new main title."); +static LocalizedString ENTER_SUB_TITLE ("ScreenEdit","Enter a new sub title."); +static LocalizedString ENTER_ARTIST ("ScreenEdit","Enter a new artist."); +static LocalizedString ENTER_GENRE ("ScreenEdit","Enter a new genre."); +static LocalizedString ENTER_CREDIT ("ScreenEdit","Enter a new credit."); +static LocalizedString ENTER_MAIN_TITLE_TRANSLIT ("ScreenEdit","Enter a new main title transliteration."); +static LocalizedString ENTER_SUB_TITLE_TRANSLIT ("ScreenEdit","Enter a new sub title transliteration."); +static LocalizedString ENTER_ARTIST_TRANSLIT ("ScreenEdit","Enter a new artist transliteration."); +static LocalizedString ENTER_LAST_SECOND_HINT ("ScreenEdit","Enter a new last second hint."); +static LocalizedString ENTER_PREVIEW_START ("ScreenEdit","Enter a new preview start."); +static LocalizedString ENTER_PREVIEW_LENGTH ("ScreenEdit","Enter a new preview length."); +void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vector &iAnswers ) +{ + Song* pSong = GAMESTATE->m_pCurSong; + pSong->m_DisplayBPMType = static_cast(iAnswers[display_bpm]); + + switch( c ) + { + case main_title: + ScreenTextEntry::TextEntry( SM_None, ENTER_MAIN_TITLE, pSong->m_sMainTitle, 100, NULL, ChangeMainTitle, NULL ); + break; + case sub_title: + ScreenTextEntry::TextEntry( SM_None, ENTER_SUB_TITLE, pSong->m_sSubTitle, 100, NULL, ChangeSubTitle, NULL ); + break; + case artist: + ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST, pSong->m_sArtist, 100, NULL, ChangeArtist, NULL ); + break; + case genre: + ScreenTextEntry::TextEntry( SM_None, ENTER_GENRE, pSong->m_sGenre, 100, NULL, ChangeGenre, NULL ); + break; + case credit: + ScreenTextEntry::TextEntry( SM_None, ENTER_CREDIT, pSong->m_sCredit, 100, NULL, ChangeCredit, NULL ); + break; + case main_title_transliteration: + ScreenTextEntry::TextEntry( SM_None, ENTER_MAIN_TITLE_TRANSLIT, pSong->m_sMainTitleTranslit, 100, NULL, ChangeMainTitleTranslit, NULL ); + break; + case sub_title_transliteration: + ScreenTextEntry::TextEntry( SM_None, ENTER_SUB_TITLE_TRANSLIT, pSong->m_sSubTitleTranslit, 100, NULL, ChangeSubTitleTranslit, NULL ); + break; + case artist_transliteration: + ScreenTextEntry::TextEntry( SM_None, ENTER_ARTIST_TRANSLIT, pSong->m_sArtistTranslit, 100, NULL, ChangeArtistTranslit, NULL ); + break; + case last_second_hint: + ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_SECOND_HINT, + FloatToString(pSong->GetSpecifiedLastSecond()), 20, + ScreenTextEntry::FloatValidate, ChangeLastSecondHint, NULL ); + break; + case preview_start: + ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_START, + FloatToString(pSong->m_fMusicSampleStartSeconds), 20, + ScreenTextEntry::FloatValidate, ChangePreviewStart, NULL ); + break; + case preview_length: + ScreenTextEntry::TextEntry( SM_None, ENTER_PREVIEW_LENGTH, + FloatToString(pSong->m_fMusicSampleLengthSeconds), 20, + ScreenTextEntry::FloatValidate, ChangePreviewLength, NULL ); + break; + case min_bpm: + ScreenTextEntry::TextEntry( SM_None, ENTER_MIN_BPM, + FloatToString(pSong->m_fSpecifiedBPMMin), 20, + ScreenTextEntry::FloatValidate, ChangeMinBPM, NULL ); + break; + case max_bpm: + ScreenTextEntry::TextEntry( SM_None, ENTER_MAX_BPM, + FloatToString(pSong->m_fSpecifiedBPMMax), 20, + ScreenTextEntry::FloatValidate, ChangeMaxBPM, NULL ); + break; + default: break; + }; + SetDirty(true); +} + + +static LocalizedString ENTER_BEAT_0_OFFSET ( "ScreenEdit", "Enter the offset for the song."); +static LocalizedString ENTER_BPM_VALUE ( "ScreenEdit", "Enter a new BPM value." ); +static LocalizedString ENTER_STOP_VALUE ( "ScreenEdit", "Enter a new Stop value." ); +static LocalizedString ENTER_DELAY_VALUE ( "ScreenEdit", "Enter a new Delay value." ); +static LocalizedString ENTER_TIME_SIGNATURE_VALUE ( "ScreenEdit", "Enter a new Time Signature." ); +static LocalizedString ENTER_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." ); +static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo value." ); +static LocalizedString ENTER_LABEL_VALUE ( "ScreenEdit", "Enter a new Label value." ); +static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp value." ); +static LocalizedString ENTER_SPEED_PERCENT_VALUE ( "ScreenEdit", "Enter a new Speed percent value." ); +static LocalizedString ENTER_SPEED_WAIT_VALUE ( "ScreenEdit", "Enter a new Speed wait value." ); +static LocalizedString ENTER_SPEED_MODE_VALUE ( "ScreenEdit", "Enter a new Speed mode value." ); +static LocalizedString ENTER_SCROLL_VALUE ( "ScreenEdit", "Enter a new Scroll value." ); +static LocalizedString ENTER_FAKE_VALUE ( "ScreenEdit", "Enter a new Fake value." ); +static LocalizedString CONFIRM_TIMING_ERASE ( "ScreenEdit", "Are you sure you want to erase this chart's timing data?" ); +void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector &iAnswers ) +{ + switch( c ) + { + DEFAULT_FAIL( c ); + case beat_0_offset: + ScreenTextEntry::TextEntry( + SM_BackFromBeat0Change, + ENTER_BEAT_0_OFFSET, + FloatToString(GetAppropriateTiming().m_fBeat0OffsetInSeconds), + 20 + ); + break; + case bpm: + ScreenTextEntry::TextEntry( + SM_BackFromBPMChange, + ENTER_BPM_VALUE, + FloatToString( GetAppropriateTiming().GetBPMAtBeat( GetBeat() ) ), + 10 + ); + break; + case stop: + ScreenTextEntry::TextEntry( + SM_BackFromStopChange, + ENTER_STOP_VALUE, + FloatToString( GetAppropriateTiming().GetStopAtBeat( GetBeat() ) ), + 10 + ); + break; + case delay: + ScreenTextEntry::TextEntry( + SM_BackFromDelayChange, + ENTER_DELAY_VALUE, + FloatToString( GetAppropriateTiming().GetDelayAtBeat( GetBeat() ) ), + 10 + ); + break; + case time_signature: + { + const TimeSignatureSegment *ts = GetAppropriateTiming().GetTimeSignatureSegmentAtBeat( GetBeat() ); + ScreenTextEntry::TextEntry( + SM_BackFromTimeSignatureChange, + ENTER_TIME_SIGNATURE_VALUE, + ssprintf( "%d/%d", ts->GetNum(), ts->GetDen() ), + 8 + ); + break; + } + case tickcount: + ScreenTextEntry::TextEntry( + SM_BackFromTickcountChange, + ENTER_TICKCOUNT_VALUE, + ssprintf( "%d", GetAppropriateTiming().GetTickcountAtBeat( GetBeat() ) ), + 2 + ); + break; + case combo: + { + const ComboSegment *cs = GetAppropriateTiming().GetComboSegmentAtBeat(GetBeat()); + ScreenTextEntry::TextEntry(SM_BackFromComboChange, + ENTER_COMBO_VALUE, + ssprintf( "%d/%d", + cs->GetCombo(), + cs->GetMissCombo()), + 7); + break; + } + case label: + ScreenTextEntry::TextEntry( + SM_BackFromLabelChange, + ENTER_LABEL_VALUE, + ssprintf( "%s", GetAppropriateTiming().GetLabelAtBeat( GetBeat() ).c_str() ), + 64 + ); + break; + case warp: + ScreenTextEntry::TextEntry( + SM_BackFromWarpChange, + ENTER_WARP_VALUE, + FloatToString( GetAppropriateTiming().GetWarpAtBeat( GetBeat() ) ), + 10 + ); + break; + case speed_percent: + ScreenTextEntry::TextEntry( + SM_BackFromSpeedPercentChange, + ENTER_SPEED_PERCENT_VALUE, + FloatToString( GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() )->GetRatio() ), + 10 + ); + break; + case scroll: + ScreenTextEntry::TextEntry( + SM_BackFromScrollChange, + ENTER_SCROLL_VALUE, + FloatToString( GetAppropriateTiming().GetScrollSegmentAtBeat( GetBeat() )->GetRatio() ), + 10 + ); + break; + case speed_wait: + ScreenTextEntry::TextEntry( + SM_BackFromSpeedWaitChange, + ENTER_SPEED_WAIT_VALUE, + FloatToString( GetAppropriateTiming().GetSpeedSegmentAtBeat( GetBeat() )->GetDelay() ), + 10 + ); + break; + case speed_mode: + { + ScreenTextEntry::TextEntry( + SM_BackFromSpeedModeChange, + ENTER_SPEED_MODE_VALUE, + "", + 3 + ); + + break; + } + case fake: + { + ScreenTextEntry::TextEntry( + SM_BackFromFakeChange, + ENTER_FAKE_VALUE, + FloatToString(GetAppropriateTiming().GetFakeAtBeat( GetBeat() ) ), + 10 + ); + break; + } + case copy_full_timing: + { + clipboardFullTiming = GetAppropriateTiming(); + break; + } + case paste_full_timing: + { + if (GAMESTATE->m_bIsUsingStepTiming) + { + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Timing = clipboardFullTiming; + } + else + { + GAMESTATE->m_pCurSong->m_SongTiming = clipboardFullTiming; + } + SetDirty(true); + break; + } + case erase_step_timing: + ScreenPrompt::Prompt( SM_DoEraseStepTiming, CONFIRM_TIMING_ERASE , PROMPT_YES_NO, ANSWER_NO ); + break; + + } +} + +void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector &iAnswers ) +{ + BackgroundChange newChange; + + FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer), iter ) + { + if( iter->m_fStartBeat == GAMESTATE->m_Position.m_fSongBeat ) + { + newChange = *iter; + // delete the old change. We'll add a new one below. + m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer).erase( iter ); + break; + } + } + + newChange.m_fStartBeat = GAMESTATE->m_Position.m_fSongBeat; + newChange.m_fRate = StringToFloat( g_BackgroundChange.rows[rate].choices[iAnswers[rate]] )/100.f; + newChange.m_sTransition = iAnswers[transition] ? g_BackgroundChange.rows[transition].choices[iAnswers[transition]] : RString(); + newChange.m_def.m_sEffect = iAnswers[effect] ? g_BackgroundChange.rows[effect].choices[iAnswers[effect]] : RString(); + newChange.m_def.m_sColor1 = iAnswers[color1] ? g_BackgroundChange.rows[color1].choices[iAnswers[color1]] : RString(); + newChange.m_def.m_sColor2 = iAnswers[color2] ? g_BackgroundChange.rows[color2].choices[iAnswers[color2]] : RString(); + switch( iAnswers[file1_type] ) + { + DEFAULT_FAIL( iAnswers[file1_type] ); + case none: newChange.m_def.m_sFile1 = ""; break; + case dynamic_random: newChange.m_def.m_sFile1 = RANDOM_BACKGROUND_FILE; break; + case baked_random: newChange.m_def.m_sFile1 = GetOneBakedRandomFile( m_pSong ); break; + case song_bganimation: + case song_movie: + case song_bitmap: + case global_bganimation: + case global_movie: + case global_movie_song_group: + case global_movie_song_group_and_genre: + { + BGChangeChoice row1 = (BGChangeChoice)(file1_song_bganimation + iAnswers[file1_type]); + newChange.m_def.m_sFile1 = g_BackgroundChange.rows[row1].choices.empty() ? "" : g_BackgroundChange.rows[row1].choices[iAnswers[row1]]; + } + break; + } + switch( iAnswers[file2_type] ) + { + DEFAULT_FAIL(iAnswers[file2_type]); + case none: newChange.m_def.m_sFile2 = ""; break; + case dynamic_random: newChange.m_def.m_sFile2 = RANDOM_BACKGROUND_FILE; break; + case baked_random: newChange.m_def.m_sFile2 = GetOneBakedRandomFile( m_pSong ); break; + case song_bganimation: + case song_movie: + case song_bitmap: + case global_bganimation: + case global_movie: + case global_movie_song_group: + case global_movie_song_group_and_genre: + { + BGChangeChoice row2 = (BGChangeChoice)(file2_song_bganimation + iAnswers[file2_type]); + newChange.m_def.m_sFile2 = g_BackgroundChange.rows[row2].choices.empty() ? "" : g_BackgroundChange.rows[row2].choices[iAnswers[row2]]; + } + break; + } + + + if( c == delete_change || newChange.m_def.m_sFile1.empty() ) + { + // don't add + } + else + { + m_pSong->AddBackgroundChange( g_CurrentBGChangeLayer, newChange ); + } + g_CurrentBGChangeLayer = BACKGROUND_LAYER_Invalid; +} + +void ScreenEdit::SetupCourseAttacks() +{ + /* This is the first beat that can be changed without it being visible. Until + * we draw for the first time, any beat can be changed. */ + GAMESTATE->m_pPlayerState[PLAYER_1]->m_fLastDrawnBeat = -100; + + // Put course options into effect. + GAMESTATE->m_pPlayerState[PLAYER_1]->m_ModsToApply.clear(); + GAMESTATE->m_pPlayerState[PLAYER_1]->RemoveActiveAttacks(); + + + if( GAMESTATE->m_pCurCourse ) + { + AttackArray Attacks; + + if( EDIT_MODE == EditMode_CourseMods ) + { + Attacks = GAMESTATE->m_pCurCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex].attacks; + } + else + { + GAMESTATE->m_pCurCourse->RevertFromDisk(); // Remove this and have a separate reload key? + + for( unsigned e = 0; e < GAMESTATE->m_pCurCourse->m_vEntries.size(); ++e ) + { + if( GAMESTATE->m_pCurCourse->m_vEntries[e].songID.ToSong() != m_pSong ) + continue; + + Attacks = GAMESTATE->m_pCurCourse->m_vEntries[e].attacks; + break; + } + } + + FOREACH( Attack, Attacks, attack ) + GAMESTATE->m_pPlayerState[PLAYER_1]->LaunchAttack( *attack ); + } + else + { + const PlayerOptions &p = GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions.GetCurrent(); + if (GAMESTATE->m_pCurSong && p.m_fNoAttack == 0 && p.m_fRandAttack == 0 ) + { + AttackArray &attacks = GAMESTATE->m_bIsUsingStepTiming ? + GAMESTATE->m_pCurSteps[PLAYER_1]->m_Attacks : + GAMESTATE->m_pCurSong->m_Attacks; + + if (attacks.size() > 0) + { + FOREACH(Attack, attacks, attack) + { + float fBeat = GetAppropriateTiming().GetBeatFromElapsedTime(attack->fStartSecond); + if (fBeat >= GetBeat()) + GAMESTATE->m_pPlayerState[PLAYER_1]->LaunchAttack( *attack ); + } + } + } + } + + GAMESTATE->m_pPlayerState[PLAYER_1]->RebuildPlayerOptionsFromActiveAttacks(); +} + +void ScreenEdit::CopyToLastSave() +{ + ASSERT( GAMESTATE->m_pCurSong != NULL ); + ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); + m_SongLastSave = *GAMESTATE->m_pCurSong; + m_vStepsLastSave.clear(); + const vector &vSteps = GAMESTATE->m_pCurSong->GetStepsByStepsType( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType ); + for (Steps *it : vSteps) + m_vStepsLastSave.push_back( *it ); +} + +void ScreenEdit::CopyFromLastSave() +{ + // We are assuming two things here: + // 1) No steps can be created by ScreenEdit + // 2) No steps can be deleted by ScreenEdit (except possibly when we exit) + *GAMESTATE->m_pCurSong = m_SongLastSave; + const vector &vSteps = GAMESTATE->m_pCurSong->GetStepsByStepsType( GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType ); + ASSERT_M( vSteps.size() == m_vStepsLastSave.size(), ssprintf("Step sizes don't match: %d, %d", int(vSteps.size()), int(m_vStepsLastSave.size())) ); + for( unsigned i = 0; i < vSteps.size(); ++i ) + *vSteps[i] = m_vStepsLastSave[i]; +} + +void ScreenEdit::RevertFromDisk() +{ + ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] != NULL ); + StepsID id; + id.FromSteps( GAMESTATE->m_pCurSteps[PLAYER_1] ); + ASSERT( id.IsValid() ); + + GAMESTATE->m_pCurSong->ReloadFromSongDir( GAMESTATE->m_pCurSong->GetSongDir() ); + + Steps *pNewSteps = id.ToSteps( GAMESTATE->m_pCurSong, true ); + if( !pNewSteps ) + { + // If the Steps we were currently editing vanished when we did the revert, + // put a blank Steps in its place. Note that this does not have to be the + // work of someone maliciously changing the simfile; it could happen to + // someone editing a new stepchart and reverting from disk, for example. + pNewSteps = GAMESTATE->m_pCurSong->CreateSteps(); + pNewSteps->CreateBlank( id.GetStepsType() ); + pNewSteps->SetDifficulty( id.GetDifficulty() ); + GAMESTATE->m_pCurSong->AddSteps( pNewSteps ); + } + GAMESTATE->m_pCurSteps[PLAYER_1].Set( pNewSteps ); + m_pSteps = pNewSteps; + + CopyToLastSave(); + SetDirty( false ); + SONGMAN->Invalidate( GAMESTATE->m_pCurSong ); +} + +void ScreenEdit::SaveUndo() +{ + m_bHasUndo = true; + m_Undo.CopyAll( m_NoteDataEdit ); +} + +static LocalizedString UNDO ("ScreenEdit", "Undo"); +static LocalizedString CANT_UNDO ("ScreenEdit", "Can't undo - no undo data."); +void ScreenEdit::Undo() +{ + if( m_bHasUndo ) + { + swap( m_Undo, m_NoteDataEdit ); + SCREENMAN->SystemMessage( UNDO ); + } + else + { + SCREENMAN->SystemMessage( CANT_UNDO ); + SCREENMAN->PlayInvalidSound(); + } +} + +void ScreenEdit::ClearUndo() +{ + m_bHasUndo = false; + m_Undo.ClearAll(); +} + +static LocalizedString CREATES_MORE_THAN_NOTES ( "ScreenEdit", "This change creates more than %d notes in a measure." ); +static LocalizedString CREATES_NOTES_PAST_END ( "ScreenEdit", "This change creates notes past the end of the music and is not allowed." ); +static LocalizedString CHANGE_REVERTED ( "ScreenEdit", "The change has been reverted." ); +void ScreenEdit::CheckNumberOfNotesAndUndo() +{ + if( EDIT_MODE.GetValue() != EditMode_Home ) + return; + + const float fBeat = GAMESTATE->m_pPlayerState[PLAYER_1]->m_Position.m_fSongBeat; + const TimeSignatureSegment * curTime = GAMESTATE->m_pCurSong->m_SongTiming.GetTimeSignatureSegmentAtBeat( fBeat ); + int rowsPerMeasure = curTime->GetDen() * curTime->GetNum(); + + for( int row=0; row<=m_NoteDataEdit.GetLastRow(); row+=rowsPerMeasure ) + { + int iNumNotesThisMeasure = 0; + FOREACH_NONEMPTY_ROW_ALL_TRACKS_RANGE( m_NoteDataEdit, r, row, row+rowsPerMeasure ) + iNumNotesThisMeasure += m_NoteDataEdit.GetNumTapNonEmptyTracks( r ); + } + + if( GAMESTATE->m_pCurSteps[0]->IsAnEdit() ) + { + /* Check that the action didn't push notes any farther past the last + * measure. This blocks Insert Beat from pushing past the end, but allows + * Delete Beat to pull back the notes that are already past the end. + */ + float fNewLastBeat = m_NoteDataEdit.GetLastBeat(); + bool bLastBeatIncreased = fNewLastBeat > m_Undo.GetLastBeat(); + bool bPassedTheEnd = fNewLastBeat > GetMaximumBeatForNewNote(); + if( bLastBeatIncreased && bPassedTheEnd ) + { + Undo(); + m_bHasUndo = false; + RString sError = CREATES_NOTES_PAST_END.GetValue() + "\n\n" + CHANGE_REVERTED.GetValue(); + ScreenPrompt::Prompt( SM_None, sError ); + return; + } + } +} + +float ScreenEdit::GetMaximumBeatForNewNote() const +{ + switch( EDIT_MODE.GetValue() ) + { + DEFAULT_FAIL( EDIT_MODE.GetValue() ); + case EditMode_Practice: + case EditMode_CourseMods: + case EditMode_Home: + { + Song &s = *GAMESTATE->m_pCurSong; + float fEndBeat = s.GetLastBeat(); + + /* Round up to the next measure end. Some songs end on weird beats + * mid-measure, and it's odd to have movement capped to these weird + * beats. */ + TimingData &timing = s.m_SongTiming; + float playerBeat = GetAppropriatePosition().m_fSongBeat; + int beatsPerMeasure = timing.GetTimeSignatureSegmentAtBeat( playerBeat )->GetNum(); + fEndBeat += beatsPerMeasure; + fEndBeat = ftruncf( fEndBeat, (float)beatsPerMeasure ); + + return fEndBeat; + } + case EditMode_Full: + return NoteRowToBeat(MAX_NOTE_ROW); + } +} + +float ScreenEdit::GetMaximumBeatForMoving() const +{ + float fEndBeat = GetMaximumBeatForNewNote(); + + /* Jump to GetLastBeat even if it's past the song's last beat + * so that users can delete garbage steps past then end that they have + * have inserted in a text editor. Once they delete all steps on + * GetLastBeat() and move off of that beat, they won't be able to return. */ + fEndBeat = max( fEndBeat, m_NoteDataEdit.GetLastBeat() ); + + return fEndBeat; +} + +struct EditHelpLine +{ + const char *szEnglishDescription; + vector veb; + + EditHelpLine( + const char *_szEnglishDescription, + EditButton eb0, + EditButton eb1 = EditButton_Invalid, + EditButton eb2 = EditButton_Invalid, + EditButton eb3 = EditButton_Invalid, + EditButton eb4 = EditButton_Invalid, + EditButton eb5 = EditButton_Invalid, + EditButton eb6 = EditButton_Invalid, + EditButton eb7 = EditButton_Invalid, + EditButton eb8 = EditButton_Invalid, + EditButton eb9 = EditButton_Invalid ) + { + szEnglishDescription = _szEnglishDescription; +#define PUSH_IF_VALID( x ) if( x != EditButton_Invalid ) veb.push_back( x ); + PUSH_IF_VALID( eb0 ); + PUSH_IF_VALID( eb1 ); + PUSH_IF_VALID( eb2 ); + PUSH_IF_VALID( eb3 ); + PUSH_IF_VALID( eb4 ); + PUSH_IF_VALID( eb5 ); + PUSH_IF_VALID( eb6 ); + PUSH_IF_VALID( eb7 ); + PUSH_IF_VALID( eb8 ); + PUSH_IF_VALID( eb9 ); +#undef PUSH_IF_VALID + } +}; +// TODO: Identify which of these can be removed and sent to a readme. +static const EditHelpLine g_EditHelpLines[] = +{ + EditHelpLine( "Move cursor", EDIT_BUTTON_SCROLL_UP_LINE, EDIT_BUTTON_SCROLL_DOWN_LINE ), + EditHelpLine( "Jump measure", EDIT_BUTTON_SCROLL_UP_PAGE, EDIT_BUTTON_SCROLL_DOWN_PAGE ), + EditHelpLine( "Jump measure", EDIT_BUTTON_SCROLL_PREV_MEASURE, EDIT_BUTTON_SCROLL_NEXT_MEASURE ), + EditHelpLine( "Select region", EDIT_BUTTON_SCROLL_SELECT ), + EditHelpLine( "Jump to first/last beat", EDIT_BUTTON_SCROLL_HOME, EDIT_BUTTON_SCROLL_END ), + EditHelpLine( "Change zoom", EDIT_BUTTON_SCROLL_SPEED_UP, EDIT_BUTTON_SCROLL_SPEED_DOWN ), + EditHelpLine( "Play", EDIT_BUTTON_PLAY_SELECTION ), + EditHelpLine( "Play current beat to end", EDIT_BUTTON_PLAY_FROM_CURSOR ), + EditHelpLine( "Play whole song", EDIT_BUTTON_PLAY_FROM_START ), + EditHelpLine( "Record", EDIT_BUTTON_RECORD_SELECTION ), + EditHelpLine( "Set selection", EDIT_BUTTON_LAY_SELECT ), + EditHelpLine( "Next/prev steps of same StepsType", EDIT_BUTTON_OPEN_NEXT_STEPS, EDIT_BUTTON_OPEN_PREV_STEPS ), + EditHelpLine( "Decrease/increase BPM at cur beat", EDIT_BUTTON_BPM_DOWN, EDIT_BUTTON_BPM_UP ), + EditHelpLine( "Decrease/increase stop at cur beat", EDIT_BUTTON_STOP_DOWN, EDIT_BUTTON_STOP_UP ), + EditHelpLine( "Decrease/increase delay at cur beat", EDIT_BUTTON_DELAY_DOWN, EDIT_BUTTON_DELAY_UP ), + EditHelpLine( "Decrease/increase music offset", EDIT_BUTTON_OFFSET_DOWN, EDIT_BUTTON_OFFSET_UP ), + EditHelpLine( "Decrease/increase sample music start", EDIT_BUTTON_SAMPLE_START_DOWN, EDIT_BUTTON_SAMPLE_START_UP ), + EditHelpLine( "Decrease/increase sample music length", EDIT_BUTTON_SAMPLE_LENGTH_DOWN, EDIT_BUTTON_SAMPLE_LENGTH_UP ), + EditHelpLine( "Play sample music", EDIT_BUTTON_PLAY_SAMPLE_MUSIC ), + EditHelpLine( "Add/Edit Background Change", EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU ), + EditHelpLine( "Insert beat and shift down", EDIT_BUTTON_INSERT ), + EditHelpLine( "Shift BPM changes and stops down one beat", EDIT_BUTTON_INSERT_SHIFT_PAUSES ), + EditHelpLine( "Delete beat and shift up", EDIT_BUTTON_DELETE ), + EditHelpLine( "Shift BPM changes and stops up one beat", EDIT_BUTTON_DELETE_SHIFT_PAUSES ), + EditHelpLine( "Cycle between tap notes", EDIT_BUTTON_CYCLE_TAP_LEFT, EDIT_BUTTON_CYCLE_TAP_RIGHT ), + EditHelpLine( "Add to/remove from right half", EDIT_BUTTON_RIGHT_SIDE ), + EditHelpLine( "Switch Timing", EDIT_BUTTON_SWITCH_TIMINGS ), + EditHelpLine( "Switch player (Routine only)", EDIT_BUTTON_SWITCH_PLAYERS ), +}; + +static bool IsMapped( EditButton eb, const MapEditToDI &editmap ) +{ + for( int s=0; s &vs ) +{ + FOREACH( RString, vs, s ) + ProcessKeyName( *s ); + + sort( vs.begin(), vs.end() ); + vector::iterator toDelete = unique( vs.begin(), vs.end() ); + vs.erase(toDelete, vs.end()); +} + +static RString GetDeviceButtonsLocalized( const vector &veb, const MapEditToDI &editmap ) +{ + vector vsPress; + vector vsHold; + for (EditButton const &eb : veb) + { + if( !IsMapped( eb, editmap ) ) + continue; + + for( int s=0; sGetLocalizedInputString(diPress)) ); + if( diHold.IsValid() ) + vsHold.push_back( Capitalize(INPUTMAN->GetLocalizedInputString(diHold)) ); + } + } + + ProcessKeyNames( vsPress ); + ProcessKeyNames( vsHold ); + + RString s = join("/",vsPress); + if( !vsHold.empty() ) + s = join("/",vsHold) + " + " + s; + return s; +} + +void ScreenEdit::DoStepAttackMenu() +{ + const 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("%g -> %g (%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"); + +void ScreenEdit::DoKeyboardTrackMenu() +{ + g_KeysoundTrack.rows.clear(); + vector &kses = m_pSong->m_vsKeysoundFile; + + vector choices; + FOREACH(RString, kses, ks) + { + choices.push_back(*ks); + } + choices.push_back(NEWKEYSND); + choices.push_back(NO_KEYSND); + int numKeysounds = kses.size(); + for (int i = 0; i < m_NoteDataEdit.GetNumTracks(); ++i) + { + const TapNote &tn = m_NoteDataEdit.GetTapNote(i, this->GetRow()); + int keyIndex = tn.iKeysoundIndex; + if (keyIndex == -1) + { + keyIndex = numKeysounds; + } + + g_KeysoundTrack.rows.push_back(MenuRowDef(i, ssprintf(TRACK_NUM.GetValue(), i + 1), + true, EditMode_Full, false, false, keyIndex, choices)); + } + g_KeysoundTrack.rows.push_back(MenuRowDef(m_NoteDataEdit.GetNumTracks(), "Remove Keysound", + true, EditMode_Full, false, false, 0, kses)); + + EditMiniMenu(&g_KeysoundTrack, SM_BackFromKeysoundTrack); +} + +void ScreenEdit::DoHelp() +{ + g_EditHelp.rows.clear(); + + for( unsigned i=0; iGetString( "EditHelpDescription", hl.szEnglishDescription ); + + // TODO: Better way of hiding routine only key on non-routine. + if( hl.veb[0] == EDIT_BUTTON_SWITCH_PLAYERS && m_InputPlayerNumber == PLAYER_INVALID ) + { + continue; + } + + g_EditHelp.rows.push_back( MenuRowDef( -1, sDescription, false, EditMode_Practice, false, false, 0, sButtons ) ); + } + + EditMiniMenu( &g_EditHelp ); +} + +// lua start +#include "LuaBinding.h" + +/** @brief Allow Lua to have access to ScreenEdit. */ +class LunaScreenEdit: public Luna +{ +public: + DEFINE_METHOD( GetEditState, GetEditState() ) + LunaScreenEdit() + { + ADD_METHOD( GetEditState ); + } +}; + +LUA_REGISTER_DERIVED_CLASS( ScreenEdit, ScreenWithMenuElements ) + +/* + * (c) 2001-2004 Chris Danford + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */