diff --git a/CHANGELOG.md b/CHANGELOG.md index 53e981284..22a36fa8e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,14 +4,28 @@ All notable changes will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.5.0] - 2024-08-?? +## [0.5.0] - 2024-09-12 ### Added - Added a new Character Select screen to switch between playable characters in Freeplay - Modding isn't 100% there but we're working on it! - Added Pico as a playable character! Unlock him by completing Weekend 1 (if you haven't already done that) - The songs from Weekend 1 have moved; you must now switch to Pico in Freeplay to access them -- Added ## new Pico remixes! Access them by selecting Pico from in the Character Select screen +- Added 10 new Pico remixes! Access them by selecting Pico from in the Character Select screen + - Bopeebo (Pico Mix) + - Fresh (Pico Mix) + - DadBattle (Pico Mix) + - Spookeez (Pico Mix) + - South (Pico Mix) + - Philly Nice (Pico Mix) + - Blammed (Pico Mix) + - Eggnog (Pico Mix) + - Ugh (Pico Mix) + - Guns (Pico Mix) +- Added 1 new Boyfriend remix! Access it by selecting Pico from in the Character Select screen + - Darnell (BF Mix) - Added 2 new Erect remixes! Access them by switching difficulty on the song + - Cocoa Erect + - Ugh Erect - Implemented support for a new Instrumental Selector in Freeplay - Beating a Pico remix lets you use that instrumental when playing as Boyfriend - Added the first batch of Erect Stages! These graphical overhauls of the original stages will be used when playing Erect remixes and Pico remixes diff --git a/source/funkin/Assets.hx b/source/funkin/Assets.hx new file mode 100644 index 000000000..5351676d4 --- /dev/null +++ b/source/funkin/Assets.hx @@ -0,0 +1,38 @@ +package funkin; + +/** + * A wrapper around `openfl.utils.Assets` which disallows access to the harmful functions. + * Later we'll add Funkin-specific caching to this. + */ +class Assets +{ + public static function getText(path:String):String + { + return openfl.utils.Assets.getText(path); + } + + public static function getMusic(path:String):openfl.media.Sound + { + return openfl.utils.Assets.getMusic(path); + } + + public static function getBitmapData(path:String):openfl.display.BitmapData + { + return openfl.utils.Assets.getBitmapData(path); + } + + public static function getBytes(path:String):haxe.io.Bytes + { + return openfl.utils.Assets.getBytes(path); + } + + public static function exists(path:String, ?type:openfl.utils.AssetType):Bool + { + return openfl.utils.Assets.exists(path, type); + } + + public static function list(type:openfl.utils.AssetType):Array + { + return openfl.utils.Assets.list(type); + } +} diff --git a/source/funkin/data/freeplay/player/PlayerData.hx b/source/funkin/data/freeplay/player/PlayerData.hx index 55657ba46..fa72f497b 100644 --- a/source/funkin/data/freeplay/player/PlayerData.hx +++ b/source/funkin/data/freeplay/player/PlayerData.hx @@ -253,6 +253,8 @@ class PlayerCharSelectData typedef PlayerResultsData = { + var music:PlayerResultsMusicData; + var perfect:Array; var excellent:Array; var great:Array; @@ -260,6 +262,27 @@ typedef PlayerResultsData = var loss:Array; }; +typedef PlayerResultsMusicData = +{ + @:optional + var PERFECT_GOLD:String; + + @:optional + var PERFECT:String; + + @:optional + var EXCELLENT:String; + + @:optional + var GREAT:String; + + @:optional + var GOOD:String; + + @:optional + var SHIT:String; +} + typedef PlayerResultsAnimationData = { /** diff --git a/source/funkin/play/ResultState.hx b/source/funkin/play/ResultState.hx index b1ff69a3a..ec6069ad4 100644 --- a/source/funkin/play/ResultState.hx +++ b/source/funkin/play/ResultState.hx @@ -404,12 +404,12 @@ class ResultState extends MusicBeatSubState // } new FlxTimer().start(rank.getMusicDelay(), _ -> { - if (rank.hasMusicIntro()) + var introMusic:String = Paths.music(getMusicPath(playerCharacter, rank) + '/' + getMusicPath(playerCharacter, rank) + '-intro'); + if (Assets.exists(introMusic)) { // Play the intro music. - var introMusic:String = Paths.music(rank.getMusicPath() + '/' + rank.getMusicPath() + '-intro'); FunkinSound.load(introMusic, 1.0, false, true, true, () -> { - FunkinSound.playMusic(rank.getMusicPath(), + FunkinSound.playMusic(getMusicPath(playerCharacter, rank), { startingVolume: 1.0, overrideExisting: true, @@ -420,7 +420,7 @@ class ResultState extends MusicBeatSubState } else { - FunkinSound.playMusic(rank.getMusicPath(), + FunkinSound.playMusic(getMusicPath(playerCharacter, rank), { startingVolume: 1.0, overrideExisting: true, @@ -441,6 +441,11 @@ class ResultState extends MusicBeatSubState super.create(); } + function getMusicPath(playerCharacter:Null, rank:ScoringRank):String + { + return playerCharacter?.getResultsMusicPath(rank) ?? 'resultsNORMAL'; + } + var rankTallyTimer:Null = null; var clearPercentTarget:Int = 100; var clearPercentLerp:Int = 0; diff --git a/source/funkin/play/scoring/Scoring.hx b/source/funkin/play/scoring/Scoring.hx index 02e5750bc..6c9f9bd97 100644 --- a/source/funkin/play/scoring/Scoring.hx +++ b/source/funkin/play/scoring/Scoring.hx @@ -556,40 +556,6 @@ enum abstract ScoringRank(String) } } - public function getMusicPath():String - { - switch (abstract) - { - case PERFECT_GOLD: - return 'resultsPERFECT'; - case PERFECT: - return 'resultsPERFECT'; - case EXCELLENT: - return 'resultsEXCELLENT'; - case GREAT: - return 'resultsNORMAL'; - case GOOD: - return 'resultsNORMAL'; - case SHIT: - return 'resultsSHIT'; - default: - return 'resultsNORMAL'; - } - } - - public function hasMusicIntro():Bool - { - switch (abstract) - { - case EXCELLENT: - return true; - case SHIT: - return true; - default: - return false; - } - } - public function getFreeplayRankIconAsset():String { switch (abstract) diff --git a/source/funkin/ui/freeplay/FreeplayDJ.hx b/source/funkin/ui/freeplay/FreeplayDJ.hx index b1528d906..3ae0361ce 100644 --- a/source/funkin/ui/freeplay/FreeplayDJ.hx +++ b/source/funkin/ui/freeplay/FreeplayDJ.hx @@ -386,6 +386,7 @@ class FreeplayDJ extends FlxAtlasSprite } else { + FlxG.log.warn("Freeplay character does not have 'charSelect' animation!"); currentState = Confirm; // Call this immediately; otherwise, we get locked out of Character Select. onCharSelectComplete(); diff --git a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx index d4dd7aaa4..09d9fd664 100644 --- a/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx +++ b/source/funkin/ui/freeplay/charselect/PlayableCharacter.hx @@ -119,6 +119,27 @@ class PlayableCharacter implements IRegistryEntry } } + public function getResultsMusicPath(rank:ScoringRank):String + { + switch (rank) + { + case PERFECT_GOLD: + return _data?.results?.music?.PERFECT_GOLD ?? "resultsPERFECT"; + case PERFECT: + return _data?.results?.music?.PERFECT ?? "resultsPERFECT"; + case EXCELLENT: + return _data?.results?.music?.EXCELLENT ?? "resultsEXCELLENT"; + case GREAT: + return _data?.results?.music?.GREAT ?? "resultsNORMAL"; + case GOOD: + return _data?.results?.music?.GOOD ?? "resultsNORMAL"; + case SHIT: + return _data?.results?.music?.SHIT ?? "resultsSHIT"; + default: + return _data?.results?.music?.GOOD ?? "resultsNORMAL"; + } + } + /** * Returns whether this character is unlocked. */