1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-09-08 06:57:44 +00:00

Compare commits

..

32 commits

Author SHA1 Message Date
Jade 1ced5fa98f
Merge c5d73de24a into d0f0974d1e 2025-07-29 06:47:55 -05:00
Abnormal d0f0974d1e erm...soggy!!! compile error fixie 2025-07-27 15:46:26 -05:00
Abnormal 369b936056 i love mergy conflicties 2025-07-27 01:09:57 -05:00
TechnikTil c40e1e3958 fix fullscreen bug and show clear save data if logged out of ng 2025-07-27 01:09:57 -05:00
TechnikTil 0ade24bac4 HOPEFULLY FIX EVERYTHING WRONG (this time it was tested between two computers) 2025-07-27 01:09:57 -05:00
TechnikTil 7370ef3249 hopefully fix everything with load from ng 2025-07-27 01:09:57 -05:00
TechnikTil b2f162a8ae move clear save data to the top, add clear ng save data 2025-07-27 01:09:57 -05:00
TechnikTil 11fa9b4050 rewrite NGSaveSlot.load to be async, dont precache assets, error handling 2025-07-27 01:09:57 -05:00
TechnikTil 89bd7a3f43 make newgrounds functions in Save static and only available if newgrounds feature flag is enabled, make sure base save slot is loaded and is not empty, and recover save data in case newgrounds doesnt wanna play nice 2025-07-27 01:09:57 -05:00
TechnikTil 942fb5efc9 Add Save Data Options, also allow user to load and save with Newgrounds Save Slots. 2025-07-27 01:09:57 -05:00
Lasercar c2eff142bd Bunker infiltrated 2025-07-22 14:45:47 -07:00
Eric 2784fa18c0
Merge pull request #5476 from Trofem/api-version-example-mods-for-0.7.0
[CHORE] Updates example_mods's API version up to 0.7.0
2025-07-22 12:03:19 -04:00
Eric ad07fddf89
Merge pull request #5489 from FunkinCrew/main
Update develop to latest main
2025-07-22 11:59:29 -04:00
Eric 2586154376
Merge pull request #5488 from FunkinCrew/develop-v0.7.3
Update main branch to v0.7.3
2025-07-22 11:59:01 -04:00
trofim.al 4768eedd5b api version 0.7.0
Update example mod's API version so it actually loads. part 2.
2025-07-22 15:04:20 +11:00
charlesisfeline 7c675ecf17 the psych confusion continues! 2025-07-22 10:03:25 +08:00
Hyper_ f1c3e99a11 lasercar................................................................................................................................................................................................. 2025-07-22 07:56:29 +08:00
Hyper_ eefe8927c4 The most deranged line loss of all time 2025-07-21 17:43:55 -05:00
cherry 3ff4f14510 Clear key from correct map 2025-07-21 17:13:42 -05:00
Abnormal 955b0db542 THE REALLY COOL STUTTERING FIX!!!!!!!!!!!! 2025-07-21 15:06:19 -05:00
Eric d2df4f0832
Merge pull request #5440 from FunkinCrew/main
Update develop branch
2025-07-21 13:08:44 -04:00
Eric 351719ab13
Merge pull request #5438 from FunkinCrew/EliteMasterEric-patch-4
Update lime, openfl, flixel repo URLs in hmm.json
2025-07-21 13:05:40 -04:00
Eric 4ecfe9863e
Update lime, openfl, flixel repo URLs in hmm.json 2025-07-21 13:05:15 -04:00
Eric 48e140e5c0
Merge pull request #5432 from FunkinCrew/develop-0.7.2
Release the mobile source code already
2025-07-21 12:17:54 -04:00
Hundrec e6bb965d6b
Merge branch 'main' into develop-0.7.2 2025-07-22 00:13:47 +08:00
Baran b00376c88c 0.7.2 bumping is real 2025-07-21 10:33:13 -05:00
CrusherNotDrip 541bb78da9 Bump to 0.7.1 2025-07-16 21:51:46 -05:00
Hundrec 5f5fff870c Update issue templates for Mobile 2025-07-14 08:25:10 -05:00
CrusherNotDrip 52acd86cdf bump versions to 0.7.0 2025-07-14 07:38:31 -05:00
Eric 29f7aca135
Merge pull request #5183 from Hundrec/patch-13
[DOCS] Fix a link in a Changelog entry
2025-06-06 01:14:53 -04:00
Hundrec 35d4d24731
that's not even the right link
what
2025-06-01 01:50:33 -06:00
Hundrec c0dde5c936
missed one
funkin.assets
2025-06-01 01:46:27 -06:00
78 changed files with 836 additions and 679 deletions

View file

@ -20,13 +20,13 @@ body:
label: Platform label: Platform
description: Which platform are you playing on? description: Which platform are you playing on?
options: options:
- Android
- iOS/iPadOS
- Newgrounds (Web/HTML5) - Newgrounds (Web/HTML5)
- Itch.io (Web/HTML5) - Itch.io (Web/HTML5)
- Itch.io (Downloadable Build) - Windows - Windows (Downloadable Build)
- Itch.io (Downloadable Build) - MacOS - MacOS (Downloadable Build)
- Itch.io (Downloadable Build) - Linux - Linux (Downloadable Build)
- Google Playstore - Android
- App Store - iOS
- Compiled from GitHub Source Code - Compiled from GitHub Source Code
validations: validations:
required: true required: true
@ -43,11 +43,23 @@ body:
- Safari - Safari
- Other (Specify in Description field) - Other (Specify in Description field)
- type: input
attributes:
label: Mobile Device Model
description: (Mobile users only) What mobile device are you playing on?
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
- type: input
attributes:
label: Mobile OS Version
description: (Mobile users only) What version is your Operating System?
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
- type: input - type: input
attributes: attributes:
label: Version label: Version
description: Which version are you playing on? The game version is in the bottom left corner of the main menu. description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
placeholder: ex. 0.6.4 placeholder: ex. 0.7.4
validations: validations:
required: true required: true

View file

@ -6,7 +6,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!" value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Issue Checklist label: Issue Checklist
@ -21,35 +21,25 @@ body:
label: Platform label: Platform
description: Which platform are you playing on? description: Which platform are you playing on?
options: options:
- Android
- iOS/iPadOS
- Newgrounds (Web/HTML5) - Newgrounds (Web/HTML5)
- Itch.io (Web/HTML5) - Itch.io (Web/HTML5)
- Itch.io (Downloadable Build) - Windows - Windows (Downloadable Build)
- Itch.io (Downloadable Build) - MacOS - MacOS (Downloadable Build)
- Itch.io (Downloadable Build) - Linux - Linux (Downloadable Build)
- Compiled from GitHub Source Code - Compiled from GitHub Source Code
validations: validations:
required: true required: true
- type: dropdown
attributes:
label: Browser
description: (Web/HTML5 users only) Which browser are you playing on?
options:
- Google Chrome
- Microsoft Edge
- Firefox
- Opera
- Safari
- Other (Specify in Description field)
- type: input - type: input
attributes: attributes:
label: Version label: Version
description: Which version are you playing on? The game version is in the bottom left corner of the main menu. description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
placeholder: ex. 0.6.4 placeholder: ex. 0.7.4
validations: validations:
required: true required: true
- type: markdown - type: markdown
attributes: attributes:
value: "## Describe the charting issue(s)." value: "## Describe the charting issue(s)."

View file

@ -6,7 +6,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!" value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Issue Checklist label: Issue Checklist
@ -22,6 +22,8 @@ body:
label: Platform label: Platform
description: Which platform are you compiling for? description: Which platform are you compiling for?
options: options:
- Android
- iOS/iPadOS
- Web/HTML5 - Web/HTML5
- Desktop (Windows) - Desktop (Windows)
- Desktop (Mac) - Desktop (Mac)
@ -33,11 +35,11 @@ body:
- type: input - type: input
attributes: attributes:
label: Version label: Version
description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file. description: Which version are you compiling? The game version is in the bottom left corner of the main menu or in the project.hxp file.
placeholder: ex. 0.6.4 placeholder: ex. 0.7.4
validations: validations:
required: true required: true
- type: markdown - type: markdown
attributes: attributes:
value: "## Describe your compiling issue." value: "## Describe your compiling issue."

View file

@ -6,7 +6,7 @@ body:
- type: markdown - type: markdown
attributes: attributes:
value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!" value: "# PLEASE READ THE [CONTRIBUTING GUIDE](https://github.com/FunkinCrew/Funkin/blob/main/docs/CONTRIBUTING.md) BEFORE OPENING ISSUES!"
- type: checkboxes - type: checkboxes
attributes: attributes:
label: Issue Checklist label: Issue Checklist
@ -21,11 +21,13 @@ body:
label: Platform label: Platform
description: Which platform are you playing on? description: Which platform are you playing on?
options: options:
- Android
- iOS/iPadOS
- Newgrounds (Web/HTML5) - Newgrounds (Web/HTML5)
- Itch.io (Web/HTML5) - Itch.io (Web/HTML5)
- Itch.io (Downloadable Build) - Windows - Windows (Downloadable Build)
- Itch.io (Downloadable Build) - MacOS - MacOS (Downloadable Build)
- Itch.io (Downloadable Build) - Linux - Linux (Downloadable Build)
- Compiled from GitHub Source Code - Compiled from GitHub Source Code
validations: validations:
required: true required: true
@ -42,11 +44,23 @@ body:
- Safari - Safari
- Other (Specify in Description field) - Other (Specify in Description field)
- type: input
attributes:
label: Mobile Device Model
description: (Mobile users only) What mobile device are you playing on?
placeholder: ex. iPhone 16, Galaxy S25, iPad 11th Gen
- type: input
attributes:
label: Mobile OS Version
description: (Mobile users only) What version is your Operating System?
placeholder: ex. iOS 18.5, Android 15, iPadOS 18.5
- type: input - type: input
attributes: attributes:
label: Version label: Version
description: Which version are you playing on? The game version is in the bottom left corner of the main menu. description: Which version are you playing on? The game version is in the bottom left corner of the main menu.
placeholder: ex. 0.6.4 placeholder: ex. 0.7.4
validations: validations:
required: true required: true
@ -61,7 +75,7 @@ body:
- type: textarea - type: textarea
attributes: attributes:
label: Description label: Description
description: Include screenshots or videos of the crash happening. Provide as much detail as you can. description: Include screenshots or videos of the crash happening. Provide as much detail as you can.
placeholder: Describe the crash here... placeholder: Describe the crash here...
validations: validations:
required: true required: true
@ -77,7 +91,7 @@ body:
- type: textarea - type: textarea
attributes: attributes:
label: Crash logs label: Crash logs
description: These can be found in the logs folder where Funkin.exe is. description: These can be found in the logs folder where Funkin.exe is, or in your mobile device's file explorer.
placeholder: Upload your logs here... placeholder: Upload your logs here...
validations: validations:
required: true required: true

View file

@ -432,7 +432,7 @@ Select. ([3d3e2bd](https://github.com/FunkinCrew/Funkin/commit/3d3e2bd3786b85814
* @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346) * @JackXson-Real made their first contribution in [#4346](https://github.com/FunkinCrew/Funkin/pull/4346)
* @VioletSnowLeopard made their first contribution in [#4382](https://github.com/FunkinCrew/Funkin/pull/4382) * @VioletSnowLeopard made their first contribution in [#4382](https://github.com/FunkinCrew/Funkin/pull/4382)
* @superpowers04 made their first contribution in [#4729](https://github.com/FunkinCrew/Funkin/pull/4729) * @superpowers04 made their first contribution in [#4729](https://github.com/FunkinCrew/Funkin/pull/4729)
* @ShadzXD made their first contribution in [#62](https://github.com/FunkinCrew/Funkin/pull/4729) * @ShadzXD made their first contribution in [funkin.assets#62](https://github.com/FunkinCrew/funkin.assets/pull/62)

2
art

@ -1 +1 @@
Subproject commit 490e97f4c6e673a52ee4f9af98325b1aa2d0c3fe Subproject commit 67e550dbd22a8ea429eecc8ce078a36f74ea7723

2
assets

@ -1 +1 @@
Subproject commit a8d15febf5e37a4fc7ffeb0d3eff9c4d36457a37 Subproject commit 69c02fa5019f603324a7d2ae362327a1eef9d109

View file

@ -60,7 +60,7 @@ This section provides guidelines to follow when [opening an issue](https://githu
## Requirements ## Requirements
Make sure you're playing: Make sure you're playing:
- the latest version of the game (currently v0.6.4) - the latest version of the game (currently v0.7.4)
- without any mods - without any mods
- on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin) - on [Newgrounds](https://www.newgrounds.com/portal/view/770371) or downloaded from [itch.io](https://ninja-muffin24.itch.io/funkin)

View file

@ -6,7 +6,7 @@
"name": "EliteMasterEric" "name": "EliteMasterEric"
} }
], ],
"api_version": "0.5.0", "api_version": "0.7.0",
"mod_version": "1.0.0", "mod_version": "1.0.0",
"license": "Apache-2.0" "license": "Apache-2.0"
} }

View file

@ -6,7 +6,7 @@
"name": "EliteMasterEric" "name": "EliteMasterEric"
} }
], ],
"api_version": "0.5.0", "api_version": "0.7.0",
"mod_version": "1.0.0", "mod_version": "1.0.0",
"license": "Apache-2.0" "license": "Apache-2.0"
} }

View file

@ -18,28 +18,28 @@
"name": "extension-admob", "name": "extension-admob",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "02334589ff9603a5f483077a44395009644f6274", "ref": "a53d5916bdcb2e48913f94d9ae1d949b049dcdc1",
"url": "https://github.com/FunkinCrew/extension-admob" "url": "https://github.com/FunkinCrew/extension-admob"
}, },
{ {
"name": "extension-androidtools", "name": "extension-androidtools",
"type": "haxelib", "type": "haxelib",
"version": "2.2.2" "version": "2.2.1"
}, },
{ {
"name": "extension-haptics", "name": "extension-haptics",
"type": "haxelib", "type": "haxelib",
"version": "1.0.4" "version": "1.0.3"
}, },
{ {
"name": "extension-iapcore", "name": "extension-iapcore",
"type": "haxelib", "type": "haxelib",
"version": "1.0.4" "version": "1.0.3"
}, },
{ {
"name": "extension-iarcore", "name": "extension-iarcore",
"type": "haxelib", "type": "haxelib",
"version": "1.0.3" "version": "1.0.2"
}, },
{ {
"name": "flixel", "name": "flixel",
@ -59,7 +59,7 @@
"name": "flxanimate", "name": "flxanimate",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "b1faf19885dad06c899cb71ffe07b4e40b8c6d0c", "ref": "39c1572add28869c558b218fffed13df1b64f376",
"url": "https://github.com/FunkinCrew/flxanimate" "url": "https://github.com/FunkinCrew/flxanimate"
}, },
{ {
@ -111,7 +111,7 @@
"name": "hxcpp", "name": "hxcpp",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "5a0dc3f644dc676a4a092b7e6c8edc8be941f024", "ref": "4e24283a047f11bded6affabbc9ec405156e026e",
"url": "https://github.com/FunkinCrew/hxcpp" "url": "https://github.com/FunkinCrew/hxcpp"
}, },
{ {
@ -170,7 +170,7 @@
"name": "lime", "name": "lime",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "e5f8c27124598505917a001588b560244731adfb", "ref": "c750ebf6b48c4bc018abe9855fbae5ffdbc4771a",
"url": "https://github.com/FunkinCrew/lime" "url": "https://github.com/FunkinCrew/lime"
}, },
{ {
@ -219,7 +219,7 @@
"name": "polymod", "name": "polymod",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "d4142dd15a3b57ed4eb149f9f6a2c3ad9935bf7b", "ref": "866f19edbcd872b3358f9a41f2f6a24c71c191d1",
"url": "https://github.com/larsiusprime/polymod" "url": "https://github.com/larsiusprime/polymod"
}, },
{ {

View file

@ -5,7 +5,6 @@ import hxp.*;
import lime.tools.*; import lime.tools.*;
import sys.FileSystem; import sys.FileSystem;
import sys.io.File; import sys.io.File;
import haxe.io.Bytes;
import haxe.io.Path; import haxe.io.Path;
import haxe.ds.Map; import haxe.ds.Map;
@ -331,10 +330,10 @@ class Project extends HXProject
static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS"; static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS";
/** /**
* `-DFEATURE_LAG_ADJUSTMENT` * `-DFEATURE_INPUT_OFFSETS`
* If this flag is enabled, the input offsets menu will be available to configure your audio and visual offsets. * If this flag is enabled, the input offsets menu will be available to configure your audio and visual offsets.
*/ */
static final FEATURE_LAG_ADJUSTMENT:FeatureFlag = "FEATURE_LAG_ADJUSTMENT"; static final FEATURE_INPUT_OFFSETS:FeatureFlag = "FEATURE_INPUT_OFFSETS";
/** /**
* `-DFEATURE_LOG_TRACE` * `-DFEATURE_LOG_TRACE`
@ -501,7 +500,13 @@ class Project extends HXProject
configureHaxelibs(); configureHaxelibs();
configureAssets(); configureAssets();
configureIcons(); configureIcons();
configureASTCTextures();
readASTCExclusion();
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
{
runASTCCompressor();
}
if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this)) if (FEATURE_MOBILE_ADVERTISEMENTS.isEnabled(this))
{ {
@ -824,7 +829,7 @@ class Project extends HXProject
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile())); FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
// Should be true except on web builds (some asset stuff breaks it) // Should be true except on web builds (some asset stuff breaks it)
FEATURE_LAG_ADJUSTMENT.apply(this, !isWeb()); FEATURE_INPUT_OFFSETS.apply(this, !isWeb());
// Should be true except on web and mobile builds. // Should be true except on web and mobile builds.
// Screenshots doesn't work there, and mobile has its own screenshots anyway. // Screenshots doesn't work there, and mobile has its own screenshots anyway.
@ -885,7 +890,7 @@ class Project extends HXProject
if (FEATURE_LOG_TRACE.isDisabled(this)) if (FEATURE_LOG_TRACE.isDisabled(this))
{ {
setHaxedef("no-traces"); addHaxeFlag("--no-traces");
} }
// Disable the built in pause screen when unfocusing the game. // Disable the built in pause screen when unfocusing the game.
@ -1246,7 +1251,27 @@ class Project extends HXProject
if (isAndroid()) if (isAndroid())
{ {
// Adaptive icons // Adaptive icons
adaptiveIcon = new AdaptiveIcon('art/icons/android/', true); // TODO: Add Adapative Icons in Lime
templatePaths.push("templates");
var androidTheme:String = "@style/LimeAppMainTheme";
if (window.fullscreen != null && window.fullscreen)
{
androidTheme += "Fullscreen";
}
final iconXmlChild:Dynamic = {
"android:label": meta.title,
"android:allowBackup": "true",
"android:theme": androidTheme,
"android:hardwareAccelerated": "true",
"android:allowNativeHeapPointerTagging": ANDROID_TARGET_SDK_VERSION >= 30 ? "false" : null,
"android:largeHeap": "true",
"android:icon": "@mipmap/ic_launcher",
"android:roundIcon": "@mipmap/ic_launcher_round"
};
config.set('android.application', iconXmlChild);
} }
else if (isIOS()) else if (isIOS())
{ {
@ -1278,24 +1303,7 @@ class Project extends HXProject
addIcon("art/icons/icon64.png", 64); addIcon("art/icons/icon64.png", 64);
addIcon("art/icons/iconOG.png"); addIcon("art/icons/iconOG.png");
} info('Done configuring icons.');
info('Done configuring icons.');
}
/**
* Configure the astc textures.
*/
function configureASTCTextures()
{
if (command != "display")
{
readASTCExclusion();
if (FEATURE_COMPRESSED_TEXTURES.isEnabled(this))
{
runASTCCompressor();
}
} }
} }
@ -1695,8 +1703,7 @@ class Project extends HXProject
*/ */
public function error(message:String):Void public function error(message:String):Void
{ {
Sys.stderr().write(Bytes.ofString('[ERROR] ${message}')); Log.error('${message}');
Sys.exit(1);
} }
/** /**
@ -1706,7 +1713,7 @@ class Project extends HXProject
{ {
if (command != "display") if (command != "display")
{ {
Sys.println('[INFO] ${message}'); Log.info('[INFO] ${message}');
} }
} }
@ -1733,37 +1740,17 @@ class Project extends HXProject
var env = new Map<String, Dynamic>(); var env = new Map<String, Dynamic>();
for (line in envFile.split('\n')) for (line in envFile.split('\n'))
{ {
if (line.length <= 0 || line.startsWith("#") || shouldExcludeEnvKey(line)) continue; if (line == "" || line.startsWith("#")) continue;
var index:Int = line.indexOf('='); var parts = line.split('=');
if (parts.length != 2) continue;
if (index == -1) continue; env.set(parts[0], parts[1]);
var field:String = line.substr(0, index);
var value:String = line.substr(index + 1);
env.set(field, value);
} }
return env; return env;
} }
private function shouldExcludeEnvKey(key:String):Bool
{
final android:Bool = key.startsWith('ANDROID_');
final ios:Bool = key.startsWith('IOS_');
final mobile:Bool = key.startsWith('MOBILE_') || ios || android;
final web:Bool = key.startsWith('WEB_');
final desktop:Bool = key.startsWith('DESKTOP_');
if (isWeb() && (mobile || desktop)) return true;
if (isDesktop() && (mobile || web)) return true;
if (isAndroid() && (ios || web || desktop)) return true;
if (isIOS() && (android || web || desktop)) return true;
return false;
}
public function readASTCExclusion():Void public function readASTCExclusion():Void
{ {
astcExcludes = File.getContent('./compression-excludes.txt').trim().split('\n'); astcExcludes = File.getContent('./compression-excludes.txt').trim().split('\n');

View file

@ -29,7 +29,7 @@ class Postbuild
var buildTime:Float = roundToTwoDecimals(end - start); var buildTime:Float = roundToTwoDecimals(end - start);
Sys.println('[INFO] Build took: ${buildTime} seconds'); trace('Build took: ${buildTime} seconds');
} }
} }

View file

@ -9,16 +9,28 @@ class Prebuild
{ {
static inline final BUILD_TIME_FILE:String = '.build_time'; static inline final BUILD_TIME_FILE:String = '.build_time';
static final NG_CREDS_PATH:String = './source/funkin/api/newgrounds/NewgroundsCredentials.hx';
static final NG_CREDS_TEMPLATE:String = "package funkin.api.newgrounds;
class NewgroundsCredentials
{
public static final APP_ID:String = #if API_NG_APP_ID haxe.macro.Compiler.getDefine(\"API_NG_APP_ID\") #else 'INSERT APP ID HERE' #end;
public static final ENCRYPTION_KEY:String = #if API_NG_ENC_KEY haxe.macro.Compiler.getDefine(\"API_NG_ENC_KEY\") #else 'INSERT ENCRYPTION KEY HERE' #end;
}";
static function main():Void static function main():Void
{ {
var start:Float = Sys.time(); var start:Float = Sys.time();
Sys.println('[INFO] Performing pre-build tasks...'); trace('[PREBUILD] Performing pre-build tasks...');
saveBuildTime(); saveBuildTime();
buildCredsFile();
var end:Float = Sys.time(); var end:Float = Sys.time();
var duration:Float = end - start; var duration:Float = end - start;
Sys.println('[INFO] Finished pre-build tasks in $duration seconds.'); trace('[PREBUILD] Finished pre-build tasks in $duration seconds.');
} }
static function saveBuildTime():Void static function saveBuildTime():Void
@ -29,4 +41,20 @@ class Prebuild
fo.writeDouble(now); fo.writeDouble(now);
fo.close(); fo.close();
} }
static function buildCredsFile():Void
{
if (sys.FileSystem.exists(NG_CREDS_PATH))
{
trace('[PREBUILD] NewgroundsCredentials.hx already exists, skipping.');
}
else
{
trace('[PREBUILD] Creating NewgroundsCredentials.hx...');
var fileContents:String = NG_CREDS_TEMPLATE;
sys.io.File.saveContent(NG_CREDS_PATH, fileContents);
}
}
} }

View file

@ -1,6 +1,5 @@
package funkin.api.discord; package funkin.api.discord;
import funkin.util.macro.EnvironmentConfigMacro;
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
import hxdiscord_rpc.Discord; import hxdiscord_rpc.Discord;
import hxdiscord_rpc.Types.DiscordButton; import hxdiscord_rpc.Types.DiscordButton;
@ -12,7 +11,7 @@ import sys.thread.Thread;
@:nullSafety @:nullSafety
class DiscordClient class DiscordClient
{ {
static final CLIENT_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("DESKTOP_DISCORD_CLIENT_ID"); static final CLIENT_ID:String = "816168432860790794";
public static var instance(get, never):DiscordClient; public static var instance(get, never):DiscordClient;
static var _instance:Null<DiscordClient> = null; static var _instance:Null<DiscordClient> = null;
@ -41,28 +40,12 @@ class DiscordClient
{ {
trace('[DISCORD] Initializing connection...'); trace('[DISCORD] Initializing connection...');
if (!hasValidCredentials()) // Discord.initialize(CLIENT_ID, handlers, true, null);
{ Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
FlxG.log.warn("Tried to initialize Discord connection, but credentials are invalid!");
return;
}
@:nullSafety(Off)
{
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, "");
}
createDaemon(); createDaemon();
} }
/**
* @returns `false` if the client ID is invalid.
*/
static function hasValidCredentials():Bool
{
return !(CLIENT_ID == null || CLIENT_ID == "" || (CLIENT_ID != null && CLIENT_ID.contains(" ")));
}
var daemon:Null<Thread> = null; var daemon:Null<Thread> = null;
function createDaemon():Void function createDaemon():Void

View file

@ -1,6 +1,5 @@
package funkin.api.newgrounds; package funkin.api.newgrounds;
import funkin.util.macro.EnvironmentConfigMacro;
import funkin.save.Save; import funkin.save.Save;
import funkin.api.newgrounds.Medals.Medal; import funkin.api.newgrounds.Medals.Medal;
#if FEATURE_NEWGROUNDS #if FEATURE_NEWGROUNDS
@ -18,11 +17,7 @@ import io.newgrounds.objects.User;
@:nullSafety @:nullSafety
class NewgroundsClient class NewgroundsClient
{ {
static final APP_ID:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_APP_ID");
static final ENCRYPTION_KEY:Null<String> = EnvironmentConfigMacro.environmentConfig?.get("API_NG_ENC_KEY");
public static var instance(get, never):NewgroundsClient; public static var instance(get, never):NewgroundsClient;
static var _instance:Null<NewgroundsClient> = null; static var _instance:Null<NewgroundsClient> = null;
static function get_instance():NewgroundsClient static function get_instance():NewgroundsClient
@ -42,8 +37,8 @@ class NewgroundsClient
trace('[NEWGROUNDS] Initializing client...'); trace('[NEWGROUNDS] Initializing client...');
#if FEATURE_NEWGROUNDS_DEBUG #if FEATURE_NEWGROUNDS_DEBUG
trace('[NEWGROUNDS] App ID: ${APP_ID}'); trace('[NEWGROUNDS] App ID: ${NewgroundsCredentials.APP_ID}');
trace('[NEWGROUNDS] Encryption Key: ${ENCRYPTION_KEY}'); trace('[NEWGROUNDS] Encryption Key: ${NewgroundsCredentials.ENCRYPTION_KEY}');
#end #end
if (!hasValidCredentials()) if (!hasValidCredentials())
@ -52,12 +47,9 @@ class NewgroundsClient
return; return;
} }
@:nullSafety(Off) var debug = #if FEATURE_NEWGROUNDS_DEBUG true #else false #end;
{ NG.create(NewgroundsCredentials.APP_ID, getSessionId(), debug, onLoginResolved);
NG.create(APP_ID, getSessionId(), #if FEATURE_NEWGROUNDS_DEBUG true #else false #end, onLoginResolved); NG.core.setupEncryption(NewgroundsCredentials.ENCRYPTION_KEY);
NG.core.setupEncryption(ENCRYPTION_KEY);
}
} }
public function init() public function init()
@ -176,12 +168,12 @@ class NewgroundsClient
*/ */
static function hasValidCredentials():Bool static function hasValidCredentials():Bool
{ {
return !(APP_ID == null return !(NewgroundsCredentials.APP_ID == null
|| APP_ID == "" || NewgroundsCredentials.APP_ID == ""
|| (APP_ID != null && APP_ID.contains(" ")) || NewgroundsCredentials.APP_ID.contains(" ")
|| ENCRYPTION_KEY == null || NewgroundsCredentials.ENCRYPTION_KEY == null
|| ENCRYPTION_KEY == "" || NewgroundsCredentials.ENCRYPTION_KEY == ""
|| (ENCRYPTION_KEY != null && ENCRYPTION_KEY.contains(" "))); || NewgroundsCredentials.ENCRYPTION_KEY.contains(" "));
} }
function onLoginResolved(outcome:LoginOutcome):Void function onLoginResolved(outcome:LoginOutcome):Void

View file

@ -1117,23 +1117,6 @@ class SongNoteDataRaw implements ICloneable<SongNoteDataRaw>
return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}' return 'SongNoteData(${this.time}ms, ' + (this.length > 0 ? '[${this.length}ms hold]' : '') + ' ${this.data}'
+ (this.kind != '' ? ' [kind: ${this.kind}])' : ')'); + (this.kind != '' ? ' [kind: ${this.kind}])' : ')');
} }
public function buildTooltip():String
{
if ((this.kind?.length ?? 0) == 0) return "";
var result:String = 'Kind: ${this.kind}';
if (this.params.length == 0) return result;
result += "\nParams:";
for (param in params)
{
result += '\n- ${param.name}: ${param.value}';
}
return result;
}
} }
/** /**

View file

@ -29,7 +29,7 @@ class AdMobUtil
/** /**
* AdMob publisher ID used for the application. * AdMob publisher ID used for the application.
*/ */
static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("MOBILE_GLOBAL_ADMOB_PUBLISHER"); static final ADMOB_PUBLISHER:String = EnvironmentConfigMacro.environmentConfig.get("GLOBAL_ADMOB_PUBLISHER");
/** /**
* Test ad unit IDs for development and testing purposes. * Test ad unit IDs for development and testing purposes.

View file

@ -1,54 +0,0 @@
package funkin.modding;
import haxe.ds.StringMap;
/**
* Temporary persistent data storage for mods to use.
*/
@:nullSafety
class ModStore
{
/**
* All registered stores for this session.
*/
public static final stores:StringMap<Dynamic> = new StringMap<Dynamic>();
/**
* Attempts to register a new store with the given ID and return it.
* If a store with the same ID already exists, that store will be returned instead (discards `data`).
*
* @id The unique ID for this store.
* @data Optional initial data, uses an empty object by default.
* @return The store data at the given ID.
*/
public static function register(id:String, ?data:Dynamic):Dynamic
{
if (stores.exists(id)) return stores.get(id);
stores.set(id, data ??= {});
return data;
}
/**
* Helper function to get a store by ID.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function get(id:String):Null<Dynamic>
{
return stores.get(id);
}
/**
* Helper function to remove a store by ID and return it.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function remove(id:String):Null<Dynamic>
{
var data:Null<Dynamic> = stores.get(id);
stores.remove(id);
return data;
}
}

View file

@ -21,6 +21,7 @@ import funkin.util.MathUtil;
import funkin.effects.RetroCameraFade; import funkin.effects.RetroCameraFade;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import funkin.util.TouchUtil; import funkin.util.TouchUtil;
import openfl.utils.Assets;
#if FEATURE_MOBILE_ADVERTISEMENTS #if FEATURE_MOBILE_ADVERTISEMENTS
import funkin.mobile.util.AdMobUtil; import funkin.mobile.util.AdMobUtil;
#end #end
@ -101,6 +102,18 @@ class GameOverSubState extends MusicBeatSubState
var canInput:Bool = false; var canInput:Bool = false;
var justDied:Bool = true;
var isSpecialAnimation:Bool = false;
var gameOverVibrationPreset:VibrationPreset =
{
period: 0,
duration: Constants.DEFAULT_VIBRATION_DURATION,
amplitude: Constants.MIN_VIBRATION_AMPLITUDE,
sharpness: Constants.DEFAULT_VIBRATION_SHARPNESS
};
public function new(params:GameOverParams) public function new(params:GameOverParams)
{ {
super(); super();
@ -185,8 +198,6 @@ class GameOverSubState extends MusicBeatSubState
addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack); addBackButton(FlxG.width - 230, FlxG.height - 200, FlxColor.WHITE, goBack);
#end #end
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
// Allow input a second later to prevent accidental gameover skips. // Allow input a second later to prevent accidental gameover skips.
new FlxTimer().start(1, function(tmr:FlxTimer) { new FlxTimer().start(1, function(tmr:FlxTimer) {
canInput = true; canInput = true;
@ -319,6 +330,9 @@ class GameOverSubState extends MusicBeatSubState
} }
} }
// Handle vibrations on update.
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
// Start death music before firstDeath gets replaced // Start death music before firstDeath gets replaced
super.update(elapsed); super.update(elapsed);
} }
@ -601,6 +615,108 @@ class GameOverSubState extends MusicBeatSubState
var hasPlayedDeathQuote:Bool = false; var hasPlayedDeathQuote:Bool = false;
/**
* Used for death haptics.
*/
var startedTimerHaptics:Bool = false;
/**
* Unique vibrations for each death animation.
*/
function handleAnimationVibrations():Void
{
if ((parentPlayState?.isMinimalMode ?? true) || boyfriend == null) return;
if (justDied)
{
if (isSpecialAnimation)
{
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION * 5);
trace("It's a special game over animation.");
}
else
{
HapticUtil.vibrate(0, Constants.DEFAULT_VIBRATION_DURATION);
}
justDied = false;
}
if (boyfriend.animation == null) return;
final curFrame:Int = (boyfriend.animation.curAnim != null) ? boyfriend.animation.curAnim.curFrame : -1;
if (boyfriend.characterId.startsWith("bf"))
{
// BF's mic drops.
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 27)
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
// BF's balls pulsating.
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && (curFrame == 0 || curFrame == 18))
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
return;
}
// Pico dies because of Darnell beating him up.
if (boyfriend.characterId == "pico-blazin")
{
if (!startedTimerHaptics)
{
startedTimerHaptics = true;
new FlxTimer().start(0.5, function(tmr:FlxTimer) {
// Pico falls on his knees.
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
new FlxTimer().start(0.6, function(tmr:FlxTimer) {
// Pico falls "asleep". :)
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
});
});
return;
}
}
else if (boyfriend.characterId.startsWith("pico") && boyfriend.characterId != "pico-holding-nene")
{
if (isSpecialAnimation)
{
if (startedTimerHaptics) return;
startedTimerHaptics = true;
// Death by Darnell's can.
new FlxTimer().start(1.85, function(tmr:FlxTimer) {
// Pico falls on his knees.
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
});
}
else
{
// Pico falls on his back.
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && curFrame == 20)
{
HapticUtil.vibrateByPreset(gameOverVibrationPreset);
}
// Blood firework woohoo!!!!
if (boyfriend.getCurrentAnimation().startsWith('deathLoop') && curFrame % 2 == 0)
{
final randomAmplitude:Float = FlxG.random.float(Constants.MIN_VIBRATION_AMPLITUDE / 100, Constants.MIN_VIBRATION_AMPLITUDE);
final randomDuration:Float = FlxG.random.float(Constants.DEFAULT_VIBRATION_DURATION / 10, Constants.DEFAULT_VIBRATION_DURATION);
HapticUtil.vibrate(0, randomDuration, randomAmplitude);
}
}
return;
}
}
public override function destroy():Void public override function destroy():Void
{ {
super.destroy(); super.destroy();

View file

@ -213,11 +213,6 @@ class PauseSubState extends MusicBeatSubState
*/ */
var menuEntryText:FlxTypedSpriteGroup<AtlasText>; var menuEntryText:FlxTypedSpriteGroup<AtlasText>;
/**
* Callback that gets called once substate gets open.
*/
var onPause:Void->Void;
// =============== // ===============
// Audio Variables // Audio Variables
// =============== // ===============
@ -227,11 +222,10 @@ class PauseSubState extends MusicBeatSubState
// Constructor // Constructor
// =============== // ===============
public function new(?params:PauseSubStateParams, ?onPause:Void->Void) public function new(?params:PauseSubStateParams)
{ {
super(); super();
this.currentMode = params?.mode ?? Standard; this.currentMode = params?.mode ?? Standard;
this.onPause = onPause;
} }
// =============== // ===============
@ -250,8 +244,6 @@ class PauseSubState extends MusicBeatSubState
AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT); AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT);
#end #end
if (onPause != null) onPause();
super.create(); super.create();
startPauseMusic(); startPauseMusic();
@ -294,7 +286,6 @@ class PauseSubState extends MusicBeatSubState
hapticTimer.cancel(); hapticTimer.cancel();
hapticTimer = null; hapticTimer = null;
pauseMusic.stop(); pauseMusic.stop();
onPause = null;
} }
// =============== // ===============
@ -453,7 +444,7 @@ class PauseSubState extends MusicBeatSubState
offsetText.y = FlxG.height - (offsetText.height + offsetText.height + 40); offsetText.y = FlxG.height - (offsetText.height + offsetText.height + 40);
offsetTextInfo.y = offsetText.y + offsetText.height + 4; offsetTextInfo.y = offsetText.y + offsetText.height + 4;
#if (!mobile && FEATURE_LAG_ADJUSTMENT) #if !mobile
metadata.add(offsetText); metadata.add(offsetText);
metadata.add(offsetTextInfo); metadata.add(offsetTextInfo);
#end #end

View file

@ -43,7 +43,6 @@ import funkin.play.cutscene.VanillaCutscenes;
import funkin.play.cutscene.VideoCutscene; import funkin.play.cutscene.VideoCutscene;
import funkin.play.notes.NoteDirection; import funkin.play.notes.NoteDirection;
import funkin.play.notes.notekind.NoteKindManager; import funkin.play.notes.notekind.NoteKindManager;
import funkin.play.notes.notekind.NoteKind;
import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteSprite;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import funkin.play.notes.Strumline; import funkin.play.notes.Strumline;
@ -1064,10 +1063,10 @@ class PlayState extends MusicBeatSubState
// If, after updating the conductor, the instrumental has finished, end the song immediately. // If, after updating the conductor, the instrumental has finished, end the song immediately.
// This helps prevent a major bug where the level suddenly loops back to the start or middle. // This helps prevent a major bug where the level suddenly loops back to the start or middle.
// if (Conductor.instance.songPosition >= (FlxG.sound.music.endTime ?? FlxG.sound.music.length)) if (Conductor.instance.songPosition >= (FlxG.sound.music.endTime ?? FlxG.sound.music.length))
// { {
// if (mayPauseGame && !isSongEnd) endSong(skipEndingTransition); if (mayPauseGame && !isSongEnd) endSong(skipEndingTransition);
// } }
} }
var pauseButtonCheck:Bool = false; var pauseButtonCheck:Bool = false;
@ -1235,12 +1234,14 @@ class PlayState extends MusicBeatSubState
switch (mode) switch (mode)
{ {
case Conversation: case Conversation:
currentConversation.pauseMusic();
preparePauseUI(); preparePauseUI();
openPauseSubState(Conversation, FullScreenScaleMode.hasFakeCutouts ? camCutouts : camCutscene, () -> currentConversation.pauseMusic()); openPauseSubState(Conversation, FullScreenScaleMode.hasFakeCutouts ? camCutouts : camCutscene);
case Cutscene: case Cutscene:
VideoCutscene.pauseVideo();
preparePauseUI(); preparePauseUI();
openPauseSubState(Cutscene, FullScreenScaleMode.hasFakeCutouts ? camCutouts : camCutscene, () -> VideoCutscene.pauseVideo()); openPauseSubState(Cutscene, FullScreenScaleMode.hasFakeCutouts ? camCutouts : camCutscene);
default: // also known as standard default: // also known as standard
if (!isInCountdown || isInCutscene) return; if (!isInCountdown || isInCutscene) return;
@ -1298,9 +1299,9 @@ class PlayState extends MusicBeatSubState
#end #end
} }
function openPauseSubState(mode:PauseMode, cam:FlxCamera, ?onPause:Void->Void):Void function openPauseSubState(mode:PauseMode, cam:FlxCamera):Void
{ {
final pauseSubState = new PauseSubState({mode: mode}, onPause); final pauseSubState = new PauseSubState({mode: mode});
FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true; FlxTransitionableState.skipNextTransOut = true;
pauseSubState.camera = cam; pauseSubState.camera = cam;
@ -1501,9 +1502,7 @@ class PlayState extends MusicBeatSubState
musicPausedBySubState = false; musicPausedBySubState = false;
} }
// The logic here is that if this sound doesn't auto-destroy forEachPausedSound((s) -> needsReset ? s.destroy() : s.resume());
// then it's gonna be reused somewhere, so we just stop it instead.
forEachPausedSound(s -> needsReset ? (s.autoDestroy ? s.destroy() : s.stop()) : s.resume());
// Resume camera tweens if we paused any. // Resume camera tweens if we paused any.
for (camTween in cameraTweensPausedBySubState) for (camTween in cameraTweensPausedBySubState)
@ -2247,13 +2246,6 @@ class PlayState extends MusicBeatSubState
var strumTime:Float = songNote.time; var strumTime:Float = songNote.time;
if (strumTime < startTime) continue; // Skip notes that are before the start time. if (strumTime < startTime) continue; // Skip notes that are before the start time.
var scoreable = true;
if (songNote.kind != null)
{
var noteKind:NoteKind = NoteKindManager.getNoteKind(songNote.kind);
if (noteKind != null) scoreable = noteKind.scoreable;
}
var noteData:Int = songNote.getDirection(); var noteData:Int = songNote.getDirection();
var playerNote:Bool = true; var playerNote:Bool = true;
@ -2264,7 +2256,7 @@ class PlayState extends MusicBeatSubState
case 0: case 0:
playerNoteData.push(songNote); playerNoteData.push(songNote);
// increment totalNotes for total possible notes able to be hit by the player // increment totalNotes for total possible notes able to be hit by the player
if (scoreable) Highscore.tallies.totalNotes++; Highscore.tallies.totalNotes++;
case 1: case 1:
opponentNoteData.push(songNote); opponentNoteData.push(songNote);
} }
@ -2823,13 +2815,14 @@ class PlayState extends MusicBeatSubState
} }
// Send the note hit event. // Send the note hit event.
var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, isComboBreak, var event:HitNoteScriptEvent = new HitNoteScriptEvent(note, healthChange, score, daRating, isComboBreak, Highscore.tallies.combo + 1, noteDiff,
note.scoreable ? Highscore.tallies.combo + 1 : Highscore.tallies.combo, noteDiff,
daRating == 'sick'); daRating == 'sick');
dispatchEvent(event); dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat! // Calling event.cancelEvent() skips all the other logic! Neat!
if (event.eventCanceled) return; if (event.eventCanceled) return;
Highscore.tallies.totalNotesHit++;
// Display the hit on the strums // Display the hit on the strums
playerStrumline.hitNote(note, !event.isComboBreak); playerStrumline.hitNote(note, !event.isComboBreak);
if (event.doesNotesplash) playerStrumline.playNoteSplash(note.noteData.getDirection()); if (event.doesNotesplash) playerStrumline.playNoteSplash(note.noteData.getDirection());
@ -2837,12 +2830,8 @@ class PlayState extends MusicBeatSubState
vocals.playerVolume = 1; vocals.playerVolume = 1;
// Display the combo meter and add the calculation to the score. // Display the combo meter and add the calculation to the score.
if (note.scoreable) applyScore(event.score, event.judgement, event.healthChange, event.isComboBreak);
{ popUpScore(event.judgement);
Highscore.tallies.totalNotesHit++;
applyScore(event.score, event.judgement, event.healthChange, event.isComboBreak);
popUpScore(event.judgement);
}
} }
/** /**

View file

@ -527,7 +527,8 @@ class ResultState extends MusicBeatSubState
bgFlash.visible = true; bgFlash.visible = true;
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24); FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
// NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors. // NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors.
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : Scoring.tallyCompletion(params.scoreData.tallies) * 100; var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick + params.scoreData.tallies.good
- params.scoreData.tallies.missed) / params.scoreData.tallies.totalNotes * 100;
clearPercentTarget = Math.floor(clearPercentFloat); clearPercentTarget = Math.floor(clearPercentFloat);
// Prevent off-by-one errors. // Prevent off-by-one errors.
@ -743,7 +744,6 @@ class ResultState extends MusicBeatSubState
super.draw(); super.draw();
songName.clipRect = FlxRect.get(Math.max(0, 520 - songName.x), 0, FlxG.width, songName.height); songName.clipRect = FlxRect.get(Math.max(0, 520 - songName.x), 0, FlxG.width, songName.height);
clearPercentSmall.forEachAlive(spr -> spr.clipRect = FlxRect.get(Math.max(0, 520 - spr.x), 0, FlxG.width, spr.height));
// PROBABLY SHOULD FIX MEMORY FREE OR WHATEVER THE PUT() FUNCTION DOES !!!! FEELS LIKE IT STUTTERS!!! // PROBABLY SHOULD FIX MEMORY FREE OR WHATEVER THE PUT() FUNCTION DOES !!!! FEELS LIKE IT STUTTERS!!!
@ -751,6 +751,105 @@ class ResultState extends MusicBeatSubState
// maskShaderSongName.frameUV = songName.frame.uv; // maskShaderSongName.frameUV = songName.frame.uv;
} }
private function handleAnimationVibrations()
{
for (atlas in characterAtlasAnimations)
{
if (atlas == null || atlas.sprite == null) continue;
switch (rank)
{
case ScoringRank.PERFECT | ScoringRank.PERFECT_GOLD:
switch (playerCharacterId)
{
// Feel the bed fun :freaky:
case "bf":
if (atlas.sprite.anim.curFrame > 87 && atlas.sprite.anim.curFrame % 5 == 0)
{
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
// GF slams into the wall.
if (atlas.sprite.anim.curFrame == 51)
{
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
break;
}
// Pico drop-kicking Nene.
case "pico":
if (atlas.sprite.anim.curFrame == 52)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION * 5, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.GREAT | ScoringRank.EXCELLENT:
switch (playerCharacterId)
{
// Pico explodes the targets with a rocket launcher.
case "pico":
// Pico shoots.
if (atlas.sprite.anim.curFrame == 45)
{
HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
break;
}
// The targets explode.
if (atlas.sprite.anim.curFrame == 50)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD, Constants.DEFAULT_VIBRATION_DURATION, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.GOOD:
switch (playerCharacterId)
{
// Pico shooting the targets.
case "pico":
if (atlas.sprite.anim.curFrame % 2 != 0) continue;
final frames:Array<Array<Int>> = [[40, 50], [80, 90], [140, 157]];
for (i in 0...frames.length)
{
if (atlas.sprite.anim.curFrame < frames[i][0] || atlas.sprite.anim.curFrame > frames[i][1]) continue;
HapticUtil.vibrate(0, 0.01, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
case ScoringRank.SHIT:
switch (playerCharacterId)
{
// BF falling and GF slams on BF with her ass.
case "bf":
if (atlas.sprite.anim.curFrame == 5 || atlas.sprite.anim.curFrame == 90)
{
HapticUtil.vibrate(Constants.DEFAULT_VIBRATION_PERIOD * 2, Constants.DEFAULT_VIBRATION_DURATION * 2, Constants.MAX_VIBRATION_AMPLITUDE);
break;
}
default:
break;
}
}
}
}
override function update(elapsed:Float):Void override function update(elapsed:Float):Void
{ {
maskShaderDifficulty.swagSprX = difficulty.x; maskShaderDifficulty.swagSprX = difficulty.x;
@ -936,6 +1035,8 @@ class ResultState extends MusicBeatSubState
#end #end
} }
if (HapticUtil.hapticsAvailable) handleAnimationVibrations();
super.update(elapsed); super.update(elapsed);
} }

View file

@ -288,22 +288,33 @@ class CharacterDataParser
{ {
var charPath:String = "freeplay/icons/"; var charPath:String = "freeplay/icons/";
final charIDParts:Array<String> = char.split("-"); // FunkinCrew please dont skin me alive for copying pixelated icon and changing it a tiny bit
var iconName:String = ""; switch (char)
for (i in 0...charIDParts.length)
{ {
iconName += charIDParts[i]; case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf" | "bf-dark":
charPath += "bfpixel";
if (Assets.exists(Paths.image(charPath + '${iconName}pixel'))) case "monster-christmas":
{ charPath += "monsterpixel";
charPath += '${iconName}pixel'; case "mom" | "mom-car":
break; charPath += "mommypixel";
} case "pico-blazin" | "pico-playable" | "pico-speaker" | "pico-pixel" | "pico-holding-nene":
else charPath += "picopixel";
{ case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen" | "gf-dark":
if (i < charIDParts.length - 1) iconName += '-'; charPath += "gfpixel";
continue; case "dad":
} charPath += "dadpixel";
case "darnell-blazin":
charPath += "darnellpixel";
case "senpai-angry":
charPath += "senpaipixel";
case "spooky-dark":
charPath += "spookypixel";
case "tankman-atlas" | "tankman-bloody":
charPath += "tankmanpixel";
case "pico-christmas" | "pico-dark":
charPath += "picopixel";
default:
charPath += '${char}pixel';
} }
if (!Assets.exists(Paths.image(charPath))) if (!Assets.exists(Paths.image(charPath)))

View file

@ -10,9 +10,6 @@ class NoteSprite extends FunkinSprite
{ {
static final DIRECTION_COLORS:Array<String> = ['purple', 'blue', 'green', 'red']; static final DIRECTION_COLORS:Array<String> = ['purple', 'blue', 'green', 'red'];
/**
* The hold note sprite for this note.
*/
public var holdNoteSprite:SustainTrail; public var holdNoteSprite:SustainTrail;
var hsvShader:HSVShader; var hsvShader:HSVShader;
@ -98,23 +95,8 @@ class NoteSprite extends FunkinSprite
return this.direction; return this.direction;
} }
/**
* The note data associated with this note sprite.
* This is used to store the strum time, length, and other properties.
*/
public var noteData:SongNoteData; public var noteData:SongNoteData;
/**
* If this note kind is scoreable (i.e., counted towards score and accuracy)
* Only accessible in scripts
* Defaults to true
*/
public var scoreable:Bool = true;
/**
* Whether this note is a hold note.
* This is true if the length is greater than 0.
*/
public var isHoldNote(get, never):Bool; public var isHoldNote(get, never):Bool;
function get_isHoldNote():Bool function get_isHoldNote():Bool

View file

@ -17,7 +17,6 @@ import funkin.play.notes.NoteVibrationsHandler;
import funkin.data.song.SongData.SongNoteData; import funkin.data.song.SongData.SongNoteData;
import funkin.util.SortUtil; import funkin.util.SortUtil;
import funkin.util.GRhythmUtil; import funkin.util.GRhythmUtil;
import funkin.play.notes.notekind.NoteKind;
import funkin.play.notes.notekind.NoteKindManager; import funkin.play.notes.notekind.NoteKindManager;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
#if mobile #if mobile
@ -1104,7 +1103,6 @@ class Strumline extends FlxSpriteGroup
if (noteSprite != null) if (noteSprite != null)
{ {
var noteKind:NoteKind = NoteKindManager.getNoteKind(note.kind);
var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.id) ?? this.noteStyle; var noteKindStyle:NoteStyle = NoteKindManager.getNoteStyle(note.kind, this.noteStyle.id) ?? this.noteStyle;
noteSprite.setupNoteGraphic(noteKindStyle); noteSprite.setupNoteGraphic(noteKindStyle);
@ -1129,7 +1127,6 @@ class Strumline extends FlxSpriteGroup
noteSprite.x -= (noteSprite.width - Strumline.STRUMLINE_SIZE) / 2; // Center it noteSprite.x -= (noteSprite.width - Strumline.STRUMLINE_SIZE) / 2; // Center it
noteSprite.x -= NUDGE; noteSprite.x -= NUDGE;
noteSprite.y = -9999; noteSprite.y = -9999;
if (noteKind != null) noteSprite.scoreable = noteKind.scoreable;
} }
return noteSprite; return noteSprite;

View file

@ -28,13 +28,6 @@ class NoteKind implements INoteScriptedClass
*/ */
public var params:Array<NoteKindParam>; public var params:Array<NoteKindParam>;
/**
* If this note kind is scoreable (ie, counted towards score and accuracy)
* Only accessible in scripts
* Defaults to true
*/
public var scoreable:Bool = true;
public function new(noteKind:String, description:String = "", ?noteStyleId:String, ?params:Array<NoteKindParam>) public function new(noteKind:String, description:String = "", ?noteStyleId:String, ?params:Array<NoteKindParam>)
{ {
this.noteKind = noteKind; this.noteKind = noteKind;

View file

@ -11,21 +11,7 @@ import funkin.play.notes.notekind.NoteKind.NoteKindParam;
class NoteKindManager class NoteKindManager
{ {
/** static var noteKinds:Map<String, NoteKind> = [];
* A map of all note kinds, keyed by their name.
* This is used to retrieve note kinds by their name.
*/
public static var noteKinds:Map<String, NoteKind> = [];
/**
* Retrieve a note kind by its name.
* @param noteKind The name of the note kind.
* @return The note kind, or null if it doesn't exist.
*/
public static function getNoteKind(noteKind:String):Null<NoteKind>
{
return noteKinds.get(noteKind);
}
public static function loadScripts():Void public static function loadScripts():Void
{ {

View file

@ -1,7 +1,6 @@
package funkin.play.scoring; package funkin.play.scoring;
import funkin.save.Save.SaveScoreData; import funkin.save.Save.SaveScoreData;
import funkin.save.Save.SaveScoreTallyData;
/** /**
* Which system to use when scoring and judging notes. * Which system to use when scoring and judging notes.
@ -375,7 +374,8 @@ class Scoring
if (scoreData.tallies.totalNotes == 0) return null; if (scoreData.tallies.totalNotes == 0) return null;
// Perfect (Platinum) is a Sick Full Clear // Perfect (Platinum) is a Sick Full Clear
if (scoreData.tallies.sick == scoreData.tallies.totalNotes) var isPerfectGold = scoreData.tallies.sick == scoreData.tallies.totalNotes;
if (isPerfectGold)
{ {
return ScoringRank.PERFECT_GOLD; return ScoringRank.PERFECT_GOLD;
} }
@ -384,21 +384,21 @@ class Scoring
// Final Grade = (Sick + Good - Miss) / (Total Notes) // Final Grade = (Sick + Good - Miss) / (Total Notes)
var completionAmount:Float = Scoring.tallyCompletion(scoreData.tallies); var grade = (scoreData.tallies.sick + scoreData.tallies.good - scoreData.tallies.missed) / scoreData.tallies.totalNotes;
if (completionAmount == Constants.RANK_PERFECT_THRESHOLD) if (grade == Constants.RANK_PERFECT_THRESHOLD)
{ {
return ScoringRank.PERFECT; return ScoringRank.PERFECT;
} }
else if (completionAmount >= Constants.RANK_EXCELLENT_THRESHOLD) else if (grade >= Constants.RANK_EXCELLENT_THRESHOLD)
{ {
return ScoringRank.EXCELLENT; return ScoringRank.EXCELLENT;
} }
else if (completionAmount >= Constants.RANK_GREAT_THRESHOLD) else if (grade >= Constants.RANK_GREAT_THRESHOLD)
{ {
return ScoringRank.GREAT; return ScoringRank.GREAT;
} }
else if (completionAmount >= Constants.RANK_GOOD_THRESHOLD) else if (grade >= Constants.RANK_GOOD_THRESHOLD)
{ {
return ScoringRank.GOOD; return ScoringRank.GOOD;
} }
@ -407,21 +407,6 @@ class Scoring
return ScoringRank.SHIT; return ScoringRank.SHIT;
} }
} }
/**
* Calculates the "completion" of a song, based on how many GOOD and SICK notes were hit, minus how many were missed
* Top secret funkin crew patented algorithm
* TODO: Could possibly move more of the "tallying" related handling here.
* In FreeplayState we make sure it's clamped between 0 and 1, and we probably always want to assume that?
*
* @param tallies
* @return Float Completion, as a float value between 0 and 1. If `tallies` is `null`, we return 0;
*/
public static function tallyCompletion(?tallies:SaveScoreTallyData):Float
{
if (tallies == null) return 0.0;
return (tallies.sick + tallies.good - tallies.missed) / tallies.totalNotes;
}
} }
enum abstract ScoringRank(String) enum abstract ScoringRank(String)

View file

@ -187,7 +187,6 @@ class Save
theme: ChartEditorTheme.Light, theme: ChartEditorTheme.Light,
playtestStartTime: false, playtestStartTime: false,
downscroll: false, downscroll: false,
showNoteKinds: true,
metronomeVolume: 1.0, metronomeVolume: 1.0,
hitsoundVolumePlayer: 1.0, hitsoundVolumePlayer: 1.0,
hitsoundVolumeOpponent: 1.0, hitsoundVolumeOpponent: 1.0,
@ -359,23 +358,6 @@ class Save
return data.optionsChartEditor.downscroll; return data.optionsChartEditor.downscroll;
} }
public var chartEditorShowNoteKinds(get, set):Bool;
function get_chartEditorShowNoteKinds():Bool
{
if (data.optionsChartEditor.showNoteKinds == null) data.optionsChartEditor.showNoteKinds = true;
return data.optionsChartEditor.showNoteKinds;
}
function set_chartEditorShowNoteKinds(value:Bool):Bool
{
// Set and apply.
data.optionsChartEditor.showNoteKinds = value;
flush();
return data.optionsChartEditor.showNoteKinds;
}
public var chartEditorPlaytestStartTime(get, set):Bool; public var chartEditorPlaytestStartTime(get, set):Bool;
function get_chartEditorPlaytestStartTime():Bool function get_chartEditorPlaytestStartTime():Bool
@ -900,12 +882,14 @@ class Save
return; return;
} }
var newCompletion = (newScoreData.tallies.sick + newScoreData.tallies.good) / newScoreData.tallies.totalNotes;
var previousCompletion = (previousScoreData.tallies.sick + previousScoreData.tallies.good) / previousScoreData.tallies.totalNotes;
// Set the high score and the high rank separately. // Set the high score and the high rank separately.
var newScore:SaveScoreData = var newScore:SaveScoreData =
{ {
score: (previousScoreData.score > newScoreData.score) ? previousScoreData.score : newScoreData.score, score: (previousScoreData.score > newScoreData.score) ? previousScoreData.score : newScoreData.score,
tallies: (previousRank > newRank tallies: (previousRank > newRank || previousCompletion > newCompletion) ? previousScoreData.tallies : newScoreData.tallies
|| Scoring.tallyCompletion(previousScoreData.tallies) > Scoring.tallyCompletion(newScoreData.tallies)) ? previousScoreData.tallies : newScoreData.tallies
}; };
song.set(difficultyId, newScore); song.set(difficultyId, newScore);
@ -1860,12 +1844,6 @@ typedef SaveDataChartEditorOptions =
*/ */
var ?downscroll:Bool; var ?downscroll:Bool;
/**
* Show Note Kind Indicator in the Chart Editor.
* @default `true`
*/
var ?showNoteKinds:Bool;
/** /**
* Metronome volume in the Chart Editor. * Metronome volume in the Chart Editor.
* @default `1.0` * @default `1.0`

View file

@ -21,25 +21,33 @@ class PixelatedIcon extends FlxFilteredSprite
{ {
var charPath:String = "freeplay/icons/"; var charPath:String = "freeplay/icons/";
final charIDParts:Array<String> = char.split("-"); switch (char)
var iconName:String = "";
for (i in 0...charIDParts.length)
{ {
iconName += charIDParts[i]; case "bf-christmas" | "bf-car" | "bf-pixel" | "bf-holding-gf":
charPath += "bfpixel";
if (Assets.exists(Paths.image(charPath + '${iconName}pixel'))) case "monster-christmas":
{ charPath += "monsterpixel";
charPath += '${iconName}pixel'; case "mom" | "mom-car":
break; charPath += "mommypixel";
} case "pico-blazin" | "pico-playable" | "pico-speaker" | "pico-pixel" | "pico-holding-nene":
else charPath += "picopixel";
{ case "gf-christmas" | "gf-car" | "gf-pixel" | "gf-tankmen":
if (i < charIDParts.length - 1) iconName += '-'; charPath += "gfpixel";
continue; case "dad":
} charPath += "dadpixel";
case "darnell-blazin":
charPath += "darnellpixel";
case "senpai-angry":
charPath += "senpaipixel";
case "spooky-dark":
charPath += "spookypixel";
case "tankman-atlas" | "tankman-bloody":
charPath += "tankmanpixel";
default:
charPath += '${char}pixel';
} }
if (!Assets.exists(Paths.image(charPath))) if (!openfl.utils.Assets.exists(Paths.image(charPath)))
{ {
trace('[WARN] Character ${char} has no freeplay icon.'); trace('[WARN] Character ${char} has no freeplay icon.');
this.visible = false; this.visible = false;
@ -50,7 +58,7 @@ class PixelatedIcon extends FlxFilteredSprite
this.visible = true; this.visible = true;
} }
var isAnimated = Assets.exists(Paths.file('images/$charPath.xml')); var isAnimated = openfl.utils.Assets.exists(Paths.file('images/$charPath.xml'));
if (isAnimated) if (isAnimated)
{ {

View file

@ -271,12 +271,6 @@ class CharSelectSubState extends MusicBeatSubState
nametag.midpointX += cutoutSize; nametag.midpointX += cutoutSize;
add(nametag); add(nametag);
@:privateAccess
{
nametag.midpointY += 200;
FlxTween.tween(nametag, {midpointY: nametag.midpointY - 200}, 1, {ease: FlxEase.expoOut});
}
nametag.scrollFactor.set(); nametag.scrollFactor.set();
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSprite, ["x", "y", "alpha", "scale", "blend"])); FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSprite, ["x", "y", "alpha", "scale", "blend"]));
@ -745,7 +739,6 @@ class CharSelectSubState extends MusicBeatSubState
FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut}); FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(nametag, {y: nametag.y + 80}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn}); FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn});

View file

@ -15,18 +15,17 @@ class CreditsDataHandler
static final CREDITS_DATA_PATH:String = "assets/data/credits.json"; static final CREDITS_DATA_PATH:String = "assets/data/credits.json";
#end #end
#if macro
public static function debugPrint(data:Null<CreditsData>):Void public static function debugPrint(data:Null<CreditsData>):Void
{ {
if (data == null) if (data == null)
{ {
Sys.println('[INFO] CreditsData(NULL)'); trace('CreditsData(NULL)');
return; return;
} }
if (data.entries == null || data.entries.length == 0) if (data.entries == null || data.entries.length == 0)
{ {
Sys.println('[INFO] CreditsData(EMPTY)'); trace('CreditsData(EMPTY)');
return; return;
} }
@ -37,9 +36,8 @@ class CreditsDataHandler
lineCount += entry?.body?.length ?? 0; lineCount += entry?.body?.length ?? 0;
} }
Sys.println('[INFO] CreditsData($entryCount entries containing $lineCount lines)'); trace('CreditsData($entryCount entries containing $lineCount lines)');
} }
#end
/** /**
* If for some reason the full credits won't load, * If for some reason the full credits won't load,

View file

@ -10,7 +10,7 @@ class CreditsDataMacro
public static macro function loadCreditsData():haxe.macro.Expr.ExprOf<CreditsData> public static macro function loadCreditsData():haxe.macro.Expr.ExprOf<CreditsData>
{ {
#if !display #if !display
Sys.println('[INFO] Hardcoding credits data...'); trace('Hardcoding credits data...');
var json = CreditsDataMacro.fetchJSON(); var json = CreditsDataMacro.fetchJSON();
if (json == null) if (json == null)

View file

@ -4,7 +4,6 @@ import flixel.math.FlxPoint;
import flixel.FlxObject; import flixel.FlxObject;
import flixel.FlxSprite; import flixel.FlxSprite;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import funkin.ui.FullScreenScaleMode;
import funkin.audio.FunkinSound; import funkin.audio.FunkinSound;
import funkin.ui.TextMenuList; import funkin.ui.TextMenuList;
import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.debug.charting.ChartEditorState;
@ -38,7 +37,7 @@ class DebugMenuSubState extends MusicBeatSubState
// Create the green background. // Create the green background.
var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat')); var menuBG = new FlxSprite().loadGraphic(Paths.image('menuDesat'));
menuBG.color = 0xFF4CAF50; menuBG.color = 0xFF4CAF50;
menuBG.setGraphicSize(Std.int(menuBG.width * 1.1 * FullScreenScaleMode.wideScale.x)); menuBG.setGraphicSize(Std.int(menuBG.width * 1.1));
menuBG.updateHitbox(); menuBG.updateHitbox();
menuBG.screenCenter(); menuBG.screenCenter();
menuBG.scrollFactor.set(0, 0); menuBG.scrollFactor.set(0, 0);

View file

@ -100,7 +100,6 @@ class DebugBoundingState extends FlxState
offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown); offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown);
offsetEditorDialog.cameras = [hudCam]; offsetEditorDialog.cameras = [hudCam];
offsetEditorDialog.closable = false;
add(offsetEditorDialog); add(offsetEditorDialog);
offsetEditorDialog.showDialog(false); offsetEditorDialog.showDialog(false);

View file

@ -94,9 +94,6 @@ import haxe.ui.components.Button;
import haxe.ui.components.DropDown; import haxe.ui.components.DropDown;
import haxe.ui.components.Label; import haxe.ui.components.Label;
import haxe.ui.components.Slider; import haxe.ui.components.Slider;
import haxe.ui.containers.dialogs.Dialogs;
import haxe.ui.containers.dialogs.Dialog.DialogButton;
import haxe.ui.containers.dialogs.MessageBox.MessageBoxType;
import haxe.ui.containers.dialogs.CollapsibleDialog; import haxe.ui.containers.dialogs.CollapsibleDialog;
import haxe.ui.containers.menus.Menu; import haxe.ui.containers.menus.Menu;
import haxe.ui.containers.menus.MenuBar; import haxe.ui.containers.menus.MenuBar;
@ -634,11 +631,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
return isViewDownscroll; return isViewDownscroll;
} }
/**
* Whether to show an indicator if a note is of a non-default kind.
*/
var showNoteKindIndicators:Bool = false;
/** /**
* The current theme used by the editor. * The current theme used by the editor.
* Dictates the appearance of many UI elements. * Dictates the appearance of many UI elements.
@ -1863,11 +1855,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
*/ */
var menubarItemDownscroll:MenuCheckBox; var menubarItemDownscroll:MenuCheckBox;
/**
* The `View -> Note Kind Indicator` menu item.
*/
var menubarItemViewIndicators:MenuCheckBox;
/** /**
* The `View -> Increase Difficulty` menu item. * The `View -> Increase Difficulty` menu item.
*/ */
@ -2371,7 +2358,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
noteSnapQuantIndex = save.chartEditorNoteQuant; noteSnapQuantIndex = save.chartEditorNoteQuant;
currentLiveInputStyle = save.chartEditorLiveInputStyle; currentLiveInputStyle = save.chartEditorLiveInputStyle;
isViewDownscroll = save.chartEditorDownscroll; isViewDownscroll = save.chartEditorDownscroll;
showNoteKindIndicators = save.chartEditorShowNoteKinds;
playtestStartTime = save.chartEditorPlaytestStartTime; playtestStartTime = save.chartEditorPlaytestStartTime;
currentTheme = save.chartEditorTheme; currentTheme = save.chartEditorTheme;
metronomeVolume = save.chartEditorMetronomeVolume; metronomeVolume = save.chartEditorMetronomeVolume;
@ -2401,7 +2387,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
save.chartEditorNoteQuant = noteSnapQuantIndex; save.chartEditorNoteQuant = noteSnapQuantIndex;
save.chartEditorLiveInputStyle = currentLiveInputStyle; save.chartEditorLiveInputStyle = currentLiveInputStyle;
save.chartEditorDownscroll = isViewDownscroll; save.chartEditorDownscroll = isViewDownscroll;
save.chartEditorShowNoteKinds = showNoteKindIndicators;
save.chartEditorPlaytestStartTime = playtestStartTime; save.chartEditorPlaytestStartTime = playtestStartTime;
save.chartEditorTheme = currentTheme; save.chartEditorTheme = currentTheme;
save.chartEditorMetronomeVolume = metronomeVolume; save.chartEditorMetronomeVolume = metronomeVolume;
@ -2534,7 +2519,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
add(gridTiledSprite); add(gridTiledSprite);
gridTiledSprite.zIndex = 10; gridTiledSprite.zIndex = 10;
gridGhostNote = new ChartEditorNoteSprite(this, true); gridGhostNote = new ChartEditorNoteSprite(this);
gridGhostNote.alpha = 0.6; gridGhostNote.alpha = 0.6;
gridGhostNote.noteData = new SongNoteData(0, 0, 0, "", []); gridGhostNote.noteData = new SongNoteData(0, 0, 0, "", []);
gridGhostNote.visible = false; gridGhostNote.visible = false;
@ -3104,9 +3089,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
menubarItemDownscroll.onClick = event -> isViewDownscroll = event.value; menubarItemDownscroll.onClick = event -> isViewDownscroll = event.value;
menubarItemDownscroll.selected = isViewDownscroll; menubarItemDownscroll.selected = isViewDownscroll;
menubarItemViewIndicators.onClick = event -> showNoteKindIndicators = menubarItemViewIndicators.selected;
menubarItemViewIndicators.selected = showNoteKindIndicators;
menubarItemDifficultyUp.onClick = _ -> incrementDifficulty(1); menubarItemDifficultyUp.onClick = _ -> incrementDifficulty(1);
menubarItemDifficultyDown.onClick = _ -> incrementDifficulty(-1); menubarItemDifficultyDown.onClick = _ -> incrementDifficulty(-1);
@ -3939,9 +3921,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
selectionSquare.width = selectionSquare.height = GRID_SIZE; selectionSquare.width = selectionSquare.height = GRID_SIZE;
selectionSquare.color = FlxColor.RED; selectionSquare.color = FlxColor.RED;
} }
// Additional cleanup on notes.
if (noteTooltipsDirty) noteSprite.updateTooltipText();
} }
for (eventSprite in renderedEvents.members) for (eventSprite in renderedEvents.members)
@ -5620,18 +5599,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
@:nullSafety(Off) @:nullSafety(Off)
function quitChartEditor():Void function quitChartEditor():Void
{ {
if (saveDataDirty) { autoSave();
Dialogs.messageBox("You are about to leave the editor without saving.\n\nAre you sure?", "Leave Editor", MessageBoxType.TYPE_YESNO, true, function(button:DialogButton) {
if (button == DialogButton.YES)
{
autoSave();
quitChartEditor();
}
});
return;
}
stopWelcomeMusic(); stopWelcomeMusic();
// TODO: PR Flixel to make onComplete nullable. // TODO: PR Flixel to make onComplete nullable.
if (audioInstTrack != null) audioInstTrack.onComplete = null; if (audioInstTrack != null) audioInstTrack.onComplete = null;
@ -6328,8 +6296,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{ {
currentScrollEase = Math.max(0, targetScrollPosition); currentScrollEase = Math.max(0, targetScrollPosition);
currentScrollEase = Math.min(currentScrollEase, songLengthInPixels); currentScrollEase = Math.min(currentScrollEase, songLengthInPixels);
scrollPositionInPixels = MathUtil.snap(MathUtil.smoothLerpPrecision(scrollPositionInPixels, currentScrollEase, FlxG.elapsed, SCROLL_EASE_DURATION, scrollPositionInPixels = MathUtil.snap(MathUtil.smoothLerpPrecision(scrollPositionInPixels, currentScrollEase, FlxG.elapsed, SCROLL_EASE_DURATION, 1 / 1000), currentScrollEase, 1 / 1000);
1 / 1000), currentScrollEase, 1 / 1000);
moveSongToScrollPosition(); moveSongToScrollPosition();
} }

View file

@ -1,7 +1,5 @@
package funkin.ui.debug.charting.components; package funkin.ui.debug.charting.components;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import flixel.FlxObject; import flixel.FlxObject;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.graphics.frames.FlxFramesCollection; import flixel.graphics.frames.FlxFramesCollection;
@ -12,9 +10,6 @@ import funkin.data.song.SongData.SongNoteData;
import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import funkin.play.notes.NoteDirection; import funkin.play.notes.NoteDirection;
import haxe.ui.tooltips.ToolTipRegionOptions;
import funkin.util.HaxeUIUtil;
import haxe.ui.tooltips.ToolTipManager;
/** /**
* A sprite that can be used to display a note in a chart. * A sprite that can be used to display a note in a chart.
@ -68,21 +63,11 @@ class ChartEditorNoteSprite extends FlxSprite
return overrideData; return overrideData;
} }
public var isGhost:Bool = false; public function new(parent:ChartEditorState)
public var tooltip:ToolTipRegionOptions;
/**
* An indicator if the note is a note kind different than Default ("").
*/
public var kindIndicator:FlxText = new FlxText(5, 5, 100, '*', 16);
public function new(parent:ChartEditorState, isGhost:Bool = false)
{ {
super(); super();
this.parentState = parent; this.parentState = parent;
this.isGhost = isGhost;
this.tooltip = HaxeUIUtil.buildTooltip('N/A');
var entries:Array<String> = NoteStyleRegistry.instance.listEntryIds(); var entries:Array<String> = NoteStyleRegistry.instance.listEntryIds();
@ -104,8 +89,6 @@ class ChartEditorNoteSprite extends FlxSprite
{ {
addNoteStyleAnimations(fetchNoteStyle(entry)); addNoteStyleAnimations(fetchNoteStyle(entry));
} }
kindIndicator.setFormat("VCR OSD Mono", 24, FlxColor.YELLOW, LEFT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK);
} }
static var noteFrameCollection:Null<FlxFramesCollection> = null; static var noteFrameCollection:Null<FlxFramesCollection> = null;
@ -173,7 +156,6 @@ class ChartEditorNoteSprite extends FlxSprite
if (this.noteData == null) if (this.noteData == null)
{ {
this.kill(); this.kill();
updateTooltipPosition();
return this.noteData; return this.noteData;
} }
@ -185,7 +167,7 @@ class ChartEditorNoteSprite extends FlxSprite
// Update the position to match the note data. // Update the position to match the note data.
updateNotePosition(); updateNotePosition();
updateTooltipText();
return this.noteData; return this.noteData;
} }
@ -212,50 +194,6 @@ class ChartEditorNoteSprite extends FlxSprite
this.x += origin.x; this.x += origin.x;
this.y += origin.y; this.y += origin.y;
} }
this.updateTooltipPosition();
}
public function updateTooltipText():Void
{
if (this.noteData == null) return;
if (this.isGhost) return;
this.tooltip.tipData = {text: this.noteData.buildTooltip()};
}
public function updateTooltipPosition():Void
{
// No tooltip for ghost sprites.
if (this.isGhost) return;
if (this.noteData == null || (this.tooltip.tipData?.text ?? "").length == 0)
{
// Disable the tooltip.
ToolTipManager.instance.unregisterTooltipRegion(this.tooltip);
}
else
{
// Update the position.
this.tooltip.left = this.x;
this.tooltip.top = this.y;
this.tooltip.width = this.width;
this.tooltip.height = this.height;
// Enable the tooltip.
ToolTipManager.instance.registerTooltipRegion(this.tooltip);
}
}
override public function draw()
{
super.draw();
if (!parentState.showNoteKindIndicators) return;
if ((this.noteData?.kind ?? "").length == 0) return; // Do not render the note kind indicator if the note kind is default.
kindIndicator.x = this.x;
kindIndicator.y = this.y;
kindIndicator.draw();
} }
function get_noteStyle():Null<String> function get_noteStyle():Null<String>

View file

@ -192,7 +192,7 @@ class AssetDataHandler
for (daFrame in obj.frames.frames) for (daFrame in obj.frames.frames)
{ {
xml += ' <SubTexture name="${daFrame.name}" x="${daFrame.frame.x}" y="${daFrame.frame.y}" width="${daFrame.frame.width}" height="${daFrame.frame.height}" frameX="${- daFrame.offset.x}" frameY="${- daFrame.offset.y}" frameWidth="${daFrame.sourceSize.x}" frameHeight="${daFrame.sourceSize.y}" flipX="${daFrame.flipX}" flipY="${daFrame.flipY}" rotated="${daFrame.angle == -90}"/>\n'; xml += ' <SubTexture name="${daFrame.name}" x="${daFrame.frame.x}" y="${daFrame.frame.y}" width="${daFrame.frame.width}" height="${daFrame.frame.height}" frameX="${- daFrame.offset.x}" frameY="${- daFrame.offset.y}" frameWidth="${daFrame.sourceSize.x}" frameHeight="${daFrame.sourceSize.y}" flipX="${daFrame.flipX}" flipY="${daFrame.flipY}"/>\n';
} }
xml += "</TextureAtlas>"; xml += "</TextureAtlas>";

View file

@ -81,6 +81,8 @@ class FreeplayDJ extends FlxAtlasSprite
public override function update(elapsed:Float):Void public override function update(elapsed:Float):Void
{ {
super.update(elapsed);
switch (currentState) switch (currentState)
{ {
case Intro: case Intro:
@ -183,8 +185,6 @@ class FreeplayDJ extends FlxAtlasSprite
default: default:
// I shit myself. // I shit myself.
} }
super.update(elapsed);
} }
function onFinishAnim(name:String):Void function onFinishAnim(name:String):Void

View file

@ -31,7 +31,6 @@ import funkin.input.Controls;
import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher; import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.PlayStatePlaylist; import funkin.play.PlayStatePlaylist;
import funkin.play.scoring.Scoring;
import funkin.play.scoring.Scoring.ScoringRank; import funkin.play.scoring.Scoring.ScoringRank;
import funkin.play.song.Song; import funkin.play.song.Song;
import funkin.save.Save; import funkin.save.Save;
@ -120,11 +119,6 @@ class FreeplayState extends MusicBeatSubState
*/ */
public static final SONGS_POS_MULTI:Float = 0.75; public static final SONGS_POS_MULTI:Float = 0.75;
/**
* For positioning the difficulty dots.
*/
public static final DEFAULT_DOTS_GROUP_POS:Array<Int> = [260, 170];
var songs:Array<Null<FreeplaySongData>> = []; var songs:Array<Null<FreeplaySongData>> = [];
var curSelected:Int = 0; var curSelected:Int = 0;
@ -175,6 +169,17 @@ class FreeplayState extends MusicBeatSubState
return grpCapsules.members[curSelected]; return grpCapsules.members[curSelected];
} }
var coolColors:Array<Int> = [
0xFF9271FD,
0xFF9271FD,
0xFF223344,
0xFF941653,
0xFFFC96D7,
0xFFA0D1FF,
0xFFFF78BF,
0xFFF6B604
];
var grpCapsules:FlxTypedGroup<SongMenuItem>; var grpCapsules:FlxTypedGroup<SongMenuItem>;
var dj:Null<FreeplayDJ> = null; var dj:Null<FreeplayDJ> = null;
@ -310,7 +315,7 @@ class FreeplayState extends MusicBeatSubState
grpCapsules = new FlxTypedGroup<SongMenuItem>(); grpCapsules = new FlxTypedGroup<SongMenuItem>();
grpDifficulties = new FlxTypedSpriteGroup<DifficultySprite>(-300, 80); grpDifficulties = new FlxTypedSpriteGroup<DifficultySprite>(-300, 80);
difficultyDots = new FlxTypedSpriteGroup<DifficultyDot>(DEFAULT_DOTS_GROUP_POS[0], DEFAULT_DOTS_GROUP_POS[1]); difficultyDots = new FlxTypedSpriteGroup<DifficultyDot>(203, 170);
letterSort = new LetterSort((CUTOUT_WIDTH * SONGS_POS_MULTI) + 400, 75); letterSort = new LetterSort((CUTOUT_WIDTH * SONGS_POS_MULTI) + 400, 75);
rankBg = new FunkinSprite(0, 0); rankBg = new FunkinSprite(0, 0);
rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette')); rankVignette = new FlxSprite(0, 0).loadGraphic(Paths.image('freeplay/rankVignette'));
@ -1265,6 +1270,7 @@ class FreeplayState extends MusicBeatSubState
}); });
new FlxTimer().start(2, _ -> { new FlxTimer().start(2, _ -> {
// dj.fistPump();
prepForNewRank = false; prepForNewRank = false;
}); });
} }
@ -1289,22 +1295,9 @@ class FreeplayState extends MusicBeatSubState
function refreshDots(amount:Int, index:Int, prevIndex:Int):Void function refreshDots(amount:Int, index:Int, prevIndex:Int):Void
{ {
var distance:Int = 30; var distance:Int = 30;
var groupOffset:Float = 14.7;
var shiftAmt:Float = (distance * amount) / 2; var shiftAmt:Float = (distance * amount) / 2;
var daSong:Null<FreeplaySongData> = currentCapsule.freeplayData; var daSong:Null<FreeplaySongData> = currentCapsule.freeplayData;
final maxDotsPerRow:Int = 8;
if (difficultyDots.group.members.length > maxDotsPerRow)
{
difficultyDots.x = DEFAULT_DOTS_GROUP_POS[0] - groupOffset * (maxDotsPerRow - 1);
}
else
{
difficultyDots.x = DEFAULT_DOTS_GROUP_POS[0] - groupOffset * (difficultyDots.group.members.length - 1);
}
var curRow:Int = 0;
var curDot:Int = 0;
for (i in 0...difficultyDots.group.members.length) for (i in 0...difficultyDots.group.members.length)
{ {
// if (difficultyDots.group.members[i] == null) continue; // if (difficultyDots.group.members[i] == null) continue;
@ -1336,16 +1329,7 @@ class FreeplayState extends MusicBeatSubState
} }
difficultyDots.group.members[i].visible = true; difficultyDots.group.members[i].visible = true;
difficultyDots.group.members[i].x = (CUTOUT_WIDTH * DJ_POS_MULTI) + ((difficultyDots.x + (distance * curDot)) - shiftAmt); difficultyDots.group.members[i].x = (CUTOUT_WIDTH * DJ_POS_MULTI) + ((difficultyDots.x + (distance * i)) - shiftAmt);
difficultyDots.group.members[i].y = DEFAULT_DOTS_GROUP_POS[1] + distance * curRow;
curDot++;
if (curDot >= maxDotsPerRow)
{
curDot = 0;
curRow++;
}
if (daSong?.data.hasDifficulty(diffId, daSong?.data.getFirstValidVariation(diffId, currentCharacter)) == false) if (daSong?.data.hasDifficulty(diffId, daSong?.data.getFirstValidVariation(diffId, currentCharacter)) == false)
{ {
@ -1598,10 +1582,12 @@ class FreeplayState extends MusicBeatSubState
} }
if (controls.FREEPLAY_FAVORITE && controls.active) favoriteSong(); if (controls.FREEPLAY_FAVORITE && controls.active) favoriteSong();
if (controls.FREEPLAY_JUMP_TO_TOP && controls.active) changeSelection(-curSelected); if (controls.FREEPLAY_JUMP_TO_TOP && controls.active) changeSelection(-curSelected);
if (controls.FREEPLAY_JUMP_TO_BOTTOM && controls.active) changeSelection(grpCapsules.countLiving() - curSelected - 1); if (controls.FREEPLAY_JUMP_TO_BOTTOM && controls.active) changeSelection(grpCapsules.countLiving() - curSelected - 1);
lerpScoreDisplays(); calculateCompletion();
handleInputs(elapsed); handleInputs(elapsed);
@ -1611,7 +1597,7 @@ class FreeplayState extends MusicBeatSubState
if (allowPicoBulletsVibration) HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5); if (allowPicoBulletsVibration) HapticUtil.vibrate(0, 0.01, (Constants.MAX_VIBRATION_AMPLITUDE / 3) * 2.5);
} }
function lerpScoreDisplays():Void function calculateCompletion():Void
{ {
lerpScore = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpScore, intendedScore, FlxG.elapsed, 0.2), intendedScore, 1); lerpScore = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpScore, intendedScore, FlxG.elapsed, 0.2), intendedScore, 1);
lerpCompletion = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpCompletion, intendedCompletion, FlxG.elapsed, 0.5), intendedCompletion, 1 / 100); lerpCompletion = MathUtil.snap(MathUtil.smoothLerpPrecision(lerpCompletion, intendedCompletion, FlxG.elapsed, 0.5), intendedCompletion, 1 / 100);
@ -2193,7 +2179,7 @@ class FreeplayState extends MusicBeatSubState
*/ */
function changeDiff(change:Int = 0, force:Bool = false, capsuleAnim:Bool = false):Void function changeDiff(change:Int = 0, force:Bool = false, capsuleAnim:Bool = false):Void
{ {
if (!controls.active && !force) return; if (!controls.active) return;
if (capsuleAnim) if (capsuleAnim)
{ {
@ -2289,10 +2275,11 @@ class FreeplayState extends MusicBeatSubState
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation); var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSong.data.id, currentDifficulty, currentVariation);
intendedScore = songScore?.score ?? 0; intendedScore = songScore?.score ?? 0;
intendedCompletion = Math.max(0, Scoring.tallyCompletion(songScore?.tallies)); intendedCompletion = songScore == null ? 0.0 : Math.max(0,
((songScore.tallies.sick + songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes));
rememberedDifficulty = currentDifficulty; rememberedDifficulty = currentDifficulty;
if (!capsuleAnim) generateSongList(currentFilter, false, true, true); if (!capsuleAnim) generateSongList(currentFilter, false, true, true);
currentCapsule.refreshDisplay(!prepForNewRank); currentCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
} }
else else
{ {
@ -2403,6 +2390,11 @@ class FreeplayState extends MusicBeatSubState
{ {
trace('RANDOM SELECTED'); trace('RANDOM SELECTED');
controls.active = false;
#if NO_FEATURE_TOUCH_CONTROLS
letterSort.inputEnabled = false;
#end
var availableSongCapsules:Array<SongMenuItem> = grpCapsules.members.filter(function(cap:SongMenuItem) { var availableSongCapsules:Array<SongMenuItem> = grpCapsules.members.filter(function(cap:SongMenuItem) {
// Dead capsules are ones which were removed from the list when changing filters. // Dead capsules are ones which were removed from the list when changing filters.
return cap.alive && cap.freeplayData != null; return cap.alive && cap.freeplayData != null;
@ -2428,10 +2420,6 @@ class FreeplayState extends MusicBeatSubState
// Seeing if I can do an animation... // Seeing if I can do an animation...
curSelected = grpCapsules.members.indexOf(targetSong); curSelected = grpCapsules.members.indexOf(targetSong);
changeSelection(0); // Trigger an update. changeSelection(0); // Trigger an update.
controls.active = false;
#if NO_FEATURE_TOUCH_CONTROLS
letterSort.inputEnabled = false;
#end
// Act like we hit Confirm on that song. // Act like we hit Confirm on that song.
capsuleOnConfirmDefault(targetSong); capsuleOnConfirmDefault(targetSong);
@ -2599,28 +2587,26 @@ class FreeplayState extends MusicBeatSubState
new FlxTimer().start(styleData?.getStartDelay(), function(tmr:FlxTimer) { new FlxTimer().start(styleData?.getStartDelay(), function(tmr:FlxTimer) {
FunkinSound.emptyPartialQueue(); FunkinSound.emptyPartialQueue();
funnyCam.fade(FlxColor.BLACK, 0.2, false, function() { Paths.setCurrentLevel(cap?.freeplayData?.levelId);
Paths.setCurrentLevel(cap?.freeplayData?.levelId); LoadingState.loadPlayState(
LoadingState.loadPlayState( {
{ targetSong: targetSong,
targetSong: targetSong, targetDifficulty: currentDifficulty,
targetDifficulty: currentDifficulty, targetVariation: currentVariation,
targetVariation: currentVariation, targetInstrumental: targetInstId,
targetInstrumental: targetInstId, practiceMode: false,
practiceMode: false, minimalMode: false,
minimalMode: false,
#if FEATURE_DEBUG_FUNCTIONS #if FEATURE_DEBUG_FUNCTIONS
botPlayMode: FlxG.keys.pressed.SHIFT, botPlayMode: FlxG.keys.pressed.SHIFT,
#else #else
botPlayMode: false, botPlayMode: false,
#end #end
// TODO: Make these an option! It's currently only accessible via chart editor. // TODO: Make these an option! It's currently only accessible via chart editor.
// startTimestamp: 0.0, // startTimestamp: 0.0,
// playbackRate: 0.5, // playbackRate: 0.5,
// botPlayMode: true, // botPlayMode: true,
}, true); }, true);
});
}); });
} }
@ -2668,7 +2654,6 @@ class FreeplayState extends MusicBeatSubState
capsule.targetPos.y = capsule.intendedY(index - curSelectedFloat); capsule.targetPos.y = capsule.intendedY(index - curSelectedFloat);
capsule.targetPos.x = capsule.intendedX(index - curSelectedFloat) + (CUTOUT_WIDTH * SONGS_POS_MULTI); capsule.targetPos.x = capsule.intendedX(index - curSelectedFloat) + (CUTOUT_WIDTH * SONGS_POS_MULTI);
if (index + 0.5 < curSelectedFloat) capsule.targetPos.y -= 100;
} }
if (curSelected != prevSelected) if (curSelected != prevSelected)
@ -2708,18 +2693,26 @@ class FreeplayState extends MusicBeatSubState
if (!prepForNewRank && curSelected != prevSelected) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); if (!prepForNewRank && curSelected != prevSelected) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(currentCapsule.freeplayData?.data.id ?? "", currentDifficulty, currentVariation); var daSongCapsule:SongMenuItem = currentCapsule;
intendedScore = songScore?.score ?? 0; if (daSongCapsule.freeplayData != null)
{
intendedCompletion = Scoring.tallyCompletion(songScore?.tallies); var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSongCapsule.freeplayData.data.id, currentDifficulty, currentVariation);
rememberedSongId = currentCapsule.freeplayData?.data.id; intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick +
if (currentCapsule.freeplayData == null) albumRoll.albumId = null; songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes);
rememberedSongId = daSongCapsule.freeplayData.data.id;
changeDiff(0, true); changeDiff();
if (currentCapsule.freeplayData == null) currentCapsule.refreshDisplay(); daSongCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
}
else else
currentCapsule.refreshDisplay(!prepForNewRank); {
intendedScore = 0;
intendedCompletion = 0.0;
rememberedSongId = null;
albumRoll.albumId = null;
changeDiff();
daSongCapsule.refreshDisplay();
}
for (index => capsule in grpCapsules.members) for (index => capsule in grpCapsules.members)
{ {
@ -2732,15 +2725,16 @@ class FreeplayState extends MusicBeatSubState
capsule.targetPos.y = capsule.intendedY(index - curSelected); capsule.targetPos.y = capsule.intendedY(index - curSelected);
capsule.targetPos.x = capsule.intendedX(index - curSelected) + (CUTOUT_WIDTH * SONGS_POS_MULTI); capsule.targetPos.x = capsule.intendedX(index - curSelected) + (CUTOUT_WIDTH * SONGS_POS_MULTI);
if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure if (index < curSelected #if FEATURE_TOUCH_CONTROLS
&& ControlsHandler.usingExternalInputDevice #end) capsule.targetPos.y -= 100; // another 100 for good measure
} }
if (grpCapsules.countLiving() > 0 && !prepForNewRank && controls.active) if (grpCapsules.countLiving() > 0 && !prepForNewRank && controls.active)
{ {
playCurSongPreview(currentCapsule); playCurSongPreview(daSongCapsule);
currentCapsule.selected = true; currentCapsule.selected = true;
// switchBackingImage(currentCapsule.freeplayData); // switchBackingImage(daSongCapsule.freeplayData);
} }
// Small vibrations every selection change. // Small vibrations every selection change.

View file

@ -148,22 +148,18 @@ class LetterSort extends FlxSpriteGroup
public function changeSelection(diff:Int = 0, playSound:Bool = true):Void public function changeSelection(diff:Int = 0, playSound:Bool = true):Void
{ {
@:privateAccess doLetterChangeAnims(diff);
if (instance.controls.active)
{
doLetterChangeAnims(diff);
var multiPosOrNeg:Float = diff > 0 ? 1 : -1; var multiPosOrNeg:Float = diff > 0 ? 1 : -1;
// if we're moving left (diff < 0), we want control of the right arrow, and vice versa // if we're moving left (diff < 0), we want control of the right arrow, and vice versa
var arrowToMove:FlxSprite = diff < 0 ? leftArrow : rightArrow; var arrowToMove:FlxSprite = diff < 0 ? leftArrow : rightArrow;
arrowToMove.offset.x = 3 * multiPosOrNeg; arrowToMove.offset.x = 3 * multiPosOrNeg;
new FlxTimer().start(2 / 24, function(_) { new FlxTimer().start(2 / 24, function(_) {
arrowToMove.offset.x = 0; arrowToMove.offset.x = 0;
}); });
if (playSound && diff != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4); if (playSound && diff != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
}
} }
/** /**

View file

@ -49,6 +49,8 @@ class SongMenuItem extends FlxSpriteGroup
public var fakeRanking:FreeplayRank; public var fakeRanking:FreeplayRank;
var ranks:Array<String> = ["fail", "average", "great", "excellent", "perfect", "perfectsick"];
public var targetPos:FlxPoint = new FlxPoint(); public var targetPos:FlxPoint = new FlxPoint();
public var doLerp:Bool = false; public var doLerp:Bool = false;
public var doJumpIn:Bool = false; public var doJumpIn:Bool = false;
@ -67,9 +69,12 @@ class SongMenuItem extends FlxSpriteGroup
public var newText:FlxSprite; public var newText:FlxSprite;
var difficultyNumbers:Array<CapsuleNumber> = []; // referred to as "bignumbers" in the .fla file! // public var weekType:FlxSprite;
var bpmNumbers:Array<CapsuleNumber> = []; // referred to as "smallnumbers" in the .fla file! public var bigNumbers:Array<CapsuleNumber> = [];
var weekNumbers:Array<CapsuleNumber> = [];
public var smallNumbers:Array<CapsuleNumber> = [];
public var weekNumbers:Array<CapsuleNumber> = [];
var impactThing:FunkinSprite; var impactThing:FunkinSprite;
@ -126,18 +131,18 @@ class SongMenuItem extends FlxSpriteGroup
for (i in 0...2) for (i in 0...2)
{ {
var num:CapsuleNumber = new CapsuleNumber(466 + (i * 30), 32, true, 0); var bigNumber:CapsuleNumber = new CapsuleNumber(466 + (i * 30), 32, true, 0);
add(num); add(bigNumber);
difficultyNumbers.push(num); bigNumbers.push(bigNumber);
} }
for (i in 0...3) for (i in 0...3)
{ {
var num:CapsuleNumber = new CapsuleNumber(185 + (i * 11), 88.5, false, 0); var smallNumber:CapsuleNumber = new CapsuleNumber(185 + (i * 11), 88.5, false, 0);
add(num); add(smallNumber);
bpmNumbers.push(num); smallNumbers.push(smallNumber);
} }
// doesn't get added, simply is here to help with visibility of things for the pop in! // doesn't get added, simply is here to help with visibility of things for the pop in!
@ -329,38 +334,38 @@ class SongMenuItem extends FlxSpriteGroup
shiftX = 186; shiftX = 186;
} }
for (i in 0...bpmNumbers.length) for (i in 0...smallNumbers.length)
{ {
bpmNumbers[i].x = this.x + (shiftX + (i * 11)); smallNumbers[i].x = this.x + (shiftX + (i * 11));
switch (i) switch (i)
{ {
case 0: case 0:
if (newBPM < 100) if (newBPM < 100)
{ {
bpmNumbers[i].digit = 0; smallNumbers[i].digit = 0;
} }
else else
{ {
bpmNumbers[i].digit = Math.floor(newBPM / 100) % 10; smallNumbers[i].digit = Math.floor(newBPM / 100) % 10;
} }
case 1: case 1:
if (newBPM < 10) if (newBPM < 10)
{ {
bpmNumbers[i].digit = 0; smallNumbers[i].digit = 0;
} }
else else
{ {
bpmNumbers[i].digit = Math.floor(newBPM / 10) % 10; smallNumbers[i].digit = Math.floor(newBPM / 10) % 10;
if (Math.floor(newBPM / 10) % 10 == 1) tempShift = -4; if (Math.floor(newBPM / 10) % 10 == 1) tempShift = -4;
} }
case 2: case 2:
bpmNumbers[i].digit = newBPM % 10; smallNumbers[i].digit = newBPM % 10;
default: default:
trace('why the fuck is this being called'); trace('why the fuck is this being called');
} }
bpmNumbers[i].x += tempShift; smallNumbers[i].x += tempShift;
} }
// diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}')); // diffRatingSprite.loadGraphic(Paths.image('freeplay/diffRatings/diff${ratingPadded}'));
// diffRatingSprite.visible = false; // diffRatingSprite.visible = false;
@ -434,21 +439,21 @@ class SongMenuItem extends FlxSpriteGroup
{ {
var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating'; var ratingPadded:String = newRating < 10 ? '0$newRating' : '$newRating';
for (i in 0...difficultyNumbers.length) for (i in 0...bigNumbers.length)
{ {
switch (i) switch (i)
{ {
case 0: case 0:
if (newRating < 10) if (newRating < 10)
{ {
difficultyNumbers[i].digit = 0; bigNumbers[i].digit = 0;
} }
else else
{ {
difficultyNumbers[i].digit = Math.floor(newRating / 10); bigNumbers[i].digit = Math.floor(newRating / 10);
} }
case 1: case 1:
difficultyNumbers[i].digit = newRating % 10; bigNumbers[i].digit = newRating % 10;
default: default:
trace('why the fuck is this being called'); trace('why the fuck is this being called');
} }

View file

@ -16,6 +16,7 @@ import funkin.modding.IScriptedClass.IStateChangingScriptedClass;
import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent;
import funkin.ui.FullScreenScaleMode; import funkin.ui.FullScreenScaleMode;
import funkin.util.BitmapUtil; import funkin.util.BitmapUtil;
import openfl.utils.Assets;
/** /**
* A class for the backing cards so they dont have to be part of freeplayState...... * A class for the backing cards so they dont have to be part of freeplayState......

View file

@ -10,6 +10,7 @@ import funkin.graphics.adobeanimate.FlxAtlasSprite;
import funkin.modding.events.ScriptEvent; import funkin.modding.events.ScriptEvent;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import funkin.util.BitmapUtil; import funkin.util.BitmapUtil;
import openfl.utils.Assets;
class NewCharacterCard extends BackingCard class NewCharacterCard extends BackingCard
{ {

View file

@ -268,8 +268,10 @@ class MainMenuState extends MusicBeatState
// reset camera when debug menu is closed // reset camera when debug menu is closed
subStateClosed.add(_ -> resetCamStuff(false)); subStateClosed.add(_ -> resetCamStuff(false));
// TODO: Why does this specific function break with null safety?
@:nullSafety(Off)
subStateOpened.add((sub:FlxSubState) -> { subStateOpened.add((sub:FlxSubState) -> {
if (Std.isOfType(sub, FreeplayState)) if (Type.getClass(sub) == FreeplayState)
{ {
new FlxTimer().start(0.5, _ -> { new FlxTimer().start(0.5, _ -> {
magenta.visible = false; magenta.visible = false;

View file

@ -545,7 +545,6 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
if (FlxG.sound.music.time < _lastTime) if (FlxG.sound.music.time < _lastTime)
{ {
localConductor.update(FlxG.sound.music.time, !calibrating); localConductor.update(FlxG.sound.music.time, !calibrating);
b = localConductor.currentBeatTime;
// Update arrows to be the correct distance away from the receptor. // Update arrows to be the correct distance away from the receptor.
var lastArrowBeat:Float = 0; var lastArrowBeat:Float = 0;
@ -559,7 +558,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
} }
if (calibrating) if (calibrating)
{ {
arrowBeat = lastArrowBeat; arrowBeat = lastArrowBeat + 2;
} }
else else
arrowBeat = 4; arrowBeat = 4;
@ -567,10 +566,6 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
testStrumline.clean(); testStrumline.clean();
testStrumline.noteData = []; testStrumline.noteData = [];
testStrumline.nextNoteIndex = 0; testStrumline.nextNoteIndex = 0;
trace('Restarting conductor');
_lastTime = FlxG.sound.music.time;
return;
} }
_lastBeat = b; _lastBeat = b;
@ -613,7 +608,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
countText.text = 'Current Offset: ' + Std.int(appliedOffsetLerp) + 'ms'; countText.text = 'Current Offset: ' + Std.int(appliedOffsetLerp) + 'ms';
var toRemove:Array<ArrowData> = []; var toRemove:Array<ArrowData> = [];
var _lastArrowBeat:Float = 0;
// Update arrows // Update arrows
for (i in 0...arrows.length) for (i in 0...arrows.length)
{ {
@ -634,13 +629,12 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
arrow.sprite.alpha -= elapsed * 5; arrow.sprite.alpha -= elapsed * 5;
} }
if (arrow.beat == _lastArrowBeat || arrow.sprite.alpha <= 0) if (arrow.sprite.alpha <= 0)
{ {
toRemove.push(arrow); toRemove.push(arrow);
arrow.sprite.kill(); arrow.sprite.kill();
continue; // arrow.debugText.kill();
} }
_lastArrowBeat = arrow.beat;
} }
// Remove arrows that are marked for removal. // Remove arrows that are marked for removal.

View file

@ -73,7 +73,7 @@ class OptionsState extends MusicBeatState
var options:OptionsMenu = optionsCodex.addPage(Options, new OptionsMenu(saveData)); var options:OptionsMenu = optionsCodex.addPage(Options, new OptionsMenu(saveData));
var preferences:PreferencesMenu = optionsCodex.addPage(Preferences, new PreferencesMenu()); var preferences:PreferencesMenu = optionsCodex.addPage(Preferences, new PreferencesMenu());
var controls:ControlsMenu = optionsCodex.addPage(Controls, new ControlsMenu()); var controls:ControlsMenu = optionsCodex.addPage(Controls, new ControlsMenu());
#if FEATURE_LAG_ADJUSTMENT #if FEATURE_INPUT_OFFSETS
var offsets:OffsetMenu = optionsCodex.addPage(Offsets, new OffsetMenu()); var offsets:OffsetMenu = optionsCodex.addPage(Offsets, new OffsetMenu());
#end #end
@ -82,7 +82,7 @@ class OptionsState extends MusicBeatState
options.onExit.add(exitToMainMenu); options.onExit.add(exitToMainMenu);
controls.onExit.add(exitControls); controls.onExit.add(exitControls);
preferences.onExit.add(optionsCodex.switchPage.bind(Options)); preferences.onExit.add(optionsCodex.switchPage.bind(Options));
#if FEATURE_LAG_ADJUSTMENT #if FEATURE_INPUT_OFFSETS
offsets.onExit.add(exitOffsets); offsets.onExit.add(exitOffsets);
#end #end
saveData.onExit.add(optionsCodex.switchPage.bind(Options)); saveData.onExit.add(optionsCodex.switchPage.bind(Options));
@ -174,8 +174,8 @@ class OptionsMenu extends Page<OptionsMenuPageName>
// createItem("CONTROL SCHEMES", function() { // createItem("CONTROL SCHEMES", function() {
// FlxG.state.openSubState(new ControlsSchemeMenu()); // FlxG.state.openSubState(new ControlsSchemeMenu());
// }); // });
#if FEATURE_LAG_ADJUSTMENT #if FEATURE_INPUT_OFFSETS
createItem("LAG ADJUSTMENT", function() { createItem("INPUT OFFSETS", function() {
FlxG.sound.music.fadeOut(0.5, 0, function(tw) { FlxG.sound.music.fadeOut(0.5, 0, function(tw) {
FunkinSound.playMusic('offsetsLoop', FunkinSound.playMusic('offsetsLoop',
{ {

View file

@ -65,6 +65,7 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
createPrefDescription(); createPrefDescription();
camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70); camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70);
if (items != null) camFollow.y = items.selectedItem.y;
menuCamera.follow(camFollow, null, 0.085); menuCamera.follow(camFollow, null, 0.085);
var margin = 160; var margin = 160;
@ -72,6 +73,7 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
menuCamera.minScrollY = 0; menuCamera.minScrollY = 0;
items.onChange.add(function(selected) { items.onChange.add(function(selected) {
camFollow.y = selected.y;
itemDesc.text = preferenceDesc[items.selectedIndex]; itemDesc.text = preferenceDesc[items.selectedIndex];
}); });
@ -202,9 +204,6 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
{ {
super.update(elapsed); super.update(elapsed);
// Positions the camera to the selected item.
if (items != null) camFollow.y = items.selectedItem.y;
// 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;

View file

@ -148,6 +148,6 @@ class NumberPreferenceItem extends TextMenuItem
function toFixed(value:Float):Float function toFixed(value:Float):Float
{ {
var multiplier:Float = Math.pow(10, precision); var multiplier:Float = Math.pow(10, precision);
return Math.round(value * multiplier) / multiplier; return Math.floor(value * multiplier) / multiplier;
} }
} }

View file

@ -25,6 +25,7 @@ import funkin.ui.transition.stickers.StickerSubState;
import funkin.util.MathUtil; import funkin.util.MathUtil;
import funkin.util.SwipeUtil; import funkin.util.SwipeUtil;
import funkin.util.TouchUtil; import funkin.util.TouchUtil;
import openfl.utils.Assets;
import funkin.ui.FullScreenScaleMode; import funkin.ui.FullScreenScaleMode;
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
import funkin.api.discord.DiscordClient; import funkin.api.discord.DiscordClient;
@ -604,14 +605,12 @@ class StoryMenuState extends MusicBeatState
var targetVariation:String = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty); var targetVariation:String = targetSong.getFirstValidVariation(PlayStatePlaylist.campaignDifficulty);
FlxG.camera.fade(FlxColor.BLACK, 0.2, false, function() { LoadingState.loadPlayState(
LoadingState.loadPlayState( {
{ targetSong: targetSong,
targetSong: targetSong, targetDifficulty: PlayStatePlaylist.campaignDifficulty,
targetDifficulty: PlayStatePlaylist.campaignDifficulty, targetVariation: targetVariation
targetVariation: targetVariation }, true);
}, true);
});
}); });
} }

View file

@ -228,11 +228,11 @@ class TitleState extends MusicBeatState
{ {
FlxG.bitmapLog.add(FlxG.camera.buffer); FlxG.bitmapLog.add(FlxG.camera.buffer);
#if (desktop || android) #if desktop
// Pressing BACK on the title screen should close the game. // Pressing BACK on the title screen should close the game.
// This lets you exit without leaving fullscreen mode. // This lets you exit without leaving fullscreen mode.
// Only applicable on desktop and Android. // Only applicable on desktop.
if (#if android FlxG.android.justReleased.BACK || #end controls.BACK) if (controls.BACK)
{ {
openfl.Lib.application.window.close(); openfl.Lib.application.window.close();
} }

View file

@ -59,21 +59,13 @@ class EnvironmentConfigMacro
for (line in envFile.split('\n')) for (line in envFile.split('\n'))
{ {
if (line.length <= 0 || line.startsWith("#") || shouldExcludeKey(line)) continue; if (line == "" || line.startsWith("#")) continue;
var index:Int = line.indexOf('='); var parts = line.split('=');
if (parts.length != 2) continue;
if (index == -1) continue; envFields.push(parts[0]);
envValues.push(parts[1]);
var field:String = line.substr(0, index);
var value:String = line.substr(index + 1);
if (value == "") continue;
Sys.println('[INFO] Found a key for environment value $field!');
envFields.push(field);
envValues.push(value);
} }
var newFields = fields.copy(); var newFields = fields.copy();
@ -108,27 +100,6 @@ class EnvironmentConfigMacro
return newFields; return newFields;
} }
private static function shouldExcludeKey(key:String):Bool
{
final android:Bool = key.startsWith('ANDROID_');
final ios:Bool = key.startsWith('IOS_');
final mobile:Bool = key.startsWith('MOBILE_') || ios || android;
final web:Bool = key.startsWith('WEB_');
final desktop:Bool = key.startsWith('DESKTOP_');
#if html5
if (mobile || desktop) return true;
#elseif desktop
if (mobile || web) return true;
#elseif android
if (ios || web || desktop) return true;
#elseif ios
if (android || web || desktop) return true;
#end
return false;
}
#end #end
} }

View file

@ -26,7 +26,7 @@ class GitCommit
process.close(); process.close();
Sys.println('[INFO] Git Commit ID: ${commitHashSplice}'); trace('Git Commit ID: ${commitHashSplice}');
// Generates a string expression // Generates a string expression
return macro $v{commitHashSplice}; return macro $v{commitHashSplice};
@ -56,7 +56,7 @@ class GitCommit
var branchName:String = branchProcess.stdout.readLine(); var branchName:String = branchProcess.stdout.readLine();
branchProcess.close(); branchProcess.close();
Sys.println('[INFO] Git Branch Name: ${branchName}'); trace('Git Branch Name: ${branchName}');
// Generates a string expression // Generates a string expression
return macro $v{branchName}; return macro $v{branchName};
@ -103,7 +103,7 @@ class GitCommit
throw e; throw e;
} }
} }
Sys.println('[INFO] Git Status Output: ${output}'); trace('Git Status Output: ${output}');
// Generates a string expression // Generates a string expression
return macro $v{output.length > 0}; return macro $v{output.length > 0};

View file

@ -0,0 +1,170 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>

View file

@ -0,0 +1,30 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View file

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

View file

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FDF150</color>
</resources>

View file

@ -1,5 +1,6 @@
package; package;
import openfl.utils.Assets;
import openfl.errors.Error; import openfl.errors.Error;
import flixel.FlxG; import flixel.FlxG;
import flixel.FlxState; import flixel.FlxState;

View file

@ -11,6 +11,7 @@ import massive.munit.async.AsyncFactory;
import funkin.data.notestyle.NoteStyleRegistry; import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import flixel.animation.FlxAnimationController; import flixel.animation.FlxAnimationController;
import openfl.utils.Assets;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
@:access(funkin.play.notes.notestyle.NoteStyle) @:access(funkin.play.notes.notestyle.NoteStyle)

View file

@ -1,5 +1,6 @@
package funkin.util.assets; package funkin.util.assets;
import openfl.utils.Assets;
import massive.munit.util.Timer; import massive.munit.util.Timer;
import massive.munit.Assert; import massive.munit.Assert;
import massive.munit.async.AsyncFactory; import massive.munit.async.AsyncFactory;