Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0d9360f0ff | ||
|
|
1cfe6ca468 | ||
|
|
3bbeafcc3e |
+2
-2
@@ -71,11 +71,11 @@ _**QOL = Quality of Life**_
|
|||||||
- Characters are located in `data/weeks/characters`.
|
- Characters are located in `data/weeks/characters`.
|
||||||
- Weeks are located in `data/weeks/weeks/`
|
- Weeks are located in `data/weeks/weeks/`
|
||||||
- If you need to rearrange the weeks in-game, you can use the `data/weeks/weeks.txt` file.
|
- If you need to rearrange the weeks in-game, you can use the `data/weeks/weeks.txt` file.
|
||||||
- Editors for Charts and Characters (Stage coming soon)
|
- Editors for Charts and Characters
|
||||||
- Undos/Redos supported
|
- Undos/Redos supported
|
||||||
- Warning on closing unsaved work
|
- Warning on closing unsaved work
|
||||||
- Clean UI (for ocd freaks)
|
- Clean UI (for ocd freaks)
|
||||||
- Mature Chart editor (Character editor rework soon)
|
- Mature Chart editor
|
||||||
- Features not found in other editors!
|
- Features not found in other editors!
|
||||||
- Every single state & substate can be modified via HScript (`data/states/StateName.hx`)
|
- Every single state & substate can be modified via HScript (`data/states/StateName.hx`)
|
||||||
- **Instances launched via `lime test windows` will automatically use assets from source.**
|
- **Instances launched via `lime test windows` will automatically use assets from source.**
|
||||||
|
|||||||
@@ -1,3 +1,6 @@
|
|||||||
|
# THIS FILE ISN'T NECESSARY,
|
||||||
|
# BUT YOU CAN USE IT TO SORT AND WHITELIST FREEPLAY SONGS
|
||||||
|
|
||||||
test
|
test
|
||||||
tutorial
|
tutorial
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# THIS FILE ISN'T NECESSARY,
|
||||||
|
# BUT YOU CAN USE IT TO SORT AND WHITELIST GAME MODES
|
||||||
|
|
||||||
|
solo
|
||||||
|
opponent
|
||||||
|
coop
|
||||||
|
coop-switched
|
||||||
|
botplay
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
# THIS FILE ISN'T NECESSARY,
|
# THIS FILE ISN'T NECESSARY,
|
||||||
# BUT YOU CAN USE IT TO SORT WEEKS
|
# BUT YOU CAN USE IT TO SORT AND WHITELIST WEEKS
|
||||||
|
|
||||||
tutorial
|
tutorial
|
||||||
week1
|
week1
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"displayName": "Botplay Mode",
|
||||||
|
"modeID": "codename.botplay"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE codename-engine-game-mode>
|
||||||
|
<mode name="Botplay Mode" id="codename.botplay"/>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"displayName": "Co-Op Mode (Switched)",
|
||||||
|
"modeID": "codename.coop-opponent",
|
||||||
|
"scripts": ["opponent", "coop"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<!DOCTYPE codename-engine-game-mode>
|
||||||
|
<mode name="Co-Op Mode (Switched)" id="codename.coop-opponent">
|
||||||
|
<script>coop</script>
|
||||||
|
<script>opponent</script>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
just a small example of what you could do with fields!!
|
||||||
|
<field name="switchStrums" type="bool" val="true"/>
|
||||||
|
<field name="strumSize" type="float" val="0.6"/>
|
||||||
|
-->
|
||||||
|
</mode>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"displayName": "Co-Op Mode",
|
||||||
|
"modeID": "codename.coop"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE codename-engine-game-mode>
|
||||||
|
<mode name="Co-Op Mode" id="codename.coop"/>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
solo
|
||||||
|
opponent
|
||||||
|
coop
|
||||||
|
coop-switched
|
||||||
|
botplay
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"displayName": "Opponent Mode",
|
||||||
|
"modeID": "codename.opponent"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE codename-engine-game-mode>
|
||||||
|
<mode name="Opponent Mode" id="codename.opponent"/>
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"displayName": "Solo Mode",
|
||||||
|
"modeID": "codename.solo"
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<!DOCTYPE codename-engine-game-mode>
|
||||||
|
<mode name="Solo Mode" id="codename.solo"/>
|
||||||
@@ -18,6 +18,7 @@
|
|||||||
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
|
<git name="hxdiscord_rpc" url="https://github.com/CodenameCrew/cne-hxdiscord_rpc" skipDeps="true" />
|
||||||
<lib name="funkin-modchart" version="1.2.4" skipDeps="true" />
|
<lib name="funkin-modchart" version="1.2.4" skipDeps="true" />
|
||||||
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
|
<lib name="hxvlc" version="1.9.3" skipDeps="true" />
|
||||||
|
<git name="bin-serializer" url="https://github.com/CodenameCrew/bin-serializer" />
|
||||||
|
|
||||||
<!-- Documentation and other features -->
|
<!-- Documentation and other features -->
|
||||||
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
|
<git name="away3d" url="https://github.com/CodenameCrew/away3d" />
|
||||||
|
|||||||
+6
-8
@@ -3,19 +3,16 @@
|
|||||||
<!-- _________________________ Application Settings _________________________ -->
|
<!-- _________________________ Application Settings _________________________ -->
|
||||||
<app
|
<app
|
||||||
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
|
title="Friday Night Funkin' - Codename Engine" main="funkin.backend.system.Main"
|
||||||
file="CodenameEngine" packageName="com.yoshman29.codenameengine"
|
file="CodenameEngine" packageName="com.codenamecrew.codenameengine"
|
||||||
package="com.yoshman29.codenameengine"
|
package="com.codenamecrew.codenameengine"
|
||||||
version="1.0.1" company="Yoshman29" />
|
version="1.0.1" company="CodenameCrew" />
|
||||||
|
|
||||||
<!--
|
<!--
|
||||||
CHANGE THE SAVE PATH & NAME FOR YOUR MOD HERE!
|
CHANGE THE SAVE PATH & NAME FOR YOUR HARDCODED MOD HERE!
|
||||||
-->
|
-->
|
||||||
<haxedef name="SAVE_PATH" value="CodenameEngine"/>
|
<haxedef name="SAVE_PATH" value="CodenameCrew/CodenameEngine"/>
|
||||||
<haxedef name="SAVE_NAME" value="save-default"/>
|
<haxedef name="SAVE_NAME" value="save-default"/>
|
||||||
|
|
||||||
<haxedef name="SAVE_OPTIONS_PATH" value="CodenameEngine"/>
|
|
||||||
<haxedef name="SAVE_OPTIONS_NAME" value="options"/>
|
|
||||||
|
|
||||||
<!--Switch Export with Unique ApplicationID and Icon-->
|
<!--Switch Export with Unique ApplicationID and Icon-->
|
||||||
<set name="APP_ID" value="0x0100f6c013bbc000" />
|
<set name="APP_ID" value="0x0100f6c013bbc000" />
|
||||||
|
|
||||||
@@ -131,6 +128,7 @@
|
|||||||
<haxelib name="away3d" if="THREE_D_SUPPORT" />
|
<haxelib name="away3d" if="THREE_D_SUPPORT" />
|
||||||
<haxelib name="format" />
|
<haxelib name="format" />
|
||||||
<haxelib name="flxanimate" />
|
<haxelib name="flxanimate" />
|
||||||
|
<haxelib name="bin-serializer" />
|
||||||
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
|
<haxelib name="nape-haxe4" if="NAPE_ENABLED"/>
|
||||||
|
|
||||||
<haxelib name="hscript-improved" />
|
<haxelib name="hscript-improved" />
|
||||||
|
|||||||
@@ -97,6 +97,41 @@ class Chart {
|
|||||||
data.setFieldDefault("vocalsSuffix", "");
|
data.setFieldDefault("vocalsSuffix", "");
|
||||||
data.setFieldDefault("needsVoices", true);
|
data.setFieldDefault("needsVoices", true);
|
||||||
data.setFieldDefault("difficulties", []);
|
data.setFieldDefault("difficulties", []);
|
||||||
|
|
||||||
|
var songNameLower = songName.toLowerCase();
|
||||||
|
var metaPath = Paths.file('songs/${songNameLower}/meta.json');
|
||||||
|
var metaDiffPath = Paths.file('songs/${songNameLower}/meta-${difficulty.toLowerCase()}.json');
|
||||||
|
|
||||||
|
var blacklist:Array<String> = [];
|
||||||
|
var data:ChartMetaData = null;
|
||||||
|
var fromMods:Bool = fromMods;
|
||||||
|
for (path in [metaDiffPath, metaPath]) {
|
||||||
|
if (Assets.exists(path)) {
|
||||||
|
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
|
||||||
|
try {
|
||||||
|
var temp = CoolUtil.parseJson(path);
|
||||||
|
|
||||||
|
// Backwards compatibility (they both can be null so != true) - Nex
|
||||||
|
var coopExlc = false;
|
||||||
|
if (Reflect.hasField(temp, "coopAllowed") && Reflect.field(temp, "coopAllowed") != true) {
|
||||||
|
blacklist = blacklist.concat(["codename.coop", "codename.coop-opponent"]);
|
||||||
|
Reflect.deleteField(temp, "coopAllowed");
|
||||||
|
coopExlc = true;
|
||||||
|
}
|
||||||
|
if (Reflect.hasField(temp, "opponentModeAllowed") && Reflect.field(temp, "opponentModeAllowed") != true) {
|
||||||
|
if (!coopExlc) blacklist.push("codename.coop-opponent");
|
||||||
|
blacklist.push("codename.opponent");
|
||||||
|
Reflect.deleteField(temp, "opponentModeAllowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
data = temp;
|
||||||
|
} catch(e) {
|
||||||
|
Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
|
||||||
|
}
|
||||||
|
if (data != null) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
data.setFieldDefault("excludedGameModes", blacklist);
|
||||||
data.setFieldDefault("variants", []);
|
data.setFieldDefault("variants", []);
|
||||||
data.setFieldDefault("metas", []);
|
data.setFieldDefault("metas", []);
|
||||||
|
|
||||||
|
|||||||
@@ -32,8 +32,7 @@ typedef ChartMetaData = {
|
|||||||
public var ?icon:String;
|
public var ?icon:String;
|
||||||
public var ?color:FlxColor;
|
public var ?color:FlxColor;
|
||||||
|
|
||||||
public var ?coopAllowed:Bool;
|
public var ?excludedGameModes:Array<String>;
|
||||||
public var ?opponentModeAllowed:Bool;
|
|
||||||
|
|
||||||
public var ?metas:Map<String, ChartMetaData>;
|
public var ?metas:Map<String, ChartMetaData>;
|
||||||
public var ?instSuffix:String;
|
public var ?instSuffix:String;
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package funkin.backend.scripting.events.menu.freeplay;
|
package funkin.backend.scripting.events.menu.freeplay;
|
||||||
|
|
||||||
|
import funkin.menus.FreeplayState.FreeplayGameMode;
|
||||||
|
|
||||||
final class FreeplaySongSelectEvent extends CancellableEvent {
|
final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||||
/**
|
/**
|
||||||
* Song name that is about to be played
|
* Song name that is about to be played
|
||||||
@@ -14,11 +16,26 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
|
|||||||
*/
|
*/
|
||||||
public var variant:String;
|
public var variant:String;
|
||||||
/**
|
/**
|
||||||
* Whenever opponent mode is enabled or not.
|
* Used game mode's data.
|
||||||
*/
|
*/
|
||||||
public var opponentMode:Bool;
|
public var gameMode:FreeplayGameMode;
|
||||||
/**
|
|
||||||
* Whenever coop mode is enabled.
|
|
||||||
*/
|
// Backwards compat
|
||||||
public var coopMode:Bool;
|
@:noCompletion public var opponentMode(get, set):Bool;
|
||||||
|
@:noCompletion private function get_opponentMode() return gameMode.modeID == "codename.opponent" || gameMode.modeID == "codename.coop-opponent";
|
||||||
|
@:noCompletion private function set_opponentMode(val:Bool) {
|
||||||
|
gameMode = coopMode ?
|
||||||
|
(val ? new FreeplayGameMode("Co-Op Mode (Switched)", "codename.coop-opponent", ["coop-switched", "opponent", "coop"]) : new FreeplayGameMode("Opponent Mode", "codename.opponent", ["opponent"])) :
|
||||||
|
(val ? new FreeplayGameMode("Opponent Mode", "codename.opponent", ["opponent"]) : null);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
@:noCompletion public var coopMode(get, set):Bool;
|
||||||
|
@:noCompletion private function get_coopMode() return gameMode.modeID == "codename.coop" || gameMode.modeID == "codename.coop-opponent";
|
||||||
|
@:noCompletion private function set_coopMode(val:Bool) {
|
||||||
|
gameMode = opponentMode ?
|
||||||
|
(val ? new FreeplayGameMode("Co-Op Mode (Switched)", "codename.coop-opponent", ["coop-switched", "opponent", "coop"]) : new FreeplayGameMode("Co-Op Mode", "codename.coop", ["coop"])) :
|
||||||
|
(val ? new FreeplayGameMode("Co-Op Mode", "codename.coop", ["coop"]) : null);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,7 @@ import lime.utils.AssetType;
|
|||||||
/**
|
/**
|
||||||
* A class that reads the `flags.ini` file, allowing to read settable Flags (customs too).
|
* A class that reads the `flags.ini` file, allowing to read settable Flags (customs too).
|
||||||
*/
|
*/
|
||||||
@:build(funkin.backend.system.macros.FlagMacro.build())
|
@:build(funkin.backend.system.macros.FlagMacro.build("onReset"))
|
||||||
class Flags {
|
class Flags {
|
||||||
public static var overridenFlags:Map<String, Bool> = [];
|
public static var overridenFlags:Map<String, Bool> = [];
|
||||||
|
|
||||||
@@ -312,6 +312,11 @@ class Flags {
|
|||||||
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
|
if (SUSTAINS_AS_ONE_NOTE == null) SUSTAINS_AS_ONE_NOTE = MOD_API_VERSION >= 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static function onReset() {
|
||||||
|
var currentModFolder = ModsFolder.currentModFolder;
|
||||||
|
if (currentModFolder != null) SAVE_NAME = currentModFolder;
|
||||||
|
}
|
||||||
|
|
||||||
public static function loadFromDatas(datas:Array<String>):Map<String, String> {
|
public static function loadFromDatas(datas:Array<String>):Map<String, String> {
|
||||||
var flags:Map<String, String> = [];
|
var flags:Map<String, String> = [];
|
||||||
for (data in datas) {
|
for (data in datas) {
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ class FlagMacro {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function build():Array<Field> {
|
public static function build(resetCallback:String):Array<Field> {
|
||||||
var fields = Context.getBuildFields();
|
var fields = Context.getBuildFields();
|
||||||
|
|
||||||
var clRef = Context.getLocalClass();
|
var clRef = Context.getLocalClass();
|
||||||
@@ -248,7 +248,10 @@ class FlagMacro {
|
|||||||
access: [APublic, AStatic],
|
access: [APublic, AStatic],
|
||||||
kind: FFun({
|
kind: FFun({
|
||||||
args: [],
|
args: [],
|
||||||
expr: macro $b{resetExprs},
|
expr: macro {
|
||||||
|
$b{resetExprs};
|
||||||
|
$i{resetCallback}();
|
||||||
|
},
|
||||||
ret: macro: Void
|
ret: macro: Void
|
||||||
}),
|
}),
|
||||||
pos: Context.currentPos(),
|
pos: Context.currentPos(),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package funkin.editors.charter;
|
package funkin.editors.charter;
|
||||||
|
|
||||||
|
import funkin.game.HealthIcon;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||||
import funkin.editors.extra.PropertyButton;
|
import funkin.editors.extra.PropertyButton;
|
||||||
@@ -21,8 +22,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
|||||||
public var displayNameTextBox:UITextBox;
|
public var displayNameTextBox:UITextBox;
|
||||||
public var iconTextBox:UITextBox;
|
public var iconTextBox:UITextBox;
|
||||||
public var iconSprite:HealthIcon;
|
public var iconSprite:HealthIcon;
|
||||||
public var opponentModeCheckbox:UICheckbox;
|
public var excludedGameModesList:UIAutoCompleteButtonList;
|
||||||
public var coopAllowedCheckbox:UICheckbox;
|
|
||||||
public var needsVoicesCheckbox:UICheckbox;
|
public var needsVoicesCheckbox:UICheckbox;
|
||||||
public var colorWheel:UIColorwheel;
|
public var colorWheel:UIColorwheel;
|
||||||
public var difficultiesTextBox:UITextBox;
|
public var difficultiesTextBox:UITextBox;
|
||||||
@@ -78,25 +78,29 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
|||||||
add(displayNameTextBox);
|
add(displayNameTextBox);
|
||||||
addLabelOn(displayNameTextBox, translate("displayName"));
|
addLabelOn(displayNameTextBox, translate("displayName"));
|
||||||
|
|
||||||
|
add(iconSprite = new HealthIcon(metadata.icon));
|
||||||
|
iconSprite.scale.set(0.5, 0.5);
|
||||||
|
iconSprite.updateHitbox();
|
||||||
|
|
||||||
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, metadata.icon, 150);
|
iconTextBox = new UITextBox(displayNameTextBox.x + 320 + 26, displayNameTextBox.y, metadata.icon, 150);
|
||||||
iconTextBox.onChange = (newIcon:String) -> {updateIcon(newIcon);}
|
iconTextBox.onChange = (newIcon:String) -> iconSprite.setIcon(newIcon);
|
||||||
add(iconTextBox);
|
add(iconTextBox);
|
||||||
addLabelOn(iconTextBox, translate("icon"));
|
addLabelOn(iconTextBox, translate("icon"));
|
||||||
|
|
||||||
updateIcon(metadata.icon);
|
updateIcon(metadata.icon);
|
||||||
|
|
||||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translate("opponentMode"), metadata.opponentModeAllowed);
|
add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID]));
|
||||||
add(opponentModeCheckbox);
|
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||||
addLabelOn(opponentModeCheckbox, translate("modesAllowed"));
|
excludedGameModesList.cameraSpacing = 0;
|
||||||
|
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
|
||||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translate("coopAllowed"), metadata.coopAllowed);
|
for (mode in metadata.excludedGameModes)
|
||||||
add(coopAllowedCheckbox);
|
excludedGameModesList.add(new UIAutoCompleteButtonList.UIAutoCompleteButton(0, 0, excludedGameModesList, excludedGameModesList.suggestItems, mode));
|
||||||
|
|
||||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color);
|
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color);
|
||||||
add(colorWheel);
|
add(colorWheel);
|
||||||
addLabelOn(colorWheel, translate("color"));
|
addLabelOn(colorWheel, translate("color"));
|
||||||
|
|
||||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, metadata.difficulties.join(", "));
|
difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, metadata.difficulties.join(", "));
|
||||||
add(difficultiesTextBox);
|
add(difficultiesTextBox);
|
||||||
addLabelOn(difficultiesTextBox, translate("difficulties"));
|
addLabelOn(difficultiesTextBox, translate("difficulties"));
|
||||||
|
|
||||||
@@ -111,9 +115,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
|||||||
add(customPropertiesButtonList);
|
add(customPropertiesButtonList);
|
||||||
addLabelOn(customPropertiesButtonList, translate("customValues"));
|
addLabelOn(customPropertiesButtonList, translate("customValues"));
|
||||||
|
|
||||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
|
||||||
{checkbox.y += 6; checkbox.x += 4;}
|
|
||||||
|
|
||||||
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
|
saveButton = new UIButton(windowSpr.x + windowSpr.bWidth - 20, windowSpr.y + windowSpr.bHeight - 20, TU.translate("editor.saveClose"), function() {
|
||||||
saveMeta();
|
saveMeta();
|
||||||
close();
|
close();
|
||||||
@@ -161,8 +162,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
|||||||
meta.displayName = displayNameTextBox.label.text;
|
meta.displayName = displayNameTextBox.label.text;
|
||||||
meta.icon = iconTextBox.label.text;
|
meta.icon = iconTextBox.label.text;
|
||||||
meta.color = colorWheel.curColor;
|
meta.color = colorWheel.curColor;
|
||||||
meta.opponentModeAllowed = opponentModeCheckbox.checked;
|
meta.excludedGameModes = [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()],
|
||||||
meta.coopAllowed = coopAllowedCheckbox.checked;
|
|
||||||
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
|
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
|
||||||
meta.customValues = customVals;
|
meta.customValues = customVals;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package funkin.editors.charter;
|
package funkin.editors.charter;
|
||||||
|
|
||||||
|
import funkin.game.HealthIcon;
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.text.FlxText.FlxTextFormat;
|
import flixel.text.FlxText.FlxTextFormat;
|
||||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||||
@@ -37,8 +38,7 @@ class SongCreationScreen extends UISubstateWindow {
|
|||||||
public var displayNameTextBox:UITextBox;
|
public var displayNameTextBox:UITextBox;
|
||||||
public var iconTextBox:UITextBox;
|
public var iconTextBox:UITextBox;
|
||||||
public var iconSprite:HealthIcon;
|
public var iconSprite:HealthIcon;
|
||||||
public var opponentModeCheckbox:UICheckbox;
|
public var excludedGameModesList:UIAutoCompleteButtonList;
|
||||||
public var coopAllowedCheckbox:UICheckbox;
|
|
||||||
public var colorWheel:UIColorwheel;
|
public var colorWheel:UIColorwheel;
|
||||||
public var difficultiesTextBox:UITextBox;
|
public var difficultiesTextBox:UITextBox;
|
||||||
|
|
||||||
@@ -166,24 +166,19 @@ class SongCreationScreen extends UISubstateWindow {
|
|||||||
|
|
||||||
updateIcon("face");
|
updateIcon("face");
|
||||||
|
|
||||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translateMeta("opponentMode"), true);
|
menuDataGroup.add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID]));
|
||||||
menuDataGroup.add(opponentModeCheckbox);
|
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||||
addLabelOn(opponentModeCheckbox, translateMeta("modesAllowed"));
|
excludedGameModesList.cameraSpacing = 0;
|
||||||
|
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
|
||||||
|
|
||||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translateMeta("coopAllowed"), true);
|
colorWheel = new UIColorwheel(iconTextBox.x, excludedGameModesList.y, 0xFFFFFF);
|
||||||
menuDataGroup.add(coopAllowedCheckbox);
|
|
||||||
|
|
||||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, 0xFFFFFF);
|
|
||||||
menuDataGroup.add(colorWheel);
|
menuDataGroup.add(colorWheel);
|
||||||
addLabelOn(colorWheel, translateMeta("color"));
|
addLabelOn(colorWheel, translateMeta("color"));
|
||||||
|
|
||||||
difficultiesTextBox = new UITextBox(opponentModeCheckbox.x, opponentModeCheckbox.y + 6 + 32 + 26, "");
|
difficultiesTextBox = new UITextBox(excludedGameModesList.x, excludedGameModesList.y + excludedGameModesList.bHeight + 32, "");
|
||||||
menuDataGroup.add(difficultiesTextBox);
|
menuDataGroup.add(difficultiesTextBox);
|
||||||
addLabelOn(difficultiesTextBox, translateMeta("difficulties"));
|
addLabelOn(difficultiesTextBox, translateMeta("difficulties"));
|
||||||
|
|
||||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
|
||||||
{checkbox.y += 6; checkbox.x += 4;}
|
|
||||||
|
|
||||||
var menuTitle:UIText;
|
var menuTitle:UIText;
|
||||||
selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importSource"), 28));
|
selectFormatGroup.add(menuTitle = new UIText(windowSpr.x + 20, windowSpr.y + 30 + 16, 0, translate("importSource"), 28));
|
||||||
|
|
||||||
@@ -433,8 +428,7 @@ class SongCreationScreen extends UISubstateWindow {
|
|||||||
displayName: displayNameTextBox.label.text,
|
displayName: displayNameTextBox.label.text,
|
||||||
icon: iconTextBox.label.text,
|
icon: iconTextBox.label.text,
|
||||||
color: colorWheel.curColor,
|
color: colorWheel.curColor,
|
||||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
excludedGameModes: [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()],
|
||||||
coopAllowed: coopAllowedCheckbox.checked,
|
|
||||||
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) if (diff.length > 0) diff.trim()]
|
difficulties: [for (diff in difficultiesTextBox.label.text.split(",")) if (diff.length > 0) diff.trim()]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,50 @@
|
|||||||
|
package funkin.editors.ui;
|
||||||
|
|
||||||
|
typedef UIAutoCompleteButtonList = UITypedAutoCompleteButtonList<UIAutoCompleteButton>;
|
||||||
|
|
||||||
|
class UITypedAutoCompleteButtonList<T:UIAutoCompleteButton> extends UIButtonList<T> {
|
||||||
|
public var suggestItems(default, set):Array<String> = [];
|
||||||
|
|
||||||
|
private function set_suggestItems(value:Array<String>) {
|
||||||
|
buttons.forEachAlive((button) -> button.textBox.suggestItems = value);
|
||||||
|
return suggestItems = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new(x:Float, y:Float, width:Int, height:Int, windowName:String, ?suggestItems:Array<String>, ?buttonSize:FlxPoint, ?buttonOffset:FlxPoint, buttonSpacing:Float = 0) {
|
||||||
|
super(x, y, width, height, windowName, buttonSize == null ? FlxPoint.get(width, width * 0.15) : buttonSize, buttonOffset, buttonSpacing);
|
||||||
|
if (suggestItems != null && suggestItems.length > 0) this.suggestItems = suggestItems;
|
||||||
|
addButton.callback = () -> add(cast new UIAutoCompleteButton(0, 0, cast this, this.suggestItems));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class UIAutoCompleteButton extends UIButton {
|
||||||
|
public var textBox:UIAutoCompleteTextBox;
|
||||||
|
public var deleteButton:UIButton;
|
||||||
|
public var deleteIcon:FlxSprite;
|
||||||
|
|
||||||
|
public function new (x:Float, y:Float, parent:UIAutoCompleteButtonList, ?suggestItems:Array<String>, text:String = "", ?w:Int, ?h:Int, multiline:Bool = false) {
|
||||||
|
super(x, y, "", null, w == null ? Std.int(parent.buttonSize.x) : w, h == null ? Std.int(parent.buttonSize.y) : h);
|
||||||
|
|
||||||
|
members.push(textBox = new UIAutoCompleteTextBox(16, bHeight/2 - (32/2), text, Std.int(bWidth - 32 - 36), 32, multiline));
|
||||||
|
if (suggestItems != null && suggestItems.length > 0) textBox.suggestItems = suggestItems;
|
||||||
|
textBox.antialiasing = false;
|
||||||
|
|
||||||
|
members.push(deleteButton = new UIButton(textBox.x + textBox.bWidth + 10, textBox.y, "", () -> { parent.remove(this); destroy(); }, 32));
|
||||||
|
deleteButton.color = 0xFFFF0000;
|
||||||
|
deleteButton.autoAlpha = false;
|
||||||
|
|
||||||
|
members.push(deleteIcon = new FlxSprite(deleteButton.x + (15/2), deleteButton.y + 8).loadGraphic(Paths.image('editors/delete-button')));
|
||||||
|
deleteIcon.antialiasing = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function update(elapsed) {
|
||||||
|
deleteButton.y = y + bHeight / 2 - deleteButton.bHeight / 2;
|
||||||
|
textBox.y = y + bHeight/2 - 16;
|
||||||
|
deleteIcon.x = deleteButton.x + (15/2); deleteIcon.y = deleteButton.y + 8;
|
||||||
|
|
||||||
|
textBox.selectable = deleteButton.selectable = selectable;
|
||||||
|
deleteButton.shouldPress = shouldPress;
|
||||||
|
|
||||||
|
super.update(elapsed);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import funkin.backend.scripting.events.menu.freeplay.*;
|
|||||||
import funkin.backend.system.Conductor;
|
import funkin.backend.system.Conductor;
|
||||||
import funkin.game.HealthIcon;
|
import funkin.game.HealthIcon;
|
||||||
import funkin.savedata.FunkinSave;
|
import funkin.savedata.FunkinSave;
|
||||||
|
import haxe.io.Path;
|
||||||
|
|
||||||
using StringTools;
|
using StringTools;
|
||||||
|
|
||||||
@@ -34,6 +35,11 @@ class FreeplayState extends MusicBeatState
|
|||||||
*/
|
*/
|
||||||
public var curDiffMetaKeys:Array<String> = [];
|
public var curDiffMetaKeys:Array<String> = [];
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Array containing all labels for game modes.
|
||||||
|
*/
|
||||||
|
public var gameModeLabels:Array<FreeplayGameMode> = [];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently selected song
|
* Currently selected song
|
||||||
*/
|
*/
|
||||||
@@ -43,9 +49,9 @@ class FreeplayState extends MusicBeatState
|
|||||||
*/
|
*/
|
||||||
public var curDifficulty:Int = 1;
|
public var curDifficulty:Int = 1;
|
||||||
/**
|
/**
|
||||||
* Currently selected coop/opponent mode
|
* Currently selected game mode
|
||||||
*/
|
*/
|
||||||
public var curCoopMode:Int = 0;
|
public var curGameMode:Int = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text containing the score info (PERSONAL BEST: 0)
|
* Text containing the score info (PERSONAL BEST: 0)
|
||||||
@@ -58,9 +64,9 @@ class FreeplayState extends MusicBeatState
|
|||||||
public var diffText:FlxText;
|
public var diffText:FlxText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text containing the current coop/opponent mode ([KEYBINDS] Co-Op mode)
|
* Text containing the current game mode, for example: ([KEYBINDS] Co-Op mode)
|
||||||
*/
|
*/
|
||||||
public var coopText:FlxText;
|
public var gameModeText:FlxText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Currently lerped score. Is updated to go towards `intendedScore`.
|
* Currently lerped score. Is updated to go towards `intendedScore`.
|
||||||
@@ -130,6 +136,7 @@ class FreeplayState extends MusicBeatState
|
|||||||
}
|
}
|
||||||
|
|
||||||
updateCurSong();
|
updateCurSong();
|
||||||
|
gameModeLabels = FreeplayGameMode.get();
|
||||||
|
|
||||||
DiscordUtil.call("onMenuLoaded", ["Freeplay"]);
|
DiscordUtil.call("onMenuLoaded", ["Freeplay"]);
|
||||||
|
|
||||||
@@ -177,14 +184,15 @@ class FreeplayState extends MusicBeatState
|
|||||||
diffText.font = scoreText.font;
|
diffText.font = scoreText.font;
|
||||||
add(diffText);
|
add(diffText);
|
||||||
|
|
||||||
coopText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24);
|
gameModeText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24);
|
||||||
coopText.font = scoreText.font;
|
gameModeText.font = scoreText.font;
|
||||||
add(coopText);
|
add(gameModeText);
|
||||||
|
|
||||||
add(scoreText);
|
add(scoreText);
|
||||||
|
|
||||||
changeSelection(0, true);
|
changeSelection(0, true);
|
||||||
changeCoopMode(0, true);
|
changeDiff(0, true);
|
||||||
|
changeGameMode(0, true);
|
||||||
|
|
||||||
interpColor = new FlxInterpolateColor(bg.color);
|
interpColor = new FlxInterpolateColor(bg.color);
|
||||||
}
|
}
|
||||||
@@ -239,17 +247,17 @@ class FreeplayState extends MusicBeatState
|
|||||||
if (canSelect) {
|
if (canSelect) {
|
||||||
changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
|
changeSelection((controls.UP_P ? -1 : 0) + (controls.DOWN_P ? 1 : 0) - FlxG.mouse.wheel);
|
||||||
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
|
changeDiff((controls.LEFT_P ? -1 : 0) + (controls.RIGHT_P ? 1 : 0));
|
||||||
changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable
|
changeGameMode((controls.CHANGE_MODE ? 1 : 0));
|
||||||
// putting it before so that its actually smooth
|
// putting it before so that its actually smooth
|
||||||
updateOptionsAlpha();
|
updateOptionsAlpha();
|
||||||
}
|
}
|
||||||
|
|
||||||
scoreText.text = TEXT_FREEPLAY_SCORE.format([Math.round(lerpScore)]);
|
scoreText.text = TEXT_FREEPLAY_SCORE.format([Math.round(lerpScore)]);
|
||||||
scoreBG.scale.set(MathUtil.maxSmart(diffText.width, scoreText.width, coopText.width) + 8, (coopText.visible ? coopText.y + coopText.height : 66));
|
scoreBG.scale.set(MathUtil.maxSmart(diffText.width, scoreText.width, gameModeText.width) + 8, (gameModeText.visible ? gameModeText.y + gameModeText.height : 66));
|
||||||
scoreBG.updateHitbox();
|
scoreBG.updateHitbox();
|
||||||
scoreBG.x = FlxG.width - scoreBG.width;
|
scoreBG.x = FlxG.width - scoreBG.width;
|
||||||
|
|
||||||
scoreText.x = coopText.x = scoreBG.x + 4;
|
scoreText.x = gameModeText.x = scoreBG.x + 4;
|
||||||
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
|
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
|
||||||
|
|
||||||
interpColor.fpsLerpTo(curSong.color, 0.0625);
|
interpColor.fpsLerpTo(curSong.color, 0.0625);
|
||||||
@@ -310,31 +318,13 @@ class FreeplayState extends MusicBeatState
|
|||||||
select();
|
select();
|
||||||
}
|
}
|
||||||
|
|
||||||
var __opponentMode:Bool = false;
|
|
||||||
var __coopMode:Bool = false;
|
|
||||||
|
|
||||||
function updateCoopModes() {
|
|
||||||
__opponentMode = false;
|
|
||||||
__coopMode = false;
|
|
||||||
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
|
|
||||||
__opponentMode = curCoopMode % 2 == 1;
|
|
||||||
__coopMode = curCoopMode >= 2;
|
|
||||||
} else if (curSong.coopAllowed) {
|
|
||||||
__coopMode = curCoopMode == 1;
|
|
||||||
} else if (curSong.opponentModeAllowed) {
|
|
||||||
__opponentMode = curCoopMode == 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects the current song.
|
* Selects the current song.
|
||||||
*/
|
*/
|
||||||
public function select() {
|
public function select() {
|
||||||
updateCoopModes();
|
|
||||||
|
|
||||||
if (curDifficulties.length == 0) return;
|
if (curDifficulties.length == 0) return;
|
||||||
|
|
||||||
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, __opponentMode, __coopMode));
|
var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curDifficulties[curDifficulty], curSong.variant, gameModeLabels[curGameMode]));
|
||||||
|
|
||||||
if (event.cancelled) return;
|
if (event.cancelled) return;
|
||||||
|
|
||||||
@@ -393,7 +383,7 @@ class FreeplayState extends MusicBeatState
|
|||||||
intendedScore = 0;
|
intendedScore = 0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
updateCoopModes();
|
|
||||||
var changes:Array<HighscoreChange> = [];
|
var changes:Array<HighscoreChange> = [];
|
||||||
if (__coopMode) changes.push(CCoopMode);
|
if (__coopMode) changes.push(CCoopMode);
|
||||||
if (__opponentMode) changes.push(COpponentMode);
|
if (__opponentMode) changes.push(COpponentMode);
|
||||||
@@ -401,47 +391,39 @@ class FreeplayState extends MusicBeatState
|
|||||||
intendedScore = saveData.score;
|
intendedScore = saveData.score;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Array containing all labels for Co-Op / Opponent modes.
|
|
||||||
*/
|
|
||||||
public var coopLabels:Array<String> = [
|
|
||||||
TU.translate("freeplay.solo"),
|
|
||||||
TU.translate("freeplay.opponentMode"),
|
|
||||||
TU.translate("freeplay.coopMode"),
|
|
||||||
TU.translate("freeplay.coopModeSwitched")
|
|
||||||
];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the current coop mode context.
|
* Change the current coop mode context.
|
||||||
* @param change How much to change
|
* @param change How much to change
|
||||||
* @param force Force the change, even if `change` is equal to 0.
|
* @param force Force the change, even if `change` is equal to 0.
|
||||||
*/
|
*/
|
||||||
public function changeCoopMode(change:Int = 0, force:Bool = false) {
|
public function changeGameMode(change:Int = 0, force:Bool = false) {
|
||||||
if (change == 0 && !force) return;
|
if (change == 0 && !force) return;
|
||||||
if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
|
|
||||||
|
|
||||||
var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed;
|
var i = 0;
|
||||||
var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change));
|
var allowed = getAllowedGameModesID();
|
||||||
|
var wrapped = FlxMath.wrap(curGameMode + change, 0, gameModeLabels.length - 1);
|
||||||
if (event.cancelled) return;
|
while (!allowed.contains(gameModeLabels[wrapped].modeID)) { // Skipping the blacklisted ones - Nex
|
||||||
|
wrapped = FlxMath.wrap(wrapped + change, 0, gameModeLabels.length - 1);
|
||||||
curCoopMode = event.value;
|
if (i++ > allowed.length) {
|
||||||
|
curGameMode = 0; // Idk honestly if I should modify the other event's variables - Nex
|
||||||
updateScore();
|
break;
|
||||||
|
|
||||||
var coopBinds = [CoolUtil.keyToString(Options.P1_CHANGE_MODE[0]), CoolUtil.keyToString(Options.P2_CHANGE_MODE[0])].filter(x -> x != "---");
|
|
||||||
if (coopBinds.length == 2 && coopBinds[1] == coopBinds[0]) coopBinds.pop();
|
|
||||||
else if (coopBinds.length == 0) coopBinds.push("---");
|
|
||||||
|
|
||||||
var key = '[${coopBinds.join(" / ")}] ';
|
|
||||||
|
|
||||||
if (bothEnabled) {
|
|
||||||
coopText.text = key + coopLabels[curCoopMode];
|
|
||||||
} else {
|
|
||||||
coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var e = EventManager.get(MenuChangeEvent).recycle(curGameMode, wrapped, change);
|
||||||
|
event("onChangeCoopMode", e); // Backwards compat - Nex
|
||||||
|
if (event("onChangeGameMode", e).cancelled) return;
|
||||||
|
|
||||||
|
// Getting from scratch the allowed game modes just in case they changed when the event got called - Nex
|
||||||
|
if (gameModeText.visible = getAllowedGameModesID().length > 0) gameModeText.text = "[TAB] " + gameModeLabels[curGameMode = e.value].modeName;
|
||||||
|
updateScore();
|
||||||
|
}
|
||||||
|
|
||||||
|
public inline function getAllowedGameModesID() {
|
||||||
|
var excluded = songs[curSelected].excludedGameModes;
|
||||||
|
return [for (mode in gameModeLabels) if (!excluded.contains(mode.modeID)) mode.modeID];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the current selection.
|
* Change the current selection.
|
||||||
* @param change How much to change
|
* @param change How much to change
|
||||||
@@ -465,13 +447,12 @@ class FreeplayState extends MusicBeatState
|
|||||||
}
|
}
|
||||||
|
|
||||||
changeDiff(0, true);
|
changeDiff(0, true);
|
||||||
|
changeGameMode(0, true);
|
||||||
|
|
||||||
#if PRELOAD_ALL
|
#if PRELOAD_ALL
|
||||||
autoplayElapsed = 0;
|
autoplayElapsed = 0;
|
||||||
songInstPlaying = false;
|
songInstPlaying = false;
|
||||||
#end
|
#end
|
||||||
|
|
||||||
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateOptionsAlpha() {
|
function updateOptionsAlpha() {
|
||||||
@@ -517,6 +498,118 @@ class FreeplayState extends MusicBeatState
|
|||||||
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
|
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
|
||||||
curSong = song;
|
curSong = song;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Backwards compat
|
||||||
|
@:noCompletion public function changeCoopMode(change:Int = 0, force:Bool = false) return changeGameMode(change, force);
|
||||||
|
|
||||||
|
@:noCompletion public var coopText(get, set):FlxText;
|
||||||
|
@:noCompletion private function get_coopText() return gameModeText;
|
||||||
|
@:noCompletion private function set_coopText(val:FlxText) return gameModeText = val;
|
||||||
|
|
||||||
|
@:noCompletion function updateCoopModes() {}
|
||||||
|
@:noCompletion var __opponentMode(get, default):Bool = false; // God this is so cursed - Nex
|
||||||
|
@:noCompletion private function get___opponentMode() return getAllowedGameModesID().contains("codename.opponent");
|
||||||
|
@:noCompletion var __coopMode(get, default):Bool = false;
|
||||||
|
@:noCompletion private function get___coopMode() return getAllowedGameModesID().contains("codename.coop");
|
||||||
|
|
||||||
|
@:noCompletion var curCoopMode(get, set):Int;
|
||||||
|
@:noCompletion private function get_curCoopMode() return curGameMode;
|
||||||
|
@:noCompletion private function set_curCoopMode(val:Int) return curGameMode = val;
|
||||||
|
|
||||||
|
@:noCompletion var coopLabels(get, set):Array<String>;
|
||||||
|
@:noCompletion private function get_coopLabels() return [for(mode in gameModeLabels) mode.modeName];
|
||||||
|
@:noCompletion private function set_coopLabels(names:Array<String>) {
|
||||||
|
gameModeLabels = [for(name in names) {
|
||||||
|
if (name.startsWith("[TAB] ")) name = name.substr("[TAB] ".length);
|
||||||
|
new FreeplayGameMode(name, switch(name) {
|
||||||
|
case "Solo": "codename.solo";
|
||||||
|
case "Opponent Mode": "codename.opponent";
|
||||||
|
case "Co-Op Mode": "codename.coop";
|
||||||
|
case "Co-Op Mode (Switched)": "codename.coop-opponent";
|
||||||
|
default: name.toLowerCase().replace(" ", "-");
|
||||||
|
}, switch(name) {
|
||||||
|
case "Solo": ["solo"];
|
||||||
|
case "Opponent Mode": ["opponent"];
|
||||||
|
case "Co-Op Mode": ["coop"];
|
||||||
|
case "Co-Op Mode (Switched)": ["coop-switched", "opponent", "coop"];
|
||||||
|
default: null;
|
||||||
|
});
|
||||||
|
}];
|
||||||
|
return names;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used for the Freeplay menu game modes.
|
||||||
|
*
|
||||||
|
* **NOTE**: `fields` is useable not just for boolean values!!
|
||||||
|
*/
|
||||||
|
class FreeplayGameMode {
|
||||||
|
public var modeName:String;
|
||||||
|
public var modeID:String;
|
||||||
|
public var scripts(default, set):Array<String>;
|
||||||
|
public var fields(default, set):Dynamic;
|
||||||
|
|
||||||
|
@:noCompletion function set_scripts(val:Array<String>) return scripts = (val == null ? [] : val);
|
||||||
|
@:noCompletion function set_fields(val:Dynamic) return fields = (val == null ? {} : val);
|
||||||
|
|
||||||
|
public function new(modeName:String, modeID:String, ?scripts:Array<String>, ?fields:Dynamic) {
|
||||||
|
this.modeName = modeName;
|
||||||
|
this.modeID = modeID;
|
||||||
|
this.scripts = scripts;
|
||||||
|
this.fields = fields;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toModifiers():Array<Dynamic> {
|
||||||
|
var fields = this.fields;
|
||||||
|
var modifiers:Array<Dynamic> = [];
|
||||||
|
for (field in {var _ = Reflect.fields(fields); _.sort(Reflect.compare); _;}) {
|
||||||
|
var val:Dynamic = Reflect.field(fields, field);
|
||||||
|
if (val != null && val != false) { // allow true, and floats/ints
|
||||||
|
modifiers.push(field);
|
||||||
|
modifiers.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return modifiers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static inline function generateDefault() return new FreeplayGameMode("Solo Mode", "solo");
|
||||||
|
|
||||||
|
public static inline function getGameModesFromSource(source:AssetSource = BOTH, useTxt:Bool = true):Array<FreeplayGameMode> {
|
||||||
|
var path = 'data/gamemodes';
|
||||||
|
var txt = Paths.txt('gamemodes/list');
|
||||||
|
var list = useTxt && Paths.assetsTree.existsSpecific(txt, "TEXT", source) ? CoolUtil.coolTextFile(txt) : Paths.getFolderContent(path, false, source);
|
||||||
|
|
||||||
|
return [for (file in list) {
|
||||||
|
if (useTxt) file += ".json";
|
||||||
|
else if (Path.extension(file) != "json") continue;
|
||||||
|
|
||||||
|
var meta:FreeplayGameMode = null;
|
||||||
|
try {
|
||||||
|
var data = CoolUtil.parseJson(Paths.file(path + "/" + file)); var id = data.modeID;
|
||||||
|
meta = new FreeplayGameMode(CoolUtil.getDefault(data.displayName, id), id, [Path.withoutExtension(file)].concat(CoolUtil.getDefault(data.scripts, [])), data.fields);
|
||||||
|
} catch(e) Logs.trace('Failed to load game mode metadata for $file ($path): ${Std.string(e)}', ERROR);
|
||||||
|
if (meta != null) meta;
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function get(useTxt:Bool = true):Array<FreeplayGameMode> {
|
||||||
|
var list:Array<FreeplayGameMode>;
|
||||||
|
|
||||||
|
switch(Flags.GAME_MODES_LIST_MOD_MODE) {
|
||||||
|
case 'prepend':
|
||||||
|
list = getGameModesFromSource(MODS, useTxt).concat(getGameModesFromSource(SOURCE, useTxt));
|
||||||
|
case 'append':
|
||||||
|
list = getGameModesFromSource(SOURCE, useTxt).concat(getGameModesFromSource(MODS, useTxt));
|
||||||
|
case 'override':
|
||||||
|
list = getGameModesFromSource(BOTH, useTxt);
|
||||||
|
default /*case 'oneOFtwo'*/:
|
||||||
|
if ((list = getGameModesFromSource(MODS, useTxt)).length == 0)
|
||||||
|
list = getGameModesFromSource(SOURCE, useTxt);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FreeplaySonglist {
|
class FreeplaySonglist {
|
||||||
@@ -553,7 +646,9 @@ class FreeplaySonglist {
|
|||||||
case 'append':
|
case 'append':
|
||||||
songList.getSongsFromSource(SOURCE, useTxt);
|
songList.getSongsFromSource(SOURCE, useTxt);
|
||||||
songList.getSongsFromSource(MODS, useTxt);
|
songList.getSongsFromSource(MODS, useTxt);
|
||||||
default /*case 'override'*/:
|
case 'override':
|
||||||
|
songList.getSongsFromSource(BOTH, useTxt);
|
||||||
|
default /*case 'oneOFtwo'*/:
|
||||||
if (songList.getSongsFromSource(MODS, useTxt))
|
if (songList.getSongsFromSource(MODS, useTxt))
|
||||||
songList.getSongsFromSource(SOURCE, useTxt);
|
songList.getSongsFromSource(SOURCE, useTxt);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -342,10 +342,17 @@ class StoryWeeklist {
|
|||||||
public function new() {}
|
public function new() {}
|
||||||
|
|
||||||
public function getWeeksFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) {
|
public function getWeeksFromSource(source:funkin.backend.assets.AssetSource, useTxt:Bool = true, loadCharactersData:Bool = true) {
|
||||||
var path:String = Paths.txt('weeks/weeks');
|
var weeksFound:Array<String> = null;
|
||||||
var weeksFound:Array<String> = useTxt && Paths.assetsTree.existsSpecific(path, "TEXT", source) ? CoolUtil.coolTextFile(path) :
|
if (useTxt) {
|
||||||
[for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)];
|
var oldPath:String = Paths.txt('weeks/weeks');
|
||||||
|
var newPath:String = Paths.txt('config/weeks');
|
||||||
|
if (Paths.assetsTree.existsSpecific(newPath, "TEXT", source)) weeksFound = CoolUtil.coolTextFile(newPath);
|
||||||
|
else if (Paths.assetsTree.existsSpecific(oldPath, "TEXT", source)) {
|
||||||
|
Logs.warn("data/weeks/weeks.txt is deprecated and will be removed in the future. Please move the file to data/config/", DARKYELLOW, "StoryWeeklist");
|
||||||
|
weeksFound = CoolUtil.coolTextFile(oldPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (weeksFound == null) weeksFound = [for (c in Paths.getFolderContent('data/weeks/weeks/', false, source)) if (Path.extension(c).toLowerCase() == "xml") Path.withoutExtension(c)];
|
||||||
if (weeksFound.length > 0) {
|
if (weeksFound.length > 0) {
|
||||||
for (w in weeksFound) {
|
for (w in weeksFound) {
|
||||||
var week = Week.loadWeek(w, loadCharactersData);
|
var week = Week.loadWeek(w, loadCharactersData);
|
||||||
|
|||||||
Reference in New Issue
Block a user