mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-12-02 09:07:28 +00:00
Many improvements to chart system and song format logic
This commit is contained in:
parent
fc4f398157
commit
6e8feecef1
|
|
@ -181,6 +181,7 @@
|
||||||
|
|
||||||
<haxedef name="CAN_OPEN_LINKS" unless="switch" />
|
<haxedef name="CAN_OPEN_LINKS" unless="switch" />
|
||||||
<haxedef name="CAN_CHEAT" if="switch debug" />
|
<haxedef name="CAN_CHEAT" if="switch debug" />
|
||||||
|
<haxedef name="haxeui_no_mouse_reset" />
|
||||||
|
|
||||||
<!-- Skip the Intro -->
|
<!-- Skip the Intro -->
|
||||||
<section if="debug">
|
<section if="debug">
|
||||||
|
|
|
||||||
4
hmm.json
4
hmm.json
|
|
@ -49,14 +49,14 @@
|
||||||
"name": "haxeui-core",
|
"name": "haxeui-core",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "9b3a2fb",
|
"ref": "fc8d656b",
|
||||||
"url": "https://github.com/haxeui/haxeui-core/"
|
"url": "https://github.com/haxeui/haxeui-core/"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "haxeui-flixel",
|
"name": "haxeui-flixel",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "master",
|
"ref": "80941a7",
|
||||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -524,10 +524,7 @@ class FreeplayState extends MusicBeatSubstate
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
|
PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
|
||||||
PlayState.isStoryMode = false;
|
PlayState.isStoryMode = false;
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
PlayState.storyDifficulty = curDifficulty;
|
||||||
PlayState.storyDifficulty_NEW = 'easy';
|
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
||||||
// SongLoad.curDiff = Highscore.formatSong()
|
|
||||||
|
|
||||||
SongLoad.curDiff = switch (curDifficulty)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
'easy';
|
'easy';
|
||||||
|
|
@ -537,6 +534,9 @@ class FreeplayState extends MusicBeatSubstate
|
||||||
'hard';
|
'hard';
|
||||||
default: 'normal';
|
default: 'normal';
|
||||||
};
|
};
|
||||||
|
// SongLoad.curDiff = Highscore.formatSong()
|
||||||
|
|
||||||
|
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
||||||
|
|
||||||
PlayState.storyWeek = songs[curSelected].week;
|
PlayState.storyWeek = songs[curSelected].week;
|
||||||
trace(' CUR WEEK ' + PlayState.storyWeek);
|
trace(' CUR WEEK ' + PlayState.storyWeek);
|
||||||
|
|
@ -565,7 +565,17 @@ class FreeplayState extends MusicBeatSubstate
|
||||||
intendedScore = FlxG.random.int(0, 100000);
|
intendedScore = FlxG.random.int(0, 100000);
|
||||||
|
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
PlayState.storyDifficulty = curDifficulty;
|
||||||
PlayState.storyDifficulty_NEW = 'easy';
|
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
'easy';
|
||||||
|
case 1:
|
||||||
|
'normal';
|
||||||
|
case 2:
|
||||||
|
'hard';
|
||||||
|
default:
|
||||||
|
'normal';
|
||||||
|
};
|
||||||
|
|
||||||
grpDifficulties.group.forEach(function(spr)
|
grpDifficulties.group.forEach(function(spr)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import flixel.graphics.FlxGraphic;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import flixel.math.FlxRect;
|
import flixel.math.FlxRect;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.charting.ChartingState;
|
|
||||||
import funkin.modding.module.ModuleHandler;
|
import funkin.modding.module.ModuleHandler;
|
||||||
import funkin.play.PlayState;
|
import funkin.play.PlayState;
|
||||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||||
|
|
@ -15,8 +14,6 @@ import funkin.play.event.SongEvent.SongEventHandler;
|
||||||
import funkin.play.song.SongData.SongDataParser;
|
import funkin.play.song.SongData.SongDataParser;
|
||||||
import funkin.play.stage.StageData;
|
import funkin.play.stage.StageData;
|
||||||
import funkin.ui.PreferencesMenu;
|
import funkin.ui.PreferencesMenu;
|
||||||
import funkin.ui.animDebugShit.DebugBoundingState;
|
|
||||||
import funkin.ui.stageBuildShit.StageBuilderState;
|
|
||||||
import funkin.util.macro.MacroUtil;
|
import funkin.util.macro.MacroUtil;
|
||||||
import openfl.display.BitmapData;
|
import openfl.display.BitmapData;
|
||||||
|
|
||||||
|
|
@ -198,14 +195,14 @@ class InitState extends FlxTransitionableState
|
||||||
PlayState.currentSong_NEW = SongDataParser.fetchSong(song);
|
PlayState.currentSong_NEW = SongDataParser.fetchSong(song);
|
||||||
PlayState.isStoryMode = isStoryMode;
|
PlayState.isStoryMode = isStoryMode;
|
||||||
PlayState.storyDifficulty = dif;
|
PlayState.storyDifficulty = dif;
|
||||||
PlayState.storyDifficulty_NEW = 'easy';
|
PlayState.storyDifficulty_NEW = switch (dif)
|
||||||
SongLoad.curDiff = switch (dif)
|
|
||||||
{
|
{
|
||||||
case 0: 'easy';
|
case 0: 'easy';
|
||||||
case 1: 'normal';
|
case 1: 'normal';
|
||||||
case 2: 'hard';
|
case 2: 'hard';
|
||||||
default: 'normal';
|
default: 'normal';
|
||||||
};
|
};
|
||||||
|
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
||||||
PlayState.storyWeek = week;
|
PlayState.storyWeek = week;
|
||||||
LoadingState.loadAndSwitchState(new PlayState());
|
LoadingState.loadAndSwitchState(new PlayState());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ class PauseSubState extends MusicBeatSubstate
|
||||||
SongLoad.curDiff = daSelected.toLowerCase();
|
SongLoad.curDiff = daSelected.toLowerCase();
|
||||||
|
|
||||||
PlayState.storyDifficulty = curSelected;
|
PlayState.storyDifficulty = curSelected;
|
||||||
PlayState.storyDifficulty_NEW = 'easy';
|
PlayState.storyDifficulty_NEW = daSelected.toLowerCase();
|
||||||
|
|
||||||
PlayState.needsReset = true;
|
PlayState.needsReset = true;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -378,8 +378,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
PlayState.campaignScore = 0;
|
PlayState.campaignScore = 0;
|
||||||
|
|
||||||
PlayState.storyDifficulty = curDifficulty;
|
PlayState.storyDifficulty = curDifficulty;
|
||||||
PlayState.storyDifficulty_NEW = 'easy';
|
PlayState.storyDifficulty_NEW = switch (curDifficulty)
|
||||||
SongLoad.curDiff = switch (curDifficulty)
|
|
||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
'easy';
|
'easy';
|
||||||
|
|
@ -390,6 +389,7 @@ class StoryMenuState extends MusicBeatState
|
||||||
default:
|
default:
|
||||||
'normal';
|
'normal';
|
||||||
};
|
};
|
||||||
|
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
|
||||||
|
|
||||||
new FlxTimer().start(1, function(tmr:FlxTimer)
|
new FlxTimer().start(1, function(tmr:FlxTimer)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -396,7 +396,7 @@ class PlayState extends MusicBeatState
|
||||||
healthBar = new FlxBar(healthBarBG.x + 4, healthBarBG.y + 4, RIGHT_TO_LEFT, Std.int(healthBarBG.width - 8), Std.int(healthBarBG.height - 8), this,
|
healthBar = new FlxBar(healthBarBG.x + 4, healthBarBG.y + 4, RIGHT_TO_LEFT, Std.int(healthBarBG.width - 8), Std.int(healthBarBG.height - 8), this,
|
||||||
'healthLerp', 0, 2);
|
'healthLerp', 0, 2);
|
||||||
healthBar.scrollFactor.set();
|
healthBar.scrollFactor.set();
|
||||||
healthBar.createFilledBar(Constants.HEALTH_BAR_RED, Constants.HEALTH_BAR_GREEN);
|
healthBar.createFilledBar(Constants.COLOR_HEALTH_BAR_RED, Constants.COLOR_HEALTH_BAR_GREEN);
|
||||||
add(healthBar);
|
add(healthBar);
|
||||||
|
|
||||||
initStage();
|
initStage();
|
||||||
|
|
@ -711,8 +711,19 @@ class PlayState extends MusicBeatState
|
||||||
|
|
||||||
// TODO: Switch playable character by manipulating this value.
|
// TODO: Switch playable character by manipulating this value.
|
||||||
// TODO: How to choose which one to use for story mode?
|
// TODO: How to choose which one to use for story mode?
|
||||||
|
|
||||||
|
var playableChars = currentChart.getPlayableChars();
|
||||||
var currentPlayer = 'bf';
|
var currentPlayer = 'bf';
|
||||||
|
|
||||||
|
if (playableChars.length == 0)
|
||||||
|
{
|
||||||
|
trace('WARNING: No playable characters found for this song.');
|
||||||
|
}
|
||||||
|
else if (playableChars.indexOf(currentPlayer) == -1)
|
||||||
|
{
|
||||||
|
currentPlayer = playableChars[0];
|
||||||
|
}
|
||||||
|
|
||||||
var currentCharData:SongPlayableChar = currentChart.getPlayableChar(currentPlayer);
|
var currentCharData:SongPlayableChar = currentChart.getPlayableChar(currentPlayer);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
||||||
|
|
@ -212,6 +212,11 @@ class SongDifficulty
|
||||||
return chars.get(id);
|
return chars.get(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getPlayableChars():Array<String>
|
||||||
|
{
|
||||||
|
return [for (i in chars.keys()) i];
|
||||||
|
}
|
||||||
|
|
||||||
public function getEvents():Array<SongEvent>
|
public function getEvents():Array<SongEvent>
|
||||||
{
|
{
|
||||||
return cast events;
|
return cast events;
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ class SongDataUtils
|
||||||
/**
|
/**
|
||||||
* Given an array of SongNoteData objects, return a new array of SongNoteData objects
|
* Given an array of SongNoteData objects, return a new array of SongNoteData objects
|
||||||
* whose timestamps are shifted by the given amount.
|
* whose timestamps are shifted by the given amount.
|
||||||
|
* Does not mutate the original array.
|
||||||
*
|
*
|
||||||
* @param notes The notes to modify.
|
* @param notes The notes to modify.
|
||||||
* @param offset The time difference to apply in milliseconds.
|
* @param offset The time difference to apply in milliseconds.
|
||||||
|
|
@ -26,7 +27,8 @@ class SongDataUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a certain subset of notes from an array of SongNoteData objects.
|
* Return a new array without a certain subset of notes from an array of SongNoteData objects.
|
||||||
|
* Does not mutate the original array.
|
||||||
*
|
*
|
||||||
* @param notes The array of notes to be subtracted from.
|
* @param notes The array of notes to be subtracted from.
|
||||||
* @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word.
|
* @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word.
|
||||||
|
|
@ -50,7 +52,8 @@ class SongDataUtils
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove a certain subset of events from an array of SongEventData objects.
|
* Return a new array without a certain subset of events from an array of SongEventData objects.
|
||||||
|
* Does not mutate the original array.
|
||||||
*
|
*
|
||||||
* @param events The array of events to be subtracted from.
|
* @param events The array of events to be subtracted from.
|
||||||
* @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word.
|
* @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word.
|
||||||
|
|
@ -67,6 +70,25 @@ class SongDataUtils
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an array of notes whose note data is flipped (player becomes opponent and vice versa)
|
||||||
|
* Does not mutate the original array.
|
||||||
|
*/
|
||||||
|
public static function flipNotes(notes:Array<SongNoteData>, ?strumlineSize:Int = 4):Array<SongNoteData>
|
||||||
|
{
|
||||||
|
return notes.map(function(note:SongNoteData):SongNoteData
|
||||||
|
{
|
||||||
|
var newData = note.data;
|
||||||
|
|
||||||
|
if (newData < strumlineSize)
|
||||||
|
newData += strumlineSize;
|
||||||
|
else
|
||||||
|
newData -= strumlineSize;
|
||||||
|
|
||||||
|
return new SongNoteData(note.time, newData, note.length, note.kind);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare an array of notes to be used as the clipboard data.
|
* Prepare an array of notes to be used as the clipboard data.
|
||||||
*
|
*
|
||||||
|
|
@ -77,6 +99,9 @@ class SongDataUtils
|
||||||
return offsetSongNoteData(sortNotes(notes), -Std.int(notes[0].time));
|
return offsetSongNoteData(sortNotes(notes), -Std.int(notes[0].time));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sort an array of notes by strum time.
|
||||||
|
*/
|
||||||
public static function sortNotes(notes:Array<SongNoteData>, ?desc:Bool = false):Array<SongNoteData>
|
public static function sortNotes(notes:Array<SongNoteData>, ?desc:Bool = false):Array<SongNoteData>
|
||||||
{
|
{
|
||||||
// TODO: Modifies the array in place. Is this okay?
|
// TODO: Modifies the array in place. Is this okay?
|
||||||
|
|
@ -87,6 +112,9 @@ class SongDataUtils
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Serialize an array of note data and write it to the clipboard.
|
||||||
|
*/
|
||||||
public static function writeNotesToClipboard(notes:Array<SongNoteData>):Void
|
public static function writeNotesToClipboard(notes:Array<SongNoteData>):Void
|
||||||
{
|
{
|
||||||
var notesString = SerializerUtil.toJSON(notes);
|
var notesString = SerializerUtil.toJSON(notes);
|
||||||
|
|
@ -98,6 +126,9 @@ class SongDataUtils
|
||||||
trace(notesString);
|
trace(notesString);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read an array of note data from the clipboard and deserialize it.
|
||||||
|
*/
|
||||||
public static function readNotesFromClipboard():Array<SongNoteData>
|
public static function readNotesFromClipboard():Array<SongNoteData>
|
||||||
{
|
{
|
||||||
var notesString = ClipboardUtil.getClipboard();
|
var notesString = ClipboardUtil.getClipboard();
|
||||||
|
|
@ -117,4 +148,37 @@ class SongDataUtils
|
||||||
return notes;
|
return notes;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a list of notes to only include notes that are within the given time range.
|
||||||
|
*/
|
||||||
|
public static function getNotesInTimeRange(notes:Array<SongNoteData>, start:Float, end:Float):Array<SongNoteData>
|
||||||
|
{
|
||||||
|
return notes.filter(function(note:SongNoteData):Bool
|
||||||
|
{
|
||||||
|
return note.time >= start && note.time <= end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a list of notes to only include notes whose data is within the given range.
|
||||||
|
*/
|
||||||
|
public static function getNotesInDataRange(notes:Array<SongNoteData>, start:Int, end:Int):Array<SongNoteData>
|
||||||
|
{
|
||||||
|
return notes.filter(function(note:SongNoteData):Bool
|
||||||
|
{
|
||||||
|
return note.data >= start && note.data <= end;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter a list of notes to only include notes whose data is one of the given values.
|
||||||
|
*/
|
||||||
|
public static function getNotesWithData(notes:Array<SongNoteData>, data:Array<Int>):Array<SongNoteData>
|
||||||
|
{
|
||||||
|
return notes.filter(function(note:SongNoteData):Bool
|
||||||
|
{
|
||||||
|
return data.indexOf(note.data) != -1;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,10 +38,12 @@ interface ChartEditorCommand
|
||||||
class AddNotesCommand implements ChartEditorCommand
|
class AddNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var notes:Array<SongNoteData>;
|
private var notes:Array<SongNoteData>;
|
||||||
|
private var appendToSelection:Bool;
|
||||||
|
|
||||||
public function new(notes:Array<SongNoteData>)
|
public function new(notes:Array<SongNoteData>, appendToSelection:Bool = false)
|
||||||
{
|
{
|
||||||
this.notes = notes;
|
this.notes = notes;
|
||||||
|
this.appendToSelection = appendToSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(state:ChartEditorState):Void
|
public function execute(state:ChartEditorState):Void
|
||||||
|
|
@ -51,7 +53,15 @@ class AddNotesCommand implements ChartEditorCommand
|
||||||
state.currentSongChartNoteData.push(note);
|
state.currentSongChartNoteData.push(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
state.currentSelection = notes;
|
if (appendToSelection)
|
||||||
|
{
|
||||||
|
state.currentSelection = state.currentSelection.concat(notes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state.currentSelection = notes;
|
||||||
|
}
|
||||||
|
|
||||||
state.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
state.playSound(Paths.sound('funnyNoise/funnyNoise-08'));
|
||||||
|
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
|
|
@ -171,6 +181,9 @@ class SwitchDifficultyCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds one or more notes to the selection.
|
||||||
|
*/
|
||||||
class SelectNotesCommand implements ChartEditorCommand
|
class SelectNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var notes:Array<SongNoteData>;
|
private var notes:Array<SongNoteData>;
|
||||||
|
|
@ -251,6 +264,43 @@ class DeselectNotesCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the selection rather than appends it.
|
||||||
|
* Deselects any notes that are not in the new selection.
|
||||||
|
*/
|
||||||
|
class SetNoteSelectionCommand implements ChartEditorCommand
|
||||||
|
{
|
||||||
|
private var notes:Array<SongNoteData>;
|
||||||
|
private var previousSelection:Array<SongNoteData>;
|
||||||
|
|
||||||
|
public function new(notes:Array<SongNoteData>, ?previousSelection:Array<SongNoteData>)
|
||||||
|
{
|
||||||
|
this.notes = notes;
|
||||||
|
this.previousSelection = previousSelection == null ? [] : previousSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.currentSelection = notes;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function undo(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.currentSelection = previousSelection;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String
|
||||||
|
{
|
||||||
|
return 'Select ${notes.length} Notes';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SelectAllNotesCommand implements ChartEditorCommand
|
class SelectAllNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var previousSelection:Array<SongNoteData>;
|
private var previousSelection:Array<SongNoteData>;
|
||||||
|
|
@ -281,6 +331,36 @@ class SelectAllNotesCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InvertSelectedNotesCommand implements ChartEditorCommand
|
||||||
|
{
|
||||||
|
private var previousSelection:Array<SongNoteData>;
|
||||||
|
|
||||||
|
public function new(?previousSelection:Array<SongNoteData>)
|
||||||
|
{
|
||||||
|
this.previousSelection = previousSelection == null ? [] : previousSelection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.currentSelection = SongDataUtils.subtractNotes(state.currentSongChartNoteData, previousSelection);
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function undo(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.currentSelection = previousSelection;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String
|
||||||
|
{
|
||||||
|
return 'Invert Selected Notes';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class DeselectAllNotesCommand implements ChartEditorCommand
|
class DeselectAllNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var previousSelection:Array<SongNoteData>;
|
private var previousSelection:Array<SongNoteData>;
|
||||||
|
|
@ -352,6 +432,52 @@ class CutNotesCommand implements ChartEditorCommand
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FlipNotesCommand implements ChartEditorCommand
|
||||||
|
{
|
||||||
|
private var notes:Array<SongNoteData>;
|
||||||
|
private var flippedNotes:Array<SongNoteData>;
|
||||||
|
|
||||||
|
public function new(notes:Array<SongNoteData>)
|
||||||
|
{
|
||||||
|
this.notes = notes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
// Delete the notes.
|
||||||
|
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, notes);
|
||||||
|
|
||||||
|
// Add the flipped notes.
|
||||||
|
flippedNotes = SongDataUtils.flipNotes(notes);
|
||||||
|
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(flippedNotes);
|
||||||
|
|
||||||
|
state.currentSelection = flippedNotes;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
state.sortChartData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function undo(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.currentSongChartNoteData = SongDataUtils.subtractNotes(state.currentSongChartNoteData, flippedNotes);
|
||||||
|
state.currentSongChartNoteData = state.currentSongChartNoteData.concat(notes);
|
||||||
|
|
||||||
|
state.currentSelection = notes;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
|
||||||
|
state.sortChartData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String
|
||||||
|
{
|
||||||
|
var len:Int = notes.length;
|
||||||
|
return 'Flip $len Notes';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class PasteNotesCommand implements ChartEditorCommand
|
class PasteNotesCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var targetTimestamp:Float;
|
private var targetTimestamp:Float;
|
||||||
|
|
@ -399,13 +525,12 @@ class PasteNotesCommand implements ChartEditorCommand
|
||||||
class AddEventsCommand implements ChartEditorCommand
|
class AddEventsCommand implements ChartEditorCommand
|
||||||
{
|
{
|
||||||
private var events:Array<SongEventData>;
|
private var events:Array<SongEventData>;
|
||||||
|
private var appendToSelection:Bool;
|
||||||
|
|
||||||
// private var previousSelection:Array<SongEventData>;
|
public function new(events:Array<SongEventData>, ?appendToSelection:Bool = false)
|
||||||
|
|
||||||
public function new(events:Array<SongEventData>, ?previousSelection:Array<SongEventData>)
|
|
||||||
{
|
{
|
||||||
this.events = events;
|
this.events = events;
|
||||||
// this.previousSelection = previousSelection == null ? [] : previousSelection;
|
this.appendToSelection = appendToSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function execute(state:ChartEditorState):Void
|
public function execute(state:ChartEditorState):Void
|
||||||
|
|
@ -423,8 +548,8 @@ class AddEventsCommand implements ChartEditorCommand
|
||||||
public function undo(state:ChartEditorState):Void
|
public function undo(state:ChartEditorState):Void
|
||||||
{
|
{
|
||||||
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
|
state.currentSongChartEventData = SongDataUtils.subtractEvents(state.currentSongChartEventData, events);
|
||||||
// TODO: Allow selecting events.
|
|
||||||
// state.currentSelection = previousSelection;
|
state.currentSelection = [];
|
||||||
|
|
||||||
state.noteDisplayDirty = true;
|
state.noteDisplayDirty = true;
|
||||||
state.notePreviewDirty = true;
|
state.notePreviewDirty = true;
|
||||||
|
|
@ -438,3 +563,42 @@ class AddEventsCommand implements ChartEditorCommand
|
||||||
return 'Add $len Events';
|
return 'Add $len Events';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ExtendNoteLengthCommand implements ChartEditorCommand
|
||||||
|
{
|
||||||
|
private var note:SongNoteData;
|
||||||
|
private var oldLength:Float;
|
||||||
|
private var newLength:Float;
|
||||||
|
|
||||||
|
public function new(note:SongNoteData, newLength:Float)
|
||||||
|
{
|
||||||
|
this.note = note;
|
||||||
|
this.oldLength = note.length;
|
||||||
|
this.newLength = newLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
note.length = newLength;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
|
||||||
|
state.sortChartData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function undo(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
note.length = oldLength;
|
||||||
|
|
||||||
|
state.noteDisplayDirty = true;
|
||||||
|
state.notePreviewDirty = true;
|
||||||
|
|
||||||
|
state.sortChartData();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toString():String
|
||||||
|
{
|
||||||
|
return 'Extend Note Length';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
package funkin.ui.debug.charting;
|
||||||
|
|
||||||
|
class ChartEditorDialogHandler
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
@ -23,6 +23,16 @@ class ChartEditorNoteSprite extends FlxSprite
|
||||||
*/
|
*/
|
||||||
public var noteSkin(default, set):String = 'Normal';
|
public var noteSkin(default, set):String = 'Normal';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This note is the previous sprite in a sustain chain.
|
||||||
|
*/
|
||||||
|
public var parentNoteSprite(default, set):ChartEditorNoteSprite = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This note is the next sprite in a sustain chain.
|
||||||
|
*/
|
||||||
|
public var childNoteSprite(default, set):ChartEditorNoteSprite = null;
|
||||||
|
|
||||||
public function new()
|
public function new()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
@ -41,22 +51,20 @@ class ChartEditorNoteSprite extends FlxSprite
|
||||||
this.animation.addByPrefix('tapUpNormal', 'green instance');
|
this.animation.addByPrefix('tapUpNormal', 'green instance');
|
||||||
this.animation.addByPrefix('tapRightNormal', 'red instance');
|
this.animation.addByPrefix('tapRightNormal', 'red instance');
|
||||||
|
|
||||||
this.animation.addByPrefix('holdLeftNormal', 'purple hold piece instance');
|
this.animation.addByPrefix('holdLeftNormal', 'LeftHoldPiece');
|
||||||
this.animation.addByPrefix('holdDownNormal', 'blue hold piece instance');
|
this.animation.addByPrefix('holdDownNormal', 'DownHoldPiece');
|
||||||
this.animation.addByPrefix('holdUpNormal', 'green hold piece instance');
|
this.animation.addByPrefix('holdUpNormal', 'UpHoldPiece');
|
||||||
this.animation.addByPrefix('holdRightNormal', 'red hold piece instance');
|
this.animation.addByPrefix('holdRightNormal', 'RightHoldPiece');
|
||||||
|
|
||||||
this.animation.addByPrefix('holdEndLeftNormal', 'pruple end hold instance');
|
this.animation.addByPrefix('holdEndLeftNormal', 'LeftHoldEnd');
|
||||||
this.animation.addByPrefix('holdEndDownNormal', 'blue end hold instance');
|
this.animation.addByPrefix('holdEndDownNormal', 'DownHoldEnd');
|
||||||
this.animation.addByPrefix('holdEndUpNormal', 'green end hold instance');
|
this.animation.addByPrefix('holdEndUpNormal', 'UpHoldEnd');
|
||||||
this.animation.addByPrefix('holdEndRightNormal', 'red end hold instance');
|
this.animation.addByPrefix('holdEndRightNormal', 'RightHoldEnd');
|
||||||
|
|
||||||
this.animation.addByPrefix('tapLeftPixel', 'pixel4');
|
this.animation.addByPrefix('tapLeftPixel', 'pixel4');
|
||||||
this.animation.addByPrefix('tapDownPixel', 'pixel5');
|
this.animation.addByPrefix('tapDownPixel', 'pixel5');
|
||||||
this.animation.addByPrefix('tapUpPixel', 'pixel6');
|
this.animation.addByPrefix('tapUpPixel', 'pixel6');
|
||||||
this.animation.addByPrefix('tapRightPixel', 'pixel7');
|
this.animation.addByPrefix('tapRightPixel', 'pixel7');
|
||||||
|
|
||||||
resizeNote();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static var noteFrameCollection:FlxFramesCollection = null;
|
static var noteFrameCollection:FlxFramesCollection = null;
|
||||||
|
|
@ -77,6 +85,12 @@ class ChartEditorNoteSprite extends FlxSprite
|
||||||
{
|
{
|
||||||
noteFrameCollection.pushFrame(frame);
|
noteFrameCollection.pushFrame(frame);
|
||||||
}
|
}
|
||||||
|
var frameCollectionNormal2 = Paths.getSparrowAtlas('NoteHoldNormal');
|
||||||
|
|
||||||
|
for (frame in frameCollectionNormal2.frames)
|
||||||
|
{
|
||||||
|
noteFrameCollection.pushFrame(frame);
|
||||||
|
}
|
||||||
|
|
||||||
// Pixel notes
|
// Pixel notes
|
||||||
var graphicPixel = FlxG.bitmap.add(Paths.image('weeb/pixelUI/arrows-pixels', 'week6'), false, null);
|
var graphicPixel = FlxG.bitmap.add(Paths.image('weeb/pixelUI/arrows-pixels', 'week6'), false, null);
|
||||||
|
|
@ -98,18 +112,27 @@ class ChartEditorNoteSprite extends FlxSprite
|
||||||
|
|
||||||
if (this.noteData == null)
|
if (this.noteData == null)
|
||||||
{
|
{
|
||||||
|
// Disown parent.
|
||||||
|
this.parentNoteSprite = null;
|
||||||
|
if (this.childNoteSprite != null)
|
||||||
|
{
|
||||||
|
// Kill all children and disown them.
|
||||||
|
this.childNoteSprite.noteData = null;
|
||||||
|
this.childNoteSprite = null;
|
||||||
|
}
|
||||||
this.kill();
|
this.kill();
|
||||||
return this.noteData;
|
return this.noteData;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.visible = true;
|
this.visible = true;
|
||||||
|
|
||||||
// Update the position to match the note skin.
|
// Update the animation to match the note data.
|
||||||
setNotePosition();
|
// Animation is updated first so size is correct before updating position.
|
||||||
|
|
||||||
// Update the animation to match the note skin.
|
|
||||||
playNoteAnimation();
|
playNoteAnimation();
|
||||||
|
|
||||||
|
// Update the position to match the note data.
|
||||||
|
setNotePosition();
|
||||||
|
|
||||||
return this.noteData;
|
return this.noteData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,25 +172,125 @@ class ChartEditorNoteSprite extends FlxSprite
|
||||||
cursorColumn += ChartEditorState.STRUMLINE_SIZE;
|
cursorColumn += ChartEditorState.STRUMLINE_SIZE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.x = cursorColumn * ChartEditorState.GRID_SIZE;
|
|
||||||
|
|
||||||
// Notes far in the song will start far down, but the group they belong to will have a high negative offset.
|
if (parentNoteSprite == null)
|
||||||
// TODO: stepTime doesn't account for fluctuating BPMs.
|
{
|
||||||
this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE;
|
this.x = cursorColumn * ChartEditorState.GRID_SIZE;
|
||||||
|
|
||||||
|
// Notes far in the song will start far down, but the group they belong to will have a high negative offset.
|
||||||
|
// TODO: stepTime doesn't account for fluctuating BPMs.
|
||||||
|
this.y = this.noteData.stepTime * ChartEditorState.GRID_SIZE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If this is a hold note, we need to adjust the position to be centered.
|
||||||
|
if (parentNoteSprite.parentNoteSprite == null)
|
||||||
|
{
|
||||||
|
this.x = parentNoteSprite.x;
|
||||||
|
this.x += (ChartEditorState.GRID_SIZE / 2);
|
||||||
|
this.x -= this.width / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.x = parentNoteSprite.x;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.y = parentNoteSprite.y;
|
||||||
|
if (parentNoteSprite.parentNoteSprite == null)
|
||||||
|
{
|
||||||
|
this.y += parentNoteSprite.height / 2;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.y += parentNoteSprite.height - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_parentNoteSprite(value:ChartEditorNoteSprite):ChartEditorNoteSprite
|
||||||
|
{
|
||||||
|
this.parentNoteSprite = value;
|
||||||
|
|
||||||
|
if (this.parentNoteSprite != null)
|
||||||
|
{
|
||||||
|
this.noteData = this.parentNoteSprite.noteData;
|
||||||
|
this.noteSkin = this.parentNoteSprite.noteSkin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.parentNoteSprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
function set_childNoteSprite(value:ChartEditorNoteSprite):ChartEditorNoteSprite
|
||||||
|
{
|
||||||
|
this.childNoteSprite = value;
|
||||||
|
|
||||||
|
if (this.parentNoteSprite != null)
|
||||||
|
{
|
||||||
|
this.noteData = this.parentNoteSprite.noteData;
|
||||||
|
this.noteSkin = this.parentNoteSprite.noteSkin;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.childNoteSprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
function playNoteAnimation()
|
function playNoteAnimation()
|
||||||
{
|
{
|
||||||
var animationName = 'tap${this.noteData.getDirectionName()}${this.noteSkin}';
|
// Decide whether to display a note or a sustain.
|
||||||
this.animation.play(animationName);
|
var baseAnimationName:String = 'tap';
|
||||||
}
|
if (this.parentNoteSprite != null)
|
||||||
|
baseAnimationName = (this.childNoteSprite != null) ? 'hold' : 'holdEnd';
|
||||||
|
|
||||||
function resizeNote()
|
// Play the appropriate animation for the type, direction, and skin.
|
||||||
{
|
var animationName = '${baseAnimationName}${this.noteData.getDirectionName()}${this.noteSkin}';
|
||||||
this.setGraphicSize(ChartEditorState.GRID_SIZE);
|
|
||||||
|
this.animation.play(animationName);
|
||||||
|
|
||||||
|
// Resize note.
|
||||||
|
|
||||||
|
switch (baseAnimationName)
|
||||||
|
{
|
||||||
|
case 'tap':
|
||||||
|
this.setGraphicSize(0, ChartEditorState.GRID_SIZE);
|
||||||
|
case 'hold':
|
||||||
|
if (parentNoteSprite.parentNoteSprite == null)
|
||||||
|
{
|
||||||
|
this.setGraphicSize(Std.int(ChartEditorState.GRID_SIZE / 2), Std.int(ChartEditorState.GRID_SIZE / 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.setGraphicSize(Std.int(ChartEditorState.GRID_SIZE / 2), ChartEditorState.GRID_SIZE);
|
||||||
|
}
|
||||||
|
case 'holdEnd':
|
||||||
|
this.setGraphicSize(Std.int(ChartEditorState.GRID_SIZE / 2), Std.int(ChartEditorState.GRID_SIZE / 2));
|
||||||
|
}
|
||||||
this.updateHitbox();
|
this.updateHitbox();
|
||||||
|
|
||||||
// TODO: Make this an attribute of the note skin.
|
// TODO: Make this an attribute of the note skin.
|
||||||
this.antialiasing = (noteSkin != 'Pixel');
|
this.antialiasing = (noteSkin != 'Pixel');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return whether this note (or its parent) is currently visible.
|
||||||
|
*/
|
||||||
|
public function isNoteVisible(viewAreaBottom:Float, viewAreaTop:Float):Bool
|
||||||
|
{
|
||||||
|
var outsideViewArea = (this.y + this.height < viewAreaTop || this.y > viewAreaBottom);
|
||||||
|
|
||||||
|
if (!outsideViewArea)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Check if this note's parent or child is visible.
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBaseNoteSprite()
|
||||||
|
{
|
||||||
|
if (this.parentNoteSprite == null)
|
||||||
|
return this;
|
||||||
|
else
|
||||||
|
return this.parentNoteSprite;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
172
source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx
Normal file
172
source/funkin/ui/debug/charting/ChartEditorThemeHandler.hx
Normal file
|
|
@ -0,0 +1,172 @@
|
||||||
|
package funkin.ui.debug.charting;
|
||||||
|
|
||||||
|
import flixel.addons.display.FlxGridOverlay;
|
||||||
|
import flixel.addons.display.FlxSliceSprite;
|
||||||
|
import flixel.math.FlxRect;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.geom.Rectangle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available themes for the chart editor state.
|
||||||
|
*/
|
||||||
|
enum ChartEditorTheme
|
||||||
|
{
|
||||||
|
Light;
|
||||||
|
Dark;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Static functions which handle building themed UI elements for a provided ChartEditorState.
|
||||||
|
*/
|
||||||
|
class ChartEditorThemeHandler
|
||||||
|
{
|
||||||
|
// TODO: There's probably a better system of organization for these colors.
|
||||||
|
// An enum of typedefs or something?
|
||||||
|
// ================================
|
||||||
|
static final BACKGROUND_COLOR_LIGHT:FlxColor = 0xFF673AB7;
|
||||||
|
static final BACKGROUND_COLOR_DARK:FlxColor = 0xFF673AB7;
|
||||||
|
|
||||||
|
// Color 1 of the grid pattern. Alternates with Color 2.
|
||||||
|
static final GRID_COLOR_1_LIGHT:FlxColor = 0xFFE7E6E6;
|
||||||
|
static final GRID_COLOR_1_DARK:FlxColor = 0xFF181919;
|
||||||
|
|
||||||
|
// Color 2 of the grid pattern. Alternates with Color 1.
|
||||||
|
static final GRID_COLOR_2_LIGHT:FlxColor = 0xFFD9D5D5;
|
||||||
|
static final GRID_COLOR_2_DARK:FlxColor = 0xFF262A2A;
|
||||||
|
|
||||||
|
// Vertical divider between characters.
|
||||||
|
static final GRID_STRUMLINE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF000000;
|
||||||
|
static final GRID_STRUMLINE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||||
|
static final GRID_STRUMLINE_DIVIDER_WIDTH:Float = 2;
|
||||||
|
|
||||||
|
// Horizontal divider between measures.
|
||||||
|
static final GRID_MEASURE_DIVIDER_COLOR_LIGHT:FlxColor = 0xFF000000;
|
||||||
|
static final GRID_MEASURE_DIVIDER_COLOR_DARK:FlxColor = 0xFFC4C4C4;
|
||||||
|
static final GRID_MEASURE_DIVIDER_WIDTH:Float = 2;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
static final SELECTION_SQUARE_BORDER_WIDTH:Int = 1;
|
||||||
|
|
||||||
|
// Fill on the square highlighting selected notes.
|
||||||
|
// Make sure this is transparent so you can see the notes underneath.
|
||||||
|
static final SELECTION_SQUARE_FILL_COLOR_LIGHT:FlxColor = 0x4033FF33;
|
||||||
|
static final SELECTION_SQUARE_FILL_COLOR_DARK:FlxColor = 0x4033FF33;
|
||||||
|
|
||||||
|
// TODO: Un-hardcode these to be based on time signature.
|
||||||
|
static final STEPS_PER_BEAT:Int = 4;
|
||||||
|
static final BEATS_PER_MEASURE:Int = 4;
|
||||||
|
|
||||||
|
public static function updateTheme(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
updateBackground(state);
|
||||||
|
updateGridBitmap(state);
|
||||||
|
updateSelectionSquare(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function updateBackground(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
state.menuBG.color = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case ChartEditorTheme.Light: BACKGROUND_COLOR_LIGHT;
|
||||||
|
case ChartEditorTheme.Dark: BACKGROUND_COLOR_DARK;
|
||||||
|
default: BACKGROUND_COLOR_LIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds the checkerboard background image of the chart editor, and adds dividing lines to it.
|
||||||
|
* @param dark Whether to draw the grid in a dark color instead of a light one.
|
||||||
|
*/
|
||||||
|
static function updateGridBitmap(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
var gridColor1:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: GRID_COLOR_1_LIGHT;
|
||||||
|
case Dark: GRID_COLOR_1_DARK;
|
||||||
|
default: GRID_COLOR_1_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
var gridColor2:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: GRID_COLOR_2_LIGHT;
|
||||||
|
case Dark: GRID_COLOR_2_DARK;
|
||||||
|
default: GRID_COLOR_2_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Draw the base grid.
|
||||||
|
|
||||||
|
// 2 * (Strumline Size) + 1 grid squares wide, by (4 * quarter notes per measure) grid squares tall.
|
||||||
|
// This gets reused to fill the screen.
|
||||||
|
var gridWidth = ChartEditorState.GRID_SIZE * (ChartEditorState.STRUMLINE_SIZE * 2 + 1);
|
||||||
|
var gridHeight = ChartEditorState.GRID_SIZE * (STEPS_PER_BEAT * BEATS_PER_MEASURE);
|
||||||
|
state.gridBitmap = FlxGridOverlay.createGrid(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE, gridWidth, gridHeight, true, gridColor1,
|
||||||
|
gridColor2);
|
||||||
|
|
||||||
|
// Draw dividers between the strumlines.
|
||||||
|
|
||||||
|
var gridStrumlineDividerColor:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||||
|
case Dark: GRID_STRUMLINE_DIVIDER_COLOR_DARK;
|
||||||
|
default: GRID_STRUMLINE_DIVIDER_COLOR_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Divider at 1 * (Strumline Size)
|
||||||
|
var dividerLineAX = ChartEditorState.GRID_SIZE * (ChartEditorState.STRUMLINE_SIZE) - (GRID_STRUMLINE_DIVIDER_WIDTH / 2);
|
||||||
|
state.gridBitmap.fillRect(new Rectangle(dividerLineAX, 0, GRID_STRUMLINE_DIVIDER_WIDTH, state.gridBitmap.height), gridStrumlineDividerColor);
|
||||||
|
// Divider at 2 * (Strumline Size)
|
||||||
|
var dividerLineBX = ChartEditorState.GRID_SIZE * (ChartEditorState.STRUMLINE_SIZE * 2) - (GRID_STRUMLINE_DIVIDER_WIDTH / 2);
|
||||||
|
state.gridBitmap.fillRect(new Rectangle(dividerLineBX, 0, GRID_STRUMLINE_DIVIDER_WIDTH, state.gridBitmap.height), gridStrumlineDividerColor);
|
||||||
|
|
||||||
|
// Draw dividers between the measures.
|
||||||
|
|
||||||
|
var gridMeasureDividerColor:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||||
|
case Dark: GRID_MEASURE_DIVIDER_COLOR_DARK;
|
||||||
|
default: GRID_MEASURE_DIVIDER_COLOR_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Divider at top
|
||||||
|
state.gridBitmap.fillRect(new Rectangle(0, 0, state.gridBitmap.width, GRID_MEASURE_DIVIDER_WIDTH / 2), gridMeasureDividerColor);
|
||||||
|
// Divider at bottom
|
||||||
|
var dividerLineBY = state.gridBitmap.height - (GRID_MEASURE_DIVIDER_WIDTH / 2);
|
||||||
|
state.gridBitmap.fillRect(new Rectangle(0, dividerLineBY, GRID_MEASURE_DIVIDER_WIDTH / 2, state.gridBitmap.height), gridMeasureDividerColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function updateSelectionSquare(state:ChartEditorState):Void
|
||||||
|
{
|
||||||
|
var selectionSquareBorderColor:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: SELECTION_SQUARE_BORDER_COLOR_LIGHT;
|
||||||
|
case Dark: SELECTION_SQUARE_BORDER_COLOR_DARK;
|
||||||
|
default: SELECTION_SQUARE_BORDER_COLOR_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
var selectionSquareFillColor:FlxColor = switch (state.currentTheme)
|
||||||
|
{
|
||||||
|
case Light: SELECTION_SQUARE_FILL_COLOR_LIGHT;
|
||||||
|
case Dark: SELECTION_SQUARE_FILL_COLOR_DARK;
|
||||||
|
default: SELECTION_SQUARE_FILL_COLOR_LIGHT;
|
||||||
|
};
|
||||||
|
|
||||||
|
state.selectionSquareBitmap = new BitmapData(ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE, true);
|
||||||
|
|
||||||
|
state.selectionSquareBitmap.fillRect(new Rectangle(0, 0, ChartEditorState.GRID_SIZE, ChartEditorState.GRID_SIZE), selectionSquareBorderColor);
|
||||||
|
state.selectionSquareBitmap.fillRect(new Rectangle(SELECTION_SQUARE_BORDER_WIDTH, SELECTION_SQUARE_BORDER_WIDTH,
|
||||||
|
ChartEditorState.GRID_SIZE - (SELECTION_SQUARE_BORDER_WIDTH * 2), ChartEditorState.GRID_SIZE - (SELECTION_SQUARE_BORDER_WIDTH * 2)),
|
||||||
|
selectionSquareFillColor);
|
||||||
|
|
||||||
|
state.selectionBoxSprite = new FlxSliceSprite(state.selectionSquareBitmap,
|
||||||
|
new FlxRect(SELECTION_SQUARE_BORDER_WIDTH
|
||||||
|
+ 4, SELECTION_SQUARE_BORDER_WIDTH
|
||||||
|
+ 4,
|
||||||
|
ChartEditorState.GRID_SIZE
|
||||||
|
- (2 * SELECTION_SQUARE_BORDER_WIDTH + 8), ChartEditorState.GRID_SIZE
|
||||||
|
- (2 * SELECTION_SQUARE_BORDER_WIDTH + 8)),
|
||||||
|
32, 32);
|
||||||
|
}
|
||||||
|
}
|
||||||
184
source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx
Normal file
184
source/funkin/ui/debug/charting/ChartEditorToolboxHandler.hx
Normal file
|
|
@ -0,0 +1,184 @@
|
||||||
|
package funkin.ui.debug.charting;
|
||||||
|
|
||||||
|
import haxe.ui.components.DropDown;
|
||||||
|
import haxe.ui.containers.Group;
|
||||||
|
import haxe.ui.containers.dialogs.Dialog;
|
||||||
|
import haxe.ui.events.UIEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available tools for the chart editor state.
|
||||||
|
*/
|
||||||
|
enum ChartEditorToolMode
|
||||||
|
{
|
||||||
|
Select;
|
||||||
|
Place;
|
||||||
|
}
|
||||||
|
|
||||||
|
class ChartEditorToolboxHandler
|
||||||
|
{
|
||||||
|
public static function setToolboxState(state:ChartEditorState, id:String, shown:Bool):Void
|
||||||
|
{
|
||||||
|
if (shown)
|
||||||
|
showToolbox(state, id);
|
||||||
|
else
|
||||||
|
hideToolbox(state, id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function showToolbox(state:ChartEditorState, id:String)
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = state.activeToolboxes.get(id);
|
||||||
|
|
||||||
|
if (toolbox == null)
|
||||||
|
toolbox = initToolbox(state, id);
|
||||||
|
|
||||||
|
if (toolbox != null)
|
||||||
|
{
|
||||||
|
toolbox.showDialog(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('ChartEditorToolboxHandler.showToolbox() - Could not retrieve toolbox: $id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function hideToolbox(state:ChartEditorState, id:String):Void
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = state.activeToolboxes.get(id);
|
||||||
|
|
||||||
|
if (toolbox == null)
|
||||||
|
toolbox = initToolbox(state, id);
|
||||||
|
|
||||||
|
if (toolbox != null)
|
||||||
|
{
|
||||||
|
toolbox.hideDialog(DialogButton.CANCEL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trace('ChartEditorToolboxHandler.hideToolbox() - Could not retrieve toolbox: $id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function minimizeToolbox(state:ChartEditorState, id:String):Void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function maximizeToolbox(state:ChartEditorState, id:String):Void
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function initToolbox(state:ChartEditorState, id:String):Dialog
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = null;
|
||||||
|
switch (id)
|
||||||
|
{
|
||||||
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT:
|
||||||
|
toolbox = buildToolboxToolsLayout(state);
|
||||||
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT:
|
||||||
|
toolbox = buildToolboxNoteDataLayout(state);
|
||||||
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT:
|
||||||
|
toolbox = buildToolboxEventDataLayout(state);
|
||||||
|
case ChartEditorState.CHART_EDITOR_TOOLBOX_SONGDATA_LAYOUT:
|
||||||
|
toolbox = buildToolboxSongDataLayout(state);
|
||||||
|
default:
|
||||||
|
trace('ChartEditorToolboxHandler.initToolbox() - Unknown toolbox ID: $id');
|
||||||
|
toolbox = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.activeToolboxes.set(id, toolbox);
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildToolboxToolsLayout(state:ChartEditorState):Dialog
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_TOOLS_LAYOUT);
|
||||||
|
|
||||||
|
// Starting position.
|
||||||
|
toolbox.x = 50;
|
||||||
|
toolbox.y = 50;
|
||||||
|
|
||||||
|
toolbox.onDialogClosed = (event:DialogEvent) ->
|
||||||
|
{
|
||||||
|
state.setUISelected('menubarItemToggleToolboxTools', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var toolsGroup:Group = toolbox.findComponent("toolboxToolsGroup", Group);
|
||||||
|
|
||||||
|
toolsGroup.onChange = (event:UIEvent) ->
|
||||||
|
{
|
||||||
|
switch (event.target.id)
|
||||||
|
{
|
||||||
|
case 'toolboxToolsGroupSelect':
|
||||||
|
state.currentToolMode = ChartEditorToolMode.Select;
|
||||||
|
case 'toolboxToolsGroupPlace':
|
||||||
|
state.currentToolMode = ChartEditorToolMode.Place;
|
||||||
|
default:
|
||||||
|
trace('ChartEditorToolboxHandler.buildToolboxToolsLayout() - Unknown toolbox tool selected: $event.target.id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildToolboxNoteDataLayout(state:ChartEditorState):Dialog
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_NOTEDATA_LAYOUT);
|
||||||
|
|
||||||
|
// Starting position.
|
||||||
|
toolbox.x = 75;
|
||||||
|
toolbox.y = 100;
|
||||||
|
|
||||||
|
toolbox.onDialogClosed = (event:DialogEvent) ->
|
||||||
|
{
|
||||||
|
state.setUISelected('menubarItemToggleToolboxNotes', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
var toolboxNotesNoteKind:DropDown = toolbox.findComponent("toolboxNotesNoteKind", DropDown);
|
||||||
|
|
||||||
|
toolboxNotesNoteKind.onChange = (event:UIEvent) ->
|
||||||
|
{
|
||||||
|
state.selectedNoteKind = event.data.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildToolboxEventDataLayout(state:ChartEditorState):Dialog
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_EVENTDATA_LAYOUT);
|
||||||
|
|
||||||
|
// Starting position.
|
||||||
|
toolbox.x = 100;
|
||||||
|
toolbox.y = 150;
|
||||||
|
|
||||||
|
toolbox.onDialogClosed = (event:DialogEvent) ->
|
||||||
|
{
|
||||||
|
state.setUISelected('menubarItemToggleToolboxEvents', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildToolboxSongDataLayout(state:ChartEditorState):Dialog
|
||||||
|
{
|
||||||
|
var toolbox:Dialog = cast state.buildComponent(ChartEditorState.CHART_EDITOR_TOOLBOX_SONGDATA_LAYOUT);
|
||||||
|
|
||||||
|
// Starting position.
|
||||||
|
toolbox.x = 950;
|
||||||
|
toolbox.y = 50;
|
||||||
|
|
||||||
|
toolbox.onDialogClosed = (event:DialogEvent) ->
|
||||||
|
{
|
||||||
|
state.setUISelected('menubarItemToggleToolboxSong', false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return toolbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
static function buildDialog(state:ChartEditorState, id:String):Dialog
|
||||||
|
{
|
||||||
|
var dialog:Dialog = cast state.buildComponent(id);
|
||||||
|
dialog.destroyOnClose = false;
|
||||||
|
return dialog;
|
||||||
|
}
|
||||||
|
}
|
||||||
3
source/funkin/ui/haxeui/components/README.md
Normal file
3
source/funkin/ui/haxeui/components/README.md
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
# funkin.ui.haxeui.components
|
||||||
|
|
||||||
|
Since there is a line in `source/module.xml` pointing to this folder, all components in this folder will automatically be accessible in any HaxeUI layouts.
|
||||||
|
|
@ -6,27 +6,29 @@ import lime.app.Application;
|
||||||
class Constants
|
class Constants
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* The scale factor to use when increasing the size of pixel art graphics.
|
* ENGINE AND VERSION DATA
|
||||||
*/
|
*/
|
||||||
public static final PIXEL_ART_SCALE = 6;
|
// ==============================
|
||||||
|
|
||||||
public static final HEALTH_BAR_RED:FlxColor = 0xFFFF0000;
|
/**
|
||||||
public static final HEALTH_BAR_GREEN:FlxColor = 0xFF66FF33;
|
* The title of the game, for debug printing purposes.
|
||||||
|
* Change this if you're making an engine.
|
||||||
public static final COUNTDOWN_VOLUME = 0.6;
|
*/
|
||||||
|
|
||||||
public static final VERSION_SUFFIX = ' PROTOTYPE';
|
|
||||||
public static var VERSION(get, null):String;
|
|
||||||
|
|
||||||
public static final FREAKY_MENU_BPM = 102;
|
|
||||||
|
|
||||||
// Change this if you're making an engine.
|
|
||||||
public static final TITLE = "Friday Night Funkin'";
|
public static final TITLE = "Friday Night Funkin'";
|
||||||
|
|
||||||
#if debug
|
/**
|
||||||
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
* The current version number of the game.
|
||||||
public static final GIT_BRANCH = funkin.util.macro.GitCommit.getGitBranch();
|
* Modify this in the `project.xml` file.
|
||||||
|
*/
|
||||||
|
public static var VERSION(get, null):String;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A suffix to add to the game version.
|
||||||
|
* Add a suffix to prototype builds and remove it for releases.
|
||||||
|
*/
|
||||||
|
public static final VERSION_SUFFIX = ' PROTOTYPE';
|
||||||
|
|
||||||
|
#if debug
|
||||||
static function get_VERSION():String
|
static function get_VERSION():String
|
||||||
{
|
{
|
||||||
return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH})' + VERSION_SUFFIX;
|
return 'v${Application.current.meta.get('version')} (${GIT_BRANCH} : ${GIT_HASH})' + VERSION_SUFFIX;
|
||||||
|
|
@ -38,6 +40,74 @@ class Constants
|
||||||
}
|
}
|
||||||
#end
|
#end
|
||||||
|
|
||||||
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
|
/**
|
||||||
|
* URL DATA
|
||||||
|
*/
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to download the game on Itch.io.
|
||||||
|
*/
|
||||||
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin/purchase";
|
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin/purchase";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Link to the game's page on Kickstarter.
|
||||||
|
*/
|
||||||
|
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GIT REPO DATA
|
||||||
|
*/
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
#if debug
|
||||||
|
/**
|
||||||
|
* The current Git branch.
|
||||||
|
*/
|
||||||
|
public static final GIT_BRANCH = funkin.util.macro.GitCommit.getGitBranch();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current Git commit hash.
|
||||||
|
*/
|
||||||
|
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
||||||
|
#end
|
||||||
|
|
||||||
|
/**
|
||||||
|
* COLORS
|
||||||
|
*/
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used by the enemy health bar.
|
||||||
|
*/
|
||||||
|
public static final COLOR_HEALTH_BAR_RED:FlxColor = 0xFFFF0000;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The color used by the player health bar.
|
||||||
|
*/
|
||||||
|
public static final COLOR_HEALTH_BAR_GREEN:FlxColor = 0xFF66FF33;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OTHER
|
||||||
|
*/
|
||||||
|
// ==============================
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The scale factor to use when increasing the size of pixel art graphics.
|
||||||
|
*/
|
||||||
|
public static final PIXEL_ART_SCALE = 6;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The BPM of the title screen and menu music.
|
||||||
|
* TODO: Move to metadata file.
|
||||||
|
*/
|
||||||
|
public static final FREAKY_MENU_BPM = 102;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The volume at which to play the countdown before the song starts.
|
||||||
|
*/
|
||||||
|
public static final COUNTDOWN_VOLUME = 0.6;
|
||||||
|
|
||||||
|
public static final DEFAULT_VARIATION = 'default';
|
||||||
|
public static final DEFAULT_DIFFICULTY = 'normal';
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue