1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-11-26 06:09:02 +00:00

Merge remote-tracking branch 'origin/rewrite/master' into a-bot-bars

This commit is contained in:
EliteMasterEric 2024-03-21 21:54:22 -04:00
commit efa0e103b0
9 changed files with 330 additions and 30 deletions

2
assets

@ -1 +1 @@
Subproject commit 35f8e6bdec79cff907a5f7578cc1cb0d2e7a495a Subproject commit 9ca1b77f8c69a391c2454a47b61c3865464d92ac

View file

@ -153,7 +153,7 @@
"name": "polymod", "name": "polymod",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "be712450e5d3ba446008884921bb56873b299a64", "ref": "5547763a22858a1f10939e082de421d587c862bf",
"url": "https://github.com/larsiusprime/polymod" "url": "https://github.com/larsiusprime/polymod"
}, },
{ {

View file

@ -100,8 +100,15 @@ class Main extends Sprite
// George recommends binding the save before FlxGame is created. // George recommends binding the save before FlxGame is created.
Save.load(); 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 #if hxcpp_debug_server
trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.'); trace('hxcpp_debug_server is enabled! You can now connect to the game with a debugger.');

View file

@ -1,11 +1,8 @@
package funkin.audio; package funkin.audio;
#if flash11
import flash.media.Sound;
import flash.utils.ByteArray;
#end
import flixel.sound.FlxSound; import flixel.sound.FlxSound;
import flixel.group.FlxGroup.FlxTypedGroup; import flixel.group.FlxGroup.FlxTypedGroup;
import flixel.util.FlxSignal.FlxTypedSignal;
import flixel.system.FlxAssets.FlxSoundAsset; import flixel.system.FlxAssets.FlxSoundAsset;
import funkin.util.tools.ICloneable; import funkin.util.tools.ICloneable;
import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongData.SongMusicData;
@ -27,6 +24,25 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
{ {
static final MAX_VOLUME:Float = 1.0; static final MAX_VOLUME:Float = 1.0;
/**
* An FlxSignal which is dispatched when the volume changes.
*/
public static var onVolumeChanged(get, never):FlxTypedSignal<Float->Void>;
static var _onVolumeChanged:Null<FlxTypedSignal<Float->Void>> = null;
static function get_onVolumeChanged():FlxTypedSignal<Float->Void>
{
if (_onVolumeChanged == null)
{
_onVolumeChanged = new FlxTypedSignal<Float->Void>();
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! * Using `FunkinSound.load` will override a dead instance from here rather than creating a new one, if possible!
*/ */

View file

@ -3,6 +3,9 @@ package funkin.graphics;
import flixel.FlxSprite; import flixel.FlxSprite;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import flixel.graphics.FlxGraphic; import flixel.graphics.FlxGraphic;
import openfl.display3D.textures.TextureBase;
import funkin.graphics.framebuffer.FixedBitmapData;
import openfl.display.BitmapData;
/** /**
* An FlxSprite with additional functionality. * 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 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); sprite.loadTexture(key);
return sprite; 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 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); sprite.loadSparrow(key);
return sprite; 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 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); sprite.loadPacker(key);
return sprite; return sprite;
} }
@ -89,6 +92,30 @@ class FunkinSprite extends FlxSprite
return this; 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. * Load an animated texture (Sparrow atlas spritesheet) as the sprite's texture.
* @param key The key of the texture to load. * @param key The key of the texture to load.
@ -119,11 +146,20 @@ class FunkinSprite extends FlxSprite
return this; 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 public static function isTextureCached(key:String):Bool
{ {
return FlxG.bitmap.get(key) != null; 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 public static function cacheTexture(key:String):Void
{ {
// We don't want to cache the same texture twice. // We don't want to cache the same texture twice.
@ -139,7 +175,7 @@ class FunkinSprite extends FlxSprite
} }
// Else, texture is currently uncached. // 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) if (graphic == null)
{ {
FlxG.log.warn('Failed to cache graphic: $key'); FlxG.log.warn('Failed to cache graphic: $key');

View file

@ -32,11 +32,11 @@ class FixedBitmapData extends BitmapData
public static function fromTexture(texture:TextureBase):FixedBitmapData public static function fromTexture(texture:TextureBase):FixedBitmapData
{ {
if (texture == null) return null; if (texture == null) return null;
final bitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0); final bitmapData:FixedBitmapData = new FixedBitmapData(texture.__width, texture.__height, true, 0);
bitmapData.readable = false; // bitmapData.readable = false;
bitmapData.__texture = texture; bitmapData.__texture = texture;
bitmapData.__textureContext = texture.__textureContext; bitmapData.__textureContext = texture.__textureContext;
bitmapData.image = null; // bitmapData.image = null;
return bitmapData; return bitmapData;
} }
} }

View file

@ -1,45 +1,58 @@
package funkin.graphics.video; package funkin.graphics.video;
import flixel.FlxBasic; import flixel.util.FlxColor;
import flixel.FlxSprite; import flixel.util.FlxSignal.FlxTypedSignal;
import funkin.audio.FunkinSound;
import openfl.display3D.textures.TextureBase;
import openfl.events.NetStatusEvent; import openfl.events.NetStatusEvent;
import openfl.media.SoundTransform;
import openfl.media.Video; import openfl.media.Video;
import openfl.net.NetConnection; import openfl.net.NetConnection;
import openfl.net.NetStream; import openfl.net.NetStream;
/** /**
* Plays a video via a NetStream. Only works on HTML5. * 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 video:Video;
var netStream:NetStream; var netStream:NetStream;
var videoPath:String;
public var finishCallback:Void->Void;
/** /**
* 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) public function new(videoPath:String)
{ {
super(); super();
this.videoPath = videoPath;
makeGraphic(2, 2, FlxColor.TRANSPARENT);
video = new Video(); video = new Video();
video.x = 0; video.x = 0;
video.y = 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); netConnection.connect(null);
netStream = new NetStream(netConnection); netStream = new NetStream(netConnection);
netStream.client = {onMetaData: client_onMetaData}; netStream.client = {onMetaData: onClientMetaData};
netConnection.addEventListener(NetStatusEvent.NET_STATUS, netConnection_onNetStatus); netConnection.addEventListener(NetStatusEvent.NET_STATUS, onNetConnectionNetStatus);
netStream.play(videoPath); netStream.play(videoPath);
} }
/**
* Tell the FlxVideo to pause playback.
*/
public function pauseVideo():Void public function pauseVideo():Void
{ {
if (netStream != null) if (netStream != null)
@ -48,6 +61,9 @@ class FlxVideo extends FlxBasic
} }
} }
/**
* Tell the FlxVideo to resume if it is paused.
*/
public function resumeVideo():Void public function resumeVideo():Void
{ {
// Resume playing the video. // 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 public function restartVideo():Void
{ {
// Seek to the beginning of the video. // Seek to the beginning of the video.
@ -66,6 +105,9 @@ class FlxVideo extends FlxBasic
} }
} }
/**
* Tell the FlxVideo to end.
*/
public function finishVideo():Void public function finishVideo():Void
{ {
netStream.dispose(); netStream.dispose();
@ -74,15 +116,48 @@ class FlxVideo extends FlxBasic
if (finishCallback != null) finishCallback(); 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.attachNetStream(netStream);
video.width = FlxG.width; onVideoReady();
video.height = FlxG.height;
} }
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(); if (event.info.code == 'NetStream.Play.Complete') finishVideo();
} }

View file

@ -367,11 +367,14 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
/** /**
* List all the difficulties in this song. * List all the difficulties in this song.
*
* @param variationId Optionally filter by a single variation. * @param variationId Optionally filter by a single variation.
* @param variationIds Optionally filter by multiple variations. * @param variationIds Optionally filter by multiple variations.
* @param showHidden Include charts which are not accessible to the player.
*
* @return The list of difficulties. * @return The list of difficulties.
*/ */
public function listDifficulties(?variationId:String, ?variationIds:Array<String>):Array<String> public function listDifficulties(?variationId:String, ?variationIds:Array<String>, showHidden:Bool = false):Array<String>
{ {
if (variationIds == null) variationIds = []; if (variationIds == null) variationIds = [];
if (variationId != null) variationIds.push(variationId); if (variationId != null) variationIds.push(variationId);
@ -387,6 +390,15 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
return difficulty.difficulty; return difficulty.difficulty;
}).nonNull().unique(); }).nonNull().unique();
diffFiltered = diffFiltered.filter(function(diffId:String):Bool {
if (showHidden) return true;
for (targetVariation in variationIds)
{
if (isDifficultyVisible(diffId, targetVariation)) return true;
}
return false;
});
diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST)); diffFiltered.sort(SortUtil.defaultsThenAlphabetically.bind(Constants.DEFAULT_DIFFICULTY_LIST));
return diffFiltered; return diffFiltered;
@ -405,6 +417,13 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
return false; return false;
} }
public function isDifficultyVisible(diffId:String, variationId:String):Bool
{
var variation = _metadata.get(variationId);
if (variation == null) return false;
return variation.playData.difficulties.contains(diffId);
}
/** /**
* Purge the cached chart data for each difficulty of this song. * Purge the cached chart data for each difficulty of this song.
*/ */

View file

@ -0,0 +1,147 @@
package funkin.ui.options;
import flixel.system.ui.FlxSoundTray;
import flixel.tweens.FlxTween;
import flixel.system.FlxAssets;
import flixel.tweens.FlxEase;
import openfl.display.Bitmap;
import openfl.display.BitmapData;
import openfl.utils.Assets;
import funkin.util.MathUtil;
/**
* Extends the default flixel soundtray, but with some art
* and lil polish!
*
* Gets added to the game in Main.hx, right after FlxGame is new'd
* since it's a Sprite rather than Flixel related object
*/
class FunkinSoundTray extends FlxSoundTray
{
var graphicScale:Float = 0.30;
var lerpYPos:Float = 0;
var volumeMaxSound:String;
public function new()
{
// calls super, then removes all children to add our own
// graphics
super();
removeChildren();
var bg:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/volumebox")));
bg.scaleX = graphicScale;
bg.scaleY = graphicScale;
addChild(bg);
y = -height;
visible = false;
// makes an alpha'd version of all the bars (bar_10.png)
var backingBar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_10")));
backingBar.x = 10;
backingBar.y = 5;
backingBar.scaleX = graphicScale;
backingBar.scaleY = graphicScale;
addChild(backingBar);
backingBar.alpha = 0.4;
// clear the bars array entirely, it was initialized
// in the super class
_bars = [];
// 1...11 due to how block named the assets,
// we are trying to get assets bars_1-10
for (i in 1...11)
{
var bar:Bitmap = new Bitmap(Assets.getBitmapData(Paths.image("soundtray/bars_" + i)));
bar.x = 10;
bar.y = 5;
bar.scaleX = graphicScale;
bar.scaleY = graphicScale;
addChild(bar);
_bars.push(bar);
}
y = -height;
screenCenter();
volumeUpSound = Paths.sound("soundtray/Volup");
volumeDownSound = Paths.sound("soundtray/Voldown");
volumeMaxSound = Paths.sound("soundtray/VolMAX");
trace("Custom tray added!");
}
override public function update(MS:Float):Void
{
y = MathUtil.coolLerp(y, lerpYPos, 0.1);
// Animate sound tray thing
if (_timer > 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;
}
}
}
}