Merge remote-tracking branch 'origin/rewrite/master' into feature/hashlink-hot-reload
This commit is contained in:
commit
bd4103fd11
|
@ -13,7 +13,7 @@ This game was made with love to Newgrounds and it's community. Extra love to Tom
|
||||||
|
|
||||||
**PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME**
|
**PLEASE USE THE LINKS ABOVE IF YOU JUST WANT TO PLAY THE GAME**
|
||||||
|
|
||||||
To learn how to install the necessary dependencies and compile the game from source, please check out our [building the game]() guide.
|
To learn how to install the necessary dependencies and compile the game from source, please check out our [building the game](/docs/compiling.md) guide.
|
||||||
|
|
||||||
# Contributing
|
# Contributing
|
||||||
|
|
||||||
|
|
2
assets
2
assets
|
@ -1 +1 @@
|
||||||
Subproject commit 160acbd8a854a9f677ef7587396340e79a5ea6ca
|
Subproject commit 599169f4786d1feb62a089d25112035977647805
|
|
@ -0,0 +1,19 @@
|
||||||
|
# Compiling Friday Night Funkin'
|
||||||
|
|
||||||
|
0. Setup
|
||||||
|
- Download Haxe from [Haxe.org](https://haxe.org)
|
||||||
|
1. Cloning the Repository: Make sure when you clone, you clone the submodules to get the assets repo:
|
||||||
|
- `git clone --recurse-submodules https://github.com/FunkinCrew/funkin-secret.git`
|
||||||
|
- If you accidentally cloned without the `assets` submodule (aka didn't follow the step above), you can run `git submodule update --init --recursive` to get the assets in a foolproof way.
|
||||||
|
2. Install `hmm` (run `haxelib --global install hmm` and then `haxelib --global run hmm setup`)
|
||||||
|
3. Install all haxelibs of the current branch by running `hmm install`
|
||||||
|
4. Platform setup
|
||||||
|
- For Windows, download the [Visual Studio Build Tools](https://aka.ms/vs/17/release/vs_BuildTools.exe)
|
||||||
|
- When prompted, select "Individual Components" and make sure to download the following:
|
||||||
|
- MSVC v143 VS 2022 C++ x64/x86 build tools
|
||||||
|
- Windows 10/11 SDK
|
||||||
|
- Mac: [`lime setup mac` Documentation](https://lime.openfl.org/docs/advanced-setup/macos/)
|
||||||
|
- Linux: [`lime setup linux` Documentation](https://lime.openfl.org/docs/advanced-setup/linux/)
|
||||||
|
- HTML5: Compiles without any extra setup
|
||||||
|
5. If you are targeting for native, you likely need to run `lime rebuild PLATFORM` and `lime rebuild PLATFORM -debug`
|
||||||
|
6. `lime test PLATFORM` !
|
|
@ -10,3 +10,6 @@
|
||||||
|
|
||||||
- `Class lists not properly generated. Try cleaning out your export folder, restarting your IDE, and rebuilding your project.`
|
- `Class lists not properly generated. Try cleaning out your export folder, restarting your IDE, and rebuilding your project.`
|
||||||
- This is a bug specific to HTML5. Simply perform the steps listed (don't forget to restart the IDE too).
|
- This is a bug specific to HTML5. Simply perform the steps listed (don't forget to restart the IDE too).
|
||||||
|
|
||||||
|
- `LINK : fatal error LNK1201: error writing to program database ''; check for insufficient disk space, invalid path, or insufficient privilege`
|
||||||
|
- This error occurs if the PDB file located in your `export` folder is in use or exceeds 4 GB. Try deleting the `export` folder and building again from scratch.
|
||||||
|
|
2
hmm.json
2
hmm.json
|
@ -11,7 +11,7 @@
|
||||||
"name": "flixel",
|
"name": "flixel",
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"dir": null,
|
"dir": null,
|
||||||
"ref": "25c84b29665329f7c6366342542a3978f29300ee",
|
"ref": "4d054bd10b05bb1309a0ba3427ffa5378e0b4b99",
|
||||||
"url": "https://github.com/FunkinCrew/flixel"
|
"url": "https://github.com/FunkinCrew/flixel"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,257 @@
|
||||||
|
package funkin.graphics;
|
||||||
|
|
||||||
|
import flash.geom.ColorTransform;
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.graphics.FlxGraphic;
|
||||||
|
import flixel.graphics.frames.FlxFrame;
|
||||||
|
import flixel.math.FlxMatrix;
|
||||||
|
import flixel.math.FlxRect;
|
||||||
|
import flixel.system.FlxAssets.FlxShader;
|
||||||
|
import funkin.graphics.shaders.RuntimeCustomBlendShader;
|
||||||
|
import funkin.graphics.framebuffer.BitmapDataUtil;
|
||||||
|
import funkin.graphics.framebuffer.FixedBitmapData;
|
||||||
|
import openfl.Lib;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display.BlendMode;
|
||||||
|
import openfl.display3D.textures.TextureBase;
|
||||||
|
import openfl.filters.BitmapFilter;
|
||||||
|
import openfl.filters.ShaderFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A FlxCamera with additional powerful features:
|
||||||
|
* - Grab the camera screen as a `BitmapData` and use it as a texture
|
||||||
|
* - Support `sprite.blend = DARKEN/HARDLIGHT/LIGHTEN/OVERLAY` to apply visual effects using certain sprites
|
||||||
|
* - NOTE: Several other blend modes work without FunkinCamera. Some still do not work.
|
||||||
|
* - NOTE: Framerate-independent camera tweening is fixed in Flixel 6.x. Rest in peace, SwagCamera.
|
||||||
|
*/
|
||||||
|
@:access(openfl.display.DisplayObject)
|
||||||
|
@:access(openfl.display.BitmapData)
|
||||||
|
@:access(openfl.display3D.Context3D)
|
||||||
|
@:access(openfl.display3D.textures.TextureBase)
|
||||||
|
@:access(flixel.graphics.FlxGraphic)
|
||||||
|
@:access(flixel.graphics.frames.FlxFrame)
|
||||||
|
class FunkinCamera extends FlxCamera
|
||||||
|
{
|
||||||
|
final grabbed:Array<BitmapData> = [];
|
||||||
|
final texturePool:Array<TextureBase> = [];
|
||||||
|
|
||||||
|
final bgTexture:TextureBase;
|
||||||
|
final bgBitmap:BitmapData;
|
||||||
|
final bgFrame:FlxFrame;
|
||||||
|
|
||||||
|
final customBlendShader:RuntimeCustomBlendShader;
|
||||||
|
final customBlendFilter:ShaderFilter;
|
||||||
|
|
||||||
|
var filtersApplied:Bool = false;
|
||||||
|
var bgItemCount:Int = 0;
|
||||||
|
|
||||||
|
public var shouldDraw:Bool = true;
|
||||||
|
|
||||||
|
public function new(x:Int = 0, y:Int = 0, width:Int = 0, height:Int = 0, zoom:Float = 0)
|
||||||
|
{
|
||||||
|
super(x, y, width, height, zoom);
|
||||||
|
bgTexture = pickTexture(width, height);
|
||||||
|
bgBitmap = FixedBitmapData.fromTexture(bgTexture);
|
||||||
|
bgFrame = new FlxFrame(new FlxGraphic('', null));
|
||||||
|
bgFrame.parent.bitmap = bgBitmap;
|
||||||
|
bgFrame.frame = new FlxRect();
|
||||||
|
customBlendShader = new RuntimeCustomBlendShader();
|
||||||
|
customBlendFilter = new ShaderFilter(customBlendShader);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grabs the camera screen and returns it as a `BitmapData`. The returned bitmap
|
||||||
|
* will not be referred by the camera so, changing it will not affect the scene.
|
||||||
|
* The returned bitmap **will be reused in the next frame**, so the content is available
|
||||||
|
* only in the current frame.
|
||||||
|
* @param applyFilters if this is `true`, the camera's filters will be applied to the grabbed bitmap,
|
||||||
|
* and the camera's filters will be disabled until the beginning of the next frame
|
||||||
|
* @param isolate if this is `true`, sprites to be rendered will only be rendered to the grabbed bitmap,
|
||||||
|
* and the grabbed bitmap will not include any previously rendered sprites
|
||||||
|
* @return the grabbed bitmap data
|
||||||
|
*/
|
||||||
|
public function grabScreen(applyFilters:Bool, isolate:Bool = false):BitmapData
|
||||||
|
{
|
||||||
|
final texture = pickTexture(width, height);
|
||||||
|
final bitmap = FixedBitmapData.fromTexture(texture);
|
||||||
|
squashTo(bitmap, applyFilters, isolate);
|
||||||
|
grabbed.push(bitmap);
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the filter immediately to the camera. This will be done independently from
|
||||||
|
* the camera's filters. This method can only be called after the first `grabScreen`
|
||||||
|
* in the frame.
|
||||||
|
* @param filter the filter
|
||||||
|
*/
|
||||||
|
public function applyFilter(filter:BitmapFilter):Void
|
||||||
|
{
|
||||||
|
if (grabbed.length == 0)
|
||||||
|
{
|
||||||
|
FlxG.log.error('grab screen before you can apply a filter!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BitmapDataUtil.applyFilter(bgBitmap, filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
function squashTo(bitmap:BitmapData, applyFilters:Bool, isolate:Bool, clearScreen:Bool = false):Void
|
||||||
|
{
|
||||||
|
if (applyFilters && isolate)
|
||||||
|
{
|
||||||
|
FlxG.log.error('cannot apply filters while isolating!');
|
||||||
|
}
|
||||||
|
if (filtersApplied && applyFilters)
|
||||||
|
{
|
||||||
|
FlxG.log.warn('filters already applied!');
|
||||||
|
}
|
||||||
|
static final matrix = new FlxMatrix();
|
||||||
|
|
||||||
|
// resize the background bitmap if needed
|
||||||
|
if (bgTexture.__width != width || bgTexture.__height != height)
|
||||||
|
{
|
||||||
|
BitmapDataUtil.resizeTexture(bgTexture, width, height);
|
||||||
|
bgBitmap.__resize(width, height);
|
||||||
|
bgFrame.parent.bitmap = bgBitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
// grab the bitmap
|
||||||
|
renderSkipping(isolate ? bgItemCount : 0);
|
||||||
|
bitmap.fillRect(bitmap.rect, 0);
|
||||||
|
matrix.setTo(1, 0, 0, 1, flashSprite.x, flashSprite.y);
|
||||||
|
if (applyFilters)
|
||||||
|
{
|
||||||
|
bitmap.draw(flashSprite, matrix);
|
||||||
|
flashSprite.filters = null;
|
||||||
|
filtersApplied = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final tmp = flashSprite.filters;
|
||||||
|
flashSprite.filters = null;
|
||||||
|
bitmap.draw(flashSprite, matrix);
|
||||||
|
flashSprite.filters = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isolate)
|
||||||
|
{
|
||||||
|
// also copy to the background bitmap
|
||||||
|
bgBitmap.fillRect(bgBitmap.rect, 0);
|
||||||
|
bgBitmap.draw(bitmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clearScreen)
|
||||||
|
{
|
||||||
|
// clear graphics data
|
||||||
|
super.clearDrawStack();
|
||||||
|
canvas.graphics.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// render the background bitmap
|
||||||
|
bgFrame.frame.set(0, 0, width, height);
|
||||||
|
matrix.setTo(viewWidth / width, 0, 0, viewHeight / height, viewMarginLeft, viewMarginTop);
|
||||||
|
drawPixels(bgFrame, matrix);
|
||||||
|
|
||||||
|
// count background draw items for future isolation
|
||||||
|
bgItemCount = 0;
|
||||||
|
{
|
||||||
|
var item = _headOfDrawStack;
|
||||||
|
while (item != null)
|
||||||
|
{
|
||||||
|
item = item.next;
|
||||||
|
bgItemCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderSkipping(count:Int):Void
|
||||||
|
{
|
||||||
|
var item = _headOfDrawStack;
|
||||||
|
while (item != null)
|
||||||
|
{
|
||||||
|
if (--count < 0) item.render(this);
|
||||||
|
item = item.next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function drawPixels(?frame:FlxFrame, ?pixels:BitmapData, matrix:FlxMatrix, ?transform:ColorTransform, ?blend:BlendMode, ?smoothing:Bool = false,
|
||||||
|
?shader:FlxShader):Void
|
||||||
|
{
|
||||||
|
if (!shouldDraw) return;
|
||||||
|
|
||||||
|
if ( switch blend
|
||||||
|
{
|
||||||
|
case DARKEN | HARDLIGHT | LIGHTEN | OVERLAY: true;
|
||||||
|
case _: false;
|
||||||
|
})
|
||||||
|
{
|
||||||
|
// squash the screen
|
||||||
|
grabScreen(false);
|
||||||
|
// render without blend
|
||||||
|
super.drawPixels(frame, pixels, matrix, transform, null, smoothing, shader);
|
||||||
|
// get the isolated bitmap
|
||||||
|
final isolated = grabScreen(false, true);
|
||||||
|
// apply fullscreen blend
|
||||||
|
customBlendShader.blend = blend;
|
||||||
|
customBlendShader.source = isolated;
|
||||||
|
customBlendShader.updateViewInfo(FlxG.width, FlxG.height, this);
|
||||||
|
applyFilter(customBlendFilter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
super.drawPixels(frame, pixels, matrix, transform, blend, smoothing, shader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override function destroy():Void
|
||||||
|
{
|
||||||
|
super.destroy();
|
||||||
|
disposeTextures();
|
||||||
|
}
|
||||||
|
|
||||||
|
override function clearDrawStack():Void
|
||||||
|
{
|
||||||
|
super.clearDrawStack();
|
||||||
|
// also clear grabbed bitmaps
|
||||||
|
for (bitmap in grabbed)
|
||||||
|
{
|
||||||
|
texturePool.push(bitmap.__texture);
|
||||||
|
bitmap.dispose(); // this doesn't release the texture
|
||||||
|
}
|
||||||
|
grabbed.clear();
|
||||||
|
// clear filters applied flag
|
||||||
|
filtersApplied = false;
|
||||||
|
bgItemCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function pickTexture(width:Int, height:Int):TextureBase
|
||||||
|
{
|
||||||
|
// zero-sized textures will be problematic
|
||||||
|
width = width < 1 ? 1 : width;
|
||||||
|
height = height < 1 ? 1 : height;
|
||||||
|
if (texturePool.length > 0)
|
||||||
|
{
|
||||||
|
final res = texturePool.pop();
|
||||||
|
BitmapDataUtil.resizeTexture(res, width, height);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
return Lib.current.stage.context3D.createTexture(width, height, BGRA, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disposeTextures():Void
|
||||||
|
{
|
||||||
|
trace('disposing textures');
|
||||||
|
for (bitmap in grabbed)
|
||||||
|
{
|
||||||
|
bitmap.dispose();
|
||||||
|
}
|
||||||
|
grabbed.clear();
|
||||||
|
for (texture in texturePool)
|
||||||
|
{
|
||||||
|
texture.dispose();
|
||||||
|
}
|
||||||
|
texturePool.resize(0);
|
||||||
|
bgTexture.dispose();
|
||||||
|
bgBitmap.dispose();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
package funkin.graphics.framebuffer;
|
||||||
|
|
||||||
|
import flixel.FlxG;
|
||||||
|
import openfl.Lib;
|
||||||
|
import openfl.display.Bitmap;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display.Sprite;
|
||||||
|
import openfl.display3D.Context3DTextureFormat;
|
||||||
|
import openfl.display3D.textures.TextureBase;
|
||||||
|
import openfl.filters.BitmapFilter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides cool stuff for `BitmapData`s that have a hardware texture internally.
|
||||||
|
*/
|
||||||
|
@:access(openfl.display.BitmapData)
|
||||||
|
@:access(openfl.display3D.textures.TextureBase)
|
||||||
|
@:access(openfl.display3D.Context3D)
|
||||||
|
class BitmapDataUtil
|
||||||
|
{
|
||||||
|
static function getCache():{sprite:Sprite, bitmap:Bitmap}
|
||||||
|
{
|
||||||
|
static var cache:{sprite:Sprite, bitmap:Bitmap} = null;
|
||||||
|
if (cache == null)
|
||||||
|
{
|
||||||
|
final sprite = new Sprite();
|
||||||
|
final bitmap = new Bitmap();
|
||||||
|
sprite.addChild(bitmap);
|
||||||
|
cache =
|
||||||
|
{
|
||||||
|
sprite: sprite,
|
||||||
|
bitmap: bitmap
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies a bitmap filter to a bitmap immediately. The bitmap filter may refer
|
||||||
|
* the bitmap itself as a shader input.
|
||||||
|
* @param bitmap the bitmap data
|
||||||
|
* @param filter the bitmap filter
|
||||||
|
*/
|
||||||
|
public static function applyFilter(bitmap:BitmapData, filter:BitmapFilter):Void
|
||||||
|
{
|
||||||
|
hardwareCheck(bitmap);
|
||||||
|
final cache = getCache();
|
||||||
|
cache.bitmap.bitmapData = bitmap;
|
||||||
|
cache.sprite.filters = [filter];
|
||||||
|
bitmap.draw(cache.sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a bitmap with a hardware texture.
|
||||||
|
* @param width the width
|
||||||
|
* @param height the height
|
||||||
|
* @param format the format if the internal texture
|
||||||
|
* @return the bitmap
|
||||||
|
*/
|
||||||
|
public static function create(width:Int, height:Int, format:Context3DTextureFormat = BGRA):FixedBitmapData
|
||||||
|
{
|
||||||
|
final texture = Lib.current.stage.context3D.createTexture(width, height, format, true);
|
||||||
|
return FixedBitmapData.fromTexture(texture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the bitmap.
|
||||||
|
* @param bitmap the bitmap data
|
||||||
|
* @param width the width
|
||||||
|
* @param height the height
|
||||||
|
*/
|
||||||
|
public static function resize(bitmap:BitmapData, width:Int, height:Int):Void
|
||||||
|
{
|
||||||
|
hardwareCheck(bitmap);
|
||||||
|
if (bitmap.width == width && bitmap.height == height) return;
|
||||||
|
bitmap.width = width;
|
||||||
|
bitmap.height = height;
|
||||||
|
resizeTexture(bitmap.__texture, width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resizes the texture.
|
||||||
|
* @param texture the texture
|
||||||
|
* @param width the width
|
||||||
|
* @param height the height
|
||||||
|
*/
|
||||||
|
public static function resizeTexture(texture:TextureBase, width:Int, height:Int):Void
|
||||||
|
{
|
||||||
|
if (texture.__width == width && texture.__height == height) return;
|
||||||
|
texture.__width = width;
|
||||||
|
texture.__height = height;
|
||||||
|
final context = texture.__context;
|
||||||
|
final gl = context.gl;
|
||||||
|
context.__bindGLTexture2D(texture.__textureID);
|
||||||
|
gl.texImage2D(gl.TEXTURE_2D, 0, texture.__internalFormat, width, height, 0, texture.__format, gl.UNSIGNED_BYTE, null);
|
||||||
|
context.__bindGLTexture2D(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the content of `src` to `dst`. The destination bitmap `dst` will be resized
|
||||||
|
* so that it has the same size as `src`.
|
||||||
|
* @param dst the destination bitmap
|
||||||
|
* @param src the source bitmap
|
||||||
|
*/
|
||||||
|
public static function copy(dst:BitmapData, src:BitmapData):Void
|
||||||
|
{
|
||||||
|
hardwareCheck(dst);
|
||||||
|
hardwareCheck(src);
|
||||||
|
final cache = getCache();
|
||||||
|
cache.bitmap.bitmapData = src;
|
||||||
|
cache.sprite.filters = null;
|
||||||
|
resize(dst, src.width, src.height);
|
||||||
|
dst.fillRect(dst.rect, 0);
|
||||||
|
dst.draw(cache.sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
static function hardwareCheck(bitmap:BitmapData):Void
|
||||||
|
{
|
||||||
|
if (bitmap.readable)
|
||||||
|
{
|
||||||
|
FlxG.log.error('do not use `BitmapDataUtil` for non-GPU bitmaps!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package funkin.graphics.framebuffer;
|
||||||
|
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display.DisplayObject;
|
||||||
|
import openfl.display.DisplayObjectContainer;
|
||||||
|
import openfl.display.IBitmapDrawable;
|
||||||
|
import openfl.display.OpenGLRenderer;
|
||||||
|
import openfl.display3D.textures.TextureBase;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* `BitmapData` is kinda broken so I fixed it.
|
||||||
|
*/
|
||||||
|
@:access(openfl.display3D.textures.TextureBase)
|
||||||
|
@:access(openfl.display.OpenGLRenderer)
|
||||||
|
class FixedBitmapData extends BitmapData
|
||||||
|
{
|
||||||
|
override function __drawGL(source:IBitmapDrawable, renderer:OpenGLRenderer):Void
|
||||||
|
{
|
||||||
|
if (Std.isOfType(source, DisplayObject))
|
||||||
|
{
|
||||||
|
final object:DisplayObjectContainer = cast source;
|
||||||
|
renderer.__stage = object.stage;
|
||||||
|
}
|
||||||
|
super.__drawGL(source, renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Never use `BitmapData.fromTexture`, always use this.
|
||||||
|
* @param texture the texture
|
||||||
|
* @return the bitmap data
|
||||||
|
*/
|
||||||
|
public static function fromTexture(texture:TextureBase):FixedBitmapData
|
||||||
|
{
|
||||||
|
if (texture == null) return null;
|
||||||
|
final bitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0);
|
||||||
|
bitmapData.readable = false;
|
||||||
|
bitmapData.__texture = texture;
|
||||||
|
bitmapData.__textureContext = texture.__textureContext;
|
||||||
|
bitmapData.image = null;
|
||||||
|
return bitmapData;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
package funkin.graphics.framebuffer;
|
||||||
|
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import openfl.Lib;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display3D.textures.TextureBase;
|
||||||
|
import openfl.geom.Matrix;
|
||||||
|
import openfl.geom.Rectangle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A single frame buffer. Used by `FrameBufferManager`.
|
||||||
|
*/
|
||||||
|
class FrameBuffer
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* The bitmap data of the frame buffer.
|
||||||
|
*/
|
||||||
|
public var bitmap(default, null):BitmapData = null;
|
||||||
|
|
||||||
|
var texture:TextureBase;
|
||||||
|
final camera:FlxCamera;
|
||||||
|
final spriteCopies:Array<SpriteCopy> = [];
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
camera = new FlxCamera();
|
||||||
|
camera.antialiasing = false;
|
||||||
|
camera.bgColor = FlxColor.TRANSPARENT;
|
||||||
|
@:privateAccess camera.flashSprite.stage = Lib.current.stage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a frame buffer with the given size.
|
||||||
|
* @param width the width
|
||||||
|
* @param height the height
|
||||||
|
* @param bgColor the background color
|
||||||
|
*/
|
||||||
|
public function create(width:Int, height:Int, bgColor:FlxColor):Void
|
||||||
|
{
|
||||||
|
dispose();
|
||||||
|
texture = Lib.current.stage.context3D.createTexture(width, height, BGRA, true);
|
||||||
|
bitmap = FixedBitmapData.fromTexture(texture);
|
||||||
|
camera.bgColor = bgColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Makes the internal camera follows the target camera.
|
||||||
|
* @param target the target camera
|
||||||
|
*/
|
||||||
|
public function follow(target:FlxCamera):Void
|
||||||
|
{
|
||||||
|
camera.x = target.x;
|
||||||
|
camera.y = target.y;
|
||||||
|
camera.width = target.width;
|
||||||
|
camera.height = target.height;
|
||||||
|
camera.scroll.x = target.scroll.x;
|
||||||
|
camera.scroll.y = target.scroll.y;
|
||||||
|
camera.setScale(target.scaleX, target.scaleY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks the frame buffer and clears the buffer.
|
||||||
|
*/
|
||||||
|
@:access(flixel.FlxCamera)
|
||||||
|
public function lock():Void
|
||||||
|
{
|
||||||
|
camera.clearDrawStack();
|
||||||
|
camera.canvas.graphics.clear();
|
||||||
|
camera.fill(camera.bgColor.to24Bit(), camera.useBgAlphaBlending, camera.bgColor.alphaFloat);
|
||||||
|
#if FLX_DEBUG
|
||||||
|
camera.debugLayer.graphics.clear();
|
||||||
|
#end
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders all sprite copies.
|
||||||
|
*/
|
||||||
|
@:access(flixel.FlxCamera)
|
||||||
|
public function render():Void
|
||||||
|
{
|
||||||
|
for (spriteCopy in spriteCopies)
|
||||||
|
{
|
||||||
|
spriteCopy.render(camera);
|
||||||
|
}
|
||||||
|
camera.render();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks the frame buffer and makes the bitmap ready to use.
|
||||||
|
*/
|
||||||
|
public function unlock():Void
|
||||||
|
{
|
||||||
|
bitmap.fillRect(new Rectangle(0, 0, bitmap.width, bitmap.height), 0);
|
||||||
|
bitmap.draw(camera.flashSprite, new Matrix(1, 0, 0, 1, camera.flashSprite.x, camera.flashSprite.y));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diposes stuff. Call `create` again if you want to reuse the instance.
|
||||||
|
*/
|
||||||
|
public function dispose():Void
|
||||||
|
{
|
||||||
|
if (texture != null)
|
||||||
|
{
|
||||||
|
texture.dispose();
|
||||||
|
texture = null;
|
||||||
|
bitmap.dispose();
|
||||||
|
bitmap = null;
|
||||||
|
}
|
||||||
|
spriteCopies.resize(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a sprite copy to the frame buffer.
|
||||||
|
* @param spriteCopy the sprite copy
|
||||||
|
*/
|
||||||
|
public function addSpriteCopy(spriteCopy:SpriteCopy):Void
|
||||||
|
{
|
||||||
|
spriteCopies.push(spriteCopy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the sprite to the frame buffer. The sprite will only be seen from
|
||||||
|
* the frame buffer.
|
||||||
|
* @param sprite the sprite
|
||||||
|
*/
|
||||||
|
public function moveSprite(sprite:FlxSprite):Void
|
||||||
|
{
|
||||||
|
sprite.cameras = [camera];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
package funkin.graphics.framebuffer;
|
||||||
|
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.FlxG;
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages frame buffers and gives access to each frame buffer.
|
||||||
|
*/
|
||||||
|
class FrameBufferManager
|
||||||
|
{
|
||||||
|
final camera:FlxCamera;
|
||||||
|
final frameBufferMap:Map<String, FrameBuffer> = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a frame buffer manager that targets `camera`.
|
||||||
|
* @param camera the target camera.
|
||||||
|
*/
|
||||||
|
public function new(camera:FlxCamera)
|
||||||
|
{
|
||||||
|
this.camera = camera;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new frame buffer with a name.
|
||||||
|
* @param name the name
|
||||||
|
* @param bgColor the background color
|
||||||
|
* @return the bitmap data of the frame buffer. the bitmap data instance
|
||||||
|
* will not be changed through frame buffer updates.
|
||||||
|
*/
|
||||||
|
public function createFrameBuffer(name:String, bgColor:FlxColor):BitmapData
|
||||||
|
{
|
||||||
|
if (frameBufferMap.exists(name))
|
||||||
|
{
|
||||||
|
FlxG.log.warn('frame buffer "$name" already exists');
|
||||||
|
frameBufferMap[name].dispose();
|
||||||
|
frameBufferMap.remove(name);
|
||||||
|
}
|
||||||
|
final fb = new FrameBuffer();
|
||||||
|
fb.create(camera.width, camera.height, bgColor);
|
||||||
|
frameBufferMap[name] = fb;
|
||||||
|
return fb.bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a copy of the sprite to the frame buffer.
|
||||||
|
* @param name the name of the frame buffer
|
||||||
|
* @param sprite the sprite
|
||||||
|
* @param color if this is not `null`, the sprite will be filled with the color.
|
||||||
|
* if this is `null`, the sprite will keep its original color.
|
||||||
|
*/
|
||||||
|
public function copySpriteTo(name:String, sprite:FlxSprite, color:Null<FlxColor> = null):Void
|
||||||
|
{
|
||||||
|
if (!frameBufferMap.exists(name))
|
||||||
|
{
|
||||||
|
FlxG.log.warn('frame buffer "$name" does not exist');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frameBufferMap[name].addSpriteCopy(new SpriteCopy(sprite, color));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the sprite to the frame buffer. The sprite will only be seen from the frame buffer.
|
||||||
|
* @param name the name of the frame buffer
|
||||||
|
* @param sprite the sprite
|
||||||
|
*/
|
||||||
|
public function moveSpriteTo(name:String, sprite:FlxSprite):Void
|
||||||
|
{
|
||||||
|
if (!frameBufferMap.exists(name))
|
||||||
|
{
|
||||||
|
FlxG.log.warn('frame buffer "$name" does not exist');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
frameBufferMap[name].moveSprite(sprite);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Call this before drawing anything.
|
||||||
|
*/
|
||||||
|
public function lock():Void
|
||||||
|
{
|
||||||
|
for (_ => fb in frameBufferMap)
|
||||||
|
{
|
||||||
|
fb.follow(camera);
|
||||||
|
fb.lock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks the frame buffers. This updates the bitmap data of each frame buffer.
|
||||||
|
*/
|
||||||
|
public function unlock():Void
|
||||||
|
{
|
||||||
|
for (_ => fb in frameBufferMap)
|
||||||
|
{
|
||||||
|
fb.render();
|
||||||
|
}
|
||||||
|
for (_ => fb in frameBufferMap)
|
||||||
|
{
|
||||||
|
fb.unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bitmap data of the frame buffer
|
||||||
|
* @param name the name of the frame buffer
|
||||||
|
* @return the bitmap data
|
||||||
|
*/
|
||||||
|
public function getFrameBuffer(name:String):BitmapData
|
||||||
|
{
|
||||||
|
return frameBufferMap[name].bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes all frame buffers. The instance can be reused.
|
||||||
|
*/
|
||||||
|
public function dispose():Void
|
||||||
|
{
|
||||||
|
for (_ => fb in frameBufferMap)
|
||||||
|
{
|
||||||
|
fb.dispose();
|
||||||
|
}
|
||||||
|
frameBufferMap.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
package funkin.graphics.framebuffer;
|
||||||
|
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.FlxSprite;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A copy of a `FlxSprite` with a specified color. Used to render the sprite to a frame buffer.
|
||||||
|
*/
|
||||||
|
class SpriteCopy
|
||||||
|
{
|
||||||
|
final sprite:FlxSprite;
|
||||||
|
final color:Null<FlxColor>;
|
||||||
|
|
||||||
|
public function new(sprite:FlxSprite, color:Null<FlxColor>)
|
||||||
|
{
|
||||||
|
this.sprite = sprite;
|
||||||
|
this.color = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the copy to the camera.
|
||||||
|
* @param camera the camera
|
||||||
|
*/
|
||||||
|
@:access(flixel.FlxSprite)
|
||||||
|
public function render(camera:FlxCamera):Void
|
||||||
|
{
|
||||||
|
if (color == null)
|
||||||
|
{
|
||||||
|
final tmpCameras = sprite._cameras;
|
||||||
|
sprite._cameras = [camera];
|
||||||
|
sprite.draw();
|
||||||
|
sprite._cameras = tmpCameras;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
final rMult = sprite.colorTransform.redMultiplier;
|
||||||
|
final gMult = sprite.colorTransform.greenMultiplier;
|
||||||
|
final bMult = sprite.colorTransform.blueMultiplier;
|
||||||
|
final aMult = sprite.colorTransform.alphaMultiplier;
|
||||||
|
final rOff = Std.int(sprite.colorTransform.redOffset);
|
||||||
|
final gOff = Std.int(sprite.colorTransform.greenOffset);
|
||||||
|
final bOff = Std.int(sprite.colorTransform.blueOffset);
|
||||||
|
final aOff = Std.int(sprite.colorTransform.alphaOffset);
|
||||||
|
final tmpCameras = sprite._cameras;
|
||||||
|
final tmpShader = sprite.shader;
|
||||||
|
|
||||||
|
sprite._cameras = [camera];
|
||||||
|
sprite.shader = null;
|
||||||
|
|
||||||
|
sprite.setColorTransform(0, 0, 0, 1, color.red, color.green, color.blue, 0);
|
||||||
|
sprite.draw();
|
||||||
|
|
||||||
|
sprite._cameras = tmpCameras;
|
||||||
|
sprite.shader = tmpShader;
|
||||||
|
sprite.setColorTransform(rMult, gMult, bMult, aMult, rOff, gOff, bOff, aOff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package funkin.graphics.shaders;
|
||||||
|
|
||||||
|
import flixel.addons.display.FlxRuntimeShader;
|
||||||
|
import openfl.Assets;
|
||||||
|
|
||||||
|
class PuddleShader extends FlxRuntimeShader
|
||||||
|
{
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super(Assets.getText(Paths.frag('puddle')));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package funkin.graphics.shaders;
|
||||||
|
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display.BlendMode;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
|
||||||
|
class RuntimeCustomBlendShader extends RuntimePostEffectShader
|
||||||
|
{
|
||||||
|
public var source(default, set):BitmapData;
|
||||||
|
|
||||||
|
function set_source(value:BitmapData):BitmapData
|
||||||
|
{
|
||||||
|
this.setBitmapData("source", value);
|
||||||
|
return source = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var blend(default, set):BlendMode;
|
||||||
|
|
||||||
|
function set_blend(value:BlendMode):BlendMode
|
||||||
|
{
|
||||||
|
this.setInt("blendMode", cast value);
|
||||||
|
return blend = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super(Assets.getText("assets/shaders/customBlend.frag"));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
package funkin.graphics.shaders;
|
||||||
|
|
||||||
|
import flixel.FlxCamera;
|
||||||
|
import flixel.FlxG;
|
||||||
|
import flixel.addons.display.FlxRuntimeShader;
|
||||||
|
import lime.graphics.opengl.GLProgram;
|
||||||
|
import lime.utils.Log;
|
||||||
|
|
||||||
|
class RuntimePostEffectShader extends FlxRuntimeShader
|
||||||
|
{
|
||||||
|
@:glVertexHeader('
|
||||||
|
// normalized screen coord
|
||||||
|
// (0, 0) is the top left of the window
|
||||||
|
// (1, 1) is the bottom right of the window
|
||||||
|
varying vec2 screenCoord;
|
||||||
|
', true)
|
||||||
|
@:glVertexBody('
|
||||||
|
screenCoord = vec2(
|
||||||
|
openfl_TextureCoord.x > 0.0 ? 1.0 : 0.0,
|
||||||
|
openfl_TextureCoord.y > 0.0 ? 1.0 : 0.0
|
||||||
|
);
|
||||||
|
')
|
||||||
|
@:glFragmentHeader('
|
||||||
|
// normalized screen coord
|
||||||
|
// (0, 0) is the top left of the window
|
||||||
|
// (1, 1) is the bottom right of the window
|
||||||
|
varying vec2 screenCoord;
|
||||||
|
|
||||||
|
// equals (FlxG.width, FlxG.height)
|
||||||
|
uniform vec2 uScreenResolution;
|
||||||
|
|
||||||
|
// equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom)
|
||||||
|
uniform vec4 uCameraBounds;
|
||||||
|
|
||||||
|
// screen coord -> world coord conversion
|
||||||
|
// returns world coord in px
|
||||||
|
vec2 screenToWorld(vec2 screenCoord) {
|
||||||
|
float left = uCameraBounds.x;
|
||||||
|
float top = uCameraBounds.y;
|
||||||
|
float right = uCameraBounds.z;
|
||||||
|
float bottom = uCameraBounds.w;
|
||||||
|
vec2 scale = vec2(right - left, bottom - top);
|
||||||
|
vec2 offset = vec2(left, top);
|
||||||
|
return screenCoord * scale + offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// world coord -> screen coord conversion
|
||||||
|
// returns normalized screen coord
|
||||||
|
vec2 worldToScreen(vec2 worldCoord) {
|
||||||
|
float left = uCameraBounds.x;
|
||||||
|
float top = uCameraBounds.y;
|
||||||
|
float right = uCameraBounds.z;
|
||||||
|
float bottom = uCameraBounds.w;
|
||||||
|
vec2 scale = vec2(right - left, bottom - top);
|
||||||
|
vec2 offset = vec2(left, top);
|
||||||
|
return (worldCoord - offset) / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
// internally used to get the maximum `openfl_TextureCoordv`
|
||||||
|
vec2 bitmapCoordScale() {
|
||||||
|
return openfl_TextureCoordv / screenCoord;
|
||||||
|
}
|
||||||
|
|
||||||
|
// internally used to compute bitmap coord
|
||||||
|
vec2 screenToBitmap(vec2 screenCoord) {
|
||||||
|
return screenCoord * bitmapCoordScale();
|
||||||
|
}
|
||||||
|
|
||||||
|
// samples the frame buffer using a screen coord
|
||||||
|
vec4 sampleBitmapScreen(vec2 screenCoord) {
|
||||||
|
return texture2D(bitmap, screenToBitmap(screenCoord));
|
||||||
|
}
|
||||||
|
|
||||||
|
// samples the frame buffer using a world coord
|
||||||
|
vec4 sampleBitmapWorld(vec2 worldCoord) {
|
||||||
|
return sampleBitmapScreen(worldToScreen(worldCoord));
|
||||||
|
}
|
||||||
|
', true)
|
||||||
|
public function new(fragmentSource:String = null, glVersion:String = null)
|
||||||
|
{
|
||||||
|
super(fragmentSource, null, glVersion);
|
||||||
|
uScreenResolution.value = [FlxG.width, FlxG.height];
|
||||||
|
}
|
||||||
|
|
||||||
|
// basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good
|
||||||
|
public function updateViewInfo(screenWidth:Float, screenHeight:Float, camera:FlxCamera):Void
|
||||||
|
{
|
||||||
|
uScreenResolution.value = [screenWidth, screenHeight];
|
||||||
|
uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom];
|
||||||
|
}
|
||||||
|
|
||||||
|
override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
final res = super.__createGLProgram(vertexSource, fragmentSource);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
Log.warn(error); // prevent the app from dying immediately
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
package funkin.graphics.shaders;
|
||||||
|
|
||||||
|
import flixel.system.FlxAssets.FlxShader;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.display.ShaderParameter;
|
||||||
|
import openfl.display.ShaderParameterType;
|
||||||
|
import openfl.utils.Assets;
|
||||||
|
|
||||||
|
typedef Light =
|
||||||
|
{
|
||||||
|
var position:Array<Float>;
|
||||||
|
var color:Array<Float>;
|
||||||
|
var radius:Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
class RuntimeRainShader extends RuntimePostEffectShader
|
||||||
|
{
|
||||||
|
static final MAX_LIGHTS:Int = 8;
|
||||||
|
|
||||||
|
public var lights:Array<
|
||||||
|
{
|
||||||
|
position:ShaderParameter<Float>,
|
||||||
|
color:ShaderParameter<Float>,
|
||||||
|
radius:ShaderParameter<Float>,
|
||||||
|
}>;
|
||||||
|
|
||||||
|
public var time(default, set):Float = 1;
|
||||||
|
|
||||||
|
function set_time(value:Float):Float
|
||||||
|
{
|
||||||
|
this.setFloat('uTime', value);
|
||||||
|
return time = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The scale of the rain depends on the world coordinate system, so higher resolution makes
|
||||||
|
// the raindrops smaller. This parameter can be used to adjust the total scale of the scene.
|
||||||
|
// The size of the raindrops is proportional to the value of this parameter.
|
||||||
|
public var scale(default, set):Float = 1;
|
||||||
|
|
||||||
|
function set_scale(value:Float):Float
|
||||||
|
{
|
||||||
|
this.setFloat('uScale', value);
|
||||||
|
return scale = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The intensity of the rain. Zero means no rain and one means the maximum amount of rain.
|
||||||
|
public var intensity(default, set):Float = 0.5;
|
||||||
|
|
||||||
|
function set_intensity(value:Float):Float
|
||||||
|
{
|
||||||
|
this.setFloat('uIntensity', value);
|
||||||
|
return intensity = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the y coord of the puddle, used to mirror things
|
||||||
|
public var puddleY(default, set):Float = 0;
|
||||||
|
|
||||||
|
function set_puddleY(value:Float):Float
|
||||||
|
{
|
||||||
|
this.setFloat('uPuddleY', value);
|
||||||
|
return puddleY = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// the y scale of the puddle, the less this value the more the puddle effects squished
|
||||||
|
public var puddleScaleY(default, set):Float = 0;
|
||||||
|
|
||||||
|
function set_puddleScaleY(value:Float):Float
|
||||||
|
{
|
||||||
|
this.setFloat('uPuddleScaleY', value);
|
||||||
|
return puddleScaleY = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var blurredScreen(default, set):BitmapData;
|
||||||
|
|
||||||
|
function set_blurredScreen(value:BitmapData):BitmapData
|
||||||
|
{
|
||||||
|
this.setBitmapData('uBlurredScreen', value);
|
||||||
|
return blurredScreen = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var mask(default, set):BitmapData;
|
||||||
|
|
||||||
|
function set_mask(value:BitmapData):BitmapData
|
||||||
|
{
|
||||||
|
this.setBitmapData('uMask', value);
|
||||||
|
return mask = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var lightMap(default, set):BitmapData;
|
||||||
|
|
||||||
|
function set_lightMap(value:BitmapData):BitmapData
|
||||||
|
{
|
||||||
|
this.setBitmapData('uLightMap', value);
|
||||||
|
return lightMap = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public var numLights(default, set):Int = 0;
|
||||||
|
|
||||||
|
function set_numLights(value:Int):Int
|
||||||
|
{
|
||||||
|
this.setInt('numLights', value);
|
||||||
|
return numLights = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function new()
|
||||||
|
{
|
||||||
|
super(Assets.getText(Paths.frag('rain')));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function update(elapsed:Float):Void
|
||||||
|
{
|
||||||
|
time += elapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
override function __processGLData(source:String, storageType:String):Void
|
||||||
|
{
|
||||||
|
super.__processGLData(source, storageType);
|
||||||
|
if (storageType == 'uniform')
|
||||||
|
{
|
||||||
|
lights = [
|
||||||
|
for (i in 0...MAX_LIGHTS)
|
||||||
|
{
|
||||||
|
position: addFloatUniform('lights[$i].position', 2),
|
||||||
|
color: addFloatUniform('lights[$i].color', 3),
|
||||||
|
radius: addFloatUniform('lights[$i].radius', 1),
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@:access(openfl.display.ShaderParameter)
|
||||||
|
function addFloatUniform(name:String, length:Int):ShaderParameter<Float>
|
||||||
|
{
|
||||||
|
final res = new ShaderParameter<Float>();
|
||||||
|
res.name = name;
|
||||||
|
res.type = [null, FLOAT, FLOAT2, FLOAT3, FLOAT4][length];
|
||||||
|
res.__arrayLength = 1;
|
||||||
|
res.__isFloat = true;
|
||||||
|
res.__isUniform = true;
|
||||||
|
res.__length = length;
|
||||||
|
__paramFloat.push(res);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,6 +6,7 @@ import funkin.util.Constants;
|
||||||
import funkin.Paths;
|
import funkin.Paths;
|
||||||
import funkin.Preferences;
|
import funkin.Preferences;
|
||||||
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
|
import flixel.FlxG; // This one in particular causes a compile error if you're using macros.
|
||||||
|
import flixel.system.debug.watch.Tracker;
|
||||||
|
|
||||||
// These are great.
|
// These are great.
|
||||||
using Lambda;
|
using Lambda;
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
package funkin.modding.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A script that can be tied to an FlxAtlasSprite
|
||||||
|
* Create a scripted class that extends FlxAtlasSprite to use this.
|
||||||
|
*/
|
||||||
|
@:hscriptClass
|
||||||
|
class ScriptedFlxAtlasSprite extends funkin.graphics.adobeanimate.FlxAtlasSprite implements HScriptedClass {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
package funkin.modding.base;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A script that can be tied to an FlxSprite.
|
||||||
|
* Create a scripted class that extends FlxSprite to use this.
|
||||||
|
*/
|
||||||
|
@:hscriptClass
|
||||||
|
class ScriptedFunkinSprite extends funkin.graphics.FunkinSprite implements HScriptedClass {}
|
|
@ -38,7 +38,8 @@ class PauseSubState extends MusicBeatSubState
|
||||||
|
|
||||||
var practiceText:FlxText;
|
var practiceText:FlxText;
|
||||||
|
|
||||||
var exitingToMenu:Bool = false;
|
public var exitingToMenu:Bool = false;
|
||||||
|
|
||||||
var bg:FlxSprite;
|
var bg:FlxSprite;
|
||||||
var metaDataGrp:FlxTypedGroup<FlxSprite>;
|
var metaDataGrp:FlxTypedGroup<FlxSprite>;
|
||||||
|
|
||||||
|
@ -234,7 +235,7 @@ class PauseSubState extends MusicBeatSubState
|
||||||
if (PlayStatePlaylist.isStoryMode)
|
if (PlayStatePlaylist.isStoryMode)
|
||||||
{
|
{
|
||||||
PlayStatePlaylist.reset();
|
PlayStatePlaylist.reset();
|
||||||
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState()));
|
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new funkin.ui.story.StoryMenuState(sticker)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,68 +1,79 @@
|
||||||
package funkin.play;
|
package funkin.play;
|
||||||
|
|
||||||
import funkin.ui.SwagCamera;
|
|
||||||
import flixel.addons.transition.FlxTransitionableSubState;
|
|
||||||
import funkin.ui.debug.charting.ChartEditorState;
|
|
||||||
import haxe.Int64;
|
|
||||||
import funkin.play.notes.notestyle.NoteStyle;
|
|
||||||
import funkin.data.notestyle.NoteStyleData;
|
|
||||||
import funkin.data.notestyle.NoteStyleRegistry;
|
|
||||||
import flixel.addons.display.FlxPieDial;
|
import flixel.addons.display.FlxPieDial;
|
||||||
import flixel.addons.transition.Transition;
|
import flixel.addons.display.FlxPieDial;
|
||||||
import flixel.addons.transition.FlxTransitionableState;
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
|
import flixel.addons.transition.FlxTransitionableState;
|
||||||
|
import flixel.addons.transition.FlxTransitionableSubState;
|
||||||
|
import flixel.addons.transition.FlxTransitionableSubState;
|
||||||
|
import flixel.addons.transition.Transition;
|
||||||
|
import flixel.addons.transition.Transition;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.FlxState;
|
import flixel.FlxState;
|
||||||
import flixel.FlxSubState;
|
import flixel.FlxSubState;
|
||||||
import flixel.input.keyboard.FlxKey;
|
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
import funkin.play.components.ComboMilestone;
|
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import funkin.play.components.HealthIcon;
|
|
||||||
import funkin.ui.MusicBeatSubState;
|
|
||||||
import flixel.math.FlxRect;
|
import flixel.math.FlxRect;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import flixel.ui.FlxBar;
|
import flixel.ui.FlxBar;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.api.newgrounds.NGio;
|
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
|
import funkin.api.newgrounds.NGio;
|
||||||
import funkin.audio.VoicesGroup;
|
import funkin.audio.VoicesGroup;
|
||||||
import funkin.save.Save;
|
import funkin.audio.VoicesGroup;
|
||||||
|
import funkin.data.dialogue.ConversationRegistry;
|
||||||
|
import funkin.data.event.SongEventRegistry;
|
||||||
|
import funkin.data.notestyle.NoteStyleData;
|
||||||
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
|
import funkin.data.notestyle.NoteStyleRegistry;
|
||||||
|
import funkin.data.song.SongData.SongCharacterData;
|
||||||
|
import funkin.data.song.SongData.SongEventData;
|
||||||
|
import funkin.data.song.SongData.SongNoteData;
|
||||||
|
import funkin.data.song.SongRegistry;
|
||||||
|
import funkin.data.stage.StageRegistry;
|
||||||
import funkin.Highscore.Tallies;
|
import funkin.Highscore.Tallies;
|
||||||
import funkin.input.PreciseInputManager;
|
import funkin.input.PreciseInputManager;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
import funkin.ui.mainmenu.MainMenuState;
|
|
||||||
import funkin.modding.events.ScriptEventDispatcher;
|
import funkin.modding.events.ScriptEventDispatcher;
|
||||||
import funkin.play.character.BaseCharacter;
|
import funkin.play.character.BaseCharacter;
|
||||||
import funkin.play.character.CharacterData.CharacterDataParser;
|
import funkin.play.character.CharacterData.CharacterDataParser;
|
||||||
|
import funkin.play.components.ComboMilestone;
|
||||||
|
import funkin.play.components.HealthIcon;
|
||||||
|
import funkin.play.components.PopUpStuff;
|
||||||
|
import funkin.play.cutscene.dialogue.Conversation;
|
||||||
import funkin.play.cutscene.dialogue.Conversation;
|
import funkin.play.cutscene.dialogue.Conversation;
|
||||||
import funkin.data.dialogue.ConversationRegistry;
|
|
||||||
import funkin.play.cutscene.VanillaCutscenes;
|
import funkin.play.cutscene.VanillaCutscenes;
|
||||||
import funkin.play.cutscene.VideoCutscene;
|
import funkin.play.cutscene.VideoCutscene;
|
||||||
import funkin.data.event.SongEventRegistry;
|
|
||||||
import funkin.play.notes.NoteSprite;
|
|
||||||
import funkin.play.notes.NoteDirection;
|
import funkin.play.notes.NoteDirection;
|
||||||
|
import funkin.play.notes.NoteSplash;
|
||||||
|
import funkin.play.notes.NoteSprite;
|
||||||
|
import funkin.play.notes.NoteSprite;
|
||||||
|
import funkin.play.notes.notestyle.NoteStyle;
|
||||||
|
import funkin.play.notes.notestyle.NoteStyle;
|
||||||
import funkin.play.notes.Strumline;
|
import funkin.play.notes.Strumline;
|
||||||
import funkin.play.notes.SustainTrail;
|
import funkin.play.notes.SustainTrail;
|
||||||
import funkin.play.scoring.Scoring;
|
import funkin.play.scoring.Scoring;
|
||||||
import funkin.play.song.Song;
|
import funkin.play.song.Song;
|
||||||
import funkin.data.song.SongRegistry;
|
|
||||||
import funkin.data.stage.StageRegistry;
|
|
||||||
import funkin.data.song.SongData.SongEventData;
|
|
||||||
import funkin.data.song.SongData.SongNoteData;
|
|
||||||
import funkin.data.song.SongData.SongCharacterData;
|
|
||||||
import funkin.play.stage.Stage;
|
import funkin.play.stage.Stage;
|
||||||
import funkin.ui.transition.LoadingState;
|
import funkin.save.Save;
|
||||||
import funkin.play.components.PopUpStuff;
|
import funkin.ui.debug.charting.ChartEditorState;
|
||||||
import funkin.ui.options.PreferencesMenu;
|
|
||||||
import funkin.ui.debug.stage.StageOffsetSubState;
|
import funkin.ui.debug.stage.StageOffsetSubState;
|
||||||
|
import funkin.ui.mainmenu.MainMenuState;
|
||||||
|
import funkin.ui.MusicBeatSubState;
|
||||||
|
import funkin.ui.options.PreferencesMenu;
|
||||||
import funkin.ui.story.StoryMenuState;
|
import funkin.ui.story.StoryMenuState;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
|
import funkin.ui.transition.LoadingState;
|
||||||
import funkin.util.SerializerUtil;
|
import funkin.util.SerializerUtil;
|
||||||
import funkin.util.SortUtil;
|
import haxe.Int64;
|
||||||
import lime.ui.Haptic;
|
import lime.ui.Haptic;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
|
import openfl.geom.Rectangle;
|
||||||
|
import openfl.Lib;
|
||||||
#if discord_rpc
|
#if discord_rpc
|
||||||
import Discord.DiscordClient;
|
import Discord.DiscordClient;
|
||||||
#end
|
#end
|
||||||
|
@ -445,6 +456,17 @@ class PlayState extends MusicBeatSubState
|
||||||
return this.subState != null;
|
return this.subState != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isExitingViaPauseMenu(get, never):Bool;
|
||||||
|
|
||||||
|
function get_isExitingViaPauseMenu():Bool
|
||||||
|
{
|
||||||
|
if (this.subState == null) return false;
|
||||||
|
if (!Std.isOfType(this.subState, PauseSubState)) return false;
|
||||||
|
|
||||||
|
var pauseSubState:PauseSubState = cast this.subState;
|
||||||
|
return pauseSubState.exitingToMenu;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data for the current difficulty for the current song.
|
* Data for the current difficulty for the current song.
|
||||||
* Includes chart data, scroll speed, and other information.
|
* Includes chart data, scroll speed, and other information.
|
||||||
|
@ -885,7 +907,7 @@ class PlayState extends MusicBeatSubState
|
||||||
// TODO: Add a song event for Handle GF dance speed.
|
// TODO: Add a song event for Handle GF dance speed.
|
||||||
|
|
||||||
// Handle player death.
|
// Handle player death.
|
||||||
if (!isInCutscene && !disableKeys && !_exiting)
|
if (!isInCutscene && !disableKeys)
|
||||||
{
|
{
|
||||||
// RESET = Quick Game Over Screen
|
// RESET = Quick Game Over Screen
|
||||||
if (controls.RESET)
|
if (controls.RESET)
|
||||||
|
@ -1282,7 +1304,7 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function initCameras():Void
|
function initCameras():Void
|
||||||
{
|
{
|
||||||
camGame = new SwagCamera();
|
camGame = new FunkinCamera();
|
||||||
camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage.
|
camGame.bgColor = BACKGROUND_COLOR; // Show a pink background behind the stage.
|
||||||
camHUD = new FlxCamera();
|
camHUD = new FlxCamera();
|
||||||
camHUD.bgColor.alpha = 0; // Show the game scene behind the camera.
|
camHUD.bgColor.alpha = 0; // Show the game scene behind the camera.
|
||||||
|
@ -1739,7 +1761,7 @@ class PlayState extends MusicBeatSubState
|
||||||
*/
|
*/
|
||||||
function resyncVocals():Void
|
function resyncVocals():Void
|
||||||
{
|
{
|
||||||
if (_exiting || vocals == null) return;
|
if (vocals == null) return;
|
||||||
|
|
||||||
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
|
// Skip this if the music is paused (GameOver, Pause menu, start-of-song offset, etc.)
|
||||||
if (!FlxG.sound.music.playing) return;
|
if (!FlxG.sound.music.playing) return;
|
||||||
|
@ -1965,7 +1987,7 @@ class PlayState extends MusicBeatSubState
|
||||||
|
|
||||||
// Mute vocals and play miss animation, but don't penalize.
|
// Mute vocals and play miss animation, but don't penalize.
|
||||||
vocals.playerVolume = 0;
|
vocals.playerVolume = 0;
|
||||||
currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
|
if (currentStage != null && currentStage.getBoyfriend() != null) currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2519,8 +2541,8 @@ class PlayState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
FlxG.sound.playMusic(Paths.music('freakyMenu/freakyMenu'));
|
||||||
|
|
||||||
transIn = FlxTransitionableState.defaultTransIn;
|
// transIn = FlxTransitionableState.defaultTransIn;
|
||||||
transOut = FlxTransitionableState.defaultTransOut;
|
// transOut = FlxTransitionableState.defaultTransOut;
|
||||||
|
|
||||||
// TODO: Rework week unlock logic.
|
// TODO: Rework week unlock logic.
|
||||||
// StoryMenuState.weekUnlocked[Std.int(Math.min(storyWeek + 1, StoryMenuState.weekUnlocked.length - 1))] = true;
|
// StoryMenuState.weekUnlocked[Std.int(Math.min(storyWeek + 1, StoryMenuState.weekUnlocked.length - 1))] = true;
|
||||||
|
|
|
@ -352,11 +352,11 @@ class ResultState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
if (params.storyMode)
|
if (params.storyMode)
|
||||||
{
|
{
|
||||||
FlxG.switchState(() -> new StoryMenuState());
|
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker)));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FlxG.switchState(() -> new FreeplayState());
|
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new FreeplayState(null, sticker)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,17 @@
|
||||||
package funkin.play.stage;
|
package funkin.play.stage;
|
||||||
|
|
||||||
|
import openfl.display.BlendMode;
|
||||||
|
import funkin.graphics.framebuffer.FrameBufferManager;
|
||||||
|
import flixel.util.FlxColor;
|
||||||
|
import funkin.graphics.framebuffer.SpriteCopy;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
import flixel.group.FlxSpriteGroup;
|
import flixel.group.FlxSpriteGroup;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import flixel.system.FlxAssets.FlxShader;
|
import flixel.system.FlxAssets.FlxShader;
|
||||||
import flixel.util.FlxSort;
|
import flixel.util.FlxSort;
|
||||||
|
import openfl.display.BitmapData;
|
||||||
import flixel.util.FlxColor;
|
import flixel.util.FlxColor;
|
||||||
import funkin.modding.IScriptedClass;
|
import funkin.modding.IScriptedClass;
|
||||||
import funkin.modding.events.ScriptEvent;
|
import funkin.modding.events.ScriptEvent;
|
||||||
|
@ -46,6 +53,13 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
return _data?.cameraZoom ?? 1.0;
|
return _data?.cameraZoom ?? 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var frameBufferMan:FrameBufferManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The texture that has the mask information. Used for shader effects.
|
||||||
|
*/
|
||||||
|
public var maskTexture:BitmapData;
|
||||||
|
|
||||||
var namedProps:Map<String, StageProp> = new Map<String, StageProp>();
|
var namedProps:Map<String, StageProp> = new Map<String, StageProp>();
|
||||||
var characters:Map<String, BaseCharacter> = new Map<String, BaseCharacter>();
|
var characters:Map<String, BaseCharacter> = new Map<String, BaseCharacter>();
|
||||||
var boppers:Array<Bopper> = new Array<Bopper>();
|
var boppers:Array<Bopper> = new Array<Bopper>();
|
||||||
|
@ -75,6 +89,10 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
*/
|
*/
|
||||||
public function onCreate(event:ScriptEvent):Void
|
public function onCreate(event:ScriptEvent):Void
|
||||||
{
|
{
|
||||||
|
if (frameBufferMan != null) frameBufferMan.dispose();
|
||||||
|
frameBufferMan = new FrameBufferManager(FlxG.camera);
|
||||||
|
setupFrameBuffers();
|
||||||
|
|
||||||
buildStage();
|
buildStage();
|
||||||
this.refresh();
|
this.refresh();
|
||||||
|
|
||||||
|
@ -701,6 +719,11 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
{
|
{
|
||||||
debugIconGroup = null;
|
debugIconGroup = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (frameBufferMan != null)
|
||||||
|
{
|
||||||
|
frameBufferMan.dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -724,13 +747,7 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onUpdate(event:UpdateScriptEvent)
|
public function onUpdate(event:UpdateScriptEvent) {}
|
||||||
{
|
|
||||||
if (FlxG.keys.justPressed.F3)
|
|
||||||
{
|
|
||||||
debugIconGroup.visible = !debugIconGroup.visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override function kill()
|
public override function kill()
|
||||||
{
|
{
|
||||||
|
@ -753,6 +770,53 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
||||||
return Sprite;
|
return Sprite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override function draw():Void
|
||||||
|
{
|
||||||
|
if (frameBufferMan != null)
|
||||||
|
{
|
||||||
|
frameBufferMan.lock();
|
||||||
|
}
|
||||||
|
super.draw();
|
||||||
|
if (frameBufferMan != null)
|
||||||
|
{
|
||||||
|
frameBufferMan.unlock();
|
||||||
|
}
|
||||||
|
frameBuffersUpdated();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the frame buffer manager is ready.
|
||||||
|
* Create frame buffers inside this method.
|
||||||
|
*/
|
||||||
|
function setupFrameBuffers():Void {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when all the frame buffers are updated. If you need any
|
||||||
|
* frame buffers before `grabScreen()`, make sure you
|
||||||
|
* grab the screen inside this method since it immediately uses the
|
||||||
|
* frame buffers.
|
||||||
|
*/
|
||||||
|
function frameBuffersUpdated():Void {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grabs the current screen and returns it as a bitmap data. You can sefely modify it.
|
||||||
|
* @param applyFilters if this is `true`, the filters set to the camera will be applied to the resulting bitmap
|
||||||
|
* @return the grabbed screen
|
||||||
|
*/
|
||||||
|
function grabScreen(applyFilters:Bool):BitmapData
|
||||||
|
{
|
||||||
|
if (Std.isOfType(FlxG.camera, FunkinCamera))
|
||||||
|
{
|
||||||
|
final cam:FunkinCamera = cast FlxG.camera;
|
||||||
|
return cam.grabScreen(applyFilters);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FlxG.log.error('cannot grab the screen: the main camera is not grabbable');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static function _fetchData(id:String):Null<StageData>
|
static function _fetchData(id:String):Null<StageData>
|
||||||
{
|
{
|
||||||
return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id));
|
return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id));
|
||||||
|
|
|
@ -16,7 +16,7 @@ import funkin.input.Controls;
|
||||||
/**
|
/**
|
||||||
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
|
* MusicBeatSubState reincorporates the functionality of MusicBeatState into an FlxSubState.
|
||||||
*/
|
*/
|
||||||
class MusicBeatSubState extends FlxTransitionableSubState implements IEventHandler
|
class MusicBeatSubState extends FlxSubState implements IEventHandler
|
||||||
{
|
{
|
||||||
public var leftWatermarkText:FlxText = null;
|
public var leftWatermarkText:FlxText = null;
|
||||||
public var rightWatermarkText:FlxText = null;
|
public var rightWatermarkText:FlxText = null;
|
||||||
|
|
|
@ -1,103 +0,0 @@
|
||||||
package funkin.ui;
|
|
||||||
|
|
||||||
import flixel.FlxCamera;
|
|
||||||
import flixel.FlxSprite;
|
|
||||||
import flixel.math.FlxPoint;
|
|
||||||
import funkin.util.MathUtil;
|
|
||||||
|
|
||||||
class SwagCamera extends FlxCamera
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* properly follow framerate
|
|
||||||
* most of this just copied from FlxCamera,
|
|
||||||
* only lines 96 and 97 are changed
|
|
||||||
*/
|
|
||||||
override public function updateFollow():Void
|
|
||||||
{
|
|
||||||
// Either follow the object closely,
|
|
||||||
// or double check our deadzone and update accordingly.
|
|
||||||
if (deadzone == null)
|
|
||||||
{
|
|
||||||
target.getMidpoint(_point);
|
|
||||||
_point.addPoint(targetOffset);
|
|
||||||
focusOn(_point);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var edge:Float;
|
|
||||||
var targetX:Float = target.x + targetOffset.x;
|
|
||||||
var targetY:Float = target.y + targetOffset.y;
|
|
||||||
|
|
||||||
if (style == SCREEN_BY_SCREEN)
|
|
||||||
{
|
|
||||||
if (targetX >= (scroll.x + width))
|
|
||||||
{
|
|
||||||
_scrollTarget.x += width;
|
|
||||||
}
|
|
||||||
else if (targetX < scroll.x)
|
|
||||||
{
|
|
||||||
_scrollTarget.x -= width;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (targetY >= (scroll.y + height))
|
|
||||||
{
|
|
||||||
_scrollTarget.y += height;
|
|
||||||
}
|
|
||||||
else if (targetY < scroll.y)
|
|
||||||
{
|
|
||||||
_scrollTarget.y -= height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
edge = targetX - deadzone.x;
|
|
||||||
if (_scrollTarget.x > edge)
|
|
||||||
{
|
|
||||||
_scrollTarget.x = edge;
|
|
||||||
}
|
|
||||||
edge = targetX + target.width - deadzone.x - deadzone.width;
|
|
||||||
if (_scrollTarget.x < edge)
|
|
||||||
{
|
|
||||||
_scrollTarget.x = edge;
|
|
||||||
}
|
|
||||||
|
|
||||||
edge = targetY - deadzone.y;
|
|
||||||
if (_scrollTarget.y > edge)
|
|
||||||
{
|
|
||||||
_scrollTarget.y = edge;
|
|
||||||
}
|
|
||||||
edge = targetY + target.height - deadzone.y - deadzone.height;
|
|
||||||
if (_scrollTarget.y < edge)
|
|
||||||
{
|
|
||||||
_scrollTarget.y = edge;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((target is FlxSprite))
|
|
||||||
{
|
|
||||||
if (_lastTargetPosition == null)
|
|
||||||
{
|
|
||||||
_lastTargetPosition = FlxPoint.get(target.x, target.y); // Creates this point.
|
|
||||||
}
|
|
||||||
_scrollTarget.x += (target.x - _lastTargetPosition.x) * followLead.x;
|
|
||||||
_scrollTarget.y += (target.y - _lastTargetPosition.y) * followLead.y;
|
|
||||||
|
|
||||||
_lastTargetPosition.x = target.x;
|
|
||||||
_lastTargetPosition.y = target.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (followLerp >= 60 / FlxG.updateFramerate)
|
|
||||||
{
|
|
||||||
scroll.copyFrom(_scrollTarget); // no easing
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// THIS THE PART THAT ACTUALLY MATTERS LOL
|
|
||||||
scroll.x = MathUtil.coolLerp(scroll.x, _scrollTarget.x, followLerp);
|
|
||||||
scroll.y = MathUtil.coolLerp(scroll.y, _scrollTarget.y, followLerp);
|
|
||||||
// scroll.x += (_scrollTarget.x - scroll.x) * MathUtil.cameraLerp(followLerp);
|
|
||||||
// scroll.y += (_scrollTarget.y - scroll.y) * MathUtil.cameraLerp(followLerp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -8,6 +8,7 @@ import flixel.FlxSprite;
|
||||||
import flixel.FlxSubState;
|
import flixel.FlxSubState;
|
||||||
import flixel.graphics.FlxGraphic;
|
import flixel.graphics.FlxGraphic;
|
||||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
import flixel.group.FlxSpriteGroup;
|
import flixel.group.FlxSpriteGroup;
|
||||||
import flixel.input.keyboard.FlxKey;
|
import flixel.input.keyboard.FlxKey;
|
||||||
import flixel.input.mouse.FlxMouseEvent;
|
import flixel.input.mouse.FlxMouseEvent;
|
||||||
|
@ -2094,7 +2095,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
|
|
||||||
loadPreferences();
|
loadPreferences();
|
||||||
|
|
||||||
uiCamera = new FlxCamera();
|
uiCamera = new FunkinCamera();
|
||||||
FlxG.cameras.reset(uiCamera);
|
FlxG.cameras.reset(uiCamera);
|
||||||
|
|
||||||
buildDefaultSongData();
|
buildDefaultSongData();
|
||||||
|
@ -5380,7 +5381,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
||||||
// Kill and replace the UI camera so it doesn't get destroyed during the state transition.
|
// Kill and replace the UI camera so it doesn't get destroyed during the state transition.
|
||||||
uiCamera.kill();
|
uiCamera.kill();
|
||||||
FlxG.cameras.remove(uiCamera, false);
|
FlxG.cameras.remove(uiCamera, false);
|
||||||
FlxG.cameras.reset(new FlxCamera());
|
FlxG.cameras.reset(new FunkinCamera());
|
||||||
|
|
||||||
this.persistentUpdate = false;
|
this.persistentUpdate = false;
|
||||||
this.persistentDraw = false;
|
this.persistentDraw = false;
|
||||||
|
|
|
@ -14,6 +14,7 @@ import flixel.group.FlxSpriteGroup;
|
||||||
import flixel.input.touch.FlxTouch;
|
import flixel.input.touch.FlxTouch;
|
||||||
import flixel.math.FlxAngle;
|
import flixel.math.FlxAngle;
|
||||||
import flixel.math.FlxMath;
|
import flixel.math.FlxMath;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
import flixel.math.FlxPoint;
|
import flixel.math.FlxPoint;
|
||||||
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
import flixel.system.debug.watch.Tracker.TrackerProfile;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
|
@ -573,7 +574,7 @@ class FreeplayState extends MusicBeatSubState
|
||||||
|
|
||||||
var swag:Alphabet = new Alphabet(1, 0, "swag");
|
var swag:Alphabet = new Alphabet(1, 0, "swag");
|
||||||
|
|
||||||
var funnyCam = new FlxCamera(0, 0, FlxG.width, FlxG.height);
|
var funnyCam = new FunkinCamera(0, 0, FlxG.width, FlxG.height);
|
||||||
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
funnyCam.bgColor = FlxColor.TRANSPARENT;
|
||||||
FlxG.cameras.add(funnyCam);
|
FlxG.cameras.add(funnyCam);
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ import flixel.group.FlxGroup.FlxTypedGroup;
|
||||||
import flixel.input.touch.FlxTouch;
|
import flixel.input.touch.FlxTouch;
|
||||||
import flixel.text.FlxText;
|
import flixel.text.FlxText;
|
||||||
import flixel.tweens.FlxEase;
|
import flixel.tweens.FlxEase;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
import flixel.tweens.FlxTween;
|
import flixel.tweens.FlxTween;
|
||||||
import funkin.ui.MusicBeatState;
|
import funkin.ui.MusicBeatState;
|
||||||
import flixel.util.FlxTimer;
|
import flixel.util.FlxTimer;
|
||||||
|
@ -152,7 +153,7 @@ class MainMenuState extends MusicBeatState
|
||||||
|
|
||||||
function resetCamStuff()
|
function resetCamStuff()
|
||||||
{
|
{
|
||||||
FlxG.cameras.reset(new SwagCamera());
|
FlxG.cameras.reset(new FunkinCamera());
|
||||||
FlxG.camera.follow(camFollow, null, 0.06);
|
FlxG.camera.follow(camFollow, null, 0.06);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import funkin.util.InputUtil;
|
||||||
import flixel.FlxCamera;
|
import flixel.FlxCamera;
|
||||||
import flixel.FlxObject;
|
import flixel.FlxObject;
|
||||||
import flixel.FlxSprite;
|
import flixel.FlxSprite;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
import flixel.group.FlxGroup;
|
import flixel.group.FlxGroup;
|
||||||
import flixel.input.actions.FlxActionInput;
|
import flixel.input.actions.FlxActionInput;
|
||||||
import flixel.input.gamepad.FlxGamepadInputID;
|
import flixel.input.gamepad.FlxGamepadInputID;
|
||||||
|
@ -47,7 +48,7 @@ class ControlsMenu extends funkin.ui.options.OptionsState.Page
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
menuCamera = new FlxCamera();
|
menuCamera = new FunkinCamera();
|
||||||
FlxG.cameras.add(menuCamera, false);
|
FlxG.cameras.add(menuCamera, false);
|
||||||
menuCamera.bgColor = 0x0;
|
menuCamera.bgColor = 0x0;
|
||||||
camera = menuCamera;
|
camera = menuCamera;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import flixel.FlxSprite;
|
||||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||||
import funkin.ui.AtlasText.AtlasFont;
|
import funkin.ui.AtlasText.AtlasFont;
|
||||||
import funkin.ui.options.OptionsState.Page;
|
import funkin.ui.options.OptionsState.Page;
|
||||||
|
import funkin.graphics.FunkinCamera;
|
||||||
import funkin.ui.TextMenuList.TextMenuItem;
|
import funkin.ui.TextMenuList.TextMenuItem;
|
||||||
|
|
||||||
class PreferencesMenu extends Page
|
class PreferencesMenu extends Page
|
||||||
|
@ -20,7 +21,7 @@ class PreferencesMenu extends Page
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
|
|
||||||
menuCamera = new SwagCamera();
|
menuCamera = new FunkinCamera();
|
||||||
FlxG.cameras.add(menuCamera, false);
|
FlxG.cameras.add(menuCamera, false);
|
||||||
menuCamera.bgColor = 0x0;
|
menuCamera.bgColor = 0x0;
|
||||||
camera = menuCamera;
|
camera = menuCamera;
|
||||||
|
|
|
@ -116,10 +116,19 @@ class StickerSubState extends MusicBeatSubState
|
||||||
{
|
{
|
||||||
grpStickers.cameras = FlxG.cameras.list;
|
grpStickers.cameras = FlxG.cameras.list;
|
||||||
|
|
||||||
if (dipshit != null)
|
/*
|
||||||
|
if (dipshit != null)
|
||||||
|
{
|
||||||
|
FlxG.removeChild(dipshit);
|
||||||
|
dipshit = null;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (grpStickers.members == null || grpStickers.members.length == 0)
|
||||||
{
|
{
|
||||||
FlxG.removeChild(dipshit);
|
switchingState = false;
|
||||||
dipshit = null;
|
close();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ind => sticker in grpStickers.members)
|
for (ind => sticker in grpStickers.members)
|
||||||
|
@ -232,18 +241,23 @@ class StickerSubState extends MusicBeatSubState
|
||||||
if (ind == grpStickers.members.length - 1)
|
if (ind == grpStickers.members.length - 1)
|
||||||
{
|
{
|
||||||
switchingState = true;
|
switchingState = true;
|
||||||
|
|
||||||
FlxTransitionableState.skipNextTransIn = true;
|
FlxTransitionableState.skipNextTransIn = true;
|
||||||
FlxTransitionableState.skipNextTransOut = true;
|
FlxTransitionableState.skipNextTransOut = true;
|
||||||
|
|
||||||
dipshit = new Sprite();
|
// I think this grabs the screen and puts it under the stickers?
|
||||||
var scrn:BitmapData = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000);
|
// Leaving this commented out rather than stripping it out because it's cool...
|
||||||
var mat:Matrix = new Matrix();
|
/*
|
||||||
scrn.draw(grpStickers.cameras[0].canvas, mat);
|
dipshit = new Sprite();
|
||||||
|
var scrn:BitmapData = new BitmapData(FlxG.width, FlxG.height, true, 0x00000000);
|
||||||
|
var mat:Matrix = new Matrix();
|
||||||
|
scrn.draw(grpStickers.cameras[0].canvas, mat);
|
||||||
|
|
||||||
var bitmap:Bitmap = new Bitmap(scrn);
|
var bitmap:Bitmap = new Bitmap(scrn);
|
||||||
|
|
||||||
dipshit.addChild(bitmap);
|
dipshit.addChild(bitmap);
|
||||||
FlxG.addChildBelowMouse(dipshit);
|
// FlxG.addChildBelowMouse(dipshit);
|
||||||
|
*/
|
||||||
|
|
||||||
FlxG.switchState(() -> targetState(this));
|
FlxG.switchState(() -> targetState(this));
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,17 @@ class WatchPlugin extends FlxBasic
|
||||||
{
|
{
|
||||||
super.update(elapsed);
|
super.update(elapsed);
|
||||||
|
|
||||||
|
var stateClassName = Type.getClassName(Type.getClass(FlxG.state));
|
||||||
|
FlxG.watch.addQuick("currentState", stateClassName);
|
||||||
|
var subStateClassNames = [];
|
||||||
|
var subState = FlxG.state.subState;
|
||||||
|
while (subState != null)
|
||||||
|
{
|
||||||
|
subStateClassNames.push(Type.getClassName(Type.getClass(subState)));
|
||||||
|
subState = subState.subState;
|
||||||
|
}
|
||||||
|
FlxG.watch.addQuick("currentSubStates", subStateClassNames.join(", "));
|
||||||
|
|
||||||
FlxG.watch.addQuick("songPosition", Conductor.instance.songPosition);
|
FlxG.watch.addQuick("songPosition", Conductor.instance.songPosition);
|
||||||
FlxG.watch.addQuick("songPositionNoOffset", Conductor.instance.songPosition + Conductor.instance.instrumentalOffset);
|
FlxG.watch.addQuick("songPositionNoOffset", Conductor.instance.songPosition + Conductor.instance.instrumentalOffset);
|
||||||
FlxG.watch.addQuick("musicTime", FlxG.sound?.music?.time ?? 0.0);
|
FlxG.watch.addQuick("musicTime", FlxG.sound?.music?.time ?? 0.0);
|
||||||
|
|
Loading…
Reference in New Issue