Merge branch 'rewrite/master' into kadedev/fix-audio-offsets

This commit is contained in:
Cameron Taylor 2024-09-23 13:07:32 -04:00
commit 1c2a403dea
75 changed files with 674 additions and 285 deletions

44
.github/label-actions.yml vendored Normal file
View file

@ -0,0 +1,44 @@
# Configuration for Label Actions - https://github.com/dessant/label-actions
# Automatically close issues and pull requests when the `status: duplicate` label is applied
'status: duplicate':
issues:
# Post a comment
comment: >
This issue is a duplicate. Please direct all discussion to the original issue.
# Close the issue
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'
prs:
# Post a comment
comment: >
This pull request is a duplicate. Please direct all discussion to the original pull request.
# Remove other status labels
unlabel:
- 'status: pending triage'
# Close the pull request
close: true
# Set a close reason
close-reason: 'not planned'
'status: rejected':
issues:
# Close the issue
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'
prs:
# Close the pull request
close: true
# Remove other status labels
unlabel:
- 'status: pending triage'
# Set a close reason
close-reason: 'not planned'

29
.github/workflows/label-actions.yml vendored Normal file
View file

@ -0,0 +1,29 @@
# Perform actions when labels are applied to issues, discussions, or pull requests
# See .github/label-actions.yml
name: 'Label Actions'
on:
issues:
types:
- labeled
- unlabeled
pull_request_target:
types:
- labeled
- unlabeled
discussion:
types:
- labeled
- unlabeled
permissions:
contents: read
issues: write
pull-requests: write
discussions: write
jobs:
action:
runs-on: ubuntu-latest
steps:
- uses: dessant/label-actions@v4

52
.github/workflows/label-issue.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: "Issue Labeler"
on:
issues:
types:
- opened
- reopened
- edited
jobs:
# When an issue is opened, perform a similarity check for potential duplicates.
# If some are found, add a label and comment listing the potential duplicate issues.
potential-duplicate:
name: Detect potential duplicate issues
runs-on: ubuntu-latest
steps:
- uses: wow-actions/potential-duplicates@v1
with:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Issue title filter work with anymatch https://www.npmjs.com/package/anymatch.
# Any matched issue will stop detection immediately.
# You can specify multi filters in each line.
filter: ''
# Exclude keywords in title before detecting.
exclude: ''
# Label to set, when potential duplicates are detected.
label: 'potential duplicate'
# Get issues with state to compare. Supported state: 'all', 'closed', 'open'.
state: all
# If similarity is higher than this threshold([0,1]), issue will be marked as duplicate.
# Turn this up if the detection is too sensitive
threshold: 0.6
# Reactions to be add to comment when potential duplicates are detected.
# Available reactions: "-1", "+1", "confused", "laugh", "heart", "hooray", "rocket", "eyes"
# reactions: '-1'
# Comment to post when potential duplicates are detected.
comment: >
Potential duplicates: {{#issues}}
- [#{{ number }}] {{ title }} ({{ accuracy }}%)
{{/issues}}
# When an issue is opened, detect if it has an empty body or incomplete issue form.
# If it does, close the issue immediately.
empty-issues:
name: Close empty issues
runs-on: ubuntu-latest
steps:
- name: Run empty issues closer action
uses: rickstaa/empty-issues-closer-action@v1
env:
github_token: ${{ secrets.GITHUB_TOKEN }}
with:
close_comment: Closing this issue because it appears to be empty. Please update the issue for it to be reopened.
open_comment: Reopening this issue because the author provided more information.

View file

@ -3,6 +3,7 @@ on:
- pull_request_target - pull_request_target
jobs: jobs:
# Apply labels to pull requests based on which files were edited
labeler: labeler:
permissions: permissions:
contents: read contents: read
@ -13,6 +14,7 @@ jobs:
uses: actions/labeler@v5 uses: actions/labeler@v5
with: with:
sync-labels: true sync-labels: true
# Apply labels to pull requests based on how many lines were edited
changed-lines-count-labeler: changed-lines-count-labeler:
permissions: permissions:
contents: read contents: read

4
.gitmodules vendored
View file

@ -1,6 +1,6 @@
[submodule "assets"] [submodule "assets"]
path = assets path = assets
url = https://github.com/FunkinCrew/funkin.assets url = https://github.com/FunkinCrew/Funkin-assets-secret
[submodule "art"] [submodule "art"]
path = art path = art
url = https://github.com/FunkinCrew/funkin.art url = https://github.com/FunkinCrew/Funkin-art-secret

2
assets

@ -1 +1 @@
Subproject commit bc7009b4242691faa5c4552f7ca8a2f28e8cb1d2 Subproject commit 72bb24ac9b0b9db4b2f9c38ef091e146ef16a1a4

View file

@ -41,9 +41,6 @@ There are several useful build flags you can add to a build to affect how it wor
- `-DFEATURE_STAGE_EDITOR` to forcibly enable the experimental stage editor. - `-DFEATURE_STAGE_EDITOR` to forcibly enable the experimental stage editor.
- `-DFEATURE_GHOST_TAPPING` to forcibly enable an experimental gameplay change to the anti-mash system. - `-DFEATURE_GHOST_TAPPING` to forcibly enable an experimental gameplay change to the anti-mash system.
# Troubleshooting - GO THROUGH THESE STEPS BEFORE OPENING ISSUES ON GITHUB! # Troubleshooting
- During the cloning process, you may experience an error along the lines of `error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)` due to poor connectivity. A common fix is to run ` git config --global http.postBuffer 4096M`. If you experience any issues during the compilation process, DO NOT open an issue on GitHub. Instead, check the [Troubleshooting Guide](TROUBLESHOOTING.md) for steps on how to resolve common problems.
- Make sure your game directory has an `assets` folder! If it's missing, copy the path to your `funkin` folder and run `cd the\path\you\copied`. Then follow the guide starting from **Step 4**.
- Check that your `assets` folder is not empty! If it is, go back to **Step 4** and follow the guide from there.
- The compilation process often fails due to having the wrong versions of the required libraries. Many errors can be resolved by deleting the `.haxelib` folder and following the guide starting from **Step 5**.

View file

@ -1,4 +1,4 @@
# Troubleshooting Common Issues # Troubleshooting Common Compilation Issues
- Weird macro error with a very tall call stack: Restart Visual Studio Code - Weird macro error with a very tall call stack: Restart Visual Studio Code
- NOTE: This is caused by Polymod somewhere, and seems to only occur when there is another compile error somewhere in the program. There is a bounty up for it. - NOTE: This is caused by Polymod somewhere, and seems to only occur when there is another compile error somewhere in the program. There is a bounty up for it.
@ -13,3 +13,11 @@
- `LINK : fatal error LNK1201: error writing to program database ''; check for insufficient disk space, invalid path, or insufficient privilege` - `LINK : fatal error LNK1201: error writing to program database ''; check for insufficient disk space, invalid path, or insufficient privilege`
- This error occurs if the PDB file located in your `export` folder is in use or exceeds 4 GB. Try deleting the `export` folder and building again from scratch. - This error occurs if the PDB file located in your `export` folder is in use or exceeds 4 GB. Try deleting the `export` folder and building again from scratch.
- `error: RPC failed; curl 92 HTTP/2 stream 0 was not closed cleanly: PROTOCOL_ERROR (err 1)`
- This error can happen during cloning as a result of poor network connectivity. A common fix is to run ` git config --global http.postBuffer 4096M` in your terminal.
- Repository is missing an `assets` folder, or `assets` folder is empty.
- You did not clone the repository correctly! Copy the path to your `funkin` folder and run `cd the\path\you\copied`. Then follow the compilation guide starting from **Step 4**.
- Other compilation issues may be caused by installing bad library versions. Try deleting the `.haxelib` folder and following the guide starting from **Step 5**.

View file

@ -35,13 +35,6 @@
"ref": "951a0103a17bfa55eed86703ce50b4fb0d7590bc", "ref": "951a0103a17bfa55eed86703ce50b4fb0d7590bc",
"url": "https://github.com/FunkinCrew/flixel-text-input" "url": "https://github.com/FunkinCrew/flixel-text-input"
}, },
{
"name": "flixel-ui",
"type": "git",
"dir": null,
"ref": "27f1ba626f80a6282fa8a187115e79a4a2133dc2",
"url": "https://github.com/HaxeFlixel/flixel-ui"
},
{ {
"name": "flxanimate", "name": "flxanimate",
"type": "git", "type": "git",
@ -77,14 +70,14 @@
"name": "haxeui-core", "name": "haxeui-core",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "22f7c5a8ffca90d4677cffd6e570f53761709fbc", "ref": "c9d96b168ea2a19274ad7c766ab1a34b57baa793",
"url": "https://github.com/haxeui/haxeui-core" "url": "https://github.com/haxeui/haxeui-core"
}, },
{ {
"name": "haxeui-flixel", "name": "haxeui-flixel",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "28bb710d0ae5d94b5108787593052165be43b980", "ref": "013b9d4e56bfe9a034e028a8d685f0b274cb73c4",
"url": "https://github.com/haxeui/haxeui-flixel" "url": "https://github.com/haxeui/haxeui-flixel"
}, },
{ {

View file

@ -941,6 +941,8 @@ class Project extends HXProject {
var commitHash:String = process.stdout.readLine(); var commitHash:String = process.stdout.readLine();
var commitHashSplice:String = commitHash.substr(0, 7); var commitHashSplice:String = commitHash.substr(0, 7);
process.close();
return commitHashSplice; return commitHashSplice;
} }
@ -955,6 +957,8 @@ class Project extends HXProject {
var branchName:String = branchProcess.stdout.readLine(); var branchName:String = branchProcess.stdout.readLine();
branchProcess.close();
return branchName; return branchName;
} }
@ -979,6 +983,8 @@ class Project extends HXProject {
} }
} }
branchProcess.close();
return output.length > 0; return output.length > 0;
} }

View file

@ -2,6 +2,7 @@ package;
import flixel.FlxGame; import flixel.FlxGame;
import flixel.FlxState; import flixel.FlxState;
import funkin.Preferences;
import funkin.util.logging.CrashHandler; import funkin.util.logging.CrashHandler;
import funkin.ui.debug.MemoryCounter; import funkin.ui.debug.MemoryCounter;
import funkin.save.Save; import funkin.save.Save;
@ -22,12 +23,6 @@ class Main extends Sprite
var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom). var gameHeight:Int = 720; // Height of the game in pixels (might be less / more in actual pixels depending on your zoom).
var initialState:Class<FlxState> = funkin.InitState; // The FlxState the game starts with. var initialState:Class<FlxState> = funkin.InitState; // The FlxState the game starts with.
var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions. var zoom:Float = -1; // If -1, zoom is automatically calculated to fit the window dimensions.
#if (web || CHEEMS)
var framerate:Int = 60; // How many frames per second the game should run at.
#else
// TODO: This should probably be in the options menu?
var framerate:Int = 144; // How many frames per second the game should run at.
#end
var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode. var skipSplash:Bool = true; // Whether to skip the flixel splash screen that appears in release mode.
var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets var startFullscreen:Bool = false; // Whether to start the game in fullscreen on desktop targets
@ -109,7 +104,7 @@ class Main extends Sprite
// George recommends binding the save before FlxGame is created. // George recommends binding the save before FlxGame is created.
Save.load(); Save.load();
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen); var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash, startFullscreen);
// FlxG.game._customSoundTray wants just the class, it calls new from // FlxG.game._customSoundTray wants just the class, it calls new from
// create() in there, which gets called when it's added to stage // create() in there, which gets called when it's added to stage
@ -123,8 +118,6 @@ class Main extends Sprite
game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil()); game.debugger.interaction.addTool(new funkin.util.TrackerToolButtonUtil());
#end #end
addChild(fpsCounter);
#if hxcpp_debug_server #if hxcpp_debug_server
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.'); trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');
#else #else

View file

@ -1,38 +1,161 @@
package funkin; package funkin;
import openfl.utils.Future;
/** /**
* A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions. * A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions.
* Later we'll add Funkin-specific caching to this. * Later we'll add Funkin-specific caching to this.
*/ */
class Assets class Assets
{ {
public static function getText(path:String):String /**
* Get the file system path for an asset
* @param path The asset path to load from, relative to the assets folder
* @return The path to the asset on the file system
*/
public static function getPath(path:String):String
{ {
return openfl.utils.Assets.getText(path); return openfl.utils.Assets.getPath(path);
}
public static function getMusic(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getMusic(path);
}
public static function getBitmapData(path:String):openfl.display.BitmapData
{
return openfl.utils.Assets.getBitmapData(path);
} }
/**
* Load bytes from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The byte contents of the file
*/
public static function getBytes(path:String):haxe.io.Bytes public static function getBytes(path:String):haxe.io.Bytes
{ {
return openfl.utils.Assets.getBytes(path); return openfl.utils.Assets.getBytes(path);
} }
/**
* Load bytes from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the byte contents of the file
*/
public static function loadBytes(path:String):Future<openfl.utils.ByteArray>
{
return openfl.utils.Assets.loadBytes(path);
}
/**
* Load text from an asset.
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The text contents of the file
*/
public static function getText(path:String):String
{
return openfl.utils.Assets.getText(path);
}
/**
* Load text from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the text contents of the file
*/
public static function loadText(path:String):Future<String>
{
return openfl.utils.Assets.loadText(path);
}
/**
* Load a Sound file from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded sound
*/
public static function getSound(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getSound(path);
}
/**
* Load a Sound file from an asset asynchronously
* @param path The asset path to load from
* @return A future which promises to return the loaded sound
*/
public static function loadSound(path:String):Future<openfl.media.Sound>
{
return openfl.utils.Assets.loadSound(path);
}
/**
* Load a Sound file from an asset, with optimizations specific to long-duration music
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded sound
*/
public static function getMusic(path:String):openfl.media.Sound
{
return openfl.utils.Assets.getMusic(path);
}
/**
* Load a Sound file from an asset asynchronously, with optimizations specific to long-duration music
* @param path The asset path to load from
* @return A future which promises to return the loaded sound
*/
public static function loadMusic(path:String):Future<openfl.media.Sound>
{
return openfl.utils.Assets.loadMusic(path);
}
/**
* Load a Bitmap from an asset
* May cause stutters or throw an error if the asset is not cached
* @param path The asset path to load from
* @return The loaded Bitmap image
*/
public static function getBitmapData(path:String):openfl.display.BitmapData
{
return openfl.utils.Assets.getBitmapData(path);
}
/**
* Load a Bitmap from an asset asynchronously
* @param path The asset path to load from
* @return The future which promises to return the loaded Bitmap image
*/
public static function loadBitmapData(path:String):Future<openfl.display.BitmapData>
{
return openfl.utils.Assets.loadBitmapData(path);
}
/**
* Determines whether the given asset of the given type exists.
* @param path The asset path to check
* @param type The asset type to check
* @return Whether the asset exists
*/
public static function exists(path:String, ?type:openfl.utils.AssetType):Bool public static function exists(path:String, ?type:openfl.utils.AssetType):Bool
{ {
return openfl.utils.Assets.exists(path, type); return openfl.utils.Assets.exists(path, type);
} }
public static function list(type:openfl.utils.AssetType):Array<String> /**
* Retrieve a list of all assets of the given type
* @param type The asset type to check
* @return A list of asset paths
*/
public static function list(?type:openfl.utils.AssetType):Array<String>
{ {
return openfl.utils.Assets.list(type); return openfl.utils.Assets.list(type);
} }
public static function hasLibrary(name:String):Bool
{
return openfl.utils.Assets.hasLibrary(name);
}
public static function getLibrary(name:String):lime.utils.AssetLibrary
{
return openfl.utils.Assets.getLibrary(name);
}
public static function loadLibrary(name:String):Future<openfl.utils.AssetLibrary>
{
return openfl.utils.Assets.loadLibrary(name);
}
} }

View file

@ -2,7 +2,6 @@ package funkin;
import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxAtlasFrames;
import openfl.utils.AssetType; import openfl.utils.AssetType;
import openfl.utils.Assets as OpenFlAssets;
/** /**
* A core class which handles determining asset paths. * A core class which handles determining asset paths.
@ -44,11 +43,11 @@ class Paths
if (currentLevel != null) if (currentLevel != null)
{ {
var levelPath:String = getLibraryPathForce(file, currentLevel); var levelPath:String = getLibraryPathForce(file, currentLevel);
if (OpenFlAssets.exists(levelPath, type)) return levelPath; if (Assets.exists(levelPath, type)) return levelPath;
} }
var levelPath:String = getLibraryPathForce(file, 'shared'); var levelPath:String = getLibraryPathForce(file, 'shared');
if (OpenFlAssets.exists(levelPath, type)) return levelPath; if (Assets.exists(levelPath, type)) return levelPath;
return getPreloadPath(file); return getPreloadPath(file);
} }

View file

@ -7,6 +7,35 @@ import funkin.save.Save;
*/ */
class Preferences class Preferences
{ {
/**
* FPS
* @default `60`
*/
public static var framerate(get, set):Int;
static function get_framerate():Int
{
#if web
return 60;
#else
return Save?.instance?.options?.framerate ?? 60;
#end
}
static function set_framerate(value:Int):Int
{
#if web
return 60;
#else
var save:Save = Save.instance;
save.options.framerate = value;
save.flush();
FlxG.updateFramerate = value;
FlxG.drawFramerate = value;
return value;
#end
}
/** /**
* Whether some particularly fowl language is displayed. * Whether some particularly fowl language is displayed.
* @default `true` * @default `true`

View file

@ -543,18 +543,19 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
*/ */
public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound> public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound>
{ {
var result = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad); var result:Null<FunkinSound> = FunkinSound.load(key, volume, false, true, true, onComplete, onLoad);
return result; return result;
} }
/** /**
* Stop all sounds in the pool and allow them to be recycled. * Stop all sounds in the pool and allow them to be recycled.
*/ */
public static function stopAllAudio(musicToo:Bool = false):Void public static function stopAllAudio(musicToo:Bool = false, persistToo:Bool = false):Void
{ {
for (sound in pool) for (sound in pool)
{ {
if (sound == null) continue; if (sound == null) continue;
if (!persistToo && sound.persist) continue;
if (!musicToo && sound == FlxG.sound.music) continue; if (!musicToo && sound == FlxG.sound.music) continue;
sound.destroy(); sound.destroy();
} }

View file

@ -264,12 +264,32 @@ class PlayerCharSelectData
*/ */
@:optional @:optional
public var position:Null<Int>; public var position:Null<Int>;
/**
* The GF name to assign for this character.
*/
@:optional
public var gf:PlayerCharSelectGFData;
}
typedef PlayerCharSelectGFData =
{
@:optional
public var assetPath:String;
@:optional
public var animInfoPath:String;
@:optional
@:default(false)
public var visualizer:Bool;
} }
typedef PlayerResultsData = typedef PlayerResultsData =
{ {
var music:PlayerResultsMusicData; var music:PlayerResultsMusicData;
var perfectGold:Array<PlayerResultsAnimationData>;
var perfect:Array<PlayerResultsAnimationData>; var perfect:Array<PlayerResultsAnimationData>;
var excellent:Array<PlayerResultsAnimationData>; var excellent:Array<PlayerResultsAnimationData>;
var great:Array<PlayerResultsAnimationData>; var great:Array<PlayerResultsAnimationData>;

View file

@ -7,7 +7,6 @@ import flxanimate.frames.FlxAnimateFrames;
import flixel.graphics.frames.FlxFrame; import flixel.graphics.frames.FlxFrame;
import flixel.system.FlxAssets.FlxGraphicAsset; import flixel.system.FlxAssets.FlxGraphicAsset;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.utils.Assets;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import flxanimate.animate.FlxKeyFrame; import flxanimate.animate.FlxKeyFrame;
@ -184,7 +183,7 @@ class FlxAtlasSprite extends FlxAnimate
// Move to the first frame of the animation. // Move to the first frame of the animation.
// goToFrameLabel(id); // goToFrameLabel(id);
trace('Playing animation $id'); // trace('Playing animation $id');
if ((id == null || id == "") || this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null)) if ((id == null || id == "") || this.anim.symbolDictionary.exists(id) || (this.anim.getByName(id) != null))
{ {
this.anim.play(id, restart, false, startFrame); this.anim.play(id, restart, false, startFrame);

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class AdjustColorShader extends FlxRuntimeShader class AdjustColorShader extends FlxRuntimeShader
{ {

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.display.ShaderInput; import openfl.display.ShaderInput;

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
/** /**
* Note... not actually gaussian! * Note... not actually gaussian!

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class Grayscale extends FlxRuntimeShader class Grayscale extends FlxRuntimeShader
{ {

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class HSVShader extends FlxRuntimeShader class HSVShader extends FlxRuntimeShader
{ {

View file

@ -1,8 +1,6 @@
package funkin.graphics.shaders; package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader; import flixel.addons.display.FlxRuntimeShader;
import openfl.utils.Assets;
import funkin.Paths;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
class MosaicEffect extends FlxRuntimeShader class MosaicEffect extends FlxRuntimeShader

View file

@ -2,7 +2,6 @@ package funkin.graphics.shaders;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import openfl.utils.Assets;
class RuntimeCustomBlendShader extends RuntimePostEffectShader class RuntimeCustomBlendShader extends RuntimePostEffectShader
{ {

View file

@ -5,7 +5,6 @@ import openfl.display.BitmapData;
import openfl.display.ShaderParameter; import openfl.display.ShaderParameter;
import openfl.display.ShaderParameterType; import openfl.display.ShaderParameterType;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import openfl.utils.Assets;
typedef Light = typedef Light =
{ {

View file

@ -0,0 +1,32 @@
package funkin.graphics.video;
import hxcodec.flixel.FlxVideoSprite;
/**
* Not to be confused with FlxVideo, this is a hxcodec based video class
* We override it simply to correct/control our volume easier.
*/
class FunkinVideoSprite extends FlxVideoSprite
{
public var volume(default, set):Float = 1;
public function new(x:Float = 0, y:Float = 0)
{
super(x, y);
set_volume(1);
}
override public function update(elapsed:Float):Void
{
super.update(elapsed);
set_volume(volume);
}
function set_volume(value:Float):Float
{
volume = value;
bitmap.volume = Std.int((FlxG.sound.muted ? 0 : 1) * (FlxG.sound.logToLinear(FlxG.sound.volume) * 100) * volume);
return volume;
}
}

View file

@ -3,6 +3,7 @@ package;
#if !macro #if !macro
// Only import these when we aren't in a macro. // Only import these when we aren't in a macro.
import funkin.util.Constants; import funkin.util.Constants;
import funkin.Assets;
import funkin.Paths; import funkin.Paths;
import funkin.Preferences; import funkin.Preferences;
import flixel.FlxG; // This one in particular causes a compile error if you're using macros. import flixel.FlxG; // This one in particular causes a compile error if you're using macros.

View file

@ -1,7 +1,6 @@
package funkin.input; package funkin.input;
import haxe.ui.backend.flixel.CursorHelper; import haxe.ui.backend.flixel.CursorHelper;
import openfl.utils.Assets;
import lime.app.Future; import lime.app.Future;
import openfl.display.BitmapData; import openfl.display.BitmapData;

View file

@ -228,6 +228,8 @@ class PolymodHandler
static function buildImports():Void static function buildImports():Void
{ {
// Add default imports for common classes. // Add default imports for common classes.
Polymod.addDefaultImport(funkin.Assets);
Polymod.addDefaultImport(funkin.Paths);
// Add import aliases for certain classes. // Add import aliases for certain classes.
// NOTE: Scripted classes are automatically aliased to their parent class. // NOTE: Scripted classes are automatically aliased to their parent class.
@ -258,7 +260,7 @@ class PolymodHandler
Polymod.blacklistImport('cpp.Lib'); Polymod.blacklistImport('cpp.Lib');
// `Unserializer` // `Unserializer`
// Unserializerr.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages // Unserializer.DEFAULT_RESOLVER.resolveClass() can access blacklisted packages
Polymod.blacklistImport('Unserializer'); Polymod.blacklistImport('Unserializer');
// `lime.system.CFFI` // `lime.system.CFFI`

View file

@ -1,8 +1,8 @@
package funkin.modding.base; package funkin.modding.base;
/** /**
* A script that can be tied to an FlxSprite. * A script that can be tied to a FunkinSprite.
* Create a scripted class that extends FlxSprite to use this. * Create a scripted class that extends FunkinSprite to use this.
*/ */
@:hscriptClass @:hscriptClass
class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements HScriptedClass {} class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements HScriptedClass {}

View file

@ -103,7 +103,7 @@ class NoteScriptEvent extends ScriptEvent
public var comboCount(default, null):Int; public var comboCount(default, null):Int;
/** /**
* Whether to play the record scratch sound (if this eventn type is `NOTE_MISS`). * Whether to play the record scratch sound (if this event type is `NOTE_MISS`).
*/ */
public var playSound(default, default):Bool; public var playSound(default, default):Bool;

View file

@ -11,7 +11,6 @@ import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
import flixel.util.FlxTimer; import flixel.util.FlxTimer;
import funkin.util.EaseUtil; import funkin.util.EaseUtil;
import funkin.audio.FunkinSound; import funkin.audio.FunkinSound;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;

View file

@ -15,7 +15,6 @@ import funkin.ui.freeplay.FreeplayState;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState; import funkin.ui.story.StoryMenuState;
import funkin.util.MathUtil; import funkin.util.MathUtil;
import openfl.utils.Assets;
import funkin.effects.RetroCameraFade; import funkin.effects.RetroCameraFade;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;

View file

@ -1623,7 +1623,7 @@ class PlayState extends MusicBeatSubState
if (girlfriend != null) if (girlfriend != null)
{ {
girlfriend.characterType = CharacterType.GF; // Don't need to do anything.
} }
else if (currentCharacterData.girlfriend != '') else if (currentCharacterData.girlfriend != '')
{ {
@ -1641,8 +1641,6 @@ class PlayState extends MusicBeatSubState
if (dad != null) if (dad != null)
{ {
dad.characterType = CharacterType.DAD;
// //
// OPPONENT HEALTH ICON // OPPONENT HEALTH ICON
// //
@ -1661,8 +1659,6 @@ class PlayState extends MusicBeatSubState
if (boyfriend != null) if (boyfriend != null)
{ {
boyfriend.characterType = CharacterType.BF;
// //
// PLAYER HEALTH ICON // PLAYER HEALTH ICON
// //

View file

@ -472,9 +472,12 @@ class ResultState extends MusicBeatSubState
{ {
ease: FlxEase.quartOut, ease: FlxEase.quartOut,
onUpdate: _ -> { onUpdate: _ -> {
clearPercentLerp = Math.round(clearPercentLerp);
clearPercentCounter.curNumber = Math.round(clearPercentCounter.curNumber);
// Only play the tick sound if the number increased. // Only play the tick sound if the number increased.
if (clearPercentLerp != clearPercentCounter.curNumber) if (clearPercentLerp != clearPercentCounter.curNumber)
{ {
trace('$clearPercentLerp and ${clearPercentCounter.curNumber}');
clearPercentLerp = clearPercentCounter.curNumber; clearPercentLerp = clearPercentCounter.curNumber;
FunkinSound.playOnce(Paths.sound('scrollMenu')); FunkinSound.playOnce(Paths.sound('scrollMenu'));
} }

View file

@ -11,7 +11,7 @@ import funkin.play.character.ScriptedCharacter.ScriptedSparrowCharacter;
import funkin.util.assets.DataAssets; import funkin.util.assets.DataAssets;
import funkin.util.VersionUtil; import funkin.util.VersionUtil;
import haxe.Json; import haxe.Json;
import openfl.utils.Assets; import flixel.graphics.frames.FlxFrame;
class CharacterDataParser class CharacterDataParser
{ {
@ -281,41 +281,78 @@ class CharacterDataParser
} }
/** /**
* TODO: Hardcode this. * Returns the idle frame of a character.
*/ */
public static function getCharPixelIconAsset(char:String):String public static function getCharPixelIconAsset(char:String):FlxFrame
{ {
var icon:String = char; var charPath:String = "freeplay/icons/";
switch (icon) // FunkinCrew please dont skin me alive for copying pixelated icon and changing it a tiny bit
switch (char)
{ {
case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf": case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf" | "bf-dark":
icon = "bf"; charPath += "bfpixel";
case "monster-christmas": case "monster-christmas":
icon = "monster"; charPath += "monsterpixel";
case "mom" | "mom-car": case "mom" | "mom-car":
icon = "mommy"; charPath += "mommypixel";
case "pico-blazin" | "pico-playable" | "pico-speaker": case "pico-blazin" | "pico-playable" | "pico-speaker":
icon = "pico"; charPath += "picopixel";
case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen": case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen" | "gf-dark":
icon = "gf"; charPath += "gfpixel";
case "dad": case "dad":
icon = "daddy"; charPath += "dadpixel";
case "darnell-blazin": case "darnell-blazin":
icon = "darnell"; charPath += "darnellpixel";
case "senpai-angry": case "senpai-angry":
icon = "senpai"; charPath += "senpaipixel";
case "spooky-dark": case "spooky-dark":
icon = "spooky"; charPath += "spookypixel";
case "tankman-atlas": case "tankman-atlas":
icon = "tankman"; charPath += "tankmanpixel";
case "pico-christmas" | "pico-dark":
charPath += "picopixel";
default:
charPath += '${char}pixel';
} }
var path = Paths.image("freeplay/icons/" + icon + "pixel"); if (!Assets.exists(Paths.image(charPath)))
if (Assets.exists(path)) return path; {
trace('[WARN] Character ${char} has no freeplay icon.');
return null;
}
// TODO: Hardcode some additional behavior or a fallback. var isAnimated = Assets.exists(Paths.file('images/$charPath.xml'));
return null; var frame:FlxFrame = null;
if (isAnimated)
{
var frames = Paths.getSparrowAtlas(charPath);
var idleFrame:FlxFrame = frames.frames.find(function(frame:FlxFrame):Bool {
return frame.name.startsWith('idle');
});
if (idleFrame == null)
{
trace('[WARN] Character ${char} has no idle in their freeplay icon.');
return null;
}
// so, haxe.ui.backend.AssetsImpl uses the parent width and height, which makes the image go crazy when rendered
// so this is a work around so that it uses the actual width and height
var imageGraphic = flixel.graphics.FlxGraphic.fromFrame(idleFrame);
var imageFrame = flixel.graphics.frames.FlxImageFrame.fromImage(imageGraphic);
frame = imageFrame.frame;
}
else
{
var imageFrame = flixel.graphics.frames.FlxImageFrame.fromImage(Paths.image(charPath));
frame = imageFrame.frame;
}
return frame;
} }
/** /**

View file

@ -5,7 +5,6 @@ import flixel.FlxSprite;
import flixel.math.FlxMath; import flixel.math.FlxMath;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.CharacterData.CharacterDataParser;
import openfl.utils.Assets;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.util.MathUtil; import funkin.util.MathUtil;
@ -33,7 +32,7 @@ class HealthIcon extends FunkinSprite
* The character this icon is representing. * The character this icon is representing.
* Setting this variable will automatically update the graphic. * Setting this variable will automatically update the graphic.
*/ */
public var characterId(default, set):Null<String>; public var characterId(default, set):String = Constants.DEFAULT_HEALTH_ICON;
/** /**
* Whether this health icon should automatically update its state based on the character's health. * Whether this health icon should automatically update its state based on the character's health.
@ -116,7 +115,7 @@ class HealthIcon extends FunkinSprite
*/ */
static final POSITION_OFFSET:Int = 26; static final POSITION_OFFSET:Int = 26;
public function new(char:String = 'bf', playerId:Int = 0) public function new(char:Null<String>, playerId:Int = 0)
{ {
super(0, 0); super(0, 0);
this.playerId = playerId; this.playerId = playerId;
@ -131,7 +130,7 @@ class HealthIcon extends FunkinSprite
snapToTargetSize(); snapToTargetSize();
} }
function set_characterId(value:Null<String>):Null<String> function set_characterId(value:Null<String>):String
{ {
if (value == characterId) return value; if (value == characterId) return value;
@ -412,20 +411,9 @@ class HealthIcon extends FunkinSprite
} }
} }
function correctCharacterId(charId:Null<String>):String function iconExists(charId:String):Bool
{ {
if (charId == null) return Assets.exists(Paths.image('icons/icon-$charId'));
{
return Constants.DEFAULT_HEALTH_ICON;
}
if (!Assets.exists(Paths.image('icons/icon-$charId')))
{
FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
return Constants.DEFAULT_HEALTH_ICON;
}
return charId;
} }
function isNewSpritesheet(charId:String):Bool function isNewSpritesheet(charId:String):Bool
@ -435,11 +423,11 @@ class HealthIcon extends FunkinSprite
function loadCharacter(charId:Null<String>):Void function loadCharacter(charId:Null<String>):Void
{ {
if (charId == null || correctCharacterId(charId) != charId) if (charId == null || !iconExists(charId))
{ {
// This will recursively trigger loadCharacter to be called again. FlxG.log.warn('No icon for character: $charId : using default placeholder face instead!');
characterId = correctCharacterId(charId); characterId = Constants.DEFAULT_HEALTH_ICON;
return; charId = characterId;
} }
isLegacyStyle = !isNewSpritesheet(charId); isLegacyStyle = !isNewSpritesheet(charId);

View file

@ -8,7 +8,6 @@ import funkin.graphics.FunkinSprite;
import funkin.play.PlayState; import funkin.play.PlayState;
import funkin.util.TimerUtil; import funkin.util.TimerUtil;
import funkin.util.EaseUtil; import funkin.util.EaseUtil;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;

View file

@ -11,7 +11,7 @@ import flixel.util.FlxTimer;
import funkin.graphics.video.FlxVideo; import funkin.graphics.video.FlxVideo;
#end #end
#if hxCodec #if hxCodec
import hxcodec.flixel.FlxVideoSprite; import funkin.graphics.video.FunkinVideoSprite;
#end #end
/** /**
@ -26,7 +26,7 @@ class VideoCutscene
static var vid:FlxVideo; static var vid:FlxVideo;
#end #end
#if hxCodec #if hxCodec
static var vid:FlxVideoSprite; static var vid:FunkinVideoSprite;
#end #end
/** /**
@ -138,7 +138,7 @@ class VideoCutscene
static function playVideoNative(filePath:String):Void static function playVideoNative(filePath:String):Void
{ {
// Video displays OVER the FlxState. // Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0); vid = new FunkinVideoSprite(0, 0);
if (vid != null) if (vid != null)
{ {

View file

@ -85,7 +85,8 @@ class StrumlineNote extends FlxSprite
noteStyle.applyStrumlineFrames(this); noteStyle.applyStrumlineFrames(this);
noteStyle.applyStrumlineAnimations(this, this.direction); noteStyle.applyStrumlineAnimations(this, this.direction);
this.setGraphicSize(Std.int(Strumline.STRUMLINE_SIZE * noteStyle.getStrumlineScale())); var scale = noteStyle.getStrumlineScale();
this.scale.set(scale, scale);
this.updateHitbox(); this.updateHitbox();
noteStyle.applyStrumlineOffsets(this); noteStyle.applyStrumlineOffsets(this);

View file

@ -87,6 +87,7 @@ class SustainTrail extends FlxSprite
public var bottomClip:Float = 0.9; public var bottomClip:Float = 0.9;
public var isPixel:Bool; public var isPixel:Bool;
public var noteStyleOffsets:Array<Float>;
var graphicWidth:Float = 0; var graphicWidth:Float = 0;
var graphicHeight:Float = 0; var graphicHeight:Float = 0;
@ -107,6 +108,7 @@ class SustainTrail extends FlxSprite
this.noteDirection = noteDirection; this.noteDirection = noteDirection;
setupHoldNoteGraphic(noteStyle); setupHoldNoteGraphic(noteStyle);
noteStyleOffsets = noteStyle.getHoldNoteOffsets();
indices = new DrawData<Int>(12, true, TRIANGLE_VERTEX_INDICES); indices = new DrawData<Int>(12, true, TRIANGLE_VERTEX_INDICES);
@ -137,7 +139,6 @@ class SustainTrail extends FlxSprite
zoom = 1.0; zoom = 1.0;
zoom *= noteStyle.fetchHoldNoteScale(); zoom *= noteStyle.fetchHoldNoteScale();
zoom *= 0.7;
// CALCULATE SIZE // CALCULATE SIZE
graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2 graphicWidth = graphic.width / 8 * zoom; // amount of notes * 2
@ -202,7 +203,7 @@ class SustainTrail extends FlxSprite
{ {
width = graphicWidth; width = graphicWidth;
height = graphicHeight; height = graphicHeight;
offset.set(0, 0); offset.set(noteStyleOffsets[0], noteStyleOffsets[1]);
origin.set(width * 0.5, height * 0.5); origin.set(width * 0.5, height * 0.5);
} }

View file

@ -93,7 +93,8 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
buildNoteAnimations(target); buildNoteAnimations(target);
// Set the scale. // Set the scale.
target.setGraphicSize(Strumline.STRUMLINE_SIZE * getNoteScale()); var scale = getNoteScale();
target.scale.set(scale, scale);
target.updateHitbox(); target.updateHitbox();
} }
@ -224,6 +225,13 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return data?.scale ?? 1.0; return data?.scale ?? 1.0;
} }
public function getHoldNoteOffsets():Array<Float>
{
var data = _data?.assets?.holdNote;
if (data == null && fallback != null) return fallback.getHoldNoteOffsets();
return data?.offsets ?? [0.0, 0.0];
}
public function applyStrumlineFrames(target:StrumlineNote):Void public function applyStrumlineFrames(target:StrumlineNote):Void
{ {
// TODO: Add support for multi-Sparrow. // TODO: Add support for multi-Sparrow.
@ -304,9 +312,16 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return thx.Arrays.filterNull(result); return thx.Arrays.filterNull(result);
} }
public function getStrumlineOffsets():Array<Float>
{
var data = _data?.assets?.noteStrumline;
if (data == null && fallback != null) return fallback.getStrumlineOffsets();
return data?.offsets ?? [0.0, 0.0];
}
public function applyStrumlineOffsets(target:StrumlineNote):Void public function applyStrumlineOffsets(target:StrumlineNote):Void
{ {
var offsets = _data?.assets?.noteStrumline?.offsets ?? [0.0, 0.0]; var offsets = getStrumlineOffsets();
target.x += offsets[0]; target.x += offsets[0];
target.y += offsets[1]; target.y += offsets[1];
} }
@ -575,7 +590,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
var result = _data.assets.judgementBad?.isPixel; var result = _data.assets.judgementBad?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false; return result ?? false;
case "GO": case "shit":
var result = _data.assets.judgementShit?.isPixel; var result = _data.assets.judgementShit?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating); if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false; return result ?? false;

View file

@ -16,7 +16,6 @@ import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent;
import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.util.SortUtil; import funkin.util.SortUtil;
import openfl.utils.Assets;
/** /**
* This is a data structure managing information about the current song. * This is a data structure managing information about the current song.

View file

@ -464,6 +464,9 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
#end #end
} }
// Set the characters type
character.characterType = charType;
// Add the character to the scene. // Add the character to the scene.
this.add(character); this.add(character);

View file

@ -17,7 +17,7 @@ import thx.semver.Version;
@:nullSafety @:nullSafety
class Save class Save
{ {
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.5"; public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.4";
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x"; public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x";
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility. // We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
@ -34,19 +34,19 @@ class Save
{ {
if (_instance == null) if (_instance == null)
{ {
_instance = new Save(FlxG.save.data); return _instance = load();
} }
return _instance; return _instance;
} }
var data:RawSaveData; var data:RawSaveData;
public static function load():Void public static function load():Save
{ {
trace("[SAVE] Loading save..."); trace("[SAVE] Loading save...");
// Bind save data. // Bind save data.
loadFromSlot(1); return loadFromSlot(1);
} }
/** /**
@ -65,7 +65,9 @@ class Save
public static function getDefault():RawSaveData public static function getDefault():RawSaveData
{ {
return { return {
version: Save.SAVE_DATA_VERSION, // Version number is an abstract(Array) internally.
// This means it copies by reference, so merging save data overides the version number lol.
version: thx.Dynamics.clone(Save.SAVE_DATA_VERSION),
volume: 1.0, volume: 1.0,
mute: false, mute: false,
@ -89,6 +91,7 @@ class Save
options: options:
{ {
// Reasonable defaults. // Reasonable defaults.
framerate: 60,
naughtyness: true, naughtyness: true,
downscroll: false, downscroll: false,
flashingLights: true, flashingLights: true,
@ -433,7 +436,9 @@ class Save
{ {
if (!data.unlocks.charactersSeen.contains(character)) if (!data.unlocks.charactersSeen.contains(character))
{ {
trace('Character seen: ' + character);
data.unlocks.charactersSeen.push(character); data.unlocks.charactersSeen.push(character);
trace('New characters seen list: ' + data.unlocks.charactersSeen);
flush(); flush();
} }
} }
@ -832,7 +837,7 @@ class Save
* If you set slot to `2`, it will load an independe * If you set slot to `2`, it will load an independe
* @param slot * @param slot
*/ */
static function loadFromSlot(slot:Int):Void static function loadFromSlot(slot:Int):Save
{ {
trace("[SAVE] Loading save from slot " + slot + "..."); trace("[SAVE] Loading save from slot " + slot + "...");
@ -850,12 +855,14 @@ class Save
trace('[SAVE] Found legacy save data, converting...'); trace('[SAVE] Found legacy save data, converting...');
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData); var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
FlxG.save.mergeData(gameSave.data, true); FlxG.save.mergeData(gameSave.data, true);
return gameSave;
} }
else else
{ {
trace('[SAVE] No legacy save data found.'); trace('[SAVE] No legacy save data found.');
var gameSave = new Save(); var gameSave = new Save();
FlxG.save.mergeData(gameSave.data, true); FlxG.save.mergeData(gameSave.data, true);
return gameSave;
} }
} }
else else
@ -863,6 +870,8 @@ class Save
trace('[SAVE] Found existing save data.'); trace('[SAVE] Found existing save data.');
var gameSave = SaveDataMigrator.migrate(FlxG.save.data); var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
FlxG.save.mergeData(gameSave.data, true); FlxG.save.mergeData(gameSave.data, true);
return gameSave;
} }
} }
@ -1133,6 +1142,12 @@ typedef SaveScoreTallyData =
*/ */
typedef SaveDataOptions = typedef SaveDataOptions =
{ {
/**
* FPS
* @default `60`
*/
var framerate:Int;
/** /**
* Whether some particularly fowl language is displayed. * Whether some particularly fowl language is displayed.
* @default `true` * @default `true`

View file

@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [2.0.4] - 2024-09-12
Note to self: Only update to 2.1.0 when migration is needed.
### Added
- `unlocks.charactersSeen:Array<String>` to `Save`
- `unlocks.oldChar:Bool` to `Save`
## [2.0.5] - 2024-05-21 ## [2.0.5] - 2024-05-21
### Fixed ### Fixed
- Resolved an issue where HTML5 wouldn't store the semantic version properly, causing the game to fail to load the save. - Resolved an issue where HTML5 wouldn't store the semantic version properly, causing the game to fail to load the save.

View file

@ -37,17 +37,25 @@ class MusicBeatSubState extends FlxSubState implements IEventHandler
return _conductorInUse = value; return _conductorInUse = value;
} }
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
{
super();
this.bgColor = bgColor;
}
var controls(get, never):Controls; var controls(get, never):Controls;
inline function get_controls():Controls inline function get_controls():Controls
return PlayerSettings.player1.controls; return PlayerSettings.player1.controls;
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
{
super();
this.bgColor = bgColor;
initCallbacks();
}
function initCallbacks()
{
subStateOpened.add(onOpenSubStateComplete);
subStateClosed.add(onCloseSubStateComplete);
}
override function create():Void override function create():Void
{ {
super.create(); super.create();

View file

@ -11,6 +11,7 @@ import funkin.modding.IScriptedClass.IBPMSyncedScriptedClass;
import flixel.math.FlxMath; import flixel.math.FlxMath;
import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent;
import funkin.vis.dsp.SpectralAnalyzer; import funkin.vis.dsp.SpectralAnalyzer;
import funkin.data.freeplay.player.PlayerRegistry;
class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
{ {
@ -27,7 +28,8 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
var analyzer:SpectralAnalyzer; var analyzer:SpectralAnalyzer;
var curGF:GFChar = GF; var currentGFPath:Null<String>;
var enableVisualizer:Bool = false;
public function new() public function new()
{ {
@ -97,7 +99,7 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
function drawFFT() function drawFFT()
{ {
if (curGF == NENE) if (enableVisualizer)
{ {
var levels = analyzer.getLevels(); var levels = analyzer.getLevels();
var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame); var frame = anim.curSymbol.timeline.get("VIZ_bars").get(anim.curFrame);
@ -172,28 +174,33 @@ class CharSelectGF extends FlxAtlasSprite implements IBPMSyncedScriptedClass
*/ */
public function switchGF(bf:String):Void public function switchGF(bf:String):Void
{ {
var prevGF:GFChar = curGF; var previousGFPath = currentGFPath;
switch (bf)
{ var bfObj = PlayerRegistry.instance.fetchEntry(bf);
case "pico": var gfData = bfObj?.getCharSelectData()?.gf;
curGF = NENE; currentGFPath = gfData?.assetPath != null ? Paths.animateAtlas(gfData?.assetPath) : null;
case "bf":
curGF = GF;
default:
curGF = GF;
}
// We don't need to update any anims if we didn't change GF // We don't need to update any anims if we didn't change GF
if (prevGF != curGF) trace('currentGFPath(${currentGFPath})');
if (currentGFPath == null)
{ {
loadAtlas(Paths.animateAtlas("charSelect/" + curGF + "Chill")); this.visible = false;
return;
}
else if (previousGFPath != currentGFPath)
{
this.visible = true;
loadAtlas(currentGFPath);
animInInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "In.txt")); enableVisualizer = gfData?.visualizer ?? false;
animOutInfo = FramesJSFLParser.parse(Paths.file("images/charSelect/" + curGF + "AnimInfo/" + curGF + "Out.txt"));
var animInfoPath = Paths.file('images/${gfData?.animInfoPath}');
animInInfo = FramesJSFLParser.parse(animInfoPath + '/In.txt');
animOutInfo = FramesJSFLParser.parse(animInfoPath + '/Out.txt');
} }
playAnimation("idle", true, false, false); playAnimation("idle", true, false, false);
// addFrameCallback(getNextFrameLabel("idle"), () -> playAnimation("idle", true, false, false));
updateHitbox(); updateHitbox();
} }
@ -213,9 +220,3 @@ enum FadeStatus
FADE_OUT; FADE_OUT;
FADE_IN; FADE_IN;
} }
enum abstract GFChar(String) from String to String
{
var GF = "gf";
var NENE = "nene";
}

View file

@ -47,7 +47,6 @@ class CharSelectPlayer extends FlxAtlasSprite implements IBPMSyncedScriptedClass
// //
if (getCurrentAnimation() == "idle") if (getCurrentAnimation() == "idle")
{ {
trace('Player beat hit');
playAnimation("idle", true, false, false); playAnimation("idle", true, false, false);
} }
}; };

View file

@ -71,6 +71,7 @@ class CharSelectSubState extends MusicBeatSubState
var availableChars:Map<Int, String> = new Map<Int, String>(); var availableChars:Map<Int, String> = new Map<Int, String>();
var pressedSelect:Bool = false; var pressedSelect:Bool = false;
var selectTimer:FlxTimer = new FlxTimer(); var selectTimer:FlxTimer = new FlxTimer();
var allowInput:Bool = false;
var selectSound:FunkinSound; var selectSound:FunkinSound;
var unlockSound:FunkinSound; var unlockSound:FunkinSound;
@ -430,6 +431,8 @@ class CharSelectSubState extends MusicBeatSubState
overrideExisting: true, overrideExisting: true,
restartTrack: true, restartTrack: true,
onLoad: function() { onLoad: function() {
allowInput = true;
@:privateAccess @:privateAccess
gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1);
#if desktop #if desktop
@ -573,6 +576,8 @@ class CharSelectSubState extends MusicBeatSubState
overrideExisting: true, overrideExisting: true,
restartTrack: true, restartTrack: true,
onLoad: function() { onLoad: function() {
allowInput = true;
@:privateAccess @:privateAccess
gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1); gfChill.analyzer = new SpectralAnalyzer(FlxG.sound.music._channel.__audioSource, 7, 0.1);
#if desktop #if desktop
@ -642,6 +647,7 @@ class CharSelectSubState extends MusicBeatSubState
function goToFreeplay():Void function goToFreeplay():Void
{ {
allowInput = false;
autoFollow = false; autoFollow = false;
FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); FlxTween.tween(cursor, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
@ -695,7 +701,7 @@ class CharSelectSubState extends MusicBeatSubState
syncAudio(elapsed); syncAudio(elapsed);
if (!pressedSelect) if (allowInput && !pressedSelect)
{ {
if (controls.UI_UP) holdTmrUp += elapsed; if (controls.UI_UP) holdTmrUp += elapsed;
if (controls.UI_UP_R) if (controls.UI_UP_R)
@ -786,10 +792,9 @@ class CharSelectSubState extends MusicBeatSubState
&& availableChars.exists(getCurrentSelected()) && availableChars.exists(getCurrentSelected())
&& Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()])) && Save.instance.charactersSeen.contains(availableChars[getCurrentSelected()]))
{ {
gfChill.visible = true;
curChar = availableChars.get(getCurrentSelected()); curChar = availableChars.get(getCurrentSelected());
if (!pressedSelect && controls.ACCEPT) if (allowInput && !pressedSelect && controls.ACCEPT)
{ {
cursorConfirmed.visible = true; cursorConfirmed.visible = true;
cursorConfirmed.x = cursor.x - 2; cursorConfirmed.x = cursor.x - 2;
@ -817,7 +822,7 @@ class CharSelectSubState extends MusicBeatSubState
}); });
} }
if (pressedSelect && controls.BACK) if (allowInput && pressedSelect && controls.BACK)
{ {
cursorConfirmed.visible = false; cursorConfirmed.visible = false;
grpCursors.visible = true; grpCursors.visible = true;
@ -847,7 +852,7 @@ class CharSelectSubState extends MusicBeatSubState
gfChill.visible = false; gfChill.visible = false;
if (controls.ACCEPT) if (allowInput && controls.ACCEPT)
{ {
cursorDenied.visible = true; cursorDenied.visible = true;
cursorDenied.x = cursor.x - 2; cursorDenied.x = cursor.x - 2;

View file

@ -4,7 +4,7 @@ package funkin.ui.charSelect;
import funkin.graphics.video.FlxVideo; import funkin.graphics.video.FlxVideo;
#end #end
#if hxCodec #if hxCodec
import hxcodec.flixel.FlxVideoSprite; import funkin.graphics.video.FunkinVideoSprite;
#end #end
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import funkin.audio.FunkinSound; import funkin.audio.FunkinSound;
@ -72,12 +72,12 @@ class IntroSubState extends MusicBeatSubState
#end #end
#if hxCodec #if hxCodec
var vid:FlxVideoSprite; var vid:FunkinVideoSprite;
function playVideoNative(filePath:String):Void function playVideoNative(filePath:String):Void
{ {
// Video displays OVER the FlxState. // Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0); vid = new FunkinVideoSprite(0, 0);
vid.scrollFactor.set(); vid.scrollFactor.set();

View file

@ -24,7 +24,6 @@ import haxe.ui.core.Screen;
import haxe.ui.events.UIEvent; import haxe.ui.events.UIEvent;
import haxe.ui.RuntimeComponentBuilder; import haxe.ui.RuntimeComponentBuilder;
import lime.utils.Assets as LimeAssets; import lime.utils.Assets as LimeAssets;
import openfl.Assets;
import openfl.events.Event; import openfl.events.Event;
import openfl.events.IOErrorEvent; import openfl.events.IOErrorEvent;
import openfl.geom.Rectangle; import openfl.geom.Rectangle;
@ -239,6 +238,11 @@ class DebugBoundingState extends FlxState
{ {
movingCharacter = false; movingCharacter = false;
} }
if (FlxG.mouse.justReleased)
{
movingCharacter = false;
}
} }
} }

View file

@ -700,7 +700,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function get_isCursorOverHaxeUI():Bool function get_isCursorOverHaxeUI():Bool
{ {
return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); return Screen.instance.hasSolidComponentUnderPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
} }
/** /**
@ -3840,7 +3840,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Handle scroll anchor // Handle scroll anchor
if (scrollAnchorScreenPos != null) if (scrollAnchorScreenPos != null)
{ {
var currentScreenPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); var currentScreenPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewX);
var distance = currentScreenPos - scrollAnchorScreenPos; var distance = currentScreenPos - scrollAnchorScreenPos;
var verticalDistance = distance.y; var verticalDistance = distance.y;
@ -4121,8 +4121,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
var overlapsRenderedEvents:Bool = FlxG.mouse.overlaps(renderedEvents); var overlapsRenderedEvents:Bool = FlxG.mouse.overlaps(renderedEvents);
// Cursor position relative to the grid. // Cursor position relative to the grid.
var cursorX:Float = FlxG.mouse.screenX - gridTiledSprite.x; var cursorX:Float = FlxG.mouse.viewX - gridTiledSprite.x;
var cursorY:Float = FlxG.mouse.screenY - gridTiledSprite.y; var cursorY:Float = FlxG.mouse.viewY - gridTiledSprite.y;
var overlapsSelectionBorder:Bool = overlapsGrid var overlapsSelectionBorder:Bool = overlapsGrid
&& ((cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2) && ((cursorX % 40) < (GRID_SELECTION_BORDER_WIDTH / 2)
@ -4137,7 +4137,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{ {
if (scrollAnchorScreenPos == null) if (scrollAnchorScreenPos == null)
{ {
scrollAnchorScreenPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); scrollAnchorScreenPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
selectionBoxStartPos = null; selectionBoxStartPos = null;
} }
else else
@ -4159,11 +4159,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
else if (notePreview != null && FlxG.mouse.overlaps(notePreview) && !isCursorOverHaxeUI) else if (notePreview != null && FlxG.mouse.overlaps(notePreview) && !isCursorOverHaxeUI)
{ {
// Clicked note preview // Clicked note preview
notePreviewScrollAreaStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); notePreviewScrollAreaStartPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
} }
else if (!isCursorOverHaxeUI && (!overlapsGrid || overlapsSelectionBorder)) else if (!isCursorOverHaxeUI && (!overlapsGrid || overlapsSelectionBorder))
{ {
selectionBoxStartPos = new FlxPoint(FlxG.mouse.screenX, FlxG.mouse.screenY); selectionBoxStartPos = new FlxPoint(FlxG.mouse.viewX, FlxG.mouse.viewY);
// Drawing selection box. // Drawing selection box.
targetCursorMode = Crosshair; targetCursorMode = Crosshair;
} }
@ -4188,7 +4188,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{ {
// Clicked on the playhead scroll area. // Clicked on the playhead scroll area.
// Move the playhead to the cursor position. // Move the playhead to the cursor position.
this.playheadPositionInPixels = FlxG.mouse.screenY - (GRID_INITIAL_Y_POS); this.playheadPositionInPixels = FlxG.mouse.viewY - (GRID_INITIAL_Y_POS);
moveSongToScrollPosition(); moveSongToScrollPosition();
// Cursor should be a grabby hand. // Cursor should be a grabby hand.
@ -4251,8 +4251,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
else else
{ {
// Minimum of 0. // Minimum of -1.
return 0; return -1;
} }
}); });
@ -4313,27 +4313,27 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Clicking and dragging. // Clicking and dragging.
// Scroll the screen if the mouse is above or below the grid. // Scroll the screen if the mouse is above or below the grid.
if (FlxG.mouse.screenY < MENU_BAR_HEIGHT) if (FlxG.mouse.viewY < MENU_BAR_HEIGHT)
{ {
// Scroll up. // Scroll up.
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY; var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
scrollPositionInPixels -= diff * 0.5; // Too fast! scrollPositionInPixels -= diff * 0.5; // Too fast!
moveSongToScrollPosition(); moveSongToScrollPosition();
} }
else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0)) else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
{ {
// Scroll down. // Scroll down.
var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0); var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
scrollPositionInPixels += diff * 0.5; // Too fast! scrollPositionInPixels += diff * 0.5; // Too fast!
moveSongToScrollPosition(); moveSongToScrollPosition();
} }
// Render the selection box. // Render the selection box.
var selectionRect:FlxRect = new FlxRect(); var selectionRect:FlxRect = new FlxRect();
selectionRect.x = Math.min(FlxG.mouse.screenX, selectionBoxStartPos.x); selectionRect.x = Math.min(FlxG.mouse.viewX, selectionBoxStartPos.x);
selectionRect.y = Math.min(FlxG.mouse.screenY, selectionBoxStartPos.y); selectionRect.y = Math.min(FlxG.mouse.viewY, selectionBoxStartPos.y);
selectionRect.width = Math.abs(FlxG.mouse.screenX - selectionBoxStartPos.x); selectionRect.width = Math.abs(FlxG.mouse.viewX - selectionBoxStartPos.x);
selectionRect.height = Math.abs(FlxG.mouse.screenY - selectionBoxStartPos.y); selectionRect.height = Math.abs(FlxG.mouse.viewY - selectionBoxStartPos.y);
setSelectionBoxBounds(selectionRect); setSelectionBoxBounds(selectionRect);
targetCursorMode = Crosshair; targetCursorMode = Crosshair;
@ -4461,8 +4461,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Player is clicking and holding on note preview to scrub around. // Player is clicking and holding on note preview to scrub around.
targetCursorMode = Grabbing; targetCursorMode = Grabbing;
var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.screenY, (notePreview?.y ?? 0.0), var clickedPosInPixels:Float = FlxMath.remapToRange(FlxG.mouse.viewY, (notePreview?.y ?? 0.0), (notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0),
(notePreview?.y ?? 0.0) + (notePreview?.height ?? 0.0), 0, songLengthInPixels); 0, songLengthInPixels);
scrollPositionInPixels = clickedPosInPixels; scrollPositionInPixels = clickedPosInPixels;
moveSongToScrollPosition(); moveSongToScrollPosition();
@ -4520,17 +4520,17 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
targetCursorMode = Grabbing; targetCursorMode = Grabbing;
// Scroll the screen if the mouse is above or below the grid. // Scroll the screen if the mouse is above or below the grid.
if (FlxG.mouse.screenY < MENU_BAR_HEIGHT) if (FlxG.mouse.viewY < MENU_BAR_HEIGHT)
{ {
// Scroll up. // Scroll up.
var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.screenY; var diff:Float = MENU_BAR_HEIGHT - FlxG.mouse.viewY;
scrollPositionInPixels -= diff * 0.5; // Too fast! scrollPositionInPixels -= diff * 0.5; // Too fast!
moveSongToScrollPosition(); moveSongToScrollPosition();
} }
else if (FlxG.mouse.screenY > (playbarHeadLayout?.y ?? 0.0)) else if (FlxG.mouse.viewY > (playbarHeadLayout?.y ?? 0.0))
{ {
// Scroll down. // Scroll down.
var diff:Float = FlxG.mouse.screenY - (playbarHeadLayout?.y ?? 0.0); var diff:Float = FlxG.mouse.viewY - (playbarHeadLayout?.y ?? 0.0);
scrollPositionInPixels += diff * 0.5; // Too fast! scrollPositionInPixels += diff * 0.5; // Too fast!
moveSongToScrollPosition(); moveSongToScrollPosition();
} }
@ -4811,11 +4811,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Show the context menu connected to the note. // Show the context menu connected to the note.
if (useSingleNoteContextMenu) if (useSingleNoteContextMenu)
{ {
this.openNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedNote.noteData); this.openNoteContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedNote.noteData);
} }
else else
{ {
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY); this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
} }
} }
else else
@ -4835,11 +4835,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|| (isHighlightedEventSelected && currentEventSelection.length == 1); || (isHighlightedEventSelected && currentEventSelection.length == 1);
if (useSingleEventContextMenu) if (useSingleEventContextMenu)
{ {
this.openEventContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedEvent.eventData); this.openEventContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedEvent.eventData);
} }
else else
{ {
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY); this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
} }
} }
else else
@ -4860,11 +4860,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
// Show the context menu connected to the note. // Show the context menu connected to the note.
if (useSingleNoteContextMenu) if (useSingleNoteContextMenu)
{ {
this.openHoldNoteContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY, highlightedHoldNote.noteData); this.openHoldNoteContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY, highlightedHoldNote.noteData);
} }
else else
{ {
this.openSelectionContextMenu(FlxG.mouse.screenX, FlxG.mouse.screenY); this.openSelectionContextMenu(FlxG.mouse.viewX, FlxG.mouse.viewY);
} }
} }
else else
@ -5139,10 +5139,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset; var songPos:Float = Conductor.instance.songPosition + Conductor.instance.instrumentalOffset;
var songPosMilliseconds:String = Std.string(Math.floor(Math.abs(songPos) % 1000)).lpad('0', 2).substr(0, 2);
var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2); var songPosSeconds:String = Std.string(Math.floor((Math.abs(songPos) / 1000) % 60)).lpad('0', 2);
var songPosMinutes:String = Std.string(Math.floor((Math.abs(songPos) / 1000) / 60)).lpad('0', 2); var songPosMinutes:String = Std.string(Math.floor((Math.abs(songPos) / 1000) / 60)).lpad('0', 2);
if (songPos < 0) songPosMinutes = '-' + songPosMinutes; if (songPos < 0) songPosMinutes = '-' + songPosMinutes;
var songPosString:String = '${songPosMinutes}:${songPosSeconds}'; var songPosString:String = '${songPosMinutes}:${songPosSeconds}:${songPosMilliseconds}';
if (playbarSongPos.value != songPosString) playbarSongPos.value = songPosString; if (playbarSongPos.value != songPosString) playbarSongPos.value = songPosString;
@ -5614,7 +5615,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
} }
else else
{ {
trace('Ignoring keybinds for View menu items because we are in live input mode (${currentLiveInputStyle}).'); // trace('Ignoring keybinds for View menu items because we are in live input mode (${currentLiveInputStyle}).');
} }
} }

View file

@ -3,7 +3,6 @@ package funkin.ui.debug.charting.components;
import funkin.data.event.SongEventRegistry; import funkin.data.event.SongEventRegistry;
import flixel.graphics.frames.FlxAtlasFrames; import flixel.graphics.frames.FlxAtlasFrames;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.utils.Assets;
import flixel.FlxObject; import flixel.FlxObject;
import flixel.FlxBasic; import flixel.FlxBasic;
import flixel.FlxSprite; import flixel.FlxSprite;

View file

@ -95,7 +95,7 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
} }
var LIMIT = 6; var LIMIT = 6;
charButton.icon = CharacterDataParser.getCharPixelIconAsset(charId); charButton.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(charId));
charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}'; charButton.text = charData.name.length > LIMIT ? '${charData.name.substr(0, LIMIT)}.' : '${charData.name}';
charButton.onClick = _ -> { charButton.onClick = _ -> {

View file

@ -13,7 +13,6 @@ import funkin.audio.waveform.WaveformSprite;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import haxe.io.Bytes; import haxe.io.Bytes;
import haxe.io.Path; import haxe.io.Path;
import openfl.utils.Assets;
/** /**
* Functions for loading audio for the chart editor. * Functions for loading audio for the chart editor.

View file

@ -221,7 +221,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.opponent); var charDataOpponent:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.opponent);
if (charDataOpponent != null) if (charDataOpponent != null)
{ {
buttonCharacterOpponent.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.opponent); buttonCharacterOpponent.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.opponent));
buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}'; buttonCharacterOpponent.text = charDataOpponent.name.length > LIMIT ? '${charDataOpponent.name.substr(0, LIMIT)}.' : '${charDataOpponent.name}';
} }
else else
@ -233,7 +233,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.girlfriend); var charDataGirlfriend:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.girlfriend);
if (charDataGirlfriend != null) if (charDataGirlfriend != null)
{ {
buttonCharacterGirlfriend.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.girlfriend); buttonCharacterGirlfriend.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.girlfriend));
buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}'; buttonCharacterGirlfriend.text = charDataGirlfriend.name.length > LIMIT ? '${charDataGirlfriend.name.substr(0, LIMIT)}.' : '${charDataGirlfriend.name}';
} }
else else
@ -245,7 +245,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.player); var charDataPlayer:Null<CharacterData> = CharacterDataParser.fetchCharacterData(chartEditorState.currentSongMetadata.playData.characters.player);
if (charDataPlayer != null) if (charDataPlayer != null)
{ {
buttonCharacterPlayer.icon = CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.player); buttonCharacterPlayer.icon = haxe.ui.util.Variant.fromImageData(CharacterDataParser.getCharPixelIconAsset(chartEditorState.currentSongMetadata.playData.characters.player));
buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}'; buttonCharacterPlayer.text = charDataPlayer.name.length > LIMIT ? '${charDataPlayer.name.substr(0, LIMIT)}.' : '${charDataPlayer.name}';
} }
else else

View file

@ -11,7 +11,6 @@ import funkin.data.freeplay.album.AlbumRegistry;
import funkin.util.assets.FlxAnimationUtil; import funkin.util.assets.FlxAnimationUtil;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.util.SortUtil; import funkin.util.SortUtil;
import openfl.utils.Assets;
/** /**
* The graphic for the album roll in the FreeplayState. * The graphic for the album roll in the FreeplayState.

View file

@ -51,7 +51,6 @@ import funkin.ui.transition.LoadingState;
import funkin.ui.transition.StickerSubState; import funkin.ui.transition.StickerSubState;
import funkin.util.MathUtil; import funkin.util.MathUtil;
import funkin.util.SortUtil; import funkin.util.SortUtil;
import lime.utils.Assets;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import funkin.data.freeplay.style.FreeplayStyleRegistry; import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongData.SongMusicData;
@ -586,13 +585,13 @@ class FreeplayState extends MusicBeatSubState
} }
}; };
exitMovers.set([fp, txtCompletion, fnfHighscoreSpr, txtCompletion, clearBoxSprite], exitMovers.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{ {
x: FlxG.width, x: FlxG.width,
speed: 0.3 speed: 0.3
}); });
exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, txtCompletion, clearBoxSprite], exitMoversCharSel.set([fp, txtCompletion, fnfHighscoreSpr, clearBoxSprite],
{ {
y: -270, y: -270,
speed: 0.8, speed: 0.8,
@ -1376,7 +1375,7 @@ class FreeplayState extends MusicBeatSubState
#if FEATURE_DEBUG_FUNCTIONS #if FEATURE_DEBUG_FUNCTIONS
if (FlxG.keys.justPressed.P) if (FlxG.keys.justPressed.P)
{ {
FlxG.switchState(FreeplayState.build( FlxG.switchState(() -> FreeplayState.build(
{ {
{ {
character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico", character: currentCharacterId == "pico" ? Constants.DEFAULT_CHARACTER : "pico",
@ -1777,12 +1776,13 @@ class FreeplayState extends MusicBeatSubState
FlxG.log.warn('WARN: could not find song with id (${daSong.songId})'); FlxG.log.warn('WARN: could not find song with id (${daSong.songId})');
return; return;
} }
var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty) ?? ''; var targetVariation:String = targetSong.getFirstValidVariation(currentDifficulty, currentCharacter) ?? '';
// TODO: This line of code makes me sad, but you can't really fix it without a breaking migration. // TODO: This line of code makes me sad, but you can't really fix it without a breaking migration.
var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION var suffixedDifficulty = (targetVariation != Constants.DEFAULT_VARIATION
&& targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty; && targetVariation != 'erect') ? '$currentDifficulty-${targetVariation}' : currentDifficulty;
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.songId, suffixedDifficulty); var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.songId, suffixedDifficulty);
trace(songScore);
intendedScore = songScore?.score ?? 0; intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes); intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
rememberedDifficulty = suffixedDifficulty; rememberedDifficulty = suffixedDifficulty;
@ -1859,7 +1859,7 @@ class FreeplayState extends MusicBeatSubState
albumRoll.setDifficultyStars(daSong?.difficultyRating); albumRoll.setDifficultyStars(daSong?.difficultyRating);
} }
// Clears the cache of songs, frees up memory, they' ll have to be loaded in later tho function clearDaCache(actualSongTho:String) // Clears the cache of songs to free up memory, they'll have to be loaded in later tho
function clearDaCache(actualSongTho:String):Void function clearDaCache(actualSongTho:String):Void
{ {
for (song in songs) for (song in songs)

View file

@ -18,7 +18,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup;

View file

@ -18,7 +18,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup;
@ -177,20 +176,21 @@ class BoyfriendCard extends BackingCard
} }
var beatFreq:Int = 1; var beatFreq:Int = 1;
var beatFreqList:Array<Int> = [1,2,4,8]; var beatFreqList:Array<Int> = [1, 2, 4, 8];
public override function beatHit():Void { public override function beatHit():Void
{
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms..... // increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)]; beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm / 140)];
if(Conductor.instance.currentBeat % beatFreq != 0) return; if (Conductor.instance.currentBeat % beatFreq != 0) return;
FlxTween.cancelTweensOf(glow); FlxTween.cancelTweensOf(glow);
FlxTween.cancelTweensOf(glowDark); FlxTween.cancelTweensOf(glowDark);
glow.alpha = 0.8; glow.alpha = 0.8;
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut}); FlxTween.tween(glow, {alpha: 0}, 16 / 24, {ease: FlxEase.quartOut});
glowDark.alpha = 0; glowDark.alpha = 0;
FlxTween.tween(glowDark, {alpha: 0.6}, 18/24, {ease: FlxEase.quartOut}); FlxTween.tween(glowDark, {alpha: 0.6}, 18 / 24, {ease: FlxEase.quartOut});
} }
public override function introDone():Void public override function introDone():Void

View file

@ -20,7 +20,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup;
import funkin.graphics.shaders.AdjustColorShader; import funkin.graphics.shaders.AdjustColorShader;

View file

@ -20,7 +20,6 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
import funkin.ui.freeplay.charselect.PlayableCharacter; import funkin.ui.freeplay.charselect.PlayableCharacter;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import lime.utils.Assets;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import flixel.group.FlxSpriteGroup; import flixel.group.FlxSpriteGroup;
import funkin.graphics.shaders.AdjustColorShader; import funkin.graphics.shaders.AdjustColorShader;
@ -233,20 +232,21 @@ class PicoCard extends BackingCard
} }
var beatFreq:Int = 1; var beatFreq:Int = 1;
var beatFreqList:Array<Int> = [1,2,4,8]; var beatFreqList:Array<Int> = [1, 2, 4, 8];
public override function beatHit():Void { public override function beatHit():Void
{
// increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms..... // increases the amount of beats that need to go by to pulse the glow because itd flash like craazy at high bpms.....
beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm/140)]; beatFreq = beatFreqList[Math.floor(Conductor.instance.bpm / 140)];
if(Conductor.instance.currentBeat % beatFreq != 0) return; if (Conductor.instance.currentBeat % beatFreq != 0) return;
FlxTween.cancelTweensOf(glow); FlxTween.cancelTweensOf(glow);
FlxTween.cancelTweensOf(glowDark); FlxTween.cancelTweensOf(glowDark);
glow.alpha = 1; glow.alpha = 1;
FlxTween.tween(glow, {alpha: 0}, 16/24, {ease: FlxEase.quartOut}); FlxTween.tween(glow, {alpha: 0}, 16 / 24, {ease: FlxEase.quartOut});
glowDark.alpha = 0; glowDark.alpha = 0;
FlxTween.tween(glowDark, {alpha: 1}, 18/24, {ease: FlxEase.quartOut}); FlxTween.tween(glowDark, {alpha: 1}, 18 / 24, {ease: FlxEase.quartOut});
} }
public override function introDone():Void public override function introDone():Void

View file

@ -113,7 +113,9 @@ class PlayableCharacter implements IRegistryEntry<PlayerData>
switch (rank) switch (rank)
{ {
case PERFECT | PERFECT_GOLD: case PERFECT_GOLD:
return _data.results.perfectGold;
case PERFECT:
return _data.results.perfect; return _data.results.perfect;
case EXCELLENT: case EXCELLENT:
return _data.results.excellent; return _data.results.excellent;

View file

@ -123,7 +123,7 @@ class MainMenuState extends MusicBeatState
})); }));
}); });
#if CAN_OPEN_LINKS #if FEATURE_OPEN_URL
// In order to prevent popup blockers from triggering, // In order to prevent popup blockers from triggering,
// we need to open the link as an immediate result of a keypress event, // we need to open the link as an immediate result of a keypress event,
// so we can't wait for the flicker animation to complete. // so we can't wait for the flicker animation to complete.
@ -234,7 +234,7 @@ class MainMenuState extends MusicBeatState
camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y); camFollow.setPosition(selected.getGraphicMidpoint().x, selected.getGraphicMidpoint().y);
} }
#if CAN_OPEN_LINKS #if FEATURE_OPEN_URL
function selectDonate() function selectDonate()
{ {
WindowUtil.openURL(Constants.URL_ITCH); WindowUtil.openURL(Constants.URL_ITCH);
@ -358,7 +358,7 @@ class MainMenuState extends MusicBeatState
#if FEATURE_DEBUG_FUNCTIONS #if FEATURE_DEBUG_FUNCTIONS
// Ctrl+Alt+Shift+P = Character Unlock screen // Ctrl+Alt+Shift+P = Character Unlock screen
// Ctrl+Alt+Shift+W = Meet requirements for Pico Unlock // Ctrl+Alt+Shift+W = Meet requirements for Pico Unlock
// Ctrl+Alt+Shift+L = Revoke requirements for Pico Unlock // Ctrl+Alt+Shift+M = Revoke requirements for Pico Unlock
// Ctrl+Alt+Shift+R = Score/Rank conflict test // Ctrl+Alt+Shift+R = Score/Rank conflict test
// Ctrl+Alt+Shift+N = Mark all characters as not seen // Ctrl+Alt+Shift+N = Mark all characters as not seen
// Ctrl+Alt+Shift+E = Dump save data // Ctrl+Alt+Shift+E = Dump save data
@ -371,7 +371,7 @@ class MainMenuState extends MusicBeatState
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W) if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.W)
{ {
FunkinSound.playOnce(Paths.sound('confirmMenu')); FunkinSound.playOnce(Paths.sound('confirmMenu'));
// Give the user a score of 1 point on Weekend 1 story mode. // Give the user a score of 1 point on Weekend 1 story mode (Easy difficulty).
// This makes the level count as cleared and displays the songs in Freeplay. // This makes the level count as cleared and displays the songs in Freeplay.
funkin.save.Save.instance.setLevelScore('weekend1', 'easy', funkin.save.Save.instance.setLevelScore('weekend1', 'easy',
{ {
@ -391,27 +391,30 @@ class MainMenuState extends MusicBeatState
}); });
} }
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.L) if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.M)
{ {
FunkinSound.playOnce(Paths.sound('confirmMenu')); FunkinSound.playOnce(Paths.sound('confirmMenu'));
// Give the user a score of 0 points on Weekend 1 story mode. // Give the user a score of 0 points on Weekend 1 story mode (all difficulties).
// This makes the level count as uncleared and no longer displays the songs in Freeplay. // This makes the level count as uncleared and no longer displays the songs in Freeplay.
funkin.save.Save.instance.setLevelScore('weekend1', 'easy', for (diff in ['easy', 'normal', 'hard'])
{ {
score: 1, funkin.save.Save.instance.setLevelScore('weekend1', diff,
tallies: {
{ score: 0,
sick: 0, tallies:
good: 0, {
bad: 0, sick: 0,
shit: 0, good: 0,
missed: 0, bad: 0,
combo: 0, shit: 0,
maxCombo: 0, missed: 0,
totalNotesHit: 0, combo: 0,
totalNotes: 0, maxCombo: 0,
} totalNotesHit: 0,
}); totalNotes: 0,
}
});
}
} }
if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R) if (FlxG.keys.pressed.CONTROL && FlxG.keys.pressed.ALT && FlxG.keys.pressed.SHIFT && FlxG.keys.justPressed.R)

View file

@ -6,7 +6,6 @@ import flixel.system.FlxAssets;
import flixel.tweens.FlxEase; import flixel.tweens.FlxEase;
import openfl.display.Bitmap; import openfl.display.Bitmap;
import openfl.display.BitmapData; import openfl.display.BitmapData;
import openfl.utils.Assets;
import funkin.util.MathUtil; import funkin.util.MathUtil;
/** /**
@ -80,10 +79,12 @@ class FunkinSoundTray extends FlxSoundTray
y = MathUtil.coolLerp(y, lerpYPos, 0.1); y = MathUtil.coolLerp(y, lerpYPos, 0.1);
alpha = MathUtil.coolLerp(alpha, alphaTarget, 0.25); alpha = MathUtil.coolLerp(alpha, alphaTarget, 0.25);
var shouldHide = (FlxG.sound.muted == false && FlxG.sound.volume > 0);
// Animate sound tray thing // Animate sound tray thing
if (_timer > 0) if (_timer > 0)
{ {
_timer -= (MS / 1000); if (shouldHide) _timer -= (MS / 1000);
alphaTarget = 1; alphaTarget = 1;
} }
else if (y >= -height) else if (y >= -height)
@ -122,7 +123,7 @@ class FunkinSoundTray extends FlxSoundTray
active = true; active = true;
var globalVolume:Int = Math.round(FlxG.sound.logToLinear(FlxG.sound.volume) * 10); var globalVolume:Int = Math.round(FlxG.sound.logToLinear(FlxG.sound.volume) * 10);
if (FlxG.sound.muted) if (FlxG.sound.muted || FlxG.sound.volume == 0)
{ {
globalVolume = 0; globalVolume = 0;
} }

View file

@ -77,6 +77,10 @@ class PreferencesMenu extends Page
createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void { createPrefItemCheckbox('Unlocked Framerate', 'Enable to unlock the framerate', function(value:Bool):Void {
Preferences.unlockedFramerate = value; Preferences.unlockedFramerate = value;
}, Preferences.unlockedFramerate); }, Preferences.unlockedFramerate);
#else
createPrefItemNumber('FPS', 'The maximum framerate that the game targets', function(value:Float) {
Preferences.framerate = Std.int(value);
}, null, Preferences.framerate, 30, 300, 5, 0);
#end #end
} }
@ -87,7 +91,6 @@ class PreferencesMenu extends Page
// Indent the selected item. // Indent the selected item.
items.forEach(function(daItem:TextMenuItem) { items.forEach(function(daItem:TextMenuItem) {
var thyOffset:Int = 0; var thyOffset:Int = 0;
// Initializing thy text width (if thou text present) // Initializing thy text width (if thou text present)
var thyTextWidth:Int = 0; var thyTextWidth:Int = 0;
if (Std.isOfType(daItem, EnumPreferenceItem)) thyTextWidth = cast(daItem, EnumPreferenceItem).lefthandText.getWidth(); if (Std.isOfType(daItem, EnumPreferenceItem)) thyTextWidth = cast(daItem, EnumPreferenceItem).lefthandText.getWidth();

View file

@ -23,7 +23,6 @@ import funkin.ui.MusicBeatState;
import funkin.ui.transition.LoadingState; import funkin.ui.transition.LoadingState;
import funkin.ui.transition.StickerSubState; import funkin.ui.transition.StickerSubState;
import funkin.util.MathUtil; import funkin.util.MathUtil;
import openfl.utils.Assets;
class StoryMenuState extends MusicBeatState class StoryMenuState extends MusicBeatState
{ {

View file

@ -4,7 +4,7 @@ package funkin.ui.title;
import funkin.graphics.video.FlxVideo; import funkin.graphics.video.FlxVideo;
#end #end
#if hxCodec #if hxCodec
import hxcodec.flixel.FlxVideoSprite; import funkin.graphics.video.FunkinVideoSprite;
#end #end
import funkin.ui.MusicBeatState; import funkin.ui.MusicBeatState;
@ -62,12 +62,12 @@ class AttractState extends MusicBeatState
#end #end
#if hxCodec #if hxCodec
var vid:FlxVideoSprite; var vid:FunkinVideoSprite;
function playVideoNative(filePath:String):Void function playVideoNative(filePath:String):Void
{ {
// Video displays OVER the FlxState. // Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0); vid = new FunkinVideoSprite(0, 0);
if (vid != null) if (vid != null)
{ {

View file

@ -273,11 +273,6 @@ class TitleState extends MusicBeatState
} }
#end #end
if (Save.instance.charactersSeen.contains("pico"))
{
Save.instance.charactersSeen.remove("pico");
Save.instance.oldChar = false;
}
Conductor.instance.update(); Conductor.instance.update();
/* if (FlxG.onMobile) /* if (FlxG.onMobile)

View file

@ -3,24 +3,23 @@ package funkin.ui.transition;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.math.FlxMath; import flixel.math.FlxMath;
import flixel.tweens.FlxEase; import flixel.tweens.FlxEase;
import funkin.graphics.FunkinSprite;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
import flixel.util.FlxTimer; import flixel.util.FlxTimer;
import flixel.util.typeLimit.NextState;
import funkin.graphics.FunkinSprite;
import funkin.graphics.shaders.ScreenWipeShader; import funkin.graphics.shaders.ScreenWipeShader;
import funkin.play.PlayState; import funkin.play.PlayState;
import funkin.play.PlayStatePlaylist; import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song.SongDifficulty; import funkin.play.song.Song.SongDifficulty;
import funkin.ui.MusicBeatState; import funkin.ui.MusicBeatState;
import haxe.io.Path; import haxe.io.Path;
import funkin.graphics.FunkinSprite;
import lime.app.Future; import lime.app.Future;
import lime.app.Promise; import lime.app.Promise;
import lime.utils.AssetLibrary; import lime.utils.AssetLibrary;
import lime.utils.AssetManifest; import lime.utils.AssetManifest;
import lime.utils.Assets as LimeAssets; import lime.utils.Assets as LimeAssets;
import openfl.filters.ShaderFilter; import openfl.filters.ShaderFilter;
import openfl.utils.Assets; import openfl.utils.Assets as OpenFLAssets;
import flixel.util.typeLimit.NextState;
class LoadingState extends MusicBeatSubState class LoadingState extends MusicBeatSubState
{ {
@ -98,7 +97,7 @@ class LoadingState extends MusicBeatSubState
function checkLoadSong(path:String):Void function checkLoadSong(path:String):Void
{ {
if (!Assets.cache.hasSound(path)) if (!OpenFLAssets.cache.hasSound(path))
{ {
var library = Assets.getLibrary('songs'); var library = Assets.getLibrary('songs');
var symbolPath = path.split(':').pop(); var symbolPath = path.split(':').pop();
@ -277,7 +276,7 @@ class LoadingState extends MusicBeatSubState
#if NO_PRELOAD_ALL #if NO_PRELOAD_ALL
static function isSoundLoaded(path:String):Bool static function isSoundLoaded(path:String):Bool
{ {
return Assets.cache.hasSound(path); return OpenFLAssets.cache.hasSound(path);
} }
static function isLibraryLoaded(library:String):Bool static function isLibraryLoaded(library:String):Bool
@ -314,7 +313,6 @@ class LoadingState extends MusicBeatSubState
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9')); FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9'));
FunkinSprite.cacheTexture(Paths.image('notes', 'shared')); FunkinSprite.cacheTexture(Paths.image('notes', 'shared'));
FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared'));
FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared')); FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared'));

View file

@ -2,7 +2,6 @@ package funkin.ui.transition;
import flixel.FlxSprite; import flixel.FlxSprite;
import haxe.Json; import haxe.Json;
import lime.utils.Assets;
import funkin.graphics.FunkinSprite; import funkin.graphics.FunkinSprite;
// import flxtyped group // import flxtyped group
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
@ -56,7 +55,7 @@ class StickerSubState extends MusicBeatSubState
// make sure that ONLY plays mp3/ogg files // make sure that ONLY plays mp3/ogg files
// if there's no mp3/ogg file, then it regenerates/reloads the random folder // if there's no mp3/ogg file, then it regenerates/reloads the random folder
var assetsInList = openfl.utils.Assets.list(); var assetsInList = Assets.list();
var soundFilterFunc = function(a:String) { var soundFilterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/'); return a.startsWith('assets/shared/sounds/stickersounds/');
@ -84,7 +83,7 @@ class StickerSubState extends MusicBeatSubState
var filterFunc = function(a:String) { var filterFunc = function(a:String) {
return a.startsWith('assets/shared/sounds/stickersounds/' + soundSelection + '/'); return a.startsWith('assets/shared/sounds/stickersounds/' + soundSelection + '/');
}; };
var assetsInList3 = openfl.utils.Assets.list(); var assetsInList3 = Assets.list();
sounds = assetsInList3.filter(filterFunc); sounds = assetsInList3.filter(filterFunc);
for (i in 0...sounds.length) for (i in 0...sounds.length)
{ {

View file

@ -481,10 +481,6 @@ class Constants
public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true; public static final JUDGEMENT_BAD_COMBO_BREAK:Bool = true;
public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true; public static final JUDGEMENT_SHIT_COMBO_BREAK:Bool = true;
// % Sick
public static final RANK_PERFECT_PLAT_THRESHOLD:Float = 1.0; // % Sick
public static final RANK_PERFECT_GOLD_THRESHOLD:Float = 0.85; // % Sick
// % Hit // % Hit
public static final RANK_PERFECT_THRESHOLD:Float = 1.00; public static final RANK_PERFECT_THRESHOLD:Float = 1.00;
public static final RANK_EXCELLENT_THRESHOLD:Float = 0.90; public static final RANK_EXCELLENT_THRESHOLD:Float = 0.90;

View file

@ -22,7 +22,7 @@ class WindowUtil
*/ */
public static function openURL(targetUrl:String):Void public static function openURL(targetUrl:String):Void
{ {
#if CAN_OPEN_LINKS #if FEATURE_OPEN_URL
#if linux #if linux
Sys.command('/usr/bin/xdg-open $targetUrl &'); Sys.command('/usr/bin/xdg-open $targetUrl &');
#else #else
@ -40,7 +40,7 @@ class WindowUtil
*/ */
public static function openFolder(targetPath:String):Void public static function openFolder(targetPath:String):Void
{ {
#if CAN_OPEN_LINKS #if FEATURE_OPEN_URL
#if windows #if windows
Sys.command('explorer', [targetPath.replace('/', '\\')]); Sys.command('explorer', [targetPath.replace('/', '\\')]);
#elseif mac #elseif mac
@ -59,7 +59,7 @@ class WindowUtil
*/ */
public static function openSelectFile(targetPath:String):Void public static function openSelectFile(targetPath:String):Void
{ {
#if CAN_OPEN_LINKS #if FEATURE_OPEN_URL
#if windows #if windows
Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]); Sys.command('explorer', ['/select,' + targetPath.replace('/', '\\')]);
#elseif mac #elseif mac

View file

@ -23,6 +23,8 @@ class GitCommit
var commitHash:String = process.stdout.readLine(); var commitHash:String = process.stdout.readLine();
var commitHashSplice:String = commitHash.substr(0, 7); var commitHashSplice:String = commitHash.substr(0, 7);
process.close();
trace('Git Commit ID: ${commitHashSplice}'); trace('Git Commit ID: ${commitHashSplice}');
// Generates a string expression // Generates a string expression
@ -52,6 +54,7 @@ class GitCommit
} }
var branchName:String = branchProcess.stdout.readLine(); var branchName:String = branchProcess.stdout.readLine();
branchProcess.close();
trace('Git Branch Name: ${branchName}'); trace('Git Branch Name: ${branchName}');
// Generates a string expression // Generates a string expression
@ -84,6 +87,7 @@ class GitCommit
try try
{ {
output = branchProcess.stdout.readLine(); output = branchProcess.stdout.readLine();
branchProcess.close();
} }
catch (e) catch (e)
{ {