Skip to content

Commit faa0dbb

Browse files
committed
CharterSelection Song Variations
1 parent 14c81ef commit faa0dbb

File tree

10 files changed

+180
-93
lines changed

10 files changed

+180
-93
lines changed

assets/languages/en/Editors.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,15 @@
4545
<group name="CharterSelection" prefix="charterSelection.">
4646
<str id="desc">Select a song to modify the charts from.</str>
4747
<str id="acceptSong">Press ACCEPT to choose a difficulty to edit.</str>
48+
<str id="acceptVariation">Press ACCEPT to choose a song variation to edit.</str>
4849
<str id="acceptDifficulty">Press ACCEPT to edit the chart for the selected difficulty.</str>
4950
<str id="selectDifficulty">Select a difficulty to continue.</str>
5051
<str id="newDifficulty">New Difficulty</str>
5152
<str id="newDifficultyDesc">Press ACCEPT to create a new chart difficulty.</str>
5253
<str id="newSong">New Song</str>
5354
<str id="newSongDesc">Press ACCEPT to create a new song.</str>
55+
<str id="newVariation">New Variation</str>
56+
<str id="newVariationDesc">Press ACCEPT to create a new song variation.</str>
5457
</group>
5558

5659
<group name="ChartCreationScreen" prefix="chartCreation.">
@@ -104,6 +107,7 @@
104107

105108
<str id="songData">Song Data</str>
106109
<str id="songName">Song Name</str>
110+
<str id="variation">Song Variation</str>
107111
<str id="bpm">BPM</str>
108112
<str id="timeSignature">Time Signature</str>
109113
<str id="menusData">Menus Data (Freeplay/Story)</str>

source/funkin/editors/charter/Charter.hx

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -600,32 +600,34 @@ class Charter extends UIState {
600600
PlayState.loadSong(__song, __diff, __variant, false, false);
601601
__resetStatics();
602602
}
603-
Conductor.setupSong(PlayState.SONG);
604-
noteTypes = PlayState.SONG.noteTypes;
605603

606-
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
607-
if (Assets.exists(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix)))
608-
vocals = FlxG.sound.load(Paths.voices(__song, __diff, PlayState.SONG.meta.vocalsSuffix));
604+
var SONG = PlayState.SONG;
605+
Conductor.setupSong(SONG);
606+
noteTypes = SONG.noteTypes;
607+
608+
FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(SONG.meta.name, __diff, SONG.meta.instSuffix)));
609+
if (Assets.exists(Paths.voices(SONG.meta.name, __diff, SONG.meta.vocalsSuffix)))
610+
vocals = FlxG.sound.load(Paths.voices(SONG.meta.name, __diff, SONG.meta.vocalsSuffix));
609611
else
610612
vocals = new FlxSound();
611613

612-
vocals.muted = !PlayState.SONG.meta.needsVoices;
614+
vocals.muted = !SONG.meta.needsVoices;
613615
vocals.group = FlxG.sound.defaultMusicGroup;
614616

615-
gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
617+
gridBackdrops.createGrids(SONG.strumLines.length);
616618

617-
for(strL in PlayState.SONG.strumLines)
619+
for(strL in SONG.strumLines)
618620
createStrumline(strumLines.members.length, strL, false, false);
619621

620622
// create notes
621623
notesGroup.autoSort = false;
622624
var noteCount:Int = 0;
623-
for (strL in PlayState.SONG.strumLines)
625+
for (strL in SONG.strumLines)
624626
noteCount += strL.notes.length;
625627
notesGroup.preallocate(noteCount);
626628

627629
var notesCreated:Int = 0;
628-
for (i => strL in PlayState.SONG.strumLines)
630+
for (i => strL in SONG.strumLines)
629631
for (note in strL.notes) {
630632
var n = new CharterNote();
631633
var t = Conductor.getStepForTime(note.time);
@@ -639,7 +641,7 @@ class Charter extends UIState {
639641
rightEventsGroup.autoSort = leftEventsGroup.autoSort = false;
640642
var lastLeftEvents:CharterEvent = null, lastRightEvents:CharterEvent = null;
641643
var lastLeftTime = Math.NaN, lastRightTime = Math.NaN;
642-
for (e in PlayState.SONG.events) if (e != null) {
644+
for (e in SONG.events) if (e != null) {
643645
if (e.global) {
644646
if (lastRightEvents != null && lastRightTime == e.time) lastRightEvents.events.push(e);
645647
else rightEventsGroup.add(lastRightEvents = new CharterEvent(Conductor.getStepForTime(lastRightTime = e.time), [e], e.global));
@@ -685,10 +687,10 @@ class Charter extends UIState {
685687
var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [];
686688

687689
if (FlxG.sound.music.loaded)
688-
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
690+
wavesToGenerate.push({name: 'Inst${PlayState.SONG.meta.instSuffix}.ogg', sound: FlxG.sound.music});
689691

690692
if (vocals.loaded)
691-
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
693+
wavesToGenerate.push({name: 'Voices${PlayState.SONG.meta.vocalsSuffix}.ogg', sound: vocals});
692694

693695
for (strumLine in strumLines)
694696
if (strumLine.vocals != null && strumLine.strumLine.vocalsSuffix != null && strumLine.strumLine.vocalsSuffix != "" && strumLine.vocals.loaded)

source/funkin/editors/charter/CharterMetaDataScreen.hx

Lines changed: 15 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -150,20 +150,21 @@ class CharterMetaDataScreen extends UISubstateWindow {
150150
Reflect.setProperty(customVals, vals.propertyText.label.text, vals.valueText.label.text);
151151
}
152152

153-
PlayState.SONG.meta = {
154-
name: songNameTextBox.label.text,
155-
bpm: bpmStepper.value,
156-
needsVoices: needsVoicesCheckbox.checked,
157-
beatsPerMeasure: Std.int(beatsPerMeasureStepper.value),
158-
stepsPerBeat: Std.int(16 / denominatorStepper.value),
159-
displayName: displayNameTextBox.label.text,
160-
icon: iconTextBox.label.text,
161-
color: colorWheel.curColor,
162-
opponentModeAllowed: opponentModeCheckbox.checked,
163-
coopAllowed: coopAllowedCheckbox.checked,
164-
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()],
165-
customValues: customVals,
166-
};
153+
var meta = PlayState.SONG.meta;
154+
if (meta == null) meta = {name: songNameTextBox.label.text};
155+
else meta.name = songNameTextBox.label.text;
156+
157+
meta.bpm = bpmStepper.value;
158+
meta.needsVoices = needsVoicesCheckbox.checked;
159+
meta.beatsPerMeasure = Std.int(beatsPerMeasureStepper.value);
160+
meta.stepsPerBeat = Std.int(16 / denominatorStepper.value);
161+
meta.displayName = displayNameTextBox.label.text;
162+
meta.icon = iconTextBox.label.text;
163+
meta.color = colorWheel.curColor;
164+
meta.opponentModeAllowed = opponentModeCheckbox.checked;
165+
meta.coopAllowed = coopAllowedCheckbox.checked;
166+
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
167+
meta.customValues = customVals;
167168

168169
Charter.instance.updateBPMEvents();
169170
Charter.instance.vocals.muted = !needsVoicesCheckbox.checked;

source/funkin/editors/charter/CharterSelection.hx

Lines changed: 65 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,55 +25,73 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
2525
public var songList:Array<String> = [];
2626
public var curSong:ChartMetaData;
2727

28-
inline public function makeChartOption(d:String, name:String):TextOption {
29-
return new TextOption(d, getID('acceptDifficulty'), () -> FlxG.switchState(new Charter(name, d, null)));
28+
inline public function makeChartOption(d:String, v:String, name:String):TextOption {
29+
return new TextOption(d, getID('acceptDifficulty'), FlxG.switchState.bind(new Charter(name, d, v)));
3030
}
3131

32-
public function makeSongOption(s:ChartMetaData):IconOption {
33-
songList.push(s.name.toLowerCase());
32+
inline public function makeVariationOption(s:ChartMetaData):TextOption {
33+
return new TextOption(s.variant, getID('acceptVariation'), " >", openSongOption.bind(s, false));
34+
}
35+
36+
public function openSongOption(s:ChartMetaData, first = true) {
37+
curSong = s;
3438

35-
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, () -> {
36-
curSong = s;
39+
var isVariant = s.variant != null && s.variant != '';
40+
var screen = new EditorTreeMenuScreen((first || !isVariant) ? (s.name + (isVariant ? ' (${s.variant})' : '')) : s.variant, getID('selectDifficulty'));
3741

38-
var screen = new EditorTreeMenuScreen(s.name, getID('selectDifficulty'), [
39-
for (d in s.difficulties) if (d != '') makeChartOption(d, s.name)
40-
]);
42+
for (d in s.difficulties) if (d != '') screen.add(makeChartOption(d, isVariant ? s.variant : null, s.name));
43+
screen.add(new Separator());
44+
for (v in s.variants) if (s.metas.get(v) != null) screen.add(makeVariationOption(s.metas.get(v)));
4145

42-
#if sys
43-
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
44-
parent.openSubState(new ChartCreationScreen(saveChart));
46+
#if sys
47+
screen.insert(0, new NewOption(getID('newDifficulty'), getID('newDifficultyDesc'), () -> {
48+
parent.openSubState(new ChartCreationScreen(saveChart));
49+
}));
50+
51+
if (!first) screen.curSelected = 1;
52+
else {
53+
cast(screen.members[0], NewOption).itemHeight = 120;
54+
screen.insert(1, new NewOption(getID('newVariation'), getID('newVariationDesc'), () -> {
55+
parent.openSubState(new VariationCreationScreen(s, saveSong));
4556
}));
46-
screen.curSelected = 1;
47-
#end
57+
screen.curSelected = 2;
58+
}
59+
#end
60+
61+
parent.addMenu(screen);
62+
}
63+
64+
public function makeSongOption(s:ChartMetaData):IconOption {
65+
songList.push(s.name.toLowerCase());
4866

49-
parent.addMenu(screen);
50-
});
51-
opt.suffix = ' >';
67+
var opt = new IconOption(s.name, getID('acceptSong'), s.icon, openSongOption.bind(s, true));
68+
opt.suffix = " >";
5269
opt.editorFlashColor = s.color.getDefault(FlxColor.WHITE);
5370

5471
return opt;
5572
}
5673

5774
public function new() {
58-
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', () -> {
75+
super('editor.chart.name', 'charterSelection.desc', 'charterSelection.', 'newSong', 'newSongDesc', #if sys () -> {
5976
parent.openSubState(new SongCreationScreen(saveSong));
60-
});
77+
} #end);
6178
freeplayList = FreeplaySonglist.get(false);
6279

6380
for (i => s in freeplayList.songs) add(makeSongOption(s));
6481
}
6582

6683
#if sys
67-
public function saveSong(creation:SongCreationData, ?callback:String -> SongCreationData -> Void) {
68-
var songAlreadyExists:Bool = songList.contains(creation.meta.name.toLowerCase());
69-
if (songAlreadyExists) {
84+
public function saveSong(creation:SongCreationData, ?callback:String -> Void) {
85+
var variant = creation.meta.variant != null && creation.meta.variant != "" ? creation.meta.variant : null;
86+
if (songList.contains(creation.meta.name.toLowerCase()) || (variant != null && curSong != null && curSong.variants.contains(variant))) {
7087
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.song-exists-title"), TU.translate("chartCreation.warnings.song-exists-body"), [
7188
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
7289
]));
7390
return;
7491
}
7592

7693
var songFolder:String = '${Paths.getAssetsRoot()}/songs/${creation.meta.name}';
94+
creation.meta = Chart.filterMetaForSaving(creation.meta);
7795

7896
#if sys
7997
// Make Directories
@@ -82,24 +100,32 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
82100
sys.FileSystem.createDirectory('$songFolder/charts');
83101

84102
// Save Files
85-
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(creation.meta));
86-
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst.${Flags.SOUND_EXT}', creation.instBytes);
87-
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices.${Flags.SOUND_EXT}', creation.voicesBytes);
103+
var instSuffix = creation.meta.instSuffix != null ? creation.meta.instSuffix : '', vocalsSuffix = creation.meta.vocalsSuffix != null ? creation.meta.vocalsSuffix : '';
104+
CoolUtil.safeSaveFile('$songFolder/meta${variant != null ? "-" + variant : ""}.json', Json.stringify(creation.meta, null, Flags.JSON_PRETTY_PRINT));
105+
if (creation.instBytes != null) sys.io.File.saveBytes('$songFolder/song/Inst$instSuffix.${Flags.SOUND_EXT}', creation.instBytes);
106+
if (creation.voicesBytes != null) sys.io.File.saveBytes('$songFolder/song/Voices$vocalsSuffix.${Flags.SOUND_EXT}', creation.voicesBytes);
88107

89-
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player.${Flags.SOUND_EXT}', creation.playerVocals);
90-
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent.${Flags.SOUND_EXT}', creation.oppVocals);
108+
if (creation.playerVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Player$vocalsSuffix.${Flags.SOUND_EXT}', creation.playerVocals);
109+
if (creation.oppVocals != null) sys.io.File.saveBytes('$songFolder/song/Voices-Opponent$vocalsSuffix.${Flags.SOUND_EXT}', creation.oppVocals);
91110
#end
92111

93-
if (callback != null) callback(songFolder, creation);
112+
if (callback != null) callback(songFolder);
94113

95114
// Add to List
96-
freeplayList.songs.insert(0, creation.meta);
97-
insert(1, makeSongOption(creation.meta));
115+
if (variant != null && curSong != null) {
116+
curSong.variants.push(variant);
117+
curSong.metas.set(variant, creation.meta);
118+
119+
parent.tree.last().add(makeVariationOption(creation.meta));
120+
}
121+
else {
122+
freeplayList.songs.insert(0, creation.meta);
123+
insert(1, makeSongOption(creation.meta));
124+
}
98125
}
99126

100127
public function saveChart(name:String, data:ChartData) {
101-
var difficultyAlreadyExists:Bool = curSong.difficulties.contains(name);
102-
if (difficultyAlreadyExists) {
128+
if (curSong.difficulties.contains(name)) {
103129
parent.openSubState(new UIWarningSubstate(TU.translate("chartCreation.warnings.chart-exists-title"), TU.translate("chartCreation.warnings.chart-exists-body"), [
104130
{label: TU.translate("editor.ok"), color: 0xFFFF0000, onClick: (t) -> {}},
105131
]));
@@ -113,11 +139,16 @@ class CharterSelectionScreen extends EditorTreeMenuScreen {
113139

114140
// Add to List
115141
curSong.difficulties.push(name);
116-
parent.tree.last().insert(parent.tree.last().length - 1, makeChartOption(name, curSong.name));
142+
143+
var screen = parent.tree.last();
144+
var idx = 0;
145+
while (!(screen.members[idx] is Separator)) idx++;
146+
screen.insert(idx, makeChartOption(name, curSong.variant != null && curSong.variant != "" ? curSong.variant : null, curSong.name));
117147

118148
// Add to Meta
119149
var meta = Json.parse(sys.io.File.getContent('$songFolder/meta.json'));
120-
if (meta.difficulties != null && !meta.difficulties.contains(name)) {
150+
if (meta.difficulties == null) meta.difficulties = [];
151+
if (!meta.difficulties.contains(name)) {
121152
meta.difficulties.push(name);
122153
CoolUtil.safeSaveFile('$songFolder/meta.json', Chart.makeMetaSaveable(meta));
123154
}

0 commit comments

Comments
 (0)