1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-01-12 23:27:35 +00:00

Merge pull request #395 from FunkinCrew/bugfix/buncha-2hot-fixes

Bugfix/buncha 2hot fixes
This commit is contained in:
Cameron Taylor 2024-04-01 22:40:30 -04:00 committed by GitHub
commit 657172ab56
16 changed files with 242 additions and 120 deletions

2
assets

@ -1 +1 @@
Subproject commit cb862903ec0975364c13080297ccbfb13f26f5cb
Subproject commit a54eb8517914e2a90b77e122ecd81cf73d60adaa

View file

@ -9,7 +9,7 @@ import openfl.utils.Assets as OpenFlAssets;
*/
class Paths
{
static var currentLevel:String;
static var currentLevel:Null<String> = null;
public static function setCurrentLevel(name:String):Void
{

View file

@ -265,10 +265,16 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
@:allow(flixel.sound.FlxSoundGroup)
override function updateTransform():Void
{
_transform.volume = #if FLX_SOUND_SYSTEM ((FlxG.sound.muted || this.muted) ? 0 : 1) * FlxG.sound.volume * #end
(group != null ? group.volume : 1) * _volume * _volumeAdjust;
if (_transform != null)
{
_transform.volume = #if FLX_SOUND_SYSTEM ((FlxG.sound.muted || this.muted) ? 0 : 1) * FlxG.sound.volume * #end
(group != null ? group.volume : 1) * _volume * _volumeAdjust;
}
if (_channel != null) _channel.soundTransform = _transform;
if (_channel != null)
{
_channel.soundTransform = _transform;
}
}
public function clone():FunkinSound
@ -336,6 +342,12 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
FlxG.sound.music.kill();
}
// Apparently HaxeFlixel isn't null safe.
@:nullSafety(Off)
{
FlxG.sound.music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, true, false, true);
}
var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, params.loop ?? true, false, true);
if (music != null)
{
@ -391,10 +403,10 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
sound._label = 'unknown';
}
if (autoPlay) sound.play();
sound.volume = volume;
sound.group = FlxG.sound.defaultSoundGroup;
sound.persist = true;
if (autoPlay) sound.play();
// Call onLoad() because the sound already loaded
if (onLoad != null && sound._sound != null) onLoad();

View file

@ -150,7 +150,7 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
/**
* Stop all the sounds in the group.
*/
public function stop()
public function stop():Void
{
if (members != null)
{
@ -160,7 +160,7 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
}
}
public override function destroy()
public override function destroy():Void
{
stop();
super.destroy();
@ -178,9 +178,14 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
function get_time():Float
{
if (getFirstAlive() != null) return getFirstAlive().time;
if (getFirstAlive() != null)
{
return getFirstAlive().time;
}
else
{
return 0;
}
}
function set_time(time:Float):Float
@ -195,16 +200,26 @@ class SoundGroup extends FlxTypedGroup<FunkinSound>
function get_playing():Bool
{
if (getFirstAlive() != null) return getFirstAlive().playing;
if (getFirstAlive() != null)
{
return getFirstAlive().playing;
}
else
{
return false;
}
}
function get_volume():Float
{
if (getFirstAlive() != null) return getFirstAlive().volume;
if (getFirstAlive() != null)
{
return getFirstAlive().volume;
}
else
{
return 1;
}
}
// in PlayState, adjust the code so that it only mutes the player1 vocal tracks?

View file

@ -8,7 +8,12 @@ import funkin.modding.IScriptedClass;
*/
class ScriptEventDispatcher
{
public static function callEvent(target:IScriptedClass, event:ScriptEvent):Void
/**
* Invoke the given event hook on the given scripted class.
* @param target The target class to call script hooks on.
* @param event The event, which determines the script hook to call and provides parameters for it.
*/
public static function callEvent(target:Null<IScriptedClass>, event:ScriptEvent):Void
{
if (target == null || event == null) return;

View file

@ -3,18 +3,18 @@ package funkin.play;
import flixel.FlxG;
import flixel.FlxObject;
import flixel.FlxSprite;
import funkin.audio.FunkinSound;
import flixel.input.touch.FlxTouch;
import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import funkin.audio.FunkinSound;
import funkin.graphics.FunkinSprite;
import funkin.modding.events.ScriptEvent;
import funkin.modding.events.ScriptEventDispatcher;
import funkin.play.character.BaseCharacter;
import funkin.play.PlayState;
import funkin.util.MathUtil;
import funkin.ui.freeplay.FreeplayState;
import funkin.ui.MusicBeatSubState;
import funkin.ui.story.StoryMenuState;
import funkin.util.MathUtil;
import openfl.utils.Assets;
/**
@ -23,13 +23,14 @@ import openfl.utils.Assets;
*
* The newest implementation uses a substate, which prevents having to reload the song and stage each reset.
*/
@:nullSafety
class GameOverSubState extends MusicBeatSubState
{
/**
* The currently active GameOverSubState.
* There should be only one GameOverSubState in existance at a time, we can use a singleton.
*/
public static var instance:GameOverSubState = null;
public static var instance:Null<GameOverSubState> = null;
/**
* Which alternate animation on the character to use.
@ -37,7 +38,7 @@ class GameOverSubState extends MusicBeatSubState
* For example, playing a different animation when BF dies in Week 4
* or Pico dies in Weekend 1.
*/
public static var animationSuffix:String = "";
public static var animationSuffix:String = '';
/**
* Which alternate game over music to use.
@ -45,17 +46,19 @@ class GameOverSubState extends MusicBeatSubState
* For example, the bf-pixel script sets this to `-pixel`
* and the pico-playable script sets this to `Pico`.
*/
public static var musicSuffix:String = "";
public static var musicSuffix:String = '';
/**
* Which alternate "blue ball" sound effect to use.
*/
public static var blueBallSuffix:String = "";
public static var blueBallSuffix:String = '';
static var blueballed:Bool = false;
/**
* The boyfriend character.
*/
var boyfriend:BaseCharacter;
var boyfriend:Null<BaseCharacter> = null;
/**
* The invisible object in the scene which the camera focuses on.
@ -82,7 +85,8 @@ class GameOverSubState extends MusicBeatSubState
var transparent:Bool;
final CAMERA_ZOOM_DURATION:Float = 0.5;
static final CAMERA_ZOOM_DURATION:Float = 0.5;
var targetCameraZoom:Float = 1.0;
public function new(params:GameOverParams)
@ -91,6 +95,8 @@ class GameOverSubState extends MusicBeatSubState
this.isChartingMode = params?.isChartingMode ?? false;
transparent = params.transparent;
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
}
/**
@ -101,14 +107,15 @@ class GameOverSubState extends MusicBeatSubState
animationSuffix = '';
musicSuffix = '';
blueBallSuffix = '';
blueballed = false;
}
override public function create():Void
public override function create():Void
{
if (instance != null)
{
// TODO: Do something in this case? IDK.
trace('WARNING: GameOverSubState instance already exists. This should not happen.');
FlxG.log.warn('WARNING: GameOverSubState instance already exists. This should not happen.');
}
instance = this;
@ -121,7 +128,7 @@ class GameOverSubState extends MusicBeatSubState
var playState = PlayState.instance;
// Add a black background to the screen.
var bg = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width * 2, FlxG.height * 2, FlxColor.BLACK);
// We make this transparent so that we can see the stage underneath during debugging,
// but it's normally opaque.
bg.alpha = transparent ? 0.25 : 1.0;
@ -138,21 +145,10 @@ class GameOverSubState extends MusicBeatSubState
boyfriend.isDead = true;
add(boyfriend);
boyfriend.resetCharacter();
// Assign a camera follow point to the boyfriend's position.
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
cameraFollowPoint.x += offsets[0];
cameraFollowPoint.y += offsets[1];
add(cameraFollowPoint);
FlxG.camera.target = null;
FlxG.camera.follow(cameraFollowPoint, LOCKON, 0.01);
targetCameraZoom = PlayState?.instance?.currentStage?.camZoom * boyfriend.getDeathCameraZoom();
}
setCameraTarget();
//
// Set up the audio
//
@ -161,6 +157,27 @@ class GameOverSubState extends MusicBeatSubState
Conductor.instance.update(0);
}
@:nullSafety(Off)
function setCameraTarget():Void
{
// Assign a camera follow point to the boyfriend's position.
cameraFollowPoint = new FlxObject(PlayState.instance.cameraFollowPoint.x, PlayState.instance.cameraFollowPoint.y, 1, 1);
cameraFollowPoint.x = boyfriend.getGraphicMidpoint().x;
cameraFollowPoint.y = boyfriend.getGraphicMidpoint().y;
var offsets:Array<Float> = boyfriend.getDeathCameraOffsets();
cameraFollowPoint.x += offsets[0];
cameraFollowPoint.y += offsets[1];
add(cameraFollowPoint);
FlxG.camera.target = null;
FlxG.camera.follow(cameraFollowPoint, LOCKON, Constants.DEFAULT_CAMERA_FOLLOW_RATE / 2);
targetCameraZoom = (PlayState?.instance?.currentStage?.camZoom ?? 1.0) * boyfriend.getDeathCameraZoom();
}
/**
* Forcibly reset the camera zoom level to that of the current stage.
* This prevents camera zoom events from adversely affecting the game over state.
*/
public function resetCameraZoom():Void
{
// Apply camera zoom level from stage data.
@ -175,7 +192,7 @@ class GameOverSubState extends MusicBeatSubState
{
hasStartedAnimation = true;
if (PlayState.instance.isMinimalMode)
if (boyfriend == null || PlayState.instance.isMinimalMode)
{
// Play the "blue balled" sound. May play a variant if one has been assigned.
playBlueBalledSFX();
@ -205,10 +222,10 @@ class GameOverSubState extends MusicBeatSubState
// MOBILE ONLY: Restart the level when tapping Boyfriend.
if (FlxG.onMobile)
{
var touch = FlxG.touches.getFirst();
var touch:FlxTouch = FlxG.touches.getFirst();
if (touch != null)
{
if (touch.overlaps(boyfriend))
if (boyfriend == null || touch.overlaps(boyfriend))
{
confirmDeath();
}
@ -228,7 +245,7 @@ class GameOverSubState extends MusicBeatSubState
blueballed = false;
PlayState.instance.deathCounter = 0;
// PlayState.seenCutscene = false; // old thing...
gameOverMusic.stop();
if (gameOverMusic != null) gameOverMusic.stop();
if (isChartingMode)
{
@ -252,7 +269,7 @@ class GameOverSubState extends MusicBeatSubState
// This enables the stepHit and beatHit events.
Conductor.instance.update(gameOverMusic.time);
}
else
else if (boyfriend != null)
{
if (PlayState.instance.isMinimalMode)
{
@ -299,7 +316,7 @@ class GameOverSubState extends MusicBeatSubState
isEnding = true;
startDeathMusic(1.0, true); // isEnding changes this function's behavior.
if (PlayState.instance.isMinimalMode) {}
if (PlayState.instance.isMinimalMode || boyfriend == null) {}
else
{
boyfriend.playAnimation('deathConfirm' + animationSuffix, true);
@ -313,7 +330,7 @@ class GameOverSubState extends MusicBeatSubState
FlxG.camera.fade(FlxColor.BLACK, 1, true, null, true);
PlayState.instance.needsReset = true;
if (PlayState.instance.isMinimalMode) {}
if (PlayState.instance.isMinimalMode || boyfriend == null) {}
else
{
// Readd Boyfriend to the stage.
@ -332,7 +349,7 @@ class GameOverSubState extends MusicBeatSubState
}
}
public override function dispatchEvent(event:ScriptEvent)
public override function dispatchEvent(event:ScriptEvent):Void
{
super.dispatchEvent(event);
@ -345,11 +362,11 @@ class GameOverSubState extends MusicBeatSubState
*/
function resolveMusicPath(suffix:String, starting:Bool = false, ending:Bool = false):Null<String>
{
var basePath = 'gameplay/gameover/gameOver';
if (starting) basePath += 'Start';
else if (ending) basePath += 'End';
var basePath:String = 'gameplay/gameover/gameOver';
if (ending) basePath += 'End';
else if (starting) basePath += 'Start';
var musicPath = Paths.music(basePath + suffix);
var musicPath:String = Paths.music(basePath + suffix);
while (!Assets.exists(musicPath) && suffix.length > 0)
{
suffix = suffix.split('-').slice(0, -1).join('-');
@ -362,23 +379,26 @@ class GameOverSubState extends MusicBeatSubState
/**
* Starts the death music at the appropriate volume.
* @param startingVolume
* @param startingVolume The initial volume for the music.
* @param force Whether or not to force the music to restart.
*/
public function startDeathMusic(startingVolume:Float = 1, force:Bool = false):Void
{
var musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding);
var onComplete = null;
var musicPath:Null<String> = resolveMusicPath(musicSuffix, isStarting, isEnding);
var onComplete:() -> Void = () -> {};
if (isStarting)
{
if (musicPath == null)
{
// Looked for starting music and didn't find it. Use middle music instead.
isStarting = false;
musicPath = resolveMusicPath(musicSuffix, isStarting, isEnding);
}
else
{
onComplete = function() {
isStarting = false;
isStarting = true;
// We need to force to ensure that the non-starting music plays.
startDeathMusic(1.0, true);
};
@ -387,13 +407,16 @@ class GameOverSubState extends MusicBeatSubState
if (musicPath == null)
{
trace('Could not find game over music!');
FlxG.log.warn('[GAMEOVER] Could not find game over music at path ($musicPath)!');
return;
}
else if (gameOverMusic == null || !gameOverMusic.playing || force)
{
if (gameOverMusic != null) gameOverMusic.stop();
gameOverMusic = FunkinSound.load(musicPath);
if (gameOverMusic == null) return;
gameOverMusic.volume = startingVolume;
gameOverMusic.looped = !(isEnding || isStarting);
gameOverMusic.onComplete = onComplete;
@ -406,13 +429,11 @@ class GameOverSubState extends MusicBeatSubState
}
}
static var blueballed:Bool = false;
/**
* Play the sound effect that occurs when
* boyfriend's testicles get utterly annihilated.
*/
public static function playBlueBalledSFX()
public static function playBlueBalledSFX():Void
{
blueballed = true;
if (Assets.exists(Paths.sound('gameplay/gameover/fnf_loss_sfx' + blueBallSuffix)))
@ -431,7 +452,7 @@ class GameOverSubState extends MusicBeatSubState
* Week 7-specific hardcoded behavior, to play a custom death quote.
* TODO: Make this a module somehow.
*/
function playJeffQuote()
function playJeffQuote():Void
{
var randomCensor:Array<Int> = [];
@ -446,20 +467,27 @@ class GameOverSubState extends MusicBeatSubState
});
}
public override function destroy()
public override function destroy():Void
{
super.destroy();
if (gameOverMusic != null) gameOverMusic.stop();
gameOverMusic = null;
if (gameOverMusic != null)
{
gameOverMusic.stop();
gameOverMusic = null;
}
blueballed = false;
instance = null;
}
public override function toString():String
{
return "GameOverSubState";
return 'GameOverSubState';
}
}
/**
* Parameters used to instantiate a GameOverSubState.
*/
typedef GameOverParams =
{
var isChartingMode:Bool;

View file

@ -73,8 +73,8 @@ class PauseSubState extends MusicBeatSubState
*/
static final PAUSE_MENU_ENTRIES_VIDEO_CUTSCENE:Array<PauseMenuEntry> = [
{text: 'Resume', callback: resume},
{text: 'Restart Cutscene', callback: restartVideoCutscene},
{text: 'Skip Cutscene', callback: skipVideoCutscene},
{text: 'Restart Cutscene', callback: restartVideoCutscene},
{text: 'Exit to Menu', callback: quitToMenu},
];

View file

@ -1566,7 +1566,7 @@ class PlayState extends MusicBeatSubState
{
if (PlayState.instance.isMinimalMode) return;
// Apply camera zoom level from stage data.
defaultCameraZoom = currentStage.camZoom;
defaultCameraZoom = currentStage?.camZoom ?? 1.0;
currentCameraZoom = defaultCameraZoom;
FlxG.camera.zoom = currentCameraZoom;

View file

@ -5,12 +5,13 @@ package funkin.play;
*
* TODO: Add getters/setters for all these properties to validate them.
*/
@:nullSafety
class PlayStatePlaylist
{
/**
* Whether the game is currently in Story Mode. If false, we are in Free Play Mode.
*/
public static var isStoryMode(default, default):Bool = false;
public static var isStoryMode:Bool = false;
/**
* The loist of upcoming songs to be played.
@ -31,8 +32,9 @@ class PlayStatePlaylist
/**
* The internal ID of the current playlist, for example `week4` or `weekend-1`.
* @default `null`, used when no playlist is loaded
*/
public static var campaignId:String = 'unknown';
public static var campaignId:Null<String> = null;
public static var campaignDifficulty:String = Constants.DEFAULT_DIFFICULTY;
@ -45,7 +47,7 @@ class PlayStatePlaylist
playlistSongIds = [];
campaignScore = 0;
campaignTitle = 'UNKNOWN';
campaignId = 'unknown';
campaignId = null;
campaignDifficulty = Constants.DEFAULT_DIFFICULTY;
}
}

View file

@ -223,7 +223,8 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
if (propSprite.frames == null || propSprite.frames.numFrames == 0)
{
trace(' ERROR: Could not build texture for prop.');
@:privateAccess
trace(' ERROR: Could not build texture for prop. Check the asset path (${Paths.currentLevel ?? 'default'}, ${dataProp.assetPath}).');
continue;
}

View file

@ -6,15 +6,14 @@ import flixel.addons.transition.FlxTransitionableState;
import flixel.FlxCamera;
import flixel.FlxSprite;
import flixel.FlxSubState;
import flixel.graphics.FlxGraphic;
import flixel.group.FlxGroup.FlxTypedGroup;
import funkin.graphics.FunkinCamera;
import flixel.group.FlxSpriteGroup;
import flixel.input.keyboard.FlxKey;
import flixel.input.mouse.FlxMouseEvent;
import flixel.math.FlxMath;
import flixel.math.FlxPoint;
import flixel.math.FlxRect;
import flixel.sound.FlxSound;
import flixel.system.debug.log.LogStyle;
import flixel.system.FlxAssets.FlxSoundAsset;
import flixel.text.FlxText;
@ -26,26 +25,19 @@ import flixel.util.FlxSort;
import flixel.util.FlxTimer;
import funkin.audio.FunkinSound;
import funkin.audio.visualize.PolygonSpectogram;
import funkin.audio.visualize.PolygonSpectogram;
import funkin.audio.VoicesGroup;
import funkin.audio.waveform.WaveformSprite;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongCharacterData;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongChartData;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongEventData;
import funkin.data.song.SongData.SongMetadata;
import funkin.data.song.SongData.SongMetadata;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongData.SongNoteData;
import funkin.data.song.SongData.SongOffsets;
import funkin.data.song.SongDataUtils;
import funkin.data.song.SongDataUtils;
import funkin.data.song.SongRegistry;
import funkin.data.song.SongRegistry;
import funkin.data.stage.StageData;
import funkin.graphics.FunkinCamera;
import funkin.graphics.FunkinSprite;
import funkin.input.Cursor;
import funkin.input.TurboKeyHandler;
@ -56,13 +48,12 @@ import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.components.HealthIcon;
import funkin.play.notes.NoteSprite;
import funkin.play.PlayState;
import funkin.play.PlayStatePlaylist;
import funkin.play.song.Song;
import funkin.save.Save;
import funkin.ui.debug.charting.commands.AddEventsCommand;
import funkin.ui.debug.charting.commands.AddNotesCommand;
import funkin.ui.debug.charting.commands.ChartEditorCommand;
import funkin.ui.debug.charting.commands.ChartEditorCommand;
import funkin.ui.debug.charting.commands.ChartEditorCommand;
import funkin.ui.debug.charting.commands.CopyItemsCommand;
import funkin.ui.debug.charting.commands.CutItemsCommand;
import funkin.ui.debug.charting.commands.DeselectAllItemsCommand;
@ -95,6 +86,7 @@ import funkin.ui.debug.charting.toolboxes.ChartEditorOffsetsToolbox;
import funkin.ui.haxeui.components.CharacterPlayer;
import funkin.ui.haxeui.HaxeUIState;
import funkin.ui.mainmenu.MainMenuState;
import funkin.ui.transition.LoadingState;
import funkin.util.Constants;
import funkin.util.FileUtil;
import funkin.util.logging.CrashHandler;
@ -119,7 +111,6 @@ import haxe.ui.containers.Grid;
import haxe.ui.containers.HBox;
import haxe.ui.containers.menus.Menu;
import haxe.ui.containers.menus.MenuBar;
import haxe.ui.containers.menus.MenuBar;
import haxe.ui.containers.menus.MenuCheckBox;
import haxe.ui.containers.menus.MenuItem;
import haxe.ui.containers.ScrollView;
@ -130,7 +121,6 @@ import haxe.ui.core.Screen;
import haxe.ui.events.DragEvent;
import haxe.ui.events.MouseEvent;
import haxe.ui.events.UIEvent;
import haxe.ui.events.UIEvent;
import haxe.ui.focus.FocusManager;
import haxe.ui.Toolkit;
import openfl.display.BitmapData;
@ -5338,30 +5328,31 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
}
catch (e)
{
this.error("Could Not Playtest", 'Got an error trying to playtest the song.\n${e}');
this.error('Could Not Playtest', 'Got an error trying to playtest the song.\n${e}');
return;
}
// TODO: Rework asset system so we can remove this.
// TODO: Rework asset system so we can remove this jank.
switch (currentSongStage)
{
case 'mainStage':
Paths.setCurrentLevel('week1');
PlayStatePlaylist.campaignId = 'week1';
case 'spookyMansion':
Paths.setCurrentLevel('week2');
PlayStatePlaylist.campaignId = 'week2';
case 'phillyTrain':
Paths.setCurrentLevel('week3');
PlayStatePlaylist.campaignId = 'week3';
case 'limoRide':
Paths.setCurrentLevel('week4');
PlayStatePlaylist.campaignId = 'week4';
case 'mallXmas' | 'mallEvil':
Paths.setCurrentLevel('week5');
PlayStatePlaylist.campaignId = 'week5';
case 'school' | 'schoolEvil':
Paths.setCurrentLevel('week6');
PlayStatePlaylist.campaignId = 'week6';
case 'tankmanBattlefield':
Paths.setCurrentLevel('week7');
PlayStatePlaylist.campaignId = 'week7';
case 'phillyStreets' | 'phillyBlazin' | 'phillyBlazin2':
Paths.setCurrentLevel('weekend1');
PlayStatePlaylist.campaignId = 'weekend1';
}
Paths.setCurrentLevel(PlayStatePlaylist.campaignId);
subStateClosed.add(reviveUICamera);
subStateClosed.add(resetConductorAfterTest);
@ -5369,7 +5360,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
FlxTransitionableState.skipNextTransIn = false;
FlxTransitionableState.skipNextTransOut = false;
var targetState = new PlayState(
var targetStateParams =
{
targetSong: targetSong,
targetDifficulty: selectedDifficulty,
@ -5380,14 +5371,13 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
startTimestamp: startTimestamp,
playbackRate: playbackRate,
overrideMusic: true,
});
};
// Override music.
if (audioInstTrack != null)
{
FlxG.sound.music = audioInstTrack;
}
targetState.vocals = audioVocalTrackGroup;
// Kill and replace the UI camera so it doesn't get destroyed during the state transition.
uiCamera.kill();
@ -5397,7 +5387,10 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
this.persistentUpdate = false;
this.persistentDraw = false;
stopWelcomeMusic();
openSubState(targetState);
LoadingState.loadPlayState(targetStateParams, false, true, function(targetState) {
targetState.vocals = audioVocalTrackGroup;
});
}
/**

View file

@ -1236,6 +1236,12 @@ class FreeplayState extends MusicBeatSubState
});
if (didReplace)
{
FunkinSound.playMusic('freakyMenu',
{
startingVolume: 0.0,
overrideExisting: true,
restartTrack: false
});
FlxG.sound.music.fadeIn(2, 0, 0.8);
}
}

View file

@ -17,7 +17,7 @@ import funkin.ui.MusicBeatState;
*/
class AttractState extends MusicBeatState
{
static final ATTRACT_VIDEO_PATH:String = Paths.videos('kickstarterTrailer');
static final ATTRACT_VIDEO_PATH:String = Paths.stripLibrary(Paths.videos('kickstarterTrailer', 'shared'));
public override function create():Void
{
@ -29,10 +29,12 @@ class AttractState extends MusicBeatState
}
#if html5
trace('Playing web video ${ATTRACT_VIDEO_PATH}');
playVideoHTML5(ATTRACT_VIDEO_PATH);
#end
#if hxCodec
trace('Playing native video ${ATTRACT_VIDEO_PATH}');
playVideoNative(ATTRACT_VIDEO_PATH);
#end
}

View file

@ -220,7 +220,7 @@ class TitleState extends MusicBeatState
function playMenuMusic():Void
{
var shouldFadeIn = (FlxG.sound.music == null);
var shouldFadeIn:Bool = (FlxG.sound.music == null);
// Load music. Includes logic to handle BPM changes.
FunkinSound.playMusic('freakyMenu',
{
@ -229,7 +229,7 @@ class TitleState extends MusicBeatState
restartTrack: true
});
// Fade from 0.0 to 0.7 over 4 seconds
if (shouldFadeIn) FlxG.sound.music.fadeIn(4.0, 0.0, 0.7);
if (shouldFadeIn) FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
}
function getIntroTextShit():Array<Array<String>>

View file

@ -22,10 +22,12 @@ import openfl.filters.ShaderFilter;
import openfl.utils.Assets;
import flixel.util.typeLimit.NextState;
class LoadingState extends MusicBeatState
class LoadingState extends MusicBeatSubState
{
inline static var MIN_TIME = 1.0;
var asSubState:Bool = false;
var target:NextState;
var playParams:Null<PlayStateParams>;
var stopMusic:Bool = false;
@ -178,7 +180,16 @@ class LoadingState extends MusicBeatState
FlxG.sound.music = null;
}
FlxG.switchState(target);
if (asSubState)
{
this.close();
// We will assume the target is a valid substate.
FlxG.state.openSubState(cast target);
}
else
{
FlxG.switchState(target);
}
}
static function getSongPath():String
@ -190,17 +201,41 @@ class LoadingState extends MusicBeatState
* Starts the transition to a new `PlayState` to start a new song.
* First switches to the `LoadingState` if assets need to be loaded.
* @param params The parameters for the next `PlayState`.
* @param asSubState Whether to open as a substate rather than switching to the `PlayState`.
* @param shouldStopMusic Whether to stop the current music while loading.
*/
public static function loadPlayState(params:PlayStateParams, shouldStopMusic = false):Void
public static function loadPlayState(params:PlayStateParams, shouldStopMusic = false, asSubState = false, ?onConstruct:PlayState->Void):Void
{
Paths.setCurrentLevel(PlayStatePlaylist.campaignId);
var playStateCtor:NextState = () -> new PlayState(params);
var playStateCtor:() -> PlayState = function() {
return new PlayState(params);
};
if (onConstruct != null)
{
playStateCtor = function() {
var result = new PlayState(params);
onConstruct(result);
return result;
};
}
#if NO_PRELOAD_ALL
// Switch to loading state while we load assets (default on HTML5 target).
var loadStateCtor:NextState = () -> new LoadingState(playStateCtor, shouldStopMusic, params);
FlxG.switchState(loadStateCtor);
var loadStateCtor:NextState = function() {
var result = new LoadingState(playStateCtor, shouldStopMusic, params);
@:privateAccess
result.asSubState = asSubState;
return result;
}
if (asSubState)
{
FlxG.state.openSubState(loadStateCtor);
}
else
{
FlxG.switchState(loadStateCtor);
}
#else
// All assets preloaded, switch directly to play state (defualt on other targets).
if (shouldStopMusic && FlxG.sound.music != null)
@ -216,6 +251,34 @@ class LoadingState extends MusicBeatState
params.targetSong.cacheCharts(true);
}
var shouldPreloadLevelAssets:Bool = !(params?.minimalMode ?? false);
if (shouldPreloadLevelAssets) preloadLevelAssets();
if (asSubState)
{
FlxG.state.openSubState(cast playStateCtor());
}
else
{
FlxG.switchState(playStateCtor);
}
#end
}
#if NO_PRELOAD_ALL
static function isSoundLoaded(path:String):Bool
{
return Assets.cache.hasSound(path);
}
static function isLibraryLoaded(library:String):Bool
{
return Assets.getLibrary(library) != null;
}
#else
static function preloadLevelAssets():Void
{
// TODO: This section is a hack! Redo this later when we have a proper asset caching system.
FunkinSprite.preparePurgeCache();
FunkinSprite.cacheTexture(Paths.image('combo'));
@ -248,7 +311,10 @@ class LoadingState extends MusicBeatState
// List all image assets in the level's library.
// This is crude and I want to remove it when we have a proper asset caching system.
// TODO: Get rid of this junk!
var library = openfl.utils.Assets.getLibrary(PlayStatePlaylist.campaignId);
var library = PlayStatePlaylist.campaignId != null ? openfl.utils.Assets.getLibrary(PlayStatePlaylist.campaignId) : null;
if (library == null) return; // We don't need to do anymore precaching.
var assets = library.list(lime.utils.AssetType.IMAGE);
trace('Got ${assets.length} assets: ${assets}');
@ -279,20 +345,6 @@ class LoadingState extends MusicBeatState
// FunkinSprite.cacheAllSongTextures(stage)
FunkinSprite.purgeCache();
FlxG.switchState(playStateCtor);
#end
}
#if NO_PRELOAD_ALL
static function isSoundLoaded(path:String):Bool
{
return Assets.cache.hasSound(path);
}
static function isLibraryLoaded(library:String):Bool
{
return Assets.getLibrary(library) != null;
}
#end

View file

@ -525,4 +525,10 @@ class Constants
* The vertical offset of the strumline from the top edge of the screen.
*/
public static final STRUMLINE_Y_OFFSET:Float = 24;
/**
* The rate at which the camera lerps to its target.
* 0.04 = 4% of distance per frame.
*/
public static final DEFAULT_CAMERA_FOLLOW_RATE:Float = 0.04;
}