diff --git a/hmm.json b/hmm.json index 42d17743f..a75dee432 100644 --- a/hmm.json +++ b/hmm.json @@ -146,7 +146,7 @@ "name": "polymod", "type": "git", "dir": null, - "ref": "be712450e5d3ba446008884921bb56873b299a64", + "ref": "5547763a22858a1f10939e082de421d587c862bf", "url": "https://github.com/larsiusprime/polymod" }, { diff --git a/source/Main.hx b/source/Main.hx index a40fda29d..758edcc65 100644 --- a/source/Main.hx +++ b/source/Main.hx @@ -100,8 +100,15 @@ class Main extends Sprite // George recommends binding the save before FlxGame is created. Save.load(); + var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen); - addChild(new FlxGame(gameWidth, gameHeight, initialState, framerate, framerate, skipSplash, startFullscreen)); + // FlxG.game._customSoundTray wants just the class, it calls new from + // create() in there, which gets called when it's added to stage + // which is why it needs to be added before addChild(game) here + @:privateAccess + game._customSoundTray = funkin.ui.options.FunkinSoundTray; + + addChild(game); #if hxcpp_debug_server trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.'); diff --git a/source/funkin/audio/FunkinSound.hx b/source/funkin/audio/FunkinSound.hx index 9efa6ed50..c64240909 100644 --- a/source/funkin/audio/FunkinSound.hx +++ b/source/funkin/audio/FunkinSound.hx @@ -1,11 +1,8 @@ package funkin.audio; -#if flash11 -import flash.media.Sound; -import flash.utils.ByteArray; -#end import flixel.sound.FlxSound; import flixel.group.FlxGroup.FlxTypedGroup; +import flixel.util.FlxSignal.FlxTypedSignal; import flixel.system.FlxAssets.FlxSoundAsset; import funkin.util.tools.ICloneable; import funkin.data.song.SongData.SongMusicData; @@ -27,6 +24,25 @@ class FunkinSound extends FlxSound implements ICloneable { static final MAX_VOLUME:Float = 1.0; + /** + * An FlxSignal which is dispatched when the volume changes. + */ + public static var onVolumeChanged(get, never):FlxTypedSignalVoid>; + + static var _onVolumeChanged:NullVoid>> = null; + + static function get_onVolumeChanged():FlxTypedSignalVoid> + { + if (_onVolumeChanged == null) + { + _onVolumeChanged = new FlxTypedSignalVoid>(); + FlxG.sound.volumeHandler = function(volume:Float) { + _onVolumeChanged.dispatch(volume); + } + } + return _onVolumeChanged; + } + /** * Using `FunkinSound.load` will override a dead instance from here rather than creating a new one, if possible! */ diff --git a/source/funkin/graphics/FunkinSprite.hx b/source/funkin/graphics/FunkinSprite.hx index 03382f757..7ead7f1fb 100644 --- a/source/funkin/graphics/FunkinSprite.hx +++ b/source/funkin/graphics/FunkinSprite.hx @@ -3,6 +3,9 @@ package funkin.graphics; import flixel.FlxSprite; import flixel.util.FlxColor; import flixel.graphics.FlxGraphic; +import openfl.display3D.textures.TextureBase; +import funkin.graphics.framebuffer.FixedBitmapData; +import openfl.display.BitmapData; /** * An FlxSprite with additional functionality. @@ -41,7 +44,7 @@ class FunkinSprite extends FlxSprite */ public static function create(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite { - var sprite = new FunkinSprite(x, y); + var sprite:FunkinSprite = new FunkinSprite(x, y); sprite.loadTexture(key); return sprite; } @@ -55,7 +58,7 @@ class FunkinSprite extends FlxSprite */ public static function createSparrow(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite { - var sprite = new FunkinSprite(x, y); + var sprite:FunkinSprite = new FunkinSprite(x, y); sprite.loadSparrow(key); return sprite; } @@ -69,7 +72,7 @@ class FunkinSprite extends FlxSprite */ public static function createPacker(x:Float = 0.0, y:Float = 0.0, key:String):FunkinSprite { - var sprite = new FunkinSprite(x, y); + var sprite:FunkinSprite = new FunkinSprite(x, y); sprite.loadPacker(key); return sprite; } @@ -89,6 +92,30 @@ class FunkinSprite extends FlxSprite return this; } + /** + * Apply an OpenFL `BitmapData` to this sprite. + * @param input The OpenFL `BitmapData` to apply + * @return This sprite, for chaining + */ + public function loadBitmapData(input:BitmapData):FunkinSprite + { + loadGraphic(input); + + return this; + } + + /** + * Apply an OpenFL `TextureBase` to this sprite. + * @param input The OpenFL `TextureBase` to apply + * @return This sprite, for chaining + */ + public function loadTextureBase(input:TextureBase):FunkinSprite + { + var inputBitmap:FixedBitmapData = FixedBitmapData.fromTexture(input); + + return loadBitmapData(inputBitmap); + } + /** * Load an animated texture (Sparrow atlas spritesheet) as the sprite's texture. * @param key The key of the texture to load. @@ -119,11 +146,20 @@ class FunkinSprite extends FlxSprite return this; } + /** + * Determine whether the texture with the given key is cached. + * @param key The key of the texture to check. + * @return Whether the texture is cached. + */ public static function isTextureCached(key:String):Bool { return FlxG.bitmap.get(key) != null; } + /** + * Ensure the texture with the given key is cached. + * @param key The key of the texture to cache. + */ public static function cacheTexture(key:String):Void { // We don't want to cache the same texture twice. @@ -139,7 +175,7 @@ class FunkinSprite extends FlxSprite } // Else, texture is currently uncached. - var graphic = flixel.graphics.FlxGraphic.fromAssetKey(key, false, null, true); + var graphic:FlxGraphic = FlxGraphic.fromAssetKey(key, false, null, true); if (graphic == null) { FlxG.log.warn('Failed to cache graphic: $key'); diff --git a/source/funkin/graphics/framebuffer/FixedBitmapData.hx b/source/funkin/graphics/framebuffer/FixedBitmapData.hx index 00b39ce1c..4ffcbb867 100644 --- a/source/funkin/graphics/framebuffer/FixedBitmapData.hx +++ b/source/funkin/graphics/framebuffer/FixedBitmapData.hx @@ -32,11 +32,11 @@ class FixedBitmapData extends BitmapData 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; + final bitmapData:FixedBitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0); + // bitmapData.readable = false; bitmapData.__texture = texture; bitmapData.__textureContext = texture.__textureContext; - bitmapData.image = null; + // bitmapData.image = null; return bitmapData; } } diff --git a/source/funkin/graphics/video/FlxVideo.hx b/source/funkin/graphics/video/FlxVideo.hx index 5e178efb3..a0fab9c09 100644 --- a/source/funkin/graphics/video/FlxVideo.hx +++ b/source/funkin/graphics/video/FlxVideo.hx @@ -1,45 +1,58 @@ package funkin.graphics.video; -import flixel.FlxBasic; -import flixel.FlxSprite; +import flixel.util.FlxColor; +import flixel.util.FlxSignal.FlxTypedSignal; +import funkin.audio.FunkinSound; +import openfl.display3D.textures.TextureBase; import openfl.events.NetStatusEvent; +import openfl.media.SoundTransform; import openfl.media.Video; import openfl.net.NetConnection; import openfl.net.NetStream; /** * Plays a video via a NetStream. Only works on HTML5. - * This does NOT replace hxCodec, nor does hxCodec replace this. hxCodec only works on desktop and does not work on HTML5! + * This does NOT replace hxCodec, nor does hxCodec replace this. + * hxCodec only works on desktop and does not work on HTML5! */ -class FlxVideo extends FlxBasic +class FlxVideo extends FunkinSprite { var video:Video; var netStream:NetStream; - - public var finishCallback:Void->Void; + var videoPath:String; /** - * Doesn't actually interact with Flixel shit, only just a pleasant to use class + * A callback to execute when the video finishes. */ + public var finishCallback:Void->Void; + public function new(videoPath:String) { super(); + this.videoPath = videoPath; + + makeGraphic(2, 2, FlxColor.TRANSPARENT); + video = new Video(); video.x = 0; video.y = 0; + video.alpha = 0; - FlxG.addChildBelowMouse(video); + FlxG.game.addChild(video); - var netConnection = new NetConnection(); + var netConnection:NetConnection = new NetConnection(); netConnection.connect(null); netStream = new NetStream(netConnection); - netStream.client = {onMetaData: client_onMetaData}; - netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus); + netStream.client = {onMetaData: onClientMetaData}; + netConnection.addEventListener(NetStatusEvent.NET_STATUS, onNetConnectionNetStatus); netStream.play(videoPath); } + /** + * Tell the FlxVideo to pause playback. + */ public function pauseVideo():Void { if (netStream != null) @@ -48,6 +61,9 @@ class FlxVideo extends FlxBasic } } + /** + * Tell the FlxVideo to resume if it is paused. + */ public function resumeVideo():Void { // Resume playing the video. @@ -57,6 +73,29 @@ class FlxVideo extends FlxBasic } } + var videoAvailable:Bool = false; + var frameTimer:Float; + + static final FRAME_RATE:Float = 60; + + public override function update(elapsed:Float):Void + { + super.update(elapsed); + + if (frameTimer >= (1 / FRAME_RATE)) + { + frameTimer = 0; + // TODO: We just draw the video buffer to the sprite 60 times a second. + // Can we copy the video buffer instead somehow? + pixels.draw(video); + } + + if (videoAvailable) frameTimer += elapsed; + } + + /** + * Tell the FlxVideo to seek to the beginning. + */ public function restartVideo():Void { // Seek to the beginning of the video. @@ -66,6 +105,9 @@ class FlxVideo extends FlxBasic } } + /** + * Tell the FlxVideo to end. + */ public function finishVideo():Void { netStream.dispose(); @@ -74,15 +116,48 @@ class FlxVideo extends FlxBasic if (finishCallback != null) finishCallback(); } - public function client_onMetaData(metaData:Dynamic) + public override function destroy():Void + { + if (netStream != null) + { + netStream.dispose(); + + if (FlxG.game.contains(video)) FlxG.game.removeChild(video); + } + + super.destroy(); + } + + /** + * Callback executed when the video stream loads. + * @param metaData The metadata of the video + */ + public function onClientMetaData(metaData:Dynamic):Void { video.attachNetStream(netStream); - video.width = FlxG.width; - video.height = FlxG.height; + onVideoReady(); } - function netConnection_onNetStatus(event:NetStatusEvent):Void + function onVideoReady():Void + { + video.width = FlxG.width; + video.height = FlxG.height; + + videoAvailable = true; + + FunkinSound.onVolumeChanged.add(onVolumeChanged); + onVolumeChanged(FlxG.sound.muted ? 0 : FlxG.sound.volume); + + makeGraphic(Std.int(video.width), Std.int(video.height), FlxColor.TRANSPARENT); + } + + function onVolumeChanged(volume:Float):Void + { + netStream.soundTransform = new SoundTransform(volume); + } + + function onNetConnectionNetStatus(event:NetStatusEvent):Void { if (event.info.code == 'NetStream.Play.Complete') finishVideo(); } diff --git a/source/funkin/play/character/BaseCharacter.hx b/source/funkin/play/character/BaseCharacter.hx index d39f19b76..2796f8123 100644 --- a/source/funkin/play/character/BaseCharacter.hx +++ b/source/funkin/play/character/BaseCharacter.hx @@ -193,6 +193,11 @@ class BaseCharacter extends Bopper return _data.death?.cameraOffsets ?? [0.0, 0.0]; } + public function getBaseScale():Float + { + return _data.scale; + } + public function getDeathCameraZoom():Float { return _data.death?.cameraZoom ?? 1.0; @@ -260,8 +265,8 @@ class BaseCharacter extends Bopper } /** - * Set the sprite scale to the appropriate value. - * @param scale + * Set the character's sprite scale to the appropriate value. + * @param scale The desired scale. */ public function setScale(scale:Null):Void { diff --git a/source/funkin/play/song/Song.hx b/source/funkin/play/song/Song.hx index 1b7740408..42266a6ae 100644 --- a/source/funkin/play/song/Song.hx +++ b/source/funkin/play/song/Song.hx @@ -367,11 +367,14 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry):Array + public function listDifficulties(?variationId:String, ?variationIds:Array, showHidden:Bool = false):Array { if (variationIds == null) variationIds = []; if (variationId != null) variationIds.push(variationId); @@ -387,6 +390,15 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry 0) + { + _timer -= (MS / 1000); + } + else if (y > -height) + { + lerpYPos = -height - 10; + + if (y <= -height) + { + visible = false; + active = false; + + #if FLX_SAVE + // Save sound preferences + if (FlxG.save.isBound) + { + FlxG.save.data.mute = FlxG.sound.muted; + FlxG.save.data.volume = FlxG.sound.volume; + FlxG.save.flush(); + } + #end + } + } + } + + /** + * Makes the little volume tray slide out. + * + * @param up Whether the volume is increasing. + */ + override public function show(up:Bool = false):Void + { + _timer = 1; + lerpYPos = 10; + visible = true; + active = true; + var globalVolume:Int = Math.round(FlxG.sound.volume * 10); + + if (FlxG.sound.muted) + { + globalVolume = 0; + } + + if (!silent) + { + var sound = up ? volumeUpSound : volumeDownSound; + + if (globalVolume == 10) sound = volumeMaxSound; + + if (sound != null) FlxG.sound.load(sound).play(); + } + + for (i in 0..._bars.length) + { + if (i < globalVolume) + { + _bars[i].visible = true; + } + else + { + _bars[i].visible = false; + } + } + } +} diff --git a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx index a43317cce..f69609531 100644 --- a/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx +++ b/source/funkin/util/plugins/ReloadAssetsDebugPlugin.hx @@ -22,7 +22,11 @@ class ReloadAssetsDebugPlugin extends FlxBasic { super.update(elapsed); + #if html5 + if (FlxG.keys.justPressed.FIVE && FlxG.keys.pressed.SHIFT) + #else if (FlxG.keys.justPressed.F5) + #end { funkin.modding.PolymodHandler.forceReloadAssets();