1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-03-20 17:09:21 +00:00

Implemented a screenshot button. FancyPreview is broken.

This commit is contained in:
EliteMasterEric 2024-02-15 21:34:24 -05:00
parent 15accdf10f
commit e24c78ae16
5 changed files with 312 additions and 7 deletions

View file

@ -50,11 +50,13 @@ class InitState extends FlxState
*/
public override function create():Void
{
// Setup a bunch of important Flixel stuff.
setupShit();
// loadSaveData(); // Moved to Main.hx
// Load player options from save data.
// Flixel has already loaded the save data, so we can just use it.
Preferences.init();
// Load controls from save data.
PlayerSettings.init();
@ -198,6 +200,10 @@ class InitState extends FlxState
//
// FLIXEL PLUGINS
//
// Plugins provide a useful interface for globally active Flixel objects,
// that receive update events regardless of the current state.
// TODO: Move Module behavior to a Flixel plugin.
funkin.util.plugins.ScreenshotPlugin.initialize();
funkin.util.plugins.EvacuateDebugPlugin.initialize();
funkin.util.plugins.ReloadAssetsDebugPlugin.initialize();
funkin.util.plugins.WatchPlugin.initialize();

View file

@ -20,7 +20,10 @@ class Preferences
static function set_naughtyness(value:Bool):Bool
{
return Save.get().options.naughtyness = value;
var save = Save.get();
save.options.naughtyness = value;
save.flush();
return value;
}
/**
@ -36,7 +39,10 @@ class Preferences
static function set_downscroll(value:Bool):Bool
{
return Save.get().options.downscroll = value;
var save = Save.get();
save.options.downscroll = value;
save.flush();
return value;
}
/**
@ -52,7 +58,10 @@ class Preferences
static function set_flashingLights(value:Bool):Bool
{
return Save.get().options.flashingLights = value;
var save = Save.get();
save.options.flashingLights = value;
save.flush();
return value;
}
/**
@ -68,7 +77,10 @@ class Preferences
static function set_zoomCamera(value:Bool):Bool
{
return Save.get().options.zoomCamera = value;
var save = Save.get();
save.options.zoomCamera = value;
save.flush();
return value;
}
/**
@ -89,7 +101,10 @@ class Preferences
toggleDebugDisplay(value);
}
return Save.get().options.debugDisplay = value;
var save = Save.get();
save.options.debugDisplay = value;
save.flush();
return value;
}
/**
@ -107,7 +122,10 @@ class Preferences
{
if (value != Save.get().options.autoPause) FlxG.autoPause = value;
return Save.get().options.autoPause = value;
var save = Save.get();
save.options.autoPause = value;
save.flush();
return value;
}
public static function init():Void

View file

@ -113,6 +113,9 @@ abstract Save(RawSaveData)
};
}
/**
* NOTE: Modifications will not be saved without calling `Save.flush()`!
*/
public var options(get, never):SaveDataOptions;
function get_options():SaveDataOptions
@ -120,6 +123,9 @@ abstract Save(RawSaveData)
return this.options;
}
/**
* NOTE: Modifications will not be saved without calling `Save.flush()`!
*/
public var modOptions(get, never):Map<String, Dynamic>;
function get_modOptions():Map<String, Dynamic>

View file

@ -20,6 +20,7 @@ class FileUtil
{
public static final FILE_FILTER_FNFC:FileFilter = new FileFilter("Friday Night Funkin' Chart (.fnfc)", "*.fnfc");
public static final FILE_FILTER_ZIP:FileFilter = new FileFilter("ZIP Archive (.zip)", "*.zip");
public static final FILE_FILTER_PNG:FileFilter = new FileFilter("PNG Image (.png)", "*.png");
public static final FILE_EXTENSION_INFO_FNFC:FileDialogExtensionInfo =
{
@ -31,6 +32,11 @@ class FileUtil
extension: 'zip',
label: 'ZIP Archive',
};
public static final FILE_EXTENSION_INFO_PNG:FileDialogExtensionInfo =
{
extension: 'png',
label: 'PNG Image',
};
/**
* Browses for a single file, then calls `onSelect(fileInfo)` when a file is selected.

View file

@ -0,0 +1,269 @@
package funkin.util.plugins;
import flixel.FlxBasic;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.FlxState;
import flixel.graphics.FlxGraphic;
import flixel.input.keyboard.FlxKey;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxColor;
import flixel.util.FlxSignal;
import flixel.util.FlxTimer;
import funkin.graphics.FunkinSprite;
import funkin.input.Cursor;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.display.PNGEncoderOptions;
import openfl.geom.Matrix;
import openfl.geom.Rectangle;
import openfl.utils.ByteArray;
typedef ScreenshotPluginParams =
{
hotkeys:Array<FlxKey>,
?region:Rectangle,
shouldHideMouse:Bool,
flashColor:Null<FlxColor>,
fancyPreview:Bool,
};
/**
* What if `flixel.addons.plugin.screengrab.FlxScreenGrab` but it's better?
* TODO: Contribute this upstream.
*/
class ScreenshotPlugin extends FlxBasic
{
public static final SCREENSHOT_FOLDER = 'screenshots';
var _hotkeys:Array<FlxKey>;
var _region:Null<Rectangle>;
var _shouldHideMouse:Bool;
var _flashColor:Null<FlxColor>;
var _fancyPreview:Bool;
/**
* A signal fired before the screenshot is taken.
*/
public var onPreScreenshot(default, null):FlxTypedSignal<Void->Void>;
/**
* A signal fired after the screenshot is taken.
* @param bitmap The bitmap that was captured.
*/
public var onPostScreenshot(default, null):FlxTypedSignal<Bitmap->Void>;
public function new(params:ScreenshotPluginParams)
{
super();
_hotkeys = params.hotkeys;
_region = params.region ?? null;
_shouldHideMouse = params.shouldHideMouse;
_flashColor = params.flashColor;
_fancyPreview = params.fancyPreview;
onPreScreenshot = new FlxTypedSignal<Void->Void>();
onPostScreenshot = new FlxTypedSignal<Bitmap->Void>();
}
public override function update(elapsed:Float):Void
{
super.update(elapsed);
if (FlxG.keys.anyJustReleased(_hotkeys))
{
capture();
}
}
/**
* Initialize the screenshot plugin.
*/
public static function initialize():Void
{
FlxG.plugins.addPlugin(new ScreenshotPlugin(
{
flashColor: Preferences.flashingLights ? FlxColor.WHITE : null, // Was originally a black flash.
// TODO: Add a way to configure screenshots from the options menu.
hotkeys: [FlxKey.PRINTSCREEN],
shouldHideMouse: false,
fancyPreview: true, // TODO: Fancy preview is broken on substates.
}));
}
public function updatePreferences():Void
{
_flashColor = Preferences.flashingLights ? FlxColor.WHITE : null;
}
/**
* Defines the region of the screen that should be captured.
* You don't need to call this method if you want to capture the entire screen, that's the default behavior.
*/
public function defineCaptureRegion(x:Int, y:Int, width:Int, height:Int):Void
{
_region = new Rectangle(x, y, width, height);
}
/**
* Capture the game screen as a bitmap.
*/
public function capture():Void
{
onPreScreenshot.dispatch();
var captureRegion = _region != null ? _region : new Rectangle(0, 0, FlxG.stage.stageWidth, FlxG.stage.stageHeight);
var wasMouseHidden = false;
if (_shouldHideMouse && FlxG.mouse.visible)
{
wasMouseHidden = true;
Cursor.hide();
}
// The actual work.
// var bitmap = new Bitmap(new BitmapData(Math.floor(captureRegion.width), Math.floor(captureRegion.height), true, 0x00000000)); // Create a transparent empty bitmap.
// var drawMatrix = new Matrix(1, 0, 0, 1, -captureRegion.x, -captureRegion.y); // Modifying this will scale or skew the bitmap.
// bitmap.bitmapData.draw(FlxG.stage, drawMatrix);
var bitmap = new Bitmap(BitmapData.fromImage(FlxG.stage.window.readPixels()));
if (wasMouseHidden)
{
Cursor.show();
}
// Save the bitmap to a file.
saveScreenshot(bitmap);
// Show some feedback.
showCaptureFeedback();
if (_fancyPreview)
{
showFancyPreview(bitmap);
}
onPostScreenshot.dispatch(bitmap);
}
final CAMERA_FLASH_DURATION = 0.25;
/**
* Visual (and audio?) feedback when a screenshot is taken.
*/
function showCaptureFeedback():Void
{
if (_flashColor != null)
{
for (camera in FlxG.cameras.list)
{
camera.flash(_flashColor, CAMERA_FLASH_DURATION);
}
}
}
static final PREVIEW_INITIAL_DELAY = 0.25; // How long before the preview starts fading in.
static final PREVIEW_FADE_IN_DURATION = 0.3; // How long the preview takes to fade in.
static final PREVIEW_FADE_OUT_DELAY = 0.25; // How long the preview stays on screen.
static final PREVIEW_FADE_OUT_DURATION = 0.3; // How long the preview takes to fade out.
function showFancyPreview(bitmap:Bitmap):Void
{
// TODO: This function looks really nice but breaks substates.
var targetCamera = new FlxCamera();
targetCamera.bgColor.alpha = 0; // Show the scene behind the camera.
FlxG.cameras.add(targetCamera);
var flxGraphic = FlxGraphic.fromBitmapData(bitmap.bitmapData, false, "screenshot", false);
var preview = new FunkinSprite(0, 0);
preview.frames = flxGraphic.imageFrame;
preview.setGraphicSize(bitmap.width / 4, bitmap.height / 4);
preview.x = FlxG.width - preview.width - 10;
preview.y = FlxG.height - preview.height - 10;
preview.alpha = 0.0;
preview.cameras = [targetCamera];
getCurrentState().add(preview);
// Wait to fade in.
new FlxTimer().start(PREVIEW_INITIAL_DELAY, function(_) {
// Fade in.
FlxTween.tween(preview, {alpha: 1.0}, PREVIEW_FADE_IN_DURATION,
{
ease: FlxEase.elasticOut,
onComplete: function(_) {
// Wait to fade out.
new FlxTimer().start(PREVIEW_FADE_OUT_DELAY, function(_) {
// Fade out.
FlxTween.tween(preview, {alpha: 0.0}, PREVIEW_FADE_OUT_DURATION,
{
ease: FlxEase.elasticIn,
onComplete: function(_) {
preview.kill();
}
});
});
}
});
});
}
static function getCurrentState():FlxState
{
var state = FlxG.state;
while (state.subState != null)
{
state = state.subState;
}
return state;
}
static function getScreenshotPath():String
{
return '$SCREENSHOT_FOLDER/screenshot-${DateUtil.generateTimestamp()}.png';
}
static function makeScreenshotPath():Void
{
FileUtil.createDirIfNotExists(SCREENSHOT_FOLDER);
}
/**
* Convert a Bitmap to a PNG ByteArray to save to a file.
*/
static function encodePNG(bitmap:Bitmap):ByteArray
{
return bitmap.bitmapData.encode(bitmap.bitmapData.rect, new PNGEncoderOptions());
}
/**
* Save the generated bitmap to a file.
* @param bitmap The bitmap to save.
*/
static function saveScreenshot(bitmap:Bitmap)
{
makeScreenshotPath();
var targetPath:String = getScreenshotPath();
var pngData = encodePNG(bitmap);
if (pngData == null)
{
trace('[WARN] Failed to encode PNG data.');
return;
}
else
{
trace('Saving screenshot to: ' + targetPath);
// TODO: Make this work on browser.
FileUtil.writeBytesToPath(targetPath, pngData);
}
}
}