diff --git a/.vscode/settings.json b/.vscode/settings.json index 5bf55ad57..2f795d56e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -3,7 +3,6 @@ "export/**/*.hx": true }, "[haxe]": { - "editor.formatOnSave": true, "editor.formatOnPaste": true, "editor.codeActionsOnSave": { "source.sortImports": true diff --git a/assets/images/UI/default/base/combo.png b/assets/images/UI/default/base/combo.png new file mode 100644 index 000000000..31c9ff8de Binary files /dev/null and b/assets/images/UI/default/base/combo.png differ diff --git a/assets/images/UI/default/base/judgements.png b/assets/images/UI/default/base/judgements.png new file mode 100644 index 000000000..af48b7809 Binary files /dev/null and b/assets/images/UI/default/base/judgements.png differ diff --git a/assets/images/UI/default/base/num-.png b/assets/images/UI/default/base/num-.png deleted file mode 100644 index 37472f0cf..000000000 Binary files a/assets/images/UI/default/base/num-.png and /dev/null differ diff --git a/assets/images/UI/default/base/num0.png b/assets/images/UI/default/base/num0.png deleted file mode 100644 index 437d317bc..000000000 Binary files a/assets/images/UI/default/base/num0.png and /dev/null differ diff --git a/assets/images/UI/default/base/num1.png b/assets/images/UI/default/base/num1.png deleted file mode 100644 index 5f1072275..000000000 Binary files a/assets/images/UI/default/base/num1.png and /dev/null differ diff --git a/assets/images/UI/default/base/num2.png b/assets/images/UI/default/base/num2.png deleted file mode 100644 index 4aad68c7f..000000000 Binary files a/assets/images/UI/default/base/num2.png and /dev/null differ diff --git a/assets/images/UI/default/base/num3.png b/assets/images/UI/default/base/num3.png deleted file mode 100644 index 7006a076e..000000000 Binary files a/assets/images/UI/default/base/num3.png and /dev/null differ diff --git a/assets/images/UI/default/base/num4.png b/assets/images/UI/default/base/num4.png deleted file mode 100644 index 26bdd9f99..000000000 Binary files a/assets/images/UI/default/base/num4.png and /dev/null differ diff --git a/assets/images/UI/default/base/num5.png b/assets/images/UI/default/base/num5.png deleted file mode 100644 index 7c3ee0270..000000000 Binary files a/assets/images/UI/default/base/num5.png and /dev/null differ diff --git a/assets/images/UI/default/base/num6.png b/assets/images/UI/default/base/num6.png deleted file mode 100644 index 57a811bdf..000000000 Binary files a/assets/images/UI/default/base/num6.png and /dev/null differ diff --git a/assets/images/UI/default/base/num7.png b/assets/images/UI/default/base/num7.png deleted file mode 100644 index c4f55d455..000000000 Binary files a/assets/images/UI/default/base/num7.png and /dev/null differ diff --git a/assets/images/UI/default/base/num8.png b/assets/images/UI/default/base/num8.png deleted file mode 100644 index 6df44745b..000000000 Binary files a/assets/images/UI/default/base/num8.png and /dev/null differ diff --git a/assets/images/UI/default/base/num9.png b/assets/images/UI/default/base/num9.png deleted file mode 100644 index 5f7abaa04..000000000 Binary files a/assets/images/UI/default/base/num9.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/bad-timings.png b/assets/images/UI/default/base/ratings/bad-timings.png deleted file mode 100644 index 1872fb3a2..000000000 Binary files a/assets/images/UI/default/base/ratings/bad-timings.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/bad.png b/assets/images/UI/default/base/ratings/bad.png deleted file mode 100644 index 7910ca698..000000000 Binary files a/assets/images/UI/default/base/ratings/bad.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/combo.png b/assets/images/UI/default/base/ratings/combo.png deleted file mode 100644 index 6a2a19288..000000000 Binary files a/assets/images/UI/default/base/ratings/combo.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/good-timings.png b/assets/images/UI/default/base/ratings/good-timings.png deleted file mode 100644 index 13d184e41..000000000 Binary files a/assets/images/UI/default/base/ratings/good-timings.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/good.png b/assets/images/UI/default/base/ratings/good.png deleted file mode 100644 index 9267eacb9..000000000 Binary files a/assets/images/UI/default/base/ratings/good.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/miss.png b/assets/images/UI/default/base/ratings/miss.png deleted file mode 100644 index dd1297ec6..000000000 Binary files a/assets/images/UI/default/base/ratings/miss.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/shit-timings.png b/assets/images/UI/default/base/ratings/shit-timings.png deleted file mode 100644 index 407d9d534..000000000 Binary files a/assets/images/UI/default/base/ratings/shit-timings.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/shit.png b/assets/images/UI/default/base/ratings/shit.png deleted file mode 100644 index 0c4d55769..000000000 Binary files a/assets/images/UI/default/base/ratings/shit.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/sick-perfect.png b/assets/images/UI/default/base/ratings/sick-perfect.png deleted file mode 100644 index 13e108361..000000000 Binary files a/assets/images/UI/default/base/ratings/sick-perfect.png and /dev/null differ diff --git a/assets/images/UI/default/base/ratings/sick.png b/assets/images/UI/default/base/ratings/sick.png deleted file mode 100644 index bba9b1f01..000000000 Binary files a/assets/images/UI/default/base/ratings/sick.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/combo.png b/assets/images/UI/default/pixel/combo.png new file mode 100644 index 000000000..ec9b487b9 Binary files /dev/null and b/assets/images/UI/default/pixel/combo.png differ diff --git a/assets/images/UI/default/pixel/dialogueBox-evil.png b/assets/images/UI/default/pixel/dialogueBox-evil.png deleted file mode 100644 index 5f0a44588..000000000 Binary files a/assets/images/UI/default/pixel/dialogueBox-evil.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/dialogueBox-evil.xml b/assets/images/UI/default/pixel/dialogueBox-evil.xml deleted file mode 100644 index 94a1fb456..000000000 --- a/assets/images/UI/default/pixel/dialogueBox-evil.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - - - - - - - - - - - diff --git a/assets/images/UI/default/pixel/dialogueBox-pixel.png b/assets/images/UI/default/pixel/dialogueBox-pixel.png deleted file mode 100644 index c7d88ff5f..000000000 Binary files a/assets/images/UI/default/pixel/dialogueBox-pixel.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/dialogueBox-pixel.xml b/assets/images/UI/default/pixel/dialogueBox-pixel.xml deleted file mode 100644 index e0a1070a5..000000000 --- a/assets/images/UI/default/pixel/dialogueBox-pixel.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/assets/images/UI/default/pixel/dialogueBox-senpaiMad.png b/assets/images/UI/default/pixel/dialogueBox-senpaiMad.png deleted file mode 100644 index 5ed523897..000000000 Binary files a/assets/images/UI/default/pixel/dialogueBox-senpaiMad.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/dialogueBox-senpaiMad.xml b/assets/images/UI/default/pixel/dialogueBox-senpaiMad.xml deleted file mode 100644 index 4f247aa69..000000000 --- a/assets/images/UI/default/pixel/dialogueBox-senpaiMad.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - diff --git a/assets/images/UI/default/pixel/hand_textbox.png b/assets/images/UI/default/pixel/hand_textbox.png deleted file mode 100644 index 8d472ae59..000000000 Binary files a/assets/images/UI/default/pixel/hand_textbox.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/judgements.png b/assets/images/UI/default/pixel/judgements.png new file mode 100644 index 000000000..9f863624d Binary files /dev/null and b/assets/images/UI/default/pixel/judgements.png differ diff --git a/assets/images/UI/default/pixel/num-.png b/assets/images/UI/default/pixel/num-.png deleted file mode 100644 index b8aef900e..000000000 Binary files a/assets/images/UI/default/pixel/num-.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num0.png b/assets/images/UI/default/pixel/num0.png deleted file mode 100644 index 45760ba41..000000000 Binary files a/assets/images/UI/default/pixel/num0.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num1.png b/assets/images/UI/default/pixel/num1.png deleted file mode 100644 index 46b6e1924..000000000 Binary files a/assets/images/UI/default/pixel/num1.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num2.png b/assets/images/UI/default/pixel/num2.png deleted file mode 100644 index db8d6b48d..000000000 Binary files a/assets/images/UI/default/pixel/num2.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num3.png b/assets/images/UI/default/pixel/num3.png deleted file mode 100644 index 2dd73cc50..000000000 Binary files a/assets/images/UI/default/pixel/num3.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num4.png b/assets/images/UI/default/pixel/num4.png deleted file mode 100644 index bd2b5cde5..000000000 Binary files a/assets/images/UI/default/pixel/num4.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num5.png b/assets/images/UI/default/pixel/num5.png deleted file mode 100644 index 3cb566eb4..000000000 Binary files a/assets/images/UI/default/pixel/num5.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num6.png b/assets/images/UI/default/pixel/num6.png deleted file mode 100644 index 729f2c2cf..000000000 Binary files a/assets/images/UI/default/pixel/num6.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num7.png b/assets/images/UI/default/pixel/num7.png deleted file mode 100644 index da16cffc9..000000000 Binary files a/assets/images/UI/default/pixel/num7.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num8.png b/assets/images/UI/default/pixel/num8.png deleted file mode 100644 index 9453c01bc..000000000 Binary files a/assets/images/UI/default/pixel/num8.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/num9.png b/assets/images/UI/default/pixel/num9.png deleted file mode 100644 index 0305d7a36..000000000 Binary files a/assets/images/UI/default/pixel/num9.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/bad-timings.png b/assets/images/UI/default/pixel/ratings/bad-timings.png deleted file mode 100644 index c36cab54b..000000000 Binary files a/assets/images/UI/default/pixel/ratings/bad-timings.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/bad.png b/assets/images/UI/default/pixel/ratings/bad.png deleted file mode 100644 index 91001f645..000000000 Binary files a/assets/images/UI/default/pixel/ratings/bad.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/combo.png b/assets/images/UI/default/pixel/ratings/combo.png deleted file mode 100644 index f72c678f0..000000000 Binary files a/assets/images/UI/default/pixel/ratings/combo.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/good-timings.png b/assets/images/UI/default/pixel/ratings/good-timings.png deleted file mode 100644 index 1e8c26a38..000000000 Binary files a/assets/images/UI/default/pixel/ratings/good-timings.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/good.png b/assets/images/UI/default/pixel/ratings/good.png deleted file mode 100644 index a44147ee0..000000000 Binary files a/assets/images/UI/default/pixel/ratings/good.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/miss.png b/assets/images/UI/default/pixel/ratings/miss.png deleted file mode 100644 index 35c380109..000000000 Binary files a/assets/images/UI/default/pixel/ratings/miss.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/shit-timings.png b/assets/images/UI/default/pixel/ratings/shit-timings.png deleted file mode 100644 index c36cab54b..000000000 Binary files a/assets/images/UI/default/pixel/ratings/shit-timings.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/shit.png b/assets/images/UI/default/pixel/ratings/shit.png deleted file mode 100644 index 2e9e9a566..000000000 Binary files a/assets/images/UI/default/pixel/ratings/shit.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/sick-perfect.png b/assets/images/UI/default/pixel/ratings/sick-perfect.png deleted file mode 100644 index 89264d1e8..000000000 Binary files a/assets/images/UI/default/pixel/ratings/sick-perfect.png and /dev/null differ diff --git a/assets/images/UI/default/pixel/ratings/sick.png b/assets/images/UI/default/pixel/ratings/sick.png deleted file mode 100644 index 3b74379b1..000000000 Binary files a/assets/images/UI/default/pixel/ratings/sick.png and /dev/null differ diff --git a/assets/images/UI/forever/base/judgements.png b/assets/images/UI/forever/base/judgements.png new file mode 100644 index 000000000..a637d2a09 Binary files /dev/null and b/assets/images/UI/forever/base/judgements.png differ diff --git a/assets/images/UI/forever/base/ratings/bad.png b/assets/images/UI/forever/base/ratings/bad.png deleted file mode 100644 index 40ac74d4a..000000000 Binary files a/assets/images/UI/forever/base/ratings/bad.png and /dev/null differ diff --git a/assets/images/UI/forever/base/ratings/good.png b/assets/images/UI/forever/base/ratings/good.png deleted file mode 100644 index 06f8b273a..000000000 Binary files a/assets/images/UI/forever/base/ratings/good.png and /dev/null differ diff --git a/assets/images/UI/forever/base/ratings/miss.png b/assets/images/UI/forever/base/ratings/miss.png deleted file mode 100644 index 2f1c15730..000000000 Binary files a/assets/images/UI/forever/base/ratings/miss.png and /dev/null differ diff --git a/assets/images/UI/forever/base/ratings/shit.png b/assets/images/UI/forever/base/ratings/shit.png deleted file mode 100644 index 88ece6497..000000000 Binary files a/assets/images/UI/forever/base/ratings/shit.png and /dev/null differ diff --git a/assets/images/UI/forever/base/ratings/sick-perfect.png b/assets/images/UI/forever/base/ratings/sick-perfect.png deleted file mode 100644 index 7739ad224..000000000 Binary files a/assets/images/UI/forever/base/ratings/sick-perfect.png and /dev/null differ diff --git a/assets/images/UI/forever/base/ratings/sick.png b/assets/images/UI/forever/base/ratings/sick.png deleted file mode 100644 index 8bf5ac141..000000000 Binary files a/assets/images/UI/forever/base/ratings/sick.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/judgements.png b/assets/images/UI/forever/pixel/judgements.png new file mode 100644 index 000000000..f55cae004 Binary files /dev/null and b/assets/images/UI/forever/pixel/judgements.png differ diff --git a/assets/images/UI/forever/pixel/ratings/bad.png b/assets/images/UI/forever/pixel/ratings/bad.png deleted file mode 100644 index 3e6cd297a..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/bad.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/ratings/good.png b/assets/images/UI/forever/pixel/ratings/good.png deleted file mode 100644 index ea0c83364..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/good.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/ratings/miss.png b/assets/images/UI/forever/pixel/ratings/miss.png deleted file mode 100644 index d72a73b2c..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/miss.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/ratings/shit.png b/assets/images/UI/forever/pixel/ratings/shit.png deleted file mode 100644 index ed8d86e08..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/shit.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/ratings/sick-perfect.png b/assets/images/UI/forever/pixel/ratings/sick-perfect.png deleted file mode 100644 index 6292ca628..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/sick-perfect.png and /dev/null differ diff --git a/assets/images/UI/forever/pixel/ratings/sick.png b/assets/images/UI/forever/pixel/ratings/sick.png deleted file mode 100644 index 7975ff81e..000000000 Binary files a/assets/images/UI/forever/pixel/ratings/sick.png and /dev/null differ diff --git a/assets/images/UI/simplylove/base/judgements.png b/assets/images/UI/simplylove/base/judgements.png new file mode 100644 index 000000000..b1b0176a8 Binary files /dev/null and b/assets/images/UI/simplylove/base/judgements.png differ diff --git a/assets/shaders/vhs.frag b/assets/shaders/vhs.frag new file mode 100644 index 000000000..86e0aaeae --- /dev/null +++ b/assets/shaders/vhs.frag @@ -0,0 +1,86 @@ +// Based on a shader by FMS_Cat. +// https://www.shadertoy.com/view/XtBXDt +// Modified to support OpenFL. + +#pragma header +#define PI 3.14159265 + +uniform float time; + +vec3 tex2D(sampler2D _tex,vec2 _p) +{ + vec3 col=texture(_tex,_p).xyz; + if(.5 Art > Noteskins > Notes folder. +Each subfolder represents one unique noteskin, the name of which is based on the folder name. + +There are two types of note formats: + +- The base game's XML format +- Forever Engine's hardcoded grid-based quant format (temporary) + +Currently the quant format is hardcoded, made of 2 images. + +NOTE_quants.png, which uses a **4x10** grid made of **157px x 157px** blocks, +and HOLD_quants.png, which uses a **4x10** grid made of **109px x 52px** blocks. + +NOTE_quants.png has 4 columns, which correspond to each arrow. + +HOLD_quants.png has 4 columns. These are used for the hold body, hold tail, roll body, and roll tail. + +These are the noteskin references. +You can use these as a template for your custom noteskins. + + +.. figure:: NOTE_quants.png + :height: 300 + :align: left + + Fig. 1: Note quants grid + +.. figure:: HOLD_quants.png + :height: 300 + + Fig. 2: Hold quants grid diff --git a/source/ForeverAssets.hx b/source/ForeverAssets.hx index c6d727fb5..90b371c17 100644 --- a/source/ForeverAssets.hx +++ b/source/ForeverAssets.hx @@ -4,11 +4,13 @@ import flixel.FlxG; import flixel.FlxSprite; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.util.FlxColor; -import gameFolder.gameObjects.Note; import gameFolder.gameObjects.userInterface.*; import gameFolder.gameObjects.userInterface.menu.*; +import gameFolder.gameObjects.userInterface.notes.*; +import gameFolder.gameObjects.userInterface.notes.Strumline.UIStaticArrow; import gameFolder.meta.data.Conductor; import gameFolder.meta.data.Section.SwagSection; +import gameFolder.meta.data.Timings; import gameFolder.meta.state.PlayState; using StringTools; @@ -20,30 +22,21 @@ using StringTools; class ForeverAssets { // - public static function generateCombo(asset:String, assetModifier:String = 'base', changeableSkin:String = 'default', baseLibrary:String, negative:Bool, - createdColor:FlxColor, scoreInt:Int, recycleGroup:FlxTypedGroup):FlxSprite + public static function generateCombo(asset:String, number:String, allSicks:Bool, assetModifier:String = 'base', changeableSkin:String = 'default', + baseLibrary:String, negative:Bool, createdColor:FlxColor, scoreInt:Int):FlxSprite { - var newSprite:FlxSprite = recycleGroup.recycle(FlxSprite) - .loadGraphic(Paths.image(ForeverTools.returnSkinAsset(asset, assetModifier, changeableSkin, baseLibrary))); + var width = 100; + var height = 140; + + if (assetModifier == 'pixel') + { + width = 10; + height = 12; + } + var newSprite:FlxSprite = new FlxSprite().loadGraphic(Paths.image(ForeverTools.returnSkinAsset(asset, assetModifier, changeableSkin, baseLibrary)), + true, width, height); switch (assetModifier) { - case 'pixel': - newSprite.alpha = 1; - newSprite.screenCenter(); - newSprite.x += (43 * scoreInt) + 20; - newSprite.y += 60; - - newSprite.color = FlxColor.WHITE; - if (negative) - newSprite.color = createdColor; - - newSprite.setGraphicSize(Std.int(newSprite.width * PlayState.daPixelZoom)); - newSprite.updateHitbox(); - - newSprite.acceleration.y = FlxG.random.int(200, 300); - newSprite.velocity.y = -FlxG.random.int(140, 160); - newSprite.velocity.x = FlxG.random.float(-5, 5); - default: newSprite.alpha = 1; newSprite.screenCenter(); @@ -54,23 +47,40 @@ class ForeverAssets if (negative) newSprite.color = createdColor; - newSprite.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); - newSprite.setGraphicSize(Std.int(newSprite.width * 0.5)); - newSprite.updateHitbox(); + newSprite.animation.add('base', [ + (Std.parseInt(number) != null ? Std.parseInt(number) + 1 : 0) + (!allSicks ? 0 : 11) + ], 0, false); + newSprite.animation.play('base'); + } - newSprite.acceleration.y = FlxG.random.int(200, 300); - newSprite.velocity.y = -FlxG.random.int(140, 160); - newSprite.velocity.x = FlxG.random.float(-5, 5); + if (assetModifier == 'pixel') + newSprite.setGraphicSize(Std.int(newSprite.width * PlayState.daPixelZoom)); + else + { + newSprite.antialiasing = true; + newSprite.setGraphicSize(Std.int(newSprite.width * 0.5)); } + newSprite.updateHitbox(); + + newSprite.acceleration.y = FlxG.random.int(200, 300); + newSprite.velocity.y = -FlxG.random.int(140, 160); + newSprite.velocity.x = FlxG.random.float(-5, 5); return newSprite; } - public static function generateRating(asset:String, assetModifier:String = 'base', changeableSkin:String = 'default', baseLibrary:String, - recycleGroup:FlxTypedGroup):FlxSprite + public static function generateRating(asset:String, perfectSick:Bool, timing:String, assetModifier:String = 'base', changeableSkin:String = 'default', + baseLibrary:String):FlxSprite { - var rating:FlxSprite = recycleGroup.recycle(FlxSprite) - .loadGraphic(Paths.image(ForeverTools.returnSkinAsset(asset, assetModifier, changeableSkin, baseLibrary))); + var width = 500; + var height = 163; + if (assetModifier == 'pixel') + { + width = 72; + height = 32; + } + var rating:FlxSprite = new FlxSprite().loadGraphic(Paths.image(ForeverTools.returnSkinAsset('judgements', assetModifier, changeableSkin, + baseLibrary)), true, width, height); switch (assetModifier) { default: @@ -81,6 +91,19 @@ class ForeverAssets rating.acceleration.y = 550; rating.velocity.y = -FlxG.random.int(140, 175); rating.velocity.x = -FlxG.random.int(0, 10); + + rating.animation.add('base', [ + Std.int((Timings.judgementsMap.get(asset)[0] * 2) + (perfectSick ? 0 : 2) + (timing == 'late' ? 1 : 0)) + ], 24, false); + rating.animation.play('base'); + } + + if (assetModifier == 'pixel') + rating.setGraphicSize(Std.int(rating.width * PlayState.daPixelZoom * 0.7)); + else + { + rating.antialiasing = true; + rating.setGraphicSize(Std.int(rating.width * 0.7)); } return rating; @@ -191,7 +214,7 @@ class ForeverAssets newStaticArrow.animation.addByPrefix('pressed', stringSect + ' press', 24, false); newStaticArrow.animation.addByPrefix('confirm', stringSect + ' confirm', 24, false); - newStaticArrow.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + newStaticArrow.antialiasing = true; newStaticArrow.setGraphicSize(Std.int(newStaticArrow.width * 0.7)); // set little offsets per note! @@ -255,7 +278,7 @@ class ForeverAssets { default: newCheckmark.frames = Paths.getSparrowAtlas(ForeverTools.returnSkinAsset(asset, assetModifier, changeableSkin, baseLibrary)); - newCheckmark.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + newCheckmark.antialiasing = true; newCheckmark.animation.addByPrefix('false finished', 'uncheckFinished'); newCheckmark.animation.addByPrefix('false', 'uncheck', 12, false); diff --git a/source/Init.hx b/source/Init.hx index fea0a0959..c7bcc43e1 100644 --- a/source/Init.hx +++ b/source/Init.hx @@ -55,6 +55,12 @@ class Init extends FlxState 'Whether to reduce movements, like icons bouncing or beat zooms in gameplay.', NOT_FORCED ], + 'Stage Darkness' => [ + 0, + 1, + 'Darkens non-ui elements, useful if you find the characters and backgrounds distracting.', + NOT_FORCED + ], 'Display Accuracy' => [true, 0, 'Whether to display your accuracy on screen.', NOT_FORCED], 'Disable Antialiasing' => [ false, @@ -89,7 +95,7 @@ class Init extends FlxState NOT_FORCED, ['none', 'Deuteranopia', 'Protanopia', 'Tritanopia'] ], - "UI Skin" => ['default', 1, 'Choose a UI Skin for ratings, combo, etc.', NOT_FORCED, ''], + "UI Skin" => ['default', 1, 'Choose a UI Skin for judgements, combo, etc.', NOT_FORCED, ''], "Note Skin" => ['default', 1, 'Choose a note skin.', NOT_FORCED, ''], "Framerate Cap" => [120, 1, 'Define your maximum FPS.', NOT_FORCED, ['']], "Opaque Arrows" => [false, 0, "Makes the arrows at the top of the screen opaque again.", NOT_FORCED], @@ -108,8 +114,17 @@ class Init extends FlxState FORCED ], 'Skip Cutscenes' => [false, 0, 'Skip the cutscenes in story mode. (Includes Dialogue)'], - 'Camera-fixed Judgements' => [false, 0, ""], - 'Display Miss Count' => [false, 0, "When enabled, displays the amount of misses you have in a song."], + 'SM-like Judgements' => [ + false, + 0, + "Fixes the judgements to the camera instead of to the world itself, making them easier to read." + ], + 'Display Miss Count' => [ + false, + 0, + "When enabled, displays the amount of combo breaks you have in a song." + ], + ]; public static var trueSettings:Map = []; @@ -179,7 +194,7 @@ class Init extends FlxState FlxG.mouse.useSystemCursor = true; // Use system cursor because it's prettier FlxG.mouse.visible = false; // Hide mouse on start - // Main.switchState(this, new TestState()); + // Main.switchState(this, new ChartingState()); gotoTitleScreen(); } @@ -216,6 +231,11 @@ class Init extends FlxState || trueSettings.get("Framerate Cap") > 360) trueSettings.set("Framerate Cap", 30); + if (!Std.isOfType(trueSettings.get("Stage Darkness"), Int) + || trueSettings.get("Stage Darkness") < 0 + || trueSettings.get("Stage Darkness") > 100) + trueSettings.set("Stage Darkness", 0); + // 'hardcoded' ui skins gameSettings.get("UI Skin")[4] = CoolUtil.returnAssetsLibrary('UI'); if (!gameSettings.get("UI Skin")[4].contains(trueSettings.get("UI Skin"))) diff --git a/source/Main.hx b/source/Main.hx index 61a473b72..f70ecd88e 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -68,7 +68,7 @@ class Main extends Sprite public static var mainClassState:Class = Init; // Determine the main class state of the game public static var framerate:Int = 120; // How many frames per second the game should run at. - public static var gameVersion:String = '0.2.3.1'; + public static var gameVersion:String = '0.2.4.1'; public static var loadedAssets:Array = []; diff --git a/source/flixel/graphics/tile/FlxGraphicsShader.hx b/source/flixel/graphics/tile/FlxGraphicsShader.hx new file mode 100644 index 000000000..a6a5be12f --- /dev/null +++ b/source/flixel/graphics/tile/FlxGraphicsShader.hx @@ -0,0 +1,106 @@ +package flixel.graphics.tile; + +import openfl.display.ShaderParameter; +import sys.io.File; +#if FLX_DRAW_QUADS +import openfl.display.GraphicsShader; + +class FlxGraphicsShader extends GraphicsShader +{ + public var alpha:ShaderParameter; + public var colorMultiplier:ShaderParameter; + public var colorOffset:ShaderParameter; + public var hasTransform:ShaderParameter; + public var hasColorTransform:ShaderParameter; + + public function new() + { + super( + // Vertex + "#pragma header + + attribute float alpha; + attribute vec4 colorMultiplier; + attribute vec4 colorOffset; + uniform bool hasColorTransform; + + void main(void) + { + #pragma body + + openfl_Alphav = openfl_Alpha * alpha; + + if (hasColorTransform) + { + openfl_ColorOffsetv = colorOffset / 255.0; + openfl_ColorMultiplierv = colorMultiplier; + } + }", + // Fragment + "#pragma header + + void main(void) + { + gl_FragColor = flixel_texture2D(bitmap, openfl_TextureCoordv); + }", false); + + glFragmentHeader += "uniform bool hasTransform; + uniform bool hasColorTransform; + + vec4 flixel_texture2D(sampler2D bitmap, vec2 coord) + { + vec4 color = texture2D(bitmap, coord); + if (!hasTransform) + { + return color; + } + + if (color.a == 0.0) + { + return vec4(0.0, 0.0, 0.0, 0.0); + } + + if (!hasColorTransform) + { + return color * openfl_Alphav; + } + + color = vec4(color.rgb / color.a, color.a); + + mat4 colorMultiplier = mat4(0); + colorMultiplier[0][0] = openfl_ColorMultiplierv.x; + colorMultiplier[1][1] = openfl_ColorMultiplierv.y; + colorMultiplier[2][2] = openfl_ColorMultiplierv.z; + colorMultiplier[3][3] = openfl_ColorMultiplierv.w; + + color = clamp(openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); + + if (color.a > 0.0) + { + return vec4(color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); + } + return vec4(0.0, 0.0, 0.0, 0.0); + }"; + + __initGL(); + + bitmap = data.bitmap; + alpha = data.alpha; + colorMultiplier = data.colorMultiplier; + colorOffset = data.colorOffset; + hasTransform = data.hasTransform; + hasColorTransform = data.hasColorTransform; + } + + /* override function __initGL() + { + super.__initGL(); + + alpha = new ShaderParameter(); + colorMultiplier = new ShaderParameter(); + colorOffset = new ShaderParameter(); + hasTransform = new ShaderParameter(); + hasColorTransform = new ShaderParameter(); + } */ +} +#end diff --git a/source/gameFolder/gameObjects/Character.hx b/source/gameFolder/gameObjects/Character.hx index 19f0137f9..95e283c79 100644 --- a/source/gameFolder/gameObjects/Character.hx +++ b/source/gameFolder/gameObjects/Character.hx @@ -32,12 +32,16 @@ class Character extends FNFSprite public function new(x:Float, y:Float, ?character:String = "bf", ?isPlayer:Bool = false) { super(x, y); - - curCharacter = character; this.isPlayer = isPlayer; + setCharacter(character); + } + + function setCharacter(character:String) + { + curCharacter = character; var tex:FlxAtlasFrames; - antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + antialiasing = true; switch (curCharacter) { diff --git a/source/gameFolder/gameObjects/Stage.hx b/source/gameFolder/gameObjects/Stage.hx index 944b827fc..3f872f695 100644 --- a/source/gameFolder/gameObjects/Stage.hx +++ b/source/gameFolder/gameObjects/Stage.hx @@ -97,7 +97,7 @@ class Stage extends FlxTypedGroup halloweenBG.animation.addByPrefix('idle', 'halloweem bg0'); halloweenBG.animation.addByPrefix('lightning', 'halloweem bg lightning strike', 24, false); halloweenBG.animation.play('idle'); - halloweenBG.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + halloweenBG.antialiasing = true; add(halloweenBG); // isHalloween = true; @@ -124,7 +124,7 @@ class Stage extends FlxTypedGroup light.visible = false; light.setGraphicSize(Std.int(light.width * 0.85)); light.updateHitbox(); - light.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + light.antialiasing = true; phillyCityLights.add(light); } @@ -182,7 +182,7 @@ class Stage extends FlxTypedGroup limo.frames = limoTex; limo.animation.addByPrefix('drive', "Limo stage", 24); limo.animation.play('drive'); - limo.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + limo.antialiasing = true; fastCar = new FNFSprite(-300, 160).loadGraphic(Paths.image('backgrounds/' + curStage + '/fastCarLol')); // loadArray.add(limo); @@ -191,7 +191,7 @@ class Stage extends FlxTypedGroup PlayState.defaultCamZoom = 0.80; var bg:FNFSprite = new FNFSprite(-1000, -500).loadGraphic(Paths.image('backgrounds/' + curStage + '/bgWalls')); - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; bg.scrollFactor.set(0.2, 0.2); bg.active = false; bg.setGraphicSize(Std.int(bg.width * 0.8)); @@ -201,14 +201,14 @@ class Stage extends FlxTypedGroup upperBoppers = new FNFSprite(-240, -90); upperBoppers.frames = Paths.getSparrowAtlas('backgrounds/' + curStage + '/upperBop'); upperBoppers.animation.addByPrefix('bop', "Upper Crowd Bob", 24, false); - upperBoppers.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + upperBoppers.antialiasing = true; upperBoppers.scrollFactor.set(0.33, 0.33); upperBoppers.setGraphicSize(Std.int(upperBoppers.width * 0.85)); upperBoppers.updateHitbox(); add(upperBoppers); var bgEscalator:FNFSprite = new FNFSprite(-1100, -600).loadGraphic(Paths.image('backgrounds/' + curStage + '/bgEscalator')); - bgEscalator.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bgEscalator.antialiasing = true; bgEscalator.scrollFactor.set(0.3, 0.3); bgEscalator.active = false; bgEscalator.setGraphicSize(Std.int(bgEscalator.width * 0.9)); @@ -216,14 +216,14 @@ class Stage extends FlxTypedGroup add(bgEscalator); var tree:FNFSprite = new FNFSprite(370, -250).loadGraphic(Paths.image('backgrounds/' + curStage + '/christmasTree')); - tree.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + tree.antialiasing = true; tree.scrollFactor.set(0.40, 0.40); add(tree); bottomBoppers = new FNFSprite(-300, 140); bottomBoppers.frames = Paths.getSparrowAtlas('backgrounds/' + curStage + '/bottomBop'); bottomBoppers.animation.addByPrefix('bop', 'Bottom Level Boppers', 24, false); - bottomBoppers.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bottomBoppers.antialiasing = true; bottomBoppers.scrollFactor.set(0.9, 0.9); bottomBoppers.setGraphicSize(Std.int(bottomBoppers.width * 1)); bottomBoppers.updateHitbox(); @@ -231,18 +231,18 @@ class Stage extends FlxTypedGroup var fgSnow:FNFSprite = new FNFSprite(-600, 700).loadGraphic(Paths.image('backgrounds/' + curStage + '/fgSnow')); fgSnow.active = false; - fgSnow.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + fgSnow.antialiasing = true; add(fgSnow); santa = new FNFSprite(-840, 150); santa.frames = Paths.getSparrowAtlas('backgrounds/' + curStage + '/santa'); santa.animation.addByPrefix('idle', 'santa idle in fear', 24, false); - santa.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + santa.antialiasing = true; add(santa); case 'mallEvil': curStage = 'mallEvil'; var bg:FNFSprite = new FNFSprite(-400, -500).loadGraphic(Paths.image('backgrounds/mall/evilBG')); - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; bg.scrollFactor.set(0.2, 0.2); bg.active = false; bg.setGraphicSize(Std.int(bg.width * 0.8)); @@ -250,12 +250,12 @@ class Stage extends FlxTypedGroup add(bg); var evilTree:FNFSprite = new FNFSprite(300, -300).loadGraphic(Paths.image('backgrounds/mall/evilTree')); - evilTree.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + evilTree.antialiasing = true; evilTree.scrollFactor.set(0.2, 0.2); add(evilTree); var evilSnow:FNFSprite = new FNFSprite(-200, 700).loadGraphic(Paths.image("backgrounds/mall/evilSnow")); - evilSnow.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + evilSnow.antialiasing = true; add(evilSnow); case 'school': curStage = 'school'; @@ -335,7 +335,7 @@ class Stage extends FlxTypedGroup PlayState.defaultCamZoom = 0.9; curStage = 'stage'; var bg:FNFSprite = new FNFSprite(-600, -200).loadGraphic(Paths.image('backgrounds/' + curStage + '/stageback')); - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; bg.scrollFactor.set(0.9, 0.9); bg.active = false; @@ -345,7 +345,7 @@ class Stage extends FlxTypedGroup var stageFront:FNFSprite = new FNFSprite(-650, 600).loadGraphic(Paths.image('backgrounds/' + curStage + '/stagefront')); stageFront.setGraphicSize(Std.int(stageFront.width * 1.1)); stageFront.updateHitbox(); - stageFront.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + stageFront.antialiasing = true; stageFront.scrollFactor.set(0.9, 0.9); stageFront.active = false; @@ -355,7 +355,7 @@ class Stage extends FlxTypedGroup var stageCurtains:FNFSprite = new FNFSprite(-500, -300).loadGraphic(Paths.image('backgrounds/' + curStage + '/stagecurtains')); stageCurtains.setGraphicSize(Std.int(stageCurtains.width * 0.9)); stageCurtains.updateHitbox(); - stageCurtains.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + stageCurtains.antialiasing = true; stageCurtains.scrollFactor.set(1.3, 1.3); stageCurtains.active = false; @@ -601,6 +601,8 @@ class Stage extends FlxTypedGroup override function add(Object:FlxBasic):FlxBasic { Main.loadedAssets.insert(Main.loadedAssets.length, Object); + if (Init.trueSettings.get('Disable Antialiasing') && Std.isOfType(Object, FlxSprite)) + cast(Object, FlxSprite).antialiasing = false; return super.add(Object); } } diff --git a/source/gameFolder/gameObjects/background/BackgroundDancer.hx b/source/gameFolder/gameObjects/background/BackgroundDancer.hx index 71d353c5f..383cfb236 100644 --- a/source/gameFolder/gameObjects/background/BackgroundDancer.hx +++ b/source/gameFolder/gameObjects/background/BackgroundDancer.hx @@ -13,7 +13,7 @@ class BackgroundDancer extends FNFSprite animation.addByIndices('danceLeft', 'bg dancer sketch PINK', [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); animation.addByIndices('danceRight', 'bg dancer sketch PINK', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); animation.play('danceLeft'); - antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + antialiasing = true; } var danceDir:Bool = false; diff --git a/source/gameFolder/gameObjects/userInterface/ClassHUD.hx b/source/gameFolder/gameObjects/userInterface/ClassHUD.hx index cdfacf137..1bca27a79 100644 --- a/source/gameFolder/gameObjects/userInterface/ClassHUD.hx +++ b/source/gameFolder/gameObjects/userInterface/ClassHUD.hx @@ -82,8 +82,14 @@ class ClassHUD extends FlxTypedGroup // small info bar, kinda like the KE watermark // based on scoretxt which I will set up as well - var infoDisplay:String = CoolUtil.dashToSpace(PlayState.SONG.song) + ' - ' + CoolUtil.difficultyFromNumber(PlayState.storyDifficulty) - + " - FE BETA v" + Main.gameVersion; + var infoDisplay:String = CoolUtil.dashToSpace(PlayState.SONG.song) + ' - ' + CoolUtil.difficultyFromNumber(PlayState.storyDifficulty); + var engineDisplay:String = "Forever Engine BETA v" + Main.gameVersion; + var engineBar:FlxText = new FlxText(0, FlxG.height - 30, 0, engineDisplay, 16); + engineBar.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); + engineBar.updateHitbox(); + engineBar.x = FlxG.width - engineBar.width - 5; + engineBar.scrollFactor.set(); + add(engineBar); infoBar = new FlxText(5, FlxG.height - 30, 0, infoDisplay, 20); infoBar.setFormat(Paths.font("vcr.ttf"), 16, FlxColor.WHITE, RIGHT, FlxTextBorderStyle.OUTLINE, FlxColor.BLACK); @@ -131,7 +137,7 @@ class ClassHUD extends FlxTypedGroup { scoreBar.text += ' // Accuracy: ' + Std.string(Math.floor(Timings.getAccuracy() * 100) / 100) + '%' + Timings.comboDisplay; if (Init.trueSettings.get('Display Miss Count')) - scoreBar.text += ' // Misses: ' + Std.string(PlayState.misses); + scoreBar.text += ' // Combo Breaks: ' + Std.string(PlayState.misses); scoreBar.text += ' // Rank: ' + Std.string(Timings.returnScoreRating().toUpperCase()); } diff --git a/source/gameFolder/gameObjects/userInterface/HealthIcon.hx b/source/gameFolder/gameObjects/userInterface/HealthIcon.hx index 4616dd8d6..47a99e7aa 100644 --- a/source/gameFolder/gameObjects/userInterface/HealthIcon.hx +++ b/source/gameFolder/gameObjects/userInterface/HealthIcon.hx @@ -22,7 +22,7 @@ class HealthIcon extends FlxSprite char = char.substring(0, char.indexOf('-')); } - antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + antialiasing = true; loadGraphic(Paths.image('icons/icon-' + char), true, 150, 150); animation.add('icon', [0, 1], 0, false, isPlayer); animation.play('icon'); diff --git a/source/gameFolder/gameObjects/userInterface/NoteSplash.hx b/source/gameFolder/gameObjects/userInterface/NoteSplash.hx deleted file mode 100644 index 0bd1175d0..000000000 --- a/source/gameFolder/gameObjects/userInterface/NoteSplash.hx +++ /dev/null @@ -1,61 +0,0 @@ -package gameFolder.gameObjects.userInterface; - -import flixel.FlxG; -import flixel.FlxSprite; -import gameFolder.gameObjects.Note; -import gameFolder.meta.state.PlayState; - -/** - Create the note splashes in week 7 whenever you get a sick! -**/ -class NoteSplash extends FlxSprite -{ - public var animOffsets:Map>; - - // - public function new(noteData:Int) - { - super(); - animOffsets = new Map>(); - - visible = false; - alpha = 0.6; - } - - override function update(elapsed:Float) - { - super.update(elapsed); - - // kill the note splash if it's done - if (animation.finished) - { - // set the splash to invisible - if (visible) - visible = false; - } - // - } - - public function addOffset(name:String, x:Float = 0, y:Float = 0) - { - animOffsets[name] = [x, y]; - } - - public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0) - { - // make sure the animation is visible - if (!Init.trueSettings.get('Disable Note Splashes')) - visible = true; - // play the animation - animation.play(AnimName, Force, Reversed, Frame); - updateHitbox(); - - var daOffset = animOffsets.get(AnimName); - if (animOffsets.exists(AnimName)) - { - offset.set(daOffset[0], daOffset[1]); - } - else - offset.set(0, 0); - } -} diff --git a/source/gameFolder/gameObjects/userInterface/UIStaticArrow.hx b/source/gameFolder/gameObjects/userInterface/UIStaticArrow.hx deleted file mode 100644 index 7d54a48b6..000000000 --- a/source/gameFolder/gameObjects/userInterface/UIStaticArrow.hx +++ /dev/null @@ -1,114 +0,0 @@ -package gameFolder.gameObjects.userInterface; - -import flixel.FlxG; -import flixel.FlxSprite; -import gameFolder.meta.state.PlayState; - -using StringTools; - -/* - import flixel.FlxG; - - import flixel.animation.FlxBaseAnimation; - import flixel.graphics.frames.FlxAtlasFrames; - import flixel.tweens.FlxEase; - import flixel.tweens.FlxTween; - */ -class UIStaticArrow extends FlxSprite -{ - /* Oh hey, just gonna port this code from the previous Skater engine - (depending on the release of this you might not have it cus I might rewrite skater to use this engine instead) - It's basically just code from the game itself but - it's in a separate class and I also added the ability to set offsets for the arrows. - - uh hey you're cute ;) - */ - public var animOffsets:Map>; - public var babyArrowType:Int = 0; - public var canFinishAnimation:Bool = true; - - public var initialX:Int; - public var initialY:Int; - - public var xTo:Float; - public var yTo:Float; - public var angleTo:Float; - - public var setAlpha:Float = (Init.trueSettings.get('Opaque Arrows')) ? 1 : 0.8; - - public function new(x:Float, y:Float, ?babyArrowType:Int = 0) - { - // this extension is just going to rely a lot on preexisting code as I wanna try to write an extension before I do options and stuff - super(x, y); - animOffsets = new Map>(); - - this.babyArrowType = babyArrowType; - - updateHitbox(); - scrollFactor.set(); - } - - // literally just character code - public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void - { - if (AnimName == 'confirm') - alpha = 1; - else - alpha = setAlpha; - - animation.play(AnimName, Force, Reversed, Frame); - updateHitbox(); - - var daOffset = animOffsets.get(AnimName); - if (animOffsets.exists(AnimName)) - { - offset.set(daOffset[0], daOffset[1]); - } - else - offset.set(0, 0); - } - - public function addOffset(name:String, x:Float = 0, y:Float = 0) - { - animOffsets[name] = [x, y]; - } - - public static function getArrowFromNumber(numb:Int) - { - // yeah no I'm not writing the same shit 4 times over - // take it or leave it my guy - var stringSect:String = ''; - switch (numb) - { - case(0): - stringSect = 'left'; - case(1): - stringSect = 'down'; - case(2): - stringSect = 'up'; - case(3): - stringSect = 'right'; - } - return stringSect; - // - } - - // that last function was so useful I gave it a sequel - public static function getColorFromNumber(numb:Int) - { - var stringSect:String = ''; - switch (numb) - { - case(0): - stringSect = 'purple'; - case(1): - stringSect = 'blue'; - case(2): - stringSect = 'green'; - case(3): - stringSect = 'red'; - } - return stringSect; - // - } -} diff --git a/source/gameFolder/gameObjects/userInterface/menu/Selector.hx b/source/gameFolder/gameObjects/userInterface/menu/Selector.hx index 4a7d1d9e7..df4f20af8 100644 --- a/source/gameFolder/gameObjects/userInterface/menu/Selector.hx +++ b/source/gameFolder/gameObjects/userInterface/menu/Selector.hx @@ -19,13 +19,15 @@ class Selector extends FlxTypedSpriteGroup public var options:Array; public var fpsCap:Bool = false; + public var darkBG:Bool = false; - public function new(x:Float = 0, y:Float = 0, word:String, options:Array, fpsCap:Bool = false) + public function new(x:Float = 0, y:Float = 0, word:String, options:Array, fpsCap:Bool = false, darkBG:Bool = false) { // call back the function super(x, y); this.options = options; + trace(options); // oops magic numbers var shiftX = 48; @@ -33,6 +35,7 @@ class Selector extends FlxTypedSpriteGroup // generate multiple pieces this.fpsCap = fpsCap; + this.darkBG = darkBG; #if html5 // lol heres how we fuck with everyone @@ -50,9 +53,14 @@ class Selector extends FlxTypedSpriteGroup #end chosenOptionString = Init.trueSettings.get(word); - if (fpsCap) + if (fpsCap || darkBG) + { chosenOptionString = Std.string(Init.trueSettings.get(word)); - optionChosen = new Alphabet(FlxG.width / 2 + ((fpsCap) ? 200 : 0), shiftY + 20, chosenOptionString, ((fpsCap) ? false : true), false); + optionChosen = new Alphabet(FlxG.width / 2 + 200, shiftY + 20, chosenOptionString, false, false); + } + else + optionChosen = new Alphabet(FlxG.width / 2, shiftY + 20, chosenOptionString, true, false); + add(optionChosen); } diff --git a/source/gameFolder/gameObjects/Note.hx b/source/gameFolder/gameObjects/userInterface/notes/Note.hx similarity index 96% rename from source/gameFolder/gameObjects/Note.hx rename to source/gameFolder/gameObjects/userInterface/notes/Note.hx index f413c1e9d..6aa25ae2a 100644 --- a/source/gameFolder/gameObjects/Note.hx +++ b/source/gameFolder/gameObjects/userInterface/notes/Note.hx @@ -1,4 +1,4 @@ -package gameFolder.gameObjects; +package gameFolder.gameObjects.userInterface.notes; import flixel.FlxG; import flixel.FlxSprite; @@ -6,7 +6,8 @@ import flixel.graphics.frames.FlxAtlasFrames; import flixel.math.FlxMath; import flixel.tweens.FlxTween; import flixel.util.FlxColor; -import gameFolder.gameObjects.userInterface.UIStaticArrow; +import gameFolder.gameObjects.userInterface.notes.*; +import gameFolder.gameObjects.userInterface.notes.Strumline.UIStaticArrow; import gameFolder.meta.*; import gameFolder.meta.data.*; import gameFolder.meta.data.Section.SwagSection; @@ -81,8 +82,7 @@ class Note extends FNFSprite canBeHit = false; if (tooLate) - if (alpha > 0.3) - alpha -= 0.05; + alpha -= 0.05; } /** @@ -142,7 +142,7 @@ class Note extends FNFSprite newNote.animation.addByPrefix('bluehold', 'blue hold piece'); newNote.setGraphicSize(Std.int(newNote.width * 0.7)); newNote.updateHitbox(); - newNote.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + newNote.antialiasing = true; } // if (!isSustainNote) @@ -243,7 +243,7 @@ class Note extends FNFSprite { newNote.setGraphicSize(Std.int(newNote.width * 0.7)); newNote.updateHitbox(); - newNote.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + newNote.antialiasing = true; } } diff --git a/source/gameFolder/gameObjects/userInterface/notes/NoteSplash.hx b/source/gameFolder/gameObjects/userInterface/notes/NoteSplash.hx new file mode 100644 index 000000000..1162aac60 --- /dev/null +++ b/source/gameFolder/gameObjects/userInterface/notes/NoteSplash.hx @@ -0,0 +1,39 @@ +package gameFolder.gameObjects.userInterface.notes; + +import gameFolder.meta.data.dependency.FNFSprite; + +/** + Create the note splashes in week 7 whenever you get a sick! +**/ +class NoteSplash extends FNFSprite +{ + public function new(noteData:Int) + { + super(x, y); + visible = false; + alpha = 0.6; + } + + override function update(elapsed:Float) + { + super.update(elapsed); + + // kill the note splash if it's done + if (animation.finished) + { + // set the splash to invisible + if (visible) + visible = false; + } + // + } + + override public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0) + { + // make sure the animation is visible + if (!Init.trueSettings.get('Disable Note Splashes')) + visible = true; + + super.playAnim(AnimName, Force, Reversed, Frame); + } +} diff --git a/source/gameFolder/gameObjects/userInterface/notes/Strumline.hx b/source/gameFolder/gameObjects/userInterface/notes/Strumline.hx new file mode 100644 index 000000000..a718d10ec --- /dev/null +++ b/source/gameFolder/gameObjects/userInterface/notes/Strumline.hx @@ -0,0 +1,197 @@ +package gameFolder.gameObjects.userInterface.notes; + +import flixel.FlxBasic; +import flixel.FlxG; +import flixel.FlxSprite; +import flixel.FlxState; +import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.math.FlxMath; +import flixel.math.FlxRect; +import flixel.tweens.FlxEase; +import flixel.tweens.FlxTween; +import flixel.util.FlxSort; +import gameFolder.meta.data.Conductor; +import gameFolder.meta.data.Timings; +import gameFolder.meta.state.PlayState; + +using StringTools; + +/* + import flixel.FlxG; + + import flixel.animation.FlxBaseAnimation; + import flixel.graphics.frames.FlxAtlasFrames; + import flixel.tweens.FlxEase; + import flixel.tweens.FlxTween; + */ +class UIStaticArrow extends FlxSprite +{ + /* Oh hey, just gonna port this code from the previous Skater engine + (depending on the release of this you might not have it cus I might rewrite skater to use this engine instead) + It's basically just code from the game itself but + it's in a separate class and I also added the ability to set offsets for the arrows. + + uh hey you're cute ;) + */ + public var animOffsets:Map>; + public var babyArrowType:Int = 0; + public var canFinishAnimation:Bool = true; + + public var initialX:Int; + public var initialY:Int; + + public var xTo:Float; + public var yTo:Float; + public var angleTo:Float; + + public var setAlpha:Float = (Init.trueSettings.get('Opaque Arrows')) ? 1 : 0.8; + + public function new(x:Float, y:Float, ?babyArrowType:Int = 0) + { + // this extension is just going to rely a lot on preexisting code as I wanna try to write an extension before I do options and stuff + super(x, y); + animOffsets = new Map>(); + + this.babyArrowType = babyArrowType; + + updateHitbox(); + scrollFactor.set(); + } + + // literally just character code + public function playAnim(AnimName:String, Force:Bool = false, Reversed:Bool = false, Frame:Int = 0):Void + { + if (AnimName == 'confirm') + alpha = 1; + else + alpha = setAlpha; + + animation.play(AnimName, Force, Reversed, Frame); + updateHitbox(); + + var daOffset = animOffsets.get(AnimName); + if (animOffsets.exists(AnimName)) + { + offset.set(daOffset[0], daOffset[1]); + } + else + offset.set(0, 0); + } + + public function addOffset(name:String, x:Float = 0, y:Float = 0) + { + animOffsets[name] = [x, y]; + } + + public static function getArrowFromNumber(numb:Int) + { + // yeah no I'm not writing the same shit 4 times over + // take it or leave it my guy + var stringSect:String = ''; + switch (numb) + { + case(0): + stringSect = 'left'; + case(1): + stringSect = 'down'; + case(2): + stringSect = 'up'; + case(3): + stringSect = 'right'; + } + return stringSect; + // + } + + // that last function was so useful I gave it a sequel + public static function getColorFromNumber(numb:Int) + { + var stringSect:String = ''; + switch (numb) + { + case(0): + stringSect = 'purple'; + case(1): + stringSect = 'blue'; + case(2): + stringSect = 'green'; + case(3): + stringSect = 'red'; + } + return stringSect; + // + } +} + +class Strumline extends FlxTypedGroup +{ + // + public var receptors:FlxTypedGroup; + public var splashNotes:FlxTypedGroup; + public var notesGroup:FlxTypedGroup; + + public var autoplay:Bool = true; + public var character:Character; + public var playState:PlayState; + public var displayJudgements:Bool = false; + + public function new(x:Float = 0, playState:PlayState, ?character:Character, ?displayJudgements:Bool = true, ?autoplay:Bool = true, + ?noteSplashes:Bool = false, ?keyAmount:Int = 4, ?downscroll:Bool = false, ?parent:Strumline) + { + super(); + + receptors = new FlxTypedGroup(); + splashNotes = new FlxTypedGroup(); + notesGroup = new FlxTypedGroup(); + + this.autoplay = autoplay; + this.character = character; + this.playState = playState; + this.displayJudgements = displayJudgements; + + for (i in 0...keyAmount) + { + var staticArrow:UIStaticArrow = ForeverAssets.generateUIArrows(-25 + x, 25 + (downscroll ? FlxG.height - 200 : 0), i, PlayState.assetModifier); + staticArrow.ID = i; + + staticArrow.x -= ((keyAmount / 2) * Note.swagWidth); + staticArrow.x += (Note.swagWidth * i); + receptors.add(staticArrow); + + staticArrow.initialX = Math.floor(staticArrow.x); + staticArrow.initialY = Math.floor(staticArrow.y); + staticArrow.angleTo = 0; + staticArrow.y -= 10; + staticArrow.playAnim('static'); + + staticArrow.alpha = 0; + FlxTween.tween(staticArrow, {y: staticArrow.initialY, alpha: staticArrow.setAlpha}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)}); + + if (noteSplashes) + { + var noteSplash:NoteSplash = ForeverAssets.generateNoteSplashes('noteSplashes', PlayState.assetModifier, 'UI', i); + splashNotes.add(noteSplash); + } + } + + add(receptors); + add(notesGroup); + if (splashNotes != null) + add(splashNotes); + } + + public function createSplash(coolNote:Note) + { + // play animation in existing notesplashes + var noteSplashRandom:String = (Std.string((FlxG.random.int(0, 1) + 1))); + splashNotes.members[coolNote.noteData].playAnim('anim' + noteSplashRandom); + } + + public function push(newNote:Note) + { + // + notesGroup.add(newNote); + // thanks sammu I have no idea how this line works lmao + notesGroup.sort(FlxSort.byY, (!Init.trueSettings.get('Downscroll')) ? FlxSort.DESCENDING : FlxSort.ASCENDING); + } +} diff --git a/source/gameFolder/meta/InfoHud.hx b/source/gameFolder/meta/InfoHud.hx index 04cda261c..1e90c9dbb 100644 --- a/source/gameFolder/meta/InfoHud.hx +++ b/source/gameFolder/meta/InfoHud.hx @@ -74,23 +74,25 @@ class InfoHud extends TextField times.shift(); } - var currentCount = times.length; - currentFPS = Math.round((currentCount + cacheCount) / 2); - memoryUsage = Math.round(System.totalMemory / (1e+6)); // division to convey the memory usage in megabytes // according to google, if this is wrong blame google lmao // pretty sure that was to avoid optimisation issues and shit but like I dunno man I'm not writing an if statement that updates all of these at once // if (currentCount != cacheCount && display) text = ""; if (displayFps) + { + currentFPS = Math.round((times.length + cacheCount) / 2); text += "FPS: " + currentFPS + "\n"; + cacheCount = times.length; + } if (displayExtra) text += "State: " + Main.mainClassState + "\n"; if (displayMemory) + { + memoryUsage = Math.round(System.totalMemory / (1e+6)); // division to convey the memory usage in megabytes text += "Memory: " + memoryUsage + " mb"; - // mb stands for my bad - - cacheCount = currentCount; + // mb stands for my bad + } } // be able to call framerates later on diff --git a/source/gameFolder/meta/MusicBeat.hx b/source/gameFolder/meta/MusicBeat.hx index ab7bc18f2..1fcc2d8ad 100644 --- a/source/gameFolder/meta/MusicBeat.hx +++ b/source/gameFolder/meta/MusicBeat.hx @@ -23,8 +23,8 @@ class MusicBeatState extends FNFUIState private var lastBeat:Float = 0; private var lastStep:Float = 0; - private var curStep:Int = 0; - private var curBeat:Int = 0; + public var curStep:Int = 0; + public var curBeat:Int = 0; private var controls(get, never):Controls; diff --git a/source/gameFolder/meta/data/ChartLoader.hx b/source/gameFolder/meta/data/ChartLoader.hx index 4453c5cb2..3e16d3f15 100644 --- a/source/gameFolder/meta/data/ChartLoader.hx +++ b/source/gameFolder/meta/data/ChartLoader.hx @@ -5,7 +5,7 @@ import flixel.FlxSprite; import flixel.math.FlxMath; import flixel.text.FlxText; import flixel.util.FlxColor; -import gameFolder.gameObjects.Note; +import gameFolder.gameObjects.userInterface.notes.*; import gameFolder.meta.data.Section.SwagSection; import gameFolder.meta.data.Song.SwagSong; import gameFolder.meta.state.PlayState; @@ -19,14 +19,10 @@ import gameFolder.meta.state.charting.ChartingState; **/ class ChartLoader { - // set up some variables maybe and then public static functions that can be used anywhere - public static var unspawnNotes:Array = []; - // hopefully this makes it easier for people to load and save chart features and such, y'know the deal lol - public static function generateChartType(?typeOfChart:String = "FNF") + public static function generateChartType(songData:SwagSong, ?typeOfChart:String = "FNF"):Array { - // - var songData = PlayState.SONG; + var unspawnNotes:Array = []; var noteData:Array; noteData = songData.notes; @@ -125,11 +121,7 @@ class ChartLoader songs made in forever engine with the base game then you can do that too. */ } - } - public static function returnUnspawnNotes() return unspawnNotes; - - public static function flushUnspawnNotes() - unspawnNotes = []; + } } diff --git a/source/gameFolder/meta/data/Conductor.hx b/source/gameFolder/meta/data/Conductor.hx index 3df8a2a12..9da1f94d7 100644 --- a/source/gameFolder/meta/data/Conductor.hx +++ b/source/gameFolder/meta/data/Conductor.hx @@ -17,12 +17,12 @@ typedef BPMChangeEvent = { var stepTime:Int; var songTime:Float; - var bpm:Int; + var bpm:Float; } class Conductor { - public static var bpm:Int = 100; + public static var bpm:Float = 100; public static var crochet:Float = ((60 / bpm) * 1000); // beats in milliseconds public static var stepCrochet:Float = crochet / 4; // steps in milliseconds @@ -37,13 +37,16 @@ class Conductor */ public static var bpmChangeMap:Array = []; - public function new() {} + public function new() + { + // + } public static function mapBPMChanges(song:SwagSong) { bpmChangeMap = []; - var curBPM:Int = song.bpm; + var curBPM:Float = song.bpm; var totalSteps:Int = 0; var totalPos:Float = 0; for (i in 0...song.notes.length) @@ -66,11 +69,11 @@ class Conductor // trace("new BPM map BUDDY " + bpmChangeMap); } - public static function changeBPM(newBpm:Int) + public static function changeBPM(newBpm:Float, measure:Float = 4 / 4) { bpm = newBpm; crochet = ((60 / bpm) * 1000); - stepCrochet = crochet / 4; + stepCrochet = (crochet / 4) * measure; } } diff --git a/source/gameFolder/meta/data/Section.hx b/source/gameFolder/meta/data/Section.hx index 57a79e5b0..cdf26a164 100644 --- a/source/gameFolder/meta/data/Section.hx +++ b/source/gameFolder/meta/data/Section.hx @@ -6,7 +6,7 @@ typedef SwagSection = var lengthInSteps:Int; var typeOfSection:Int; var mustHitSection:Bool; - var bpm:Int; + var bpm:Float; var changeBPM:Bool; var altAnim:Bool; } diff --git a/source/gameFolder/meta/data/Song.hx b/source/gameFolder/meta/data/Song.hx index a64547953..1755802a8 100644 --- a/source/gameFolder/meta/data/Song.hx +++ b/source/gameFolder/meta/data/Song.hx @@ -12,7 +12,7 @@ typedef SwagSong = { var song:String; var notes:Array; - var bpm:Int; + var bpm:Float; var needsVoices:Bool; var speed:Float; @@ -27,7 +27,7 @@ class Song { public var song:String; public var notes:Array; - public var bpm:Int; + public var bpm:Float; public var needsVoices:Bool = true; public var speed:Float = 1; diff --git a/source/gameFolder/meta/data/Timings.hx b/source/gameFolder/meta/data/Timings.hx index 92dcaf04b..b08dd5741 100644 --- a/source/gameFolder/meta/data/Timings.hx +++ b/source/gameFolder/meta/data/Timings.hx @@ -1,10 +1,10 @@ package gameFolder.meta.data; -import gameFolder.gameObjects.Note; +import gameFolder.gameObjects.userInterface.notes.*; import gameFolder.meta.state.PlayState; /** - Here's a class that calculates timings and ratings for the songs and such + Here's a class that calculates timings and judgements for the songs and such **/ class Timings { @@ -15,17 +15,17 @@ class Timings // from left to right // max milliseconds, score from it and percentage - public static var ratingsMap:Map> = [ - "sick" => [45, 350, 100], - "good" => [100, 150, 50], - "bad" => [120, 0, 15], - "shit" => [140, -20, -75], - "miss" => [180, -50, -100], + public static var judgementsMap:Map> = [ + "sick" => [0, 45, 350, 100], + "good" => [1, 100, 150, 40], + "bad" => [2, 120, 0, 5], + "shit" => [3, 140, -50, -100], + "miss" => [4, 180, -100, -150], ]; public static var msThreshold:Float = 0; - // set the score ratings for later use + // set the score judgements for later use public static var scoreRating:Map = ["s" => 90, "a" => 80, "b" => 70, "c" => 50, "d" => 40, "e" => 20, "f" => 0,]; public static var ratingFinal:String = "f"; @@ -43,9 +43,9 @@ class Timings // reset ms threshold var biggestThreshold:Float = 0; - for (i in ratingsMap.keys()) - if (ratingsMap.get(i)[0] > biggestThreshold) - biggestThreshold = ratingsMap.get(i)[0]; + for (i in judgementsMap.keys()) + if (judgementsMap.get(i)[1] > biggestThreshold) + biggestThreshold = judgementsMap.get(i)[1]; msThreshold = biggestThreshold; notesHit = 0; diff --git a/source/gameFolder/meta/data/font/Alphabet.hx b/source/gameFolder/meta/data/font/Alphabet.hx index 3d8f6fe42..d81d5964a 100644 --- a/source/gameFolder/meta/data/font/Alphabet.hx +++ b/source/gameFolder/meta/data/font/Alphabet.hx @@ -336,7 +336,7 @@ class AlphaCharacter extends FlxSprite var tex = Paths.getSparrowAtlas('UI/default/base/alphabet'); frames = tex; - antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + antialiasing = true; } public function createBold(letter:String) diff --git a/source/gameFolder/meta/state/CustomTitlescreen.hx b/source/gameFolder/meta/state/CustomTitlescreen.hx index d3f9f52a3..dccdd53ab 100644 --- a/source/gameFolder/meta/state/CustomTitlescreen.hx +++ b/source/gameFolder/meta/state/CustomTitlescreen.hx @@ -90,14 +90,14 @@ class CustomTitlescreen extends MusicBeatState persistentUpdate = true; var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); - // bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + // bg.antialiasing = true; // bg.setGraphicSize(Std.int(bg.width * 0.6)); // bg.updateHitbox(); add(bg); logoBl = new FlxSprite(); logoBl.loadGraphic(Paths.image('menus/base/title/FELogo')); - logoBl.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + logoBl.antialiasing = true; logoBl.setGraphicSize(Std.int(logoBl.width / 2)); logoBl.updateHitbox(); @@ -115,7 +115,7 @@ class CustomTitlescreen extends MusicBeatState titleText.frames = Paths.getSparrowAtlas('menus/base/title/titleEnter'); titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24); titleText.animation.addByPrefix('press', "ENTER PRESSED", 24); - titleText.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + titleText.antialiasing = true; titleText.animation.play('idle'); titleText.updateHitbox(); // titleText.screenCenter(X); diff --git a/source/gameFolder/meta/state/PlayState.hx b/source/gameFolder/meta/state/PlayState.hx index 4548de15b..45bd00057 100644 --- a/source/gameFolder/meta/state/PlayState.hx +++ b/source/gameFolder/meta/state/PlayState.hx @@ -23,6 +23,8 @@ import flixel.util.FlxSort; import flixel.util.FlxTimer; import gameFolder.gameObjects.*; import gameFolder.gameObjects.userInterface.*; +import gameFolder.gameObjects.userInterface.notes.*; +import gameFolder.gameObjects.userInterface.notes.Strumline.UIStaticArrow; import gameFolder.meta.*; import gameFolder.meta.MusicBeat.MusicBeatState; import gameFolder.meta.data.*; @@ -30,9 +32,12 @@ import gameFolder.meta.data.Song.SwagSong; import gameFolder.meta.state.charting.*; import gameFolder.meta.state.menus.*; import gameFolder.meta.subState.*; +import openfl.display.GraphicsShader; import openfl.events.KeyboardEvent; +import openfl.filters.ShaderFilter; import openfl.media.Sound; import openfl.utils.Assets; +import sys.io.File; using StringTools; @@ -60,22 +65,15 @@ class PlayState extends MusicBeatState public static var gf:Character; public static var boyfriend:Boyfriend; - public var boyfriendAutoplay:Bool = false; - - private var dadAutoplay:Bool = true; // this is for testing purposes - public static var assetModifier:String = 'base'; public static var changeableSkin:String = 'default'; - private var notes:FlxTypedGroup; private var unspawnNotes:Array = []; private var ratingArray:Array = []; private var allSicks:Bool = true; - // control arrays I'll use later - var holdControls:Array = []; - var pressControls:Array = []; - var releaseControls:Array = []; // haha garcello! + // if you ever wanna add more keys + private var numberOfKeys:Int = 4; // get it cus release // I'm funny just trust me @@ -87,27 +85,17 @@ class PlayState extends MusicBeatState public static var detailsSub:String = ""; public static var detailsPausedText:String = ""; - // private static var prevCamFollow:FlxObject; - // strums - private var strumLine:FlxTypedGroup; - - private var strumLineNotes:FlxTypedGroup; - - private var boyfriendStrums:FlxTypedGroup; - private var dadStrums:FlxTypedGroup; - private var curSong:String = ""; - private var splashNotes:FlxTypedGroup; - private var gfSpeed:Int = 1; public static var health:Float = 1; // mario public static var combo:Int = 0; public static var misses:Int = 0; - private var generatedMusic:Bool = false; + public var generatedMusic:Bool = false; + private var startingSong:Bool = false; private var paused:Bool = false; var startedCountdown:Bool = false; @@ -120,8 +108,8 @@ class PlayState extends MusicBeatState public static var camHUD:FlxCamera; public static var camGame:FlxCamera; - private var camDisplaceX:Float = 0; - private var camDisplaceY:Float = 0; // might not use depending on result + public var camDisplaceX:Float = 0; + public var camDisplaceY:Float = 0; // might not use depending on result public static var defaultCamZoom:Float = 1.05; @@ -142,9 +130,14 @@ class PlayState extends MusicBeatState public static var daPixelZoom:Float = 6; public static var determinedChartType:String = ""; - private var ratingsGroup:FlxTypedGroup; - private var timingsGroup:FlxTypedGroup; - private var scoreGroup:FlxTypedGroup; + // strumlines + private var dadStrums:Strumline; + private var boyfriendStrums:Strumline; + + public static var strumLines:FlxTypedGroup; + public static var strumHUD:Array = []; + + private var allUIs:Array = []; // at the beginning of the playstate override public function create() @@ -165,11 +158,6 @@ class PlayState extends MusicBeatState assetModifier = 'base'; changeableSkin = 'default'; - // initialise the groups! - ratingsGroup = new FlxTypedGroup(); - timingsGroup = new FlxTypedGroup(); - scoreGroup = new FlxTypedGroup(); - // stop any existing music tracks playing resetMusic(); if (FlxG.sound.music != null) @@ -184,6 +172,7 @@ class PlayState extends MusicBeatState FlxG.cameras.reset(camGame); FlxG.cameras.add(camHUD); + allUIs.push(camHUD); FlxCamera.defaultCameras = [camGame]; // default song @@ -205,6 +194,10 @@ class PlayState extends MusicBeatState if (SONG.stage != null) curStage = SONG.stage; + // cache ratings LOL + displayRating('sick', 'early', true); + popUpCombo(true); + stageBuild = new Stage(curStage); add(stageBuild); @@ -262,33 +255,14 @@ class PlayState extends MusicBeatState // set song position before beginning Conductor.songPosition = -(Conductor.crochet * 4); - // create strums and ui elements - strumLine = new FlxTypedGroup(); - var strumLineY:Int = 50; - - if (Init.trueSettings.get('Downscroll')) - strumLineY = FlxG.height - (strumLineY * 3); - // trace('downscroll works???'); - - for (i in 0...8) - { - var strumLinePart = new FlxSprite(0, strumLineY).makeGraphic(FlxG.width, 10); - strumLinePart.scrollFactor.set(); - - strumLine.add(strumLinePart); - } - - // set up the elements for the notes - strumLineNotes = new FlxTypedGroup(); - add(strumLineNotes); - - // now splash notes - splashNotes = new FlxTypedGroup(); - add(splashNotes); + // darken everything but the arrows and ui via a flxsprite + var darknessBG:FlxSprite = new FlxSprite(FlxG.width * -0.5, FlxG.height * -0.5).makeGraphic(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK); + darknessBG.alpha = Init.trueSettings.get('Stage Darkness') * 0.01; + darknessBG.scrollFactor.set(0, 0); + add(darknessBG); - // and now the note strums - boyfriendStrums = new FlxTypedGroup(); - dadStrums = new FlxTypedGroup(); + // strum setup + strumLines = new FlxTypedGroup(); // generate the song generateSong(SONG.song); @@ -299,7 +273,7 @@ class PlayState extends MusicBeatState // create the game camera camFollow = new FlxObject(0, 0, 1, 1); camFollow.setPosition(camPos.x, camPos.y); - // check if the camera was following someone previouslyw + // check if the camera was following someone previously if (prevCamFollow != null) { camFollow = prevCamFollow; @@ -308,11 +282,6 @@ class PlayState extends MusicBeatState add(camFollow); - // set up camera dependencies (so that ui elements correspond to their cameras and such) - strumLineNotes.cameras = [camHUD]; - splashNotes.cameras = [camHUD]; - notes.cameras = [camHUD]; - // actually set the camera up var camLerp = Main.framerateAdjust(0.04); FlxG.camera.follow(camFollow, LOCKON, camLerp); @@ -325,12 +294,31 @@ class PlayState extends MusicBeatState startingSong = true; startedCountdown = true; - for (i in 0...2) - if (i != 0 || !Init.trueSettings.get('Centered Notefield')) - generateStaticArrows(i); - - if (Init.trueSettings.get('Centered Notefield')) - staticDisplace = 4; + // + var placement = (FlxG.width / 2); + dadStrums = new Strumline(placement - (FlxG.width / 4), this, dadOpponent, false, true, false, 4, Init.trueSettings.get('Downscroll')); + dadStrums.visible = !Init.trueSettings.get('Centered Notefield'); + boyfriendStrums = new Strumline(placement + (!Init.trueSettings.get('Centered Notefield') ? (FlxG.width / 4) : 0), this, boyfriend, true, false, true, + 4, Init.trueSettings.get('Downscroll')); + + strumLines.add(dadStrums); + strumLines.add(boyfriendStrums); + + // strumline camera setup + strumHUD = []; + for (i in 0...strumLines.length) + { + // generate a new strum camera + strumHUD[i] = new FlxCamera(); + strumHUD[i].bgColor.alpha = 0; + + strumHUD[i].cameras = [camHUD]; + allUIs.push(strumHUD[i]); + FlxG.cameras.add(strumHUD[i]); + // set this strumline's camera to the designated camera + strumLines.members[i].cameras = [strumHUD[i]]; + } + add(strumLines); uiHUD = new ClassHUD(); add(uiHUD); @@ -343,6 +331,37 @@ class PlayState extends MusicBeatState songIntroCutscene(); else startCountdown(); + + /** + * SHADERS + * + * This is a highly experimental code by gedehari to support runtime shader parsing. + * Usually, to add a shader, you would make it a class, but now, I modified it so + * you can parse it from a file. + * + * This feature is planned to be used for modcharts + * (at this time of writing, it's not available yet). + * + * This example below shows that you can apply shaders as a FlxCamera filter. + * the GraphicsShader class accepts two arguments, one is for vertex shader, and + * the second is for fragment shader. + * Pass in an empty string to use the default vertex/fragment shader. + * + * Next, the Shader is passed to a new instance of ShaderFilter, neccesary to make + * the filter work. And that's it! + * + * To access shader uniforms, just reference the `data` property of the GraphicsShader + * instance. + * + * Thank you for reading! -gedehari + */ + + // Uncomment the code below to apply the effect + + /* + var shader:GraphicsShader = new GraphicsShader("", File.getContent("./assets/shaders/vhs.frag")); + FlxG.camera.setFilters([new ShaderFilter(shader)]); + */ } var staticDisplace:Int = 0; @@ -383,9 +402,6 @@ class PlayState extends MusicBeatState else Main.switchState(this, new OriginalChartingState()); } - - if (FlxG.keys.justPressed.SIX) - boyfriendAutoplay = !boyfriendAutoplay; } ///* @@ -491,24 +507,13 @@ class PlayState extends MusicBeatState var easeLerp = 0.95; // camera stuffs FlxG.camera.zoom = FlxMath.lerp(defaultCamZoom + forceZoom[0], FlxG.camera.zoom, easeLerp); - camHUD.zoom = FlxMath.lerp(1 + forceZoom[1], camHUD.zoom, easeLerp); + for (hud in allUIs) + hud.zoom = FlxMath.lerp(1 + forceZoom[1], hud.zoom, easeLerp); // not even forcezoom anymore but still FlxG.camera.angle = FlxMath.lerp(0 + forceZoom[2], FlxG.camera.angle, easeLerp); - camHUD.angle = FlxMath.lerp(0 + forceZoom[3], camHUD.angle, easeLerp); - - /* - if ((strumLineNotes != null) && (strumLineNotes.members.length > 0) && (!startingSong)) - { - // fuckin uh strumline note stuffs - for (i in 0...strumLineNotes.members.length) - { - strumLineNotes.members[i].x = FlxMath.lerp(strumLineNotes.members[i].xTo, strumLineNotes.members[i].x, easeLerp); - strumLineNotes.members[i].y = FlxMath.lerp(strumLineNotes.members[i].yTo, strumLineNotes.members[i].y, easeLerp); - - strumLineNotes.members[i].angle = FlxMath.lerp(strumLineNotes.members[i].angleTo, strumLineNotes.members[i].angle, easeLerp); - } - }*/ + for (hud in allUIs) + hud.angle = FlxMath.lerp(0 + forceZoom[3], hud.angle, easeLerp); if (health <= 0 && startedCountdown) { @@ -525,208 +530,19 @@ class PlayState extends MusicBeatState } // spawn in the notes from the array - if (unspawnNotes[0] != null) - { - if ((unspawnNotes[0].strumTime - Conductor.songPosition) < 3500) - { - var dunceNote:Note = unspawnNotes[0]; - if (!dunceNote.mustPress && Init.trueSettings.get('Centered Notefield')) - animationsPlay.push(dunceNote); - else - notes.add(dunceNote); - - unspawnNotes.splice(unspawnNotes.indexOf(dunceNote), 1); - - // thanks sammu I have no idea how this line works lmao - notes.sort(FlxSort.byY, (!Init.trueSettings.get('Downscroll')) ? FlxSort.DESCENDING : FlxSort.ASCENDING); - } - } - - if (animationsPlay[0] != null && (animationsPlay[0].strumTime - Conductor.songPosition) <= 0) + if ((unspawnNotes[0] != null) && ((unspawnNotes[0].strumTime - Conductor.songPosition) < 3500)) { - goodNoteHit(animationsPlay[0], dadOpponent, dadStrums, false); - animationsPlay.splice(animationsPlay.indexOf(animationsPlay[0]), 1); + var dunceNote:Note = unspawnNotes[0]; + // push note to its correct strumline + strumLines.members[Math.floor((dunceNote.noteData + (dunceNote.mustPress ? 4 : 0)) / numberOfKeys)].push(dunceNote); + unspawnNotes.splice(unspawnNotes.indexOf(dunceNote), 1); } - // handle all of the note calls noteCalls(); } - override public function onFocus():Void - { - if (!paused) - updateRPC(false); - super.onFocus(); - } - - override public function onFocusLost():Void - { - updateRPC(true); - super.onFocusLost(); - } - - public static function updateRPC(pausedRPC:Bool) - { - #if !html5 - var displayRPC:String = (pausedRPC) ? detailsPausedText : songDetails; - - if (health > 0) - { - if (Conductor.songPosition > 0 && !pausedRPC) - Discord.changePresence(displayRPC, detailsSub, iconRPC, true, songLength - Conductor.songPosition); - else - Discord.changePresence(displayRPC, detailsSub, iconRPC); - } - #end - } - - var animationsPlay:Array = []; - - private function mainControls(daNote:Note, char:Character, charStrum:FlxTypedGroup, autoplay:Bool, ?otherSide:Int = 0):Void - { - // call character type for later I'm so sorry this is painful - var charCallType:Int = 0; - if (char == boyfriend) - charCallType = 1; - - // uh if condition from the original game - - // I have no idea what I have done - var downscrollMultiplier = 1; - if (Init.trueSettings.get('Downscroll')) - downscrollMultiplier = -1; - - // im very sorry for this if condition I made it worse lmao - ///* - if (daNote.isSustainNote - && (((daNote.y + daNote.offset.y <= (strumLine.members[Math.floor(daNote.noteData + (otherSide * 4))].y + Note.swagWidth / 2)) - && !Init.trueSettings.get('Downscroll')) - || (((daNote.y - (daNote.offset.y * daNote.scale.y) + daNote.height) >= (strumLine.members[Math.floor(daNote.noteData + (otherSide * 4))].y - + Note.swagWidth / 2)) - && Init.trueSettings.get('Downscroll'))) - && (autoplay || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit)))) - { - var swagRectY = ((strumLine.members[Math.floor(daNote.noteData + (otherSide * 4))].y + Note.swagWidth / 2 - daNote.y) / daNote.scale.y); - var swagRect = new FlxRect(0, 0, daNote.width * 2, daNote.height * 2); - // I feel genuine pain - // basically these should be flipped based on if it is downscroll or not - if (Init.trueSettings.get('Downscroll')) - { - swagRect.height = swagRectY; - swagRect.y -= swagRect.height - daNote.height; - } - else - { - swagRect.y = swagRectY; - swagRect.height -= swagRect.y; - } - - daNote.clipRect = swagRect; - } - // */ - - // here I'll set up the autoplay functions - if (autoplay) - { - // check if the note was a good hit - if (daNote.strumTime <= Conductor.songPosition) - { - // use a switch thing cus it feels right idk lol - // make sure the strum is played for the autoplay stuffs - /* - charStrum.forEach(function(cStrum:UIStaticArrow) - { - strumCallsAuto(cStrum, 0, daNote); - }); - */ - - // kill the note, then remove it from the array - var canDisplayRating = false; - if (charCallType == 1) - { - canDisplayRating = true; - for (noteDouble in notesPressedAutoplay) - { - if (noteDouble.noteData == daNote.noteData) - { - // if (Math.abs(noteDouble.strumTime - daNote.strumTime) < 10) - canDisplayRating = false; - // removing the fucking check apparently fixes it - // god damn it that stupid glitch with the double ratings is annoying - } - // - } - notesPressedAutoplay.push(daNote); - } - - goodNoteHit(daNote, char, charStrum, canDisplayRating); - } - // - } - - // unoptimised asf camera control based on strums - switch (charCallType) - { - case 1: - strumCameraRoll(boyfriendStrums, true); - default: - strumCameraRoll(dadStrums, false); - } - } - - private function strumCallsAuto(cStrum:UIStaticArrow, ?callType:Int = 1, ?daNote:Note):Void - { - switch (callType) - { - case 1: - // end the animation if the calltype is 1 and it is done - if ((cStrum.animation.finished) && (cStrum.canFinishAnimation)) - cStrum.playAnim('static'); - default: - // check if it is the correct strum - if (daNote.noteData == cStrum.ID) - { - // if (cStrum.animation.curAnim.name != 'confirm') - cStrum.playAnim('confirm'); // play the correct strum's confirmation animation (haha rhymes) - - // stuff for sustain notes - if ((daNote.isSustainNote) && (!daNote.animation.curAnim.name.endsWith('holdend'))) - cStrum.canFinishAnimation = false; // basically, make it so the animation can't be finished if there's a sustain note below - else - cStrum.canFinishAnimation = true; - } - } - } - - private function strumCameraRoll(cStrum:FlxTypedGroup, mustHit:Bool) - { - if (!Init.trueSettings.get('No Camera Note Movement')) - { - var camDisplaceExtend:Float = 1.5; - var camDisplaceSpeed = 0.0125; - if (PlayState.SONG.notes[Std.int(curStep / 16)] != null) - { - if ((PlayState.SONG.notes[Std.int(curStep / 16)].mustHitSection && mustHit) - || (!PlayState.SONG.notes[Std.int(curStep / 16)].mustHitSection && !mustHit)) - { - if ((cStrum.members[0].animation.curAnim.name == 'confirm') && (camDisplaceX > -camDisplaceExtend)) - camDisplaceX -= camDisplaceSpeed; - else if ((cStrum.members[3].animation.curAnim.name == 'confirm') && (camDisplaceX < camDisplaceExtend)) - camDisplaceX += camDisplaceSpeed; - } - } - } - // - } - - // call a note array - public var notesPressedAutoplay:Array = []; - - private function noteCalls():Void + function noteCalls() { - // get ready for nested script calls! - - // set up the controls for later usage // (control stuffs don't go here they go in noteControls(), I just have them here so I don't call them every. single. time. noteControls() is called) var up = controls.UP; var right = controls.RIGHT; @@ -747,149 +563,115 @@ class PlayState extends MusicBeatState var pressControls = [leftP, downP, upP, rightP]; var releaseControls = [leftR, downR, upR, rightR]; - // handle strumline stuffs - for (i in 0...strumLine.length) - if (strumLineNotes.members[i] != null) - strumLine.members[i].y = strumLineNotes.members[i].y + 25; - - for (i in 0...splashNotes.length) - { - // splash note positions - splashNotes.members[i].x = strumLineNotes.members[i + 4 - staticDisplace].x - 48; - splashNotes.members[i].y = strumLineNotes.members[i + 4 - staticDisplace].y - 56; - } - // reset strums - for (i in 0...4) + for (strumline in strumLines) { - boyfriendStrums.forEach(function(cStrum:UIStaticArrow) + // handle strumline stuffs + var i = 0; + for (uiNote in strumline.receptors) { - if (boyfriendAutoplay) - strumCallsAuto(cStrum); - }); - dadStrums.forEach(function(cStrum:UIStaticArrow) - { - if (dadAutoplay) - strumCallsAuto(cStrum); - }); + if (strumline.autoplay) + strumCallsAuto(uiNote); + } + + if (strumline.splashNotes != null) + for (i in 0...strumline.splashNotes.length) + { + strumline.splashNotes.members[i].x = strumline.receptors.members[i].x - 48; + strumline.splashNotes.members[i].y = strumline.receptors.members[i].y - 56; + } } // if the song is generated if (generatedMusic && startedCountdown) { - // nested script #1 - controlPlayer(boyfriend, boyfriendAutoplay, boyfriendStrums, holdControls, pressControls, releaseControls); - // controlPlayer(dadOpponent, dadAutoplay, dadStrums, holdControls, pressControls, releaseControls, false); - - notesPressedAutoplay = []; - // call every single note that exists! - notes.forEachAlive(function(daNote:Note) + for (strumline in strumLines) { - // ya so this might be a lil unoptimised so I'm gonna keep it to a minimum with the calls honestly I'd rather not do them a lot + if (!strumline.autoplay) + controlPlayer(strumline.character, strumline.autoplay, strumline, holdControls, pressControls, releaseControls); - // first we wanna orient the note positions. - // lord forgive me for what I'm about to do but I can't use booleans as integers - - // don't follow this it's hellaaaa stupid code - var otherSide = 0; - if (daNote.mustPress) - otherSide = 1; - var noteSkin:String = Init.trueSettings.get("Note Skin"); - - // set the notes x and y - var downscrollMultiplier = 1; - if (Init.trueSettings.get('Downscroll')) - downscrollMultiplier = -1; - - var psuedoY:Float = (downscrollMultiplier * - -((Conductor.songPosition - daNote.strumTime) * (0.45 * FlxMath.roundDecimal(daNote.noteSpeed, 2)))); - /* - heres the part where I talk about how shitty my downscroll code is - mostly because I don't actually understand downscroll and I don't play downscroll so its really more - of an afterthought, if you feel like improving the code lemme know or make a pr or something I'll gladly accept it + strumline.notesGroup.forEachAlive(function(daNote:Note) + { + // set the notes x and y + var downscrollMultiplier = 1; + if (Init.trueSettings.get('Downscroll')) + downscrollMultiplier = -1; - EDIT: I'm gonna try to revise it but no promises - ya I give up if you wanna fix it go ahead idc anymore - UPDATE: I MIGHT HAVE FIXED IT!!!! - */ + var psuedoY:Float = (downscrollMultiplier * + -((Conductor.songPosition - daNote.strumTime) * (0.45 * FlxMath.roundDecimal(daNote.noteSpeed, 2)))); - var psuedoX = 25 + daNote.noteVisualOffset; + var psuedoX = 25 + daNote.noteVisualOffset; - daNote.y = strumLine.members[Math.floor(daNote.noteData + (otherSide * 4 - staticDisplace))].y - + (Math.cos(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoY) - + (Math.sin(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoX); - // painful math equation - daNote.x = strumLineNotes.members[Math.floor(daNote.noteData + (otherSide * 4 - staticDisplace))].x - + (Math.cos(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoX) - + (Math.sin(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoY); + daNote.y = strumline.receptors.members[Math.floor(daNote.noteData)].y + + (Math.cos(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoY) + + (Math.sin(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoX); + // painful math equation + daNote.x = strumline.receptors.members[Math.floor(daNote.noteData)].x + + (Math.cos(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoX) + + (Math.sin(flixel.math.FlxAngle.asRadians(daNote.noteDirection)) * psuedoY); - if (daNote.isSustainNote) - { - // note alignments (thanks pixl for pointing out what made old downscroll weird) - if ((daNote.animation.curAnim.name.endsWith('holdend')) && (daNote.prevNote != null)) + if (daNote.isSustainNote) { - if (Init.trueSettings.get('Downscroll')) - daNote.y += (daNote.prevNote.height); + // note alignments (thanks pixl for pointing out what made old downscroll weird) + if ((daNote.animation.curAnim.name.endsWith('holdend')) && (daNote.prevNote != null)) + { + if (Init.trueSettings.get('Downscroll')) + daNote.y += (daNote.prevNote.height); + else + daNote.y -= ((daNote.prevNote.height / 2)); + } else - daNote.y -= ((daNote.prevNote.height / 2)); + daNote.y -= ((daNote.height / 2) * downscrollMultiplier); + if (Init.trueSettings.get('Downscroll')) + daNote.flipY = true; } - else - daNote.y -= ((daNote.height / 2) * downscrollMultiplier); - if (Init.trueSettings.get('Downscroll')) - daNote.flipY = true; - } - - // also set note rotation - daNote.angle = -daNote.noteDirection; - // hell breaks loose here, we're using nested scripts! - // get the note lane and run the corresponding script - ///* - if (daNote.mustPress) - mainControls(daNote, boyfriend, boyfriendStrums, boyfriendAutoplay, otherSide); - else - mainControls(daNote, dadOpponent, dadStrums, dadAutoplay); // dadOpponent autoplay is true by default and should be true unless neccessary - // */ + // also set note rotation + daNote.angle = -daNote.noteDirection; - // check where the note is and make sure it is either active or inactive - if (daNote.y > FlxG.height) - { - daNote.active = false; - daNote.visible = false; - } - else - { - daNote.visible = true; - daNote.active = true; - } + // hell breaks loose here, we're using nested scripts! + mainControls(daNote, strumline.character, strumline, strumline.autoplay); - // if the note is off screen (above) - if (((!Init.trueSettings.get('Downscroll')) && (daNote.y < -daNote.height)) - || ((Init.trueSettings.get('Downscroll')) && (daNote.y > (FlxG.height + daNote.height)))) - { - if ((daNote.tooLate || !daNote.wasGoodHit) && (daNote.mustPress)) + // check where the note is and make sure it is either active or inactive + if (daNote.y > FlxG.height) + { + daNote.active = false; + daNote.visible = false; + } + else { - vocals.volume = 0; - missNoteCheck((Init.trueSettings.get('Ghost Tapping')) ? true : false, daNote.noteData, boyfriend, true); - // ambiguous name - Timings.updateAccuracy(0); + daNote.visible = true; + daNote.active = true; } - daNote.active = false; - daNote.visible = false; + // if the note is off screen (above) + if (((!Init.trueSettings.get('Downscroll')) && (daNote.y < -daNote.height)) + || ((Init.trueSettings.get('Downscroll')) && (daNote.y > (FlxG.height + daNote.height)))) + { + if ((daNote.tooLate || !daNote.wasGoodHit) && (daNote.mustPress)) + { + vocals.volume = 0; + missNoteCheck((Init.trueSettings.get('Ghost Tapping')) ? true : false, daNote.noteData, boyfriend, true); + // ambiguous name + Timings.updateAccuracy(0); + } - // note damage here I guess - daNote.kill(); - notes.remove(daNote, true); - daNote.destroy(); - } - }); - // + daNote.active = false; + daNote.visible = false; + + // note damage here I guess + daNote.kill(); + if (strumline.notesGroup.members.contains(daNote)) + strumline.notesGroup.remove(daNote, true); + daNote.destroy(); + } + }); + } } } - function controlPlayer(character:Character, autoplay:Bool, characterStrums:FlxTypedGroup, holdControls:Array, - pressControls:Array, releaseControls:Array, ?mustPress = true) + function controlPlayer(character:Character, autoplay:Bool, characterStrums:Strumline, holdControls:Array, pressControls:Array, + releaseControls:Array) { if (!autoplay) { @@ -903,21 +685,17 @@ class PlayState extends MusicBeatState var possibleNoteList:Array = []; var pressedNotes:Array = []; - notes.forEachAlive(function(daNote:Note) + characterStrums.notesGroup.forEachAlive(function(daNote:Note) { - if ((daNote.noteData == i) && daNote.canBeHit && daNote.mustPress && !daNote.tooLate && !daNote.wasGoodHit) - { + if ((daNote.noteData == i) && daNote.canBeHit && !daNote.tooLate && !daNote.wasGoodHit) possibleNoteList.push(daNote); - possibleNoteList.sort((a, b) -> Std.int(a.strumTime - b.strumTime)); - } }); + possibleNoteList.sort((a, b) -> Std.int(a.strumTime - b.strumTime)); // if there is a list of notes that exists for that control if (possibleNoteList.length > 0) { var eligable = true; - // this may be impractical, but I want overlayed notes to be played, just not count towards score or combo - // this is so that they run code and stuff var firstNote = true; // loop through the possible notes for (coolNote in possibleNoteList) @@ -954,7 +732,7 @@ class PlayState extends MusicBeatState if (holdControls.contains(true)) { // check notes that are alive - notes.forEachAlive(function(coolNote:Note) + characterStrums.notesGroup.forEachAlive(function(coolNote:Note) { if (coolNote.canBeHit && coolNote.mustPress && coolNote.isSustainNote && holdControls[coolNote.noteData]) goodNoteHit(coolNote, character, characterStrums); @@ -964,7 +742,7 @@ class PlayState extends MusicBeatState // control camera movements // strumCameraRoll(characterStrums, true); - characterStrums.forEach(function(strum:UIStaticArrow) + characterStrums.receptors.forEach(function(strum:UIStaticArrow) { if ((pressControls[strum.ID]) && (strum.animation.curAnim.name != 'confirm')) strum.playAnim('pressed'); @@ -983,32 +761,302 @@ class PlayState extends MusicBeatState } } - private var ratingTiming:String = ""; - - function popUpScore(baseRating:String, coolNote:Note) + function goodNoteHit(coolNote:Note, character:Character, characterStrums:Strumline, ?canDisplayJudgement:Bool = true) { - // set up the rating - var score:Int = 50; + if (!coolNote.wasGoodHit) + { + coolNote.wasGoodHit = true; + vocals.volume = 1; - // notesplashes - if (baseRating == "sick") // create the note splash if you hit a sick - createSplash(coolNote); + characterPlayAnimation(coolNote, character); + if (characterStrums.receptors.members[coolNote.noteData] != null) + characterStrums.receptors.members[coolNote.noteData].playAnim('confirm', true); + + if (canDisplayJudgement) + { + // special thanks to sam, they gave me the original system which kinda inspired my idea for this new one + + // get the note ms timing + var noteDiff:Float = Math.abs(coolNote.strumTime - Conductor.songPosition + 4); + trace(noteDiff); + // get the timing + if (coolNote.strumTime < Conductor.songPosition) + ratingTiming = "late"; + else + ratingTiming = "early"; + + // loop through all avaliable judgements + var foundRating:String = 'miss'; + var lowestThreshold:Float = Math.POSITIVE_INFINITY; + for (myRating in Timings.judgementsMap.keys()) + { + var myThreshold:Float = Timings.judgementsMap.get(myRating)[1]; + if (noteDiff <= myThreshold && (myThreshold < lowestThreshold)) + { + foundRating = myRating; + lowestThreshold = myThreshold; + } + } + + if (!coolNote.isSustainNote) + { + increaseCombo(foundRating, coolNote.noteData, character); + popUpScore(foundRating, ratingTiming, characterStrums, coolNote); + healthCall(Timings.judgementsMap.get(foundRating)[3]); + } + else if (coolNote.isSustainNote) + { + // call updated accuracy stuffs + Timings.updateAccuracy(100, true); + if (coolNote.animation.name.endsWith('holdend')) + healthCall(100); + } + } + + if (!coolNote.isSustainNote) + { + // coolNote.callMods(); + coolNote.kill(); + if (characterStrums.notesGroup.members.contains(coolNote)) + characterStrums.notesGroup.remove(coolNote, true); + coolNote.destroy(); + } + // + } + } + + function missNoteCheck(?includeAnimation:Bool = false, direction:Int = 0, character:Character, popMiss:Bool = false, lockMiss:Bool = false) + { + if (includeAnimation) + { + var stringDirection:String = UIStaticArrow.getArrowFromNumber(direction); + + FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); + character.playAnim('sing' + stringDirection.toUpperCase() + 'miss', lockMiss); + } + decreaseCombo(popMiss); + + // + } + + function characterPlayAnimation(coolNote:Note, character:Character) + { + // alright so we determine which animation needs to play + // get alt strings and stuffs + var stringArrow:String = ''; + var altString:String = ''; + + var baseString = 'sing' + UIStaticArrow.getArrowFromNumber(coolNote.noteData).toUpperCase(); + + // I tried doing xor and it didnt work lollll + if (coolNote.noteAlt > 0) + altString = '-alt'; + if (((SONG.notes[Math.floor(curStep / 16)] != null) && (SONG.notes[Math.floor(curStep / 16)].altAnim)) + && (character.animOffsets.exists(baseString + '-alt'))) + { + if (altString != '-alt') + altString = '-alt'; + else + altString = ''; + } + + stringArrow = baseString + altString; + // if (coolNote.foreverMods.get('string')[0] != "") + // stringArrow = coolNote.noteString; + + character.playAnim(stringArrow, true); + character.holdTimer = 0; + } + + private function strumCallsAuto(cStrum:UIStaticArrow, ?callType:Int = 1, ?daNote:Note):Void + { + switch (callType) + { + case 1: + // end the animation if the calltype is 1 and it is done + if ((cStrum.animation.finished) && (cStrum.canFinishAnimation)) + cStrum.playAnim('static'); + default: + // check if it is the correct strum + if (daNote.noteData == cStrum.ID) + { + // if (cStrum.animation.curAnim.name != 'confirm') + cStrum.playAnim('confirm'); // play the correct strum's confirmation animation (haha rhymes) + + // stuff for sustain notes + if ((daNote.isSustainNote) && (!daNote.animation.curAnim.name.endsWith('holdend'))) + cStrum.canFinishAnimation = false; // basically, make it so the animation can't be finished if there's a sustain note below + else + cStrum.canFinishAnimation = true; + } + } + } + + private function mainControls(daNote:Note, char:Character, strumline:Strumline, autoplay:Bool):Void + { + var notesPressedAutoplay = []; + // I have no idea what I have done + var downscrollMultiplier = 1; + if (Init.trueSettings.get('Downscroll')) + downscrollMultiplier = -1; + + // im very sorry for this if condition I made it worse lmao + ///* + if (daNote.isSustainNote + && (((daNote.y + daNote.offset.y <= (strumline.receptors.members[Math.floor(daNote.noteData)].y + Note.swagWidth / 2)) + && !Init.trueSettings.get('Downscroll')) + || (((daNote.y - (daNote.offset.y * daNote.scale.y) + daNote.height) >= (strumline.receptors.members[Math.floor(daNote.noteData)].y + + Note.swagWidth / 2)) + && Init.trueSettings.get('Downscroll'))) + && (autoplay || (daNote.wasGoodHit || (daNote.prevNote.wasGoodHit && !daNote.canBeHit)))) + { + var swagRectY = ((strumline.receptors.members[Math.floor(daNote.noteData)].y + Note.swagWidth / 2 - daNote.y) / daNote.scale.y); + var swagRect = new FlxRect(0, 0, daNote.width * 2, daNote.height * 2); + // I feel genuine pain + // basically these should be flipped based on if it is downscroll or not + if (Init.trueSettings.get('Downscroll')) + { + swagRect.height = swagRectY; + swagRect.y -= swagRect.height - daNote.height; + } + else + { + swagRect.y = swagRectY; + swagRect.height -= swagRect.y; + } + + daNote.clipRect = swagRect; + } + // */ + + // here I'll set up the autoplay functions + if (autoplay) + { + // check if the note was a good hit + if (daNote.strumTime <= Conductor.songPosition) + { + // use a switch thing cus it feels right idk lol + // make sure the strum is played for the autoplay stuffs + /* + charStrum.forEach(function(cStrum:UIStaticArrow) + { + strumCallsAuto(cStrum, 0, daNote); + }); + */ + + // kill the note, then remove it from the array + var canDisplayJudgement = false; + if (strumline.displayJudgements) + { + canDisplayJudgement = true; + for (noteDouble in notesPressedAutoplay) + { + if (noteDouble.noteData == daNote.noteData) + { + // if (Math.abs(noteDouble.strumTime - daNote.strumTime) < 10) + canDisplayJudgement = false; + // removing the fucking check apparently fixes it + // god damn it that stupid glitch with the double judgements is annoying + } + // + } + notesPressedAutoplay.push(daNote); + } + + goodNoteHit(daNote, char, strumline, canDisplayJudgement); + } + // + } + + // unoptimised asf camera control based on strums + strumCameraRoll(strumline.receptors, daNote.mustPress); + } + + private function strumCameraRoll(cStrum:FlxTypedGroup, mustHit:Bool) + { + if (!Init.trueSettings.get('No Camera Note Movement')) + { + var camDisplaceExtend:Float = 1.5; + var camDisplaceSpeed = 0.0125; + if (PlayState.SONG.notes[Std.int(curStep / 16)] != null) + { + if ((PlayState.SONG.notes[Std.int(curStep / 16)].mustHitSection && mustHit) + || (!PlayState.SONG.notes[Std.int(curStep / 16)].mustHitSection && !mustHit)) + { + if ((cStrum.members[0].animation.curAnim.name == 'confirm') && (camDisplaceX > -camDisplaceExtend)) + camDisplaceX -= camDisplaceSpeed; + else if ((cStrum.members[3].animation.curAnim.name == 'confirm') && (camDisplaceX < camDisplaceExtend)) + camDisplaceX += camDisplaceSpeed; + } + } + } + // + } + + override public function onFocus():Void + { + if (!paused) + updateRPC(false); + super.onFocus(); + } + + override public function onFocusLost():Void + { + updateRPC(true); + super.onFocusLost(); + } + + public static function updateRPC(pausedRPC:Bool) + { + #if !html5 + var displayRPC:String = (pausedRPC) ? detailsPausedText : songDetails; + + if (health > 0) + { + if (Conductor.songPosition > 0 && !pausedRPC) + Discord.changePresence(displayRPC, detailsSub, iconRPC, true, songLength - Conductor.songPosition); + else + Discord.changePresence(displayRPC, detailsSub, iconRPC); + } + #end + } + + var animationsPlay:Array = []; + + private var ratingTiming:String = ""; + + function popUpScore(baseRating:String, timing:String, strumline:Strumline, coolNote:Note) + { + // set up the rating + var score:Int = 50; + + // notesplashes + if (baseRating == "sick") // create the note splash if you hit a sick + createSplash(coolNote, strumline); else // if it isn't a sick, and you had a sick combo, then it becomes not sick :( if (allSicks) allSicks = false; - displayRating(baseRating); - Timings.updateAccuracy(Timings.ratingsMap.get(baseRating)[2]); - score = Std.int(Timings.ratingsMap.get(baseRating)[1]); + displayRating(baseRating, timing); + Timings.updateAccuracy(Timings.judgementsMap.get(baseRating)[3]); + score = Std.int(Timings.judgementsMap.get(baseRating)[2]); songScore += score; popUpCombo(); } + public function createSplash(coolNote:Note, strumline:Strumline) + { + // play animation in existing notesplashes + var noteSplashRandom:String = (Std.string((FlxG.random.int(0, 1) + 1))); + if (strumline.splashNotes != null) + strumline.splashNotes.members[coolNote.noteData].playAnim('anim' + noteSplashRandom); + } + private var createdColor = FlxColor.fromRGB(204, 66, 66); - function popUpCombo() + function popUpCombo(?preload:Bool = false) { var comboString:String = Std.string(combo); var negative = false; @@ -1018,9 +1066,19 @@ class PlayState extends MusicBeatState for (scoreInt in 0...stringArray.length) { // numScore.loadGraphic(Paths.image('UI/' + pixelModifier + 'num' + stringArray[scoreInt])); - var numScore = ForeverAssets.generateCombo('num' + stringArray[scoreInt], assetModifier, changeableSkin, 'UI', negative, createdColor, scoreInt, - scoreGroup); + var numScore = ForeverAssets.generateCombo('combo', stringArray[scoreInt], (!negative ? allSicks : false), assetModifier, changeableSkin, 'UI', + negative, createdColor, scoreInt); add(numScore); + // hardcoded lmao + if (Init.trueSettings.get('SM-like Judgements')) + { + numScore.cameras = [camHUD]; + numScore.x += 100; + numScore.y += 50; + } + + if (preload) + numScore.visible = false; FlxTween.tween(numScore, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) @@ -1032,10 +1090,6 @@ class PlayState extends MusicBeatState } } - // - // - // - function decreaseCombo(?popMiss:Bool = false) { // painful if statement @@ -1052,12 +1106,13 @@ class PlayState extends MusicBeatState misses++; // display negative combo - popUpCombo(); if (popMiss) { - displayRating("miss"); - healthCall(Timings.ratingsMap.get("miss")[2]); + // doesnt matter miss ratings dont have timings + displayRating("miss", 'late'); + healthCall(Timings.judgementsMap.get("miss")[3]); } + popUpCombo(); // gotta do it manually here lol Timings.updateFCDisplay(); @@ -1068,7 +1123,7 @@ class PlayState extends MusicBeatState // trolled this can actually decrease your combo if you get a bad/shit/miss if (baseRating != null) { - if (Timings.ratingsMap.get(baseRating)[2] > 0) + if (Timings.judgementsMap.get(baseRating)[3] > 0) { if (combo < 0) combo = 0; @@ -1079,94 +1134,25 @@ class PlayState extends MusicBeatState } } - public function createSplash(coolNote:Note) + public function displayRating(daRating:String, timing:String, ?cache:Bool = false) { - // play animation in existing notesplashes - var noteSplashRandom:String = (Std.string((FlxG.random.int(0, 1) + 1))); - splashNotes.members[coolNote.noteData].playAnim('anim' + noteSplashRandom); - } - - public function displayRating(daRating:String) - { - // set a custom color if you have a perfect sick combo - var perfectSickString:String = ""; - if ((allSicks) && (daRating == "sick")) - perfectSickString = "-perfect"; /* so you might be asking "oh but if the rating isn't sick why not just reset it" - because miss ratings can pop, and they dont mess with your sick combo + because miss judgements can pop, and they dont mess with your sick combo */ - - var noTiming:Bool = false; - if ((daRating == "sick") || (daRating == "miss")) - noTiming = true; - - var rating = ForeverAssets.generateRating('ratings/$daRating$perfectSickString', assetModifier, changeableSkin, 'UI', ratingsGroup); - - // this has to be loaded after unfortunately as much as I like to condense all of my code down - if (assetModifier == 'pixel') - rating.setGraphicSize(Std.int(rating.width * daPixelZoom * 0.7)); - else - { - rating.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); - rating.setGraphicSize(Std.int(rating.width * 0.7)); - } - + var rating = ForeverAssets.generateRating('$daRating', (daRating == 'sick' ? allSicks : false), timing, assetModifier, changeableSkin, 'UI'); add(rating); - // ooof this is very bad - if (!noTiming) + if (Init.trueSettings.get('SM-like Judgements')) { - var timing = timingsGroup.recycle(FlxSprite); - timingsGroup.add(timing); - // rating timing - // setting the width, it's half of the sprite's width, I don't like doing this but that code scares me in terms of optimisations - var newWidth = 166; - if (assetModifier == 'pixel') - newWidth = 26; - - timing.loadGraphic(Paths.image(ForeverTools.returnSkinAsset('ratings/$daRating-timings', assetModifier, changeableSkin, 'UI')), true, newWidth); - timing.alpha = 1; - // this code is quickly becoming painful lmao - timing.animation.add('early', [0]); - timing.animation.add('late', [1]); - timing.animation.play(ratingTiming); - - timing.x = rating.x; - timing.y = rating.y; - timing.acceleration.y = rating.acceleration.y; - timing.velocity.y = rating.velocity.y; - timing.velocity.x = rating.velocity.x; - - // messy messy pixel stuffs - // but thank you pixl your timings are awesome - if (assetModifier == 'pixel') - { - // positions are stupid - timing.x += (newWidth / 2) * daPixelZoom; - timing.setGraphicSize(Std.int(timing.width * daPixelZoom * 0.7)); - if (ratingTiming != 'late') - timing.x -= newWidth * 0.5 * daPixelZoom; - } - else - { - timing.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); - timing.setGraphicSize(Std.int(timing.width * 0.7)); - if (ratingTiming == 'late') - timing.x += newWidth * 0.5; - } - - add(timing); - - FlxTween.tween(timing, {alpha: 0}, 0.2, { - onComplete: function(tween:FlxTween) - { - timing.kill(); - }, - startDelay: Conductor.crochet * 0.00125 - }); + // bound to camera + rating.cameras = [camHUD]; + rating.screenCenter(); } + if (cache) + rating.visible = false; + ///* FlxTween.tween(rating, {alpha: 0}, 0.2, { onComplete: function(tween:FlxTween) @@ -1178,69 +1164,6 @@ class PlayState extends MusicBeatState // */ } - function goodNoteHit(coolNote:Note, character:Character, characterStrums:FlxTypedGroup, ?canDisplayRating:Bool = true) - { - if (!coolNote.wasGoodHit) - { - coolNote.wasGoodHit = true; - vocals.volume = 1; - - characterPlayAnimation(coolNote, character); - if (characterStrums.members[coolNote.noteData] != null) - characterStrums.members[coolNote.noteData].playAnim('confirm', true); - - if (canDisplayRating) - { - // special thanks to sam, they gave me the original system which kinda inspired my idea for this new one - - // get the note ms timing - var noteDiff:Float = Math.abs(coolNote.strumTime - Conductor.songPosition); - // get the timing - if (coolNote.strumTime < Conductor.songPosition) - ratingTiming = "late"; - else - ratingTiming = "early"; - - // loop through all avaliable ratings - var foundRating:String = 'miss'; - var lowestThreshold:Float = Math.POSITIVE_INFINITY; - for (myRating in Timings.ratingsMap.keys()) - { - var myThreshold:Float = Timings.ratingsMap.get(myRating)[0]; - if (noteDiff <= myThreshold && (myThreshold < lowestThreshold)) - { - foundRating = myRating; - lowestThreshold = myThreshold; - } - } - - if (!coolNote.isSustainNote) - { - increaseCombo(foundRating, coolNote.noteData, character); - popUpScore(foundRating, coolNote); - healthCall(Timings.ratingsMap.get(foundRating)[2]); - } - else if (coolNote.isSustainNote) - { - // call updated accuracy stuffs - Timings.updateAccuracy(100, true); - if (coolNote.animation.name.endsWith('holdend')) - healthCall(100); - } - } - - if (!coolNote.isSustainNote) - { - // coolNote.callMods(); - coolNote.kill(); - if (notes.members.contains(coolNote)) - notes.remove(coolNote, true); - coolNote.destroy(); - } - // - } - } - function healthCall(?ratingMultiplier:Float = 0) { // health += 0.012; @@ -1248,49 +1171,6 @@ class PlayState extends MusicBeatState health += (healthBase * (ratingMultiplier / 100)); } - function missNoteCheck(?includeAnimation:Bool = false, direction:Int = 0, character:Character, popMiss:Bool = false, lockMiss:Bool = false) - { - if (includeAnimation) - { - var stringDirection:String = UIStaticArrow.getArrowFromNumber(direction); - - FlxG.sound.play(Paths.soundRandom('missnote', 1, 3), FlxG.random.float(0.1, 0.2)); - character.playAnim('sing' + stringDirection.toUpperCase() + 'miss', lockMiss); - } - decreaseCombo(popMiss); - - // - } - - function characterPlayAnimation(coolNote:Note, character:Character) - { - // alright so we determine which animation needs to play - // get alt strings and stuffs - var stringArrow:String = ''; - var altString:String = ''; - - var baseString = 'sing' + UIStaticArrow.getArrowFromNumber(coolNote.noteData).toUpperCase(); - - // I tried doing xor and it didnt work lollll - if (coolNote.noteAlt > 0) - altString = '-alt'; - if (((SONG.notes[Math.floor(curStep / 16)] != null) && (SONG.notes[Math.floor(curStep / 16)].altAnim)) - && (character.animOffsets.exists(baseString + '-alt'))) - { - if (altString != '-alt') - altString = '-alt'; - else - altString = ''; - } - - stringArrow = baseString + altString; - // if (coolNote.foreverMods.get('string')[0] != "") - // stringArrow = coolNote.noteString; - - character.playAnim(stringArrow, true); - character.holdTimer = 0; - } - function startSong():Void { startingSong = false; @@ -1344,18 +1224,9 @@ class PlayState extends MusicBeatState FlxG.sound.list.add(songMusic); FlxG.sound.list.add(vocals); - // here's where the chart loading takes place - notes = new FlxTypedGroup(); - add(notes); - // generate the chart - // much simpler looking than in the original game lol - ChartLoader.generateChartType(determinedChartType); - - // return the unspawned notes that were generated in said chart - unspawnNotes = []; - unspawnNotes = ChartLoader.returnUnspawnNotes(); - ChartLoader.flushUnspawnNotes(); + unspawnNotes = ChartLoader.generateChartType(SONG, determinedChartType); + // sometime my brain farts dont ask me why these functions were separated before // sort through them unspawnNotes.sort(sortByShit); @@ -1370,68 +1241,6 @@ class PlayState extends MusicBeatState return FlxSort.byValues(FlxSort.ASCENDING, Obj1.strumTime, Obj2.strumTime); } - public function generateStaticArrows(player:Int):Void - { - for (i in 0...4) - { - // FlxG.log.add(i); - // var babyArrow:FlxSprite = new FlxSprite(0, strumLine.y); - var babyArrow:UIStaticArrow = ForeverAssets.generateUIArrows(0, strumLine.members[Math.floor(i + (player * 4))].y - 25, i, assetModifier); - babyArrow.ID = i; // + (player * 4); - - switch (player) - { - case 1: - boyfriendStrums.add(babyArrow); - default: - dadStrums.add(babyArrow); - } - - if (!Init.trueSettings.get('Centered Notefield')) - { - babyArrow.x += 75; - babyArrow.x += Note.swagWidth * i; - babyArrow.x += ((FlxG.width / 2) * player); - } - else - { - babyArrow.screenCenter(X); - babyArrow.x -= Note.swagWidth; - babyArrow.x -= 54; - babyArrow.x += Note.swagWidth * i; - } - - babyArrow.initialX = Math.floor(babyArrow.x); - babyArrow.initialY = Math.floor(babyArrow.y); - - babyArrow.xTo = babyArrow.initialX; - babyArrow.yTo = babyArrow.initialY; - babyArrow.angleTo = 0; - - babyArrow.y -= 10; - babyArrow.playAnim('static'); - - babyArrow.alpha = 0; - FlxTween.tween(babyArrow, {y: babyArrow.initialY, alpha: babyArrow.setAlpha}, 1, {ease: FlxEase.circOut, startDelay: 0.5 + (0.2 * i)}); - - strumLineNotes.add(babyArrow); - - // generate note splashes - if (player == 1) - { - var noteSplash:NoteSplash = ForeverAssets.generateNoteSplashes('noteSplashes', assetModifier, 'UI', i); - noteSplash.x += Note.swagWidth * i; - noteSplash.x += ((FlxG.width / 2) * player); - splashNotes.add(noteSplash); - } - } - // - } - - // - // I need some space okay? this code is claustrophobic as hell - // - function resyncVocals():Void { vocals.pause(); @@ -1474,6 +1283,8 @@ class PlayState extends MusicBeatState { FlxG.camera.zoom += 0.015; camHUD.zoom += 0.05; + for (hud in strumHUD) + hud.zoom += 0.05; } uiHUD.beatHit(); @@ -1791,6 +1602,8 @@ class PlayState extends MusicBeatState override function add(Object:FlxBasic):FlxBasic { Main.loadedAssets.insert(Main.loadedAssets.length, Object); + if (Init.trueSettings.get('Disable Antialiasing') && Std.isOfType(Object, FlxSprite)) + cast(Object, FlxSprite).antialiasing = false; return super.add(Object); } } diff --git a/source/gameFolder/meta/state/TitleState.hx b/source/gameFolder/meta/state/TitleState.hx index d7bdeaad7..11037cf5d 100644 --- a/source/gameFolder/meta/state/TitleState.hx +++ b/source/gameFolder/meta/state/TitleState.hx @@ -92,14 +92,14 @@ class TitleState extends MusicBeatState persistentUpdate = true; var bg:FlxSprite = new FlxSprite().makeGraphic(FlxG.width, FlxG.height, FlxColor.BLACK); - // bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + // bg.antialiasing = true; // bg.setGraphicSize(Std.int(bg.width * 0.6)); // bg.updateHitbox(); add(bg); logoBl = new FlxSprite(-150, -100); logoBl.frames = Paths.getSparrowAtlas('menus/base/title/logoBumpin'); - logoBl.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + logoBl.antialiasing = true; logoBl.animation.addByPrefix('bump', 'logo bumpin', 24); logoBl.animation.play('bump'); logoBl.updateHitbox(); @@ -110,7 +110,7 @@ class TitleState extends MusicBeatState gfDance.frames = Paths.getSparrowAtlas('menus/base/title/gfDanceTitle'); gfDance.animation.addByIndices('danceLeft', 'gfDance', [30, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], "", 24, false); gfDance.animation.addByIndices('danceRight', 'gfDance', [15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29], "", 24, false); - gfDance.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + gfDance.antialiasing = true; add(gfDance); add(logoBl); @@ -118,7 +118,7 @@ class TitleState extends MusicBeatState titleText.frames = Paths.getSparrowAtlas('menus/base/title/titleEnter'); titleText.animation.addByPrefix('idle', "Press Enter to Begin", 24); titleText.animation.addByPrefix('press', "ENTER PRESSED", 24); - titleText.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + titleText.antialiasing = true; titleText.animation.play('idle'); titleText.updateHitbox(); // titleText.screenCenter(X); @@ -126,7 +126,7 @@ class TitleState extends MusicBeatState // var logo:FlxSprite = new FlxSprite().loadGraphic(Paths.image('menus/base/title/logo')); // logo.screenCenter(); - // logo.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + // logo.antialiasing = true; // add(logo); // FlxTween.tween(logoBl, {y: logoBl.y + 50}, 0.6, {ease: FlxEase.quadInOut, type: PINGPONG}); @@ -152,7 +152,7 @@ class TitleState extends MusicBeatState ngSpr.setGraphicSize(Std.int(ngSpr.width * 0.8)); ngSpr.updateHitbox(); ngSpr.screenCenter(X); - ngSpr.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + ngSpr.antialiasing = true; FlxTween.tween(credTextShit, {y: credTextShit.y + 20}, 2.9, {ease: FlxEase.quadInOut, type: PINGPONG}); diff --git a/source/gameFolder/meta/state/charting/ChartingState.hx b/source/gameFolder/meta/state/charting/ChartingState.hx index 2cda28b53..2470c1cd9 100644 --- a/source/gameFolder/meta/state/charting/ChartingState.hx +++ b/source/gameFolder/meta/state/charting/ChartingState.hx @@ -7,6 +7,7 @@ import flixel.FlxObject; import flixel.FlxSprite; import flixel.addons.display.FlxBackdrop; import flixel.addons.display.FlxGridOverlay; +import flixel.addons.display.FlxTiledSprite; import flixel.addons.display.shapes.FlxShapeBox; import flixel.addons.ui.FlxInputText; import flixel.addons.ui.FlxUI; @@ -25,6 +26,8 @@ import flixel.util.FlxColor; import flixel.util.FlxGradient; import gameFolder.gameObjects.*; import gameFolder.gameObjects.userInterface.*; +import gameFolder.gameObjects.userInterface.notes.*; +import gameFolder.gameObjects.userInterface.notes.Strumline.UIStaticArrow; import gameFolder.meta.MusicBeat.MusicBeatState; import gameFolder.meta.data.*; import gameFolder.meta.data.Section.SwagSection; @@ -36,6 +39,7 @@ import haxe.io.Bytes; import lime.media.AudioBuffer; import openfl.events.Event; import openfl.events.IOErrorEvent; +import openfl.geom.ColorTransform; import openfl.geom.Rectangle; import openfl.media.Sound; import openfl.net.FileReference; @@ -54,155 +58,61 @@ import sys.thread.Thread; **/ class ChartingState extends MusicBeatState { - private var curSection:Int = 0; - private var chartType:String; - - var strumLine:FlxSpriteGroup; - var typingShit:FlxInputText; - - var camHUD:FlxCamera; - var camGame:FlxCamera; - var strumLineCam:FlxObject; + var _song:SwagSong; var songMusic:FlxSound; var vocals:FlxSound; private var keysTotal = 8; - private var dummyArrow:FlxSprite; - private var curRenderedNotes:FlxTypedGroup; - private var curRenderedSustains:FlxTypedGroup; - private var curRenderedSections:FlxTypedGroup; - - private var arrowGroup:FlxTypedSpriteGroup; + var strumLine:FlxSprite; - private var iconL:HealthIcon; - private var iconR:HealthIcon; - - var curSelectedNotes:Array>; + var camHUD:FlxCamera; + var camGame:FlxCamera; + var strumLineCam:FlxObject; public static var songPosition:Float = 0; public static var curSong:SwagSong; - private var sectionsMap:Map; - - var _song:SwagSong; + public static var gridSize:Int = 50; - var newWaveform:FlxSprite; + private var dummyArrow:FlxSprite; + private var curRenderedNotes:FlxTypedGroup; + private var curRenderedSustains:FlxTypedGroup; + private var curRenderedSections:FlxTypedGroup; - override public function create():Void + override public function create() { + // super.create(); - // - chartType = 'FNF'; + generateBackground(); if (PlayState.SONG != null) _song = PlayState.SONG; else - _song = Song.loadFromJson('fresh-hard', 'fresh'); - - PlayState.resetMusic(); - if (FlxG.sound.music != null) - { - FlxG.sound.music.stop(); - // vocals.stop(); - } + _song = Song.loadFromJson('test', 'test'); - #if !html5 - Discord.changePresence('CHARTING STATE', 'Freeplay'); - #end - - FlxG.mouse.useSystemCursor = false; // Use system cursor because it's prettier - FlxG.mouse.visible = true; // Hide mouse on start - - strumLineCam = new FlxObject(0, 0); - strumLineCam.screenCenter(X); - - // generate the chart itself - addSection(); loadSong(_song.song); - generateBackground(); + Conductor.changeBPM(_song.bpm); + Conductor.mapBPMChanges(_song); + + generateGrid(); - // set up these dumb shits loll - sectionsAll = new FlxTypedGroup(); curRenderedNotes = new FlxTypedGroup(); curRenderedSustains = new FlxTypedGroup(); curRenderedSections = new FlxTypedGroup(); - sectionsMap = new Map(); - - generateChart(); - - // render the waveforms here instead - /* - newWaveform = generateWaveform(Paths.inst(_song.song), 0, 0); - add(newWaveform); - */ - - // uh heres the epic setup for these - add(sectionsAll); add(curRenderedSections); add(curRenderedSustains); add(curRenderedNotes); - /* Create Cool UI elements here */ + strumLineCam = new FlxObject(0, 0); + strumLineCam.screenCenter(X); // epic strum line - strumLine = new FlxSpriteGroup(0, 0); - - var strumLineBase:FlxSprite = new FlxSprite(0, 0).makeGraphic(Std.int(FlxG.width / 2), 2); - strumLine.add(strumLineBase); - - // dont ask me why this is a sprite I just didnt wanna bother with flxshape tbh - var strumLineMarkerL:FlxSprite = new FlxSprite(-8, -12).loadGraphic(Paths.image('UI/forever/base/chart editor/marker')); - strumLine.add(strumLineMarkerL); - var strumLineMarkerR:FlxSprite = new FlxSprite((FlxG.width / 2) - 8, -12).loadGraphic(Paths.image('UI/forever/base/chart editor/marker')); - strumLine.add(strumLineMarkerR); - - // center the strumline - strumLine.screenCenter(X); - - // add the cool icons - iconL = new HealthIcon(_song.player2, false); - iconR = new HealthIcon(_song.player1, true); - iconL.setGraphicSize(Std.int(iconL.width / 2)); - iconR.setGraphicSize(Std.int(iconR.width / 2)); - - iconL.setPosition(-64, -128); - iconR.setPosition(strumLineBase.width - 80, -128); - - strumLine.add(iconL); - strumLine.add(iconR); - + strumLine = new FlxSprite(0, 0).makeGraphic(Std.int(FlxG.width / 2), 2); add(strumLine); - - // cursor - dummyArrow = new FlxSprite().makeGraphic(gridSize, gridSize); - add(dummyArrow); - - // and now the epic note thingies - arrowGroup = new FlxTypedSpriteGroup(0, 0); - for (i in 0...horizontalSize) - { - var typeReal:Int = i; - if (typeReal > 3) - typeReal -= 4; - - var newArrow:UIStaticArrow = ForeverAssets.generateUIArrows(((FlxG.width / 2) - ((horizontalSize / 2) * gridSize)) + ((i - 1) * gridSize) + 1, - -76, typeReal, 'chart editor'); - - newArrow.ID = i; - newArrow.setGraphicSize(gridSize, gridSize); - newArrow.updateHitbox(); - newArrow.alpha = 0.9; - newArrow.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); - - // lol silly idiot - newArrow.playAnim('static'); - - arrowGroup.add(newArrow); - } - add(arrowGroup); + strumLine.screenCenter(X); // code from the playstate so I can separate the camera and hud camGame = new FlxCamera(); @@ -213,702 +123,134 @@ class ChartingState extends MusicBeatState FlxG.cameras.add(camHUD); FlxCamera.defaultCameras = [camGame]; - generateHUD(); - - // - Conductor.changeBPM(_song.bpm); - Conductor.mapBPMChanges(_song); - FlxG.camera.follow(strumLineCam); - - debugText = new FlxText(0, 0, 0, '', 24); - // add(debugText); - } - - private var debugText:FlxText; - private var informationBar:FlxText; - - private function generateHUD() - { - // interactible hud - var sidebar = new FlxShapeBox(916, 160, 326, 480, {thickness: 24, color: FlxColor.WHITE}, FlxColor.WHITE); - sidebar.alpha = (26 / 255); - - addSectionUI(); - addSongUI(); - addNoteUI(); - - // - var constTextSize:Int = 24; - informationBar = new FlxText(5, FlxG.height - (constTextSize * 4) - 5, 0, 'BEAT:', constTextSize); - informationBar.setFormat(Paths.font("vcr.ttf"), constTextSize); - informationBar.cameras = [camHUD]; - - add(informationBar); - } - - var UI_box:FlxUITabMenu; - var _file:FileReference; - - var stepperLength:FlxUINumericStepper; - var check_mustHitSection:FlxUICheckBox; - var check_changeBPM:FlxUICheckBox; - var stepperSectionBPM:FlxUINumericStepper; - var check_altAnim:FlxUICheckBox; - - function addSectionUI():Void - { - var tabs = [ - {name: "Song", label: 'Song'}, - {name: "Section", label: 'Section'}, - {name: "Note", label: 'Note'} - ]; - - UI_box = new FlxUITabMenu(null, tabs, true); - - UI_box.resize(300, 400); - UI_box.x = 916; - UI_box.y = 160; - add(UI_box); - - var tab_group_section = new FlxUI(null, UI_box); - tab_group_section.name = 'Section'; - - stepperLength = new FlxUINumericStepper(10, 10, 4, 0, 0, 999, 0); - stepperLength.value = _song.notes[curSection].lengthInSteps; - stepperLength.name = "section_length"; - - stepperSectionBPM = new FlxUINumericStepper(10, 80, 1, Conductor.bpm, 0, 999, 0); - stepperSectionBPM.value = Conductor.bpm; - stepperSectionBPM.name = 'section_bpm'; - - var stepperCopy:FlxUINumericStepper = new FlxUINumericStepper(110, 130, 1, 1, -999, 999, 0); - - var copyButton:FlxButton = new FlxButton(10, 130, "Copy last section", function() - { - // copySection(Std.int(stepperCopy.value)); - }); - - var clearSectionButton:FlxButton = new FlxButton(10, 150, "Clear", function() {}); - - var swapSection:FlxButton = new FlxButton(10, 170, "Swap section", function() - { - for (i in 0..._song.notes[curSection].sectionNotes.length) - { - var note = _song.notes[curSection].sectionNotes[i]; - note[1] = (note[1] + 4) % 8; - _song.notes[curSection].sectionNotes[i] = note; - // updateGrid(); - } - }); - - check_mustHitSection = new FlxUICheckBox(10, 30, null, null, "Must hit section", 100); - check_mustHitSection.name = 'check_mustHit'; - check_mustHitSection.checked = true; - // _song.needsVoices = check_mustHit.checked; - - check_altAnim = new FlxUICheckBox(10, 400, null, null, "Alt Animation", 100); - check_altAnim.name = 'check_altAnim'; - - check_changeBPM = new FlxUICheckBox(10, 60, null, null, 'Change BPM', 100); - check_changeBPM.name = 'check_changeBPM'; - - tab_group_section.add(stepperLength); - tab_group_section.add(stepperSectionBPM); - tab_group_section.add(stepperCopy); - tab_group_section.add(check_mustHitSection); - tab_group_section.add(check_altAnim); - tab_group_section.add(check_changeBPM); - tab_group_section.add(copyButton); - tab_group_section.add(clearSectionButton); - tab_group_section.add(swapSection); - - UI_box.addGroup(tab_group_section); - UI_box.cameras = [camHUD]; - } - - function addSongUI():Void - { - var UI_songTitle = new FlxUIInputText(10, 10, 70, _song.song, 8); - typingShit = UI_songTitle; - - var check_voices = new FlxUICheckBox(10, 25, null, null, "Has voice track", 100); - check_voices.checked = _song.needsVoices; - // _song.needsVoices = check_voices.checked; - check_voices.callback = function() - { - _song.needsVoices = check_voices.checked; - trace('CHECKED!'); - }; - - var check_mute_inst = new FlxUICheckBox(10, 200, null, null, "Mute Instrumental (in editor)", 100); - check_mute_inst.checked = false; - check_mute_inst.callback = function() - { - var vol:Float = 1; - - if (check_mute_inst.checked) - vol = 0; - - FlxG.sound.music.volume = vol; - }; - - var saveButton:FlxButton = new FlxButton(110, 8, "Save", function() - { - saveLevel(); - }); - - var reloadSong:FlxButton = new FlxButton(saveButton.x + saveButton.width + 10, saveButton.y, "Reload Audio", function() - { - loadSong(_song.song); - }); - - var reloadSongJson:FlxButton = new FlxButton(reloadSong.x, saveButton.y + 30, "Reload JSON", function() - { - loadJson(_song.song.toLowerCase()); - }); - - var loadAutosaveBtn:FlxButton = new FlxButton(reloadSongJson.x, reloadSongJson.y + 30, 'load autosave', loadAutosave); - - var stepperSpeed:FlxUINumericStepper = new FlxUINumericStepper(10, 80, 0.1, 1, 0.1, 10, 1); - stepperSpeed.value = _song.speed; - stepperSpeed.name = 'song_speed'; - - var stepperBPM:FlxUINumericStepper = new FlxUINumericStepper(10, 65, 1, 1, 1, 339, 0); - stepperBPM.value = Conductor.bpm; - stepperBPM.name = 'song_bpm'; - - var characters:Array = CoolUtil.coolTextFile(Paths.txt('characterList')); - - var player1DropDown = new FlxUIDropDownMenu(10, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) - { - _song.player1 = characters[Std.parseInt(character)]; - updateHeads(); - }); - player1DropDown.selectedLabel = _song.player1; - - var player2DropDown = new FlxUIDropDownMenu(140, 100, FlxUIDropDownMenu.makeStrIdLabelArray(characters, true), function(character:String) - { - _song.player2 = characters[Std.parseInt(character)]; - updateHeads(); - }); - - player2DropDown.selectedLabel = _song.player2; - - var tab_group_song = new FlxUI(null, UI_box); - tab_group_song.name = "Song"; - tab_group_song.add(UI_songTitle); - - tab_group_song.add(check_voices); - tab_group_song.add(check_mute_inst); - tab_group_song.add(saveButton); - tab_group_song.add(reloadSong); - tab_group_song.add(reloadSongJson); - tab_group_song.add(loadAutosaveBtn); - tab_group_song.add(stepperBPM); - tab_group_song.add(stepperSpeed); - tab_group_song.add(player1DropDown); - tab_group_song.add(player2DropDown); - - UI_box.addGroup(tab_group_song); - UI_box.scrollFactor.set(); - - FlxG.camera.follow(strumLine); - } - - private function updateHUD() - { - // - var fakeStep = Std.string(FlxMath.roundDecimal((Conductor.songPosition / Conductor.stepCrochet), 2)); - var songTime = Std.string(FlxMath.roundDecimal(Conductor.songPosition / 1000, 2)); - informationBar.text = 'STEP: $fakeStep\nBEAT: $curBeat\nTIME: $songTime' + '\nBPM: ' + Conductor.bpm + '\n'; - - // putting this code here cus fuck you - - if (FlxG.keys.pressed.BACKSPACE) - { - pauseMusic(); - openSubState(new PreferenceSubstate(camHUD)); - } - } - - function pauseMusic() - { - songMusic.time = Math.max(songMusic.time, 0); - songMusic.time = Math.min(songMusic.time, songMusic.length); - - resyncVocals(); - songMusic.pause(); - vocals.pause(); - } - - function resyncVocals():Void - { - vocals.pause(); - - songMusic.play(); - Conductor.songPosition = songMusic.time; - vocals.time = Conductor.songPosition; - vocals.play(); } - var stepperSusLength:FlxUINumericStepper; - var stepperType:FlxUINumericStepper; - - function addNoteUI():Void - { - var tab_group_note = new FlxUI(null, UI_box); - tab_group_note.name = 'Note'; - - stepperSusLength = new FlxUINumericStepper(10, 10, Conductor.stepCrochet / 2, 0, 0, Conductor.stepCrochet * 16); - stepperSusLength.value = 0; - stepperSusLength.name = 'note_susLength'; - - var applyLength:FlxButton = new FlxButton(100, 10, 'Apply'); - - tab_group_note.add(stepperSusLength); - tab_group_note.add(applyLength); - - // note types - stepperType = new FlxUINumericStepper(10, 30, Conductor.stepCrochet / 125, 0, 0, (Conductor.stepCrochet / 125) + 10); // 10 is placeholder - // I have no idea what i'm doing lmfao - stepperType.value = 0; - stepperType.name = 'note_type'; - - tab_group_note.add(stepperType); - - UI_box.addGroup(tab_group_note); - // I'm genuinely tempted to go around and remove every instance of the word "sus" it is genuinely killing me inside - } - - function loadSong(daSong:String):Void - { - if (songMusic != null) - songMusic.stop(); - - if (vocals != null) - vocals.stop(); - - songMusic = new FlxSound().loadEmbedded(Sound.fromFile('./' + Paths.inst(daSong)), false, true); - if (_song.needsVoices) - vocals = new FlxSound().loadEmbedded(Sound.fromFile('./' + Paths.voices(daSong)), false, true); - else - vocals = new FlxSound(); - FlxG.sound.list.add(songMusic); - FlxG.sound.list.add(vocals); - - songMusic.play(); - vocals.play(); - - if (curSong == _song) - songMusic.time = songPosition; - curSong = _song; - - pauseMusic(); - - songMusic.onComplete = function() - { - vocals.pause(); - songMusic.pause(); - }; - // - } - - private var scrollSpeed:Float = 0.75; - private var canPlay:Bool = true; - - private var isPlacing:Bool = false; - private var isPlacingStrums:Bool = false; - private var notesExtending:Array = []; - override public function update(elapsed:Float) { - var beatTime:Float = ((Conductor.songPosition / 1000) * (Conductor.bpm / 60)); - - // coolGrid.y = (750 * (Math.cos((beatTime / 5) * Math.PI))); - // coolGrid.x = Conductor.songPosition; - if (FlxG.keys.justPressed.SPACE) { if (songMusic.playing) - pauseMusic(); + { + songMusic.pause(); + vocals.pause(); + // playButtonAnimation('pause'); + } else { vocals.play(); songMusic.play(); // reset note tick sounds - hitSoundsPlayed = []; + // hitSoundsPlayed = []; // playButtonAnimation('play'); } } - // originally only for note ticks but - // repurposed for arrow presses - if (songMusic.playing) {} - - arrowGroup.forEach(function(arrow:UIStaticArrow) - { - if (arrow.animation.curAnim.finished) - arrow.playAnim('static'); - }); - + var scrollSpeed:Float = 0.75; if (FlxG.mouse.wheel != 0) { - pauseMusic(); + songMusic.pause(); + vocals.pause(); songMusic.time = Math.max(songMusic.time - (FlxG.mouse.wheel * Conductor.stepCrochet * scrollSpeed), 0); songMusic.time = Math.min(songMusic.time, songMusic.length); vocals.time = songMusic.time; } - // I don't know if this is optimised I'm sorry if it isn't - checkExists(curRenderedNotes); - checkExists(curRenderedSustains); - checkExists(curRenderedSections); - // strumline camera stuffs! Conductor.songPosition = songMusic.time; strumLine.y = getYfromStrum(Conductor.songPosition); - curSection = getSectionfromY(strumLine.y); - strumLineCam.y = strumLine.y + (FlxG.height / 3); - arrowGroup.y = strumLine.y; coolGradient.y = strumLineCam.y - (FlxG.height / 2); coolGrid.y = strumLineCam.y - (FlxG.height / 2); super.update(elapsed); - var shiftThing:Int = 1; - if (FlxG.keys.pressed.SHIFT) - shiftThing = 4; - - if (FlxG.keys.justPressed.RIGHT || FlxG.keys.justPressed.D) - { - songMusic.time = getStrumTime(sectionsMap.get(Std.int(Math.min(curSection + shiftThing, sectionsMax)))[2]); - pauseMusic(); - } - else if (FlxG.keys.justPressed.LEFT || FlxG.keys.justPressed.A) - { - songMusic.time = getStrumTime(sectionsMap.get(Std.int(Math.max(curSection - shiftThing, 0)))[2]); - pauseMusic(); - } - - // mouse stuffs! - - debugText.setPosition(FlxG.mouse.x + 48, FlxG.mouse.y); - debugText.text = Std.string(Math.floor(dummyArrow.x / gridSize)); - - ///* - if (FlxG.mouse.x > ((FlxG.width / 2) - (gridSize * (horizontalSize / 2))) - && FlxG.mouse.x < ((FlxG.width / 2) + (gridSize * (horizontalSize / 2))) - && FlxG.mouse.y > 0 - && FlxG.mouse.y < (gridSize * sectionsMax * verticalSize)) - { - var testInterval = 16; - dummyArrow.x = (Math.floor((FlxG.mouse.x - testInterval) / gridSize) * gridSize) + testInterval; - if (FlxG.keys.pressed.SHIFT) - dummyArrow.y = FlxG.mouse.y - testInterval; - else - dummyArrow.y = Math.floor(FlxG.mouse.y / gridSize) * gridSize; - - // moved this in here for the sake of not dying - if (FlxG.mouse.justPressed) - { - if (!FlxG.mouse.overlaps(curRenderedNotes)) - { - // add note funny - var noteStrum = getStrumTime(dummyArrow.y); - - var notesSection = getSectionfromY(dummyArrow.y); - var noteData = adjustSide(Math.floor(dummyArrow.x / gridSize) - 8, notesSection); - var noteSus = 0; // ninja you will NOT get away with this - - noteCleanup(notesSection, noteStrum, noteData); - - _song.notes[notesSection].sectionNotes.push([noteStrum, noteData, noteSus]); - generateChartNote(noteData, noteStrum, noteSus, 0, notesSection, sectionsMap.get(notesSection)[3]); - - updateSelection(_song.notes[notesSection].sectionNotes[_song.notes[notesSection].sectionNotes.length - 1], notesSection, true); - - isPlacing = true; - } - else - { - curRenderedNotes.forEachAlive(function(note:Note) - { - if (FlxG.mouse.overlaps(note)) - { - if (FlxG.keys.pressed.CONTROL) - { - // selectNote(note); - } - else - { - // delete the epic note - var notesSection = getSectionfromY(note.y); - // persona 3 mass destruction - destroySustain(note, notesSection); - - noteCleanup(notesSection, note.strumTime, note.rawNoteData); - - note.kill(); - curRenderedNotes.remove(note); - note.destroy(); - // - } - } - // lol - }); - } - } - } - // */ - - if (FlxG.mouse.pressed) - { - if (isPlacing) - { - // adjust the note length lol - for (i in 0...curSelectedNotes.length) - { - // distance stuffs - var lastNotePlacement = getYfromStrum(curSelectedNotes[curSelectedNotes.length - 1][0]); - // idk how to math so weird ass equation here - if (curRenderedNotes.members.contains(notesExtending[i])) - adjustNoteSustain(notesExtending[i], (((FlxG.mouse.y - (lastNotePlacement + gridSize)) / gridSize) * Conductor.stepCrochet)); - // someone please fix this for me I'm so burnt out - } - } - } - else - isPlacing = false; - if (FlxG.keys.justPressed.ENTER) { songPosition = songMusic.time; - FlxG.mouse.useSystemCursor = true; - FlxG.mouse.visible = false; // Hide mouse PlayState.SONG = _song; ForeverTools.killMusic([songMusic, vocals]); Main.switchState(this, new PlayState()); } - - updateHUD(); } - private function updateSelection(noteToAdd, section, ?reset = false) - { - // lol reset funny selection list if script calls for it - if (reset) - { - curSelectedNotes = []; - notesExtending = []; - } - - curSelectedNotes.push(noteToAdd); - // find the notes for the actual display - var chosenNoteMap:Map> = sectionsMap.get(section)[3]; - // remove all connected notes in the note's map - for (i in chosenNoteMap.keys()) - { - // - if ((i.strumTime == noteToAdd[0] && i.rawNoteData == noteToAdd[1]) && (i.exists)) - notesExtending.push(i); - } - #if debug - trace(curSelectedNotes + ', fakenotes ' + notesExtending); - #end - } - - private function noteCleanup(notesSection, strumTime, noteData) + function getStrumTime(yPos:Float):Float { - // go through all notes in the section and remove any dupes - for (removeNote in _song.notes[notesSection].sectionNotes) - { - // hopefully this works - if ((removeNote[0] == strumTime) && (removeNote[1] == noteData)) - _song.notes[notesSection].sectionNotes.remove(removeNote); - } + return FlxMath.remapToRange(yPos, 0, (songMusic.length / Conductor.stepCrochet) * gridSize, songMusic.length, 0); } - /** - This is the last thing I'm trying if it doesnt work I'm just moving on with my life and fixing it after skater is done - **/ - private function destroySustain(note, notesSection) + function getYfromStrum(strumTime:Float):Float { - var chosenNoteMap:Map> = sectionsMap.get(notesSection)[3]; - // remove all connected notes in the note's map - #if debug - trace(chosenNoteMap.get(note)); - #end - - if (chosenNoteMap.get(note) != null) - { - for (i in 0...chosenNoteMap.get(note).length) - { - chosenNoteMap.get(note)[i].kill(); - curRenderedSustains.remove(chosenNoteMap.get(note)[i]); - chosenNoteMap.get(note)[i].destroy(); - } - } + return FlxMath.remapToRange(strumTime, 0, songMusic.length, 0, (songMusic.length / Conductor.stepCrochet) * gridSize); } - function updateHeads():Void - { - if (check_mustHitSection.checked) - { - iconL.animation.play(_song.player1); - iconR.animation.play(_song.player2); - } - else - { - iconL.animation.play(_song.player2); - iconR.animation.play(_song.player1); - } - } + var fullGrid:FlxTiledSprite; - private function adjustNoteSustain(note:Note, newSus:Float) + function generateGrid() { - // - var notesSection = getSectionfromY(note.y); - var noteSustains:Map> = sectionsMap.get(notesSection)[3]; + // create new sprite + var base:FlxSprite = FlxGridOverlay.create(gridSize, gridSize, gridSize * 2, gridSize * 2, true, FlxColor.WHITE, FlxColor.BLACK); + fullGrid = new FlxTiledSprite(null, gridSize * keysTotal, gridSize); + // base graphic change data + var newAlpha = (26 / 255); + base.graphic.bitmap.colorTransform(base.graphic.bitmap.rect, new ColorTransform(1, 1, 1, newAlpha)); + fullGrid.loadGraphic(base.graphic); + fullGrid.screenCenter(X); - if (newSus > 0) - { - if ((noteSustains.get(note) != null) && (noteSustains.get(note)[0].exists)) - { - // if a note map does already exist (the note was a sustain note before) - var constSize = Std.int(gridSize / 3); - noteSustains.get(note)[0].setGraphicSize(constSize, getNoteVerticalSize(newSus)); - noteSustains.get(note)[0].updateHitbox(); - // - noteSustains.get(note)[1].y = note.y + (noteSustains.get(note)[0].height) + (gridSize / 2); - } - else - generateSustain(note.strumTime, note.rawNoteData, newSus, 0, note, noteSustains); - } - else if ((noteSustains.get(note) != null) && (noteSustains.get(note)[0].exists)) // remove the sustain note instead - destroySustain(note, notesSection); + // fullgrid height + fullGrid.height = (songMusic.length / Conductor.stepCrochet) * gridSize; - // set the note sustain in the actual chart info - for (i in 0...curSelectedNotes.length) - curSelectedNotes[i][2] = Math.max(newSus, 0); + add(fullGrid); } - private function getNoteVerticalSize(newSus:Float) + function generateNotes() { - var constSize = Std.int(gridSize / 3); - return Math.floor(FlxMath.remapToRange(newSus, 0, Conductor.stepCrochet * getChartSizeMax(), 0, gridSize * getChartSizeMax()) - constSize); + // GENERATING THE GRID NOTES! } - private function returnFromNote(note:Note) - { - // just the note selector from the og chart editor - var counter:Int = 0; - var returnNote = null; - var notesSection = getSectionfromY(note.y); - - for (i in _song.notes[notesSection].sectionNotes) - { - if (i.strumTime == note.strumTime && i.noteData == note.rawNoteData) - returnNote = _song.notes[notesSection].sectionNotes[counter]; - counter += 1; - } - - return returnNote; - } - - var coolGrid:FlxBackdrop; - var coolGradient:FlxSprite; - - private function checkExists(group:FlxTypedGroup) - { - group.forEach(function(object:Dynamic) - { - if ((object.y < (strumLineCam.y - (FlxG.height / 2) - object.height)) || (object.y > (strumLineCam.y + (FlxG.height / 2)))) - object.alive = false; - else - object.alive = true; - }); - } - - private function generateBackground() + function loadSong(daSong:String):Void { - coolGrid = new FlxBackdrop(null, 1, 1, true, true, 1, 1); - coolGrid.loadGraphic(Paths.image('UI/forever/base/chart editor/grid')); - coolGrid.alpha = (32 / 255); - add(coolGrid); - - // gradient - coolGradient = FlxGradient.createGradientFlxSprite(FlxG.width, FlxG.height, - FlxColor.gradient(FlxColor.fromRGB(188, 158, 255, 200), FlxColor.fromRGB(80, 12, 108, 255), 16)); - coolGradient.alpha = (32 / 255); - add(coolGradient); - } + if (songMusic != null) + songMusic.stop(); - var gridSize:Int = 52; - var horizontalSize:Int = 8; - var verticalSize:Int = 16; + if (vocals != null) + vocals.stop(); - private var sectionsMax:Int = 0; - private var sectionsAll:FlxTypedGroup; + songMusic = new FlxSound().loadEmbedded(Sound.fromFile('./' + Paths.inst(daSong)), false, true); + if (_song.needsVoices) + vocals = new FlxSound().loadEmbedded(Sound.fromFile('./' + Paths.voices(daSong)), false, true); + else + vocals = new FlxSound(); + FlxG.sound.list.add(songMusic); + FlxG.sound.list.add(vocals); - private var prevNote:Note; - private var hitSoundsPlayed:Array = []; - private var fullSectionSize:Float = 0; + songMusic.play(); + vocals.play(); - private function generateChart() - { - // generate all sections + if (curSong == _song) + songMusic.time = songPosition; + curSong = _song; - // YOU DUMMY PUT IT OVER HERE NOT AFTER LMAAOOOOO - removeAllNotes(); + pauseMusic(); - sectionsMax = 1; - for (section in _song.notes) + songMusic.onComplete = function() { - // set up cool section stuffs here! - // section map will be used to control sections easily so I can just do stuffs without it breaking - var curGridSprite:FlxSprite = FlxGridOverlay.create(gridSize, gridSize, gridSize * horizontalSize, gridSize * section.lengthInSteps, true, - FlxColor.WHITE, FlxColor.BLACK); - curGridSprite.alpha = (26 / 255); - curGridSprite.screenCenter(X); - curGridSprite.y = fullSectionSize; - - sectionsAll.add(curGridSprite); - regenerateSection(sectionsMax - 1, fullSectionSize); - - // generate notes - var curNoteMap:Map = new Map>(); - - for (i in section.sectionNotes) - { - // note stuffs - var daNoteAlt = 0; - if (i.length > 2) - daNoteAlt = i[3]; - - generateChartNote(i[1], i[0], i[2], daNoteAlt, sectionsMax - 1, curNoteMap); - } - - // - sectionsMap.set(sectionsMax - 1, [curGridSprite, section.lengthInSteps, fullSectionSize, curNoteMap]); - fullSectionSize += (gridSize * section.lengthInSteps); - sectionsMax++; - } - // lolll - sectionsMax--; + ForeverTools.killMusic([songMusic, vocals]); + loadSong(daSong); + }; + // } private function generateChartNote(daNoteInfo, daStrumTime, daSus, daNoteAlt, noteSection, curNoteMap:Map) { // - var note:Note = ForeverAssets.generateArrow(PlayState.assetModifier, daStrumTime, daNoteInfo % 4, 0, daNoteAlt); + var note:Note = new Note(daStrumTime, daNoteInfo % 4, daNoteAlt); // I love how there's 3 different engines that use this exact same variable name lmao note.rawNoteData = daNoteInfo; note.sustainLength = daSus; @@ -916,8 +258,8 @@ class ChartingState extends MusicBeatState note.updateHitbox(); note.screenCenter(X); - note.x -= ((gridSize * (horizontalSize / 2)) - (gridSize / 2)); - note.x += Math.floor(adjustSide(daNoteInfo, noteSection) * gridSize); + note.x -= ((gridSize * (keysTotal / 2)) - (gridSize / 2)); + note.x += Math.floor(adjustSide(daNoteInfo, _song.notes[noteSection].mustHitSection) * gridSize); note.y = Math.floor(getYfromStrum(daStrumTime)); @@ -929,317 +271,81 @@ class ChartingState extends MusicBeatState private function generateSustain(daStrumTime:Float = 0, daNoteInfo:Int = 0, daSus:Float = 0, daNoteAlt:Float = 0, note:Note, curNoteMap:Map) { - if (daSus > 0) - { - prevNote = note; - var constSize = Std.int(gridSize / 3); - - var sustainVis:Note = ForeverAssets.generateArrow(PlayState.assetModifier, daStrumTime + (Conductor.stepCrochet * daSus) + Conductor.stepCrochet, - daNoteInfo % 4, 0, daNoteAlt, true, prevNote); - - sustainVis.setGraphicSize(constSize, getNoteVerticalSize(daSus / 2)); - sustainVis.updateHitbox(); - sustainVis.x = note.x + constSize; - sustainVis.y = note.y + (gridSize / 2); - - var sustainEnd:Note = ForeverAssets.generateArrow(PlayState.assetModifier, daStrumTime + (Conductor.stepCrochet * daSus) + Conductor.stepCrochet, - daNoteInfo % 4, 0, daNoteAlt, true, sustainVis); - sustainEnd.setGraphicSize(constSize, constSize); - sustainEnd.updateHitbox(); - sustainEnd.x = sustainVis.x; - sustainEnd.y = note.y + (sustainVis.height) + (gridSize / 2); - - // loll for later - sustainVis.rawNoteData = daNoteInfo; - sustainEnd.rawNoteData = daNoteInfo; - - curRenderedSustains.add(sustainVis); - curRenderedSustains.add(sustainEnd); - // - - // set the note at the current note map - curNoteMap.set(note, [sustainVis, sustainEnd]); - } - } - - private function regenerateSection(section:Int, placement:Float) - { - // this will be used to regenerate a box that shows what section the camera is focused on - - // oh and section information lol - var extraSize = 6; - var sectionLine:FlxSprite = new FlxSprite(FlxG.width / 2 - (gridSize * (horizontalSize / 2)) - (extraSize / 2), - placement).makeGraphic(gridSize * horizontalSize + extraSize, 2); - sectionLine.alpha = (88 / 255); - - // section camera - var sectionExtend:Float = 0; - if (_song.notes[section].mustHitSection) - sectionExtend = (gridSize * (horizontalSize / 2)); - - var sectionCamera:FlxSprite = new FlxSprite(FlxG.width / 2 - (gridSize * (horizontalSize / 2)) + (sectionExtend), - placement).makeGraphic(Std.int(gridSize * (horizontalSize / 2)), _song.notes[section].lengthInSteps * gridSize, FlxColor.fromRGB(43, 116, 219)); - sectionCamera.alpha = (88 / 255); - curRenderedSections.add(sectionCamera); - - // set up section numbers - for (i in 0...2) - { - var sectionNumber:FlxText = new FlxText(0, sectionLine.y - 12, 0, Std.string(section), 20); - // set the x of the section number - sectionNumber.x = sectionLine.x - sectionNumber.width - 5; - if (i == 1) - sectionNumber.x = sectionLine.x + sectionLine.width + 5; - - sectionNumber.setFormat(Paths.font("vcr.ttf"), 24, FlxColor.WHITE); - sectionNumber.antialiasing = false; - sectionNumber.alpha = sectionLine.alpha; - curRenderedSections.add(sectionNumber); - } - - for (i in 1...Std.int(_song.notes[section].lengthInSteps / 4)) - { - // create a smaller section stepper - var sectionStep:FlxSprite = new FlxSprite(FlxG.width / 2 - (gridSize * (horizontalSize / 2)) - (extraSize / 2), - placement + (i * (gridSize * 4))).makeGraphic(gridSize * horizontalSize + extraSize, 1); - sectionStep.alpha = sectionLine.alpha; - curRenderedSections.add(sectionStep); - } - - curRenderedSections.add(sectionLine); - } - - private function removeAllNotes() - { - curRenderedNotes.clear(); - curRenderedSustains.clear(); - } - - private function addSection(lengthInSteps:Int = 16):Void - { - var sec:SwagSection = { - lengthInSteps: lengthInSteps, - bpm: _song.bpm, - changeBPM: false, - mustHitSection: true, - sectionNotes: [], - typeOfSection: 0, - altAnim: false - }; - - _song.notes.push(sec); - } - - function getStrumTime(yPos:Float):Float - { - return FlxMath.remapToRange(yPos, 0, getChartSizeMax() * gridSize, 0, getChartSizeMax() * Conductor.stepCrochet); - } - - function getYfromStrum(strumTime:Float):Float - { - return FlxMath.remapToRange(strumTime, 0, (getChartSizeMax() * Conductor.stepCrochet), 0, (getChartSizeMax() * gridSize)); - } - - function getChartSizeMax() - { - // return the chart's length (in steps) - var totalReturn = 0; - for (sections in _song.notes) - totalReturn += sections.lengthInSteps; - return totalReturn; - } - - function getSectionfromY(location:Float) - { - // simple script for stuff to work properly - var newSection:Int = 0; - for (sections in sectionsMap.keys()) - { - // find new section loll - if ((Math.floor(location / gridSize) * gridSize) >= sectionsMap.get(sections)[2]) - newSection = sections; - } - return newSection; - } - - function adjustSide(noteData:Int, sectionTemp:Int) - { - return (_song.notes[sectionTemp].mustHitSection ? ((noteData + 4) % 8) : noteData); - } + /* + if (daSus > 0) + { + //prevNote = note; + var constSize = Std.int(gridSize / 3); - private var daSpacing:Float = 0.3; + var sustainVis:Note = new Note(daStrumTime + (Conductor.stepCrochet * daSus) + Conductor.stepCrochet, daNoteInfo % 4, daNoteAlt, prevNote, true); + sustainVis.setGraphicSize(constSize, + Math.floor(FlxMath.remapToRange((daSus / 2) - constSize, 0, Conductor.stepCrochet * verticalSize, 0, gridSize * verticalSize))); + sustainVis.updateHitbox(); + sustainVis.x = note.x + constSize; + sustainVis.y = note.y + (gridSize / 2); + + var sustainEnd:Note = new Note(daStrumTime + (Conductor.stepCrochet * daSus) + Conductor.stepCrochet, daNoteInfo % 4, daNoteAlt, sustainVis, true); + sustainEnd.setGraphicSize(constSize, constSize); + sustainEnd.updateHitbox(); + sustainEnd.x = sustainVis.x; + sustainEnd.y = note.y + (sustainVis.height) + (gridSize / 2); + + // loll for later + sustainVis.rawNoteData = daNoteInfo; + sustainEnd.rawNoteData = daNoteInfo; + + curRenderedSustains.add(sustainVis); + curRenderedSustains.add(sustainEnd); + // - function loadLevel():Void - { - trace(_song.notes); + // set the note at the current note map + curNoteMap.set(note, [sustainVis, sustainEnd]); + } + */ } - function getNotes():Array - { - var noteData:Array = []; - - for (i in _song.notes) - { - noteData.push(i.sectionNotes); - } - - return noteData; - } + ///* + var coolGrid:FlxBackdrop; + var coolGradient:FlxSprite; - function loadJson(song:String):Void + private function generateBackground() { - PlayState.SONG = Song.loadFromJson(song.toLowerCase(), song.toLowerCase()); - FlxG.resetState(); - } + coolGrid = new FlxBackdrop(null, 1, 1, true, true, 1, 1); + coolGrid.loadGraphic(Paths.image('UI/forever/base/chart editor/grid')); + coolGrid.alpha = (32 / 255); + add(coolGrid); - function loadAutosave():Void - { - PlayState.SONG = Song.parseJSONshit(FlxG.save.data.autosave); - FlxG.resetState(); + // gradient + coolGradient = FlxGradient.createGradientFlxSprite(FlxG.width, FlxG.height, + FlxColor.gradient(FlxColor.fromRGB(188, 158, 255, 200), FlxColor.fromRGB(80, 12, 108, 255), 16)); + coolGradient.alpha = (32 / 255); + add(coolGradient); } - private function saveLevel() + function adjustSide(noteData:Int, sectionTemp:Bool) { - var json = { - "song": _song - }; - - var data:String = Json.stringify(json); - - if ((data != null) && (data.length > 0)) - { - _file = new FileReference(); - _file.addEventListener(Event.COMPLETE, onSaveComplete); - _file.addEventListener(Event.CANCEL, onSaveCancel); - _file.addEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file.save(data.trim(), _song.song.toLowerCase() + ".json"); - } + return (sectionTemp ? ((noteData + 4) % 8) : noteData); } - function autosaveSong():Void + function pauseMusic() { - FlxG.save.data.autosave = Json.stringify({ - "song": _song - }); - FlxG.save.flush(); - } + songMusic.time = Math.max(songMusic.time, 0); + songMusic.time = Math.min(songMusic.time, songMusic.length); - function onSaveComplete(_):Void - { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - FlxG.log.notice("Successfully saved LEVEL DATA."); + resyncVocals(); + songMusic.pause(); + vocals.pause(); } - /** - * Called when the save file dialog is cancelled. - */ - function onSaveCancel(_):Void + function resyncVocals():Void { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - } + vocals.pause(); - /** - * Called if there is an error while saving the gameplay recording. - */ - function onSaveError(_):Void - { - _file.removeEventListener(Event.COMPLETE, onSaveComplete); - _file.removeEventListener(Event.CANCEL, onSaveCancel); - _file.removeEventListener(IOErrorEvent.IO_ERROR, onSaveError); - _file = null; - FlxG.log.error("Problem saving Level data"); + songMusic.play(); + Conductor.songPosition = songMusic.time; + vocals.time = Conductor.songPosition; + vocals.play(); } - function generateWaveform(loadedSong:String, x:Float = 0, y:Float = 0):FlxSprite - { - // generate the waveform based on gedehari's code - // https://github.com/gedehari/HaxeFlixel-Waveform-Rendering - // (yes he let me use this lol) - - var audioBuffer:AudioBuffer = AudioBuffer.fromFile('./$loadedSong'); - var tempSong = new FlxSound().loadEmbedded(Sound.fromAudioBuffer(audioBuffer), false, true); - // FlxG.sound.list.add(tempSong); - - var generatedWaveform = new FlxSprite(x, y).makeGraphic(3000, 720, FlxColor.fromRGB(0, 0, 0, 1)); - generatedWaveform.x = x - (generatedWaveform.width / 2); - generatedWaveform.y = y - (generatedWaveform.height / 2); - - generatedWaveform.angle = 360 - 90; - - var bytes:Bytes = audioBuffer.data.toBytes(); - - #if !html5 - Thread.create(function() - { - var currentTime:Float = Sys.time(); - var finishedTime:Float; - - var index:Int = 0; - var drawIndex:Int = 0; - var samplesPerCollumn:Int = Std.int(tempSong.length / 600); - - var min:Float = 0; - var max:Float = 0; - - Sys.println("Iterating"); - - while ((index * 4) < (bytes.length - 1)) - { - var byte:Int = bytes.getUInt16(index * 4); - - if (byte > 65535 / 2) - byte -= 65535; - - var sample:Float = (byte / 65535); - - if (sample > 0) - { - if (sample > max) - max = sample; - } - else if (sample < 0) - { - if (sample < min) - min = sample; - } - - if ((index % samplesPerCollumn) == 0) - { - // trace("min: " + min + ", max: " + max); - - if (drawIndex > 1280) - { - drawIndex = 0; - } - - var pixelsMin:Float = Math.abs(min * 300); - var pixelsMax:Float = max * 300; - - generatedWaveform.pixels.fillRect(new Rectangle(drawIndex, 0, 1, 720), 0xFF000000); - generatedWaveform.pixels.fillRect(new Rectangle(drawIndex, (FlxG.height / 2) - pixelsMin, 1, pixelsMin + pixelsMax), FlxColor.WHITE); - drawIndex += 1; - - min = 0; - max = 0; - } - - index += 1; - } - - finishedTime = Sys.time(); - - Sys.println("Took " + (finishedTime - currentTime) + " seconds."); - }); - #end - - // tempSong.stop(); - - return generatedWaveform; - } + // */ } diff --git a/source/gameFolder/meta/state/charting/OriginalChartingState.hx b/source/gameFolder/meta/state/charting/OriginalChartingState.hx index 98e94c629..19338fb15 100644 --- a/source/gameFolder/meta/state/charting/OriginalChartingState.hx +++ b/source/gameFolder/meta/state/charting/OriginalChartingState.hx @@ -23,6 +23,7 @@ import flixel.ui.FlxSpriteButton; import flixel.util.FlxColor; import gameFolder.gameObjects.*; import gameFolder.gameObjects.userInterface.*; +import gameFolder.gameObjects.userInterface.notes.*; import gameFolder.meta.MusicBeat.MusicBeatState; import gameFolder.meta.data.*; import gameFolder.meta.data.Conductor.BPMChangeEvent; @@ -83,7 +84,7 @@ class OriginalChartingState extends MusicBeatState **/ var curSelectedNote:Array; - var tempBpm:Int = 0; + var tempBpm:Float = 0; var vocals:FlxSound; @@ -473,7 +474,7 @@ class OriginalChartingState extends MusicBeatState }*/ function sectionStartTime():Float { - var daBPM:Int = _song.bpm; + var daBPM:Float = _song.bpm; var daPos:Float = 0; for (i in 0...curSection) { @@ -848,7 +849,7 @@ class OriginalChartingState extends MusicBeatState else { // get last bpm - var daBPM:Int = _song.bpm; + var daBPM:Float = _song.bpm; for (i in 0...curSection) if (_song.notes[i].changeBPM) daBPM = _song.notes[i].bpm; diff --git a/source/gameFolder/meta/state/charting/TestState.hx b/source/gameFolder/meta/state/charting/TestState.hx index 8ccd90b21..bc02f2308 100644 --- a/source/gameFolder/meta/state/charting/TestState.hx +++ b/source/gameFolder/meta/state/charting/TestState.hx @@ -16,9 +16,9 @@ import flixel.graphics.frames.FlxFrame; import flixel.group.FlxGroup.FlxTypedGroup; import flixel.util.FlxColor; import flixel.util.FlxGradient; -import gameFolder.gameObjects.Note; import gameFolder.gameObjects.userInterface.menu.DebugUI.UIBox; import gameFolder.gameObjects.userInterface.menu.DebugUI; +import gameFolder.gameObjects.userInterface.notes.Note; import gameFolder.meta.MusicBeat.MusicBeatState; import haxe.io.Bytes; import lime.media.AudioBuffer; diff --git a/source/gameFolder/meta/state/menus/FreeplayState.hx b/source/gameFolder/meta/state/menus/FreeplayState.hx index b0bca40ec..8119ce163 100644 --- a/source/gameFolder/meta/state/menus/FreeplayState.hx +++ b/source/gameFolder/meta/state/menus/FreeplayState.hx @@ -21,6 +21,7 @@ import gameFolder.meta.data.font.Alphabet; import lime.utils.Assets; import openfl.media.Sound; import sys.FileSystem; +import sys.thread.Thread; using StringTools; @@ -31,6 +32,7 @@ class FreeplayState extends MusicBeatState var selector:FlxText; var curSelected:Int = 0; + var curSongPlaying:Int = -1; var curDifficulty:Int = 1; var scoreText:FlxText; @@ -38,6 +40,9 @@ class FreeplayState extends MusicBeatState var lerpScore:Int = 0; var intendedScore:Int = 0; + var songThread:Thread; + var threadActive:Bool = true; + private var grpSongs:FlxTypedGroup; private var curPlaying:Bool = false; @@ -88,7 +93,7 @@ class FreeplayState extends MusicBeatState } // LOAD MUSIC - ForeverTools.resetMenuMusic(); + // ForeverTools.resetMenuMusic(); #if !html5 Discord.changePresence('FREEPLAY MENU', 'Main Menu'); @@ -211,6 +216,7 @@ class FreeplayState extends MusicBeatState if (controls.BACK) { + threadActive = false; Main.switchState(this, new MainMenuState()); } @@ -229,6 +235,8 @@ class FreeplayState extends MusicBeatState if (FlxG.sound.music != null) FlxG.sound.music.stop(); + threadActive = false; + Main.switchState(this, new PlayState()); } @@ -305,11 +313,61 @@ class FreeplayState extends MusicBeatState } // + trace("curSelected: " + curSelected); + changeDiff(); + changeSongPlaying(); + } + + function changeSongPlaying() + { + if (songThread == null) + { + songThread = Thread.create(function() + { + while (true) + { + if (!threadActive) + { + trace("Killing thread"); + return; + } + + var index:Null = Thread.readMessage(false); + if (index != null) + { + if (index == curSelected && index != curSongPlaying) + { + trace("Loading index " + index); + + var inst:Sound = Sound.fromFile('./' + Paths.inst(songs[curSelected].songName)); + + if (index == curSelected && threadActive) + { + FlxG.sound.playMusic(inst); + + if (FlxG.sound.music.fadeTween != null) + FlxG.sound.music.fadeTween.cancel(); + + FlxG.sound.music.volume = 0.0; + FlxG.sound.music.fadeIn(1.0, 0.0, 1.0); + + curSongPlaying = curSelected; + } + else + trace("Nevermind, skipping " + index); + } + else + trace("Skipping " + index); + } + } + }); + } + + songThread.sendMessage(curSelected); } var playingSongs:Array = []; - var songPlayThread:sys.thread.Thread; } class SongMetadata diff --git a/source/gameFolder/meta/state/menus/MainMenuState.hx b/source/gameFolder/meta/state/menus/MainMenuState.hx index 0b0ff7074..33ffdf65c 100644 --- a/source/gameFolder/meta/state/menus/MainMenuState.hx +++ b/source/gameFolder/meta/state/menus/MainMenuState.hx @@ -59,7 +59,7 @@ class MainMenuState extends MusicBeatState bg.setGraphicSize(Std.int(bg.width * 1.1)); bg.updateHitbox(); bg.screenCenter(); - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; add(bg); magenta = new FlxSprite(-85).loadGraphic(Paths.image('menus/base/menuDesat')); @@ -69,7 +69,7 @@ class MainMenuState extends MusicBeatState magenta.updateHitbox(); magenta.screenCenter(); magenta.visible = false; - magenta.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + magenta.antialiasing = true; magenta.color = 0xFFfd719b; add(magenta); @@ -109,7 +109,7 @@ class MainMenuState extends MusicBeatState // actually add the item menuItems.add(menuItem); menuItem.scrollFactor.set(); - menuItem.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + menuItem.antialiasing = true; menuItem.updateHitbox(); /* diff --git a/source/gameFolder/meta/state/menus/OptionsMenuState.hx b/source/gameFolder/meta/state/menus/OptionsMenuState.hx index 3a9d63a1c..5067913bd 100644 --- a/source/gameFolder/meta/state/menus/OptionsMenuState.hx +++ b/source/gameFolder/meta/state/menus/OptionsMenuState.hx @@ -43,6 +43,8 @@ class OptionsMenuState extends MusicBeatState If you plug in a value, the script will run when the option is hovered over. */ + // NOTE : Make sure to check Init.hx if you are trying to add options. + #if !html5 Discord.changePresence('OPTIONS MENU', 'Main Menu'); #end @@ -87,10 +89,12 @@ class OptionsMenuState extends MusicBeatState ['', null], ['Disable Antialiasing', getFromOption], ['No Camera Note Movement', getFromOption], + ['SM-like Judgements', getFromOption], ['', null], ['Accessibility Settings', null], ['', null], ['Filter', getFromOption], + ["Stage Darkness", getFromOption], ['Reduced Movements', getFromOption], // this shouldn't be get from option, just testing ['', null], @@ -120,7 +124,7 @@ class OptionsMenuState extends MusicBeatState bg.updateHitbox(); bg.screenCenter(); bg.color = 0xCE64DF; - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; add(bg); infoText = new FlxText(5, FlxG.height - 24, 0, "", 32); @@ -387,7 +391,7 @@ class OptionsMenuState extends MusicBeatState case 1: // selector var selector:Selector = new Selector(10, letter.y, letter.text, Init.gameSettings.get(letter.text)[4], - (letter.text == 'Framerate Cap') ? true : false); + (letter.text == 'Framerate Cap') ? true : false, (letter.text == 'Stage Darkness') ? true : false); extrasMap.set(letter, selector); default: @@ -454,8 +458,58 @@ class OptionsMenuState extends MusicBeatState function updateSelector(selector:Selector, updateBy:Int) { var fps = selector.fpsCap; - if (!fps) + var bgdark = selector.darkBG; + if (fps) { + // bro I dont even know if the engine works in html5 why am I even doing this + // lazily hardcoded fps cap + var originalFPS = Init.trueSettings.get(activeSubgroup.members[curSelection].text); + var increase = 15 * updateBy; + if (originalFPS + increase < 30) + increase = 0; + // high fps cap + if (originalFPS + increase > 360) + increase = 0; + + if (updateBy == -1) + selector.selectorPlay('left', 'press'); + else + selector.selectorPlay('right', 'press'); + + FlxG.sound.play(Paths.sound('scrollMenu')); + + originalFPS += increase; + selector.chosenOptionString = Std.string(originalFPS); + selector.optionChosen.text = Std.string(originalFPS); + Init.trueSettings.set(activeSubgroup.members[curSelection].text, originalFPS); + Init.saveSettings(); + } + else if (bgdark) + { + // lazily hardcoded darkness cap + var originaldark = Init.trueSettings.get(activeSubgroup.members[curSelection].text); + var increase = 5 * updateBy; + if (originaldark + increase < 0) + increase = 0; + // high darkness cap + if (originaldark + increase > 100) + increase = 0; + + if (updateBy == -1) + selector.selectorPlay('left', 'press'); + else + selector.selectorPlay('right', 'press'); + + FlxG.sound.play(Paths.sound('scrollMenu')); + + originaldark += increase; + selector.chosenOptionString = Std.string(originaldark); + selector.optionChosen.text = Std.string(originaldark); + Init.trueSettings.set(activeSubgroup.members[curSelection].text, originaldark); + Init.saveSettings(); + } + else if (!fps && !bgdark) + { // get the current option as a number var storedNumber:Int = 0; for (curOption in 0...selector.options.length) @@ -483,30 +537,6 @@ class OptionsMenuState extends MusicBeatState Init.trueSettings.set(activeSubgroup.members[curSelection].text, selector.chosenOptionString); Init.saveSettings(); } - else - { // bro I dont even know if the engine works in html5 why am I even doing this - // lazily hardcoded fps cap - var originalFPS = Init.trueSettings.get(activeSubgroup.members[curSelection].text); - var increase = 15 * updateBy; - if (originalFPS + increase < 30) - increase = 0; - // high fps cap - if (originalFPS + increase > 360) - increase = 0; - - if (updateBy == -1) - selector.selectorPlay('left', 'press'); - else - selector.selectorPlay('right', 'press'); - - FlxG.sound.play(Paths.sound('scrollMenu')); - - originalFPS += increase; - selector.chosenOptionString = Std.string(originalFPS); - selector.optionChosen.text = Std.string(originalFPS); - Init.trueSettings.set(activeSubgroup.members[curSelection].text, originalFPS); - Init.saveSettings(); - } } public function callNewGroup() diff --git a/source/gameFolder/meta/state/menus/StoryMenuState.hx b/source/gameFolder/meta/state/menus/StoryMenuState.hx index 2774749c9..df2ba37fb 100644 --- a/source/gameFolder/meta/state/menus/StoryMenuState.hx +++ b/source/gameFolder/meta/state/menus/StoryMenuState.hx @@ -102,7 +102,7 @@ class StoryMenuState extends MusicBeatState grpWeekText.add(weekThing); weekThing.screenCenter(X); - weekThing.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + weekThing.antialiasing = true; // weekThing.updateHitbox(); // Needs an offset thingie @@ -113,7 +113,7 @@ class StoryMenuState extends MusicBeatState lock.animation.addByPrefix('lock', 'lock'); lock.animation.play('lock'); lock.ID = i; - lock.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + lock.antialiasing = true; grpLocks.add(lock); } } @@ -123,7 +123,7 @@ class StoryMenuState extends MusicBeatState for (char in 0...3) { var weekCharacterThing:MenuCharacter = new MenuCharacter((FlxG.width * 0.25) * (1 + char) - 150, weekCharacters[curWeek][char]); - weekCharacterThing.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + weekCharacterThing.antialiasing = true; switch (weekCharacterThing.character) { case 'dad': diff --git a/source/gameFolder/meta/subState/OptionsSubstate.hx b/source/gameFolder/meta/subState/OptionsSubstate.hx index 8b564ac6f..4ef07c2bb 100644 --- a/source/gameFolder/meta/subState/OptionsSubstate.hx +++ b/source/gameFolder/meta/subState/OptionsSubstate.hx @@ -33,7 +33,7 @@ class OptionsSubstate extends MusicBeatSubState bg.updateHitbox(); bg.screenCenter(); bg.color = 0xCE64DF; - bg.antialiasing = (!Init.trueSettings.get('Disable Antialiasing')); + bg.antialiasing = true; add(bg); super.create(); diff --git a/source/openfl/display/GraphicsShader.hx b/source/openfl/display/GraphicsShader.hx new file mode 100644 index 000000000..6efc342dc --- /dev/null +++ b/source/openfl/display/GraphicsShader.hx @@ -0,0 +1,134 @@ +package openfl.display; + +import openfl.utils.ByteArray; + +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +class GraphicsShader extends Shader +{ + public var bitmap:ShaderInput; + + var glVertexHeader:String = "attribute float openfl_Alpha; + attribute vec4 openfl_ColorMultiplier; + attribute vec4 openfl_ColorOffset; + attribute vec4 openfl_Position; + attribute vec2 openfl_TextureCoord; + + varying float openfl_Alphav; + varying vec4 openfl_ColorMultiplierv; + varying vec4 openfl_ColorOffsetv; + varying vec2 openfl_TextureCoordv; + + uniform mat4 openfl_Matrix; + uniform bool openfl_HasColorTransform; + uniform vec2 openfl_TextureSize;"; + + var glVertexBody:String = "openfl_Alphav = openfl_Alpha; + openfl_TextureCoordv = openfl_TextureCoord; + + if (openfl_HasColorTransform) { + + openfl_ColorMultiplierv = openfl_ColorMultiplier; + openfl_ColorOffsetv = openfl_ColorOffset / 255.0; + + } + + gl_Position = openfl_Matrix * openfl_Position;"; + + var glFragmentHeader:String = "varying float openfl_Alphav; + varying vec4 openfl_ColorMultiplierv; + varying vec4 openfl_ColorOffsetv; + varying vec2 openfl_TextureCoordv; + + uniform bool openfl_HasColorTransform; + uniform vec2 openfl_TextureSize; + uniform sampler2D bitmap;"; + + var glFragmentBody:String = "vec4 color = texture2D (bitmap, openfl_TextureCoordv); + + if (color.a == 0.0) { + + gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0); + + } else if (openfl_HasColorTransform) { + + color = vec4 (color.rgb / color.a, color.a); + + mat4 colorMultiplier = mat4 (0); + colorMultiplier[0][0] = openfl_ColorMultiplierv.x; + colorMultiplier[1][1] = openfl_ColorMultiplierv.y; + colorMultiplier[2][2] = openfl_ColorMultiplierv.z; + colorMultiplier[3][3] = 1.0; // openfl_ColorMultiplierv.w; + + color = clamp (openfl_ColorOffsetv + (color * colorMultiplier), 0.0, 1.0); + + if (color.a > 0.0) { + + gl_FragColor = vec4 (color.rgb * color.a * openfl_Alphav, color.a * openfl_Alphav); + + } else { + + gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0); + + } + + } else { + + gl_FragColor = color * openfl_Alphav; + + }"; + + public function new(glVertexSource:String = "", glFragmentSource:String = "", initNow:Bool = true) + { + super(null); + + if (glVertexSource != "") + this.glVertexSource = glVertexSource; + else + this.glVertexSource = "#pragma header + void main(void) { + #pragma body + }"; + + if (glFragmentSource != "") + this.glFragmentSource = glFragmentSource; + else + this.glFragmentSource = "#pragma header + void main(void) { + #pragma body + }"; + + if (initNow) + __initGL(); + } + + override public function __initGL() + { + processSource(); + + __isGenerated = true; + super.__initGL(); + + bitmap = data.bitmap; + } + + function processSource() + { + if (glVertexSource != null || glFragmentSource != null) + { + if (glFragmentSource != null && glFragmentHeader != null && glFragmentBody != null) + { + glFragmentSource = StringTools.replace(glFragmentSource, "#pragma header", glFragmentHeader); + glFragmentSource = StringTools.replace(glFragmentSource, "#pragma body", glFragmentBody); + } + + if (glVertexSource != null && glVertexHeader != null && glVertexBody != null) + { + glVertexSource = StringTools.replace(glVertexSource, "#pragma header", glVertexHeader); + glVertexSource = StringTools.replace(glVertexSource, "#pragma body", glVertexBody); + } + } + } +} diff --git a/source/openfl/display/Shader.hx b/source/openfl/display/Shader.hx new file mode 100644 index 000000000..7cec52148 --- /dev/null +++ b/source/openfl/display/Shader.hx @@ -0,0 +1,951 @@ +package openfl.display; + +#if !flash +import openfl.display._internal.ShaderBuffer; +import openfl.display3D.Context3D; +import openfl.display3D.Program3D; +import openfl.display3D._internal.GLProgram; +import openfl.display3D._internal.GLShader; +import openfl.utils.ByteArray; +import openfl.utils._internal.Float32Array; +import openfl.utils._internal.Log; + +/** + // TODO: Document GLSL Shaders + A Shader instance represents a Pixel Bender shader kernel in ActionScript. + To use a shader in your application, you create a Shader instance for it. + You then use that Shader instance in the appropriate way according to the + effect you want to create. For example, to use the shader as a filter, you + assign the Shader instance to the `shader` property of a ShaderFilter + object. + A shader defines a function that executes on all the pixels in an image, + one pixel at a time. The result of each call to the function is the output + color at that pixel coordinate in the image. A shader can specify one or + more input images, which are images whose content can be used in + determining the output of the function. A shader can also specify one or + more parameters, which are input values that can be used in calculating + the function output. In a single shader execution, the input and parameter + values are constant. The only thing that varies is the coordinate of the + pixel whose color is the function result. Shader function calls for + multiple output pixel coordinates execute in parallel to improve shader + execution performance. + + The shader bytecode can be loaded at run time using a URLLoader instance. + The following example demonstrates loading a shader bytecode file at run + time and linking it to a Shader instance. + + ```as3 + var loader:URLLoader = new URLLoader(); + loader.dataFormat = URLLoaderDataFormat.BINARY; + loader.addEventListener(Event.COMPLETE, onLoadComplete); + loader.load(new URLRequest("myShader.pbj")); + var shader:Shader; + + function onLoadComplete(event:Event):void { + // Create a new shader and set the loaded data as its bytecode + shader = new Shader(); + shader.byteCode = loader.data; + + // You can also pass the bytecode to the Shader() constructor like this: + // shader = new Shader(loader.data); + + // do something with the shader + } + ``` + + You can also embed the shader into the SWF at compile time using the + `[Embed]` metadata tag. The `[Embed]` metadata tag is only available if + you use the Flex SDK to compile the SWF. The `[Embed]` tag's `source` + parameter points to the shader file, and its `mimeType` parameter is + `"application/octet-stream"`, as in this example: + + ```as3 + [Embed(source="myShader.pbj", mimeType="application/octet-stream)] var MyShaderClass:Class; + + // ... + + // create a new shader and set the embedded shader as its bytecode var + shaderShader = new Shader(); + shader.byteCode = new MyShaderClass(); + + // You can also pass the bytecode to the Shader() constructor like this: + // var shader:Shader = new Shader(new MyShaderClass()); + + // do something with the shader + ``` + + In either case, you link the raw shader (the `URLLoader.data` property or + an instance of the `[Embed]` data class) to the Shader instance. As the + previous examples demonstrate, you can do this in two ways. You can pass + the shader bytecode as an argument to the `Shader()` constructor. + Alternatively, you can set it as the Shader instance's `byteCode` + property. + + Once a Shader instance is created, it can be used in one of several ways: + + * A shader fill: The output of the shader is used as a fill for content + drawn with the drawing API. Pass the Shader instance as an argument to the + `Graphics.beginShaderFill()` method. + * A shader filter: The output of the shader is used as a graphic filter + applied to a display object. Assign the Shader instance to the `shader` + property of a ShaderFilter instance. + * A blend mode: The output of the shader is rendered as the blending + between two overlapping display objects. Assign the Shader instance to the + `blendShader` property of the upper of the two display objects. + * Background shader processing: The shader executes in the background, + avoiding the possibility of freezing the display, and dispatches an event + when processing is complete. Assign the Shader instance to the `shader` + property of a ShaderJob instance. + + Shader fills, filters, and blends are not supported under GPU rendering. + + **Mobile Browser Support:** This feature is not supported in mobile + browsers. + + _AIR profile support:_ This feature is supported on all desktop operating + systems, but it is not supported on all mobile devices. It is not + supported on AIR for TV devices. See + AIR Profile Support for more information regarding API support across + multiple profiles. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(openfl.display3D.Context3D) +@:access(openfl.display3D.Program3D) +@:access(openfl.display.ShaderInput) +@:access(openfl.display.ShaderParameter) +// #if (!display && !macro) +#if !macro +@:autoBuild(openfl.utils._internal.ShaderMacro.build()) +#end +class Shader +{ + /** + The raw shader bytecode for this Shader instance. + **/ + public var byteCode(null, default):ByteArray; + + /** + Provides access to parameters, input images, and metadata for the + Shader instance. ShaderParameter objects representing parameters for + the shader, ShaderInput objects representing the input images for the + shader, and other values representing the shader's metadata are + dynamically added as properties of the `data` property object when the + Shader instance is created. Those properties can be used to introspect + the shader and to set parameter and input values. + For information about accessing and manipulating the dynamic + properties of the `data` object, see the ShaderData class description. + **/ + public var data(get, set):ShaderData; + + /** + Get or set the fragment source used when compiling with GLSL. + + This property is not available on the Flash target. + **/ + public var glFragmentSource(get, set):String; + + /** + The compiled GLProgram if available. + + This property is not available on the Flash target. + **/ + @SuppressWarnings("checkstyle:Dynamic") public var glProgram(default, null):GLProgram; + + /** + Get or set the vertex source used when compiling with GLSL. + + This property is not available on the Flash target. + **/ + public var glVertexSource(get, set):String; + + /** + The precision of math operations performed by the shader. + The set of possible values for the `precisionHint` property is defined + by the constants in the ShaderPrecision class. + + The default value is `ShaderPrecision.FULL`. Setting the precision to + `ShaderPrecision.FAST` can speed up math operations at the expense of + precision. + + Full precision mode (`ShaderPrecision.FULL`) computes all math + operations to the full width of the IEEE 32-bit floating standard and + provides consistent behavior on all platforms. In this mode, some math + operations such as trigonometric and exponential functions can be + slow. + + Fast precision mode (`ShaderPrecision.FAST`) is designed for maximum + performance but does not work consistently on different platforms and + individual CPU configurations. In many cases, this level of precision + is sufficient to create graphic effects without visible artifacts. + + The precision mode selection affects the following shader operations. + These operations are faster on an Intel processor with the SSE + instruction set: + + * `sin(x)` + * `cos(x)` + * `tan(x)` + * `asin(x)` + * `acos(x)` + * `atan(x)` + * `atan(x, y)` + * `exp(x)` + * `exp2(x)` + * `log(x)` + * `log2(x)` + * `pow(x, y)` + * `reciprocal(x)` + * `sqrt(x)` + **/ + public var precisionHint:ShaderPrecision; + + /** + The compiled Program3D if available. + + This property is not available on the Flash target. + **/ + public var program:Program3D; + + @:noCompletion private var __alpha:ShaderParameter; + @:noCompletion private var __bitmap:ShaderInput; + @:noCompletion private var __colorMultiplier:ShaderParameter; + @:noCompletion private var __colorOffset:ShaderParameter; + @:noCompletion private var __context:Context3D; + @:noCompletion private var __data:ShaderData; + @:noCompletion private var __glFragmentSource:String; + @:noCompletion private var __glSourceDirty:Bool; + @:noCompletion private var __glVertexSource:String; + @:noCompletion private var __hasColorTransform:ShaderParameter; + @:noCompletion private var __inputBitmapData:Array>; + @:noCompletion private var __isGenerated:Bool; + @:noCompletion private var __matrix:ShaderParameter; + @:noCompletion private var __numPasses:Int; + @:noCompletion private var __paramBool:Array>; + @:noCompletion private var __paramFloat:Array>; + @:noCompletion private var __paramInt:Array>; + @:noCompletion private var __position:ShaderParameter; + @:noCompletion private var __textureCoord:ShaderParameter; + @:noCompletion private var __texture:ShaderInput; + @:noCompletion private var __textureSize:ShaderParameter; + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperties(Shader.prototype, { + "data": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_data (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_data (v); }") + }, + "glFragmentSource": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_glFragmentSource (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_glFragmentSource (v); }") + }, + "glVertexSource": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_glVertexSource (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_glVertexSource (v); }") + }, + }); + } + #end + + /** + Creates a new Shader instance. + + @param code The raw shader bytecode to link to the Shader. + **/ + public function new(code:ByteArray = null) + { + byteCode = code; + precisionHint = FULL; + + __glSourceDirty = true; + __numPasses = 1; + __data = new ShaderData(code); + } + + @:noCompletion private function __clearUseArray():Void + { + for (parameter in __paramBool) + { + parameter.__useArray = false; + } + + for (parameter in __paramFloat) + { + parameter.__useArray = false; + } + + for (parameter in __paramInt) + { + parameter.__useArray = false; + } + } + + // private function __clone ():Shader { + // var classType = Type.getClass (this); + // var shader = Type.createInstance (classType, []); + // for (input in __inputBitmapData) { + // if (input.input != null) { + // var field = Reflect.field (shader.data, input.name); + // field.channels = input.channels; + // field.height = input.height; + // field.input = input.input; + // field.smoothing = input.smoothing; + // field.width = input.width; + // } + // } + // for (param in __paramBool) { + // if (param.value != null) { + // Reflect.field (shader.data, param.name).value = param.value.copy (); + // } + // } + // for (param in __paramFloat) { + // if (param.value != null) { + // Reflect.field (shader.data, param.name).value = param.value.copy (); + // } + // } + // for (param in __paramInt) { + // if (param.value != null) { + // Reflect.field (shader.data, param.name).value = param.value.copy (); + // } + // } + // return shader; + // } + @:noCompletion private function __createGLShader(source:String, type:Int):GLShader + { + var gl = __context.gl; + + var shader = gl.createShader(type); + gl.shaderSource(shader, source); + gl.compileShader(shader); + + if (gl.getShaderParameter(shader, gl.COMPILE_STATUS) == 0) + { + var message = (type == gl.VERTEX_SHADER) ? "Error compiling vertex shader" : "Error compiling fragment shader"; + message += "\n" + gl.getShaderInfoLog(shader); + message += "\n" + source; + Log.error(message); + } + + return shader; + } + + @:noCompletion private function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram + { + var gl = __context.gl; + + var vertexShader = __createGLShader(vertexSource, gl.VERTEX_SHADER); + var fragmentShader = __createGLShader(fragmentSource, gl.FRAGMENT_SHADER); + + var program = gl.createProgram(); + + // Fix support for drivers that don't draw if attribute 0 is disabled + for (param in __paramFloat) + { + if (param.name.indexOf("Position") > -1 && StringTools.startsWith(param.name, "openfl_")) + { + gl.bindAttribLocation(program, 0, param.name); + break; + } + } + + gl.attachShader(program, vertexShader); + gl.attachShader(program, fragmentShader); + gl.linkProgram(program); + + if (gl.getProgramParameter(program, gl.LINK_STATUS) == 0) + { + var message = "Unable to initialize the shader program"; + message += "\n" + gl.getProgramInfoLog(program); + Log.error(message); + } + + return program; + } + + @:noCompletion private function __disable():Void + { + if (program != null) + { + __disableGL(); + } + } + + @:noCompletion private function __disableGL():Void + { + var gl = __context.gl; + + var textureCount = 0; + + for (input in __inputBitmapData) + { + input.__disableGL(__context, textureCount); + textureCount++; + } + + for (parameter in __paramBool) + { + parameter.__disableGL(__context); + } + + for (parameter in __paramFloat) + { + parameter.__disableGL(__context); + } + + for (parameter in __paramInt) + { + parameter.__disableGL(__context); + } + + __context.__bindGLArrayBuffer(null); + + #if lime + if (__context.__context.type == OPENGL) + { + gl.disable(gl.TEXTURE_2D); + } + #end + } + + @:noCompletion private function __enable():Void + { + __init(); + + if (program != null) + { + __enableGL(); + } + } + + @:noCompletion private function __enableGL():Void + { + var textureCount = 0; + + var gl = __context.gl; + + for (input in __inputBitmapData) + { + gl.uniform1i(input.index, textureCount); + textureCount++; + } + + #if lime + if (__context.__context.type == OPENGL && textureCount > 0) + { + gl.enable(gl.TEXTURE_2D); + } + #end + } + + @:noCompletion private function __init():Void + { + if (__data == null) + { + __data = cast new ShaderData(null); + } + + if (__glFragmentSource != null && __glVertexSource != null && (program == null || __glSourceDirty)) + { + __initGL(); + } + } + + @:noCompletion private function __initGL():Void + { + if (__glSourceDirty || __paramBool == null) + { + __glSourceDirty = false; + program = null; + + __inputBitmapData = new Array(); + __paramBool = new Array(); + __paramFloat = new Array(); + __paramInt = new Array(); + + __processGLData(glVertexSource, "attribute"); + __processGLData(glVertexSource, "uniform"); + __processGLData(glFragmentSource, "uniform"); + } + + if (__context != null && program == null) + { + var gl = __context.gl; + + var prefix = "#ifdef GL_ES + " + + (precisionHint == FULL ? "#ifdef GL_FRAGMENT_PRECISION_HIGH + precision highp float; + #else + precision mediump float; + #endif" : "precision lowp float;") + + " + #endif + "; + + var vertex = prefix + glVertexSource; + var fragment = prefix + glFragmentSource; + + var id = vertex + fragment; + + if (__context.__programs.exists(id)) + { + program = __context.__programs.get(id); + } + else + { + program = __context.createProgram(GLSL); + + // TODO + // program.uploadSources (vertex, fragment); + program.__glProgram = __createGLProgram(vertex, fragment); + + __context.__programs.set(id, program); + } + + if (program != null) + { + glProgram = program.__glProgram; + + for (input in __inputBitmapData) + { + if (input.__isUniform) + { + input.index = gl.getUniformLocation(glProgram, input.name); + } + else + { + input.index = gl.getAttribLocation(glProgram, input.name); + } + } + + for (parameter in __paramBool) + { + if (parameter.__isUniform) + { + parameter.index = gl.getUniformLocation(glProgram, parameter.name); + } + else + { + parameter.index = gl.getAttribLocation(glProgram, parameter.name); + } + } + + for (parameter in __paramFloat) + { + if (parameter.__isUniform) + { + parameter.index = gl.getUniformLocation(glProgram, parameter.name); + } + else + { + parameter.index = gl.getAttribLocation(glProgram, parameter.name); + } + } + + for (parameter in __paramInt) + { + if (parameter.__isUniform) + { + parameter.index = gl.getUniformLocation(glProgram, parameter.name); + } + else + { + parameter.index = gl.getAttribLocation(glProgram, parameter.name); + } + } + } + } + } + + @:noCompletion private function __processGLData(source:String, storageType:String):Void + { + var lastMatch = 0, position, regex, name, type; + + if (storageType == "uniform") + { + regex = ~/uniform ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/; + } + else + { + regex = ~/attribute ([A-Za-z0-9]+) ([A-Za-z0-9_]+)/; + } + + while (regex.matchSub(source, lastMatch)) + { + type = regex.matched(1); + name = regex.matched(2); + + if (StringTools.startsWith(name, "gl_")) + { + continue; + } + + var isUniform = (storageType == "uniform"); + + if (StringTools.startsWith(type, "sampler")) + { + var input = new ShaderInput(); + input.name = name; + input.__isUniform = isUniform; + __inputBitmapData.push(input); + + switch (name) + { + case "openfl_Texture": + __texture = input; + case "bitmap": + __bitmap = input; + default: + } + + Reflect.setField(__data, name, input); + //if (__isGenerated) Reflect.setField(this, name, input); + } + else if (!Reflect.hasField(__data, name) || Reflect.field(__data, name) == null) + { + var parameterType:ShaderParameterType = switch (type) + { + case "bool": BOOL; + case "double", "float": FLOAT; + case "int", "uint": INT; + case "bvec2": BOOL2; + case "bvec3": BOOL3; + case "bvec4": BOOL4; + case "ivec2", "uvec2": INT2; + case "ivec3", "uvec3": INT3; + case "ivec4", "uvec4": INT4; + case "vec2", "dvec2": FLOAT2; + case "vec3", "dvec3": FLOAT3; + case "vec4", "dvec4": FLOAT4; + case "mat2", "mat2x2": MATRIX2X2; + case "mat2x3": MATRIX2X3; + case "mat2x4": MATRIX2X4; + case "mat3x2": MATRIX3X2; + case "mat3", "mat3x3": MATRIX3X3; + case "mat3x4": MATRIX3X4; + case "mat4x2": MATRIX4X2; + case "mat4x3": MATRIX4X3; + case "mat4", "mat4x4": MATRIX4X4; + default: null; + } + + var length = switch (parameterType) + { + case BOOL2, INT2, FLOAT2: 2; + case BOOL3, INT3, FLOAT3: 3; + case BOOL4, INT4, FLOAT4, MATRIX2X2: 4; + case MATRIX3X3: 9; + case MATRIX4X4: 16; + default: 1; + } + + var arrayLength = switch (parameterType) + { + case MATRIX2X2: 2; + case MATRIX3X3: 3; + case MATRIX4X4: 4; + default: 1; + } + + switch (parameterType) + { + case BOOL, BOOL2, BOOL3, BOOL4: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + parameter.__isBool = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramBool.push(parameter); + + if (name == "openfl_HasColorTransform") + { + __hasColorTransform = parameter; + } + + Reflect.setField(__data, name, parameter); + //if (__isGenerated) Reflect.setField(this, name, parameter); + + case INT, INT2, INT3, INT4: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + parameter.__isInt = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramInt.push(parameter); + Reflect.setField(__data, name, parameter); + //if (__isGenerated) Reflect.setField(this, name, parameter); + + default: + var parameter = new ShaderParameter(); + parameter.name = name; + parameter.type = parameterType; + parameter.__arrayLength = arrayLength; + #if lime + if (arrayLength > 0) parameter.__uniformMatrix = new Float32Array(arrayLength * arrayLength); + #end + parameter.__isFloat = true; + parameter.__isUniform = isUniform; + parameter.__length = length; + __paramFloat.push(parameter); + + if (StringTools.startsWith(name, "openfl_")) + { + switch (name) + { + case "openfl_Alpha": __alpha = parameter; + case "openfl_ColorMultiplier": __colorMultiplier = parameter; + case "openfl_ColorOffset": __colorOffset = parameter; + case "openfl_Matrix": __matrix = parameter; + case "openfl_Position": __position = parameter; + case "openfl_TextureCoord": __textureCoord = parameter; + case "openfl_TextureSize": __textureSize = parameter; + default: + } + } + + Reflect.setField(__data, name, parameter); + //if (__isGenerated) Reflect.setField(this, name, parameter); + } + } + + position = regex.matchedPos(); + lastMatch = position.pos + position.len; + } + } + + @:noCompletion private function __update():Void + { + if (program != null) + { + __updateGL(); + } + } + + @:noCompletion private function __updateFromBuffer(shaderBuffer:ShaderBuffer, bufferOffset:Int):Void + { + if (program != null) + { + __updateGLFromBuffer(shaderBuffer, bufferOffset); + } + } + + @:noCompletion private function __updateGL():Void + { + var textureCount = 0; + + for (input in __inputBitmapData) + { + input.__updateGL(__context, textureCount); + textureCount++; + } + + for (parameter in __paramBool) + { + parameter.__updateGL(__context); + } + + for (parameter in __paramFloat) + { + parameter.__updateGL(__context); + } + + for (parameter in __paramInt) + { + parameter.__updateGL(__context); + } + } + + @:noCompletion private function __updateGLFromBuffer(shaderBuffer:ShaderBuffer, bufferOffset:Int):Void + { + var textureCount = 0; + var input, inputData, inputFilter, inputMipFilter, inputWrap; + + for (i in 0...shaderBuffer.inputCount) + { + input = shaderBuffer.inputRefs[i]; + inputData = shaderBuffer.inputs[i]; + inputFilter = shaderBuffer.inputFilter[i]; + inputMipFilter = shaderBuffer.inputMipFilter[i]; + inputWrap = shaderBuffer.inputWrap[i]; + + if (inputData != null) + { + input.__updateGL(__context, textureCount, inputData, inputFilter, inputMipFilter, inputWrap); + textureCount++; + } + } + + var gl = __context.gl; + + if (shaderBuffer.paramDataLength > 0) + { + if (shaderBuffer.paramDataBuffer == null) + { + shaderBuffer.paramDataBuffer = gl.createBuffer(); + } + + // Log.verbose ("bind param data buffer (length: " + shaderBuffer.paramData.length + ") (" + shaderBuffer.paramCount + ")"); + + __context.__bindGLArrayBuffer(shaderBuffer.paramDataBuffer); + gl.bufferData(gl.ARRAY_BUFFER, shaderBuffer.paramData, gl.DYNAMIC_DRAW); + } + else + { + // Log.verbose ("bind buffer null"); + + __context.__bindGLArrayBuffer(null); + } + + var boolIndex = 0; + var floatIndex = 0; + var intIndex = 0; + + var boolCount = shaderBuffer.paramBoolCount; + var floatCount = shaderBuffer.paramFloatCount; + var paramData = shaderBuffer.paramData; + + var boolRef, floatRef, intRef, hasOverride; + var overrideBoolValue:Array = null, + overrideFloatValue:Array = null, + overrideIntValue:Array = null; + + for (i in 0...shaderBuffer.paramCount) + { + hasOverride = false; + + if (i < boolCount) + { + boolRef = shaderBuffer.paramRefs_Bool[boolIndex]; + + for (j in 0...shaderBuffer.overrideBoolCount) + { + if (boolRef.name == shaderBuffer.overrideBoolNames[j]) + { + overrideBoolValue = shaderBuffer.overrideBoolValues[j]; + hasOverride = true; + break; + } + } + + if (hasOverride) + { + boolRef.__updateGL(__context, overrideBoolValue); + } + else + { + boolRef.__updateGLFromBuffer(__context, paramData, shaderBuffer.paramPositions[i], shaderBuffer.paramLengths[i], bufferOffset); + } + + boolIndex++; + } + else if (i < boolCount + floatCount) + { + floatRef = shaderBuffer.paramRefs_Float[floatIndex]; + + for (j in 0...shaderBuffer.overrideFloatCount) + { + if (floatRef.name == shaderBuffer.overrideFloatNames[j]) + { + overrideFloatValue = shaderBuffer.overrideFloatValues[j]; + hasOverride = true; + break; + } + } + + if (hasOverride) + { + floatRef.__updateGL(__context, overrideFloatValue); + } + else + { + floatRef.__updateGLFromBuffer(__context, paramData, shaderBuffer.paramPositions[i], shaderBuffer.paramLengths[i], bufferOffset); + } + + floatIndex++; + } + else + { + intRef = shaderBuffer.paramRefs_Int[intIndex]; + + for (j in 0...shaderBuffer.overrideIntCount) + { + if (intRef.name == shaderBuffer.overrideIntNames[j]) + { + overrideIntValue = cast shaderBuffer.overrideIntValues[j]; + hasOverride = true; + break; + } + } + + if (hasOverride) + { + intRef.__updateGL(__context, overrideIntValue); + } + else + { + intRef.__updateGLFromBuffer(__context, paramData, shaderBuffer.paramPositions[i], shaderBuffer.paramLengths[i], bufferOffset); + } + + intIndex++; + } + } + } + + // Get & Set Methods + @:noCompletion private function get_data():ShaderData + { + if (__glSourceDirty || __data == null) + { + __init(); + } + + return __data; + } + + @:noCompletion private function set_data(value:ShaderData):ShaderData + { + return __data = cast value; + } + + @:noCompletion private function get_glFragmentSource():String + { + return __glFragmentSource; + } + + @:noCompletion private function set_glFragmentSource(value:String):String + { + if (value != __glFragmentSource) + { + __glSourceDirty = true; + } + + return __glFragmentSource = value; + } + + @:noCompletion private function get_glVertexSource():String + { + return __glVertexSource; + } + + @:noCompletion private function set_glVertexSource(value:String):String + { + if (value != __glVertexSource) + { + __glSourceDirty = true; + } + + return __glVertexSource = value; + } +} +#else +typedef Shader = flash.display.Shader; +#end diff --git a/source/openfl/filters/ColorMatrixFilter.hx b/source/openfl/filters/ColorMatrixFilter.hx new file mode 100644 index 000000000..a3973af3c --- /dev/null +++ b/source/openfl/filters/ColorMatrixFilter.hx @@ -0,0 +1,316 @@ +package openfl.filters; + +#if !flash +import openfl.display.BitmapData; +import openfl.display.DisplayObjectRenderer; +import openfl.display.Shader; +import openfl.geom.Point; +import openfl.geom.Rectangle; +#if lime +import lime._internal.graphics.ImageCanvasUtil; // TODO +import lime.math.RGBA; +#end + +/** + The ColorMatrixFilter class lets you apply a 4 x 5 matrix transformation + on the RGBA color and alpha values of every pixel in the input image to + produce a result with a new set of RGBA color and alpha values. It allows + saturation changes, hue rotation, luminance to alpha, and various other + effects. You can apply the filter to any display object (that is, objects + that inherit from the DisplayObject class), such as MovieClip, + SimpleButton, TextField, and Video objects, as well as to BitmapData + objects. + **Note:** For RGBA values, the most significant byte represents the red + channel value, followed by green, blue, and then alpha. + + To create a new color matrix filter, use the syntax `new + ColorMatrixFilter()`. The use of filters depends on the object to which + you apply the filter: + + * To apply filters to movie clips, text fields, buttons, and video, use + the `filters` property (inherited from DisplayObject). Setting the + `filters` property of an object does not modify the object, and you can + remove the filter by clearing the `filters` property. + * To apply filters to BitmapData objects, use the + `BitmapData.applyFilter()` method. Calling `applyFilter()` on a BitmapData + object takes the source BitmapData object and the filter object and + generates a filtered image as a result. + + If you apply a filter to a display object, the `cacheAsBitmap` property of + the display object is set to `true`. If you remove all filters, the + original value of `cacheAsBitmap` is restored. + + A filter is not applied if the resulting image exceeds the maximum + dimensions. In AIR 1.5 and Flash Player 10, the maximum is 8,191 pixels in + width or height, and the total number of pixels cannot exceed 16,777,215 + pixels. (So, if an image is 8,191 pixels wide, it can only be 2,048 pixels + high.) In Flash Player 9 and earlier and AIR 1.1 and earlier, the + limitation is 2,880 pixels in height and 2,880 pixels in width. For + example, if you zoom in on a large movie clip with a filter applied, the + filter is turned off if the resulting image reaches the maximum + dimensions. +**/ +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:final class ColorMatrixFilter extends BitmapFilter +{ + @:noCompletion private static var __colorMatrixShader:ColorMatrixShader = new ColorMatrixShader(); + + /** + An array of 20 items for 4 x 5 color transform. The `matrix` property + cannot be changed by directly modifying its value (for example, + `myFilter.matrix[2] = 1;`). Instead, you must get a reference to the + array, make the change to the reference, and reset the value. + The color matrix filter separates each source pixel into its red, + green, blue, and alpha components as srcR, srcG, srcB, srcA. To + calculate the result of each of the four channels, the value of each + pixel in the image is multiplied by the values in the transformation + matrix. An offset, between -255 and 255, can optionally be added to + each result (the fifth item in each row of the matrix). The filter + combines each color component back into a single pixel and writes out + the result. In the following formula, a[0] through a[19] correspond to + entries 0 through 19 in the 20-item array that is passed to the + `matrix` property: + + ``` + redResult = (a[0] * srcR) + (a[1] * srcG) + (a[2] * srcB) + (a[3] * srcA) + a[4] + greenResult = (a[5] * srcR) + (a[6] * srcG) + (a[7] * srcB) + (a[8] * srcA) + a[9] + blueResult = (a[10] * srcR) + (a[11] * srcG) + (a[12] * srcB) + (a[13] * srcA) + a[14] + alphaResult = (a[15] * srcR) + (a[16] * srcG) + (a[17] * srcB) + (a[18] * srcA) + a[19] + ``` + + For each color value in the array, a value of 1 is equal to 100% of + that channel being sent to the output, preserving the value of the + color channel. + + The calculations are performed on unmultiplied color values. If the + input graphic consists of premultiplied color values, those values are + automatically converted into unmultiplied color values for this + operation. + + Two optimized modes are available: + + **Alpha only.** When you pass to the filter a matrix that adjusts only + the alpha component, as shown here, the filter optimizes its + performance: + + ``` + 1 0 0 0 0 + 0 1 0 0 0 + 0 0 1 0 0 + 0 0 0 N 0 (where N is between 0.0 and 1.0) + ``` + + **Faster version**. Available only with SSE/AltiVec + accelerator-enabled processors, such as Intel + Pentium 3 and later and Apple G4 and later. + The accelerator is used when the multiplier terms are in the range + -15.99 to 15.99 and the adder terms a[4], a[9], a[14], and a[19] are + in the range -8000 to 8000. + + @throws TypeError The Array is `null` when being set + **/ + public var matrix(get, set):Array; + + @:noCompletion private var __matrix:Array; + + #if openfljs + @:noCompletion private static function __init__() + { + untyped Object.defineProperties(ColorMatrixFilter.prototype, { + "matrix": { + get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_matrix (); }"), + set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_matrix (v); }") + }, + }); + } + #end + + /** + Initializes a new ColorMatrixFilter instance with the specified + parameters. + **/ + public function new(matrix:Array = null) + { + super(); + + this.matrix = matrix; + + __numShaderPasses = 1; + __needSecondBitmapData = false; + } + + public override function clone():BitmapFilter + { + return new ColorMatrixFilter(__matrix); + } + + @:noCompletion private override function __applyFilter(destBitmapData:BitmapData, sourceBitmapData:BitmapData, sourceRect:Rectangle, + destPoint:Point):BitmapData + { + #if lime + var sourceImage = sourceBitmapData.image; + var image = destBitmapData.image; + + #if (js && html5) + ImageCanvasUtil.convertToData(sourceImage); + ImageCanvasUtil.convertToData(image); + #end + + var sourceData = sourceImage.data; + var destData = image.data; + + var offsetX = Std.int(destPoint.x - sourceRect.x); + var offsetY = Std.int(destPoint.y - sourceRect.y); + var sourceStride = sourceBitmapData.width * 4; + var destStride = destBitmapData.width * 4; + + var sourceFormat = sourceImage.buffer.format; + var destFormat = image.buffer.format; + var sourcePremultiplied = sourceImage.buffer.premultiplied; + var destPremultiplied = image.buffer.premultiplied; + + var sourcePixel:RGBA, destPixel:RGBA = 0; + var sourceOffset:Int, destOffset:Int; + + for (row in Std.int(sourceRect.y)...Std.int(sourceRect.height)) + { + for (column in Std.int(sourceRect.x)...Std.int(sourceRect.width)) + { + sourceOffset = (row * sourceStride) + (column * 4); + destOffset = ((row + offsetX) * destStride) + ((column + offsetY) * 4); + + sourcePixel.readUInt8(sourceData, sourceOffset, sourceFormat, sourcePremultiplied); + + if (sourcePixel.a == 0) + { + destPixel = 0; + } + else + { + destPixel.r = Std.int(Math.max(0, + Math.min((__matrix[0] * sourcePixel.r) + (__matrix[1] * sourcePixel.g) + (__matrix[2] * sourcePixel.b) + + (__matrix[3] * sourcePixel.a) + __matrix[4], + 255))); + destPixel.g = Std.int(Math.max(0, + Math.min((__matrix[5] * sourcePixel.r) + (__matrix[6] * sourcePixel.g) + (__matrix[7] * sourcePixel.b) + + (__matrix[8] * sourcePixel.a) + __matrix[9], + 255))); + destPixel.b = Std.int(Math.max(0, + Math.min((__matrix[10] * sourcePixel.r) + (__matrix[11] * sourcePixel.g) + (__matrix[12] * sourcePixel.b) + + (__matrix[13] * sourcePixel.a) + __matrix[14], + 255))); + destPixel.a = Std.int(Math.max(0, + Math.min((__matrix[15] * sourcePixel.r) + (__matrix[16] * sourcePixel.g) + (__matrix[17] * sourcePixel.b) + + (__matrix[18] * sourcePixel.a) + __matrix[19], + 255))); + } + + destPixel.writeUInt8(destData, destOffset, destFormat, destPremultiplied); + } + } + + destBitmapData.image.dirty = true; + #end + return destBitmapData; + } + + @:noCompletion private override function __initShader(renderer:DisplayObjectRenderer, pass:Int, sourceBitmapData:BitmapData):Shader + { + __colorMatrixShader.init(matrix); + return __colorMatrixShader; + } + + // Get & Set Methods + @:noCompletion private function get_matrix():Array + { + return __matrix; + } + + @:noCompletion private function set_matrix(value:Array):Array + { + if (value == null) + { + value = [1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0]; + } + + return __matrix = value; + } +} + +#if !openfl_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@SuppressWarnings("checkstyle:FieldDocComment") +private class ColorMatrixShader extends BitmapFilterShader +{ + @:glFragmentSource("varying vec2 openfl_TextureCoordv; + uniform sampler2D openfl_Texture; + + uniform mat4 uMultipliers; + uniform vec4 uOffsets; + + void main(void) { + + vec4 color = texture2D (openfl_Texture, openfl_TextureCoordv); + + if (color.a == 0.0) { + + gl_FragColor = vec4 (0.0, 0.0, 0.0, 0.0); + + } else { + + color = vec4 (color.rgb / color.a, color.a); + color = uOffsets + color * uMultipliers; + + gl_FragColor = vec4 (color.rgb * color.a, color.a); + + } + + }") + public function new() + { + super(); + + #if !macro + data.uMultipliers.value = [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1]; + data.uOffsets.value = [0, 0, 0, 0]; + #end + } + + public function init(matrix:Array):Void + { + #if !macro + var multipliers = data.uMultipliers.value; + var offsets = data.uOffsets.value; + + multipliers[0] = matrix[0]; + multipliers[1] = matrix[1]; + multipliers[2] = matrix[2]; + multipliers[3] = matrix[3]; + multipliers[4] = matrix[5]; + multipliers[5] = matrix[6]; + multipliers[6] = matrix[7]; + multipliers[7] = matrix[8]; + multipliers[8] = matrix[10]; + multipliers[9] = matrix[11]; + multipliers[10] = matrix[12]; + multipliers[11] = matrix[13]; + multipliers[12] = matrix[15]; + multipliers[13] = matrix[16]; + multipliers[14] = matrix[17]; + multipliers[15] = matrix[18]; + + offsets[0] = matrix[4] / 255.0; + offsets[1] = matrix[9] / 255.0; + offsets[2] = matrix[14] / 255.0; + offsets[3] = matrix[19] / 255.0; + #end + } +} +#else +typedef ColorMatrixFilter = flash.filters.ColorMatrixFilter; +#end