Merge branch 'master' into feature/chart-editor-file-keybinds

This commit is contained in:
Cameron Taylor 2023-09-12 22:37:37 -04:00 committed by GitHub
commit ff34771ca3
14 changed files with 715 additions and 119 deletions

View file

@ -4,9 +4,34 @@ import openfl.utils.Assets;
import lime.app.Future; import lime.app.Future;
import openfl.display.BitmapData; import openfl.display.BitmapData;
@:nullSafety
class Cursor 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 = static final CURSOR_DEFAULT_PARAMS:CursorParams =
{ {
@ -15,7 +40,7 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorDefault:BitmapData = null; static var assetCursorDefault:Null<BitmapData> = null;
static final CURSOR_CROSS_PARAMS:CursorParams = static final CURSOR_CROSS_PARAMS:CursorParams =
{ {
@ -24,7 +49,7 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorCross:BitmapData = null; static var assetCursorCross:Null<BitmapData> = null;
static final CURSOR_ERASER_PARAMS:CursorParams = static final CURSOR_ERASER_PARAMS:CursorParams =
{ {
@ -33,16 +58,16 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorEraser:BitmapData = null; static var assetCursorEraser:Null<BitmapData> = null;
static final CURSOR_GRABBING_PARAMS:CursorParams = static final CURSOR_GRABBING_PARAMS:CursorParams =
{ {
graphic: "assets/images/cursor/cursor-grabbing.png", graphic: "assets/images/cursor/cursor-grabbing.png",
scale: 1.0, scale: 1.0,
offsetX: 32, offsetX: -8,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorGrabbing:BitmapData = null; static var assetCursorGrabbing:Null<BitmapData> = null;
static final CURSOR_HOURGLASS_PARAMS:CursorParams = static final CURSOR_HOURGLASS_PARAMS:CursorParams =
{ {
@ -51,25 +76,34 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorHourglass:BitmapData = null; static var assetCursorHourglass:Null<BitmapData> = null;
static final CURSOR_POINTER_PARAMS:CursorParams = static final CURSOR_POINTER_PARAMS:CursorParams =
{ {
graphic: "assets/images/cursor/cursor-pointer.png", graphic: "assets/images/cursor/cursor-pointer.png",
scale: 1.0, scale: 1.0,
offsetX: 8, offsetX: -8,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorPointer:BitmapData = null; static var assetCursorPointer:Null<BitmapData> = null;
static final CURSOR_TEXT_PARAMS:CursorParams = static final CURSOR_TEXT_PARAMS:CursorParams =
{ {
graphic: "assets/images/cursor/cursor-text.png", graphic: "assets/images/cursor/cursor-text.png",
scale: 1.0, scale: 0.2,
offsetX: 0, offsetX: 0,
offsetY: 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 = static final CURSOR_ZOOM_IN_PARAMS:CursorParams =
{ {
@ -78,7 +112,7 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 0, offsetY: 0,
}; };
static var assetCursorZoomIn:BitmapData = null; static var assetCursorZoomIn:Null<BitmapData> = null;
static final CURSOR_ZOOM_OUT_PARAMS:CursorParams = static final CURSOR_ZOOM_OUT_PARAMS:CursorParams =
{ {
@ -87,11 +121,36 @@ class Cursor
offsetX: 0, offsetX: 0,
offsetY: 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; cursorMode = value;
setCursorGraphic(cursorMode); setCursorGraphic(cursorMode);
@ -99,16 +158,9 @@ class Cursor
return cursorMode; return cursorMode;
} }
public static inline function show():Void /**
{ * Synchronous.
FlxG.mouse.visible = true; */
}
public static inline function hide():Void
{
FlxG.mouse.visible = false;
}
static function setCursorGraphic(?value:CursorMode = null):Void static function setCursorGraphic(?value:CursorMode = null):Void
{ {
if (value == null) if (value == null)
@ -117,6 +169,156 @@ class Cursor
return; 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) switch (value)
{ {
case Default: case Default:
@ -127,6 +329,7 @@ class Cursor
assetCursorDefault = bitmapData; assetCursorDefault = bitmapData;
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
}); });
future.onError(onCursorError.bind(Default));
} }
else else
{ {
@ -141,6 +344,7 @@ class Cursor
assetCursorCross = bitmapData; assetCursorCross = bitmapData;
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
}); });
future.onError(onCursorError.bind(Cross));
} }
else else
{ {
@ -155,6 +359,7 @@ class Cursor
assetCursorEraser = bitmapData; assetCursorEraser = bitmapData;
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
}); });
future.onError(onCursorError.bind(Eraser));
} }
else else
{ {
@ -169,6 +374,7 @@ class Cursor
assetCursorGrabbing = bitmapData; assetCursorGrabbing = bitmapData;
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
}); });
future.onError(onCursorError.bind(Grabbing));
} }
else else
{ {
@ -183,6 +389,7 @@ class Cursor
assetCursorHourglass = bitmapData; assetCursorHourglass = bitmapData;
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
}); });
future.onError(onCursorError.bind(Hourglass));
} }
else else
{ {
@ -197,6 +404,7 @@ class Cursor
assetCursorPointer = bitmapData; assetCursorPointer = bitmapData;
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
}); });
future.onError(onCursorError.bind(Pointer));
} }
else else
{ {
@ -211,6 +419,7 @@ class Cursor
assetCursorText = bitmapData; assetCursorText = bitmapData;
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
}); });
future.onError(onCursorError.bind(Text));
} }
else else
{ {
@ -225,6 +434,7 @@ class Cursor
assetCursorZoomIn = bitmapData; assetCursorZoomIn = bitmapData;
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
}); });
future.onError(onCursorError.bind(ZoomIn));
} }
else else
{ {
@ -239,14 +449,45 @@ class Cursor
assetCursorZoomOut = bitmapData; assetCursorZoomOut = bitmapData;
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
}); });
future.onError(onCursorError.bind(ZoomOut));
} }
else else
{ {
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); 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: default:
setCursorGraphic(null); loadCursorGraphic(null);
} }
} }
@ -254,6 +495,11 @@ class Cursor
{ {
FlxG.mouse.load(graphic, params.scale, params.offsetX, params.offsetY); 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 // https://developer.mozilla.org/en-US/docs/Web/CSS/cursor
@ -268,6 +514,8 @@ enum CursorMode
Text; Text;
ZoomIn; ZoomIn;
ZoomOut; ZoomOut;
Crosshair;
Cell;
} }
/** /**

View file

@ -179,7 +179,7 @@ class SongMigrator
songMetadata.playData.playableChars = {}; songMetadata.playData.playableChars = {};
try 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) catch (e)
{ {

View file

@ -6,6 +6,7 @@ import funkin.util.SerializerUtil;
import funkin.play.song.SongData.SongChartData; import funkin.play.song.SongData.SongChartData;
import funkin.play.song.SongData.SongMetadata; import funkin.play.song.SongData.SongMetadata;
import flixel.util.FlxTimer; import flixel.util.FlxTimer;
import funkin.ui.haxeui.components.FunkinLink;
import funkin.util.SortUtil; import funkin.util.SortUtil;
import funkin.input.Cursor; import funkin.input.Cursor;
import funkin.play.character.BaseCharacter; import funkin.play.character.BaseCharacter;
@ -122,7 +123,7 @@ class ChartEditorDialogHandler
trace('[WARN] Could not fetch song name for ${targetSongId}'); trace('[WARN] Could not fetch song name for ${targetSongId}');
} }
var linkTemplateSong:Link = new Link(); var linkTemplateSong:Link = new FunkinLink();
linkTemplateSong.text = songName; linkTemplateSong.text = songName;
linkTemplateSong.onClick = function(_event) { linkTemplateSong.onClick = function(_event) {
dialog.hideDialog(DialogButton.CANCEL); dialog.hideDialog(DialogButton.CANCEL);
@ -742,6 +743,14 @@ class ChartEditorDialogHandler
var songVariationMetadataEntryLabel:Label = songVariationMetadataEntry.findComponent('chartEntryLabel', Label); var songVariationMetadataEntryLabel:Label = songVariationMetadataEntry.findComponent('chartEntryLabel', Label);
songVariationMetadataEntryLabel.text = 'Drag and drop <song>-metadata-${variation}.json file, or click to browse.'; songVariationMetadataEntryLabel.text = 'Drag and drop <song>-metadata-${variation}.json file, or click to browse.';
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); songVariationMetadataEntry.onClick = onClickMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel);
addDropHandler(songVariationMetadataEntry, onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel)); addDropHandler(songVariationMetadataEntry, onDropFileMetadataVariation.bind(variation).bind(songVariationMetadataEntryLabel));
chartContainerB.addComponent(songVariationMetadataEntry); chartContainerB.addComponent(songVariationMetadataEntry);
@ -751,6 +760,14 @@ class ChartEditorDialogHandler
var songVariationChartDataEntryLabel:Label = songVariationChartDataEntry.findComponent('chartEntryLabel', Label); var songVariationChartDataEntryLabel:Label = songVariationChartDataEntry.findComponent('chartEntryLabel', Label);
songVariationChartDataEntryLabel.text = 'Drag and drop <song>-chart-${variation}.json file, or click to browse.'; songVariationChartDataEntryLabel.text = 'Drag and drop <song>-chart-${variation}.json file, or click to browse.';
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); songVariationChartDataEntry.onClick = onClickChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel);
addDropHandler(songVariationChartDataEntry, onDropFileChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel)); addDropHandler(songVariationChartDataEntry, onDropFileChartDataVariation.bind(variation).bind(songVariationChartDataEntryLabel));
chartContainerB.addComponent(songVariationChartDataEntry); chartContainerB.addComponent(songVariationChartDataEntry);
@ -885,6 +902,14 @@ class ChartEditorDialogHandler
metadataEntry.onClick = onClickMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel); metadataEntry.onClick = onClickMetadataVariation.bind(Constants.DEFAULT_VARIATION).bind(metadataEntryLabel);
addDropHandler(metadataEntry, onDropFileMetadataVariation.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); chartContainerA.addComponent(metadataEntry);
@ -928,7 +953,6 @@ class ChartEditorDialogHandler
importBox.swapClass('upload-bg', 'upload-bg-hover'); importBox.swapClass('upload-bg', 'upload-bg-hover');
Cursor.cursorMode = Pointer; Cursor.cursorMode = Pointer;
} }
importBox.onMouseOut = function(_event) { importBox.onMouseOut = function(_event) {
importBox.swapClass('upload-bg-hover', 'upload-bg'); importBox.swapClass('upload-bg-hover', 'upload-bg');
Cursor.cursorMode = Default; Cursor.cursorMode = Default;

View file

@ -1195,6 +1195,9 @@ class ChartEditorState extends HaxeUIState
// Set the z-index of the HaxeUI. // Set the z-index of the HaxeUI.
this.component.zIndex = 100; this.component.zIndex = 100;
// Show the mouse cursor.
Cursor.show();
fixCamera(); fixCamera();
// Get rid of any music from the previous state. // Get rid of any music from the previous state.
@ -1282,9 +1285,13 @@ class ChartEditorState extends HaxeUIState
buildNoteGroup(); buildNoteGroup();
gridPlayheadScrollArea = new FlxSprite(gridTiledSprite.x - PLAYHEAD_SCROLL_AREA_WIDTH, gridPlayheadScrollArea = new FlxSprite(0, 0);
MENU_BAR_HEIGHT).makeGraphic(PLAYHEAD_SCROLL_AREA_WIDTH, FlxG.height - MENU_BAR_HEIGHT, PLAYHEAD_SCROLL_AREA_COLOR); gridPlayheadScrollArea.makeGraphic(10, 10, PLAYHEAD_SCROLL_AREA_COLOR); // Make it 10x10px and then scale it as needed.
add(gridPlayheadScrollArea); 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; gridPlayheadScrollArea.zIndex = 25;
// The playhead that show the current position in the song. // The playhead that show the current position in the song.
@ -1870,13 +1877,15 @@ class ChartEditorState extends HaxeUIState
if (pageUpKeyHandler.activated) if (pageUpKeyHandler.activated)
{ {
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure; 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 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; shouldPause = true;
} }
@ -1891,13 +1900,15 @@ class ChartEditorState extends HaxeUIState
if (pageDownKeyHandler.activated) if (pageDownKeyHandler.activated)
{ {
var measureHeight:Float = GRID_SIZE * 4 * Conductor.beatsPerMeasure; 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 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; shouldPause = true;
} }
@ -2046,6 +2057,11 @@ class ChartEditorState extends HaxeUIState
if (shouldHandleCursor) 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()"; if (gridTiledSprite == null) throw "ERROR: Tried to handle cursor, but gridTiledSprite is null! Check ChartEditorState.buildGrid()";
var overlapsGrid:Bool = FlxG.mouse.overlaps(gridTiledSprite); var overlapsGrid:Bool = FlxG.mouse.overlaps(gridTiledSprite);
@ -2055,9 +2071,9 @@ class ChartEditorState extends HaxeUIState
var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y; var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y;
var overlapsSelectionBorder:Bool = overlapsGrid 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)) || (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) if (FlxG.mouse.justPressed)
{ {
@ -2073,6 +2089,8 @@ class ChartEditorState extends HaxeUIState
else if (!overlapsGrid || overlapsSelectionBorder) else if (!overlapsGrid || overlapsSelectionBorder)
{ {
selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY);
// Drawing selection box.
targetCursorMode = Crosshair;
} }
else else
{ {
@ -2083,23 +2101,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) if (gridPlayheadScrollAreaPressed && FlxG.mouse.released)
{ {
gridPlayheadScrollAreaPressed = false; gridPlayheadScrollAreaPressed = false;
@ -2116,6 +2117,9 @@ class ChartEditorState extends HaxeUIState
// Move the playhead to the cursor position. // Move the playhead to the cursor position.
this.playheadPositionInPixels = FlxG.mouse.screenY - MENU_BAR_HEIGHT - GRID_TOP_PAD; this.playheadPositionInPixels = FlxG.mouse.screenY - MENU_BAR_HEIGHT - GRID_TOP_PAD;
moveSongToScrollPosition(); moveSongToScrollPosition();
// Cursor should be a grabby hand.
if (targetCursorMode == null) targetCursorMode = Grabbing;
} }
// The song position of the cursor, in steps. // The song position of the cursor, in steps.
@ -2268,6 +2272,8 @@ class ChartEditorState extends HaxeUIState
selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x); selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x);
selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y); selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y);
setSelectionBoxBounds(selectionRect); setSelectionBoxBounds(selectionRect);
targetCursorMode = Crosshair;
} }
} }
else if (FlxG.mouse.justReleased) else if (FlxG.mouse.justReleased)
@ -2359,7 +2365,9 @@ class ChartEditorState extends HaxeUIState
} }
else if (notePreviewScrollAreaStartPos != null) 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), var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.screenY, (notePreview?.y ?? 0.0),
(notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels); (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels);
@ -2527,8 +2535,6 @@ class ChartEditorState extends HaxeUIState
// Handle grid cursor. // Handle grid cursor.
if (overlapsGrid && !overlapsSelectionBorder && !gridPlayheadScrollAreaPressed) if (overlapsGrid && !overlapsSelectionBorder && !gridPlayheadScrollAreaPressed)
{ {
Cursor.cursorMode = Pointer;
// Indicate that we can place a note here. // Indicate that we can place a note here.
if (cursorColumn == eventColumn) if (cursorColumn == eventColumn)
@ -2548,6 +2554,8 @@ class ChartEditorState extends HaxeUIState
gridGhostEvent.visible = true; gridGhostEvent.visible = true;
gridGhostEvent.eventData = eventData; gridGhostEvent.eventData = eventData;
gridGhostEvent.updateEventPosition(renderedEvents); gridGhostEvent.updateEventPosition(renderedEvents);
targetCursorMode = Cell;
} }
else else
{ {
@ -2569,30 +2577,56 @@ class ChartEditorState extends HaxeUIState
gridGhostNote.visible = true; gridGhostNote.visible = true;
gridGhostNote.noteData = noteData; gridGhostNote.noteData = noteData;
gridGhostNote.updateNotePosition(renderedNotes); gridGhostNote.updateNotePosition(renderedNotes);
}
// gridCursor.visible = true; targetCursorMode = Cell;
// // 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);
} }
else else
{ {
if (gridGhostNote != null) gridGhostNote.visible = false; if (gridGhostNote != null) gridGhostNote.visible = false;
if (gridGhostEvent != null) gridGhostEvent.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 else
{ {
if (gridGhostNote != null) gridGhostNote.visible = false; if (gridGhostNote != null) gridGhostNote.visible = false;
if (gridGhostEvent != null) gridGhostEvent.visible = false; if (gridGhostEvent != null) gridGhostEvent.visible = false;
}
if (isCursorOverHaxeUIButton && Cursor.cursorMode == Default) // Do not set Cursor.cursorMode here, because it will be set by the HaxeUI.
{
Cursor.cursorMode = Pointer;
} }
} }
@ -2838,10 +2872,10 @@ class ChartEditorState extends HaxeUIState
} }
// Sort the notes DESCENDING. This keeps the sustain behind the associated note. // 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. // 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. // Add a debug value which displays the current size of the note pool.
@ -3697,25 +3731,29 @@ class ChartEditorState extends HaxeUIState
this.scrollPositionInPixels = value; this.scrollPositionInPixels = value;
// Move the grid sprite to the correct position. // 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); if (isViewDownscroll)
} {
else gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
{ gridPlayheadScrollArea.y = gridTiledSprite.y;
if (gridTiledSprite != null) gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD); }
else
{
gridTiledSprite.y = -scrollPositionInPixels + (MENU_BAR_HEIGHT + GRID_TOP_PAD);
gridPlayheadScrollArea.y = gridTiledSprite.y;
}
} }
// Move the rendered notes to the correct position. // Move the rendered notes to the correct position.
renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0); renderedNotes.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
renderedHoldNotes.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); renderedEvents.setPosition(gridTiledSprite?.x ?? 0.0, gridTiledSprite?.y ?? 0.0);
renderedSelectionSquares.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. // Offset the selection box start position, if we are dragging.
if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff; if (selectionBoxStartPos != null) selectionBoxStartPos.y -= diff;
// Update the note preview viewport box. // Update the note preview viewport box.
setNotePreviewViewportBounds(calculateNotePreviewViewportBounds()); setNotePreviewViewportBounds(calculateNotePreviewViewportBounds());
return this.scrollPositionInPixels; return this.scrollPositionInPixels;
} }
@ -3874,6 +3912,11 @@ class ChartEditorState extends HaxeUIState
songLengthInMs = audioInstTrack.length; songLengthInMs = audioInstTrack.length;
if (gridTiledSprite != null) gridTiledSprite.height = songLengthInPixels; if (gridTiledSprite != null) gridTiledSprite.height = songLengthInPixels;
if (gridPlayheadScrollArea != null)
{
gridPlayheadScrollArea.setGraphicSize(Std.int(gridPlayheadScrollArea.width), songLengthInPixels);
gridPlayheadScrollArea.updateHitbox();
}
buildSpectrogram(audioInstTrack); buildSpectrogram(audioInstTrack);
} }
@ -4158,10 +4201,12 @@ class ChartEditorState extends HaxeUIState
function sortChartData():Void function sortChartData():Void
{ {
// TODO: .insertionSort()
currentSongChartNoteData.sort(function(a:SongNoteData, b:SongNoteData):Int { currentSongChartNoteData.sort(function(a:SongNoteData, b:SongNoteData):Int {
return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time); return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time);
}); });
// TODO: .insertionSort()
currentSongChartEventData.sort(function(a:SongEventData, b:SongEventData):Int { currentSongChartEventData.sort(function(a:SongEventData, b:SongEventData):Int {
return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time); return FlxSort.byValues(FlxSort.ASCENDING, a.time, b.time);
}); });
@ -4209,6 +4254,9 @@ class ChartEditorState extends HaxeUIState
cleanupAutoSave(); cleanupAutoSave();
// Hide the mouse cursor on other states.
Cursor.hide();
@:privateAccess @:privateAccess
ChartEditorNoteSprite.noteFrameCollection = null; ChartEditorNoteSprite.noteFrameCollection = null;
} }

View file

@ -30,7 +30,7 @@ typedef AnimationInfo =
@:composite(Layout) @:composite(Layout)
class CharacterPlayer extends Box class CharacterPlayer extends Box
{ {
var character:BaseCharacter; var character:Null<BaseCharacter>;
public function new(defaultToBf:Bool = true) public function new(defaultToBf:Bool = true)
{ {
@ -47,7 +47,7 @@ class CharacterPlayer extends Box
function get_charId():String function get_charId():String
{ {
return character.characterId; return character?.characterId ?? '';
} }
function set_charId(value:String):String function set_charId(value:String):String
@ -60,7 +60,7 @@ class CharacterPlayer extends Box
function get_charName():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 // 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. // Prevent script issues by fetching with debug=true.
var newCharacter:BaseCharacter = CharacterDataParser.fetchCharacter(id, 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. // Assign character.
character = newCharacter; character = newCharacter;

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View 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;
}
}

View file

@ -98,19 +98,40 @@ class Level implements IRegistryEntry<LevelData>
return true; 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 public function buildBackground():FlxSprite
{ {
if (_data.background.startsWith('#')) if (!_data.background.startsWith('#'))
{
// Color specified
var color:FlxColor = FlxColor.fromString(_data.background);
return new FlxSprite().makeGraphic(FlxG.width, 400, color);
}
else
{ {
// Image specified // Image specified
return new FlxSprite().loadGraphic(Paths.image(_data.background)); 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> public function getDifficulties():Array<String>

View file

@ -135,10 +135,15 @@ class StoryMenuState extends MusicBeatState
this.bgColor = FlxColor.BLACK; this.bgColor = FlxColor.BLACK;
levelTitles = new FlxTypedGroup<LevelTitle>(); levelTitles = new FlxTypedGroup<LevelTitle>();
levelTitles.zIndex = 15;
add(levelTitles); add(levelTitles);
updateBackground(); 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 = new FlxTypedGroup<LevelProp>();
levelProps.zIndex = 1000; levelProps.zIndex = 1000;
add(levelProps); add(levelProps);
@ -153,17 +158,20 @@ class StoryMenuState extends MusicBeatState
scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420'); scoreText = new FlxText(10, 10, 0, 'HIGH SCORE: 42069420');
scoreText.setFormat("VCR OSD Mono", 32); scoreText.setFormat("VCR OSD Mono", 32);
scoreText.zIndex = 1000;
add(scoreText); add(scoreText);
modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]'); modeText = new FlxText(10, 10, 0, 'Base Game Levels [TAB to switch]');
modeText.setFormat("VCR OSD Mono", 32); modeText.setFormat("VCR OSD Mono", 32);
modeText.screenCenter(X); modeText.screenCenter(X);
modeText.visible = hasModdedLevels(); modeText.visible = hasModdedLevels();
modeText.zIndex = 1000;
add(modeText); add(modeText);
levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1'); levelTitleText = new FlxText(FlxG.width * 0.7, 10, 0, 'LEVEL 1');
levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT); levelTitleText.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
levelTitleText.alpha = 0.7; levelTitleText.alpha = 0.7;
levelTitleText.zIndex = 1000;
add(levelTitleText); add(levelTitleText);
buildLevelTitles(); buildLevelTitles();
@ -384,6 +392,7 @@ class StoryMenuState extends MusicBeatState
if (currentIndex < 0) currentIndex = levelList.length - 1; if (currentIndex < 0) currentIndex = levelList.length - 1;
if (currentIndex >= levelList.length) currentIndex = 0; if (currentIndex >= levelList.length) currentIndex = 0;
var previousLevelId:String = currentLevelId;
currentLevelId = levelList[currentIndex]; currentLevelId = levelList[currentIndex];
updateData(); updateData();
@ -399,18 +408,14 @@ class StoryMenuState extends MusicBeatState
currentLevelTitle = item; currentLevelTitle = item;
item.alpha = 1.0; item.alpha = 1.0;
} }
else if (index > currentIndex)
{
item.alpha = 0.6;
}
else else
{ {
item.alpha = 0.0; item.alpha = 0.6;
} }
} }
updateText(); updateText();
updateBackground(); updateBackground(previousLevelId);
updateProps(); updateProps();
refresh(); 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; // Build a new background and display it immediately.
levelBackground = currentLevel.buildBackground();
FlxTween.tween(oldBackground, {alpha: 0.0}, 0.6, levelBackground.x = 0;
{ levelBackground.y = 56;
ease: FlxEase.linear, levelBackground.zIndex = 100;
onComplete: function(_) { levelBackground.alpha = 1.0; // Not hidden.
remove(oldBackground); add(levelBackground);
}
});
} }
else
{
var previousLevel = LevelRegistry.instance.fetchEntry(previousLevelId);
levelBackground = currentLevel.buildBackground(); if (currentLevel.isBackgroundSimple() && previousLevel.isBackgroundSimple())
levelBackground.x = 0;
levelBackground.y = 56;
levelBackground.alpha = 0.0;
levelBackground.zIndex = 100;
add(levelBackground);
FlxTween.tween(levelBackground, {alpha: 1.0}, 0.6,
{ {
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 function updateProps():Void