diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 9ae13a76..449f82c6 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -7,7 +7,7 @@ jobs: build: name: Mac OS Build permissions: write-all - runs-on: macos-latest + runs-on: macos-13 steps: - name: Pulling the new commit uses: actions/checkout@v2 diff --git a/assets/data/characters/bf.xml b/assets/data/characters/bf.xml index 04e2b3c5..245fbffd 100644 --- a/assets/data/characters/bf.xml +++ b/assets/data/characters/bf.xml @@ -11,4 +11,8 @@ - \ No newline at end of file + + + + + diff --git a/assets/data/notes/Alt Anim Note.hx b/assets/data/notes/Alt Anim Note.hx index 08acea42..b44b1398 100644 --- a/assets/data/notes/Alt Anim Note.hx +++ b/assets/data/notes/Alt Anim Note.hx @@ -1 +1,2 @@ -function onNoteHit(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt"; \ No newline at end of file +function onNoteHit(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt"; +function onPlayerMiss(event) if (event.noteType == "Alt Anim Note") event.animSuffix = "-alt"; \ No newline at end of file diff --git a/assets/data/notes/No Anim Note.hx b/assets/data/notes/No Anim Note.hx index 31eb0c63..206d4f96 100644 --- a/assets/data/notes/No Anim Note.hx +++ b/assets/data/notes/No Anim Note.hx @@ -1 +1,2 @@ -function onNoteHit(event) if (event.noteType == "No Anim Note") event.animCancelled = true; \ No newline at end of file +function onNoteHit(event) if (event.noteType == "No Anim Note") event.animCancelled = true; +function onPlayerMiss(event) if (event.noteType == "No Anim Note") event.animCancelled = true; \ No newline at end of file diff --git a/assets/data/stages/mall.xml b/assets/data/stages/mall.xml index 3552cf27..3565f1be 100644 --- a/assets/data/stages/mall.xml +++ b/assets/data/stages/mall.xml @@ -1,21 +1,21 @@ - + - + - + - - + + + + + + + - - - - - - \ No newline at end of file + diff --git a/source/funkin/backend/assets/ModsFolderLibrary.hx b/source/funkin/backend/assets/ModsFolderLibrary.hx index eaf4b679..884738c5 100644 --- a/source/funkin/backend/assets/ModsFolderLibrary.hx +++ b/source/funkin/backend/assets/ModsFolderLibrary.hx @@ -89,7 +89,7 @@ class ModsFolderLibrary extends AssetLibrary implements IModsAssetLibrary { return __getFiles(folder, false); public function __getFiles(folder:String, folders:Bool = false) { - if (!folder.endsWith("/")) folder = folder + "/"; + if (!folder.endsWith("/")) folder += "/"; if (!__parseAsset(folder)) return []; var path = getAssetPath(); try { diff --git a/source/funkin/backend/assets/Paths.hx b/source/funkin/backend/assets/Paths.hx index dbc65f8c..dd81ea5b 100644 --- a/source/funkin/backend/assets/Paths.hx +++ b/source/funkin/backend/assets/Paths.hx @@ -173,7 +173,7 @@ class Paths public static function getFrames(key:String, assetsPath:Bool = false, ?library:String) { if (tempFramesCache.exists(key)) { var frames = tempFramesCache[key]; - if (frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable) + if (frames != null && frames.parent != null && frames.parent.bitmap != null && frames.parent.bitmap.readable) return frames; else tempFramesCache.remove(key); @@ -271,4 +271,4 @@ class ScriptPathInfo { this.file = file; this.library = library; } -} \ No newline at end of file +} diff --git a/source/funkin/backend/assets/ZipFolderLibrary.hx b/source/funkin/backend/assets/ZipFolderLibrary.hx index 60627c82..ea4dab25 100644 --- a/source/funkin/backend/assets/ZipFolderLibrary.hx +++ b/source/funkin/backend/assets/ZipFolderLibrary.hx @@ -20,6 +20,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary { public var zip:SysZip; public var assets:Map = []; + public var lowerCaseAssets:Map = []; + public var nameMap:Map = []; public function new(basePath:String, libName:String, ?modName:String) { this.libName = libName; @@ -33,8 +35,10 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary { zip = SysZip.openFromFile(basePath); zip.read(); - for(entry in zip.entries) - assets[entry.fileName.toLowerCase()] = entry; + for(entry in zip.entries) { + lowerCaseAssets[entry.fileName.toLowerCase()] = assets[entry.fileName.toLowerCase()] = assets[entry.fileName] = entry; + nameMap.set(entry.fileName.toLowerCase(), entry.fileName); + } super(); } @@ -86,6 +90,8 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary { } _parsedAsset = _parsedAsset.toLowerCase(); + if(nameMap.exists(_parsedAsset)) + _parsedAsset = nameMap.get(_parsedAsset); return true; } @@ -105,35 +111,45 @@ class ZipFolderLibrary extends AssetLibrary implements IModsAssetLibrary { return '[ZIP]$basePath/$_parsedAsset'; } + // TODO: rewrite this to 1 function, like ModsFolderLibrary public function getFiles(folder:String):Array { - var content:Array = []; - - if (!folder.endsWith("/")) folder = folder + "/"; + if (!folder.endsWith("/")) folder += "/"; if (!__parseAsset(folder)) return []; + var content:Array = []; + + var checkPath = _parsedAsset.toLowerCase(); + @:privateAccess - for(k=>e in assets) { - if (k.toLowerCase().startsWith(_parsedAsset)) { + for(k=>e in lowerCaseAssets) { + if (k.toLowerCase().startsWith(checkPath)) { + if(nameMap.exists(k)) + k = nameMap.get(k); var fileName = k.substr(_parsedAsset.length); - if (!fileName.contains("/")) - content.push(fileName); + if (!fileName.contains("/") && fileName.length > 0) + content.pushOnce(fileName); } } return content; } public function getFolders(folder:String):Array { - var content:Array = []; - - if (!folder.endsWith("/")) folder = folder + "/"; + if (!folder.endsWith("/")) folder += "/"; if (!__parseAsset(folder)) return []; + var content:Array = []; + + var checkPath = _parsedAsset.toLowerCase(); + @:privateAccess - for(k=>e in assets) { - if (k.toLowerCase().startsWith(_parsedAsset)) { + for(k=>e in lowerCaseAssets) { + if (k.toLowerCase().startsWith(checkPath)) { + if(nameMap.exists(k)) + k = nameMap.get(k); var fileName = k.substr(_parsedAsset.length); - if (fileName.contains("/")) { - var s = fileName.split("/")[0]; + var index = fileName.indexOf("/"); + if (index != -1 && fileName.length > 0) { + var s = fileName.substr(0, index); content.pushOnce(s); } } diff --git a/source/funkin/backend/chart/Chart.hx b/source/funkin/backend/chart/Chart.hx index 93279d43..1669879d 100644 --- a/source/funkin/backend/chart/Chart.hx +++ b/source/funkin/backend/chart/Chart.hx @@ -56,7 +56,6 @@ class Chart { data.setFieldDefault("name", songName); data.setFieldDefault("beatsPerMeasure", 4); data.setFieldDefault("stepsPerBeat", 4); - data.setFieldDefault("needsVoices", true); data.setFieldDefault("icon", "face"); data.setFieldDefault("difficulties", []); data.setFieldDefault("coopAllowed", false); diff --git a/source/funkin/backend/chart/ChartData.hx b/source/funkin/backend/chart/ChartData.hx index c46164f5..7e6ad795 100644 --- a/source/funkin/backend/chart/ChartData.hx +++ b/source/funkin/backend/chart/ChartData.hx @@ -20,7 +20,6 @@ typedef ChartMetaData = { public var ?displayName:String; public var ?beatsPerMeasure:Float; public var ?stepsPerBeat:Float; - public var ?needsVoices:Bool; public var ?icon:String; public var ?color:Dynamic; public var ?difficulties:Array; diff --git a/source/funkin/backend/chart/EventsData.hx b/source/funkin/backend/chart/EventsData.hx index e801f18f..05f35bde 100644 --- a/source/funkin/backend/chart/EventsData.hx +++ b/source/funkin/backend/chart/EventsData.hx @@ -48,7 +48,7 @@ class EventsData { } ], "Alt Animation Toggle" => [{name: "Enable On Sing Poses", type: TBool, defValue: true}, {name: "Enable On Idle", type: TBool, defValue: true}, {name: "Strumline", type: TStrumLine, defValue: 0}], - "Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}], + "Play Animation" => [{name: "Character", type: TStrumLine, defValue: 0}, {name: "Animation", type: TString, defValue: "animation"}, {name: "Is forced?", type: TBool, defValue: true}], ]; public static var eventsList:Array = defaultEventsList.copy(); diff --git a/source/funkin/backend/chart/FNFLegacyParser.hx b/source/funkin/backend/chart/FNFLegacyParser.hx index 447aaba9..c6a5be3e 100644 --- a/source/funkin/backend/chart/FNFLegacyParser.hx +++ b/source/funkin/backend/chart/FNFLegacyParser.hx @@ -41,7 +41,6 @@ class FNFLegacyParser { } result.meta.bpm = data.bpm; - result.meta.needsVoices = data.needsVoices.getDefault(true); var camFocusedBF:Bool = false; var altAnims:Bool = false; @@ -132,7 +131,7 @@ class FNFLegacyParser { if ((swagSection.mustHitSection && strumLine.type == OPPONENT) || (!swagSection.mustHitSection && strumLine.type == PLAYER)) sectionNote[1] += 4; - swagSection.sectionNotes.push(note); + swagSection.sectionNotes.push(sectionNote); } } @@ -145,7 +144,6 @@ class FNFLegacyParser { song: chart.meta.name, notes: null, bpm: chart.meta.bpm, - needsVoices: chart.meta.needsVoices, speed: chart.scrollSpeed, player1: null, @@ -206,7 +204,6 @@ typedef SwagSong = var song:String; var notes:Array; var bpm:Float; - var needsVoices:Bool; var speed:Float; var ?stage:String; var ?noteTypes:Array; diff --git a/source/funkin/backend/scripting/HScript.hx b/source/funkin/backend/scripting/HScript.hx index 4305519e..f2638f82 100644 --- a/source/funkin/backend/scripting/HScript.hx +++ b/source/funkin/backend/scripting/HScript.hx @@ -64,7 +64,13 @@ class HScript extends Script { } private function importFailedCallback(cl:Array):Bool { - var assetsPath = 'assets/source/${cl.join("/")}'; + if(_importFailedCallback(cl, "") || _importFailedCallback(cl, "source/")) { + return true; + } + return false; + } + private function _importFailedCallback(cl:Array, prefix:String):Bool { + var assetsPath = 'assets/$prefix${cl.join("/")}'; for(hxExt in ["hx", "hscript", "hsc", "hxs"]) { var p = '$assetsPath.$hxExt'; if (__importedPaths.contains(p)) diff --git a/source/funkin/backend/system/Controls.hx b/source/funkin/backend/system/Controls.hx index d1a11142..651b510f 100644 --- a/source/funkin/backend/system/Controls.hx +++ b/source/funkin/backend/system/Controls.hx @@ -428,13 +428,18 @@ class Controls extends FlxActionSet return byName[name].check(); } + public function getKeyName(control:Control):String + { + return getDialogueName(getActionFromControl(control)); + } + public function getDialogueName(action:FlxActionDigital):String { var input = action.inputs[0]; return switch input.device { - case KEYBOARD: return '[${(input.inputID : FlxKey)}]'; - case GAMEPAD: return '(${(input.inputID : FlxGamepadInputID)})'; + case KEYBOARD: return '${(input.inputID : FlxKey)}'; + case GAMEPAD: return '${(input.inputID : FlxGamepadInputID)}'; case device: throw 'unhandled device: $device'; } } diff --git a/source/funkin/backend/system/MainState.hx b/source/funkin/backend/system/MainState.hx index 9e4996c7..ff3e9f56 100644 --- a/source/funkin/backend/system/MainState.hx +++ b/source/funkin/backend/system/MainState.hx @@ -68,11 +68,6 @@ class MainState extends FlxState { betaWarningShown = true; } - #if sys - sys.FileSystem.createDirectory('./.temp/'); - #if windows - try new funkin.backend.utils.native.HiddenProcess("attrib +h .temp") - catch(e) Logs.warn('Failed to set hidden attribute on .temp folder'); - #end #end + CoolUtil.safeSetAttribute('./.temp/', HIDDEN); } } \ No newline at end of file diff --git a/source/funkin/backend/utils/CoolUtil.hx b/source/funkin/backend/utils/CoolUtil.hx index 97ecacc5..b2901b69 100644 --- a/source/funkin/backend/utils/CoolUtil.hx +++ b/source/funkin/backend/utils/CoolUtil.hx @@ -1,5 +1,8 @@ package funkin.backend.utils; +#if sys +import sys.FileSystem; +#end import flixel.animation.FlxAnimation; import flixel.input.keyboard.FlxKey; import flixel.sound.FlxSound; @@ -62,18 +65,15 @@ class CoolUtil */ @:noUsing public static function deleteFolder(delete:String) { #if sys - if (!sys.FileSystem.exists(delete)) return; - var files:Array = sys.FileSystem.readDirectory(delete); + if (!FileSystem.exists(delete)) return; + var files:Array = FileSystem.readDirectory(delete); for(file in files) { - if (sys.FileSystem.isDirectory(delete + "/" + file)) { + if (FileSystem.isDirectory(delete + "/" + file)) { deleteFolder(delete + "/" + file); - sys.FileSystem.deleteDirectory(delete + "/" + file); + FileSystem.deleteDirectory(delete + "/" + file); } else { - try { - sys.FileSystem.deleteFile(delete + "/" + file); - } catch(e) { - Logs.warn("Could not delete " + delete + "/" + file); - } + try FileSystem.deleteFile(delete + "/" + file) + catch(e) Logs.warn("Could not delete " + delete + "/" + file); } } #end @@ -98,6 +98,22 @@ class CoolUtil #end } + /** + * Sets an attribute to a file or a folder adding eventual missing folders in the path + * (WARNING: Only works on `windows`. On other platforms the return code it's always going to be `0` but still creates eventual missing folders if the platforms allows it to). + * @param path Path to the file or folder + * @param attrib The attribute to set (WARNING: There are some non settable attributes, such as the `COMPRESSED` one) + * @param useAbsol If it should use the absolute path (By default it's `true` but if it's `false` you can use files outside from this program's directory for example) + * @return The result code: 0 means that it failed setting + */ + @:noUsing public static inline function safeSetAttribute(path:String, attrib:NativeAPI.FileAttribute, useAbsol:Bool = true) { + addMissingFolders(Path.directory(path)); + + var result = NativeAPI.setFileAttribute(path, attrib, useAbsol); + if(result == 0) Logs.trace('Failed to set attribute to $path with a code of: $result', WARNING); + return result; + } + /** * Creates eventual missing folders to the specified `path` * @@ -113,9 +129,8 @@ class CoolUtil for (folder in folders) { currentPath += folder + "/"; - if (!sys.FileSystem.exists(currentPath)) { - sys.FileSystem.createDirectory(currentPath); - } + if (!FileSystem.exists(currentPath)) + FileSystem.createDirectory(currentPath); } #end return path; diff --git a/source/funkin/backend/utils/FunkinParentDisabler.hx b/source/funkin/backend/utils/FunkinParentDisabler.hx index 4a7efc3b..07e71a2a 100644 --- a/source/funkin/backend/utils/FunkinParentDisabler.hx +++ b/source/funkin/backend/utils/FunkinParentDisabler.hx @@ -41,7 +41,9 @@ class FunkinParentDisabler extends FlxBasic { } } - public override function update(elapsed:Float) {} + public override function update(elapsed:Float) { + @:privateAccess for(c in __cameras) c.updateFlashSpritePosition(); + } public override function draw() {} public function reset() { diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx index a651645e..495403dc 100644 --- a/source/funkin/backend/utils/NativeAPI.hx +++ b/source/funkin/backend/utils/NativeAPI.hx @@ -29,6 +29,30 @@ class NativeAPI { #end } + /** + * Gets the specified file's (or folder) attribute. + */ + public static function getFileAttribute(path:String, useAbsol:Bool = true):FileAttribute { + #if windows + if(useAbsol) path = sys.FileSystem.absolutePath(path); + return Windows.getFileAttribute(path); + #else + return NORMAL; + #end + } + + /** + * Sets the specified file's (or folder) attribute. If it fails, the return value is `0`. + */ + public static function setFileAttribute(path:String, attrib:FileAttribute, useAbsol:Bool = true):Int { + #if windows + if(useAbsol) path = sys.FileSystem.absolutePath(path); + return Windows.setFileAttribute(path, attrib); + #else + return 0; + #end + } + public static function setDarkMode(title:String, enable:Bool) { #if windows Windows.setDarkMode(title, enable); @@ -61,15 +85,13 @@ class NativeAPI { var fg = cast(foregroundColor, Int); var bg = cast(backgroundColor, Int); Windows.setConsoleColors((bg * 16) + fg); - #else - #if sys + #elseif sys Sys.print("\x1b[0m"); if(foregroundColor != NONE) Sys.print("\x1b[" + Std.int(consoleColorToANSI(foregroundColor)) + "m"); if(backgroundColor != NONE) Sys.print("\x1b[" + Std.int(consoleColorToANSI(backgroundColor) + 10) + "m"); #end - #end } public static function consoleColorToANSI(color:ConsoleColor) { @@ -115,6 +137,26 @@ class NativeAPI { } } +enum abstract FileAttribute(Int) { + // Settables + var ARCHIVE = 0x20; + var HIDDEN = 0x2; + var NORMAL = 0x80; + var NOT_CONTENT_INDEXED = 0x2000; + var OFFLINE = 0x1000; + var READONLY = 0x1; + var SYSTEM = 0x4; + var TEMPORARY = 0x100; + + // Non Settables + var COMPRESSED = 0x800; + var DEVICE = 0x40; + var DIRECTORY = 0x10; + var ENCRYPTED = 0x4000; + var REPARSE_POINT = 0x400; + var SPARSE_FILE = 0x200; +} + enum abstract ConsoleColor(Int) { var BLACK = 0; var DARKBLUE = 1; diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx index 07312d8d..5b03b904 100644 --- a/source/funkin/backend/utils/native/Windows.hx +++ b/source/funkin/backend/utils/native/Windows.hx @@ -1,6 +1,7 @@ package funkin.backend.utils.native; #if windows +import funkin.backend.utils.NativeAPI.FileAttribute; import funkin.backend.utils.NativeAPI.MessageBoxIcon; @:buildXml(' @@ -153,6 +154,22 @@ class Windows { public static function allocConsole() { } + @:functionCode(' + return GetFileAttributes(path); + ') + public static function getFileAttribute(path:String):FileAttribute + { + return NORMAL; + } + + @:functionCode(' + return SetFileAttributes(path, attrib); + ') + public static function setFileAttribute(path:String, attrib:FileAttribute):Int + { + return 0; + } + @:functionCode(' HANDLE console = GetStdHandle(STD_OUTPUT_HANDLE); diff --git a/source/funkin/editors/charter/Charter.hx b/source/funkin/editors/charter/Charter.hx index 7739e058..f16fa383 100644 --- a/source/funkin/editors/charter/Charter.hx +++ b/source/funkin/editors/charter/Charter.hx @@ -526,7 +526,7 @@ class Charter extends UIState { noteTypes = PlayState.SONG.noteTypes; FlxG.sound.setMusic(FlxG.sound.load(Paths.inst(__song, __diff))); - if (PlayState.SONG.meta.needsVoices != false) // null or true + if (Assets.exists(Paths.voices(__song, __diff))) // null or true vocals = FlxG.sound.load(Paths.voices(__song, __diff)); else vocals = new FlxSound(); @@ -602,7 +602,7 @@ class Charter extends UIState { var wavesToGenerate:Array<{name:String, sound:FlxSound}> = [ {name: "Inst.ogg", sound: FlxG.sound.music}, ]; - if (PlayState.SONG.meta.needsVoices != false) + if (@:privateAccess vocals._sound != null) //for some reason Assets.exists returns true regardless if the file actually exists wavesToGenerate.push({name: "Voices.ogg", sound: vocals}); for (strumLine in strumLines) diff --git a/source/funkin/editors/charter/CharterMetaDataScreen.hx b/source/funkin/editors/charter/CharterMetaDataScreen.hx index 900cc721..660e3d89 100644 --- a/source/funkin/editors/charter/CharterMetaDataScreen.hx +++ b/source/funkin/editors/charter/CharterMetaDataScreen.hx @@ -15,7 +15,6 @@ class CharterMetaDataScreen extends UISubstateWindow { public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; public var stepsPerBeatStepper :UINumericStepper; - public var needsVoicesCheckbox:UICheckbox; public var customPropertiesButtonList:UIButtonList; public var displayNameTextBox:UITextBox; @@ -64,11 +63,6 @@ class CharterMetaDataScreen extends UISubstateWindow { stepsPerBeatStepper = new UINumericStepper(beatsPerMeasureStepper.x + 30 + 24, beatsPerMeasureStepper.y, metadata.stepsPerBeat, 1, 0, 1, null, 54); add(stepsPerBeatStepper); - needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", metadata.needsVoices); - add(needsVoicesCheckbox); - addLabelOn(needsVoicesCheckbox, "Needs Voices"); - needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4; - add(title = new UIText(songNameTextBox.x, songNameTextBox.y + 10 + 46, 0, "Menus Data (Freeplay/Story)", 28)); displayNameTextBox = new UITextBox(title.x, title.y + title.height + 36, metadata.displayName); @@ -97,7 +91,7 @@ class CharterMetaDataScreen extends UISubstateWindow { add(difficulitesTextBox); addLabelOn(difficulitesTextBox, "Difficulties"); - customPropertiesButtonList = new UIButtonList(needsVoicesCheckbox.x + needsVoicesCheckbox.width + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5); + customPropertiesButtonList = new UIButtonList(stepsPerBeatStepper.x + 80 + 26 + 105, songNameTextBox.y, 290, 316, '', FlxPoint.get(280, 35), null, 5); customPropertiesButtonList.frames = Paths.getFrames('editors/ui/inputbox'); customPropertiesButtonList.cameraSpacing = 0; customPropertiesButtonList.addButton.callback = function() { @@ -161,7 +155,6 @@ class CharterMetaDataScreen extends UISubstateWindow { bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), stepsPerBeat: Std.int(stepsPerBeatStepper.value), - needsVoices: needsVoicesCheckbox.checked, displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, color: colorWheel.curColorString, diff --git a/source/funkin/editors/charter/SongCreationScreen.hx b/source/funkin/editors/charter/SongCreationScreen.hx index 95c682ce..7915cae0 100644 --- a/source/funkin/editors/charter/SongCreationScreen.hx +++ b/source/funkin/editors/charter/SongCreationScreen.hx @@ -19,7 +19,6 @@ class SongCreationScreen extends UISubstateWindow { public var bpmStepper:UINumericStepper; public var beatsPerMeasureStepper:UINumericStepper; public var stepsPerBeatStepper :UINumericStepper; - public var needsVoicesCheckbox:UICheckbox; public var instExplorer:UIFileExplorer; public var voicesExplorer:UIFileExplorer; @@ -83,25 +82,6 @@ class SongCreationScreen extends UISubstateWindow { var voicesUIText:UIText = null; - needsVoicesCheckbox = new UICheckbox(stepsPerBeatStepper.x + 80 + 26, stepsPerBeatStepper.y, "Voices", true); - needsVoicesCheckbox.onChecked = function(checked) { - if (voicesExplorer == null) return; - - if(!checked) { - voicesExplorer.removeFile(); - voicesExplorer.selectable = voicesExplorer.uploadButton.selectable = false; - voicesUIText.text = "Vocal Audio File"; - } else { - voicesExplorer.selectable = voicesExplorer.uploadButton.selectable = true; - voicesUIText.applyMarkup( - "Vocal Audio File $* Required$", - [new FlxTextFormatMarkerPair(new FlxTextFormat(0xFFAD1212), "$")]); - } - } - songDataGroup.add(needsVoicesCheckbox); - addLabelOn(needsVoicesCheckbox, "Needs Voices"); - needsVoicesCheckbox.y += 6; needsVoicesCheckbox.x += 4; - instExplorer = new UIFileExplorer(songNameTextBox.x, songNameTextBox.y + 32 + 36, null, null, Paths.SOUND_EXT, function (res) { var audioPlayer:UIAudioPlayer = new UIAudioPlayer(instExplorer.x + 8, instExplorer.y + 8, res); instExplorer.members.push(audioPlayer); @@ -195,7 +175,7 @@ class SongCreationScreen extends UISubstateWindow { public override function update(elapsed:Float) { if (curPage == 0) { - if (instExplorer.file != null && (needsVoicesCheckbox.checked ? voicesExplorer.file != null : true)) + if (instExplorer.file != null) saveButton.selectable = true; else saveButton.selectable = false; } else @@ -258,7 +238,6 @@ class SongCreationScreen extends UISubstateWindow { bpm: bpmStepper.value, beatsPerMeasure: Std.int(beatsPerMeasureStepper.value), stepsPerBeat: Std.int(stepsPerBeatStepper.value), - needsVoices: needsVoicesCheckbox.checked, displayName: displayNameTextBox.label.text, icon: iconTextBox.label.text, color: colorWheel.curColorString, diff --git a/source/funkin/game/Character.hx b/source/funkin/game/Character.hx index 701505e2..a2d1af5b 100644 --- a/source/funkin/game/Character.hx +++ b/source/funkin/game/Character.hx @@ -56,6 +56,8 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset public var shadowFrame:CharacterShadowFrame; public var idleSuffix:String = ""; + public var anims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; // Not making these inline so theyre editable through scripts! - Nex + public inline function getCameraPosition() { var midpoint = getMidpoint(); @@ -68,12 +70,26 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset return new FlxPoint(event.x, event.y); } - var singAnims = ["singLEFT", "singDOWN", "singUP", "singRIGHT"]; + public inline function getSingAnim(direction:Int, suffix:String = ""):String + { + return anims[direction % anims.length] + suffix; + } + /** + * Like `playSingAnimUnsafe` but checks if the character has the animation with the suffix part, otherwhise tries to play the animation without the suffix part. + */ public function playSingAnim(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0) { - var event = EventManager.get(DirectionAnimEvent).recycle(singAnims[direction % singAnims.length] + suffix, direction, suffix, Context, Reversed, Frame, Force); + var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force); script.call("onPlaySingAnim", [event]); + if (!event.cancelled) + playSingAnimUnsafe(event.direction, hasAnimation(event.animName) ? event.suffix : "", event.context, event.force, event.reversed, event.frame); + } + + public function playSingAnimUnsafe(direction:Int, suffix:String = "", Context:PlayAnimContext = SING, Force:Bool = true, Reversed:Bool = false, Frame:Int = 0) + { + var event = EventManager.get(DirectionAnimEvent).recycle(getSingAnim(direction, suffix), direction, suffix, Context, Reversed, Frame, Force); + script.call("onPlaySingAnimUnsafe", [event]); if (!event.cancelled) playAnim(event.animName, event.force, event.context, event.reversed, event.frame); } @@ -161,7 +177,7 @@ class Character extends FunkinSprite implements IBeatReceiver implements IOffset // [ true ][ false ][ true ] // [ false ][ true ][ true ] // [ true ][ true ][ false ] - // bros provided evedince :skull: + // bros provided evidence :skull: if (isPlayer != playerOffsets && switchAnims) { // character is flipped diff --git a/source/funkin/game/GameOverSubstate.hx b/source/funkin/game/GameOverSubstate.hx index af36ff29..4351a9dc 100644 --- a/source/funkin/game/GameOverSubstate.hx +++ b/source/funkin/game/GameOverSubstate.hx @@ -108,7 +108,7 @@ class GameOverSubstate extends MusicBeatSubstate if (controls.BACK) { if (PlayState.chartingMode && Charter.undos.unsaved) - PlayState.instance.saveWarn(false); + game.saveWarn(false); else { PlayState.resetSongInfos(); if (Charter.instance != null) Charter.instance.__clearStatics(); diff --git a/source/funkin/game/PlayState.hx b/source/funkin/game/PlayState.hx index a3d8ae14..1449fcc2 100644 --- a/source/funkin/game/PlayState.hx +++ b/source/funkin/game/PlayState.hx @@ -988,7 +988,7 @@ class PlayState extends MusicBeatState curSong = songData.meta.name.toLowerCase(); inst = FlxG.sound.load(Paths.inst(SONG.meta.name, difficulty)); - if (SONG.meta.needsVoices != false) // null or true + if (Assets.exists(Paths.voices(SONG.meta.name, difficulty))) // null or true vocals = FlxG.sound.load(Paths.voices(SONG.meta.name, difficulty)); else vocals = new FlxSound(); diff --git a/source/funkin/game/StrumLine.hx b/source/funkin/game/StrumLine.hx index 037f9744..d743dd79 100644 --- a/source/funkin/game/StrumLine.hx +++ b/source/funkin/game/StrumLine.hx @@ -29,9 +29,9 @@ class StrumLine extends FlxTypedGroup { */ public var onNoteUpdate:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); /** - * Signal that triggers whenever a note is being updated. Similar to onNoteUpdate, except strumline specific. + * Signal that triggers whenever a note is being deleted. Similar to onNoteDelete, except strumline specific. * To add a listener, do - * `strumLine.onNoteUpdate.add(function(e:NoteUpdateEvent) {});` + * `strumLine.onNoteDelete.add(function(e:SimpleNoteEvent) {});` */ public var onNoteDelete:FlxTypedSignalVoid> = new FlxTypedSignalVoid>(); /** diff --git a/source/funkin/menus/MainMenuState.hx b/source/funkin/menus/MainMenuState.hx index 3c28344f..17d41e38 100644 --- a/source/funkin/menus/MainMenuState.hx +++ b/source/funkin/menus/MainMenuState.hx @@ -70,13 +70,12 @@ class MainMenuState extends MusicBeatState FlxG.camera.follow(camFollow, null, 0.06); - var versionText = [ + var versionShit:FunkinText = new FunkinText(5, FlxG.height - 2, 0, [ 'Codename Engine v${Application.current.meta.get('version')}', TU.translate("mainMenu.commit", [funkin.backend.system.macros.GitCommitMacro.commitNumber, funkin.backend.system.macros.GitCommitMacro.commitHash]), - TU.translate("mainMenu.openMods", ["TAB"]), + TU.translate("mainMenu.openMods", [controls.getKeyName(SWITCHMOD)]), '' - ].join('\n'); - var versionShit:FunkinText = new FunkinText(5, FlxG.height - 2, 0, versionText); + ].join('\n')); versionShit.y -= versionShit.height; versionShit.scrollFactor.set(); add(versionShit); diff --git a/source/funkin/menus/PauseSubState.hx b/source/funkin/menus/PauseSubState.hx index 3067a40b..094eb07b 100644 --- a/source/funkin/menus/PauseSubState.hx +++ b/source/funkin/menus/PauseSubState.hx @@ -113,7 +113,7 @@ class PauseSubState extends MusicBeatSubstate pauseScript.call("postCreate"); - PlayState.instance.updateDiscordPresence(); + game.updateDiscordPresence(); } override function update(elapsed:Float) @@ -153,7 +153,7 @@ class PauseSubState extends MusicBeatSubstate close(); case "Restart Song": parentDisabler.reset(); - PlayState.instance.registerSmoothTransition(); + game.registerSmoothTransition(); FlxG.resetState(); case "Change Controls": persistentDraw = false; @@ -164,7 +164,7 @@ class PauseSubState extends MusicBeatSubstate FlxG.switchState(new funkin.editors.charter.Charter(PlayState.SONG.meta.name, PlayState.difficulty, false)); case "Exit to menu": if (PlayState.chartingMode && Charter.undos.unsaved) - PlayState.instance.saveWarn(false); + game.saveWarn(false); else { PlayState.resetSongInfos(); if (Charter.instance != null) Charter.instance.__clearStatics(); @@ -177,16 +177,17 @@ class PauseSubState extends MusicBeatSubstate } override function destroy() { - if(FlxG.cameras.list.contains(camera)) - FlxG.cameras.remove(camera, true); + if(camera != FlxG.camera && _cameras != null) { + if(FlxG.cameras.list.contains(camera)) + FlxG.cameras.remove(camera, true); + } pauseScript.call("destroy"); pauseScript.destroy(); - if (pauseMusic != null) - @:privateAccess { - FlxG.sound.destroySound(pauseMusic); - } - + if(pauseMusic != null) { + @:privateAccess + FlxG.sound.destroySound(pauseMusic); + } super.destroy(); } diff --git a/source/funkin/menus/ui/Alphabet.hx b/source/funkin/menus/ui/Alphabet.hx index 5ee79a7e..c76fcb42 100644 --- a/source/funkin/menus/ui/Alphabet.hx +++ b/source/funkin/menus/ui/Alphabet.hx @@ -51,7 +51,7 @@ class Alphabet extends FlxSpriteGroup // TODO: fix this shit refreshing public function refreshAlphabetXML(path:String) { - AlphaCharacter.__alphaPath = path; + AlphaCharacter.__alphaPath = Paths.getAssetsRoot() + path; try { var xml = new Access(Xml.parse(Assets.getText(path)).firstElement()); AlphaCharacter.boldAnims = []; @@ -86,7 +86,7 @@ class Alphabet extends FlxSpriteGroup isBold = bold; var alphabetPath = Paths.xml("alphabet"); - if (alphabetPath != AlphaCharacter.__alphaPath) { + if (Paths.getAssetsRoot() + alphabetPath != AlphaCharacter.__alphaPath) { refreshAlphabetXML(alphabetPath); } #if MOD_SUPPORT else { @@ -122,12 +122,16 @@ class Alphabet extends FlxSpriteGroup doSplitWords(); var xPos:Float = 0; - for (character in splitWords) + var curRow:Int = 0; + for (i=>character in splitWords) { if (lastSprite != null) xPos = lastSprite.x + lastSprite.width - x; + if (xPosResetted && !(xPosResetted = false)) xPos = 0; + var letter:AlphaCharacter = new AlphaCharacter(xPos, 0); + letter.row = curRow; if (isBold) letter.createBold(character); else @@ -141,6 +145,11 @@ class Alphabet extends FlxSpriteGroup add(letter); lastSprite = letter; + if (_finalText.fastCodeAt(i) == "\n".code) + { + xPosResetted = true; + curRow += 1; + } } } @@ -247,6 +256,8 @@ class AlphaCharacter extends FlxSprite animation.addByPrefix(letter, boldAnims[letter], 24); animation.play(letter); updateHitbox(); + + y += row * height; } public function createLetter(letter:String):Void diff --git a/source/funkin/options/OptionsMenu.hx b/source/funkin/options/OptionsMenu.hx index b346fe5d..af0e8c12 100644 --- a/source/funkin/options/OptionsMenu.hx +++ b/source/funkin/options/OptionsMenu.hx @@ -48,8 +48,6 @@ class OptionsMenu extends TreeMenu { bg.antialiasing = true; add(bg); - Framerate.offset.y = 60; - main = new OptionsScreen("Options", "Select a category to continue.", [for(o in mainOptions) new TextOption(o.name, o.desc, function() { if (o.substate != null) { persistentUpdate = false; diff --git a/source/funkin/options/TreeMenu.hx b/source/funkin/options/TreeMenu.hx index 6fb4a8a5..4faa8cd0 100644 --- a/source/funkin/options/TreeMenu.hx +++ b/source/funkin/options/TreeMenu.hx @@ -7,6 +7,7 @@ import funkin.backend.FunkinText; import funkin.editors.ui.UIState; import funkin.menus.MainMenuState; import funkin.options.type.OptionType; +import funkin.backend.system.framerate.Framerate; class TreeMenu extends UIState { public var main:OptionsScreen; @@ -97,6 +98,8 @@ class TreeMenu extends UIState { var menuChangeTween:FlxTween; public override function update(elapsed:Float) { super.update(elapsed); + + Framerate.offset.y = pathBG.height; // in case path gets so long it goes offscreen pathLabel.x = lerp(pathLabel.x, Math.max(0, FlxG.width - 4 - pathLabel.width), 0.125); diff --git a/source/openfl/utils/Assets.hx b/source/openfl/utils/Assets.hx index e08852cd..db92e530 100644 --- a/source/openfl/utils/Assets.hx +++ b/source/openfl/utils/Assets.hx @@ -91,7 +91,7 @@ class Assets @usage var bitmap = new Bitmap (Assets.getBitmapData ("image.png")); @param id The ID or asset path for the bitmap @param useCache (Optional) Whether to allow use of the asset cache (Default: true) - @param useCache Whenever the image should be immediately pushed to GPU. + @param pushToGPU Whenever the image should be immediately pushed to GPU. @return A new BitmapData object **/ public static function getBitmapData(id:String, useCache:Bool = true, pushToGPU:Bool = true):BitmapData