Merge branch 'death-alt' of https://github.com/FunkinCrew/Funkin-secret into death-alt

This commit is contained in:
EliteMasterEric 2023-06-15 03:49:24 -04:00
commit 9a541c5769
121 changed files with 2310 additions and 4417 deletions

View file

@ -27,6 +27,7 @@ jobs:
- uses: ./.github/actions/setup-haxeshit - uses: ./.github/actions/setup-haxeshit
- name: Build game? - name: Build game?
run: | run: |
sudo apt-get install -y libx11-dev libxinerama-dev libxrandr-dev libgl1-mesa-dev libgl-dev libxi-dev libxext-dev libasound2-dev
haxelib run lime build html5 -debug --times haxelib run lime build html5 -debug --times
ls ls
- uses: ./.github/actions/upload-itch - uses: ./.github/actions/upload-itch

192
.vscode/settings.json vendored
View file

@ -1,84 +1,120 @@
{ {
"[haxe]": { "[haxe]": {
// Automatically keep Haxe files formatted. // Automatically keep Haxe files formatted.
"editor.formatOnSave": true, "editor.formatOnSave": true,
"editor.formatOnPaste": true, "editor.formatOnPaste": true,
"editor.codeActionsOnSave": { "editor.codeActionsOnSave": {
// Compilation server issues can cause auto-cleanup to remove valid imports. // Compilation server issues can cause auto-cleanup to remove valid imports.
"source.organizeImports": false "source.organizeImports": false
},
"editor.defaultFormatter": "nadako.vshaxe",
"editor.tabSize": 2
}, },
"editor.defaultFormatter": "nadako.vshaxe",
"[json]": { "editor.tabSize": 2
// Automatically keep JSON files formatted. },
"editor.formatOnSave": true,
"editor.formatOnPaste": true, "[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode" // Automatically keep JSON files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
// Automatically keep JSONC files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"prettier.tabWidth": 2,
// XML formatting style configuration
"xml.format.enabled": true,
"xml.format.legacy": false,
"xml.format.emptyElements": "collapse",
"xml.preferences.quoteStyle": "double",
"xml.format.enforceQuoteStyle": "preferred",
"xml.format.preserveAttributeLineBreaks": false,
"xml.format.preservedNewlines": 0,
"xml.format.splitAttributes": false,
"xml.format.joinCDATALines": true,
"xml.format.preserveEmptyContent": false,
"xml.format.joinCommentLines": false,
"xml.format.joinContentLines": false,
"xml.format.spaceBeforeEmptyCloseTag": true,
"xml.format.xsiSchemaLocationSplit": "onPair",
"xml.format.splitAttributesIndentSize": 2,
"xml.format.closingBracketNewLine": false,
"xml.format.preserveSpace": [
"xsl:text",
"xsl:comment",
"xsl:processing-instruction",
"literallayout",
"programlisting",
"screen",
"synopsis",
"pre",
"xd:pre"
],
"xml.format.maxLineWidth": 0,
"xml.format.grammarAwareFormatting": true,
// Generic file formatting style configuration
"files.insertFinalNewline": true,
"files.trimFinalNewlines": false,
"files.trimTrailingWhitespace": true,
// Automatically detect indentation.
"editor.detectIndentation": true,
"editor.insertSpaces": true,
"editor.tabSize": 2,
// Automatically enforce Linux style line endings.
"files.eol": "\n",
"haxe.displayPort": "auto",
"haxe.enableCompilationServer": true,
"haxe.displayServer": {
"arguments": ["-v"]
},
// Fix file associations for HScript.
"files.associations": {
"*.hxp": "haxe",
"*.hscript": "haxe",
"*.haxe": "haxe",
"*.hxs": "haxe",
"*.hxc": "haxe"
},
"projectManager.git.baseFolders": ["./"],
"haxecheckstyle.sourceFolders": ["src", "Source"],
"haxecheckstyle.externalSourceRoots": [],
"haxecheckstyle.configurationFile": "checkstyle.json",
"haxecheckstyle.codeSimilarityBufferSize": 100,
"lime.targetConfigurations": [
{
"label": "Windows / Debug",
"target": "windows",
"args": ["-debug"]
}, },
{
"[jsonc]": { "label": "Windows / Debug (DEBUG ASSETS)",
// Automatically keep JSONC files formatted. "target": "windows",
"editor.formatOnSave": true, "args": ["-debug", "-DDEBUG_ASSETS"]
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
}, },
"prettier.tabWidth": 2, {
"label": "Windows / Debug (ANIMATE)",
// Automatically detect indentation. "target": "windows",
"editor.detectIndentation": true, "args": ["-debug", "-DANIMATE"]
"editor.insertSpaces": true,
"editor.tabSize": 2,
// Automatically enforce Linux style line endings.
"files.eol": "\n",
"haxe.displayPort": "auto",
"haxe.enableCompilationServer": true,
"haxe.displayServer": {
"arguments": ["-v"]
}, },
// Fix file associations for HScript. {
"files.associations": { "label": "HTML5 / Debug",
"*.hxp": "haxe", "target": "html5",
"*.hscript": "haxe", "args": ["-debug"]
"*.haxe": "haxe",
"*.hxs": "haxe",
"*.hxc": "haxe"
}, },
"projectManager.git.baseFolders": ["./"], {
"label": "HTML5 / Debug (Watch)",
"haxecheckstyle.sourceFolders": ["src", "Source"], "target": "html5",
"haxecheckstyle.externalSourceRoots": [], "args": ["-debug", "-watch"]
"haxecheckstyle.configurationFile": "checkstyle.json", }
"haxecheckstyle.codeSimilarityBufferSize": 100, ]
}
"lime.targetConfigurations": [
{
"label": "Windows / Debug",
"target": "windows",
"args": ["-debug"]
},
{
"label": "Windows / Debug (DEBUG ASSETS)",
"target": "windows",
"args": ["-debug", "-DDEBUG_ASSETS"]
},
{
"label": "Windows / Debug (ANIMATE)",
"target": "windows",
"args": ["-debug", "-DANIMATE"]
},
{
"label": "HTML5 / Debug",
"target": "html5",
"args": ["-debug"]
},
{
"label": "HTML5 / Debug (Watch)",
"target": "html5",
"args": ["-debug", "-watch"]
}
]
}

View file

@ -1,48 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<project> <project>
<!-- _________________________ Application Settings _________________________ --> <!-- _________________________ Application Settings _________________________ -->
<app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.3.0" company="ninjamuffin99" /> <app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.3.0" company="ninjamuffin99" />
<!--Switch Export with Unique ApplicationID and Icon--> <!--Switch Export with Unique ApplicationID and Icon-->
<set name="APP_ID" value="0x0100f6c013bbc000" /> <set name="APP_ID" value="0x0100f6c013bbc000" />
<!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file <!--The flixel preloader is not accurate in Chrome. You can use it regularly if you embed the swf into a html file
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"--> or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
<!-- <app preloader="Preloader" resizable="true" /> --> <!-- <app preloader="Preloader" resizable="true" /> -->
<app preloader="Preloader" /> <app preloader="Preloader" />
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2--> <!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
<set name="SWF_VERSION" value="11.8" /> <set name="SWF_VERSION" value="11.8" />
<!-- ____________________________ Window Settings ___________________________ --> <!-- ____________________________ Window Settings ___________________________ -->
<!--These window settings apply to all targets--> <!--These window settings apply to all targets-->
<window width="1280" height="720" fps="" background="#000000" hardware="true" vsync="false" /> <window width="1280" height="720" fps="" background="#000000" hardware="true" vsync="false" />
<!--HTML5-specific--> <!--HTML5-specific-->
<window if="html5" resizable="true" /> <window if="html5" resizable="true" />
<!--Desktop-specific--> <!--Desktop-specific-->
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false" /> <window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false" />
<!--Mobile-specific--> <!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" /> <window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" />
<!-- _____________________________ Path Settings ____________________________ --> <!-- _____________________________ Path Settings ____________________________ -->
<set name="BUILD_DIR" value="export/debug" if="debug" /> <set name="BUILD_DIR" value="export/debug" if="debug" />
<set name="BUILD_DIR" value="export/release" unless="debug" /> <set name="BUILD_DIR" value="export/release" unless="debug" />
<set name="BUILD_DIR" value="export/32bit" if="32bit" /> <set name="BUILD_DIR" value="export/32bit" if="32bit" />
<classpath name="source" /> <classpath name="source" />
<assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" /> <assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" />
<assets path="assets/preload" rename="assets" exclude="*.mp3" unless="web" /> <assets path="assets/preload" rename="assets" exclude="*.mp3" unless="web" />
<define name="PRELOAD_ALL" unless="web" /> <define name="PRELOAD_ALL" unless="web" />
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL" /> <define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL" />
<section if="PRELOAD_ALL"> <section if="PRELOAD_ALL">
<library name="songs" preload="true" /> <library name="songs" preload="true" />
<library name="shared" preload="true" /> <library name="shared" preload="true" />
@ -56,7 +41,6 @@
<library name="week7" preload="true" /> <library name="week7" preload="true" />
<library name="weekend1" preload="true" /> <library name="weekend1" preload="true" />
</section> </section>
<section if="NO_PRELOAD_ALL"> <section if="NO_PRELOAD_ALL">
<library name="songs" preload="false" /> <library name="songs" preload="false" />
<library name="shared" preload="false" /> <library name="shared" preload="false" />
@ -70,7 +54,6 @@
<library name="week7" preload="false" /> <library name="week7" preload="false" />
<library name="weekend1" preload="false" /> <library name="weekend1" preload="false" />
</section> </section>
<assets path="assets/songs" library="songs" exclude="*.fla|*.ogg" if="web" /> <assets path="assets/songs" library="songs" exclude="*.fla|*.ogg" if="web" />
<assets path="assets/songs" library="songs" exclude="*.fla|*.mp3" unless="web" /> <assets path="assets/songs" library="songs" exclude="*.fla|*.mp3" unless="web" />
<assets path="assets/shared" library="shared" exclude="*.fla|*.ogg" if="web" /> <assets path="assets/shared" library="shared" exclude="*.fla|*.ogg" if="web" />
@ -93,9 +76,7 @@
<assets path="assets/week7" library="week7" exclude="*.fla|*.mp3" unless="web" /> <assets path="assets/week7" library="week7" exclude="*.fla|*.mp3" unless="web" />
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.ogg" if="web" /> <assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.ogg" if="web" />
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" /> <assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" />
<!-- <assets path='example_mods' rename='mods' embed='false'/> --> <!-- <assets path='example_mods' rename='mods' embed='false'/> -->
<!-- <!--
AUTOMATICALLY MOVING EXAMPLE MODS INTO THE BUILD CAUSES ISSUES AUTOMATICALLY MOVING EXAMPLE MODS INTO THE BUILD CAUSES ISSUES
Currently, this line will add the mod files to the library manifest, Currently, this line will add the mod files to the library manifest,
@ -103,131 +84,103 @@
If we can exclude the `mods` folder from the manifest, we can re-enable this line. If we can exclude the `mods` folder from the manifest, we can re-enable this line.
<assets path='example_mods' rename='mods' embed='false' exclude="*.md" /> <assets path='example_mods' rename='mods' embed='false' exclude="*.md" />
--> -->
<assets path='art/readme.txt' rename='do NOT readme.txt' /> <assets path="art/readme.txt" rename="do NOT readme.txt" />
<assets path="CHANGELOG.md" rename="changelog.txt" />
<assets path="CHANGELOG.md" rename='changelog.txt' /> <!-- NOTE FOR FUTURE SELF SINCE FONTS ARE ALWAYS FUCKY
<!-- NOTE FOR FUTURE SELF SINCE FONTS ARE ALWAYS FUCKY
TO FIX ONE OF THEM, I CONVERTED IT TO OTF. DUNNO IF YOU NEED TO TO FIX ONE OF THEM, I CONVERTED IT TO OTF. DUNNO IF YOU NEED TO
THEN UHHH I USED THE NAME OF THE FONT WITH SETFORMAT() ON THE TEXT!!! THEN UHHH I USED THE NAME OF THE FONT WITH SETFORMAT() ON THE TEXT!!!
NOT USING A DIRECT THING TO THE ASSET!!! NOT USING A DIRECT THING TO THE ASSET!!!
--> -->
<assets path="assets/fonts" embed='true' /> <assets path="assets/fonts" embed="true" />
<!-- _______________________________ Libraries ______________________________ --> <!-- _______________________________ Libraries ______________________________ -->
<haxelib name="lime" /> <!-- Game engine backend --> <haxelib name="lime" /> <!-- Game engine backend -->
<haxelib name="openfl" /> <!-- Game engine backend --> <haxelib name="openfl" /> <!-- Game engine backend -->
<haxelib name="flixel" /> <!-- Game engine --> <haxelib name="flixel" /> <!-- Game engine -->
<haxedev set='webgl' /> <haxedev set="webgl" />
<!--In case you want to use the addons package--> <!--In case you want to use the addons package-->
<haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel --> <haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel -->
<haxelib name="hscript" /> <!-- Scripting --> <haxelib name="hscript" /> <!-- Scripting -->
<haxelib name="flixel-ui" /> <!-- UI framework (deprecate this? --> <haxelib name="flixel-ui" /> <!-- UI framework (deprecate this? -->
<haxelib name="haxeui-core"/> <!-- UI framework --> <haxelib name="haxeui-core" /> <!-- UI framework -->
<haxelib name="haxeui-flixel"/> <!-- Integrate HaxeUI with Flixel --> <haxelib name="haxeui-flixel" /> <!-- Integrate HaxeUI with Flixel -->
<haxelib name="polymod" /> <!-- Modding framework --> <haxelib name="polymod" /> <!-- Modding framework -->
<haxelib name="flxanimate" /> <!-- Texture atlas rendering --> <haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
<!-- <haxelib name="hxcodec" /> Video playback --> <haxelib name="hxCodec" /> <!-- Video playback -->
<haxelib name="json2object" /> <!-- JSON parsing --> <haxelib name="json2object" /> <!-- JSON parsing -->
<haxelib name="thx.semver" /> <haxelib name="thx.semver" />
<haxelib name="hxcpp-debug-server" if="desktop debug" /> <haxelib name="hxcpp-debug-server" if="desktop debug" />
<!--Disable the Flixel core focus lost screen--> <!--Disable the Flixel core focus lost screen-->
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" /> <haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!--> <!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
<haxedef name="FLX_NO_DEBUG" unless="debug" /> <haxedef name="FLX_NO_DEBUG" unless="debug" />
<!--Enable this for Nape release builds for a serious peformance improvement--> <!--Enable this for Nape release builds for a serious peformance improvement-->
<haxedef name="NAPE_RELEASE_BUILD" unless="debug" /> <haxedef name="NAPE_RELEASE_BUILD" unless="debug" />
<!-- TODO: REMOVE THIS!!!! --> <!--
Hide deprecation warnings until they're fixed.
TODO: REMOVE THIS!!!!
-->
<haxeflag name="-w" value="-WDeprecated" /> <haxeflag name="-w" value="-WDeprecated" />
<!-- _________________________________ Custom _______________________________ --> <!-- Haxe 4.3.0+: Enable pretty syntax errors and stuff. -->
<haxedef name="message-reporting" value="pretty" />
<!-- _________________________________ Custom _______________________________ -->
<!-- Disable trace() calls in release builds to bump up performance. --> <!-- Disable trace() calls in release builds to bump up performance. -->
<haxeflag name="--no-traces" unless="debug" /> <haxeflag name="--no-traces" unless="debug" />
<!-- HScript relies heavily on Reflection, which means we can't use DCE. --> <!-- HScript relies heavily on Reflection, which means we can't use DCE. -->
<haxeflag name="-dce no" /> <haxeflag name="-dce no" />
<!-- Ensure all Funkin' classes are available at runtime. --> <!-- Ensure all Funkin' classes are available at runtime. -->
<haxeflag name="--macro" value="include('funkin')" /> <haxeflag name="--macro" value="include('funkin')" />
<!-- Ensure all UI components are available at runtime. --> <!-- Ensure all UI components are available at runtime. -->
<haxeflag name="--macro" value="include('haxe.ui.backend.flixel.components')" /> <haxeflag name="--macro" value="include('haxe.ui.backend.flixel.components')" />
<haxeflag name="--macro" value="include('haxe.ui.containers.dialogs')" /> <haxeflag name="--macro" value="include('haxe.ui.containers.dialogs')" />
<haxeflag name="--macro" value="include('haxe.ui.containers.menus')" /> <haxeflag name="--macro" value="include('haxe.ui.containers.menus')" />
<haxeflag name="--macro" value="include('haxe.ui.containers.properties')" /> <haxeflag name="--macro" value="include('haxe.ui.containers.properties')" />
<haxeflag name="--macro" value="include('haxe.ui.core')" /> <haxeflag name="--macro" value="include('haxe.ui.core')" />
<haxeflag name="--macro" value="include('haxe.ui.components')" /> <haxeflag name="--macro" value="include('haxe.ui.components')" />
<haxeflag name="--macro" value="include('haxe.ui.containers')" /> <haxeflag name="--macro" value="include('haxe.ui.containers')" />
<!-- <!--
Ensure additional class packages are available at runtime (some only really used by scripts). Ensure additional class packages are available at runtime (some only really used by scripts).
Ignore packages we can't include. Ignore packages we can't include.
--> -->
<haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" /> <haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" />
<!-- Necessary to provide stack traces for HScript. --> <!-- Necessary to provide stack traces for HScript. -->
<haxedef name="hscriptPos" /> <haxedef name="hscriptPos" />
<haxedef name="HXCPP_CHECK_POINTER" /> <haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" /> <haxedef name="HXCPP_STACK_LINE" />
<!-- This macro allows addition of new functionality to existing Flixel. --> <!-- This macro allows addition of new functionality to existing Flixel. -->
<haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" /> <haxeflag name="--macro" value="addMetadata('@:build(funkin.util.macro.FlxMacro.buildFlxBasic())', 'flixel.FlxBasic')" />
<!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)--> <!--Place custom nodes like icons here (higher priority to override the HaxeFlixel icon)-->
<icon path="art/icon16.png" size="16" />
<icon path="art/icon16.png" size='16' /> <icon path="art/icon32.png" size="32" />
<icon path="art/icon32.png" size='32' /> <icon path="art/icon64.png" size="64" />
<icon path="art/icon64.png" size='64' />
<icon path="art/iconOG.png" /> <icon path="art/iconOG.png" />
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> --> <!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
<haxedef name="CAN_OPEN_LINKS" unless="switch" /> <haxedef name="CAN_OPEN_LINKS" unless="switch" />
<haxedef name="CAN_CHEAT" if="switch debug" /> <haxedef name="CAN_CHEAT" if="switch debug" />
<haxedef name="haxeui_no_mouse_reset" /> <haxedef name="haxeui_no_mouse_reset" />
<!-- Skip the Intro --> <!-- Skip the Intro -->
<section if="debug"> <section if="debug">
<!-- Starts the game at the specified week, at the first song --> <!-- Starts the game at the specified week, at the first song -->
<!-- <haxedef name="week" value="1" if="debug"/> --> <!-- <haxedef name="week" value="1" if="debug"/> -->
<!-- Starts the game at the specified song --> <!-- Starts the game at the specified song -->
<!-- <haxedef name="song" value="bopeebo" if="debug"/> --> <!-- <haxedef name="song" value="bopeebo" if="debug"/> -->
<!-- Difficulty, only used for week or song, defaults to 1 --> <!-- Difficulty, only used for week or song, defaults to 1 -->
<!-- <haxedef name="dif" value="2" if="debug"/> --> <!-- <haxedef name="dif" value="2" if="debug"/> -->
</section> </section>
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> --> <!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
<section if="newgrounds"> <section if="newgrounds">
<!-- Enables Ng.core.verbose --> <!-- Enables Ng.core.verbose -->
<!-- <haxedef name="NG_VERBOSE" /> --> <!-- <haxedef name="NG_VERBOSE" /> -->
<!-- Enables a NG debug session, so medals don't permently unlock --> <!-- Enables a NG debug session, so medals don't permently unlock -->
<!-- <haxedef name="NG_DEBUG" /> --> <!-- <haxedef name="NG_DEBUG" /> -->
<!-- pretends that the saved session Id was expired, forcing the reconnect prompt --> <!-- pretends that the saved session Id was expired, forcing the reconnect prompt -->
<!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> --> <!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> -->
</section> </section>
<!-- <prebuild haxe="trace('prebuilding');"/> --> <!-- <prebuild haxe="trace('prebuilding');"/> -->
<!-- <postbuild haxe="art/Postbuild.hx"/> --> <!-- <postbuild haxe="art/Postbuild.hx"/> -->
<!-- <config:ios allow-provisioning-updates="true" team-id="" /> --> <!-- <config:ios allow-provisioning-updates="true" team-id="" /> -->
<!-- Options for Polymod --> <!-- Options for Polymod -->
<section if="polymod"> <section if="polymod">
<!-- Turns on additional debug logging. --> <!-- Turns on additional debug logging. -->
@ -247,14 +200,12 @@
<!-- Determines the file in the mod folder used for the icon. --> <!-- Determines the file in the mod folder used for the icon. -->
<haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" /> <haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" />
</section> </section>
<section if="TOOLS">
<section if='TOOLS'>
<!-- Compiles tool for old song conversion shit --> <!-- Compiles tool for old song conversion shit -->
<!-- Assumes you use it on windows/desktop!!!! --> <!-- Assumes you use it on windows/desktop!!!! -->
<postbuild command='haxe -main art/SongConverter.hx --cs export/songShit' /> <postbuild command="haxe -main art/SongConverter.hx --cs export/songShit" />
<assets path='export/songShit/bin/SongConverter.exe' rename='SongConverter.exe' /> <assets path="export/songShit/bin/SongConverter.exe" rename="SongConverter.exe" />
<!-- <postbuild command='ren export/songShit/bin export/songShit/tools '/> --> <!-- <postbuild command='ren export/songShit/bin export/songShit/tools '/> -->
<!-- <postbuild command='move export/songShit/tools export/release/windows/bin'/> --> <!-- <postbuild command='move export/songShit/tools export/release/windows/bin'/> -->
</section> </section>
</project> </project>

View file

@ -1,9 +1,11 @@
{ {
"title": "Intro Mod", "title": "Intro Mod",
"description": "An introductory mod.", "description": "An introductory mod.",
"contributors": [{ "contributors": [
"name": "MasterEric" {
}], "name": "MasterEric"
}
],
"api_version": "0.1.0", "api_version": "0.1.0",
"mod_version": "1.0.0", "mod_version": "1.0.0",
"license": "Apache-2.0" "license": "Apache-2.0"

View file

@ -1,9 +1,11 @@
{ {
"title": "Testing123", "title": "Testing123",
"description": "Newgrounds? More like OLDGROUNDS lol.", "description": "Newgrounds? More like OLDGROUNDS lol.",
"contributors": [{ "contributors": [
"name": "MasterEric" {
}], "name": "MasterEric"
}
],
"api_version": "0.1.0", "api_version": "0.1.0",
"mod_version": "1.0.0", "mod_version": "1.0.0",
"license": "Apache-2.0" "license": "Apache-2.0"

View file

@ -65,10 +65,10 @@
"version": "2.5.0" "version": "2.5.0"
}, },
{ {
"name": "hxcodec", "name": "hxCodec",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "91adeec", "ref": "c42ab99",
"url": "https://github.com/polybiusproxy/hxCodec" "url": "https://github.com/polybiusproxy/hxCodec"
}, },
{ {
@ -95,7 +95,7 @@
"name": "lime", "name": "lime",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "deecd6c", "ref": "5634ad7",
"url": "https://github.com/openfl/lime" "url": "https://github.com/openfl/lime"
}, },
{ {
@ -118,4 +118,4 @@
"version": "0.2.2" "version": "0.2.2"
} }
] ]
} }

View file

@ -125,8 +125,7 @@ class Alphabet extends FlxSpriteGroup
var xPos:Float = 0; var xPos:Float = 0;
var curRow:Int = 0; var curRow:Int = 0;
new FlxTimer().start(0.05, function(tmr:FlxTimer) new FlxTimer().start(0.05, function(tmr:FlxTimer) {
{
// trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum)); // trace(_finalText.fastCodeAt(loopNum) + " " + _finalText.charAt(loopNum));
if (_finalText.fastCodeAt(loopNum) == "\n".code) if (_finalText.fastCodeAt(loopNum) == "\n".code)
{ {

View file

@ -2,7 +2,7 @@ package funkin;
import flixel.FlxSubState; import flixel.FlxSubState;
class ButtonRemapSubstate extends FlxSubState class ButtonRemapSubState extends FlxSubState
{ {
public function new() public function new()
{ {

View file

@ -27,8 +27,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false); effectStuff.animation.addByPrefix('funny', 'NOTE COMBO animation', 24, false);
effectStuff.animation.play('funny'); effectStuff.animation.play('funny');
effectStuff.antialiasing = true; effectStuff.antialiasing = true;
effectStuff.animation.finishCallback = function(nameThing) effectStuff.animation.finishCallback = function(nameThing) {
{
kill(); kill();
}; };
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7)); effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
@ -42,8 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
{ {
if (onScreenTime < 0.9) if (onScreenTime < 0.9)
{ {
new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) new FlxTimer().start((Conductor.crochet / 1000) * 0.25, function(tmr) {
{
forceFinish(); forceFinish();
}); });
} }
@ -64,16 +62,14 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
if (effectStuff.animation.curAnim.curFrame == 18) if (effectStuff.animation.curAnim.curFrame == 18)
{ {
grpNumbers.forEach(function(spr:ComboNumber) grpNumbers.forEach(function(spr:ComboNumber) {
{
spr.animation.reset(); spr.animation.reset();
}); });
} }
if (effectStuff.animation.curAnim.curFrame == 20) if (effectStuff.animation.curAnim.curFrame == 20)
{ {
grpNumbers.forEach(function(spr:ComboNumber) grpNumbers.forEach(function(spr:ComboNumber) {
{
spr.kill(); spr.kill();
}); });
} }

View file

@ -50,6 +50,16 @@ class Conductor
// OLD, replaced with timeChanges. // OLD, replaced with timeChanges.
public static var bpmChangeMap:Array<BPMChangeEvent> = []; public static var bpmChangeMap:Array<BPMChangeEvent> = [];
/**
* Duration of a measure in milliseconds. Calculated based on bpm.
*/
public static var measureLengthMs(get, null):Float;
static function get_measureLengthMs():Float
{
return crochet * timeSignatureNumerator;
}
/** /**
* Duration of a beat in millisecond. Calculated based on bpm. * Duration of a beat in millisecond. Calculated based on bpm.
*/ */
@ -149,9 +159,9 @@ class Conductor
/** /**
* Forcibly defines the current BPM of the song. * Forcibly defines the current BPM of the song.
* Useful for things like the chart editor that need to manipulate BPM in real time. * Useful for things like the chart editor that need to manipulate BPM in real time.
* *
* Set to null to reset to the BPM defined by the timeChanges. * Set to null to reset to the BPM defined by the timeChanges.
* *
* WARNING: Avoid this for things like setting the BPM of the title screen music, * WARNING: Avoid this for things like setting the BPM of the title screen music,
* you should have a metadata file for it instead. * you should have a metadata file for it instead.
*/ */
@ -166,7 +176,7 @@ class Conductor
/** /**
* Update the conductor with the current song position. * Update the conductor with the current song position.
* BPM, current step, etc. will be re-calculated based on the song position. * BPM, current step, etc. will be re-calculated based on the song position.
* *
* @param songPosition The current position in the song in milliseconds. * @param songPosition The current position in the song in milliseconds.
* Leave blank to use the FlxG.sound.music position. * Leave blank to use the FlxG.sound.music position.
*/ */

View file

@ -20,13 +20,6 @@ import openfl.filters.ShaderFilter;
class CoolUtil class CoolUtil
{ {
public static var difficultyArray:Array<String> = ['EASY', "NORMAL", "HARD"];
public static function difficultyString():String
{
return difficultyArray[PlayState.storyDifficulty];
}
public static function coolBaseLog(base:Float, fin:Float):Float public static function coolBaseLog(base:Float, fin:Float):Float
{ {
return Math.log(fin) / Math.log(base); return Math.log(fin) / Math.log(base);
@ -119,8 +112,7 @@ class CoolUtil
FlxTween.tween(screenWipeShit, {daAlphaShit: 1}, time, FlxTween.tween(screenWipeShit, {daAlphaShit: 1}, time,
{ {
ease: FlxEase.quadInOut, ease: FlxEase.quadInOut,
onComplete: function(twn) onComplete: function(twn) {
{
screenShit.destroy(); screenShit.destroy();
FlxG.switchState(new MainMenuState()); FlxG.switchState(new MainMenuState());
} }
@ -130,7 +122,7 @@ class CoolUtil
/** /**
* Just saves the json with some default values hehe * Just saves the json with some default values hehe
* @param json * @param json
* @return String * @return String
*/ */
public static inline function jsonStringify(data:Dynamic):String public static inline function jsonStringify(data:Dynamic):String

View file

@ -1,80 +0,0 @@
package funkin;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.addons.display.FlxGridOverlay;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxPoint;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import openfl.Assets;
import openfl.display.BitmapData;
import openfl.display.MovieClip;
import openfl.display.Timeline;
import openfl.geom.Matrix;
import openfl.geom.Rectangle;
class CutsceneAnimTestState extends FlxState
{
var cutsceneGroup:CutsceneCharacter;
var curSelected:Int = 0;
var debugTxt:FlxText;
var funnySprite:FlxSprite = new FlxSprite();
var clip:MovieClip;
public function new()
{
super();
var gridBG:FlxSprite = FlxGridOverlay.create(10, 10);
gridBG.scrollFactor.set(0.5, 0.5);
add(gridBG);
debugTxt = new FlxText(900, 20, 0, "", 20);
debugTxt.color = FlxColor.BLUE;
add(debugTxt);
clip = Assets.getMovieClip("tanky:");
// clip.x = FlxG.width/2;
// clip.y = FlxG.height/2;
FlxG.stage.addChild(clip);
var swagShit:MovieClip = Assets.getMovieClip('tankBG:');
// swagShit.scaleX = 5;
FlxG.stage.addChild(swagShit);
swagShit.gotoAndStop(13);
var swfMountain = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000);
swfMountain.draw(swagShit, swagShit.transform.matrix);
var mountains:FlxSprite = new FlxSprite().loadGraphic(swfMountain);
// add(mountains);
FlxG.stage.removeChild(swagShit);
funnySprite.x = FlxG.width / 2;
funnySprite.y = FlxG.height / 2;
add(funnySprite);
}
override function update(elapsed:Float)
{
super.update(elapsed);
// jam sprite into top left corner
var drawMatrix:Matrix = clip.transform.matrix;
var bounds:Rectangle = clip.getBounds(null);
drawMatrix.tx = -bounds.x;
drawMatrix.ty = -bounds.y;
// make bitmapdata only as big as it needs to be
var funnyBmp:BitmapData = new BitmapData(Math.ceil(bounds.width), Math.ceil(bounds.height), true, 0x00000000);
funnyBmp.draw(clip, drawMatrix, true);
funnySprite.loadGraphic(funnyBmp);
// jam sprite back into place lol
funnySprite.offset.x = -bounds.x;
funnySprite.offset.y = -bounds.y;
}
}

View file

@ -1,75 +0,0 @@
package funkin;
import flixel.FlxSprite;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.math.FlxPoint;
class CutsceneCharacter extends FlxTypedGroup<FlxSprite>
{
public var coolPos:FlxPoint = FlxPoint.get();
public var animShit:Map<String, FlxPoint> = new Map();
var imageShit:String;
public function new(x:Float, y:Float, imageShit:String)
{
super();
coolPos.set(x, y);
this.imageShit = imageShit;
parseOffsets();
createCutscene(0);
}
// shitshow, oh well
var arrayLMFAOOOO:Array<String> = [];
function parseOffsets()
{
var splitShit:Array<String> = CoolUtil.coolTextFile(Paths.file('images/cutsceneStuff/' + imageShit + "CutsceneOffsets.txt"));
for (i in splitShit)
{
var xAndY:FlxPoint = FlxPoint.get();
var dumbSplit:Array<String> = i.split('---')[1].trim().split(' ');
trace('cool split: ' + i.split('---')[1]);
trace(dumbSplit);
xAndY.set(Std.parseFloat(dumbSplit[0]), Std.parseFloat(dumbSplit[1]));
animShit.set(i.split('---')[0].trim(), xAndY);
arrayLMFAOOOO.push(i.split('---')[0].trim());
}
trace(animShit);
}
public function createCutscene(daNum:Int = 0)
{
var cutScene:FlxSprite = new FlxSprite(coolPos.x + animShit.get(arrayLMFAOOOO[daNum]).x, coolPos.y + animShit.get(arrayLMFAOOOO[daNum]).y);
cutScene.frames = Paths.getSparrowAtlas('cutsceneStuff/' + imageShit + "-" + daNum);
cutScene.animation.addByPrefix('weed', arrayLMFAOOOO[daNum], 24, false);
cutScene.animation.play('weed');
cutScene.antialiasing = true;
cutScene.animation.finishCallback = function(anim:String)
{
cutScene.kill();
cutScene.destroy();
cutScene = null;
if (daNum + 1 < arrayLMFAOOOO.length) createCutscene(daNum + 1);
else
ended();
};
add(cutScene);
}
public var onFinish:Void->Void;
public function ended():Void
{
if (onFinish != null) onFinish();
}
}

View file

@ -38,7 +38,7 @@ class DialogueBox extends FlxSpriteGroup
{ {
super(); super();
switch (PlayState.currentSong.song.toLowerCase()) switch (PlayState.instance.currentSong.songId.toLowerCase())
{ {
case 'senpai': case 'senpai':
FlxG.sound.playMusic(Paths.music('Lunchbox'), 0); FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
@ -53,8 +53,7 @@ class DialogueBox extends FlxSpriteGroup
bgFade.alpha = 0; bgFade.alpha = 0;
add(bgFade); add(bgFade);
new FlxTimer().start(0.83, function(tmr:FlxTimer) new FlxTimer().start(0.83, function(tmr:FlxTimer) {
{
bgFade.alpha += (1 / 5) * 0.7; bgFade.alpha += (1 / 5) * 0.7;
if (bgFade.alpha > 0.7) bgFade.alpha = 0.7; if (bgFade.alpha > 0.7) bgFade.alpha = 0.7;
}, 5); }, 5);
@ -80,7 +79,7 @@ class DialogueBox extends FlxSpriteGroup
box = new FlxSprite(-20, 45); box = new FlxSprite(-20, 45);
var hasDialog:Bool = false; var hasDialog:Bool = false;
switch (PlayState.currentSong.song.toLowerCase()) switch (PlayState.instance.currentSong.songId.toLowerCase())
{ {
case 'senpai': case 'senpai':
hasDialog = true; hasDialog = true;
@ -152,8 +151,8 @@ class DialogueBox extends FlxSpriteGroup
override function update(elapsed:Float):Void override function update(elapsed:Float):Void
{ {
// HARD CODING CUZ IM STUPDI // HARD CODING CUZ IM STUPDI
if (PlayState.currentSong.song.toLowerCase() == 'roses') portraitLeft.visible = false; if (PlayState.instance.currentSong.songId.toLowerCase() == 'roses') portraitLeft.visible = false;
if (PlayState.currentSong.song.toLowerCase() == 'thorns') if (PlayState.instance.currentSong.songId.toLowerCase() == 'thorns')
{ {
portraitLeft.color = FlxColor.BLACK; portraitLeft.color = FlxColor.BLACK;
swagDialogue.color = FlxColor.WHITE; swagDialogue.color = FlxColor.WHITE;
@ -189,11 +188,10 @@ class DialogueBox extends FlxSpriteGroup
{ {
isEnding = true; isEnding = true;
if (PlayState.currentSong.song.toLowerCase() == 'senpai' if (PlayState.instance.currentSong.songId.toLowerCase() == 'senpai'
|| PlayState.currentSong.song.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0); || PlayState.instance.currentSong.songId.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0);
new FlxTimer().start(0.2, function(tmr:FlxTimer) new FlxTimer().start(0.2, function(tmr:FlxTimer) {
{
box.alpha -= 1 / 5; box.alpha -= 1 / 5;
bgFade.alpha -= 1 / 5 * 0.7; bgFade.alpha -= 1 / 5 * 0.7;
portraitLeft.visible = false; portraitLeft.visible = false;
@ -203,8 +201,7 @@ class DialogueBox extends FlxSpriteGroup
dropText.alpha = swagDialogue.alpha; dropText.alpha = swagDialogue.alpha;
}, 5); }, 5);
new FlxTimer().start(1.2, function(tmr:FlxTimer) new FlxTimer().start(1.2, function(tmr:FlxTimer) {
{
finishThing(); finishThing();
kill(); kill();
}); });
@ -233,8 +230,7 @@ class DialogueBox extends FlxSpriteGroup
// swagDialogue.text = ; // swagDialogue.text = ;
swagDialogue.resetText(dialogueList[0]); swagDialogue.resetText(dialogueList[0]);
swagDialogue.start(0.04); swagDialogue.start(0.04);
swagDialogue.completeCallback = function() swagDialogue.completeCallback = function() {
{
trace('dialogue finish'); trace('dialogue finish');
handSelect.visible = true; handSelect.visible = true;
dialogueEnded = true; dialogueEnded = true;

View file

@ -58,8 +58,7 @@ class DiscordClient
public static function initialize() public static function initialize()
{ {
var DiscordDaemon = sys.thread.Thread.create(() -> var DiscordDaemon = sys.thread.Thread.create(() -> {
{
new DiscordClient(); new DiscordClient();
}); });
trace("Discord Client initialized"); trace("Discord Client initialized");

View file

@ -34,12 +34,14 @@ import funkin.play.song.SongData.SongDataParser;
import funkin.shaderslmfao.AngleMask; import funkin.shaderslmfao.AngleMask;
import funkin.shaderslmfao.PureColor; import funkin.shaderslmfao.PureColor;
import funkin.shaderslmfao.StrokeShader; import funkin.shaderslmfao.StrokeShader;
import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song;
import lime.app.Future; import lime.app.Future;
import lime.utils.Assets; import lime.utils.Assets;
class FreeplayState extends MusicBeatSubstate class FreeplayState extends MusicBeatSubState
{ {
var songs:Array<SongMetadata> = []; var songs:Array<FreeplaySongData> = [];
// var selector:FlxText; // var selector:FlxText;
var curSelected:Int = 0; var curSelected:Int = 0;
@ -112,15 +114,15 @@ class FreeplayState extends MusicBeatSubstate
#if debug #if debug
isDebug = true; isDebug = true;
addSong('Test', 1, 'bf-pixel'); addSong('Test', 'tutorial', 'bf-pixel');
addSong('Pyro', 8, 'darnell'); addSong('Pyro', 'weekend1', 'darnell');
#end #end
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist')); var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
for (i in 0...initSonglist.length) for (i in 0...initSonglist.length)
{ {
songs.push(new SongMetadata(initSonglist[i], 1, 'gf')); songs.push(new FreeplaySongData(initSonglist[i], 'tutorial', 'gf'));
} }
if (FlxG.sound.music != null) if (FlxG.sound.music != null)
@ -128,22 +130,28 @@ class FreeplayState extends MusicBeatSubstate
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu')); if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu'));
} }
if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 1, ['dad']); // if (StoryMenuState.weekUnlocked[2] || isDebug)
addWeek(['Bopeebo', 'Fresh', 'Dadbattle'], 'week1', ['dad']);
if (StoryMenuState.weekUnlocked[2] || isDebug) addWeek(['Spookeez', 'South', 'Monster'], 2, ['spooky', 'spooky', 'monster']); // if (StoryMenuState.weekUnlocked[2] || isDebug)
addWeek(['Spookeez', 'South', 'Monster'], 'week2', ['spooky', 'spooky', 'monster']);
if (StoryMenuState.weekUnlocked[3] || isDebug) addWeek(['Pico', 'Philly', 'Blammed'], 3, ['pico']); // if (StoryMenuState.weekUnlocked[3] || isDebug)
addWeek(['Pico', 'Philly-Nice', 'Blammed'], 'week3', ['pico']);
if (StoryMenuState.weekUnlocked[4] || isDebug) addWeek(['Satin-Panties', 'High', 'Milf'], 4, ['mom']); // if (StoryMenuState.weekUnlocked[4] || isDebug)
addWeek(['Satin-Panties', 'High', 'MILF'], 'week4', ['mom']);
if (StoryMenuState.weekUnlocked[5] || isDebug) addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 5, // if (StoryMenuState.weekUnlocked[5] || isDebug)
['parents-christmas', 'parents-christmas', 'monster-christmas']); addWeek(['Cocoa', 'Eggnog', 'Winter-Horrorland'], 'week5', ['parents-christmas', 'parents-christmas', 'monster-christmas']);
if (StoryMenuState.weekUnlocked[6] || isDebug) addWeek(['Senpai', 'Roses', 'Thorns'], 6, ['senpai', 'senpai', 'spirit']); // if (StoryMenuState.weekUnlocked[6] || isDebug)
addWeek(['Senpai', 'Roses', 'Thorns'], 'week6', ['senpai', 'senpai', 'spirit']);
if (StoryMenuState.weekUnlocked[7] || isDebug) addWeek(['Ugh', 'Guns', 'Stress'], 7, ['tankman']); // if (StoryMenuState.weekUnlocked[7] || isDebug)
addWeek(['Ugh', 'Guns', 'Stress'], 'week7', ['tankman']);
addWeek(["Darnell", "lit-up", "2hot", "blazin"], 8, ['darnell']); addWeek(["Darnell", "lit-up", "2hot", "blazin"], 'weekend1', ['darnell']);
// LOAD MUSIC // LOAD MUSIC
@ -426,7 +434,7 @@ class FreeplayState extends MusicBeatSubstate
var swag:Alphabet = new Alphabet(1, 0, "swag"); var swag:Alphabet = new Alphabet(1, 0, "swag");
// JUST DOIN THIS SHIT FOR TESTING!!! // JUST DOIN THIS SHIT FOR TESTING!!!
/* /*
var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md')); var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md'));
var texFel:TextField = new TextField(); var texFel:TextField = new TextField();
@ -464,7 +472,7 @@ class FreeplayState extends MusicBeatSubstate
grpCapsules.clear(); grpCapsules.clear();
// var regexp:EReg = regexp; // var regexp:EReg = regexp;
var tempSongs:Array<SongMetadata> = songs; var tempSongs:Array<FreeplaySongData> = songs;
if (filterStuff != null) if (filterStuff != null)
{ {
@ -553,19 +561,19 @@ class FreeplayState extends MusicBeatSubstate
changeDiff(); changeDiff();
} }
public function addSong(songName:String, weekNum:Int, songCharacter:String) public function addSong(songName:String, levelId:String, songCharacter:String)
{ {
songs.push(new SongMetadata(songName, weekNum, songCharacter)); songs.push(new FreeplaySongData(songName, levelId, songCharacter));
} }
public function addWeek(songs:Array<String>, weekNum:Int, ?songCharacters:Array<String>) public function addWeek(songs:Array<String>, levelId:String, ?songCharacters:Array<String>)
{ {
if (songCharacters == null) songCharacters = ['bf']; if (songCharacters == null) songCharacters = ['bf'];
var num:Int = 0; var num:Int = 0;
for (song in songs) for (song in songs)
{ {
addSong(song, weekNum, songCharacters[num]); addSong(song, levelId, songCharacters[num]);
if (songCharacters.length != 1) num++; if (songCharacters.length != 1) num++;
} }
@ -851,11 +859,9 @@ class FreeplayState extends MusicBeatSubstate
curDifficulty = 1; curDifficulty = 1;
}*/ }*/
PlayState.currentSong = SongLoad.loadFromJson(poop, songs[curSelected].songName.toLowerCase()); PlayStatePlaylist.isStoryMode = false;
PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase()); var targetSong:Song = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
PlayState.isStoryMode = false; var targetDifficulty:String = switch (curDifficulty)
PlayState.storyDifficulty = curDifficulty;
PlayState.storyDifficulty_NEW = switch (curDifficulty)
{ {
case 0: case 0:
'easy'; 'easy';
@ -865,27 +871,41 @@ class FreeplayState extends MusicBeatSubstate
'hard'; 'hard';
default: 'normal'; default: 'normal';
}; };
// SongLoad.curDiff = Highscore.formatSong()
SongLoad.curDiff = PlayState.storyDifficulty_NEW; // TODO: Implement additional difficulties into the interface properly.
if (FlxG.keys.pressed.E)
{
targetDifficulty = 'erect';
}
PlayState.storyWeek = songs[curSelected].week; // TODO: Implement Pico into the interface properly.
// trace(' CUR WEEK ' + PlayState.storyWeek); var targetCharacter:String = 'bf';
if (FlxG.keys.pressed.P)
{
targetCharacter = 'pico';
}
PlayStatePlaylist.campaignId = songs[curSelected].levelId;
// Visual and audio effects. // Visual and audio effects.
FlxG.sound.play(Paths.sound('confirmMenu')); FlxG.sound.play(Paths.sound('confirmMenu'));
dj.confirm(); dj.confirm();
new FlxTimer().start(1, function(tmr:FlxTimer) { new FlxTimer().start(1, function(tmr:FlxTimer) {
LoadingState.loadAndSwitchState(new PlayState(), true); LoadingState.loadAndSwitchState(new PlayState(
{
targetSong: targetSong,
targetDifficulty: targetDifficulty,
targetCharacter: targetCharacter,
}), true);
}); });
} }
} }
override function startOutro(onComplete:() -> Void):Void override function switchTo(nextState:FlxState):Bool
{ {
clearDaCache(songs[curSelected].songName); clearDaCache(songs[curSelected].songName);
super.startOutro(onComplete); return super.switchTo(nextState);
} }
function changeDiff(change:Int = 0) function changeDiff(change:Int = 0)
@ -901,19 +921,6 @@ class FreeplayState extends MusicBeatSubstate
intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty); intendedScore = Highscore.getScore(songs[curSelected].songName, curDifficulty);
intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty); intendedCompletion = Highscore.getCompletion(songs[curSelected].songName, curDifficulty);
PlayState.storyDifficulty = curDifficulty;
PlayState.storyDifficulty_NEW = switch (curDifficulty)
{
case 0:
'easy';
case 1:
'normal';
case 2:
'hard';
default:
'normal';
};
grpDifficulties.group.forEach(function(spr) { grpDifficulties.group.forEach(function(spr) {
spr.visible = false; spr.visible = false;
}); });
@ -1040,17 +1047,17 @@ enum abstract FilterType(String)
var ALL; var ALL;
} }
class SongMetadata class FreeplaySongData
{ {
public var songName:String = ""; public var songName:String = "";
public var week:Int = 0; public var levelId:String = "";
public var songCharacter:String = ""; public var songCharacter:String = "";
public var isFav:Bool = false; public var isFav:Bool = false;
public function new(song:String, week:Int, songCharacter:String, ?isFav:Bool = false) public function new(song:String, levelId:String, songCharacter:String, ?isFav:Bool = false)
{ {
this.songName = song; this.songName = song;
this.week = week; this.levelId = levelId;
this.songCharacter = songCharacter; this.songCharacter = songCharacter;
this.isFav = isFav; this.isFav = isFav;
} }

View file

@ -11,12 +11,16 @@ class GitarooPause extends MusicBeatState
var replaySelect:Bool = false; var replaySelect:Bool = false;
public function new():Void var previousParams:PlayStateParams;
public function new(previousParams:PlayStateParams):Void
{ {
super(); super();
this.previousParams = previousParams;
} }
override function create() override function create():Void
{ {
if (FlxG.sound.music != null) FlxG.sound.music.stop(); if (FlxG.sound.music != null) FlxG.sound.music.stop();
@ -49,7 +53,7 @@ class GitarooPause extends MusicBeatState
super.create(); super.create();
} }
override function update(elapsed:Float) override function update(elapsed:Float):Void
{ {
if (controls.UI_LEFT_P || controls.UI_RIGHT_P) changeThing(); if (controls.UI_LEFT_P || controls.UI_RIGHT_P) changeThing();
@ -57,7 +61,7 @@ class GitarooPause extends MusicBeatState
{ {
if (replaySelect) if (replaySelect)
{ {
FlxG.switchState(new PlayState()); FlxG.switchState(new PlayState(previousParams));
} }
else else
{ {

View file

@ -39,7 +39,17 @@ class Highscore
return false; return false;
} }
public static function saveCompletion(song:String, completion:Float, ?diff:Int = 0):Bool public static function saveScoreForDifficulty(song:String, score:Int = 0, diff:String = 'normal'):Bool
{
var diffInt:Int = 1;
if (diff == 'easy') diffInt = 0;
else if (diff == 'hard') diffInt = 2;
return saveScore(song, score, diffInt);
}
public static function saveCompletion(song:String, completion:Float, diff:Int = 0):Bool
{ {
var formattedSong:String = formatSong(song, diff); var formattedSong:String = formatSong(song, diff);
@ -57,20 +67,42 @@ class Highscore
return false; return false;
} }
public static function saveWeekScore(week:Int = 1, score:Int = 0, ?diff:Int = 0):Void public static function saveCompletionForDifficulty(song:String, completion:Float, diff:String = 'normal'):Bool
{
var diffInt:Int = 1;
if (diff == 'easy') diffInt = 0;
else if (diff == 'hard') diffInt = 2;
return saveCompletion(song, completion, diffInt);
}
public static function saveWeekScore(week:String, score:Int = 0, diff:Int = 0):Void
{ {
#if newgrounds #if newgrounds
NGio.postScore(score, "Week " + week); NGio.postScore(score, 'Campaign ID $week');
#end #end
var formattedSong:String = formatSong('week' + week, diff); var formattedSong:String = formatSong(week, diff);
if (songScores.exists(formattedSong)) if (songScores.exists(formattedSong))
{ {
if (songScores.get(formattedSong) < score) setScore(formattedSong, score); if (songScores.get(formattedSong) < score) setScore(formattedSong, score);
} }
else else
{
setScore(formattedSong, score); setScore(formattedSong, score);
}
}
public static function saveWeekScoreForDifficulty(week:String, score:Int = 0, diff:String = 'normal'):Void
{
var diffInt:Int = 1;
if (diff == 'easy') diffInt = 0;
else if (diff == 'hard') diffInt = 2;
saveWeekScore(week, score, diffInt);
} }
static function setCompletion(formattedSong:String, completion:Float):Void static function setCompletion(formattedSong:String, completion:Float):Void
@ -122,7 +154,7 @@ class Highscore
return songCompletion.get(formatSong(song, diff)); return songCompletion.get(formatSong(song, diff));
} }
public static function getAllScores() public static function getAllScores():Void
{ {
trace(songScores.toString()); trace(songScores.toString());
} }

View file

@ -12,7 +12,7 @@ import flixel.util.FlxColor;
import funkin.modding.module.ModuleHandler; import funkin.modding.module.ModuleHandler;
import funkin.play.PlayState; import funkin.play.PlayState;
import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.event.SongEvent.SongEventParser; import funkin.play.event.SongEventData.SongEventParser;
import funkin.play.song.SongData.SongDataParser; import funkin.play.song.SongData.SongDataParser;
import funkin.ui.PreferencesMenu; import funkin.ui.PreferencesMenu;
import funkin.util.WindowUtil; import funkin.util.WindowUtil;
@ -140,10 +140,10 @@ class InitState extends FlxTransitionableState
// WEEK UNLOCK PROGRESSION!! // WEEK UNLOCK PROGRESSION!!
// StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked; // StoryMenuState.weekUnlocked = FlxG.save.data.weekUnlocked;
if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true); // if (StoryMenuState.weekUnlocked.length < 4) StoryMenuState.weekUnlocked.insert(0, true);
// QUICK PATCH OOPS! // QUICK PATCH OOPS!
if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true; // if (!StoryMenuState.weekUnlocked[0]) StoryMenuState.weekUnlocked[0] = true;
} }
if (FlxG.save.data.seenVideo != null) VideoState.seenVideo = FlxG.save.data.seenVideo; if (FlxG.save.data.seenVideo != null) VideoState.seenVideo = FlxG.save.data.seenVideo;
@ -237,20 +237,18 @@ class InitState extends FlxTransitionableState
{ {
var dif:Int = getDif(); var dif:Int = getDif();
PlayState.currentSong = SongLoad.loadFromJson(song, song); var targetDifficulty = switch (dif)
PlayState.currentSong_NEW = SongDataParser.fetchSong(song);
PlayState.isStoryMode = isStoryMode;
PlayState.storyDifficulty = dif;
PlayState.storyDifficulty_NEW = switch (dif)
{ {
case 0: 'easy'; case 0: 'easy';
case 1: 'normal'; case 1: 'normal';
case 2: 'hard'; case 2: 'hard';
default: 'normal'; default: 'normal';
}; };
SongLoad.curDiff = PlayState.storyDifficulty_NEW; LoadingState.loadAndSwitchState(new PlayState(
PlayState.storyWeek = week; {
LoadingState.loadAndSwitchState(new PlayState()); targetSong: SongDataParser.fetchSong(song),
targetDifficulty: targetDifficulty,
}));
} }
} }

View file

@ -14,7 +14,7 @@ import funkin.ui.CoolStatsGraph;
import haxe.Timer; import haxe.Timer;
import openfl.events.KeyboardEvent; import openfl.events.KeyboardEvent;
class LatencyState extends MusicBeatSubstate class LatencyState extends MusicBeatSubState
{ {
var offsetText:FlxText; var offsetText:FlxText;
var noteGrp:FlxTypedGroup<Note>; var noteGrp:FlxTypedGroup<Note>;

View file

@ -1,5 +1,6 @@
package funkin; package funkin;
import funkin.play.PlayStatePlaylist;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.FlxState; import flixel.FlxState;
import flixel.math.FlxMath; import flixel.math.FlxMath;
@ -32,7 +33,7 @@ class LoadingState extends MusicBeatState
this.stopMusic = stopMusic; this.stopMusic = stopMusic;
} }
override function create() override function create():Void
{ {
var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d); var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, 0xFFcaff4d);
add(bg); add(bg);
@ -50,63 +51,59 @@ class LoadingState extends MusicBeatState
loadBar.screenCenter(X); loadBar.screenCenter(X);
add(loadBar); add(loadBar);
initSongsManifest().onComplete(function(lib) initSongsManifest().onComplete(function(lib) {
{
callbacks = new MultiCallback(onLoad); callbacks = new MultiCallback(onLoad);
var introComplete = callbacks.add("introComplete"); var introComplete = callbacks.add('introComplete');
checkLoadSong(getSongPath()); // checkLoadSong(getSongPath());
if (PlayState.currentSong.needsVoices) // if (PlayState.currentSong.needsVoices)
{ // {
var files = PlayState.currentSong.voiceList; // var files = PlayState.currentSong.voiceList;
//
// if (files == null) files = ['']; // loads with no file name assumption, to load 'Voices.ogg' or whatev normally
//
// for (sndFile in files)
// {
// checkLoadSong(getVocalPath(sndFile));
// }
// }
if (files == null) files = [""]; // loads with no file name assumption, to load "Voices.ogg" or whatev normally checkLibrary('shared');
checkLibrary(PlayStatePlaylist.campaignId);
checkLibrary('tutorial');
for (sndFile in files) var fadeTime:Float = 0.5;
{
checkLoadSong(getVocalPath(sndFile));
}
}
checkLibrary("shared");
if (PlayState.storyWeek > 0) checkLibrary("week" + PlayState.storyWeek);
else
checkLibrary("tutorial");
var fadeTime = 0.5;
FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true); FlxG.camera.fade(FlxG.camera.bgColor, fadeTime, true);
new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete()); new FlxTimer().start(fadeTime + MIN_TIME, function(_) introComplete());
}); });
} }
function checkLoadSong(path:String) function checkLoadSong(path:String):Void
{ {
if (!Assets.cache.hasSound(path)) if (!Assets.cache.hasSound(path))
{ {
var library = Assets.getLibrary("songs"); var library = Assets.getLibrary('songs');
var symbolPath = path.split(":").pop(); var symbolPath = path.split(':').pop();
// @:privateAccess // @:privateAccess
// library.types.set(symbolPath, SOUND); // library.types.set(symbolPath, SOUND);
// @:privateAccess // @:privateAccess
// library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]); // library.pathGroups.set(symbolPath, [library.__cacheBreak(symbolPath)]);
var callback = callbacks.add("song:" + path); var callback = callbacks.add('song:' + path);
Assets.loadSound(path).onComplete(function(_) Assets.loadSound(path).onComplete(function(_) {
{
callback(); callback();
}); });
} }
} }
function checkLibrary(library:String) function checkLibrary(library:String):Void
{ {
trace(Assets.hasLibrary(library)); trace(Assets.hasLibrary(library));
if (Assets.getLibrary(library) == null) if (Assets.getLibrary(library) == null)
{ {
@:privateAccess @:privateAccess
if (!LimeAssets.libraryPaths.exists(library)) throw "Missing library: " + library; if (!LimeAssets.libraryPaths.exists(library)) throw 'Missing library: ' + library;
var callback = callbacks.add("library:" + library); var callback = callbacks.add('library:' + library);
Assets.loadLibrary(library).onComplete(function(_) Assets.loadLibrary(library).onComplete(function(_) {
{
callback(); callback();
}); });
} }
@ -124,7 +121,7 @@ class LoadingState extends MusicBeatState
var targetShit:Float = 0; var targetShit:Float = 0;
override function update(elapsed:Float) override function update(elapsed:Float):Void
{ {
super.update(elapsed); super.update(elapsed);
@ -150,57 +147,41 @@ class LoadingState extends MusicBeatState
} }
#if debug #if debug
if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + " unfired:" + callbacks.getUnfired()); if (FlxG.keys.justPressed.SPACE) trace('fired: ' + callbacks.getFired() + ' unfired:' + callbacks.getUnfired());
#end #end
} }
function onLoad() function onLoad():Void
{ {
if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
FlxG.switchState(target); FlxG.switchState(target);
} }
static function getSongPath() static function getSongPath():String
{ {
return Paths.inst(PlayState.currentSong.song); return Paths.inst(PlayState.instance.currentSong.songId);
} }
static function getVocalPath(?suffix:String) inline static public function loadAndSwitchState(nextState:FlxState, shouldStopMusic = false):Void
{ {
return Paths.voices(PlayState.currentSong.song, suffix); FlxG.switchState(getNextState(nextState, shouldStopMusic));
} }
inline static public function loadAndSwitchState(target:FlxState, stopMusic = false) static function getNextState(nextState:FlxState, shouldStopMusic = false):FlxState
{ {
FlxG.switchState(getNextState(target, stopMusic)); Paths.setCurrentLevel(PlayStatePlaylist.campaignId);
}
static function getNextState(target:FlxState, stopMusic = false):FlxState
{
if (PlayState.storyWeek == 0)
{
Paths.setCurrentLevel('tutorial');
}
else if (PlayState.storyWeek == 8)
{
// TODO: Refactor this code.
Paths.setCurrentLevel("weekend1");
}
else
{
Paths.setCurrentLevel("week" + PlayState.storyWeek);
}
#if NO_PRELOAD_ALL #if NO_PRELOAD_ALL
var loaded = isSoundLoaded(getSongPath()) // var loaded = isSoundLoaded(getSongPath())
&& (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath())) // && (!PlayState.currentSong.needsVoices || isSoundLoaded(getVocalPath()))
&& isLibraryLoaded("shared"); // && isLibraryLoaded('shared');
//
if (!loaded) return new LoadingState(target, stopMusic); if (true) return new LoadingState(nextState, shouldStopMusic);
#end #end
if (stopMusic && FlxG.sound.music != null) FlxG.sound.music.stop(); if (shouldStopMusic && FlxG.sound.music != null) FlxG.sound.music.stop();
return target; return nextState;
} }
#if NO_PRELOAD_ALL #if NO_PRELOAD_ALL
@ -215,16 +196,16 @@ class LoadingState extends MusicBeatState
} }
#end #end
override function destroy() override function destroy():Void
{ {
super.destroy(); super.destroy();
callbacks = null; callbacks = null;
} }
static function initSongsManifest() static function initSongsManifest():Future<AssetLibrary>
{ {
var id = "songs"; var id = 'songs';
var promise = new Promise<AssetLibrary>(); var promise = new Promise<AssetLibrary>();
var library = LimeAssets.getLibrary(id); var library = LimeAssets.getLibrary(id);
@ -246,10 +227,10 @@ class LoadingState extends MusicBeatState
} }
else else
{ {
if (path.endsWith(".bundle")) if (path.endsWith('.bundle'))
{ {
rootPath = path; rootPath = path;
path += "/library.json"; path += '/library.json';
} }
else else
{ {
@ -259,11 +240,10 @@ class LoadingState extends MusicBeatState
path = LimeAssets.__cacheBreak(path); path = LimeAssets.__cacheBreak(path);
} }
AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) AssetManifest.loadFromFile(path, rootPath).onComplete(function(manifest) {
{
if (manifest == null) if (manifest == null)
{ {
promise.error("Cannot parse asset manifest for library \"" + id + "\""); promise.error('Cannot parse asset manifest for library \'' + id + '\'');
return; return;
} }
@ -271,7 +251,7 @@ class LoadingState extends MusicBeatState
if (library == null) if (library == null)
{ {
promise.error("Cannot open library \"" + id + "\""); promise.error('Cannot open library \'' + id + '\'');
} }
else else
{ {
@ -280,9 +260,8 @@ class LoadingState extends MusicBeatState
library.onChange.add(LimeAssets.onChange.dispatch); library.onChange.add(LimeAssets.onChange.dispatch);
promise.completeWith(Future.withValue(library)); promise.completeWith(Future.withValue(library));
} }
}).onError(function(_) }).onError(function(_) {
{ promise.error('There is no asset library with an ID of \'' + id + '\'');
promise.error("There is no asset library with an ID of \"" + id + "\"");
}); });
return promise.future; return promise.future;
@ -305,14 +284,13 @@ class MultiCallback
this.logId = logId; this.logId = logId;
} }
public function add(id = "untitled") public function add(id = 'untitled'):Void->Void
{ {
id = '$length:$id'; id = '$length:$id';
length++; length++;
numRemaining++; numRemaining++;
var func:Void->Void = null; var func:Void->Void = null;
func = function() func = function() {
{
if (unfired.exists(id)) if (unfired.exists(id))
{ {
unfired.remove(id); unfired.remove(id);
@ -339,9 +317,9 @@ class MultiCallback
if (logId != null) trace('$logId: $msg'); if (logId != null) trace('$logId: $msg');
} }
public function getFired() public function getFired():Array<String>
return fired.copy(); return fired.copy();
public function getUnfired() public function getUnfired():Array<Void->Void>
return unfired.array(); return unfired.array();
} }

View file

@ -223,8 +223,8 @@ class MainMenuState extends MusicBeatState
/** /**
* Calls openPrompt and redraws the login/logout button * Calls openPrompt and redraws the login/logout button
* @param prompt * @param prompt
* @param onClose * @param onClose
*/ */
public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void) public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
{ {

View file

@ -24,8 +24,7 @@ class MemoryCounter extends TextField
text = "RAM: "; text = "RAM: ";
#if flash #if flash
addEventListener(Event.ENTER_FRAME, function(e) addEventListener(Event.ENTER_FRAME, function(e) {
{
var time = Lib.getTimer(); var time = Lib.getTimer();
__enterFrame(time - currentTime); __enterFrame(time - currentTime);
}); });

View file

@ -35,8 +35,8 @@ class MusicBeatState extends FlxUIState
function initCallbacks() function initCallbacks()
{ {
subStateOpened.add(onOpenSubstateComplete); subStateOpened.add(onOpenSubStateComplete);
subStateClosed.add(onCloseSubstateComplete); subStateClosed.add(onCloseSubStateComplete);
} }
override function create() override function create()
@ -162,18 +162,18 @@ class MusicBeatState extends FlxUIState
} }
} }
public override function openSubState(targetSubstate:FlxSubState):Void public override function openSubState(targetSubState:FlxSubState):Void
{ {
var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubstate, true); var event = new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_BEGIN, targetSubState, true);
dispatchEvent(event); dispatchEvent(event);
if (event.eventCanceled) return; if (event.eventCanceled) return;
super.openSubState(targetSubstate); super.openSubState(targetSubState);
} }
function onOpenSubstateComplete(targetState:FlxSubState):Void function onOpenSubStateComplete(targetState:FlxSubState):Void
{ {
dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_END, targetState, true)); dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_OPEN_END, targetState, true));
} }
@ -189,7 +189,7 @@ class MusicBeatState extends FlxUIState
super.closeSubState(); super.closeSubState();
} }
function onCloseSubstateComplete(targetState:FlxSubState):Void function onCloseSubStateComplete(targetState:FlxSubState):Void
{ {
dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_END, targetState, true)); dispatchEvent(new SubStateScriptEvent(ScriptEvent.SUBSTATE_CLOSE_END, targetState, true));
} }

View file

@ -7,9 +7,9 @@ import funkin.modding.events.ScriptEvent;
import funkin.modding.module.ModuleHandler; import funkin.modding.module.ModuleHandler;
/** /**
* MusicBeatSubstate reincorporates the functionality of MusicBeatState into an FlxSubState. * MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
*/ */
class MusicBeatSubstate extends FlxSubState class MusicBeatSubState extends FlxSubState
{ {
public function new(bgColor:FlxColor = FlxColor.TRANSPARENT) public function new(bgColor:FlxColor = FlxColor.TRANSPARENT)
{ {

View file

@ -49,8 +49,7 @@ class NGio
trace('checking NG.io version'); trace('checking NG.io version');
GAME_VER = "v" + Application.current.meta.get('version'); GAME_VER = "v" + Application.current.meta.get('version');
NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response) NG.core.calls.app.getCurrentVersion(GAME_VER).addDataHandler(function(response) {
{
GAME_VER = response.result.data.currentVersion; GAME_VER = response.result.data.currentVersion;
trace('CURRENT NG VERSION: ' + GAME_VER); trace('CURRENT NG VERSION: ' + GAME_VER);
callback(GAME_VER); callback(GAME_VER);
@ -141,8 +140,7 @@ class NGio
var onCancel:Void->Void = null; var onCancel:Void->Void = null;
if (onComplete != null) if (onComplete != null)
{ {
onSuccess = function() onSuccess = function() {
{
onNGLogin(); onNGLogin();
onComplete(Success); onComplete(Success);
} }
@ -228,7 +226,7 @@ class NGio
scoreboardsLoaded = true; scoreboardsLoaded = true;
ngScoresLoaded.dispatch(); ngScoresLoaded.dispatch();
/* /*
for (score in NG.core.scoreBoards.get(8737).scores) for (score in NG.core.scoreBoards.get(8737).scores)
{ {
trace('score loaded user:${score.user.name}, score:${score.formatted_value}'); trace('score loaded user:${score.user.name}, score:${score.formatted_value}');

View file

@ -1,5 +1,6 @@
package funkin; package funkin;
import funkin.play.Strumline.StrumlineArrow;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.math.FlxMath; import flixel.math.FlxMath;
import funkin.noteStuff.NoteBasic.NoteData; import funkin.noteStuff.NoteBasic.NoteData;
@ -15,9 +16,9 @@ class Note extends FlxSprite
public var data = new NoteData(); public var data = new NoteData();
/** /**
* code colors for.... code.... * code colors for.... code....
* i think goes in order of left to right * i think goes in order of left to right
* *
* left 0 * left 0
* down 1 * down 1
* up 2 * up 2
@ -215,6 +216,24 @@ class Note extends FlxSprite
} }
} }
public function alignToSturmlineArrow(arrow:StrumlineArrow):Void
{
x = arrow.x;
if (isSustainNote && prevNote != null)
{
if (prevNote.isSustainNote)
{
x = prevNote.x;
}
else
{
x += prevNote.width / 2;
x -= width / 2;
}
}
}
override function destroy() override function destroy()
{ {
prevNote = null; prevNote = null;

View file

@ -34,8 +34,7 @@ class NoteSplash extends FlxSprite
animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true); animation.play('note' + noteData + '-' + FlxG.random.int(0, 1), true);
animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2); animation.curAnim.frameRate = 24 + FlxG.random.int(-2, 2);
animation.finishCallback = function(name) animation.finishCallback = function(name) {
{
kill(); kill();
}; };
updateHitbox(); updateHitbox();

View file

@ -1,9 +1,10 @@
package funkin; package funkin;
import funkin.play.PlayStatePlaylist;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.sound.FlxSound; import flixel.system.FlxSound;
import flixel.text.FlxText; import flixel.text.FlxText;
import flixel.tweens.FlxEase; import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
@ -11,7 +12,7 @@ import flixel.util.FlxColor;
import funkin.play.PlayState; import funkin.play.PlayState;
import funkin.play.song.SongData.SongDataParser; import funkin.play.song.SongData.SongDataParser;
class PauseSubState extends MusicBeatSubstate class PauseSubState extends MusicBeatSubState
{ {
var grpMenuShit:FlxTypedGroup<Alphabet>; var grpMenuShit:FlxTypedGroup<Alphabet>;
@ -20,9 +21,9 @@ class PauseSubState extends MusicBeatSubstate
'Restart Song', 'Restart Song',
'Change Difficulty', 'Change Difficulty',
'Toggle Practice Mode', 'Toggle Practice Mode',
'Exit to menu' 'Exit to Menu'
]; ];
var difficultyChoices:Array<String> = ['EASY', 'NORMAL', 'HARD', 'BACK']; var difficultyChoices:Array<String> = ['EASY', 'NORMAL', 'HARD', 'ERECT', 'BACK'];
var menuItems:Array<String> = []; var menuItems:Array<String> = [];
var curSelected:Int = 0; var curSelected:Int = 0;
@ -41,10 +42,14 @@ class PauseSubState extends MusicBeatSubstate
menuItems = pauseOG; menuItems = pauseOG;
if (PlayState.storyWeek == 6) // consistent with logic that decides asset lib!! if (PlayStatePlaylist.campaignId == 'week6')
{
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast-pixel'), true, true); pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast-pixel'), true, true);
}
else else
{
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true); pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true);
}
pauseMusic.volume = 0; pauseMusic.volume = 0;
pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2))); pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2)));
@ -58,43 +63,38 @@ class PauseSubState extends MusicBeatSubstate
metaDataGrp = new FlxTypedGroup<FlxSprite>(); metaDataGrp = new FlxTypedGroup<FlxSprite>();
add(metaDataGrp); add(metaDataGrp);
var levelInfo:FlxText = new FlxText(20, 15, 0, "", 32); var levelInfo:FlxText = new FlxText(20, 15, 0, '', 32);
if (PlayState.instance.currentChart != null) if (PlayState.instance.currentChart != null)
{ {
levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}'; levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}';
} }
else
{
levelInfo.text += PlayState.currentSong.song;
}
levelInfo.scrollFactor.set(); levelInfo.scrollFactor.set();
levelInfo.setFormat(Paths.font("vcr.ttf"), 32); levelInfo.setFormat(Paths.font('vcr.ttf'), 32);
levelInfo.updateHitbox(); levelInfo.updateHitbox();
metaDataGrp.add(levelInfo); metaDataGrp.add(levelInfo);
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, "", 32); var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, '', 32);
levelDifficulty.text += CoolUtil.difficultyString(); levelDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase();
levelDifficulty.scrollFactor.set(); levelDifficulty.scrollFactor.set();
levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32); levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32);
levelDifficulty.updateHitbox(); levelDifficulty.updateHitbox();
metaDataGrp.add(levelDifficulty); metaDataGrp.add(levelDifficulty);
var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, "", 32); var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, '', 32);
deathCounter.text = "Blue balled: " + PlayState.deathCounter; deathCounter.text = 'Blue balled: ${PlayState.instance.deathCounter}';
deathCounter.text += "\n" + Highscore.tallies.totalNotesHit; FlxG.watch.addQuick('totalNotesHit', Highscore.tallies.totalNotesHit);
deathCounter.text += "\n" + Highscore.tallies.totalNotes; FlxG.watch.addQuick('totalNotes', Highscore.tallies.totalNotes);
deathCounter.text += "\n" + Std.string(Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes);
deathCounter.scrollFactor.set(); deathCounter.scrollFactor.set();
deathCounter.setFormat(Paths.font('vcr.ttf'), 32); deathCounter.setFormat(Paths.font('vcr.ttf'), 32);
deathCounter.updateHitbox(); deathCounter.updateHitbox();
metaDataGrp.add(deathCounter); metaDataGrp.add(deathCounter);
practiceText = new FlxText(20, 15 + 64 + 32, 0, "PRACTICE MODE", 32); practiceText = new FlxText(20, 15 + 64 + 32, 0, 'PRACTICE MODE', 32);
practiceText.scrollFactor.set(); practiceText.scrollFactor.set();
practiceText.setFormat(Paths.font('vcr.ttf'), 32); practiceText.setFormat(Paths.font('vcr.ttf'), 32);
practiceText.updateHitbox(); practiceText.updateHitbox();
practiceText.x = FlxG.width - (practiceText.width + 20); practiceText.x = FlxG.width - (practiceText.width + 20);
practiceText.visible = PlayState.isPracticeMode; practiceText.visible = PlayState.instance.isPracticeMode;
metaDataGrp.add(practiceText); metaDataGrp.add(practiceText);
levelDifficulty.alpha = 0; levelDifficulty.alpha = 0;
@ -137,7 +137,7 @@ class PauseSubState extends MusicBeatSubstate
changeSelection(); changeSelection();
} }
override function update(elapsed:Float) override function update(elapsed:Float):Void
{ {
if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed; if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed;
@ -180,41 +180,39 @@ class PauseSubState extends MusicBeatSubstate
{ {
var daSelected:String = menuItems[curSelected]; var daSelected:String = menuItems[curSelected];
// TODO: Why is this based on the menu item's name? Make this an enum or something.
switch (daSelected) switch (daSelected)
{ {
case "Resume": case 'Resume':
close(); close();
case "EASY" | 'NORMAL' | "HARD":
PlayState.currentSong = SongLoad.loadFromJson(PlayState.currentSong.song.toLowerCase(), PlayState.currentSong.song.toLowerCase());
PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.currentSong.song.toLowerCase());
SongLoad.curDiff = daSelected.toLowerCase();
PlayState.storyDifficulty = curSelected;
PlayState.storyDifficulty_NEW = daSelected.toLowerCase();
PlayState.needsReset = true;
close();
case 'Toggle Practice Mode':
PlayState.isPracticeMode = !PlayState.isPracticeMode;
practiceText.visible = PlayState.isPracticeMode;
case 'Change Difficulty': case 'Change Difficulty':
menuItems = difficultyChoices; menuItems = difficultyChoices;
regenMenu(); regenMenu();
case 'EASY' | 'NORMAL' | 'HARD' | 'ERECT':
PlayState.instance.currentSong = SongDataParser.fetchSong(PlayState.instance.currentSong.songId.toLowerCase());
PlayState.instance.currentDifficulty = daSelected.toLowerCase();
PlayState.instance.needsReset = true;
close();
case 'BACK': case 'BACK':
menuItems = pauseOG; menuItems = pauseOG;
regenMenu(); regenMenu();
case "Restart Song":
PlayState.needsReset = true;
case 'Toggle Practice Mode':
PlayState.instance.isPracticeMode = true;
practiceText.visible = PlayState.instance.isPracticeMode;
case 'Restart Song':
PlayState.instance.needsReset = true;
close(); close();
// FlxG.resetState();
case "Exit to menu": case 'Exit to Menu':
exitingToMenu = true; exitingToMenu = true;
PlayState.seenCutscene = false; PlayState.instance.deathCounter = 0;
PlayState.deathCounter = 0;
for (item in grpMenuShit.members) for (item in grpMenuShit.members)
{ {
@ -225,9 +223,9 @@ class PauseSubState extends MusicBeatSubstate
FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true; FlxTransitionableState.skipNextTransOut = true;
if (PlayState.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY)); if (PlayStatePlaylist.isStoryMode) openSubState(new funkin.ui.StickerSubState(null, STORY));
else else
openSubState(new funkin.ui.StickerSubState()); openSubState(new funkin.ui.StickerSubState(null, FREEPLAY));
} }
} }
@ -239,7 +237,7 @@ class PauseSubState extends MusicBeatSubstate
} }
} }
override function destroy() override function destroy():Void
{ {
pauseMusic.destroy(); pauseMusic.destroy();
@ -260,12 +258,10 @@ class PauseSubState extends MusicBeatSubstate
item.targetY = index - curSelected; item.targetY = index - curSelected;
item.alpha = 0.6; item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0) if (item.targetY == 0)
{ {
item.alpha = 1; item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
} }
} }
} }

View file

@ -281,7 +281,7 @@ class SongLoad
// castNoteDataToNoteData(swagShit.noteMap[diff]); // castNoteDataToNoteData(swagShit.noteMap[diff]);
/* /*
switch (diff) switch (diff)
{ {
case "easy": case "easy":

View file

@ -1,497 +0,0 @@
package funkin;
import flixel.FlxSprite;
import flixel.addons.transition.FlxTransitionableState;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.group.FlxGroup;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import funkin.MenuItem.WeekType;
import funkin.play.PlayState;
import funkin.play.song.SongData.SongDataParser;
import lime.net.curl.CURLCode;
import openfl.Assets;
import funkin.ui.StickerSubState;
#if discord_rpc
import Discord.DiscordClient;
#end
class StoryMenuState extends MusicBeatState
{
var scoreText:FlxText;
var weekData:Array<Array<String>> = [
['Tutorial'],
['Bopeebo', 'Fresh', 'Dadbattle'],
['Spookeez', 'South', "Monster"],
['Pico', 'Philly', "Blammed"],
['Satin-Panties', "High", "Milf"],
['Cocoa', 'Eggnog', 'Winter-Horrorland'],
['Senpai', 'Roses', 'Thorns'],
['Ugh', 'Guns', 'Stress'],
['Darnell', "lit-up", "2hot", "blazin"]
];
var curDifficulty:Int = 1;
// TODO: This info is just hardcoded right now.
// We should probably make it so that weeks must be completed in order to unlock the next week.
public static var weekUnlocked:Array<Bool> = [true, true, true, true, true, true, true, true, true];
var weekCharacters:Array<Dynamic> = [
['dad', 'bf', 'gf'],
['dad', 'bf', 'gf'],
['spooky', 'bf', 'gf'],
['pico', 'bf', 'gf'],
['mom', 'bf', 'gf'],
['parents-christmas', 'bf', 'gf'],
['senpai', 'bf', 'gf'],
['tankman', 'bf', 'gf'],
['darnell', 'pico', 'nene']
];
var weekNames:Array<String> = [
"",
"Daddy Dearest",
"Spooky Month",
"PICO",
"MOMMY MUST MURDER",
"RED SNOW",
"hating simulator ft. moawling",
"TANKMAN",
"Due Debts"
];
var weekType:Array<WeekType> = [WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEK, WEEKEND];
var weekTypeInc:Map<WeekType, Int> = new Map();
var txtWeekTitle:FlxText;
var curWeek:Int = 0;
var txtTracklist:FlxText;
var grpWeekText:FlxTypedGroup<MenuItem>;
var grpWeekCharacters:Array<FlxTypedGroup<MenuCharacter>>;
// var grpWeekCharacters:FlxTypedGroup<MenuCharacter>;
var grpLocks:FlxTypedGroup<FlxSprite>;
var difficultySelectors:FlxGroup;
var sprDifficulty:FlxSprite;
var leftArrow:FlxSprite;
var rightArrow:FlxSprite;
var yellowBG:FlxSprite; // not actually, yellow, lol!
var targetColor:Int = 0xFFF9CF51;
var stickerSubState:StickerSubState;
public function new(?stickers:StickerSubState = null)
{
if (stickers != null)
{
stickerSubState = stickers;
}
super();
}
override function create()
{
transIn = FlxTransitionableState.defaultTransIn;
transOut = FlxTransitionableState.defaultTransOut;
if (FlxG.sound.music != null)
{
if (!FlxG.sound.music.playing) FlxG.sound.playMusic(Paths.music('freakyMenu'));
}
if (stickerSubState != null)
{
this.persistentUpdate = true;
this.persistentDraw = true;
openSubState(stickerSubState);
stickerSubState.degenStickers();
// resetSubState();
}
persistentUpdate = persistentDraw = true;
scoreText = new FlxText(10, 10, 0, "SCORE: 49324858");
scoreText.setFormat("VCR OSD Mono", 32);
txtWeekTitle = new FlxText(FlxG.width * 0.7, 10, 0, "");
txtWeekTitle.setFormat("VCR OSD Mono", 32, FlxColor.WHITE, RIGHT);
txtWeekTitle.alpha = 0.7;
var rankText:FlxText = new FlxText(0, 10);
rankText.text = 'RANK: GREAT';
rankText.setFormat(Paths.font("vcr.ttf"), 32);
rankText.size = scoreText.size;
rankText.screenCenter(X);
var ui_tex = Paths.getSparrowAtlas('campaign_menu_UI_assets');
yellowBG = new FlxSprite(0, 56).makeGraphic(FlxG.width, 400, FlxColor.WHITE);
yellowBG.color = 0xFFF9CF51;
// 0xFF413CAE blue
// 0xFFF9CF51 yello
grpWeekText = new FlxTypedGroup<MenuItem>();
add(grpWeekText);
var blackBarThingie:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, 56, FlxColor.BLACK);
add(blackBarThingie);
// grpWeekCharacters = new FlxTypedGroup<MenuCharacter>();
grpWeekCharacters = [];
grpLocks = new FlxTypedGroup<FlxSprite>();
add(grpLocks);
#if discord_rpc
// Updating Discord Rich Presence
DiscordClient.changePresence("In the Menus", null);
#end
for (i in 0...weekData.length)
{
if (!weekTypeInc.exists(weekType[i])) weekTypeInc[weekType[i]] = 1;
if (i == 0 && weekType[i] == WEEK) weekTypeInc[weekType[i]] = 0; // set week to 0 by default?
var weekThing:MenuItem = new MenuItem(0, yellowBG.y + yellowBG.height + 10, weekTypeInc[weekType[i]], weekType[i]);
weekThing.y += ((weekThing.height + 20) * i);
weekThing.targetY = i;
grpWeekText.add(weekThing);
weekTypeInc[weekType[i]] += 1;
weekThing.screenCenter(X);
weekThing.antialiasing = true;
// weekThing.updateHitbox();
// Needs an offset thingie
if (!weekUnlocked[i])
{
var lock:FlxSprite = new FlxSprite(weekThing.width + 10 + weekThing.x);
lock.frames = ui_tex;
lock.animation.addByPrefix('lock', 'lock');
lock.animation.play('lock');
lock.ID = i;
lock.antialiasing = true;
grpLocks.add(lock);
}
}
var sizeChart:Map<String, Array<Float>> = new Map();
var sizeTxt:Array<String> = Assets.getText(Paths.file("data/storychardata.txt")).split("\n");
for (item in sizeTxt)
{
var items:Array<String> = item.split(" ");
var stuf:Array<Float> = [];
var name:String = items.shift();
for (num in items)
stuf.push(Std.parseFloat(num));
sizeChart.set(name, stuf);
}
for (index => week in weekCharacters)
{
grpWeekCharacters.push(new FlxTypedGroup<MenuCharacter>());
for (char in 0...week.length)
{
var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[index][char]);
weekCharacterThing.y += 70;
weekCharacterThing.antialiasing = true;
var size:Float = 0.9;
switch (char)
{
case 0 | 2:
size = 0.5;
if (char == 0 && weekCharacterThing.character == "pico") weekCharacterThing.flipX = true;
case 1:
size = 0.9;
weekCharacterThing.x -= 80;
}
if (sizeChart.exists(weekCharacterThing.character))
{
var nums:Array<Float> = sizeChart[weekCharacterThing.character];
size = nums[char];
// IDK, this might be busted ass null shit?
if (char != 1)
{
weekCharacterThing.x += nums[3];
weekCharacterThing.y += nums[4];
}
}
weekCharacterThing.setGraphicSize(Std.int(weekCharacterThing.width * size));
weekCharacterThing.updateHitbox();
grpWeekCharacters[index].add(weekCharacterThing);
trace("ADD CHARACTER");
}
trace(grpWeekCharacters[index].toString());
}
difficultySelectors = new FlxGroup();
add(difficultySelectors);
leftArrow = new FlxSprite(grpWeekText.members[0].x + grpWeekText.members[0].width + 10, grpWeekText.members[0].y + 10);
leftArrow.frames = ui_tex;
leftArrow.animation.addByPrefix('idle', "arrow left");
leftArrow.animation.addByPrefix('press', "arrow push left");
leftArrow.animation.play('idle');
difficultySelectors.add(leftArrow);
sprDifficulty = new FlxSprite(leftArrow.x + 130, leftArrow.y);
sprDifficulty.frames = ui_tex;
sprDifficulty.animation.addByPrefix('easy', 'EASY');
sprDifficulty.animation.addByPrefix('normal', 'NORMAL');
sprDifficulty.animation.addByPrefix('hard', 'HARD');
sprDifficulty.animation.play('easy');
changeDifficulty();
difficultySelectors.add(sprDifficulty);
rightArrow = new FlxSprite(sprDifficulty.x + sprDifficulty.width + 50, leftArrow.y);
rightArrow.frames = ui_tex;
rightArrow.animation.addByPrefix('idle', 'arrow right');
rightArrow.animation.addByPrefix('press', "arrow push right", 24, false);
rightArrow.animation.play('idle');
difficultySelectors.add(rightArrow);
add(yellowBG);
for (grp in grpWeekCharacters)
{
add(grp);
// trace("ADDED GRP");
}
// add(grpWeekCharacters);
txtTracklist = new FlxText(FlxG.width * 0.05, yellowBG.x + yellowBG.height + 100, 0, "Tracks", 32);
txtTracklist.alignment = CENTER;
txtTracklist.font = rankText.font;
txtTracklist.color = 0xFFe55777;
add(txtTracklist);
// add(rankText);
add(scoreText);
add(txtWeekTitle);
updateText();
super.create();
}
override function update(elapsed:Float)
{
// scoreText.setFormat('VCR OSD Mono', 32);
yellowBG.color = FlxColor.interpolate(yellowBG.color, targetColor, 0.06);
lerpScore = CoolUtil.coolLerp(lerpScore, intendedScore, 0.5);
scoreText.text = "WEEK SCORE:" + Math.round(lerpScore);
txtWeekTitle.text = weekNames[curWeek].toUpperCase();
txtWeekTitle.x = FlxG.width - (txtWeekTitle.width + 10);
// FlxG.watch.addQuick('font', scoreText.font);
difficultySelectors.visible = weekUnlocked[curWeek];
grpLocks.forEach(function(lock:FlxSprite) {
lock.y = grpWeekText.members[lock.ID].y;
});
if (!movedBack)
{
if (!selectedWeek)
{
if (controls.UI_UP_P)
{
changeWeek(-1);
}
if (controls.UI_DOWN_P)
{
changeWeek(1);
}
if (controls.UI_RIGHT) rightArrow.animation.play('press')
else
rightArrow.animation.play('idle');
if (controls.UI_LEFT) leftArrow.animation.play('press');
else
leftArrow.animation.play('idle');
if (controls.UI_RIGHT_P) changeDifficulty(1);
if (controls.UI_LEFT_P) changeDifficulty(-1);
}
if (controls.ACCEPT)
{
selectWeek();
}
}
if (controls.BACK && !movedBack && !selectedWeek)
{
FlxG.sound.play(Paths.sound('cancelMenu'));
movedBack = true;
FlxG.switchState(new MainMenuState());
}
super.update(elapsed);
}
var movedBack:Bool = false;
var selectedWeek:Bool = false;
var stopspamming:Bool = false;
function selectWeek()
{
if (weekUnlocked[curWeek])
{
if (stopspamming == false)
{
FlxG.sound.play(Paths.sound('confirmMenu'));
grpWeekText.members[curWeek].startFlashing();
grpWeekCharacters[curWeek].members[1].animation.play('bfConfirm');
stopspamming = true;
}
PlayState.storyPlaylist = weekData[curWeek];
PlayState.isStoryMode = true;
selectedWeek = true;
PlayState.currentSong = SongLoad.loadFromJson(PlayState.storyPlaylist[0].toLowerCase(), PlayState.storyPlaylist[0].toLowerCase());
PlayState.currentSong_NEW = SongDataParser.fetchSong(PlayState.storyPlaylist[0].toLowerCase());
PlayState.storyWeek = curWeek;
PlayState.campaignScore = 0;
PlayState.storyDifficulty = curDifficulty;
PlayState.storyDifficulty_NEW = switch (curDifficulty)
{
case 0:
'easy';
case 1:
'normal';
case 2:
'hard';
default:
'normal';
};
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
new FlxTimer().start(1, function(tmr:FlxTimer) {
LoadingState.loadAndSwitchState(new PlayState(), true);
});
}
}
function changeDifficulty(change:Int = 0):Void
{
curDifficulty += change;
if (curDifficulty < 0) curDifficulty = 2;
if (curDifficulty > 2) curDifficulty = 0;
sprDifficulty.offset.x = 0;
switch (curDifficulty)
{
case 0:
sprDifficulty.animation.play('easy');
sprDifficulty.offset.x = 20;
case 1:
sprDifficulty.animation.play('normal');
sprDifficulty.offset.x = 70;
case 2:
sprDifficulty.animation.play('hard');
sprDifficulty.offset.x = 20;
}
sprDifficulty.alpha = 0;
// USING THESE WEIRD VALUES SO THAT IT DOESNT FLOAT UP
sprDifficulty.y = leftArrow.y - 15;
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
FlxTween.tween(sprDifficulty, {y: leftArrow.y + 15, alpha: 1}, 0.07);
}
var lerpScore:Float = 0;
var intendedScore:Int = 0;
function changeWeek(change:Int = 0):Void
{
curWeek += change;
if (curWeek >= weekData.length) curWeek = 0;
if (curWeek < 0) curWeek = weekData.length - 1;
var bullShit:Int = 0;
for (item in grpWeekText.members)
{
item.targetY = bullShit - curWeek;
if (item.targetY == Std.int(0) && weekUnlocked[curWeek]) item.alpha = 1;
else
item.alpha = 0.6;
bullShit++;
}
FlxG.sound.play(Paths.sound('scrollMenu'));
updateText();
}
function updateText()
{
switch (weekType[curWeek])
{
case WEEK:
targetColor = 0xFFF9CF51;
case WEEKEND:
targetColor = 0xFF413CAE;
}
for (ind => grp in grpWeekCharacters)
grp.visible = ind == curWeek;
txtTracklist.text = "Tracks\n";
var trackNames:Array<String> = weekData[curWeek];
for (i in trackNames)
{
txtTracklist.text += '\n${i}';
}
txtTracklist.text = txtTracklist.text.toUpperCase();
txtTracklist.screenCenter(X);
txtTracklist.x -= FlxG.width * 0.35;
intendedScore = Highscore.getWeekScore(curWeek, curDifficulty);
}
}

View file

@ -53,7 +53,7 @@ class TitleState extends MusicBeatState
super.create(); super.create();
/* /*
#elseif web #elseif web
@ -84,8 +84,7 @@ class TitleState extends MusicBeatState
*/ */
// netConnection.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown); // netConnection.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown);
new FlxTimer().start(1, function(tmr:FlxTimer) new FlxTimer().start(1, function(tmr:FlxTimer) {
{
startIntro(); startIntro();
}); });
} }
@ -284,44 +283,6 @@ class TitleState extends MusicBeatState
FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG}); FlxTween.tween(FlxG.stage.window, {y: FlxG.stage.window.y + 100}, 0.7, {ease: FlxEase.quadInOut, type: PINGPONG});
} }
/*
FlxG.watch.addQuick('cur display', FlxG.stage.window.display.id);
if (FlxG.keys.justPressed.Y)
{
// trace(FlxG.stage.window.display.name);
if (FlxG.gamepads.firstActive != null)
{
trace(FlxG.gamepads.firstActive.model);
FlxG.gamepads.firstActive.id
}
else
trace('gamepad null');
// FlxG.stage.window.title = Std.string(FlxG.random.int(0, 20000));
// FlxG.stage.window.setIcon(Image.fromFile('assets/images/icon16.png'));
// FlxG.stage.window.readPixels;
if (FlxG.stage.window.width == Std.int(FlxG.stage.window.display.bounds.width))
{
FlxG.stage.window.width = 1280;
FlxG.stage.window.height = 720;
FlxG.stage.window.y = 30;
}
else
{
FlxG.stage.window.width = Std.int(FlxG.stage.window.display.bounds.width);
FlxG.stage.window.height = Std.int(FlxG.stage.window.display.bounds.height);
FlxG.stage.window.x = Std.int(FlxG.stage.window.display.bounds.x);
FlxG.stage.window.y = Std.int(FlxG.stage.window.display.bounds.y);
}
}
*/
#if debug
if (FlxG.keys.justPressed.EIGHT) FlxG.switchState(new CutsceneAnimTestState());
#end
if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time; if (FlxG.sound.music != null) Conductor.songPosition = FlxG.sound.music.time;
if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen; if (FlxG.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
@ -373,8 +334,7 @@ class TitleState extends MusicBeatState
#if newgrounds #if newgrounds
if (!OutdatedSubState.leftState) if (!OutdatedSubState.leftState)
{ {
NGio.checkVersion(function(version) NGio.checkVersion(function(version) {
{
// Check if version is outdated // Check if version is outdated
var localVersion:String = "v" + Application.current.meta.get('version'); var localVersion:String = "v" + Application.current.meta.get('version');
var onlineVersion = version.split(" ")[0].trim(); var onlineVersion = version.split(" ")[0].trim();
@ -391,8 +351,7 @@ class TitleState extends MusicBeatState
}); });
} }
#end #end
new FlxTimer().start(2, function(tmr:FlxTimer) new FlxTimer().start(2, function(tmr:FlxTimer) {
{
// These assets are very unlikely to be used for the rest of gameplay, so it unloads them from cache/memory // These assets are very unlikely to be used for the rest of gameplay, so it unloads them from cache/memory
// Saves about 50mb of RAM or so??? // Saves about 50mb of RAM or so???
Assets.cache.clear(Paths.image('gfDanceTitle')); Assets.cache.clear(Paths.image('gfDanceTitle'));
@ -404,7 +363,7 @@ class TitleState extends MusicBeatState
// FlxG.sound.play(Paths.music('titleShoot'), 0.7); // FlxG.sound.play(Paths.music('titleShoot'), 0.7);
} }
if (pressedEnter && !skippedIntro && initialized) skipIntro(); if (pressedEnter && !skippedIntro && initialized) skipIntro();
/* /*
#if web #if web
if (!initialized && controls.ACCEPT) if (!initialized && controls.ACCEPT)
{ {

View file

@ -226,7 +226,7 @@ class NGUtil
scoreboardsLoaded = true; scoreboardsLoaded = true;
ngScoresLoaded.dispatch(); ngScoresLoaded.dispatch();
/* /*
for (score in NG.core.scoreBoards.get(8737).scores) for (score in NG.core.scoreBoards.get(8737).scores)
{ {
trace('score loaded user:${score.user.name}, score:${score.formatted_value}'); trace('score loaded user:${score.user.name}, score:${score.formatted_value}');

View file

@ -36,14 +36,12 @@ class NgPrompt extends Prompt
#if web #if web
prompt.buttons.getItem("yes").fireInstantly = true; prompt.buttons.getItem("yes").fireInstantly = true;
#end #end
prompt.onYes = function() prompt.onYes = function() {
{
prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end);
prompt.setButtons(None); prompt.setButtons(None);
openPassportUrl(); openPassportUrl();
}; };
prompt.onNo = function() prompt.onNo = function() {
{
prompt.close(); prompt.close();
prompt = null; prompt = null;
NGio.cancelLogin(); NGio.cancelLogin();
@ -92,8 +90,7 @@ class NgPrompt extends Prompt
{ {
var user = io.newgrounds.NG.core.user.name; var user = io.newgrounds.NG.core.user.name;
var prompt = new NgPrompt('Log out of $user?', Yes_No); var prompt = new NgPrompt('Log out of $user?', Yes_No);
prompt.onYes = function() prompt.onYes = function() {
{
NGio.logout(); NGio.logout();
prompt.close(); prompt.close();
}; };

View file

@ -44,7 +44,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
/** /**
* Finds the largest deviation from the desired time inside this SoundGroup. * Finds the largest deviation from the desired time inside this SoundGroup.
* *
* @param targetTime The time to check against. * @param targetTime The time to check against.
* If none is provided, it checks the time of all members against the first member of this SoundGroup. * If none is provided, it checks the time of all members against the first member of this SoundGroup.
* @return The largest deviation from the target time found. * @return The largest deviation from the target time found.

File diff suppressed because it is too large Load diff

View file

@ -11,7 +11,7 @@ typedef EntryConstructorFunction = String->Void;
/** /**
* A base type for a Registry, which is an object which handles loading scriptable objects. * A base type for a Registry, which is an object which handles loading scriptable objects.
* *
* @param T The type to construct. Must implement `IRegistryEntry`. * @param T The type to construct. Must implement `IRegistryEntry`.
* @param J The type of the JSON data used when constructing. * @param J The type of the JSON data used when constructing.
*/ */
@ -139,8 +139,8 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
/** /**
* Read, parse, and validate the JSON data and produce the corresponding data object. * Read, parse, and validate the JSON data and produce the corresponding data object.
* *
* NOTE: Must be implemented on the implementation class annd * NOTE: Must be implemented on the implementation class annd
*/ */
public abstract function parseEntryData(id:String):Null<J>; public abstract function parseEntryData(id:String):Null<J>;
@ -161,7 +161,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
/** /**
* Create a entry, attached to a scripted class, from the given class name. * Create a entry, attached to a scripted class, from the given class name.
* @param clsName * @param clsName
*/ */
abstract function createScriptedEntry(clsName:String):Null<T>; abstract function createScriptedEntry(clsName:String):Null<T>;
} }

View file

@ -48,8 +48,7 @@ class BGScrollingText extends FlxSpriteGroup
function set_funnyColor(col:Int):Int function set_funnyColor(col:Int):Int
{ {
grpTexts.forEach(function(txt) grpTexts.forEach(function(txt) {
{
txt.color = col; txt.color = col;
}); });
@ -85,8 +84,7 @@ class BGScrollingText extends FlxSpriteGroup
function sortTextShit():Void function sortTextShit():Void
{ {
grpTexts.sort(function(Order:Int, Obj1:FlxObject, Obj2:FlxObject) grpTexts.sort(function(Order:Int, Obj1:FlxObject, Obj2:FlxObject) {
{
return FlxSort.byValues(Order, Obj1.x, Obj2.x); return FlxSort.byValues(Order, Obj1.x, Obj2.x);
}); });
} }

View file

@ -77,9 +77,9 @@ class SustainTrail extends FlxSprite
/** /**
* Normally you would take strumTime:Float, noteData:Int, sustainLength:Float, parentNote:Note (?) * Normally you would take strumTime:Float, noteData:Int, sustainLength:Float, parentNote:Note (?)
* @param NoteData * @param NoteData
* @param SustainLength * @param SustainLength
* @param FileName * @param FileName
*/ */
public function new(NoteData:Int, SustainLength:Float, Path:String, ?Alpha:Float = 0.6, ?Pixel:Bool = false) public function new(NoteData:Int, SustainLength:Float, Path:String, ?Alpha:Float = 0.6, ?Pixel:Bool = false)
{ {

View file

@ -18,7 +18,7 @@ class FlxVideo extends FlxBasic
public var finishCallback:Void->Void; public var finishCallback:Void->Void;
/** /**
* Doesn't actually interact with Flixel shit, only just a pleasant to use class * Doesn't actually interact with Flixel shit, only just a pleasant to use class
*/ */
public function new(vidSrc:String) public function new(vidSrc:String)
{ {

View file

@ -123,8 +123,7 @@ class Cursor
if (assetCursorDefault == null) if (assetCursorDefault == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_DEFAULT_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_DEFAULT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorDefault = bitmapData; assetCursorDefault = bitmapData;
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS); applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
}); });
@ -138,8 +137,7 @@ class Cursor
if (assetCursorCross == null) if (assetCursorCross == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSS_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSS_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorCross = bitmapData; assetCursorCross = bitmapData;
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS); applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
}); });
@ -153,8 +151,7 @@ class Cursor
if (assetCursorEraser == null) if (assetCursorEraser == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ERASER_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ERASER_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorEraser = bitmapData; assetCursorEraser = bitmapData;
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS); applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
}); });
@ -168,8 +165,7 @@ class Cursor
if (assetCursorGrabbing == null) if (assetCursorGrabbing == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_GRABBING_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_GRABBING_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorGrabbing = bitmapData; assetCursorGrabbing = bitmapData;
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS); applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
}); });
@ -183,8 +179,7 @@ class Cursor
if (assetCursorHourglass == null) if (assetCursorHourglass == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_HOURGLASS_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_HOURGLASS_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorHourglass = bitmapData; assetCursorHourglass = bitmapData;
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS); applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
}); });
@ -198,8 +193,7 @@ class Cursor
if (assetCursorPointer == null) if (assetCursorPointer == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_POINTER_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_POINTER_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorPointer = bitmapData; assetCursorPointer = bitmapData;
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS); applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
}); });
@ -213,8 +207,7 @@ class Cursor
if (assetCursorText == null) if (assetCursorText == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_TEXT_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_TEXT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorText = bitmapData; assetCursorText = bitmapData;
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS); applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
}); });
@ -228,8 +221,7 @@ class Cursor
if (assetCursorZoomIn == null) if (assetCursorZoomIn == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorZoomIn = bitmapData; assetCursorZoomIn = bitmapData;
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS); applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
}); });
@ -243,8 +235,7 @@ class Cursor
if (assetCursorZoomOut == null) if (assetCursorZoomOut == null)
{ {
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic); var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData) future.onComplete(function(bitmapData:BitmapData) {
{
assetCursorZoomOut = bitmapData; assetCursorZoomOut = bitmapData;
applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS); applyCursorParams(assetCursorZoomOut, CURSOR_ZOOM_OUT_PARAMS);
}); });

View file

@ -5,10 +5,10 @@ import flixel.FlxBasic;
/** /**
* Handles repeating behavior when holding down a key or key combination. * Handles repeating behavior when holding down a key or key combination.
* *
* When the `keys` are pressed, `activated` will be true for the first frame, * When the `keys` are pressed, `activated` will be true for the first frame,
* then wait `delay` seconds before becoming true for one frame every `interval` seconds. * then wait `delay` seconds before becoming true for one frame every `interval` seconds.
* *
* Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly. * Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly.
*/ */
class TurboKeyHandler extends FlxBasic class TurboKeyHandler extends FlxBasic

View file

@ -4,7 +4,7 @@ import funkin.modding.events.ScriptEvent;
/** /**
* Defines a set of callbacks available to all scripted classes. * Defines a set of callbacks available to all scripted classes.
* *
* Includes events handling basic life cycle relevant to all scripted classes. * Includes events handling basic life cycle relevant to all scripted classes.
*/ */
interface IScriptedClass interface IScriptedClass
@ -24,10 +24,10 @@ interface IStateChangingScriptedClass extends IScriptedClass
public function onStateChangeBegin(event:StateChangeScriptEvent):Void; public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
public function onStateChangeEnd(event:StateChangeScriptEvent):Void; public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
public function onSubstateOpenBegin(event:SubStateScriptEvent):Void; public function onSubStateOpenBegin(event:SubStateScriptEvent):Void;
public function onSubstateOpenEnd(event:SubStateScriptEvent):Void; public function onSubStateOpenEnd(event:SubStateScriptEvent):Void;
public function onSubstateCloseBegin(event:SubStateScriptEvent):Void; public function onSubStateCloseBegin(event:SubStateScriptEvent):Void;
public function onSubstateCloseEnd(event:SubStateScriptEvent):Void; public function onSubStateCloseEnd(event:SubStateScriptEvent):Void;
} }
/** /**
@ -53,7 +53,7 @@ interface INoteScriptedClass extends IScriptedClass
/** /**
* Developer note: * Developer note:
* *
* I previously considered adding events for onKeyDown, onKeyUp, mouse events, etc. * I previously considered adding events for onKeyDown, onKeyUp, mouse events, etc.
* However, I realized that you can simply call something like the following within a module: * However, I realized that you can simply call something like the following within a module:
* `FlxG.state.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);` * `FlxG.state.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);`

View file

@ -8,7 +8,7 @@ class PolymodErrorHandler
* Show a popup with the given text. * Show a popup with the given text.
* This displays a system popup, it WILL interrupt the game. * This displays a system popup, it WILL interrupt the game.
* Make sure to only use this when it's important, like when there's a script error. * Make sure to only use this when it's important, like when there's a script error.
* *
* @param name The name at the top of the popup. * @param name The name at the top of the popup.
* @param desc The body text of the popup. * @param desc The body text of the popup.
*/ */

View file

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

View file

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

View file

@ -19,23 +19,23 @@ class ScriptEvent
* Called when the relevant object is created. * Called when the relevant object is created.
* Keep in mind that the constructor may be called before the object is needed, * Keep in mind that the constructor may be called before the object is needed,
* for the purposes of caching data or otherwise. * for the purposes of caching data or otherwise.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final CREATE:ScriptEventType = "CREATE"; public static inline final CREATE:ScriptEventType = 'CREATE';
/** /**
* Called when the relevant object is destroyed. * Called when the relevant object is destroyed.
* This should perform relevant cleanup to ensure good performance. * This should perform relevant cleanup to ensure good performance.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final DESTROY:ScriptEventType = "DESTROY"; public static inline final DESTROY:ScriptEventType = 'DESTROY';
/** /**
* Called when the relevent object is added to the game state. * Called when the relevent object is added to the game state.
* This assumes all data is loaded and ready to go. * This assumes all data is loaded and ready to go.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final ADDED:ScriptEventType = 'ADDED'; public static inline final ADDED:ScriptEventType = 'ADDED';
@ -43,38 +43,38 @@ class ScriptEvent
/** /**
* Called during the update function. * Called during the update function.
* This is called every frame, so be careful! * This is called every frame, so be careful!
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final UPDATE:ScriptEventType = "UPDATE"; public static inline final UPDATE:ScriptEventType = 'UPDATE';
/** /**
* Called when the player moves to pause the game. * Called when the player moves to pause the game.
* *
* This event IS cancelable! Canceling the event will prevent the game from pausing. * This event IS cancelable! Canceling the event will prevent the game from pausing.
*/ */
public static inline final PAUSE:ScriptEventType = "PAUSE"; public static inline final PAUSE:ScriptEventType = 'PAUSE';
/** /**
* Called when the player moves to unpause the game while paused. * Called when the player moves to unpause the game while paused.
* *
* This event IS cancelable! Canceling the event will prevent the game from resuming. * This event IS cancelable! Canceling the event will prevent the game from resuming.
*/ */
public static inline final RESUME:ScriptEventType = "RESUME"; public static inline final RESUME:ScriptEventType = 'RESUME';
/** /**
* Called once per step in the song. This happens 4 times per measure. * Called once per step in the song. This happens 4 times per measure.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SONG_BEAT_HIT:ScriptEventType = "BEAT_HIT"; public static inline final SONG_BEAT_HIT:ScriptEventType = 'BEAT_HIT';
/** /**
* Called once per step in the song. This happens 16 times per measure. * Called once per step in the song. This happens 16 times per measure.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SONG_STEP_HIT:ScriptEventType = "STEP_HIT"; public static inline final SONG_STEP_HIT:ScriptEventType = 'STEP_HIT';
/** /**
* Called when a character hits a note. * Called when a character hits a note.
@ -83,7 +83,7 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being hit, * This event IS cancelable! Canceling this event prevents the note from being hit,
* and will likely result in a miss later. * and will likely result in a miss later.
*/ */
public static inline final NOTE_HIT:ScriptEventType = "NOTE_HIT"; public static inline final NOTE_HIT:ScriptEventType = 'NOTE_HIT';
/** /**
* Called when a character misses a note. * Called when a character misses a note.
@ -92,7 +92,7 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being considered missed, * This event IS cancelable! Canceling this event prevents the note from being considered missed,
* avoiding a combo break and lost health. * avoiding a combo break and lost health.
*/ */
public static inline final NOTE_MISS:ScriptEventType = "NOTE_MISS"; public static inline final NOTE_MISS:ScriptEventType = 'NOTE_MISS';
/** /**
* Called when a character presses a note when there was none there, causing them to lose health. * Called when a character presses a note when there was none there, causing them to lose health.
@ -101,137 +101,137 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being considered missed, * This event IS cancelable! Canceling this event prevents the note from being considered missed,
* avoiding lost health/score and preventing the miss animation. * avoiding lost health/score and preventing the miss animation.
*/ */
public static inline final NOTE_GHOST_MISS:ScriptEventType = "NOTE_GHOST_MISS"; public static inline final NOTE_GHOST_MISS:ScriptEventType = 'NOTE_GHOST_MISS';
/** /**
* Called when a song event is reached in the chart. * Called when a song event is reached in the chart.
* *
* This event IS cancelable! Cancelling this event prevents the event from being triggered, * This event IS cancelable! Cancelling this event prevents the event from being triggered,
* thus blocking its normal functionality. * thus blocking its normal functionality.
*/ */
public static inline final SONG_EVENT:ScriptEventType = "SONG_EVENT"; public static inline final SONG_EVENT:ScriptEventType = 'SONG_EVENT';
/** /**
* Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin. * Called when the song starts. This occurs as the countdown ends and the instrumental and vocals begin.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SONG_START:ScriptEventType = "SONG_START"; public static inline final SONG_START:ScriptEventType = 'SONG_START';
/** /**
* Called when the song ends. This happens as the instrumental and vocals end. * Called when the song ends. This happens as the instrumental and vocals end.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SONG_END:ScriptEventType = "SONG_END"; public static inline final SONG_END:ScriptEventType = 'SONG_END';
/** /**
* Called when the countdown begins. This occurs before the song starts. * Called when the countdown begins. This occurs before the song starts.
* *
* This event IS cancelable! Canceling this event will prevent the countdown from starting. * This event IS cancelable! Canceling this event will prevent the countdown from starting.
* - The song will not start until you call Countdown.performCountdown() later. * - The song will not start until you call Countdown.performCountdown() later.
* - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it. * - Note that calling performCountdown() will trigger this event again, so be sure to add logic to ignore it.
*/ */
public static inline final COUNTDOWN_START:ScriptEventType = "COUNTDOWN_START"; public static inline final COUNTDOWN_START:ScriptEventType = 'COUNTDOWN_START';
/** /**
* Called when a step of the countdown happens. * Called when a step of the countdown happens.
* Includes information about what step of the countdown was hit. * Includes information about what step of the countdown was hit.
* *
* This event IS cancelable! Canceling this event will pause the countdown. * This event IS cancelable! Canceling this event will pause the countdown.
* - The countdown will not resume until you call PlayState.resumeCountdown(). * - The countdown will not resume until you call PlayState.resumeCountdown().
*/ */
public static inline final COUNTDOWN_STEP:ScriptEventType = "COUNTDOWN_STEP"; public static inline final COUNTDOWN_STEP:ScriptEventType = 'COUNTDOWN_STEP';
/** /**
* Called when the countdown is done but just before the song starts. * Called when the countdown is done but just before the song starts.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final COUNTDOWN_END:ScriptEventType = "COUNTDOWN_END"; public static inline final COUNTDOWN_END:ScriptEventType = 'COUNTDOWN_END';
/** /**
* Called before the game over screen triggers and the death animation plays. * Called before the game over screen triggers and the death animation plays.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final GAME_OVER:ScriptEventType = "GAME_OVER"; public static inline final GAME_OVER:ScriptEventType = 'GAME_OVER';
/** /**
* Called after the player presses a key to restart the game. * Called after the player presses a key to restart the game.
* This can happen from the pause menu or the game over screen. * This can happen from the pause menu or the game over screen.
* *
* This event IS cancelable! Canceling this event will prevent the game from restarting. * This event IS cancelable! Canceling this event will prevent the game from restarting.
*/ */
public static inline final SONG_RETRY:ScriptEventType = "SONG_RETRY"; public static inline final SONG_RETRY:ScriptEventType = 'SONG_RETRY';
/** /**
* Called when the player pushes down any key on the keyboard. * Called when the player pushes down any key on the keyboard.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final KEY_DOWN:ScriptEventType = "KEY_DOWN"; public static inline final KEY_DOWN:ScriptEventType = 'KEY_DOWN';
/** /**
* Called when the player releases a key on the keyboard. * Called when the player releases a key on the keyboard.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final KEY_UP:ScriptEventType = "KEY_UP"; public static inline final KEY_UP:ScriptEventType = 'KEY_UP';
/** /**
* Called when the game has finished loading the notes from JSON. * Called when the game has finished loading the notes from JSON.
* This allows modders to mutate the notes before they are used in the song. * This allows modders to mutate the notes before they are used in the song.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SONG_LOADED:ScriptEventType = "SONG_LOADED"; public static inline final SONG_LOADED:ScriptEventType = 'SONG_LOADED';
/** /**
* Called when the game is about to switch the current FlxState. * Called when the game is about to switch the current FlxState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final STATE_CHANGE_BEGIN:ScriptEventType = "STATE_CHANGE_BEGIN"; public static inline final STATE_CHANGE_BEGIN:ScriptEventType = 'STATE_CHANGE_BEGIN';
/** /**
* Called when the game has finished switching the current FlxState. * Called when the game has finished switching the current FlxState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final STATE_CHANGE_END:ScriptEventType = "STATE_CHANGE_END"; public static inline final STATE_CHANGE_END:ScriptEventType = 'STATE_CHANGE_END';
/** /**
* Called when the game is about to open a new FlxSubState. * Called when the game is about to open a new FlxSubState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = "SUBSTATE_OPEN_BEGIN"; public static inline final SUBSTATE_OPEN_BEGIN:ScriptEventType = 'SUBSTATE_OPEN_BEGIN';
/** /**
* Called when the game has finished opening a new FlxSubState. * Called when the game has finished opening a new FlxSubState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SUBSTATE_OPEN_END:ScriptEventType = "SUBSTATE_OPEN_END"; public static inline final SUBSTATE_OPEN_END:ScriptEventType = 'SUBSTATE_OPEN_END';
/** /**
* Called when the game is about to close the current FlxSubState. * Called when the game is about to close the current FlxSubState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = "SUBSTATE_CLOSE_BEGIN"; public static inline final SUBSTATE_CLOSE_BEGIN:ScriptEventType = 'SUBSTATE_CLOSE_BEGIN';
/** /**
* Called when the game has finished closing the current FlxSubState. * Called when the game has finished closing the current FlxSubState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
public static inline final SUBSTATE_CLOSE_END:ScriptEventType = "SUBSTATE_CLOSE_END"; public static inline final SUBSTATE_CLOSE_END:ScriptEventType = 'SUBSTATE_CLOSE_END';
/** /**
* Called when the game is exiting the current FlxState. * Called when the game is exiting the current FlxState.
* *
* This event is not cancelable. * This event is not cancelable.
*/ */
/** /**
@ -276,9 +276,12 @@ class ScriptEvent
} }
} }
/**
* Cancel this event.
* This is an alias for cancelEvent() but I make this typo all the time.
*/
public function cancel():Void public function cancel():Void
{ {
// This typo happens enough that I just added this.
cancelEvent(); cancelEvent();
} }
@ -316,11 +319,17 @@ class NoteScriptEvent extends ScriptEvent
*/ */
public var comboCount(default, null):Int; public var comboCount(default, null):Int;
/**
* Whether to play the record scratch sound (if this eventn type is `NOTE_MISS`).
*/
public var playSound(default, default):Bool;
public function new(type:ScriptEventType, note:Note, comboCount:Int = 0, cancelable:Bool = false):Void public function new(type:ScriptEventType, note:Note, comboCount:Int = 0, cancelable:Bool = false):Void
{ {
super(type, cancelable); super(type, cancelable);
this.note = note; this.note = note;
this.comboCount = comboCount; this.comboCount = comboCount;
this.playSound = true;
} }
public override function toString():String public override function toString():String
@ -468,7 +477,7 @@ class CountdownScriptEvent extends ScriptEvent
*/ */
public var step(default, null):CountdownStep; public var step(default, null):CountdownStep;
public function new(type:ScriptEventType, step:CountdownStep, cancelable = true):Void public function new(type:ScriptEventType, step:CountdownStep, cancelable:Bool = true):Void
{ {
super(type, cancelable); super(type, cancelable);
this.step = step; this.step = step;

View file

@ -113,16 +113,16 @@ class ScriptEventDispatcher
t.onStateChangeEnd(cast event); t.onStateChangeEnd(cast event);
return; return;
case ScriptEvent.SUBSTATE_OPEN_BEGIN: case ScriptEvent.SUBSTATE_OPEN_BEGIN:
t.onSubstateOpenBegin(cast event); t.onSubStateOpenBegin(cast event);
return; return;
case ScriptEvent.SUBSTATE_OPEN_END: case ScriptEvent.SUBSTATE_OPEN_END:
t.onSubstateOpenEnd(cast event); t.onSubStateOpenEnd(cast event);
return; return;
case ScriptEvent.SUBSTATE_CLOSE_BEGIN: case ScriptEvent.SUBSTATE_CLOSE_BEGIN:
t.onSubstateCloseBegin(cast event); t.onSubStateCloseBegin(cast event);
return; return;
case ScriptEvent.SUBSTATE_CLOSE_END: case ScriptEvent.SUBSTATE_CLOSE_END:
t.onSubstateCloseEnd(cast event); t.onSubStateCloseEnd(cast event);
return; return;
} }
} }

View file

@ -25,7 +25,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
/** /**
* Determines the order in which modules receive events. * Determines the order in which modules receive events.
* You can modify this to change the order in which a given module receives events. * You can modify this to change the order in which a given module receives events.
* *
* Priority 1 is processed before Priority 1000, etc. * Priority 1 is processed before Priority 1000, etc.
*/ */
public var priority(default, set):Int; public var priority(default, set):Int;
@ -41,7 +41,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
/** /**
* Called when the module is initialized. * Called when the module is initialized.
* It may not be safe to reference other modules here since they may not be loaded yet. * It may not be safe to reference other modules here since they may not be loaded yet.
* *
* NOTE: To make the module start inactive, call `this.active = false` in the constructor. * NOTE: To make the module start inactive, call `this.active = false` in the constructor.
*/ */
public function new(moduleId:String, priority:Int = 1000):Void public function new(moduleId:String, priority:Int = 1000):Void
@ -107,13 +107,13 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
public function onStateChangeEnd(event:StateChangeScriptEvent) {} public function onStateChangeEnd(event:StateChangeScriptEvent) {}
public function onSubstateOpenBegin(event:SubStateScriptEvent) {} public function onSubStateOpenBegin(event:SubStateScriptEvent) {}
public function onSubstateOpenEnd(event:SubStateScriptEvent) {} public function onSubStateOpenEnd(event:SubStateScriptEvent) {}
public function onSubstateCloseBegin(event:SubStateScriptEvent) {} public function onSubStateCloseBegin(event:SubStateScriptEvent) {}
public function onSubstateCloseEnd(event:SubStateScriptEvent) {} public function onSubStateCloseEnd(event:SubStateScriptEvent) {}
public function onSongRetry(event:ScriptEvent) {} public function onSongRetry(event:ScriptEvent) {}
} }

View file

@ -16,7 +16,7 @@ class ModuleHandler
/** /**
* Parses and preloads the game's stage data and scripts when the game starts. * Parses and preloads the game's stage data and scripts when the game starts.
* *
* If you want to force stages to be reloaded, you can just call this function again. * If you want to force stages to be reloaded, you can just call this function again.
*/ */
public static function loadModuleCache():Void public static function loadModuleCache():Void
@ -66,8 +66,7 @@ class ModuleHandler
{ {
modulePriorityOrder = moduleCache.keys().array(); modulePriorityOrder = moduleCache.keys().array();
modulePriorityOrder.sort(function(a:String, b:String):Int modulePriorityOrder.sort(function(a:String, b:String):Int {
{
var aModule:Module = moduleCache.get(a); var aModule:Module = moduleCache.get(a);
var bModule:Module = moduleCache.get(b); var bModule:Module = moduleCache.get(b);

View file

@ -7,7 +7,7 @@ import openfl.Assets;
* Just various functions that IDK where to put em!!! * Just various functions that IDK where to put em!!!
* Semi-temp for now? the note stuff is super clutter-y right now * Semi-temp for now? the note stuff is super clutter-y right now
* so I am putting this new stuff here right now XDD * so I am putting this new stuff here right now XDD
* *
* A lot of this stuff can probably be moved to where appropriate! * A lot of this stuff can probably be moved to where appropriate!
* i dont care about NoteUtil.hx at all!!! * i dont care about NoteUtil.hx at all!!!
*/ */
@ -15,7 +15,7 @@ class NoteUtil
{ {
/** /**
* IDK THING FOR BOTH LOL! DIS SHIT HACK-Y * IDK THING FOR BOTH LOL! DIS SHIT HACK-Y
* @param jsonPath * @param jsonPath
* @return Map<Int, Array<SongEventInfo>> * @return Map<Int, Array<SongEventInfo>>
*/ */
public static function loadSongEvents(jsonPath:String):Map<Int, Array<SongEventInfo>> public static function loadSongEvents(jsonPath:String):Map<Int, Array<SongEventInfo>>
@ -34,7 +34,7 @@ class NoteUtil
/** /**
* Parses song event json stuff into a neater lil map grouping? * Parses song event json stuff into a neater lil map grouping?
* @param songEvents * @param songEvents
*/ */
public static function parseSongEvents(songEvents:Array<SongEvent>):Map<Int, Array<SongEventInfo>> public static function parseSongEvents(songEvents:Array<SongEvent>):Map<Int, Array<SongEventInfo>>
{ {

View file

@ -37,7 +37,7 @@ class Countdown
// Stop any existing countdown. // Stop any existing countdown.
stopCountdown(); stopCountdown();
PlayState.isInCountdown = true; PlayState.instance.isInCountdown = true;
Conductor.songPosition = Conductor.crochet * -5; Conductor.songPosition = Conductor.crochet * -5;
// Handle onBeatHit events manually // Handle onBeatHit events manually
@:privateAccess @:privateAccess
@ -46,8 +46,7 @@ class Countdown
// The timer function gets called based on the beat of the song. // The timer function gets called based on the beat of the song.
countdownTimer = new FlxTimer(); countdownTimer = new FlxTimer();
countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) {
{
countdownStep = decrement(countdownStep); countdownStep = decrement(countdownStep);
// Handle onBeatHit events manually // Handle onBeatHit events manually
@ -102,7 +101,7 @@ class Countdown
/** /**
* Pauses the countdown at the current step. You can start it up again later by calling resumeCountdown(). * Pauses the countdown at the current step. You can start it up again later by calling resumeCountdown().
* *
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event.
*/ */
public static function pauseCountdown() public static function pauseCountdown()
@ -115,7 +114,7 @@ class Countdown
/** /**
* Resumes the countdown at the current step. Only makes sense if you called pauseCountdown() first. * Resumes the countdown at the current step. Only makes sense if you called pauseCountdown() first.
* *
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event. * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStep event.
*/ */
public static function resumeCountdown() public static function resumeCountdown()
@ -128,7 +127,7 @@ class Countdown
/** /**
* Stops the countdown at the current step. You will have to restart it again later. * Stops the countdown at the current step. You will have to restart it again later.
* *
* If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event. * If you want to call this from a module, it's better to use the event system and cancel the onCountdownStart event.
*/ */
public static function stopCountdown() public static function stopCountdown()
@ -166,7 +165,7 @@ class Countdown
/** /**
* Retrieves the graphic to use for this step of the countdown. * Retrieves the graphic to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
* *
* This is public so modules can do lol funny shit. * This is public so modules can do lol funny shit.
*/ */
public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void
@ -216,8 +215,7 @@ class Countdown
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.crochet / 1000, FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100, alpha: 0}, Conductor.crochet / 1000,
{ {
ease: FlxEase.cubeInOut, ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween) onComplete: function(twn:FlxTween) {
{
countdownSprite.destroy(); countdownSprite.destroy();
} }
}); });
@ -228,7 +226,7 @@ class Countdown
/** /**
* Retrieves the sound file to use for this step of the countdown. * Retrieves the sound file to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles? * TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
* *
* This is public so modules can do lol funny shit. * This is public so modules can do lol funny shit.
*/ */
public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void

View file

@ -16,10 +16,10 @@ import funkin.ui.PreferencesMenu;
/** /**
* A substate which renders over the PlayState when the player dies. * A substate which renders over the PlayState when the player dies.
* Displays the player death animation, plays the music, and handles restarting the song. * Displays the player death animation, plays the music, and handles restarting the song.
* *
* The newest implementation uses a substate, which prevents having to reload the song and stage each reset. * The newest implementation uses a substate, which prevents having to reload the song and stage each reset.
*/ */
class GameOverSubstate extends MusicBeatSubstate class GameOverSubState extends MusicBeatSubState
{ {
/** /**
* Which alternate animation on the character to use. * Which alternate animation on the character to use.
@ -92,7 +92,7 @@ class GameOverSubstate extends MusicBeatSubstate
bg.scrollFactor.set(); bg.scrollFactor.set();
add(bg); add(bg);
// Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubstate. // Pluck Boyfriend from the PlayState and place him (in the same position) in the GameOverSubState.
// We can then play the character's `firstDeath` animation. // We can then play the character's `firstDeath` animation.
boyfriend = PlayState.instance.currentStage.getBoyfriend(true); boyfriend = PlayState.instance.currentStage.getBoyfriend(true);
boyfriend.isDead = true; boyfriend.isDead = true;
@ -160,19 +160,21 @@ class GameOverSubstate extends MusicBeatSubstate
} }
// KEYBOARD ONLY: Restart the level when pressing the assigned key. // KEYBOARD ONLY: Restart the level when pressing the assigned key.
if (controls.ACCEPT) if (controls.ACCEPT && blueballed)
{ {
blueballed = false;
confirmDeath(); confirmDeath();
} }
// KEYBOARD ONLY: Return to the menu when pressing the assigned key. // KEYBOARD ONLY: Return to the menu when pressing the assigned key.
if (controls.BACK) if (controls.BACK)
{ {
blueballed = false;
PlayState.deathCounter = 0; PlayState.deathCounter = 0;
PlayState.seenCutscene = false; PlayState.seenCutscene = false;
gameOverMusic.stop(); gameOverMusic.stop();
if (PlayState.isStoryMode) FlxG.switchState(new StoryMenuState()); if (PlayStatePlaylist.isStoryMode) FlxG.switchState(new StoryMenuState());
else else
FlxG.switchState(new FreeplayState()); FlxG.switchState(new FreeplayState());
} }
@ -186,11 +188,11 @@ class GameOverSubstate extends MusicBeatSubstate
else else
{ {
// Music hasn't started yet. // Music hasn't started yet.
switch (PlayState.storyWeek) switch (PlayStatePlaylist.campaignId)
{ {
// TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded. // TODO: Make the behavior for playing Jeff's voicelines generic or un-hardcoded.
// This will simplify the class and make it easier for mods to add death quotes. // This will simplify the class and make it easier for mods to add death quotes.
case 7: case "week7":
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote) if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote)
{ {
playingJeffQuote = true; playingJeffQuote = true;
@ -227,9 +229,9 @@ class GameOverSubstate extends MusicBeatSubstate
new FlxTimer().start(0.7, function(tmr:FlxTimer) { new FlxTimer().start(0.7, function(tmr:FlxTimer) {
// ...fade out the graphics. Then after that happens... // ...fade out the graphics. Then after that happens...
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() { FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
// ...close the GameOverSubstate. // ...close the GameOverSubState.
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true); FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
PlayState.needsReset = true; PlayState.instance.needsReset = true;
// Readd Boyfriend to the stage. // Readd Boyfriend to the stage.
boyfriend.isDead = false; boyfriend.isDead = false;
@ -252,7 +254,7 @@ class GameOverSubstate extends MusicBeatSubstate
/** /**
* Starts the death music at the appropriate volume. * Starts the death music at the appropriate volume.
* @param startingVolume * @param startingVolume
*/ */
function startDeathMusic(?startingVolume:Float = 1, ?force:Bool = false):Void function startDeathMusic(?startingVolume:Float = 1, ?force:Bool = false):Void
{ {
@ -269,12 +271,15 @@ class GameOverSubstate extends MusicBeatSubstate
} }
} }
static var blueballed:Bool = false;
/** /**
* Play the sound effect that occurs when * Play the sound effect that occurs when
* boyfriend's testicles get utterly annihilated. * boyfriend's testicles get utterly annihilated.
*/ */
public static function playBlueBalledSFX() public static function playBlueBalledSFX()
{ {
blueballed = true;
FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix)); FlxG.sound.play(Paths.sound('fnf_loss_sfx' + blueBallSuffix));
} }

View file

@ -317,7 +317,7 @@ class HealthIcon extends FlxSprite
/** /**
* Load health icon animations from a Sparrow XML file (the kind used by characters) * Load health icon animations from a Sparrow XML file (the kind used by characters)
* Note that this is looking for SPECIFIC animation names, so you may need to modify the XML. * Note that this is looking for SPECIFIC animation names, so you may need to modify the XML.
* @param charId * @param charId
*/ */
function loadAnimationNew():Void function loadAnimationNew():Void
{ {
@ -333,7 +333,7 @@ class HealthIcon extends FlxSprite
/** /**
* Load health icon animations using the legacy format. * Load health icon animations using the legacy format.
* Simply assumes two icons, the idle and losing icons. * Simply assumes two icons, the idle and losing icons.
* @param charId * @param charId
*/ */
function loadAnimationOld():Void function loadAnimationOld():Void
{ {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,56 @@
package funkin.play;
import funkin.util.Constants;
/**
* Manages playback of multiple songs in a row.
*
* TODO: Add getters/setters for all these properties to validate them.
*/
class PlayStatePlaylist
{
/**
* Whether the game is currently in Story Mode. If false, we are in Free Play Mode.
*/
public static var isStoryMode(default, default):Bool = false;
/**
* The loist of upcoming songs to be played.
* When the user completes a song in Story Mode, the first entry in this list is played.
* When this list is empty, move to the Results screen instead.
*/
public static var playlistSongIds:Array<String> = [];
/**
* The cumulative score for all the songs in the playlist.
*/
public static var campaignScore:Int = 0;
/**
* The title of this playlist, for example `Week 4` or `Weekend 1`
*/
public static var campaignTitle:String = 'UNKNOWN';
/**
* The internal ID of the current playlist, for example `week4` or `weekend-1`.
*/
public static var campaignId:String = 'unknown';
/**
* The current difficulty selected for this level (as a named ID).
*/
public static var currentDifficulty(default, default):String = Constants.DEFAULT_DIFFICULTY;
/**
* Resets the playlist to its default state.
*/
public static function reset():Void
{
isStoryMode = false;
playlistSongIds = [];
campaignScore = 0;
campaignTitle = 'UNKNOWN';
campaignId = 'unknown';
currentDifficulty = Constants.DEFAULT_DIFFICULTY;
}
}

View file

@ -19,7 +19,7 @@ import funkin.shaderslmfao.LeftMaskShader;
import funkin.ui.TallyCounter; import funkin.ui.TallyCounter;
import flxanimate.FlxAnimate.Settings; import flxanimate.FlxAnimate.Settings;
class ResultState extends MusicBeatSubstate class ResultState extends MusicBeatSubState
{ {
var resultsVariation:ResultVariations; var resultsVariation:ResultVariations;
var songName:FlxBitmapText; var songName:FlxBitmapText;
@ -118,16 +118,16 @@ class ResultState extends MusicBeatSubstate
difficulty = new FlxSprite(555); difficulty = new FlxSprite(555);
var diffSpr:String = switch (CoolUtil.difficultyString()) var diffSpr:String = switch (PlayState.instance.currentDifficulty)
{ {
case "EASY": case 'EASY':
"difEasy"; 'difEasy';
case "NORMAL": case 'NORMAL':
"difNormal"; 'difNormal';
case "HARD": case 'HARD':
"difHard"; 'difHard';
case _: case _:
"difNormal"; 'difNormal';
} }
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr)); difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
@ -144,7 +144,7 @@ class ResultState extends MusicBeatSubstate
} }
else else
{ {
songName.text += PlayState.currentSong.song; songName.text += PlayState.instance.currentSong.songId;
} }
songName.antialiasing = true; songName.antialiasing = true;

View file

@ -13,7 +13,7 @@ import funkin.util.Constants;
/** /**
* A group controlling the individual notes of the strumline for a given player. * A group controlling the individual notes of the strumline for a given player.
* *
* FUN FACT: Setting the X and Y of a FlxSpriteGroup will move all the sprites in the group. * FUN FACT: Setting the X and Y of a FlxSpriteGroup will move all the sprites in the group.
*/ */
class Strumline extends FlxTypedSpriteGroup<StrumlineArrow> class Strumline extends FlxTypedSpriteGroup<StrumlineArrow>
@ -62,8 +62,8 @@ class Strumline extends FlxTypedSpriteGroup<StrumlineArrow>
/** /**
* Apply a small animation which moves the arrow down and fades it in. * Apply a small animation which moves the arrow down and fades it in.
* Only plays at the start of Free Play songs. * Only plays at the start of Free Play songs.
* *
* Note that modifying the offset of the whole strumline won't have the * Note that modifying the offset of the whole strumline won't have the
* @param arrow The arrow to animate. * @param arrow The arrow to animate.
* @param index The index of the arrow in the strumline. * @param index The index of the arrow in the strumline.
*/ */

View file

@ -32,7 +32,7 @@ typedef AnimateAtlasAnimation =
/** /**
* An AnimateAtlasCharacter is a Character which is rendered by * An AnimateAtlasCharacter is a Character which is rendered by
* displaying an animation derived from an Adobe Animate texture atlas spritesheet file. * displaying an animation derived from an Adobe Animate texture atlas spritesheet file.
* *
* BaseCharacter has game logic, AnimateAtlasCharacter has only rendering logic. * BaseCharacter has game logic, AnimateAtlasCharacter has only rendering logic.
* KEEP THEM SEPARATE! * KEEP THEM SEPARATE!
*/ */
@ -537,7 +537,7 @@ class AnimateAtlasCharacter extends BaseCharacter
/** /**
* Returns the left-most position of the left-most member. * Returns the left-most position of the left-most member.
* If there are no members, x is returned. * If there are no members, x is returned.
* *
* @since 5.0.0 * @since 5.0.0
* @return the left-most position of the left-most member * @return the left-most position of the left-most member
*/ */
@ -554,7 +554,7 @@ class AnimateAtlasCharacter extends BaseCharacter
/** /**
* Returns the right-most position of the right-most member. * Returns the right-most position of the right-most member.
* If there are no members, x is returned. * If there are no members, x is returned.
* *
* @since 5.0.0 * @since 5.0.0
* @return the right-most position of the right-most member * @return the right-most position of the right-most member
*/ */
@ -586,7 +586,7 @@ class AnimateAtlasCharacter extends BaseCharacter
/** /**
* Returns the top-most position of the top-most member. * Returns the top-most position of the top-most member.
* If there are no members, y is returned. * If there are no members, y is returned.
* *
* @since 5.0.0 * @since 5.0.0
* @return the top-most position of the top-most member * @return the top-most position of the top-most member
*/ */
@ -603,7 +603,7 @@ class AnimateAtlasCharacter extends BaseCharacter
/** /**
* Returns the top-most position of the top-most member. * Returns the top-most position of the top-most member.
* If there are no members, y is returned. * If there are no members, y is returned.
* *
* @since 5.0.0 * @since 5.0.0
* @return the bottom-most position of the bottom-most member * @return the bottom-most position of the bottom-most member
*/ */

View file

@ -9,7 +9,7 @@ import funkin.play.stage.Bopper;
/** /**
* A Character is a stage prop which bops to the music as well as controlled by the strumlines. * A Character is a stage prop which bops to the music as well as controlled by the strumlines.
* *
* Remember: The character's origin is at its FEET. (horizontal center, vertical bottom) * Remember: The character's origin is at its FEET. (horizontal center, vertical bottom)
*/ */
class BaseCharacter extends Bopper class BaseCharacter extends Bopper
@ -43,7 +43,7 @@ class BaseCharacter extends Bopper
/** /**
* Set to true when the character being used in a special way. * Set to true when the character being used in a special way.
* This includes the Chart Editor and the Animation Editor. * This includes the Chart Editor and the Animation Editor.
* *
* Used by scripts to ensure that they don't try to run code to interact with the stage when the stage doesn't actually exist. * Used by scripts to ensure that they don't try to run code to interact with the stage when the stage doesn't actually exist.
*/ */
public var debug:Bool = false; public var debug:Bool = false;
@ -76,7 +76,7 @@ class BaseCharacter extends Bopper
/** /**
* The absolute position of the top-left of the character. * The absolute position of the top-left of the character.
* @return * @return
*/ */
public var cornerPosition(get, set):FlxPoint; public var cornerPosition(get, set):FlxPoint;
@ -112,7 +112,7 @@ class BaseCharacter extends Bopper
/** /**
* Returns the point the camera should focus on. * Returns the point the camera should focus on.
* Should be approximately centered on the character, and should not move based on the current animation. * Should be approximately centered on the character, and should not move based on the current animation.
* *
* Set the position of this rather than reassigning it, so that anything referencing it will not be affected. * Set the position of this rather than reassigning it, so that anything referencing it will not be affected.
*/ */
public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0); public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0);
@ -242,7 +242,7 @@ class BaseCharacter extends Bopper
/** /**
* Set the sprite scale to the appropriate value. * Set the sprite scale to the appropriate value.
* @param scale * @param scale
*/ */
public function setScale(scale:Null<Float>):Void public function setScale(scale:Null<Float>):Void
{ {
@ -394,14 +394,14 @@ class BaseCharacter extends Bopper
/** /**
* Since no `onBeatHit` or `dance` calls happen in GameOverSubState, * Since no `onBeatHit` or `dance` calls happen in GameOverSubState,
* this regularly gets called instead. * this regularly gets called instead.
* *
* @param force Force the deathLoop animation to play, even if `firstDeath` is still playing. * @param force Force the deathLoop animation to play, even if `firstDeath` is still playing.
*/ */
public function playDeathAnimation(force:Bool = false):Void public function playDeathAnimation(force:Bool = false):Void
{ {
if (force || (getCurrentAnimation().startsWith('firstDeath') && isAnimationFinished())) if (force || (getCurrentAnimation().startsWith('firstDeath') && isAnimationFinished()))
{ {
playAnimation('deathLoop' + GameOverSubstate.animationSuffix); playAnimation('deathLoop' + GameOverSubState.animationSuffix);
} }
} }
@ -582,6 +582,13 @@ class BaseCharacter extends Bopper
// restart even if already playing, because the character might sing the same note twice. // restart even if already playing, because the character might sing the same note twice.
playAnimation(anim, true); playAnimation(anim, true);
} }
public override function playAnimation(name:String, restart:Bool = false, ?ignoreOther:Bool = false, ?reversed:Bool = false):Void
{
FlxG.watch.addQuick('playAnim(${characterName})', name);
trace('playAnim(${characterName}): ${name}');
super.playAnimation(name, restart, ignoreOther, reversed);
}
} }
/** /**

View file

@ -33,7 +33,7 @@ class CharacterDataParser
/** /**
* Parses and preloads the game's stage data and scripts when the game starts. * Parses and preloads the game's stage data and scripts when the game starts.
* *
* If you want to force stages to be reloaded, you can just call this function again. * If you want to force stages to be reloaded, you can just call this function again.
*/ */
public static function loadCharacterCache():Void public static function loadCharacterCache():Void
@ -294,7 +294,7 @@ class CharacterDataParser
/** /**
* Load a character's JSON file and parse its data. * Load a character's JSON file and parse its data.
* *
* @param charId The character to load. * @param charId The character to load.
* @return The character data, or null if validation failed. * @return The character data, or null if validation failed.
*/ */
@ -363,8 +363,8 @@ class CharacterDataParser
/** /**
* Set unspecified parameters to their defaults. * Set unspecified parameters to their defaults.
* If the parameter is mandatory, print an error message. * If the parameter is mandatory, print an error message.
* @param id * @param id
* @param input * @param input
* @return The validated character data * @return The validated character data
*/ */
static function validateCharacterData(id:String, input:CharacterData):Null<CharacterData> static function validateCharacterData(id:String, input:CharacterData):Null<CharacterData>
@ -624,7 +624,7 @@ typedef CharacterData =
/** /**
* The frequency at which the character will play its idle animation, in beats. * The frequency at which the character will play its idle animation, in beats.
* Increasing this number will make the character dance less often. * Increasing this number will make the character dance less often.
* *
* @default 1 * @default 1
*/ */
var danceEvery:Null<Int>; var danceEvery:Null<Int>;
@ -633,7 +633,7 @@ typedef CharacterData =
* The minimum duration that a character will play a note animation for, in beats. * The minimum duration that a character will play a note animation for, in beats.
* If this number is too low, you may see the character start playing the idle animation between notes. * If this number is too low, you may see the character start playing the idle animation between notes.
* If this number is too high, you may see the the character play the sing animation for too long after the notes are gone. * If this number is too high, you may see the the character play the sing animation for too long after the notes are gone.
* *
* Examples: * Examples:
* - Daddy Dearest uses a value of `1.525`. * - Daddy Dearest uses a value of `1.525`.
* @default 1.0 * @default 1.0
@ -654,7 +654,7 @@ typedef CharacterData =
/** /**
* Whether or not the whole ass sprite is flipped by default. * Whether or not the whole ass sprite is flipped by default.
* Useful for characters that could also be played (Pico) * Useful for characters that could also be played (Pico)
* *
* @default false * @default false
*/ */
var flipX:Null<Bool>; var flipX:Null<Bool>;

View file

@ -8,11 +8,11 @@ import funkin.play.character.CharacterData.CharacterRenderType;
/** /**
* For some characters which use Sparrow atlases, the spritesheets need to be split * For some characters which use Sparrow atlases, the spritesheets need to be split
* into multiple files. This character renderer handles by showing the appropriate sprite. * into multiple files. This character renderer handles by showing the appropriate sprite.
* *
* Examples in base game include BF Holding GF (most of the sprites are in one file * Examples in base game include BF Holding GF (most of the sprites are in one file
* but the death animation is in a separate file). * but the death animation is in a separate file).
* Only example I can think of in mods is Tricky (which has a separate file for each animation). * Only example I can think of in mods is Tricky (which has a separate file for each animation).
* *
* BaseCharacter has game logic, SparrowCharacter has only rendering logic. * BaseCharacter has game logic, SparrowCharacter has only rendering logic.
* KEEP THEM SEPARATE! * KEEP THEM SEPARATE!
* *

View file

@ -8,7 +8,7 @@ import funkin.play.character.CharacterData.CharacterRenderType;
/** /**
* A SparrowCharacter is a Character which is rendered by * A SparrowCharacter is a Character which is rendered by
* displaying an animation derived from a SparrowV2 atlas spritesheet file. * displaying an animation derived from a SparrowV2 atlas spritesheet file.
* *
* BaseCharacter has game logic, SparrowCharacter has only rendering logic. * BaseCharacter has game logic, SparrowCharacter has only rendering logic.
* KEEP THEM SEPARATE! * KEEP THEM SEPARATE!
*/ */

View file

@ -1,13 +1,10 @@
package funkin.play.cutscene; package funkin.play.cutscene;
// import hxcodec.flixel.FlxVideoSprite;
// import hxcodec.flixel.FlxCutsceneState;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.tweens.FlxEase; import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import flixel.util.FlxTimer; import flixel.util.FlxTimer;
import funkin.graphics.video.FlxVideo;
/** /**
* Static methods for playing cutscenes in the PlayState. * Static methods for playing cutscenes in the PlayState.
@ -15,112 +12,46 @@ import funkin.graphics.video.FlxVideo;
*/ */
class VanillaCutscenes class VanillaCutscenes
{ {
/**
* Well, well, well, what have we got here?
*/
public static function playUghCutscene():Void
{
playVideoCutscene('music/ughCutscene.mp4');
}
/**
* Nice bars for an ugly, boring teenager!
*/
public static function playGunsCutscene():Void
{
playVideoCutscene('music/gunsCutscene.mp4');
}
/**
* Don't you have a school to shoot up?
*/
public static function playStressCutscene():Void
{
playVideoCutscene('music/stressCutscene.mp4');
}
static var blackScreen:FlxSprite; static var blackScreen:FlxSprite;
/** static final TWEEN_DURATION:Float = 2.0;
* Plays a cutscene from a video file, then starts the countdown once the video is done.
* TODO: Cutscene is currently skipped on native platforms.
*/
static function playVideoCutscene(path:String):Void
{
// Tell PlayState to stop the song until the video is done.
PlayState.isInCutscene = true;
PlayState.instance.camHUD.visible = false;
// Display a black screen to hide the game while the video is playing.
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
blackScreen.scrollFactor.set(0, 0);
blackScreen.cameras = [PlayState.instance.camCutscene];
PlayState.instance.add(blackScreen);
#if html5
// Video displays OVER the FlxState.
vid = new FlxVideo(path);
vid.finishCallback = finishCutscene.bind(0.5);
#else
// Video displays OVER the FlxState.
// vid = new FlxVideoSprite(0, 0);
vid.cameras = [PlayState.instance.camCutscene];
PlayState.instance.add(vid);
vid.playVideo(Paths.file(path), false);
vid.onEndReached.add(finishCutscene.bind(0.5));
#end
}
static var vid:#if html5 FlxVideo #else Dynamic /**FlxVideoSprite **/ #end;
/** /**
* Does the cleanup to start the countdown after the video is done. * Plays the cutscene that appears at the start of Winter Horrorland.
* Gets called immediately if the video can't be played. * TODO: Move this to `winter-horrorland.hxc`
*/
public static function finishCutscene(?transitionTime:Float = 2.5):Void
{
trace('ALERT: Finish cutscene called!');
#if html5
#else
vid.stop();
PlayState.instance.remove(vid);
#end
PlayState.instance.camHUD.visible = true;
FlxTween.tween(blackScreen, {alpha: 0}, transitionTime,
{
ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
PlayState.instance.remove(blackScreen);
blackScreen = null;
}
});
FlxTween.tween(FlxG.camera, {zoom: PlayState.defaultCameraZoom}, transitionTime,
{
ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
PlayState.instance.startCountdown();
}
});
}
/**
* FNF corruption mod???
*/ */
public static function playHorrorStartCutscene():Void public static function playHorrorStartCutscene():Void
{ {
PlayState.isInCutscene = true; PlayState.instance.isInCutscene = true;
PlayState.instance.camHUD.visible = false; PlayState.instance.camHUD.visible = false;
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
blackScreen.scrollFactor.set(0, 0); blackScreen.scrollFactor.set(0, 0);
blackScreen.zIndex = 1000000;
PlayState.instance.add(blackScreen); PlayState.instance.add(blackScreen);
new FlxTimer().start(0.1, _ -> finishCutscene(2.5)); new FlxTimer().start(0.1, function(_) {
trace('Playing horrorland cutscene...');
PlayState.instance.remove(blackScreen);
// Force set the camera position and zoom.
PlayState.instance.cameraFollowPoint.setPosition(400, -2050);
PlayState.instance.resetCamera();
FlxG.camera.zoom = 2.5;
// Play the Sound effect.
FlxG.sound.play(Paths.sound('Lights_Turn_On'), function() {
// Fade in the HUD.
trace('SFX done...');
PlayState.instance.camHUD.visible = true;
PlayState.instance.camHUD.alpha = 0.0; // Use alpha rather than visible to let us fade it in.
FlxTween.tween(PlayState.instance.camHUD, {alpha: 1.0}, TWEEN_DURATION, {ease: FlxEase.quadInOut});
// Start the countdown.
trace('Zoom out done...');
trace('Begin countdown (ends cutscene)');
PlayState.instance.startCountdown();
});
});
} }
} }

View file

@ -0,0 +1,155 @@
package funkin.play.cutscene;
import funkin.play.PlayState;
import flixel.FlxSprite;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
#if html5
import funkin.graphics.video.FlxVideo;
#else
import hxcodec.flixel.FlxVideoSprite;
#end
/**
* Assumes you are in the PlayState.
*/
class VideoCutscene
{
static var blackScreen:FlxSprite;
/**
* Play a video cutscene.
* TODO: Currently this is hardcoded to start the countdown after the video is done.
* @param path The path to the video file. Use Paths.file(path) to get the correct path.
*/
public static function play(filePath:String):Void
{
if (PlayState.instance == null) return;
if (!openfl.Assets.exists(filePath))
{
trace('ERROR: Video file does not exist: ${filePath}');
return;
}
// Trigger the cutscene. Don't play the song in the background.
PlayState.instance.isInCutscene = true;
PlayState.instance.camHUD.visible = false;
PlayState.instance.camCutscene.visible = true;
// Display a black screen to hide the game while the video is playing.
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
blackScreen.scrollFactor.set(0, 0);
blackScreen.cameras = [PlayState.instance.camCutscene];
PlayState.instance.add(blackScreen);
#if html5
playVideoHTML5(filePath);
#else
playVideoNative(filePath);
#end
}
public static function isPlaying():Bool
{
return vid != null;
}
#if html5
static var vid:FlxVideo;
static function playVideoHTML5(filePath:String):Void
{
// Video displays OVER the FlxState.
vid = new FlxVideo(filePath);
if (vid != null)
{
vid.zIndex = 0;
vid.finishCallback = finishVideo.bind(0.5);
vid.cameras = [PlayState.instance.camCutscene];
PlayState.instance.add(vid);
PlayState.instance.refresh();
}
else
{
trace('ALERT: Video is null! Could not play cutscene!');
}
}
#else
static var vid:FlxVideoSprite;
static function playVideoNative(filePath:String):Void
{
// Video displays OVER the FlxState.
vid = new FlxVideoSprite(0, 0);
if (vid != null)
{
vid.zIndex = 0;
vid.bitmap.onEndReached.add(finishVideo.bind(0.5));
vid.cameras = [PlayState.instance.camCutscene];
PlayState.instance.add(vid);
PlayState.instance.refresh();
vid.play(filePath, false);
}
else
{
trace('ALERT: Video is null! Could not play cutscene!');
}
}
#end
public static function finishVideo(?transitionTime:Float = 0.5):Void
{
trace('ALERT: Finish video cutscene called!');
#if html5
if (vid != null)
{
PlayState.instance.remove(vid);
}
#else
if (vid != null)
{
vid.stop();
PlayState.instance.remove(vid);
}
#end
vid.destroy();
vid = null;
PlayState.instance.camCutscene.visible = true;
PlayState.instance.camHUD.visible = true;
FlxTween.tween(blackScreen, {alpha: 0}, transitionTime,
{
ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
PlayState.instance.remove(blackScreen);
blackScreen = null;
}
});
FlxTween.tween(FlxG.camera, {zoom: PlayState.instance.defaultCameraZoom}, transitionTime,
{
ease: FlxEase.quadInOut,
onComplete: function(twn:FlxTween) {
PlayState.instance.startCountdown();
}
});
}
}
/*
trace('Video playback failed (${e})');
vid = null;
finishCutscene(0.5);
*/

View file

@ -1,12 +1,14 @@
package funkin.play.event; package funkin.play.event;
import funkin.play.event.SongEvent;
import funkin.play.song.SongData; import funkin.play.song.SongData;
import funkin.play.event.SongEvent;
import funkin.play.event.SongEventData.SongEventFieldType;
import funkin.play.event.SongEventData.SongEventSchema;
/** /**
* This class represents a handler for a type of song event. * This class represents a handler for a type of song event.
* It is used by the ScriptedSongEvent class to handle user-defined events. * It is used by the ScriptedSongEvent class to handle user-defined events.
* *
* Example: Focus on Boyfriend: * Example: Focus on Boyfriend:
* ``` * ```
* { * {
@ -16,7 +18,7 @@ import funkin.play.song.SongData;
* } * }
* } * }
* ``` * ```
* *
* Example: Focus on 10px above Girlfriend: * Example: Focus on 10px above Girlfriend:
* ``` * ```
* { * {
@ -27,7 +29,7 @@ import funkin.play.song.SongData;
* } * }
* } * }
* ``` * ```
* *
* Example: Focus on (100, 100): * Example: Focus on (100, 100):
* ``` * ```
* { * {

View file

@ -3,6 +3,8 @@ package funkin.play.event;
import flixel.FlxSprite; import flixel.FlxSprite;
import funkin.play.character.BaseCharacter; import funkin.play.character.BaseCharacter;
import funkin.play.event.SongEvent; import funkin.play.event.SongEvent;
import funkin.play.event.SongEventData.SongEventFieldType;
import funkin.play.event.SongEventData.SongEventSchema;
import funkin.play.song.SongData; import funkin.play.song.SongData;
class PlayAnimationSongEvent extends SongEvent class PlayAnimationSongEvent extends SongEvent

View file

@ -7,7 +7,7 @@ import polymod.hscript.HScriptedClass;
* A script that can be tied to a SongEvent. * A script that can be tied to a SongEvent.
* Create a scripted class that extends SongEvent, * Create a scripted class that extends SongEvent,
* then call `super('SongEventType')` to use this. * then call `super('SongEventType')` to use this.
* *
* - Remember to override `handleEvent(data:SongEventData)` to perform your actions when the event is hit. * - Remember to override `handleEvent(data:SongEventData)` to perform your actions when the event is hit.
* - Remember to override `getTitle()` to return an event name that will be displayed in the editor. * - Remember to override `getTitle()` to return an event name that will be displayed in the editor.
* - Remember to override `getEventSchema()` to return a schema for the event data, used to build a form in the chart editor. * - Remember to override `getEventSchema()` to return a schema for the event data, used to build a form in the chart editor.

View file

@ -0,0 +1,90 @@
package funkin.play.event;
import funkin.util.Constants;
import flixel.tweens.FlxTween;
import flixel.FlxCamera;
import flixel.tweens.FlxEase;
import funkin.play.event.SongEvent;
import funkin.play.song.SongData;
import funkin.play.event.SongEventData;
import funkin.play.event.SongEventData.SongEventFieldType;
/**
* This class represents a handler for configuring camera bop intensity and rate.
*
* Example: Bop the camera twice as hard, once per beat (rather than once every four beats).
* ```
* {
* 'e': 'SetCameraBop',
* 'v': {
* 'intensity': 2.0,
* 'rate': 1,
* }
* }
* ```
*
* Example: Reset the camera bop to default values.
* ```
* {
* 'e': 'SetCameraBop',
* 'v': {}
* }
* ```
*/
class SetCameraBopSongEvent extends SongEvent
{
public function new()
{
super('SetCameraBop');
}
public override function handleEvent(data:SongEventData):Void
{
// Does nothing if there is no PlayState camera or stage.
if (PlayState.instance == null) return;
var rate:Null<Int> = data.getInt('rate');
if (rate == null) rate = Constants.DEFAULT_ZOOM_RATE;
var intensity:Null<Float> = data.getFloat('intensity');
if (intensity == null) intensity = 1.0;
PlayState.instance.cameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity;
PlayState.instance.hudCameraZoomIntensity = Constants.DEFAULT_ZOOM_INTENSITY * intensity * 2.0;
PlayState.instance.cameraZoomRate = rate;
trace('Set camera zoom rate to ${PlayState.instance.cameraZoomRate}');
}
public override function getTitle():String
{
return 'Set Camera Bop';
}
/**
* ```
* {
* 'intensity': FLOAT, // Zoom amount
* 'rate': INT, // Zoom rate (beats/zoom)
* }
* ```
* @return SongEventSchema
*/
public override function getEventSchema():SongEventSchema
{
return [
{
name: 'intensity',
title: 'Intensity',
defaultValue: 1.0,
step: 0.1,
type: SongEventFieldType.FLOAT
},
{
name: 'rate',
title: 'Rate (beats/zoom)',
defaultValue: 4,
step: 1,
type: SongEventFieldType.INTEGER,
}
];
}
}

View file

@ -1,7 +1,7 @@
package funkin.play.event; package funkin.play.event;
import funkin.util.macro.ClassMacro;
import funkin.play.song.SongData.SongEventData; import funkin.play.song.SongData.SongEventData;
import funkin.play.event.SongEventData.SongEventSchema;
/** /**
* This class represents a handler for a type of song event. * This class represents a handler for a type of song event.
@ -52,233 +52,3 @@ class SongEvent
return 'SongEvent(${this.id})'; return 'SongEvent(${this.id})';
} }
} }
/**
* This class statically handles the parsing of internal and scripted song event handlers.
*/
class SongEventParser
{
/**
* Every built-in event class must be added to this list.
* Thankfully, with the power of `SongEventMacro`, this is done automatically.
*/
static final BUILTIN_EVENTS:List<Class<SongEvent>> = ClassMacro.listSubclassesOf(SongEvent);
/**
* Map of internal handlers for song events.
* These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`.
*/
static final eventCache:Map<String, SongEvent> = new Map<String, SongEvent>();
public static function loadEventCache():Void
{
clearEventCache();
//
// BASE GAME EVENTS
//
registerBaseEvents();
registerScriptedEvents();
}
static function registerBaseEvents()
{
trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...');
for (eventCls in BUILTIN_EVENTS)
{
var eventClsName:String = Type.getClassName(eventCls);
if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue;
var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]);
if (event != null)
{
trace(' Loaded built-in song event: (${event.id})');
eventCache.set(event.id, event);
}
else
{
trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}');
}
}
}
static function registerScriptedEvents()
{
var scriptedEventClassNames:Array<String> = ScriptedSongEvent.listScriptClasses();
if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return;
trace('Instantiating ${scriptedEventClassNames.length} scripted song events...');
for (eventCls in scriptedEventClassNames)
{
var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN");
if (event != null)
{
trace(' Loaded scripted song event: ${event.id}');
eventCache.set(event.id, event);
}
else
{
trace(' Failed to instantiate scripted song event class: ${eventCls}');
}
}
}
public static function listEventIds():Array<String>
{
return eventCache.keys().array();
}
public static function listEvents():Array<SongEvent>
{
return eventCache.values();
}
public static function getEvent(id:String):SongEvent
{
return eventCache.get(id);
}
public static function getEventSchema(id:String):SongEventSchema
{
var event:SongEvent = getEvent(id);
if (event == null) return null;
return event.getEventSchema();
}
static function clearEventCache()
{
eventCache.clear();
}
public static function handleEvent(data:SongEventData):Void
{
var eventType:String = data.event;
var eventHandler:SongEvent = eventCache.get(eventType);
if (eventHandler != null)
{
eventHandler.handleEvent(data);
}
else
{
trace('WARNING: No event handler for event with id: ${eventType}');
}
data.activated = true;
}
public static inline function handleEvents(events:Array<SongEventData>):Void
{
for (event in events)
{
handleEvent(event);
}
}
/**
* Given a list of song events and the current timestamp,
* return a list of events that should be handled.
*/
public static function queryEvents(events:Array<SongEventData>, currentTime:Float):Array<SongEventData>
{
return events.filter(function(event:SongEventData):Bool
{
// If the event is already activated, don't activate it again.
if (event.activated) return false;
// If the event is in the future, don't activate it.
if (event.time > currentTime) return false;
return true;
});
}
/**
* Reset activation of all the provided events.
*/
public static function resetEvents(events:Array<SongEventData>):Void
{
for (event in events)
{
event.activated = false;
// TODO: Add an onReset() method to SongEvent?
}
}
}
enum abstract SongEventFieldType(String) from String to String
{
/**
* The STRING type will display as a text field.
*/
var STRING = "string";
/**
* The INTEGER type will display as a text field that only accepts numbers.
*/
var INTEGER = "integer";
/**
* The FLOAT type will display as a text field that only accepts numbers.
*/
var FLOAT = "float";
/**
* The BOOL type will display as a checkbox.
*/
var BOOL = "bool";
/**
* The ENUM type will display as a dropdown.
* Make sure to specify the `keys` field in the schema.
*/
var ENUM = "enum";
}
typedef SongEventSchemaField =
{
/**
* The name of the property as it should be saved in the event data.
*/
name:String,
/**
* The title of the field to display in the UI.
*/
title:String,
/**
* The type of the field.
*/
type:SongEventFieldType,
/**
* Used for ENUM values.
* The key is the display name and the value is the actual value.
*/
?keys:Map<String, Dynamic>,
/**
* Used for INTEGER and FLOAT values.
* The minimum value that can be entered.
*/
?min:Float,
/**
* Used for INTEGER and FLOAT values.
* The maximum value that can be entered.
*/
?max:Float,
/**
* Used for INTEGER and FLOAT values.
* The step value that will be used when incrementing/decrementing the value.
*/
?step:Float,
/**
* An optional default value for the field.
*/
?defaultValue:Dynamic,
}
typedef SongEventSchema = Array<SongEventSchemaField>;

View file

@ -0,0 +1,235 @@
package funkin.play.event;
import funkin.play.event.SongEventData.SongEventSchema;
import funkin.play.song.SongData.SongEventData;
import funkin.util.macro.ClassMacro;
import funkin.play.event.ScriptedSongEvent;
/**
* This class statically handles the parsing of internal and scripted song event handlers.
*/
class SongEventParser
{
/**
* Every built-in event class must be added to this list.
* Thankfully, with the power of `SongEventMacro`, this is done automatically.
*/
static final BUILTIN_EVENTS:List<Class<SongEvent>> = ClassMacro.listSubclassesOf(SongEvent);
/**
* Map of internal handlers for song events.
* These may be either `ScriptedSongEvents` or built-in classes extending `SongEvent`.
*/
static final eventCache:Map<String, SongEvent> = new Map<String, SongEvent>();
public static function loadEventCache():Void
{
clearEventCache();
//
// BASE GAME EVENTS
//
registerBaseEvents();
registerScriptedEvents();
}
static function registerBaseEvents()
{
trace('Instantiating ${BUILTIN_EVENTS.length} built-in song events...');
for (eventCls in BUILTIN_EVENTS)
{
var eventClsName:String = Type.getClassName(eventCls);
if (eventClsName == 'funkin.play.event.SongEvent' || eventClsName == 'funkin.play.event.ScriptedSongEvent') continue;
var event:SongEvent = Type.createInstance(eventCls, ["UNKNOWN"]);
if (event != null)
{
trace(' Loaded built-in song event: (${event.id})');
eventCache.set(event.id, event);
}
else
{
trace(' Failed to load built-in song event: ${Type.getClassName(eventCls)}');
}
}
}
static function registerScriptedEvents()
{
var scriptedEventClassNames:Array<String> = ScriptedSongEvent.listScriptClasses();
if (scriptedEventClassNames == null || scriptedEventClassNames.length == 0) return;
trace('Instantiating ${scriptedEventClassNames.length} scripted song events...');
for (eventCls in scriptedEventClassNames)
{
var event:SongEvent = ScriptedSongEvent.init(eventCls, "UKNOWN");
if (event != null)
{
trace(' Loaded scripted song event: ${event.id}');
eventCache.set(event.id, event);
}
else
{
trace(' Failed to instantiate scripted song event class: ${eventCls}');
}
}
}
public static function listEventIds():Array<String>
{
return eventCache.keys().array();
}
public static function listEvents():Array<SongEvent>
{
return eventCache.values();
}
public static function getEvent(id:String):SongEvent
{
return eventCache.get(id);
}
public static function getEventSchema(id:String):SongEventSchema
{
var event:SongEvent = getEvent(id);
if (event == null) return null;
return event.getEventSchema();
}
static function clearEventCache()
{
eventCache.clear();
}
public static function handleEvent(data:SongEventData):Void
{
var eventType:String = data.event;
var eventHandler:SongEvent = eventCache.get(eventType);
if (eventHandler != null)
{
eventHandler.handleEvent(data);
}
else
{
trace('WARNING: No event handler for event with id: ${eventType}');
}
data.activated = true;
}
public static inline function handleEvents(events:Array<SongEventData>):Void
{
for (event in events)
{
handleEvent(event);
}
}
/**
* Given a list of song events and the current timestamp,
* return a list of events that should be handled.
*/
public static function queryEvents(events:Array<SongEventData>, currentTime:Float):Array<SongEventData>
{
return events.filter(function(event:SongEventData):Bool {
// If the event is already activated, don't activate it again.
if (event.activated) return false;
// If the event is in the future, don't activate it.
if (event.time > currentTime) return false;
return true;
});
}
/**
* Reset activation of all the provided events.
*/
public static function resetEvents(events:Array<SongEventData>):Void
{
for (event in events)
{
event.activated = false;
// TODO: Add an onReset() method to SongEvent?
}
}
}
enum abstract SongEventFieldType(String) from String to String
{
/**
* The STRING type will display as a text field.
*/
var STRING = "string";
/**
* The INTEGER type will display as a text field that only accepts numbers.
*/
var INTEGER = "integer";
/**
* The FLOAT type will display as a text field that only accepts numbers.
*/
var FLOAT = "float";
/**
* The BOOL type will display as a checkbox.
*/
var BOOL = "bool";
/**
* The ENUM type will display as a dropdown.
* Make sure to specify the `keys` field in the schema.
*/
var ENUM = "enum";
}
typedef SongEventSchemaField =
{
/**
* The name of the property as it should be saved in the event data.
*/
name:String,
/**
* The title of the field to display in the UI.
*/
title:String,
/**
* The type of the field.
*/
type:SongEventFieldType,
/**
* Used for ENUM values.
* The key is the display name and the value is the actual value.
*/
?keys:Map<String, Dynamic>,
/**
* Used for INTEGER and FLOAT values.
* The minimum value that can be entered.
*/
?min:Float,
/**
* Used for INTEGER and FLOAT values.
* The maximum value that can be entered.
*/
?max:Float,
/**
* Used for INTEGER and FLOAT values.
* The step value that will be used when incrementing/decrementing the value.
*/
?step:Float,
/**
* An optional default value for the field.
*/
?defaultValue:Dynamic,
}
typedef SongEventSchema = Array<SongEventSchemaField>;

View file

@ -0,0 +1,147 @@
package funkin.play.event;
import flixel.tweens.FlxTween;
import flixel.FlxCamera;
import flixel.tweens.FlxEase;
import funkin.play.event.SongEvent;
import funkin.play.song.SongData;
import funkin.play.event.SongEventData;
import funkin.play.event.SongEventData.SongEventFieldType;
/**
* This class represents a handler for camera zoom events.
*
* Example: Zoom to 1.3x:
* ```
* {
* 'e': 'ZoomCamera',
* 'v': 1.3
* }
* ```
*
* Example: Zoom to 1.3x
* ```
* {
* 'e': 'FocusCamera',
* 'v': {
* 'char': 2,
* 'y': -10,
* }
* }
* ```
*
* Example: Focus on (100, 100):
* ```
* {
* 'e': 'FocusCamera',
* 'v': {
* 'char': -1,
* 'x': 100,
* 'y': 100,
* }
* }
* ```
*/
class ZoomCameraSongEvent extends SongEvent
{
public function new()
{
super('ZoomCamera');
}
public override function handleEvent(data:SongEventData):Void
{
// Does nothing if there is no PlayState camera or stage.
if (PlayState.instance == null) return;
var zoom:Null<Float> = data.getFloat('zoom');
if (zoom == null) zoom = 1.0;
var duration:Null<Float> = data.getFloat('duration');
if (duration == null) duration = 4.0;
var ease:Null<String> = data.getString('ease');
if (ease == null) ease = 'linear';
// If it's a string, check the value.
switch (ease)
{
case 'INSTANT':
// Set the zoom. Use defaultCameraZoom to prevent breaking camera bops.
PlayState.instance.defaultCameraZoom = zoom * FlxCamera.defaultZoom;
default:
var easeFunction:Null<Float->Float> = Reflect.field(FlxEase, ease);
if (easeFunction == null)
{
trace('Invalid ease function: $ease');
return;
}
FlxTween.tween(PlayState.instance, {defaultCameraZoom: zoom * FlxCamera.defaultZoom}, (Conductor.stepCrochet * duration / 1000), {ease: easeFunction});
}
}
public override function getTitle():String
{
return 'Zoom Camera';
}
/**
* ```
* {
* 'zoom': FLOAT, // Target zoom level.
* 'duration': FLOAT, // Optional duration in steps
* 'ease': ENUM, // Optional easing function
* }
* @return SongEventSchema
*/
public override function getEventSchema():SongEventSchema
{
return [
{
name: 'zoom',
title: 'Zoom Level',
defaultValue: 1.0,
step: 0.1,
type: SongEventFieldType.FLOAT
},
{
name: 'duration',
title: 'Duration (in steps)',
defaultValue: 4.0,
step: 0.5,
type: SongEventFieldType.FLOAT,
},
{
name: 'ease',
title: 'Easing Type',
defaultValue: 'linear',
type: SongEventFieldType.ENUM,
keys: [
'Linear' => 'linear',
'Instant' => 'INSTANT',
'Quad In' => 'quadIn',
'Quad Out' => 'quadOut',
'Quad In/Out' => 'quadInOut',
'Cube In' => 'cubeIn',
'Cube Out' => 'cubeOut',
'Cube In/Out' => 'cubeInOut',
'Quart In' => 'quartIn',
'Quart Out' => 'quartOut',
'Quart In/Out' => 'quartInOut',
'Quint In' => 'quintIn',
'Quint Out' => 'quintOut',
'Quint In/Out' => 'quintInOut',
'Smooth Step In' => 'smoothStepIn',
'Smooth Step Out' => 'smoothStepOut',
'Smooth Step In/Out' => 'smoothStepInOut',
'Sine In' => 'sineIn',
'Sine Out' => 'sineOut',
'Sine In/Out' => 'sineInOut',
'Elastic In' => 'elasticIn',
'Elastic Out' => 'elasticOut',
'Elastic In/Out' => 'elasticInOut',
]
}
];
}
}

View file

@ -324,7 +324,7 @@ class SongDifficulty
/** /**
* Build a list of vocal files for the given character. * Build a list of vocal files for the given character.
* Automatically resolves suffixed character IDs (so bf-car will resolve to bf if needed). * Automatically resolves suffixed character IDs (so bf-car will resolve to bf if needed).
* *
* @param id The character we are about to play. * @param id The character we are about to play.
*/ */
public function buildVoiceList(?id:String = 'bf'):Array<String> public function buildVoiceList(?id:String = 'bf'):Array<String>

View file

@ -24,7 +24,7 @@ class SongDataParser
/** /**
* Parses and preloads the game's song metadata and scripts when the game starts. * Parses and preloads the game's song metadata and scripts when the game starts.
* *
* If you want to force song metadata to be reloaded, you can just call this function again. * If you want to force song metadata to be reloaded, you can just call this function again.
*/ */
public static function loadSongCache():Void public static function loadSongCache():Void
@ -217,7 +217,7 @@ typedef RawSongMetadata =
{ {
/** /**
* A semantic versioning string for the song data format. * A semantic versioning string for the song data format.
* *
*/ */
var version:Version; var version:Version;
@ -414,7 +414,7 @@ abstract SongNoteData(RawSongNoteData)
/** /**
* The strumline index of the note, if applicable. * The strumline index of the note, if applicable.
* Strips the direction from the data. * Strips the direction from the data.
* *
* 0 = player, 1 = opponent, etc. * 0 = player, 1 = opponent, etc.
*/ */
public inline function getStrumlineIndex(strumlineSize:Int = 4):Int public inline function getStrumlineIndex(strumlineSize:Int = 4):Int

View file

@ -14,14 +14,13 @@ class SongDataUtils
* Given an array of SongNoteData objects, return a new array of SongNoteData objects * Given an array of SongNoteData objects, return a new array of SongNoteData objects
* whose timestamps are shifted by the given amount. * whose timestamps are shifted by the given amount.
* Does not mutate the original array. * Does not mutate the original array.
* *
* @param notes The notes to modify. * @param notes The notes to modify.
* @param offset The time difference to apply in milliseconds. * @param offset The time difference to apply in milliseconds.
*/ */
public static function offsetSongNoteData(notes:Array<SongNoteData>, offset:Int):Array<SongNoteData> public static function offsetSongNoteData(notes:Array<SongNoteData>, offset:Int):Array<SongNoteData>
{ {
return notes.map(function(note:SongNoteData):SongNoteData return notes.map(function(note:SongNoteData):SongNoteData {
{
return new SongNoteData(note.time + offset, note.data, note.length, note.kind); return new SongNoteData(note.time + offset, note.data, note.length, note.kind);
}); });
} }
@ -30,14 +29,13 @@ class SongDataUtils
* Given an array of SongEventData objects, return a new array of SongEventData objects * Given an array of SongEventData objects, return a new array of SongEventData objects
* whose timestamps are shifted by the given amount. * whose timestamps are shifted by the given amount.
* Does not mutate the original array. * Does not mutate the original array.
* *
* @param events The events to modify. * @param events The events to modify.
* @param offset The time difference to apply in milliseconds. * @param offset The time difference to apply in milliseconds.
*/ */
public static function offsetSongEventData(events:Array<SongEventData>, offset:Int):Array<SongEventData> public static function offsetSongEventData(events:Array<SongEventData>, offset:Int):Array<SongEventData>
{ {
return events.map(function(event:SongEventData):SongEventData return events.map(function(event:SongEventData):SongEventData {
{
return new SongEventData(event.time + offset, event.event, event.value); return new SongEventData(event.time + offset, event.event, event.value);
}); });
} }
@ -45,7 +43,7 @@ class SongDataUtils
/** /**
* Return a new array without a certain subset of notes from an array of SongNoteData objects. * Return a new array without a certain subset of notes from an array of SongNoteData objects.
* Does not mutate the original array. * Does not mutate the original array.
* *
* @param notes The array of notes to be subtracted from. * @param notes The array of notes to be subtracted from.
* @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word. * @param subtrahend The notes to remove from the `notes` array. Yes, subtrahend is a real word.
*/ */
@ -53,8 +51,7 @@ class SongDataUtils
{ {
if (notes.length == 0 || subtrahend.length == 0) return notes; if (notes.length == 0 || subtrahend.length == 0) return notes;
var result = notes.filter(function(note:SongNoteData):Bool var result = notes.filter(function(note:SongNoteData):Bool {
{
for (x in subtrahend) for (x in subtrahend)
// SongNoteData's == operation has been overridden so that this will work. // SongNoteData's == operation has been overridden so that this will work.
if (x == note) return false; if (x == note) return false;
@ -68,7 +65,7 @@ class SongDataUtils
/** /**
* Return a new array without a certain subset of events from an array of SongEventData objects. * Return a new array without a certain subset of events from an array of SongEventData objects.
* Does not mutate the original array. * Does not mutate the original array.
* *
* @param events The array of events to be subtracted from. * @param events The array of events to be subtracted from.
* @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word. * @param subtrahend The events to remove from the `events` array. Yes, subtrahend is a real word.
*/ */
@ -76,8 +73,7 @@ class SongDataUtils
{ {
if (events.length == 0 || subtrahend.length == 0) return events; if (events.length == 0 || subtrahend.length == 0) return events;
return events.filter(function(event:SongEventData):Bool return events.filter(function(event:SongEventData):Bool {
{
// SongEventData's == operation has been overridden so that this will work. // SongEventData's == operation has been overridden so that this will work.
return !subtrahend.has(event); return !subtrahend.has(event);
}); });
@ -89,8 +85,7 @@ class SongDataUtils
*/ */
public static function flipNotes(notes:Array<SongNoteData>, ?strumlineSize:Int = 4):Array<SongNoteData> public static function flipNotes(notes:Array<SongNoteData>, ?strumlineSize:Int = 4):Array<SongNoteData>
{ {
return notes.map(function(note:SongNoteData):SongNoteData return notes.map(function(note:SongNoteData):SongNoteData {
{
var newData = note.data; var newData = note.data;
if (newData < strumlineSize) newData += strumlineSize; if (newData < strumlineSize) newData += strumlineSize;
@ -103,7 +98,7 @@ class SongDataUtils
/** /**
* Prepare an array of notes to be used as the clipboard data. * Prepare an array of notes to be used as the clipboard data.
* *
* Offset the provided array of notes such that the first note is at 0 milliseconds. * Offset the provided array of notes such that the first note is at 0 milliseconds.
*/ */
public static function buildNoteClipboard(notes:Array<SongNoteData>):Array<SongNoteData> public static function buildNoteClipboard(notes:Array<SongNoteData>):Array<SongNoteData>
@ -113,7 +108,7 @@ class SongDataUtils
/** /**
* Prepare an array of events to be used as the clipboard data. * Prepare an array of events to be used as the clipboard data.
* *
* Offset the provided array of events such that the first event is at 0 milliseconds. * Offset the provided array of events such that the first event is at 0 milliseconds.
*/ */
public static function buildEventClipboard(events:Array<SongEventData>):Array<SongEventData> public static function buildEventClipboard(events:Array<SongEventData>):Array<SongEventData>
@ -127,8 +122,7 @@ class SongDataUtils
public static function sortNotes(notes:Array<SongNoteData>, ?desc:Bool = false):Array<SongNoteData> public static function sortNotes(notes:Array<SongNoteData>, ?desc:Bool = false):Array<SongNoteData>
{ {
// TODO: Modifies the array in place. Is this okay? // TODO: Modifies the array in place. Is this okay?
notes.sort(function(a:SongNoteData, b:SongNoteData):Int notes.sort(function(a:SongNoteData, b:SongNoteData):Int {
{
return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time);
}); });
return notes; return notes;
@ -140,8 +134,7 @@ class SongDataUtils
public static function sortEvents(events:Array<SongEventData>, ?desc:Bool = false):Array<SongEventData> public static function sortEvents(events:Array<SongEventData>, ?desc:Bool = false):Array<SongEventData>
{ {
// TODO: Modifies the array in place. Is this okay? // TODO: Modifies the array in place. Is this okay?
events.sort(function(a:SongEventData, b:SongEventData):Int events.sort(function(a:SongEventData, b:SongEventData):Int {
{
return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time); return FlxSort.byValues(desc ? FlxSort.DESCENDING : FlxSort.ASCENDING, a.time, b.time);
}); });
return events; return events;
@ -192,8 +185,7 @@ class SongDataUtils
*/ */
public static function getNotesInTimeRange(notes:Array<SongNoteData>, start:Float, end:Float):Array<SongNoteData> public static function getNotesInTimeRange(notes:Array<SongNoteData>, start:Float, end:Float):Array<SongNoteData>
{ {
return notes.filter(function(note:SongNoteData):Bool return notes.filter(function(note:SongNoteData):Bool {
{
return note.time >= start && note.time <= end; return note.time >= start && note.time <= end;
}); });
} }
@ -203,8 +195,7 @@ class SongDataUtils
*/ */
public static function getEventsInTimeRange(events:Array<SongEventData>, start:Float, end:Float):Array<SongEventData> public static function getEventsInTimeRange(events:Array<SongEventData>, start:Float, end:Float):Array<SongEventData>
{ {
return events.filter(function(event:SongEventData):Bool return events.filter(function(event:SongEventData):Bool {
{
return event.time >= start && event.time <= end; return event.time >= start && event.time <= end;
}); });
} }
@ -214,8 +205,7 @@ class SongDataUtils
*/ */
public static function getNotesInDataRange(notes:Array<SongNoteData>, start:Int, end:Int):Array<SongNoteData> public static function getNotesInDataRange(notes:Array<SongNoteData>, start:Int, end:Int):Array<SongNoteData>
{ {
return notes.filter(function(note:SongNoteData):Bool return notes.filter(function(note:SongNoteData):Bool {
{
return note.data >= start && note.data <= end; return note.data >= start && note.data <= end;
}); });
} }
@ -225,8 +215,7 @@ class SongDataUtils
*/ */
public static function getNotesWithData(notes:Array<SongNoteData>, data:Array<Int>):Array<SongNoteData> public static function getNotesWithData(notes:Array<SongNoteData>, data:Array<Int>):Array<SongNoteData>
{ {
return notes.filter(function(note:SongNoteData):Bool return notes.filter(function(note:SongNoteData):Bool {
{
return data.indexOf(note.data) != -1; return data.indexOf(note.data) != -1;
}); });
} }

View file

@ -50,8 +50,7 @@ class SongSerializer
*/ */
public static function importSongChartDataAsync(callback:SongChartData->Void):Void public static function importSongChartDataAsync(callback:SongChartData->Void):Void
{ {
browseFileReference(function(fileReference:FileReference) browseFileReference(function(fileReference:FileReference) {
{
var data = fileReference.data.toString(); var data = fileReference.data.toString();
if (data == null) return; if (data == null) return;
@ -68,8 +67,7 @@ class SongSerializer
*/ */
public static function importSongMetadataAsync(callback:SongMetadata->Void):Void public static function importSongMetadataAsync(callback:SongMetadata->Void):Void
{ {
browseFileReference(function(fileReference:FileReference) browseFileReference(function(fileReference:FileReference) {
{
var data = fileReference.data.toString(); var data = fileReference.data.toString();
if (data == null) return; if (data == null) return;
@ -103,7 +101,7 @@ class SongSerializer
/** /**
* Save a SongChartData object as a JSON file to a specified path. * Save a SongChartData object as a JSON file to a specified path.
* Works great on HTML5 and desktop. * Works great on HTML5 and desktop.
* *
* @param path The file path to save to. * @param path The file path to save to.
*/ */
public static function exportSongChartDataAs(path:String, data:SongChartData) public static function exportSongChartDataAs(path:String, data:SongChartData)
@ -116,7 +114,7 @@ class SongSerializer
/** /**
* Save a SongMetadata object as a JSON file to a specified path. * Save a SongMetadata object as a JSON file to a specified path.
* Works great on HTML5 and desktop. * Works great on HTML5 and desktop.
* *
* @param path The file path to save to. * @param path The file path to save to.
*/ */
public static function exportSongMetadataAs(path:String, data:SongMetadata) public static function exportSongMetadataAs(path:String, data:SongMetadata)
@ -163,19 +161,17 @@ class SongSerializer
/** /**
* Browse for a file to read and execute a callback once we have a file reference. * Browse for a file to read and execute a callback once we have a file reference.
* Works great on HTML5 or desktop. * Works great on HTML5 or desktop.
* *
* @param callback The function to call when the file is loaded. * @param callback The function to call when the file is loaded.
*/ */
static function browseFileReference(callback:FileReference->Void) static function browseFileReference(callback:FileReference->Void)
{ {
var file = new FileReference(); var file = new FileReference();
file.addEventListener(Event.SELECT, function(e) file.addEventListener(Event.SELECT, function(e) {
{
var selectedFileRef:FileReference = e.target; var selectedFileRef:FileReference = e.target;
trace('Selected file: ' + selectedFileRef.name); trace('Selected file: ' + selectedFileRef.name);
selectedFileRef.addEventListener(Event.COMPLETE, function(e) selectedFileRef.addEventListener(Event.COMPLETE, function(e) {
{
var loadedFileRef:FileReference = e.target; var loadedFileRef:FileReference = e.target;
trace('Loaded file: ' + loadedFileRef.name); trace('Loaded file: ' + loadedFileRef.name);
callback(loadedFileRef); callback(loadedFileRef);
@ -192,16 +188,13 @@ class SongSerializer
static function writeFileReference(path:String, data:String) static function writeFileReference(path:String, data:String)
{ {
var file = new FileReference(); var file = new FileReference();
file.addEventListener(Event.COMPLETE, function(e:Event) file.addEventListener(Event.COMPLETE, function(e:Event) {
{
trace('Successfully wrote file.'); trace('Successfully wrote file.');
}); });
file.addEventListener(Event.CANCEL, function(e:Event) file.addEventListener(Event.CANCEL, function(e:Event) {
{
trace('Cancelled writing file.'); trace('Cancelled writing file.');
}); });
file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) file.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent) {
{
trace('IO error writing file.'); trace('IO error writing file.');
}); });
file.save(data, path); file.save(data, path);

View file

@ -30,7 +30,7 @@ class SongValidator
/** /**
* Validates the fields of a SongMetadata object (excluding the version field). * Validates the fields of a SongMetadata object (excluding the version field).
* *
* @param input The SongMetadata object to validate. * @param input The SongMetadata object to validate.
* @param songId The ID of the song being validated. Only used for error messages. * @param songId The ID of the song being validated. Only used for error messages.
* @return The validated SongMetadata object. * @return The validated SongMetadata object.
@ -73,7 +73,7 @@ class SongValidator
/** /**
* Validates the fields of a SongPlayData object. * Validates the fields of a SongPlayData object.
* *
* @param input The SongPlayData object to validate. * @param input The SongPlayData object to validate.
* @param songId The ID of the song being validated. Only used for error messages. * @param songId The ID of the song being validated. Only used for error messages.
* @return The validated SongPlayData object. * @return The validated SongPlayData object.
@ -85,7 +85,7 @@ class SongValidator
/** /**
* Validates the fields of a TimeChange object. * Validates the fields of a TimeChange object.
* *
* @param input The TimeChange object to validate. * @param input The TimeChange object to validate.
* @param songId The ID of the song being validated. Only used for error messages. * @param songId The ID of the song being validated. Only used for error messages.
* @return The validated TimeChange object. * @return The validated TimeChange object.
@ -113,7 +113,7 @@ class SongValidator
/** /**
* Validates the fields of a SongChartData object (excluding the version field). * Validates the fields of a SongChartData object (excluding the version field).
* *
* @param input The SongChartData object to validate. * @param input The SongChartData object to validate.
* @param songId The ID of the song being validated. Only used for error messages. * @param songId The ID of the song being validated. Only used for error messages.
* @return The validated SongChartData object. * @return The validated SongChartData object.

View file

@ -26,7 +26,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
* Whether the bopper should dance left and right. * Whether the bopper should dance left and right.
* - If true, alternate playing `danceLeft` and `danceRight`. * - If true, alternate playing `danceLeft` and `danceRight`.
* - If false, play `idle` every time. * - If false, play `idle` every time.
* *
* You can manually set this value, or you can leave it as `null` to determine it automatically. * You can manually set this value, or you can leave it as `null` to determine it automatically.
*/ */
public var shouldAlternate:Null<Bool> = null; public var shouldAlternate:Null<Bool> = null;
@ -139,7 +139,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
* @param name The name of the current animation. * @param name The name of the current animation.
* @param frameNumber The number of the current frame. * @param frameNumber The number of the current frame.
* @param frameIndex The index of the current frame. * @param frameIndex The index of the current frame.
* *
* For example, if an animation was defined as having the indexes [3, 0, 1, 2], * For example, if an animation was defined as having the indexes [3, 0, 1, 2],
* then the first callback would have frameNumber = 0 and frameIndex = 3. * then the first callback would have frameNumber = 0 and frameIndex = 3.
*/ */
@ -223,7 +223,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
/** /**
* Ensure that a given animation exists before playing it. * Ensure that a given animation exists before playing it.
* Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play. * Will gracefully check for name, then name with stripped suffixes, then 'idle', then fail to play.
* @param name * @param name
*/ */
function correctAnimationName(name:String):String function correctAnimationName(name:String):String
{ {

View file

@ -19,7 +19,7 @@ typedef StagePropGroup = FlxTypedSpriteGroup<StageProp>;
/** /**
* A Stage is a group of objects rendered in the PlayState. * A Stage is a group of objects rendered in the PlayState.
* *
* A Stage is comprised of one or more props, each of which is a FlxSprite. * A Stage is comprised of one or more props, each of which is a FlxSprite.
*/ */
class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
@ -39,8 +39,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
* The Stage elements get initialized at the beginning of the game. * The Stage elements get initialized at the beginning of the game.
* They're used to cache the data needed to build the stage, * They're used to cache the data needed to build the stage,
* then accessed and fleshed out when the stage needs to be built. * then accessed and fleshed out when the stage needs to be built.
* *
* @param stageId * @param stageId
*/ */
public function new(stageId:String) public function new(stageId:String)
{ {
@ -543,7 +543,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass
/** /**
* Retrieve a list of all the asset paths required to load the stage. * Retrieve a list of all the asset paths required to load the stage.
* Override this in a scripted class to ensure that all necessary assets are loaded! * Override this in a scripted class to ensure that all necessary assets are loaded!
* *
* @return An array of file names. * @return An array of file names.
*/ */
public function fetchAssetPaths():Array<String> public function fetchAssetPaths():Array<String>

View file

@ -31,7 +31,7 @@ class StageDataParser
/** /**
* Parses and preloads the game's stage data and scripts when the game starts. * Parses and preloads the game's stage data and scripts when the game starts.
* *
* If you want to force stages to be reloaded, you can just call this function again. * If you want to force stages to be reloaded, you can just call this function again.
*/ */
public static function loadStageCache():Void public static function loadStageCache():Void
@ -68,8 +68,7 @@ class StageDataParser
// UNSCRIPTED STAGES // UNSCRIPTED STAGES
// //
var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/'); var stageIdList:Array<String> = DataAssets.listDataFilesInPath('stages/');
var unscriptedStageIds:Array<String> = stageIdList.filter(function(stageId:String):Bool var unscriptedStageIds:Array<String> = stageIdList.filter(function(stageId:String):Bool {
{
return !stageCache.exists(stageId); return !stageCache.exists(stageId);
}); });
trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...'); trace(' Instantiating ${unscriptedStageIds.length} non-scripted stages...');
@ -126,7 +125,7 @@ class StageDataParser
/** /**
* Load a stage's JSON file, parse its data, and return it. * Load a stage's JSON file, parse its data, and return it.
* *
* @param stageId The stage to load. * @param stageId The stage to load.
* @return The stage data, or null if validation failed. * @return The stage data, or null if validation failed.
*/ */
@ -199,8 +198,8 @@ class StageDataParser
/** /**
* Set unspecified parameters to their defaults. * Set unspecified parameters to their defaults.
* If the parameter is mandatory, print an error message. * If the parameter is mandatory, print an error message.
* @param id * @param id
* @param input * @param input
* @return The validated stage data * @return The validated stage data
*/ */
static function validateStageData(id:String, input:StageData):Null<StageData> static function validateStageData(id:String, input:StageData):Null<StageData>
@ -461,7 +460,7 @@ typedef StageDataProp =
* If not zero, this prop will play an animation every X beats of the song. * If not zero, this prop will play an animation every X beats of the song.
* This requires animations to be defined. If `danceLeft` and `danceRight` are defined, * This requires animations to be defined. If `danceLeft` and `danceRight` are defined,
* they will alternated between, otherwise the `idle` animation will be used. * they will alternated between, otherwise the `idle` animation will be used.
* *
* @default 0 * @default 0
*/ */
var danceEvery:Null<Int>; var danceEvery:Null<Int>;

View file

@ -18,7 +18,7 @@ class StageProp extends FlxSprite implements IStateStageProp
/** /**
* Called when this prop is added to the stage. * Called when this prop is added to the stage.
* @param event * @param event
*/ */
public function onAdd(event:ScriptEvent):Void {} public function onAdd(event:ScriptEvent):Void {}

View file

@ -13,7 +13,7 @@ class AngleMask extends FlxShader
vec2 uv = openfl_TextureCoordv.xy; vec2 uv = openfl_TextureCoordv.xy;
vec2 start = vec2(0.0, 0.0); vec2 start = vec2(0.0, 0.0);
vec2 end = vec2(endPosition.x / openfl_TextureSize.x, 1.0); vec2 end = vec2(endPosition.x / openfl_TextureSize.x, 1.0);

View file

@ -66,8 +66,8 @@ class ColorSwapShader extends FlxShader
const float offset = 1.0 / 128.0; const float offset = 1.0 / 128.0;
vec3 normalizeColor(vec3 color) vec3 normalizeColor(vec3 color)
{ {
@ -101,7 +101,7 @@ class ColorSwapShader extends FlxShader
vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv); vec4 color = flixel_texture2D(bitmap, openfl_TextureCoordv);
vec4 swagColor = vec4(rgb2hsv(vec3(color[0], color[1], color[2])), color[3]); vec4 swagColor = vec4(rgb2hsv(vec3(color[0], color[1], color[2])), color[3]);
// [0] is the hue??? // [0] is the hue???
swagColor[0] += uTime; swagColor[0] += uTime;
// swagColor[1] += uTime; // swagColor[1] += uTime;
@ -109,7 +109,7 @@ class ColorSwapShader extends FlxShader
// money += swagColor[0]; // money += swagColor[0];
color = vec4(hsv2rgb(vec3(swagColor[0], swagColor[1], swagColor[2])), swagColor[3]); color = vec4(hsv2rgb(vec3(swagColor[0], swagColor[1], swagColor[2])), swagColor[3]);
if (awesomeOutline) if (awesomeOutline)
{ {
@ -119,7 +119,7 @@ class ColorSwapShader extends FlxShader
if (color.a <= 0.5) { if (color.a <= 0.5) {
float w = size.x / openfl_TextureSize.x; float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y; float h = size.y / openfl_TextureSize.y;
if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0. if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0.
@ -130,12 +130,12 @@ class ColorSwapShader extends FlxShader
} }
gl_FragColor = color; gl_FragColor = color;
/* /*
if (color.a > 0.5) if (color.a > 0.5)
gl_FragColor = color; gl_FragColor = color;
else else

View file

@ -47,7 +47,7 @@ class OverlayBlend extends FlxShader
{ {
vec2 funnyUv = openfl_TextureCoordv; vec2 funnyUv = openfl_TextureCoordv;
vec4 color = flixel_texture2D(bitmap, funnyUv); vec4 color = flixel_texture2D(bitmap, funnyUv);
vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy); vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy);
vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv.xy + vec2(0.1, 0.2)); vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv.xy + vec2(0.1, 0.2));

View file

@ -45,7 +45,7 @@ class ScreenWipeShader extends FlxShader
{ {
vec2 funnyUv = openfl_TextureCoordv; vec2 funnyUv = openfl_TextureCoordv;
vec4 color = flixel_texture2D(bitmap, funnyUv); vec4 color = flixel_texture2D(bitmap, funnyUv);
vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy); vec2 reallyFunnyUv = vec2(vec2(0.0, 0.0) - gl_FragCoord.xy / openfl_TextureSize.xy);
vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv); vec4 gf = flixel_texture2D(funnyShit, openfl_TextureCoordv);

View file

@ -44,7 +44,7 @@ class StrokeShader extends FlxShader
if (sample.a == 0.) { if (sample.a == 0.) {
float w = size.x / openfl_TextureSize.x; float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y; float h = size.y / openfl_TextureSize.y;
if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0. if (flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x + w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y)).a != 0.
|| flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0. || flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x, openfl_TextureCoordv.y + h)).a != 0.

View file

@ -50,7 +50,7 @@ class TitleOutline extends FlxShader
if (color.a == 0.0) { if (color.a == 0.0) {
float w = size.x / openfl_TextureSize.x; float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y; float h = size.y / openfl_TextureSize.y;
vec4 colorOffset = flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y - h)); vec4 colorOffset = flixel_texture2D(bitmap, vec2(openfl_TextureCoordv.x - w, openfl_TextureCoordv.y - h));
@ -59,7 +59,7 @@ class TitleOutline extends FlxShader
if (hsvShit.b <= 0.1 && colorOffset.a != 0.) if (hsvShit.b <= 0.1 && colorOffset.a != 0.)
color = vec4(0.0, 1.0, 0.8, color.a); color = vec4(0.0, 1.0, 0.8, color.a);
} }
gl_FragColor = color; gl_FragColor = color;
} }

View file

@ -86,7 +86,7 @@ class AtlasText extends FlxTypedSpriteGroup<AtlasChar>
/** /**
* Converts all characters to fit the font's `allowedCase`. * Converts all characters to fit the font's `allowedCase`.
* @param text * @param text
*/ */
function restrictCase(text:String) function restrictCase(text:String)
{ {

View file

@ -121,8 +121,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
var margin = 100; var margin = 100;
menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2); menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2);
menuCamera.minScrollY = 0; menuCamera.minScrollY = 0;
controlGrid.onChange.add(function(selected) controlGrid.onChange.add(function(selected) {
{
camFollow.y = selected.y; camFollow.y = selected.y;
labels.forEach((label) -> label.alpha = 0.6); labels.forEach((label) -> label.alpha = 0.6);

View file

@ -121,9 +121,9 @@ class MenuTypedList<T:MenuItem> extends FlxTypedGroup<T>
/** /**
* Controls navigation on a linear list of items such as Vertical. * Controls navigation on a linear list of items such as Vertical.
* @param prev * @param prev
* @param next * @param next
* @param allowWrap * @param allowWrap
*/ */
inline function navList(prev:Bool, next:Bool, allowWrap:Bool) inline function navList(prev:Bool, next:Bool, allowWrap:Bool)
{ {
@ -164,8 +164,7 @@ class MenuTypedList<T:MenuItem> extends FlxTypedGroup<T>
{ {
busy = true; busy = true;
FlxG.sound.play(Paths.sound('confirmMenu')); FlxG.sound.play(Paths.sound('confirmMenu'));
FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) FlxFlicker.flicker(selected, 1, 0.06, true, false, function(_) {
{
busy = false; busy = false;
selected.callback(); selected.callback();
}); });

View file

@ -36,14 +36,12 @@ class NgPrompt extends Prompt
#if web #if web
prompt.buttons.getItem("yes").fireInstantly = true; prompt.buttons.getItem("yes").fireInstantly = true;
#end #end
prompt.onYes = function() prompt.onYes = function() {
{
prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end); prompt.setText("Connecting..." #if web + "\n(check your popup blocker)" #end);
prompt.setButtons(None); prompt.setButtons(None);
openPassportUrl(); openPassportUrl();
}; };
prompt.onNo = function() prompt.onNo = function() {
{
prompt.close(); prompt.close();
prompt = null; prompt = null;
NGio.cancelLogin(); NGio.cancelLogin();
@ -92,8 +90,7 @@ class NgPrompt extends Prompt
{ {
var user = io.newgrounds.NG.core.user.name; var user = io.newgrounds.NG.core.user.name;
var prompt = new NgPrompt('Log out of $user?', Yes_No); var prompt = new NgPrompt('Log out of $user?', Yes_No);
prompt.onYes = function() prompt.onYes = function() {
{
NGio.logout(); NGio.logout();
prompt.close(); prompt.close();
}; };

View file

@ -145,8 +145,7 @@ class Page extends FlxGroup
function openPrompt(prompt:Prompt, onClose:Void->Void) function openPrompt(prompt:Prompt, onClose:Void->Void)
{ {
enabled = false; enabled = false;
prompt.closeCallback = function() prompt.closeCallback = function() {
{
enabled = true; enabled = true;
if (onClose != null) onClose(); if (onClose != null) onClose();
} }
@ -217,16 +216,15 @@ class OptionsMenu extends Page
/** /**
* Calls openPrompt and redraws the login/logout button * Calls openPrompt and redraws the login/logout button
* @param prompt * @param prompt
* @param onClose * @param onClose
*/ */
public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void) public function openNgPrompt(prompt:Prompt, ?onClose:Void->Void)
{ {
var onPromptClose = checkLoginStatus; var onPromptClose = checkLoginStatus;
if (onClose != null) if (onClose != null)
{ {
onPromptClose = function() onPromptClose = function() {
{
checkLoginStatus(); checkLoginStatus();
onClose(); onClose();
} }

View file

@ -55,8 +55,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
FlxTween.tween(rating, {alpha: 0}, 0.2, FlxTween.tween(rating, {alpha: 0}, 0.2,
{ {
onComplete: function(tween:FlxTween) onComplete: function(tween:FlxTween) {
{
remove(rating, true); remove(rating, true);
rating.destroy(); rating.destroy();
}, },
@ -106,8 +105,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, FlxTween.tween(comboSpr, {alpha: 0}, 0.2,
{ {
onComplete: function(tween:FlxTween) onComplete: function(tween:FlxTween) {
{
remove(comboSpr, true); remove(comboSpr, true);
comboSpr.destroy(); comboSpr.destroy();
}, },
@ -153,8 +151,7 @@ class PopUpStuff extends FlxTypedGroup<FlxSprite>
FlxTween.tween(numScore, {alpha: 0}, 0.2, FlxTween.tween(numScore, {alpha: 0}, 0.2,
{ {
onComplete: function(tween:FlxTween) onComplete: function(tween:FlxTween) {
{
remove(numScore, true); remove(numScore, true);
numScore.destroy(); numScore.destroy();
}, },

View file

@ -43,8 +43,7 @@ class PreferencesMenu extends Page
menuCamera.deadzone.set(0, margin, menuCamera.width, 40); menuCamera.deadzone.set(0, margin, menuCamera.width, 40);
menuCamera.minScrollY = 0; menuCamera.minScrollY = 0;
items.onChange.add(function(selected) items.onChange.add(function(selected) {
{
camFollow.y = selected.y; camFollow.y = selected.y;
}); });
} }
@ -82,8 +81,7 @@ class PreferencesMenu extends Page
function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void function createPrefItem(prefName:String, prefString:String, prefValue:Dynamic):Void
{ {
items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() items.createItem(120, (120 * items.length) + 30, prefName, AtlasFont.BOLD, function() {
{
preferenceCheck(prefString, prefValue); preferenceCheck(prefString, prefValue);
switch (Type.typeof(prefValue).getName()) switch (Type.typeof(prefValue).getName())
@ -145,8 +143,7 @@ class PreferencesMenu extends Page
// menuCamera.followLerp = CoolUtil.camLerpShit(0.05); // menuCamera.followLerp = CoolUtil.camLerpShit(0.05);
items.forEach(function(daItem:TextMenuItem) items.forEach(function(daItem:TextMenuItem) {
{
if (items.selectedItem == daItem) daItem.x = 150; if (items.selectedItem == daItem) daItem.x = 150;
else else
daItem.x = 120; daItem.x = 120;

View file

@ -17,7 +17,7 @@ import openfl.geom.Matrix;
import openfl.display.Sprite; import openfl.display.Sprite;
import openfl.display.Bitmap; import openfl.display.Bitmap;
class StickerSubState extends MusicBeatSubstate class StickerSubState extends MusicBeatSubState
{ {
public var grpStickers:FlxTypedGroup<StickerSprite>; public var grpStickers:FlxTypedGroup<StickerSprite>;
@ -36,7 +36,8 @@ class StickerSubState extends MusicBeatSubstate
add(grpStickers); add(grpStickers);
// makes the stickers on the most recent camera, which is more often than not... a UI camera!! // makes the stickers on the most recent camera, which is more often than not... a UI camera!!
grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]]; // grpStickers.cameras = [FlxG.cameras.list[FlxG.cameras.list.length - 1]];
grpStickers.cameras = FlxG.cameras.list;
if (oldStickers != null) if (oldStickers != null)
{ {
@ -208,8 +209,10 @@ class StickerSubState extends MusicBeatSubstate
FlxG.switchState(new FreeplayState(this)); FlxG.switchState(new FreeplayState(this));
case STORY: case STORY:
FlxG.switchState(new StoryMenuState(this)); FlxG.switchState(new StoryMenuState(this));
case MAIN_MENU:
FlxG.switchState(new MainMenuState());
default: default:
FlxG.switchState(new FreeplayState(this)); FlxG.switchState(new MainMenuState());
} }
} }
@ -354,6 +357,7 @@ typedef StickerShit =
enum abstract NEXTSTATE(String) enum abstract NEXTSTATE(String)
{ {
var MAIN_MENU = 'mainmenu';
var FREEPLAY = 'freeplay'; var FREEPLAY = 'freeplay';
var STORY = 'story'; var STORY = 'story';
} }

View file

@ -44,7 +44,7 @@ import sys.io.File;
class DebugBoundingState extends FlxState class DebugBoundingState extends FlxState
{ {
/* /*
TODAY'S TO-DO TODAY'S TO-DO
- Cleaner UI - Cleaner UI
*/ */

View file

@ -2,11 +2,11 @@ package funkin.ui.debug;
import flixel.FlxObject; import flixel.FlxObject;
import flixel.FlxSprite; import flixel.FlxSprite;
import funkin.MusicBeatSubstate; import funkin.MusicBeatSubState;
import funkin.ui.TextMenuList; import funkin.ui.TextMenuList;
import funkin.ui.debug.charting.ChartEditorState; import funkin.ui.debug.charting.ChartEditorState;
class DebugMenuSubState extends MusicBeatSubstate class DebugMenuSubState extends MusicBeatSubState
{ {
var items:TextMenuList; var items:TextMenuList;

View file

@ -552,7 +552,7 @@ class ChartEditorDialogHandler
/** /**
* Builds and opens a dialog displaying the user guide, providing guidance and help on how to use the chart editor. * Builds and opens a dialog displaying the user guide, providing guidance and help on how to use the chart editor.
* *
* @param state The current chart editor state. * @param state The current chart editor state.
* @return The dialog that was opened. * @return The dialog that was opened.
*/ */

View file

@ -58,7 +58,7 @@ using Lambda;
* Built with HaxeUI for use by both developers and modders. * Built with HaxeUI for use by both developers and modders.
* *
* Some functionality is moved to other classes to help maintain my sanity. * Some functionality is moved to other classes to help maintain my sanity.
* *
* @author MasterEric * @author MasterEric
*/ */
// Give other classes access to private instance fields // Give other classes access to private instance fields
@ -602,7 +602,7 @@ class ChartEditorState extends HaxeUIState
* A map of the audio tracks for each character's vocals. * A map of the audio tracks for each character's vocals.
* - Keys are the character IDs. * - Keys are the character IDs.
* - Values are the FlxSound objects to play that character's vocals. * - Values are the FlxSound objects to play that character's vocals.
* *
* When switching characters, the elements of the VoicesGroup will be swapped to match the new character. * When switching characters, the elements of the VoicesGroup will be swapped to match the new character.
*/ */
var audioVocalTracks:Map<String, FlxSound> = new Map<String, FlxSound>(); var audioVocalTracks:Map<String, FlxSound> = new Map<String, FlxSound>();
@ -2945,7 +2945,7 @@ class ChartEditorState extends HaxeUIState
/** /**
* Loads an instrumental from an absolute file path, replacing the current instrumental. * Loads an instrumental from an absolute file path, replacing the current instrumental.
* *
* @param path The absolute path to the audio file. * @param path The absolute path to the audio file.
* @return Success or failure. * @return Success or failure.
*/ */
@ -3132,7 +3132,7 @@ class ChartEditorState extends HaxeUIState
/** /**
* Perform (or redo) a command, then add it to the undo stack. * Perform (or redo) a command, then add it to the undo stack.
* *
* @param command The command to perform. * @param command The command to perform.
* @param purgeRedoStack If true, the redo stack will be cleared. * @param purgeRedoStack If true, the redo stack will be cleared.
*/ */

View file

@ -3,6 +3,7 @@ package funkin.ui.debug.charting;
import haxe.ui.data.ArrayDataSource; import haxe.ui.data.ArrayDataSource;
import funkin.play.character.BaseCharacter.CharacterType; import funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.event.SongEvent; import funkin.play.event.SongEvent;
import funkin.play.event.SongEventData;
import funkin.play.song.SongData.SongTimeChange; import funkin.play.song.SongData.SongTimeChange;
import funkin.play.song.SongSerializer; import funkin.play.song.SongSerializer;
import funkin.ui.haxeui.components.CharacterPlayer; import funkin.ui.haxeui.components.CharacterPlayer;
@ -133,8 +134,7 @@ class ChartEditorToolboxHandler
toolbox.x = 50; toolbox.x = 50;
toolbox.y = 50; toolbox.y = 50;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxTools', false); state.setUICheckboxSelected('menubarItemToggleToolboxTools', false);
} }
@ -142,8 +142,7 @@ class ChartEditorToolboxHandler
if (toolsGroup == null) return null; if (toolsGroup == null) return null;
toolsGroup.onChange = (event:UIEvent) -> toolsGroup.onChange = (event:UIEvent) -> {
{
switch (event.target.id) switch (event.target.id)
{ {
case 'toolboxToolsGroupSelect': case 'toolboxToolsGroupSelect':
@ -168,8 +167,7 @@ class ChartEditorToolboxHandler
toolbox.x = 75; toolbox.x = 75;
toolbox.y = 100; toolbox.y = 100;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false); state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false);
} }
@ -177,8 +175,7 @@ class ChartEditorToolboxHandler
var toolboxNotesCustomKindLabel:Label = toolbox.findComponent("toolboxNotesCustomKindLabel", Label); var toolboxNotesCustomKindLabel:Label = toolbox.findComponent("toolboxNotesCustomKindLabel", Label);
var toolboxNotesCustomKind:TextField = toolbox.findComponent("toolboxNotesCustomKind", TextField); var toolboxNotesCustomKind:TextField = toolbox.findComponent("toolboxNotesCustomKind", TextField);
toolboxNotesNoteKind.onChange = (event:UIEvent) -> toolboxNotesNoteKind.onChange = (event:UIEvent) -> {
{
var isCustom = (event.data.id == '~CUSTOM~'); var isCustom = (event.data.id == '~CUSTOM~');
if (isCustom) if (isCustom)
@ -197,8 +194,7 @@ class ChartEditorToolboxHandler
} }
} }
toolboxNotesCustomKind.onChange = (event:UIEvent) -> toolboxNotesCustomKind.onChange = (event:UIEvent) -> {
{
state.selectedNoteKind = toolboxNotesCustomKind.text; state.selectedNoteKind = toolboxNotesCustomKind.text;
} }
@ -215,8 +211,7 @@ class ChartEditorToolboxHandler
toolbox.x = 100; toolbox.x = 100;
toolbox.y = 150; toolbox.y = 150;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false); state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false);
} }
@ -232,8 +227,7 @@ class ChartEditorToolboxHandler
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id}); toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
} }
toolboxEventsEventKind.onChange = (event:UIEvent) -> toolboxEventsEventKind.onChange = (event:UIEvent) -> {
{
var eventType:String = event.data.value; var eventType:String = event.data.value;
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType'); trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
@ -320,8 +314,7 @@ class ChartEditorToolboxHandler
target.addComponent(input); target.addComponent(input);
input.onChange = (event:UIEvent) -> input.onChange = (event:UIEvent) -> {
{
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}'); trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}');
if (event.target.value == null) state.selectedEventData.remove(event.target.id); if (event.target.value == null) state.selectedEventData.remove(event.target.id);
@ -341,8 +334,7 @@ class ChartEditorToolboxHandler
toolbox.x = 125; toolbox.x = 125;
toolbox.y = 200; toolbox.y = 200;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false); state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false);
} }
@ -352,35 +344,28 @@ class ChartEditorToolboxHandler
var difficultyToolboxLoadMetadata:Button = toolbox.findComponent("difficultyToolboxLoadMetadata", Button); var difficultyToolboxLoadMetadata:Button = toolbox.findComponent("difficultyToolboxLoadMetadata", Button);
var difficultyToolboxLoadChart:Button = toolbox.findComponent("difficultyToolboxLoadChart", Button); var difficultyToolboxLoadChart:Button = toolbox.findComponent("difficultyToolboxLoadChart", Button);
difficultyToolboxSaveMetadata.onClick = (event:UIEvent) -> difficultyToolboxSaveMetadata.onClick = (event:UIEvent) -> {
{
SongSerializer.exportSongMetadata(state.currentSongMetadata); SongSerializer.exportSongMetadata(state.currentSongMetadata);
}; };
difficultyToolboxSaveChart.onClick = (event:UIEvent) -> difficultyToolboxSaveChart.onClick = (event:UIEvent) -> {
{
SongSerializer.exportSongChartData(state.currentSongChartData); SongSerializer.exportSongChartData(state.currentSongChartData);
}; };
difficultyToolboxSaveAll.onClick = (event:UIEvent) -> difficultyToolboxSaveAll.onClick = (event:UIEvent) -> {
{
state.exportAllSongData(); state.exportAllSongData();
}; };
difficultyToolboxLoadMetadata.onClick = (event:UIEvent) -> difficultyToolboxLoadMetadata.onClick = (event:UIEvent) -> {
{
// Replace metadata for current variation. // Replace metadata for current variation.
SongSerializer.importSongMetadataAsync(function(songMetadata) SongSerializer.importSongMetadataAsync(function(songMetadata) {
{
state.currentSongMetadata = songMetadata; state.currentSongMetadata = songMetadata;
}); });
}; };
difficultyToolboxLoadChart.onClick = (event:UIEvent) -> difficultyToolboxLoadChart.onClick = (event:UIEvent) -> {
{
// Replace chart data for current variation. // Replace chart data for current variation.
SongSerializer.importSongChartDataAsync(function(songChartData) SongSerializer.importSongChartDataAsync(function(songChartData) {
{
state.currentSongChartData = songChartData; state.currentSongChartData = songChartData;
state.noteDisplayDirty = true; state.noteDisplayDirty = true;
}); });
@ -401,14 +386,12 @@ class ChartEditorToolboxHandler
toolbox.x = 150; toolbox.x = 150;
toolbox.y = 250; toolbox.y = 250;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false); state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false);
} }
var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField); var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField);
inputSongName.onChange = (event:UIEvent) -> inputSongName.onChange = (event:UIEvent) -> {
{
var valid = event.target.text != null && event.target.text != ""; var valid = event.target.text != null && event.target.text != "";
if (valid) if (valid)
@ -423,8 +406,7 @@ class ChartEditorToolboxHandler
}; };
var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField); var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField);
inputSongArtist.onChange = (event:UIEvent) -> inputSongArtist.onChange = (event:UIEvent) -> {
{
var valid = event.target.text != null && event.target.text != ""; var valid = event.target.text != null && event.target.text != "";
if (valid) if (valid)
@ -439,8 +421,7 @@ class ChartEditorToolboxHandler
}; };
var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown); var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown);
inputStage.onChange = (event:UIEvent) -> inputStage.onChange = (event:UIEvent) -> {
{
var valid = event.data != null && event.data.id != null; var valid = event.data != null && event.data.id != null;
if (valid) if (valid)
@ -450,15 +431,13 @@ class ChartEditorToolboxHandler
}; };
var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown); var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown);
inputNoteSkin.onChange = (event:UIEvent) -> inputNoteSkin.onChange = (event:UIEvent) -> {
{
if (event.data.id == null) return; if (event.data.id == null) return;
state.currentSongMetadata.playData.noteSkin = event.data.id; state.currentSongMetadata.playData.noteSkin = event.data.id;
}; };
var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper); var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper);
inputBPM.onChange = (event:UIEvent) -> inputBPM.onChange = (event:UIEvent) -> {
{
if (event.value == null || event.value <= 0) return; if (event.value == null || event.value <= 0) return;
var timeChanges = state.currentSongMetadata.timeChanges; var timeChanges = state.currentSongMetadata.timeChanges;
@ -477,8 +456,7 @@ class ChartEditorToolboxHandler
}; };
var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider); var inputScrollSpeed:Slider = toolbox.findComponent('inputScrollSpeed', Slider);
inputScrollSpeed.onChange = (event:UIEvent) -> inputScrollSpeed.onChange = (event:UIEvent) -> {
{
var valid = event.target.value != null && event.target.value > 0; var valid = event.target.value != null && event.target.value > 0;
if (valid) if (valid)
@ -505,8 +483,7 @@ class ChartEditorToolboxHandler
toolbox.x = 175; toolbox.x = 175;
toolbox.y = 300; toolbox.y = 300;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxCharacters', false); state.setUICheckboxSelected('menubarItemToggleToolboxCharacters', false);
} }
@ -523,8 +500,7 @@ class ChartEditorToolboxHandler
toolbox.x = 200; toolbox.x = 200;
toolbox.y = 350; toolbox.y = 350;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false); state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false);
} }
@ -548,8 +524,7 @@ class ChartEditorToolboxHandler
toolbox.x = 200; toolbox.x = 200;
toolbox.y = 350; toolbox.y = 350;
toolbox.onDialogClosed = (event:DialogEvent) -> toolbox.onDialogClosed = (event:DialogEvent) -> {
{
state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false); state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false);
} }

Some files were not shown because too many files have changed in this diff Show more