mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-23 08:07:54 -05:00
Merge branch 'rewrite/master' into bugfix/chart-editor-upload-vocals-rewrite
This commit is contained in:
commit
f13772da67
10 changed files with 538 additions and 236 deletions
|
@ -15,7 +15,7 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
}
|
}
|
||||||
|
|
||||||
@:arrayAccess
|
@:arrayAccess
|
||||||
public inline function getByName(name:String):SongEventSchemaField
|
public function getByName(name:String):SongEventSchemaField
|
||||||
{
|
{
|
||||||
for (field in this)
|
for (field in this)
|
||||||
{
|
{
|
||||||
|
@ -41,6 +41,32 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
{
|
{
|
||||||
return this[k] = v;
|
return this[k] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function stringifyFieldValue(name:String, value:Dynamic):String
|
||||||
|
{
|
||||||
|
var field:SongEventSchemaField = getByName(name);
|
||||||
|
if (field == null) return 'Unknown';
|
||||||
|
|
||||||
|
switch (field.type)
|
||||||
|
{
|
||||||
|
case SongEventFieldType.STRING:
|
||||||
|
return Std.string(value);
|
||||||
|
case SongEventFieldType.INTEGER:
|
||||||
|
return Std.string(value);
|
||||||
|
case SongEventFieldType.FLOAT:
|
||||||
|
return Std.string(value);
|
||||||
|
case SongEventFieldType.BOOL:
|
||||||
|
return Std.string(value);
|
||||||
|
case SongEventFieldType.ENUM:
|
||||||
|
for (key in field.keys.keys())
|
||||||
|
{
|
||||||
|
if (field.keys.get(key) == value) return key;
|
||||||
|
}
|
||||||
|
return Std.string(value);
|
||||||
|
default:
|
||||||
|
return 'Unknown';
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package funkin.data.song;
|
package funkin.data.song;
|
||||||
|
|
||||||
import funkin.data.event.SongEventRegistry;
|
import funkin.data.event.SongEventRegistry;
|
||||||
|
import funkin.play.event.SongEvent;
|
||||||
import funkin.data.event.SongEventSchema;
|
import funkin.data.event.SongEventSchema;
|
||||||
import funkin.data.song.SongRegistry;
|
import funkin.data.song.SongRegistry;
|
||||||
import thx.semver.Version;
|
import thx.semver.Version;
|
||||||
|
@ -702,6 +703,11 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public inline function getHandler():Null<SongEvent>
|
||||||
|
{
|
||||||
|
return SongEventRegistry.getEvent(this.event);
|
||||||
|
}
|
||||||
|
|
||||||
public inline function getSchema():Null<SongEventSchema>
|
public inline function getSchema():Null<SongEventSchema>
|
||||||
{
|
{
|
||||||
return SongEventRegistry.getEventSchema(this.event);
|
return SongEventRegistry.getEventSchema(this.event);
|
||||||
|
@ -752,6 +758,39 @@ abstract SongEventData(SongEventDataRaw) from SongEventDataRaw to SongEventDataR
|
||||||
return this.value == null ? null : cast Reflect.field(this.value, key);
|
return this.value == null ? null : cast Reflect.field(this.value, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function buildTooltip():String
|
||||||
|
{
|
||||||
|
var eventHandler = getHandler();
|
||||||
|
var eventSchema = getSchema();
|
||||||
|
|
||||||
|
if (eventSchema == null) return 'Unknown Event: ${this.event}';
|
||||||
|
|
||||||
|
var result = '${eventHandler.getTitle()}';
|
||||||
|
|
||||||
|
var defaultKey = eventSchema.getFirstField()?.name;
|
||||||
|
var valueStruct:haxe.DynamicAccess<Dynamic> = valueAsStruct(defaultKey);
|
||||||
|
|
||||||
|
for (pair in valueStruct.keyValueIterator())
|
||||||
|
{
|
||||||
|
var key = pair.key;
|
||||||
|
var value = pair.value;
|
||||||
|
|
||||||
|
var title = eventSchema.getByName(key)?.title ?? 'UnknownField';
|
||||||
|
|
||||||
|
if (eventSchema.stringifyFieldValue(key, value) != null) trace(eventSchema.stringifyFieldValue(key, value));
|
||||||
|
var valueStr = eventSchema.stringifyFieldValue(key, value) ?? 'UnknownValue';
|
||||||
|
|
||||||
|
result += '\n- ${title}: ${valueStr}';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function clone():SongEventData
|
||||||
|
{
|
||||||
|
return new SongEventData(this.time, this.event, this.value);
|
||||||
|
}
|
||||||
|
|
||||||
@:op(A == B)
|
@:op(A == B)
|
||||||
public function op_equals(other:SongEventData):Bool
|
public function op_equals(other:SongEventData):Bool
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,12 @@ import funkin.play.character.BaseCharacter;
|
||||||
*/
|
*/
|
||||||
class GameOverSubState extends MusicBeatSubState
|
class GameOverSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* The currently active GameOverSubState.
|
||||||
|
* There should be only one GameOverSubState in existance at a time, we can use a singleton.
|
||||||
|
*/
|
||||||
|
public static var instance:GameOverSubState = null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Which alternate animation on the character to use.
|
* Which alternate animation on the character to use.
|
||||||
* You can set this via script.
|
* You can set this via script.
|
||||||
|
@ -88,6 +94,13 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
|
|
||||||
override public function create()
|
override public function create()
|
||||||
{
|
{
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
// TODO: Do something in this case? IDK.
|
||||||
|
trace('WARNING: GameOverSubState instance already exists. This should not happen.');
|
||||||
|
}
|
||||||
|
instance = this;
|
||||||
|
|
||||||
super.create();
|
super.create();
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -283,10 +296,10 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void
|
function startDeathMusic(?startingVolume:Float = 1, force:Bool = false):Void
|
||||||
{
|
{
|
||||||
var musicPath = Paths.music('gameOver' + musicSuffix);
|
var musicPath = Paths.music('gameplay/gameover/gameOver' + musicSuffix);
|
||||||
if (isEnding)
|
if (isEnding)
|
||||||
{
|
{
|
||||||
musicPath = Paths.music('gameOverEnd' + musicSuffix);
|
musicPath = Paths.music('gameplay/gameover/gameOverEnd' + musicSuffix);
|
||||||
}
|
}
|
||||||
if (!gameOverMusic.playing || force)
|
if (!gameOverMusic.playing || force)
|
||||||
{
|
{
|
||||||
|
@ -306,7 +319,7 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
public static function playBlueBalledSFX()
|
public static function playBlueBalledSFX()
|
||||||
{
|
{
|
||||||
blueballed = true;
|
blueballed = true;
|
||||||
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix));
|
FlxG.sound.play(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix));
|
||||||
}
|
}
|
||||||
|
|
||||||
var playingJeffQuote:Bool = false;
|
var playingJeffQuote:Bool = false;
|
||||||
|
@ -329,6 +342,11 @@ class GameOverSubState extends MusicBeatSubState
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override function toString():String
|
||||||
|
{
|
||||||
|
return "GameOverSubState";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef GameOverParams =
|
typedef GameOverParams =
|
||||||
|
|
|
@ -1354,6 +1354,7 @@ class PlayState extends MusicBeatSubState
|
||||||
function loadStage(id:String):Void
|
function loadStage(id:String):Void
|
||||||
{
|
{
|
||||||
currentStage = StageRegistry.instance.fetchEntry(id);
|
currentStage = StageRegistry.instance.fetchEntry(id);
|
||||||
|
currentStage.revive(); // Stages are killed and props destroyed when the PlayState is destroyed to save memory.
|
||||||
|
|
||||||
if (currentStage != null)
|
if (currentStage != null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -35,6 +35,7 @@ import funkin.data.song.SongData.SongCharacterData;
|
||||||
import funkin.data.song.SongData.SongChartData;
|
import funkin.data.song.SongData.SongChartData;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
import funkin.data.song.SongData.SongMetadata;
|
import funkin.data.song.SongData.SongMetadata;
|
||||||
|
import funkin.ui.debug.charting.toolboxes.ChartEditorDifficultyToolbox;
|
||||||
import funkin.data.song.SongData.SongNoteData;
|
import funkin.data.song.SongData.SongNoteData;
|
||||||
import funkin.data.song.SongData.SongOffsets;
|
import funkin.data.song.SongData.SongOffsets;
|
||||||
import funkin.data.song.SongDataUtils;
|
import funkin.data.song.SongDataUtils;
|
||||||
|
@ -105,6 +106,7 @@ import haxe.ui.components.Label;
|
||||||
import haxe.ui.components.Button;
|
import haxe.ui.components.Button;
|
||||||
import haxe.ui.components.NumberStepper;
|
import haxe.ui.components.NumberStepper;
|
||||||
import haxe.ui.components.Slider;
|
import haxe.ui.components.Slider;
|
||||||
|
import haxe.ui.components.VerticalSlider;
|
||||||
import haxe.ui.components.TextField;
|
import haxe.ui.components.TextField;
|
||||||
import haxe.ui.containers.dialogs.CollapsibleDialog;
|
import haxe.ui.containers.dialogs.CollapsibleDialog;
|
||||||
import haxe.ui.containers.Frame;
|
import haxe.ui.containers.Frame;
|
||||||
|
@ -721,6 +723,34 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
|
return hitsoundsEnabledPlayer || hitsoundsEnabledOpponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sound multiplier for vocals and hitsounds on the player's side.
|
||||||
|
*/
|
||||||
|
var soundMultiplierPlayer(default, set):Float = 1.0;
|
||||||
|
|
||||||
|
function set_soundMultiplierPlayer(value:Float):Float
|
||||||
|
{
|
||||||
|
soundMultiplierPlayer = value;
|
||||||
|
var vocalTargetVolume:Float = (menubarItemVolumeVocals.value ?? 100.0) / 100.0;
|
||||||
|
if (audioVocalTrackGroup != null) audioVocalTrackGroup.playerVolume = vocalTargetVolume * soundMultiplierPlayer;
|
||||||
|
|
||||||
|
return soundMultiplierPlayer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sound multiplier for vocals and hitsounds on the opponent's side.
|
||||||
|
*/
|
||||||
|
var soundMultiplierOpponent(default, set):Float = 1.0;
|
||||||
|
|
||||||
|
function set_soundMultiplierOpponent(value:Float):Float
|
||||||
|
{
|
||||||
|
soundMultiplierOpponent = value;
|
||||||
|
var vocalTargetVolume:Float = (menubarItemVolumeVocals.value ?? 100.0) / 100.0;
|
||||||
|
if (audioVocalTrackGroup != null) audioVocalTrackGroup.opponentVolume = vocalTargetVolume * soundMultiplierOpponent;
|
||||||
|
|
||||||
|
return soundMultiplierOpponent;
|
||||||
|
}
|
||||||
|
|
||||||
// Auto-save
|
// Auto-save
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1750,6 +1780,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
var buttonSelectEvent:Button;
|
var buttonSelectEvent:Button;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The slider above the grid that sets the volume of the player's sounds.
|
||||||
|
* Constructed manually and added to the layout so we can control its position.
|
||||||
|
*/
|
||||||
|
var sliderVolumePlayer:Slider;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The slider above the grid that sets the volume of the opponent's sounds.
|
||||||
|
* Constructed manually and added to the layout so we can control its position.
|
||||||
|
*/
|
||||||
|
var sliderVolumeOpponent:Slider;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RENDER OBJECTS
|
* RENDER OBJECTS
|
||||||
*/
|
*/
|
||||||
|
@ -1959,7 +2001,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
buildGrid();
|
buildGrid();
|
||||||
buildMeasureTicks();
|
buildMeasureTicks();
|
||||||
buildNotePreview();
|
buildNotePreview();
|
||||||
buildSelectionBox();
|
|
||||||
|
|
||||||
buildAdditionalUI();
|
buildAdditionalUI();
|
||||||
populateOpenRecentMenu();
|
populateOpenRecentMenu();
|
||||||
|
@ -2215,7 +2256,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
add(gridGhostHoldNote);
|
add(gridGhostHoldNote);
|
||||||
gridGhostHoldNote.zIndex = 11;
|
gridGhostHoldNote.zIndex = 11;
|
||||||
|
|
||||||
gridGhostEvent = new ChartEditorEventSprite(this);
|
gridGhostEvent = new ChartEditorEventSprite(this, true);
|
||||||
gridGhostEvent.alpha = 0.6;
|
gridGhostEvent.alpha = 0.6;
|
||||||
gridGhostEvent.eventData = new SongEventData(-1, '', {});
|
gridGhostEvent.eventData = new SongEventData(-1, '', {});
|
||||||
gridGhostEvent.visible = false;
|
gridGhostEvent.visible = false;
|
||||||
|
@ -2288,17 +2329,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
setNotePreviewViewportBounds(calculateNotePreviewViewportBounds());
|
setNotePreviewViewportBounds(calculateNotePreviewViewportBounds());
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSelectionBox():Void
|
|
||||||
{
|
|
||||||
if (selectionBoxSprite == null) throw 'ERROR: Tried to build selection box, but selectionBoxSprite is null! Check ChartEditorThemeHandler.updateTheme().';
|
|
||||||
|
|
||||||
selectionBoxSprite.scrollFactor.set(0, 0);
|
|
||||||
add(selectionBoxSprite);
|
|
||||||
selectionBoxSprite.zIndex = 30;
|
|
||||||
|
|
||||||
setSelectionBoxBounds();
|
|
||||||
}
|
|
||||||
|
|
||||||
function setSelectionBoxBounds(bounds:FlxRect = null):Void
|
function setSelectionBoxBounds(bounds:FlxRect = null):Void
|
||||||
{
|
{
|
||||||
if (selectionBoxSprite == null)
|
if (selectionBoxSprite == null)
|
||||||
|
@ -2320,6 +2350,19 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Automatically goes through and calls render on everything you added.
|
||||||
|
*/
|
||||||
|
override public function draw():Void
|
||||||
|
{
|
||||||
|
if (selectionBoxStartPos != null)
|
||||||
|
{
|
||||||
|
trace('selectionBoxSprite: ${selectionBoxSprite.visible} ${selectionBoxSprite.exists} ${this.members.contains(selectionBoxSprite)}');
|
||||||
|
}
|
||||||
|
|
||||||
|
super.draw();
|
||||||
|
}
|
||||||
|
|
||||||
function calculateNotePreviewViewportBounds():FlxRect
|
function calculateNotePreviewViewportBounds():FlxRect
|
||||||
{
|
{
|
||||||
var bounds:FlxRect = new FlxRect();
|
var bounds:FlxRect = new FlxRect();
|
||||||
|
@ -2558,6 +2601,37 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
performCommand(new SetItemSelectionCommand([], currentSongChartEventData));
|
performCommand(new SetItemSelectionCommand([], currentSongChartEventData));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setupSideSlider(x, y):VerticalSlider
|
||||||
|
{
|
||||||
|
var slider = new VerticalSlider();
|
||||||
|
slider.allowFocus = false;
|
||||||
|
slider.x = x;
|
||||||
|
slider.y = y;
|
||||||
|
slider.width = NOTE_SELECT_BUTTON_HEIGHT;
|
||||||
|
slider.height = GRID_SIZE * 4;
|
||||||
|
slider.pos = slider.max;
|
||||||
|
slider.tooltip = "Slide to set the volume of sounds on this side.";
|
||||||
|
slider.zIndex = 110;
|
||||||
|
slider.styleNames = "sideSlider";
|
||||||
|
add(slider);
|
||||||
|
|
||||||
|
return slider;
|
||||||
|
}
|
||||||
|
|
||||||
|
var sliderY = GRID_INITIAL_Y_POS + 34;
|
||||||
|
sliderVolumeOpponent = setupSideSlider(GRID_X_POS - 64, sliderY);
|
||||||
|
sliderVolumePlayer = setupSideSlider(buttonSelectEvent.x + buttonSelectEvent.width, sliderY);
|
||||||
|
|
||||||
|
sliderVolumePlayer.onChange = event -> {
|
||||||
|
var volume:Float = event.value.toFloat() / 100.0;
|
||||||
|
soundMultiplierPlayer = volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
sliderVolumeOpponent.onChange = event -> {
|
||||||
|
var volume:Float = event.value.toFloat() / 100.0;
|
||||||
|
soundMultiplierOpponent = volume;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2798,7 +2872,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
menubarItemVolumeVocals.onChange = event -> {
|
menubarItemVolumeVocals.onChange = event -> {
|
||||||
var volume:Float = event.value.toFloat() / 100.0;
|
var volume:Float = event.value.toFloat() / 100.0;
|
||||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.volume = volume;
|
if (audioVocalTrackGroup != null)
|
||||||
|
{
|
||||||
|
audioVocalTrackGroup.playerVolume = volume * soundMultiplierPlayer;
|
||||||
|
audioVocalTrackGroup.opponentVolume = volume * soundMultiplierOpponent;
|
||||||
|
}
|
||||||
menubarLabelVolumeVocals.text = 'Voices - ${Std.int(event.value)}%';
|
menubarLabelVolumeVocals.text = 'Voices - ${Std.int(event.value)}%';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3367,6 +3445,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Setting event data resets position relative to the grid so we fix that.
|
// Setting event data resets position relative to the grid so we fix that.
|
||||||
eventSprite.x += renderedEvents.x;
|
eventSprite.x += renderedEvents.x;
|
||||||
eventSprite.y += renderedEvents.y;
|
eventSprite.y += renderedEvents.y;
|
||||||
|
eventSprite.updateTooltipPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add hold notes that have been made visible (but not their parents)
|
// Add hold notes that have been made visible (but not their parents)
|
||||||
|
@ -4654,48 +4733,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
{
|
{
|
||||||
difficultySelectDirty = false;
|
difficultySelectDirty = false;
|
||||||
|
|
||||||
// Manage the Select Difficulty tree view.
|
var difficultyToolbox:ChartEditorDifficultyToolbox = cast this.getToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
|
||||||
if (difficultyToolbox == null) return;
|
if (difficultyToolbox == null) return;
|
||||||
|
|
||||||
var treeView:Null<TreeView> = difficultyToolbox.findComponent('difficultyToolboxTree');
|
difficultyToolbox.updateTree();
|
||||||
if (treeView == null) return;
|
|
||||||
|
|
||||||
// Clear the tree view so we can rebuild it.
|
|
||||||
treeView.clearNodes();
|
|
||||||
|
|
||||||
// , icon: 'haxeui-core/styles/default/haxeui_tiny.png'
|
|
||||||
var treeSong:TreeViewNode = treeView.addNode({id: 'stv_song', text: 'S: $currentSongName'});
|
|
||||||
treeSong.expanded = true;
|
|
||||||
|
|
||||||
for (curVariation in availableVariations)
|
|
||||||
{
|
|
||||||
trace('DIFFICULTY TOOLBOX: Variation ${curVariation}');
|
|
||||||
var variationMetadata:Null<SongMetadata> = songMetadata.get(curVariation);
|
|
||||||
if (variationMetadata == null) continue;
|
|
||||||
|
|
||||||
var treeVariation:TreeViewNode = treeSong.addNode(
|
|
||||||
{
|
|
||||||
id: 'stv_variation_$curVariation',
|
|
||||||
text: 'V: ${curVariation.toTitleCase()}'
|
|
||||||
});
|
|
||||||
treeVariation.expanded = true;
|
|
||||||
|
|
||||||
var difficultyList:Array<String> = variationMetadata.playData.difficulties;
|
|
||||||
|
|
||||||
for (difficulty in difficultyList)
|
|
||||||
{
|
|
||||||
trace('DIFFICULTY TOOLBOX: Difficulty ${curVariation}_$difficulty');
|
|
||||||
var _treeDifficulty:TreeViewNode = treeVariation.addNode(
|
|
||||||
{
|
|
||||||
id: 'stv_difficulty_${curVariation}_$difficulty',
|
|
||||||
text: 'D: ${difficulty.toTitleCase()}'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
treeView.onChange = onChangeTreeDifficulty;
|
|
||||||
refreshDifficultyTreeSelection(treeView);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5440,7 +5481,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
return event != null && currentEventSelection.indexOf(event) != -1;
|
return event != null && currentEventSelection.indexOf(event) != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createDifficulty(variation:String, difficulty:String, scrollSpeed:Float = 1.0)
|
function createDifficulty(variation:String, difficulty:String, scrollSpeed:Float = 1.0):Void
|
||||||
{
|
{
|
||||||
var variationMetadata:Null<SongMetadata> = songMetadata.get(variation);
|
var variationMetadata:Null<SongMetadata> = songMetadata.get(variation);
|
||||||
if (variationMetadata == null) return;
|
if (variationMetadata == null) return;
|
||||||
|
@ -5462,6 +5503,42 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function removeDifficulty(variation:String, difficulty:String):Void
|
||||||
|
{
|
||||||
|
var variationMetadata:Null<SongMetadata> = songMetadata.get(variation);
|
||||||
|
if (variationMetadata == null) return;
|
||||||
|
|
||||||
|
variationMetadata.playData.difficulties.remove(difficulty);
|
||||||
|
|
||||||
|
var resultChartData = songChartData.get(variation);
|
||||||
|
if (resultChartData != null)
|
||||||
|
{
|
||||||
|
resultChartData.scrollSpeed.remove(difficulty);
|
||||||
|
resultChartData.notes.remove(difficulty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (songMetadata.size() > 1)
|
||||||
|
{
|
||||||
|
if (variationMetadata.playData.difficulties.length == 0)
|
||||||
|
{
|
||||||
|
songMetadata.remove(variation);
|
||||||
|
songChartData.remove(variation);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (variation == selectedVariation)
|
||||||
|
{
|
||||||
|
var firstVariation = songMetadata.keyValues()[0];
|
||||||
|
if (firstVariation != null) selectedVariation = firstVariation;
|
||||||
|
variationMetadata = songMetadata.get(selectedVariation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selectedDifficulty == difficulty
|
||||||
|
|| !variationMetadata.playData.difficulties.contains(selectedDifficulty)) selectedDifficulty = variationMetadata.playData.difficulties[0];
|
||||||
|
|
||||||
|
difficultySelectDirty = true; // Force the Difficulty toolbox to update.
|
||||||
|
}
|
||||||
|
|
||||||
function incrementDifficulty(change:Int):Void
|
function incrementDifficulty(change:Int):Void
|
||||||
{
|
{
|
||||||
var currentDifficultyIndex:Int = availableDifficulties.indexOf(selectedDifficulty);
|
var currentDifficultyIndex:Int = availableDifficulties.indexOf(selectedDifficulty);
|
||||||
|
@ -5510,8 +5587,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
Conductor.instance.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
Conductor.instance.mapTimeChanges(this.currentSongMetadata.timeChanges);
|
||||||
updateTimeSignature();
|
updateTimeSignature();
|
||||||
|
|
||||||
refreshDifficultyTreeSelection();
|
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5519,8 +5596,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1];
|
var prevDifficulty = availableDifficulties[currentDifficultyIndex - 1];
|
||||||
selectedDifficulty = prevDifficulty;
|
selectedDifficulty = prevDifficulty;
|
||||||
|
|
||||||
refreshDifficultyTreeSelection();
|
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -5538,8 +5615,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var nextDifficulty = availableDifficulties[0];
|
var nextDifficulty = availableDifficulties[0];
|
||||||
selectedDifficulty = nextDifficulty;
|
selectedDifficulty = nextDifficulty;
|
||||||
|
|
||||||
refreshDifficultyTreeSelection();
|
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -5547,7 +5624,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1];
|
var nextDifficulty = availableDifficulties[currentDifficultyIndex + 1];
|
||||||
selectedDifficulty = nextDifficulty;
|
selectedDifficulty = nextDifficulty;
|
||||||
|
|
||||||
refreshDifficultyTreeSelection();
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5663,7 +5740,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
audioInstTrack.volume = instTargetVolume;
|
audioInstTrack.volume = instTargetVolume;
|
||||||
audioInstTrack.onComplete = null;
|
audioInstTrack.onComplete = null;
|
||||||
}
|
}
|
||||||
if (audioVocalTrackGroup != null) audioVocalTrackGroup.volume = vocalTargetVolume;
|
if (audioVocalTrackGroup != null)
|
||||||
|
{
|
||||||
|
audioVocalTrackGroup.playerVolume = vocalTargetVolume * soundMultiplierPlayer;
|
||||||
|
audioVocalTrackGroup.opponentVolume = vocalTargetVolume * soundMultiplierOpponent;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateTimeSignature():Void
|
function updateTimeSignature():Void
|
||||||
|
@ -5679,92 +5760,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
// ==================
|
// ==================
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the currently selected item in the Difficulty tree view to the node representing the current difficulty.
|
|
||||||
* @param treeView The tree view to update. If `null`, the tree view will be found.
|
|
||||||
*/
|
|
||||||
function refreshDifficultyTreeSelection(?treeView:TreeView):Void
|
|
||||||
{
|
|
||||||
if (treeView == null)
|
|
||||||
{
|
|
||||||
// Manage the Select Difficulty tree view.
|
|
||||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
|
||||||
if (difficultyToolbox == null) return;
|
|
||||||
|
|
||||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
|
||||||
if (treeView == null) return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView);
|
|
||||||
if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the node representing the current difficulty in the Difficulty tree view.
|
|
||||||
* @param treeView The tree view to search. If `null`, the tree view will be found.
|
|
||||||
* @return The node representing the current difficulty, or `null` if not found.
|
|
||||||
*/
|
|
||||||
function getCurrentTreeDifficultyNode(?treeView:TreeView = null):Null<TreeViewNode>
|
|
||||||
{
|
|
||||||
if (treeView == null)
|
|
||||||
{
|
|
||||||
var difficultyToolbox:Null<CollapsibleDialog> = this.getToolbox_OLD(CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
|
||||||
if (difficultyToolbox == null) return null;
|
|
||||||
|
|
||||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
|
||||||
if (treeView == null) return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var result:TreeViewNode = treeView.findNodeByPath('stv_song/stv_variation_$selectedVariation/stv_difficulty_${selectedVariation}_$selectedDifficulty',
|
|
||||||
'id');
|
|
||||||
if (result == null) return null;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when selecting a tree element in the Difficulty toolbox.
|
|
||||||
* @param event The click event.
|
|
||||||
*/
|
|
||||||
function onChangeTreeDifficulty(event:UIEvent):Void
|
|
||||||
{
|
|
||||||
// Get the newly selected node.
|
|
||||||
var treeView:TreeView = cast event.target;
|
|
||||||
var targetNode:TreeViewNode = treeView.selectedNode;
|
|
||||||
|
|
||||||
if (targetNode == null)
|
|
||||||
{
|
|
||||||
trace('No target node!');
|
|
||||||
// Reset the user's selection.
|
|
||||||
var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView);
|
|
||||||
if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (targetNode.data.id.split('_')[1])
|
|
||||||
{
|
|
||||||
case 'difficulty':
|
|
||||||
var variation:String = targetNode.data.id.split('_')[2];
|
|
||||||
var difficulty:String = targetNode.data.id.split('_')[3];
|
|
||||||
|
|
||||||
if (variation != null && difficulty != null)
|
|
||||||
{
|
|
||||||
trace('Changing difficulty to "$variation:$difficulty"');
|
|
||||||
selectedVariation = variation;
|
|
||||||
selectedDifficulty = difficulty;
|
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
|
||||||
}
|
|
||||||
// case 'song':
|
|
||||||
// case 'variation':
|
|
||||||
default:
|
|
||||||
// Reset the user's selection.
|
|
||||||
trace('Selected wrong node type, resetting selection.');
|
|
||||||
var currentTreeDifficultyNode = getCurrentTreeDifficultyNode(treeView);
|
|
||||||
if (currentTreeDifficultyNode != null) treeView.selectedNode = currentTreeDifficultyNode;
|
|
||||||
this.refreshToolbox(CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* STATIC FUNCTIONS
|
* STATIC FUNCTIONS
|
||||||
*/
|
*/
|
||||||
|
@ -5865,9 +5860,9 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
switch (noteData.getStrumlineIndex())
|
switch (noteData.getStrumlineIndex())
|
||||||
{
|
{
|
||||||
case 0: // Player
|
case 0: // Player
|
||||||
if (hitsoundsEnabledPlayer) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolume);
|
if (hitsoundsEnabledPlayer) this.playSound(Paths.sound('chartingSounds/hitNotePlayer'), hitsoundVolume * soundMultiplierPlayer);
|
||||||
case 1: // Opponent
|
case 1: // Opponent
|
||||||
if (hitsoundsEnabledOpponent) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolume);
|
if (hitsoundsEnabledOpponent) this.playSound(Paths.sound('chartingSounds/hitNoteOpponent'), hitsoundVolume * soundMultiplierOpponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ import flixel.graphics.frames.FlxFramesCollection;
|
||||||
import flixel.graphics.frames.FlxTileFrames;
|
import flixel.graphics.frames.FlxTileFrames;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import funkin.data.song.SongData.SongEventData;
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
import haxe.ui.tooltips.ToolTipRegionOptions;
|
||||||
|
import funkin.util.HaxeUIUtil;
|
||||||
|
import haxe.ui.tooltips.ToolTipManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A sprite that can be used to display a song event in a chart.
|
* A sprite that can be used to display a song event in a chart.
|
||||||
|
@ -36,6 +39,13 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
|
|
||||||
public var overrideStepTime(default, set):Null<Float> = null;
|
public var overrideStepTime(default, set):Null<Float> = null;
|
||||||
|
|
||||||
|
public var tooltip:ToolTipRegionOptions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether this sprite is a "ghost" sprite used when hovering to place a new event.
|
||||||
|
*/
|
||||||
|
public var isGhost:Bool = false;
|
||||||
|
|
||||||
function set_overrideStepTime(value:Null<Float>):Null<Float>
|
function set_overrideStepTime(value:Null<Float>):Null<Float>
|
||||||
{
|
{
|
||||||
if (overrideStepTime == value) return overrideStepTime;
|
if (overrideStepTime == value) return overrideStepTime;
|
||||||
|
@ -45,12 +55,14 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
return overrideStepTime;
|
return overrideStepTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function new(parent:ChartEditorState)
|
public function new(parent:ChartEditorState, isGhost:Bool = false)
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.parentState = parent;
|
this.parentState = parent;
|
||||||
|
this.isGhost = isGhost;
|
||||||
|
|
||||||
|
this.tooltip = HaxeUIUtil.buildTooltip('N/A');
|
||||||
this.frames = buildFrames();
|
this.frames = buildFrames();
|
||||||
|
|
||||||
buildAnimations();
|
buildAnimations();
|
||||||
|
@ -142,6 +154,7 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
|
// Disown parent. MAKE SURE TO REVIVE BEFORE REUSING
|
||||||
this.kill();
|
this.kill();
|
||||||
this.visible = false;
|
this.visible = false;
|
||||||
|
updateTooltipPosition();
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -151,6 +164,8 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.eventData = value;
|
this.eventData = value;
|
||||||
// Update the position to match the note data.
|
// Update the position to match the note data.
|
||||||
updateEventPosition();
|
updateEventPosition();
|
||||||
|
// Update the tooltip text.
|
||||||
|
this.tooltip.tipData = {text: this.eventData.buildTooltip()};
|
||||||
return this.eventData;
|
return this.eventData;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -169,6 +184,31 @@ class ChartEditorEventSprite extends FlxSprite
|
||||||
this.x += origin.x;
|
this.x += origin.x;
|
||||||
this.y += origin.y;
|
this.y += origin.y;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.updateTooltipPosition();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateTooltipPosition():Void
|
||||||
|
{
|
||||||
|
// No tooltip for ghost sprites.
|
||||||
|
if (this.isGhost) return;
|
||||||
|
|
||||||
|
if (this.eventData == null)
|
||||||
|
{
|
||||||
|
// Disable the tooltip.
|
||||||
|
ToolTipManager.instance.unregisterTooltipRegion(this.tooltip);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Update the position.
|
||||||
|
this.tooltip.left = this.x;
|
||||||
|
this.tooltip.top = this.y;
|
||||||
|
this.tooltip.width = this.width;
|
||||||
|
this.tooltip.height = this.height;
|
||||||
|
|
||||||
|
// Enable the tooltip.
|
||||||
|
ToolTipManager.instance.registerTooltipRegion(this.tooltip);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -317,6 +317,12 @@ class ChartEditorThemeHandler
|
||||||
ChartEditorState.GRID_SIZE
|
ChartEditorState.GRID_SIZE
|
||||||
- (2 * SELECTION_SQUARE_BORDER_WIDTH + 8)),
|
- (2 * SELECTION_SQUARE_BORDER_WIDTH + 8)),
|
||||||
32, 32);
|
32, 32);
|
||||||
|
|
||||||
|
state.selectionBoxSprite.scrollFactor.set(0, 0);
|
||||||
|
state.selectionBoxSprite.zIndex = 30;
|
||||||
|
state.add(state.selectionBoxSprite);
|
||||||
|
|
||||||
|
state.setSelectionBoxBounds();
|
||||||
}
|
}
|
||||||
|
|
||||||
static function updateNotePreview(state:ChartEditorState):Void
|
static function updateNotePreview(state:ChartEditorState):Void
|
||||||
|
|
|
@ -36,6 +36,7 @@ import haxe.ui.containers.dialogs.Dialog.DialogEvent;
|
||||||
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
import funkin.ui.debug.charting.toolboxes.ChartEditorBaseToolbox;
|
||||||
import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox;
|
import funkin.ui.debug.charting.toolboxes.ChartEditorMetadataToolbox;
|
||||||
import funkin.ui.debug.charting.toolboxes.ChartEditorEventDataToolbox;
|
import funkin.ui.debug.charting.toolboxes.ChartEditorEventDataToolbox;
|
||||||
|
import funkin.ui.debug.charting.toolboxes.ChartEditorDifficultyToolbox;
|
||||||
import haxe.ui.containers.Frame;
|
import haxe.ui.containers.Frame;
|
||||||
import haxe.ui.containers.Grid;
|
import haxe.ui.containers.Grid;
|
||||||
import haxe.ui.containers.TreeView;
|
import haxe.ui.containers.TreeView;
|
||||||
|
@ -84,7 +85,7 @@ class ChartEditorToolboxHandler
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
||||||
onShowToolboxPlaytestProperties(state, toolbox);
|
onShowToolboxPlaytestProperties(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
||||||
onShowToolboxDifficulty(state, toolbox);
|
cast(toolbox, ChartEditorBaseToolbox).refresh();
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
|
||||||
// TODO: Fix this.
|
// TODO: Fix this.
|
||||||
cast(toolbox, ChartEditorBaseToolbox).refresh();
|
cast(toolbox, ChartEditorBaseToolbox).refresh();
|
||||||
|
@ -123,8 +124,6 @@ class ChartEditorToolboxHandler
|
||||||
onHideToolboxEventData(state, toolbox);
|
onHideToolboxEventData(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYTEST_PROPERTIES_LAYOUT:
|
||||||
onHideToolboxPlaytestProperties(state, toolbox);
|
onHideToolboxPlaytestProperties(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT:
|
|
||||||
onHideToolboxDifficulty(state, toolbox);
|
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT:
|
||||||
onHideToolboxMetadata(state, toolbox);
|
onHideToolboxMetadata(state, toolbox);
|
||||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT:
|
||||||
|
@ -309,8 +308,6 @@ class ChartEditorToolboxHandler
|
||||||
|
|
||||||
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onHideToolboxEventData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
|
||||||
|
|
||||||
static function onShowToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onShowToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
|
||||||
static function onHideToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
static function onHideToolboxPlaytestProperties(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||||
|
@ -358,91 +355,15 @@ class ChartEditorToolboxHandler
|
||||||
return toolbox;
|
return toolbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function buildToolboxDifficultyLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
static function buildToolboxDifficultyLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
||||||
{
|
{
|
||||||
var toolbox:CollapsibleDialog = cast RuntimeComponentBuilder.fromAsset(ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
var toolbox:ChartEditorBaseToolbox = ChartEditorDifficultyToolbox.build(state);
|
||||||
|
|
||||||
if (toolbox == null) return null;
|
if (toolbox == null) return null;
|
||||||
|
|
||||||
// Starting position.
|
|
||||||
toolbox.x = 125;
|
|
||||||
toolbox.y = 200;
|
|
||||||
|
|
||||||
toolbox.onDialogClosed = function(event:UIEvent) {
|
|
||||||
state.menubarItemToggleToolboxDifficulty.selected = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var difficultyToolboxAddVariation:Null<Button> = toolbox.findComponent('difficultyToolboxAddVariation', Button);
|
|
||||||
if (difficultyToolboxAddVariation == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxAddVariation component.';
|
|
||||||
var difficultyToolboxAddDifficulty:Null<Button> = toolbox.findComponent('difficultyToolboxAddDifficulty', Button);
|
|
||||||
if (difficultyToolboxAddDifficulty == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxAddDifficulty component.';
|
|
||||||
var difficultyToolboxSaveMetadata:Null<Button> = toolbox.findComponent('difficultyToolboxSaveMetadata', Button);
|
|
||||||
if (difficultyToolboxSaveMetadata == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveMetadata component.';
|
|
||||||
var difficultyToolboxSaveChart:Null<Button> = toolbox.findComponent('difficultyToolboxSaveChart', Button);
|
|
||||||
if (difficultyToolboxSaveChart == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveChart component.';
|
|
||||||
// var difficultyToolboxSaveAll:Null<Button> = toolbox.findComponent('difficultyToolboxSaveAll', Button);
|
|
||||||
// if (difficultyToolboxSaveAll == null) throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxSaveAll component.';
|
|
||||||
var difficultyToolboxLoadMetadata:Null<Button> = toolbox.findComponent('difficultyToolboxLoadMetadata', Button);
|
|
||||||
if (difficultyToolboxLoadMetadata == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxLoadMetadata component.';
|
|
||||||
var difficultyToolboxLoadChart:Null<Button> = toolbox.findComponent('difficultyToolboxLoadChart', Button);
|
|
||||||
if (difficultyToolboxLoadChart == null)
|
|
||||||
throw 'ChartEditorToolboxHandler.buildToolboxDifficultyLayout() - Could not find difficultyToolboxLoadChart component.';
|
|
||||||
|
|
||||||
difficultyToolboxAddVariation.onClick = function(_:UIEvent) {
|
|
||||||
state.openAddVariationDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
difficultyToolboxAddDifficulty.onClick = function(_:UIEvent) {
|
|
||||||
state.openAddDifficultyDialog(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) {
|
|
||||||
var vari:String = state.selectedVariation != Constants.DEFAULT_VARIATION ? '-${state.selectedVariation}' : '';
|
|
||||||
FileUtil.writeFileReference('${state.currentSongId}$vari-metadata.json', state.currentSongMetadata.serialize());
|
|
||||||
};
|
|
||||||
|
|
||||||
difficultyToolboxSaveChart.onClick = function(_:UIEvent) {
|
|
||||||
var vari:String = state.selectedVariation != Constants.DEFAULT_VARIATION ? '-${state.selectedVariation}' : '';
|
|
||||||
FileUtil.writeFileReference('${state.currentSongId}$vari-chart.json', state.currentSongChartData.serialize());
|
|
||||||
};
|
|
||||||
|
|
||||||
difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) {
|
|
||||||
// Replace metadata for current variation.
|
|
||||||
SongSerializer.importSongMetadataAsync(function(songMetadata) {
|
|
||||||
state.currentSongMetadata = songMetadata;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
difficultyToolboxLoadChart.onClick = function(_:UIEvent) {
|
|
||||||
// Replace chart data for current variation.
|
|
||||||
SongSerializer.importSongChartDataAsync(function(songChartData) {
|
|
||||||
state.currentSongChartData = songChartData;
|
|
||||||
state.noteDisplayDirty = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
state.difficultySelectDirty = true;
|
|
||||||
|
|
||||||
return toolbox;
|
return toolbox;
|
||||||
}
|
}
|
||||||
|
|
||||||
static function onShowToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void
|
|
||||||
{
|
|
||||||
// Update the selected difficulty when reopening the toolbox.
|
|
||||||
var treeView:Null<TreeView> = toolbox.findComponent('difficultyToolboxTree');
|
|
||||||
if (treeView == null) return;
|
|
||||||
|
|
||||||
var current = state.getCurrentTreeDifficultyNode(treeView);
|
|
||||||
if (current == null) return;
|
|
||||||
treeView.selectedNode = current;
|
|
||||||
trace('selected node: ${treeView.selectedNode}');
|
|
||||||
}
|
|
||||||
|
|
||||||
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<ChartEditorBaseToolbox>
|
||||||
{
|
{
|
||||||
var toolbox:ChartEditorBaseToolbox = ChartEditorMetadataToolbox.build(state);
|
var toolbox:ChartEditorBaseToolbox = ChartEditorMetadataToolbox.build(state);
|
||||||
|
|
|
@ -0,0 +1,239 @@
|
||||||
|
package funkin.ui.debug.charting.toolboxes;
|
||||||
|
|
||||||
|
import funkin.play.character.BaseCharacter.CharacterType;
|
||||||
|
import funkin.play.character.CharacterData;
|
||||||
|
import funkin.data.stage.StageData;
|
||||||
|
import funkin.data.stage.StageRegistry;
|
||||||
|
import funkin.ui.debug.charting.commands.ChangeStartingBPMCommand;
|
||||||
|
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
|
||||||
|
import haxe.ui.components.Button;
|
||||||
|
import haxe.ui.components.CheckBox;
|
||||||
|
import haxe.ui.containers.dialogs.Dialogs;
|
||||||
|
import haxe.ui.containers.dialogs.Dialog.DialogButton;
|
||||||
|
import funkin.data.song.SongData.SongMetadata;
|
||||||
|
import haxe.ui.components.DropDown;
|
||||||
|
import haxe.ui.components.HorizontalSlider;
|
||||||
|
import funkin.util.FileUtil;
|
||||||
|
import haxe.ui.containers.dialogs.MessageBox.MessageBoxType;
|
||||||
|
import funkin.play.song.SongSerializer;
|
||||||
|
import haxe.ui.components.Label;
|
||||||
|
import haxe.ui.components.NumberStepper;
|
||||||
|
import haxe.ui.components.Slider;
|
||||||
|
import haxe.ui.components.TextField;
|
||||||
|
import funkin.play.stage.Stage;
|
||||||
|
import haxe.ui.containers.Box;
|
||||||
|
import haxe.ui.containers.TreeView;
|
||||||
|
import haxe.ui.containers.TreeViewNode;
|
||||||
|
import haxe.ui.containers.Frame;
|
||||||
|
import haxe.ui.events.UIEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The toolbox which allows viewing the list of difficulties, switching to a specific one,
|
||||||
|
* and adding/removing variations and difficulties.
|
||||||
|
*/
|
||||||
|
// @:nullSafety // TODO: Fix null safety when used with HaxeUI build macros.
|
||||||
|
@:access(funkin.ui.debug.charting.ChartEditorState)
|
||||||
|
@:build(haxe.ui.ComponentBuilder.build("assets/exclude/data/ui/chart-editor/toolboxes/difficulty.xml"))
|
||||||
|
class ChartEditorDifficultyToolbox extends ChartEditorBaseToolbox
|
||||||
|
{
|
||||||
|
var difficultyToolboxTree:TreeView;
|
||||||
|
var difficultyToolboxAddVariation:Button;
|
||||||
|
var difficultyToolboxAddDifficulty:Button;
|
||||||
|
var difficultyToolboxRemoveDifficulty:Button;
|
||||||
|
var difficultyToolboxSaveMetadata:Button;
|
||||||
|
var difficultyToolboxSaveChart:Button;
|
||||||
|
var difficultyToolboxLoadMetadata:Button;
|
||||||
|
var difficultyToolboxLoadChart:Button;
|
||||||
|
|
||||||
|
public function new(chartEditorState2:ChartEditorState)
|
||||||
|
{
|
||||||
|
super(chartEditorState2);
|
||||||
|
|
||||||
|
initialize();
|
||||||
|
|
||||||
|
this.onDialogClosed = onClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClose(event:UIEvent)
|
||||||
|
{
|
||||||
|
chartEditorState.menubarItemToggleToolboxDifficulty.selected = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function initialize():Void
|
||||||
|
{
|
||||||
|
// Starting position.
|
||||||
|
// TODO: Save and load this.
|
||||||
|
this.x = 150;
|
||||||
|
this.y = 250;
|
||||||
|
|
||||||
|
difficultyToolboxAddVariation.onClick = function(_:UIEvent) {
|
||||||
|
chartEditorState.openAddVariationDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxAddDifficulty.onClick = function(_:UIEvent) {
|
||||||
|
chartEditorState.openAddDifficultyDialog(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxRemoveDifficulty.onClick = function(_:UIEvent) {
|
||||||
|
var currentVariation:String = chartEditorState.selectedVariation;
|
||||||
|
var currentDifficulty:String = chartEditorState.selectedDifficulty;
|
||||||
|
|
||||||
|
trace('Removing difficulty "$currentVariation:$currentDifficulty"');
|
||||||
|
|
||||||
|
var callback = (button) -> {
|
||||||
|
switch (button)
|
||||||
|
{
|
||||||
|
case DialogButton.YES:
|
||||||
|
// Remove the difficulty.
|
||||||
|
chartEditorState.removeDifficulty(currentVariation, currentDifficulty);
|
||||||
|
refresh();
|
||||||
|
case DialogButton.NO: // Do nothing.
|
||||||
|
default: // Do nothing.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Dialogs.messageBox("Are you sure? This cannot be undone.", "Remove Difficulty", MessageBoxType.TYPE_YESNO, callback);
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxSaveMetadata.onClick = function(_:UIEvent) {
|
||||||
|
var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : '';
|
||||||
|
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-metadata.json', chartEditorState.currentSongMetadata.serialize());
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxSaveChart.onClick = function(_:UIEvent) {
|
||||||
|
var vari:String = chartEditorState.selectedVariation != Constants.DEFAULT_VARIATION ? '-${chartEditorState.selectedVariation}' : '';
|
||||||
|
FileUtil.writeFileReference('${chartEditorState.currentSongId}$vari-chart.json', chartEditorState.currentSongChartData.serialize());
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxLoadMetadata.onClick = function(_:UIEvent) {
|
||||||
|
// Replace metadata for current variation.
|
||||||
|
SongSerializer.importSongMetadataAsync(function(songMetadata) {
|
||||||
|
chartEditorState.currentSongMetadata = songMetadata;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
difficultyToolboxLoadChart.onClick = function(_:UIEvent) {
|
||||||
|
// Replace chart data for current variation.
|
||||||
|
SongSerializer.importSongChartDataAsync(function(songChartData) {
|
||||||
|
chartEditorState.currentSongChartData = songChartData;
|
||||||
|
chartEditorState.noteDisplayDirty = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clear the tree view and rebuild it with the current song metadata (variation and difficulty list).
|
||||||
|
*/
|
||||||
|
public function updateTree():Void
|
||||||
|
{
|
||||||
|
// Clear the tree view so we can rebuild it.
|
||||||
|
difficultyToolboxTree.clearNodes();
|
||||||
|
|
||||||
|
// , icon: 'haxeui-core/styles/default/haxeui_tiny.png'
|
||||||
|
var treeSong:TreeViewNode = difficultyToolboxTree.addNode({id: 'stv_song', text: 'S: ${chartEditorState.currentSongName}'});
|
||||||
|
treeSong.expanded = true;
|
||||||
|
|
||||||
|
for (curVariation in chartEditorState.availableVariations)
|
||||||
|
{
|
||||||
|
var variationMetadata:Null<SongMetadata> = chartEditorState.songMetadata.get(curVariation);
|
||||||
|
if (variationMetadata == null) continue;
|
||||||
|
|
||||||
|
var treeVariation:TreeViewNode = treeSong.addNode(
|
||||||
|
{
|
||||||
|
id: 'stv_variation_$curVariation',
|
||||||
|
text: 'V: ${curVariation.toTitleCase()}'
|
||||||
|
});
|
||||||
|
treeVariation.expanded = true;
|
||||||
|
|
||||||
|
var difficultyList:Array<String> = variationMetadata.playData.difficulties;
|
||||||
|
|
||||||
|
for (difficulty in difficultyList)
|
||||||
|
{
|
||||||
|
var _treeDifficulty:TreeViewNode = treeVariation.addNode(
|
||||||
|
{
|
||||||
|
id: 'stv_difficulty_${curVariation}_$difficulty',
|
||||||
|
text: 'D: ${difficulty.toTitleCase()}'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
difficultyToolboxTree.onChange = onTreeChange;
|
||||||
|
refreshTreeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the selected item in the tree to the current variation/difficulty.
|
||||||
|
*
|
||||||
|
* @param targetNode The node to select. If null, the current variation/difficulty will be used.
|
||||||
|
*/
|
||||||
|
public function refreshTreeSelection():Void
|
||||||
|
{
|
||||||
|
var targetNode = getCurrentTreeNode();
|
||||||
|
if (targetNode != null) difficultyToolboxTree.selectedNode = targetNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the node in the tree representing the current variation/difficulty.
|
||||||
|
*/
|
||||||
|
function getCurrentTreeNode():TreeViewNode
|
||||||
|
{
|
||||||
|
return
|
||||||
|
difficultyToolboxTree.findNodeByPath('stv_song/stv_variation_$chartEditorState.selectedVariation/stv_difficulty_${chartEditorState.selectedVariation}_$chartEditorState.selectedDifficulty',
|
||||||
|
'id');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when an item in the tree is selected. Updates the current variation/difficulty.
|
||||||
|
*/
|
||||||
|
function onTreeChange(event:UIEvent):Void
|
||||||
|
{
|
||||||
|
// Get the newly selected node.
|
||||||
|
var treeView:TreeView = cast event.target;
|
||||||
|
var targetNode:TreeViewNode = difficultyToolboxTree.selectedNode;
|
||||||
|
|
||||||
|
if (targetNode == null)
|
||||||
|
{
|
||||||
|
trace('No target node!');
|
||||||
|
// Reset the user's selection.
|
||||||
|
refreshTreeSelection();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (targetNode.data.id.split('_')[1])
|
||||||
|
{
|
||||||
|
case 'difficulty':
|
||||||
|
var variation:String = targetNode.data.id.split('_')[2];
|
||||||
|
var difficulty:String = targetNode.data.id.split('_')[3];
|
||||||
|
|
||||||
|
if (variation != null && difficulty != null)
|
||||||
|
{
|
||||||
|
trace('Changing difficulty to "$variation:$difficulty"');
|
||||||
|
chartEditorState.selectedVariation = variation;
|
||||||
|
chartEditorState.selectedDifficulty = difficulty;
|
||||||
|
chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
|
refreshTreeSelection();
|
||||||
|
}
|
||||||
|
// case 'song':
|
||||||
|
// case 'variation':
|
||||||
|
default:
|
||||||
|
// Reset the user's selection.
|
||||||
|
trace('Selected wrong node type, resetting selection.');
|
||||||
|
refreshTreeSelection();
|
||||||
|
chartEditorState.refreshToolbox(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override function refresh():Void
|
||||||
|
{
|
||||||
|
super.refresh();
|
||||||
|
|
||||||
|
refreshTreeSelection();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function build(chartEditorState:ChartEditorState):ChartEditorDifficultyToolbox
|
||||||
|
{
|
||||||
|
return new ChartEditorDifficultyToolbox(chartEditorState);
|
||||||
|
}
|
||||||
|
}
|
17
source/funkin/util/HaxeUIUtil.hx
Normal file
17
source/funkin/util/HaxeUIUtil.hx
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package funkin.util;
|
||||||
|
|
||||||
|
import haxe.ui.tooltips.ToolTipRegionOptions;
|
||||||
|
|
||||||
|
class HaxeUIUtil
|
||||||
|
{
|
||||||
|
public static function buildTooltip(text:String, ?left:Float, ?top:Float, ?width:Float, ?height:Float):ToolTipRegionOptions
|
||||||
|
{
|
||||||
|
return {
|
||||||
|
tipData: {text: text},
|
||||||
|
left: left ?? 0.0,
|
||||||
|
top: top ?? 0.0,
|
||||||
|
width: width ?? 0.0,
|
||||||
|
height: height ?? 0.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue