mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-11-28 15:26:12 +00:00
Merge branch 'rewrite/master' into feature/chart-editor-offset-toolbox
This commit is contained in:
commit
984c26c0e5
2
hmm.json
2
hmm.json
|
|
@ -54,7 +54,7 @@
|
||||||
"name": "haxeui-core",
|
"name": "haxeui-core",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "5b2d5b8e7e470cf637953e1369c80a1f42016a75",
|
"ref": "8a7846b",
|
||||||
"url": "https://github.com/haxeui/haxeui-core"
|
"url": "https://github.com/haxeui/haxeui-core"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,14 @@ import funkin.data.song.SongData.SongEventData;
|
||||||
import funkin.util.macro.ClassMacro;
|
import funkin.util.macro.ClassMacro;
|
||||||
import funkin.play.event.ScriptedSongEvent;
|
import funkin.play.event.ScriptedSongEvent;
|
||||||
|
|
||||||
@:forward(name, tittlte, type, keys, min, max, step, defaultValue, iterator)
|
@:forward(name, title, type, keys, min, max, step, units, defaultValue, iterator)
|
||||||
abstract SongEventSchema(SongEventSchemaRaw)
|
abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* These units look better when placed immediately next to the value, rather than after a space.
|
||||||
|
*/
|
||||||
|
static final NO_SPACE_UNITS:Array<String> = ['x', '°', '%'];
|
||||||
|
|
||||||
public function new(?fields:Array<SongEventSchemaField>)
|
public function new(?fields:Array<SongEventSchemaField>)
|
||||||
{
|
{
|
||||||
this = fields;
|
this = fields;
|
||||||
|
|
@ -42,7 +47,7 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
return this[k] = v;
|
return this[k] = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function stringifyFieldValue(name:String, value:Dynamic):String
|
public function stringifyFieldValue(name:String, value:Dynamic, addUnits:Bool = true):String
|
||||||
{
|
{
|
||||||
var field:SongEventSchemaField = getByName(name);
|
var field:SongEventSchemaField = getByName(name);
|
||||||
if (field == null) return 'Unknown';
|
if (field == null) return 'Unknown';
|
||||||
|
|
@ -52,21 +57,36 @@ abstract SongEventSchema(SongEventSchemaRaw)
|
||||||
case SongEventFieldType.STRING:
|
case SongEventFieldType.STRING:
|
||||||
return Std.string(value);
|
return Std.string(value);
|
||||||
case SongEventFieldType.INTEGER:
|
case SongEventFieldType.INTEGER:
|
||||||
return Std.string(value);
|
var returnValue:String = Std.string(value);
|
||||||
|
if (addUnits) return addUnitsToString(returnValue, field);
|
||||||
|
return returnValue;
|
||||||
case SongEventFieldType.FLOAT:
|
case SongEventFieldType.FLOAT:
|
||||||
return Std.string(value);
|
var returnValue:String = Std.string(value);
|
||||||
|
if (addUnits) return addUnitsToString(returnValue, field);
|
||||||
|
return returnValue;
|
||||||
case SongEventFieldType.BOOL:
|
case SongEventFieldType.BOOL:
|
||||||
return Std.string(value);
|
return Std.string(value);
|
||||||
case SongEventFieldType.ENUM:
|
case SongEventFieldType.ENUM:
|
||||||
|
var valueString:String = Std.string(value);
|
||||||
for (key in field.keys.keys())
|
for (key in field.keys.keys())
|
||||||
{
|
{
|
||||||
if (field.keys.get(key) == value) return key;
|
// Comparing these values as strings because comparing Dynamic variables is jank.
|
||||||
|
if (Std.string(field.keys.get(key)) == valueString) return key;
|
||||||
}
|
}
|
||||||
return Std.string(value);
|
return valueString;
|
||||||
default:
|
default:
|
||||||
return 'Unknown';
|
return 'Unknown';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addUnitsToString(value:String, field:SongEventSchemaField)
|
||||||
|
{
|
||||||
|
if (field.units == null || field.units == '') return value;
|
||||||
|
|
||||||
|
var unit:String = field.units;
|
||||||
|
|
||||||
|
return value + (NO_SPACE_UNITS.contains(unit) ? '' : ' ') + '${unit}';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
typedef SongEventSchemaRaw = Array<SongEventSchemaField>;
|
||||||
|
|
@ -115,6 +135,12 @@ typedef SongEventSchemaField =
|
||||||
*/
|
*/
|
||||||
?step:Float,
|
?step:Float,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for INTEGER and FLOAT values.
|
||||||
|
* The units that the value is expressed in (pixels, percent, etc).
|
||||||
|
*/
|
||||||
|
?units:String,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An optional default value for the field.
|
* An optional default value for the field.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -153,8 +153,8 @@ class SongDataUtils
|
||||||
public static function buildNoteClipboard(notes:Array<SongNoteData>, ?timeOffset:Int = null):Array<SongNoteData>
|
public static function buildNoteClipboard(notes:Array<SongNoteData>, ?timeOffset:Int = null):Array<SongNoteData>
|
||||||
{
|
{
|
||||||
if (notes.length == 0) return notes;
|
if (notes.length == 0) return notes;
|
||||||
if (timeOffset == null) timeOffset = -Std.int(notes[0].time);
|
if (timeOffset == null) timeOffset = Std.int(notes[0].time);
|
||||||
return offsetSongNoteData(sortNotes(notes), timeOffset);
|
return offsetSongNoteData(sortNotes(notes), -timeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -165,8 +165,8 @@ class SongDataUtils
|
||||||
public static function buildEventClipboard(events:Array<SongEventData>, ?timeOffset:Int = null):Array<SongEventData>
|
public static function buildEventClipboard(events:Array<SongEventData>, ?timeOffset:Int = null):Array<SongEventData>
|
||||||
{
|
{
|
||||||
if (events.length == 0) return events;
|
if (events.length == 0) return events;
|
||||||
if (timeOffset == null) timeOffset = -Std.int(events[0].time);
|
if (timeOffset == null) timeOffset = Std.int(events[0].time);
|
||||||
return offsetSongEventData(sortEvents(events), timeOffset);
|
return offsetSongEventData(sortEvents(events), -timeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -135,10 +135,10 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
return new SongEventSchema([
|
return new SongEventSchema([
|
||||||
{
|
{
|
||||||
name: "char",
|
name: "char",
|
||||||
title: "Character",
|
title: "Target",
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
type: SongEventFieldType.ENUM,
|
type: SongEventFieldType.ENUM,
|
||||||
keys: ["Position" => -1, "Boyfriend" => 0, "Dad" => 1, "Girlfriend" => 2]
|
keys: ["Position" => -1, "Player" => 0, "Opponent" => 1, "Girlfriend" => 2]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "x",
|
name: "x",
|
||||||
|
|
@ -146,6 +146,7 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
step: 10.0,
|
step: 10.0,
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: "px"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "y",
|
name: "y",
|
||||||
|
|
@ -153,6 +154,7 @@ class FocusCameraSongEvent extends SongEvent
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
step: 10.0,
|
step: 10.0,
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: "px"
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,14 +78,16 @@ class SetCameraBopSongEvent extends SongEvent
|
||||||
title: 'Intensity',
|
title: 'Intensity',
|
||||||
defaultValue: 1.0,
|
defaultValue: 1.0,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
type: SongEventFieldType.FLOAT
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: 'x'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'rate',
|
name: 'rate',
|
||||||
title: 'Rate (beats/zoom)',
|
title: 'Rate',
|
||||||
defaultValue: 4,
|
defaultValue: 4,
|
||||||
step: 1,
|
step: 1,
|
||||||
type: SongEventFieldType.INTEGER,
|
type: SongEventFieldType.INTEGER,
|
||||||
|
units: 'beats/zoom'
|
||||||
}
|
}
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -106,14 +106,16 @@ class ZoomCameraSongEvent extends SongEvent
|
||||||
title: 'Zoom Level',
|
title: 'Zoom Level',
|
||||||
defaultValue: 1.0,
|
defaultValue: 1.0,
|
||||||
step: 0.1,
|
step: 0.1,
|
||||||
type: SongEventFieldType.FLOAT
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: 'x'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'duration',
|
name: 'duration',
|
||||||
title: 'Duration (in steps)',
|
title: 'Duration',
|
||||||
defaultValue: 4.0,
|
defaultValue: 4.0,
|
||||||
step: 0.5,
|
step: 0.5,
|
||||||
type: SongEventFieldType.FLOAT,
|
type: SongEventFieldType.FLOAT,
|
||||||
|
units: 'steps'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'ease',
|
name: 'ease',
|
||||||
|
|
|
||||||
|
|
@ -688,6 +688,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
*/
|
*/
|
||||||
var activeToolboxes:Map<String, CollapsibleDialog> = new Map<String, CollapsibleDialog>();
|
var activeToolboxes:Map<String, CollapsibleDialog> = new Map<String, CollapsibleDialog>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The camera component we're using for this state.
|
||||||
|
*/
|
||||||
|
var uiCamera:FlxCamera;
|
||||||
|
|
||||||
// Audio
|
// Audio
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -2040,7 +2045,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
loadPreferences();
|
loadPreferences();
|
||||||
|
|
||||||
fixCamera();
|
uiCamera = new FlxCamera();
|
||||||
|
FlxG.cameras.reset(uiCamera);
|
||||||
|
|
||||||
buildDefaultSongData();
|
buildDefaultSongData();
|
||||||
|
|
||||||
|
|
@ -5297,7 +5303,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
Paths.setCurrentLevel('weekend1');
|
Paths.setCurrentLevel('weekend1');
|
||||||
}
|
}
|
||||||
|
|
||||||
subStateClosed.add(fixCamera);
|
subStateClosed.add(reviveUICamera);
|
||||||
subStateClosed.add(resetConductorAfterTest);
|
subStateClosed.add(resetConductorAfterTest);
|
||||||
|
|
||||||
FlxTransitionableState.skipNextTransIn = false;
|
FlxTransitionableState.skipNextTransIn = false;
|
||||||
|
|
@ -5322,6 +5328,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
targetState.vocals = audioVocalTrackGroup;
|
targetState.vocals = audioVocalTrackGroup;
|
||||||
|
|
||||||
|
// Kill and replace the UI camera so it doesn't get destroyed during the state transition.
|
||||||
|
uiCamera.kill();
|
||||||
|
FlxG.cameras.remove(uiCamera, false);
|
||||||
|
FlxG.cameras.reset(new FlxCamera());
|
||||||
|
|
||||||
this.persistentUpdate = false;
|
this.persistentUpdate = false;
|
||||||
this.persistentDraw = false;
|
this.persistentDraw = false;
|
||||||
stopWelcomeMusic();
|
stopWelcomeMusic();
|
||||||
|
|
@ -5411,13 +5422,12 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fix a camera issue caused when closing the PlayState used when testing.
|
* Revive the UI camera and re-establish it as the main camera so UI elements depending on it don't explode.
|
||||||
*/
|
*/
|
||||||
function fixCamera(_:FlxSubState = null):Void
|
function reviveUICamera(_:FlxSubState = null):Void
|
||||||
{
|
{
|
||||||
FlxG.cameras.reset(new FlxCamera());
|
uiCamera.revive();
|
||||||
FlxG.camera.focusOn(new FlxPoint(FlxG.width / 2, FlxG.height / 2));
|
FlxG.cameras.reset(uiCamera);
|
||||||
FlxG.camera.zoom = 1.0;
|
|
||||||
|
|
||||||
add(this.root);
|
add(this.root);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,26 +46,14 @@ class CopyItemsCommand implements ChartEditorCommand
|
||||||
|
|
||||||
function performVisuals(state:ChartEditorState):Void
|
function performVisuals(state:ChartEditorState):Void
|
||||||
{
|
{
|
||||||
|
var hasNotes:Bool = false;
|
||||||
|
var hasEvents:Bool = false;
|
||||||
|
|
||||||
|
// Wiggle copied notes.
|
||||||
if (state.currentNoteSelection.length > 0)
|
if (state.currentNoteSelection.length > 0)
|
||||||
{
|
{
|
||||||
// Display the "Copied Notes" text.
|
hasNotes = true;
|
||||||
if (state.txtCopyNotif != null)
|
|
||||||
{
|
|
||||||
state.txtCopyNotif.visible = true;
|
|
||||||
state.txtCopyNotif.text = "Copied " + state.currentNoteSelection.length + " notes to clipboard";
|
|
||||||
state.txtCopyNotif.x = FlxG.mouse.x - (state.txtCopyNotif.width / 2);
|
|
||||||
state.txtCopyNotif.y = FlxG.mouse.y - 16;
|
|
||||||
FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5,
|
|
||||||
{
|
|
||||||
type: FlxTween.ONESHOT,
|
|
||||||
ease: FlxEase.quadOut,
|
|
||||||
onComplete: function(_) {
|
|
||||||
state.txtCopyNotif.visible = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wiggle the notes.
|
|
||||||
for (note in state.renderedNotes.members)
|
for (note in state.renderedNotes.members)
|
||||||
{
|
{
|
||||||
if (state.isNoteSelected(note.noteData))
|
if (state.isNoteSelected(note.noteData))
|
||||||
|
|
@ -91,8 +79,13 @@ class CopyItemsCommand implements ChartEditorCommand
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wiggle copied events.
|
||||||
|
if (state.currentEventSelection.length > 0)
|
||||||
|
{
|
||||||
|
hasEvents = true;
|
||||||
|
|
||||||
// Wiggle the events.
|
|
||||||
for (event in state.renderedEvents.members)
|
for (event in state.renderedEvents.members)
|
||||||
{
|
{
|
||||||
if (state.isEventSelected(event.eventData))
|
if (state.isEventSelected(event.eventData))
|
||||||
|
|
@ -119,6 +112,39 @@ class CopyItemsCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Display the "Copied Notes" text.
|
||||||
|
if ((hasNotes || hasEvents) && state.txtCopyNotif != null)
|
||||||
|
{
|
||||||
|
var copiedString:String = '';
|
||||||
|
if (hasNotes)
|
||||||
|
{
|
||||||
|
var copiedNotes:Int = state.currentNoteSelection.length;
|
||||||
|
copiedString += '${copiedNotes} note';
|
||||||
|
if (copiedNotes > 1) copiedString += 's';
|
||||||
|
|
||||||
|
if (hasEvents) copiedString += ' and ';
|
||||||
|
}
|
||||||
|
if (hasEvents)
|
||||||
|
{
|
||||||
|
var copiedEvents:Int = state.currentEventSelection.length;
|
||||||
|
copiedString += '${state.currentEventSelection.length} event';
|
||||||
|
if (copiedEvents > 1) copiedString += 's';
|
||||||
|
}
|
||||||
|
|
||||||
|
state.txtCopyNotif.visible = true;
|
||||||
|
state.txtCopyNotif.text = 'Copied ${copiedString} to clipboard';
|
||||||
|
state.txtCopyNotif.x = FlxG.mouse.x - (state.txtCopyNotif.width / 2);
|
||||||
|
state.txtCopyNotif.y = FlxG.mouse.y - 16;
|
||||||
|
FlxTween.tween(state.txtCopyNotif, {y: state.txtCopyNotif.y - 32}, 0.5,
|
||||||
|
{
|
||||||
|
type: FlxTween.ONESHOT,
|
||||||
|
ease: FlxEase.quadOut,
|
||||||
|
onComplete: function(_) {
|
||||||
|
state.txtCopyNotif.visible = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function undo(state:ChartEditorState):Void
|
public function undo(state:ChartEditorState):Void
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ import haxe.ui.core.Component;
|
||||||
import funkin.data.event.SongEventRegistry;
|
import funkin.data.event.SongEventRegistry;
|
||||||
import haxe.ui.components.TextField;
|
import haxe.ui.components.TextField;
|
||||||
import haxe.ui.containers.Box;
|
import haxe.ui.containers.Box;
|
||||||
|
import haxe.ui.containers.HBox;
|
||||||
import haxe.ui.containers.Frame;
|
import haxe.ui.containers.Frame;
|
||||||
import haxe.ui.events.UIEvent;
|
import haxe.ui.events.UIEvent;
|
||||||
import haxe.ui.data.ArrayDataSource;
|
import haxe.ui.data.ArrayDataSource;
|
||||||
|
|
@ -214,7 +215,20 @@ class ChartEditorEventDataToolbox extends ChartEditorBaseToolbox
|
||||||
input.text = field.type;
|
input.text = field.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.addComponent(input);
|
// Putting in a box so we can add a unit label easily if there is one.
|
||||||
|
var inputBox:HBox = new HBox();
|
||||||
|
inputBox.addComponent(input);
|
||||||
|
|
||||||
|
// Add a unit label if applicable.
|
||||||
|
if (field.units != null && field.units != "")
|
||||||
|
{
|
||||||
|
var units:Label = new Label();
|
||||||
|
units.text = field.units;
|
||||||
|
units.verticalAlign = "center";
|
||||||
|
inputBox.addComponent(units);
|
||||||
|
}
|
||||||
|
|
||||||
|
target.addComponent(inputBox);
|
||||||
|
|
||||||
// Update the value of the event data.
|
// Update the value of the event data.
|
||||||
input.onChange = function(event:UIEvent) {
|
input.onChange = function(event:UIEvent) {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue