Skip to content

Commit 26cc1db

Browse files
committed
subfolders!
1 parent e56ed07 commit 26cc1db

11 files changed

Lines changed: 172 additions & 31 deletions

File tree

assets/images/editors/folder.png

618 Bytes
Loading

assets/languages/en/Editors.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@
4444
<group name="ChartEditor">
4545
<group name="CharterSelection" prefix="charterSelection.">
4646
<str id="desc">Select a song to modify the charts from.</str>
47+
<str id="acceptFolder">Press ACCEPT to edit this folder.</str>
48+
<str id="desc-folder">Select a song to modify the charts from in {0}</str>
4749
<str id="acceptSong">Press ACCEPT to choose a difficulty to edit.</str>
4850
<str id="acceptVariation">Press ACCEPT to choose a song variation to edit.</str>
4951
<str id="acceptDifficulty">Press ACCEPT to edit the chart for the selected difficulty.</str>
@@ -470,6 +472,8 @@
470472
<str id="newTypeface">New Typeface</str>
471473
<str id="newTypefaceDesc">Press ACCEPT to create a new typeface.</str>
472474
<str id="desc">Select a typeface to edit</str>
475+
<str id="desc-folder">Select a typeface to edit in {0}</str>
476+
<str id="acceptFolder">Press ACCEPT to edit this folder.</str>
473477

474478
<group name="Warnings" prefix="warnings.">
475479
<str id="notImplemented-title">New Typeface: Feature Not Implemented!</str>
@@ -530,6 +534,8 @@
530534
<str id="acceptStage">Press ACCEPT to edit this stage.</str>
531535
<str id="newStage">New Stage</str>
532536
<str id="acceptNewStage">Press ACCEPT to create a new stage.</str>
537+
<str id="acceptFolder">Press ACCEPT to edit this folder.</str>
538+
<str id="desc-folder">Select a stage to edit in {0}</str>
533539
</group>
534540

535541
<group name="StageEditor" prefix="stageEditor.">

source/funkin/backend/chart/Chart.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ class Chart {
8585
}
8686

8787
inline public static function defaultChartMetaFields(data:ChartMetaData):ChartMetaData {
88-
data.setFieldDefault("displayName", data.name);
88+
data.setFieldDefault("displayName", CoolUtil.getFilename(data.name));
8989

9090
data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
9191
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);

source/funkin/editors/alphabet/AlphabetSelection.hx

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import haxe.xml.Access;
55
import funkin.game.Character;
66
import funkin.editors.EditorTreeMenu;
77
import funkin.options.type.NewOption;
8+
import funkin.options.type.FolderOption;
89
import funkin.options.type.TextOption;
910
import funkin.options.type.OptionType;
1011

@@ -24,12 +25,43 @@ class AlphabetSelectionScreen extends EditorTreeMenuScreen {
2425
]));
2526
});
2627

27-
var modsList:Array<String> = [];
28-
for (file in Paths.getFolderContent('data/alphabet/', true, BOTH)) // mods ? MODS : BOTH
29-
if (Path.extension(file) == "xml") modsList.push(CoolUtil.getFilename(file));
28+
var modsList:Array<String> = getAlphabetList();
3029

31-
for (typeface in modsList)
32-
add(new AlphabetIconOption(typeface, getID('acceptTypeface'), typeface, () -> FlxG.switchState(new AlphabetEditor(typeface))));
30+
function generateList(modsList:Array<String>, folderPath:String = ""):Array<FlxSprite> {
31+
var list:Array<FlxSprite> = [];
32+
33+
for (char in modsList) {
34+
if (char.endsWith("/")) {
35+
var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
36+
37+
list.push(new FolderOption(folderName + ' >', getID('acceptFolder'), () -> {
38+
var newModsList = getAlphabetList(char);
39+
var newList:Array<FlxSprite> = generateList(newModsList, folderPath + folderName + "/");
40+
parent.addMenu(new EditorTreeMenuScreen(folderPath + folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
41+
}));
42+
}
43+
else {
44+
list.push(new AlphabetIconOption(char, getID('acceptTypeface'), folderPath + char, () -> FlxG.switchState(new AlphabetEditor(folderPath + char))));
45+
}
46+
}
47+
48+
return list;
49+
}
50+
51+
for (o in generateList(modsList)) add(o);
52+
}
53+
54+
// this is a duplicate of Character.getList, but using that would cause confusion and yea
55+
public function getAlphabetList(folder:String = 'data/alphabet/'):Array<String> {
56+
var list:Array<String> = [];
57+
for (path in Paths.getFolderDirectories(folder, true, BOTH)) {
58+
if(!path.endsWith("/")) path += "/";
59+
list.push(path);
60+
}
61+
for (path in Paths.getFolderContent(folder, true, BOTH))
62+
if (Path.extension(path) == "xml")
63+
list.push(CoolUtil.getFilename(path));
64+
return list;
3365
}
3466
}
3567

source/funkin/editors/character/CharacterSelection.hx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import funkin.editors.ui.UIImageExplorer.ImageSaveData;
66
import funkin.editors.EditorTreeMenu;
77
import funkin.options.type.IconOption;
88
import funkin.options.type.NewOption;
9+
import funkin.options.type.FolderOption;
910
import funkin.options.type.TextOption;
1011
import funkin.options.type.OptionType;
1112

@@ -40,10 +41,10 @@ class CharacterSelectionScreen extends EditorTreeMenuScreen {
4041
if (char.endsWith("/")) {
4142
var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
4243

43-
list.push(new TextOption(folderName, getID('acceptFolder'), ' >', () -> {
44+
list.push(new FolderOption(folderName + ' >', getID('acceptFolder'), () -> {
4445
var newModsList = Character.getList(isMods, true, char);
4546
var newList:Array<FlxSprite> = generateList(newModsList, isMods, folderPath + folderName + "/");
46-
parent.addMenu(new EditorTreeMenuScreen(folderPath + folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
47+
parent.addMenu(new EditorTreeMenuScreen(folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
4748
}));
4849
}
4950
else {

source/funkin/editors/charter/CharterSelection.hx

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
3737
curSong = s;
3838

3939
var isVariant = s.variant != null && s.variant != '';
40-
var screen = new EditorTreeMenuScreen((first || !isVariant) ? (s.name + (isVariant ? ' (${s.variant})' : '')) : s.variant, getID('selectDifficulty'));
40+
var screen = new EditorTreeMenuScreen((first || !isVariant) ? (CoolUtil.getFilename(s.name) + (isVariant ? ' (${s.variant})' : '')) : s.variant, getID('selectDifficulty'));
4141

4242
for (d in s.difficulties) if (d != '') screen.add(makeChartOption(d, isVariant ? s.variant : null, s.name));
4343
if (s.difficulties.length > 0 && s.variants.length > 0) screen.add(new Separator()); // Create a separator only when there are both difficulty and variant options available.
@@ -64,7 +64,7 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
6464
public function makeSongOption(s:ChartMetaData):IconOption {
6565
songList.push(s.name.toLowerCase());
6666

67-
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> openSongOption(s, true));
67+
var opt = new IconOption(CoolUtil.getFilename(s.name), getID('acceptSong'), s.icon, () -> openSongOption(s, true));
6868
opt.suffix = " >";
6969
opt.editorFlashColor = s.color.getDefault(FlxColor.WHITE);
7070

@@ -75,9 +75,30 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
7575
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', #if sys () -> {
7676
parent.openSubState(new SongCreationScreen(saveSong));
7777
} #end);
78-
freeplayList = FreeplaySonglist.get(false);
78+
freeplayList = FreeplaySonglist.get(false, 'songs/', false);
79+
80+
function generateList(modsList:Array<ChartMetaData>, folderPath:String = ""):Array<FlxSprite> {
81+
var list:Array<FlxSprite> = [];
82+
83+
for (char in modsList) {
84+
if (char.name.endsWith("/")) {
85+
var folderName = CoolUtil.getFilename(char.name.substr(0, char.name.length-1));
86+
87+
list.push(new FolderOption(folderName + ' >', getID('acceptFolder'), () -> {
88+
var newModsList = FreeplaySonglist.get(false, 'songs/' + folderPath + folderName + '/', false).songs;
89+
var newList:Array<FlxSprite> = generateList(newModsList, folderPath + folderName + "/");
90+
parent.addMenu(new EditorTreeMenuScreen(folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
91+
}));
92+
}
93+
else {
94+
list.push(makeSongOption(char));
95+
}
96+
}
97+
98+
return list;
99+
}
79100

80-
for (i => s in freeplayList.songs) add(makeSongOption(s));
101+
for (o in generateList(freeplayList.songs)) add(o);
81102
}
82103

83104
#if sys

source/funkin/editors/stage/StageSelection.hx

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import funkin.editors.stage.StageCreationScreen.StageCreationData;
44
import funkin.editors.EditorTreeMenu;
55
import funkin.game.Stage;
66
import funkin.options.type.NewOption;
7+
import funkin.options.type.FolderOption;
78
import funkin.options.type.OptionType;
89
import funkin.options.type.TextOption;
910

@@ -20,9 +21,9 @@ class StageSelection extends EditorTreeMenu {
2021
class StageSelectionScreen extends EditorTreeMenuScreen {
2122
public var stages:Array<String> = [];
2223

23-
public function makeStageOption(stage:String):TextOption {
24+
public function makeStageOption(stage:String, ?folder:String = ''):TextOption {
2425
return new TextOption(stage, getID('acceptStage'), () -> {
25-
FlxG.switchState(new StageEditor(stage));
26+
FlxG.switchState(new StageEditor(folder + stage));
2627
});
2728
}
2829

@@ -31,11 +32,36 @@ class StageSelectionScreen extends EditorTreeMenuScreen {
3132
parent.openSubState(new StageCreationScreen(saveStage));
3233
});
3334

34-
var modsList:Array<String> = Stage.getList(true, true);
35-
for (stage in (modsList.length == 0 ? Stage.getList(false, true) : modsList)) {
36-
stages.push(stage.toLowerCase());
37-
add(makeStageOption(stage));
35+
var isMods:Bool = true;
36+
var modsList = Stage.getList(true, true, true);
37+
38+
if (modsList.length == 0) {
39+
modsList = Stage.getList(false, true, true);
40+
isMods = false;
3841
}
42+
43+
function generateList(modsList:Array<String>, isMods:Bool, folderPath:String = ""):Array<FlxSprite> {
44+
var list:Array<FlxSprite> = [];
45+
46+
for (char in modsList) {
47+
if (char.endsWith("/")) {
48+
var folderName = CoolUtil.getFilename(char.substr(0, char.length-1));
49+
50+
list.push(new FolderOption(folderName + ' >', getID('acceptFolder'), () -> {
51+
var newModsList = Stage.getList(isMods, true, true, char);
52+
var newList:Array<FlxSprite> = generateList(newModsList, isMods, folderPath + folderName + "/");
53+
parent.addMenu(new EditorTreeMenuScreen(folderName, translate('desc-folder', [folderPath + folderName + "/"]), newList));
54+
}));
55+
}
56+
else {
57+
list.push(makeStageOption(char, folderPath));
58+
}
59+
}
60+
61+
return list;
62+
}
63+
64+
for (o in generateList(modsList, isMods)) add(o);
3965
}
4066

4167
public function saveStage(creation:StageCreationData) {

source/funkin/game/Stage.hx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -397,12 +397,19 @@ class Stage extends FlxBasic implements IBeatReceiver {
397397
* Gets a list of stages that are available to be used.
398398
* @param mods Whenever only the mods folder should be checked
399399
**/
400-
public static function getList(?mods:Bool = false, ?xmlOnly:Bool = false):Array<String> {
400+
public static function getList(?mods:Bool = false, ?xmlOnly:Bool = false, includeFolders:Bool = false, folder:String = 'data/stages/'):Array<String> {
401401
var list:Array<String> = [];
402402
var extensions:Array<String> = ["xml"];
403403
if (!xmlOnly) extensions.push("hx");
404404

405-
for (path in Paths.getFolderContent("data/stages/", false, mods ? MODS : BOTH)) {
405+
if(includeFolders) {
406+
for (path in Paths.getFolderDirectories(folder, true, mods ? MODS : BOTH)) {
407+
if(!path.endsWith("/")) path += "/";
408+
list.push(path);
409+
}
410+
}
411+
412+
for (path in Paths.getFolderContent(folder, false, mods ? MODS : BOTH)) {
406413
var extension = Path.extension(path);
407414
if (extensions.contains(extension)) {
408415
// list.pushOnce("test");

source/funkin/menus/FreeplayState.hx

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -434,10 +434,11 @@ class FreeplayState extends MusicBeatState
434434

435435
class FreeplaySonglist {
436436
public var songs:Array<ChartMetaData> = [];
437+
public static final EXCLUDE_SUBFOLDERS:Array<String> = ['charts', 'scripts', 'song'];
437438

438439
public function new() {}
439440

440-
public function getSongsFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true) {
441+
public function getSongsFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, ?startDir:String = 'songs/', ?flatten:Bool = true) {
441442
var songsFound:Array<String> = null;
442443
if (useTxt) {
443444
var oldPath = Paths.txt('freeplaySonglist');
@@ -448,27 +449,49 @@ class FreeplaySonglist {
448449
songsFound = CoolUtil.coolTextFile(oldPath);
449450
}
450451
}
451-
if (songsFound == null) songsFound = Paths.getFolderDirectories("songs", false, source);
452+
// todo: make this better
453+
if (songsFound == null) {
454+
songsFound = [];
455+
var songDirs = Paths.getFolderDirectories(startDir, false, source);
456+
if (!flatten) {
457+
for (i in songDirs) {
458+
var subs = Paths.getFolderDirectories('$startDir$i', false, source).filter(a -> !EXCLUDE_SUBFOLDERS.contains(a));
459+
songsFound.push((subs.length > 0) ? '$i/' : i);
460+
}
461+
} else {
462+
function poop(a:Array<Dynamic>, startDir:String) {
463+
for (i in a) {
464+
var subs = Paths.getFolderDirectories('$startDir$i', false, source).filter(a -> !EXCLUDE_SUBFOLDERS.contains(a));
465+
if (subs.length > 0) poop(subs, '$startDir$i/');
466+
else songsFound.push(startDir.substr('songs/'.length) + i);
467+
}
468+
}
469+
poop(songDirs, startDir);
470+
}
471+
// put folders at the top
472+
songsFound = songsFound.filter(a -> a.endsWith('/'))
473+
.concat(songsFound.filter(a -> !a.endsWith('/')));
474+
}
452475
if (songsFound.length > 0) {
453-
for (s in songsFound) songs.push(Chart.loadChartMeta(s, source == MODS));
476+
for (s in songsFound) songs.push(Chart.loadChartMeta(startDir.substr('songs/'.length) + s, source == MODS));
454477
return false;
455478
}
456479
return true;
457480
}
458481

459-
public static function get(useTxt:Bool = true) {
482+
public static function get(useTxt:Bool = true, ?startDir:String = 'songs/', ?flatten:Bool = true) {
460483
var songList = new FreeplaySonglist();
461484

462485
switch(Flags.SONGS_LIST_MOD_MODE) {
463486
case 'prepend':
464-
songList.getSongsFromSource(MODS, useTxt);
465-
songList.getSongsFromSource(SOURCE, useTxt);
487+
songList.getSongsFromSource(MODS, useTxt, startDir, flatten);
488+
songList.getSongsFromSource(SOURCE, useTxt, startDir, flatten);
466489
case 'append':
467-
songList.getSongsFromSource(SOURCE, useTxt);
468-
songList.getSongsFromSource(MODS, useTxt);
490+
songList.getSongsFromSource(SOURCE, useTxt, startDir, flatten);
491+
songList.getSongsFromSource(MODS, useTxt, startDir, flatten);
469492
default /*case 'override'*/:
470-
if (songList.getSongsFromSource(MODS, useTxt))
471-
songList.getSongsFromSource(SOURCE, useTxt);
493+
if (songList.getSongsFromSource(MODS, useTxt, startDir, flatten))
494+
songList.getSongsFromSource(SOURCE, useTxt, startDir, flatten);
472495
}
473496

474497
return songList;

source/funkin/menus/TitleState.hx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ class IntroText {
317317
for(e in lines) {
318318
if (e is String) {
319319
var text = cast(e, String);
320-
for(k=>e in state.curWacky) text = text.replace('{introText${k+1}}', e);
320+
for(k=>e in state.curWacky) text = text.replace('{introText${k+1}}', e.trim());
321321
state.addMoreText(text);
322322
} else if (e is Dynamic) {
323323
var image:TitleStateImage = e;

0 commit comments

Comments
 (0)