Merge branch 'rewrite/master' into hl-compiles

This commit is contained in:
EliteMasterEric 2024-04-04 18:16:07 -04:00
commit e35eb63d53
23 changed files with 574 additions and 74 deletions

2
assets

@ -1 +1 @@
Subproject commit 15cf800ac9cc4e55210dafb8f2c64838360f9fd0 Subproject commit 5027bc656c9df5ec208ab256f9494bd7da425111

View file

@ -139,7 +139,7 @@
"name": "openfl", "name": "openfl",
"type": "git", "type": "git",
"dir": null, "dir": null,
"ref": "f229d76361c7e31025a048fe7909847f75bb5d5e", "ref": "228c1b5063911e2ad75cef6e3168ef0a4b9f9134",
"url": "https://github.com/FunkinCrew/openfl" "url": "https://github.com/FunkinCrew/openfl"
}, },
{ {

View file

@ -441,7 +441,7 @@ class PauseSubState extends MusicBeatSubState
var entries:Array<PauseMenuEntry> = []; var entries:Array<PauseMenuEntry> = [];
if (PlayState.instance.currentChart != null) if (PlayState.instance.currentChart != null)
{ {
var difficultiesInVariation = PlayState.instance.currentSong.listDifficulties(PlayState.instance.currentChart.variation); var difficultiesInVariation = PlayState.instance.currentSong.listDifficulties(PlayState.instance.currentChart.variation, true);
trace('DIFFICULTIES: ${difficultiesInVariation}'); trace('DIFFICULTIES: ${difficultiesInVariation}');
for (difficulty in difficultiesInVariation) for (difficulty in difficultiesInVariation)
{ {

View file

@ -2224,8 +2224,8 @@ class PlayState extends MusicBeatSubState
holdNote.handledMiss = true; holdNote.handledMiss = true;
// 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;
if (currentStage != null && currentStage.getBoyfriend() != null) currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true); // if (currentStage != null && currentStage.getBoyfriend() != null) currentStage.getBoyfriend().playSingAnimation(holdNote.noteData.getDirection(), true);
} }
} }
} }
@ -2576,7 +2576,7 @@ class PlayState extends MusicBeatSubState
// If daRating is 'miss', that means we made a mistake and should not continue. // If daRating is 'miss', that means we made a mistake and should not continue.
FlxG.log.warn('popUpScore judged a note as a miss!'); FlxG.log.warn('popUpScore judged a note as a miss!');
// TODO: Remove this. // TODO: Remove this.
comboPopUps.displayRating('miss'); // comboPopUps.displayRating('miss');
return; return;
} }
@ -2857,7 +2857,7 @@ class PlayState extends MusicBeatSubState
FlxTransitionableState.skipNextTransIn = true; FlxTransitionableState.skipNextTransIn = true;
FlxTransitionableState.skipNextTransOut = true; FlxTransitionableState.skipNextTransOut = true;
FlxG.sound.music.stop(); if (FlxG.sound.music != null) FlxG.sound.music.stop();
vocals.stop(); vocals.stop();
// TODO: Softcode this cutscene. // TODO: Softcode this cutscene.

View file

@ -0,0 +1,140 @@
package funkin.play;
import flixel.FlxSprite;
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
{
public var scoreShit(default, set):Int = 0;
function set_scoreShit(val):Int
{
if (group == null || group.members == null) return val;
var loopNum:Int = group.members.length - 1;
var dumbNumb = Std.parseInt(Std.string(val));
var prevNum:ScoreNum;
while (dumbNumb > 0)
{
group.members[loopNum].digit = dumbNumb % 10;
// var funnyNum = group.members[loopNum];
// prevNum = group.members[loopNum + 1];
// if (prevNum != null)
// {
// funnyNum.x = prevNum.x - (funnyNum.width * 0.7);
// }
// funnyNum.y = (funnyNum.baseY - (funnyNum.height / 2)) + 73;
// funnyNum.x = (funnyNum.baseX - (funnyNum.width / 2)) + 450; // this plus value is hand picked lol!
dumbNumb = Math.floor(dumbNumb / 10);
loopNum--;
}
while (loopNum > 0)
{
group.members[loopNum].digit = 10;
loopNum--;
}
return val;
}
public function animateNumbers():Void
{
for (i in group.members)
{
i.playAnim();
}
}
public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100)
{
super(x, y);
for (i in 0...digitCount)
{
add(new ScoreNum(x + (65 * i), y));
}
this.scoreShit = scoreShit;
}
public function updateScore(scoreNew:Int)
{
scoreShit = scoreNew;
}
}
class ScoreNum extends FlxSprite
{
public var digit(default, set):Int = 10;
function set_digit(val):Int
{
if (val >= 0 && animation.curAnim != null && animation.curAnim.name != numToString[val])
{
animation.play(numToString[val], true, false, 0);
updateHitbox();
switch (val)
{
case 1:
// offset.x -= 15;
case 5:
// set offsets
// offset.x += 0;
// offset.y += 10;
case 7:
// offset.y += 6;
case 4:
// offset.y += 5;
case 9:
// offset.y += 5;
default:
centerOffsets(false);
}
}
return digit = val;
}
public function playAnim():Void
{
animation.play(numToString[digit], true, false, 0);
}
public var baseY:Float = 0;
public var baseX:Float = 0;
var numToString:Array<String> = [
"ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "DISABLED"
];
public function new(x:Float, y:Float)
{
super(x, y);
baseY = y;
baseX = x;
frames = Paths.getSparrowAtlas('resultScreen/score-digital-numbers');
for (i in 0...10)
{
var stringNum:String = numToString[i];
animation.addByPrefix(stringNum, '$stringNum DIGITAL', 24, false);
}
animation.addByPrefix('DISABLED', 'DISABLED', 24, false);
this.digit = 10;
animation.play(numToString[digit], true);
updateHitbox();
}
}

View file

@ -11,6 +11,7 @@ import flixel.math.FlxPoint;
import funkin.ui.MusicBeatSubState; import funkin.ui.MusicBeatSubState;
import flixel.math.FlxRect; import flixel.math.FlxRect;
import flixel.text.FlxBitmapText; import flixel.text.FlxBitmapText;
import funkin.ui.freeplay.FreeplayScore;
import flixel.tweens.FlxEase; import flixel.tweens.FlxEase;
import funkin.ui.freeplay.FreeplayState; import funkin.ui.freeplay.FreeplayState;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
@ -188,7 +189,7 @@ class ResultState extends MusicBeatSubState
scorePopin.visible = false; scorePopin.visible = false;
add(scorePopin); add(scorePopin);
var highscoreNew:FlxSprite = new FlxSprite(280, 580); var highscoreNew:FlxSprite = new FlxSprite(310, 570);
highscoreNew.frames = Paths.getSparrowAtlas("resultScreen/highscoreNew"); highscoreNew.frames = Paths.getSparrowAtlas("resultScreen/highscoreNew");
highscoreNew.animation.addByPrefix("new", "NEW HIGHSCORE", 24); highscoreNew.animation.addByPrefix("new", "NEW HIGHSCORE", 24);
highscoreNew.visible = false; highscoreNew.visible = false;
@ -228,9 +229,9 @@ class ResultState extends MusicBeatSubState
var tallyMissed:TallyCounter = new TallyCounter(260, (hStuf * 9) + extraYOffset, params.scoreData.tallies.missed, 0xFFC68AE6); var tallyMissed:TallyCounter = new TallyCounter(260, (hStuf * 9) + extraYOffset, params.scoreData.tallies.missed, 0xFFC68AE6);
ratingGrp.add(tallyMissed); ratingGrp.add(tallyMissed);
var score:TallyCounter = new TallyCounter(825, 630, params.scoreData.score, RIGHT); var score:ResultScore = new ResultScore(35, 305, 10, params.scoreData.score);
score.scale.set(2, 2); score.visible = false;
ratingGrp.add(score); add(score);
for (ind => rating in ratingGrp.members) for (ind => rating in ratingGrp.members)
{ {
@ -247,6 +248,10 @@ class ResultState extends MusicBeatSubState
ratingsPopin.animation.finishCallback = anim -> { ratingsPopin.animation.finishCallback = anim -> {
scorePopin.animation.play("score"); scorePopin.animation.play("score");
scorePopin.animation.finishCallback = anim -> {
score.visible = true;
score.animateNumbers();
};
scorePopin.visible = true; scorePopin.visible = true;
if (params.isNewHighscore) if (params.isNewHighscore)

View file

@ -77,13 +77,6 @@ class NoteHoldCover extends FlxTypedSpriteGroup<FlxSprite>
public override function update(elapsed):Void public override function update(elapsed):Void
{ {
super.update(elapsed); super.update(elapsed);
if ((!holdNote.alive || holdNote.missedNote) && !glow.animation.curAnim.name.startsWith('holdCoverEnd'))
{
// If alive is false, the hold note was held to completion.
// If missedNote is true, the hold note was "dropped".
playEnd();
}
} }
public function playStart():Void public function playStart():Void

View file

@ -370,8 +370,6 @@ class Strumline extends FlxSpriteGroup
// Hold note is offscreen, kill it. // Hold note is offscreen, kill it.
holdNote.visible = false; holdNote.visible = false;
holdNote.kill(); // Do not destroy! Recycling is faster. holdNote.kill(); // Do not destroy! Recycling is faster.
// The cover will see this and clean itself up.
} }
else if (holdNote.hitNote && holdNote.sustainLength <= 0) else if (holdNote.hitNote && holdNote.sustainLength <= 0)
{ {
@ -385,10 +383,16 @@ class Strumline extends FlxSpriteGroup
playStatic(holdNote.noteDirection); playStatic(holdNote.noteDirection);
} }
if (holdNote.cover != null) if (holdNote.cover != null && isPlayer)
{ {
holdNote.cover.playEnd(); holdNote.cover.playEnd();
} }
else if (holdNote.cover != null)
{
// *lightning* *zap* *crackle*
holdNote.cover.visible = false;
holdNote.cover.kill();
}
holdNote.visible = false; holdNote.visible = false;
holdNote.kill(); holdNote.kill();
@ -410,6 +414,13 @@ class Strumline extends FlxSpriteGroup
{ {
holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + yOffset + STRUMLINE_SIZE / 2; holdNote.y = this.y - INITIAL_OFFSET + calculateNoteYPos(holdNote.strumTime, vwoosh) + yOffset + STRUMLINE_SIZE / 2;
} }
// Clean up the cover.
if (holdNote.cover != null)
{
holdNote.cover.visible = false;
holdNote.cover.kill();
}
} }
else if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote) else if (Conductor.instance.songPosition > holdNote.strumTime && holdNote.hitNote)
{ {

View file

@ -404,11 +404,12 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
* *
* @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 showLocked Include charts which are not unlocked
* @param showHidden Include charts which are not accessible to the player. * @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>, showHidden:Bool = false):Array<String> public function listDifficulties(?variationId:String, ?variationIds:Array<String>, showLocked:Bool = false, 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);

View file

@ -9,6 +9,7 @@ import funkin.save.migrator.SaveDataMigrator;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle; import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme; import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
import thx.semver.Version; import thx.semver.Version;
import funkin.util.SerializerUtil;
@:nullSafety @:nullSafety
class Save class Save
@ -391,6 +392,22 @@ class Save
*/ */
public function getLevelScore(levelId:String, difficultyId:String = 'normal'):Null<SaveScoreData> public function getLevelScore(levelId:String, difficultyId:String = 'normal'):Null<SaveScoreData>
{ {
if (data.scores?.levels == null)
{
if (data.scores == null)
{
data.scores =
{
songs: [],
levels: []
};
}
else
{
data.scores.levels = [];
}
}
var level = data.scores.levels.get(levelId); var level = data.scores.levels.get(levelId);
if (level == null) if (level == null)
{ {
@ -641,6 +658,9 @@ class Save
{ {
trace("[SAVE] Loading save from slot " + slot + "..."); trace("[SAVE] Loading save from slot " + slot + "...");
// Prevent crashes if the save data is corrupted.
SerializerUtil.initSerializer();
FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH); FlxG.save.bind('$SAVE_NAME${slot}', SAVE_PATH);
if (FlxG.save.isEmpty()) if (FlxG.save.isEmpty())

View file

@ -73,7 +73,7 @@ class ChartEditorImportExportHandler
state.loadInstFromAsset(Paths.inst(songId, '-$variation'), variation); state.loadInstFromAsset(Paths.inst(songId, '-$variation'), variation);
} }
for (difficultyId in song.listDifficulties(variation)) for (difficultyId in song.listDifficulties(variation, true, true))
{ {
var diff:Null<SongDifficulty> = song.getDifficulty(difficultyId, variation); var diff:Null<SongDifficulty> = song.getDifficulty(difficultyId, variation);
if (diff == null) continue; if (diff == null) continue;

View file

@ -308,16 +308,6 @@ class ChartEditorToolboxHandler
state.playtestBotPlayMode = checkboxBotPlay.selected; state.playtestBotPlayMode = checkboxBotPlay.selected;
}; };
var checkboxDebugger:Null<CheckBox> = toolbox.findComponent('playtestDebuggerCheckbox', CheckBox);
if (checkboxDebugger == null) throw 'ChartEditorToolboxHandler.buildToolboxPlaytestPropertiesLayout() - Could not find playtestDebuggerCheckbox component.';
state.enabledDebuggerPopup = checkboxDebugger.selected;
checkboxDebugger.onClick = _ -> {
state.enabledDebuggerPopup = checkboxDebugger.selected;
};
var checkboxSongScripts:Null<CheckBox> = toolbox.findComponent('playtestSongScriptsCheckbox', CheckBox); var checkboxSongScripts:Null<CheckBox> = toolbox.findComponent('playtestSongScriptsCheckbox', CheckBox);
if (checkboxSongScripts == null) if (checkboxSongScripts == null)

View file

@ -171,7 +171,7 @@ class LatencyState extends MusicBeatSubState
trace(FlxG.sound.music._channel.position); trace(FlxG.sound.music._channel.position);
*/ */
localConductor.update(swagSong.time, false); // localConductor.update(swagSong.time, false);
if (FlxG.keys.justPressed.S) if (FlxG.keys.justPressed.S)
{ {

View file

@ -42,11 +42,11 @@ class FreeplayScore extends FlxTypedSpriteGroup<ScoreNum>
return val; return val;
} }
public function new(x:Float, y:Float, scoreShit:Int = 100) public function new(x:Float, y:Float, digitCount:Int, scoreShit:Int = 100)
{ {
super(x, y); super(x, y);
for (i in 0...7) for (i in 0...digitCount)
{ {
add(new ScoreNum(x + (45 * i), y, 0)); add(new ScoreNum(x + (45 * i), y, 0));
} }

View file

@ -195,7 +195,7 @@ class FreeplayState extends MusicBeatSubState
var song:Song = SongRegistry.instance.fetchEntry(songId); var song:Song = SongRegistry.instance.fetchEntry(songId);
// Only display songs which actually have available charts for the current character. // Only display songs which actually have available charts for the current character.
var availableDifficultiesForSong:Array<String> = song.listDifficulties(displayedVariations); var availableDifficultiesForSong:Array<String> = song.listDifficulties(displayedVariations, false);
if (availableDifficultiesForSong.length == 0) continue; if (availableDifficultiesForSong.length == 0) continue;
songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations)); songs.push(new FreeplaySongData(levelId, songId, song, displayedVariations));
@ -425,7 +425,7 @@ class FreeplayState extends MusicBeatSubState
tmr.time = FlxG.random.float(20, 60); tmr.time = FlxG.random.float(20, 60);
}, 0); }, 0);
fp = new FreeplayScore(460, 60, 100); fp = new FreeplayScore(460, 60, 7, 100);
fp.visible = false; fp.visible = false;
add(fp); add(fp);
@ -1400,7 +1400,7 @@ class FreeplaySongData
function updateValues(variations:Array<String>):Void function updateValues(variations:Array<String>):Void
{ {
this.songDifficulties = song.listDifficulties(variations); this.songDifficulties = song.listDifficulties(variations, false, false);
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY; if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations); var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations);

View file

@ -83,10 +83,10 @@ class SongMenuItem extends FlxSpriteGroup
diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00')); diffRatingSprite = new FlxSprite(145, 90).loadGraphic(Paths.image('freeplay/diffRatings/diff00'));
diffRatingSprite.shader = grayscaleShader; diffRatingSprite.shader = grayscaleShader;
diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y);
// TODO: Readd once ratings are fully implemented // TODO: Readd once ratings are fully implemented
// add(diffRatingSprite); // add(diffRatingSprite);
diffRatingSprite.origin.set(capsule.origin.x - diffRatingSprite.x, capsule.origin.y - diffRatingSprite.y); // grpHide.add(diffRatingSprite);
grpHide.add(diffRatingSprite);
songText = new CapsuleText(capsule.width * 0.26, 45, 'Random', Std.int(40 * realScaled)); songText = new CapsuleText(capsule.width * 0.26, 45, 'Random', Std.int(40 * realScaled));
add(songText); add(songText);

View file

@ -169,7 +169,7 @@ class Level implements IRegistryEntry<LevelData>
if (firstSong != null) if (firstSong != null)
{ {
// Don't display alternate characters in Story Mode. Only show `default` and `erect` variations. // Don't display alternate characters in Story Mode. Only show `default` and `erect` variations.
for (difficulty in firstSong.listDifficulties([Constants.DEFAULT_VARIATION, 'erect'])) for (difficulty in firstSong.listDifficulties([Constants.DEFAULT_VARIATION, 'erect'], false, false))
{ {
difficulties.push(difficulty); difficulties.push(difficulty);
} }

View file

@ -1,5 +1,10 @@
package funkin.ui.transition.preload; package funkin.ui.transition.preload;
import openfl.filters.GlowFilter;
import openfl.display.SpreadMethod;
import openfl.display.GradientType;
import openfl.geom.Matrix;
import openfl.filters.BlurFilter;
import openfl.events.MouseEvent; import openfl.events.MouseEvent;
import flash.display.Bitmap; import flash.display.Bitmap;
import flash.display.BitmapData; import flash.display.BitmapData;
@ -46,7 +51,7 @@ class FunkinPreloader extends FlxBasePreloader
*/ */
static final BAR_PADDING:Float = 20; static final BAR_PADDING:Float = 20;
static final BAR_HEIGHT:Int = 20; static final BAR_HEIGHT:Int = 12;
/** /**
* Logo takes this long (in seconds) to fade in. * Logo takes this long (in seconds) to fade in.
@ -108,13 +113,22 @@ class FunkinPreloader extends FlxBasePreloader
#if TOUCH_HERE_TO_PLAY #if TOUCH_HERE_TO_PLAY
var touchHereToPlay:Bitmap; var touchHereToPlay:Bitmap;
#end #end
var progressBarPieces:Array<Sprite>;
var progressBar:Bitmap; var progressBar:Bitmap;
var progressLeftText:TextField; var progressLeftText:TextField;
var progressRightText:TextField; var progressRightText:TextField;
var dspText:TextField;
var enhancedText:TextField;
var stereoText:TextField;
var vfdShader:VFDOverlay;
var box:Sprite;
var progressLines:Sprite;
public function new() public function new()
{ {
super(Constants.PRELOADER_MIN_STAGE_TIME, Constants.SITE_LOCK); super(Constants.PRELOADER_MIN_STAGE_TIME);
// We can't even call trace() yet, until Flixel loads. // We can't even call trace() yet, until Flixel loads.
trace('Initializing custom preloader...'); trace('Initializing custom preloader...');
@ -146,7 +160,7 @@ class FunkinPreloader extends FlxBasePreloader
bmp.x = (this._width - bmp.width) / 2; bmp.x = (this._width - bmp.width) / 2;
bmp.y = (this._height - bmp.height) / 2; bmp.y = (this._height - bmp.height) / 2;
}); });
addChild(logo); // addChild(logo);
#if TOUCH_HERE_TO_PLAY #if TOUCH_HERE_TO_PLAY
touchHereToPlay = createBitmap(TouchHereToPlayImage, function(bmp:Bitmap) { touchHereToPlay = createBitmap(TouchHereToPlayImage, function(bmp:Bitmap) {
@ -160,16 +174,48 @@ class FunkinPreloader extends FlxBasePreloader
addChild(touchHereToPlay); addChild(touchHereToPlay);
#end #end
var amountOfPieces:Int = 16;
progressBarPieces = [];
var maxBarWidth = this._width - BAR_PADDING * 2;
var pieceWidth = maxBarWidth / amountOfPieces;
var pieceGap:Int = 8;
progressLines = new openfl.display.Sprite();
progressLines.graphics.lineStyle(2, Constants.COLOR_PRELOADER_BAR);
progressLines.graphics.drawRect(-2, 480, this._width + 4, 30);
addChild(progressLines);
var progressBarPiece = new Sprite();
progressBarPiece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
progressBarPiece.graphics.drawRoundRect(0, 0, pieceWidth - pieceGap, BAR_HEIGHT, 4, 4);
progressBarPiece.graphics.endFill();
for (i in 0...amountOfPieces)
{
var piece = new Sprite();
piece.graphics.beginFill(Constants.COLOR_PRELOADER_BAR);
piece.graphics.drawRoundRect(0, 0, pieceWidth - pieceGap, BAR_HEIGHT, 4, 4);
piece.graphics.endFill();
piece.x = i * (piece.width + pieceGap);
piece.y = this._height - BAR_PADDING - BAR_HEIGHT - 200;
addChild(piece);
progressBarPieces.push(piece);
}
// Create the progress bar. // Create the progress bar.
progressBar = new Bitmap(new BitmapData(1, BAR_HEIGHT, true, Constants.COLOR_PRELOADER_BAR)); // progressBar = new Bitmap(new BitmapData(1, BAR_HEIGHT, true, Constants.COLOR_PRELOADER_BAR));
progressBar.x = BAR_PADDING; // progressBar.x = BAR_PADDING;
progressBar.y = this._height - BAR_PADDING - BAR_HEIGHT; // progressBar.y = this._height - BAR_PADDING - BAR_HEIGHT;
addChild(progressBar); // addChild(progressBar);
// Create the progress message. // Create the progress message.
progressLeftText = new TextField(); progressLeftText = new TextField();
dspText = new TextField();
enhancedText = new TextField();
stereoText = new TextField();
var progressLeftTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); var progressLeftTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
progressLeftTextFormat.align = TextFormatAlign.LEFT; progressLeftTextFormat.align = TextFormatAlign.LEFT;
progressLeftText.defaultTextFormat = progressLeftTextFormat; progressLeftText.defaultTextFormat = progressLeftTextFormat;
@ -177,13 +223,14 @@ class FunkinPreloader extends FlxBasePreloader
progressLeftText.width = this._width - BAR_PADDING * 2; progressLeftText.width = this._width - BAR_PADDING * 2;
progressLeftText.text = 'Downloading assets...'; progressLeftText.text = 'Downloading assets...';
progressLeftText.x = BAR_PADDING; progressLeftText.x = BAR_PADDING;
progressLeftText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4; progressLeftText.y = this._height - BAR_PADDING - BAR_HEIGHT - 290;
// progressLeftText.shader = new VFDOverlay();
addChild(progressLeftText); addChild(progressLeftText);
// Create the progress %. // Create the progress %.
progressRightText = new TextField(); progressRightText = new TextField();
var progressRightTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); var progressRightTextFormat = new TextFormat("DS-Digital", 16, Constants.COLOR_PRELOADER_BAR, true);
progressRightTextFormat.align = TextFormatAlign.RIGHT; progressRightTextFormat.align = TextFormatAlign.RIGHT;
progressRightText.defaultTextFormat = progressRightTextFormat; progressRightText.defaultTextFormat = progressRightTextFormat;
@ -193,6 +240,60 @@ class FunkinPreloader extends FlxBasePreloader
progressRightText.x = BAR_PADDING; progressRightText.x = BAR_PADDING;
progressRightText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4; progressRightText.y = this._height - BAR_PADDING - BAR_HEIGHT - 16 - 4;
addChild(progressRightText); addChild(progressRightText);
box = new Sprite();
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 1);
box.graphics.drawRoundRect(0, 0, 64, 20, 5, 5);
box.graphics.drawRoundRect(70, 0, 58, 20, 5, 5);
box.graphics.endFill();
box.graphics.beginFill(Constants.COLOR_PRELOADER_BAR, 0.1);
box.graphics.drawRoundRect(0, 0, 128, 20, 5, 5);
box.graphics.endFill();
box.x = 880;
box.y = 440;
addChild(box);
dspText.selectable = false;
dspText.textColor = 0x000000;
dspText.width = this._width;
dspText.height = 20;
dspText.text = 'DSP';
dspText.x = 10;
dspText.y = -5;
box.addChild(dspText);
enhancedText.selectable = false;
enhancedText.textColor = Constants.COLOR_PRELOADER_BAR;
enhancedText.width = this._width;
enhancedText.height = 100;
enhancedText.text = 'ENHANCED';
enhancedText.x = -100;
enhancedText.y = 0;
box.addChild(enhancedText);
stereoText.selectable = false;
stereoText.textColor = Constants.COLOR_PRELOADER_BAR;
stereoText.width = this._width;
stereoText.height = 100;
stereoText.text = 'STEREO';
stereoText.x = 0;
stereoText.y = -40;
box.addChild(stereoText);
// var dummyMatrix:openfl.geom.Matrix = new Matrix();
// dummyMatrix.createGradientBox(this._width, this._height * 0.1, 90 * Math.PI / 180);
// var gradient:Sprite = new Sprite();
// gradient.graphics.beginGradientFill(GradientType.LINEAR, [0xFFFFFF, 0x000000], [1, 1], [0, 255], dummyMatrix, SpreadMethod.REFLECT);
// gradient.graphics.drawRect(0, 0, this._width, this._height);
// gradient.graphics.endFill();
// addChild(gradient);
var vfdBitmap:Bitmap = new Bitmap(new BitmapData(this._width, this._height, true, 0xFFFFFFFF));
addChild(vfdBitmap);
vfdShader = new VFDOverlay();
vfdBitmap.shader = vfdShader;
} }
var lastElapsed:Float = 0.0; var lastElapsed:Float = 0.0;
@ -200,6 +301,8 @@ class FunkinPreloader extends FlxBasePreloader
override function update(percent:Float):Void override function update(percent:Float):Void
{ {
var elapsed:Float = (Date.now().getTime() - this._startTime) / 1000.0; var elapsed:Float = (Date.now().getTime() - this._startTime) / 1000.0;
vfdShader.update(elapsed * 100);
// trace('Time since last frame: ' + (lastElapsed - elapsed)); // trace('Time since last frame: ' + (lastElapsed - elapsed));
downloadingAssetsPercent = percent; downloadingAssetsPercent = percent;
@ -748,12 +851,19 @@ class FunkinPreloader extends FlxBasePreloader
else else
{ {
renderLogoFadeIn(elapsed); renderLogoFadeIn(elapsed);
// Render progress bar
var maxWidth = this._width - BAR_PADDING * 2;
var barWidth = maxWidth * percent;
var piecesToRender:Int = Std.int(percent * progressBarPieces.length);
for (i => piece in progressBarPieces)
{
piece.alpha = i <= piecesToRender ? 0.9 : 0.1;
}
} }
// Render progress bar // progressBar.width = barWidth;
var maxWidth = this._width - BAR_PADDING * 2;
var barWidth = maxWidth * percent;
progressBar.width = barWidth;
// Cycle ellipsis count to show loading // Cycle ellipsis count to show loading
var ellipsisCount:Int = Std.int(elapsed / ELLIPSIS_TIME) % 3 + 1; var ellipsisCount:Int = Std.int(elapsed / ELLIPSIS_TIME) % 3 + 1;
@ -766,29 +876,29 @@ class FunkinPreloader extends FlxBasePreloader
{ {
// case FunkinPreloaderState.NotStarted: // case FunkinPreloaderState.NotStarted:
default: default:
updateProgressLeftText('Loading (0/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Loading \n0/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.DownloadingAssets: case FunkinPreloaderState.DownloadingAssets:
updateProgressLeftText('Downloading assets (1/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Downloading assets \n1/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.PreloadingPlayAssets: case FunkinPreloaderState.PreloadingPlayAssets:
updateProgressLeftText('Preloading assets (2/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Preloading assets \n2/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.InitializingScripts: case FunkinPreloaderState.InitializingScripts:
updateProgressLeftText('Initializing scripts (3/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Initializing scripts \n3/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.CachingGraphics: case FunkinPreloaderState.CachingGraphics:
updateProgressLeftText('Caching graphics (4/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Caching graphics \n4/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.CachingAudio: case FunkinPreloaderState.CachingAudio:
updateProgressLeftText('Caching audio (5/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Caching audio \n5/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.CachingData: case FunkinPreloaderState.CachingData:
updateProgressLeftText('Caching data (6/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Caching data \n6/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.ParsingSpritesheets: case FunkinPreloaderState.ParsingSpritesheets:
updateProgressLeftText('Parsing spritesheets (7/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Parsing spritesheets \n7/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.ParsingStages: case FunkinPreloaderState.ParsingStages:
updateProgressLeftText('Parsing stages (8/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Parsing stages \n8/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.ParsingCharacters: case FunkinPreloaderState.ParsingCharacters:
updateProgressLeftText('Parsing characters (9/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Parsing characters \n9/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.ParsingSongs: case FunkinPreloaderState.ParsingSongs:
updateProgressLeftText('Parsing songs (10/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Parsing songs \n10/$TOTAL_STEPS $ellipsis');
case FunkinPreloaderState.Complete: case FunkinPreloaderState.Complete:
updateProgressLeftText('Finishing up ($TOTAL_STEPS/$TOTAL_STEPS)$ellipsis'); updateProgressLeftText('Finishing up \n$TOTAL_STEPS/$TOTAL_STEPS $ellipsis');
#if TOUCH_HERE_TO_PLAY #if TOUCH_HERE_TO_PLAY
case FunkinPreloaderState.TouchHereToPlay: case FunkinPreloaderState.TouchHereToPlay:
updateProgressLeftText(null); updateProgressLeftText(null);
@ -815,10 +925,21 @@ class FunkinPreloader extends FlxBasePreloader
else if (progressLeftText.text != text) else if (progressLeftText.text != text)
{ {
// We have to keep updating the text format, because the font can take a frame or two to load. // We have to keep updating the text format, because the font can take a frame or two to load.
var progressLeftTextFormat = new TextFormat("VCR OSD Mono", 16, Constants.COLOR_PRELOADER_BAR, true); var progressLeftTextFormat = new TextFormat("DS-Digital", 32, Constants.COLOR_PRELOADER_BAR, true);
progressLeftTextFormat.align = TextFormatAlign.LEFT; progressLeftTextFormat.align = TextFormatAlign.LEFT;
progressLeftText.defaultTextFormat = progressLeftTextFormat; progressLeftText.defaultTextFormat = progressLeftTextFormat;
progressLeftText.text = text; progressLeftText.text = text;
dspText.defaultTextFormat = new TextFormat("Quantico", 20, 0x000000, false);
dspText.text = 'DSP\t\t\t\t\tFNF'; // fukin dum....
dspText.textColor = 0x000000;
enhancedText.defaultTextFormat = new TextFormat("Inconsolata Black", 16, Constants.COLOR_PRELOADER_BAR, false);
enhancedText.text = 'ENHANCED';
enhancedText.textColor = Constants.COLOR_PRELOADER_BAR;
stereoText.defaultTextFormat = new TextFormat("Inconsolata Bold", 36, Constants.COLOR_PRELOADER_BAR, false);
stereoText.text = 'NATURAL STEREO';
} }
} }
} }
@ -845,9 +966,17 @@ class FunkinPreloader extends FlxBasePreloader
logo.y = (this._height - logo.height) / 2; logo.y = (this._height - logo.height) / 2;
// Fade out progress bar too. // Fade out progress bar too.
progressBar.alpha = logo.alpha; // progressBar.alpha = logo.alpha;
progressLeftText.alpha = logo.alpha; progressLeftText.alpha = logo.alpha;
progressRightText.alpha = logo.alpha; progressRightText.alpha = logo.alpha;
box.alpha = logo.alpha;
dspText.alpha = logo.alpha;
enhancedText.alpha = logo.alpha;
stereoText.alpha = logo.alpha;
progressLines.alpha = logo.alpha;
for (piece in progressBarPieces)
piece.alpha = logo.alpha;
return elapsedFinished; return elapsedFinished;
} }
@ -901,8 +1030,8 @@ class FunkinPreloader extends FlxBasePreloader
{ {
// Ensure the graphics are properly destroyed and GC'd. // Ensure the graphics are properly destroyed and GC'd.
removeChild(logo); removeChild(logo);
removeChild(progressBar); // removeChild(progressBar);
logo = progressBar = null; logo = null;
super.destroy(); super.destroy();
} }

View file

@ -0,0 +1,70 @@
package funkin.ui.transition.preload;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import openfl.display.GraphicsShader;
class VFDOverlay extends GraphicsShader
{
public var elapsedTime(default, set):Float = 0;
function set_elapsedTime(value:Float):Float
{
u_time.value = [value];
return value;
}
@:glFragmentSource('#pragma header
const vec2 s = vec2(1, 1.7320508);
uniform float u_time;
float rand(float co) { return fract(sin(co*(91.3458)) * 47453.5453); }
float rand(vec2 co){ return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); }
void main(void) {
vec4 col = texture2D (bitmap, openfl_TextureCoordv);
vec2 game_res = vec2(1280.0, 720.0);
const float tileAmount = 10.;
vec2 uv = (2. * openfl_TextureCoordv.xy * -1.);
uv *= 50.;
vec4 hexCenter = floor(vec4(uv, uv - vec2(0.5, 1.0)) / s.xyxy) + 0.5;
vec4 offset = vec4(uv - hexCenter.xy * s, uv - (hexCenter.zw + 0.5) * s) + 0.0;
vec4 hexInfo = dot(offset.xy, offset.xy) < dot(offset.zw, offset.zw) ? vec4(offset.xy, hexCenter.xy) : vec4(offset.zw, hexCenter.zw);
// Distance to the nearest edge of a hexagon
vec2 p = abs(hexInfo.xy) ;
float edgeDist = max(dot(p, normalize(vec2(1.0, sqrt(3.0)))), p.x);
float edgeWidth = 0.05 * tileAmount; // Adjust edge width based on tile amount
float edgeSharpness = 0.011 * tileAmount;
float outline = smoothstep(edgeWidth - edgeSharpness, edgeWidth, edgeDist);
float color_mix = mix(0.0, 0.3, outline); // Mix black outline with white fill
float flicker = (sin(u_time) * 0.05) + 1.0;
float sinshit = smoothstep(-3.0, 1.0, sin(uv.y * 3.));
col = vec4(vec3(0.0), color_mix);
col = mix(col, vec4(0., 0., 0., sinshit), 0.5 * flicker);
float specs = rand(uv.xy);
vec4 noise = vec4(0., 0., 0., specs);
col = mix(col, noise, 0.1);
gl_FragColor = col;
}
')
public function new()
{
super();
this.elapsedTime = 0;
}
public function update(elapsed:Float):Void
{
this.elapsedTime += elapsed;
}
}

View file

@ -140,7 +140,7 @@ class Constants
/** /**
* Color for the preloader progress bar * Color for the preloader progress bar
*/ */
public static final COLOR_PRELOADER_BAR:FlxColor = 0xFF00FF00; public static final COLOR_PRELOADER_BAR:FlxColor = 0xFFA4FF11;
/** /**
* Color for the preloader site lock background * Color for the preloader site lock background
@ -357,7 +357,7 @@ class Constants
* The progress bare is automatically rescaled to match. * The progress bare is automatically rescaled to match.
*/ */
#if debug #if debug
public static final PRELOADER_MIN_STAGE_TIME:Float = 1.0; public static final PRELOADER_MIN_STAGE_TIME:Float = 0.0;
#else #else
public static final PRELOADER_MIN_STAGE_TIME:Float = 0.1; public static final PRELOADER_MIN_STAGE_TIME:Float = 0.1;
#end #end

View file

@ -63,6 +63,31 @@ class SerializerUtil
} }
} }
public static function initSerializer():Void
{
haxe.Unserializer.DEFAULT_RESOLVER = new FunkinTypeResolver();
}
/**
* Serialize a Haxe object using the built-in Serializer.
* @param input The object to serialize
* @return The serialized object as a string
*/
public static function fromHaxeObject(input:Dynamic):String
{
return haxe.Serializer.run(input);
}
/**
* Convert a serialized Haxe object back into a Haxe object.
* @param input The serialized object as a string
* @return The deserialized object
*/
public static function toHaxeObject(input:String):Dynamic
{
return haxe.Unserializer.run(input);
}
/** /**
* Customize how certain types are serialized when converting to JSON. * Customize how certain types are serialized when converting to JSON.
*/ */
@ -90,3 +115,26 @@ class SerializerUtil
return result; return result;
} }
} }
class FunkinTypeResolver
{
public function new()
{
// Blank constructor.
}
public function resolveClass(name:String):Class<Dynamic>
{
if (name == 'Dynamic')
{
FlxG.log.warn('Found invalid class type in save data, indicates partial save corruption.');
return null;
}
return Type.resolveClass(name);
};
public function resolveEnum(name:String):Enum<Dynamic>
{
return Type.resolveEnum(name);
};
}

View file

@ -1,5 +1,6 @@
package funkin.util; package funkin.util;
import funkin.util.tools.MapTools;
import haxe.DynamicAccess; import haxe.DynamicAccess;
/** /**
@ -26,6 +27,57 @@ class StructureUtil
return result; return result;
} }
public static function toMap(a:Dynamic):haxe.ds.Map<String, Dynamic>
{
var result:haxe.ds.Map<String, Dynamic> = [];
for (field in Reflect.fields(a))
{
result.set(field, Reflect.field(a, field));
}
return result;
}
public static function isMap(a:Dynamic):Bool
{
return Std.isOfType(a, haxe.Constraints.IMap);
}
public static function isObject(a:Dynamic):Bool
{
switch (Type.typeof(a))
{
case TObject:
return true;
default:
return false;
}
}
public static function isPrimitive(a:Dynamic):Bool
{
switch (Type.typeof(a))
{
case TInt | TFloat | TBool:
return true;
case TClass(c):
return false;
case TEnum(e):
return false;
case TObject:
return false;
case TFunction:
return false;
case TNull:
return true;
case TUnknown:
return false;
default:
return false;
}
}
/** /**
* Merge two structures, with the second overwriting the first. * Merge two structures, with the second overwriting the first.
* Performs a DEEP clone, where child structures are also merged recursively. * Performs a DEEP clone, where child structures are also merged recursively.
@ -37,7 +89,30 @@ class StructureUtil
{ {
if (a == null) return b; if (a == null) return b;
if (b == null) return null; if (b == null) return null;
if (isPrimitive(a) && isPrimitive(b)) return b;
if (isMap(b))
{
if (isMap(a))
{
return MapTools.merge(a, b);
}
else
{
return StructureUtil.toMap(a).merge(b);
}
}
if (!Reflect.isObject(a) || !Reflect.isObject(b)) return b; if (!Reflect.isObject(a) || !Reflect.isObject(b)) return b;
if (Std.isOfType(b, haxe.ds.StringMap))
{
if (Std.isOfType(a, haxe.ds.StringMap))
{
return MapTools.merge(a, b);
}
else
{
return StructureUtil.toMap(a).merge(b);
}
}
var result:DynamicAccess<Dynamic> = Reflect.copy(a); var result:DynamicAccess<Dynamic> = Reflect.copy(a);

View file

@ -33,6 +33,24 @@ class MapTools
return map.copy(); return map.copy();
} }
/**
* Create a new map which is a combination of the two given maps.
* @param a The base map.
* @param b The other map. The values from this take precedence.
* @return The combined map.
*/
public static function merge<K, T>(a:Map<K, T>, b:Map<K, T>):Map<K, T>
{
var result = a.copy();
for (pair in b.keyValueIterator())
{
result.set(pair.key, pair.value);
}
return result;
}
/** /**
* Create a new array with clones of all elements of the given array, to prevent modifying the original. * Create a new array with clones of all elements of the given array, to prevent modifying the original.
*/ */