Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/chart generator operators #1

Open
wants to merge 25 commits into
base: feature/chart-generator
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions source/funkin/InitState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import funkin.data.freeplay.album.AlbumRegistry;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.charting.GenerateChartOperatorRegistry;
import funkin.data.charting.GenerateDifficultyOperatorRegistry;
import funkin.data.song.SongRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.stage.StageRegistry;
Expand Down Expand Up @@ -166,6 +168,8 @@ class InitState extends FlxState
trace('Parsing game data...');
var perfStart:Float = TimerUtil.start();
SongEventRegistry.loadEventCache(); // SongEventRegistry is structured differently so it's not a BaseRegistry.
GenerateChartOperatorRegistry.instance.loadEntries();
GenerateDifficultyOperatorRegistry.instance.loadEntries();
SongRegistry.instance.loadEntries();
LevelRegistry.instance.loadEntries();
NoteStyleRegistry.instance.loadEntries();
Expand Down
173 changes: 173 additions & 0 deletions source/funkin/data/BaseClassRegistry.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package funkin.data;

import haxe.Constraints.Constructible;
import flixel.FlxG;

using StringTools;

@:generic
abstract class BaseClassRegistry<T:(IClassRegistryEntry)>
{
/**
* This registry's id
*/
public final registryId:String;

final entries:Map<String, T>;
final scriptedEntries:Map<String, String>;

public function new(registryId:String)
{
this.registryId = registryId;
this.entries = new Map<String, T>();
this.scriptedEntries = new Map<String, String>();

// Lazy initialization of singletons should let this get called,
// but we have this check just in case.
if (FlxG.game != null)
{
FlxG.console.registerObject('registry$registryId', this);
}
}

/**
* Loads all built-in and scripted classes
*/
public function loadEntries():Void
{
this.entries.clear();
this.scriptedEntries.clear();

registerBuiltInClasses();
registerScriptedClasses();
}

/**
* Retrieve all registered Entries
* @return Entries
*/
public function fetchEntries():Array<T>
{
return entries.values();
}

/**
* Retrieve all registered Entries' ids
* @return Ids
*/
public function fetchEntryIds():Array<String>
{
return entries.keys().array();
}

/**
* Retrieve only registered scripted Entries
* @return Scripted Entries
*/
public function fetchScriptedEntries():Array<String>
{
return scriptedEntries.values();
}

/**
* Retrieve only registered scripted Entries' ids
* @return Scripted Ids
*/
public function fetchScriptedEntryIds():Array<String>
{
return scriptedEntries.keys().array();
}

/**
* Retrive entry by id
* @param id The ID
* @return Null<T>
*/
public function fetchEntry(id:String):Null<T>
{
return entries.get(id);
}

/**
* Creates an instance of an entry
* @param id The id
* @return Null<T>
*/
public function createInstanceOf(id:String):Null<T>
{
var scriptedEntry = scriptedEntries.get(id);
if (scriptedEntry != null)
{
return createScriptedEntry(scriptedEntry);
}

var entry = entries.get(id);
if (entry == null)
{
return null;
}
return Type.createInstance(Type.getClass(entry), []);
}

function registerBuiltInClasses():Void
{
final baseEntryName:String = entryTraceName();

trace('Instantiating ${getBuiltInEntries().length - 2} built-in${baseEntryName}s...');
for (builtInEntryCls in getBuiltInEntries())
{
var builtInEntryClsName:String = Type.getClassName(builtInEntryCls);
if (ignoreBuiltInEntry(builtInEntryClsName))
{
continue;
}

var builtInEntry:T = Type.createInstance(builtInEntryCls, []);

if (builtInEntry != null)
{
trace(' Loaded built-in${baseEntryName}: ${builtInEntry.id}');
entries.set(builtInEntry.id, builtInEntry);
}
else
{
trace(' Failed to load built-in${baseEntryName}: ${builtInEntryClsName}');
}
}
}

function registerScriptedClasses():Void
{
final baseEntryName:String = entryTraceName();

var scriptedEntryClsNames:Array<String> = listScriptedClasses();
trace('Instantiating ${scriptedEntryClsNames.length} scripted${baseEntryName}s...');
if (scriptedEntryClsNames == null || scriptedEntryClsNames.length == 0) return;

for (scriptedEntryCls in scriptedEntryClsNames)
{
var scriptedEntry:Null<T> = createScriptedEntry(scriptedEntryCls);

if (scriptedEntry != null)
{
trace(' Loaded scripted${baseEntryName}: ${scriptedEntry.id}');
entries.set(scriptedEntry.id, scriptedEntry);
scriptedEntries.set(scriptedEntry.id, scriptedEntryCls);
}
else
{
trace(' Failed to instantiate scripted${baseEntryName} class: ${scriptedEntryCls}');
}
}
}

abstract function entryTraceName():String;

abstract function listScriptedClasses():Array<String>;

abstract function ignoreBuiltInEntry(builtInEntryName:String):Bool;

abstract function getBuiltInEntries():List<Class<T>>;

abstract function createScriptedEntry(clsName:String):Null<T>;
}
9 changes: 9 additions & 0 deletions source/funkin/data/IClassRegistryEntry.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package funkin.data;

interface IClassRegistryEntry
{
public final id:String;

public function destroy():Void;
public function toString():String;
}
13 changes: 13 additions & 0 deletions source/funkin/data/charting/GenerateChartOperatorRegistry.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package funkin.data.charting;

import funkin.data.BaseClassRegistry;
import funkin.ui.debug.charting.util.GenerateChartOperator;

@:build(funkin.util.macro.ClassRegistryMacro.build())
class GenerateChartOperatorRegistry extends BaseClassRegistry<GenerateChartOperator>
{
public function new()
{
super('generateChartOperatorRegistry');
}
}
13 changes: 13 additions & 0 deletions source/funkin/data/charting/GenerateDifficultyOperatorRegistry.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package funkin.data.charting;

import funkin.data.BaseClassRegistry;
import funkin.ui.debug.charting.util.GenerateDifficultyOperator;

@:build(funkin.util.macro.ClassRegistryMacro.build())
class GenerateDifficultyOperatorRegistry extends BaseClassRegistry<GenerateDifficultyOperator>
{
public function new()
{
super('generateDifficultyOperatorRegistry');
}
}
4 changes: 4 additions & 0 deletions source/funkin/modding/PolymodHandler.hx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import funkin.data.event.SongEventRegistry;
import funkin.data.story.level.LevelRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notekind.NoteKindManager;
import funkin.data.charting.GenerateChartOperatorRegistry;
import funkin.data.charting.GenerateDifficultyOperatorRegistry;
import funkin.data.song.SongRegistry;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.stage.StageRegistry;
Expand Down Expand Up @@ -416,6 +418,8 @@ class PolymodHandler
// to ensure build macros work properly.
SongEventRegistry.loadEventCache();

GenerateChartOperatorRegistry.instance.loadEntries();
GenerateDifficultyOperatorRegistry.instance.loadEntries();
SongRegistry.instance.loadEntries();
LevelRegistry.instance.loadEntries();
NoteStyleRegistry.instance.loadEntries();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package funkin.ui.debug.charting.components;

import funkin.ui.debug.charting.ChartEditorState;
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
import funkin.ui.debug.charting.util.GenerateDifficultyOperator;
import funkin.data.charting.GenerateDifficultyOperatorRegistry;
import haxe.ui.containers.HBox;
import haxe.ui.containers.VBox;
import haxe.ui.containers.ScrollView;
import haxe.ui.components.TextField;
import haxe.ui.components.NumberStepper;

/**
* The component which contains the difficulty data item for the difficulty generator.
Expand All @@ -14,6 +16,8 @@ import haxe.ui.components.NumberStepper;
@:access(funkin.ui.debug.charting.ChartEditorState)
class ChartEditorDifficultyItem extends HBox
{
public var algorithm(default, null):GenerateDifficultyOperator;

var view:ScrollView;

public function new(state:ChartEditorState, view:ScrollView)
Expand Down Expand Up @@ -44,6 +48,13 @@ class ChartEditorDifficultyItem extends HBox
difficultyDropdown.dataSource.add({text: difficulty.toTitleCase(), value: difficulty});
}
difficultyDropdown.value = difficultyDropdown.dataSource.get(0);

ChartEditorDropdowns.populateDropdownWithGenerateDifficultyOperators(algorithmDropdown, 'defaultOperator');
algorithmDropdown.onChange = function(_) {
algorithmBox.removeComponentAt(0);
buildAlgorithmParams();
}
buildAlgorithmParams();
}

override function update(elapsed:Float):Void
Expand All @@ -59,4 +70,16 @@ class ChartEditorDifficultyItem extends HBox
difficultyFrame.text = "Difficulty";
}
}

function buildAlgorithmParams():Void
{
var vbox:VBox = new VBox();
vbox.percentWidth = 100;

algorithm?.destroy();
algorithm = GenerateDifficultyOperatorRegistry.instance.createInstanceOf(algorithmDropdown.value.id);
algorithm?.buildUI(vbox);

algorithmBox.addComponent(vbox);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ package funkin.ui.debug.charting.dialogs;
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogParams;
import funkin.ui.debug.charting.dialogs.ChartEditorBaseDialog.DialogDropTarget;
import funkin.ui.debug.charting.components.ChartEditorChannelItem;
import funkin.ui.debug.charting.util.ChartEditorDropdowns;
import funkin.ui.debug.charting.util.GenerateChartOperator;
import funkin.data.charting.GenerateChartOperatorRegistry;
import funkin.input.Cursor;
import haxe.ui.containers.dialogs.Dialogs;
import haxe.ui.containers.dialogs.Dialog.DialogButton;
import haxe.ui.containers.dialogs.Dialog.DialogEvent;
import haxe.ui.containers.VBox;
import haxe.ui.containers.Box;
import haxe.ui.containers.ScrollView;
import haxe.io.Path;
Expand All @@ -22,13 +26,23 @@ class ChartEditorGenerateChartDialog extends ChartEditorBaseDialog
{
var dropHandler:DialogDropTarget;
var midiEntry:ChartEditorGenerateChartMidiEntry;
var algorithm(default, set):GenerateChartOperator;

function set_algorithm(value:GenerateChartOperator):GenerateChartOperator
{
this.algorithm = value;
dialogHints.disabled = this.algorithm == null || this.midi == null;
dialogNotes.disabled = this.algorithm == null || this.midi == null;
return value;
}

var midi(default, set):Null<MidiFile>;

function set_midi(value:Null<MidiFile>):Null<MidiFile>
{
this.midi = value;
dialogHints.disabled = this.midi == null;
dialogNotes.disabled = this.midi == null;
dialogHints.disabled = this.algorithm == null || this.midi == null;
dialogNotes.disabled = this.algorithm == null || this.midi == null;

if (this.midi != null)
{
Expand Down Expand Up @@ -66,6 +80,13 @@ class ChartEditorGenerateChartDialog extends ChartEditorBaseDialog

channelView.addComponent(new ChartEditorChannelItem(channelView));

ChartEditorDropdowns.populateDropdownWithGenerateChartOperators(algorithmDropdown, 'defaultOperator');
algorithmDropdown.onChange = function(_) {
paramView.removeComponentAt(0);
buildAlgorithmParams();
}
buildAlgorithmParams();

buildDropHandler();

chartEditorState.isHaxeUIDialogOpen = true;
Expand Down Expand Up @@ -182,7 +203,13 @@ class ChartEditorGenerateChartDialog extends ChartEditorBaseDialog
}
}

chartEditorState.generateChartFromMidi({midi: midi, channels: channels, onlyHints: onlyHints});
chartEditorState.generateChartFromMidi(
{
algorithm: algorithm,
midi: midi,
channels: channels,
onlyHints: onlyHints
});
}

public override function onClose(event:DialogEvent):Void
Expand All @@ -205,6 +232,18 @@ class ChartEditorGenerateChartDialog extends ChartEditorBaseDialog
this.dialogCancel.disabled = false;
}

function buildAlgorithmParams():Void
{
var vbox:VBox = new VBox();
vbox.percentWidth = 100;

algorithm?.destroy();
algorithm = GenerateChartOperatorRegistry.instance.createInstanceOf(algorithmDropdown.value.id);
algorithm?.buildUI(vbox);

paramView.addComponent(vbox);
}

public static function build(state:ChartEditorState, ?closable:Bool, ?modal:Bool):ChartEditorGenerateChartDialog
{
var dialog = new ChartEditorGenerateChartDialog(state,
Expand Down
Loading
Loading