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;