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