mirror of
https://github.com/FunkinCrew/Funkin.git
synced 2024-11-27 01:55:52 -05:00
Merge branch 'master' of https://github.com/ninjamuffin99/funkin-secret into feature/ghost-hold-notes
This commit is contained in:
commit
a2e43b8e4e
35 changed files with 1155 additions and 329 deletions
|
@ -200,6 +200,12 @@
|
|||
<postbuild haxe="source/Prebuild.hx"/> -->
|
||||
<postbuild haxe="source/Postbuild.hx"/> -->
|
||||
|
||||
<!-- Enable this on platforms which do not support dropping files onto the window. -->
|
||||
<set name="FILE_DROP_UNSUPPORTED" if="mac" />
|
||||
<section unless="FILE_DROP_UNSUPPORTED">
|
||||
<set name="FILE_DROP_SUPPORTED" />
|
||||
</section>
|
||||
|
||||
<!-- Options for Polymod -->
|
||||
<section if="polymod">
|
||||
<!-- Turns on additional debug logging. -->
|
||||
|
|
6
hmm.json
6
hmm.json
|
@ -49,8 +49,8 @@
|
|||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "f5daafe93bdfa957538f199294a54e0476c805b7",
|
||||
"url": "https://github.com/haxeui/haxeui-core/"
|
||||
"ref": "e92d5cfac847943fac84696b103670d55c2c774f",
|
||||
"url": "https://github.com/haxeui/haxeui-core"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
|
@ -137,7 +137,7 @@
|
|||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "ef43deb2c68d8a4bcd73abfbd77324fc8220d0c1",
|
||||
"ref": "d33d489a137ff8fdece4994cf1302f0b6334ed08",
|
||||
"url": "https://github.com/EliteMasterEric/openfl"
|
||||
},
|
||||
{
|
||||
|
|
|
@ -47,7 +47,7 @@ class Conductor
|
|||
/**
|
||||
* Beats per minute of the current song at the current time.
|
||||
*/
|
||||
public static var bpm(get, null):Float;
|
||||
public static var bpm(get, never):Float;
|
||||
|
||||
static function get_bpm():Float
|
||||
{
|
||||
|
@ -67,7 +67,7 @@ class Conductor
|
|||
/**
|
||||
* Duration of a measure in milliseconds. Calculated based on bpm.
|
||||
*/
|
||||
public static var measureLengthMs(get, null):Float;
|
||||
public static var measureLengthMs(get, never):Float;
|
||||
|
||||
static function get_measureLengthMs():Float
|
||||
{
|
||||
|
@ -77,7 +77,7 @@ class Conductor
|
|||
/**
|
||||
* Duration of a beat (quarter note) in milliseconds. Calculated based on bpm.
|
||||
*/
|
||||
public static var beatLengthMs(get, null):Float;
|
||||
public static var beatLengthMs(get, never):Float;
|
||||
|
||||
static function get_beatLengthMs():Float
|
||||
{
|
||||
|
@ -88,14 +88,14 @@ class Conductor
|
|||
/**
|
||||
* Duration of a step (sixtennth note) in milliseconds. Calculated based on bpm.
|
||||
*/
|
||||
public static var stepLengthMs(get, null):Float;
|
||||
public static var stepLengthMs(get, never):Float;
|
||||
|
||||
static function get_stepLengthMs():Float
|
||||
{
|
||||
return beatLengthMs / timeSignatureNumerator;
|
||||
}
|
||||
|
||||
public static var timeSignatureNumerator(get, null):Int;
|
||||
public static var timeSignatureNumerator(get, never):Int;
|
||||
|
||||
static function get_timeSignatureNumerator():Int
|
||||
{
|
||||
|
@ -104,7 +104,7 @@ class Conductor
|
|||
return currentTimeChange.timeSignatureNum;
|
||||
}
|
||||
|
||||
public static var timeSignatureDenominator(get, null):Int;
|
||||
public static var timeSignatureDenominator(get, never):Int;
|
||||
|
||||
static function get_timeSignatureDenominator():Int
|
||||
{
|
||||
|
@ -151,7 +151,7 @@ class Conductor
|
|||
public static var audioOffset:Float = 0;
|
||||
public static var offset:Float = 0;
|
||||
|
||||
public static var beatsPerMeasure(get, null):Float;
|
||||
public static var beatsPerMeasure(get, never):Float;
|
||||
|
||||
static function get_beatsPerMeasure():Float
|
||||
{
|
||||
|
@ -159,7 +159,7 @@ class Conductor
|
|||
return stepsPerMeasure / Constants.STEPS_PER_BEAT;
|
||||
}
|
||||
|
||||
public static var stepsPerMeasure(get, null):Int;
|
||||
public static var stepsPerMeasure(get, never):Int;
|
||||
|
||||
static function get_stepsPerMeasure():Int
|
||||
{
|
||||
|
|
|
@ -4,9 +4,34 @@ import openfl.utils.Assets;
|
|||
import lime.app.Future;
|
||||
import openfl.display.BitmapData;
|
||||
|
||||
@:nullSafety
|
||||
class Cursor
|
||||
{
|
||||
public static var cursorMode(default, set):CursorMode;
|
||||
/**
|
||||
* The current cursor mode.
|
||||
* Set this value to change the cursor graphic.
|
||||
*/
|
||||
public static var cursorMode(default, set):Null<CursorMode> = null;
|
||||
|
||||
/**
|
||||
* Show the cursor.
|
||||
*/
|
||||
public static inline function show():Void
|
||||
{
|
||||
FlxG.mouse.visible = true;
|
||||
// Reset the cursor mode.
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the cursor.
|
||||
*/
|
||||
public static inline function hide():Void
|
||||
{
|
||||
FlxG.mouse.visible = false;
|
||||
// Reset the cursor mode.
|
||||
Cursor.cursorMode = null;
|
||||
}
|
||||
|
||||
static final CURSOR_DEFAULT_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -15,7 +40,7 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorDefault:BitmapData = null;
|
||||
static var assetCursorDefault:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_CROSS_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -24,7 +49,7 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorCross:BitmapData = null;
|
||||
static var assetCursorCross:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_ERASER_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -33,16 +58,16 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorEraser:BitmapData = null;
|
||||
static var assetCursorEraser:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_GRABBING_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-grabbing.png",
|
||||
scale: 1.0,
|
||||
offsetX: 32,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorGrabbing:BitmapData = null;
|
||||
static var assetCursorGrabbing:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_HOURGLASS_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -51,25 +76,34 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorHourglass:BitmapData = null;
|
||||
static var assetCursorHourglass:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_POINTER_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-pointer.png",
|
||||
scale: 1.0,
|
||||
offsetX: 8,
|
||||
offsetX: -8,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorPointer:BitmapData = null;
|
||||
static var assetCursorPointer:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_TEXT_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-text.png",
|
||||
scale: 1.0,
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorText:BitmapData = null;
|
||||
static var assetCursorText:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_TEXT_VERTICAL_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-text-vertical.png",
|
||||
scale: 0.2,
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorTextVertical:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_ZOOM_IN_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -78,7 +112,7 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorZoomIn:BitmapData = null;
|
||||
static var assetCursorZoomIn:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_ZOOM_OUT_PARAMS:CursorParams =
|
||||
{
|
||||
|
@ -87,11 +121,36 @@ class Cursor
|
|||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
};
|
||||
static var assetCursorZoomOut:BitmapData = null;
|
||||
static var assetCursorZoomOut:Null<BitmapData> = null;
|
||||
|
||||
static function set_cursorMode(value:CursorMode):CursorMode
|
||||
static final CURSOR_CROSSHAIR_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-crosshair.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
static var assetCursorCrosshair:Null<BitmapData> = null;
|
||||
|
||||
static final CURSOR_CELL_PARAMS:CursorParams =
|
||||
{
|
||||
graphic: "assets/images/cursor/cursor-cell.png",
|
||||
scale: 1.0,
|
||||
offsetX: -16,
|
||||
offsetY: -16,
|
||||
};
|
||||
static var assetCursorCell:Null<BitmapData> = null;
|
||||
|
||||
// DESIRED CURSOR: Resize NS (vertical)
|
||||
// DESIRED CURSOR: Resize EW (horizontal)
|
||||
// DESIRED CURSOR: Resize NESW (diagonal)
|
||||
// DESIRED CURSOR: Resize NWSE (diagonal)
|
||||
// DESIRED CURSOR: Help (Cursor with question mark)
|
||||
// DESIRED CURSOR: Menu (Cursor with menu icon)
|
||||
|
||||
static function set_cursorMode(value:Null<CursorMode>):Null<CursorMode>
|
||||
{
|
||||
if (cursorMode != value)
|
||||
if (value != null && cursorMode != value)
|
||||
{
|
||||
cursorMode = value;
|
||||
setCursorGraphic(cursorMode);
|
||||
|
@ -99,16 +158,9 @@ class Cursor
|
|||
return cursorMode;
|
||||
}
|
||||
|
||||
public static inline function show():Void
|
||||
{
|
||||
FlxG.mouse.visible = true;
|
||||
}
|
||||
|
||||
public static inline function hide():Void
|
||||
{
|
||||
FlxG.mouse.visible = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous.
|
||||
*/
|
||||
static function setCursorGraphic(?value:CursorMode = null):Void
|
||||
{
|
||||
if (value == null)
|
||||
|
@ -117,6 +169,156 @@ class Cursor
|
|||
return;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Default:
|
||||
if (assetCursorDefault == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_DEFAULT_PARAMS.graphic);
|
||||
assetCursorDefault = bitmapData;
|
||||
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
|
||||
}
|
||||
|
||||
case Cross:
|
||||
if (assetCursorCross == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CROSS_PARAMS.graphic);
|
||||
assetCursorCross = bitmapData;
|
||||
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
|
||||
}
|
||||
|
||||
case Eraser:
|
||||
if (assetCursorEraser == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ERASER_PARAMS.graphic);
|
||||
assetCursorEraser = bitmapData;
|
||||
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
|
||||
}
|
||||
|
||||
case Grabbing:
|
||||
if (assetCursorGrabbing == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_GRABBING_PARAMS.graphic);
|
||||
assetCursorGrabbing = bitmapData;
|
||||
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
|
||||
}
|
||||
|
||||
case Hourglass:
|
||||
if (assetCursorHourglass == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_HOURGLASS_PARAMS.graphic);
|
||||
assetCursorHourglass = bitmapData;
|
||||
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
|
||||
}
|
||||
|
||||
case Pointer:
|
||||
if (assetCursorPointer == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_POINTER_PARAMS.graphic);
|
||||
assetCursorPointer = bitmapData;
|
||||
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
|
||||
}
|
||||
|
||||
case Text:
|
||||
if (assetCursorText == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_TEXT_PARAMS.graphic);
|
||||
assetCursorText = bitmapData;
|
||||
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
|
||||
}
|
||||
|
||||
case ZoomIn:
|
||||
if (assetCursorZoomIn == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic);
|
||||
assetCursorZoomIn = bitmapData;
|
||||
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
|
||||
}
|
||||
|
||||
case ZoomOut:
|
||||
if (assetCursorZoomOut == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic);
|
||||
assetCursorZoomOut = bitmapData;
|
||||
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
|
||||
}
|
||||
|
||||
case Crosshair:
|
||||
if (assetCursorCrosshair == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CROSSHAIR_PARAMS.graphic);
|
||||
assetCursorCrosshair = bitmapData;
|
||||
applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS);
|
||||
}
|
||||
|
||||
case Cell:
|
||||
if (assetCursorCell == null)
|
||||
{
|
||||
var bitmapData:BitmapData = Assets.getBitmapData(CURSOR_CELL_PARAMS.graphic);
|
||||
assetCursorCell = bitmapData;
|
||||
applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS);
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS);
|
||||
}
|
||||
|
||||
default:
|
||||
setCursorGraphic(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous.
|
||||
*/
|
||||
static function loadCursorGraphic(?value:CursorMode = null):Void
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
FlxG.mouse.unload();
|
||||
return;
|
||||
}
|
||||
|
||||
switch (value)
|
||||
{
|
||||
case Default:
|
||||
|
@ -127,6 +329,7 @@ class Cursor
|
|||
assetCursorDefault = bitmapData;
|
||||
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Default));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -141,6 +344,7 @@ class Cursor
|
|||
assetCursorCross = bitmapData;
|
||||
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Cross));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -155,6 +359,7 @@ class Cursor
|
|||
assetCursorEraser = bitmapData;
|
||||
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Eraser));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -169,6 +374,7 @@ class Cursor
|
|||
assetCursorGrabbing = bitmapData;
|
||||
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Grabbing));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -183,6 +389,7 @@ class Cursor
|
|||
assetCursorHourglass = bitmapData;
|
||||
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Hourglass));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -197,6 +404,7 @@ class Cursor
|
|||
assetCursorPointer = bitmapData;
|
||||
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Pointer));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -211,6 +419,7 @@ class Cursor
|
|||
assetCursorText = bitmapData;
|
||||
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Text));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -225,6 +434,7 @@ class Cursor
|
|||
assetCursorZoomIn = bitmapData;
|
||||
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(ZoomIn));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -239,14 +449,45 @@ class Cursor
|
|||
assetCursorZoomOut = bitmapData;
|
||||
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(ZoomOut));
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
|
||||
}
|
||||
|
||||
case Crosshair:
|
||||
if (assetCursorCrosshair == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSSHAIR_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
assetCursorCrosshair = bitmapData;
|
||||
applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Crosshair));
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorCrosshair, CURSOR_CROSSHAIR_PARAMS);
|
||||
}
|
||||
|
||||
case Cell:
|
||||
if (assetCursorCell == null)
|
||||
{
|
||||
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CELL_PARAMS.graphic);
|
||||
future.onComplete(function(bitmapData:BitmapData) {
|
||||
assetCursorCell = bitmapData;
|
||||
applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS);
|
||||
});
|
||||
future.onError(onCursorError.bind(Cell));
|
||||
}
|
||||
else
|
||||
{
|
||||
applyCursorParams(assetCursorCell, CURSOR_CELL_PARAMS);
|
||||
}
|
||||
|
||||
default:
|
||||
setCursorGraphic(null);
|
||||
loadCursorGraphic(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -254,6 +495,11 @@ class Cursor
|
|||
{
|
||||
FlxG.mouse.load(graphic, params.scale, params.offsetX, params.offsetY);
|
||||
}
|
||||
|
||||
static function onCursorError(cursorMode:CursorMode, error:String):Void
|
||||
{
|
||||
trace("Failed to load cursor graphic for cursor mode " + cursorMode + ": " + error);
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
|
||||
|
@ -268,6 +514,8 @@ enum CursorMode
|
|||
Text;
|
||||
ZoomIn;
|
||||
ZoomOut;
|
||||
Crosshair;
|
||||
Cell;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -26,7 +26,7 @@ class TurboKeyHandler extends FlxBasic
|
|||
/**
|
||||
* Whether all of the keys for this handler are pressed.
|
||||
*/
|
||||
public var allPressed(get, null):Bool;
|
||||
public var allPressed(get, never):Bool;
|
||||
|
||||
/**
|
||||
* Whether all of the keys for this handler are activated,
|
||||
|
|
|
@ -251,14 +251,14 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
var overrideMusic:Bool = false;
|
||||
|
||||
public var isSubState(get, null):Bool;
|
||||
public var isSubState(get, never):Bool;
|
||||
|
||||
function get_isSubState():Bool
|
||||
{
|
||||
return this._parentState != null;
|
||||
}
|
||||
|
||||
public var isChartingMode(get, null):Bool;
|
||||
public var isChartingMode(get, never):Bool;
|
||||
|
||||
function get_isChartingMode():Bool
|
||||
{
|
||||
|
@ -427,7 +427,7 @@ class PlayState extends MusicBeatSubState
|
|||
* Data for the current difficulty for the current song.
|
||||
* Includes chart data, scroll speed, and other information.
|
||||
*/
|
||||
public var currentChart(get, null):SongDifficulty;
|
||||
public var currentChart(get, never):SongDifficulty;
|
||||
|
||||
function get_currentChart():SongDifficulty
|
||||
{
|
||||
|
@ -439,7 +439,7 @@ class PlayState extends MusicBeatSubState
|
|||
* The internal ID of the currently active Stage.
|
||||
* Used to retrieve the data required to build the `currentStage`.
|
||||
*/
|
||||
public var currentStageId(get, null):String;
|
||||
public var currentStageId(get, never):String;
|
||||
|
||||
function get_currentStageId():String
|
||||
{
|
||||
|
|
|
@ -66,7 +66,7 @@ class BaseCharacter extends Bopper
|
|||
* The offset between the corner of the sprite and the origin of the sprite (at the character's feet).
|
||||
* cornerPosition = stageData - characterOrigin
|
||||
*/
|
||||
public var characterOrigin(get, null):FlxPoint;
|
||||
public var characterOrigin(get, never):FlxPoint;
|
||||
|
||||
function get_characterOrigin():FlxPoint
|
||||
{
|
||||
|
@ -103,7 +103,7 @@ class BaseCharacter extends Bopper
|
|||
/**
|
||||
* The absolute position of the character's feet, at the bottom-center of the sprite.
|
||||
*/
|
||||
public var feetPosition(get, null):FlxPoint;
|
||||
public var feetPosition(get, never):FlxPoint;
|
||||
|
||||
function get_feetPosition():FlxPoint
|
||||
{
|
||||
|
@ -264,7 +264,7 @@ class BaseCharacter extends Bopper
|
|||
/**
|
||||
* The per-character camera offset.
|
||||
*/
|
||||
var characterCameraOffsets(get, null):Array<Float>;
|
||||
var characterCameraOffsets(get, never):Array<Float>;
|
||||
|
||||
function get_characterCameraOffsets():Array<Float>
|
||||
{
|
||||
|
|
|
@ -50,7 +50,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
|
|||
*/
|
||||
var currentDialogueEntry:Int = 0;
|
||||
|
||||
var currentDialogueEntryCount(get, null):Int;
|
||||
var currentDialogueEntryCount(get, never):Int;
|
||||
|
||||
function get_currentDialogueEntryCount():Int
|
||||
{
|
||||
|
@ -62,14 +62,14 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
|
|||
* **/
|
||||
var currentDialogueLine:Int = 0;
|
||||
|
||||
var currentDialogueLineCount(get, null):Int;
|
||||
var currentDialogueLineCount(get, never):Int;
|
||||
|
||||
function get_currentDialogueLineCount():Int
|
||||
{
|
||||
return currentDialogueEntryData.text.length;
|
||||
}
|
||||
|
||||
var currentDialogueEntryData(get, null):DialogueEntryData;
|
||||
var currentDialogueEntryData(get, never):DialogueEntryData;
|
||||
|
||||
function get_currentDialogueEntryData():DialogueEntryData
|
||||
{
|
||||
|
@ -79,7 +79,7 @@ class Conversation extends FlxSpriteGroup implements IDialogueScriptedClass
|
|||
return conversationData.dialogue[currentDialogueEntry];
|
||||
}
|
||||
|
||||
var currentDialogueLineString(get, null):String;
|
||||
var currentDialogueLineString(get, never):String;
|
||||
|
||||
function get_currentDialogueLineString():String
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ import flixel.util.FlxColor;
|
|||
class DialogueBox extends FlxSpriteGroup implements IDialogueScriptedClass
|
||||
{
|
||||
public final dialogueBoxId:String;
|
||||
public var dialogueBoxName(get, null):String;
|
||||
public var dialogueBoxName(get, never):String;
|
||||
|
||||
function get_dialogueBoxName():String
|
||||
{
|
||||
|
|
|
@ -8,7 +8,7 @@ import funkin.modding.IScriptedClass.IDialogueScriptedClass;
|
|||
|
||||
/**
|
||||
* The character sprite which displays during dialogue.
|
||||
*
|
||||
*
|
||||
* Most conversations have two speakers, with one being flipped.
|
||||
*/
|
||||
class Speaker extends FlxSprite implements IDialogueScriptedClass
|
||||
|
@ -26,7 +26,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass
|
|||
/**
|
||||
* A readable name for this speaker.
|
||||
*/
|
||||
public var speakerName(get, null):String;
|
||||
public var speakerName(get, never):String;
|
||||
|
||||
function get_speakerName():String
|
||||
{
|
||||
|
@ -129,7 +129,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass
|
|||
|
||||
/**
|
||||
* Set the sprite scale to the appropriate value.
|
||||
* @param scale
|
||||
* @param scale
|
||||
*/
|
||||
public function setScale(scale:Null<Float>):Void
|
||||
{
|
||||
|
@ -184,7 +184,7 @@ class Speaker extends FlxSprite implements IDialogueScriptedClass
|
|||
/**
|
||||
* Ensure that a given animation exists before playing it.
|
||||
* Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play.
|
||||
* @param name
|
||||
* @param name
|
||||
*/
|
||||
function correctAnimationName(name:String):String
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ class Strumline extends FlxSpriteGroup
|
|||
static final KEY_COUNT:Int = 4;
|
||||
static final NOTE_SPLASH_CAP:Int = 6;
|
||||
|
||||
static var RENDER_DISTANCE_MS(get, null):Float;
|
||||
static var RENDER_DISTANCE_MS(get, never):Float;
|
||||
|
||||
static function get_RENDER_DISTANCE_MS():Float
|
||||
{
|
||||
|
|
|
@ -31,7 +31,7 @@ class SustainTrail extends FlxSprite
|
|||
public var noteDirection:NoteDirection = 0;
|
||||
public var sustainLength(default, set):Float = 0; // millis
|
||||
public var fullSustainLength:Float = 0;
|
||||
public var noteData:SongNoteData;
|
||||
public var noteData:Null<SongNoteData>;
|
||||
|
||||
public var cover:NoteHoldCover = null;
|
||||
|
||||
|
|
|
@ -179,7 +179,7 @@ class SongMigrator
|
|||
songMetadata.playData.playableChars = {};
|
||||
try
|
||||
{
|
||||
Reflect.setField(songMetadata.playData.playableChars, songData.song.player1, new SongPlayableChar('', songData.song.player2));
|
||||
songMetadata.playData.playableChars.set(songData.song.player1, new SongPlayableChar('', songData.song.player2));
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
|
|
|
@ -20,7 +20,7 @@ class SongValidator
|
|||
public static final DEFAULT_STAGE:String = "mainStage";
|
||||
public static final DEFAULT_SCROLLSPEED:Float = 1.0;
|
||||
|
||||
public static var DEFAULT_GENERATEDBY(get, null):String;
|
||||
public static var DEFAULT_GENERATEDBY(get, never):String;
|
||||
|
||||
static function get_DEFAULT_GENERATEDBY():String
|
||||
{
|
||||
|
|
|
@ -35,6 +35,7 @@ interface ChartEditorCommand
|
|||
public function toString():String;
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class AddNotesCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -98,6 +99,7 @@ class AddNotesCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class RemoveNotesCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -153,6 +155,7 @@ class RemoveNotesCommand implements ChartEditorCommand
|
|||
/**
|
||||
* Appends one or more items to the selection.
|
||||
*/
|
||||
@:nullSafety
|
||||
class SelectItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -220,6 +223,7 @@ class SelectItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class AddEventsCommand implements ChartEditorCommand
|
||||
{
|
||||
var events:Array<SongEventData>;
|
||||
|
@ -278,6 +282,7 @@ class AddEventsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class RemoveEventsCommand implements ChartEditorCommand
|
||||
{
|
||||
var events:Array<SongEventData>;
|
||||
|
@ -327,6 +332,7 @@ class RemoveEventsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class RemoveItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -385,6 +391,7 @@ class RemoveItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class SwitchDifficultyCommand implements ChartEditorCommand
|
||||
{
|
||||
var prevDifficulty:String;
|
||||
|
@ -424,6 +431,7 @@ class SwitchDifficultyCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class DeselectItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -478,6 +486,7 @@ class DeselectItemsCommand implements ChartEditorCommand
|
|||
* Sets the selection rather than appends it.
|
||||
* Deselects any notes that are not in the new selection.
|
||||
*/
|
||||
@:nullSafety
|
||||
class SetItemSelectionCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -518,6 +527,7 @@ class SetItemSelectionCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class SelectAllItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var previousNoteSelection:Array<SongNoteData>;
|
||||
|
@ -553,6 +563,7 @@ class SelectAllItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class InvertSelectedItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var previousNoteSelection:Array<SongNoteData>;
|
||||
|
@ -587,6 +598,7 @@ class InvertSelectedItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class DeselectAllItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var previousNoteSelection:Array<SongNoteData>;
|
||||
|
@ -622,6 +634,7 @@ class DeselectAllItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class CutItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
|
@ -679,14 +692,16 @@ class CutItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class FlipNotesCommand implements ChartEditorCommand
|
||||
{
|
||||
var notes:Array<SongNoteData>;
|
||||
var flippedNotes:Array<SongNoteData>;
|
||||
var notes:Array<SongNoteData> = [];
|
||||
var flippedNotes:Array<SongNoteData> = [];
|
||||
|
||||
public function new(notes:Array<SongNoteData>)
|
||||
{
|
||||
this.notes = notes;
|
||||
this.flippedNotes = SongDataUtils.flipNotes(notes);
|
||||
}
|
||||
|
||||
public function execute(state:ChartEditorState):Void
|
||||
|
@ -695,7 +710,6 @@ class FlipNotesCommand implements ChartEditorCommand
|
|||
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||
|
||||
// Add the flipped notes.
|
||||
flippedNotes = SongDataUtils.flipNotes(notes);
|
||||
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(flippedNotes);
|
||||
|
||||
state.currentNoteSelection = flippedNotes;
|
||||
|
@ -729,12 +743,13 @@ class FlipNotesCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class PasteItemsCommand implements ChartEditorCommand
|
||||
{
|
||||
var targetTimestamp:Float;
|
||||
// Notes we added with this command, for undo.
|
||||
var addedNotes:Array<SongNoteData>;
|
||||
var addedEvents:Array<SongEventData>;
|
||||
var addedNotes:Array<SongNoteData> = [];
|
||||
var addedEvents:Array<SongEventData> = [];
|
||||
|
||||
public function new(targetTimestamp:Float)
|
||||
{
|
||||
|
@ -787,6 +802,7 @@ class PasteItemsCommand implements ChartEditorCommand
|
|||
}
|
||||
}
|
||||
|
||||
@:nullSafety
|
||||
class ExtendNoteLengthCommand implements ChartEditorCommand
|
||||
{
|
||||
var note:SongNoteData;
|
||||
|
|
|
@ -6,6 +6,7 @@ import funkin.util.SerializerUtil;
|
|||
import funkin.play.song.SongData.SongChartData;
|
||||
import funkin.play.song.SongData.SongMetadata;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.ui.haxeui.components.FunkinLink;
|
||||
import funkin.util.SortUtil;
|
||||
import funkin.input.Cursor;
|
||||
import funkin.play.character.BaseCharacter;
|
||||
|
@ -40,6 +41,7 @@ using Lambda;
|
|||
/**
|
||||
* Handles dialogs for the new Chart Editor.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorDialogHandler
|
||||
{
|
||||
static final CHART_EDITOR_DIALOG_ABOUT_LAYOUT:String = Paths.ui('chart-editor/dialogs/about');
|
||||
|
@ -59,7 +61,7 @@ class ChartEditorDialogHandler
|
|||
* @param state The current chart editor state.
|
||||
* @return The dialog that was opened.
|
||||
*/
|
||||
public static inline function openAboutDialog(state:ChartEditorState):Dialog
|
||||
public static inline function openAboutDialog(state:ChartEditorState):Null<Dialog>
|
||||
{
|
||||
return openDialog(state, CHART_EDITOR_DIALOG_ABOUT_LAYOUT, true, true);
|
||||
}
|
||||
|
@ -70,12 +72,14 @@ class ChartEditorDialogHandler
|
|||
* @param closable Whether the dialog can be closed by the user.
|
||||
* @return The dialog that was opened.
|
||||
*/
|
||||
public static function openWelcomeDialog(state:ChartEditorState, closable:Bool = true):Dialog
|
||||
public static function openWelcomeDialog(state:ChartEditorState, closable:Bool = true):Null<Dialog>
|
||||
{
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_WELCOME_LAYOUT, true, closable);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_WELCOME_LAYOUT, true, closable);
|
||||
if (dialog == null) throw 'Could not locate Welcome dialog';
|
||||
|
||||
// Add handlers to the "Create From Song" section.
|
||||
var linkCreateBasic:Link = dialog.findComponent('splashCreateFromSongBasic', Link);
|
||||
var linkCreateBasic:Null<Link> = dialog.findComponent('splashCreateFromSongBasic', Link);
|
||||
if (linkCreateBasic == null) throw 'Could not locate splashCreateFromSongBasic link in Welcome dialog';
|
||||
linkCreateBasic.onClick = function(_event) {
|
||||
// Hide the welcome dialog
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
|
@ -86,7 +90,8 @@ class ChartEditorDialogHandler
|
|||
openCreateSongWizard(state, false);
|
||||
}
|
||||
|
||||
var linkImportChartLegacy:Link = dialog.findComponent('splashImportChartLegacy', Link);
|
||||
var linkImportChartLegacy:Null<Link> = dialog.findComponent('splashImportChartLegacy', Link);
|
||||
if (linkImportChartLegacy == null) throw 'Could not locate splashImportChartLegacy link in Welcome dialog';
|
||||
linkImportChartLegacy.onClick = function(_event) {
|
||||
// Hide the welcome dialog
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
|
@ -95,7 +100,8 @@ class ChartEditorDialogHandler
|
|||
openImportChartWizard(state, 'legacy', false);
|
||||
};
|
||||
|
||||
var buttonBrowse:Button = dialog.findComponent('splashBrowse', Button);
|
||||
var buttonBrowse:Null<Button> = dialog.findComponent('splashBrowse', Button);
|
||||
if (buttonBrowse == null) throw 'Could not locate splashBrowse button in Welcome dialog';
|
||||
buttonBrowse.onClick = function(_event) {
|
||||
// Hide the welcome dialog
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
|
@ -104,25 +110,32 @@ class ChartEditorDialogHandler
|
|||
openBrowseWizard(state, false);
|
||||
}
|
||||
|
||||
var splashTemplateContainer:VBox = dialog.findComponent('splashTemplateContainer', VBox);
|
||||
var splashTemplateContainer:Null<VBox> = dialog.findComponent('splashTemplateContainer', VBox);
|
||||
if (splashTemplateContainer == null) throw 'Could not locate splashTemplateContainer in Welcome dialog';
|
||||
|
||||
var songList:Array<String> = SongDataParser.listSongIds();
|
||||
songList.sort(SortUtil.alphabetically);
|
||||
|
||||
for (targetSongId in songList)
|
||||
{
|
||||
var songData:Song = SongDataParser.fetchSong(targetSongId);
|
||||
var songData:Null<Song> = SongDataParser.fetchSong(targetSongId);
|
||||
|
||||
if (songData == null) continue;
|
||||
|
||||
var songName:Null<String> = songData.getDifficulty('normal') ?.songName;
|
||||
if (songName == null) songName = songData.getDifficulty() ?.songName;
|
||||
var diffNormal:Null<SongDifficulty> = songData.getDifficulty('normal');
|
||||
var songName:Null<String> = diffNormal?.songName;
|
||||
if (songName == null)
|
||||
{
|
||||
var diffDefault:Null<SongDifficulty> = songData.getDifficulty();
|
||||
songName = diffDefault?.songName;
|
||||
}
|
||||
if (songName == null)
|
||||
{
|
||||
trace('[WARN] Could not fetch song name for ${targetSongId}');
|
||||
continue;
|
||||
}
|
||||
|
||||
var linkTemplateSong:Link = new Link();
|
||||
var linkTemplateSong:Link = new FunkinLink();
|
||||
linkTemplateSong.text = songName;
|
||||
linkTemplateSong.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
|
@ -184,7 +197,8 @@ class ChartEditorDialogHandler
|
|||
{
|
||||
// Open the "Open Chart" wizard
|
||||
// Step 1. Open Chart
|
||||
var openChartDialog:Dialog = openImportChartDialog(state, format);
|
||||
var openChartDialog:Null<Dialog> = openImportChartDialog(state, format);
|
||||
if (openChartDialog == null) throw 'Could not locate Import Chart dialog';
|
||||
openChartDialog.onDialogClosed = function(_event) {
|
||||
state.isHaxeUIDialogOpen = false;
|
||||
if (_event.button == DialogButton.APPLY)
|
||||
|
@ -260,15 +274,18 @@ class ChartEditorDialogHandler
|
|||
@:haxe.warning("-WVarInit") // Hide the warning about the onDropFile handler.
|
||||
public static function openUploadInstDialog(state:ChartEditorState, closable:Bool = true):Dialog
|
||||
{
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT, true, closable);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_INST_LAYOUT, true, closable);
|
||||
if (dialog == null) throw 'Could not locate Upload Instrumental dialog';
|
||||
|
||||
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Upload Instrumental dialog';
|
||||
|
||||
buttonCancel.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var instrumentalBox:Box = dialog.findComponent('instrumentalBox', Box);
|
||||
var instrumentalBox:Null<Box> = dialog.findComponent('instrumentalBox', Box);
|
||||
if (instrumentalBox == null) throw 'Could not locate instrumentalBox in Upload Instrumental dialog';
|
||||
|
||||
instrumentalBox.onMouseOver = function(_event) {
|
||||
instrumentalBox.swapClass('upload-bg', 'upload-bg-hover');
|
||||
|
@ -285,7 +302,7 @@ class ChartEditorDialogHandler
|
|||
instrumentalBox.onClick = function(_event) {
|
||||
Dialogs.openBinaryFile('Open Instrumental', [
|
||||
{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile:SelectedFileInfo) {
|
||||
if (selectedFile != null)
|
||||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
if (state.loadInstrumentalFromBytes(selectedFile.bytes))
|
||||
{
|
||||
|
@ -342,7 +359,7 @@ class ChartEditorDialogHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
var message:String = if (!ChartEditorState.SUPPORTED_MUSIC_FORMATS.contains(path.ext))
|
||||
var message:String = if (!ChartEditorState.SUPPORTED_MUSIC_FORMATS.contains(path.ext ?? ''))
|
||||
{
|
||||
'File format (${path.ext}) not supported for instrumental track (${path.file}.${path.ext})';
|
||||
}
|
||||
|
@ -375,6 +392,15 @@ class ChartEditorDialogHandler
|
|||
handler:(String->Void)
|
||||
}> = [];
|
||||
|
||||
/**
|
||||
* Add a callback for when a file is dropped on a component.
|
||||
*
|
||||
* On OS X you can’t drop on the application window, but rather only the app icon
|
||||
* (either in the dock while running or the icon on the hard drive) so this must be disabled
|
||||
* and UI updated appropriately.
|
||||
* @param component
|
||||
* @param handler
|
||||
*/
|
||||
static function addDropHandler(component:Component, handler:String->Void):Void
|
||||
{
|
||||
#if desktop
|
||||
|
@ -428,15 +454,17 @@ class ChartEditorDialogHandler
|
|||
@:haxe.warning("-WVarInit")
|
||||
public static function openSongMetadataDialog(state:ChartEditorState):Dialog
|
||||
{
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT, true, false);
|
||||
|
||||
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_SONG_METADATA_LAYOUT, true, false);
|
||||
if (dialog == null) throw 'Could not locate Song Metadata dialog';
|
||||
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Song Metadata dialog';
|
||||
buttonCancel.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var dialogSongName:TextField = dialog.findComponent('dialogSongName', TextField);
|
||||
var dialogSongName:Null<TextField> = dialog.findComponent('dialogSongName', TextField);
|
||||
if (dialogSongName == null) throw 'Could not locate dialogSongName TextField in Song Metadata dialog';
|
||||
dialogSongName.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
|
@ -447,12 +475,13 @@ class ChartEditorDialogHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.songName = null;
|
||||
state.currentSongMetadata.songName = "";
|
||||
}
|
||||
};
|
||||
state.currentSongMetadata.songName = null;
|
||||
state.currentSongMetadata.songName = "";
|
||||
|
||||
var dialogSongArtist:TextField = dialog.findComponent('dialogSongArtist', TextField);
|
||||
var dialogSongArtist:Null<TextField> = dialog.findComponent('dialogSongArtist', TextField);
|
||||
if (dialogSongArtist == null) throw 'Could not locate dialogSongArtist TextField in Song Metadata dialog';
|
||||
dialogSongArtist.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
|
@ -463,26 +492,29 @@ class ChartEditorDialogHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.artist = null;
|
||||
state.currentSongMetadata.artist = "";
|
||||
}
|
||||
};
|
||||
state.currentSongMetadata.artist = null;
|
||||
state.currentSongMetadata.artist = "";
|
||||
|
||||
var dialogStage:DropDown = dialog.findComponent('dialogStage', DropDown);
|
||||
var dialogStage:Null<DropDown> = dialog.findComponent('dialogStage', DropDown);
|
||||
if (dialogStage == null) throw 'Could not locate dialogStage DropDown in Song Metadata dialog';
|
||||
dialogStage.onChange = function(event:UIEvent) {
|
||||
if (event.data == null && event.data.id == null) return;
|
||||
state.currentSongMetadata.playData.stage = event.data.id;
|
||||
};
|
||||
state.currentSongMetadata.playData.stage = null;
|
||||
state.currentSongMetadata.playData.stage = 'mainStage';
|
||||
|
||||
var dialogNoteSkin:DropDown = dialog.findComponent('dialogNoteSkin', DropDown);
|
||||
var dialogNoteSkin:Null<DropDown> = dialog.findComponent('dialogNoteSkin', DropDown);
|
||||
if (dialogNoteSkin == null) throw 'Could not locate dialogNoteSkin DropDown in Song Metadata dialog';
|
||||
dialogNoteSkin.onChange = function(event:UIEvent) {
|
||||
if (event.data.id == null) return;
|
||||
state.currentSongMetadata.playData.noteSkin = event.data.id;
|
||||
};
|
||||
state.currentSongMetadata.playData.noteSkin = null;
|
||||
state.currentSongMetadata.playData.noteSkin = 'funkin';
|
||||
|
||||
var dialogBPM:NumberStepper = dialog.findComponent('dialogBPM', NumberStepper);
|
||||
var dialogBPM:Null<NumberStepper> = dialog.findComponent('dialogBPM', NumberStepper);
|
||||
if (dialogBPM == null) throw 'Could not locate dialogBPM NumberStepper in Song Metadata dialog';
|
||||
dialogBPM.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
|
@ -501,8 +533,10 @@ class ChartEditorDialogHandler
|
|||
state.currentSongMetadata.timeChanges = timeChanges;
|
||||
};
|
||||
|
||||
var dialogCharGrid:PropertyGrid = dialog.findComponent('dialogCharGrid', PropertyGrid);
|
||||
var dialogCharAdd:Button = dialog.findComponent('dialogCharAdd', Button);
|
||||
var dialogCharGrid:Null<PropertyGrid> = dialog.findComponent('dialogCharGrid', PropertyGrid);
|
||||
if (dialogCharGrid == null) throw 'Could not locate dialogCharGrid PropertyGrid in Song Metadata dialog';
|
||||
var dialogCharAdd:Null<Button> = dialog.findComponent('dialogCharAdd', Button);
|
||||
if (dialogCharAdd == null) throw 'Could not locate dialogCharAdd Button in Song Metadata dialog';
|
||||
dialogCharAdd.onClick = function(event:UIEvent) {
|
||||
var charGroup:PropertyGroup;
|
||||
charGroup = buildCharGroup(state, null, () -> dialogCharGrid.removeComponent(charGroup));
|
||||
|
@ -512,15 +546,16 @@ class ChartEditorDialogHandler
|
|||
// Empty the character list.
|
||||
state.currentSongMetadata.playData.playableChars = {};
|
||||
// Add at least one character group with no Remove button.
|
||||
dialogCharGrid.addComponent(buildCharGroup(state, 'bf', null));
|
||||
dialogCharGrid.addComponent(buildCharGroup(state, 'bf'));
|
||||
|
||||
var dialogContinue:Button = dialog.findComponent('dialogContinue', Button);
|
||||
var dialogContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
|
||||
if (dialogContinue == null) throw 'Could not locate dialogContinue button in Song Metadata dialog';
|
||||
dialogContinue.onClick = (_event) -> dialog.hideDialog(DialogButton.APPLY);
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
static function buildCharGroup(state:ChartEditorState, key:String = null, removeFunc:Void->Void):PropertyGroup
|
||||
static function buildCharGroup(state:ChartEditorState, key:String = '', removeFunc:Void->Void = null):PropertyGroup
|
||||
{
|
||||
var groupKey:String = key;
|
||||
|
||||
|
@ -545,32 +580,36 @@ class ChartEditorDialogHandler
|
|||
|
||||
var removeGroup:Void->Void = function() {
|
||||
state.currentSongMetadata.playData.playableChars.remove(groupKey);
|
||||
removeFunc();
|
||||
if (removeFunc != null) removeFunc();
|
||||
}
|
||||
|
||||
var charData:SongPlayableChar = getCharData();
|
||||
|
||||
var charGroup:PropertyGroup = cast state.buildComponent(CHART_EDITOR_DIALOG_SONG_METADATA_CHARGROUP_LAYOUT);
|
||||
|
||||
var charGroupPlayer:DropDown = charGroup.findComponent('charGroupPlayer', DropDown);
|
||||
var charGroupPlayer:Null<DropDown> = charGroup.findComponent('charGroupPlayer', DropDown);
|
||||
if (charGroupPlayer == null) throw 'Could not locate charGroupPlayer DropDown in Song Metadata dialog';
|
||||
charGroupPlayer.onChange = function(event:UIEvent) {
|
||||
charGroup.text = event.data.text;
|
||||
moveCharGroup(event.data.id);
|
||||
};
|
||||
|
||||
var charGroupOpponent:DropDown = charGroup.findComponent('charGroupOpponent', DropDown);
|
||||
var charGroupOpponent:Null<DropDown> = charGroup.findComponent('charGroupOpponent', DropDown);
|
||||
if (charGroupOpponent == null) throw 'Could not locate charGroupOpponent DropDown in Song Metadata dialog';
|
||||
charGroupOpponent.onChange = function(event:UIEvent) {
|
||||
charData.opponent = event.data.id;
|
||||
};
|
||||
charGroupOpponent.value = getCharData().opponent;
|
||||
|
||||
var charGroupGirlfriend:DropDown = charGroup.findComponent('charGroupGirlfriend', DropDown);
|
||||
var charGroupGirlfriend:Null<DropDown> = charGroup.findComponent('charGroupGirlfriend', DropDown);
|
||||
if (charGroupGirlfriend == null) throw 'Could not locate charGroupGirlfriend DropDown in Song Metadata dialog';
|
||||
charGroupGirlfriend.onChange = function(event:UIEvent) {
|
||||
charData.girlfriend = event.data.id;
|
||||
};
|
||||
charGroupGirlfriend.value = getCharData().girlfriend;
|
||||
|
||||
var charGroupRemove:Button = charGroup.findComponent('charGroupRemove', Button);
|
||||
var charGroupRemove:Null<Button> = charGroup.findComponent('charGroupRemove', Button);
|
||||
if (charGroupRemove == null) throw 'Could not locate charGroupRemove Button in Song Metadata dialog';
|
||||
charGroupRemove.onClick = function(event:UIEvent) {
|
||||
removeGroup();
|
||||
};
|
||||
|
@ -592,21 +631,26 @@ class ChartEditorDialogHandler
|
|||
|
||||
for (charKey in state.currentSongMetadata.playData.playableChars.keys())
|
||||
{
|
||||
var charData:SongPlayableChar = state.currentSongMetadata.playData.playableChars.get(charKey);
|
||||
var charData:Null<SongPlayableChar> = state.currentSongMetadata.playData.playableChars.get(charKey);
|
||||
if (charData == null) continue;
|
||||
charIdsForVocals.push(charKey);
|
||||
if (charData.opponent != null) charIdsForVocals.push(charData.opponent);
|
||||
}
|
||||
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_VOCALS_LAYOUT, true, closable);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_UPLOAD_VOCALS_LAYOUT, true, closable);
|
||||
if (dialog == null) throw 'Could not locate Upload Vocals dialog';
|
||||
|
||||
var dialogContainer:Component = dialog.findComponent('vocalContainer');
|
||||
var dialogContainer:Null<Component> = dialog.findComponent('vocalContainer');
|
||||
if (dialogContainer == null) throw 'Could not locate vocalContainer in Upload Vocals dialog';
|
||||
|
||||
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Upload Vocals dialog';
|
||||
buttonCancel.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var dialogNoVocals:Button = dialog.findComponent('dialogNoVocals', Button);
|
||||
var dialogNoVocals:Null<Button> = dialog.findComponent('dialogNoVocals', Button);
|
||||
if (dialogNoVocals == null) throw 'Could not locate dialogNoVocals button in Upload Vocals dialog';
|
||||
dialogNoVocals.onClick = function(_event) {
|
||||
// Dismiss
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
|
@ -615,13 +659,18 @@ class ChartEditorDialogHandler
|
|||
for (charKey in charIdsForVocals)
|
||||
{
|
||||
trace('Adding vocal upload for character ${charKey}');
|
||||
var charMetadata:CharacterData = CharacterDataParser.fetchCharacterData(charKey);
|
||||
var charMetadata:Null<CharacterData> = CharacterDataParser.fetchCharacterData(charKey);
|
||||
var charName:String = charMetadata != null ? charMetadata.name : charKey;
|
||||
|
||||
var vocalsEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_UPLOAD_VOCALS_ENTRY_LAYOUT);
|
||||
|
||||
var vocalsEntryLabel:Label = vocalsEntry.findComponent('vocalsEntryLabel', Label);
|
||||
var vocalsEntryLabel:Null<Label> = vocalsEntry.findComponent('vocalsEntryLabel', Label);
|
||||
if (vocalsEntryLabel == null) throw 'Could not locate vocalsEntryLabel in Upload Vocals dialog';
|
||||
#if FILE_DROP_SUPPORTED
|
||||
vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
|
||||
#else
|
||||
vocalsEntryLabel.text = 'Click to browse for vocals for $charName.';
|
||||
#end
|
||||
|
||||
var onDropFile:String->Void = function(pathStr:String) {
|
||||
trace('Selected file: $pathStr');
|
||||
|
@ -638,14 +687,18 @@ class ChartEditorDialogHandler
|
|||
type: NotificationType.Success,
|
||||
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||
});
|
||||
#end
|
||||
|
||||
vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
|
||||
#else
|
||||
vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${path.file}.${path.ext}';
|
||||
#end
|
||||
|
||||
dialogNoVocals.hidden = true;
|
||||
removeDropHandler(onDropFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
var message:String = if (!ChartEditorState.SUPPORTED_MUSIC_FORMATS.contains(path.ext))
|
||||
var message:String = if (!ChartEditorState.SUPPORTED_MUSIC_FORMATS.contains(path.ext ?? ''))
|
||||
{
|
||||
'File format (${path.ext}) not supported for vocal track (${path.file}.${path.ext})';
|
||||
}
|
||||
|
@ -665,17 +718,25 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
vocalsEntryLabel.text = 'Drag and drop vocals for $charName here, or click to browse.';
|
||||
#else
|
||||
vocalsEntryLabel.text = 'Click to browse for vocals for $charName.';
|
||||
#end
|
||||
}
|
||||
};
|
||||
|
||||
vocalsEntry.onClick = function(_event) {
|
||||
Dialogs.openBinaryFile('Open $charName Vocals', [
|
||||
{label: 'Audio File (.ogg)', extension: 'ogg'}], function(selectedFile) {
|
||||
if (selectedFile != null)
|
||||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.name);
|
||||
#if FILE_DROP_SUPPORTED
|
||||
vocalsEntryLabel.text = 'Vocals for $charName (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
|
||||
#else
|
||||
vocalsEntryLabel.text = 'Vocals for $charName (click to browse)\n${selectedFile.name}';
|
||||
#end
|
||||
state.loadVocalsFromBytes(selectedFile.bytes, charKey);
|
||||
dialogNoVocals.hidden = true;
|
||||
removeDropHandler(onDropFile);
|
||||
|
@ -684,11 +745,14 @@ class ChartEditorDialogHandler
|
|||
}
|
||||
|
||||
// onDropFile
|
||||
#if FILE_DROP_SUPPORTED
|
||||
addDropHandler(vocalsEntry, onDropFile);
|
||||
#end
|
||||
dialogContainer.addComponent(vocalsEntry);
|
||||
}
|
||||
|
||||
var dialogContinue:Button = dialog.findComponent('dialogContinue', Button);
|
||||
var dialogContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
|
||||
if (dialogContinue == null) throw 'Could not locate dialogContinue button in Upload Vocals dialog';
|
||||
dialogContinue.onClick = function(_event) {
|
||||
// Dismiss
|
||||
dialog.hideDialog(DialogButton.APPLY);
|
||||
|
@ -706,20 +770,25 @@ class ChartEditorDialogHandler
|
|||
@:haxe.warning('-WVarInit')
|
||||
public static function openChartDialog(state:ChartEditorState, closable:Bool = true):Dialog
|
||||
{
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_OPEN_CHART_LAYOUT, true, closable);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_OPEN_CHART_LAYOUT, true, closable);
|
||||
if (dialog == null) throw 'Could not locate Open Chart dialog';
|
||||
|
||||
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Open Chart dialog';
|
||||
buttonCancel.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var chartContainerA:Component = dialog.findComponent('chartContainerA');
|
||||
var chartContainerB:Component = dialog.findComponent('chartContainerB');
|
||||
var chartContainerA:Null<Component> = dialog.findComponent('chartContainerA');
|
||||
if (chartContainerA == null) throw 'Could not locate chartContainerA in Open Chart dialog';
|
||||
var chartContainerB:Null<Component> = dialog.findComponent('chartContainerB');
|
||||
if (chartContainerB == null) throw 'Could not locate chartContainerB in Open Chart dialog';
|
||||
|
||||
var songMetadata:Map<String, SongMetadata> = [];
|
||||
var songChartData:Map<String, SongChartData> = [];
|
||||
|
||||
var buttonContinue:Button = dialog.findComponent('dialogContinue', Button);
|
||||
var buttonContinue:Null<Button> = dialog.findComponent('dialogContinue', Button);
|
||||
if (buttonContinue == null) throw 'Could not locate dialogContinue button in Open Chart dialog';
|
||||
buttonContinue.onClick = function(_event) {
|
||||
state.loadSong(songMetadata, songChartData);
|
||||
|
||||
|
@ -740,8 +809,13 @@ class ChartEditorDialogHandler
|
|||
|
||||
// Build an entry for -chart.json.
|
||||
var songDefaultChartDataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT);
|
||||
var songDefaultChartDataEntryLabel:Label = songDefaultChartDataEntry.findComponent('chartEntryLabel', Label);
|
||||
var songDefaultChartDataEntryLabel:Null<Label> = songDefaultChartDataEntry.findComponent('chartEntryLabel', Label);
|
||||
if (songDefaultChartDataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
|
||||
#if FILE_DROP_SUPPORTED
|
||||
songDefaultChartDataEntryLabel.text = 'Drag and drop <song>-chart.json file, or click to browse.';
|
||||
#else
|
||||
songDefaultChartDataEntryLabel.text = 'Click to browse for <song>-chart.json file.';
|
||||
#end
|
||||
|
||||
songDefaultChartDataEntry.onClick = onClickChartDataVariation.bind(Constants.DEFAULT_VARIATION).bind(songDefaultChartDataEntryLabel);
|
||||
addDropHandler(songDefaultChartDataEntry, onDropFileChartDataVariation.bind(Constants.DEFAULT_VARIATION).bind(songDefaultChartDataEntryLabel));
|
||||
|
@ -751,20 +825,50 @@ class ChartEditorDialogHandler
|
|||
{
|
||||
// Build entries for -metadata-<variation>.json.
|
||||
var songVariationMetadataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT);
|
||||
var songVariationMetadataEntryLabel:Label = songVariationMetadataEntry.findComponent('chartEntryLabel', Label);
|
||||
var songVariationMetadataEntryLabel:Null<Label> = songVariationMetadataEntry.findComponent('chartEntryLabel', Label);
|
||||
if (songVariationMetadataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
|
||||
#if FILE_DROP_SUPPORTED
|
||||
songVariationMetadataEntryLabel.text = 'Drag and drop <song>-metadata-${variation}.json file, or click to browse.';
|
||||
#else
|
||||
songVariationMetadataEntryLabel.text = 'Click to browse for <song>-metadata-${variation}.json file.';
|
||||
#end
|
||||
|
||||
songVariationMetadataEntry.onMouseOver = function(_event) {
|
||||
songVariationMetadataEntry.swapClass('upload-bg', 'upload-bg-hover');
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
songVariationMetadataEntry.onMouseOut = function(_event) {
|
||||
songVariationMetadataEntry.swapClass('upload-bg-hover', 'upload-bg');
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel);
|
||||
#if FILE_DROP_SUPPORTED
|
||||
addDropHandler(songVariationMetadataEntry, onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel));
|
||||
#end
|
||||
chartContainerB.addComponent(songVariationMetadataEntry);
|
||||
|
||||
// Build entries for -chart-<variation>.json.
|
||||
var songVariationChartDataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT);
|
||||
var songVariationChartDataEntryLabel:Label = songVariationChartDataEntry.findComponent('chartEntryLabel', Label);
|
||||
var songVariationChartDataEntryLabel:Null<Label> = songVariationChartDataEntry.findComponent('chartEntryLabel', Label);
|
||||
if (songVariationChartDataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
|
||||
#if FILE_DROP_SUPPORTED
|
||||
songVariationChartDataEntryLabel.text = 'Drag and drop <song>-chart-${variation}.json file, or click to browse.';
|
||||
#else
|
||||
songVariationChartDataEntryLabel.text = 'Click to browse for <song>-chart-${variation}.json file.';
|
||||
#end
|
||||
|
||||
songVariationChartDataEntry.onMouseOver = function(_event) {
|
||||
songVariationChartDataEntry.swapClass('upload-bg', 'upload-bg-hover');
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
songVariationChartDataEntry.onMouseOut = function(_event) {
|
||||
songVariationChartDataEntry.swapClass('upload-bg-hover', 'upload-bg');
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
songVariationChartDataEntry.onClick = onClickChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel);
|
||||
#if FILE_DROP_SUPPORTED
|
||||
addDropHandler(songVariationChartDataEntry, onDropFileChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel));
|
||||
#end
|
||||
chartContainerB.addComponent(songVariationChartDataEntry);
|
||||
}
|
||||
}
|
||||
|
@ -805,7 +909,11 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
|
||||
#else
|
||||
label.text = 'Metadata file (click to browse)\n${path.file}.${path.ext}';
|
||||
#end
|
||||
|
||||
if (variation == Constants.DEFAULT_VARIATION) constructVariationEntries(songMetadataVariation.playData.songVariations);
|
||||
};
|
||||
|
@ -813,7 +921,7 @@ class ChartEditorDialogHandler
|
|||
onClickMetadataVariation = function(variation:String, label:Label, _event:UIEvent) {
|
||||
Dialogs.openBinaryFile('Open Chart ($variation) Metadata', [
|
||||
{label: 'JSON File (.json)', extension: 'json'}], function(selectedFile) {
|
||||
if (selectedFile != null)
|
||||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.name);
|
||||
|
||||
|
@ -835,7 +943,11 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
label.text = 'Metadata file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
|
||||
#else
|
||||
label.text = 'Metadata file (click to browse)\n${selectedFile.name}';
|
||||
#end
|
||||
|
||||
if (variation == Constants.DEFAULT_VARIATION) constructVariationEntries(songMetadataVariation.playData.songVariations);
|
||||
}
|
||||
|
@ -866,13 +978,17 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${path.file}.${path.ext}';
|
||||
#else
|
||||
label.text = 'Chart data file (click to browse)\n${path.file}.${path.ext}';
|
||||
#end
|
||||
};
|
||||
|
||||
onClickChartDataVariation = function(variation:String, label:Label, _event:UIEvent) {
|
||||
Dialogs.openBinaryFile('Open Chart ($variation) Metadata', [
|
||||
{label: 'JSON File (.json)', extension: 'json'}], function(selectedFile) {
|
||||
if (selectedFile != null)
|
||||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.name);
|
||||
|
||||
|
@ -896,17 +1012,35 @@ class ChartEditorDialogHandler
|
|||
});
|
||||
#end
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
label.text = 'Chart data file (drag and drop, or click to browse)\nSelected file: ${selectedFile.name}';
|
||||
#else
|
||||
label.text = 'Chart data file (click to browse)\n${selectedFile.name}';
|
||||
#end
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var metadataEntry:Component = state.buildComponent(CHART_EDITOR_DIALOG_OPEN_CHART_ENTRY_LAYOUT);
|
||||
var metadataEntryLabel:Label = metadataEntry.findComponent('chartEntryLabel', Label);
|
||||
var metadataEntryLabel:Null<Label> = metadataEntry.findComponent('chartEntryLabel', Label);
|
||||
if (metadataEntryLabel == null) throw 'Could not locate chartEntryLabel in Open Chart dialog';
|
||||
|
||||
#if FILE_DROP_SUPPORTED
|
||||
metadataEntryLabel.text = 'Drag and drop <song>-metadata.json file, or click to browse.';
|
||||
#else
|
||||
metadataEntryLabel.text = 'Click to browse for <song>-metadata.json file.';
|
||||
#end
|
||||
|
||||
metadataEntry.onClick = onClickMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel);
|
||||
addDropHandler(metadataEntry, onDropFileMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel));
|
||||
metadataEntry.onMouseOver = function(_event) {
|
||||
metadataEntry.swapClass('upload-bg', 'upload-bg-hover');
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
metadataEntry.onMouseOut = function(_event) {
|
||||
metadataEntry.swapClass('upload-bg-hover', 'upload-bg');
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
|
||||
chartContainerA.addComponent(metadataEntry);
|
||||
|
||||
|
@ -920,9 +1054,10 @@ class ChartEditorDialogHandler
|
|||
* @param closable
|
||||
* @return Dialog
|
||||
*/
|
||||
public static function openImportChartDialog(state:ChartEditorState, format:String, closable:Bool = true):Dialog
|
||||
public static function openImportChartDialog(state:ChartEditorState, format:String, closable:Bool = true):Null<Dialog>
|
||||
{
|
||||
var dialog:Dialog = openDialog(state, CHART_EDITOR_DIALOG_IMPORT_CHART_LAYOUT, true, closable);
|
||||
var dialog:Null<Dialog> = openDialog(state, CHART_EDITOR_DIALOG_IMPORT_CHART_LAYOUT, true, closable);
|
||||
if (dialog == null) return null;
|
||||
|
||||
var prettyFormat:String = switch (format)
|
||||
{
|
||||
|
@ -938,19 +1073,20 @@ class ChartEditorDialogHandler
|
|||
|
||||
dialog.title = 'Import Chart - ${prettyFormat}';
|
||||
|
||||
var buttonCancel:Button = dialog.findComponent('dialogCancel', Button);
|
||||
var buttonCancel:Null<Button> = dialog.findComponent('dialogCancel', Button);
|
||||
if (buttonCancel == null) throw 'Could not locate dialogCancel button in Import Chart dialog';
|
||||
|
||||
buttonCancel.onClick = function(_event) {
|
||||
dialog.hideDialog(DialogButton.CANCEL);
|
||||
}
|
||||
|
||||
var importBox:Box = dialog.findComponent('importBox', Box);
|
||||
var importBox:Null<Box> = dialog.findComponent('importBox', Box);
|
||||
if (importBox == null) throw 'Could not locate importBox in Import Chart dialog';
|
||||
|
||||
importBox.onMouseOver = function(_event) {
|
||||
importBox.swapClass('upload-bg', 'upload-bg-hover');
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
importBox.onMouseOut = function(_event) {
|
||||
importBox.swapClass('upload-bg-hover', 'upload-bg');
|
||||
Cursor.cursorMode = Default;
|
||||
|
@ -959,8 +1095,8 @@ class ChartEditorDialogHandler
|
|||
var onDropFile:String->Void;
|
||||
|
||||
importBox.onClick = function(_event) {
|
||||
Dialogs.openBinaryFile('Import Chart - ${prettyFormat}', [fileFilter], function(selectedFile:SelectedFileInfo) {
|
||||
if (selectedFile != null)
|
||||
Dialogs.openBinaryFile('Import Chart - ${prettyFormat}', fileFilter != null ? [fileFilter] : [], function(selectedFile:SelectedFileInfo) {
|
||||
if (selectedFile != null && selectedFile.bytes != null)
|
||||
{
|
||||
trace('Selected file: ' + selectedFile.fullPath);
|
||||
var selectedFileJson:Dynamic = SerializerUtil.fromJSONBytes(selectedFile.bytes);
|
||||
|
@ -1014,7 +1150,7 @@ class ChartEditorDialogHandler
|
|||
* @param state The current chart editor state.
|
||||
* @return The dialog that was opened.
|
||||
*/
|
||||
public static inline function openUserGuideDialog(state:ChartEditorState):Dialog
|
||||
public static inline function openUserGuideDialog(state:ChartEditorState):Null<Dialog>
|
||||
{
|
||||
return openDialog(state, CHART_EDITOR_DIALOG_USER_GUIDE_LAYOUT, true, true);
|
||||
}
|
||||
|
@ -1024,9 +1160,9 @@ class ChartEditorDialogHandler
|
|||
* @param modal Makes the background uninteractable while the dialog is open.
|
||||
* @param closable Hides the close button on the dialog, preventing it from being closed unless the user interacts with the dialog.
|
||||
*/
|
||||
static function openDialog(state:ChartEditorState, key:String, modal:Bool = true, closable:Bool = true):Dialog
|
||||
static function openDialog(state:ChartEditorState, key:String, modal:Bool = true, closable:Bool = true):Null<Dialog>
|
||||
{
|
||||
var dialog:Dialog = cast state.buildComponent(key);
|
||||
var dialog:Null<Dialog> = cast state.buildComponent(key);
|
||||
if (dialog == null) return null;
|
||||
|
||||
dialog.destroyOnClose = true;
|
||||
|
|
|
@ -16,6 +16,7 @@ import funkin.play.song.SongData.SongEventData;
|
|||
* A event sprite that can be used to display a song event in a chart.
|
||||
* Designed to be used and reused efficiently. Has no gameplay functionality.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorEventSprite extends FlxSprite
|
||||
{
|
||||
public static final DEFAULT_EVENT = 'Default';
|
||||
|
@ -45,16 +46,18 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
refresh();
|
||||
}
|
||||
|
||||
static var eventFrames:Null<FlxFramesCollection> = null;
|
||||
|
||||
/**
|
||||
* Build a set of animations to allow displaying different types of chart events.
|
||||
* @param force `true` to force rebuilding the frames.
|
||||
*/
|
||||
static function buildFrames(force:Bool = false):FlxFramesCollection
|
||||
{
|
||||
static var eventFrames:FlxFramesCollection = null;
|
||||
|
||||
if (eventFrames != null && !force) return eventFrames;
|
||||
eventFrames = new FlxAtlasFrames(null);
|
||||
|
||||
initEmptyEventFrames();
|
||||
if (eventFrames == null) throw 'Failed to initialize empty event frames.';
|
||||
|
||||
// Push the default event as a frame.
|
||||
var defaultFrames:FlxAtlasFrames = Paths.getSparrowAtlas('ui/chart-editor/events/$DEFAULT_EVENT');
|
||||
|
@ -83,6 +86,12 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
return eventFrames;
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
static function initEmptyEventFrames():Void
|
||||
{
|
||||
eventFrames = new FlxAtlasFrames(null);
|
||||
}
|
||||
|
||||
function buildAnimations():Void
|
||||
{
|
||||
var eventNames:Array<String> = [DEFAULT_EVENT].concat(SongEventParser.listEventIds());
|
||||
|
@ -133,6 +142,8 @@ class ChartEditorEventSprite extends FlxSprite
|
|||
|
||||
public function updateEventPosition(?origin:FlxObject)
|
||||
{
|
||||
if (this.eventData == null) return;
|
||||
|
||||
this.x = (ChartEditorState.STRUMLINE_SIZE * 2 + 1 - 1) * ChartEditorState.GRID_SIZE;
|
||||
if (this.eventData.stepTime >= 0) this.y = this.eventData.stepTime * ChartEditorState.GRID_SIZE;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ import funkin.play.song.SongData.SongNoteData;
|
|||
* A hold note sprite that can be used to display a note in a chart.
|
||||
* Designed to be used and reused efficiently. Has no gameplay functionality.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorHoldNoteSprite extends SustainTrail
|
||||
{
|
||||
/**
|
||||
|
@ -110,8 +111,10 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
return !aboveViewArea && !belowViewArea;
|
||||
}
|
||||
|
||||
public function updateHoldNotePosition(?origin:FlxObject)
|
||||
public function updateHoldNotePosition(?origin:FlxObject):Void
|
||||
{
|
||||
if (this.noteData == null) return;
|
||||
|
||||
var cursorColumn:Int = this.noteData.data;
|
||||
|
||||
if (cursorColumn < 0) cursorColumn = 0;
|
||||
|
|
|
@ -10,6 +10,7 @@ import flixel.util.FlxSpriteUtil;
|
|||
/**
|
||||
* Handles the note scrollbar preview in the chart editor.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorNotePreview extends FlxSprite
|
||||
{
|
||||
//
|
||||
|
|
|
@ -3,6 +3,8 @@ package funkin.ui.debug.charting;
|
|||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.graphics.frames.FlxFramesCollection;
|
||||
import flixel.graphics.frames.FlxAtlasFrames;
|
||||
import flixel.graphics.frames.FlxFrame;
|
||||
import flixel.graphics.frames.FlxTileFrames;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.play.song.SongData.SongNoteData;
|
||||
|
@ -11,6 +13,7 @@ import funkin.play.song.SongData.SongNoteData;
|
|||
* A note sprite that can be used to display a note in a chart.
|
||||
* Designed to be used and reused efficiently. Has no gameplay functionality.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorNoteSprite extends FlxSprite
|
||||
{
|
||||
/**
|
||||
|
@ -32,7 +35,7 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
/**
|
||||
* The name of the note style currently in use.
|
||||
*/
|
||||
public var noteStyle(get, null):String;
|
||||
public var noteStyle(get, never):String;
|
||||
|
||||
public function new(parent:ChartEditorState)
|
||||
{
|
||||
|
@ -45,6 +48,8 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
initFrameCollection();
|
||||
}
|
||||
|
||||
if (noteFrameCollection == null) throw 'ERROR: Could not initialize note sprite animations.';
|
||||
|
||||
this.frames = noteFrameCollection;
|
||||
|
||||
// Initialize all the animations, not just the one we're going to use immediately,
|
||||
|
@ -77,18 +82,19 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
*/
|
||||
static function initFrameCollection():Void
|
||||
{
|
||||
noteFrameCollection = new FlxFramesCollection(null, ATLAS, null);
|
||||
buildEmptyFrameCollection();
|
||||
if (noteFrameCollection == null) return;
|
||||
|
||||
// TODO: Automatically iterate over the list of note skins.
|
||||
|
||||
// Normal notes
|
||||
var frameCollectionNormal = Paths.getSparrowAtlas('NOTE_assets');
|
||||
var frameCollectionNormal:FlxAtlasFrames = Paths.getSparrowAtlas('NOTE_assets');
|
||||
|
||||
for (frame in frameCollectionNormal.frames)
|
||||
{
|
||||
noteFrameCollection.pushFrame(frame);
|
||||
}
|
||||
var frameCollectionNormal2 = Paths.getSparrowAtlas('NoteHoldNormal');
|
||||
var frameCollectionNormal2:FlxAtlasFrames = Paths.getSparrowAtlas('NoteHoldNormal');
|
||||
|
||||
for (frame in frameCollectionNormal2.frames)
|
||||
{
|
||||
|
@ -101,13 +107,20 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
var frameCollectionPixel = FlxTileFrames.fromGraphic(graphicPixel, new FlxPoint(17, 17));
|
||||
for (i in 0...frameCollectionPixel.frames.length)
|
||||
{
|
||||
var frame = frameCollectionPixel.frames[i];
|
||||
var frame:Null<FlxFrame> = frameCollectionPixel.frames[i];
|
||||
if (frame == null) continue;
|
||||
|
||||
frame.name = 'pixel' + i;
|
||||
noteFrameCollection.pushFrame(frame);
|
||||
}
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
static function buildEmptyFrameCollection():Void
|
||||
{
|
||||
noteFrameCollection = new FlxFramesCollection(null, ATLAS, null);
|
||||
}
|
||||
|
||||
function set_noteData(value:Null<SongNoteData>):Null<SongNoteData>
|
||||
{
|
||||
this.noteData = value;
|
||||
|
@ -178,6 +191,8 @@ class ChartEditorNoteSprite extends FlxSprite
|
|||
|
||||
public function playNoteAnimation():Void
|
||||
{
|
||||
if (this.noteData == null) return;
|
||||
|
||||
// Decide whether to display a note or a sustain.
|
||||
var baseAnimationName:String = 'tap';
|
||||
|
||||
|
|
|
@ -1221,6 +1221,9 @@ class ChartEditorState extends HaxeUIState
|
|||
// Set the z-index of the HaxeUI.
|
||||
this.component.zIndex = 100;
|
||||
|
||||
// Show the mouse cursor.
|
||||
Cursor.show();
|
||||
|
||||
fixCamera();
|
||||
|
||||
// Get rid of any music from the previous state.
|
||||
|
@ -1315,9 +1318,13 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
buildNoteGroup();
|
||||
|
||||
gridPlayheadScrollArea = new FlxSprite(gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH,
|
||||
MENU_BAR_HEIGHT).makeGraphic(PLAYHEAD_SCROLL_AREA_WIDTH, FlxG.height - MENU_BAR_HEIGHT, PLAYHEAD_SCROLL_AREA_COLOR);
|
||||
gridPlayheadScrollArea = new FlxSprite(0, 0);
|
||||
gridPlayheadScrollArea.makeGraphic(10, 10, PLAYHEAD_SCROLL_AREA_COLOR); // Make it 10x10px and then scale it as needed.
|
||||
add(gridPlayheadScrollArea);
|
||||
gridPlayheadScrollArea.setGraphicSize(PLAYHEAD_SCROLL_AREA_WIDTH, 3000);
|
||||
gridPlayheadScrollArea.updateHitbox();
|
||||
gridPlayheadScrollArea.x = gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH;
|
||||
gridPlayheadScrollArea.y = MENU_BAR_HEIGHT + GRID_TOP_PAD;
|
||||
gridPlayheadScrollArea.zIndex = 25;
|
||||
|
||||
// The playhead that show the current position in the song.
|
||||
|
@ -1878,14 +1885,21 @@ class ChartEditorState extends HaxeUIState
|
|||
**/
|
||||
function handleScrollKeybinds():Void
|
||||
{
|
||||
// Don't scroll when the cursor is over the UI.
|
||||
if (isCursorOverHaxeUI) return;
|
||||
// Don't scroll when the cursor is over the UI, unless a playbar button (the << >> ones) is pressed.
|
||||
if (isCursorOverHaxeUI && playbarButtonPressed == null) return;
|
||||
|
||||
var scrollAmount:Float = 0; // Amount to scroll the grid.
|
||||
var playheadAmount:Float = 0; // Amount to scroll the playhead relative to the grid.
|
||||
var shouldPause:Bool = false; // Whether to pause the song when scrolling.
|
||||
var shouldEase:Bool = false; // Whether to ease the scroll.
|
||||
|
||||
// Mouse Wheel = Scroll
|
||||
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
scrollAmount = -10 * FlxG.mouse.wheel;
|
||||
shouldPause = true;
|
||||
}
|
||||
|
||||
// Up Arrow = Scroll Up
|
||||
if (upKeyHandler.activated && currentLiveInputStyle != LiveInputStyle.WASD)
|
||||
{
|
||||
|
@ -1903,13 +1917,15 @@ class ChartEditorState extends HaxeUIState
|
|||
if (pageUpKeyHandler.activated)
|
||||
{
|
||||
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
||||
var targetScrollPosition:Float = Math.floor(scrollPositionInPixels / measureHeight) * measureHeight;
|
||||
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
||||
var targetScrollPosition:Float = Math.floor(playheadPos / measureHeight) * measureHeight;
|
||||
// If we would move less than one grid, instead move to the top of the previous measure.
|
||||
if (Math.abs(targetScrollPosition - scrollPositionInPixels) < GRID_SIZE)
|
||||
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
||||
if (targetScrollAmount < GRID_SIZE)
|
||||
{
|
||||
targetScrollPosition -= GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
||||
targetScrollPosition -= GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure;
|
||||
}
|
||||
scrollAmount = targetScrollPosition - scrollPositionInPixels;
|
||||
scrollAmount = targetScrollPosition - playheadPos;
|
||||
|
||||
shouldPause = true;
|
||||
}
|
||||
|
@ -1924,13 +1940,15 @@ class ChartEditorState extends HaxeUIState
|
|||
if (pageDownKeyHandler.activated)
|
||||
{
|
||||
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
||||
var targetScrollPosition:Float = Math.ceil(scrollPositionInPixels / measureHeight) * measureHeight;
|
||||
var playheadPos:Float = scrollPositionInPixels + playheadPositionInPixels;
|
||||
var targetScrollPosition:Float = Math.ceil(playheadPos / measureHeight) * measureHeight;
|
||||
// If we would move less than one grid, instead move to the top of the next measure.
|
||||
if (Math.abs(targetScrollPosition - scrollPositionInPixels) < GRID_SIZE)
|
||||
var targetScrollAmount = Math.abs(targetScrollPosition - playheadPos);
|
||||
if (targetScrollAmount < GRID_SIZE)
|
||||
{
|
||||
targetScrollPosition += GRID_SIZE * 4 * Conductor.beatsPerMeasure;
|
||||
targetScrollPosition += GRID_SIZE * Constants.STEPS_PER_BEAT * Conductor.beatsPerMeasure;
|
||||
}
|
||||
scrollAmount = targetScrollPosition - scrollPositionInPixels;
|
||||
scrollAmount = targetScrollPosition - playheadPos;
|
||||
|
||||
shouldPause = true;
|
||||
}
|
||||
|
@ -1941,13 +1959,6 @@ class ChartEditorState extends HaxeUIState
|
|||
shouldPause = true;
|
||||
}
|
||||
|
||||
// Mouse Wheel = Scroll
|
||||
if (FlxG.mouse.wheel != 0 && !FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
scrollAmount = -10 * FlxG.mouse.wheel;
|
||||
shouldPause = true;
|
||||
}
|
||||
|
||||
// Middle Mouse + Drag = Scroll but move the playhead the same amount.
|
||||
if (FlxG.mouse.pressedMiddle)
|
||||
{
|
||||
|
@ -2060,29 +2071,11 @@ class ChartEditorState extends HaxeUIState
|
|||
if (FlxG.keys.justPressed.LEFT)
|
||||
{
|
||||
noteSnapQuantIndex--;
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
title: 'Note Snapping',
|
||||
body: 'Updated note snapping to 1/${noteSnapQuant}',
|
||||
type: NotificationType.Success,
|
||||
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||
});
|
||||
#end
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.RIGHT)
|
||||
{
|
||||
noteSnapQuantIndex++;
|
||||
#if !mac
|
||||
NotificationManager.instance.addNotification(
|
||||
{
|
||||
title: 'Note Snapping',
|
||||
body: 'Updated note snapping to 1/${noteSnapQuant}',
|
||||
type: NotificationType.Success,
|
||||
expiryMs: ChartEditorState.NOTIFICATION_DISMISS_TIME
|
||||
});
|
||||
#end
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2097,6 +2090,11 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
if (shouldHandleCursor)
|
||||
{
|
||||
// Over the course of this big conditional block,
|
||||
// we determine what the cursor should look like,
|
||||
// and fall back to the default cursor if none of the conditions are met.
|
||||
var targetCursorMode:Null<CursorMode> = null;
|
||||
|
||||
if (gridTiledSprite == null) throw "ERROR: Tried to handle cursor, but gridTiledSprite is null! Check ChartEditorState.buildGrid()";
|
||||
|
||||
var overlapsGrid:Bool = FlxG.mouse.overlaps(gridTiledSprite);
|
||||
|
@ -2106,9 +2104,9 @@ class ChartEditorState extends HaxeUIState
|
|||
var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y;
|
||||
|
||||
var overlapsSelectionBorder:Bool = overlapsGrid
|
||||
&& (cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2)
|
||||
&& ((cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2)
|
||||
|| (cursorX % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2))
|
||||
|| (cursorY % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) || (cursorY % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2));
|
||||
|| (cursorY % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) || (cursorY % 40) > (40 - (GRID_SELECTION_BORDER_WIDTH / 2)));
|
||||
|
||||
if (FlxG.mouse.justPressed)
|
||||
{
|
||||
|
@ -2124,6 +2122,8 @@ class ChartEditorState extends HaxeUIState
|
|||
else if (!overlapsGrid || overlapsSelectionBorder)
|
||||
{
|
||||
selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
|
||||
// Drawing selection box.
|
||||
targetCursorMode = Crosshair;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2134,23 +2134,6 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
}
|
||||
|
||||
if (gridPlayheadScrollAreaPressed)
|
||||
{
|
||||
Cursor.cursorMode = Grabbing;
|
||||
}
|
||||
else if (notePreviewScrollAreaStartPos != null)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
else if (gridPlayheadScrollArea != null && FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
else
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
|
||||
if (gridPlayheadScrollAreaPressed && FlxG.mouse.released)
|
||||
{
|
||||
gridPlayheadScrollAreaPressed = false;
|
||||
|
@ -2167,6 +2150,9 @@ class ChartEditorState extends HaxeUIState
|
|||
// Move the playhead to the cursor position.
|
||||
this.playheadPositionInPixels = FlxG.mouse.screenY - MENU_BAR_HEIGHT - GRID_TOP_PAD;
|
||||
moveSongToScrollPosition();
|
||||
|
||||
// Cursor should be a grabby hand.
|
||||
if (targetCursorMode == null) targetCursorMode = Grabbing;
|
||||
}
|
||||
|
||||
// The song position of the cursor, in steps.
|
||||
|
@ -2325,6 +2311,8 @@ class ChartEditorState extends HaxeUIState
|
|||
selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x);
|
||||
selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y);
|
||||
setSelectionBoxBounds(selectionRect);
|
||||
|
||||
targetCursorMode = Crosshair;
|
||||
}
|
||||
}
|
||||
else if (FlxG.mouse.justReleased)
|
||||
|
@ -2416,7 +2404,9 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
else if (notePreviewScrollAreaStartPos != null)
|
||||
{
|
||||
trace('Updating current song time while clicking and holding...');
|
||||
// Player is clicking and holding on note preview to scrub around.
|
||||
targetCursorMode = Grabbing;
|
||||
|
||||
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.screenY, (notePreview?.y ?? 0.0),
|
||||
(notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels);
|
||||
|
||||
|
@ -2586,8 +2576,6 @@ class ChartEditorState extends HaxeUIState
|
|||
// Handle grid cursor.
|
||||
if (overlapsGrid && !overlapsSelectionBorder && !gridPlayheadScrollAreaPressed)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
|
||||
// Indicate that we can place a note here.
|
||||
|
||||
if (cursorColumn == eventColumn)
|
||||
|
@ -2608,6 +2596,8 @@ class ChartEditorState extends HaxeUIState
|
|||
gridGhostEvent.visible = true;
|
||||
gridGhostEvent.eventData = eventData;
|
||||
gridGhostEvent.updateEventPosition(renderedEvents);
|
||||
|
||||
targetCursorMode = Cell;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -2628,32 +2618,58 @@ class ChartEditorState extends HaxeUIState
|
|||
gridGhostNote.visible = true;
|
||||
gridGhostNote.noteData = noteData;
|
||||
gridGhostNote.updateNotePosition(renderedNotes);
|
||||
}
|
||||
|
||||
// gridCursor.visible = true;
|
||||
// // X and Y are the cursor position relative to the grid, snapped to the top left of the grid square.
|
||||
// gridCursor.x = Math.floor(cursorX / GRID_SIZE) * GRID_SIZE + gridTiledSprite.x + (GRID_SELECTION_BORDER_WIDTH / 2);
|
||||
// gridCursor.y = cursorStep * GRID_SIZE + gridTiledSprite.y + (GRID_SELECTION_BORDER_WIDTH / 2);
|
||||
targetCursorMode = Cell;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gridGhostNote != null) gridGhostNote.visible = false;
|
||||
if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false;
|
||||
if (gridGhostEvent != null) gridGhostEvent.visible = false;
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
||||
|
||||
if (targetCursorMode == null)
|
||||
{
|
||||
if (FlxG.mouse.pressed)
|
||||
{
|
||||
if (overlapsSelectionBorder)
|
||||
{
|
||||
targetCursorMode = Crosshair;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (FlxG.mouse.overlaps(notePreview))
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (FlxG.mouse.overlaps(gridPlayheadScrollArea))
|
||||
{
|
||||
targetCursorMode = Pointer;
|
||||
}
|
||||
else if (overlapsSelectionBorder)
|
||||
{
|
||||
targetCursorMode = Crosshair;
|
||||
}
|
||||
else if (overlapsGrid)
|
||||
{
|
||||
targetCursorMode = Cell;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Actually set the cursor mode to the one we specified earlier.
|
||||
Cursor.cursorMode = targetCursorMode ?? Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gridGhostNote != null) gridGhostNote.visible = false;
|
||||
if (gridGhostHoldNote != null) gridGhostHoldNote.visible = false;
|
||||
if (gridGhostEvent != null) gridGhostEvent.visible = false;
|
||||
}
|
||||
|
||||
if (isCursorOverHaxeUIButton && Cursor.cursorMode == Default)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
// Do not set Cursor.cursorMode here, because it will be set by the HaxeUI.
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2705,7 +2721,7 @@ class ChartEditorState extends HaxeUIState
|
|||
var displayedHoldNoteData:Array<SongNoteData> = [];
|
||||
for (holdNoteSprite in renderedHoldNotes.members)
|
||||
{
|
||||
if (holdNoteSprite == null || !holdNoteSprite.exists || !holdNoteSprite.visible) continue;
|
||||
if (holdNoteSprite == null || holdNoteSprite.noteData == null || !holdNoteSprite.exists || !holdNoteSprite.visible) continue;
|
||||
|
||||
if (!holdNoteSprite.isHoldNoteVisible(FlxG.height - MENU_BAR_HEIGHT, GRID_TOP_PAD))
|
||||
{
|
||||
|
@ -2781,7 +2797,8 @@ class ChartEditorState extends HaxeUIState
|
|||
// The note sprite handles animation playback and positioning.
|
||||
noteSprite.noteData = noteData;
|
||||
|
||||
// Setting note data resets position relative to the grid so we fix that.
|
||||
// Setting note data resets the position relative to the group!
|
||||
// If we don't update the note position AFTER setting the note data, the note will be rendered offscreen at y=5000.
|
||||
noteSprite.updateNotePosition(renderedNotes);
|
||||
|
||||
// Add hold notes that are now visible (and not already displayed).
|
||||
|
@ -2899,10 +2916,10 @@ class ChartEditorState extends HaxeUIState
|
|||
}
|
||||
|
||||
// Sort the notes DESCENDING. This keeps the sustain behind the associated note.
|
||||
renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING);
|
||||
renderedNotes.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort()
|
||||
|
||||
// Sort the events DESCENDING. This keeps the sustain behind the associated note.
|
||||
renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING);
|
||||
renderedEvents.sort(FlxSort.byY, FlxSort.DESCENDING); // TODO: .group.insertionSort()
|
||||
}
|
||||
|
||||
// Add a debug value which displays the current size of the note pool.
|
||||
|
@ -2960,6 +2977,18 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
function handleFileKeybinds():Void
|
||||
{
|
||||
// CTRL + N = New Chart
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.N)
|
||||
{
|
||||
ChartEditorDialogHandler.openWelcomeDialog(this, true);
|
||||
}
|
||||
|
||||
// CTRL + O = Open Chart
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.O)
|
||||
{
|
||||
ChartEditorDialogHandler.openBrowseWizard(this, true);
|
||||
}
|
||||
|
||||
// CTRL + Q = Quit to Menu
|
||||
if (FlxG.keys.pressed.CONTROL && FlxG.keys.justPressed.Q)
|
||||
{
|
||||
|
@ -3188,7 +3217,8 @@ class ChartEditorState extends HaxeUIState
|
|||
difficultySelectDirty = false;
|
||||
|
||||
// Manage the Select Difficulty tree view.
|
||||
var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return;
|
||||
|
||||
var treeView:Null<TreeView> = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
if (treeView == null) return;
|
||||
|
@ -3234,7 +3264,7 @@ class ChartEditorState extends HaxeUIState
|
|||
if (treeView == null)
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return;
|
||||
|
||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
|
@ -3248,7 +3278,7 @@ class ChartEditorState extends HaxeUIState
|
|||
function handlePlayerPreviewToolbox():Void
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var charPreviewToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
var charPlayer:Null<CharacterPlayer> = charPreviewToolbox.findComponent('charPlayer');
|
||||
|
@ -3283,7 +3313,7 @@ class ChartEditorState extends HaxeUIState
|
|||
function handleOpponentPreviewToolbox():Void
|
||||
{
|
||||
// Manage the Select Difficulty tree view.
|
||||
var charPreviewToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
var charPreviewToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
if (charPreviewToolbox == null) return;
|
||||
|
||||
var charPlayer:Null<CharacterPlayer> = charPreviewToolbox.findComponent('charPlayer');
|
||||
|
@ -3355,7 +3385,7 @@ class ChartEditorState extends HaxeUIState
|
|||
{
|
||||
if (treeView == null)
|
||||
{
|
||||
var difficultyToolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
var difficultyToolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
if (difficultyToolbox == null) return null;
|
||||
|
||||
treeView = difficultyToolbox.findComponent('difficultyToolboxTree');
|
||||
|
@ -3418,7 +3448,8 @@ class ChartEditorState extends HaxeUIState
|
|||
*/
|
||||
function refreshSongMetadataToolbox():Void
|
||||
{
|
||||
var toolbox:CollapsibleDialog = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
var toolbox:Null<CollapsibleDialog> = ChartEditorToolboxHandler.getToolbox(this, CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
if (toolbox == null) return;
|
||||
|
||||
var inputSongName:Null<TextField> = toolbox.findComponent('inputSongName', TextField);
|
||||
if (inputSongName != null) inputSongName.value = currentSongMetadata.songName;
|
||||
|
@ -3746,25 +3777,29 @@ class ChartEditorState extends HaxeUIState
|
|||
this.scrollPositionInPixels = value;
|
||||
|
||||
// Move the grid sprite to the correct position.
|
||||
if (isViewDownscroll)
|
||||
if (gridTiledSprite != null)
|
||||
{
|
||||
if (gridTiledSprite != null) gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gridTiledSprite != null) gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
|
||||
if (isViewDownscroll)
|
||||
{
|
||||
gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
|
||||
gridPlayheadScrollArea.y = gridTiledSprite.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
|
||||
gridPlayheadScrollArea.y = gridTiledSprite.y;
|
||||
}
|
||||
}
|
||||
|
||||
// Move the rendered notes to the correct position.
|
||||
renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
|
||||
renderedHoldNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
|
||||
renderedEvents.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
|
||||
renderedSelectionSquares.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
|
||||
|
||||
// Offset the selection box start position, if we are dragging.
|
||||
if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff;
|
||||
// Update the note preview viewport box.
|
||||
setNotePreviewViewportBounds(calculateNotePreviewViewportBounds());
|
||||
|
||||
return this.scrollPositionInPixels;
|
||||
}
|
||||
|
||||
|
@ -3923,6 +3958,11 @@ class ChartEditorState extends HaxeUIState
|
|||
songLengthInMs = audioInstTrack.length;
|
||||
|
||||
if (gridTiledSprite != null) gridTiledSprite.height = songLengthInPixels;
|
||||
if (gridPlayheadScrollArea != null)
|
||||
{
|
||||
gridPlayheadScrollArea.setGraphicSize(Std.int(gridPlayheadScrollArea.width), songLengthInPixels);
|
||||
gridPlayheadScrollArea.updateHitbox();
|
||||
}
|
||||
|
||||
buildSpectrogram(audioInstTrack);
|
||||
}
|
||||
|
@ -4209,10 +4249,12 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
function sortChartData():Void
|
||||
{
|
||||
// TODO: .insertionSort()
|
||||
currentSongChartNoteData.sort(function(a:SongNoteData, b:SongNoteData):Int {
|
||||
return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time);
|
||||
});
|
||||
|
||||
// TODO: .insertionSort()
|
||||
currentSongChartEventData.sort(function(a:SongEventData, b:SongEventData):Int {
|
||||
return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time);
|
||||
});
|
||||
|
@ -4260,6 +4302,9 @@ class ChartEditorState extends HaxeUIState
|
|||
|
||||
cleanupAutoSave();
|
||||
|
||||
// Hide the mouse cursor on other states.
|
||||
Cursor.hide();
|
||||
|
||||
@:privateAccess
|
||||
ChartEditorNoteSprite.noteFrameCollection = null;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ enum ChartEditorTheme
|
|||
/**
|
||||
* Static functions which handle building themed UI elements for a provided ChartEditorState.
|
||||
*/
|
||||
@:nullSafety
|
||||
class ChartEditorThemeHandler
|
||||
{
|
||||
// TODO: There's probably a better system of organization for these colors.
|
||||
|
@ -50,6 +51,11 @@ class ChartEditorThemeHandler
|
|||
static final GRID_MEASURE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||
static final GRID_MEASURE_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH;
|
||||
|
||||
// Horizontal divider between beats.
|
||||
static final GRID_BEAT_DIVIDER_COLOR_LIGHT:FlxColor = 0xFFC1C1C1;
|
||||
static final GRID_BEAT_DIVIDER_COLOR_DARK:FlxColor = 0xFF848484;
|
||||
static final GRID_BEAT_DIVIDER_WIDTH:Float = ChartEditorState.GRID_SELECTION_BORDER_WIDTH;
|
||||
|
||||
// Border on the square highlighting selected notes.
|
||||
static final SELECTION_SQUARE_BORDER_COLOR_LIGHT:FlxColor = 0xFF339933;
|
||||
static final SELECTION_SQUARE_BORDER_COLOR_DARK:FlxColor = 0xFF339933;
|
||||
|
@ -92,6 +98,7 @@ class ChartEditorThemeHandler
|
|||
*/
|
||||
static function updateBackground(state:ChartEditorState):Void
|
||||
{
|
||||
if (state.menuBG == null) return;
|
||||
state.menuBG.color = switch (state.currentTheme)
|
||||
{
|
||||
case ChartEditorTheme.Light: BACKGROUND_COLOR_LIGHT;
|
||||
|
@ -141,7 +148,7 @@ class ChartEditorThemeHandler
|
|||
ChartEditorState.GRID_SELECTION_BORDER_WIDTH),
|
||||
selectionBorderColor);
|
||||
|
||||
// Selection borders in the middle.
|
||||
// Selection borders horizontally along the middle.
|
||||
for (i in 1...(Conductor.stepsPerMeasure))
|
||||
{
|
||||
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2),
|
||||
|
@ -159,7 +166,7 @@ class ChartEditorThemeHandler
|
|||
state.gridBitmap.height),
|
||||
selectionBorderColor);
|
||||
|
||||
// Selection borders across the middle.
|
||||
// Selection borders vertically along the middle.
|
||||
for (i in 1...TOTAL_COLUMN_COUNT)
|
||||
{
|
||||
state.gridBitmap.fillRect(new Rectangle((ChartEditorState.GRID_SIZE * i) - (ChartEditorState.GRID_SELECTION_BORDER_WIDTH / 2), 0,
|
||||
|
@ -172,7 +179,7 @@ class ChartEditorThemeHandler
|
|||
ChartEditorState.GRID_SELECTION_BORDER_WIDTH, state.gridBitmap.height),
|
||||
selectionBorderColor);
|
||||
|
||||
// Draw dividers between the measures.
|
||||
// Draw horizontal dividers between the measures.
|
||||
|
||||
var gridMeasureDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
|
@ -187,7 +194,30 @@ class ChartEditorThemeHandler
|
|||
var dividerLineBY:Float = state.gridBitmap.height - (GRID_MEASURE_DIVIDER_WIDTH / 2);
|
||||
state.gridBitmap.fillRect(new Rectangle(0, dividerLineBY, state.gridBitmap.width, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor);
|
||||
|
||||
// Draw dividers between the strumlines.
|
||||
// Draw horizontal dividers between the beats.
|
||||
|
||||
var gridBeatDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
case Light: GRID_BEAT_DIVIDER_COLOR_LIGHT;
|
||||
case Dark: GRID_BEAT_DIVIDER_COLOR_DARK;
|
||||
default: GRID_BEAT_DIVIDER_COLOR_LIGHT;
|
||||
};
|
||||
|
||||
// Selection borders horizontally in the middle.
|
||||
for (i in 1...(Conductor.stepsPerMeasure))
|
||||
{
|
||||
if ((i % Conductor.beatsPerMeasure) == 0)
|
||||
{
|
||||
state.gridBitmap.fillRect(new Rectangle(0, (ChartEditorState.GRID_SIZE * i) - (GRID_BEAT_DIVIDER_WIDTH / 2), state.gridBitmap.width,
|
||||
GRID_BEAT_DIVIDER_WIDTH),
|
||||
gridBeatDividerColor);
|
||||
}
|
||||
}
|
||||
|
||||
// Divider at top
|
||||
state.gridBitmap.fillRect(new Rectangle(0, 0, state.gridBitmap.width, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor);
|
||||
|
||||
// Draw vertical dividers between the strumlines.
|
||||
|
||||
var gridStrumlineDividerColor:FlxColor = switch (state.currentTheme)
|
||||
{
|
||||
|
|
|
@ -39,6 +39,7 @@ enum ChartEditorToolMode
|
|||
/**
|
||||
* Static functions which handle building themed UI elements for a provided ChartEditorState.
|
||||
*/
|
||||
@:nullSafety
|
||||
@:allow(funkin.ui.debug.charting.ChartEditorState)
|
||||
class ChartEditorToolboxHandler
|
||||
{
|
||||
|
@ -56,7 +57,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
public static function showToolbox(state:ChartEditorState, id:String):Void
|
||||
{
|
||||
var toolbox:CollapsibleDialog = state.activeToolboxes.get(id);
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) toolbox = initToolbox(state, id);
|
||||
|
||||
|
@ -95,7 +96,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
public static function hideToolbox(state:ChartEditorState, id:String):Void
|
||||
{
|
||||
var toolbox:CollapsibleDialog = state.activeToolboxes.get(id);
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) toolbox = initToolbox(state, id);
|
||||
|
||||
|
@ -134,7 +135,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
public static function minimizeToolbox(state:ChartEditorState, id:String):Void
|
||||
{
|
||||
var toolbox:CollapsibleDialog = state.activeToolboxes.get(id);
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) return;
|
||||
|
||||
|
@ -143,16 +144,16 @@ class ChartEditorToolboxHandler
|
|||
|
||||
public static function maximizeToolbox(state:ChartEditorState, id:String):Void
|
||||
{
|
||||
var toolbox:CollapsibleDialog = state.activeToolboxes.get(id);
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
if (toolbox == null) return;
|
||||
|
||||
toolbox.minimized = false;
|
||||
}
|
||||
|
||||
public static function initToolbox(state:ChartEditorState, id:String):CollapsibleDialog
|
||||
public static function initToolbox(state:ChartEditorState, id:String):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = null;
|
||||
var toolbox:Null<CollapsibleDialog> = null;
|
||||
switch (id)
|
||||
{
|
||||
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
||||
|
@ -193,9 +194,9 @@ class ChartEditorToolboxHandler
|
|||
* @param id The asset ID of the toolbox layout.
|
||||
* @return The toolbox.
|
||||
*/
|
||||
public static function getToolbox(state:ChartEditorState, id:String):CollapsibleDialog
|
||||
public static function getToolbox(state:ChartEditorState, id:String):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = state.activeToolboxes.get(id);
|
||||
var toolbox:Null<CollapsibleDialog> = state.activeToolboxes.get(id);
|
||||
|
||||
// Initialize the toolbox without showing it.
|
||||
if (toolbox == null) toolbox = initToolbox(state, id);
|
||||
|
@ -205,7 +206,7 @@ class ChartEditorToolboxHandler
|
|||
return toolbox;
|
||||
}
|
||||
|
||||
static function buildToolboxToolsLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxToolsLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT);
|
||||
|
||||
|
@ -219,7 +220,8 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxTools', false);
|
||||
}
|
||||
|
||||
var toolsGroup:Group = toolbox.findComponent('toolboxToolsGroup', Group);
|
||||
var toolsGroup:Null<Group> = toolbox.findComponent('toolboxToolsGroup', Group);
|
||||
if (toolsGroup == null) throw 'ChartEditorToolboxHandler.buildToolboxToolsLayout() - Could not find toolboxToolsGroup component.';
|
||||
|
||||
if (toolsGroup == null) return null;
|
||||
|
||||
|
@ -242,7 +244,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxTools(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxNoteDataLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxNoteDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT);
|
||||
|
||||
|
@ -256,9 +258,13 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false);
|
||||
}
|
||||
|
||||
var toolboxNotesNoteKind:DropDown = toolbox.findComponent('toolboxNotesNoteKind', DropDown);
|
||||
var toolboxNotesCustomKindLabel:Label = toolbox.findComponent('toolboxNotesCustomKindLabel', Label);
|
||||
var toolboxNotesCustomKind:TextField = toolbox.findComponent('toolboxNotesCustomKind', TextField);
|
||||
var toolboxNotesNoteKind:Null<DropDown> = toolbox.findComponent('toolboxNotesNoteKind', DropDown);
|
||||
if (toolboxNotesNoteKind == null) throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesNoteKind component.';
|
||||
var toolboxNotesCustomKindLabel:Null<Label> = toolbox.findComponent('toolboxNotesCustomKindLabel', Label);
|
||||
if (toolboxNotesCustomKindLabel == null)
|
||||
throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesCustomKindLabel component.';
|
||||
var toolboxNotesCustomKind:Null<TextField> = toolbox.findComponent('toolboxNotesCustomKind', TextField);
|
||||
if (toolboxNotesCustomKind == null) throw 'ChartEditorToolboxHandler.buildToolboxNoteDataLayout() - Could not find toolboxNotesCustomKind component.';
|
||||
|
||||
toolboxNotesNoteKind.onChange = function(event:UIEvent) {
|
||||
var isCustom:Bool = (event.data.id == '~CUSTOM~');
|
||||
|
@ -290,7 +296,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxNoteData(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxEventDataLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxEventDataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT);
|
||||
|
||||
|
@ -304,8 +310,10 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false);
|
||||
}
|
||||
|
||||
var toolboxEventsEventKind:DropDown = toolbox.findComponent('toolboxEventsEventKind', DropDown);
|
||||
var toolboxEventsDataGrid:Grid = toolbox.findComponent('toolboxEventsDataGrid', Grid);
|
||||
var toolboxEventsEventKind:Null<DropDown> = toolbox.findComponent('toolboxEventsEventKind', DropDown);
|
||||
if (toolboxEventsEventKind == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsEventKind component.';
|
||||
var toolboxEventsDataGrid:Null<Grid> = toolbox.findComponent('toolboxEventsDataGrid', Grid);
|
||||
if (toolboxEventsDataGrid == null) throw 'ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Could not find toolboxEventsDataGrid component.';
|
||||
|
||||
toolboxEventsEventKind.dataSource = new ArrayDataSource();
|
||||
|
||||
|
@ -349,6 +357,8 @@ class ChartEditorToolboxHandler
|
|||
|
||||
for (field in schema)
|
||||
{
|
||||
if (field == null) continue;
|
||||
|
||||
// Add a label.
|
||||
var label:Label = new Label();
|
||||
label.text = field.title;
|
||||
|
@ -360,33 +370,36 @@ class ChartEditorToolboxHandler
|
|||
case INTEGER:
|
||||
var numberStepper:NumberStepper = new NumberStepper();
|
||||
numberStepper.id = field.name;
|
||||
numberStepper.step = field.step == null ? 1.0 : field.step;
|
||||
numberStepper.min = field.min;
|
||||
numberStepper.max = field.max;
|
||||
numberStepper.value = field.defaultValue;
|
||||
numberStepper.step = field.step ?? 1.0;
|
||||
numberStepper.min = field.min ?? 0.0;
|
||||
numberStepper.max = field.max ?? 10.0;
|
||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||
input = numberStepper;
|
||||
case FLOAT:
|
||||
var numberStepper:NumberStepper = new NumberStepper();
|
||||
numberStepper.id = field.name;
|
||||
numberStepper.step = field.step == null ? 0.1 : field.step;
|
||||
numberStepper.min = field.min;
|
||||
numberStepper.max = field.max;
|
||||
numberStepper.value = field.defaultValue;
|
||||
numberStepper.step = field.step ?? 0.1;
|
||||
numberStepper.min = field.min ?? 0.0;
|
||||
numberStepper.max = field.max ?? 1.0;
|
||||
if (field.defaultValue != null) numberStepper.value = field.defaultValue;
|
||||
input = numberStepper;
|
||||
case BOOL:
|
||||
var checkBox:CheckBox = new CheckBox();
|
||||
checkBox.id = field.name;
|
||||
checkBox.selected = field.defaultValue;
|
||||
if (field.defaultValue != null) checkBox.selected = field.defaultValue;
|
||||
input = checkBox;
|
||||
case ENUM:
|
||||
var dropDown:DropDown = new DropDown();
|
||||
dropDown.id = field.name;
|
||||
dropDown.dataSource = new ArrayDataSource();
|
||||
|
||||
if (field.keys == null) throw 'Field "${field.name}" is of Enum type but has no keys.';
|
||||
|
||||
// Add entries to the dropdown.
|
||||
|
||||
for (optionName in field.keys.keys())
|
||||
{
|
||||
var optionValue:String = field.keys.get(optionName);
|
||||
var optionValue:Null<String> = field.keys.get(optionName);
|
||||
trace('$optionName : $optionValue');
|
||||
dropDown.dataSource.add({value: optionValue, text: optionName});
|
||||
}
|
||||
|
@ -397,7 +410,7 @@ class ChartEditorToolboxHandler
|
|||
case STRING:
|
||||
input = new TextField();
|
||||
input.id = field.name;
|
||||
input.text = field.defaultValue;
|
||||
if (field.defaultValue != null) input.text = field.defaultValue;
|
||||
default:
|
||||
// Unknown type. Display a label so we know what it is.
|
||||
input = new Label();
|
||||
|
@ -417,7 +430,7 @@ class ChartEditorToolboxHandler
|
|||
}
|
||||
}
|
||||
|
||||
static function buildToolboxDifficultyLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxDifficultyLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_DIFFICULTY_LAYOUT);
|
||||
|
||||
|
@ -431,11 +444,20 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false);
|
||||
}
|
||||
|
||||
var difficultyToolboxSaveMetadata:Button = toolbox.findComponent('difficultyToolboxSaveMetadata', Button);
|
||||
var difficultyToolboxSaveChart:Button = toolbox.findComponent('difficultyToolboxSaveChart', Button);
|
||||
var difficultyToolboxSaveAll:Button = toolbox.findComponent('difficultyToolboxSaveAll', Button);
|
||||
var difficultyToolboxLoadMetadata:Button = toolbox.findComponent('difficultyToolboxLoadMetadata', Button);
|
||||
var difficultyToolboxLoadChart:Button = toolbox.findComponent('difficultyToolboxLoadChart', Button);
|
||||
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.';
|
||||
|
||||
difficultyToolboxSaveMetadata.onClick = function(event:UIEvent) {
|
||||
SongSerializer.exportSongMetadata(state.currentSongMetadata, state.currentSongId);
|
||||
|
@ -472,16 +494,18 @@ class ChartEditorToolboxHandler
|
|||
static function onShowToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void
|
||||
{
|
||||
// Update the selected difficulty when reopening the toolbox.
|
||||
var treeView:TreeView = toolbox.findComponent('difficultyToolboxTree');
|
||||
var treeView:Null<TreeView> = toolbox.findComponent('difficultyToolboxTree');
|
||||
if (treeView == null) return;
|
||||
|
||||
treeView.selectedNode = state.getCurrentTreeDifficultyNode(treeView);
|
||||
var current = state.getCurrentTreeDifficultyNode(treeView);
|
||||
if (current == null) return;
|
||||
treeView.selectedNode = current;
|
||||
trace('selected node: ${treeView.selectedNode}');
|
||||
}
|
||||
|
||||
static function onHideToolboxDifficulty(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxMetadataLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxMetadataLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_METADATA_LAYOUT);
|
||||
|
||||
|
@ -495,7 +519,8 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false);
|
||||
}
|
||||
|
||||
var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField);
|
||||
var inputSongName:Null<TextField> = toolbox.findComponent('inputSongName', TextField);
|
||||
if (inputSongName == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongName component.';
|
||||
inputSongName.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
|
@ -506,12 +531,13 @@ class ChartEditorToolboxHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.songName = null;
|
||||
state.currentSongMetadata.songName = '';
|
||||
}
|
||||
};
|
||||
inputSongName.value = state.currentSongMetadata.songName;
|
||||
|
||||
var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField);
|
||||
var inputSongArtist:Null<TextField> = toolbox.findComponent('inputSongArtist', TextField);
|
||||
if (inputSongArtist == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputSongArtist component.';
|
||||
inputSongArtist.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
|
@ -522,12 +548,13 @@ class ChartEditorToolboxHandler
|
|||
}
|
||||
else
|
||||
{
|
||||
state.currentSongMetadata.artist = null;
|
||||
state.currentSongMetadata.artist = '';
|
||||
}
|
||||
};
|
||||
inputSongArtist.value = state.currentSongMetadata.artist;
|
||||
|
||||
var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown);
|
||||
var inputStage:Null<DropDown> = toolbox.findComponent('inputStage', DropDown);
|
||||
if (inputStage == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputStage component.';
|
||||
inputStage.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.data != null && event.data.id != null;
|
||||
|
||||
|
@ -538,14 +565,16 @@ class ChartEditorToolboxHandler
|
|||
};
|
||||
inputStage.value = state.currentSongMetadata.playData.stage;
|
||||
|
||||
var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown);
|
||||
var inputNoteSkin:Null<DropDown> = toolbox.findComponent('inputNoteSkin', DropDown);
|
||||
if (inputNoteSkin == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputNoteSkin component.';
|
||||
inputNoteSkin.onChange = function(event:UIEvent) {
|
||||
if ((event?.data?.id ?? null) == null) return;
|
||||
state.currentSongMetadata.playData.noteSkin = event.data.id;
|
||||
};
|
||||
inputNoteSkin.value = state.currentSongMetadata.playData.noteSkin;
|
||||
|
||||
var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper);
|
||||
var inputBPM:Null<NumberStepper> = toolbox.findComponent('inputBPM', NumberStepper);
|
||||
if (inputBPM == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputBPM component.';
|
||||
inputBPM.onChange = function(event:UIEvent) {
|
||||
if (event.value == null || event.value <= 0) return;
|
||||
|
||||
|
@ -565,9 +594,11 @@ class ChartEditorToolboxHandler
|
|||
};
|
||||
inputBPM.value = state.currentSongMetadata.timeChanges[0].bpm;
|
||||
|
||||
var labelScrollSpeed:Label = toolbox.findComponent('labelScrollSpeed', Label);
|
||||
var labelScrollSpeed:Null<Label> = toolbox.findComponent('labelScrollSpeed', Label);
|
||||
if (labelScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find labelScrollSpeed component.';
|
||||
|
||||
var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider);
|
||||
var inputScrollSpeed:Null<Slider> = toolbox.findComponent('inputScrollSpeed', Slider);
|
||||
if (inputScrollSpeed == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find inputScrollSpeed component.';
|
||||
inputScrollSpeed.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.value != null && event.target.value > 0;
|
||||
|
||||
|
@ -585,10 +616,12 @@ class ChartEditorToolboxHandler
|
|||
inputScrollSpeed.value = state.currentSongChartScrollSpeed;
|
||||
labelScrollSpeed.text = 'Scroll Speed: ${state.currentSongChartScrollSpeed}x';
|
||||
|
||||
var frameVariation:Frame = toolbox.findComponent('frameVariation', Frame);
|
||||
var frameVariation:Null<Frame> = toolbox.findComponent('frameVariation', Frame);
|
||||
if (frameVariation == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameVariation component.';
|
||||
frameVariation.text = 'Variation: ${state.selectedVariation.toTitleCase()}';
|
||||
|
||||
var frameDifficulty:Frame = toolbox.findComponent('frameDifficulty', Frame);
|
||||
var frameDifficulty:Null<Frame> = toolbox.findComponent('frameDifficulty', Frame);
|
||||
if (frameDifficulty == null) throw 'ChartEditorToolboxHandler.buildToolboxMetadataLayout() - Could not find frameDifficulty component.';
|
||||
frameDifficulty.text = 'Difficulty: ${state.selectedDifficulty.toTitleCase()}';
|
||||
|
||||
return toolbox;
|
||||
|
@ -601,7 +634,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxMetadata(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxCharactersLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxCharactersLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_CHARACTERS_LAYOUT);
|
||||
|
||||
|
@ -622,7 +655,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxCharacters(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxPlayerPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_PLAYER_PREVIEW_LAYOUT);
|
||||
|
||||
|
@ -636,7 +669,8 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false);
|
||||
}
|
||||
|
||||
var charPlayer:CharacterPlayer = toolbox.findComponent('charPlayer');
|
||||
var charPlayer:Null<CharacterPlayer> = toolbox.findComponent('charPlayer');
|
||||
if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxPlayerPreviewLayout() - Could not find charPlayer component.';
|
||||
// TODO: We need to implement character swapping in ChartEditorState.
|
||||
charPlayer.loadCharacter('bf');
|
||||
charPlayer.characterType = CharacterType.BF;
|
||||
|
@ -650,7 +684,7 @@ class ChartEditorToolboxHandler
|
|||
|
||||
static function onHideToolboxPlayerPreview(state:ChartEditorState, toolbox:CollapsibleDialog):Void {}
|
||||
|
||||
static function buildToolboxOpponentPreviewLayout(state:ChartEditorState):CollapsibleDialog
|
||||
static function buildToolboxOpponentPreviewLayout(state:ChartEditorState):Null<CollapsibleDialog>
|
||||
{
|
||||
var toolbox:CollapsibleDialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_OPPONENT_PREVIEW_LAYOUT);
|
||||
|
||||
|
@ -664,7 +698,8 @@ class ChartEditorToolboxHandler
|
|||
state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false);
|
||||
}
|
||||
|
||||
var charPlayer:CharacterPlayer = toolbox.findComponent('charPlayer');
|
||||
var charPlayer:Null<CharacterPlayer> = toolbox.findComponent('charPlayer');
|
||||
if (charPlayer == null) throw 'ChartEditorToolboxHandler.buildToolboxOpponentPreviewLayout() - Could not find charPlayer component.';
|
||||
// TODO: We need to implement character swapping in ChartEditorState.
|
||||
charPlayer.loadCharacter('dad');
|
||||
charPlayer.characterType = CharacterType.DAD;
|
||||
|
|
|
@ -30,7 +30,7 @@ typedef AnimationInfo =
|
|||
@:composite(Layout)
|
||||
class CharacterPlayer extends Box
|
||||
{
|
||||
var character:BaseCharacter;
|
||||
var character:Null<BaseCharacter>;
|
||||
|
||||
public function new(defaultToBf:Bool = true)
|
||||
{
|
||||
|
@ -47,7 +47,7 @@ class CharacterPlayer extends Box
|
|||
|
||||
function get_charId():String
|
||||
{
|
||||
return character.characterId;
|
||||
return character?.characterId ?? '';
|
||||
}
|
||||
|
||||
function set_charId(value:String):String
|
||||
|
@ -56,11 +56,11 @@ class CharacterPlayer extends Box
|
|||
return value;
|
||||
}
|
||||
|
||||
public var charName(get, null):String;
|
||||
public var charName(get, never):String;
|
||||
|
||||
function get_charName():String
|
||||
{
|
||||
return character.characterName;
|
||||
return character?.characterName ?? "Unknown";
|
||||
}
|
||||
|
||||
// possible haxeui bug: if listener is added after event is dispatched, event is "lost"... is it smart to "collect and redispatch"? Not sure
|
||||
|
@ -86,7 +86,11 @@ class CharacterPlayer extends Box
|
|||
|
||||
// Prevent script issues by fetching with debug=true.
|
||||
var newCharacter:BaseCharacter = CharacterDataParser.fetchCharacter(id, true);
|
||||
if (newCharacter == null) return; // Fail if character doesn't exist.
|
||||
if (newCharacter == null)
|
||||
{
|
||||
character = null;
|
||||
return; // Fail if character doesn't exist.
|
||||
}
|
||||
|
||||
// Assign character.
|
||||
character = newCharacter;
|
||||
|
|
30
source/funkin/ui/haxeui/components/FunkinButton.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinButton.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import haxe.ui.components.Button;
|
||||
|
||||
/**
|
||||
* A HaxeUI button which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinButton extends Button
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
30
source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinHorizontalSlider.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import haxe.ui.components.HorizontalSlider;
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
|
||||
/**
|
||||
* A HaxeUI horizontal slider which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinHorizontalSlider extends HorizontalSlider
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
30
source/funkin/ui/haxeui/components/FunkinLink.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinLink.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import haxe.ui.components.Link;
|
||||
|
||||
/**
|
||||
* A HaxeUI link which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinLink extends Link
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
32
source/funkin/ui/haxeui/components/FunkinMenuBar.hx
Normal file
32
source/funkin/ui/haxeui/components/FunkinMenuBar.hx
Normal file
|
@ -0,0 +1,32 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import haxe.ui.containers.menus.MenuBar;
|
||||
import haxe.ui.core.CompositeBuilder;
|
||||
|
||||
/**
|
||||
* A HaxeUI menu bar which:
|
||||
* - Changes the current cursor when each button is hovered over.
|
||||
*/
|
||||
class FunkinMenuBar extends MenuBar
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
registerListeners();
|
||||
}
|
||||
|
||||
private function registerListeners():Void {}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
30
source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinMenuCheckBox.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import haxe.ui.containers.menus.MenuCheckBox;
|
||||
|
||||
/**
|
||||
* A HaxeUI menu checkbox which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinMenuCheckBox extends MenuCheckBox
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
30
source/funkin/ui/haxeui/components/FunkinMenuItem.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinMenuItem.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import haxe.ui.containers.menus.MenuItem;
|
||||
|
||||
/**
|
||||
* A HaxeUI menu item which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinMenuItem extends MenuItem
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
30
source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx
Normal file
30
source/funkin/ui/haxeui/components/FunkinMenuOptionBox.hx
Normal file
|
@ -0,0 +1,30 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import haxe.ui.containers.menus.MenuOptionBox;
|
||||
import funkin.input.Cursor;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
|
||||
/**
|
||||
* A HaxeUI menu option box which:
|
||||
* - Changes the current cursor when hovered over.
|
||||
*/
|
||||
class FunkinMenuOptionBox extends MenuOptionBox
|
||||
{
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
this.onMouseOver = handleMouseOver;
|
||||
this.onMouseOut = handleMouseOut;
|
||||
}
|
||||
|
||||
private function handleMouseOver(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Pointer;
|
||||
}
|
||||
|
||||
private function handleMouseOut(event:MouseEvent)
|
||||
{
|
||||
Cursor.cursorMode = Default;
|
||||
}
|
||||
}
|
|
@ -98,19 +98,40 @@ class Level implements IRegistryEntry<LevelData>
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a sprite for the background of the level.
|
||||
* Can be overriden by ScriptedLevel. Not used if `isBackgroundSimple` returns true.
|
||||
*/
|
||||
public function buildBackground():FlxSprite
|
||||
{
|
||||
if (_data.background.startsWith('#'))
|
||||
{
|
||||
// Color specified
|
||||
var color:FlxColor = FlxColor.fromString(_data.background);
|
||||
return new FlxSprite().makeGraphic(FlxG.width, 400, color);
|
||||
}
|
||||
else
|
||||
if (!_data.background.startsWith('#'))
|
||||
{
|
||||
// Image specified
|
||||
return new FlxSprite().loadGraphic(Paths.image(_data.background));
|
||||
}
|
||||
|
||||
// Color specified
|
||||
var result:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 400, FlxColor.WHITE);
|
||||
result.color = getBackgroundColor();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the background is a solid color.
|
||||
* If you have a ScriptedLevel with a fancy background, you may want to override this to false.
|
||||
*/
|
||||
public function isBackgroundSimple():Bool
|
||||
{
|
||||
return _data.background.startsWith('#');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the background is a solid color.
|
||||
* If you have a ScriptedLevel with a fancy background, you may want to override this to false.
|
||||
*/
|
||||
public function getBackgroundColor():FlxColor
|
||||
{
|
||||
return FlxColor.fromString(_data.background);
|
||||
}
|
||||
|
||||
public function getDifficulties():Array<String>
|
||||
|
|
|
@ -135,10 +135,15 @@ class StoryMenuState extends MusicBeatState
|
|||
this.bgColor = FlxColor.BLACK;
|
||||
|
||||
levelTitles = new FlxTypedGroup<LevelTitle>();
|
||||
levelTitles.zIndex = 15;
|
||||
add(levelTitles);
|
||||
|
||||
updateBackground();
|
||||
|
||||
var black:FlxSprite = new FlxSprite(levelBackground.x, 0).makeGraphic(FlxG.width, Std.int(400 + levelBackground.y), FlxColor.BLACK);
|
||||
black.zIndex = levelBackground.zIndex - 1;
|
||||
add(black);
|
||||
|
||||
levelProps = new FlxTypedGroup<LevelProp>();
|
||||
levelProps.zIndex = 1000;
|
||||
add(levelProps);
|
||||
|
@ -153,17 +158,20 @@ class StoryMenuState extends MusicBeatState
|
|||
|
||||
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
|
||||
scoreText.setFormat("VCR OSD Mono", 32);
|
||||
scoreText.zIndex = 1000;
|
||||
add(scoreText);
|
||||
|
||||
modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]');
|
||||
modeText.setFormat("VCR OSD Mono", 32);
|
||||
modeText.screenCenter(X);
|
||||
modeText.visible = hasModdedLevels();
|
||||
modeText.zIndex = 1000;
|
||||
add(modeText);
|
||||
|
||||
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
|
||||
levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
|
||||
levelTitleText.alpha = 0.7;
|
||||
levelTitleText.zIndex = 1000;
|
||||
add(levelTitleText);
|
||||
|
||||
buildLevelTitles();
|
||||
|
@ -384,6 +392,7 @@ class StoryMenuState extends MusicBeatState
|
|||
if (currentIndex < 0) currentIndex = levelList.length - 1;
|
||||
if (currentIndex >= levelList.length) currentIndex = 0;
|
||||
|
||||
var previousLevelId:String = currentLevelId;
|
||||
currentLevelId = levelList[currentIndex];
|
||||
|
||||
updateData();
|
||||
|
@ -399,18 +408,14 @@ class StoryMenuState extends MusicBeatState
|
|||
currentLevelTitle = item;
|
||||
item.alpha = 1.0;
|
||||
}
|
||||
else if (index > currentIndex)
|
||||
{
|
||||
item.alpha = 0.6;
|
||||
}
|
||||
else
|
||||
{
|
||||
item.alpha = 0.0;
|
||||
item.alpha = 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
updateText();
|
||||
updateBackground();
|
||||
updateBackground(previousLevelId);
|
||||
updateProps();
|
||||
refresh();
|
||||
}
|
||||
|
@ -533,32 +538,66 @@ class StoryMenuState extends MusicBeatState
|
|||
});
|
||||
}
|
||||
|
||||
function updateBackground():Void
|
||||
function updateBackground(?previousLevelId:String = ''):Void
|
||||
{
|
||||
if (levelBackground != null)
|
||||
if (levelBackground == null || previousLevelId == '')
|
||||
{
|
||||
var oldBackground:FlxSprite = levelBackground;
|
||||
|
||||
FlxTween.tween(oldBackground, {alpha: 0.0}, 0.6,
|
||||
{
|
||||
ease: FlxEase.linear,
|
||||
onComplete: function(_) {
|
||||
remove(oldBackground);
|
||||
}
|
||||
});
|
||||
// Build a new background and display it immediately.
|
||||
levelBackground = currentLevel.buildBackground();
|
||||
levelBackground.x = 0;
|
||||
levelBackground.y = 56;
|
||||
levelBackground.zIndex = 100;
|
||||
levelBackground.alpha = 1.0; // Not hidden.
|
||||
add(levelBackground);
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousLevel = LevelRegistry.instance.fetchEntry(previousLevelId);
|
||||
|
||||
levelBackground = currentLevel.buildBackground();
|
||||
levelBackground.x = 0;
|
||||
levelBackground.y = 56;
|
||||
levelBackground.alpha = 0.0;
|
||||
levelBackground.zIndex = 100;
|
||||
add(levelBackground);
|
||||
|
||||
FlxTween.tween(levelBackground, {alpha: 1.0}, 0.6,
|
||||
if (currentLevel.isBackgroundSimple() && previousLevel.isBackgroundSimple())
|
||||
{
|
||||
ease: FlxEase.linear
|
||||
});
|
||||
var previousColor:FlxColor = previousLevel.getBackgroundColor();
|
||||
var currentColor:FlxColor = currentLevel.getBackgroundColor();
|
||||
if (previousColor != currentColor)
|
||||
{
|
||||
// Both the previous and current level were simple backgrounds.
|
||||
// Fade between colors directly, rather than fading one background out and another in.
|
||||
FlxTween.color(levelBackground, 0.4, previousColor, currentColor);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Do no fade at all if the colors aren't different.
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Either the previous or current level has a complex background.
|
||||
// We need to fade the old background out and the new one in.
|
||||
|
||||
// Reference the old background and fade it out.
|
||||
var oldBackground:FlxSprite = levelBackground;
|
||||
FlxTween.tween(oldBackground, {alpha: 0.0}, 0.6,
|
||||
{
|
||||
ease: FlxEase.linear,
|
||||
onComplete: function(_) {
|
||||
remove(oldBackground);
|
||||
}
|
||||
});
|
||||
|
||||
// Build a new background and fade it in.
|
||||
levelBackground = currentLevel.buildBackground();
|
||||
levelBackground.x = 0;
|
||||
levelBackground.y = 56;
|
||||
levelBackground.alpha = 0.0; // Hidden to start.
|
||||
levelBackground.zIndex = 100;
|
||||
add(levelBackground);
|
||||
|
||||
FlxTween.tween(levelBackground, {alpha: 1.0}, 0.6,
|
||||
{
|
||||
ease: FlxEase.linear
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateProps():Void
|
||||
|
|
|
@ -20,7 +20,7 @@ class Constants
|
|||
* The current version number of the game.
|
||||
* Modify this in the `project.xml` file.
|
||||
*/
|
||||
public static var VERSION(get, null):String;
|
||||
public static var VERSION(get, never):String;
|
||||
|
||||
/**
|
||||
* A suffix to add to the game version.
|
||||
|
|
|
@ -240,6 +240,10 @@ class FileUtil
|
|||
onSaveAll(paths);
|
||||
}
|
||||
|
||||
trace('Browsing for directory to save individual files to...');
|
||||
#if mac
|
||||
defaultPath = null;
|
||||
#end
|
||||
browseForDirectory(null, onSelectDir, onCancel, defaultPath, 'Choose directory to save all files to...');
|
||||
|
||||
return true;
|
||||
|
|
Loading…
Reference in a new issue