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
- name: Build game?
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
ls
- uses: ./.github/actions/upload-itch

192
.vscode/settings.json vendored
View file

@ -1,84 +1,120 @@
{
"[haxe]": {
// Automatically keep Haxe files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.codeActionsOnSave": {
// Compilation server issues can cause auto-cleanup to remove valid imports.
"source.organizeImports": false
},
"editor.defaultFormatter": "nadako.vshaxe",
"editor.tabSize": 2
"[haxe]": {
// Automatically keep Haxe files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.codeActionsOnSave": {
// Compilation server issues can cause auto-cleanup to remove valid imports.
"source.organizeImports": false
},
"[json]": {
// Automatically keep JSON files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
"editor.defaultFormatter": "nadako.vshaxe",
"editor.tabSize": 2
},
"[json]": {
// 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]": {
// Automatically keep JSONC files formatted.
"editor.formatOnSave": true,
"editor.formatOnPaste": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
{
"label": "Windows / Debug (DEBUG ASSETS)",
"target": "windows",
"args": ["-debug", "-DDEBUG_ASSETS"]
},
"prettier.tabWidth": 2,
// 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"]
{
"label": "Windows / Debug (ANIMATE)",
"target": "windows",
"args": ["-debug", "-DANIMATE"]
},
// Fix file associations for HScript.
"files.associations": {
"*.hxp": "haxe",
"*.hscript": "haxe",
"*.haxe": "haxe",
"*.hxs": "haxe",
"*.hxc": "haxe"
{
"label": "HTML5 / Debug",
"target": "html5",
"args": ["-debug"]
},
"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"]
},
{
"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"]
}
]
}
{
"label": "HTML5 / Debug (Watch)",
"target": "html5",
"args": ["-debug", "-watch"]
}
]
}

View file

@ -1,48 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<project>
<!-- _________________________ 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" />
<!--Switch Export with Unique ApplicationID and Icon-->
<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
or you can set the actual size of your file manually at "FlxPreloaderBase-onUpdate-bytesTotal"-->
<!-- <app preloader="Preloader" resizable="true" /> -->
<app preloader="Preloader" />
<!--Minimum without FLX_NO_GAMEPAD: 11.8, without FLX_NO_NATIVE_CURSOR: 11.2-->
<set name="SWF_VERSION" value="11.8" />
<!-- ____________________________ Window Settings ___________________________ -->
<!--These window settings apply to all targets-->
<window width="1280" height="720" fps="" background="#000000" hardware="true" vsync="false" />
<!--HTML5-specific-->
<window if="html5" resizable="true" />
<!--Desktop-specific-->
<window if="desktop" orientation="landscape" fullscreen="false" resizable="true" vsync="false" />
<!--Mobile-specific-->
<window if="mobile" orientation="landscape" fullscreen="true" width="0" height="0" resizable="false" />
<!-- _____________________________ Path Settings ____________________________ -->
<set name="BUILD_DIR" value="export/debug" if="debug" />
<set name="BUILD_DIR" value="export/release" unless="debug" />
<set name="BUILD_DIR" value="export/32bit" if="32bit" />
<classpath name="source" />
<assets path="assets/preload" rename="assets" exclude="*.ogg" if="web" />
<assets path="assets/preload" rename="assets" exclude="*.mp3" unless="web" />
<define name="PRELOAD_ALL" unless="web" />
<define name="NO_PRELOAD_ALL" unless="PRELOAD_ALL" />
<section if="PRELOAD_ALL">
<library name="songs" preload="true" />
<library name="shared" preload="true" />
@ -56,7 +41,6 @@
<library name="week7" preload="true" />
<library name="weekend1" preload="true" />
</section>
<section if="NO_PRELOAD_ALL">
<library name="songs" preload="false" />
<library name="shared" preload="false" />
@ -70,7 +54,6 @@
<library name="week7" preload="false" />
<library name="weekend1" preload="false" />
</section>
<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/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/weekend1" library="weekend1" exclude="*.fla|*.ogg" if="web" />
<assets path="assets/weekend1" library="weekend1" exclude="*.fla|*.mp3" unless="web" />
<!-- <assets path='example_mods' rename='mods' embed='false'/> -->
<!--
AUTOMATICALLY MOVING EXAMPLE MODS INTO THE BUILD CAUSES ISSUES
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.
<assets path='example_mods' rename='mods' embed='false' exclude="*.md" />
-->
<assets path='art/readme.txt' rename='do NOT readme.txt' />
<assets path="CHANGELOG.md" rename='changelog.txt' />
<!-- NOTE FOR FUTURE SELF SINCE FONTS ARE ALWAYS FUCKY
<assets path="art/readme.txt" rename="do NOT readme.txt" />
<assets path="CHANGELOG.md" rename="changelog.txt" />
<!-- NOTE FOR FUTURE SELF SINCE FONTS ARE ALWAYS FUCKY
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!!!
NOT USING A DIRECT THING TO THE ASSET!!!
-->
<assets path="assets/fonts" embed='true' />
<assets path="assets/fonts" embed="true" />
<!-- _______________________________ Libraries ______________________________ -->
<haxelib name="lime" /> <!-- Game engine backend -->
<haxelib name="openfl" /> <!-- Game engine backend -->
<haxelib name="flixel" /> <!-- Game engine -->
<haxedev set='webgl' />
<haxedev set="webgl" />
<!--In case you want to use the addons package-->
<haxelib name="flixel-addons" /> <!-- Additional utilities for Flixel -->
<haxelib name="hscript" /> <!-- Scripting -->
<haxelib name="flixel-ui" /> <!-- UI framework (deprecate this? -->
<haxelib name="haxeui-core"/> <!-- UI framework -->
<haxelib name="haxeui-flixel"/> <!-- Integrate HaxeUI with Flixel -->
<haxelib name="haxeui-core" /> <!-- UI framework -->
<haxelib name="haxeui-flixel" /> <!-- Integrate HaxeUI with Flixel -->
<haxelib name="polymod" /> <!-- Modding framework -->
<haxelib name="flxanimate" /> <!-- Texture atlas rendering -->
<!-- <haxelib name="hxcodec" /> Video playback -->
<haxelib name="hxCodec" /> <!-- Video playback -->
<haxelib name="json2object" /> <!-- JSON parsing -->
<haxelib name="thx.semver" />
<haxelib name="hxcpp-debug-server" if="desktop debug" />
<!--Disable the Flixel core focus lost screen-->
<haxedef name="FLX_NO_FOCUS_LOST_SCREEN" />
<!--Disable the Flixel core debugger. Automatically gets set whenever you compile in release mode!-->
<haxedef name="FLX_NO_DEBUG" unless="debug" />
<!--Enable this for Nape release builds for a serious peformance improvement-->
<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" />
<!-- _________________________________ 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. -->
<haxeflag name="--no-traces" unless="debug" />
<!-- HScript relies heavily on Reflection, which means we can't use DCE. -->
<haxeflag name="-dce no" />
<!-- Ensure all Funkin' classes are available at runtime. -->
<haxeflag name="--macro" value="include('funkin')" />
<!-- 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.containers.dialogs')" />
<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.core')" />
<haxeflag name="--macro" value="include('haxe.ui.components')" />
<haxeflag name="--macro" value="include('haxe.ui.containers')" />
<haxeflag name="--macro" value="include('haxe.ui.components')" />
<haxeflag name="--macro" value="include('haxe.ui.containers')" />
<!--
Ensure additional class packages are available at runtime (some only really used by scripts).
Ignore packages we can't include.
-->
<haxeflag name="--macro" value="include('flixel', true, [ 'flixel.addons.editors.spine.*', 'flixel.addons.nape.*', 'flixel.system.macros.*' ])" />
<!-- Necessary to provide stack traces for HScript. -->
<haxedef name="hscriptPos" />
<haxedef name="HXCPP_CHECK_POINTER" />
<haxedef name="HXCPP_STACK_LINE" />
<!-- This macro allows addition of new functionality to existing Flixel. -->
<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)-->
<icon path="art/icon16.png" size='16' />
<icon path="art/icon32.png" size='32' />
<icon path="art/icon64.png" size='64' />
<icon path="art/icon16.png" size="16" />
<icon path="art/icon32.png" size="32" />
<icon path="art/icon64.png" size="64" />
<icon path="art/iconOG.png" />
<!-- <haxedef name="SKIP_TO_PLAYSTATE" if="debug" /> -->
<haxedef name="CAN_OPEN_LINKS" unless="switch" />
<haxedef name="CAN_CHEAT" if="switch debug" />
<haxedef name="haxeui_no_mouse_reset" />
<!-- Skip the Intro -->
<section if="debug">
<!-- Starts the game at the specified week, at the first song -->
<!-- <haxedef name="week" value="1" if="debug"/> -->
<!-- Starts the game at the specified song -->
<!-- <haxedef name="song" value="bopeebo" if="debug"/> -->
<!-- Difficulty, only used for week or song, defaults to 1 -->
<!-- <haxedef name="dif" value="2" if="debug"/> -->
</section>
<!-- <haxedef name="CLEAR_INPUT_SAVE"/> -->
<section if="newgrounds">
<!-- Enables Ng.core.verbose -->
<!-- <haxedef name="NG_VERBOSE" /> -->
<!-- Enables a NG debug session, so medals don't permently unlock -->
<!-- <haxedef name="NG_DEBUG" /> -->
<!-- pretends that the saved session Id was expired, forcing the reconnect prompt -->
<!-- <haxedef name="NG_FORCE_EXPIRED_SESSION" if="debug" /> -->
</section>
<!-- <prebuild haxe="trace('prebuilding');"/> -->
<!-- <postbuild haxe="art/Postbuild.hx"/> -->
<!-- <config:ios allow-provisioning-updates="true" team-id="" /> -->
<!-- Options for Polymod -->
<section if="polymod">
<!-- Turns on additional debug logging. -->
@ -247,14 +200,12 @@
<!-- Determines the file in the mod folder used for the icon. -->
<haxedef name="POLYMOD_MOD_ICON_FILE" value="_polymod_icon.png" />
</section>
<section if='TOOLS'>
<section if="TOOLS">
<!-- Compiles tool for old song conversion shit -->
<!-- Assumes you use it on windows/desktop!!!! -->
<postbuild command='haxe -main art/SongConverter.hx --cs export/songShit' />
<assets path='export/songShit/bin/SongConverter.exe' rename='SongConverter.exe' />
<postbuild command="haxe -main art/SongConverter.hx --cs export/songShit" />
<assets path="export/songShit/bin/SongConverter.exe" rename="SongConverter.exe" />
<!-- <postbuild command='ren export/songShit/bin export/songShit/tools '/> -->
<!-- <postbuild command='move export/songShit/tools export/release/windows/bin'/> -->
</section>
</project>
</project>

View file

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

View file

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

View file

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

View file

@ -125,8 +125,7 @@ class Alphabet extends FlxSpriteGroup
var xPos:Float = 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));
if (_finalText.fastCodeAt(loopNum) == "\n".code)
{

View file

@ -2,7 +2,7 @@ package funkin;
import flixel.FlxSubState;
class ButtonRemapSubstate extends FlxSubState
class ButtonRemapSubState extends FlxSubState
{
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.play('funny');
effectStuff.antialiasing = true;
effectStuff.animation.finishCallback = function(nameThing)
{
effectStuff.animation.finishCallback = function(nameThing) {
kill();
};
effectStuff.setGraphicSize(Std.int(effectStuff.width * 0.7));
@ -42,8 +41,7 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
{
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();
});
}
@ -64,16 +62,14 @@ class ComboCounter extends FlxTypedSpriteGroup<FlxSprite>
if (effectStuff.animation.curAnim.curFrame == 18)
{
grpNumbers.forEach(function(spr:ComboNumber)
{
grpNumbers.forEach(function(spr:ComboNumber) {
spr.animation.reset();
});
}
if (effectStuff.animation.curAnim.curFrame == 20)
{
grpNumbers.forEach(function(spr:ComboNumber)
{
grpNumbers.forEach(function(spr:ComboNumber) {
spr.kill();
});
}

View file

@ -50,6 +50,16 @@ class Conductor
// OLD, replaced with timeChanges.
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.
*/
@ -149,9 +159,9 @@ class Conductor
/**
* Forcibly defines the current BPM of the song.
* 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.
*
*
* WARNING: Avoid this for things like setting the BPM of the title screen music,
* you should have a metadata file for it instead.
*/
@ -166,7 +176,7 @@ class Conductor
/**
* Update the conductor with the current 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.
* Leave blank to use the FlxG.sound.music position.
*/

View file

@ -20,13 +20,6 @@ import openfl.filters.ShaderFilter;
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
{
return Math.log(fin) / Math.log(base);
@ -119,8 +112,7 @@ class CoolUtil
FlxTween.tween(screenWipeShit, {daAlphaShit: 1}, time,
{
ease: FlxEase.quadInOut,
onComplete: function(twn)
{
onComplete: function(twn) {
screenShit.destroy();
FlxG.switchState(new MainMenuState());
}
@ -130,7 +122,7 @@ class CoolUtil
/**
* Just saves the json with some default values hehe
* @param json
* @param json
* @return 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();
switch (PlayState.currentSong.song.toLowerCase())
switch (PlayState.instance.currentSong.songId.toLowerCase())
{
case 'senpai':
FlxG.sound.playMusic(Paths.music('Lunchbox'), 0);
@ -53,8 +53,7 @@ class DialogueBox extends FlxSpriteGroup
bgFade.alpha = 0;
add(bgFade);
new FlxTimer().start(0.83, function(tmr:FlxTimer)
{
new FlxTimer().start(0.83, function(tmr:FlxTimer) {
bgFade.alpha += (1 / 5) * 0.7;
if (bgFade.alpha > 0.7) bgFade.alpha = 0.7;
}, 5);
@ -80,7 +79,7 @@ class DialogueBox extends FlxSpriteGroup
box = new FlxSprite(-20, 45);
var hasDialog:Bool = false;
switch (PlayState.currentSong.song.toLowerCase())
switch (PlayState.instance.currentSong.songId.toLowerCase())
{
case 'senpai':
hasDialog = true;
@ -152,8 +151,8 @@ class DialogueBox extends FlxSpriteGroup
override function update(elapsed:Float):Void
{
// HARD CODING CUZ IM STUPDI
if (PlayState.currentSong.song.toLowerCase() == 'roses') portraitLeft.visible = false;
if (PlayState.currentSong.song.toLowerCase() == 'thorns')
if (PlayState.instance.currentSong.songId.toLowerCase() == 'roses') portraitLeft.visible = false;
if (PlayState.instance.currentSong.songId.toLowerCase() == 'thorns')
{
portraitLeft.color = FlxColor.BLACK;
swagDialogue.color = FlxColor.WHITE;
@ -189,11 +188,10 @@ class DialogueBox extends FlxSpriteGroup
{
isEnding = true;
if (PlayState.currentSong.song.toLowerCase() == 'senpai'
|| PlayState.currentSong.song.toLowerCase() == 'thorns') FlxG.sound.music.fadeOut(2.2, 0);
if (PlayState.instance.currentSong.songId.toLowerCase() == 'senpai'
|| 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;
bgFade.alpha -= 1 / 5 * 0.7;
portraitLeft.visible = false;
@ -203,8 +201,7 @@ class DialogueBox extends FlxSpriteGroup
dropText.alpha = swagDialogue.alpha;
}, 5);
new FlxTimer().start(1.2, function(tmr:FlxTimer)
{
new FlxTimer().start(1.2, function(tmr:FlxTimer) {
finishThing();
kill();
});
@ -233,8 +230,7 @@ class DialogueBox extends FlxSpriteGroup
// swagDialogue.text = ;
swagDialogue.resetText(dialogueList[0]);
swagDialogue.start(0.04);
swagDialogue.completeCallback = function()
{
swagDialogue.completeCallback = function() {
trace('dialogue finish');
handSelect.visible = true;
dialogueEnded = true;

View file

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

View file

@ -34,12 +34,14 @@ import funkin.play.song.SongData.SongDataParser;
import funkin.shaderslmfao.AngleMask;
import funkin.shaderslmfao.PureColor;
import funkin.shaderslmfao.StrokeShader;
import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song;
import lime.app.Future;
import lime.utils.Assets;
class FreeplayState extends MusicBeatSubstate
class FreeplayState extends MusicBeatSubState
{
var songs:Array<SongMetadata> = [];
var songs:Array<FreeplaySongData> = [];
// var selector:FlxText;
var curSelected:Int = 0;
@ -112,15 +114,15 @@ class FreeplayState extends MusicBeatSubstate
#if debug
isDebug = true;
addSong('Test', 1, 'bf-pixel');
addSong('Pyro', 8, 'darnell');
addSong('Test', 'tutorial', 'bf-pixel');
addSong('Pyro', 'weekend1', 'darnell');
#end
var initSonglist = CoolUtil.coolTextFile(Paths.txt('freeplaySonglist'));
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)
@ -128,22 +130,28 @@ class FreeplayState extends MusicBeatSubstate
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,
['parents-christmas', 'parents-christmas', 'monster-christmas']);
// if (StoryMenuState.weekUnlocked[5] || isDebug)
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
@ -426,7 +434,7 @@ class FreeplayState extends MusicBeatSubstate
var swag:Alphabet = new Alphabet(1, 0, "swag");
// JUST DOIN THIS SHIT FOR TESTING!!!
/*
/*
var md:String = Markdown.markdownToHtml(Assets.getText('CHANGELOG.md'));
var texFel:TextField = new TextField();
@ -464,7 +472,7 @@ class FreeplayState extends MusicBeatSubstate
grpCapsules.clear();
// var regexp:EReg = regexp;
var tempSongs:Array<SongMetadata> = songs;
var tempSongs:Array<FreeplaySongData> = songs;
if (filterStuff != null)
{
@ -553,19 +561,19 @@ class FreeplayState extends MusicBeatSubstate
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'];
var num:Int = 0;
for (song in songs)
{
addSong(song, weekNum, songCharacters[num]);
addSong(song, levelId, songCharacters[num]);
if (songCharacters.length != 1) num++;
}
@ -851,11 +859,9 @@ class FreeplayState extends MusicBeatSubstate
curDifficulty = 1;
}*/
PlayState.currentSong = SongLoad.loadFromJson(poop, songs[curSelected].songName.toLowerCase());
PlayState.currentSong_NEW = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
PlayState.isStoryMode = false;
PlayState.storyDifficulty = curDifficulty;
PlayState.storyDifficulty_NEW = switch (curDifficulty)
PlayStatePlaylist.isStoryMode = false;
var targetSong:Song = SongDataParser.fetchSong(songs[curSelected].songName.toLowerCase());
var targetDifficulty:String = switch (curDifficulty)
{
case 0:
'easy';
@ -865,27 +871,41 @@ class FreeplayState extends MusicBeatSubstate
'hard';
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;
// trace(' CUR WEEK ' + PlayState.storyWeek);
// TODO: Implement Pico into the interface properly.
var targetCharacter:String = 'bf';
if (FlxG.keys.pressed.P)
{
targetCharacter = 'pico';
}
PlayStatePlaylist.campaignId = songs[curSelected].levelId;
// Visual and audio effects.
FlxG.sound.play(Paths.sound('confirmMenu'));
dj.confirm();
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);
super.startOutro(onComplete);
return super.switchTo(nextState);
}
function changeDiff(change:Int = 0)
@ -901,19 +921,6 @@ class FreeplayState extends MusicBeatSubstate
intendedScore = Highscore.getScore(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) {
spr.visible = false;
});
@ -1040,17 +1047,17 @@ enum abstract FilterType(String)
var ALL;
}
class SongMetadata
class FreeplaySongData
{
public var songName:String = "";
public var week:Int = 0;
public var levelId:String = "";
public var songCharacter:String = "";
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.week = week;
this.levelId = levelId;
this.songCharacter = songCharacter;
this.isFav = isFav;
}

View file

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

View file

@ -39,7 +39,17 @@ class Highscore
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);
@ -57,20 +67,42 @@ class Highscore
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
NGio.postScore(score, "Week " + week);
NGio.postScore(score, 'Campaign ID $week');
#end
var formattedSong:String = formatSong('week' + week, diff);
var formattedSong:String = formatSong(week, diff);
if (songScores.exists(formattedSong))
{
if (songScores.get(formattedSong) < score) setScore(formattedSong, score);
}
else
{
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
@ -122,7 +154,7 @@ class Highscore
return songCompletion.get(formatSong(song, diff));
}
public static function getAllScores()
public static function getAllScores():Void
{
trace(songScores.toString());
}

View file

@ -12,7 +12,7 @@ import flixel.util.FlxColor;
import funkin.modding.module.ModuleHandler;
import funkin.play.PlayState;
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.ui.PreferencesMenu;
import funkin.util.WindowUtil;
@ -140,10 +140,10 @@ class InitState extends FlxTransitionableState
// WEEK UNLOCK PROGRESSION!!
// 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!
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;
@ -237,20 +237,18 @@ class InitState extends FlxTransitionableState
{
var dif:Int = getDif();
PlayState.currentSong = SongLoad.loadFromJson(song, song);
PlayState.currentSong_NEW = SongDataParser.fetchSong(song);
PlayState.isStoryMode = isStoryMode;
PlayState.storyDifficulty = dif;
PlayState.storyDifficulty_NEW = switch (dif)
var targetDifficulty = switch (dif)
{
case 0: 'easy';
case 1: 'normal';
case 2: 'hard';
default: 'normal';
};
SongLoad.curDiff = PlayState.storyDifficulty_NEW;
PlayState.storyWeek = week;
LoadingState.loadAndSwitchState(new PlayState());
LoadingState.loadAndSwitchState(new PlayState(
{
targetSong: SongDataParser.fetchSong(song),
targetDifficulty: targetDifficulty,
}));
}
}

View file

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

View file

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

View file

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

View file

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

View file

@ -35,8 +35,8 @@ class MusicBeatState extends FlxUIState
function initCallbacks()
{
subStateOpened.add(onOpenSubstateComplete);
subStateClosed.add(onCloseSubstateComplete);
subStateOpened.add(onOpenSubStateComplete);
subStateClosed.add(onCloseSubStateComplete);
}
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);
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));
}
@ -189,7 +189,7 @@ class MusicBeatState extends FlxUIState
super.closeSubState();
}
function onCloseSubstateComplete(targetState:FlxSubState):Void
function onCloseSubStateComplete(targetState:FlxSubState):Void
{
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;
/**
* 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)
{

View file

@ -49,8 +49,7 @@ class NGio
trace('checking NG.io 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;
trace('CURRENT NG VERSION: ' + GAME_VER);
callback(GAME_VER);
@ -141,8 +140,7 @@ class NGio
var onCancel:Void->Void = null;
if (onComplete != null)
{
onSuccess = function()
{
onSuccess = function() {
onNGLogin();
onComplete(Success);
}
@ -228,7 +226,7 @@ class NGio
scoreboardsLoaded = true;
ngScoresLoaded.dispatch();
/*
/*
for (score in NG.core.scoreBoards.get(8737).scores)
{
trace('score loaded user:${score.user.name}, score:${score.formatted_value}');

View file

@ -1,5 +1,6 @@
package funkin;
import funkin.play.Strumline.StrumlineArrow;
import flixel.FlxSprite;
import flixel.math.FlxMath;
import funkin.noteStuff.NoteBasic.NoteData;
@ -15,9 +16,9 @@ class Note extends FlxSprite
public var data = new NoteData();
/**
* code colors for.... code....
* code colors for.... code....
* i think goes in order of left to right
*
*
* left 0
* down 1
* 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()
{
prevNote = null;

View file

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

View file

@ -1,9 +1,10 @@
package funkin;
import funkin.play.PlayStatePlaylist;
import flixel.FlxSprite;
import flixel.addons.transition.FlxTransitionableState;
import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.sound.FlxSound;
import flixel.system.FlxSound;
import flixel.text.FlxText;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
@ -11,7 +12,7 @@ import flixel.util.FlxColor;
import funkin.play.PlayState;
import funkin.play.song.SongData.SongDataParser;
class PauseSubState extends MusicBeatSubstate
class PauseSubState extends MusicBeatSubState
{
var grpMenuShit:FlxTypedGroup<Alphabet>;
@ -20,9 +21,9 @@ class PauseSubState extends MusicBeatSubstate
'Restart Song',
'Change Difficulty',
'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 curSelected:Int = 0;
@ -41,10 +42,14 @@ class PauseSubState extends MusicBeatSubstate
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);
}
else
{
pauseMusic = new FlxSound().loadEmbedded(Paths.music('breakfast'), true, true);
}
pauseMusic.volume = 0;
pauseMusic.play(false, FlxG.random.int(0, Std.int(pauseMusic.length / 2)));
@ -58,43 +63,38 @@ class PauseSubState extends MusicBeatSubstate
metaDataGrp = new FlxTypedGroup<FlxSprite>();
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)
{
levelInfo.text += '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}';
}
else
{
levelInfo.text += PlayState.currentSong.song;
}
levelInfo.scrollFactor.set();
levelInfo.setFormat(Paths.font("vcr.ttf"), 32);
levelInfo.setFormat(Paths.font('vcr.ttf'), 32);
levelInfo.updateHitbox();
metaDataGrp.add(levelInfo);
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, "", 32);
levelDifficulty.text += CoolUtil.difficultyString();
var levelDifficulty:FlxText = new FlxText(20, 15 + 32, 0, '', 32);
levelDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase();
levelDifficulty.scrollFactor.set();
levelDifficulty.setFormat(Paths.font('vcr.ttf'), 32);
levelDifficulty.updateHitbox();
metaDataGrp.add(levelDifficulty);
var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, "", 32);
deathCounter.text = "Blue balled: " + PlayState.deathCounter;
deathCounter.text += "\n" + Highscore.tallies.totalNotesHit;
deathCounter.text += "\n" + Highscore.tallies.totalNotes;
deathCounter.text += "\n" + Std.string(Highscore.tallies.totalNotesHit / Highscore.tallies.totalNotes);
var deathCounter:FlxText = new FlxText(20, 15 + 64, 0, '', 32);
deathCounter.text = 'Blue balled: ${PlayState.instance.deathCounter}';
FlxG.watch.addQuick('totalNotesHit', Highscore.tallies.totalNotesHit);
FlxG.watch.addQuick('totalNotes', Highscore.tallies.totalNotes);
deathCounter.scrollFactor.set();
deathCounter.setFormat(Paths.font('vcr.ttf'), 32);
deathCounter.updateHitbox();
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.setFormat(Paths.font('vcr.ttf'), 32);
practiceText.updateHitbox();
practiceText.x = FlxG.width - (practiceText.width + 20);
practiceText.visible = PlayState.isPracticeMode;
practiceText.visible = PlayState.instance.isPracticeMode;
metaDataGrp.add(practiceText);
levelDifficulty.alpha = 0;
@ -137,7 +137,7 @@ class PauseSubState extends MusicBeatSubstate
changeSelection();
}
override function update(elapsed:Float)
override function update(elapsed:Float):Void
{
if (pauseMusic.volume < 0.5) pauseMusic.volume += 0.01 * elapsed;
@ -180,41 +180,39 @@ class PauseSubState extends MusicBeatSubstate
{
var daSelected:String = menuItems[curSelected];
// TODO: Why is this based on the menu item's name? Make this an enum or something.
switch (daSelected)
{
case "Resume":
case 'Resume':
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':
menuItems = difficultyChoices;
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':
menuItems = pauseOG;
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();
// FlxG.resetState();
case "Exit to menu":
case 'Exit to Menu':
exitingToMenu = true;
PlayState.seenCutscene = false;
PlayState.deathCounter = 0;
PlayState.instance.deathCounter = 0;
for (item in grpMenuShit.members)
{
@ -225,9 +223,9 @@ class PauseSubState extends MusicBeatSubstate
FlxTransitionableState.skipNextTransIn = 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
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();
@ -260,12 +258,10 @@ class PauseSubState extends MusicBeatSubstate
item.targetY = index - curSelected;
item.alpha = 0.6;
// item.setGraphicSize(Std.int(item.width * 0.8));
if (item.targetY == 0)
{
item.alpha = 1;
// item.setGraphicSize(Std.int(item.width));
}
}
}

View file

@ -281,7 +281,7 @@ class SongLoad
// castNoteDataToNoteData(swagShit.noteMap[diff]);
/*
/*
switch (diff)
{
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();
/*
/*
#elseif web
@ -84,8 +84,7 @@ class TitleState extends MusicBeatState
*/
// netConnection.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown);
new FlxTimer().start(1, function(tmr:FlxTimer)
{
new FlxTimer().start(1, function(tmr:FlxTimer) {
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});
}
/*
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.keys.justPressed.F) FlxG.fullscreen = !FlxG.fullscreen;
@ -373,8 +334,7 @@ class TitleState extends MusicBeatState
#if newgrounds
if (!OutdatedSubState.leftState)
{
NGio.checkVersion(function(version)
{
NGio.checkVersion(function(version) {
// Check if version is outdated
var localVersion:String = "v" + Application.current.meta.get('version');
var onlineVersion = version.split(" ")[0].trim();
@ -391,8 +351,7 @@ class TitleState extends MusicBeatState
});
}
#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
// Saves about 50mb of RAM or so???
Assets.cache.clear(Paths.image('gfDanceTitle'));
@ -404,7 +363,7 @@ class TitleState extends MusicBeatState
// FlxG.sound.play(Paths.music('titleShoot'), 0.7);
}
if (pressedEnter && !skippedIntro && initialized) skipIntro();
/*
/*
#if web
if (!initialized && controls.ACCEPT)
{

View file

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

View file

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

View file

@ -44,7 +44,7 @@ class SoundGroup extends FlxTypedGroup<FlxSound>
/**
* Finds the largest deviation from the desired time inside this SoundGroup.
*
*
* @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.
* @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.
*
*
* @param T The type to construct. Must implement `IRegistryEntry`.
* @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.
*
* 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>;
@ -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.
* @param clsName
* @param clsName
*/
abstract function createScriptedEntry(clsName:String):Null<T>;
}

View file

@ -48,8 +48,7 @@ class BGScrollingText extends FlxSpriteGroup
function set_funnyColor(col:Int):Int
{
grpTexts.forEach(function(txt)
{
grpTexts.forEach(function(txt) {
txt.color = col;
});
@ -85,8 +84,7 @@ class BGScrollingText extends FlxSpriteGroup
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);
});
}

View file

@ -77,9 +77,9 @@ class SustainTrail extends FlxSprite
/**
* Normally you would take strumTime:Float, noteData:Int, sustainLength:Float, parentNote:Note (?)
* @param NoteData
* @param SustainLength
* @param FileName
* @param NoteData
* @param SustainLength
* @param FileName
*/
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;
/**
* 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)
{

View file

@ -123,8 +123,7 @@ class Cursor
if (assetCursorDefault == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_DEFAULT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorDefault = bitmapData;
applyCursorParams(assetCursorDefault, CURSOR_DEFAULT_PARAMS);
});
@ -138,8 +137,7 @@ class Cursor
if (assetCursorCross == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_CROSS_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorCross = bitmapData;
applyCursorParams(assetCursorCross, CURSOR_CROSS_PARAMS);
});
@ -153,8 +151,7 @@ class Cursor
if (assetCursorEraser == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ERASER_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorEraser = bitmapData;
applyCursorParams(assetCursorEraser, CURSOR_ERASER_PARAMS);
});
@ -168,8 +165,7 @@ class Cursor
if (assetCursorGrabbing == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_GRABBING_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorGrabbing = bitmapData;
applyCursorParams(assetCursorGrabbing, CURSOR_GRABBING_PARAMS);
});
@ -183,8 +179,7 @@ class Cursor
if (assetCursorHourglass == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_HOURGLASS_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorHourglass = bitmapData;
applyCursorParams(assetCursorHourglass, CURSOR_HOURGLASS_PARAMS);
});
@ -198,8 +193,7 @@ class Cursor
if (assetCursorPointer == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_POINTER_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorPointer = bitmapData;
applyCursorParams(assetCursorPointer, CURSOR_POINTER_PARAMS);
});
@ -213,8 +207,7 @@ class Cursor
if (assetCursorText == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_TEXT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorText = bitmapData;
applyCursorParams(assetCursorText, CURSOR_TEXT_PARAMS);
});
@ -228,8 +221,7 @@ class Cursor
if (assetCursorZoomIn == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_IN_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorZoomIn = bitmapData;
applyCursorParams(assetCursorZoomIn, CURSOR_ZOOM_IN_PARAMS);
});
@ -243,8 +235,7 @@ class Cursor
if (assetCursorZoomOut == null)
{
var future:Future<BitmapData> = Assets.loadBitmapData(CURSOR_ZOOM_OUT_PARAMS.graphic);
future.onComplete(function(bitmapData:BitmapData)
{
future.onComplete(function(bitmapData:BitmapData) {
assetCursorZoomOut = bitmapData;
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.
*
*
* 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.
*
*
* Example: Pressing Ctrl+Z will undo, while holding Ctrl+Z will start to undo repeatedly.
*/
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.
*
*
* Includes events handling basic life cycle relevant to all scripted classes.
*/
interface IScriptedClass
@ -24,10 +24,10 @@ interface IStateChangingScriptedClass extends IScriptedClass
public function onStateChangeBegin(event:StateChangeScriptEvent):Void;
public function onStateChangeEnd(event:StateChangeScriptEvent):Void;
public function onSubstateOpenBegin(event:SubStateScriptEvent):Void;
public function onSubstateOpenEnd(event:SubStateScriptEvent):Void;
public function onSubstateCloseBegin(event:SubStateScriptEvent):Void;
public function onSubstateCloseEnd(event:SubStateScriptEvent):Void;
public function onSubStateOpenBegin(event:SubStateScriptEvent):Void;
public function onSubStateOpenEnd(event:SubStateScriptEvent):Void;
public function onSubStateCloseBegin(event:SubStateScriptEvent):Void;
public function onSubStateCloseEnd(event:SubStateScriptEvent):Void;
}
/**
@ -53,7 +53,7 @@ interface INoteScriptedClass extends IScriptedClass
/**
* Developer note:
*
*
* 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:
* `FlxG.state.addEventListener(KeyboardEvent.KEY_DOWN, onKeyDown);`

View file

@ -8,7 +8,7 @@ class PolymodErrorHandler
* Show a popup with the given text.
* 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.
*
*
* @param name The name at the top 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.
* Keep in mind that the constructor may be called before the object is needed,
* for the purposes of caching data or otherwise.
*
*
* 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.
* This should perform relevant cleanup to ensure good performance.
*
*
* 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.
* This assumes all data is loaded and ready to go.
*
*
* This event is not cancelable.
*/
public static inline final ADDED:ScriptEventType = 'ADDED';
@ -43,38 +43,38 @@ class ScriptEvent
/**
* Called during the update function.
* This is called every frame, so be careful!
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
@ -83,7 +83,7 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being hit,
* 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.
@ -92,7 +92,7 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
* 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.
@ -101,137 +101,137 @@ class ScriptEvent
* This event IS cancelable! Canceling this event prevents the note from being considered missed,
* 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.
*
*
* This event IS cancelable! Cancelling this event prevents the event from being triggered,
* 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.
*
*
* 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.
*
*
* 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.
*
*
* This event IS cancelable! Canceling this event will prevent the countdown from starting.
* - 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.
*/
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.
* Includes information about what step of the countdown was hit.
*
*
* This event IS cancelable! Canceling this event will pause the countdown.
* - 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.
*
*
* 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.
*
*
* 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.
* 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.
*/
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.
*
*
* 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.
*
*
* 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.
* This allows modders to mutate the notes before they are used in the song.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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.
*
*
* 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
{
// This typo happens enough that I just added this.
cancelEvent();
}
@ -316,11 +319,17 @@ class NoteScriptEvent extends ScriptEvent
*/
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
{
super(type, cancelable);
this.note = note;
this.comboCount = comboCount;
this.playSound = true;
}
public override function toString():String
@ -468,7 +477,7 @@ class CountdownScriptEvent extends ScriptEvent
*/
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);
this.step = step;

View file

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

View file

@ -25,7 +25,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
/**
* Determines the order in which modules receive events.
* You can modify this to change the order in which a given module receives events.
*
*
* Priority 1 is processed before Priority 1000, etc.
*/
public var priority(default, set):Int;
@ -41,7 +41,7 @@ class Module implements IPlayStateScriptedClass implements IStateChangingScripte
/**
* Called when the module is initialized.
* 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.
*/
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 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) {}
}

View file

@ -16,7 +16,7 @@ class ModuleHandler
/**
* 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.
*/
public static function loadModuleCache():Void
@ -66,8 +66,7 @@ class ModuleHandler
{
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 bModule:Module = moduleCache.get(b);

View file

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

View file

@ -37,7 +37,7 @@ class Countdown
// Stop any existing countdown.
stopCountdown();
PlayState.isInCountdown = true;
PlayState.instance.isInCountdown = true;
Conductor.songPosition = Conductor.crochet * -5;
// Handle onBeatHit events manually
@:privateAccess
@ -46,8 +46,7 @@ class Countdown
// The timer function gets called based on the beat of the song.
countdownTimer = new FlxTimer();
countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer)
{
countdownTimer.start(Conductor.crochet / 1000, function(tmr:FlxTimer) {
countdownStep = decrement(countdownStep);
// 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().
*
*
* 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()
@ -115,7 +114,7 @@ class Countdown
/**
* 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.
*/
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.
*
*
* 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()
@ -166,7 +165,7 @@ class Countdown
/**
* Retrieves the graphic to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
*
*
* This is public so modules can do lol funny shit.
*/
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,
{
ease: FlxEase.cubeInOut,
onComplete: function(twn:FlxTween)
{
onComplete: function(twn:FlxTween) {
countdownSprite.destroy();
}
});
@ -228,7 +226,7 @@ class Countdown
/**
* Retrieves the sound file to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
*
*
* This is public so modules can do lol funny shit.
*/
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.
* 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.
*/
class GameOverSubstate extends MusicBeatSubstate
class GameOverSubState extends MusicBeatSubState
{
/**
* Which alternate animation on the character to use.
@ -92,7 +92,7 @@ class GameOverSubstate extends MusicBeatSubstate
bg.scrollFactor.set();
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.
boyfriend = PlayState.instance.currentStage.getBoyfriend(true);
boyfriend.isDead = true;
@ -160,19 +160,21 @@ class GameOverSubstate extends MusicBeatSubstate
}
// KEYBOARD ONLY: Restart the level when pressing the assigned key.
if (controls.ACCEPT)
if (controls.ACCEPT && blueballed)
{
blueballed = false;
confirmDeath();
}
// KEYBOARD ONLY: Return to the menu when pressing the assigned key.
if (controls.BACK)
{
blueballed = false;
PlayState.deathCounter = 0;
PlayState.seenCutscene = false;
gameOverMusic.stop();
if (PlayState.isStoryMode) FlxG.switchState(new StoryMenuState());
if (PlayStatePlaylist.isStoryMode) FlxG.switchState(new StoryMenuState());
else
FlxG.switchState(new FreeplayState());
}
@ -186,11 +188,11 @@ class GameOverSubstate extends MusicBeatSubstate
else
{
// Music hasn't started yet.
switch (PlayState.storyWeek)
switch (PlayStatePlaylist.campaignId)
{
// 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.
case 7:
case "week7":
if (boyfriend.getCurrentAnimation().startsWith('firstDeath') && boyfriend.isAnimationFinished() && !playingJeffQuote)
{
playingJeffQuote = true;
@ -227,9 +229,9 @@ class GameOverSubstate extends MusicBeatSubstate
new FlxTimer().start(0.7, function(tmr:FlxTimer) {
// ...fade out the graphics. Then after that happens...
FlxG.camera.fade(FlxColor.BLACK, 2, false, function() {
// ...close the GameOverSubstate.
// ...close the GameOverSubState.
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
PlayState.needsReset = true;
PlayState.instance.needsReset = true;
// Readd Boyfriend to the stage.
boyfriend.isDead = false;
@ -252,7 +254,7 @@ class GameOverSubstate extends MusicBeatSubstate
/**
* Starts the death music at the appropriate volume.
* @param startingVolume
* @param startingVolume
*/
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
* boyfriend's testicles get utterly annihilated.
*/
public static function playBlueBalledSFX()
{
blueballed = true;
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)
* Note that this is looking for SPECIFIC animation names, so you may need to modify the XML.
* @param charId
* @param charId
*/
function loadAnimationNew():Void
{
@ -333,7 +333,7 @@ class HealthIcon extends FlxSprite
/**
* Load health icon animations using the legacy format.
* Simply assumes two icons, the idle and losing icons.
* @param charId
* @param charId
*/
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 flxanimate.FlxAnimate.Settings;
class ResultState extends MusicBeatSubstate
class ResultState extends MusicBeatSubState
{
var resultsVariation:ResultVariations;
var songName:FlxBitmapText;
@ -118,16 +118,16 @@ class ResultState extends MusicBeatSubstate
difficulty = new FlxSprite(555);
var diffSpr:String = switch (CoolUtil.difficultyString())
var diffSpr:String = switch (PlayState.instance.currentDifficulty)
{
case "EASY":
"difEasy";
case "NORMAL":
"difNormal";
case "HARD":
"difHard";
case 'EASY':
'difEasy';
case 'NORMAL':
'difNormal';
case 'HARD':
'difHard';
case _:
"difNormal";
'difNormal';
}
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
@ -144,7 +144,7 @@ class ResultState extends MusicBeatSubstate
}
else
{
songName.text += PlayState.currentSong.song;
songName.text += PlayState.instance.currentSong.songId;
}
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.
*
*
* FUN FACT: Setting the X and Y of a FlxSpriteGroup will move all the sprites in the group.
*/
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.
* 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 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
* displaying an animation derived from an Adobe Animate texture atlas spritesheet file.
*
*
* BaseCharacter has game logic, AnimateAtlasCharacter has only rendering logic.
* KEEP THEM SEPARATE!
*/
@ -537,7 +537,7 @@ class AnimateAtlasCharacter extends BaseCharacter
/**
* Returns the left-most position of the left-most member.
* If there are no members, x is returned.
*
*
* @since 5.0.0
* @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.
* If there are no members, x is returned.
*
*
* @since 5.0.0
* @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.
* If there are no members, y is returned.
*
*
* @since 5.0.0
* @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.
* If there are no members, y is returned.
*
*
* @since 5.0.0
* @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.
*
*
* Remember: The character's origin is at its FEET. (horizontal center, vertical bottom)
*/
class BaseCharacter extends Bopper
@ -43,7 +43,7 @@ class BaseCharacter extends Bopper
/**
* Set to true when the character being used in a special way.
* 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.
*/
public var debug:Bool = false;
@ -76,7 +76,7 @@ class BaseCharacter extends Bopper
/**
* The absolute position of the top-left of the character.
* @return
* @return
*/
public var cornerPosition(get, set):FlxPoint;
@ -112,7 +112,7 @@ class BaseCharacter extends Bopper
/**
* Returns the point the camera should focus on.
* 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.
*/
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.
* @param scale
* @param scale
*/
public function setScale(scale:Null<Float>):Void
{
@ -394,14 +394,14 @@ class BaseCharacter extends Bopper
/**
* Since no `onBeatHit` or `dance` calls happen in GameOverSubState,
* this regularly gets called instead.
*
*
* @param force Force the deathLoop animation to play, even if `firstDeath` is still playing.
*/
public function playDeathAnimation(force:Bool = false):Void
{
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.
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.
*
*
* If you want to force stages to be reloaded, you can just call this function again.
*/
public static function loadCharacterCache():Void
@ -294,7 +294,7 @@ class CharacterDataParser
/**
* Load a character's JSON file and parse its data.
*
*
* @param charId The character to load.
* @return The character data, or null if validation failed.
*/
@ -363,8 +363,8 @@ class CharacterDataParser
/**
* Set unspecified parameters to their defaults.
* If the parameter is mandatory, print an error message.
* @param id
* @param input
* @param id
* @param input
* @return The validated character data
*/
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.
* Increasing this number will make the character dance less often.
*
*
* @default 1
*/
var danceEvery:Null<Int>;
@ -633,7 +633,7 @@ typedef CharacterData =
* 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 high, you may see the the character play the sing animation for too long after the notes are gone.
*
*
* Examples:
* - Daddy Dearest uses a value of `1.525`.
* @default 1.0
@ -654,7 +654,7 @@ typedef CharacterData =
/**
* Whether or not the whole ass sprite is flipped by default.
* Useful for characters that could also be played (Pico)
*
*
* @default false
*/
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
* 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
* 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).
*
*
* BaseCharacter has game logic, SparrowCharacter has only rendering logic.
* KEEP THEM SEPARATE!
*

View file

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

View file

@ -1,13 +1,10 @@
package funkin.play.cutscene;
// import hxcodec.flixel.FlxVideoSprite;
// import hxcodec.flixel.FlxCutsceneState;
import flixel.FlxSprite;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import funkin.graphics.video.FlxVideo;
/**
* Static methods for playing cutscenes in the PlayState.
@ -15,112 +12,46 @@ import funkin.graphics.video.FlxVideo;
*/
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;
/**
* 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;
static final TWEEN_DURATION:Float = 2.0;
/**
* Does the cleanup to start the countdown after the video is done.
* Gets called immediately if the video can't be played.
*/
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???
* Plays the cutscene that appears at the start of Winter Horrorland.
* TODO: Move this to `winter-horrorland.hxc`
*/
public static function playHorrorStartCutscene():Void
{
PlayState.isInCutscene = true;
PlayState.instance.isInCutscene = true;
PlayState.instance.camHUD.visible = false;
blackScreen = new FlxSprite(-200, -200).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
blackScreen.scrollFactor.set(0, 0);
blackScreen.zIndex = 1000000;
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;
import funkin.play.event.SongEvent;
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.
* It is used by the ScriptedSongEvent class to handle user-defined events.
*
*
* Example: Focus on Boyfriend:
* ```
* {
@ -16,7 +18,7 @@ import funkin.play.song.SongData;
* }
* }
* ```
*
*
* Example: Focus on 10px above Girlfriend:
* ```
* {
@ -27,7 +29,7 @@ import funkin.play.song.SongData;
* }
* }
* ```
*
*
* Example: Focus on (100, 100):
* ```
* {

View file

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

View file

@ -7,7 +7,7 @@ import polymod.hscript.HScriptedClass;
* A script that can be tied to a SongEvent.
* Create a scripted class that extends SongEvent,
* 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 `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.

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;
import funkin.util.macro.ClassMacro;
import funkin.play.song.SongData.SongEventData;
import funkin.play.event.SongEventData.SongEventSchema;
/**
* This class represents a handler for a type of song event.
@ -52,233 +52,3 @@ class SongEvent
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.
* Automatically resolves suffixed character IDs (so bf-car will resolve to bf if needed).
*
*
* @param id The character we are about to play.
*/
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.
*
*
* If you want to force song metadata to be reloaded, you can just call this function again.
*/
public static function loadSongCache():Void
@ -217,7 +217,7 @@ typedef RawSongMetadata =
{
/**
* A semantic versioning string for the song data format.
*
*
*/
var version:Version;
@ -414,7 +414,7 @@ abstract SongNoteData(RawSongNoteData)
/**
* The strumline index of the note, if applicable.
* Strips the direction from the data.
*
*
* 0 = player, 1 = opponent, etc.
*/
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
* whose timestamps are shifted by the given amount.
* Does not mutate the original array.
*
*
* @param notes The notes to modify.
* @param offset The time difference to apply in milliseconds.
*/
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);
});
}
@ -30,14 +29,13 @@ class SongDataUtils
* Given an array of SongEventData objects, return a new array of SongEventData objects
* whose timestamps are shifted by the given amount.
* Does not mutate the original array.
*
*
* @param events The events to modify.
* @param offset The time difference to apply in milliseconds.
*/
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);
});
}
@ -45,7 +43,7 @@ class SongDataUtils
/**
* Return a new array without a certain subset of notes from an array of SongNoteData objects.
* Does not mutate the original array.
*
*
* @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.
*/
@ -53,8 +51,7 @@ class SongDataUtils
{
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)
// SongNoteData's == operation has been overridden so that this will work.
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.
* Does not mutate the original array.
*
*
* @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.
*/
@ -76,8 +73,7 @@ class SongDataUtils
{
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.
return !subtrahend.has(event);
});
@ -89,8 +85,7 @@ class SongDataUtils
*/
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;
if (newData < strumlineSize) newData += strumlineSize;
@ -103,7 +98,7 @@ class SongDataUtils
/**
* 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.
*/
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.
*
*
* Offset the provided array of events such that the first event is at 0 milliseconds.
*/
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>
{
// 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 notes;
@ -140,8 +134,7 @@ class SongDataUtils
public static function sortEvents(events:Array<SongEventData>, ?desc:Bool = false):Array<SongEventData>
{
// 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 events;
@ -192,8 +185,7 @@ class SongDataUtils
*/
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;
});
}
@ -203,8 +195,7 @@ class SongDataUtils
*/
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;
});
}
@ -214,8 +205,7 @@ class SongDataUtils
*/
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;
});
}
@ -225,8 +215,7 @@ class SongDataUtils
*/
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;
});
}

View file

@ -50,8 +50,7 @@ class SongSerializer
*/
public static function importSongChartDataAsync(callback:SongChartData->Void):Void
{
browseFileReference(function(fileReference:FileReference)
{
browseFileReference(function(fileReference:FileReference) {
var data = fileReference.data.toString();
if (data == null) return;
@ -68,8 +67,7 @@ class SongSerializer
*/
public static function importSongMetadataAsync(callback:SongMetadata->Void):Void
{
browseFileReference(function(fileReference:FileReference)
{
browseFileReference(function(fileReference:FileReference) {
var data = fileReference.data.toString();
if (data == null) return;
@ -103,7 +101,7 @@ class SongSerializer
/**
* Save a SongChartData object as a JSON file to a specified path.
* Works great on HTML5 and desktop.
*
*
* @param path The file path to save to.
*/
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.
* Works great on HTML5 and desktop.
*
*
* @param path The file path to save to.
*/
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.
* Works great on HTML5 or desktop.
*
*
* @param callback The function to call when the file is loaded.
*/
static function browseFileReference(callback:FileReference->Void)
{
var file = new FileReference();
file.addEventListener(Event.SELECT, function(e)
{
file.addEventListener(Event.SELECT, function(e) {
var selectedFileRef:FileReference = e.target;
trace('Selected file: ' + selectedFileRef.name);
selectedFileRef.addEventListener(Event.COMPLETE, function(e)
{
selectedFileRef.addEventListener(Event.COMPLETE, function(e) {
var loadedFileRef:FileReference = e.target;
trace('Loaded file: ' + loadedFileRef.name);
callback(loadedFileRef);
@ -192,16 +188,13 @@ class SongSerializer
static function writeFileReference(path:String, data:String)
{
var file = new FileReference();
file.addEventListener(Event.COMPLETE, function(e:Event)
{
file.addEventListener(Event.COMPLETE, function(e:Event) {
trace('Successfully wrote file.');
});
file.addEventListener(Event.CANCEL, function(e:Event)
{
file.addEventListener(Event.CANCEL, function(e:Event) {
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.');
});
file.save(data, path);

View file

@ -30,7 +30,7 @@ class SongValidator
/**
* Validates the fields of a SongMetadata object (excluding the version field).
*
*
* @param input The SongMetadata object to validate.
* @param songId The ID of the song being validated. Only used for error messages.
* @return The validated SongMetadata object.
@ -73,7 +73,7 @@ class SongValidator
/**
* Validates the fields of a SongPlayData object.
*
*
* @param input The SongPlayData object to validate.
* @param songId The ID of the song being validated. Only used for error messages.
* @return The validated SongPlayData object.
@ -85,7 +85,7 @@ class SongValidator
/**
* Validates the fields of a TimeChange object.
*
*
* @param input The TimeChange object to validate.
* @param songId The ID of the song being validated. Only used for error messages.
* @return The validated TimeChange object.
@ -113,7 +113,7 @@ class SongValidator
/**
* Validates the fields of a SongChartData object (excluding the version field).
*
*
* @param input The SongChartData object to validate.
* @param songId The ID of the song being validated. Only used for error messages.
* @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.
* - If true, alternate playing `danceLeft` and `danceRight`.
* - If false, play `idle` every time.
*
*
* You can manually set this value, or you can leave it as `null` to determine it automatically.
*/
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 frameNumber The number 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],
* 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.
* 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
{

View file

@ -19,7 +19,7 @@ typedef StagePropGroup = FlxTypedSpriteGroup<StageProp>;
/**
* 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.
*/
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.
* They're used to cache the data needed to build the stage,
* then accessed and fleshed out when the stage needs to be built.
*
* @param stageId
*
* @param stageId
*/
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.
* Override this in a scripted class to ensure that all necessary assets are loaded!
*
*
* @return An array of file names.
*/
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.
*
*
* If you want to force stages to be reloaded, you can just call this function again.
*/
public static function loadStageCache():Void
@ -68,8 +68,7 @@ class StageDataParser
// UNSCRIPTED 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);
});
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.
*
*
* @param stageId The stage to load.
* @return The stage data, or null if validation failed.
*/
@ -199,8 +198,8 @@ class StageDataParser
/**
* Set unspecified parameters to their defaults.
* If the parameter is mandatory, print an error message.
* @param id
* @param input
* @param id
* @param input
* @return The validated stage data
*/
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.
* This requires animations to be defined. If `danceLeft` and `danceRight` are defined,
* they will alternated between, otherwise the `idle` animation will be used.
*
*
* @default 0
*/
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.
* @param event
* @param event
*/
public function onAdd(event:ScriptEvent):Void {}

View file

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

View file

@ -47,7 +47,7 @@ class OverlayBlend extends FlxShader
{
vec2 funnyUv = openfl_TextureCoordv;
vec4 color = flixel_texture2D(bitmap, funnyUv);
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));

View file

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

View file

@ -44,7 +44,7 @@ class StrokeShader extends FlxShader
if (sample.a == 0.) {
float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y;
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, openfl_TextureCoordv.y + h)).a != 0.

View file

@ -50,7 +50,7 @@ class TitleOutline extends FlxShader
if (color.a == 0.0) {
float w = size.x / openfl_TextureSize.x;
float h = size.y / openfl_TextureSize.y;
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.)
color = vec4(0.0, 1.0, 0.8, color.a);
}
gl_FragColor = color;
}

View file

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

View file

@ -121,8 +121,7 @@ class ControlsMenu extends funkin.ui.OptionsState.Page
var margin = 100;
menuCamera.deadzone.set(0, margin, menuCamera.width, menuCamera.height - margin * 2);
menuCamera.minScrollY = 0;
controlGrid.onChange.add(function(selected)
{
controlGrid.onChange.add(function(selected) {
camFollow.y = selected.y;
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.
* @param prev
* @param next
* @param allowWrap
* @param prev
* @param next
* @param allowWrap
*/
inline function navList(prev:Bool, next:Bool, allowWrap:Bool)
{
@ -164,8 +164,7 @@ class MenuTypedList<T:MenuItem> extends FlxTypedGroup<T>
{
busy = true;
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;
selected.callback();
});

View file

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

View file

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

View file

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

View file

@ -43,8 +43,7 @@ class PreferencesMenu extends Page
menuCamera.deadzone.set(0, margin, menuCamera.width, 40);
menuCamera.minScrollY = 0;
items.onChange.add(function(selected)
{
items.onChange.add(function(selected) {
camFollow.y = selected.y;
});
}
@ -82,8 +81,7 @@ class PreferencesMenu extends Page
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);
switch (Type.typeof(prefValue).getName())
@ -145,8 +143,7 @@ class PreferencesMenu extends Page
// menuCamera.followLerp = CoolUtil.camLerpShit(0.05);
items.forEach(function(daItem:TextMenuItem)
{
items.forEach(function(daItem:TextMenuItem) {
if (items.selectedItem == daItem) daItem.x = 150;
else
daItem.x = 120;

View file

@ -17,7 +17,7 @@ import openfl.geom.Matrix;
import openfl.display.Sprite;
import openfl.display.Bitmap;
class StickerSubState extends MusicBeatSubstate
class StickerSubState extends MusicBeatSubState
{
public var grpStickers:FlxTypedGroup<StickerSprite>;
@ -36,7 +36,8 @@ class StickerSubState extends MusicBeatSubstate
add(grpStickers);
// 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)
{
@ -208,8 +209,10 @@ class StickerSubState extends MusicBeatSubstate
FlxG.switchState(new FreeplayState(this));
case STORY:
FlxG.switchState(new StoryMenuState(this));
case MAIN_MENU:
FlxG.switchState(new MainMenuState());
default:
FlxG.switchState(new FreeplayState(this));
FlxG.switchState(new MainMenuState());
}
}
@ -354,6 +357,7 @@ typedef StickerShit =
enum abstract NEXTSTATE(String)
{
var MAIN_MENU = 'mainmenu';
var FREEPLAY = 'freeplay';
var STORY = 'story';
}

View file

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

View file

@ -2,11 +2,11 @@ package funkin.ui.debug;
import flixel.FlxObject;
import flixel.FlxSprite;
import funkin.MusicBeatSubstate;
import funkin.MusicBeatSubState;
import funkin.ui.TextMenuList;
import funkin.ui.debug.charting.ChartEditorState;
class DebugMenuSubState extends MusicBeatSubstate
class DebugMenuSubState extends MusicBeatSubState
{
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.
*
*
* @param state The current chart editor state.
* @return The dialog that was opened.
*/

View file

@ -58,7 +58,7 @@ using Lambda;
* Built with HaxeUI for use by both developers and modders.
*
* Some functionality is moved to other classes to help maintain my sanity.
*
*
* @author MasterEric
*/
// 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.
* - Keys are the character IDs.
* - 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.
*/
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.
*
*
* @param path The absolute path to the audio file.
* @return Success or failure.
*/
@ -3132,7 +3132,7 @@ class ChartEditorState extends HaxeUIState
/**
* Perform (or redo) a command, then add it to the undo stack.
*
*
* @param command The command to perform.
* @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 funkin.play.character.BaseCharacter.CharacterType;
import funkin.play.event.SongEvent;
import funkin.play.event.SongEventData;
import funkin.play.song.SongData.SongTimeChange;
import funkin.play.song.SongSerializer;
import funkin.ui.haxeui.components.CharacterPlayer;
@ -133,8 +134,7 @@ class ChartEditorToolboxHandler
toolbox.x = 50;
toolbox.y = 50;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxTools', false);
}
@ -142,8 +142,7 @@ class ChartEditorToolboxHandler
if (toolsGroup == null) return null;
toolsGroup.onChange = (event:UIEvent) ->
{
toolsGroup.onChange = (event:UIEvent) -> {
switch (event.target.id)
{
case 'toolboxToolsGroupSelect':
@ -168,8 +167,7 @@ class ChartEditorToolboxHandler
toolbox.x = 75;
toolbox.y = 100;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxNotes', false);
}
@ -177,8 +175,7 @@ class ChartEditorToolboxHandler
var toolboxNotesCustomKindLabel:Label = toolbox.findComponent("toolboxNotesCustomKindLabel", Label);
var toolboxNotesCustomKind:TextField = toolbox.findComponent("toolboxNotesCustomKind", TextField);
toolboxNotesNoteKind.onChange = (event:UIEvent) ->
{
toolboxNotesNoteKind.onChange = (event:UIEvent) -> {
var isCustom = (event.data.id == '~CUSTOM~');
if (isCustom)
@ -197,8 +194,7 @@ class ChartEditorToolboxHandler
}
}
toolboxNotesCustomKind.onChange = (event:UIEvent) ->
{
toolboxNotesCustomKind.onChange = (event:UIEvent) -> {
state.selectedNoteKind = toolboxNotesCustomKind.text;
}
@ -215,8 +211,7 @@ class ChartEditorToolboxHandler
toolbox.x = 100;
toolbox.y = 150;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxEvents', false);
}
@ -232,8 +227,7 @@ class ChartEditorToolboxHandler
toolboxEventsEventKind.dataSource.add({text: event.getTitle(), value: event.id});
}
toolboxEventsEventKind.onChange = (event:UIEvent) ->
{
toolboxEventsEventKind.onChange = (event:UIEvent) -> {
var eventType:String = event.data.value;
trace('ChartEditorToolboxHandler.buildToolboxEventDataLayout() - Event type changed: $eventType');
@ -320,8 +314,7 @@ class ChartEditorToolboxHandler
target.addComponent(input);
input.onChange = (event:UIEvent) ->
{
input.onChange = (event:UIEvent) -> {
trace('ChartEditorToolboxHandler.buildEventDataFormFromSchema() - ${event.target.id} = ${event.target.value}');
if (event.target.value == null) state.selectedEventData.remove(event.target.id);
@ -341,8 +334,7 @@ class ChartEditorToolboxHandler
toolbox.x = 125;
toolbox.y = 200;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxDifficulty', false);
}
@ -352,35 +344,28 @@ class ChartEditorToolboxHandler
var difficultyToolboxLoadMetadata:Button = toolbox.findComponent("difficultyToolboxLoadMetadata", Button);
var difficultyToolboxLoadChart:Button = toolbox.findComponent("difficultyToolboxLoadChart", Button);
difficultyToolboxSaveMetadata.onClick = (event:UIEvent) ->
{
difficultyToolboxSaveMetadata.onClick = (event:UIEvent) -> {
SongSerializer.exportSongMetadata(state.currentSongMetadata);
};
difficultyToolboxSaveChart.onClick = (event:UIEvent) ->
{
difficultyToolboxSaveChart.onClick = (event:UIEvent) -> {
SongSerializer.exportSongChartData(state.currentSongChartData);
};
difficultyToolboxSaveAll.onClick = (event:UIEvent) ->
{
difficultyToolboxSaveAll.onClick = (event:UIEvent) -> {
state.exportAllSongData();
};
difficultyToolboxLoadMetadata.onClick = (event:UIEvent) ->
{
difficultyToolboxLoadMetadata.onClick = (event:UIEvent) -> {
// Replace metadata for current variation.
SongSerializer.importSongMetadataAsync(function(songMetadata)
{
SongSerializer.importSongMetadataAsync(function(songMetadata) {
state.currentSongMetadata = songMetadata;
});
};
difficultyToolboxLoadChart.onClick = (event:UIEvent) ->
{
difficultyToolboxLoadChart.onClick = (event:UIEvent) -> {
// Replace chart data for current variation.
SongSerializer.importSongChartDataAsync(function(songChartData)
{
SongSerializer.importSongChartDataAsync(function(songChartData) {
state.currentSongChartData = songChartData;
state.noteDisplayDirty = true;
});
@ -401,14 +386,12 @@ class ChartEditorToolboxHandler
toolbox.x = 150;
toolbox.y = 250;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxMetadata', false);
}
var inputSongName:TextField = toolbox.findComponent('inputSongName', TextField);
inputSongName.onChange = (event:UIEvent) ->
{
inputSongName.onChange = (event:UIEvent) -> {
var valid = event.target.text != null && event.target.text != "";
if (valid)
@ -423,8 +406,7 @@ class ChartEditorToolboxHandler
};
var inputSongArtist:TextField = toolbox.findComponent('inputSongArtist', TextField);
inputSongArtist.onChange = (event:UIEvent) ->
{
inputSongArtist.onChange = (event:UIEvent) -> {
var valid = event.target.text != null && event.target.text != "";
if (valid)
@ -439,8 +421,7 @@ class ChartEditorToolboxHandler
};
var inputStage:DropDown = toolbox.findComponent('inputStage', DropDown);
inputStage.onChange = (event:UIEvent) ->
{
inputStage.onChange = (event:UIEvent) -> {
var valid = event.data != null && event.data.id != null;
if (valid)
@ -450,15 +431,13 @@ class ChartEditorToolboxHandler
};
var inputNoteSkin:DropDown = toolbox.findComponent('inputNoteSkin', DropDown);
inputNoteSkin.onChange = (event:UIEvent) ->
{
inputNoteSkin.onChange = (event:UIEvent) -> {
if (event.data.id == null) return;
state.currentSongMetadata.playData.noteSkin = event.data.id;
};
var inputBPM:NumberStepper = toolbox.findComponent('inputBPM', NumberStepper);
inputBPM.onChange = (event:UIEvent) ->
{
inputBPM.onChange = (event:UIEvent) -> {
if (event.value == null || event.value <= 0) return;
var timeChanges = state.currentSongMetadata.timeChanges;
@ -477,8 +456,7 @@ class ChartEditorToolboxHandler
};
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;
if (valid)
@ -505,8 +483,7 @@ class ChartEditorToolboxHandler
toolbox.x = 175;
toolbox.y = 300;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxCharacters', false);
}
@ -523,8 +500,7 @@ class ChartEditorToolboxHandler
toolbox.x = 200;
toolbox.y = 350;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxPlayerPreview', false);
}
@ -548,8 +524,7 @@ class ChartEditorToolboxHandler
toolbox.x = 200;
toolbox.y = 350;
toolbox.onDialogClosed = (event:DialogEvent) ->
{
toolbox.onDialogClosed = (event:DialogEvent) -> {
state.setUICheckboxSelected('menubarItemToggleToolboxOpponentPreview', false);
}

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