Merge branch 'main' into internal-merge

# Conflicts:
#	source/funkin/backend/system/Flags.hx
This commit is contained in:
Ralty
2026-07-07 11:26:08 +07:00
10 changed files with 1129 additions and 85 deletions
+1 -1
View File
@@ -75,7 +75,7 @@ function onCountdown(event) {
};
}
function onPlayerHit(event:NoteHitEvent) {
function onRatingsShown(event:RatingsShowEvent) {
if (!enablePixelUI) return;
event.ratingPrefix = "stages/school/ui/";
event.ratingScale = daPixelZoom * 0.7;
+83 -4
View File
@@ -1,19 +1,98 @@
package funkin.backend;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.math.FlxMath;
import flixel.text.FlxText;
import flixel.util.FlxColor;
import funkin.backend.system.Flags;
class FunkinText extends FlxText {
public function new(X:Float = 0, Y:Float = 0, FieldWidth:Float = 0, ?Text:String, ?Size:Int, Border:Bool = true) {
if (Size == null) Size = Flags.DEFAULT_FONT_SIZE;
class FunkinText extends FlxText
{
public var zoomFactor:Float = 1;
public var zoomFactorEnabled:Bool = true;
public function new(X:Float = 0, Y:Float = 0, FieldWidth:Float = 0, ?Text:String, ?Size:Int, Border:Bool = true)
{
if (Size == null)
Size = Flags.DEFAULT_FONT_SIZE;
super(X, Y, FieldWidth, Text, Size);
setFormat(Paths.font(Flags.DEFAULT_FONT), Size, FlxColor.WHITE);
if (Border) {
if (Border)
{
borderStyle = OUTLINE;
borderSize = 1;
borderColor = 0xFF000000;
}
}
private inline function __shouldDoZoomFactor():Bool
{
return zoomFactorEnabled && zoomFactor != 1;
}
private inline function __getZoomScaleX(camera:FlxCamera):Float
{
return (camera.scaleX > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleX, 1, zoomFactor));
}
private inline function __getZoomScaleY(camera:FlxCamera):Float
{
return (camera.scaleY > 0 ? Math.max : Math.min)(0, FlxMath.lerp(1 / camera.scaleY, 1, zoomFactor));
}
private inline function __getZoomAnchorX(camera:FlxCamera):Float
{
if (Flags.USE_LEGACY_ZOOM_FACTOR)
return camera.width * 0.5;
return camera.width * 0.5 + camera.scroll.x * scrollFactor.x;
}
private inline function __getZoomAnchorY(camera:FlxCamera):Float
{
if (Flags.USE_LEGACY_ZOOM_FACTOR)
return camera.height * 0.5;
return camera.height * 0.5 + camera.scroll.y * scrollFactor.y;
}
override public function draw():Void
{
if (!__shouldDoZoomFactor())
{
super.draw();
return;
}
var camera:FlxCamera = this.camera;
if (camera == null)
camera = FlxG.camera;
var oldX:Float = x;
var oldY:Float = y;
var oldScaleX:Float = scale.x;
var oldScaleY:Float = scale.y;
var zoomScaleX:Float = __getZoomScaleX(camera);
var zoomScaleY:Float = __getZoomScaleY(camera);
var anchorX:Float = __getZoomAnchorX(camera);
var anchorY:Float = __getZoomAnchorY(camera);
x = (x - anchorX) * zoomScaleX + anchorX;
y = (y - anchorY) * zoomScaleY + anchorY;
scale.set(scale.x * zoomScaleX, scale.y * zoomScaleY);
super.draw();
x = oldX;
y = oldY;
scale.set(oldScaleX, oldScaleY);
}
}
+1 -1
View File
@@ -75,7 +75,7 @@ class EventsData {
defValue: "In"
},
{name: "Mode", type: TDropDown(['direct', 'stage']), defValue: "direct"},
{name: "Multiplicative?", type: TBool, defValue: true}
{name: "Multiplicative?", type: TBool, defValue: false}
],
"Camera Modulo Change" => [
{name: "Modulo Interval", type: TInt(1, 9999999, 1), defValue: 4},
@@ -0,0 +1,96 @@
package funkin.backend.scripting.events.gameplay;
import flixel.math.FlxPoint;
import flixel.tweens.FlxTween;
final class RatingsShowEvent extends CancellableEvent
{
/**
* Rating sprite (may be null)
*/
public var ratingSprite:Null<FlxSprite>;
/**
* Number sprite (may be null)
*/
public var numberSprite:Null<FlxSprite>;
/**
* Combo sprite (may be null)
*/
public var comboSprite:Null<FlxSprite>;
/**
* Scale of combo numbers. (may be null)
*/
public var numScale:Null<Float> = 0.5;
/**
* Whenever antialiasing should be enabled on combo numbers. (may be null)
*/
public var numAntialiasing:Null<Bool> = true;
/**
* Scale of the rating sprites. (may be null)
*/
public var ratingScale:Null<Float> = 0.7;
/**
* Whenever antialiasing should be enabled on ratings. (may be null)
*/
public var ratingAntialiasing:Null<Bool> = true;
/**
* Prefix of the rating sprite path. Defaults to "game/score/"
*/
public var ratingPrefix:String;
/**
* Suffix of the rating sprite path.
*/
public var ratingSuffix:String;
/**
* The sprite's acceleration.
*/
public var acceleration:Float;
/**
* A FlxPoint which x or y properties preposition the sprites current velocity.
*/
public var velocity:FlxPoint;
/**
* The duration of the sprite's alpha tween.
*/
public var tweenDuration:Float;
/**
* The start delay of the sprite's alpha tween.
*/
public var startDelay:Float;
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayRating:Bool;
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayNumbers:Bool;
/**
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
*/
public var displayCombo:Bool;
/**
* Whether the sprite should be tweened or not.
*/
public var playTween:Bool;
/**
* The amount of spacing for the combo numbers. (may be null)
*/
public var numSpacing:Null<Float>;
/**
* The position of the sprite.
*/
public var position:FlxPoint;
/**
* Whether to reset the sprite or not.
*/
public var resetSprite:Bool;
/**
* The rating name of the rating sprite. (may be null)
*/
public var rating:Null<String>;
/**
* The FlxTween instance. (null before "onPostRatingsShown")
*/
public var tween:Null<FlxTween>;
}
@@ -34,11 +34,11 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Whenever the Rating sprites should be shown or not.
*/
public var displayRating:Bool;
public var displayRating:Null<Bool>;
/**
* Whenever the Combo sprite should be shown or not (like old Week 7 patches).
*/
public var displayCombo:Bool;
public var displayCombo:Null<Bool>;
/**
* Note that has been pressed
*/
@@ -66,11 +66,11 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Prefix of the rating sprite path. Defaults to "game/score/"
*/
public var ratingPrefix:String;
public var ratingPrefix:Null<String>;
/**
* Suffix of the rating sprite path.
*/
public var ratingSuffix:String;
public var ratingSuffix:Null<String>;
/**
* Direction of the press (0 = Left, 1 = Down, 2 = Up, 3 = Right)
*/
@@ -98,19 +98,19 @@ final class NoteHitEvent extends CancellableEvent {
/**
* Scale of combo numbers.
*/
public var numScale:Float = 0.5;
public var numScale:Null<Float>;
/**
* Whenever antialiasing should be enabled on combo number.
*/
public var numAntialiasing:Bool = true;
public var numAntialiasing:Null<Bool>;
/**
* Scale of ratings.
*/
public var ratingScale:Float = 0.7;
public var ratingScale:Null<Float>;
/**
* Whenever antialiasing should be enabled on ratings.
*/
public var ratingAntialiasing:Bool = true;
public var ratingAntialiasing:Null<Bool>;
/**
* Whenever the animation should be forced to play (if it's null it will be forced based on the sprite's data xml, if it has one).
*/
@@ -0,0 +1,766 @@
package funkin.backend.shaders;
import haxe.Timer;
import openfl.filters.BitmapFilter;
import openfl.filters.BitmapFilterShader;
import openfl.display.BitmapData;
import openfl.display.DisplayObjectRenderer;
import openfl.display.BlendMode;
import openfl.display.Shader;
import openfl.geom.Point;
import openfl.geom.Rectangle;
/**
This BloomEffect was modified by heihua based on openfl.filters.BlurFilter.
The BloomEffect class applies a bloom/glow visual effect to display objects.
A bloom effect extracts bright areas from an image, blurs them, and combines
them back to create a glowing halo around bright objects. This effect is
commonly used to simulate intense light, emissive materials, or to add a
dreamy, atmospheric quality to scenes.
The effect consists of three stages:
1. Extraction - Bright pixels above a threshold are extracted
2. Blurring - The extracted bright areas are blurred horizontally and vertically
3. Combination - The blurred result is blended back with the original image
**/
@:noCustomClass
class BloomEffect extends BitmapFilter
{
@:noCompletion private static var __blurShader:BlurShader;
@:noCompletion private static var __combineShader:CombineShader;
@:noCompletion private static var __extractShader:ExtractShader;
@:noCompletion private static var __extractLowShader:ExtractLowShader;
/**
Values that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized to render
more quickly than other values.
**/
public var blurX(get, set):Float;
/**
Values that are a power of 2 (such as 2, 4, 8, 16 and 32) are optimized to render
more quickly than other values.
**/
public var blurY(get, set):Float;
/**
The downscaling factor for bloom rendering. Higher values significantly reduce
GPU performance cost, but setting values too high may cause noticeable flickering.
Recommended range is 8-24.
**/
public var quality(get, set):Float;
/**
The intensity of the bloom effect. Higher values produce more pronounced bloom.
**/
public var strength(get, set):Float;
/**
The brightness threshold for bloom extraction. Pixels brighter than this value
will contribute to the bloom effect. Value range is 0.0 to 1.0.
**/
public var threshold(get, set):Float;
/**
The smoothness of the threshold transition in blur shader.
Higher values create a smoother transition for brightness correction.
Value range is 0.0 to 1.0. Default is 0.1.
**/
public var smoothness(get, set):Float;
/**
Enables extended rendering area to avoid edge artifacts. Enabling this option
will increase performance cost. Generally not required when rendering to camera.
**/
public var extension(get, set):Bool;
/**
Low-quality pixel sampling mode. Disabling it significantly reduces screen flickering,
with minimal performance impact on desktop platforms but a higher
performance cost on non-desktop platforms.
**/
public var useLowQualityExtract(get, set):Bool;
/**
The weights for calculating brightness (RGB to grayscale).
Order: [Red, Green, Blue]. Default is [0.2126, 0.7152, 0.0722].
**/
public var weights(get, set):Array<Float>;
/**
The blend mode used when combining the bloom with the original image.
BlendMode currently supports: (BlendMode.ADD, BlendMode.ALPHA, BlendMode.HARDLIGHT,
BlendMode.LIGHTEN, BlendMode.MULTIPLY, BlendMode.OVERLAY, BlendMode.SCREEN,
BlendMode.COLORDODGE, BlendMode.SOFTLIGHT).
Default is BlendMode.ADD.
**/
public var blendMode(get, set):BlendMode;
@:noCompletion private var __blurX:Float;
@:noCompletion private var __blurY:Float;
@:noCompletion private var __horizontalPasses:Int;
@:noCompletion private var __quality:Float;
@:noCompletion private var __verticalPasses:Int;
@:noCompletion private var __strength:Float;
@:noCompletion private var __threshold:Float;
@:noCompletion private var __smoothness:Float;
@:noCompletion private var __extension:Bool;
@:noCompletion private var __useLowQualityExtract:Bool;
@:noCompletion private var __weights:Array<Float>;
@:noCompletion private var __blendMode:BlendMode;
#if openfljs
@:noCompletion private static function __init__()
{
untyped Object.defineProperties(BloomEffect.prototype, {
"blurX": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blurX (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blurX (v); }")
},
"blurY": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blurY (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blurY (v); }")
},
"quality": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_quality (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_quality (v); }")
},
"strength": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_strength (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_strength (v); }")
},
"threshold": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_threshold (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_threshold (v); }")
},
"useLowQualityExtract": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_useLowQualityExtract (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_useLowQualityExtract (v); }")
},
"weights": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_weights (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_weights (v); }")
},
"blendMode": {
get: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function () { return this.get_blendMode (); }"),
set: untyped #if haxe4 js.Syntax.code #else __js__ #end ("function (v) { return this.set_blendMode (v); }")
},
});
}
#end
/**
Initializes the bloom filter with the specified parameters.
@param blurX The amount to blur horizontally.
@param blurY The amount to blur vertically.
@param quality The downscaling factor for bloom rendering (higher values reduce
GPU cost but may cause flickering if too high).
@param strength The intensity of the bloom effect.
@param threshold The brightness threshold for bloom extraction (0.0 to 1.0).
@param smoothness The smoothness of threshold transition in blur (0.0 to 1.0).
@param useLowQualityExtract Enables performance-optimized extraction with
potentially more flickering.
**/
public function new(blurX:Float = 50, blurY:Float = 50, quality:Float = 8, strength:Float = 0.6, threshold:Float = 0.6, smoothness:Float = 0.1, useLowQualityExtract:Bool = true)
{
super();
if (__blurShader == null) __blurShader = new BlurShader();
if (__combineShader == null) __combineShader = new CombineShader();
if (__extractShader == null) __extractShader = new ExtractShader();
if (__extractLowShader == null) __extractLowShader = new ExtractLowShader();
this.blurX = blurX;
this.blurY = blurY;
this.quality = quality;
this.strength = strength;
this.threshold = threshold;
this.smoothness = smoothness;
this.extension = false;
this.useLowQualityExtract = useLowQualityExtract;
this.weights = [0.2126, 0.7152, 0.0722];
this.blendMode = BlendMode.ADD;
__needSecondBitmapData = true;
__preserveObject = true;
__renderDirty = true;
}
public override function clone():BitmapFilter
{
var cloned = new BloomEffect(__blurX, __blurY, __quality, __strength, __threshold, __smoothness, __useLowQualityExtract);
cloned.weights = __weights != null ? __weights.copy() : [0.2126, 0.7152, 0.0722];
cloned.blendMode = __blendMode;
return cloned;
}
@:noCompletion private override function __applyFilter(bitmapData:BitmapData, sourceBitmapData:BitmapData, sourceRect:Rectangle, destPoint:Point):BitmapData
{
trace("Due to technical limitations, I'm unable to implement the bitmapData rendering method. If you know how to implement it, you're welcome to contribute this feature.");
return sourceBitmapData;
}
@:noCompletion private override function __initShader(renderer:DisplayObjectRenderer, pass:Int, sourceBitmapData:BitmapData):Shader
{
final numBlurPasses = __horizontalPasses + __verticalPasses;
switch pass
{
case 0:
if (__useLowQualityExtract)
{
__extractLowShader.uThreshold.value[0] = __threshold;
__extractLowShader.uSmoothness.value[0] = __smoothness;
__extractLowShader.uQuality.value[0] = __quality;
__extractLowShader.uWeights.value = __weights;
return __extractLowShader;
}
else
{
__extractShader.uThreshold.value[0] = __threshold;
__extractShader.uSmoothness.value[0] = __smoothness;
__extractShader.uQuality.value[0] = __quality;
__extractShader.uWeights.value = __weights;
return __extractShader;
}
case _ if (pass <= numBlurPasses):
final blurPass = pass - 1;
final isHorizontal = blurPass < __horizontalPasses;
final scalePass = isHorizontal ? blurPass : blurPass - __horizontalPasses;
final scale = Math.pow(0.5, scalePass >> 1);
final blurRadius = isHorizontal ? blurX * scale : blurY * scale;
if (isHorizontal)
{
__blurShader.uRadius.value[0] = blurRadius / __quality;
__blurShader.uRadius.value[1] = 0.0;
}
else
{
__blurShader.uRadius.value[0] = 0.0;
__blurShader.uRadius.value[1] = blurRadius / __quality;
}
__blurShader.uQuality.value[0] = __quality;
__blurShader.uStrength.value[0] = Math.pow(__strength, 1.0 / numBlurPasses);
return __blurShader;
default:
__combineShader.sourceBitmap.input = sourceBitmapData;
__combineShader.uThreshold.value[0] = __threshold;
__combineShader.uQuality.value[0] = __quality;
__combineShader.uBlendMode.value[0] = cast __blendMode;
return __combineShader;
}
}
// Get & Set Methods
@:noCompletion private function get_blurX():Float
{
return __blurX;
}
@:noCompletion private function set_blurX(value:Float):Float
{
if (value != __blurX)
{
__blurX = value;
__renderDirty = true;
if (!__extension)
{
// Setting it to 1 prevents bloom flickering at the screen edges
__leftExtension = 1;
__rightExtension = 1;
}
else
{
__leftExtension = (value > 0 ? Math.ceil(value) : 0);
__rightExtension = __leftExtension;
}
__horizontalPasses = (value <= 0) ? 0 : Math.ceil(value * 0.0625 / quality) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
}
return value;
}
@:noCompletion private function get_blurY():Float
{
return __blurY;
}
@:noCompletion private function set_blurY(value:Float):Float
{
if (value != __blurY)
{
__blurY = value;
__renderDirty = true;
if (!__extension)
{
// Setting it to 1 prevents bloom flickering at the screen edges
__topExtension = 1;
__bottomExtension = 1;
}
else
{
__topExtension = (value > 0 ? Math.ceil(value) : 0);
__bottomExtension = __topExtension;
}
__verticalPasses = (value <= 0) ? 0 : Math.ceil(value * 0.0625 / quality) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
}
return value;
}
@:noCompletion private function get_quality():Float
{
return __quality;
}
@:noCompletion private function set_quality(value:Float):Float
{
__horizontalPasses = (__blurX <= 0) ? 0 : Math.round(__blurX * 0.125 / value) + 1;
__verticalPasses = (__blurY <= 0) ? 0 : Math.round(__blurY * 0.125 / value) + 1;
__numShaderPasses = __horizontalPasses + __verticalPasses + 2;
if (value != __quality)
__renderDirty = true;
return __quality = value;
}
@:noCompletion private function get_strength():Float
{
return __strength;
}
@:noCompletion private function set_strength(value:Float):Float
{
if (value != __strength)
{
__strength = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_threshold():Float
{
return __threshold;
}
@:noCompletion private function set_threshold(value:Float):Float
{
if (value != __threshold)
{
__threshold = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_smoothness():Float
{
return __smoothness;
}
@:noCompletion private function set_smoothness(value:Float):Float
{
if (value != __smoothness)
{
__smoothness = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_extension():Bool
{
return __extension;
}
@:noCompletion private function set_extension(value:Bool):Bool
{
if (value != __extension)
{
__extension = value;
if (!value)
__leftExtension = __rightExtension = __topExtension = __bottomExtension = 0;
else
{
__leftExtension = __rightExtension = (__blurX > 0 ? Math.ceil(__blurX) : 0);
__topExtension = __bottomExtension = (__blurY > 0 ? Math.ceil(__blurY) : 0);
}
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_useLowQualityExtract():Bool
{
return __useLowQualityExtract;
}
@:noCompletion private function set_useLowQualityExtract(value:Bool):Bool
{
if (value != __useLowQualityExtract)
{
__useLowQualityExtract = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_weights():Array<Float>
{
return __weights;
}
@:noCompletion private function set_weights(value:Array<Float>):Array<Float>
{
if (value != __weights)
{
__weights = value;
__renderDirty = true;
}
return value;
}
@:noCompletion private function get_blendMode():BlendMode
{
return __blendMode;
}
@:noCompletion private function set_blendMode(value:BlendMode):BlendMode
{
if (value != __blendMode)
{
__blendMode = value;
__renderDirty = true;
}
return value;
}
}
private class BlurShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform float uStrength;
varying mat2 vBlurCoord0;
varying mat2 vBlurCoord1;
varying vec2 vBlurCoord2;
varying mat2 vBlurCoord3;
varying mat2 vBlurCoord4;
varying float invQuality;
void main(void) {
if ((all(greaterThanEqual(vBlurCoord2, vec2(0.0))) && all(lessThanEqual(vBlurCoord2, vec2(1.0)))) == false) return;
vec4 sum = texture2D(openfl_Texture, clamp(vBlurCoord0[0], 0.0, invQuality)) * 0.028532;
sum += texture2D(openfl_Texture, clamp(vBlurCoord0[1], 0.0, invQuality)) * 0.067234;
sum += texture2D(openfl_Texture, clamp(vBlurCoord1[0], 0.0, invQuality)) * 0.124009;
sum += texture2D(openfl_Texture, clamp(vBlurCoord1[1], 0.0, invQuality)) * 0.179044;
sum += texture2D(openfl_Texture, clamp(vBlurCoord2, 0.0, invQuality)) * 0.202360;
sum += texture2D(openfl_Texture, clamp(vBlurCoord3[0], 0.0, invQuality)) * 0.179044;
sum += texture2D(openfl_Texture, clamp(vBlurCoord3[1], 0.0, invQuality)) * 0.124009;
sum += texture2D(openfl_Texture, clamp(vBlurCoord4[0], 0.0, invQuality)) * 0.067234;
sum += texture2D(openfl_Texture, clamp(vBlurCoord4[1], 0.0, invQuality)) * 0.028532;
gl_FragColor = sum * uStrength;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 uRadius;
uniform vec2 uTextureSize;
uniform float uQuality;
varying mat2 vBlurCoord0;
varying mat2 vBlurCoord1;
varying vec2 vBlurCoord2;
varying mat2 vBlurCoord3;
varying mat2 vBlurCoord4;
varying float invQuality;
void main(void) {
vec4 pos = openfl_Position;
invQuality = 1.0 / uQuality;
pos.xy *= invQuality;
gl_Position = openfl_Matrix * pos;
vec2 r = uRadius / uTextureSize;
vec2 coord = openfl_TextureCoord * invQuality;
vBlurCoord0[0] = coord - r;
vBlurCoord0[1] = coord - r * 0.25;
vBlurCoord1[0] = coord - r * 0.5;
vBlurCoord1[1] = coord - r * 0.75;
vBlurCoord2 = coord;
vBlurCoord3[0] = coord + r * 0.25;
vBlurCoord3[1] = coord + r * 0.5;
vBlurCoord4[0] = coord + r * 0.75;
vBlurCoord4[1] = coord + r;
}
")
public function new()
{
super();
uStrength.value = [1.0];
uRadius.value = [0, 0];
uQuality.value = [8];
uTextureSize.value = [1, 1];
}
@:noCompletion private override function __update():Void
{
#if !macro
uTextureSize.value[0] = __texture.input.width;
uTextureSize.value[1] = __texture.input.height;
#end
super.__update();
}
}
private class ExtractLowShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform float uThreshold;
uniform float uSmoothness;
uniform vec3 uWeights;
varying vec2 vTexCoord;
void main(void) {
if ((all(greaterThanEqual(vTexCoord, vec2(0.0))) && all(lessThanEqual(vTexCoord, vec2(1.0)))) == false) return;
vec4 texel = texture2D(openfl_Texture, vTexCoord);
float brightness = min(dot(texel.rgb, uWeights), 1.0);
float mask = smoothstep(uThreshold, uThreshold + uSmoothness, brightness);
gl_FragColor = texel * mask;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec2 vTexCoord;
void main(void) {
vec4 pos = openfl_Position;
pos.xy /= uQuality;
gl_Position = openfl_Matrix * pos;
vTexCoord = openfl_TextureCoord;
}
")
public function new()
{
super();
uThreshold.value = [0.6];
uSmoothness.value = [0.1];
uQuality.value = [8];
uWeights.value = [0.2126, 0.7152, 0.0722];
}
}
private class ExtractShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform vec2 openfl_TextureSize;
uniform float uThreshold;
uniform float uSmoothness;
uniform float uQuality;
uniform vec3 uWeights;
varying vec2 vTexCoord;
varying vec4 border;
void main(void) {
if ((all(greaterThanEqual(vTexCoord, border.xy)) && all(lessThanEqual(vTexCoord, border.zw))) == false) return;
float quality = floor(uQuality) / 2.0;
vec2 texelSize = 1.0 / openfl_TextureSize;
vec4 accumulated = vec4(0.0);
int sampleCount = 0;
for (float dx = -quality; dx <= quality; dx += 2.0) {
for (float dy = -quality; dy <= quality; dy += 2.0) {
vec2 sampleCoord = vTexCoord + vec2(dx, dy) * texelSize;
vec4 texel = texture2D(openfl_Texture, sampleCoord);
float brightness = min(dot(texel.rgb, uWeights), 1.0);
float mask = smoothstep(uThreshold, uThreshold + uSmoothness, brightness);
accumulated += texel * mask;
sampleCount++;
}
}
gl_FragColor = accumulated / float(sampleCount);
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec2 vTexCoord;
varying vec4 border;
void main(void) {
vec4 pos = openfl_Position;
pos.xy /= uQuality;
vec2 size = 1.0 / openfl_TextureSize * uQuality;
border = vec4(size, vec2(1.0) - size);
gl_Position = openfl_Matrix * pos;
vTexCoord = openfl_TextureCoord;
}
")
public function new()
{
super();
uThreshold.value = [0.6];
uSmoothness.value = [0.1];
uQuality.value = [8];
uWeights.value = [0.2126, 0.7152, 0.0722];
}
}
private class CombineShader extends BitmapFilterShader
{
@:glFragmentSource("
uniform sampler2D openfl_Texture;
uniform sampler2D sourceBitmap;
uniform float uThreshold;
uniform int uBlendMode;
varying vec4 textureCoords;
vec4 blendScreen(vec4 src, vec4 bloom) {
return vec4(1.0) - (vec4(1.0) - src) * (vec4(1.0) - bloom);
}
vec4 blendMultiply(vec4 src, vec4 bloom) {
return src * bloom;
}
vec4 blendLighten(vec4 src, vec4 bloom) {
return max(src, bloom);
}
vec4 blendOverlay(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (src[i] < 0.5) {
result[i] = 2.0 * src[i] * bloom[i];
} else {
result[i] = 1.0 - 2.0 * (1.0 - src[i]) * (1.0 - bloom[i]);
}
}
return result;
}
vec4 blendColorDodge(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (bloom[i] < 1.0) {
result[i] = min(1.0, src[i] / (1.0 - bloom[i]));
} else {
result[i] = 1.0;
}
}
return result;
}
vec4 blendSoftLight(vec4 src, vec4 bloom) {
vec4 result = vec4(0.0);
for (int i = 0; i < 4; i++) {
if (bloom[i] < 0.5) {
result[i] = src[i] - (1.0 - 2.0 * bloom[i]) * src[i] * (1.0 - src[i]);
} else {
float d = (src[i] <= 0.25) ? ((16.0 * src[i] - 12.0) * src[i] + 4.0) * src[i] : sqrt(src[i]);
result[i] = src[i] + (2.0 * bloom[i] - 1.0) * (d - src[i]);
}
}
return result;
}
vec4 blendAlpha(vec4 src, vec4 bloom) {
return src + bloom * (1.0 - src.a);
}
void main(void) {
vec4 src = texture2D(sourceBitmap, textureCoords.xy);
vec4 bloom = texture2D(openfl_Texture, textureCoords.zw);
vec4 result;
if(uBlendMode == 0)
result = src + bloom;
else if(uBlendMode == 1)
result = blendAlpha(src, bloom);
else if(uBlendMode == 5)
result = blendOverlay(src, bloom);
else if(uBlendMode == 8)
result = blendLighten(src, bloom);
else if(uBlendMode == 9)
result = blendMultiply(src, bloom);
else if(uBlendMode == 11)
result = blendOverlay(src, bloom);
else if(uBlendMode == 12)
result = blendScreen(src, bloom);
else if(uBlendMode == 15)
result = blendColorDodge(src, bloom);
else if(uBlendMode == 17)
result = blendSoftLight(src, bloom);
else
result = src + bloom;
gl_FragColor = result;
}
")
@:glVertexSource("
attribute vec4 openfl_Position;
attribute vec2 openfl_TextureCoord;
uniform mat4 openfl_Matrix;
uniform vec2 openfl_TextureSize;
uniform float uQuality;
varying vec4 textureCoords;
void main(void) {
gl_Position = openfl_Matrix * openfl_Position;
textureCoords = vec4(openfl_TextureCoord, openfl_TextureCoord / uQuality);
}
")
public function new()
{
super();
uQuality.value = [8];
uThreshold.value = [0.6];
uBlendMode.value = [0];
}
}
+4
View File
@@ -106,6 +106,7 @@ class Flags {
public static var DEFAULT_BEATS_PER_MEASURE:Int = 4;
public static var DEFAULT_STEPS_PER_BEAT:Int = 4;
public static var DEFAULT_LOOP_TIME:Float = 0.0;
public static var ICONS_AUTOPOSITION:Bool = true;
@:lazy public static var DEFAULT_SOUND_TIME_SCALED_PITCH:Null<Bool> = null;
@:lazy public static var USE_FLXTRAIL_FRAMES:Null<Bool> = null;
@@ -133,6 +134,9 @@ class Flags {
@:also(funkin.game.PlayState.opponentMode)
public static var DEFAULT_OPPONENT_MODE:Bool = false;
public static var EARLY_HIT_WINDOW_RANGE:Float = 1.0; // was 0.5 for easier early hitting, but now 1 to demotivate mashing and getting away with it.
public static var LATE_HIT_WINDOW_RANGE:Float = 1.0;
public static var SHITS_BREAK_COMBO:Bool = true;
public static var USE_LEGACY_TIMING:Null<Bool> = null;
public static var DEFAULT_NOTE_MS_LIMIT:Float = 1500;
+2 -3
View File
@@ -317,9 +317,8 @@ class Note extends FlxSprite
return super.isOnScreen(camera);
}
// The * 0.5 is so that it's easier to hit them too late, instead of too early
public var earlyPressWindow:Float = 0.5;
public var latePressWindow:Float = 1;
public var earlyPressWindow:Float = Flags.EARLY_HIT_WINDOW_RANGE;
public var latePressWindow:Float = Flags.LATE_HIT_WINDOW_RANGE;
public function updateSustain(strum:Strum) {
var scrollSpeed = strum.getScrollSpeed(this);
+143 -64
View File
@@ -623,6 +623,18 @@ class PlayState extends MusicBeatState
curRating = event.rating;
}
private function onRatingChange(rating:Rating) {
if (!hits.exists(rating.name))
hits.set(rating.name, 0);
if (Options.ghostTapping) {
comboBreaks = false;
for (rating in ratingManager.ratingData)
comboBreaks = comboBreaks || rating.breaksCombo;
} else
comboBreaks = true;
}
private inline function set_health(v:Float)
return health = FlxMath.bound(v, 0, maxHealth);
private inline function set_maxHealth(v:Float) {
@@ -690,6 +702,14 @@ class PlayState extends MusicBeatState
detailsText = isStoryMode ? ("Story Mode: " + storyWeek.name) : "Freeplay";
for (rating in [for (i in ratingManager.ratingData) i.name]) hits.set(rating, 0); // Ensure all keys exist as to prevent null errors.
if (Options.ghostTapping) {
comboBreaks = false;
for (rating in ratingManager.ratingData)
comboBreaks = comboBreaks || rating.breaksCombo;
} else
comboBreaks = true;
ratingManager.onRatingAdded.add(onRatingChange);
ratingManager.onRatingRemoved.add(onRatingChange);
// Checks if cutscene files exists
var cutscenePath = Paths.script('songs/${SONG.meta.name}/cutscene');
@@ -923,7 +943,7 @@ class PlayState extends MusicBeatState
// Make icons appear in the correct spot during cutscenes
healthBar.update(0);
if (updateIconPositions != null)
if (updateIconPositions != null && Flags.ICONS_AUTOPOSITION)
updateIconPositions();
__updateNote_event = EventManager.get(NoteUpdateEvent);
@@ -1936,9 +1956,9 @@ class PlayState extends MusicBeatState
var event:NoteHitEvent;
if (strumLine != null && !strumLine.cpu)
event = EventManager.get(NoteHitEvent).recycle(false, !note.isSustainNote, !note.isSustainNote, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, rating.score, note.isSustainNote ? null : rating.accuracy, 0.023, rating.name, Options.splashesEnabled && !note.isSustainNote && rating.splash, 0.5, true, 0.7, true, true, iconP1);
event = EventManager.get(NoteHitEvent).recycle(rating.breaksCombo, !note.isSustainNote, !note.isSustainNote, null, null, null, note, strumLine.characters, true, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), null, null, note.strumID, rating.score, note.isSustainNote ? null : rating.accuracy, rating.health, rating.name, Options.splashesEnabled && !note.isSustainNote && rating.splash, null, null, null, null, null, iconP1);
else
event = EventManager.get(NoteHitEvent).recycle(false, false, false, null, defaultDisplayRating, defaultDisplayCombo, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), "game/score/", "", note.strumID, 0, null, 0, rating.name, false, 0.5, true, 0.7, true, true, iconP2);
event = EventManager.get(NoteHitEvent).recycle(rating.breaksCombo, false, false, null, null, null, note, strumLine.characters, false, note.noteType, note.animSuffix.getDefault(note.strumID < strumLine.members.length ? strumLine.members[note.strumID].animSuffix : strumLine.animSuffix), null, null, note.strumID, 0, null, 0, rating.name, false, null, null, null, null, true, iconP2);
event.deleteNote = !note.isSustainNote; // work around, to allow sustain notes to be deleted
event = scripts.event(strumLine != null && !strumLine.cpu ? "onPlayerHit" : "onDadHit", event);
strumLine.onHit.dispatch(event);
@@ -1954,13 +1974,17 @@ class PlayState extends MusicBeatState
totalAccuracyAmount += event.accuracy;
updateRating();
}
if (event.countAsCombo) combo++;
if (event.misses) {
combo = 0;
misses++;
} else if (event.countAsCombo)
combo++;
if (event.showRating || (event.showRating == null && event.player))
{
displayCombo(event);
if (event.displayRating)
displayRating(event.rating, event);
displayRatingNumbers(event);
displayRating(event.rating, event);
ratingNum += 1;
}
if (event.player) hits[rating.name] += 1;
@@ -1997,81 +2021,136 @@ class PlayState extends MusicBeatState
gameAndCharsEvent("onPostNoteHit", event);
}
public function displayRating(myRating:String, ?evt:NoteHitEvent = null):Void {
var hasEvent = evt != null;
var pre:String = hasEvent ? evt.ratingPrefix : "";
var suf:String = hasEvent ? evt.ratingSuffix : "";
public function displayRating(myRating:String, ?evt:NoteHitEvent):Void
{
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(comboGroup.recycleLoop(FlxSprite), null, null, null, null, 0.7, true, "game/score/", "", 550, FlxPoint.get(FlxG.random.int(0, 10), FlxG.random.int(140, 175)), 0.2, (Conductor.crochet * 0.001), true, false, false, true, null, FlxPoint.get(comboGroup.x + -40, comboGroup.y + -60), true, myRating, null);
gameAndCharsEvent("onRatingsShown", event);
var rating:FlxSprite = comboGroup.recycleLoop(FlxSprite);
CoolUtil.resetSprite(rating, comboGroup.x + -40, comboGroup.y + -60);
rating.loadAnimatedGraphic(Paths.image('${pre}${myRating}${suf}'));
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
if (hasEvent) {
rating.scale.set(evt.ratingScale, evt.ratingScale);
rating.antialiasing = evt.ratingAntialiasing;
if (event.cancelled || !event.displayRating) { // TODO: Find a better way for this?
event.ratingSprite.kill();
return;
}
var hasEvent:Bool = evt != null;
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var ratingScale:Float = hasEvent && evt.ratingScale != null ? evt.ratingScale : event.ratingScale;
var rating:FlxSprite = event.ratingSprite.loadAnimatedGraphic(Paths.image('${pre}${event.rating}${suf}'));
if (event.resetSprite) {
CoolUtil.resetSprite(rating, event.position.x, event.position.y);
}
rating.acceleration.y = event.acceleration;
rating.velocity.y -= event.velocity.y;
rating.velocity.x -= event.velocity.x;
rating.scale.set(ratingScale, ratingScale);
rating.antialiasing = hasEvent && evt.ratingAntialiasing != null ? evt.ratingAntialiasing : event.ratingAntialiasing;
rating.updateHitbox();
FlxTween.tween(rating, {alpha: 0}, 0.2, {
startDelay: Conductor.crochet * 0.001,
onComplete: function(tween:FlxTween) {
rating.kill();
}
});
if (event.playTween) {
event.tween = FlxTween.tween(rating, {alpha: 0}, event.tweenDuration, {
startDelay: event.startDelay,
onComplete: function(tween:FlxTween) {
rating.kill();
}
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
}
public function displayCombo(?evt:NoteHitEvent = null):Void {
public function displayCombo(?evt:NoteHitEvent):Void {
if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) {
var hasEvent = evt != null;
var pre:String = hasEvent ? evt.ratingPrefix : "";
var suf:String = hasEvent ? evt.ratingSuffix : "";
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(null, null, comboGroup.recycleLoop(FlxSprite), null, null, 0.7, true, "game/score/", "", 600, FlxPoint.get(FlxG.random.int(0, 10), 150), 0.2, (Conductor.crochet * 0.001), false, false, evt != null && evt.displayCombo != null ? evt.displayCombo : defaultDisplayCombo, true, null, FlxPoint.get(comboGroup.x, comboGroup.y), true, null, null);
gameAndCharsEvent("onRatingsShown", event);
if (evt.displayCombo) {
var comboSpr:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}combo${suf}'));
CoolUtil.resetSprite(comboSpr, comboGroup.x, comboGroup.y);
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.velocity.x += FlxG.random.int(1, 10);
if (hasEvent) {
comboSpr.scale.set(evt.ratingScale, evt.ratingScale);
comboSpr.antialiasing = evt.ratingAntialiasing;
}
comboSpr.updateHitbox();
FlxTween.tween(comboSpr, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
comboSpr.kill();
},
startDelay: Conductor.crochet * 0.001
});
if (event.cancelled || !event.displayCombo) { // TODO: Find a better way for this?
event.comboSprite.kill();
return;
}
var hasEvent:Bool = evt != null;
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var ratingScale:Float = hasEvent && evt.ratingScale != null ? evt.ratingScale : event.ratingScale;
var comboSpr:FlxSprite = event.comboSprite.loadAnimatedGraphic(Paths.image('${pre}combo${suf}'));
if (event.resetSprite) {
CoolUtil.resetSprite(comboSpr, event.position.x, event.position.y);
}
comboSpr.acceleration.y = event.acceleration;
comboSpr.velocity.y -= event.velocity.y;
comboSpr.velocity.x += event.velocity.x;
comboSpr.scale.set(ratingScale, ratingScale);
comboSpr.antialiasing = hasEvent && evt.ratingAntialiasing != null ? evt.ratingAntialiasing : event.ratingAntialiasing;
comboSpr.updateHitbox();
if (event.playTween) {
event.tween = FlxTween.tween(comboSpr, {alpha: 0}, event.tweenDuration, {
onComplete: function(tween:FlxTween) {
comboSpr.kill();
},
startDelay: event.startDelay
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
}
}
public function displayRatingNumbers(?evt:NoteHitEvent):Void {
if (minDigitDisplay >= 0 && (combo == 0 || combo >= minDigitDisplay)) {
var separatedScore:String = Std.string(combo).addZeros(3);
for (i in 0...separatedScore.length)
{
var numScore:FlxSprite = comboGroup.recycleLoop(FlxSprite).loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}'));
CoolUtil.resetSprite(numScore, comboGroup.x + (43 * i) - 90, comboGroup.y + 80);
if (hasEvent) {
numScore.antialiasing = evt.numAntialiasing;
numScore.scale.set(evt.numScale, evt.numScale);
var event:RatingsShowEvent = EventManager.get(RatingsShowEvent).recycle(null, comboGroup.recycleLoop(FlxSprite), null, 0.5, true, null, null, "game/score/", "", FlxG.random.int(200, 300), FlxPoint.get(FlxG.random.float(-5, 5), FlxG.random.int(140, 160)), 0.2, (Conductor.crochet * 0.002), false, true, false, true, 43, FlxPoint.get(comboGroup.x - 90, comboGroup.y + 80), true, null, null);
gameAndCharsEvent("onRatingsShown", event);
if (event.cancelled || !event.displayNumbers) { // TODO: Find a better way for this?
event.numberSprite.kill();
continue;
}
var hasEvent:Bool = evt != null;
var pre:String = hasEvent && evt.ratingPrefix != null ? evt.ratingPrefix : event.ratingPrefix;
var suf:String = hasEvent && evt.ratingSuffix != null ? evt.ratingSuffix : event.ratingSuffix;
var numScale:Float = hasEvent && evt.numScale != null ? evt.numScale : event.numScale;
var numScore:FlxSprite = event.numberSprite.loadAnimatedGraphic(Paths.image('${pre}num${separatedScore.charAt(i)}${suf}'));
event.position.x += event.numSpacing * i;
if (event.resetSprite) {
CoolUtil.resetSprite(numScore, event.position.x, event.position.y);
}
numScore.antialiasing = hasEvent && evt.numAntialiasing != null ? evt.numAntialiasing : event.numAntialiasing;
numScore.scale.set(numScale, numScale);
numScore.updateHitbox();
numScore.acceleration.y = FlxG.random.int(200, 300);
numScore.velocity.y -= FlxG.random.int(140, 160);
numScore.velocity.x = FlxG.random.float(-5, 5);
numScore.acceleration.y = event.acceleration;
numScore.velocity.y -= event.velocity.y;
numScore.velocity.x = event.velocity.x;
FlxTween.tween(numScore, {alpha: 0}, 0.2, {
onComplete: function(tween:FlxTween)
{
numScore.kill();
},
startDelay: Conductor.crochet * 0.002
});
if (event.playTween) {
event.tween = FlxTween.tween(numScore, {alpha: 0}, event.tweenDuration, {
onComplete: function(tween:FlxTween) {
numScore.kill();
},
startDelay: event.startDelay
});
}
gameAndCharsEvent("onPostRatingsShown", event);
event.velocity.put();
event.position.put();
}
}
}
+25 -4
View File
@@ -2,6 +2,7 @@ package funkin.game.scoring;
import funkin.game.scoring.*;
import funkin.game.scoring.HitWindowData.WindowPreset;
import flixel.util.FlxSignal;
import haxe.ds.StringMap;
@@ -10,6 +11,9 @@ import haxe.ds.StringMap;
*/
class RatingManager
{
public var onRatingAdded:FlxTypedSignal<Rating->Void> = new FlxTypedSignal();
public var onRatingRemoved:FlxTypedSignal<Rating->Void> = new FlxTypedSignal();
public var hitWindows:StringMap<Float>;
public var ratingData:Array<Rating> = [];
public var lastHitWindow:Float = -1;
@@ -50,9 +54,9 @@ class RatingManager
}
addRating({name: "sick", window: getWindow("sick"), accuracy: 1, score: 300, splash: true});
addRating({name: "good", window: getWindow("good"), accuracy: 0.75, score: 200});
addRating({name: "bad", window: getWindow("bad"), accuracy: 0.45, score: 100});
addRating({name: "shit", window: getWindow("shit"), accuracy: 0.25, score: 50});
addRating({name: "good", window: getWindow("good"), accuracy: 0.75, score: 200, health: 0.015});
addRating({name: "bad", window: getWindow("bad"), accuracy: 0.45, score: 100, health: 0});
addRating({name: "shit", window: getWindow("shit"), accuracy: 0.25, score: 50, health: -0.05, breaksCombo: Flags.SHITS_BREAK_COMBO});
}
public function addRating(data:Dynamic)
@@ -71,7 +75,9 @@ class RatingManager
window: window,
accuracy: data.accuracy != null ? data.accuracy : 1,
score: data.score != null ? data.score : 0,
health: data.health != null ? data.health : 0.023,
splash: data.splash == true,
breaksCombo: data.breaksCombo == true,
hittable: data.hittable != null ? data.hittable : true
};
@@ -86,13 +92,18 @@ class RatingManager
ratingData.push(newRating);
ratingData.sort((a, b) -> Reflect.compare(a.window, b.window));
onRatingAdded.dispatch(newRating);
}
public function removeRating(name:String):Void
{
if (name == null) return;
name = name.toLowerCase();
ratingData = ratingData.filter(r -> r.name != name);
var toRemove = ratingData.filter(r -> r.name == name);
for (rating in toRemove) {
ratingData.remove(rating);
onRatingRemoved.dispatch(rating);
}
}
public function getHitWindow(name:String):Float
@@ -126,11 +137,21 @@ final class Rating
*/
public var score:Int = 0;
/**
* Amount of health given when earning this rating.
*/
public var health:Float = 0.023;
/**
* If this rating was hit, a note splash will appear.
*/
@:optional public var splash:Bool = false;
/**
* Whether the rating will break your combo or not.
*/
@:optional public var breaksCombo:Bool = false;
/**
* Whether the rating is hittable or not.
*/