Charter/Editor additions and fixes (#793)

* camera highlights again

* option to mute all hitsounds

* context menus inside context menus and wip sliders in context menu

* wip volume slider stuff

* strumline preview fix with resizable editors

* more slider stuff

* editor sfx option should now actually work for everything

* fixed strumline vocals volume, fixed note type option keybinds and moved bookmarks and note types into submenus

* fix for saving difficulty specific meta json

* some extra misc changes

fixed camera highlights not updating when zooming in/out, optimized some of the highlight draw code so it doesn't do a full loop, and added the note scale flag into the strumline preview

* fix compiling after merge
This commit is contained in:
TheZoroForce240
2026-03-01 21:13:33 +07:00
committed by GitHub
parent f44d0192af
commit 4fe3ba76e3
25 changed files with 621 additions and 155 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 804 B

After

Width:  |  Height:  |  Size: 1.0 KiB

+8 -3
View File
@@ -223,6 +223,7 @@
<str id="resetZoom">Reset Zoom</str>
<str id="showSectionsSeparator">Show Sections Separator</str>
<str id="showBeatsSeparator">Show Beats Separator</str>
<str id="showCameraHighlights">Show Camera Highlights</str>
<str id="rainbowWaveforms">Rainbow Waveforms</str>
<str id="lowDetailWaveforms">Low Detail Waveforms</str>
<str id="scrollLeft">Scroll Left</str>
@@ -236,8 +237,9 @@
<str id="goEnd">Go forward to the end</str> <!-- used to be "Go to the end" -->
<str id="addOpponentCamera">Add camera on Opponent</str>
<str id="addPlayerCamera">Add camera on Player</str>
<str id="muteInst">Mute instrumental</str>
<str id="muteVoices">Mute voices</str>
<str id="inst">Instrumental</str>
<str id="voices">Voices</str>
<str id="hitsounds">Hitsounds</str>
</group>
<group name="BookmarkMenu" prefix="bookmarks.">
@@ -250,6 +252,7 @@
<str id="editBookmarkListTitle">Bookmark List</str>
<str id="editBookmarksTitle">Edit Bookmarks</str>
<str id="newBookmarkName">New Bookmark</str>
<str id="bookmarkList">Bookmark List</str>
</group>
<str id="topBar.note">Note</str>
@@ -258,6 +261,7 @@
<str id="subtractSustainLength">Subtract sustain length</str>
<str id="selectAll">Select all</str>
<str id="selectMeasure">Select measure</str>
<str id="noteTypesList">Note Types List</str>
<str id="editNoteTypesList">Edit Note Types List</str>
</group>
<str id="topBar.snap">Snap</str>
@@ -308,8 +312,9 @@
<group name="StrumlineOptions" prefix="strumLine.">
<str id="button-name">Options ↓</str>
<str id="waveforms">Waveforms</str>
<str id="hitsounds">Hitsounds</str>
<str id="muteVocals">Mute Vocals</str>
<str id="vocals">Vocals</str>
<str id="edit">Edit</str>
<str id="delete">Delete</str>
+7 -3
View File
@@ -278,9 +278,13 @@ class Chart {
var filteredChart = filterChartForSaving(chart, saveSettings.saveMetaInChart, saveSettings.saveLocalEvents, saveSettings.saveGlobalEvents && saveSettings.seperateGlobalEvents != true);
#if sys
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "";
var metaPath = 'meta$variantSuffix.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
var songPath = saveSettings.songFolder == null ? 'songs/${chart.meta.name}' : saveSettings.songFolder, variantSuffix = variant != null && variant != "" ? '-$variant' : "", difficultySuffix = difficulty != null && difficulty != "" ? '-$difficulty' : "";
var metaPath = 'meta$variantSuffix.json', altMetaPath = 'meta${variantSuffix}${difficultySuffix}.json', prettyPrint = saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null, temp:String;
if ((temp = Paths.assetsTree.getPath('assets/$songPath/$altMetaPath')) != null) { //check for difficulty specific
songPath = temp.substr(0, temp.length - altMetaPath.length - 1);
metaPath = temp;
}
else if ((temp = Paths.assetsTree.getPath('assets/$songPath/$metaPath')) != null) {
songPath = temp.substr(0, temp.length - metaPath.length - 1);
metaPath = temp;
}
@@ -403,12 +403,12 @@ class CharacterAnimButton extends UIButton {
public function toggleGhost() {
if (valid && parent.ghosts.indexOf(anim) == -1) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTENABLE_SOUND);
parent.ghosts.push(anim);
ghostIcon.animation.play("alive", true);
ghostIcon.color = 0xFFFFFFFF;
} else {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARACTER_GHOSTDISABLE_SOUND);
parent.ghosts.remove(anim);
ghostIcon.animation.play("dead", true);
ghostIcon.color = 0xFFADADAD;
@@ -119,7 +119,7 @@ class CharacterAnimsWindow extends UIButtonList<CharacterAnimButton> {
}
public function deleteAnimation(button:CharacterAnimButton, addToUndo:Bool = true) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (buttons.members.length <= 1) return;
if (character.getAnimName() == button.anim)
@:privateAccess CharacterEditor.instance._animation_down(null);
@@ -453,7 +453,7 @@ class CharacterEditor extends UIState {
function _file_save(_) {
#if sys
FlxG.sound.play(Paths.sound('editors/save'));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/characters/${character.curCharacter}.xml',
buildCharacter()
@@ -465,7 +465,7 @@ class CharacterEditor extends UIState {
}
function _file_saveas(_) {
FlxG.sound.play(Paths.sound('editors/save'));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
openSubState(new SaveSubstate(buildCharacter(), {
defaultSaveFile: '${character.curCharacter}.xml'
}));
@@ -499,7 +499,7 @@ class CharacterEditor extends UIState {
}
function _undo(undo:CharacterEditorChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
switch (undo) {
case null: // do nothing
case CCharEditPosition(oldPos, newPos):
@@ -573,7 +573,7 @@ class CharacterEditor extends UIState {
}
function _redo(redo:CharacterEditorChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
switch (redo) {
case null: // do nothing
case CCharEditPosition(oldPos, newPos):
+154 -53
View File
@@ -77,10 +77,10 @@ class Charter extends UIState {
public var strumlineLockButton:CharterStrumlineButton;
public var hitsound:FlxSound;
public var hitsoundGlobalVolume:Float = 1.0;
public var metronome:FlxSound;
public var vocals:FlxSound;
public var voicesMuted:Bool = false;
public var quant:Int = 16;
public var quants:Array<Int> = [4, 8, 12, 16, 20, 24, 32, 48, 64, 192]; // different quants
@@ -96,6 +96,7 @@ class Charter extends UIState {
public var rightEventRowText:UIText;
public var leftEventsGroup:CharterEventGroup = new CharterEventGroup();
public var rightEventsGroup:CharterEventGroup = new CharterEventGroup();
public var cameraMovementChanges:Array<CameraChange> = [];
public var charterCamera:FlxCamera;
public var uiCamera:FlxCamera;
@@ -308,6 +309,11 @@ class Charter extends UIState {
onSelect: _view_showeventBeatSeparator,
icon: Options.charterShowBeats ? 1 : 0
},
{
label: translate("view.showCameraHighlights"),
onSelect: _view_showeventCameraHighlights,
icon: Options.charterShowCameraHighlights ? 1 : 0
},
null,
{
label: translate("view.rainbowWaveforms"),
@@ -515,7 +521,7 @@ class Charter extends UIState {
strumlineLockButton = new CharterStrumlineButton("editors/charter/lock-strumline", translate("lock-unlock"));
strumlineLockButton.onClick = function () {
FlxG.sound.play(Paths.sound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND));
UIState.playEditorSound(!strumLines.draggable ? Flags.DEFAULT_CHARTER_STRUMUNLOCK_SOUND : Flags.DEFAULT_CHARTER_STRUMLOCK_SOUND);
if (strumLines != null) {
strumLines.draggable = !strumLines.draggable;
strumlineLockButton.textTweenColor.color = strumLines.draggable ? 0xFF5C95CA : 0xFFE16565;
@@ -685,6 +691,7 @@ class Charter extends UIState {
CharterGridSeperatorBase.lastConductorSprY = Math.NEGATIVE_INFINITY;
updateWaveforms();
updateCameraChanges();
}
public function getWavesToGenerate():Array<{name:String, sound:FlxSound}> {
@@ -740,6 +747,38 @@ class Charter extends UIState {
}
}
public function updateCameraChanges() {
if (!Options.charterShowCameraHighlights) return;
cameraMovementChanges = [];
for (grp in [leftEventsGroup, rightEventsGroup]) {
grp.filterEvents();
grp.sortEvents();
for(e in grp.members) {
for(event in e.events) {
if (event.name == "Camera Movement") {
cameraMovementChanges.push({
strumLineID: event.params[0],
step: e.step,
endStep: __endStep
});
}
}
}
}
//need to sort again for both local and global events to be used
cameraMovementChanges.sort(function(e1, e2) {
return FlxSort.byValues(FlxSort.ASCENDING, e1.step, e2.step);
});
//update previous change
if (cameraMovementChanges.length > 0) {
for (i in 1...cameraMovementChanges.length) {
cameraMovementChanges[i-1].endStep = cameraMovementChanges[i].step;
}
}
}
public override function beatHit(curBeat:Int) {
super.beatHit(curBeat);
if (FlxG.sound.music.playing) {
@@ -802,7 +841,7 @@ class Charter extends UIState {
else
Chart.save(PlayState.SONG, __diff.toLowerCase(), __variant, {saveMetaInChart: true, saveLocalEvents: true, seperateGlobalEvents: true, prettyPrint: Options.editorCharterPrettyPrint});
FlxG.sound.play(Paths.sound('editors/save'));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
undos.save();
}
autoSaveNotif.cancelled = false;
@@ -962,7 +1001,7 @@ class Charter extends UIState {
notesGroup.add(note);
selection = [note];
undos.addToUndo(CCreateSelection([note]));
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEPLACE_SOUND);
}
isSelecting = false;
}
@@ -1091,7 +1130,7 @@ class Charter extends UIState {
if (selected == null) return selected;
if (selected is CharterNote) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARTER_NOTEDELETE_SOUND);
var note:CharterNote = cast selected;
note.strumLineID = strumLines.members.indexOf(note.strumLine);
note.strumLine = null; // For static undos :D
@@ -1571,20 +1610,20 @@ class Charter extends UIState {
else {undos = null; FlxG.switchState(new CharterSelection()); Charter.instance.__clearStatics();}
}
function _file_save_all(_) {saveEverything(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_save(_) {saveChart(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas(_) {saveChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_events_save(_) {saveEvents(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_events_saveas(_) {saveEventsAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_save_no_events(_) {saveChart(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_no_events(_) {saveChartAs(true, false); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_meta_save(_) {saveMeta(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_meta_saveas(_) {saveMetaAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_saveas_psych(_) {savePsychChartAs(); FlxG.sound.play(Paths.sound('editors/save'));}
function _file_save_all(_) {saveEverything(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save(_) {saveChart(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas(_) {saveChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_save(_) {saveEvents(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_events_saveas(_) {saveEventsAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_save_no_events(_) {saveChart(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_no_events(_) {saveChartAs(true, false); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_save(_) {saveMeta(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_meta_saveas(_) {saveMetaAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_fnflegacy(_) {saveLegacyChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _file_saveas_psych(_) {savePsychChartAs(); UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);}
function _edit_copy(_, playSFX=true) {
if (playSFX) FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
if (playSFX) UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
if(selection.length == 0) return;
var minStep:Float = selection[0].step;
@@ -1603,7 +1642,7 @@ class Charter extends UIState {
];
}
function _edit_paste(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_PASTE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
if (clipboard.length <= 0) return;
var minStep = curStep;
@@ -1631,7 +1670,7 @@ class Charter extends UIState {
}
function _edit_cut(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CUT_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
if (selection == null || selection.length == 0) return;
_edit_copy(_, false);
@@ -1639,7 +1678,7 @@ class Charter extends UIState {
}
function _edit_delete(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DELETE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DELETE_SOUND);
if (selection == null || selection.length == 0) return;
selection.loop((n:CharterNote) -> {
noteDeleteAnims.deleteNotes.push({note: n, time: noteDeleteAnims.deleteTime});
@@ -1648,7 +1687,7 @@ class Charter extends UIState {
}
function _undo(undo:CharterChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_UNDO_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
switch(undo) {
case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine):
@@ -1709,7 +1748,7 @@ class Charter extends UIState {
}
function _redo(redo:CharterChange) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
switch(redo) {
case null: // do nothing
case CDeleteStrumLine(strumLineID, strumLine):
@@ -1808,14 +1847,24 @@ class Charter extends UIState {
function _playback_metronome(t) {
t.icon = (Options.charterMetronomeEnabled = !Options.charterMetronomeEnabled) ? 1 : 0;
}
function _song_muteinst(t) {
FlxG.sound.music.volume = FlxG.sound.music.volume > 0 ? 0 : 1;
t.icon = 1 - Std.int(Math.ceil(FlxG.sound.music.volume));
public function _slider_mutetoggle(t:UIContextMenuOption) {
if (t.slider == null) return;
t.button.slider.value = t.button.slider.value > 0 ? 0 : 1;
}
function _song_mutevoices(t) {
vocals.volume = (voicesMuted = !voicesMuted) ? 0 : 1;
for (strumLine in strumLines.members) strumLine.updateVoicesVolume();
t.icon = voicesMuted ? 1 : 0;
function _song_instvolume(t) {
FlxG.sound.music.volume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_voicesvolume(t) {
vocals.volume = t.slider.value;
for (strumLine in strumLines.members) strumLine.vocals.volume = t.slider.value * strumLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _song_hitsoundvolume(t) {
hitsoundGlobalVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
function _playback_back(_) {
if (FlxG.sound.music.playing) return;
@@ -1860,6 +1909,7 @@ class Charter extends UIState {
__event.refreshEventIcons();
(__event.global ? rightEventsGroup : leftEventsGroup).add(__event);
undos.addToUndo(CEditEvent(__event, [], __event.events));
updateCameraChanges();
}
public function getBookmarkList():Array<ChartBookmark> {
@@ -2011,26 +2061,56 @@ class Charter extends UIState {
if (bookmarks.length > 0)
{
var bookmarkOptions:Array<UIContextMenuOption> = [];
var goToBookmark = TU.getRaw("charter.bookmarks.goTo");
for (b in bookmarks)
{
newChilds.push({
bookmarkOptions.push({
label: goToBookmark.format([b.name]),
onSelect: function(_) { Conductor.songPosition = Conductor.getTimeForStep(b.time); }
});
}
newChilds.push({
label: translate("bookmarks.bookmarkList"),
childs: bookmarkOptions
});
newChilds.push(null);
}
newChilds.push({
label: translate("song.muteInst"),
onSelect: _song_muteinst
label: translate("song.inst"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_instvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
});
newChilds.push({
label: translate("song.muteVoices"),
onSelect: _song_mutevoices
label: translate("song.voices"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_voicesvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
});
newChilds.push({
label: translate("song.hitsounds"),
slider: {
min: 0,
max: 1,
value: 1,
onChange: _song_hitsoundvolume
},
onIconClick: _slider_mutetoggle,
icon: 7
});
if (songTopButton != null) songTopButton.contextMenu = newChilds;
@@ -2055,6 +2135,10 @@ class Charter extends UIState {
function _view_showeventBeatSeparator(t) {
t.icon = (Options.charterShowBeats = !Options.charterShowBeats) ? 1 : 0;
}
function _view_showeventCameraHighlights(t) {
t.icon = (Options.charterShowCameraHighlights = !Options.charterShowCameraHighlights) ? 1 : 0;
updateCameraChanges();
}
function _view_switchWaveformRainbow(t) {
t.icon = (Options.charterRainbowWaveforms = !Options.charterRainbowWaveforms) ? 1 : 0;
@@ -2080,8 +2164,8 @@ class Charter extends UIState {
inline function _snap_decreasesnap(_) changequant(-1);
inline function _snap_resetsnap(_) setquant(16);
inline function changequant(change:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
inline function setquant(newQuant:Int) {FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND)); quant = newQuant; buildSnapsUI();}
inline function changequant(change:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = quants[FlxMath.wrap(quants.indexOf(quant) + change, 0, quants.length-1)]; buildSnapsUI();};
inline function setquant(newQuant:Int) {UIState.playEditorSound(Flags.DEFAULT_CHARTER_SNAPPINGCHANGE_SOUND); quant = newQuant; buildSnapsUI();}
function buildSnapsUI():Array<UIContextMenuOption> {
var snapsTopButton:UITopMenuButton = topMenuSpr == null ? null : cast topMenuSpr.members[snapIndex];
@@ -2117,12 +2201,12 @@ class Charter extends UIState {
}
inline function _note_addsustain(t) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINADD_SOUND);
changeNoteSustain(1);
}
inline function _note_subtractsustain(t) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_CHARTER_SUSTAINDELETE_SOUND);
changeNoteSustain(-1);
}
@@ -2199,16 +2283,18 @@ class Charter extends UIState {
keybind: [CONTROL, SHIFT, A],
onSelect: _note_selectmeasure
},
null,
{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}
null
];
var noteKeys:Array<FlxKey> = [ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE];
var noteTypeOptions:Array<UIContextMenuOption> = [{
label: "(0) " + translate("noteTypes.default"),
keybind: [ZERO],
onSelect: (_) -> {changeNoteType(0);},
icon: this.noteType == 0 ? 1 : 0
}];
final noteKeys:Array<Array<Array<FlxKey>>> = [[[ZERO], [NUMPADZERO]], [[ONE], [NUMPADONE]], [[TWO], [NUMPADTWO]], [[THREE], [NUMPADTHREE]], [[FOUR], [NUMPADFOUR]], [[FIVE], [NUMPADFIVE]],
[[SIX], [NUMPADSIX]], [[SEVEN], [NUMPADSEVEN]], [[EIGHT], [NUMPADEIGHT]], [[NINE], [NUMPADNINE]]];
for (i=>type in noteTypes) {
var realNoteID:Int = i+1; // Default Note not stored
var newChild:UIContextMenuOption = {
@@ -2217,9 +2303,13 @@ class Charter extends UIState {
onSelect: (_) -> {changeNoteType(realNoteID);},
icon: this.noteType == realNoteID ? 1 : 0
};
if (realNoteID <= 9) newChild.keybind = [noteKeys[realNoteID]];
newChilds.push(newChild);
if (realNoteID <= 9) newChild.keybinds = noteKeys[realNoteID];
noteTypeOptions.push(newChild);
}
newChilds.push({
label: translate("note.noteTypesList"),
childs: noteTypeOptions
});
newChilds.push({
label: translate("note.editNoteTypesList"),
color: 0xFF959829, icon: 4,
@@ -2227,6 +2317,7 @@ class Charter extends UIState {
onSelect: editNoteTypesList
});
if (noteTopButton != null) noteTopButton.contextMenu = newChilds;
if (topMenu != null && topMenu[noteIndex] != null) topMenu[noteIndex].childs = newChilds;
return newChilds;
}
@@ -2314,8 +2405,12 @@ class Charter extends UIState {
}
}
public inline function hitsoundsEnabled(id:Int)
return strumLines.members[id] != null && strumLines.members[id].hitsounds;
public inline function playHitsound(id:Int) {
if (strumLines.members[id] != null && strumLines.members[id].hitsoundVolume > 0 && hitsoundGlobalVolume > 0) {
hitsound.volume = hitsoundGlobalVolume * strumLines.members[id].hitsoundVolume;
hitsound.replay();
}
}
public inline function __fixSelection(selection:Selection):Selection {
var newSelection:Selection = new Selection();
@@ -2406,7 +2501,7 @@ class Charter extends UIState {
quantSelected: quant,
noteTypeSelected: noteType,
strumlinesDraggable: strumLines.draggable,
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsounds],
hitSounds: [for (strumLine in strumLines.members) strumLine.hitsoundVolume > 0],
mutedVocals: [for (strumLine in strumLines.members) !(strumLine.vocals.volume > 0)],
waveforms: [for (strumLine in strumLines.members) strumLine.selectedWaveform]
}
@@ -2422,7 +2517,7 @@ class Charter extends UIState {
strumLines.draggable = playtestInfo.strumlinesDraggable;
for (i => strumLine in strumLines.members)
strumLine.hitsounds = playtestInfo.hitSounds[i];
strumLine.hitsoundVolume = playtestInfo.hitSounds[i] ? 1 : 0;
for (i => strumLine in strumLines.members)
strumLine.vocals.volume = playtestInfo.mutedVocals[i] ? 0 : 1;
for (i => strumLine in strumLines.members)
@@ -2514,3 +2609,9 @@ typedef PlaytestInfo = {
var mutedVocals:Array<Bool>;
var waveforms:Array<Int>;
}
typedef CameraChange = {
var strumLineID:Int;
var step:Float;
var endStep:Float;
}
@@ -82,7 +82,7 @@ class CharterAutoSaveUI extends UISliceSprite {
icon.animation.curAnim.curFrame = 1;
for (member in [this, progressBar, progressBarBack, autosavingText]) member.color = 0xFFA3EC95;
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_AUTOSAVE_SOUND);
(new FlxTimer()).start(1, (_) -> {disappearAnimation();});
});
@@ -178,6 +178,8 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
public var gridShader:CustomShader = new CustomShader("engine/charterGrid");
var __lastKeyCount:Int = 4;
public var cameraHighlight:CameraHighlight;
public function new() {
super();
@@ -187,6 +189,10 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
add(gridBackDrop);
gridShader.hset("segments", 4);
cameraHighlight = new CameraHighlight(this);
cameraHighlight.makeSolid(1, 1, 0xFFFFFFFF);
add(cameraHighlight);
waveformSprite = new FlxSprite().makeSolid(1, 1, 0xFF000000);
waveformSprite.scale.set(160, 1);
waveformSprite.updateHitbox();
@@ -247,10 +253,11 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
x = strumLine.x;
alpha = strumLine.strumLine.visible ? 0.9 : 0.4;
keyCount = strumLine.keyCount;
cameraHighlight.color = strumLine.highlightColor;
} else alpha = 0.9;
for (spr in [gridBackDrop, beatSeparator, topLimit, bottomLimit,
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite]) {
topSeparator, bottomSeparator, conductorFollowerSpr, waveformSprite, cameraHighlight]) {
spr.x = x; if (spr != waveformSprite) spr.alpha = alpha;
spr.cameras = this.cameras;
}
@@ -272,6 +279,9 @@ class CharterBackdrop extends FlxTypedGroup<FlxBasic> {
spr.updateHitbox();
}
cameraHighlight.scale.x = (keyCount * 40)-2;
cameraHighlight.x += 1;
waveformSprite.visible = waveformSprite.shader != null;
if (waveformSprite.shader == null) return;
@@ -311,16 +321,19 @@ class CharterGridSeperatorBase extends FlxSprite {
private static var lastMaxMeasure:Float = -1;
public static var lastConductorSprY:Float = Math.NEGATIVE_INFINITY;
public static var lastCameraZoom:Float = -1;
private static var beatStepTimes:Array<Float> = [];
private static var measureStepTimes:Array<Float> = [];
private static var timeSignatureChangeGaps:Array<Float> = [];
private function recalculateBeats() {
@:privateAccess
var currentZoom = Charter.instance.__camZoom;
var conductorSprY = Charter.instance.gridBackdrops.conductorSprY;
if (conductorSprY == lastConductorSprY) return;
if (conductorSprY == lastConductorSprY && currentZoom == lastCameraZoom) return; //only update if song pos or camera zoom has changed
var zoomOffset = ((FlxG.height * (1/cameras[0].zoom)) * 0.5);
var zoomOffset = ((FlxG.height * (1/currentZoom)) * 0.5);
minStep = (conductorSprY - zoomOffset)/40;
maxStep = (conductorSprY + zoomOffset)/40;
@@ -359,6 +372,7 @@ class CharterGridSeperatorBase extends FlxSprite {
}
lastConductorSprY = conductorSprY;
lastCameraZoom = currentZoom;
}
private inline function calculateTimeSignatureGaps() {
@@ -446,6 +460,77 @@ class CharterGridSeperator extends CharterGridSeperatorBase {
}
}
class CameraHighlight extends FlxSprite {
private var grid:CharterBackdrop;
private var _currentCameraMovementIndex:Int = 0;
public function new(grid:CharterBackdrop) {
this.grid = grid;
super();
}
override public function draw() {
if (!Options.charterShowCameraHighlights || grid.strumLine == null) return;
@:privateAccess
var minStep = CharterGridSeperatorBase.minStep;
@:privateAccess
var maxStep = CharterGridSeperatorBase.maxStep;
@:privateAccess
var strumLineID = Charter.instance.strumLines.isDragging ? Charter.instance.strumLines.__pastStrumlines.indexOf(grid.strumLine) : Charter.instance.gridBackdrops.members.indexOf(grid);
//first default camera change
if ((Charter.instance.cameraMovementChanges[0] == null || Charter.instance.cameraMovementChanges[0].step != 0) && strumLineID == 0) {
var endStep = Charter.instance.cameraMovementChanges[0] != null ? Charter.instance.cameraMovementChanges[0].step : Charter.instance.__endStep;
if (endStep >= minStep) {
setupHighlight(0, endStep); super.draw();
setupLine(endStep); super.draw();
}
}
//update index if gone backwards
while(_currentCameraMovementIndex > 0) {
if (Charter.instance.cameraMovementChanges[_currentCameraMovementIndex] == null || Charter.instance.cameraMovementChanges[_currentCameraMovementIndex].endStep >= minStep) {
_currentCameraMovementIndex--;
} else {
break;
}
}
var seenFirstVisible = false;
for (i in _currentCameraMovementIndex...Charter.instance.cameraMovementChanges.length) {
var change = Charter.instance.cameraMovementChanges[i];
if (change.endStep >= minStep) {
if (!seenFirstVisible) {
_currentCameraMovementIndex = i; //remember the index for the next frame, so we dont need to loop through everything every time
seenFirstVisible = true;
}
if (change.strumLineID == strumLineID) {
setupHighlight(change.step, change.endStep); super.draw();
setupLine(change.step); super.draw();
setupLine(change.endStep); super.draw();
}
}
if (change.endStep > maxStep) break;
}
}
private inline function setupHighlight(step:Float, endStep:Float) {
y = step * 40;
alpha = 0.15;
scale.y = (endStep -step) * 40;
updateHitbox();
}
private inline function setupLine(step:Float) {
y = (step * 40)-1;
alpha = 0.8;
scale.y = 2;
updateHitbox();
}
}
class EventBackdrop extends FlxBackdrop {
public var eventBeatSeparator:CharterEventGridSeperator;
+3 -2
View File
@@ -161,8 +161,9 @@ class CharterNote extends UISprite implements ICharterSelectable {
}
if (__passed != (__passed = step < Conductor.curStepFloat + (Options.songOffsetAffectEditors ? (Conductor.songOffset / Conductor.stepCrochet) : 0))) {
if (__passed && FlxG.sound.music.playing && Charter.instance.hitsoundsEnabled(strumLineID))
Charter.instance.hitsound.replay();
if (__passed && FlxG.sound.music.playing) {
Charter.instance.playHitsound(strumLineID);
}
}
if (strumLine != null) {
@@ -21,7 +21,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
for (i in 0...keyCount){
var strum = new FlxSprite();
strum.frames = Paths.getFrames("game/notes/default");
strum.setGraphicSize(Std.int((strum.width * 0.7) * scale));
strum.setGraphicSize(Std.int((strum.width * Flags.DEFAULT_NOTE_SCALE) * scale));
strum.updateHitbox();
var animPrefix = strumAnimPrefix[i % 4];
@@ -33,7 +33,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
note = new FlxSprite();
note.frames = Paths.getFrames("game/notes/default");
note.setGraphicSize(Std.int((note.width * 0.7) * scale));
note.setGraphicSize(Std.int((note.width * Flags.DEFAULT_NOTE_SCALE) * scale));
note.updateHitbox();
note.animation.addByPrefix('purple', 'purple0');
note.animation.play('purple');
@@ -41,7 +41,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
add(note);
}
var noteTime:Float = FlxG.height;
var noteTime:Float = FlxG.initialHeight;
var scroll:Float = 1.0;
public function updatePos(x:Float, y:Float, scale:Float, spacing:Float, keyCount:Int, scrollSpeed:Float){
@@ -53,7 +53,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
strum.x = CoolUtil.fpsLerp(strum.x, x + (Note.swagWidth * scale * spacing * i), 0.2);
strum.y = CoolUtil.fpsLerp(strum.y, y + (Note.swagWidth*0.5) - (Note.swagWidth * scale * 0.5), 0.2);
strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, 0.7 * scale, 0.2);
strum.scale.x = strum.scale.y = CoolUtil.fpsLerp(strum.scale.x, Flags.DEFAULT_NOTE_SCALE * scale, 0.2);
strum.updateHitbox();
}
@@ -61,7 +61,7 @@ class CharterPreviewStrumLine extends FlxTypedGroup<FlxSprite>
scroll = CoolUtil.fpsLerp(scroll, scrollSpeed, 0.2);
noteTime -= FlxG.elapsed * scroll * 1000 * 0.45;
if (noteTime <= 0.0)
noteTime = FlxG.height;
noteTime = FlxG.initialHeight;
note.x = members[0].x;
note.y = members[0].y + noteTime;
@@ -105,6 +105,7 @@ class CharterStrumLineGroup extends FlxTypedGroup<CharterStrumline> {
draggingObj = null;
fixEvents();
refreshStrumlineIDs();
Charter.instance.updateCameraChanges();
}
public inline function fixEvents() {
@@ -1,5 +1,7 @@
package funkin.editors.charter;
import funkin.editors.ui.UIContextMenu.UIContextMenuOption;
import flixel.util.FlxColor;
import flixel.group.FlxSpriteGroup;
import flixel.sound.FlxSound;
import funkin.backend.chart.ChartData.ChartStrumLine;
@@ -10,11 +12,11 @@ import funkin.game.HealthIcon;
class CharterStrumline extends UISprite {
public var strumLine:ChartStrumLine;
public var hitsounds:Bool = true;
public var draggingSprite:UISprite;
public var healthIcons:FlxSpriteGroup;
public var button:CharterStrumlineOptions;
public var highlightColor:FlxColor;
public var draggable:Bool = false;
public var dragging:Bool = false;
@@ -22,7 +24,9 @@ class CharterStrumline extends UISprite {
public var curMenu:UIContextMenu = null;
public var vocals:FlxSound;
public var voicesMuted:Bool = false;
public var hasVocals:Bool = false;
public var vocalsVolume:Float = 1;
public var hitsoundVolume:Float = 1;
public var keyCount:Int = 4;
public var startingID(get, null):Int;
@@ -142,15 +146,22 @@ class CharterStrumline extends UISprite {
if (asset != null) {
vocals.reset();
vocals.loadEmbedded(asset);
hasVocals = true;
}
else {
vocals.destroy();
hasVocals = false;
}
vocals.group = FlxG.sound.defaultMusicGroup;
}
public function updateVoicesVolume() {
vocals.volume = (voicesMuted || (Charter.instance?.voicesMuted ?? false)) ? 0 : 1;
highlightColor = 0xFFFFFFFF;
if (icons[0] != null) {
var characterXML = Character.getXMLFromCharName(icons[0]);
if (characterXML != null && characterXML.x.exists("color")) highlightColor = FlxColor.fromString(characterXML.x.get("color"));
//make darker colors more visible for the highlight
highlightColor.brightness = Math.max(highlightColor.brightness, 0.65);
}
}
}
@@ -173,18 +184,17 @@ class CharterStrumlineOptions extends UITopMenuButton {
contextMenu = [
{
label: TU.translate("charter.strumLine.hitsounds"),
onSelect: function(_) {
strLine.hitsounds = !strLine.hitsounds;
slider: {
min: 0,
max: 1,
value: strLine.hitsoundVolume,
onChange: function(t) {
strLine.hitsoundVolume = t.slider.value;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
},
icon: strLine.hitsounds ? 1 : 0
},
{
label: TU.translate("charter.strumLine.muteVocals"),
onSelect: function(_) {
strLine.voicesMuted = !strLine.voicesMuted;
strLine.updateVoicesVolume();
},
icon: strLine.voicesMuted ? 1 : 0
onIconClick: Charter.instance._slider_mutetoggle,
icon: 7
},
null,
{
@@ -205,20 +215,44 @@ class CharterStrumlineOptions extends UITopMenuButton {
}
];
contextMenu.insert(0, {
label: TU.translate("charter.strumLine.noWaveform"),
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
});
if (strLine.hasVocals) {
contextMenu.insert(1, {
label: TU.translate("charter.strumLine.vocals"),
slider: {
min: 0,
max: 1,
value: strLine.vocalsVolume,
onChange: function(t) {
strLine.vocalsVolume = t.slider.value;
strLine.vocals.volume = Charter.instance.vocals.volume * strLine.vocalsVolume;
t.icon = t.slider.value > 0.5 ? 7 : (t.slider.value > 0 ? 8 : 9);
}
},
onIconClick: Charter.instance._slider_mutetoggle,
icon: 7
});
}
var waveformOptions:Array<UIContextMenuOption> = [
{
label: TU.translate("charter.strumLine.noWaveform"),
onSelect: function(_) {strLine.selectedWaveform = -1;},
icon: strLine.selectedWaveform == -1 ? 1 : 0
}
];
for (i => name in Charter.waveformHandler.waveformList)
contextMenu.insert(1+i, {
waveformOptions.push({
label: name,
onSelect: function(_) {strLine.selectedWaveform = i;},
icon: strLine.selectedWaveform == i ? 6 : 5
});
contextMenu.insert(1+Charter.waveformHandler.waveformList.length, null);
contextMenu.insert(0, {
label: TU.translate("charter.strumLine.waveforms"),
childs: waveformOptions
});
contextMenu.insert(1, null);
var cam = Charter.instance.charterCamera;
var point = CoolUtil.worldToScreenPosition(this, cam);
@@ -33,6 +33,8 @@ class CharterStrumlineScreen extends UISubstateWindow {
public var strumLineCam:HudCamera;
public var previewStrumLine:CharterPreviewStrumLine;
public var previewBorder:Array<FlxSprite> = [];
private final borderGap:Int = 5;
private var onSave:ChartStrumLine -> Void = null;
@@ -176,20 +178,30 @@ class CharterStrumlineScreen extends UISubstateWindow {
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
keyCountStepper = new UINumericStepper(stagePositionDropdown.x, vocalsSuffixDropDown.y, strumLine.keyCount != null ? strumLine.keyCount : 4, 1, 0, 1, 1000, 84);
// if (Flags.CHARTER_ADVANCED_SETTINGS) {
add(keyCountStepper);
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
// }
add(keyCountStepper);
addLabelOn(keyCountStepper, TU.translate("charterStrumLine.keyCount"));
strumLineCam = new HudCamera();
strumLineCam.downscroll = Options.downscroll;
strumLineCam.bgColor = 0;
strumLineCam.alpha = 0;
FlxG.cameras.add(strumLineCam, false);
updateStrumlineCam(FlxG.width, FlxG.height);
previewStrumLine = new CharterPreviewStrumLine(0, 0, 0, 1, 4, 0);
previewStrumLine.camera = strumLineCam;
add(previewStrumLine);
FlxTween.tween(strumLineCam, {alpha: 1}, 0.25, {ease: FlxEase.cubeOut});
//preview border
previewBorder.push(new FlxSprite(-borderGap, -borderGap).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(FlxG.initialWidth, 0).makeSolid(borderGap, FlxG.initialHeight, 0x88FFFFFF));
previewBorder.push(new FlxSprite(-borderGap, FlxG.initialHeight).makeSolid(FlxG.initialWidth + (borderGap*2), borderGap, 0x88FFFFFF));
for (border in previewBorder) {
border.camera = strumLineCam;
add(border);
}
}
function saveStrumline() {
@@ -222,7 +234,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
previewStrumLine.visible = visibleCheckbox.checked;
var xOffset:Float = StrumLine.calculateStartingXPos(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value));
var xOffset:Float = StrumLine.calculateStartingXPosFromInitialWidth(hudXStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value));
previewStrumLine.updatePos(xOffset, hudYStepper.value, hudScaleStepper.value, hudSpacingStepper.value, Std.int(keyCountStepper.value), scrollSpeed);
super.update(elapsed);
@@ -233,6 +245,26 @@ class CharterStrumlineScreen extends UISubstateWindow {
FlxTween.cancelTweensOf(strumLineCam);
FlxG.cameras.remove(strumLineCam);
}
public override function onResize(width:Int, height:Int) {
super.onResize(width, height);
if (!UIState.resolutionAware) return;
if ((width < FlxG.initialWidth || height < FlxG.initialHeight) && !Options.bypassEditorsResize) {
width = FlxG.initialWidth; height = FlxG.initialHeight;
}
updateStrumlineCam(width, height);
}
private function updateStrumlineCam(windowWidth:Int, windowHeight:Int) {
strumLineCam.width = FlxG.initialWidth + (borderGap*2);
strumLineCam.height = FlxG.initialHeight + (borderGap*2);
strumLineCam.x = (windowWidth/2) - (strumLineCam.width/2);
strumLineCam.y = (windowHeight/2) - (strumLineCam.height/2);
strumLineCam.scroll.x = -borderGap;
strumLineCam.scroll.y = -borderGap;
}
}
class CharacterButton extends UIButton {
+4 -4
View File
@@ -508,7 +508,7 @@ class StageEditor extends UIState {
function _file_save(_) {
#if sys
FlxG.sound.play(Paths.sound('editors/save'));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
CoolUtil.safeSaveFile(
'${Paths.getAssetsRoot()}/data/stages/${__stage}.xml',
buildStage()
@@ -520,7 +520,7 @@ class StageEditor extends UIState {
}
function _file_saveas(_) {
FlxG.sound.play(Paths.sound('editors/save'));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_SAVE_SOUND);
openSubState(new SaveSubstate(buildStage(), {
defaultSaveFile: '${__stage}.xml'
}));
@@ -730,7 +730,7 @@ class StageEditor extends UIState {
}
function _edit_undo(_) {
FlxG.sound.play(Flags.DEFAULT_EDITOR_UNDO_SOUND);
UIState.playEditorSound(Flags.DEFAULT_EDITOR_UNDO_SOUND);
var undo = undos.undo();
switch(undo) {
case null:
@@ -756,7 +756,7 @@ class StageEditor extends UIState {
}
function _edit_redo(_) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_REDO_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_REDO_SOUND);
var redo = undos.redo();
switch(redo) {
case null:
+1 -1
View File
@@ -30,7 +30,7 @@ class UIButton extends UISliceSprite {
super.onHovered();
if (FlxG.mouse.justPressed) {
hasBeenPressed = true;
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_BUTTONCLICK_SOUND);
}
if (FlxG.mouse.justReleased && callback != null && shouldPress && hasBeenPressed) {
callback();
+226 -32
View File
@@ -16,6 +16,12 @@ class UIContextMenu extends MusicBeatSubstate {
public var contextMenuOptions:Array<UIContextMenuOptionSpr> = [];
public var separators:Array<FlxSprite> = [];
public var childContextMenu:UIContextMenu = null;
public var parentContextMenu:UIContextMenu = null;
private var childContextMenuOptionIndex:Int = -1;
@:allow(funkin.editors.ui.UIContextMenu)
private var lastHoveredOptionIndex:Int = -1;
var scroll:Float = 0.0;
var flipped:Bool = false;
@@ -96,6 +102,10 @@ class UIContextMenu extends MusicBeatSubstate {
for(o in separators)
o.x -= bg.bWidth;
}
for(o in contextMenuOptions) {
o.postCreate();
}
}
public function select(option:UIContextMenuOption) {
@@ -105,12 +115,12 @@ class UIContextMenu extends MusicBeatSubstate {
if (callback != null)
callback(this, index, option);
if (option.closeOnSelect == null ? true : option.closeOnSelect)
close();
closeWithParents();
}
public override function update(elapsed:Float) {
if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild)
close();
if (__oobDeletion && FlxG.mouse.justPressed && !bg.hoveredByChild && !hoveringAnyChildren())
closeWithParents();
__oobDeletion = true;
@@ -121,6 +131,14 @@ class UIContextMenu extends MusicBeatSubstate {
contextCam.scroll.y = CoolUtil.fpsLerp(contextCam.scroll.y, scroll, 0.5);
contextCam.alpha = CoolUtil.fpsLerp(contextCam.alpha, 1, 0.25);
if (parentContextMenu != null) {
if (hoveringAnyParents() && parentContextMenu.lastHoveredOptionIndex != parentContextMenu.childContextMenuOptionIndex) {
closeWithChildren();
parentContextMenu.childContextMenuOptionIndex = -1;
}
}
}
public override function destroy() {
@@ -129,6 +147,56 @@ class UIContextMenu extends MusicBeatSubstate {
if (UIState.state.curContextMenu == this)
UIState.state.curContextMenu = null;
}
public function openChildContextMenu(optionSpr:UIContextMenuOptionSpr) {
var index = contextMenuOptions.indexOf(optionSpr);
if (index != childContextMenuOptionIndex) {
childContextMenuOptionIndex = index;
var child = new UIContextMenu(optionSpr.option.childs, null, optionSpr.x + optionSpr.bWidth + 4, optionSpr.y - 4);
persistentDraw = true;
persistentUpdate = true;
child.parentContextMenu = this;
childContextMenu = child;
openSubState(child);
}
}
public function closeWithParents() {
close();
if (parentContextMenu != null) {
parentContextMenu.closeWithParents();
}
}
public function closeWithChildren() {
if (childContextMenu != null) {
childContextMenu.closeWithChildren();
}
close();
}
public function hoveringAnyParents() {
if (parentContextMenu != null) {
return parentContextMenu.bg.hoveredByChild || parentContextMenu.hoveringAnyParents();
}
return false;
}
public function hoveringAnyChildren() {
if (childContextMenu != null) {
return childContextMenu.bg.hoveredByChild || childContextMenu.hoveringAnyChildren();
}
return false;
}
}
typedef UIContextMenuSliderOptionData = {
var min:Float;
var max:Float;
var value:Float;
var ?onChange:UIContextMenuOption->Void;
//default = 120, ignored if sameLine = false
var ?width:Float;
//disables stepper and text if false, default = false
var ?showValues:Bool;
//if true, the slider will be on the same line as the label text, otherwise it will be on the next line below the label
var ?sameLine:Bool;
}
typedef UIContextMenuCallback = UIContextMenu->Int->UIContextMenuOption->Void;
@@ -144,13 +212,24 @@ typedef UIContextMenuOption = {
var ?button:UIContextMenuOptionSpr;
var ?onCreate:UIContextMenuOptionSpr->Void;
var ?childs:Array<UIContextMenuOption>;
var ?slider:UIContextMenuSliderOptionData;
var ?onIconClick:UIContextMenuOption->Void;
}
enum abstract UIContextMenuOptionType(Int) from Int {
var DEFAULT = 0;
var SUBMENU = 1;
var SLIDER = 2;
}
class UIContextMenuOptionSpr extends UISliceSprite {
public var label:UIText;
public var labelKeybind:UIText;
public var icon:FlxSprite;
public var icon:UIContextMenuOptionIcon;
public var option:UIContextMenuOption;
public var optionType:UIContextMenuOptionType = DEFAULT;
public var slider:UISlider = null;
var parent:UIContextMenu;
@@ -160,40 +239,96 @@ class UIContextMenuOptionSpr extends UISliceSprite {
this.parent = parent;
this.color = option.color;
if (option.icon != null && option.icon > 0) {
icon = new FlxSprite(0, 0).loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
icon.animation.add('icon', [option.icon-1], 0, true);
icon.animation.play('icon');
}
var w:Int = label.frameWidth + 22;
var h:Int = label.frameHeight;
if (option.keybinds == null) {
if (option.keybind != null) {
option.keybinds = [option.keybind];
}
}
if (option.childs != null) optionType = SUBMENU;
if (option.slider != null) optionType = SLIDER;
if (option.keybinds != null || option.keybindText != null) {
var text = if(option.keybindText == null) {
var textKeys:Array<String> = [];
for (o in option.keybinds[0]) {
if (Std.int(o) > 0) {
textKeys.push(o.toUIString());
switch(optionType) {
case SUBMENU:
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, ">");
case SLIDER:
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, "");
//slider needs to be created after so that it can match the menu width (when not on the same line)
if (option.slider.sameLine != null && option.slider.sameLine) {
var sliderWidth = option.slider.width != null ? Std.int(option.slider.width) : 120;
w += 120 + slider.barWidth;
} else {
h *= 2;
}
default:
if (option.keybinds == null) {
if (option.keybind != null) {
option.keybinds = [option.keybind];
}
}
textKeys.join("+");
} else {
option.keybindText;
}
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text);
labelKeybind.alpha = 0.75;
if (option.keybinds != null || option.keybindText != null) {
var text = if(option.keybindText == null) {
var textKeys:Array<String> = [];
for (o in option.keybinds[0]) {
if (Std.int(o) > 0) {
textKeys.push(o.toUIString());
}
}
textKeys.join("+");
} else {
option.keybindText;
}
labelKeybind = new UIText(label.x + label.frameWidth + 10, 2, 0, text);
labelKeybind.alpha = 0.75;
w = Std.int(labelKeybind.x + labelKeybind.frameWidth + 10);
}
}
super(x, y, labelKeybind != null ? Std.int(labelKeybind.x + labelKeybind.frameWidth + 10) : (label.frameWidth + 22), label.frameHeight, 'editors/ui/menu-item');
super(x, y, w, h, 'editors/ui/menu-item');
members.push(label);
if (icon != null)
members.push(icon);
updateIcon();
if (labelKeybind != null)
members.push(labelKeybind);
members.push(labelKeybind);
}
//Called after all options are created and the context menu width/height is final
public function postCreate() {
switch(optionType) {
case SLIDER:
var sliderWidth = bWidth-50;
if (option.slider.sameLine != null && option.slider.sameLine) {
option.slider.width != null ? Std.int(option.slider.width) : 120;
}
slider = new UISlider(0, 0, sliderWidth, option.slider.value,
[{start: option.slider.min, end: option.slider.max, size: option.slider.max-option.slider.min}], false);
slider.onChange = function(v) {
option.slider.value = v;
if (option.slider.onChange != null) option.slider.onChange(option);
updateIcon(); //check if icon has changed
@:privateAccess
labelKeybind.text = '${CoolUtil.quantize(slider.__barProgress * 100, 1)}%';
};
slider.value = option.slider.value;
if (option.slider.showValues == null || !option.slider.showValues) {
slider.startText.visible = false;
slider.endText.visible = false;
slider.valueStepper.visible = false;
slider.valueStepper.selectable = false;
}
members.push(slider);
case SUBMENU:
default:
}
}
public override function draw() {
@@ -205,12 +340,71 @@ class UIContextMenuOptionSpr extends UISliceSprite {
icon.follow(this, 0, 0);
if (labelKeybind != null)
labelKeybind.follow(this, bWidth - 10 - labelKeybind.frameWidth, 2);
if (slider != null) {
if (option.slider.sameLine != null && option.slider.sameLine) {
slider.follow(this, bWidth - 18 - slider.barWidth - (slider.endText.visible ? slider.endText.width : 0), 5);
} else {
slider.follow(this, 20, 5 + label.frameHeight);
}
}
super.draw();
}
public override function onHovered() {
super.onHovered();
if (FlxG.mouse.justReleased)
parent.select(option);
parent.lastHoveredOptionIndex = parent.contextMenuOptions.indexOf(this);
switch(optionType) {
case SUBMENU:
parent.openChildContextMenu(this);
case SLIDER:
default:
if (FlxG.mouse.justReleased)
parent.select(option);
}
}
public function updateIcon() {
var currentIcon = option.icon != null ? option.icon : 0;
if (icon == null && currentIcon > 0) {
members.push(icon = new UIContextMenuOptionIcon(option));
}
if (icon != null) {
icon.updateIconState(currentIcon);
}
}
}
class UIContextMenuOptionIcon extends UISprite {
private var option:UIContextMenuOption;
private var _lastState:Int = 0;
override public function new(option:UIContextMenuOption) {
super();
this.option = option;
loadGraphic(Paths.image('editors/ui/context-icons'), true, 20, 20);
selectable = option.onIconClick != null;
cursor = option.onIconClick != null ? CLICK : ARROW;
}
public function updateIconState(state:Int) {
if (_lastState == state) return;
_lastState = state;
visible = state > 0;
if (state > 0) {
animation.add('icon', [state-1], 0, true);
animation.play('icon');
}
}
public override function onHovered() {
super.onHovered();
if (FlxG.mouse.justReleased && option.onIconClick != null) {
option.onIconClick(option);
}
}
}
+1 -1
View File
@@ -87,7 +87,7 @@ class UIDropDown extends UISliceSprite {
}
public function openContextMenu() {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_DROPDOWNAPPEAR_SOUND);
var screenPos = getScreenPosition(null, __lastDrawCameras[0] == null ? FlxG.camera : __lastDrawCameras[0]);
curMenu = UIState.state.openContextMenu([
for(k=>o in items) {
+8 -3
View File
@@ -106,7 +106,7 @@ class UIState extends MusicBeatState {
}
if (FlxG.mouse.justPressed) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CLICK_SOUND));
playEditorSound(Flags.DEFAULT_EDITOR_CLICK_SOUND);
}
if (FlxG.mouse.justReleased)
@@ -147,7 +147,7 @@ class UIState extends MusicBeatState {
}
public function closeCurrentContextMenu() {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
playEditorSound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND);
if(curContextMenu != null) {
curContextMenu.close();
curContextMenu = null;
@@ -155,7 +155,7 @@ class UIState extends MusicBeatState {
}
public function openContextMenu(options:Array<UIContextMenuOption>, ?callback:UIContextMenuCallback, ?x:Float, ?y:Float, ?w:Int) {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
playEditorSound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND);
var state = FlxG.state;
while(state.subState != null && !(state._requestSubStateReset && state._requestedSubState == null))
state = state.subState;
@@ -191,4 +191,9 @@ class UIState extends MusicBeatState {
resolutionAware = true;
FlxG.scaleMode = uiScaleMode;
}
public static function playEditorSound(path:String) {
if (!Options.editorSFX) return;
FlxG.sound.play(Paths.sound(path));
}
}
+1 -1
View File
@@ -86,7 +86,7 @@ class UISubstateWindow extends MusicBeatSubstate {
}
public override function destroy() {
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_WINDOWCLOSE_SOUND);
super.destroy();
for(e in camShaders)
e.removeShader(blurShader);
+6 -6
View File
@@ -166,7 +166,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
changeSelection(1);
case BACKSPACE:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
if (modifier.ctrlKey) {
var wordBounds = findWholeWord(label.text, position);
@@ -182,7 +182,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
changeSelection(-1);
}
case DELETE:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTREMOVE_SOUND);
if (modifier.ctrlKey) {
var wordBounds = findWholeWord(label.text, position, true);
@@ -201,7 +201,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
case END:
position = label.text.length;
case V:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_PASTE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_PASTE_SOUND);
// Hey lj here, fixed copying because before we checked if the modifier was left or right ctrl
// but somehow it gave a int outside of the KeyModifier's range :sob:
// apparently there is a boolean that just checks for you. yw :D
@@ -214,7 +214,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
if (data != null)
onTextInput(data);
case C:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_COPY_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_COPY_SOUND);
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey)
return;
@@ -222,7 +222,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
// copying
Clipboard.generalClipboard.setData(TEXT_FORMAT, label.text);
case X:
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_CUT_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_CUT_SOUND);
// if we are not holding ctrl, ignore
if (!modifier.ctrlKey)
@@ -236,7 +236,7 @@ class UITextBox extends UISliceSprite implements IUIFocusable {
if (modifier.ctrlKey || modifier.altKey || modifier.shiftKey)
return;
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_TEXTTYPE_SOUND);
}
}
+1 -1
View File
@@ -15,7 +15,7 @@ class UIWindow extends UISliceSprite {
content = new FlxTypedGroup<FlxBasic>();
members.push(content);
FlxG.sound.play(Paths.sound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND));
UIState.playEditorSound(Flags.DEFAULT_EDITOR_WINDOWAPPEAR_SOUND);
}
public override function update(elapsed:Float) {
+3
View File
@@ -447,6 +447,9 @@ class StrumLine extends FlxTypedGroup<Strum> {
public static inline function calculateStartingXPos(hudXRatio:Float, scale:Float, spacing:Float, keyCount:Int) {
return (FlxG.width * hudXRatio) - ((Note.swagWidth * scale * ((keyCount/2)-0.5) * spacing) + Note.swagWidth * 0.5 * scale);
}
public static inline function calculateStartingXPosFromInitialWidth(hudXRatio:Float, scale:Float, spacing:Float, keyCount:Int) {
return (FlxG.initialWidth * hudXRatio) - ((Note.swagWidth * scale * ((keyCount/2)-0.5) * spacing) + Note.swagWidth * 0.5 * scale);
}
/**
* SETTERS & GETTERS
+1
View File
@@ -84,6 +84,7 @@ class Options
public static var charterMetronomeEnabled:Bool = false;
public static var charterShowSections:Bool = true;
public static var charterShowBeats:Bool = true;
public static var charterShowCameraHighlights:Bool = true;
public static var charterEnablePlaytestScripts:Bool = true;
public static var charterRainbowWaveforms:Bool = false;
public static var charterLowDetailWaveforms:Bool = false;
@@ -4,7 +4,7 @@ import flixel.util.FlxTimer;
import funkin.backend.system.Conductor;
class GameplayOptions extends TreeMenuScreen {
var __metronome = FlxG.sound.load(Paths.sound('editors/charter/metronome'));
var __metronome = FlxG.sound.load(Paths.sound(Flags.DEFAULT_CHARTER_METRONOME_SOUND));
var offsetSetting:NumOption;
public function new() {