Bring dev/scripted-game-modes
This commit is contained in:
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"displayName": "Botplay Mode",
|
||||
"modeID": "codename.botplay"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"displayName": "Co-Op Mode (Switched)",
|
||||
"modeID": "codename.coop-opponent",
|
||||
"scripts": ["opponent", "coop"]
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"displayName": "Co-Op Mode",
|
||||
"modeID": "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,4 @@
|
||||
{
|
||||
"displayName": "Solo Mode",
|
||||
"modeID": "codename.solo"
|
||||
}
|
||||
@@ -97,6 +97,41 @@ class Chart {
|
||||
data.setFieldDefault("vocalsSuffix", "");
|
||||
data.setFieldDefault("needsVoices", true);
|
||||
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("metas", []);
|
||||
|
||||
|
||||
@@ -32,8 +32,7 @@ typedef ChartMetaData = {
|
||||
public var ?icon:String;
|
||||
public var ?color:FlxColor;
|
||||
|
||||
public var ?coopAllowed:Bool;
|
||||
public var ?opponentModeAllowed:Bool;
|
||||
public var ?excludedGameModes:Array<String>;
|
||||
|
||||
public var ?metas:Map<String, ChartMetaData>;
|
||||
public var ?instSuffix:String;
|
||||
|
||||
@@ -402,4 +402,4 @@ class Script extends FlxBasic implements IFlxDestroyable {
|
||||
* Called when the script is loaded.
|
||||
*/
|
||||
public function onLoad() {}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package funkin.backend.scripting.events.menu.freeplay;
|
||||
|
||||
import funkin.menus.FreeplayState.FreeplayGameMode;
|
||||
|
||||
final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||
/**
|
||||
* Song name that is about to be played
|
||||
@@ -14,11 +16,26 @@ final class FreeplaySongSelectEvent extends CancellableEvent {
|
||||
*/
|
||||
public var variant:String;
|
||||
/**
|
||||
* Whenever opponent mode is enabled or not.
|
||||
* Used game mode's data.
|
||||
*/
|
||||
public var opponentMode:Bool;
|
||||
/**
|
||||
* Whenever coop mode is enabled.
|
||||
*/
|
||||
public var coopMode:Bool;
|
||||
public var gameMode:FreeplayGameMode;
|
||||
|
||||
|
||||
// Backwards compat
|
||||
@: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;
|
||||
}
|
||||
}
|
||||
@@ -389,4 +389,4 @@ class Flags {
|
||||
}
|
||||
loadPost();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1495,4 +1495,4 @@ enum abstract CoolSfx(Int) from Int {
|
||||
var CHECKED = 3;
|
||||
var UNCHECKED = 4;
|
||||
var WARNING = 5;
|
||||
}
|
||||
}
|
||||
@@ -541,4 +541,4 @@ typedef DEvents =
|
||||
var ?joinGame:String->Void;
|
||||
var ?spectateGame:String->Void;
|
||||
var ?joinRequest:DUser->Void;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import funkin.game.HealthIcon;
|
||||
import flixel.math.FlxPoint;
|
||||
import funkin.backend.chart.ChartData.ChartMetaData;
|
||||
import funkin.editors.extra.PropertyButton;
|
||||
@@ -21,8 +22,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
public var displayNameTextBox:UITextBox;
|
||||
public var iconTextBox:UITextBox;
|
||||
public var iconSprite:HealthIcon;
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var excludedGameModesList:UIAutoCompleteButtonList;
|
||||
public var needsVoicesCheckbox:UICheckbox;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
@@ -78,25 +78,29 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
add(displayNameTextBox);
|
||||
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.onChange = (newIcon:String) -> {updateIcon(newIcon);}
|
||||
iconTextBox.onChange = (newIcon:String) -> iconSprite.setIcon(newIcon);
|
||||
add(iconTextBox);
|
||||
addLabelOn(iconTextBox, translate("icon"));
|
||||
|
||||
updateIcon(metadata.icon);
|
||||
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translate("opponentMode"), metadata.opponentModeAllowed);
|
||||
add(opponentModeCheckbox);
|
||||
addLabelOn(opponentModeCheckbox, translate("modesAllowed"));
|
||||
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translate("coopAllowed"), metadata.coopAllowed);
|
||||
add(coopAllowedCheckbox);
|
||||
add(excludedGameModesList = new UIAutoCompleteButtonList(displayNameTextBox.x, iconTextBox.y + 62, Std.int(displayNameTextBox.bWidth), 100, "", [for (mode in funkin.menus.FreeplayState.FreeplayGameMode.get()) mode.modeID]));
|
||||
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
excludedGameModesList.cameraSpacing = 0;
|
||||
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
|
||||
for (mode in metadata.excludedGameModes)
|
||||
excludedGameModesList.add(new UIAutoCompleteButtonList.UIAutoCompleteButton(0, 0, excludedGameModesList, excludedGameModesList.suggestItems, mode));
|
||||
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, metadata.color);
|
||||
add(colorWheel);
|
||||
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);
|
||||
addLabelOn(difficultiesTextBox, translate("difficulties"));
|
||||
|
||||
@@ -111,9 +115,6 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
add(customPropertiesButtonList);
|
||||
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() {
|
||||
saveMeta();
|
||||
close();
|
||||
@@ -161,8 +162,7 @@ class CharterMetaDataScreen extends UISubstateWindow {
|
||||
meta.displayName = displayNameTextBox.label.text;
|
||||
meta.icon = iconTextBox.label.text;
|
||||
meta.color = colorWheel.curColor;
|
||||
meta.opponentModeAllowed = opponentModeCheckbox.checked;
|
||||
meta.coopAllowed = coopAllowedCheckbox.checked;
|
||||
meta.excludedGameModes = [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.trim()],
|
||||
meta.difficulties = [for (diff in difficultiesTextBox.label.text.split(",")) diff.trim()];
|
||||
meta.customValues = customVals;
|
||||
|
||||
|
||||
@@ -287,4 +287,4 @@ class CharacterButton extends UIButton {
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package funkin.editors.charter;
|
||||
|
||||
import funkin.game.HealthIcon;
|
||||
import flixel.group.FlxGroup;
|
||||
import flixel.text.FlxText.FlxTextFormat;
|
||||
import flixel.text.FlxText.FlxTextFormatMarkerPair;
|
||||
@@ -37,8 +38,7 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
public var displayNameTextBox:UITextBox;
|
||||
public var iconTextBox:UITextBox;
|
||||
public var iconSprite:HealthIcon;
|
||||
public var opponentModeCheckbox:UICheckbox;
|
||||
public var coopAllowedCheckbox:UICheckbox;
|
||||
public var excludedGameModesList:UIAutoCompleteButtonList;
|
||||
public var colorWheel:UIColorwheel;
|
||||
public var difficultiesTextBox:UITextBox;
|
||||
|
||||
@@ -166,24 +166,19 @@ class SongCreationScreen extends UISubstateWindow {
|
||||
|
||||
updateIcon("face");
|
||||
|
||||
opponentModeCheckbox = new UICheckbox(displayNameTextBox.x, iconTextBox.y + 10 + 32 + 26, translateMeta("opponentMode"), true);
|
||||
menuDataGroup.add(opponentModeCheckbox);
|
||||
addLabelOn(opponentModeCheckbox, translateMeta("modesAllowed"));
|
||||
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]));
|
||||
excludedGameModesList.frames = Paths.getFrames('editors/ui/inputbox');
|
||||
excludedGameModesList.cameraSpacing = 0;
|
||||
addLabelOn(excludedGameModesList, "Excluded Game Modes (IDs)");
|
||||
|
||||
coopAllowedCheckbox = new UICheckbox(opponentModeCheckbox.x + 150 + 26, opponentModeCheckbox.y, translateMeta("coopAllowed"), true);
|
||||
menuDataGroup.add(coopAllowedCheckbox);
|
||||
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, coopAllowedCheckbox.y, 0xFFFFFF);
|
||||
colorWheel = new UIColorwheel(iconTextBox.x, excludedGameModesList.y, 0xFFFFFF);
|
||||
menuDataGroup.add(colorWheel);
|
||||
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);
|
||||
addLabelOn(difficultiesTextBox, translateMeta("difficulties"));
|
||||
|
||||
for (checkbox in [opponentModeCheckbox, coopAllowedCheckbox])
|
||||
{checkbox.y += 6; checkbox.x += 4;}
|
||||
|
||||
var menuTitle:UIText;
|
||||
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,
|
||||
icon: iconTextBox.label.text,
|
||||
color: colorWheel.curColor,
|
||||
opponentModeAllowed: opponentModeCheckbox.checked,
|
||||
coopAllowed: coopAllowedCheckbox.checked,
|
||||
excludedGameModes: [for (button in excludedGameModesList.buttons.members) button.textBox.label.text.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);
|
||||
}
|
||||
}
|
||||
@@ -588,4 +588,4 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset
|
||||
list.push(CoolUtil.getFilename(path));
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import funkin.backend.scripting.events.menu.freeplay.*;
|
||||
import funkin.backend.system.Conductor;
|
||||
import funkin.game.HealthIcon;
|
||||
import funkin.savedata.FunkinSave;
|
||||
import haxe.io.Path;
|
||||
|
||||
using StringTools;
|
||||
|
||||
@@ -34,6 +35,11 @@ class FreeplayState extends MusicBeatState
|
||||
*/
|
||||
public var curDiffMetaKeys:Array<String> = [];
|
||||
|
||||
/*
|
||||
* Array containing all labels for game modes.
|
||||
*/
|
||||
public var gameModeLabels:Array<FreeplayGameMode> = [];
|
||||
|
||||
/**
|
||||
* Currently selected song
|
||||
*/
|
||||
@@ -43,9 +49,9 @@ class FreeplayState extends MusicBeatState
|
||||
*/
|
||||
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)
|
||||
@@ -58,9 +64,9 @@ class FreeplayState extends MusicBeatState
|
||||
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`.
|
||||
@@ -130,6 +136,7 @@ class FreeplayState extends MusicBeatState
|
||||
}
|
||||
|
||||
updateCurSong();
|
||||
gameModeLabels = FreeplayGameMode.get();
|
||||
|
||||
DiscordUtil.call("onMenuLoaded", ["Freeplay"]);
|
||||
|
||||
@@ -177,14 +184,15 @@ class FreeplayState extends MusicBeatState
|
||||
diffText.font = scoreText.font;
|
||||
add(diffText);
|
||||
|
||||
coopText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24);
|
||||
coopText.font = scoreText.font;
|
||||
add(coopText);
|
||||
gameModeText = new FlxText(diffText.x, diffText.y + diffText.height + 2, 0, "", 24);
|
||||
gameModeText.font = scoreText.font;
|
||||
add(gameModeText);
|
||||
|
||||
add(scoreText);
|
||||
|
||||
changeSelection(0, true);
|
||||
changeCoopMode(0, true);
|
||||
changeDiff(0, true);
|
||||
changeGameMode(0, true);
|
||||
|
||||
interpColor = new FlxInterpolateColor(bg.color);
|
||||
}
|
||||
@@ -239,17 +247,17 @@ class FreeplayState extends MusicBeatState
|
||||
if (canSelect) {
|
||||
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));
|
||||
changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable
|
||||
changeGameMode((controls.CHANGE_MODE ? 1 : 0));
|
||||
// putting it before so that its actually smooth
|
||||
updateOptionsAlpha();
|
||||
}
|
||||
|
||||
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.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));
|
||||
|
||||
interpColor.fpsLerpTo(curSong.color, 0.0625);
|
||||
@@ -310,31 +318,13 @@ class FreeplayState extends MusicBeatState
|
||||
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.
|
||||
*/
|
||||
public function select() {
|
||||
updateCoopModes();
|
||||
|
||||
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;
|
||||
|
||||
@@ -393,7 +383,7 @@ class FreeplayState extends MusicBeatState
|
||||
intendedScore = 0;
|
||||
return;
|
||||
}
|
||||
updateCoopModes();
|
||||
|
||||
var changes:Array<HighscoreChange> = [];
|
||||
if (__coopMode) changes.push(CCoopMode);
|
||||
if (__opponentMode) changes.push(COpponentMode);
|
||||
@@ -401,45 +391,37 @@ class FreeplayState extends MusicBeatState
|
||||
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.
|
||||
* @param change How much to change
|
||||
* @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 (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
|
||||
|
||||
var bothEnabled = curSong.coopAllowed && curSong.opponentModeAllowed;
|
||||
var event = event("onChangeCoopMode", EventManager.get(MenuChangeEvent).recycle(curCoopMode, FlxMath.wrap(curCoopMode + change, 0, bothEnabled ? 3 : 1), change));
|
||||
|
||||
if (event.cancelled) return;
|
||||
|
||||
curCoopMode = event.value;
|
||||
|
||||
updateScore();
|
||||
|
||||
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 i = 0;
|
||||
var allowed = getAllowedGameModesID();
|
||||
var wrapped = FlxMath.wrap(curGameMode + change, 0, gameModeLabels.length - 1);
|
||||
while (!allowed.contains(gameModeLabels[wrapped].modeID)) { // Skipping the blacklisted ones - Nex
|
||||
wrapped = FlxMath.wrap(wrapped + change, 0, gameModeLabels.length - 1);
|
||||
if (i++ > allowed.length) {
|
||||
curGameMode = 0; // Idk honestly if I should modify the other event's variables - Nex
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -465,13 +447,12 @@ class FreeplayState extends MusicBeatState
|
||||
}
|
||||
|
||||
changeDiff(0, true);
|
||||
changeGameMode(0, true);
|
||||
|
||||
#if PRELOAD_ALL
|
||||
autoplayElapsed = 0;
|
||||
songInstPlaying = false;
|
||||
#end
|
||||
|
||||
coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
|
||||
}
|
||||
|
||||
function updateOptionsAlpha() {
|
||||
@@ -486,7 +467,7 @@ class FreeplayState extends MusicBeatState
|
||||
|
||||
iconArray[curSelected].alpha = selectedAlpha;
|
||||
|
||||
for (i=>item in grpSongs.members)
|
||||
for (i => item in grpSongs.members)
|
||||
{
|
||||
item.targetY = i - curSelected;
|
||||
|
||||
@@ -517,6 +498,118 @@ class FreeplayState extends MusicBeatState
|
||||
else if ((curSong = song.metas.get(curDiffMetaKeys[curDifficulty])) == null)
|
||||
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 {
|
||||
@@ -553,7 +646,9 @@ class FreeplaySonglist {
|
||||
case 'append':
|
||||
songList.getSongsFromSource(SOURCE, useTxt);
|
||||
songList.getSongsFromSource(MODS, useTxt);
|
||||
default /*case 'override'*/:
|
||||
case 'override':
|
||||
songList.getSongsFromSource(BOTH, useTxt);
|
||||
default /*case 'oneOFtwo'*/:
|
||||
if (songList.getSongsFromSource(MODS, useTxt))
|
||||
songList.getSongsFromSource(SOURCE, useTxt);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user