Files
itgmania212121/src/ScreenEdit.cpp
T

4148 lines
157 KiB
C++
Raw Normal View History

#include "global.h"
#include <utility>
#include <float.h>
#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<float> g_iDefaultRecordLength( "DefaultRecordLength", 4 );
static Preference<bool> g_bEditorShowBGChangesPlay( "EditorShowBGChangesPlay", true );
// Defines specific to ScreenEdit
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_BackFromStepsInformation );
AutoScreenMessage( SM_BackFromOptions );
AutoScreenMessage( SM_BackFromSongInformation );
AutoScreenMessage( SM_BackFromBGChange );
AutoScreenMessage( SM_BackFromInsertTapAttack );
AutoScreenMessage( SM_BackFromInsertTapAttackPlayerOptions );
AutoScreenMessage( SM_BackFromInsertCourseAttack );
AutoScreenMessage( SM_BackFromInsertCourseAttackPlayerOptions );
AutoScreenMessage( SM_BackFromCourseModeMenu );
AutoScreenMessage( SM_DoRevertToLastSave );
AutoScreenMessage( SM_DoRevertFromDisk );
AutoScreenMessage( SM_BackFromTimingDataInformation );
AutoScreenMessage( SM_BackFromBPMChange );
AutoScreenMessage( SM_BackFromStopChange );
AutoScreenMessage( SM_BackFromDelayChange );
AutoScreenMessage( SM_BackFromTimeSignatureNumeratorChange );
AutoScreenMessage( SM_BackFromTimeSignatureDenominatorChange );
AutoScreenMessage( SM_BackFromTickcountChange );
AutoScreenMessage( SM_BackFromComboChange );
2011-03-25 17:40:56 -04:00
AutoScreenMessage( SM_BackFromWarpChange );
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.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_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 attack 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_Cm);
// 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:
/* 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.button[EDIT_BUTTON_DELAY_UP][0] = DeviceInput(DEVICE_KEYBOARD, KEY_F10);
*/
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_OPEN_COURSE_MENU][0] = DeviceInput(DEVICE_KEYBOARD, KEY_Cc);
m_EditMappingsDeviceInput.button[EDIT_BUTTON_OPEN_COURSE_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;
}
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_TAP_ATTACK][0] = 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_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_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);
m_PlayMappingsDeviceInput.button[EDIT_BUTTON_RETURN_TO_EDIT][0] = DeviceInput(DEVICE_KEYBOARD, KEY_ESC);
m_PlayMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK;
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_RecordMappingsMenuButton.button[EDIT_BUTTON_RETURN_TO_EDIT][1] = GAME_BUTTON_BACK;
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_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::edit_steps_information, "Edit steps information", 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_timing_data, "Edit Timing 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_AreaMenu(
"ScreenMiniMenuAreaMenu",
MenuRowDef( ScreenEdit::cut, "Cut", true, EditMode_Practice, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::copy, "Copy", true, EditMode_Practice, true, true, 0, NULL ),
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::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","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::insert_and_shift, "Insert beat and shift down", true, EditMode_Practice, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::delete_and_shift, "Delete beat and shift up", true, EditMode_Practice, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::shift_pauses_forward, "Shift pauses and BPM changes down", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::shift_pauses_backward, "Shift pauses and BPM changes up", 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_pause_to_beat, "Convert pause to beats", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::undo, "Undo", true, EditMode_Practice, true, true, 0, NULL )
);
static MenuDef g_StepsInformation(
"ScreenMiniMenuStepsInformation",
MenuRowDef( ScreenEdit::difficulty, "Difficulty", true, EditMode_Practice, true, true, 0, NULL ),
// xxx: this giant list of numbers SUUUUUUUUUUCKS -aj
MenuRowDef( ScreenEdit::meter, "Meter", true, EditMode_Practice, true, false, 0, "1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25" ),
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::predict_meter, "Predicted Meter", false, EditMode_Full, true, true, 0, NULL ),
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::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_beat_hint, "Last beat hint", true, EditMode_Full, true, true, 0, NULL )
);
static MenuDef g_TimingDataInformation(
"ScreenMiniMenuTimingDataInformation",
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_numerator, "Edit time signature (top)", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::time_signature_denominator, "Edit time signature (bottom)", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::tickcount, "Edit tickcount", true, EditMode_Full, true, true, 0, NULL ),
2011-03-25 17:40:56 -04:00
MenuRowDef( ScreenEdit::combo, "Edit combo", true, EditMode_Full, true, true, 0, NULL ),
MenuRowDef( ScreenEdit::warp, "Edit warp", true, EditMode_Full, true, true, 0, NULL )
);
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<RString> vsPathsOut;
vector<RString> vsNamesOut;
BackgroundUtil::GetGlobalRandomMovies(
pSong,
"",
vsPathsOut,
vsNamesOut,
bTryGenre );
if( !vsNamesOut.empty() )
return vsNamesOut[RandomInt(vsNamesOut.size())];
else
return RString();
}
static MenuDef g_InsertTapAttack(
"ScreenMiniMenuInsertTapAttack",
MenuRowDef( -1, "Duration seconds", true, EditMode_Practice, true, false, 3, "5","10","15","20","25","30","35","40","45" ),
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_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.
switch( num )
{
case 0: defaultValueOut = "default"; return;
case 1: defaultValueOut = "default"; return;
}
defaultValueOut = "default";
}
static Preference1D<RString> EDITOR_NOTE_SKINS( SetDefaultEditorNoteSkin, NUM_PLAYERS );
static ThemeMetric<RString> EDIT_MODIFIERS ("ScreenEdit","EditModifiers");
REGISTER_SCREEN_CLASS( ScreenEdit );
void ScreenEdit::Init()
{
m_pSoundMusic = NULL;
SubscribeToMessage( "Judgment" );
ASSERT( GAMESTATE->m_pCurSong );
ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] );
EDIT_MODE.Load( m_sName, "EditMode" );
ScreenWithMenuElements::Init();
InitEditMappings();
// save the originals for reverting later
CopyToLastSave();
m_CurrentAction = MAIN_MENU_CHOICE_INVALID;
if( GAMESTATE->GetCurrentStyle()->m_StyleType == StyleType_TwoPlayersSharedSides )
m_InputPlayerNumber = PLAYER_1;
else
m_InputPlayerNumber = PLAYER_INVALID;
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];
m_bReturnToRecordMenuAfterPlay = false;
m_fBeatToReturnTo = 0;
m_selectedTap = TAP_ORIGINAL_TAP;
GAMESTATE->m_bGameplayLeadIn.Set( true );
GAMESTATE->m_EditMode = EDIT_MODE.GetValue();
GAMESTATE->m_fSongBeat = 0;
m_fTrailingBeat = GAMESTATE->m_fSongBeat;
m_iShiftAnchor = -1;
m_iStartPlayingAt = -1;
m_iStopPlayingAt = -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;
2011-03-27 18:25:30 -05:00
// 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 );
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, -(int)SCREEN_HEIGHT/2, (int)SCREEN_HEIGHT/2 );
this->AddChild( &m_NoteFieldRecord );
m_EditState = EditState_Invalid;
TransitionEditState( STATE_EDITING );
m_bRemoveNoteButtonDown = false;
m_Clipboard.SetNumTracks( m_NoteDataEdit.GetNumTracks() );
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_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();
}
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() );
}
void ScreenEdit::PlayPreviewMusic()
{
SOUND->StopMusic();
SOUND->PlayMusic(
m_pSong->GetMusicPath(),
NULL,
false,
m_pSong->m_fMusicSampleStartSeconds,
m_pSong->m_fMusicSampleLengthSeconds,
0.0f,
1.5f );
}
void ScreenEdit::MakeFilteredMenuDef( const MenuDef* pDef, MenuDef &menu )
{
menu = *pDef;
menu.rows.clear();
vector<MenuRowDef> 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_Timing, tm );
}
if( m_EditState == STATE_RECORDING )
{
for( int t=0; t<GAMESTATE->GetCurrentStyle()->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;
float fStartPlayingAtBeat = NoteRowToBeat(m_iStartPlayingAt);
if( GAMESTATE->m_fSongBeat <= fStartPlayingAtBeat )
continue;
float fStartedHoldingSeconds = m_pSoundMusic->GetPositionSeconds() - fSecsHeld;
float fStartBeat = max( fStartPlayingAtBeat, m_pSong->GetBeatFromElapsedTime(fStartedHoldingSeconds) );
float fEndBeat = max( fStartBeat, GAMESTATE->m_fSongBeat );
fEndBeat = min( fEndBeat, NoteRowToBeat(m_iStopPlayingAt) );
// 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; t<GAMESTATE->GetCurrentStyle()->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_pSong->m_Timing.GetElapsedTimeFromBeat( fLastBeat );
fLastBeat = m_pSong->m_Timing.GetBeatFromElapsedTime( fSeconds + 0.5f );
}
float fStopAtSeconds = m_pSong->m_Timing.GetElapsedTimeFromBeat( NoteRowToBeat(m_iStopPlayingAt) ) + 1;
if( GAMESTATE->m_fMusicSeconds > fStopAtSeconds )
{
// loop
TransitionEditState( STATE_PLAYING );
}
}
//LOG->Trace( "ScreenEdit::Update(%f)", fDeltaTime );
ScreenWithMenuElements::Update( fDeltaTime );
// Update trailing beat
float fDelta = GAMESTATE->m_fSongBeat - m_fTrailingBeat;
if( fabsf(fDelta) < 10 )
fapproach( m_fTrailingBeat, GAMESTATE->m_fSongBeat,
fDeltaTime*40 / m_NoteFieldEdit.GetPlayerState()->m_PlayerOptions.GetCurrent().m_fScrollSpeed );
else
fapproach( m_fTrailingBeat, GAMESTATE->m_fSongBeat,
fabsf(fDelta) * fDeltaTime*5 );
PlayTicks();
}
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 DESCRIPTION("ScreenEdit", "Description");
static LocalizedString CHART_STYLE("ScreenEdit", "Chart Style");
static LocalizedString MAIN_TITLE("ScreenEdit", "Main title");
static LocalizedString SUBTITLE("ScreenEdit", "Subtitle");
static LocalizedString TAP_NOTE_TYPE("ScreenEdit", "Tap Note");
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 BEAT_0_OFFSET("ScreenEdit", "Beat 0 offset");
static LocalizedString PREVIEW_START("ScreenEdit", "Preview Start");
static LocalizedString PREVIEW_LENGTH("ScreenEdit", "Preview Length");
static ThemeMetric<RString> CURRENT_BEAT_FORMAT("ScreenEdit", "CurrentBeatFormat");
static ThemeMetric<RString> CURRENT_SECOND_FORMAT("ScreenEdit", "CurrentSecondFormat");
static ThemeMetric<RString> SNAP_TO_FORMAT("ScreenEdit", "SnapToFormat");
static ThemeMetric<RString> SELECTION_BEAT_BEGIN_FORMAT("ScreenEdit", "SelectionBeatBeginFormat");
static ThemeMetric<RString> SELECTION_BEAT_UNFINISHED_FORMAT("ScreenEdit", "SelectionBeatUnfinishedFormat");
static ThemeMetric<RString> SELECTION_BEAT_END_FORMAT("ScreenEdit", "SelectionBeatEndFormat");
static ThemeMetric<RString> DIFFICULTY_FORMAT("ScreenEdit", "DifficultyFormat");
static ThemeMetric<RString> ROUTINE_PLAYER_FORMAT("ScreenEdit", "RoutinePlayerFormat");
static ThemeMetric<RString> DESCRIPTION_FORMAT("ScreenEdit", "DescriptionFormat");
static ThemeMetric<RString> CHART_STYLE_FORMAT("ScreenEdit", "ChartStyleFormat");
static ThemeMetric<RString> MAIN_TITLE_FORMAT("ScreenEdit", "MainTitleFormat");
static ThemeMetric<RString> SUBTITLE_FORMAT("ScreenEdit", "SubtitleFormat");
static ThemeMetric<RString> TAP_NOTE_TYPE_FORMAT("ScreenEdit", "TapNoteTypeFormat");
static ThemeMetric<RString> NUM_STEPS_FORMAT("ScreenEdit", "NumStepsFormat");
static ThemeMetric<RString> NUM_JUMPS_FORMAT("ScreenEdit", "NumJumpsFormat");
static ThemeMetric<RString> NUM_HOLDS_FORMAT("ScreenEdit", "NumHoldsFormat");
static ThemeMetric<RString> NUM_MINES_FORMAT("ScreenEdit", "NumMinesFormat");
static ThemeMetric<RString> NUM_HANDS_FORMAT("ScreenEdit", "NumHandsFormat");
static ThemeMetric<RString> NUM_ROLLS_FORMAT("ScreenEdit", "NumRollsFormat");
static ThemeMetric<RString> NUM_LIFTS_FORMAT("ScreenEdit", "NumLiftsFormat");
static ThemeMetric<RString> NUM_FAKES_FORMAT("ScreenEdit", "NumFakesFormat");
static ThemeMetric<RString> BEAT_0_OFFSET_FORMAT("ScreenEdit", "Beat0OffsetFormat");
static ThemeMetric<RString> PREVIEW_START_FORMAT("ScreenEdit", "PreviewStartFormat");
static ThemeMetric<RString> 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(), GAMESTATE->m_fSongBeat );
sText += ssprintf( CURRENT_SECOND_FORMAT.GetValue(), CURRENT_SECOND.GetValue().c_str(), m_pSong->GetElapsedTimeFromBeat(GAMESTATE->m_fSongBeat) );
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;
}
switch( EDIT_MODE.GetValue() )
{
DEFAULT_FAIL( EDIT_MODE.GetValue() );
case EditMode_Practice:
case EditMode_CourseMods:
case EditMode_Home:
break;
case 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_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( TAP_NOTE_TYPE_FORMAT.GetValue(), TAP_NOTE_TYPE.GetValue().c_str(), TapNoteTypeToString( m_selectedTap.type ).c_str() );
break;
}
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( BEAT_0_OFFSET_FORMAT.GetValue(), BEAT_0_OFFSET.GetValue().c_str(), m_pSong->m_Timing.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 );
}
void ScreenEdit::DrawPrimitives()
{
// HACK: Draw using the trailing beat
float fSongBeat = GAMESTATE->m_fSongBeat; // save song beat
float fSongBeatNoOffset = GAMESTATE->m_fSongBeatNoOffset;
float fSongBeatVisible = GAMESTATE->m_fSongBeatVisible;
if( !m_pSoundMusic->IsPlaying() )
{
GAMESTATE->m_fSongBeat = m_fTrailingBeat; // put trailing beat in effect
GAMESTATE->m_fSongBeatNoOffset = m_fTrailingBeat; // put trailing beat in effect
GAMESTATE->m_fSongBeatVisible = m_fTrailingBeat; // put trailing beat in effect
}
ScreenWithMenuElements::DrawPrimitives();
if( !m_pSoundMusic->IsPlaying() )
{
GAMESTATE->m_fSongBeat = fSongBeat; // restore real song beat
GAMESTATE->m_fSongBeatNoOffset = fSongBeatNoOffset;
GAMESTATE->m_fSongBeatVisible = fSongBeatVisible;
}
}
void 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;
if( m_In.IsTransitioning() || m_Out.IsTransitioning() )
return;
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:
InputEdit( input, EditB );
m_bTextInfoNeedsUpdate = true;
break;
case STATE_RECORDING:
InputRecord( input, EditB );
break;
case STATE_RECORDING_PAUSED:
InputRecordPaused( input, EditB );
break;
case STATE_PLAYING:
InputPlay( input, EditB );
break;
}
}
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 int FindAttackAtTime( const AttackArray& attacks, float fStartTime )
{
for( unsigned i = 0; i < attacks.size(); ++i )
{
if( fabs(attacks[i].fStartSecond - fStartTime) < 0.001f )
return i;
}
return -1;
}
static LocalizedString SWITCHED_TO ( "ScreenEdit", "Switched to" );
static LocalizedString NO_BACKGROUNDS_AVAILABLE ( "ScreenEdit", "No backgrounds available" );
2011-03-17 14:41:26 -04:00
static ThemeMetric<bool> INVERT_SCROLL_BUTTONS ( "ScreenEdit", "InvertScrollSpeedButtons" );
void ScreenEdit::InputEdit( const InputEventPlus &input, EditButton EditB )
{
if( input.type == IET_RELEASE )
{
if( EditPressed( EDIT_BUTTON_SCROLL_SELECT, input.DeviceI ) )
m_iShiftAnchor = -1;
return;
}
int beatsPerMeasure = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_fSongBeat ).m_iNumerator;
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 )
break; // 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() );
const float fSongBeat = GAMESTATE->m_fSongBeat;
const int iSongIndex = BeatToNoteRow( fSongBeat );
if( iCol >= m_NoteDataEdit.GetNumTracks() ) // this button is not in the range of columns for this Style
break;
// 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();
}
}
break;
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 );
}
break;
}
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 );
}
break;
}
case EDIT_BUTTON_SCROLL_SPEED_UP:
case EDIT_BUTTON_SCROLL_SPEED_DOWN:
{
PlayerState *pPlayerState = const_cast<PlayerState *> (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:
2011-03-17 14:41:26 -04:00
INVERT_SCROLL_BUTTONS ? ++iSpeed : --iSpeed;
break;
case EDIT_BUTTON_SCROLL_SPEED_UP:
2011-03-17 14:41:26 -04:00
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 );
break;
}
break;
case EDIT_BUTTON_SCROLL_HOME:
ScrollTo( 0 );
break;
case EDIT_BUTTON_SCROLL_END:
ScrollTo( m_NoteDataEdit.GetLastBeat() );
break;
case EDIT_BUTTON_SCROLL_UP_LINE:
case EDIT_BUTTON_SCROLL_UP_PAGE:
case EDIT_BUTTON_SCROLL_DOWN_LINE:
case EDIT_BUTTON_SCROLL_DOWN_PAGE:
{
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 = beatsPerMeasure;
if( EditB == EDIT_BUTTON_SCROLL_UP_PAGE )
fBeatsToMove *= -1;
break;
}
if( m_PlayerStateEdit.m_PlayerOptions.GetSong().m_fScrolls[PlayerOptions::SCROLL_REVERSE] > 0.5 )
fBeatsToMove *= -1;
float fDestinationBeat = GAMESTATE->m_fSongBeat + fBeatsToMove;
fDestinationBeat = Quantize( fDestinationBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
ScrollTo( fDestinationBeat );
}
break;
case EDIT_BUTTON_SCROLL_NEXT_MEASURE:
{
float fDestinationBeat = GAMESTATE->m_fSongBeat + beatsPerMeasure;
fDestinationBeat = ftruncf( fDestinationBeat, (float)beatsPerMeasure );
ScrollTo( fDestinationBeat );
break;
}
case EDIT_BUTTON_SCROLL_PREV_MEASURE:
{
float fDestinationBeat = QuantizeUp( GAMESTATE->m_fSongBeat, (float)beatsPerMeasure );
fDestinationBeat -= (float)beatsPerMeasure;
ScrollTo( fDestinationBeat );
break;
}
case EDIT_BUTTON_SCROLL_NEXT:
{
int iRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
NoteDataUtil::GetNextEditorPosition( m_NoteDataEdit, iRow );
ScrollTo( NoteRowToBeat(iRow) );
}
break;
case EDIT_BUTTON_SCROLL_PREV:
{
int iRow = BeatToNoteRow( GAMESTATE->m_fSongBeat );
NoteDataUtil::GetPrevEditorPosition( m_NoteDataEdit, iRow );
ScrollTo( NoteRowToBeat(iRow) );
}
break;
case EDIT_BUTTON_SNAP_NEXT:
if( m_SnapDisplay.PrevSnapMode() )
OnSnapModeChange();
break;
case EDIT_BUTTON_SNAP_PREV:
if( m_SnapDisplay.NextSnapMode() )
OnSnapModeChange();
break;
case EDIT_BUTTON_LAY_SELECT:
{
const int iCurrentRow = BeatToNoteRow(GAMESTATE->m_fSongBeat);
if( m_NoteFieldEdit.m_iBeginMarker==-1 && m_NoteFieldEdit.m_iEndMarker==-1 )
{
// lay begin marker
m_NoteFieldEdit.m_iBeginMarker = BeatToNoteRow(GAMESTATE->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();
}
break;
case EDIT_BUTTON_OPEN_AREA_MENU:
{
// update enabled/disabled in g_AreaMenu
bool bAreaSelected = m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1;
g_AreaMenu.rows[cut].bEnabled = bAreaSelected;
g_AreaMenu.rows[copy].bEnabled = bAreaSelected;
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[clear].bEnabled = bAreaSelected;
g_AreaMenu.rows[quantize].bEnabled = bAreaSelected;
g_AreaMenu.rows[turn].bEnabled = bAreaSelected;
g_AreaMenu.rows[transform].bEnabled = bAreaSelected;
g_AreaMenu.rows[alter].bEnabled = bAreaSelected;
g_AreaMenu.rows[tempo].bEnabled = bAreaSelected;
g_AreaMenu.rows[play].bEnabled = bAreaSelected;
g_AreaMenu.rows[record].bEnabled = bAreaSelected;
g_AreaMenu.rows[convert_to_pause].bEnabled = bAreaSelected;
g_AreaMenu.rows[undo].bEnabled = m_bHasUndo;
EditMiniMenu( &g_AreaMenu, SM_BackFromAreaMenu );
}
break;
case EDIT_BUTTON_OPEN_EDIT_MENU:
EditMiniMenu( &g_MainMenu, SM_BackFromMainMenu );
break;
case EDIT_BUTTON_OPEN_INPUT_HELP:
DoHelp();
break;
case EDIT_BUTTON_OPEN_NEXT_STEPS:
case EDIT_BUTTON_OPEN_PREV_STEPS:
{
// don't keep undo when changing Steps
ClearUndo();
// save current steps
Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
ASSERT( pSteps );
pSteps->SetNoteData( m_NoteDataEdit );
// Get all Steps of this StepsType
StepsType st = pSteps->m_StepsType;
vector<Steps*> vSteps;
SongUtil::GetSteps( GAMESTATE->m_pCurSong, vSteps, st );
// Sort them by difficulty.
StepsUtil::SortStepsByTypeAndDifficulty( vSteps );
// Find out what index the current Steps are
vector<Steps*>::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;
}
it--;
break;
case EDIT_BUTTON_OPEN_NEXT_STEPS:
it++;
if( it==vSteps.end() )
{
SCREENMAN->PlayInvalidSound();
return;
}
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( pSteps->m_StepsType ).szName,
DifficultyToString( pSteps->GetDifficulty() ).c_str(),
pSteps->GetDescription().c_str(),
it - vSteps.begin() + 1,
int(vSteps.size()) );
SCREENMAN->SystemMessage( s );
m_soundSwitchSteps.Play();
}
break;
case EDIT_BUTTON_BPM_UP:
case EDIT_BUTTON_BPM_DOWN:
{
float fBPM = m_pSong->GetBPMAtBeat( GAMESTATE->m_fSongBeat );
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;
m_pSong->SetBPMAtBeat( GAMESTATE->m_fSongBeat, fNewBPM );
(fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play();
SetDirty( true );
}
break;
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;
}
unsigned i;
for( i=0; i<m_pSong->m_Timing.m_StopSegments.size(); i++ )
{
if( m_pSong->m_Timing.m_StopSegments[i].m_iStartRow == BeatToNoteRow(GAMESTATE->m_fSongBeat) )
break;
}
if( i == m_pSong->m_Timing.m_StopSegments.size() ) // there is no StopSegment at the current beat
{
// create a new StopSegment
if( fDelta > 0 )
m_pSong->AddStopSegment( StopSegment(BeatToNoteRow(GAMESTATE->m_fSongBeat), fDelta) );
}
else // StopSegment being modified is m_Timing.m_StopSegments[i]
{
m_pSong->m_Timing.m_StopSegments[i].m_fStopSeconds += fDelta;
if( m_pSong->m_Timing.m_StopSegments[i].m_fStopSeconds <= 0 )
m_pSong->m_Timing.m_StopSegments.erase( m_pSong->m_Timing.m_StopSegments.begin()+i,
m_pSong->m_Timing.m_StopSegments.begin()+i+1);
}
(fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play();
SetDirty( true );
}
break;
/*
case EDIT_BUTTON_DELAY_UP:
case EDIT_BUTTON_DELAY_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;
}
unsigned i;
for( i=0; i<m_pSong->m_Timing.m_StopSegments.size(); i++ )
{
if( m_pSong->m_Timing.m_StopSegments[i].m_iStartRow == BeatToNoteRow(GAMESTATE->m_fSongBeat) )
break;
}
if( i == m_pSong->m_Timing.m_StopSegments.size() ) // there is no delay segment at the current beat
{
// create a new delay segment
if( fDelta > 0 )
m_pSong->AddStopSegment( StopSegment(BeatToNoteRow(GAMESTATE->m_fSongBeat), fDelta, true) );
}
else // delay segment being modified is m_Timing.m_StopSegments[i]
{
m_pSong->m_Timing.m_StopSegments[i].m_fStopSeconds += fDelta;
if( m_pSong->m_Timing.m_StopSegments[i].m_fStopSeconds <= 0 )
m_pSong->m_Timing.m_StopSegments.erase( m_pSong->m_Timing.m_StopSegments.begin()+i,
m_pSong->m_Timing.m_StopSegments.begin()+i+1);
}
(fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play();
SetDirty( true );
}
break;
*/
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;
}
m_pSong->m_Timing.m_fBeat0OffsetInSeconds += fDelta;
(fDelta>0 ? m_soundValueIncrease : m_soundValueDecrease).Play();
SetDirty( true );
}
break;
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 );
}
break;
case EDIT_BUTTON_PLAY_SAMPLE_MUSIC:
PlayPreviewMusic();
break;
case EDIT_BUTTON_OPEN_BGCHANGE_LAYER1_MENU:
case EDIT_BUTTON_OPEN_BGCHANGE_LAYER2_MENU:
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<RString> 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_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 );
}
break;
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<Course*> 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 );
}
break;
case EDIT_BUTTON_OPEN_COURSE_ATTACK_MENU:
{
Course *pCourse = GAMESTATE->m_pCurCourse;
if( pCourse == NULL )
break;
CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex];
float fStartTime = m_pSong->GetElapsedTimeFromBeat( GAMESTATE->m_fSongBeat );
int iAttack = FindAttackAtTime( ce.attacks, fStartTime );
if( iAttack >= 0 )
{
const RString sDuration = ssprintf( "%.5f", 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 );
}
break;
case EDIT_BUTTON_ADD_COURSE_MODS:
{
float fStart, fEnd;
PlayerOptions po;
const Course *pCourse = GAMESTATE->m_pCurCourse;
if( pCourse == NULL )
break;
const CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex];
if( m_NoteFieldEdit.m_iBeginMarker == -1 )
{
fStart = -1;
fEnd = -1;
po.FromString( ce.sModifiers );
}
else
{
fStart = m_pSong->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 = m_pSong->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 );
}
break;
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
{
bool bAlreadyBGChangeHere = false;
BackgroundLayer iLayer = BACKGROUND_LAYER_1;
BackgroundChange bgChange;
bgChange.m_fStartBeat = GAMESTATE->m_fSongBeat;
FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(iLayer), bgc )
{
if( bgc->m_fStartBeat == GAMESTATE->m_fSongBeat )
{
bAlreadyBGChangeHere = true;
bgChange = *bgc;
m_pSong->GetBackgroundChanges(iLayer).erase( bgc );
break;
}
}
bgChange.m_def.m_sFile1 = sName;
m_pSong->AddBackgroundChange( iLayer, bgChange );
m_soundMarker.Play();
}
}
break;
case EDIT_BUTTON_PLAY_FROM_START:
HandleMainMenuChoice( play_whole_song );
break;
case EDIT_BUTTON_PLAY_FROM_CURSOR:
HandleMainMenuChoice( play_current_beat_to_end );
break;
case EDIT_BUTTON_PLAY_SELECTION:
HandleMainMenuChoice( play_selection );
break;
case EDIT_BUTTON_RECORD_SELECTION:
if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 )
{
HandleAreaMenuChoice( record );
}
else
{
if( g_iDefaultRecordLength.Get() == -1 )
{
m_iStartPlayingAt = BeatToNoteRow(GAMESTATE->m_fSongBeat);
m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() + 1 );
}
else
{
m_iStartPlayingAt = BeatToNoteRow( ftruncf(GAMESTATE->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;
}
TransitionEditState( STATE_RECORDING );
}
break;
case EDIT_BUTTON_RECORD_FROM_CURSOR:
m_iStartPlayingAt = BeatToNoteRow(GAMESTATE->m_fSongBeat);
m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() );
TransitionEditState( STATE_RECORDING );
break;
case EDIT_BUTTON_INSERT:
HandleAreaMenuChoice( insert_and_shift );
SCREENMAN->PlayInvalidSound();
break;
case EDIT_BUTTON_INSERT_SHIFT_PAUSES:
HandleAreaMenuChoice( shift_pauses_forward );
SCREENMAN->PlayInvalidSound();
break;
case EDIT_BUTTON_DELETE:
HandleAreaMenuChoice( delete_and_shift );
SCREENMAN->PlayInvalidSound();
break;
case EDIT_BUTTON_DELETE_SHIFT_PAUSES:
HandleAreaMenuChoice( shift_pauses_backward );
SCREENMAN->PlayInvalidSound();
break;
case EDIT_BUTTON_SAVE:
HandleMainMenuChoice( ScreenEdit::save );
break;
case EDIT_BUTTON_UNDO:
Undo();
break;
case EDIT_BUTTON_SWITCH_PLAYERS:
if( m_InputPlayerNumber != PLAYER_INVALID )
{
enum_add( m_InputPlayerNumber, 1 );
if( m_InputPlayerNumber == NUM_PLAYERS )
m_InputPlayerNumber = PLAYER_1;
}
break;
}
}
void ScreenEdit::InputRecord( const InputEventPlus &input, EditButton EditB )
{
if( input.type == IET_FIRST_PRESS && EditB == EDIT_BUTTON_RETURN_TO_EDIT )
{
TransitionEditState( STATE_EDITING );
return;
}
if( input.pn != PLAYER_1 )
return; // ignore
const int iCol = GAMESTATE->GetCurrentStyle()->GameInputToColumn( input.GameI );
switch( input.type )
{
DEFAULT_FAIL( input.type );
case IET_FIRST_PRESS:
{
if( EditIsBeingPressed(EDIT_BUTTON_REMOVE_NOTE) )
{
// Remove notes in Update.
break;
}
// Add a tap
float fBeat = GAMESTATE->m_fSongBeat;
fBeat = Quantize( fBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
const int iRow = BeatToNoteRow( fBeat );
if( iRow < 0 )
break;
// Don't add outside of the range.
if( iRow < m_iStartPlayingAt || iRow >= m_iStopPlayingAt )
return;
// 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 );
}
break;
case IET_REPEAT:
case IET_RELEASE:
// don't add or extend holds here; we do it in Update()
break;
}
}
void ScreenEdit::InputRecordPaused( const InputEventPlus &input, EditButton EditB )
{
if( input.type != IET_FIRST_PRESS )
return; // 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 );
break;
case EDIT_BUTTON_PLAY_SELECTION:
TransitionEditState( STATE_PLAYING );
break;
case EDIT_BUTTON_RECORD_SELECTION:
TransitionEditState( STATE_RECORDING );
break;
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;
}
}
// 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 );
break;
case EDIT_BUTTON_RETURN_TO_EDIT:
TransitionEditState( STATE_EDITING );
break;
}
}
void ScreenEdit::InputPlay( const InputEventPlus &input, EditButton EditB )
{
switch( input.type )
{
case IET_RELEASE:
case IET_FIRST_PRESS:
break;
default:
return;
}
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_INVALID:
break;
case GameButtonType_Step:
if( iCol != -1 )
m_Player->Step( iCol, -1, input.DeviceI.ts, false, bRelease );
break;
case GameButtonType_Fret:
if( iCol != -1 )
m_Player->Fret( iCol, -1, input.DeviceI.ts, false, bRelease );
break;
case GameButtonType_Strum:
m_Player->Strum( iCol, -1, input.DeviceI.ts, false, bRelease );
break;
}
}
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 = GAMESTATE->m_fSongBeat;
TransitionEditState( STATE_EDITING );
break;
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;
}
m_pSong->m_Timing.m_fBeat0OffsetInSeconds += fOffsetDelta;
}
break;
}
}
}
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 = GAMESTATE->m_fSongBeat;
break;
case STATE_PLAYING:
AdjustSync::HandleSongEnd();
if( AdjustSync::IsSyncDataChanged() )
ScreenSaveSync::PromptSaveSync();
break;
case STATE_RECORDING:
SetDirty( true );
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;
}
}
// 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();
switch( em )
{
case STATE_RECORDING:
case STATE_RECORDING_PAUSED:
PO_GROUP_ASSIGN_N( GAMESTATE->m_pPlayerState[PLAYER_1]->m_PlayerOptions, ModsLevel_Stage, m_fScrolls, PlayerOptions::SCROLL_CENTERED, 1.0f );
break;
}
// 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.
GAMESTATE->m_fSongBeat = m_fBeatToReturnTo;
// Make sure we're snapped.
GAMESTATE->m_fSongBeat = Quantize( GAMESTATE->m_fSongBeat, NoteTypeToBeat(m_SnapDisplay.GetNoteType()) );
/* Playing and recording have lead-ins, which may start before beat 0;
* make sure we don't stay there if we escaped out early. */
GAMESTATE->m_fSongBeat = max( GAMESTATE->m_fSongBeat, 0 );
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. */
float fSeconds = m_pSong->m_Timing.GetElapsedTimeFromBeat( NoteRowToBeat(m_iStartPlayingAt) ) - 1;
GAMESTATE->UpdateSongPosition( fSeconds, m_pSong->m_Timing );
GAMESTATE->m_bGameplayLeadIn.Set( false );
/* Reset the note skin, in case preferences have changed. */
// XXX
// GAMESTATE->ResetNoteSkins();
//GAMESTATE->res
break;
}
case STATE_RECORDING_PAUSED:
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 );
pSteps->SetNoteData( m_NoteDataEdit );
m_pSong->ReCalculateRadarValuesAndLastBeat();
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;
}
// 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 = m_pSong->GetElapsedTimeFromBeat(GAMESTATE->m_fSongBeat);
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;
}
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 = GAMESTATE->m_fSongBeat;
if( fOriginalBeat == fDestinationBeat )
return;
GAMESTATE->m_fSongBeat = fDestinationBeat;
// check to see if they're holding a button
for( int n=0; n<NUM_EDIT_BUTTON_COLUMNS; n++ )
{
int iCol = n;
// Ctrl + number = input to right half
if( EditIsBeingPressed(EDIT_BUTTON_RIGHT_SIDE) )
ShiftToRightSide( iCol, m_NoteDataEdit.GetNumTracks() );
if( iCol >= 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();
}
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." );
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 )
{
HandleAreaMenuChoice( (AreaMenuChoice)ScreenMiniMenu::s_iLastRowCode, ScreenMiniMenu::s_viLastAnswers );
}
else if( SM == SM_BackFromStepsInformation )
{
HandleStepsInformationChoice( (StepsInformationChoice)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_BackFromBPMChange )
{
float fBPM = StringToFloat( ScreenTextEntry::s_sLastAnswer );
if( fBPM > 0 )
m_pSong->SetBPMAtBeat( GAMESTATE->m_fSongBeat, fBPM );
SetDirty( true );
}
else if( SM == SM_BackFromStopChange )
{
float fStop = StringToFloat( ScreenTextEntry::s_sLastAnswer );
if( fStop >= 0 )
m_pSong->m_Timing.SetStopAtBeat( GAMESTATE->m_fSongBeat, fStop );
SetDirty( true );
}
else if( SM == SM_BackFromDelayChange )
{
float fDelay = StringToFloat( ScreenTextEntry::s_sLastAnswer );
if( fDelay >= 0 )
m_pSong->m_Timing.SetStopAtBeat( GAMESTATE->m_fSongBeat, fDelay, true );
SetDirty( true );
}
else if( SM == SM_BackFromTimeSignatureNumeratorChange )
{
int iNum = atoi( ScreenTextEntry::s_sLastAnswer );
if( iNum > 0 )
{
m_pSong->m_Timing.SetTimeSignatureNumeratorAtBeat( GAMESTATE->m_fSongBeat, iNum );
}
SetDirty( true );
}
else if ( SM == SM_BackFromTimeSignatureDenominatorChange )
{
int iDen = atoi( ScreenTextEntry::s_sLastAnswer );
if( iDen > 0)
{
m_pSong->m_Timing.SetTimeSignatureDenominatorAtBeat( GAMESTATE->m_fSongBeat, iDen );
}
SetDirty( true );
}
else if ( SM == SM_BackFromTickcountChange )
{
int iTick = atoi( ScreenTextEntry::s_sLastAnswer );
if ( iTick >= 0 && iTick <= ROWS_PER_BEAT )
{
m_pSong->m_Timing.SetTickcountAtBeat( GAMESTATE->m_fSongBeat, iTick );
}
SetDirty( true );
}
else if ( SM == SM_BackFromComboChange )
{
int iCombo = atoi( ScreenTextEntry::s_sLastAnswer );
if ( iCombo >= 0 )
{
m_pSong->m_Timing.SetComboAtBeat( GAMESTATE->m_fSongBeat, iCombo );
}
SetDirty( true );
}
2011-03-25 17:40:56 -04:00
else if ( SM == SM_BackFromWarpChange )
{
float fWarp = StringToFloat( ScreenTextEntry::s_sLastAnswer );
2011-03-28 15:08:09 -04:00
m_pSong->m_Timing.SetWarpAtBeat( GAMESTATE->m_fSongBeat, fWarp );
2011-03-25 17:40:56 -04:00
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 );
}
}
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_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_BackFromInsertCourseAttack )
{
int iDurationChoice = ScreenMiniMenu::s_viLastAnswers[0];
Course *pCourse = GAMESTATE->m_pCurCourse;
CourseEntry &ce = pCourse->m_vEntries[GAMESTATE->m_iEditCourseEntryIndex];
int iAttack;
g_fLastInsertAttackPositionSeconds = m_pSong->GetElapsedTimeFromBeat( GAMESTATE->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_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_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<Steps*> &apSteps = pSong->GetAllSteps();
vector<Steps*> 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. */
GAMESTATE->m_fSongBeat = m_fTrailingBeat;
}
else if( SM == SM_LoseFocus )
{
// Snap the trailing beat, in case we lose focus while tweening.
m_fTrailingBeat = GAMESTATE->m_fSongBeat;
}
ScreenWithMenuElements::HandleScreenMessage( SM );
}
void ScreenEdit::OnSnapModeChange()
{
m_soundChangeSnap.Play();
NoteType nt = m_SnapDisplay.GetNoteType();
int iStepIndex = BeatToNoteRow( GAMESTATE->m_fSongBeat );
int iElementsPerNoteType = BeatToNoteRow( NoteTypeToBeat(nt) );
int iStepIndexHangover = iStepIndex % iElementsPerNoteType;
GAMESTATE->m_fSongBeat -= 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 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 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 ChangeLastBeatHint( const RString &sNew )
{
GAMESTATE->m_pCurSong->m_fSpecifiedLastBeat = StringToFloat( sNew );
}
// 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<int> &iAnswers )
{
switch( c )
{
DEFAULT_FAIL( c );
case play_selection:
if( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 )
HandleAreaMenuChoice( 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_fSongBeat);
m_iStopPlayingAt = max( m_iStartPlayingAt, m_NoteDataEdit.GetLastRow() );
TransitionEditState( STATE_PLAYING );
}
break;
case set_selection_start:
{
const int iCurrentRow = BeatToNoteRow(GAMESTATE->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_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];
float fMusicSeconds = m_pSoundMusic->GetLengthSeconds();
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].iDefaultChoice = clamp( pSteps->GetMeter()-1, 0, MAX_METER+1 );
g_StepsInformation.rows[meter].bEnabled = (EDIT_MODE.GetValue() >= EditMode_Home);
g_StepsInformation.rows[predict_meter].SetOneUnthemedChoice( ssprintf("%.2f",pSteps->PredictMeter()) );
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[tap_notes].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumTapNotes()) );
g_StepsInformation.rows[jumps].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumJumps()) );
g_StepsInformation.rows[hands].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHands()) );
g_StepsInformation.rows[quads].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumQuads()) );
g_StepsInformation.rows[holds].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumHoldNotes()) );
g_StepsInformation.rows[mines].SetOneUnthemedChoice( ssprintf("%d", m_NoteDataEdit.GetNumMines()) );
g_StepsInformation.rows[stream].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetStreamRadarValue(m_NoteDataEdit,fMusicSeconds)) );
g_StepsInformation.rows[voltage].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetVoltageRadarValue(m_NoteDataEdit,fMusicSeconds)) );
g_StepsInformation.rows[air].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetAirRadarValue(m_NoteDataEdit,fMusicSeconds)) );
g_StepsInformation.rows[freeze].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetFreezeRadarValue(m_NoteDataEdit,fMusicSeconds)) );
g_StepsInformation.rows[chaos].SetOneUnthemedChoice( ssprintf("%.2f", NoteDataUtil::GetChaosRadarValue(m_NoteDataEdit,fMusicSeconds)) );
EditMiniMenu( &g_StepsInformation, SM_BackFromStepsInformation, SM_None );
}
break;
case save:
case save_on_exit:
{
m_CurrentAction = c;
// copy edit into current Steps
m_pSteps->SetNoteData( m_NoteDataEdit );
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: ASSERT(0);
}
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_beat_hint].SetOneUnthemedChoice( ssprintf("%.5f", pSong->m_fSpecifiedLastBeat) );
EditMiniMenu( &g_SongInformation, SM_BackFromSongInformation );
}
break;
case edit_timing_data:
{
const Song* pSong = GAMESTATE->m_pCurSong;
TimingData pTime = pSong->m_Timing;
const float fBeat = GAMESTATE->m_fSongBeat;
g_TimingDataInformation.rows[bpm].SetOneUnthemedChoice( ssprintf("%.5f", pSong->GetBPMAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[stop].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetStopAtBeat( fBeat ) ) ) ;
g_TimingDataInformation.rows[delay].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetDelayAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[time_signature_numerator].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTimeSignatureNumeratorAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[time_signature_denominator].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTimeSignatureDenominatorAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[tickcount].SetOneUnthemedChoice( ssprintf("%d", pTime.GetTickcountAtBeat( fBeat ) ) );
g_TimingDataInformation.rows[combo].SetOneUnthemedChoice( ssprintf("%d", pTime.GetComboAtBeat( fBeat ) ) );
2011-03-25 17:40:56 -04:00
g_TimingDataInformation.rows[warp].SetOneUnthemedChoice( ssprintf("%.5f", pTime.GetWarpAtBeat( fBeat ) ) );
EditMiniMenu( &g_TimingDataInformation, SM_BackFromTimingDataInformation );
}
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;
};
}
void ScreenEdit::HandleAreaMenuChoice( AreaMenuChoice c, const vector<int> &iAnswers, bool bAllowUndo )
{
bool bSaveUndo = false;
switch( c )
{
DEFAULT_FAIL( c );
case cut:
case copy:
case play:
case record:
case undo:
bSaveUndo = false;
break;
case paste_at_current_beat:
case paste_at_begin_marker:
case clear:
case quantize:
case turn:
case transform:
case alter:
case tempo:
case insert_and_shift:
case delete_and_shift:
case shift_pauses_forward:
case shift_pauses_backward:
case convert_to_pause:
case convert_pause_to_beat:
bSaveUndo = true;
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 cut:
{
HandleAreaMenuChoice( copy );
HandleAreaMenuChoice( clear );
}
break;
case copy:
{
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 );
m_Clipboard.ClearAll();
m_Clipboard.CopyRange( m_NoteDataEdit, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker );
}
break;
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( GAMESTATE->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 clear:
{
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 );
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 );
HandleAreaMenuChoice( 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 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, iBeginRow ); 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 );
HandleAreaMenuChoice( 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
m_pSong->m_Timing.ScaleRegion( fScale, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker, true );
// scale all other steps.
const vector<Steps*> sIter = m_pSong->GetAllSteps();
RString sTempStyle, sTempDiff;
for( unsigned i = 0; i < sIter.size(); i++ )
{
if( sIter[i]->IsAutogen() )
continue;
// XXX: Edits are distinguished by description.
// Compare vs m_pSteps.
if( (sIter[i]->m_StepsType == GAMESTATE->m_pCurSteps[PLAYER_1]->m_StepsType) &&
(sIter[i]->GetDifficulty() == GAMESTATE->m_pCurSteps[PLAYER_1]->GetDifficulty()) )
continue;
NoteData ndTemp;
sIter[i]->GetNoteData( ndTemp );
NoteDataUtil::ScaleRegion( ndTemp, fScale, m_NoteFieldEdit.m_iBeginMarker, m_NoteFieldEdit.m_iEndMarker );
sIter[i]->SetNoteData( ndTemp );
}
m_NoteFieldEdit.m_iEndMarker = iNewEndIndex;
}
break;
case play:
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 );
m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker;
m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker;
TransitionEditState( STATE_PLAYING );
break;
case record:
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 );
m_iStartPlayingAt = m_NoteFieldEdit.m_iBeginMarker;
m_iStopPlayingAt = m_NoteFieldEdit.m_iEndMarker;
TransitionEditState( STATE_RECORDING );
break;
case insert_and_shift:
NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat), BeatToNoteRow(1) );
break;
case delete_and_shift:
NoteDataUtil::DeleteRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat), BeatToNoteRow(1) );
break;
case shift_pauses_forward:
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat), BeatToNoteRow(1) );
break;
case shift_pauses_backward:
m_pSong->m_Timing.DeleteRows( BeatToNoteRow(GAMESTATE->m_fSongBeat), BeatToNoteRow(1) );
break;
case convert_to_pause:
{
ASSERT( m_NoteFieldEdit.m_iBeginMarker!=-1 && m_NoteFieldEdit.m_iEndMarker!=-1 );
float fMarkerStart = m_pSong->m_Timing.GetElapsedTimeFromBeat( NoteRowToBeat(m_NoteFieldEdit.m_iBeginMarker) );
float fMarkerEnd = m_pSong->m_Timing.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
);
m_pSong->m_Timing.DeleteRows( m_NoteFieldEdit.m_iBeginMarker,
m_NoteFieldEdit.m_iEndMarker-m_NoteFieldEdit.m_iBeginMarker );
m_pSong->m_Timing.SetStopAtRow( m_NoteFieldEdit.m_iBeginMarker, fStopLength );
m_NoteFieldEdit.m_iBeginMarker = -1;
m_NoteFieldEdit.m_iEndMarker = -1;
break;
}
case convert_pause_to_beat:
{
// TODO: Convert both Delays and Stops at once.
float fStopSeconds = m_pSong->m_Timing.GetStopAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) );
m_pSong->m_Timing.SetStopAtBeat( GAMESTATE->m_fSongBeat, 0 );
float fStopBeats = fStopSeconds * m_pSong->GetBPMAtBeat(GAMESTATE->m_fSongBeat) / 60;
// don't move the step from where it is, just move everything later
NoteDataUtil::InsertRows( m_NoteDataEdit, BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
m_pSong->m_Timing.InsertRows( BeatToNoteRow(GAMESTATE->m_fSongBeat) + 1, BeatToNoteRow(fStopBeats) );
}
break;
case undo:
Undo();
break;
};
if( bSaveUndo )
CheckNumberOfNotesAndUndo();
}
static LocalizedString ENTER_NEW_DESCRIPTION( "ScreenEdit", "Enter a new description." );
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." );
void ScreenEdit::HandleStepsInformationChoice( StepsInformationChoice c, const vector<int> &iAnswers )
{
Steps* pSteps = GAMESTATE->m_pCurSteps[PLAYER_1];
Difficulty dc = (Difficulty)iAnswers[difficulty];
pSteps->SetDifficulty( dc );
int iMeter = iAnswers[meter]+1;
pSteps->SetMeter( iMeter );
switch( c )
{
case description:
ScreenTextEntry::TextEntry(
SM_None,
ENTER_NEW_DESCRIPTION,
m_pSteps->GetDescription(),
(dc == Difficulty_Edit) ? MAX_EDIT_STEPS_DESCRIPTION_LENGTH : 255,
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;
}
}
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_BEAT_HINT ("ScreenEdit","Enter a new last beat hint.");
void ScreenEdit::HandleSongInformationChoice( SongInformationChoice c, const vector<int> &iAnswers )
{
Song* pSong = GAMESTATE->m_pCurSong;
switch( c )
{
DEFAULT_FAIL(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_beat_hint:
ScreenTextEntry::TextEntry( SM_None, ENTER_LAST_BEAT_HINT, ssprintf("%.5f", pSong->m_fSpecifiedLastBeat), 20, ScreenTextEntry::FloatValidate, ChangeLastBeatHint, NULL );
};
}
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_NUMERATOR_VALUE ( "ScreenEdit", "Enter a new Time Signature numerator value." );
static LocalizedString ENTER_TIME_SIGNATURE_DENOMINATOR_VALUE ( "ScreenEdit", "Enter a new Time Signature denominator value." );
static LocalizedString ENTER_TICKCOUNT_VALUE ( "ScreenEdit", "Enter a new Tickcount value." );
static LocalizedString ENTER_COMBO_VALUE ( "ScreenEdit", "Enter a new Combo value." );
2011-03-25 17:40:56 -04:00
static LocalizedString ENTER_WARP_VALUE ( "ScreenEdit", "Enter a new Warp value." );
void ScreenEdit::HandleTimingDataInformationChoice( TimingDataInformationChoice c, const vector<int> &iAnswers )
{
switch( c )
{
DEFAULT_FAIL( c );
case bpm:
ScreenTextEntry::TextEntry(
SM_BackFromBPMChange,
ENTER_BPM_VALUE,
ssprintf( "%.4f", m_pSong->GetBPMAtBeat(GAMESTATE->m_fSongBeat) ),
10
);
break;
case stop:
ScreenTextEntry::TextEntry(
SM_BackFromStopChange,
ENTER_STOP_VALUE,
ssprintf( "%.4f", m_pSong->m_Timing.GetStopAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) ) ),
10
);
break;
case delay:
ScreenTextEntry::TextEntry(
SM_BackFromDelayChange,
ENTER_DELAY_VALUE,
ssprintf( "%.4f", m_pSong->m_Timing.GetDelayAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) ) ),
10
);
break;
case time_signature_numerator:
ScreenTextEntry::TextEntry(
SM_BackFromTimeSignatureNumeratorChange,
ENTER_TIME_SIGNATURE_NUMERATOR_VALUE,
ssprintf( "%d", m_pSong->m_Timing.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_fSongBeat ).m_iNumerator ),
3
);
break;
case time_signature_denominator:
ScreenTextEntry::TextEntry(
SM_BackFromTimeSignatureDenominatorChange,
ENTER_TIME_SIGNATURE_DENOMINATOR_VALUE,
ssprintf( "%d", m_pSong->m_Timing.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_fSongBeat ).m_iDenominator ),
3
);
break;
case tickcount:
ScreenTextEntry::TextEntry(
SM_BackFromTickcountChange,
ENTER_TICKCOUNT_VALUE,
ssprintf( "%d", m_pSong->m_Timing.GetTickcountAtBeat( GAMESTATE->m_fSongBeat ) ),
2
);
break;
case combo:
ScreenTextEntry::TextEntry(
SM_BackFromComboChange,
ENTER_COMBO_VALUE,
ssprintf( "%d", m_pSong->m_Timing.GetComboAtBeat( GAMESTATE->m_fSongBeat ) ),
4
);
break;
2011-03-25 17:40:56 -04:00
case warp:
ScreenTextEntry::TextEntry(
SM_BackFromWarpChange,
ENTER_WARP_VALUE,
ssprintf( "%.4f", m_pSong->m_Timing.GetWarpAtRow( BeatToNoteRow(GAMESTATE->m_fSongBeat) ) ),
10
);
break;
}
}
void ScreenEdit::HandleBGChangeChoice( BGChangeChoice c, const vector<int> &iAnswers )
{
BackgroundChange newChange;
FOREACH( BackgroundChange, m_pSong->GetBackgroundChanges(g_CurrentBGChangeLayer), iter )
{
if( iter->m_fStartBeat == GAMESTATE->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_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 );
}
GAMESTATE->m_pPlayerState[PLAYER_1]->RebuildPlayerOptionsFromActiveAttacks();
}
void ScreenEdit::CopyToLastSave()
{
ASSERT( GAMESTATE->m_pCurSong );
ASSERT( GAMESTATE->m_pCurSteps[PLAYER_1] );
m_SongLastSave = *GAMESTATE->m_pCurSong;
m_vStepsLastSave.clear();
const vector<Steps*> &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<Steps*> &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] );
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 = new Steps();
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 MORE_THAN_NOTES ( "ScreenEdit", "More than %d notes per measure is not allowed. This change has been reverted." );
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;
TimeSignatureSegment curTime = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_fSongBeat );
int rowsPerMeasure = curTime.m_iDenominator * curTime.m_iNumerator;
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( iNumNotesThisMeasure > MAX_NOTES_PER_MEASURE )
{
Undo();
m_bHasUndo = false;
RString sError = ssprintf( CREATES_MORE_THAN_NOTES.GetValue() + "\n\n" + MORE_THAN_NOTES.GetValue(), MAX_NOTES_PER_MEASURE, MAX_NOTES_PER_MEASURE );
ScreenPrompt::Prompt( SM_None, sError );
return;
}
}
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:
{
float fEndBeat = GAMESTATE->m_pCurSong->m_fLastBeat;
/* 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. */
int beatsPerMeasure = GAMESTATE->m_pCurSong->m_Timing.GetTimeSignatureSegmentAtBeat( GAMESTATE->m_fSongBeat ).m_iNumerator;
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 m_pCurSong->m_fLastBeat
* 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<EditButton> 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
}
};
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 player (Routine only)", EDIT_BUTTON_SWITCH_PLAYERS ),
};
static bool IsMapped( EditButton eb, const MapEditToDI &editmap )
{
for( int s=0; s<NUM_EDIT_TO_DEVICE_SLOTS; s++ )
{
DeviceInput diPress = editmap.button[eb][s];
if( diPress.IsValid() )
return true;
}
return false;
}
static void ProcessKeyName( RString &s )
{
s.Replace( "Key_", "" );
}
static void ProcessKeyNames( vector<RString> &vs )
{
FOREACH( RString, vs, s )
ProcessKeyName( *s );
sort( vs.begin(), vs.end() );
vector<RString>::iterator toDelete = unique( vs.begin(), vs.end() );
vs.erase(toDelete, vs.end());
}
static RString GetDeviceButtonsLocalized( const vector<EditButton> &veb, const MapEditToDI &editmap )
{
vector<RString> vsPress;
vector<RString> vsHold;
FOREACH_CONST( EditButton, veb, eb )
{
if( !IsMapped( *eb, editmap ) )
continue;
for( int s=0; s<NUM_EDIT_TO_DEVICE_SLOTS; s++ )
{
DeviceInput diPress = editmap.button[*eb][s];
DeviceInput diHold = editmap.hold[*eb][s];
if( diPress.IsValid() )
vsPress.push_back( Capitalize(INPUTMAN->GetLocalizedInputString(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::DoHelp()
{
g_EditHelp.rows.clear();
for( unsigned i=0; i<ARRAYLEN(g_EditHelpLines); ++i )
{
const EditHelpLine &hl = g_EditHelpLines[i];
if( !IsMapped(hl.veb[0],m_EditMappingsDeviceInput) )
continue;
RString sButtons = GetDeviceButtonsLocalized( hl.veb, m_EditMappingsDeviceInput );
RString sDescription = THEME->GetString( "EditHelpDescription", hl.szEnglishDescription );
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<ScreenEdit>
{
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.
*/