diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md
index dd818bf4..47912cfb 100644
--- a/.github/ISSUE_TEMPLATE/bug.md
+++ b/.github/ISSUE_TEMPLATE/bug.md
@@ -2,12 +2,18 @@
name: Bug Report
about: Report a bug or critical performance issue
title: 'Bug Report: [DESCRIBE YOUR BUG IN DETAIL HERE]'
-labels: bug
+labels: ''
+assignees: ''
+
---
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
[weed]: <> (MAKE SURE TO ADD YOUR TARGET OR IT'LL BE IGNORED!)
+### What version of the engine are you using?
+[weed]: <> (IF YOU COMPILED THE ENGINE PLEASE PROVIDE A COMMIT HASH THAT WE CAN BASE OFF OF)
+Version:
+
### Which target are you using?
[weed]: <> (CHANGE [ ] TO [x] IN FRONT OF YOUR TARGET!)
@@ -17,4 +23,4 @@ labels: bug
### Explain your issue
##### Please check first if your issue haven't already been reported yet.
-[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
\ No newline at end of file
+[weed]: <> (MAKE SURE TO ADD A SCREENSHOT OF THE CRASH HANDLER, IF AVAILABLE, AND STEPS TO REPRODUCE THE BUG!)
diff --git a/.github/ISSUE_TEMPLATE/compiling.md b/.github/ISSUE_TEMPLATE/compiling.md
index 4f516469..dd28d038 100644
--- a/.github/ISSUE_TEMPLATE/compiling.md
+++ b/.github/ISSUE_TEMPLATE/compiling.md
@@ -1,8 +1,11 @@
---
name: Compiling help
-about: If you need help compiling the game, and you're running into issues. (Look through the 'compiling help' label in case it's been solved!)
+about: If you need help compiling the game, and you're running into issues. (Look
+ through the 'compiling help' label in case it's been solved!)
title: 'Compiling help: [BRIEF DESCRIPTION / ERROR MESSAGE OUTPUT]'
-labels: compiling help
+labels: ''
+assignees: ''
+
---
[weed]: <> (MAKE SURE THAT THE ISSUE IS AS COMPLETE AS POSSIBLE. ISSUES THAT HAVE BARELY ANY INFORMATION OR IS ONLY MADE FOR TROLLING WILL BE INSTANTLY CLOSED.)
diff --git a/.github/ISSUE_TEMPLATE/enhancement.md b/.github/ISSUE_TEMPLATE/enhancement.md
index e1cc3ae0..d41691ff 100644
--- a/.github/ISSUE_TEMPLATE/enhancement.md
+++ b/.github/ISSUE_TEMPLATE/enhancement.md
@@ -2,7 +2,10 @@
name: Enhancement
about: Suggest a new feature
title: 'Enhancement: '
-labels: enhancement
+labels: ''
+assignees: ''
+
---
+
#### Please check for duplicates or similar issues before creating this issue.
## What is your suggestion, and why should it be implemented?
diff --git a/.vscode/settings.json b/.vscode/settings.json
index fea6e023..d7ed0f09 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,5 +6,37 @@
"*.hscript": "haxe",
"*.hsc": "haxe",
"*.hxs": "haxe"
- }
+ },
+ "lime.targetConfigurations": [
+ {
+ "label": "Windows (Test Build)",
+ "target": "windows",
+ "args": ["-DTEST_BUILD"]
+ },
+ {
+ "label": "Linux (Test Build)",
+ "target": "linux",
+ "args": ["-DTEST_BUILD"]
+ },
+ {
+ "label": "MacOS (Test Build)",
+ "target": "macos",
+ "args": ["-DTEST_BUILD"]
+ },
+ {
+ "label": "Windows / Debug (Test Build)",
+ "target": "windows",
+ "args": ["-debug", "-DTEST_BUILD"]
+ },
+ {
+ "label": "Linux / Debug (Test Build)",
+ "target": "linux",
+ "args": ["-debug", "-DTEST_BUILD"]
+ },
+ {
+ "label": "MacOS / Debug (Test Build)",
+ "target": "macos",
+ "args": ["-debug", "-DTEST_BUILD"]
+ },
+ ]
}
\ No newline at end of file
diff --git a/README.md b/README.md
index fb3918bb..88bdc22b 100644
--- a/README.md
+++ b/README.md
@@ -18,10 +18,10 @@ Codename Engine is a new Friday Night Funkin' Engine aimed at simplifying moddin
## How to download
-Latest builds for the engine can be found in the [Actions](https://github.com/YoshiCrafter29/CodenameEngine/actions) tab.
+Latest builds for the engine can be found in the [Actions](https://github.com/CodenameCrew/CodenameEngine/actions) tab.
In the future (when the engine won't be a WIP anymore) we're gonna also publish the engine on platforms like Gamebanana; stay tuned!
-If you don't have a github account please go onto https://fnf-cne-devs.github.io/ and click the download button for the respective operating system.
+If you don't have a github account please go onto https://codename-engine.com/ and click the download button for the respective operating system.
How to build
diff --git a/art/flashFiles/FNF_main_menu_assets.fla b/art/flashFiles/FNF_main_menu_assets.fla
index 80d3615e..1b579331 100644
Binary files a/art/flashFiles/FNF_main_menu_assets.fla and b/art/flashFiles/FNF_main_menu_assets.fla differ
diff --git a/assets/images/editors/charter/event-icons/Camera Position.png b/assets/images/editors/charter/event-icons/Camera Position.png
new file mode 100644
index 00000000..ddc79379
Binary files /dev/null and b/assets/images/editors/charter/event-icons/Camera Position.png differ
diff --git a/assets/images/editors/charter/event-icons/Camera Zoom.png b/assets/images/editors/charter/event-icons/Camera Zoom.png
new file mode 100644
index 00000000..e685b525
Binary files /dev/null and b/assets/images/editors/charter/event-icons/Camera Zoom.png differ
diff --git a/assets/languages/en/Editors.xml b/assets/languages/en/Editors.xml
index bc36ff0e..fd1fc32a 100644
--- a/assets/languages/en/Editors.xml
+++ b/assets/languages/en/Editors.xml
@@ -9,6 +9,8 @@
Save
Save & Close
Close
+ Not Now
+ Yes
@@ -665,4 +667,12 @@
Save & Exit To Menu
Cancel
+
+
+ Missing mod folder configuration!
+ Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)
+
+ Created mod config!
+ Your mod config file has been created at {0}!
+
\ No newline at end of file
diff --git a/assets/languages/en/Options.xml b/assets/languages/en/Options.xml
index f8b1552c..68862bac 100644
--- a/assets/languages/en/Options.xml
+++ b/assets/languages/en/Options.xml
@@ -42,6 +42,7 @@
Back
Reset
Pause
+ Change Mode
Engine
@@ -50,6 +51,8 @@
Volume
+ Up
+ Down
Mute
Developer
diff --git a/assets/languages/es/Options.xml b/assets/languages/es/Options.xml
index 122b5450..5033ffe5 100644
--- a/assets/languages/es/Options.xml
+++ b/assets/languages/es/Options.xml
@@ -49,6 +49,8 @@
Volumen
+ Arriba
+ Abajo
mutear
Desarrollador
diff --git a/assets/languages/it/Options.xml b/assets/languages/it/Options.xml
index dc392539..6498fc61 100644
--- a/assets/languages/it/Options.xml
+++ b/assets/languages/it/Options.xml
@@ -48,6 +48,8 @@
Cambia Mod
+ Su
+ Giù
Muto
Sviluppatore
diff --git a/assets/languages/pl/Editors.xml b/assets/languages/pl/Editors.xml
index 0185177a..9dfde365 100644
--- a/assets/languages/pl/Editors.xml
+++ b/assets/languages/pl/Editors.xml
@@ -9,6 +9,8 @@
Zapisz
Zapisz i Zamknij
Zamknij
+ Nie Teraz
+ Tak
@@ -653,4 +655,12 @@
Zapisz i Wyjdź do Menu
Anuluj
+
+
+ Brakujący plik konfiguracyjny modu!
+ Twój mod obecnie brakuje jego pliku konfiguracyjnego!\n\nCzy chcesz go automatycznie wygenerować?\n\n(PS: Jeżeli to nie twój mod, wyłącz Tryb Dewelopera, aby zapobiec wyskakiwaniu się tego okienka.)
+
+ Utworzono konfigurację modu!
+ Twój plik konfiguracyjny modu został utworzony w {0}!
+
diff --git a/assets/languages/pl/Main.xml b/assets/languages/pl/Main.xml
index f3d334d3..644d8784 100644
--- a/assets/languages/pl/Main.xml
+++ b/assets/languages/pl/Main.xml
@@ -57,7 +57,7 @@
Wszyscy współpracownicy silnika! - Naciśnij RESET aby zaktualizować listę (Jeden reset co 2 minuty).
- Kredyty
+ Twórcy
Ludzie którzy to umożliwili!
Wybierz to aby zobaczyć wszystkich współpracowników!
Wybierz to aby otworzyć stronę itch.io oryginalnej gry!
diff --git a/assets/languages/pl/Options.xml b/assets/languages/pl/Options.xml
index 1d9eb931..d5fb2d34 100644
--- a/assets/languages/pl/Options.xml
+++ b/assets/languages/pl/Options.xml
@@ -38,6 +38,7 @@
Wstecz
Reset
Pauza
+ Zmień Tryb
Silnik
@@ -46,6 +47,8 @@
Głośność
+ Głóśniej
+ Ciszej
Wycisz
Deweloper
diff --git a/assets/languages/pl/assets/images/menus/mainmenu/credits.png b/assets/languages/pl/assets/images/menus/mainmenu/credits.png
index 668d9f8f..5ed403c9 100644
Binary files a/assets/languages/pl/assets/images/menus/mainmenu/credits.png and b/assets/languages/pl/assets/images/menus/mainmenu/credits.png differ
diff --git a/assets/languages/pl/assets/images/menus/mainmenu/credits.xml b/assets/languages/pl/assets/images/menus/mainmenu/credits.xml
index 23e50a85..e4447e71 100644
--- a/assets/languages/pl/assets/images/menus/mainmenu/credits.xml
+++ b/assets/languages/pl/assets/images/menus/mainmenu/credits.xml
@@ -1,15 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/languages/pl/assets/images/menus/mainmenu/freeplay.png b/assets/languages/pl/assets/images/menus/mainmenu/freeplay.png
index 14b05822..1cb04c63 100644
Binary files a/assets/languages/pl/assets/images/menus/mainmenu/freeplay.png and b/assets/languages/pl/assets/images/menus/mainmenu/freeplay.png differ
diff --git a/assets/languages/pl/assets/images/menus/mainmenu/freeplay.xml b/assets/languages/pl/assets/images/menus/mainmenu/freeplay.xml
index d54fb418..a4aed8fd 100644
--- a/assets/languages/pl/assets/images/menus/mainmenu/freeplay.xml
+++ b/assets/languages/pl/assets/images/menus/mainmenu/freeplay.xml
@@ -1,15 +1,15 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/assets/languages/pl/config.ini b/assets/languages/pl/config.ini
index 24c29233..ee614071 100644
--- a/assets/languages/pl/config.ini
+++ b/assets/languages/pl/config.ini
@@ -1,3 +1,3 @@
name="Polski"
credits="GlitchDotSmh, Kolze & Verwex"
-version="1.0.0"
\ No newline at end of file
+version="1.1.0"
\ No newline at end of file
diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx
index 1891a78b..48cc8f4f 100644
--- a/source/funkin/backend/assets/Paths.hx
+++ b/source/funkin/backend/assets/Paths.hx
@@ -38,6 +38,7 @@ class Paths
for (entry in entries) {
if (entry.toLowerCase() == part.toLowerCase()) {
fixedPath += entry + (it != parts.length - 1 ? "/" : "");
+ break;
}
}
}
@@ -88,18 +89,18 @@ class Paths
inline static public function music(key:String, ?library:String, ?ext:String)
return getPath('music/$key.${ext != null ? ext : Flags.SOUND_EXT}', library);
- inline static public function voices(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
+ inline static public function voices(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
- var diff = getPath('songs/$song/song/Voices$prefix-${difficulty}.${ext}', null);
- return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$prefix.${ext}', null);
+ var diff = getPath('songs/$song/song/Voices$suffix-${difficulty}.${ext}', null);
+ return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Voices$suffix.${ext}', null);
}
- inline static public function inst(song:String, ?difficulty:String, ?prefix:String = "", ?ext:String) {
+ inline static public function inst(song:String, ?difficulty:String, ?suffix:String = "", ?ext:String) {
if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
if (ext == null) ext = Flags.SOUND_EXT;
- var diff = getPath('songs/$song/song/Inst$prefix-${difficulty}.${ext}', null);
- return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$prefix.${ext}', null);
+ var diff = getPath('songs/$song/song/Inst$suffix-${difficulty}.${ext}', null);
+ return OpenFlAssets.exists(diff) ? diff : getPath('songs/$song/song/Inst$suffix.${ext}', null);
}
static public function image(key:String, ?library:String, checkForAtlas:Bool = false, ?ext:String) {
diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx
index a03d2d43..a9637e6a 100644
--- a/source/funkin/backend/chart/Chart.hx
+++ b/source/funkin/backend/chart/Chart.hx
@@ -83,13 +83,11 @@ class Chart {
return data;
}
- public static function loadChartMeta(songName:String, ?difficulty:String, fromMods:Bool = true):ChartMetaData {
- if (difficulty == null) difficulty = Flags.DEFAULT_DIFFICULTY;
-
+ public static function loadChartMeta(songName:String, difficulty:String = '', fromMods:Bool = true, includeMetaDifficulties:Bool = true):ChartMetaData {
var metaPath = Paths.file('songs/${songName}/meta.json');
var metaDiffPath = Paths.file('songs/${songName}/meta-${difficulty}.json');
- var data:ChartMetaData = null;
+ var data:ChartMetaData = null, fromDifficulty = false;
var fromMods:Bool = fromMods;
for (path in [metaDiffPath, metaPath]) if (Assets.exists(path)) {
fromMods = Paths.assetsTree.existsSpecific(path, "TEXT", MODS);
@@ -98,24 +96,31 @@ class Chart {
tempData.color = CoolUtil.getColorFromDynamic(tempData.color).getDefault(Flags.DEFAULT_COLOR);
data = tempData;
} catch(e) Logs.trace('Failed to load song metadata for ${songName} ($path): ${Std.string(e)}', ERROR);
- if (data != null) break;
+ if (data != null) {
+ fromDifficulty = path == metaDiffPath;
+ break;
+ }
}
- if (data == null) data = {
- name: songName,
- bpm: 100
- };
+ if (data == null) {
+ data = {
+ name: songName,
+ bpm: Flags.DEFAULT_BPM
+ };
+ }
+ else {
+ data.name = songName;
+ data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
+ }
- data.name = songName;
- data.setFieldDefault("bpm", Flags.DEFAULT_BPM);
+ data.setFieldDefault("displayName", data.name);
data.setFieldDefault("beatsPerMeasure", Flags.DEFAULT_BEATS_PER_MEASURE);
data.setFieldDefault("stepsPerBeat", Flags.DEFAULT_STEPS_PER_BEAT);
- data.setFieldDefault("needsVoices", true);
data.setFieldDefault("icon", Flags.DEFAULT_HEALTH_ICON);
data.setFieldDefault("difficulties", []);
data.setFieldDefault("coopAllowed", Flags.DEFAULT_COOP_ALLOWED);
data.setFieldDefault("opponentModeAllowed", Flags.DEFAULT_OPPONENT_MODE_ALLOWED);
- data.setFieldDefault("displayName", data.name);
+ data.setFieldDefault("instSuffix", "");
if (data.difficulties.length <= 0) {
data.difficulties = [for(f in Paths.getFolderContent('songs/${songName}/charts/', false, fromMods ? MODS : SOURCE)) if (Path.extension(f.toUpperCase()) == "JSON") Path.withoutExtension(f)];
@@ -132,6 +137,14 @@ class Chart {
}
}
+ if (includeMetaDifficulties && !fromDifficulty) {
+ data.metas = [];
+ for (difficulty in data.difficulties) {
+ if (!data.metas.exists(difficulty) && Assets.exists(Paths.file('songs/${songName}/meta-${difficulty}.json')))
+ data.metas.set(difficulty, loadChartMeta(songName, difficulty, fromMods, false));
+ }
+ }
+
return data;
}
@@ -265,7 +278,7 @@ class Chart {
CoolUtil.safeSaveFile(chartPath, Json.stringify(filteredChart, null, saveSettings.prettyPrint == true ? Flags.JSON_PRETTY_PRINT : null));
- if (saveSettings.overrideExistingMeta == true || !FileSystem.exists(metaPath))
+ if (meta != null && (saveSettings.overrideExistingMeta || !FileSystem.exists(metaPath)))
CoolUtil.safeSaveFile(metaPath, makeMetaSaveable(meta));
#end
return filteredChart;
@@ -305,7 +318,8 @@ class Chart {
public static inline function makeMetaSaveable(meta:ChartMetaData, prettyPrint:Bool = true):String {
var data:Dynamic = Reflect.copy(meta);
- if (data.color != null) data.color = new FlxColor(data.color).toWebString(); // dont even ask me - Nex
+ if (data.color != null) data.color = FlxColor.fromInt(data.color).toWebString(); // dont even ask me - Nex
+ Reflect.deleteField(data, 'metas');
return Json.stringify(data, null, prettyPrint ? Flags.JSON_PRETTY_PRINT : null);
}
}
@@ -317,4 +331,4 @@ typedef ChartSaveSettings = {
var ?saveGlobalEvents:Bool;
var ?prettyPrint:Bool;
var ?folder:String;
-}
\ No newline at end of file
+}
diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx
index ba1b7ae7..16653fd9 100644
--- a/source/funkin/backend/chart/ChartData.hx
+++ b/source/funkin/backend/chart/ChartData.hx
@@ -28,7 +28,8 @@ typedef ChartMetaData = {
public var ?coopAllowed:Bool;
public var ?opponentModeAllowed:Bool;
public var ?customValues:Dynamic;
- public var ?needsVoices:Bool;
+ public var ?metas:Map;
+ public var ?instSuffix:String;
}
typedef ChartStrumLine = {
diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx
index 84e96aa2..9abdfe8e 100644
--- a/source/funkin/backend/chart/FNFLegacyParser.hx
+++ b/source/funkin/backend/chart/FNFLegacyParser.hx
@@ -40,7 +40,6 @@ class FNFLegacyParser {
}
result.meta.bpm = data.bpm;
- result.meta.needsVoices = data.needsVoices.getDefault(true);
var camFocusedBF:Bool = false;
var altAnims:Bool = false;
@@ -157,7 +156,7 @@ class FNFLegacyParser {
notes: null,
bpm: chart.meta.bpm,
speed: chart.scrollSpeed,
- needsVoices: chart.meta.needsVoices,
+ needsVoices: true,
player1: null,
player2: null,
diff --git a/source/funkin/backend/chart/VSliceParser.hx b/source/funkin/backend/chart/VSliceParser.hx
index b58ab9c7..4ef045f0 100644
--- a/source/funkin/backend/chart/VSliceParser.hx
+++ b/source/funkin/backend/chart/VSliceParser.hx
@@ -67,7 +67,6 @@ class VSliceParser {
var timeChanges = metadata.timeChanges;
result.meta.bpm = timeChanges[0].bpm;
- result.meta.needsVoices = true;
for (note in data)
{
diff --git a/source/funkin/backend/system/Controls.hx b/source/funkin/backend/system/Controls.hx
index 5f6f8f98..a7a540c2 100644
--- a/source/funkin/backend/system/Controls.hx
+++ b/source/funkin/backend/system/Controls.hx
@@ -22,6 +22,7 @@ enum Control
ACCEPT;
BACK;
PAUSE;
+ CHANGE_MODE;
//CHEAT;
SWITCHMOD;
@@ -145,6 +146,11 @@ class Controls extends FlxActionSet
@:pressed("reset") public var RESET_HOLD(get, set): Bool;
@:justReleased("reset") public var RESET_R(get, set): Bool;
+ @:gamepad([FlxGamepadInputID.BACK]) // select button
+ @:justPressed("change-mode") public var CHANGE_MODE(get, set): Bool;
+ @:pressed("change-mode") public var CHANGE_MODE_HOLD(get, set): Bool;
+ @:justReleased("change-mode") public var CHANGE_MODE_R(get, set): Bool;
+
//@:gamepad([])
//@:justPressed("cheat") public var CHEAT(get, set): Bool;
//@:pressed("cheat") public var CHEAT_HOLD(get, set): Bool;
diff --git a/source/funkin/backend/system/github/GitHub.hx b/source/funkin/backend/system/github/GitHub.hx
index 1e29d143..59d45478 100644
--- a/source/funkin/backend/system/github/GitHub.hx
+++ b/source/funkin/backend/system/github/GitHub.hx
@@ -12,13 +12,13 @@ import haxe.Exception;
* Requires the `GITHUB_API` macro to be defined.
* This has no authentication, so it's limited to unauthenticated requests, and rate limits.
**/
-class GitHub {
+final class GitHub {
/**
* Gets all the releases from a specific GitHub repository using the GitHub API.
- * @param user The user/organization that owns the repository
- * @param repository The repository name
- * @param onError Error Callback
- * @return Releases
+ * @param user The user/organization that owns the repository.
+ * @param repository The repository name.
+ * @param onError Error Callback.
+ * @return Releases in Array.
*/
public static function getReleases(user:String, repository:String, ?onError:Exception->Void):Array {
#if GITHUB_API
@@ -38,17 +38,16 @@ class GitHub {
/**
* Gets the contributors list from a specific GitHub repository using the GitHub API.
- * @param user The user/organization that owns the repository
- * @param repository The repository name
- * @param onError Error Callback
- * @return Contributors List
+ * @param user The user/organization that owns the repository.
+ * @param repository The repository name.
+ * @param onError Error Callback.
+ * @return Contributors List as Array.
*/
public static function getContributors(user:String, repository:String, ?onError:Exception->Void):Array {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/repos/${user}/${repository}/contributors'));
- if (!(data is Array))
- throw __parseGitHubException(data);
+ if (!(data is Array)) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -61,16 +60,15 @@ class GitHub {
/**
* Gets a specific GitHub organization using the GitHub API.
- * @param org The organization to get
- * @param onError Error Callback
- * @return Organization
+ * @param org The organization to get.
+ * @param onError Error Callback.
+ * @return Organization.
*/
public static function getOrganization(org:String, ?onError:Exception->Void):GitHubOrganization {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org'));
- if (Reflect.hasField(data, "documentation_url"))
- throw __parseGitHubException(data);
+ if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -84,16 +82,15 @@ class GitHub {
/**
* Gets the members list from a specific GitHub organization using the GitHub API.
* NOTE: Members use Contributors' structure!
- * @param org The organization to get the members from
- * @param onError Error Callback
- * @return Members List
+ * @param org The organization to get the members from.
+ * @param onError Error Callback.
+ * @return Members List as Array
*/
public static function getOrganizationMembers(org:String, ?onError:Exception->Void):Array {
#if GITHUB_API
try {
var data = Json.parse(HttpUtil.requestText('https://api.github.com/orgs/$org/members'));
- if (Reflect.hasField(data, "documentation_url"))
- throw __parseGitHubException(data);
+ if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
@@ -107,18 +104,15 @@ class GitHub {
/**
* Gets a specific GitHub user/organization using the GitHub API.
* NOTE: If organization, it will be returned with the structure of a normal user; use `getOrganization` if you specifically want an organization!
- * @param user The user/organization to get
- * @param onError Error Callback
- * @return User/Organization
+ * @param user The user/organization to get.
+ * @param onError Error Callback.
+ * @return User/Organization.
*/
public static function getUser(user:String, ?onError:Exception->Void):GitHubUser {
#if GITHUB_API
try {
- var url = 'https://api.github.com/users/$user';
-
- var data = Json.parse(HttpUtil.requestText(url));
- if (Reflect.hasField(data, "documentation_url"))
- throw __parseGitHubException(data);
+ var data = Json.parse(HttpUtil.requestText('https://api.github.com/users/$user'));
+ if (Reflect.hasField(data, "documentation_url")) throw __parseGitHubException(data);
return data;
} catch(e) {
diff --git a/source/funkin/backend/system/updating/UpdateAvailableScreen.hx b/source/funkin/backend/system/updating/UpdateAvailableScreen.hx
index fd13b2cc..6e0be38b 100644
--- a/source/funkin/backend/system/updating/UpdateAvailableScreen.hx
+++ b/source/funkin/backend/system/updating/UpdateAvailableScreen.hx
@@ -24,7 +24,11 @@ class UpdateAvailableScreen extends MusicBeatState {
public function new(check:UpdateCheckCallback) {
super();
+
this.check = check;
+ if (Date.now().getTime() - check.date.getTime() > 15000) {
+ if ((check = UpdateUtil.checkForUpdates(true)).newUpdate) this.check = check;
+ }
}
public override function create() {
diff --git a/source/funkin/backend/system/updating/UpdateUtil.hx b/source/funkin/backend/system/updating/UpdateUtil.hx
index d696fdc9..f5b4a4e0 100644
--- a/source/funkin/backend/system/updating/UpdateUtil.hx
+++ b/source/funkin/backend/system/updating/UpdateUtil.hx
@@ -2,60 +2,97 @@ package funkin.backend.system.updating;
import funkin.backend.system.github.GitHub;
import funkin.backend.system.github.GitHubRelease;
-import haxe.io.Path;
+
import lime.app.Application;
+
+import sys.thread.Mutex;
+import sys.thread.Thread;
import sys.FileSystem;
+import haxe.io.Path;
+
using funkin.backend.system.github.GitHub;
class UpdateUtil {
+ public static var lastUpdateCheck:Null;
+
+ private static var __waitCallbacks:ArrayVoid>;
+ private static var __mutex:Mutex;
+
public static function init() {
// deletes old bak file if it exists
#if sys
var bakPath = '${Path.withoutExtension(Sys.programPath())}.bak';
- if (FileSystem.exists(bakPath))
- FileSystem.deleteFile(bakPath);
+ if (FileSystem.exists(bakPath)) FileSystem.deleteFile(bakPath);
#end
+
+ __waitCallbacks = [];
+ __mutex = new Mutex();
+ Thread.create(checkForUpdates.bind(true, false));
}
- public static function checkForUpdates():UpdateCheckCallback {
- var curTag = 'v${Flags.VERSION}';
- trace("Current version: " + curTag);
+ public static function waitForUpdates(force = false, callback:UpdateCheckCallback->Void, lazy = false) {
+ if (__mutex.tryAcquire()) {
+ __mutex.release();
+ if (__shouldCheck(lazy) || force) {
+ __waitCallbacks.push(callback);
+ Thread.create(checkForUpdates.bind(force, false));
+ }
+ else
+ callback(lastUpdateCheck);
+ }
+ else
+ __waitCallbacks.push(callback);
+ }
- var error = false;
+ public static function checkForUpdates(force = false, lazy = false):UpdateCheckCallback {
+ var wasAcquired = !__mutex.tryAcquire();
+ if (wasAcquired) __mutex.acquire();
- var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, function(e) {
+ if ((!force || wasAcquired) && !__shouldCheck(lazy)) {
+ __mutex.release();
+ return lastUpdateCheck;
+ }
+
+ lastUpdateCheck = __checkForUpdates();
+ __mutex.release();
+
+ FlxG.signals.preUpdate.addOnce(__callWaitCallbacks);
+
+ return lastUpdateCheck;
+ }
+
+ static function __checkForUpdates():UpdateCheckCallback {
+ var curTag = 'v${Flags.VERSION}', error = false;
+ var newUpdates = __doReleaseFiltering(GitHub.getReleases(Flags.REPO_OWNER, Flags.REPO_NAME, (e) -> {
error = true;
}), curTag);
- if (error) return {
- success: false,
- newUpdate: false
- };
-
- if (newUpdates.length <= 0) {
- return {
- success: true,
- newUpdate: false
- };
+ var updateCheck:UpdateCheckCallback = {
+ success: !error,
+ newUpdate: !error && newUpdates.length > 0,
+ currentVersionTag: curTag,
+ date: Date.now()
}
- return {
- success: true,
- newUpdate: true,
- currentVersionTag: curTag,
- newVersionTag: newUpdates.last().tag_name,
- updates: newUpdates
- };
+ if (updateCheck.newUpdate) updateCheck.newVersionTag = (updateCheck.updates = newUpdates).last().tag_name;
+ return updateCheck;
+ }
+
+ static function __shouldCheck(lazy:Bool):Bool
+ return lastUpdateCheck == null || !lazy && (!lastUpdateCheck.newUpdate || Date.now().getTime() - lastUpdateCheck.date.getTime() > 1800000);
+
+ static function __callWaitCallbacks() {
+ for (callback in __waitCallbacks) callback(lastUpdateCheck);
+ __waitCallbacks.resize(0);
}
- static var __curVersionPos = -2;
static function __doReleaseFiltering(releases:Array, currentVersionTag:String) {
releases = releases.filterReleases(Options.betaUpdates, false);
if (releases.length <= 0)
return releases;
- var newArray:Array = [];
+ var newArray:Array = [], __curVersionPos = -2;
var skipNextBinaryChecks:Bool = false;
for(index in 0...releases.length) {
@@ -73,12 +110,9 @@ class UpdateUtil {
}
if (containsBinary) {
skipNextBinaryChecks = true; // no need to check for older versions
- if (release.tag_name == currentVersionTag) {
- __curVersionPos = -1;
- }
+ if (release.tag_name == currentVersionTag) __curVersionPos = -1;
newArray.insert(0, release);
- if (__curVersionPos > -2)
- __curVersionPos++;
+ if (__curVersionPos > -2) __curVersionPos++;
}
}
if (__curVersionPos < -1)
@@ -98,4 +132,6 @@ typedef UpdateCheckCallback = {
@:optional var newVersionTag:String;
@:optional var updates:Array;
+
+ @:optional var date:Date;
}
\ No newline at end of file
diff --git a/source/funkin/backend/utils/AudioAnalyzer.hx b/source/funkin/backend/utils/AudioAnalyzer.hx
index eccd5a4d..174f148c 100644
--- a/source/funkin/backend/utils/AudioAnalyzer.hx
+++ b/source/funkin/backend/utils/AudioAnalyzer.hx
@@ -474,7 +474,7 @@ final class AudioAnalyzer {
var n = Math.floor((endPos - startPos) * __toBits);
if (startPos >= time && startPos < backend.bufferTimes[backend.bufferSizes.length - 1] * 1000) {
var pos = Math.floor((startPos - time) * __toBits), buf = backend.bufferDatas[i].buffer, size = backend.bufferSizes[i], c = 0;
- while (pos > size) {
+ while (pos >= size) {
if (++i >= backend.bufferSizes.length) {
n = 0;
break;
@@ -484,6 +484,7 @@ final class AudioAnalyzer {
size = backend.bufferSizes[i];
}
pos -= pos % __sampleSize;
+ n -= pos % __sampleSize;
while (n > 0) {
callback(getByte(buf, pos, __wordSize), c);
diff --git a/source/funkin/editors/ModConfigWarning.hx b/source/funkin/editors/ModConfigWarning.hx
index 4caf1b48..f3a1ac13 100644
--- a/source/funkin/editors/ModConfigWarning.hx
+++ b/source/funkin/editors/ModConfigWarning.hx
@@ -56,9 +56,9 @@ LOGO_TEXT=""
super.createPost();
hadPopup = true;
- var substate = new UIWarningSubstate("Missing mod folder configuration!", "Your mod is currently missing a mod config file!\n\nWould you like to automatically generate one?\n\n(PS: If this is not your mod please disable Developer mode to stop this popup from appearing.)", [
+ var substate = new UIWarningSubstate(TU.translate("modConfigWarning.warningTitle"), TU.translate("modConfigWarning.warningDesc"), [
{
- label: "Not Now",
+ label: TU.translate("editor.notNow"),
color: 0x969533,
onClick: function (_) {
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
@@ -66,13 +66,13 @@ LOGO_TEXT=""
}
},
{
- label: "Yes",
+ label: TU.translate("editor.yes"),
onClick: function(_) {
var path = '${library.folderPath}/data/config/modpack.ini';
CoolUtil.safeSaveFile(path, defaultModConfigText);
- openSubState(new UIWarningSubstate("Created mod config!", "Your mod config file has been created at " + path + "!", [
+ openSubState(new UIWarningSubstate(TU.translate("modConfigWarning.createdTitle"), TU.translate("modConfigWarning.createdDesc", [path]), [
{
- label: "Ok",
+ label: TU.translate("editor.ok"),
onClick: function (_) {
MusicBeatState.skipTransOut = MusicBeatState.skipTransIn = false;
FlxG.switchState(cast Type.createInstance(goToState, []));
diff --git a/source/funkin/editors/alphabet/AlphabetEditor.hx b/source/funkin/editors/alphabet/AlphabetEditor.hx
index 630fada1..3015d7c8 100644
--- a/source/funkin/editors/alphabet/AlphabetEditor.hx
+++ b/source/funkin/editors/alphabet/AlphabetEditor.hx
@@ -209,6 +209,9 @@ class AlphabetEditor extends UIState {
scaleX: 1,
scaleY: 1,
+ flipX: false,
+ flipY: false,
+
hasColorMode: false,
colorMode: bigLetter.colorMode
};
diff --git a/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx b/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx
index 2c4b08fa..17581a09 100644
--- a/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx
+++ b/source/funkin/editors/alphabet/AlphabetMainDataScreen.hx
@@ -204,6 +204,9 @@ UPPERLETTER and LOWERLETTTER can be used in any backup prefix.";
cos: 1.0,
sin: 0.0,
+ flipX: false,
+ flipY: false,
+
hasColorMode: false,
colorMode: 0
}],
diff --git a/source/funkin/editors/alphabet/GlyphInfoWindow.hx b/source/funkin/editors/alphabet/GlyphInfoWindow.hx
index 766c0bca..02326321 100644
--- a/source/funkin/editors/alphabet/GlyphInfoWindow.hx
+++ b/source/funkin/editors/alphabet/GlyphInfoWindow.hx
@@ -11,6 +11,9 @@ class GlyphInfoWindow extends UIWindow {
public var angleBox:UINumericStepper;
public var colorModeDrop:UIDropDown;
+ public var flipXBox:UICheckbox;
+ public var flipYBox:UICheckbox;
+
public var outlineTitle:UIText;
public var outlineSep:UISprite;
public var outlineCheck:UICheckbox;
@@ -145,6 +148,9 @@ class GlyphInfoWindow extends UIWindow {
cos: compon.cos,
sin: compon.sin,
+ flipX: compon.flipX,
+ flipY: compon.flipY,
+
hasColorMode: compon.hasColorMode,
colorMode: compon.colorMode
};
@@ -159,6 +165,22 @@ class GlyphInfoWindow extends UIWindow {
}
members.push(outlineCheck);
+ flipXBox = new UICheckbox(outlineCheck.x, outlineCheck.y - outlineCheck.height - 8, "Flip X?");
+ flipXBox.onChecked = function(check:Bool) {
+ compon.flipX = check;
+ if (outlineCheck.checked)
+ data.components[compon.outIndex].flipX = check;
+ }
+ members.push(flipXBox);
+
+ flipYBox = new UICheckbox(flipXBox.x + flipXBox.width + itemMargin * 5, flipXBox.y, "Flip Y?");
+ flipYBox.onChecked = function(check:Bool) {
+ compon.flipY = check;
+ if (outlineCheck.checked)
+ data.components[compon.outIndex].flipY = check;
+ }
+ members.push(flipYBox);
+
outlineTitle = new UIText(colorModeDrop.x, colorModeDrop.y + colorModeDrop.bHeight + itemMargin, 0, "Outline Data");
members.push(outlineTitle);
outlineSep = new UISprite(x + itemMargin, colorModeDrop.y + colorModeDrop.bHeight + itemMargin + 19);
@@ -239,6 +261,8 @@ class GlyphInfoWindow extends UIWindow {
angleBox.value = com.angle;
@:privateAccess colorModeDrop.index = com.hasColorMode ? com.colorMode : AlphabetEditor.instance.tape.colorMode;
@:privateAccess colorModeDrop.label.text = colorModeDrop.options[colorModeDrop.index];
+ flipXBox.checked = com.flipX;
+ flipYBox.checked = com.flipY;
outlineCheck.checked = com.outIndex != null;
outlineBox.label.text = "";
diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx
index 2e88f2fe..9514ffc2 100644
--- a/source/funkin/editors/charter/Charter.hx
+++ b/source/funkin/editors/charter/Charter.hx
@@ -601,11 +601,8 @@ class Charter extends UIState {
Conductor.setupSong(PlayState.SONG);
noteTypes = PlayState.SONG.noteTypes;
- FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff)));
- if (PlayState.SONG.meta.needsVoices != false && Assets.exists(Paths.voices(__song, __diff))) // null or true
- vocals = FlxG.sound.load(Paths.voices(__song, __diff));
- else
- vocals = new FlxSound();
+ FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff, PlayState.SONG.meta.instSuffix)));
+ vocals = Assets.exists(Paths.voices(__song, __diff)) ? FlxG.sound.load(Paths.voices(__song, __diff)) : new FlxSound();
vocals.group = FlxG.sound.defaultMusicGroup;
gridBackdrops.createGrids(PlayState.SONG.strumLines.length);
@@ -683,7 +680,7 @@ class Charter extends UIState {
if (FlxG.sound.music.loaded)
wavesToGenerate.push({name: "Inst.ogg", sound: FlxG.sound.music});
- if (PlayState.SONG.meta.needsVoices != false && vocals.loaded)
+ if (vocals.loaded)
wavesToGenerate.push({name: "Voices.ogg", sound: vocals});
for (strumLine in strumLines)
diff --git a/source/funkin/editors/charter/CharterStrumlineScreen.hx b/source/funkin/editors/charter/CharterStrumlineScreen.hx
index ce7aaad9..a6beb4ad 100644
--- a/source/funkin/editors/charter/CharterStrumlineScreen.hx
+++ b/source/funkin/editors/charter/CharterStrumlineScreen.hx
@@ -171,7 +171,7 @@ class CharterStrumlineScreen extends UISubstateWindow {
}
}
- vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
+ vocalsSuffixDropDown = new UIDropDown(typeDropdown.x, hudScaleStepper.y + 128, 200, 32, suffixList, strumLine.vocalsSuffix != null && strumLine.vocalsSuffix != "" && Assets.exists(Paths.voices(Charter.__song.toLowerCase(), "", strumLine.vocalsSuffix)) ? suffixList.indexOf(strumLine.vocalsSuffix) : 0);
add(vocalsSuffixDropDown);
addLabelOn(vocalsSuffixDropDown, TU.translate("charterStrumLine.vocalSuffix"));
diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx
index eefdc61a..5528640d 100644
--- a/source/funkin/editors/charter/SongCreationScreen.hx
+++ b/source/funkin/editors/charter/SongCreationScreen.hx
@@ -7,6 +7,7 @@ import funkin.backend.chart.ChartData;
import funkin.backend.chart.FNFLegacyParser.SwagSong;
import funkin.backend.chart.PsychParser;
import funkin.backend.chart.VSliceParser;
+import funkin.backend.utils.ZipUtil;
import funkin.backend.utils.ZipUtil.ZipReader;
import funkin.game.HealthIcon;
import haxe.Json;
@@ -360,11 +361,11 @@ class SongCreationScreen extends UISubstateWindow {
{
try switch(engineDropdown.index)
{
- case 0 /*"V-Slice Project (.fnfc)"*/:
+ case 2 /*"V-Slice Project (.fnfc)"*/:
var files:Map = [];
for (field in new ZipReader(new BytesInput(importChartFile.file)).read()) {
var fileName = field.fileName;
- var fileContent = field.data;
+ var fileContent = ZipUtil.unzip(field);
files.set(fileName, fileContent);
}
saveFromVSlice(files);
@@ -405,8 +406,8 @@ class SongCreationScreen extends UISubstateWindow {
CoolUtil.safeSaveFile('$songFolder/charts/${songId}.json', Json.stringify(base, Flags.JSON_PRETTY_PRINT));
#end
});
- } catch (e) {
- openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", Std.string(e), [
+ } catch (e:haxe.Exception) {
+ openSubState(new UIWarningSubstate("Importing Song/Charts: Error!", e.details(), [
{label: "Ok", color: 0xFFFF0000, onClick: function(t) {}}
]));
}
diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx
index 6a12bd64..b61b1f9e 100644
--- a/source/funkin/game/PlayState.hx
+++ b/source/funkin/game/PlayState.hx
@@ -552,7 +552,7 @@ class PlayState extends MusicBeatState
@:noCompletion @:dox(hide) private var _endSongCalled:Bool = false;
@:dox(hide)
- var __vocalSyncTimer:Float = 0;
+ var __vocalSyncTimer:Float = 1;
private function get_accuracy():Float {
if (accuracyPressedNotes <= 0) return -1;
@@ -1113,12 +1113,10 @@ class PlayState extends MusicBeatState
curSong = songData.meta.name.toLowerCase();
curSongID = curSong.replace(" ", "-");
- FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty))));
- if (SONG.meta.needsVoices != false && Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true
- vocals = FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(Paths.voices(SONG.meta.name, difficulty)) : Paths.voices(SONG.meta.name, difficulty));
- else
- vocals = new FlxSound();
+ FlxG.sound.setMusic(inst = FlxG.sound.load(Assets.getMusic(Paths.inst(SONG.meta.name, difficulty, SONG.meta.instSuffix))));
+ var vocalsPath = Paths.voices(SONG.meta.name, difficulty);
+ vocals = Assets.exists(vocalsPath) ? FlxG.sound.load(Options.streamedVocals ? Assets.getMusic(vocalsPath) : vocalsPath) : new FlxSound();
vocals.group = FlxG.sound.defaultMusicGroup;
vocals.persist = false;
@@ -1224,7 +1222,7 @@ class PlayState extends MusicBeatState
var time = Conductor.songPosition + Conductor.songOffset;
for (strumLine in strumLines.members) strumLine.vocals.play(true, time);
vocals.play(true, time);
- inst.play(true, time);
+ if (!inst.playing) inst.play(true, time);
gameAndCharsCall("onVocalsResync");
}
@@ -1375,20 +1373,19 @@ class PlayState extends MusicBeatState
updateIconPositions();
if (startingSong) {
- if (startedCountdown) {
- Conductor.songPosition += Conductor.songOffset + elapsed * 1000;
- if (Conductor.songPosition >= 0)
- startSong();
+ if (startedCountdown && (Conductor.songPosition += Conductor.songOffset + elapsed * 1000) >= 0) {
+ Conductor.songPosition = Conductor.songOffset;
+ startSong();
}
}
else if (FlxG.sound.music != null && (__vocalSyncTimer -= elapsed) < 0) {
__vocalSyncTimer = 1;
var instTime = FlxG.sound.music.getActualTime();
- var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 30;
+ var isOffsync:Bool = vocals.loaded && Math.abs(instTime - vocals.getActualTime()) > 100;
if (!isOffsync) {
for (strumLine in strumLines.members) {
- if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 30)) break;
+ if ((isOffsync = strumLine.vocals.loaded && Math.abs(instTime - strumLine.vocals.getActualTime()) > 100)) break;
}
}
@@ -1689,17 +1686,12 @@ class PlayState extends MusicBeatState
endingSong = true;
gameAndCharsCall("onSongEnd");
canPause = false;
- inst.volume = 0;
- vocals.volume = 0;
- for (strumLine in strumLines.members) {
- strumLine.vocals.volume = 0;
- strumLine.vocals.pause();
- }
- inst.pause();
- vocals.pause();
- if (validScore)
- {
+ for (strumLine in strumLines.members) strumLine.vocals.stop();
+ inst.stop();
+ vocals.stop();
+
+ if (validScore) {
#if !switch
FunkinSave.setSongHighscore(SONG.meta.name, difficulty, {
score: songScore,
@@ -1727,20 +1719,18 @@ class PlayState extends MusicBeatState
* Immediately switches to the next song, or goes back to the Story/Freeplay menu.
*/
public function nextSong() {
- if (isStoryMode)
- {
+ if (isStoryMode) {
campaignScore += songScore;
campaignMisses += misses;
campaignAccuracyTotal += accuracy;
campaignAccuracyCount++;
storyPlaylist.shift();
- if (storyPlaylist.length <= 0)
- {
+ if (storyPlaylist.length <= 0) {
FlxG.switchState(new StoryMenuState());
- if (validScore)
- {
+ if (validScore) {
+ #if !switch
// TODO: more week info saving
FunkinSave.setWeekHighscore(storyWeek.id, difficulty, {
score: campaignScore,
@@ -1749,29 +1739,23 @@ class PlayState extends MusicBeatState
hits: [],
date: Date.now().toString()
});
+ #end
}
FlxG.save.flush();
}
- else
- {
+ else {
Logs.infos('Loading next song (${storyPlaylist[0].toLowerCase()}/$difficulty)', "PlayState");
registerSmoothTransition();
- FlxG.sound.music.stop();
-
__loadSong(storyPlaylist[0], difficulty);
-
FlxG.switchState(new PlayState());
}
}
+ else if (chartingMode)
+ FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
else
- {
- if (chartingMode)
- FlxG.switchState(new funkin.editors.charter.Charter(SONG.meta.name, difficulty, false));
- else
- FlxG.switchState(new FreeplayState());
- }
+ FlxG.switchState(new FreeplayState());
}
public function registerSmoothTransition() {
@@ -1943,6 +1927,8 @@ class PlayState extends MusicBeatState
}
if (event.deleteNote) strumLine.deleteNote(note);
+
+ gameAndCharsEvent("onPostNoteHit", event);
}
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
diff --git a/source/funkin/menus/FreeplayState.hx b/source/funkin/menus/FreeplayState.hx
index 42952043..e02c34aa 100644
--- a/source/funkin/menus/FreeplayState.hx
+++ b/source/funkin/menus/FreeplayState.hx
@@ -19,6 +19,16 @@ class FreeplayState extends MusicBeatState
*/
public var songs:Array = [];
+ /**
+ * Current song metadata
+ */
+ public var curSong:Null;
+
+ /**
+ * Current song metadata difficulty
+ */
+ public var curSongDifficulty:Null;
+
/**
* Currently selected song
*/
@@ -43,7 +53,7 @@ class FreeplayState extends MusicBeatState
public var diffText:FlxText;
/**
- * Text containing the current coop/opponent mode ([TAB] Co-Op mode)
+ * Text containing the current coop/opponent mode ([KEYBINDS] Co-Op mode)
*/
public var coopText:FlxText;
@@ -107,7 +117,9 @@ class FreeplayState extends MusicBeatState
curSelected = k;
}
}
- if (songs[curSelected] != null) {
+ updateCurSongMeta();
+
+ if (curSong != null) {
for(k=>diff in songs[curSelected].difficulties) {
if (diff == Options.freeplayLastDifficulty) {
curDifficulty = k;
@@ -225,7 +237,7 @@ 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((FlxG.keys.justPressed.TAB ? 1 : 0)); // TODO: make this configurable
+ changeCoopMode((controls.CHANGE_MODE ? 1 : 0)); // TODO: make this configurable
// putting it before so that its actually smooth
updateOptionsAlpha();
}
@@ -238,14 +250,14 @@ class FreeplayState extends MusicBeatState
scoreText.x = coopText.x = scoreBG.x + 4;
diffText.x = Std.int(scoreBG.x + ((scoreBG.width - diffText.width) / 2));
- interpColor.fpsLerpTo(songs[curSelected].color, 0.0625);
+ interpColor.fpsLerpTo(curSong.color, 0.0625);
bg.color = interpColor.color;
#if PRELOAD_ALL
var dontPlaySongThisFrame = false;
autoplayElapsed += elapsed;
- if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay || FlxG.keys.justPressed.SPACE)) {
- if (curPlayingInst != (curPlayingInst = Paths.inst(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]))) {
+ if (!disableAutoPlay && !songInstPlaying && (autoplayElapsed > timeUntilAutoplay)) {
+ if (curPlayingInst != (curPlayingInst = Paths.inst(curSong.name, curSongDifficulty, curSong.instSuffix))) {
var streamed = false;
if (Options.streamedMusic) {
var sound = Assets.getMusic(curPlayingInst, true, false);
@@ -253,7 +265,7 @@ class FreeplayState extends MusicBeatState
if (streamed && autoplayShouldPlay) {
FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
+ Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
}
}
@@ -268,7 +280,7 @@ class FreeplayState extends MusicBeatState
if (sound != null && autoplayShouldPlay) {
FlxG.sound.playMusic(sound, 0);
- Conductor.changeBPM(songs[curSelected].bpm, songs[curSelected].beatsPerMeasure, songs[curSelected].stepsPerBeat);
+ Conductor.changeBPM(curSong.bpm, curSong.beatsPerMeasure, curSong.stepsPerBeat);
}
}
if (!disableAsyncLoading) Main.execAsync(huh);
@@ -302,7 +314,6 @@ class FreeplayState extends MusicBeatState
function updateCoopModes() {
__opponentMode = false;
__coopMode = false;
- var curSong = songs[curSelected];
if (curSong.coopAllowed && curSong.opponentModeAllowed) {
__opponentMode = curCoopMode % 2 == 1;
__coopMode = curCoopMode >= 2;
@@ -321,7 +332,7 @@ class FreeplayState extends MusicBeatState
if (songs[curSelected].difficulties.length <= 0) return;
- var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], __opponentMode, __coopMode));
+ var event = event("onSelect", EventManager.get(FreeplaySongSelectEvent).recycle(curSong.name, curSongDifficulty, __opponentMode, __coopMode));
if (event.cancelled) return;
@@ -329,17 +340,17 @@ class FreeplayState extends MusicBeatState
autoplayShouldPlay = false;
#end
- Options.freeplayLastSong = songs[curSelected].name;
- Options.freeplayLastDifficulty = songs[curSelected].difficulties[curDifficulty];
+ Options.freeplayLastSong = curSong.name;
+ Options.freeplayLastDifficulty = curSongDifficulty;
PlayState.loadSong(event.song, event.difficulty, event.opponentMode, event.coopMode);
FlxG.switchState(new PlayState());
}
public function convertChart() {
- trace('Converting ${songs[curSelected].name} (${songs[curSelected].difficulties[curDifficulty]}) to Codename format...');
- var chart = Chart.parse(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty]);
- Chart.save('${Main.pathBack}assets/songs/${songs[curSelected].name}', chart, songs[curSelected].difficulties[curDifficulty]);
+ trace('Converting ${curSong.name} (${curSongDifficulty}) to Codename format...');
+ var chart = Chart.parse(curSong.name, curSongDifficulty);
+ Chart.save('${Main.pathBack}assets/songs/${curSong.name}', chart, curSongDifficulty);
}
/**
@@ -351,20 +362,31 @@ class FreeplayState extends MusicBeatState
{
if (change == 0 && !force) return;
- var curSong = songs[curSelected];
- var validDifficulties = curSong.difficulties.length > 0;
- var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, curSong.difficulties.length-1) : 0, change));
+ var song = songs[curSelected];
+ var validDifficulties = song.difficulties.length > 0;
+ var event = event("onChangeDiff", EventManager.get(MenuChangeEvent).recycle(curDifficulty, validDifficulties ? FlxMath.wrap(curDifficulty + change, 0, song.difficulties.length-1) : 0, change));
- if (event.cancelled) return;
+ if (event.cancelled) {
+ if (force) updateCurSongMeta();
+ return;
+ }
+ var prevInstSuffix = curSong.instSuffix;
curDifficulty = event.value;
-
+ updateCurSongMeta();
updateScore();
- if (curSong.difficulties.length > 1)
- diffText.text = '< ${curSong.difficulties[curDifficulty].toUpperCase()} >';
+ #if PRELOAD_ALL
+ if (curSong.instSuffix != prevInstSuffix) {
+ autoplayElapsed = 0;
+ songInstPlaying = false;
+ }
+ #end
+
+ if (song.difficulties.length > 1)
+ diffText.text = '< ${song.difficulties[curDifficulty].toUpperCase()} >';
else
- diffText.text = validDifficulties ? curSong.difficulties[curDifficulty].toUpperCase() : "-";
+ diffText.text = validDifficulties ? song.difficulties[curDifficulty].toUpperCase() : "-";
}
function updateScore() {
@@ -376,7 +398,7 @@ class FreeplayState extends MusicBeatState
var changes:Array = [];
if (__coopMode) changes.push(CCoopMode);
if (__opponentMode) changes.push(COpponentMode);
- var saveData = FunkinSave.getSongHighscore(songs[curSelected].name, songs[curSelected].difficulties[curDifficulty], changes);
+ var saveData = FunkinSave.getSongHighscore(curSong.name, curSongDifficulty, changes);
intendedScore = saveData.score;
}
@@ -397,9 +419,9 @@ class FreeplayState extends MusicBeatState
*/
public function changeCoopMode(change:Int = 0, force:Bool = false) {
if (change == 0 && !force) return;
- if (!songs[curSelected].coopAllowed && !songs[curSelected].opponentModeAllowed) return;
+ if (!curSong.coopAllowed && !curSong.opponentModeAllowed) return;
- var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
+ 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;
@@ -408,12 +430,16 @@ class FreeplayState extends MusicBeatState
updateScore();
- var key = "[TAB] "; // TODO: make this configurable
+ 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 * (songs[curSelected].coopAllowed ? 2 : 1)];
+ coopText.text = key + coopLabels[curCoopMode * (curSong.coopAllowed ? 2 : 1)];
}
}
@@ -426,7 +452,6 @@ class FreeplayState extends MusicBeatState
{
if (change == 0 && !force) return;
- var bothEnabled = songs[curSelected].coopAllowed && songs[curSelected].opponentModeAllowed;
var event = event("onChangeSelection", EventManager.get(MenuChangeEvent).recycle(curSelected, FlxMath.wrap(curSelected + change, 0, songs.length-1), change));
if (event.cancelled) return;
@@ -440,7 +465,7 @@ class FreeplayState extends MusicBeatState
songInstPlaying = false;
#end
- coopText.visible = songs[curSelected].coopAllowed || songs[curSelected].opponentModeAllowed;
+ coopText.visible = curSong.coopAllowed || curSong.opponentModeAllowed;
}
function updateOptionsAlpha() {
@@ -465,6 +490,16 @@ class FreeplayState extends MusicBeatState
item.alpha = selectedAlpha;
}
}
+
+ function updateCurSongMeta() {
+ var song = songs[curSelected];
+ if (song == null) {
+ curSong = null;
+ curSongDifficulty = null;
+ }
+ else if ((curSong = song.metas.get(curSongDifficulty = song.difficulties[curDifficulty])) == null)
+ curSong = song;
+ }
}
class FreeplaySonglist {
@@ -485,7 +520,7 @@ class FreeplaySonglist {
}
if (songsFound == null) songsFound = Paths.getFolderDirectories("songs", false, source);
if (songsFound.length > 0) {
- for (s in songsFound) songs.push(Chart.loadChartMeta(s, Flags.DEFAULT_DIFFICULTY, source == MODS));
+ for (s in songsFound) songs.push(Chart.loadChartMeta(s, source == MODS));
return false;
}
return true;
diff --git a/source/funkin/menus/TitleState.hx b/source/funkin/menus/TitleState.hx
index f3ea3d78..e54d12f8 100644
--- a/source/funkin/menus/TitleState.hx
+++ b/source/funkin/menus/TitleState.hx
@@ -129,7 +129,7 @@ class TitleState extends MusicBeatState
if (pressedEnter && transitioning && skippedIntro) {
FlxG.camera.stopFX();// FlxG.camera.visible = false;
- goToMainMenu();
+ goToMainMenu(false);
}
if (pressedEnter && !transitioning && skippedIntro)
@@ -152,25 +152,24 @@ class TitleState extends MusicBeatState
transitioning = true;
// FlxG.sound.music.stop();
- new FlxTimer().start(2, (_) -> goToMainMenu());
+ new FlxTimer().start(2, (_) -> goToMainMenu(false));
}
- function goToMainMenu() {
+ function goToMainMenu(force = true) {
#if UPDATE_CHECKING
- if (!Flags.DISABLE_AUTOUPDATER) {
- var report = hasCheckedUpdates ? null : funkin.backend.system.updating.UpdateUtil.checkForUpdates();
- hasCheckedUpdates = true;
+ if (!force && !Flags.DISABLE_AUTOUPDATER) {
+ funkin.backend.system.updating.UpdateUtil.waitForUpdates(false, (report) -> {
+ hasCheckedUpdates = true;
+ if (FlxG.state != this) return;
- if (report != null && report.newUpdate) {
- FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
- } else {
- FlxG.switchState(new MainMenuState());
- }
+ if (!report.newUpdate) goToMainMenu(true);
+ else FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
+ }, true);
+ }
+ else
+ #end {
+ FlxG.switchState(new MainMenuState());
}
- else FlxG.switchState(new MainMenuState());
- #else
- FlxG.switchState(new MainMenuState());
- #end
}
public function createCoolText(textArray:Array)
diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx
index fcc447a9..fd920642 100644
--- a/source/funkin/menus/ui/Alphabet.hx
+++ b/source/funkin/menus/ui/Alphabet.hx
@@ -37,6 +37,9 @@ final class AlphabetComponent {
public var scaleX:Float;
public var scaleY:Float;
+ public var flipX:Bool;
+ public var flipY:Bool;
+
public var hasColorMode:Bool;
public var colorMode:ColorMode;
}
@@ -346,9 +349,9 @@ class Alphabet extends FlxSprite {
_matrix.translate(_frame.frame.width * -0.5, _frame.frame.height * -0.5);
if (__useDrawScale)
- _matrix.scale(__drawScale.x, __drawScale.y);
+ _matrix.scale(__drawScale.x * (__component.flipX ? -1 : 1), __drawScale.y * (__component.flipY ? -1 : 1));
else
- _matrix.scale(__component.scaleX, __component.scaleY);
+ _matrix.scale(__component.scaleX * (__component.flipX ? -1 : 1), __component.scaleY * (__component.flipY ? -1 : 1));
if(__component.shouldRotate)
_matrix.rotateWithTrig(__component.cos, __component.sin);
_matrix.translate(_frame.frame.width * 0.5, _frame.frame.height * 0.5);
@@ -558,6 +561,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
+ flipX: node.get("flipX") == "true",
+ flipY: node.get("flipY") == "true",
+
hasColorMode: false,
colorMode: 0
}],
@@ -593,6 +599,9 @@ class Alphabet extends FlxSprite {
var colorMode = ["tint", "offsets", "none"].indexOf(component.get("colorMode"));
+ var xFlip = component.get("flipX") == "true";
+ var yFlip = component.get("flipY") == "true";
+
if (component.get("hasOutline") == "true") {
components.insert(startIndex, {
refIndex: componentsPushed,
@@ -608,6 +617,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
+ flipX: xFlip,
+ flipY: yFlip,
+
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -628,6 +640,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
+ flipX: xFlip,
+ flipY: yFlip,
+
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -659,6 +674,9 @@ class Alphabet extends FlxSprite {
var yScale:Float = Std.parseFloat(node.get("scaleY")).getDefaultFloat(1.0);
var advance:Float = Std.parseFloat(node.get("advance"));
+ var xFlip = node.get("flipX") == "true";
+ var yFlip = node.get("flipY") == "true";
+
var colorMode = ["tint", "offsets", "none"].indexOf(node.get("colorMode"));
var components:Array = [];
@@ -677,6 +695,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
+ flipX: xFlip,
+ flipY: yFlip,
+
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -696,6 +717,9 @@ class Alphabet extends FlxSprite {
cos: angleCos,
sin: angleSin,
+ flipX: xFlip,
+ flipY: yFlip,
+
hasColorMode: colorMode >= 0,
colorMode: colorMode,
});
@@ -758,7 +782,7 @@ class Alphabet extends FlxSprite {
}
private static var alphabetProperties:Array = ["fps", "advance", "lineGap", "forceCasing", "useColorOffsets", "antialiasing"];
- private static var componentProperties:Array = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY"];
+ private static var componentProperties:Array = ["name", "anim", "x", "y", "scaleX", "scaleY", "angle", "offsetX", "offsetY", "flipX", "flipY"];
public function buildXML():Xml {
var xml = Xml.createElement("alphabet");
diff --git a/source/funkin/options/Options.hx b/source/funkin/options/Options.hx
index 20432b55..c6c61a41 100644
--- a/source/funkin/options/Options.hx
+++ b/source/funkin/options/Options.hx
@@ -117,6 +117,7 @@ class Options
public static var P1_ACCEPT:Array = [ENTER];
public static var P1_BACK:Array = [BACKSPACE];
public static var P1_PAUSE:Array = [ENTER];
+ public static var P1_CHANGE_MODE:Array = [TAB];
// Misc
public static var P1_RESET:Array = [R];
@@ -148,6 +149,7 @@ class Options
public static var P2_ACCEPT:Array = [SPACE];
public static var P2_BACK:Array = [ESCAPE];
public static var P2_PAUSE:Array = [ESCAPE];
+ public static var P2_CHANGE_MODE:Array = [];
// Misc
public static var P2_RESET:Array = [];
@@ -179,6 +181,7 @@ class Options
public static var SOLO_ACCEPT(get, null):Array;
public static var SOLO_BACK(get, null):Array;
public static var SOLO_PAUSE(get, null):Array;
+ public static var SOLO_CHANGE_MODE(get, null):Array;
// Misc
public static var SOLO_RESET(get, null):Array;
diff --git a/source/funkin/options/categories/MiscOptions.hx b/source/funkin/options/categories/MiscOptions.hx
index 659f59ea..38ea1905 100644
--- a/source/funkin/options/categories/MiscOptions.hx
+++ b/source/funkin/options/categories/MiscOptions.hx
@@ -11,7 +11,7 @@ class MiscOptions extends TreeMenuScreen {
#if UPDATE_CHECKING
add(new Checkbox(getNameID('betaUpdates'), getDescID('betaUpdates'), 'betaUpdates'));
add(new TextOption(getNameID('checkForUpdates'), getDescID('checkForUpdates'), () -> {
- var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates();
+ var report = funkin.backend.system.updating.UpdateUtil.checkForUpdates(true);
if (report.newUpdate) FlxG.switchState(new funkin.backend.system.updating.UpdateAvailableScreen(report));
else {
CoolUtil.playMenuSFX(CANCEL);
diff --git a/source/funkin/options/keybinds/KeybindsOptions.hx b/source/funkin/options/keybinds/KeybindsOptions.hx
index fc68c838..0ae12058 100644
--- a/source/funkin/options/keybinds/KeybindsOptions.hx
+++ b/source/funkin/options/keybinds/KeybindsOptions.hx
@@ -97,17 +97,21 @@ class KeybindsOptions extends MusicBeatSubstate {
name: translate("ui.pause"),
control: 'PAUSE'
},
+ {
+ name: translate("ui.changeMode"),
+ control: 'CHANGE_MODE'
+ },
]
},
{
name: translate("category.volume"),
settings: [
{
- name: translate("up"),
+ name: translate("volume.up"),
control: 'VOLUME_UP'
},
{
- name: translate("down"),
+ name: translate("volume.down"),
control: 'VOLUME_DOWN'
},
{
diff --git a/source/lime/_internal/backend/native/NativeAudioSource.hx b/source/lime/_internal/backend/native/NativeAudioSource.hx
index 33e886b0..894f6274 100644
--- a/source/lime/_internal/backend/native/NativeAudioSource.hx
+++ b/source/lime/_internal/backend/native/NativeAudioSource.hx
@@ -25,10 +25,10 @@ import lime.utils.ArrayBufferView;
@:access(lime.media.AudioBuffer)
@:access(lime.utils.ArrayBufferView)
class NativeAudioSource {
- private static var STREAM_BUFFER_SAMPLES:Int = 0x1000;
- private static var STREAM_MAX_BUFFERS:Int = 32;
+ private static var STREAM_BUFFER_SAMPLES:Int = 0x4000;
+ private static var STREAM_MAX_BUFFERS:Int = 8;
private static var STREAM_TIMER_FREQUENCY:Int = 100;
- private static var STREAM_BUFFER_FREQUENCY:Int = 6;
+ private static var STREAM_BUFFER_FREQUENCY:Int = 3;
private var parent:AudioSource;
private var disposed:Bool;