mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-01-24 21:47:20 +00:00
Work in progress on HaxeUI chart editor
This commit is contained in:
parent
f790cd8fd6
commit
eb3ad49a30
58
Project.xml
58
Project.xml
|
@ -123,63 +123,16 @@
|
|||
<haxelib name="flixel-addons" />
|
||||
<haxelib name="hscript" />
|
||||
|
||||
<!--In case you want to use the ui package-->
|
||||
<haxelib name="flixel-ui" />
|
||||
<!--haxelib name="newgrounds" unless="switch"/> -->
|
||||
<haxelib name="faxe" if='switch' />
|
||||
<haxelib name="haxeui-core"/>
|
||||
<haxelib name="haxeui-flixel"/>
|
||||
<haxelib name="polymod" />
|
||||
<haxelib name="flxanimate" />
|
||||
|
||||
<haxelib name="thx.semver" />
|
||||
|
||||
<!-- <haxelib name="colyseus"/> -->
|
||||
<!-- <haxelib name="colyseus-websocket" /> -->
|
||||
<!-- <haxelib name="newgrounds"/> -->
|
||||
<haxelib name="hxcpp-debug-server" if="desktop debug" />
|
||||
|
||||
<!-- swf stufffff -->
|
||||
<!-- <haxelib name="swf"/> -->
|
||||
<!-- <library path="assets/tanky.swf" preload="true"/> -->
|
||||
<!-- <library path="assets/tankBG.swf" preload="true"/> -->
|
||||
|
||||
<!-- <haxelib name="flixel-animate" /> -->
|
||||
<!-- <haxelib name="spinehaxe" /> -->
|
||||
<!-- https://github.com/ninjamuffin99/Flixel-Animate-Atlas-Player -->
|
||||
|
||||
|
||||
<!--<haxelib name="discord_rpc" if="cpp"/> -->
|
||||
<!-- foesn't work with neko -->
|
||||
<!-- <haxelib name="hxcpp-debug-server" if="desktop"/> -->
|
||||
|
||||
<!-- <haxelib name="markdown" /> -->
|
||||
<!-- <haxelib name="HtmlParser" /> -->
|
||||
|
||||
<!--In case you want to use nape with flixel-->
|
||||
<!--<haxelib name="nape-haxe4" />-->
|
||||
|
||||
<!-- ______________________________ Haxedefines _____________________________ -->
|
||||
|
||||
<!--Enable the Flixel core recording system-->
|
||||
<!--<haxedef name="FLX_RECORD" />-->
|
||||
|
||||
<!--Disable the right and middle mouse buttons-->
|
||||
<!-- <haxedef name="FLX_NO_MOUSE_ADVANCED" /> -->
|
||||
|
||||
<!--Disable the native cursor API on Flash-->
|
||||
<!--<haxedef name="FLX_NO_NATIVE_CURSOR" />-->
|
||||
|
||||
<!--Optimise inputs, be careful you will get null errors if you don't use conditionals in your game-->
|
||||
<!-- <haxedef name="FLX_NO_MOUSE" if="mobile" /> -->
|
||||
<!-- <haxedef name="FLX_NO_KEYBOARD" if="mobile" /> -->
|
||||
<!-- <haxedef name="FLX_NO_TOUCH" if="desktop" /> -->
|
||||
<!--<haxedef name="FLX_NO_GAMEPAD" />-->
|
||||
|
||||
<!--Disable the Flixel core sound tray-->
|
||||
<!--<haxedef name="FLX_NO_SOUND_TRAY" />-->
|
||||
|
||||
<!--Disable the Flixel sound management code-->
|
||||
<!--<haxedef name="FLX_NO_SOUND_SYSTEM" />-->
|
||||
|
||||
<!--Disable the Flixel core focus lost screen-->
|
||||
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
|
||||
|
||||
|
@ -198,6 +151,11 @@
|
|||
<haxeflag name="-dce no" />
|
||||
<haxeflag name="--macro" value="include('funkin')" />
|
||||
|
||||
<!-- Ensure all UI components are available at runtime. -->
|
||||
<haxeflag name="--macro" value="include('haxe.ui.components')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers')" />
|
||||
<haxeflag name="--macro" value="include('haxe.ui.containers.menus')" />
|
||||
|
||||
<!-- Necessary to provide stack traces for HScript. -->
|
||||
<haxedef name="hscriptPos" />
|
||||
<haxedef name="HXCPP_CHECK_POINTER" />
|
||||
|
@ -259,8 +217,6 @@
|
|||
<haxedef name="POLYMOD_SCRIPT_LIBRARY" value="scripts" />
|
||||
<!-- The base path from which scripts should be accessed. -->
|
||||
<haxedef name="POLYMOD_ROOT_PATH" value="scripts/" />
|
||||
<!-- Determines the precision required for mods to be compatible. -->
|
||||
<haxedef name="POLYMOD_API_VERSION_MATCH" value="MATCH_MINOR" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file appending. -->
|
||||
<haxedef name="POLYMOD_APPEND_FOLDER" value="_append" />
|
||||
<!-- Determines the subdirectory of the mod folder used for file merges. -->
|
||||
|
|
29
hmm.json
29
hmm.json
|
@ -21,6 +21,13 @@
|
|||
"ref": "a3877f0",
|
||||
"url": "https://github.com/MasterEric/flixel-addons"
|
||||
},
|
||||
{
|
||||
"name": "flixel-addons",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "dev",
|
||||
"url": "https://github.com/MasterEric/flixel-addons"
|
||||
},
|
||||
{
|
||||
"name": "flixel-ui",
|
||||
"type": "haxelib",
|
||||
|
@ -33,6 +40,20 @@
|
|||
"ref": "18b2060",
|
||||
"url": "https://github.com/Dot-Stuff/flxanimate"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-core",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "master",
|
||||
"url": "https://github.com/haxeui/haxeui-core/"
|
||||
},
|
||||
{
|
||||
"name": "haxeui-flixel",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "master",
|
||||
"url": "https://github.com/haxeui/haxeui-flixel"
|
||||
},
|
||||
{
|
||||
"name": "hmm",
|
||||
"type": "haxelib",
|
||||
|
@ -57,15 +78,15 @@
|
|||
"name": "lime",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "770bf0e",
|
||||
"ref": "develop",
|
||||
"url": "https://github.com/openfl/lime"
|
||||
},
|
||||
{
|
||||
"name": "flixel-addons",
|
||||
"name": "openfl",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "dev",
|
||||
"url": "https://github.com/MasterEric/flixel-addons"
|
||||
"ref": "develop",
|
||||
"url": "https://github.com/openfl/openfl"
|
||||
},
|
||||
{
|
||||
"name": "polymod",
|
||||
|
|
|
@ -2,8 +2,8 @@ package;
|
|||
|
||||
import flixel.FlxGame;
|
||||
import flixel.FlxState;
|
||||
import funkin.InitState;
|
||||
import funkin.MemoryCounter;
|
||||
import haxe.ui.Toolkit;
|
||||
import openfl.Lib;
|
||||
import openfl.display.FPS;
|
||||
import openfl.display.Sprite;
|
||||
|
@ -15,7 +15,7 @@ class Main extends Sprite
|
|||
{
|
||||
var gameWidth:Int = 1280; // Width of the game in pixels (might be less / more in actual pixels depending on your zoom).
|
||||
var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom).
|
||||
var initialState:Class<FlxState> = InitState; // The FlxState the game starts with.
|
||||
var initialState:Class<FlxState> = funkin.InitState; // The FlxState the game starts with.
|
||||
var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions.
|
||||
#if web
|
||||
var framerate:Int = 60; // How many frames per second the game should run at.
|
||||
|
@ -69,20 +69,6 @@ class Main extends Sprite
|
|||
|
||||
private function setupGame():Void
|
||||
{
|
||||
// Lib.current.stage.color = null;
|
||||
|
||||
var stageWidth:Int = Lib.current.stage.stageWidth;
|
||||
var stageHeight:Int = Lib.current.stage.stageHeight;
|
||||
|
||||
// if (zoom == -1)
|
||||
// {
|
||||
// var ratioX:Float = stageWidth / gameWidth;
|
||||
// var ratioY:Float = stageHeight / gameHeight;
|
||||
// zoom = Math.min(ratioX, ratioY);
|
||||
// gameWidth = Math.ceil(stageWidth / zoom);
|
||||
// gameHeight = Math.ceil(stageHeight / zoom);
|
||||
// }
|
||||
|
||||
/**
|
||||
* The `zoom` argument of FlxGame was removed in the dev branch of Flixel,
|
||||
* since it was considered confusing and unintuitive.
|
||||
|
@ -92,9 +78,11 @@ class Main extends Sprite
|
|||
*/
|
||||
|
||||
#if !debug
|
||||
initialState = TitleState;
|
||||
initialState = funkin.TitleState;
|
||||
#end
|
||||
|
||||
initHaxeUI();
|
||||
|
||||
addChild(new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen));
|
||||
|
||||
#if debug
|
||||
|
@ -105,53 +93,14 @@ class Main extends Sprite
|
|||
addChild(memoryCounter);
|
||||
#end
|
||||
#end
|
||||
|
||||
/*
|
||||
video = new Video();
|
||||
addChild(video);
|
||||
|
||||
var netConnection = new NetConnection();
|
||||
netConnection.connect(null);
|
||||
|
||||
netStream = new NetStream(netConnection);
|
||||
netStream.client = {onMetaData: client_onMetaData};
|
||||
netStream.addEventListener(AsyncErrorEvent.ASYNC_ERROR, netStream_onAsyncError);
|
||||
|
||||
#if (js && html5)
|
||||
overlay = new Sprite();
|
||||
overlay.graphics.beginFill(0, 0.5);
|
||||
overlay.graphics.drawRect(0, 0, 560, 320);
|
||||
overlay.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown);
|
||||
overlay.buttonMode = true;
|
||||
addChild(overlay);
|
||||
|
||||
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus);
|
||||
#else
|
||||
netStream.play("assets/preload/music/dredd.mp4");
|
||||
#end
|
||||
*/
|
||||
}
|
||||
/*
|
||||
private function client_onMetaData(metaData:Dynamic)
|
||||
{
|
||||
video.attachNetStream(netStream);
|
||||
|
||||
video.width = video.videoWidth;
|
||||
video.height = video.videoHeight;
|
||||
}
|
||||
|
||||
private function netStream_onAsyncError(event:AsyncErrorEvent):Void
|
||||
{
|
||||
trace("Error loading video");
|
||||
}
|
||||
|
||||
private function netConnection_onNetStatus(event:NetStatusEvent):Void
|
||||
{
|
||||
}
|
||||
|
||||
private function overlay_onMouseDown(event:MouseEvent):Void
|
||||
{
|
||||
netStream.play("assets/preload/music/dredd.mp4");
|
||||
}
|
||||
*/
|
||||
function initHaxeUI()
|
||||
{
|
||||
// Calling this before any HaxeUI components get used is important:
|
||||
// - It initializes the theme styles.
|
||||
// - It scans the class path and registers any HaxeUI components.
|
||||
Toolkit.init();
|
||||
Toolkit.theme = "dark"; // don't be cringe
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,17 +112,10 @@ class MainMenuState extends MusicBeatState
|
|||
persistentUpdate = false;
|
||||
openSubState(new FreeplayState());
|
||||
});
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
var hasPopupBlocker = #if web true #else false #end;
|
||||
|
||||
if (VideoState.seenVideo)
|
||||
{
|
||||
createMenuItem('kickstarter', 'mainmenu/kickstarter', selectDonate, hasPopupBlocker);
|
||||
}
|
||||
else
|
||||
{
|
||||
createMenuItem('donate', 'mainmenu/donate', selectDonate, hasPopupBlocker);
|
||||
}
|
||||
createMenuItem('donate', 'mainmenu/donate', selectDonate, hasPopupBlocker);
|
||||
#end
|
||||
|
||||
createMenuItem('options', 'mainmenu/options', function()
|
||||
|
@ -195,7 +188,7 @@ class MainMenuState extends MusicBeatState
|
|||
#if CAN_OPEN_LINKS
|
||||
function selectDonate()
|
||||
{
|
||||
WindowUtil.openURL(Constants.URL_KICKSTARTER);
|
||||
WindowUtil.openURL(Constants.URL_ITCH);
|
||||
}
|
||||
#end
|
||||
|
||||
|
|
|
@ -10,8 +10,7 @@ import funkin.Conductor.BPMChangeEvent;
|
|||
import funkin.modding.PolymodHandler;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||
import funkin.play.stage.StageData.StageDataParser;
|
||||
import funkin.ui.debug.DebugMenuSubState;
|
||||
import funkin.util.SortUtil;
|
||||
|
||||
/**
|
||||
|
@ -61,6 +60,15 @@ class MusicBeatState extends FlxUIState
|
|||
if (FlxG.keys.justPressed.F5)
|
||||
debug_refreshModules();
|
||||
|
||||
// ` / ~
|
||||
if (FlxG.keys.justPressed.GRAVEACCENT)
|
||||
{
|
||||
// TODO: Does this break anything?
|
||||
this.persistentUpdate = false;
|
||||
this.persistentDraw = false;
|
||||
FlxG.state.openSubState(new DebugMenuSubState());
|
||||
}
|
||||
|
||||
// everyStep();
|
||||
var oldStep:Int = curStep;
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package funkin;
|
||||
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.FlxSubState;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.Conductor.BPMChangeEvent;
|
||||
import funkin.modding.events.ScriptEvent;
|
||||
import funkin.modding.module.ModuleHandler;
|
||||
|
@ -73,6 +73,15 @@ class MusicBeatSubstate extends FlxSubState
|
|||
ModuleHandler.callEvent(event);
|
||||
}
|
||||
|
||||
/**
|
||||
* Close this substate and replace it with a different one.
|
||||
*/
|
||||
public function switchSubState(substate:FlxSubState):Void
|
||||
{
|
||||
this.close();
|
||||
this._parentState.openSubState(substate);
|
||||
}
|
||||
|
||||
public function beatHit():Bool
|
||||
{
|
||||
var event = new SongTimeScriptEvent(ScriptEvent.SONG_BEAT_HIT, curBeat, curStep);
|
||||
|
|
|
@ -117,6 +117,11 @@ class Paths
|
|||
return 'assets/fonts/$key';
|
||||
}
|
||||
|
||||
inline static public function ui(key:String, ?library:String)
|
||||
{
|
||||
return xml('ui/$key', library);
|
||||
}
|
||||
|
||||
static public function getSparrowAtlas(key:String, ?library:String)
|
||||
{
|
||||
return FlxAtlasFrames.fromSparrow(image(key, library), file('images/$key.xml', library));
|
||||
|
|
|
@ -866,8 +866,8 @@ class PlayState extends MusicBeatState
|
|||
for (songNotes in section.sectionNotes)
|
||||
{
|
||||
var daStrumTime:Float = songNotes.strumTime;
|
||||
// TODO: Replace 4 with strumlineSize
|
||||
var daNoteData:Int = Std.int(songNotes.noteData % 4);
|
||||
|
||||
var gottaHitNote:Bool = section.mustHitSection;
|
||||
|
||||
if (songNotes.highStakes) // noteData > 3
|
||||
|
@ -917,6 +917,7 @@ class PlayState extends MusicBeatState
|
|||
sustainNote.x += FlxG.width / 2; // general offset
|
||||
}
|
||||
|
||||
// TODO: Replace 4 with strumlineSize
|
||||
swagNote.mustPress = gottaHitNote;
|
||||
|
||||
if (swagNote.mustPress)
|
||||
|
@ -938,7 +939,7 @@ class PlayState extends MusicBeatState
|
|||
}
|
||||
else
|
||||
{
|
||||
swagNote.x += FlxG.width / 2; // general offset
|
||||
// swagNote.x += FlxG.width / 2; // general offset
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -223,6 +223,11 @@ class CharacterDataParser
|
|||
}
|
||||
}
|
||||
|
||||
public static function listCharacterIds():Array<String>
|
||||
{
|
||||
return [for (x in characterCache.keys()) x];
|
||||
}
|
||||
|
||||
static function clearCharacterCache():Void
|
||||
{
|
||||
if (characterCache != null)
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
package funkin.play.song;
|
||||
|
||||
import funkin.play.song.SongData.SongDataParser;
|
||||
import funkin.play.song.SongData.SongMetadata;
|
||||
|
||||
/**
|
||||
* This is a data structure managing information about the current song.
|
||||
* This structure is created when the game starts, and includes all the data
|
||||
|
@ -9,20 +12,21 @@ package funkin.play.song;
|
|||
* It also receives script events; scripted classes which extend this class
|
||||
* can be used to perform custom gameplay behaviors only on specific songs.
|
||||
*/
|
||||
class Song implements IPlayStateScriptedClass
|
||||
class Song // implements IPlayStateScriptedClass
|
||||
{
|
||||
public var songId(default, null):String;
|
||||
|
||||
public var songName(get, null):String;
|
||||
|
||||
final _metadata:SongMetadata;
|
||||
final _chartData:SongChartData;
|
||||
|
||||
// final _chartData:SongChartData;
|
||||
|
||||
public function new(id:String)
|
||||
{
|
||||
this.songId = songId;
|
||||
this.songId = id;
|
||||
|
||||
_metadata = SongDataParser.parseSongMetadata(this.songId);
|
||||
_metadata = SongDataParser.parseSongMetadata(songId);
|
||||
if (_metadata == null)
|
||||
{
|
||||
throw 'Could not find song data for songId: $songId';
|
||||
|
@ -35,4 +39,9 @@ class Song implements IPlayStateScriptedClass
|
|||
return null;
|
||||
return _metadata.name;
|
||||
}
|
||||
|
||||
public function toString():String
|
||||
{
|
||||
return 'Song($songId)';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
package funkin.play.song;
|
||||
|
||||
import funkin.util.assets.DataAssets;
|
||||
import openfl.utils.Assets;
|
||||
import thx.semver.Version;
|
||||
|
||||
using StringTools;
|
||||
|
||||
/**
|
||||
* Contains utilities for loading and parsing stage data.
|
||||
*/
|
||||
|
@ -15,7 +21,7 @@ class SongDataParser
|
|||
/**
|
||||
* A list containing all the songs available to the game.
|
||||
*/
|
||||
static final songCache:Map<String, Stage> = new Map<String, Stage>();
|
||||
static final songCache:Map<String, Song> = new Map<String, Song>();
|
||||
|
||||
static final DEFAULT_SONG_ID = 'UNKNOWN';
|
||||
|
||||
|
@ -59,11 +65,10 @@ class SongDataParser
|
|||
trace(' Instantiating ${unscriptedSongIds.length} non-scripted songs...');
|
||||
for (songId in unscriptedSongIds)
|
||||
{
|
||||
var song:Song;
|
||||
try
|
||||
{
|
||||
stage = new Song(songId);
|
||||
if (stage != null)
|
||||
var song = new Song(songId);
|
||||
if (song != null)
|
||||
{
|
||||
trace(' Loaded song data: ${song.songId}');
|
||||
songCache.set(song.songId, song);
|
||||
|
@ -83,7 +88,7 @@ class SongDataParser
|
|||
/**
|
||||
* Retrieves a particular song from the cache.
|
||||
*/
|
||||
public static function fetchStage(songId:String):Null<Song>
|
||||
public static function fetchSong(songId:String):Null<Song>
|
||||
{
|
||||
if (songCache.exists(songId))
|
||||
{
|
||||
|
@ -102,22 +107,20 @@ class SongDataParser
|
|||
{
|
||||
if (songCache != null)
|
||||
{
|
||||
for (song in songCache)
|
||||
{
|
||||
song.destroy();
|
||||
}
|
||||
songCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static function parseSongMetadata(songId:String):Null<SongMetadata>
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
static function loadSongMetadataFile(songPath:String, variant:String = ''):String
|
||||
{
|
||||
var songMetadataFilePath:String = Paths.json('stages/${stagePath}');
|
||||
var rawJson = Assets.getText(stageFilePath).trim();
|
||||
var songMetadataFilePath:String = (variant != '') ? Paths.json('songs/${songPath}') : Paths.json('songs/${songPath}');
|
||||
|
||||
var rawJson:String = Assets.getText(songMetadataFilePath).trim();
|
||||
|
||||
while (!rawJson.endsWith("}"))
|
||||
{
|
||||
|
@ -127,3 +130,25 @@ class SongDataParser
|
|||
return rawJson;
|
||||
}
|
||||
}
|
||||
|
||||
typedef SongMetadata =
|
||||
{
|
||||
var version:Version;
|
||||
|
||||
var songName:String;
|
||||
var artist:String;
|
||||
var timeFormat:SongTimeFormat;
|
||||
var divisions:Int;
|
||||
var timeChanges:Array<SongTimeChange>;
|
||||
};
|
||||
|
||||
typedef SongChartData =
|
||||
{
|
||||
};
|
||||
|
||||
enum abstract SongTimeFormat(String) from String to String
|
||||
{
|
||||
var TICKS = "ticks";
|
||||
var FLOAT = "float";
|
||||
var MILLISECONDS = "ms";
|
||||
}
|
||||
|
|
|
@ -375,8 +375,11 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
|
|||
|
||||
// Add the character to the scene.
|
||||
this.add(character);
|
||||
|
||||
#if debug
|
||||
debugIconGroup.add(debugIcon);
|
||||
debugIconGroup.add(debugIcon2);
|
||||
#end
|
||||
}
|
||||
|
||||
public inline function getGirlfriendPosition():FlxPoint
|
||||
|
|
|
@ -141,6 +141,11 @@ class StageDataParser
|
|||
return validateStageData(stageId, stageData);
|
||||
}
|
||||
|
||||
public static function listStageIds():Array<String>
|
||||
{
|
||||
return [for (x in stageCache.keys()) x];
|
||||
}
|
||||
|
||||
static function loadStageFile(stagePath:String):String
|
||||
{
|
||||
var stageFilePath:String = Paths.json('stages/${stagePath}');
|
||||
|
|
|
@ -27,7 +27,7 @@ class OptionsState extends MusicBeatState
|
|||
menuBG.scrollFactor.set(0, 0);
|
||||
add(menuBG);
|
||||
|
||||
var options = addPage(Options, new OptionsMenu(false));
|
||||
var options = addPage(Options, new OptionsMenu());
|
||||
var preferences = addPage(Preferences, new PreferencesMenu());
|
||||
var controls = addPage(Controls, new ControlsMenu());
|
||||
|
||||
|
@ -167,22 +167,14 @@ class OptionsMenu extends Page
|
|||
{
|
||||
var items:TextMenuList;
|
||||
|
||||
public function new(showDonate:Bool)
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
|
||||
add(items = new TextMenuList());
|
||||
createItem("PREFERENCES", function() switchPage(Preferences));
|
||||
createItem("CONTROLS", function() switchPage(Controls));
|
||||
// createItem("COLORS", function() switchPage(Colors));
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
if (showDonate)
|
||||
{
|
||||
var hasPopupBlocker = #if web true #else false #end;
|
||||
createItem("DONATE", selectDonate, hasPopupBlocker);
|
||||
}
|
||||
#end
|
||||
#if newgrounds
|
||||
if (NGio.isLoggedIn)
|
||||
createItem("LOGOUT", selectLogout);
|
||||
|
@ -215,13 +207,6 @@ class OptionsMenu extends Page
|
|||
return items.length > 2;
|
||||
}
|
||||
|
||||
#if CAN_OPEN_LINKS
|
||||
function selectDonate()
|
||||
{
|
||||
WindowUtil.openURL(Constants.URL_ITCH);
|
||||
}
|
||||
#end
|
||||
|
||||
#if newgrounds
|
||||
function selectLogin()
|
||||
{
|
||||
|
|
95
source/funkin/ui/debug/DebugMenuSubState.hx
Normal file
95
source/funkin/ui/debug/DebugMenuSubState.hx
Normal file
|
@ -0,0 +1,95 @@
|
|||
package funkin.ui.debug;
|
||||
|
||||
import flixel.FlxObject;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.MusicBeatSubstate;
|
||||
import funkin.ui.TextMenuList;
|
||||
import funkin.ui.debug.charting.ChartEditorState;
|
||||
|
||||
class DebugMenuSubState extends MusicBeatSubstate
|
||||
{
|
||||
var items:TextMenuList;
|
||||
|
||||
/**
|
||||
* Camera focus point
|
||||
*/
|
||||
var camFocusPoint:FlxObject;
|
||||
|
||||
override function create()
|
||||
{
|
||||
super.create();
|
||||
|
||||
// Create an object for the camera to track.
|
||||
camFocusPoint = new FlxObject(0, 0);
|
||||
add(camFocusPoint);
|
||||
|
||||
// Follow the camera focus as we scroll.
|
||||
FlxG.camera.follow(camFocusPoint, null, 0.06);
|
||||
|
||||
// Create the green background.
|
||||
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
|
||||
menuBG.color = 0xFF4CAF50;
|
||||
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
|
||||
menuBG.updateHitbox();
|
||||
menuBG.screenCenter();
|
||||
menuBG.scrollFactor.set(0, 0);
|
||||
add(menuBG);
|
||||
|
||||
// Create the list for menu items.
|
||||
items = new TextMenuList();
|
||||
// Move the camera when the menu is scrolled.
|
||||
items.onChange.add(onMenuChange);
|
||||
add(items);
|
||||
|
||||
// Create each menu item.
|
||||
// Call onMenuChange when the first item is created to move the camera .
|
||||
onMenuChange(createItem("CHART EDITOR", openChartEditor));
|
||||
createItem("ANIMATION EDITOR", openAnimationEditor);
|
||||
createItem("STAGE EDITOR", openStageEditor);
|
||||
}
|
||||
|
||||
function onMenuChange(selected:TextMenuItem)
|
||||
{
|
||||
camFocusPoint.setPosition(selected.x + selected.width / 2, selected.y + selected.height / 2);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (controls.BACK)
|
||||
{
|
||||
FlxG.sound.play(Paths.sound('cancelMenu'));
|
||||
exitDebugMenu();
|
||||
}
|
||||
}
|
||||
|
||||
function createItem(name:String, callback:Void->Void, fireInstantly = false)
|
||||
{
|
||||
var item = items.createItem(0, 100 + items.length * 100, name, BOLD, callback);
|
||||
item.fireInstantly = fireInstantly;
|
||||
item.screenCenter(X);
|
||||
return item;
|
||||
}
|
||||
|
||||
function openChartEditor()
|
||||
{
|
||||
FlxG.switchState(new ChartEditorState());
|
||||
}
|
||||
|
||||
function openAnimationEditor()
|
||||
{
|
||||
trace('Animation Editor');
|
||||
}
|
||||
|
||||
function openStageEditor()
|
||||
{
|
||||
trace('Stage Editor');
|
||||
}
|
||||
|
||||
function exitDebugMenu()
|
||||
{
|
||||
// TODO: Add a transition?
|
||||
this.close();
|
||||
}
|
||||
}
|
237
source/funkin/ui/debug/charting/ChartEditorState.hx
Normal file
237
source/funkin/ui/debug/charting/ChartEditorState.hx
Normal file
|
@ -0,0 +1,237 @@
|
|||
package funkin.ui.debug.charting;
|
||||
|
||||
import flixel.FlxSprite;
|
||||
import flixel.addons.display.FlxGridOverlay;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
import flixel.util.FlxColor;
|
||||
import funkin.ui.haxeui.HaxeUIState;
|
||||
import haxe.ui.containers.dialogs.Dialog;
|
||||
import haxe.ui.containers.menus.MenuItem;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.events.MouseEvent;
|
||||
import openfl.display.BitmapData;
|
||||
|
||||
class ChartEditorState extends HaxeUIState
|
||||
{
|
||||
static final CHART_EDITOR_LAYOUT = Paths.ui('chart-editor/main-view');
|
||||
|
||||
/**
|
||||
* The number of notes on each character's strumline.
|
||||
* TODO: Refactor this logic for larger strumlines in the future.
|
||||
*/
|
||||
static final STRUMLINE_SIZE = 4;
|
||||
|
||||
/**
|
||||
* The height of the menu bar in pixels. Used for positioning UI components.
|
||||
*/
|
||||
static final MENU_BAR_HEIGHT = 32;
|
||||
|
||||
/**
|
||||
* The width (and height) of each grid square, in pixels.
|
||||
*/
|
||||
static final GRID_SIZE:Int = 40;
|
||||
|
||||
/**
|
||||
* Pixel distance between the menu bar and the start of the chart grid.
|
||||
*/
|
||||
static final GRID_TOP_PAD:Int = 8;
|
||||
|
||||
static final GRID_ALTERNATE:Bool = true;
|
||||
static final GRID_COLOR_1:FlxColor = 0xFFE7E6E6;
|
||||
static final GRID_COLOR_2:FlxColor = 0xFFD9D5D5;
|
||||
|
||||
var gridBitmap:BitmapData;
|
||||
var gridSprites:FlxSpriteGroup;
|
||||
var gridDividerA:FlxSprite;
|
||||
var gridDividerB:FlxSprite;
|
||||
|
||||
var menuBG:FlxSprite;
|
||||
|
||||
// TODO: Make the unit of measurement for this non-arbitrary
|
||||
// to assist with logic later.
|
||||
var scrollPosition(default, set):Float = -1.0;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super(CHART_EDITOR_LAYOUT);
|
||||
}
|
||||
|
||||
override function create()
|
||||
{
|
||||
FlxG.sound.music.stop();
|
||||
|
||||
buildBackground();
|
||||
buildGrid();
|
||||
|
||||
super.create();
|
||||
|
||||
setupMenuListeners();
|
||||
|
||||
scrollPosition = 0;
|
||||
}
|
||||
|
||||
function buildBackground()
|
||||
{
|
||||
menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
|
||||
add(menuBG);
|
||||
menuBG.color = 0xFF673ab7;
|
||||
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
|
||||
menuBG.updateHitbox();
|
||||
menuBG.screenCenter();
|
||||
menuBG.scrollFactor.set(0, 0);
|
||||
}
|
||||
|
||||
function buildGrid()
|
||||
{
|
||||
// The checkerboard background image of the chart.
|
||||
// 2 * (Strumline Size) + 1 grid squares wide, by 2 grid squares tall.
|
||||
// This gets reused to fill the screen.
|
||||
gridBitmap = FlxGridOverlay.createGrid(GRID_SIZE, GRID_SIZE, GRID_SIZE * (STRUMLINE_SIZE * 2 + 1), GRID_SIZE * 2, GRID_ALTERNATE, GRID_COLOR_1,
|
||||
GRID_COLOR_2);
|
||||
|
||||
gridSprites = new FlxSpriteGroup();
|
||||
add(gridSprites);
|
||||
|
||||
for (i in 0...10)
|
||||
{
|
||||
var gridSprite = new FlxSprite().loadGraphic(gridBitmap);
|
||||
gridSprite.x = FlxG.width / 2 - GRID_SIZE * STRUMLINE_SIZE; // Center the grid.
|
||||
gridSprite.y = MENU_BAR_HEIGHT + GRID_TOP_PAD + (i * gridSprite.height); // Push down to account for the menu bar.
|
||||
gridSprites.add(gridSprite);
|
||||
}
|
||||
|
||||
// The black divider between the two halves of the chart.
|
||||
gridDividerA = new FlxSprite(gridSprites.members[0].x + GRID_SIZE * STRUMLINE_SIZE,
|
||||
MENU_BAR_HEIGHT).makeGraphic(2, FlxG.height - MENU_BAR_HEIGHT, FlxColor.BLACK);
|
||||
add(gridDividerA);
|
||||
gridDividerB = new FlxSprite(gridSprites.members[0].x + GRID_SIZE * STRUMLINE_SIZE * 2,
|
||||
MENU_BAR_HEIGHT).makeGraphic(2, FlxG.height - MENU_BAR_HEIGHT, FlxColor.BLACK);
|
||||
add(gridDividerB);
|
||||
}
|
||||
|
||||
public override function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
FlxG.mouse.visible = true;
|
||||
|
||||
handleScroll();
|
||||
|
||||
if (FlxG.keys.justPressed.B)
|
||||
toggleSidebar();
|
||||
}
|
||||
|
||||
function handleScroll()
|
||||
{
|
||||
var scrollAmount:Float = 0;
|
||||
|
||||
if (FlxG.keys.justPressed.UP)
|
||||
{
|
||||
scrollAmount = -10;
|
||||
}
|
||||
if (FlxG.keys.justPressed.DOWN)
|
||||
{
|
||||
scrollAmount = 10;
|
||||
}
|
||||
if (FlxG.mouse.wheel != 0)
|
||||
{
|
||||
scrollAmount = -10 * FlxG.mouse.wheel;
|
||||
}
|
||||
|
||||
if (FlxG.keys.pressed.SHIFT)
|
||||
{
|
||||
scrollAmount *= 10;
|
||||
}
|
||||
if (FlxG.keys.pressed.CONTROL)
|
||||
{
|
||||
scrollAmount /= 10;
|
||||
}
|
||||
|
||||
this.scrollPosition += scrollAmount;
|
||||
}
|
||||
|
||||
function set_scrollPosition(value:Float):Float
|
||||
{
|
||||
// TODO: Calculate this.
|
||||
var MAX_SCROLL = 10000;
|
||||
if (value == scrollPosition || value < 0 || value > MAX_SCROLL)
|
||||
return scrollPosition;
|
||||
|
||||
this.scrollPosition = value;
|
||||
|
||||
trace('SCROLL: $scrollPosition');
|
||||
|
||||
// Move the grid sprites to the correct position.
|
||||
gridSprites.y = -scrollPosition;
|
||||
|
||||
// Nudge the grid dividers down if needed.
|
||||
if (-gridSprites.y < GRID_TOP_PAD)
|
||||
{
|
||||
gridDividerA.y = MENU_BAR_HEIGHT + GRID_TOP_PAD + gridSprites.y;
|
||||
gridDividerB.y = MENU_BAR_HEIGHT + GRID_TOP_PAD + gridSprites.y;
|
||||
}
|
||||
else
|
||||
{
|
||||
gridDividerA.y = MENU_BAR_HEIGHT;
|
||||
gridDividerB.y = MENU_BAR_HEIGHT;
|
||||
}
|
||||
|
||||
// Rearrange grid sprites so they stay on screen.
|
||||
gridSprites.forEachAlive(function(sprite:FlxSprite)
|
||||
{
|
||||
// If this grid sprite is off the top of the screen...
|
||||
if (sprite.y + sprite.height < MENU_BAR_HEIGHT)
|
||||
{
|
||||
// Move it to the bottom of the screen.
|
||||
sprite.y += sprite.height * gridSprites.length;
|
||||
}
|
||||
// If this grid sprite is off the bottom of the screen...
|
||||
if (sprite.y > FlxG.height)
|
||||
{
|
||||
// Move it to the top of the screen.
|
||||
sprite.y -= sprite.height * gridSprites.length;
|
||||
}
|
||||
});
|
||||
|
||||
// TODO: Add a clip rectangle to the FlxSpriteGroup to hide the grid sprites that got moved up,
|
||||
// when we scroll back to the top of the chart.
|
||||
// Note that clip rectangles on sprite groups are borken right now, so we'll have to wait for that to be fixed.
|
||||
|
||||
return this.scrollPosition;
|
||||
}
|
||||
|
||||
function toggleSidebar()
|
||||
{
|
||||
var sidebar:Component = this.component.findComponent('sidebar', Component);
|
||||
|
||||
sidebar.visible = !sidebar.visible;
|
||||
}
|
||||
|
||||
function openDialog(key:String, modal:Bool = true)
|
||||
{
|
||||
var dialog:Dialog = cast buildComponent(Paths.ui(key));
|
||||
|
||||
// modal = true makes the background unclickable
|
||||
dialog.showDialog(modal);
|
||||
}
|
||||
|
||||
function setupMenuListeners()
|
||||
{
|
||||
addMenuListener('menubarItemToggleSidebar', (event:MouseEvent) -> toggleSidebar());
|
||||
addMenuListener('menubarItemAbout', (event:MouseEvent) -> openDialog('chart-editor/dialogs/about'));
|
||||
addMenuListener('menubarItemUserGuide', (event:MouseEvent) -> openDialog('chart-editor/dialogs/user-guide'));
|
||||
}
|
||||
|
||||
function addMenuListener(key:String, callback:MouseEvent->Void)
|
||||
{
|
||||
var menuItem:MenuItem = this.component.findComponent(key, MenuItem);
|
||||
if (menuItem == null)
|
||||
{
|
||||
trace('WARN: Could not locate menu item: $key');
|
||||
}
|
||||
else
|
||||
{
|
||||
menuItem.onClick = callback;
|
||||
}
|
||||
}
|
||||
}
|
48
source/funkin/ui/haxeui/HaxeUIState.hx
Normal file
48
source/funkin/ui/haxeui/HaxeUIState.hx
Normal file
|
@ -0,0 +1,48 @@
|
|||
package funkin.ui.haxeui;
|
||||
|
||||
import haxe.ui.RuntimeComponentBuilder;
|
||||
import haxe.ui.core.Component;
|
||||
|
||||
class HaxeUIState extends MusicBeatState
|
||||
{
|
||||
public var component:Component;
|
||||
|
||||
var _componentKey:String;
|
||||
|
||||
public function new(key:String)
|
||||
{
|
||||
super();
|
||||
_componentKey = key;
|
||||
}
|
||||
|
||||
override function create()
|
||||
{
|
||||
super.create();
|
||||
|
||||
if (component == null)
|
||||
component = buildComponent(_componentKey);
|
||||
if (component != null)
|
||||
add(component);
|
||||
}
|
||||
|
||||
public function buildComponent(assetPath:String)
|
||||
{
|
||||
try
|
||||
{
|
||||
return RuntimeComponentBuilder.fromAsset(assetPath);
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
trace('[ERROR] Failed to build component from asset: ' + assetPath);
|
||||
trace(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
override function destroy()
|
||||
{
|
||||
if (component != null)
|
||||
remove(component);
|
||||
component = null;
|
||||
}
|
||||
}
|
93
source/funkin/ui/haxeui/HaxeUISubState.hx
Normal file
93
source/funkin/ui/haxeui/HaxeUISubState.hx
Normal file
|
@ -0,0 +1,93 @@
|
|||
package funkin.ui.haxeui;
|
||||
|
||||
import haxe.ui.RuntimeComponentBuilder;
|
||||
import haxe.ui.core.Component;
|
||||
|
||||
class HaxeUISubState extends MusicBeatSubstate
|
||||
{
|
||||
// The component representing the main UI.
|
||||
public var component:Component;
|
||||
|
||||
var _componentKey:String;
|
||||
|
||||
public function new(key:String)
|
||||
{
|
||||
super();
|
||||
_componentKey = key;
|
||||
}
|
||||
|
||||
override function create()
|
||||
{
|
||||
super.create();
|
||||
|
||||
refreshComponent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a component from a given XML file.
|
||||
* Call this in your code to load additional components at runtime.
|
||||
*/
|
||||
public function buildComponent(assetPath:String)
|
||||
{
|
||||
trace('Building component $assetPath');
|
||||
return RuntimeComponentBuilder.fromAsset(assetPath);
|
||||
}
|
||||
|
||||
override function update(elapsed:Float)
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
// Force quit.
|
||||
if (FlxG.keys.justPressed.F4)
|
||||
FlxG.switchState(new MainMenuState());
|
||||
|
||||
// Refresh the component.
|
||||
if (FlxG.keys.justPressed.F5)
|
||||
{
|
||||
refreshComponent();
|
||||
}
|
||||
}
|
||||
|
||||
function refreshComponent()
|
||||
{
|
||||
/*
|
||||
if (component != null)
|
||||
{
|
||||
remove(component);
|
||||
component = null;
|
||||
}
|
||||
|
||||
if (component != null)
|
||||
{
|
||||
trace('Success!');
|
||||
add(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('Failed to build component $_componentKey');
|
||||
}
|
||||
*/
|
||||
|
||||
if (component == null)
|
||||
{
|
||||
component = buildComponent(_componentKey);
|
||||
add(component);
|
||||
trace(component);
|
||||
}
|
||||
else
|
||||
{
|
||||
var component2 = buildComponent(_componentKey);
|
||||
component2.x += 100;
|
||||
add(component2);
|
||||
trace(component2);
|
||||
remove(component);
|
||||
}
|
||||
}
|
||||
|
||||
override function destroy()
|
||||
{
|
||||
if (component != null)
|
||||
remove(component);
|
||||
component = null;
|
||||
}
|
||||
}
|
101
source/funkin/ui/haxeui/components/TabSideBar.hx
Normal file
101
source/funkin/ui/haxeui/components/TabSideBar.hx
Normal file
|
@ -0,0 +1,101 @@
|
|||
package funkin.ui.haxeui.components;
|
||||
|
||||
import haxe.ui.Toolkit;
|
||||
import haxe.ui.containers.SideBar;
|
||||
import haxe.ui.core.Component;
|
||||
import haxe.ui.core.Screen;
|
||||
import haxe.ui.styles.elements.AnimationKeyFrame;
|
||||
import haxe.ui.styles.elements.AnimationKeyFrames;
|
||||
import haxe.ui.styles.elements.Directive;
|
||||
|
||||
class TabSideBar extends SideBar
|
||||
{
|
||||
var closeButton:Component;
|
||||
|
||||
public function new()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
inline function getCloseButton()
|
||||
{
|
||||
if (closeButton == null)
|
||||
{
|
||||
closeButton = findComponent("closeSideBar", Component);
|
||||
}
|
||||
return closeButton;
|
||||
}
|
||||
|
||||
public override function hide()
|
||||
{
|
||||
var animation = Toolkit.styleSheet.findAnimation("sideBarRestoreContent");
|
||||
var first:AnimationKeyFrame = animation.keyFrames[0];
|
||||
var last:AnimationKeyFrame = animation.keyFrames[animation.keyFrames.length - 1];
|
||||
var rootComponent = Screen.instance.rootComponents[0];
|
||||
|
||||
first.set(new Directive("left", Value.VDimension(Dimension.PX(rootComponent.left))));
|
||||
first.set(new Directive("top", Value.VDimension(Dimension.PX(rootComponent.top))));
|
||||
first.set(new Directive("width", Value.VDimension(Dimension.PX(rootComponent.width))));
|
||||
first.set(new Directive("height", Value.VDimension(Dimension.PX(rootComponent.height))));
|
||||
|
||||
last.set(new Directive("left", Value.VDimension(Dimension.PX(0))));
|
||||
last.set(new Directive("top", Value.VDimension(Dimension.PX(0))));
|
||||
last.set(new Directive("width", Value.VDimension(Dimension.PX(Screen.instance.width))));
|
||||
last.set(new Directive("height", Value.VDimension(Dimension.PX(Screen.instance.height))));
|
||||
|
||||
for (r in Screen.instance.rootComponents)
|
||||
{
|
||||
if (r.classes.indexOf("sidebar") == -1)
|
||||
{
|
||||
r.swapClass("sideBarRestoreContent", "sideBarModifyContent");
|
||||
r.onAnimationEnd = function(_)
|
||||
{
|
||||
r.restorePercentSizes();
|
||||
r.onAnimationEnd = null;
|
||||
rootComponent.removeClass("sideBarRestoreContent");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
hideSideBar();
|
||||
}
|
||||
|
||||
private override function hideSideBar()
|
||||
{
|
||||
var showSideBarClass = null;
|
||||
var hideSideBarClass = null;
|
||||
if (position == "left")
|
||||
{
|
||||
showSideBarClass = "showSideBarLeft";
|
||||
hideSideBarClass = "hideSideBarLeft";
|
||||
}
|
||||
else if (position == "right")
|
||||
{
|
||||
showSideBarClass = "showSideBarRight";
|
||||
hideSideBarClass = "hideSideBarRight";
|
||||
}
|
||||
else if (position == "top")
|
||||
{
|
||||
showSideBarClass = "showSideBarTop";
|
||||
hideSideBarClass = "hideSideBarTop";
|
||||
}
|
||||
else if (position == "bottom")
|
||||
{
|
||||
showSideBarClass = "showSideBarBottom";
|
||||
hideSideBarClass = "hideSideBarBottom";
|
||||
}
|
||||
|
||||
this.onAnimationEnd = function(_)
|
||||
{
|
||||
this.removeClass(hideSideBarClass);
|
||||
// onHideAnimationEnd();
|
||||
}
|
||||
|
||||
this.swapClass(hideSideBarClass, showSideBarClass);
|
||||
|
||||
if (modal == true)
|
||||
{
|
||||
hideModalOverlay();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -20,6 +20,9 @@ class Constants
|
|||
|
||||
public static final FREAKY_MENU_BPM = 102;
|
||||
|
||||
// Change this if you're making an engine.
|
||||
public static final TITLE = "Friday Night Funkin'";
|
||||
|
||||
#if debug
|
||||
public static final GIT_HASH = funkin.util.macro.GitCommit.getGitCommitHash();
|
||||
|
||||
|
@ -35,5 +38,5 @@ class Constants
|
|||
#end
|
||||
|
||||
public static final URL_KICKSTARTER:String = "https://www.kickstarter.com/projects/funkin/friday-night-funkin-the-full-ass-game/";
|
||||
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin";
|
||||
public static final URL_ITCH:String = "https://ninja-muffin24.itch.io/funkin/purchase";
|
||||
}
|
||||
|
|
22
source/module.xml
Normal file
22
source/module.xml
Normal file
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<module>
|
||||
<!-- A module provides additional behavior and configuration for HaxeUI. -->
|
||||
<components>
|
||||
<!--
|
||||
Ensure all components get included at compilation time.
|
||||
This needs to be done HERE and not via the `include` macro because `Toolkit.init()`
|
||||
reads this to build the component registry.
|
||||
-->
|
||||
<class package="haxe.ui.core" loadAll="true" />
|
||||
|
||||
<class package="haxe.ui.components" loadAll="true" />
|
||||
|
||||
<class package="haxe.ui.containers" loadAll="true" />
|
||||
<class package="haxe.ui.containers.menus" loadAll="true" />
|
||||
<class package="haxe.ui.containers.dialogs" loadAll="true" />
|
||||
<class package="haxe.ui.containers.properties" loadAll="true" />
|
||||
|
||||
<!-- Custom components. -->
|
||||
<class package="funkin.ui.haxeui.components" loadAll="true" />
|
||||
</components>
|
||||
</module>
|
Loading…
Reference in a new issue