mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-03-21 17:39:20 +00:00
Merge branch 'rewrite/master' into pursnake/character-scale
This commit is contained in:
commit
02be65c383
4
.gitmodules
vendored
4
.gitmodules
vendored
|
@ -1,6 +1,6 @@
|
|||
[submodule "assets"]
|
||||
path = assets
|
||||
url = https://github.com/FunkinCrew/Funkin-assets-secret
|
||||
url = https://github.com/FunkinCrew/Funkin-Assets-secret
|
||||
[submodule "art"]
|
||||
path = art
|
||||
url = https://github.com/FunkinCrew/Funkin-art-secret
|
||||
url = https://github.com/FunkinCrew/Funkin-Art-secret
|
||||
|
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -6,31 +6,52 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## [0.4.0] - 2024-05-??
|
||||
### Added
|
||||
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from
|
||||
- Improvements to the Freeplay screen, with song difficulty ratings and player rank displays.
|
||||
- Reworked the Results screen, with additional animations and audio based on your performance.
|
||||
- 2 new Erect remixes, Eggnog and Satin Panties. Check them out from the Freeplay menu!
|
||||
- Major visual improvements to the Results screen, with additional animations and audio based on your performance.
|
||||
- Major visual improvements to the Freeplay screen, with song difficulty ratings and player rank displays.
|
||||
- Freeplay now plays a preview of songs when you hover over them.
|
||||
- Added a Charter field to the chart format, to allow for crediting the creator of a level's chart.
|
||||
- You can see who charted a song from the Pause menu.
|
||||
- Added a new Scroll Speed chart event to change the note speed mid-song (thanks )
|
||||
### Changed
|
||||
- Tweaked the charts for several songs:
|
||||
- Monster
|
||||
- Winter Horrorland
|
||||
- Stress
|
||||
- Lit Up
|
||||
- Tutorial (increased the note speed slightly)
|
||||
- Senpai (increased the note speed)
|
||||
- Thorns (increased the note speed slightly)
|
||||
- Favorite songs marked in Freeplay are now stored between sessions.
|
||||
- In the event that the game cannot load your save data, it will now perform a backup before clearing it, so that we can try to repair it in the future.
|
||||
- Custom note styles are now properly supported for songs; add new notestyles via JSON, then select it for use from the Chart Editor Metadata toolbox. (thanks Keoiki!)
|
||||
- Improved logic for NoteHitScriptEvents, allowing you to view the hit diff and modify whether a note hit is a combo break (thanks nebulazorua!)
|
||||
- Health icons now support a Winning frame without requiring a spritesheet, simply include a third frame in the icon file. (thanks gamerbross!)
|
||||
- Remember that for more complex behaviors such as animations or transitions, you should use an XML file to define each frame.
|
||||
### Fixed
|
||||
- Fixed a bug where the game would silently fail to load saves on HTML5
|
||||
- Fixed some bugs with the props on the Story Menu not bopping properly
|
||||
- Additional fixes to the Loading bar on HTML5 (thanks lemz1!)
|
||||
- Fixed several bugs with the TitleState, including missing music when returning from the Main Menu (thanks gamerbross!)
|
||||
- Fixed a camera bug in the Main Menu (thanks richTrash21!)
|
||||
- Fixed a bug where changing difficulties in Story mode wouldn't update the score (thanks sectorA!)
|
||||
- Fixed a crash in Freeplay caused by a level referencing an invalid song (thanks gamerbross!)
|
||||
- Fixed a bug where pressing the volume keys would stop the Toy commercial (thanks gamerbross!)
|
||||
- Fixed a bug where the Chart Editor would crash when losing (thanks gamerbross!)
|
||||
- Fixed a bug where the Chart Editor Playtest would crash when losing (thanks gamerbross!)
|
||||
- Fixed a bug where hold notes would display improperly in the Chart Editor when downscroll was enabled for gameplay (thanks gamerbross!)
|
||||
- Fixed a bug where hold notes would be positioned wrong on downscroll (thanks MaybeMaru!)
|
||||
- Removed a large number of unused imports to optimize builds (thanks Ethan-makes-music!)
|
||||
- Improved debug logging for unscripted stages (thanks gamerbross!)
|
||||
- Made improvements to compiling documentation (thanks gedehari!)
|
||||
- Fixed a crash on Linux caused by an old version of hxCodec (thanks Noobz4Life!)
|
||||
- Optimized animation handling for characters (thanks richTrash21!)
|
||||
- Additional bug fixes and optimizations.
|
||||
|
||||
## [0.3.3] - 2024-05-14
|
||||
### Changed
|
||||
- Cleaned up some code in `PlayAnimationSongEvent.hx` (thanks BurgerBalls!)
|
||||
### Fixed
|
||||
- Fix Web Loading Bar (thanks lemz1!)
|
||||
- Fixes to the Loading bar on HTML5 (thanks lemz1!)
|
||||
- Don't allow any more inputs when exiting freeplay (thanks gamerbros!)
|
||||
- Fixed using mouse wheel to scroll on freeplay (thanks JugieNoob!)
|
||||
- Fixed the reset's of the health icons, score, and notes when re-entering gameplay from gameover (thanks ImCodist!)
|
||||
|
@ -38,11 +59,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
- Fixed camera stutter once a wipe transition to the Main Menu completes (thanks ImCodist!)
|
||||
- Fixed an issue where hold note would be invisible for a single frame (thanks ImCodist!)
|
||||
- Fix tween accumulation on title screen when pressing Y multiple times (thanks TheGaloXx!)
|
||||
- Fix for a game over easter egg so you don't accidentally exit it when viewing
|
||||
- Fix a crash when querying FlxG.state in the crash handler
|
||||
- Fix for a game over easter egg so you don't accidentally exit it when viewing
|
||||
- Fix an issue where the Freeplay menu never displays 100% clear
|
||||
- Fix an issue where Weekend 1 Pico attempted to retrieve a missing asset.
|
||||
- Fix an issue where duplicate keybinds would be stoed, potentially causing a crash
|
||||
- Chart debug key now properly returns you to the previous chart editor session if you were playtesting a chart (thanks nebulazorua!)
|
||||
- Hopefully fixed Freeplay crashes on AMD gpu's
|
||||
- Fix a crash on Freeplay found on AMD graphics cards
|
||||
|
||||
## [0.3.2] - 2024-05-03
|
||||
### Added
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<project>
|
||||
<project xmlns="http://lime.openfl.org/project/1.0.4" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://lime.openfl.org/project/1.0.4 http://lime.openfl.org/xsd/project-1.0.4.xsd">
|
||||
<!-- _________________________ Application Settings _________________________ -->
|
||||
<app title="Friday Night Funkin'" file="Funkin" packageName="com.funkin.fnf" package="com.funkin.fnf" main="Main" version="0.3.3" company="ninjamuffin99" />
|
||||
<!--Switch Export with Unique ApplicationID and Icon-->
|
||||
|
@ -28,7 +29,7 @@
|
|||
<set name="BUILD_DIR" value="export/debug" if="debug" />
|
||||
<set name="BUILD_DIR" value="export/release" unless="debug" />
|
||||
<set name="BUILD_DIR" value="export/32bit" if="32bit" />
|
||||
<classpath name="source" />
|
||||
<source path="source" />
|
||||
<assets path="assets/preload" rename="assets" exclude="*.ogg|*.wav" if="web" />
|
||||
<assets path="assets/preload" rename="assets" exclude="*.mp3|*.wav" unless="web" />
|
||||
<define name="PRELOAD_ALL" unless="web" />
|
||||
|
@ -126,6 +127,7 @@
|
|||
<haxelib name="hxCodec" if="desktop" unless="hl" /> <!-- Video playback -->
|
||||
<haxelib name="funkin.vis"/>
|
||||
|
||||
<haxelib name="FlxPartialSound" /> <!-- Loading partial sound data -->
|
||||
|
||||
<haxelib name="json2object" /> <!-- JSON parsing -->
|
||||
<haxelib name="thx.core" /> <!-- General utility library, "the lodash of Haxe" -->
|
||||
|
|
2
art
2
art
|
@ -1 +1 @@
|
|||
Subproject commit 66572f85d826ce2ec1d45468c12733b161237ffa
|
||||
Subproject commit faeba700c5526bd4fd57ccc927d875c82b9d3553
|
2
assets
2
assets
|
@ -1 +1 @@
|
|||
Subproject commit 8fea0bf1fe07b6dd0efb8ecf46dc8091b0177007
|
||||
Subproject commit 0062c05d559ae281ce39f8df3da6efb1f92ca808
|
|
@ -79,7 +79,7 @@
|
|||
{
|
||||
"props": {
|
||||
"ignoreExtern": true,
|
||||
"format": "^[a-z][A-Z][A-Z0-9]*(_[A-Z0-9_]+)*$",
|
||||
"format": "^[a-zA-Z0-9]+(?:_[a-zA-Z0-9]+)*$",
|
||||
"tokens": ["INLINE", "NOTINLINE"]
|
||||
},
|
||||
"type": "ConstantName"
|
||||
|
|
7
hmm.json
7
hmm.json
|
@ -40,6 +40,13 @@
|
|||
"ref": "17e0d59fdbc2b6283a5c0e4df41f1c7f27b71c49",
|
||||
"url": "https://github.com/FunkinCrew/flxanimate"
|
||||
},
|
||||
{
|
||||
"name": "FlxPartialSound",
|
||||
"type": "git",
|
||||
"dir": null,
|
||||
"ref": "f986332ba5ab02abd386ce662578baf04904604a",
|
||||
"url": "https://github.com/FunkinCrew/FlxPartialSound.git"
|
||||
},
|
||||
{
|
||||
"name": "format",
|
||||
"type": "haxelib",
|
||||
|
|
|
@ -219,7 +219,9 @@ class InitState extends FlxState
|
|||
FlxG.switchState(() -> new funkin.play.ResultState(
|
||||
{
|
||||
storyMode: false,
|
||||
title: "CUM SONG",
|
||||
title: "Cum Song Erect by Kawai Sprite",
|
||||
songId: "cum",
|
||||
difficultyId: "nightmare",
|
||||
isNewHighscore: true,
|
||||
scoreData:
|
||||
{
|
||||
|
@ -227,7 +229,7 @@ class InitState extends FlxState
|
|||
tallies:
|
||||
{
|
||||
sick: 130,
|
||||
good: 25,
|
||||
good: 60,
|
||||
bad: 69,
|
||||
shit: 69,
|
||||
missed: 69,
|
||||
|
|
|
@ -123,9 +123,17 @@ class Paths
|
|||
return 'songs:assets/songs/${song.toLowerCase()}/Voices$suffix.${Constants.EXT_SOUND}';
|
||||
}
|
||||
|
||||
public static function inst(song:String, ?suffix:String = ''):String
|
||||
/**
|
||||
* Gets the path to an `Inst.mp3/ogg` song instrumental from songs:assets/songs/`song`/
|
||||
* @param song name of the song to get instrumental for
|
||||
* @param suffix any suffix to add to end of song name, used for `-erect` variants usually
|
||||
* @param withExtension if it should return with the audio file extension `.mp3` or `.ogg`.
|
||||
* @return String
|
||||
*/
|
||||
public static function inst(song:String, ?suffix:String = '', ?withExtension:Bool = true):String
|
||||
{
|
||||
return 'songs:assets/songs/${song.toLowerCase()}/Inst$suffix.${Constants.EXT_SOUND}';
|
||||
var ext:String = withExtension ? '.${Constants.EXT_SOUND}' : '';
|
||||
return 'songs:assets/songs/${song.toLowerCase()}/Inst$suffix$ext';
|
||||
}
|
||||
|
||||
public static function image(key:String, ?library:String):String
|
||||
|
@ -153,3 +161,11 @@ class Paths
|
|||
return FlxAtlasFrames.fromSpriteSheetPacker(image(key, library), file('images/$key.txt', library));
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract PathsFunction(String)
|
||||
{
|
||||
var MUSIC;
|
||||
var INST;
|
||||
var VOICES;
|
||||
var SOUND;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@ import funkin.audio.waveform.WaveformDataParser;
|
|||
import funkin.data.song.SongData.SongMusicData;
|
||||
import funkin.data.song.SongRegistry;
|
||||
import funkin.util.tools.ICloneable;
|
||||
import funkin.util.flixel.sound.FlxPartialSound;
|
||||
import funkin.Paths.PathsFunction;
|
||||
import openfl.Assets;
|
||||
import lime.app.Future;
|
||||
import lime.app.Promise;
|
||||
import openfl.media.SoundMixer;
|
||||
|
||||
#if (openfl >= "8.0.0")
|
||||
|
@ -341,23 +346,76 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
FlxG.log.warn('Tried and failed to find music metadata for $key');
|
||||
}
|
||||
}
|
||||
|
||||
var music = FunkinSound.load(Paths.music('$key/$key'), params?.startingVolume ?? 1.0, params.loop ?? true, false, true);
|
||||
if (music != null)
|
||||
var pathsFunction = params.pathsFunction ?? MUSIC;
|
||||
var suffix = params.suffix ?? '';
|
||||
var pathToUse = switch (pathsFunction)
|
||||
{
|
||||
FlxG.sound.music = music;
|
||||
case MUSIC: Paths.music('$key/$key');
|
||||
case INST: Paths.inst('$key', suffix);
|
||||
default: Paths.music('$key/$key');
|
||||
}
|
||||
|
||||
// Prevent repeat update() and onFocus() calls.
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
var shouldLoadPartial = params.partialParams?.loadPartial ?? false;
|
||||
|
||||
return true;
|
||||
// even if we arent' trying to partial load a song, we want to error out any songs in progress,
|
||||
// so we don't get overlapping music if someone were to load a new song while a partial one is loading!
|
||||
|
||||
emptyPartialQueue();
|
||||
|
||||
if (shouldLoadPartial)
|
||||
{
|
||||
var music = FunkinSound.loadPartial(pathToUse, params.partialParams?.start ?? 0.0, params.partialParams?.end ?? 1.0, params?.startingVolume ?? 1.0,
|
||||
params.loop ?? true, false, false, params.onComplete);
|
||||
|
||||
if (music != null)
|
||||
{
|
||||
partialQueue.push(music);
|
||||
|
||||
@:nullSafety(Off)
|
||||
music.future.onComplete(function(partialMusic:Null<FunkinSound>) {
|
||||
FlxG.sound.music = partialMusic;
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
|
||||
if (FlxG.sound.music != null && params.onLoad != null) params.onLoad();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
var music = FunkinSound.load(pathToUse, params?.startingVolume ?? 1.0, params.loop ?? true, false, true);
|
||||
if (music != null)
|
||||
{
|
||||
FlxG.sound.music = music;
|
||||
|
||||
// Prevent repeat update() and onFocus() calls.
|
||||
FlxG.sound.list.remove(FlxG.sound.music);
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static function emptyPartialQueue():Void
|
||||
{
|
||||
while (partialQueue.length > 0)
|
||||
{
|
||||
@:nullSafety(Off)
|
||||
partialQueue.pop().error("Cancel loading partial sound");
|
||||
}
|
||||
}
|
||||
|
||||
static var partialQueue:Array<Promise<Null<FunkinSound>>> = [];
|
||||
|
||||
/**
|
||||
* Creates a new `FunkinSound` object synchronously.
|
||||
*
|
||||
|
@ -414,6 +472,49 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
|
|||
return sound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Will load a section of a sound file, useful for Freeplay where we don't want to load all the bytes of a song
|
||||
* @param path The path to the sound file
|
||||
* @param start The start time of the sound file
|
||||
* @param end The end time of the sound file
|
||||
* @param volume Volume to start at
|
||||
* @param looped Whether the sound file should loop
|
||||
* @param autoDestroy Whether the sound file should be destroyed after it finishes playing
|
||||
* @param autoPlay Whether the sound file should play immediately
|
||||
* @param onComplete Callback when the sound finishes playing
|
||||
* @param onLoad Callback when the sound finishes loading
|
||||
* @return A FunkinSound object
|
||||
*/
|
||||
public static function loadPartial(path:String, start:Float = 0, end:Float = 1, volume:Float = 1.0, looped:Bool = false, autoDestroy:Bool = false,
|
||||
autoPlay:Bool = true, ?onComplete:Void->Void, ?onLoad:Void->Void):Promise<Null<FunkinSound>>
|
||||
{
|
||||
var promise:lime.app.Promise<Null<FunkinSound>> = new lime.app.Promise<Null<FunkinSound>>();
|
||||
|
||||
// split the path and get only after first :
|
||||
// we are bypassing the openfl/lime asset library fuss
|
||||
path = Paths.stripLibrary(path);
|
||||
|
||||
var soundRequest = FlxPartialSound.partialLoadFromFile(path, start, end);
|
||||
|
||||
if (soundRequest == null)
|
||||
{
|
||||
promise.complete(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
promise.future.onError(function(e) {
|
||||
soundRequest.error("Sound loading was errored or cancelled");
|
||||
});
|
||||
|
||||
soundRequest.future.onComplete(function(partialSound) {
|
||||
var snd = FunkinSound.load(partialSound, volume, looped, autoDestroy, autoPlay, onComplete, onLoad);
|
||||
promise.complete(snd);
|
||||
});
|
||||
}
|
||||
|
||||
return promise;
|
||||
}
|
||||
|
||||
@:nullSafety(Off)
|
||||
public override function destroy():Void
|
||||
{
|
||||
|
@ -474,6 +575,12 @@ typedef FunkinSoundPlayMusicParams =
|
|||
*/
|
||||
var ?startingVolume:Float;
|
||||
|
||||
/**
|
||||
* The suffix of the music file to play. Usually for "-erect" tracks when loading an INST file
|
||||
* @default ``
|
||||
*/
|
||||
var ?suffix:String;
|
||||
|
||||
/**
|
||||
* Whether to override music if a different track is already playing.
|
||||
* @default `false`
|
||||
|
@ -497,4 +604,22 @@ typedef FunkinSoundPlayMusicParams =
|
|||
* @default `true`
|
||||
*/
|
||||
var ?mapTimeChanges:Bool;
|
||||
|
||||
/**
|
||||
* Which Paths function to use to load a song
|
||||
* @default `MUSIC`
|
||||
*/
|
||||
var ?pathsFunction:PathsFunction;
|
||||
|
||||
var ?partialParams:PartialSoundParams;
|
||||
|
||||
var ?onComplete:Void->Void;
|
||||
var ?onLoad:Void->Void;
|
||||
}
|
||||
|
||||
typedef PartialSoundParams =
|
||||
{
|
||||
var loadPartial:Bool;
|
||||
var start:Float;
|
||||
var end:Float;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@ All notable changes to this project 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).
|
||||
|
||||
## [2.2.3]
|
||||
### Added
|
||||
- Added `charter` field to denote authorship of a chart.
|
||||
|
||||
## [2.2.2]
|
||||
### Added
|
||||
- Added `playData.previewStart` and `playData.previewEnd` fields to specify when in the song should the song's audio should be played as a preview in Freeplay.
|
||||
|
|
|
@ -30,6 +30,9 @@ class SongMetadata implements ICloneable<SongMetadata>
|
|||
@:default("Unknown")
|
||||
public var artist:String;
|
||||
|
||||
@:optional
|
||||
public var charter:Null<String> = null;
|
||||
|
||||
@:optional
|
||||
@:default(96)
|
||||
public var divisions:Null<Int>; // Optional field
|
||||
|
@ -53,6 +56,8 @@ class SongMetadata implements ICloneable<SongMetadata>
|
|||
@:default(funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY)
|
||||
public var generatedBy:String;
|
||||
|
||||
@:optional
|
||||
@:default(funkin.data.song.SongData.SongTimeFormat.MILLISECONDS)
|
||||
public var timeFormat:SongTimeFormat;
|
||||
|
||||
public var timeChanges:Array<SongTimeChange>;
|
||||
|
@ -112,14 +117,23 @@ class SongMetadata implements ICloneable<SongMetadata>
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
// Update generatedBy and version before writing.
|
||||
updateVersionToLatest();
|
||||
|
||||
var ignoreNullOptionals = true;
|
||||
var writer = new json2object.JsonWriter<SongMetadata>(ignoreNullOptionals);
|
||||
// I believe @:jignored should be iggnored by the writer?
|
||||
// I believe @:jignored should be ignored by the writer?
|
||||
// var output = this.clone();
|
||||
// output.variation = null; // Not sure how to make a field optional on the reader and ignored on the writer.
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.version = SongRegistry.SONG_METADATA_VERSION;
|
||||
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a string representation suitable for debugging.
|
||||
*/
|
||||
|
@ -368,6 +382,12 @@ class SongMusicData implements ICloneable<SongMusicData>
|
|||
this.variation = variation == null ? Constants.DEFAULT_VARIATION : variation;
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.version = SongRegistry.SONG_MUSIC_DATA_VERSION;
|
||||
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
|
||||
}
|
||||
|
||||
public function clone():SongMusicData
|
||||
{
|
||||
var result:SongMusicData = new SongMusicData(this.songName, this.artist, this.variation);
|
||||
|
@ -600,11 +620,20 @@ class SongChartData implements ICloneable<SongChartData>
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
// Update generatedBy and version before writing.
|
||||
updateVersionToLatest();
|
||||
|
||||
var ignoreNullOptionals = true;
|
||||
var writer = new json2object.JsonWriter<SongChartData>(ignoreNullOptionals);
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.version = SongRegistry.SONG_CHART_DATA_VERSION;
|
||||
this.generatedBy = SongRegistry.DEFAULT_GENERATEDBY;
|
||||
}
|
||||
|
||||
public function clone():SongChartData
|
||||
{
|
||||
// We have to manually perform the deep clone here because Map.deepClone() doesn't work.
|
||||
|
|
|
@ -20,7 +20,7 @@ class SongRegistry extends BaseRegistry<Song, SongMetadata>
|
|||
* Handle breaking changes by incrementing this value
|
||||
* and adding migration to the `migrateStageData()` function.
|
||||
*/
|
||||
public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.2";
|
||||
public static final SONG_METADATA_VERSION:thx.semver.Version = "2.2.3";
|
||||
|
||||
public static final SONG_METADATA_VERSION_RULE:thx.semver.VersionRule = "2.2.x";
|
||||
|
||||
|
|
|
@ -61,10 +61,18 @@ class ChartManifestData
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
// Update generatedBy and version before writing.
|
||||
updateVersionToLatest();
|
||||
|
||||
var writer = new json2object.JsonWriter<ChartManifestData>();
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.version = CHART_MANIFEST_DATA_VERSION;
|
||||
}
|
||||
|
||||
public static function deserialize(contents:String):Null<ChartManifestData>
|
||||
{
|
||||
var parser = new json2object.JsonParser<ChartManifestData>();
|
||||
|
|
|
@ -36,7 +36,7 @@ class FNFLegacyImporter
|
|||
{
|
||||
trace('Migrating song metadata from FNF Legacy.');
|
||||
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', 'Kawai Sprite', 'default');
|
||||
var songMetadata:SongMetadata = new SongMetadata('Import', Constants.DEFAULT_ARTIST, 'default');
|
||||
|
||||
var hadError:Bool = false;
|
||||
|
||||
|
@ -65,7 +65,7 @@ class FNFLegacyImporter
|
|||
|
||||
songMetadata.timeChanges = rebuildTimeChanges(songData);
|
||||
|
||||
songMetadata.playData.characters = new SongCharacterData(songData?.song?.player1 ?? 'bf', 'gf', songData?.song?.player2 ?? 'dad', 'mom');
|
||||
songMetadata.playData.characters = new SongCharacterData(songData?.song?.player1 ?? 'bf', 'gf', songData?.song?.player2 ?? 'dad');
|
||||
|
||||
return songMetadata;
|
||||
}
|
||||
|
|
|
@ -58,9 +58,17 @@ class StageData
|
|||
*/
|
||||
public function serialize(pretty:Bool = true):String
|
||||
{
|
||||
// Update generatedBy and version before writing.
|
||||
updateVersionToLatest();
|
||||
|
||||
var writer = new json2object.JsonWriter<StageData>();
|
||||
return writer.write(this, pretty ? ' ' : null);
|
||||
}
|
||||
|
||||
public function updateVersionToLatest():Void
|
||||
{
|
||||
this.version = StageRegistry.STAGE_DATA_VERSION;
|
||||
}
|
||||
}
|
||||
|
||||
typedef StageDataCharacters =
|
||||
|
|
|
@ -101,6 +101,10 @@ class PauseSubState extends MusicBeatSubState
|
|||
*/
|
||||
static final MUSIC_FINAL_VOLUME:Float = 0.75;
|
||||
|
||||
static final CHARTER_FADE_DELAY:Float = 15.0;
|
||||
|
||||
static final CHARTER_FADE_DURATION:Float = 0.75;
|
||||
|
||||
/**
|
||||
* Defines which pause music to use.
|
||||
*/
|
||||
|
@ -163,6 +167,12 @@ class PauseSubState extends MusicBeatSubState
|
|||
*/
|
||||
var metadataDeaths:FlxText;
|
||||
|
||||
/**
|
||||
* A text object which displays the current song's artist.
|
||||
* Fades to the charter after a period before fading back.
|
||||
*/
|
||||
var metadataArtist:FlxText;
|
||||
|
||||
/**
|
||||
* The actual text objects for the menu entries.
|
||||
*/
|
||||
|
@ -203,6 +213,8 @@ class PauseSubState extends MusicBeatSubState
|
|||
regenerateMenu();
|
||||
|
||||
transitionIn();
|
||||
|
||||
startCharterTimer();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,6 +234,8 @@ class PauseSubState extends MusicBeatSubState
|
|||
public override function destroy():Void
|
||||
{
|
||||
super.destroy();
|
||||
charterFadeTween.cancel();
|
||||
charterFadeTween = null;
|
||||
pauseMusic.stop();
|
||||
}
|
||||
|
||||
|
@ -270,16 +284,25 @@ class PauseSubState extends MusicBeatSubState
|
|||
metadata.scrollFactor.set(0, 0);
|
||||
add(metadata);
|
||||
|
||||
var metadataSong:FlxText = new FlxText(20, 15, FlxG.width - 40, 'Song Name - Artist');
|
||||
var metadataSong:FlxText = new FlxText(20, 15, FlxG.width - 40, 'Song Name');
|
||||
metadataSong.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
|
||||
if (PlayState.instance?.currentChart != null)
|
||||
{
|
||||
metadataSong.text = '${PlayState.instance.currentChart.songName} - ${PlayState.instance.currentChart.songArtist}';
|
||||
metadataSong.text = '${PlayState.instance.currentChart.songName}';
|
||||
}
|
||||
metadataSong.scrollFactor.set(0, 0);
|
||||
metadata.add(metadataSong);
|
||||
|
||||
var metadataDifficulty:FlxText = new FlxText(20, 15 + 32, FlxG.width - 40, 'Difficulty: ');
|
||||
metadataArtist = new FlxText(20, metadataSong.y + 32, FlxG.width - 40, 'Artist: ${Constants.DEFAULT_ARTIST}');
|
||||
metadataArtist.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
|
||||
if (PlayState.instance?.currentChart != null)
|
||||
{
|
||||
metadataArtist.text = 'Artist: ${PlayState.instance.currentChart.songArtist}';
|
||||
}
|
||||
metadataArtist.scrollFactor.set(0, 0);
|
||||
metadata.add(metadataArtist);
|
||||
|
||||
var metadataDifficulty:FlxText = new FlxText(20, metadataArtist.y + 32, FlxG.width - 40, 'Difficulty: ');
|
||||
metadataDifficulty.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
|
||||
if (PlayState.instance?.currentDifficulty != null)
|
||||
{
|
||||
|
@ -288,12 +311,12 @@ class PauseSubState extends MusicBeatSubState
|
|||
metadataDifficulty.scrollFactor.set(0, 0);
|
||||
metadata.add(metadataDifficulty);
|
||||
|
||||
metadataDeaths = new FlxText(20, 15 + 64, FlxG.width - 40, '${PlayState.instance?.deathCounter} Blue Balls');
|
||||
metadataDeaths = new FlxText(20, metadataDifficulty.y + 32, FlxG.width - 40, '${PlayState.instance?.deathCounter} Blue Balls');
|
||||
metadataDeaths.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
|
||||
metadataDeaths.scrollFactor.set(0, 0);
|
||||
metadata.add(metadataDeaths);
|
||||
|
||||
metadataPractice = new FlxText(20, 15 + 96, FlxG.width - 40, 'PRACTICE MODE');
|
||||
metadataPractice = new FlxText(20, metadataDeaths.y + 32, FlxG.width - 40, 'PRACTICE MODE');
|
||||
metadataPractice.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
|
||||
metadataPractice.visible = PlayState.instance?.isPracticeMode ?? false;
|
||||
metadataPractice.scrollFactor.set(0, 0);
|
||||
|
@ -302,6 +325,62 @@ class PauseSubState extends MusicBeatSubState
|
|||
updateMetadataText();
|
||||
}
|
||||
|
||||
var charterFadeTween:Null<FlxTween> = null;
|
||||
|
||||
function startCharterTimer():Void
|
||||
{
|
||||
charterFadeTween = FlxTween.tween(metadataArtist, {alpha: 0.0}, CHARTER_FADE_DURATION,
|
||||
{
|
||||
startDelay: CHARTER_FADE_DELAY,
|
||||
ease: FlxEase.quartOut,
|
||||
onComplete: (_) -> {
|
||||
if (PlayState.instance?.currentChart != null)
|
||||
{
|
||||
metadataArtist.text = 'Charter: ${PlayState.instance.currentChart.charter ?? 'Unknown'}';
|
||||
}
|
||||
else
|
||||
{
|
||||
metadataArtist.text = 'Charter: ${Constants.DEFAULT_CHARTER}';
|
||||
}
|
||||
|
||||
FlxTween.tween(metadataArtist, {alpha: 1.0}, CHARTER_FADE_DURATION,
|
||||
{
|
||||
ease: FlxEase.quartOut,
|
||||
onComplete: (_) -> {
|
||||
startArtistTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startArtistTimer():Void
|
||||
{
|
||||
charterFadeTween = FlxTween.tween(metadataArtist, {alpha: 0.0}, CHARTER_FADE_DURATION,
|
||||
{
|
||||
startDelay: CHARTER_FADE_DELAY,
|
||||
ease: FlxEase.quartOut,
|
||||
onComplete: (_) -> {
|
||||
if (PlayState.instance?.currentChart != null)
|
||||
{
|
||||
metadataArtist.text = 'Artist: ${PlayState.instance.currentChart.songArtist}';
|
||||
}
|
||||
else
|
||||
{
|
||||
metadataArtist.text = 'Artist: ${Constants.DEFAULT_ARTIST}';
|
||||
}
|
||||
|
||||
FlxTween.tween(metadataArtist, {alpha: 1.0}, CHARTER_FADE_DURATION,
|
||||
{
|
||||
ease: FlxEase.quartOut,
|
||||
onComplete: (_) -> {
|
||||
startCharterTimer();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform additional animations to transition the pause menu in when it is first displayed.
|
||||
*/
|
||||
|
|
|
@ -2317,8 +2317,6 @@ class PlayState extends MusicBeatSubState
|
|||
var notesInRange:Array<NoteSprite> = playerStrumline.getNotesMayHit();
|
||||
var holdNotesInRange:Array<SustainTrail> = playerStrumline.getHoldNotesHitOrMissed();
|
||||
|
||||
// If there are notes in range, pressing a key will cause a ghost miss.
|
||||
|
||||
var notesByDirection:Array<Array<NoteSprite>> = [[], [], [], []];
|
||||
|
||||
for (note in notesInRange)
|
||||
|
@ -2340,17 +2338,27 @@ class PlayState extends MusicBeatSubState
|
|||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (Constants.GHOST_TAPPING && (holdNotesInRange.length + notesInRange.length > 0) && notesInDirection.length == 0)
|
||||
else if (Constants.GHOST_TAPPING && (!playerStrumline.mayGhostTap()) && notesInDirection.length == 0)
|
||||
{
|
||||
// Pressed a wrong key with no notes nearby AND with notes in a different direction available.
|
||||
// Pressed a wrong key with notes visible on-screen.
|
||||
// Perform a ghost miss (anti-spam).
|
||||
ghostNoteMiss(input.noteDirection, notesInRange.length > 0);
|
||||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('PENALTY Score: ${songScore}');
|
||||
}
|
||||
else if (notesInDirection.length > 0)
|
||||
else if (notesInDirection.length == 0)
|
||||
{
|
||||
// Press a key with no penalty.
|
||||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
trace('NO PENALTY Score: ${songScore}');
|
||||
}
|
||||
else
|
||||
{
|
||||
// Choose the first note, deprioritizing low priority notes.
|
||||
var targetNote:Null<NoteSprite> = notesInDirection.find((note) -> !note.lowPriority);
|
||||
|
@ -2360,17 +2368,13 @@ class PlayState extends MusicBeatSubState
|
|||
// Judge and hit the note.
|
||||
trace('Hit note! ${targetNote.noteData}');
|
||||
goodNoteHit(targetNote, input);
|
||||
trace('Score: ${songScore}');
|
||||
|
||||
notesInDirection.remove(targetNote);
|
||||
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playConfirm(input.noteDirection);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Play the strumline animation.
|
||||
playerStrumline.playPress(input.noteDirection);
|
||||
}
|
||||
}
|
||||
|
||||
while (inputReleaseQueue.length > 0)
|
||||
|
@ -3118,9 +3122,10 @@ class PlayState extends MusicBeatSubState
|
|||
var res:ResultState = new ResultState(
|
||||
{
|
||||
storyMode: PlayStatePlaylist.isStoryMode,
|
||||
songId: currentChart.song.id,
|
||||
difficultyId: currentDifficulty,
|
||||
title: PlayStatePlaylist.isStoryMode ? ('${PlayStatePlaylist.campaignTitle}') : ('${currentChart.songName} by ${currentChart.songArtist}'),
|
||||
prevScoreData: prevScoreData,
|
||||
difficultyId: currentDifficulty,
|
||||
scoreData:
|
||||
{
|
||||
score: PlayStatePlaylist.isStoryMode ? PlayStatePlaylist.campaignScore : songScore,
|
||||
|
@ -3139,7 +3144,7 @@ class PlayState extends MusicBeatSubState
|
|||
},
|
||||
isNewHighscore: isNewHighscore
|
||||
});
|
||||
res.camera = camHUD;
|
||||
this.persistentDraw = false;
|
||||
openSubState(res);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,11 +2,16 @@ package funkin.play;
|
|||
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.util.FlxTimer;
|
||||
import flixel.tweens.FlxEase;
|
||||
|
||||
class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
|
||||
{
|
||||
public var scoreShit(default, set):Int = 0;
|
||||
|
||||
public var scoreStart:Int = 0;
|
||||
|
||||
function set_scoreShit(val):Int
|
||||
{
|
||||
if (group == null || group.members == null) return val;
|
||||
|
@ -16,7 +21,8 @@ class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
|
|||
|
||||
while (dumbNumb > 0)
|
||||
{
|
||||
group.members[loopNum].digit = dumbNumb % 10;
|
||||
scoreStart += 1;
|
||||
group.members[loopNum].finalDigit = dumbNumb % 10;
|
||||
|
||||
// var funnyNum = group.members[loopNum];
|
||||
// prevNum = group.members[loopNum + 1];
|
||||
|
@ -44,9 +50,15 @@ class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
|
|||
|
||||
public function animateNumbers():Void
|
||||
{
|
||||
for (i in group.members)
|
||||
for (i in group.members.length-scoreStart...group.members.length)
|
||||
{
|
||||
i.playAnim();
|
||||
// if(i.finalDigit == 10) continue;
|
||||
|
||||
new FlxTimer().start((i-1)/24, _ -> {
|
||||
group.members[i].finalDelay = scoreStart - (i-1);
|
||||
group.members[i].playAnim();
|
||||
group.members[i].shuffle();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,12 +83,26 @@ class ResultScore extends FlxTypedSpriteGroup<ScoreNum>
|
|||
class ScoreNum extends FlxSprite
|
||||
{
|
||||
public var digit(default, set):Int = 10;
|
||||
public var finalDigit(default, set):Int = 10;
|
||||
public var glow:Bool = true;
|
||||
|
||||
function set_finalDigit(val):Int
|
||||
{
|
||||
animation.play('GONE', true, false, 0);
|
||||
|
||||
return finalDigit = val;
|
||||
}
|
||||
|
||||
function set_digit(val):Int
|
||||
{
|
||||
if (val >= 0 && animation.curAnim != null && animation.curAnim.name != numToString[val])
|
||||
{
|
||||
animation.play(numToString[val], true, false, 0);
|
||||
if(glow){
|
||||
animation.play(numToString[val], true, false, 0);
|
||||
glow = false;
|
||||
}else{
|
||||
animation.play(numToString[val], true, false, 4);
|
||||
}
|
||||
updateHitbox();
|
||||
|
||||
switch (val)
|
||||
|
@ -107,6 +133,10 @@ class ScoreNum extends FlxSprite
|
|||
animation.play(numToString[digit], true, false, 0);
|
||||
}
|
||||
|
||||
public var shuffleTimer:FlxTimer;
|
||||
public var finalTween:FlxTween;
|
||||
public var finalDelay:Float = 0;
|
||||
|
||||
public var baseY:Float = 0;
|
||||
public var baseX:Float = 0;
|
||||
|
||||
|
@ -114,6 +144,47 @@ class ScoreNum extends FlxSprite
|
|||
"ZERO", "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "DISABLED"
|
||||
];
|
||||
|
||||
function finishShuffleTween():Void{
|
||||
|
||||
var tweenFunction = function(x) {
|
||||
var digitRounded = Math.floor(x);
|
||||
//if(digitRounded == finalDigit) glow = true;
|
||||
digit = digitRounded;
|
||||
};
|
||||
|
||||
finalTween = FlxTween.num(0.0, finalDigit, 23/24, {
|
||||
ease: FlxEase.quadOut,
|
||||
onComplete: function (input) {
|
||||
new FlxTimer().start((finalDelay)/24, _ -> {
|
||||
animation.play(animation.curAnim.name, true, false, 0);
|
||||
});
|
||||
// fuck
|
||||
}
|
||||
}, tweenFunction);
|
||||
}
|
||||
|
||||
|
||||
function shuffleProgress(shuffleTimer:FlxTimer):Void
|
||||
{
|
||||
var tempDigit:Int = digit;
|
||||
tempDigit += 1;
|
||||
if(tempDigit > 9) tempDigit = 0;
|
||||
if(tempDigit < 0) tempDigit = 0;
|
||||
digit = tempDigit;
|
||||
|
||||
if (shuffleTimer.loops > 0 && shuffleTimer.loopsLeft == 0)
|
||||
{
|
||||
//digit = finalDigit;
|
||||
finishShuffleTween();
|
||||
}
|
||||
}
|
||||
|
||||
public function shuffle():Void{
|
||||
var duration:Float = 41/24;
|
||||
var interval:Float = 1/24;
|
||||
shuffleTimer = new FlxTimer().start(interval, shuffleProgress, Std.int(duration / interval));
|
||||
}
|
||||
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
super(x, y);
|
||||
|
@ -130,6 +201,7 @@ class ScoreNum extends FlxSprite
|
|||
}
|
||||
|
||||
animation.addByPrefix('DISABLED', 'DISABLED', 24, false);
|
||||
animation.addByPrefix('GONE', 'GONE', 24, false);
|
||||
|
||||
this.digit = 10;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import funkin.ui.story.StoryMenuState;
|
|||
import funkin.graphics.adobeanimate.FlxAtlasSprite;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.graphics.FunkinSprite;
|
||||
import flixel.effects.FlxFlicker;
|
||||
import flixel.graphics.frames.FlxBitmapFont;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.math.FlxPoint;
|
||||
|
@ -15,12 +16,15 @@ import funkin.ui.freeplay.FreeplayScore;
|
|||
import flixel.text.FlxText;
|
||||
import flixel.util.FlxColor;
|
||||
import flixel.tweens.FlxEase;
|
||||
import funkin.graphics.FunkinCamera;
|
||||
import funkin.ui.freeplay.FreeplayState;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.addons.display.FlxBackdrop;
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.util.FlxGradient;
|
||||
import flixel.util.FlxTimer;
|
||||
import funkin.save.Save;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.save.Save.SaveScoreData;
|
||||
import funkin.graphics.shaders.LeftMaskShader;
|
||||
import funkin.play.components.TallyCounter;
|
||||
|
@ -34,7 +38,7 @@ class ResultState extends MusicBeatSubState
|
|||
{
|
||||
final params:ResultsStateParams;
|
||||
|
||||
final rank:ResultRank;
|
||||
final rank:ScoringRank;
|
||||
final songName:FlxBitmapText;
|
||||
final difficulty:FlxSprite;
|
||||
final clearPercentSmall:ClearPercentCounter;
|
||||
|
@ -52,20 +56,30 @@ class ResultState extends MusicBeatSubState
|
|||
final score:ResultScore;
|
||||
|
||||
var bfPerfect:Null<FlxAtlasSprite> = null;
|
||||
var heartsPerfect:Null<FlxAtlasSprite> = null;
|
||||
var bfExcellent:Null<FlxAtlasSprite> = null;
|
||||
var bfGreat:Null<FlxAtlasSprite> = null;
|
||||
var gfGreat:Null<FlxAtlasSprite> = null;
|
||||
var bfGood:Null<FlxSprite> = null;
|
||||
var gfGood:Null<FlxSprite> = null;
|
||||
var bfShit:Null<FlxAtlasSprite> = null;
|
||||
|
||||
var rankBg:FunkinSprite;
|
||||
final cameraBG:FunkinCamera;
|
||||
final cameraScroll:FunkinCamera;
|
||||
final cameraEverything:FunkinCamera;
|
||||
|
||||
public function new(params:ResultsStateParams)
|
||||
{
|
||||
super();
|
||||
|
||||
this.params = params;
|
||||
|
||||
rank = calculateRank(params);
|
||||
// rank = SHIT;
|
||||
rank = Scoring.calculateRank(params.scoreData) ?? SHIT;
|
||||
|
||||
cameraBG = new FunkinCamera('resultsBG', 0, 0, FlxG.width, FlxG.height);
|
||||
cameraScroll = new FunkinCamera('resultsScroll', 0, 0, FlxG.width, FlxG.height);
|
||||
cameraEverything = new FunkinCamera('resultsEverything', 0, 0, FlxG.width, FlxG.height);
|
||||
|
||||
// We build a lot of this stuff in the constructor, then place it in create().
|
||||
// This prevents having to do `null` checks everywhere.
|
||||
|
@ -84,39 +98,59 @@ class ResultState extends MusicBeatSubState
|
|||
clearPercentSmall.zIndex = 1000;
|
||||
clearPercentSmall.visible = false;
|
||||
|
||||
bgFlash = FlxGradient.createGradientFlxSprite(FlxG.width, FlxG.height, [0xFFFFEB69, 0xFFFFE66A], 90);
|
||||
bgFlash = FlxGradient.createGradientFlxSprite(FlxG.width, FlxG.height, [0xFFFFF1A6, 0xFFFFF1BE], 90);
|
||||
|
||||
resultsAnim = FunkinSprite.createSparrow(-200, -10, "resultScreen/results");
|
||||
|
||||
ratingsPopin = FunkinSprite.createSparrow(-150, 120, "resultScreen/ratingsPopin");
|
||||
ratingsPopin = FunkinSprite.createSparrow(-135, 135, "resultScreen/ratingsPopin");
|
||||
|
||||
scorePopin = FunkinSprite.createSparrow(-180, 520, "resultScreen/scorePopin");
|
||||
scorePopin = FunkinSprite.createSparrow(-180, 515, "resultScreen/scorePopin");
|
||||
|
||||
highscoreNew = new FlxSprite(310, 570);
|
||||
highscoreNew = new FlxSprite(44, 557);
|
||||
|
||||
score = new ResultScore(35, 305, 10, params.scoreData.score);
|
||||
|
||||
rankBg = new FunkinSprite(0, 0);
|
||||
}
|
||||
|
||||
override function create():Void
|
||||
{
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.stop();
|
||||
|
||||
// We need multiple cameras so we can put one at an angle.
|
||||
cameraScroll.angle = -3.8;
|
||||
|
||||
cameraBG.bgColor = FlxColor.MAGENTA;
|
||||
cameraScroll.bgColor = FlxColor.TRANSPARENT;
|
||||
cameraEverything.bgColor = FlxColor.TRANSPARENT;
|
||||
|
||||
FlxG.cameras.add(cameraBG, false);
|
||||
FlxG.cameras.add(cameraScroll, false);
|
||||
FlxG.cameras.add(cameraEverything, false);
|
||||
|
||||
FlxG.cameras.setDefaultDrawTarget(cameraEverything, true);
|
||||
this.camera = cameraEverything;
|
||||
|
||||
// Reset the camera zoom on the results screen.
|
||||
FlxG.camera.zoom = 1.0;
|
||||
|
||||
var bg:FlxSprite = FlxGradient.createGradientFlxSprite(FlxG.width, FlxG.height, [0xFFFECC5C, 0xFFFDC05C], 90);
|
||||
bg.scrollFactor.set();
|
||||
bg.zIndex = 10;
|
||||
bg.cameras = [cameraBG];
|
||||
add(bg);
|
||||
|
||||
bgFlash.scrollFactor.set();
|
||||
bgFlash.visible = false;
|
||||
bgFlash.zIndex = 20;
|
||||
// bgFlash.cameras = [cameraBG];
|
||||
add(bgFlash);
|
||||
|
||||
// The sound system which falls into place behind the score text. Plays every time!
|
||||
var soundSystem:FlxSprite = FunkinSprite.createSparrow(-15, -180, 'resultScreen/soundSystem');
|
||||
soundSystem.animation.addByPrefix("idle", "sound system", 24, false);
|
||||
soundSystem.visible = false;
|
||||
new FlxTimer().start(0.3, _ -> {
|
||||
new FlxTimer().start(8 / 24, _ -> {
|
||||
soundSystem.animation.play("idle");
|
||||
soundSystem.visible = true;
|
||||
});
|
||||
|
@ -126,7 +160,21 @@ class ResultState extends MusicBeatSubState
|
|||
switch (rank)
|
||||
{
|
||||
case PERFECT | PERFECT_GOLD:
|
||||
bfPerfect = new FlxAtlasSprite(370, -180, Paths.animateAtlas("resultScreen/results-bf/resultsPERFECT", "shared"));
|
||||
heartsPerfect = new FlxAtlasSprite(1342, 370, Paths.animateAtlas("resultScreen/results-bf/resultsPERFECT/hearts", "shared"));
|
||||
heartsPerfect.visible = false;
|
||||
heartsPerfect.zIndex = 501;
|
||||
add(heartsPerfect);
|
||||
|
||||
heartsPerfect.anim.onComplete = () -> {
|
||||
if (heartsPerfect != null)
|
||||
{
|
||||
// bfPerfect.anim.curFrame = 137;
|
||||
heartsPerfect.anim.curFrame = 43;
|
||||
heartsPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
};
|
||||
|
||||
bfPerfect = new FlxAtlasSprite(1342, 370, Paths.animateAtlas("resultScreen/results-bf/resultsPERFECT", "shared"));
|
||||
bfPerfect.visible = false;
|
||||
bfPerfect.zIndex = 500;
|
||||
add(bfPerfect);
|
||||
|
@ -134,36 +182,56 @@ class ResultState extends MusicBeatSubState
|
|||
bfPerfect.anim.onComplete = () -> {
|
||||
if (bfPerfect != null)
|
||||
{
|
||||
// bfPerfect.anim.curFrame = 137;
|
||||
bfPerfect.anim.curFrame = 137;
|
||||
bfPerfect.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
};
|
||||
|
||||
case EXCELLENT:
|
||||
bfExcellent = new FlxAtlasSprite(380, -170, Paths.animateAtlas("resultScreen/results-bf/resultsEXCELLENT", "shared"));
|
||||
bfExcellent = new FlxAtlasSprite(1329, 429, Paths.animateAtlas("resultScreen/results-bf/resultsEXCELLENT", "shared"));
|
||||
bfExcellent.visible = false;
|
||||
bfExcellent.zIndex = 500;
|
||||
add(bfExcellent);
|
||||
|
||||
bfExcellent.onAnimationFinish.add((animName) -> {
|
||||
bfExcellent.anim.onComplete = () -> {
|
||||
if (bfExcellent != null)
|
||||
{
|
||||
bfExcellent.playAnimation('Loop Start');
|
||||
bfExcellent.anim.curFrame = 28;
|
||||
bfExcellent.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
case GREAT:
|
||||
bfGreat = new FlxAtlasSprite(640, 200, Paths.animateAtlas("resultScreen/results-bf/resultsGREAT", "shared"));
|
||||
gfGreat = new FlxAtlasSprite(802, 331, Paths.animateAtlas("resultScreen/results-bf/resultsGREAT/gf", "shared"));
|
||||
gfGreat.visible = false;
|
||||
gfGreat.zIndex = 499;
|
||||
add(gfGreat);
|
||||
|
||||
gfGreat.scale.set(0.93, 0.93);
|
||||
|
||||
gfGreat.anim.onComplete = () -> {
|
||||
if (gfGreat != null)
|
||||
{
|
||||
gfGreat.anim.curFrame = 9;
|
||||
gfGreat.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
};
|
||||
|
||||
bfGreat = new FlxAtlasSprite(929, 363, Paths.animateAtlas("resultScreen/results-bf/resultsGREAT/bf", "shared"));
|
||||
bfGreat.visible = false;
|
||||
bfGreat.zIndex = 500;
|
||||
add(bfGreat);
|
||||
|
||||
bfGreat.onAnimationFinish.add((animName) -> {
|
||||
bfGreat.scale.set(0.93, 0.93);
|
||||
|
||||
bfGreat.anim.onComplete = () -> {
|
||||
if (bfGreat != null)
|
||||
{
|
||||
bfGreat.playAnimation('Loop Start');
|
||||
bfGreat.anim.curFrame = 15;
|
||||
bfGreat.anim.play(); // unpauses this anim, since it's on PlayOnce!
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
case GOOD:
|
||||
gfGood = FunkinSprite.createSparrow(625, 325, 'resultScreen/results-bf/resultsGOOD/resultGirlfriendGOOD');
|
||||
|
@ -203,7 +271,7 @@ class ResultState extends MusicBeatSubState
|
|||
});
|
||||
}
|
||||
|
||||
var diffSpr:String = 'dif${params?.difficultyId ?? 'Normal'}';
|
||||
var diffSpr:String = 'diff_${params?.difficultyId ?? 'Normal'}';
|
||||
difficulty.loadGraphic(Paths.image("resultScreen/" + diffSpr));
|
||||
add(difficulty);
|
||||
|
||||
|
@ -223,7 +291,7 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
var blackTopBar:FlxSprite = new FlxSprite().loadGraphic(Paths.image("resultScreen/topBarBlack"));
|
||||
blackTopBar.y = -blackTopBar.height;
|
||||
FlxTween.tween(blackTopBar, {y: 0}, 0.4, {ease: FlxEase.quartOut});
|
||||
FlxTween.tween(blackTopBar, {y: 0}, 7 / 24, {ease: FlxEase.quartOut, startDelay: 3 / 24});
|
||||
blackTopBar.zIndex = 1010;
|
||||
add(blackTopBar);
|
||||
|
||||
|
@ -231,7 +299,7 @@ class ResultState extends MusicBeatSubState
|
|||
resultsAnim.visible = false;
|
||||
resultsAnim.zIndex = 1200;
|
||||
add(resultsAnim);
|
||||
new FlxTimer().start(0.3, _ -> {
|
||||
new FlxTimer().start(6 / 24, _ -> {
|
||||
resultsAnim.visible = true;
|
||||
resultsAnim.animation.play("result");
|
||||
});
|
||||
|
@ -240,7 +308,7 @@ class ResultState extends MusicBeatSubState
|
|||
ratingsPopin.visible = false;
|
||||
ratingsPopin.zIndex = 1200;
|
||||
add(ratingsPopin);
|
||||
new FlxTimer().start(1.0, _ -> {
|
||||
new FlxTimer().start(21 / 24, _ -> {
|
||||
ratingsPopin.visible = true;
|
||||
ratingsPopin.animation.play("idle");
|
||||
});
|
||||
|
@ -249,23 +317,47 @@ class ResultState extends MusicBeatSubState
|
|||
scorePopin.visible = false;
|
||||
scorePopin.zIndex = 1200;
|
||||
add(scorePopin);
|
||||
new FlxTimer().start(1.0, _ -> {
|
||||
new FlxTimer().start(36 / 24, _ -> {
|
||||
scorePopin.visible = true;
|
||||
scorePopin.animation.play("score");
|
||||
scorePopin.animation.finishCallback = anim -> {
|
||||
score.visible = true;
|
||||
score.animateNumbers();
|
||||
};
|
||||
scorePopin.animation.finishCallback = anim -> {};
|
||||
});
|
||||
|
||||
new FlxTimer().start(37 / 24, _ -> {
|
||||
score.visible = true;
|
||||
score.animateNumbers();
|
||||
startRankTallySequence();
|
||||
});
|
||||
|
||||
new FlxTimer().start(rank.getBFDelay(), _ -> {
|
||||
afterRankTallySequence();
|
||||
});
|
||||
|
||||
new FlxTimer().start(rank.getFlashDelay(), _ -> {
|
||||
displayRankText();
|
||||
});
|
||||
|
||||
highscoreNew.frames = Paths.getSparrowAtlas("resultScreen/highscoreNew");
|
||||
highscoreNew.animation.addByPrefix("new", "NEW HIGHSCORE", 24);
|
||||
highscoreNew.animation.addByPrefix("new", "highscoreAnim0", 24, false);
|
||||
highscoreNew.visible = false;
|
||||
highscoreNew.setGraphicSize(Std.int(highscoreNew.width * 0.8));
|
||||
// highscoreNew.setGraphicSize(Std.int(highscoreNew.width * 0.8));
|
||||
highscoreNew.updateHitbox();
|
||||
highscoreNew.zIndex = 1200;
|
||||
add(highscoreNew);
|
||||
|
||||
new FlxTimer().start(rank.getHighscoreDelay(), _ -> {
|
||||
if (params.isNewHighscore ?? false)
|
||||
{
|
||||
highscoreNew.visible = true;
|
||||
highscoreNew.animation.play("new");
|
||||
highscoreNew.animation.finishCallback = _ -> highscoreNew.animation.play("new", true, false, 16);
|
||||
}
|
||||
else
|
||||
{
|
||||
highscoreNew.visible = false;
|
||||
}
|
||||
});
|
||||
|
||||
var hStuf:Int = 50;
|
||||
|
||||
var ratingGrp:FlxTypedGroup<TallyCounter> = new FlxTypedGroup<TallyCounter>();
|
||||
|
@ -283,7 +375,10 @@ class ResultState extends MusicBeatSubState
|
|||
ratingGrp.add(maxCombo);
|
||||
|
||||
hStuf += 2;
|
||||
var extraYOffset:Float = 5;
|
||||
var extraYOffset:Float = 7;
|
||||
|
||||
hStuf += 2;
|
||||
|
||||
var tallySick:TallyCounter = new TallyCounter(230, (hStuf * 5) + extraYOffset, params.scoreData.tallies.sick, 0xFF89E59E);
|
||||
ratingGrp.add(tallySick);
|
||||
|
||||
|
@ -312,20 +407,49 @@ class ResultState extends MusicBeatSubState
|
|||
});
|
||||
}
|
||||
|
||||
ratingsPopin.animation.finishCallback = anim -> {
|
||||
startRankTallySequence();
|
||||
// if (params.isNewHighscore ?? false)
|
||||
// {
|
||||
// highscoreNew.visible = true;
|
||||
// highscoreNew.animation.play("new");
|
||||
// //FlxTween.tween(highscoreNew, {y: highscoreNew.y + 10}, 0.8, {ease: FlxEase.quartOut});
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// highscoreNew.visible = false;
|
||||
// }
|
||||
|
||||
if (params.isNewHighscore ?? false)
|
||||
new FlxTimer().start(rank.getMusicDelay(), _ -> {
|
||||
if (rank.hasMusicIntro())
|
||||
{
|
||||
highscoreNew.visible = true;
|
||||
highscoreNew.animation.play("new");
|
||||
FlxTween.tween(highscoreNew, {y: highscoreNew.y + 10}, 0.8, {ease: FlxEase.quartOut});
|
||||
// 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(),
|
||||
{
|
||||
startingVolume: 1.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: true,
|
||||
loop: rank.shouldMusicLoop()
|
||||
});
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
highscoreNew.visible = false;
|
||||
FunkinSound.playMusic(rank.getMusicPath(),
|
||||
{
|
||||
startingVolume: 1.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: true,
|
||||
loop: rank.shouldMusicLoop()
|
||||
});
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
rankBg.makeSolidColor(FlxG.width, FlxG.height, 0xFF000000);
|
||||
rankBg.zIndex = 99999;
|
||||
add(rankBg);
|
||||
|
||||
rankBg.alpha = 0;
|
||||
|
||||
refresh();
|
||||
|
||||
|
@ -338,6 +462,8 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
function startRankTallySequence():Void
|
||||
{
|
||||
bgFlash.visible = true;
|
||||
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
|
||||
var clearPercentFloat = (params.scoreData.tallies.sick + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100;
|
||||
clearPercentTarget = Math.floor(clearPercentFloat);
|
||||
// Prevent off-by-one errors.
|
||||
|
@ -346,8 +472,8 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
trace('Clear percent target: ' + clearPercentFloat + ', round: ' + clearPercentTarget);
|
||||
|
||||
var clearPercentCounter:ClearPercentCounter = new ClearPercentCounter(FlxG.width / 2 + 300, FlxG.height / 2 - 100, clearPercentLerp);
|
||||
FlxTween.tween(clearPercentCounter, {curNumber: clearPercentTarget}, 1.5,
|
||||
var clearPercentCounter:ClearPercentCounter = new ClearPercentCounter(FlxG.width / 2 + 190, FlxG.height / 2 - 70, clearPercentLerp);
|
||||
FlxTween.tween(clearPercentCounter, {curNumber: clearPercentTarget}, 58 / 24,
|
||||
{
|
||||
ease: FlxEase.quartOut,
|
||||
onUpdate: _ -> {
|
||||
|
@ -362,10 +488,6 @@ class ResultState extends MusicBeatSubState
|
|||
// Play confirm sound.
|
||||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
|
||||
// Flash background.
|
||||
bgFlash.visible = true;
|
||||
FlxTween.tween(bgFlash, {alpha: 0}, 0.4);
|
||||
|
||||
// Just to be sure that the lerp didn't mess things up.
|
||||
clearPercentCounter.curNumber = clearPercentTarget;
|
||||
|
||||
|
@ -374,9 +496,10 @@ class ResultState extends MusicBeatSubState
|
|||
clearPercentCounter.flash(false);
|
||||
});
|
||||
|
||||
displayRankText();
|
||||
// displayRankText();
|
||||
|
||||
new FlxTimer().start(2.0, _ -> {
|
||||
// previously 2.0 seconds
|
||||
new FlxTimer().start(0.25, _ -> {
|
||||
FlxTween.tween(clearPercentCounter, {alpha: 0}, 0.5,
|
||||
{
|
||||
startDelay: 0.5,
|
||||
|
@ -386,7 +509,7 @@ class ResultState extends MusicBeatSubState
|
|||
}
|
||||
});
|
||||
|
||||
afterRankTallySequence();
|
||||
// afterRankTallySequence();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -411,7 +534,6 @@ class ResultState extends MusicBeatSubState
|
|||
{
|
||||
highscoreNew.visible = true;
|
||||
highscoreNew.animation.play("new");
|
||||
FlxTween.tween(highscoreNew, {y: highscoreNew.y + 10}, 0.8, {ease: FlxEase.quartOut});
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -425,16 +547,35 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
function displayRankText():Void
|
||||
{
|
||||
var rankTextVert:FunkinSprite = FunkinSprite.create(FlxG.width - 64, 100, rank.getVerTextAsset());
|
||||
rankTextVert.zIndex = 2000;
|
||||
bgFlash.visible = true;
|
||||
bgFlash.alpha = 1;
|
||||
FlxTween.tween(bgFlash, {alpha: 0}, 14 / 24);
|
||||
|
||||
var rankTextVert:FlxBackdrop = new FlxBackdrop(Paths.image(rank.getVerTextAsset()), Y, 0, 30);
|
||||
rankTextVert.x = FlxG.width - 44;
|
||||
rankTextVert.y = 100;
|
||||
rankTextVert.zIndex = 990;
|
||||
add(rankTextVert);
|
||||
|
||||
for (i in 0...10)
|
||||
FlxFlicker.flicker(rankTextVert, 2 / 24 * 3, 2 / 24, true);
|
||||
|
||||
// Scrolling.
|
||||
new FlxTimer().start(30 / 24, _ -> {
|
||||
rankTextVert.velocity.y = -80;
|
||||
});
|
||||
|
||||
for (i in 0...12)
|
||||
{
|
||||
var rankTextBack:FunkinSprite = FunkinSprite.create(FlxG.width / 2 - 80, 50, rank.getHorTextAsset());
|
||||
rankTextBack.y += (rankTextBack.height * i / 2) + 10;
|
||||
var rankTextBack:FlxBackdrop = new FlxBackdrop(Paths.image(rank.getHorTextAsset()), X, 10, 0);
|
||||
rankTextBack.x = FlxG.width / 2 - 320;
|
||||
rankTextBack.y = 50 + (135 * i / 2) + 10;
|
||||
// rankTextBack.angle = -3.8;
|
||||
rankTextBack.zIndex = 100;
|
||||
rankTextBack.cameras = [cameraScroll];
|
||||
add(rankTextBack);
|
||||
|
||||
// Scrolling.
|
||||
rankTextBack.velocity.x = (i % 2 == 0) ? -7.0 : 7.0;
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
@ -444,28 +585,6 @@ class ResultState extends MusicBeatSubState
|
|||
{
|
||||
showSmallClearPercent();
|
||||
|
||||
FunkinSound.playMusic(rank.getMusicPath(),
|
||||
{
|
||||
startingVolume: 1.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: true,
|
||||
loop: rank.shouldMusicLoop()
|
||||
});
|
||||
|
||||
FlxG.sound.music.onComplete = () -> {
|
||||
if (rank == SHIT)
|
||||
{
|
||||
FunkinSound.playMusic('bluu',
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: true,
|
||||
loop: true
|
||||
});
|
||||
FlxG.sound.music.fadeIn(10.0, 0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
switch (rank)
|
||||
{
|
||||
case PERFECT | PERFECT_GOLD:
|
||||
|
@ -478,7 +597,17 @@ class ResultState extends MusicBeatSubState
|
|||
bfPerfect.visible = true;
|
||||
bfPerfect.playAnimation('');
|
||||
}
|
||||
|
||||
new FlxTimer().start(106 / 24, _ -> {
|
||||
if (heartsPerfect == null)
|
||||
{
|
||||
trace("Could not build heartsPerfect animation!");
|
||||
}
|
||||
else
|
||||
{
|
||||
heartsPerfect.visible = true;
|
||||
heartsPerfect.playAnimation('');
|
||||
}
|
||||
});
|
||||
case EXCELLENT:
|
||||
if (bfExcellent == null)
|
||||
{
|
||||
|
@ -487,9 +616,8 @@ class ResultState extends MusicBeatSubState
|
|||
else
|
||||
{
|
||||
bfExcellent.visible = true;
|
||||
bfExcellent.playAnimation('Intro');
|
||||
bfExcellent.playAnimation('');
|
||||
}
|
||||
|
||||
case GREAT:
|
||||
if (bfGreat == null)
|
||||
{
|
||||
|
@ -498,9 +626,20 @@ class ResultState extends MusicBeatSubState
|
|||
else
|
||||
{
|
||||
bfGreat.visible = true;
|
||||
bfGreat.playAnimation('Intro');
|
||||
bfGreat.playAnimation('');
|
||||
}
|
||||
|
||||
new FlxTimer().start(6 / 24, _ -> {
|
||||
if (gfGreat == null)
|
||||
{
|
||||
trace("Could not build GREAT animation for gf!");
|
||||
}
|
||||
else
|
||||
{
|
||||
gfGreat.visible = true;
|
||||
gfGreat.playAnimation('');
|
||||
}
|
||||
});
|
||||
case SHIT:
|
||||
if (bfShit == null)
|
||||
{
|
||||
|
@ -511,7 +650,6 @@ class ResultState extends MusicBeatSubState
|
|||
bfShit.visible = true;
|
||||
bfShit.playAnimation('Intro');
|
||||
}
|
||||
|
||||
case GOOD:
|
||||
if (bfGood == null)
|
||||
{
|
||||
|
@ -521,7 +659,6 @@ class ResultState extends MusicBeatSubState
|
|||
{
|
||||
bfGood.animation.play('fall');
|
||||
bfGood.visible = true;
|
||||
|
||||
new FlxTimer().start((1 / 24) * 22, _ -> {
|
||||
// plays about 22 frames (at 24fps timing) after bf spawns in
|
||||
if (gfGood != null)
|
||||
|
@ -554,13 +691,13 @@ class ResultState extends MusicBeatSubState
|
|||
{
|
||||
clearPercentSmall.x = (difficulty.x + difficulty.width) + 60;
|
||||
clearPercentSmall.y = -clearPercentSmall.height;
|
||||
FlxTween.tween(clearPercentSmall, {y: 122 - 5}, 0.5, {ease: FlxEase.expoOut, startDelay: 0.8});
|
||||
FlxTween.tween(clearPercentSmall, {y: 122 - 5}, 0.5, {ease: FlxEase.expoOut, startDelay: 0.85});
|
||||
}
|
||||
|
||||
songName.y = -songName.height;
|
||||
var fuckedupnumber = (10) * (songName.text.length / 15);
|
||||
FlxTween.tween(songName, {y: diffYTween - 25 - fuckedupnumber}, 0.5, {ease: FlxEase.expoOut, startDelay: 0.9});
|
||||
songName.x = clearPercentSmall.x + clearPercentSmall.width - 30;
|
||||
songName.x = clearPercentSmall.x + 94;
|
||||
|
||||
new FlxTimer().start(timerLength, _ -> {
|
||||
var tempSpeed = FlxPoint.get(speedOfTween.x, speedOfTween.y);
|
||||
|
@ -588,7 +725,9 @@ class ResultState extends MusicBeatSubState
|
|||
refresh();
|
||||
}
|
||||
|
||||
movingSongStuff = true;
|
||||
new FlxTimer().start(2.5, _ -> {
|
||||
movingSongStuff = true;
|
||||
});
|
||||
}
|
||||
|
||||
var movingSongStuff:Bool = false;
|
||||
|
@ -608,6 +747,79 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
// if(FlxG.keys.justPressed.R){
|
||||
// FlxG.switchState(() -> new funkin.play.ResultState(
|
||||
// {
|
||||
// storyMode: false,
|
||||
// title: "Cum Song Erect by Kawai Sprite",
|
||||
// songId: "cum",
|
||||
// difficultyId: "nightmare",
|
||||
// isNewHighscore: true,
|
||||
// scoreData:
|
||||
// {
|
||||
// score: 1_234_567,
|
||||
// tallies:
|
||||
// {
|
||||
// sick: 200,
|
||||
// good: 0,
|
||||
// bad: 0,
|
||||
// shit: 0,
|
||||
// missed: 0,
|
||||
// combo: 0,
|
||||
// maxCombo: 69,
|
||||
// totalNotesHit: 200,
|
||||
// totalNotes: 200 // 0,
|
||||
// }
|
||||
// },
|
||||
// }));
|
||||
// }
|
||||
|
||||
// if(heartsPerfect != null){
|
||||
// if (FlxG.keys.justPressed.I)
|
||||
// {
|
||||
// heartsPerfect.y -= 1;
|
||||
// trace(heartsPerfect.x, heartsPerfect.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.J)
|
||||
// {
|
||||
// heartsPerfect.x -= 1;
|
||||
// trace(heartsPerfect.x, heartsPerfect.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.L)
|
||||
// {
|
||||
// heartsPerfect.x += 1;
|
||||
// trace(heartsPerfect.x, heartsPerfect.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.K)
|
||||
// {
|
||||
// heartsPerfect.y += 1;
|
||||
// trace(heartsPerfect.x, heartsPerfect.y);
|
||||
// }
|
||||
// }
|
||||
|
||||
// if(bfGreat != null){
|
||||
// if (FlxG.keys.justPressed.W)
|
||||
// {
|
||||
// bfGreat.y -= 1;
|
||||
// trace(bfGreat.x, bfGreat.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.A)
|
||||
// {
|
||||
// bfGreat.x -= 1;
|
||||
// trace(bfGreat.x, bfGreat.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.D)
|
||||
// {
|
||||
// bfGreat.x += 1;
|
||||
// trace(bfGreat.x, bfGreat.y);
|
||||
// }
|
||||
// if (FlxG.keys.justPressed.S)
|
||||
// {
|
||||
// bfGreat.y += 1;
|
||||
// trace(bfGreat.x, bfGreat.y);
|
||||
// }
|
||||
// }
|
||||
|
||||
// maskShaderSongName.swagSprX = songName.x;
|
||||
maskShaderDifficulty.swagSprX = difficulty.x;
|
||||
|
||||
|
@ -635,154 +847,69 @@ class ResultState extends MusicBeatSubState
|
|||
|
||||
if (controls.PAUSE)
|
||||
{
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0}, 0.8);
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1,
|
||||
{
|
||||
onComplete: _ -> {
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4);
|
||||
}
|
||||
});
|
||||
if (FlxG.sound.music != null)
|
||||
{
|
||||
FlxTween.tween(FlxG.sound.music, {volume: 0}, 0.8);
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 3}, 0.1,
|
||||
{
|
||||
onComplete: _ -> {
|
||||
FlxTween.tween(FlxG.sound.music, {pitch: 0.5}, 0.4);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (params.storyMode)
|
||||
{
|
||||
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> new StoryMenuState(sticker)));
|
||||
}
|
||||
else
|
||||
{
|
||||
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build(null, sticker)));
|
||||
var rigged:Bool = true;
|
||||
if (rank > Scoring.calculateRank(params?.prevScoreData)) // if (rigged)
|
||||
{
|
||||
trace('THE RANK IS Higher.....');
|
||||
|
||||
FlxTween.tween(rankBg, {alpha: 1}, 0.5,
|
||||
{
|
||||
ease: FlxEase.expoOut,
|
||||
onComplete: function(_) {
|
||||
FlxG.switchState(FreeplayState.build(
|
||||
{
|
||||
{
|
||||
fromResults:
|
||||
{
|
||||
oldRank: Scoring.calculateRank(params?.prevScoreData),
|
||||
newRank: rank,
|
||||
songId: params.songId,
|
||||
difficultyId: params.difficultyId,
|
||||
playRankAnim: true
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('rank is lower...... and/or equal');
|
||||
openSubState(new funkin.ui.transition.StickerSubState(null, (sticker) -> FreeplayState.build(
|
||||
{
|
||||
{
|
||||
fromResults:
|
||||
{
|
||||
oldRank: null,
|
||||
playRankAnim: false,
|
||||
newRank: rank,
|
||||
songId: params.songId,
|
||||
difficultyId: params.difficultyId
|
||||
}
|
||||
}
|
||||
}, sticker)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.update(elapsed);
|
||||
}
|
||||
|
||||
public static function calculateRank(params:ResultsStateParams):ResultRank
|
||||
{
|
||||
// Perfect (Platinum) is a Sick Full Clear
|
||||
var isPerfectGold = params.scoreData.tallies.sick == params.scoreData.tallies.totalNotes;
|
||||
if (isPerfectGold) return ResultRank.PERFECT_GOLD;
|
||||
|
||||
// Else, use the standard grades
|
||||
|
||||
// Grade % (only good and sick), 1.00 is a full combo
|
||||
var grade = (params.scoreData.tallies.sick + params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes;
|
||||
// Clear % (including bad and shit). 1.00 is a full clear but not a full combo
|
||||
var clear = (params.scoreData.tallies.totalNotesHit) / params.scoreData.tallies.totalNotes;
|
||||
|
||||
if (grade == Constants.RANK_PERFECT_THRESHOLD)
|
||||
{
|
||||
return ResultRank.PERFECT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_EXCELLENT_THRESHOLD)
|
||||
{
|
||||
return ResultRank.EXCELLENT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_GREAT_THRESHOLD)
|
||||
{
|
||||
return ResultRank.GREAT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_GOOD_THRESHOLD)
|
||||
{
|
||||
return ResultRank.GOOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ResultRank.SHIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract ResultRank(String)
|
||||
{
|
||||
var PERFECT_GOLD;
|
||||
var PERFECT;
|
||||
var EXCELLENT;
|
||||
var GREAT;
|
||||
var GOOD;
|
||||
var SHIT;
|
||||
|
||||
public function getMusicPath():String
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultsPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultsPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultsNORMAL';
|
||||
case GREAT:
|
||||
return 'resultsNORMAL';
|
||||
case GOOD:
|
||||
return 'resultsNORMAL';
|
||||
case SHIT:
|
||||
return 'resultsSHIT';
|
||||
default:
|
||||
return 'resultsNORMAL';
|
||||
}
|
||||
}
|
||||
|
||||
public function shouldMusicLoop():Bool
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return true;
|
||||
case PERFECT:
|
||||
return true;
|
||||
case EXCELLENT:
|
||||
return true;
|
||||
case GREAT:
|
||||
return true;
|
||||
case GOOD:
|
||||
return true;
|
||||
case SHIT:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHorTextAsset()
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultScreen/rankText/rankScrollPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultScreen/rankText/rankScrollPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultScreen/rankText/rankScrollEXCELLENT';
|
||||
case GREAT:
|
||||
return 'resultScreen/rankText/rankScrollGREAT';
|
||||
case GOOD:
|
||||
return 'resultScreen/rankText/rankScrollGOOD';
|
||||
case SHIT:
|
||||
return 'resultScreen/rankText/rankScrollLOSS';
|
||||
default:
|
||||
return 'resultScreen/rankText/rankScrollGOOD';
|
||||
}
|
||||
}
|
||||
|
||||
public function getVerTextAsset()
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultScreen/rankText/rankTextPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultScreen/rankText/rankTextPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultScreen/rankText/rankTextEXCELLENT';
|
||||
case GREAT:
|
||||
return 'resultScreen/rankText/rankTextGREAT';
|
||||
case GOOD:
|
||||
return 'resultScreen/rankText/rankTextGOOD';
|
||||
case SHIT:
|
||||
return 'resultScreen/rankText/rankTextLOSS';
|
||||
default:
|
||||
return 'resultScreen/rankText/rankTextGOOD';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef ResultsStateParams =
|
||||
|
@ -797,6 +924,8 @@ typedef ResultsStateParams =
|
|||
*/
|
||||
var title:String;
|
||||
|
||||
var songId:String;
|
||||
|
||||
/**
|
||||
* Whether the displayed score is a new highscore
|
||||
*/
|
||||
|
|
|
@ -35,7 +35,7 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
super(x, y);
|
||||
|
||||
flashShader = new PureColor(FlxColor.WHITE);
|
||||
flashShader.colorSet = true;
|
||||
flashShader.colorSet = false;
|
||||
|
||||
curNumber = startingNumber;
|
||||
|
||||
|
@ -54,22 +54,19 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
*/
|
||||
public function flash(enabled:Bool):Void
|
||||
{
|
||||
for (member in members)
|
||||
{
|
||||
member.shader = enabled ? flashShader : null;
|
||||
}
|
||||
flashShader.colorSet = enabled;
|
||||
}
|
||||
|
||||
var tmr:Float = 0;
|
||||
|
||||
override function update(elapsed:Float)
|
||||
override function update(elapsed:Float):Void
|
||||
{
|
||||
super.update(elapsed);
|
||||
|
||||
if (numberChanged) drawNumbers();
|
||||
}
|
||||
|
||||
function drawNumbers()
|
||||
function drawNumbers():Void
|
||||
{
|
||||
var seperatedScore:Array<Int> = [];
|
||||
var tempCombo:Int = Math.round(curNumber);
|
||||
|
@ -86,7 +83,7 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
|
||||
for (ind => num in seperatedScore)
|
||||
{
|
||||
var digitIndex = ind + 1;
|
||||
var digitIndex:Int = ind + 1;
|
||||
// If there's only one digit, move it to the right
|
||||
// If there's three digits, move them all to the left
|
||||
var digitOffset = (seperatedScore.length == 1) ? 1 : (seperatedScore.length == 3) ? -1 : 0;
|
||||
|
@ -105,6 +102,8 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
// var variant:Bool = (seperatedScore.length % 2 != 0) ? (digitIndex % 2 == 0) : (digitIndex % 2 == 1);
|
||||
var numb:ClearPercentNumber = new ClearPercentNumber(xPos, yPos, num, variant, this.small);
|
||||
numb.scale.set(this.scale.x, this.scale.y);
|
||||
numb.shader = flashShader;
|
||||
numb.visible = true;
|
||||
add(numb);
|
||||
}
|
||||
else
|
||||
|
@ -113,8 +112,13 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
|
|||
// Reset the position of the number
|
||||
members[digitIndex].x = xPos + this.x;
|
||||
members[digitIndex].y = yPos + this.y;
|
||||
members[digitIndex].visible = true;
|
||||
}
|
||||
}
|
||||
for (ind in (seperatedScore.length + 1)...(members.length))
|
||||
{
|
||||
members[ind].visible = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -171,6 +171,20 @@ class Strumline extends FlxSpriteGroup
|
|||
updateNotes();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `true` if no notes are in range of the strumline and the player can spam without penalty.
|
||||
*/
|
||||
public function mayGhostTap():Bool
|
||||
{
|
||||
// TODO: Refine this. Only querying "can be hit" is too tight but "is being rendered" is too loose.
|
||||
// Also, if you just hit a note, there should be a (short) period where this is off so you can't spam.
|
||||
|
||||
// If there are any notes on screen, we can't ghost tap.
|
||||
return notes.members.filter(function(note:NoteSprite) {
|
||||
return note != null && note.alive && !note.hasBeenHit;
|
||||
}).length == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return notes that are within `Constants.HIT_WINDOW` ms of the strumline.
|
||||
* @return An array of `NoteSprite` objects.
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package funkin.play.scoring;
|
||||
|
||||
import funkin.save.Save.SaveScoreData;
|
||||
|
||||
/**
|
||||
* Which system to use when scoring and judging notes.
|
||||
*/
|
||||
|
@ -344,4 +346,303 @@ class Scoring
|
|||
return 'miss';
|
||||
}
|
||||
}
|
||||
|
||||
public static function calculateRank(scoreData:Null<SaveScoreData>):Null<ScoringRank>
|
||||
{
|
||||
if (scoreData?.tallies.totalNotes == 0 || scoreData == null) return null;
|
||||
|
||||
// we can return null here, meaning that the player hasn't actually played and finished the song (thus has no data)
|
||||
if (scoreData.tallies.totalNotes == 0) return null;
|
||||
|
||||
// Perfect (Platinum) is a Sick Full Clear
|
||||
var isPerfectGold = scoreData.tallies.sick == scoreData.tallies.totalNotes;
|
||||
if (isPerfectGold) return ScoringRank.PERFECT_GOLD;
|
||||
|
||||
// Else, use the standard grades
|
||||
|
||||
// Grade % (only good and sick), 1.00 is a full combo
|
||||
var grade = (scoreData.tallies.sick + scoreData.tallies.good) / scoreData.tallies.totalNotes;
|
||||
// Clear % (including bad and shit). 1.00 is a full clear but not a full combo
|
||||
var clear = (scoreData.tallies.totalNotesHit) / scoreData.tallies.totalNotes;
|
||||
|
||||
if (grade == Constants.RANK_PERFECT_THRESHOLD)
|
||||
{
|
||||
return ScoringRank.PERFECT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_EXCELLENT_THRESHOLD)
|
||||
{
|
||||
return ScoringRank.EXCELLENT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_GREAT_THRESHOLD)
|
||||
{
|
||||
return ScoringRank.GREAT;
|
||||
}
|
||||
else if (grade >= Constants.RANK_GOOD_THRESHOLD)
|
||||
{
|
||||
return ScoringRank.GOOD;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ScoringRank.SHIT;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum abstract ScoringRank(String)
|
||||
{
|
||||
var PERFECT_GOLD;
|
||||
var PERFECT;
|
||||
var EXCELLENT;
|
||||
var GREAT;
|
||||
var GOOD;
|
||||
var SHIT;
|
||||
|
||||
@:op(A > B) static function compare(a:Null<ScoringRank>, b:Null<ScoringRank>):Bool
|
||||
{
|
||||
if (a != null && b == null) return true;
|
||||
if (a == null || b == null) return false;
|
||||
|
||||
var temp1:Int = 0;
|
||||
var temp2:Int = 0;
|
||||
|
||||
// temp 1
|
||||
switch (a)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
temp1 = 5;
|
||||
case PERFECT:
|
||||
temp1 = 4;
|
||||
case EXCELLENT:
|
||||
temp1 = 3;
|
||||
case GREAT:
|
||||
temp1 = 2;
|
||||
case GOOD:
|
||||
temp1 = 1;
|
||||
case SHIT:
|
||||
temp1 = 0;
|
||||
default:
|
||||
temp1 = -1;
|
||||
}
|
||||
|
||||
// temp 2
|
||||
switch (b)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
temp2 = 5;
|
||||
case PERFECT:
|
||||
temp2 = 4;
|
||||
case EXCELLENT:
|
||||
temp2 = 3;
|
||||
case GREAT:
|
||||
temp2 = 2;
|
||||
case GOOD:
|
||||
temp2 = 1;
|
||||
case SHIT:
|
||||
temp2 = 0;
|
||||
default:
|
||||
temp2 = -1;
|
||||
}
|
||||
|
||||
if (temp1 > temp2)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delay in seconds
|
||||
*/
|
||||
public function getMusicDelay():Float
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 95/24;
|
||||
case EXCELLENT:
|
||||
return 0;
|
||||
case GREAT:
|
||||
return 5/24;
|
||||
case GOOD:
|
||||
return 3/24;
|
||||
case SHIT:
|
||||
return 2/24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
}
|
||||
|
||||
public function getBFDelay():Float
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 95/24;
|
||||
case EXCELLENT:
|
||||
return 97/24;
|
||||
case GREAT:
|
||||
return 95/24;
|
||||
case GOOD:
|
||||
return 95/24;
|
||||
case SHIT:
|
||||
return 95/24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
}
|
||||
|
||||
public function getFlashDelay():Float
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 129/24;
|
||||
case EXCELLENT:
|
||||
return 122/24;
|
||||
case GREAT:
|
||||
return 109/24;
|
||||
case GOOD:
|
||||
return 107/24;
|
||||
case SHIT:
|
||||
return 186/24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHighscoreDelay():Float
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD | PERFECT:
|
||||
// return 2.5;
|
||||
return 140/24;
|
||||
case EXCELLENT:
|
||||
return 140/24;
|
||||
case GREAT:
|
||||
return 129/24;
|
||||
case GOOD:
|
||||
return 127/24;
|
||||
case SHIT:
|
||||
return 207/24;
|
||||
default:
|
||||
return 3.5;
|
||||
}
|
||||
}
|
||||
|
||||
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():Null<String>
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'PERFECTSICK';
|
||||
case PERFECT:
|
||||
return 'PERFECT';
|
||||
case EXCELLENT:
|
||||
return 'EXCELLENT';
|
||||
case GREAT:
|
||||
return 'GREAT';
|
||||
case GOOD:
|
||||
return 'GOOD';
|
||||
case SHIT:
|
||||
return 'LOSS';
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function shouldMusicLoop():Bool
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD | PERFECT | EXCELLENT | GREAT | GOOD:
|
||||
return true;
|
||||
case SHIT:
|
||||
return false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getHorTextAsset()
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultScreen/rankText/rankScrollPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultScreen/rankText/rankScrollPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultScreen/rankText/rankScrollEXCELLENT';
|
||||
case GREAT:
|
||||
return 'resultScreen/rankText/rankScrollGREAT';
|
||||
case GOOD:
|
||||
return 'resultScreen/rankText/rankScrollGOOD';
|
||||
case SHIT:
|
||||
return 'resultScreen/rankText/rankScrollLOSS';
|
||||
default:
|
||||
return 'resultScreen/rankText/rankScrollGOOD';
|
||||
}
|
||||
}
|
||||
|
||||
public function getVerTextAsset()
|
||||
{
|
||||
switch (abstract)
|
||||
{
|
||||
case PERFECT_GOLD:
|
||||
return 'resultScreen/rankText/rankTextPERFECT';
|
||||
case PERFECT:
|
||||
return 'resultScreen/rankText/rankTextPERFECT';
|
||||
case EXCELLENT:
|
||||
return 'resultScreen/rankText/rankTextEXCELLENT';
|
||||
case GREAT:
|
||||
return 'resultScreen/rankText/rankTextGREAT';
|
||||
case GOOD:
|
||||
return 'resultScreen/rankText/rankTextGOOD';
|
||||
case SHIT:
|
||||
return 'resultScreen/rankText/rankTextLOSS';
|
||||
default:
|
||||
return 'resultScreen/rankText/rankTextGOOD';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,6 +91,12 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
return _metadata.keys().array();
|
||||
}
|
||||
|
||||
// this returns false so that any new song can override this and return true when needed
|
||||
public function isSongNew(currentDifficulty:String):Bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set to false if the song was edited in the charter and should not be saved as a high score.
|
||||
*/
|
||||
|
@ -120,6 +126,18 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
return DEFAULT_ARTIST;
|
||||
}
|
||||
|
||||
/**
|
||||
* The artist of the song.
|
||||
*/
|
||||
public var charter(get, never):String;
|
||||
|
||||
function get_charter():String
|
||||
{
|
||||
if (_data != null) return _data?.charter ?? 'Unknown';
|
||||
if (_metadata.size() > 0) return _metadata.get(Constants.DEFAULT_VARIATION)?.charter ?? 'Unknown';
|
||||
return Constants.DEFAULT_CHARTER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The ID of the song to load.
|
||||
* @param ignoreErrors If false, an exception will be thrown if the song data could not be loaded.
|
||||
|
@ -270,6 +288,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
|
||||
difficulty.songName = metadata.songName;
|
||||
difficulty.songArtist = metadata.artist;
|
||||
difficulty.charter = metadata.charter ?? Constants.DEFAULT_CHARTER;
|
||||
difficulty.timeFormat = metadata.timeFormat;
|
||||
difficulty.divisions = metadata.divisions;
|
||||
difficulty.timeChanges = metadata.timeChanges;
|
||||
|
@ -334,6 +353,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
{
|
||||
difficulty.songName = metadata.songName;
|
||||
difficulty.songArtist = metadata.artist;
|
||||
difficulty.charter = metadata.charter ?? Constants.DEFAULT_CHARTER;
|
||||
difficulty.timeFormat = metadata.timeFormat;
|
||||
difficulty.divisions = metadata.divisions;
|
||||
difficulty.timeChanges = metadata.timeChanges;
|
||||
|
@ -364,7 +384,7 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
|
|||
*/
|
||||
public function getDifficulty(?diffId:String, ?variation:String, ?variations:Array<String>):Null<SongDifficulty>
|
||||
{
|
||||
if (diffId == null) diffId = listDifficulties(variation)[0];
|
||||
if (diffId == null) diffId = listDifficulties(variation, variations)[0];
|
||||
if (variation == null) variation = Constants.DEFAULT_VARIATION;
|
||||
if (variations == null) variations = [variation];
|
||||
|
||||
|
@ -590,6 +610,7 @@ class SongDifficulty
|
|||
|
||||
public var songName:String = Constants.DEFAULT_SONGNAME;
|
||||
public var songArtist:String = Constants.DEFAULT_ARTIST;
|
||||
public var charter:String = Constants.DEFAULT_CHARTER;
|
||||
public var timeFormat:SongTimeFormat = Constants.DEFAULT_TIMEFORMAT;
|
||||
public var divisions:Null<Int> = null;
|
||||
public var looped:Bool = false;
|
||||
|
|
|
@ -852,6 +852,11 @@ class Stage extends FlxSpriteGroup implements IPlayStateScriptedClass implements
|
|||
}
|
||||
}
|
||||
|
||||
public override function toString():String
|
||||
{
|
||||
return 'Stage($id)';
|
||||
}
|
||||
|
||||
static function _fetchData(id:String):Null<StageData>
|
||||
{
|
||||
return StageRegistry.instance.parseEntryDataWithMigration(id, StageRegistry.instance.fetchEntryVersion(id));
|
||||
|
|
|
@ -1,20 +1,22 @@
|
|||
package funkin.save;
|
||||
|
||||
import flixel.util.FlxSave;
|
||||
import funkin.save.migrator.SaveDataMigrator;
|
||||
import thx.semver.Version;
|
||||
import funkin.input.Controls.Device;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.scoring.Scoring.ScoringRank;
|
||||
import funkin.save.migrator.RawSaveData_v1_0_0;
|
||||
import funkin.save.migrator.SaveDataMigrator;
|
||||
import funkin.save.migrator.SaveDataMigrator;
|
||||
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
|
||||
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
|
||||
import thx.semver.Version;
|
||||
import funkin.util.SerializerUtil;
|
||||
import thx.semver.Version;
|
||||
import thx.semver.Version;
|
||||
|
||||
@:nullSafety
|
||||
class Save
|
||||
{
|
||||
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.4";
|
||||
public static final SAVE_DATA_VERSION:thx.semver.Version = "2.0.5";
|
||||
public static final SAVE_DATA_VERSION_RULE:thx.semver.VersionRule = "2.0.x";
|
||||
|
||||
// We load this version's saves from a new save path, to maintain SOME level of backwards compatibility.
|
||||
|
@ -54,6 +56,9 @@ class Save
|
|||
if (data == null) this.data = Save.getDefault();
|
||||
else
|
||||
this.data = data;
|
||||
|
||||
// Make sure the verison number is up to date before we flush.
|
||||
this.data.version = Save.SAVE_DATA_VERSION;
|
||||
}
|
||||
|
||||
public static function getDefault():RawSaveData
|
||||
|
@ -492,6 +497,11 @@ class Save
|
|||
return song.get(difficultyId);
|
||||
}
|
||||
|
||||
public function getSongRank(songId:String, difficultyId:String = 'normal'):Null<ScoringRank>
|
||||
{
|
||||
return Scoring.calculateRank(getSongScore(songId, difficultyId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the score the user achieved for a given song on a given difficulty.
|
||||
*/
|
||||
|
@ -706,7 +716,6 @@ class Save
|
|||
{
|
||||
trace('[SAVE] Found legacy save data, converting...');
|
||||
var gameSave = SaveDataMigrator.migrateFromLegacy(legacySaveData);
|
||||
@:privateAccess
|
||||
FlxG.save.mergeData(gameSave.data, true);
|
||||
}
|
||||
else
|
||||
|
@ -718,13 +727,94 @@ class Save
|
|||
}
|
||||
else
|
||||
{
|
||||
trace('[SAVE] Loaded save data.');
|
||||
@:privateAccess
|
||||
trace('[SAVE] Found existing save data.');
|
||||
var gameSave = SaveDataMigrator.migrate(FlxG.save.data);
|
||||
FlxG.save.mergeData(gameSave.data, true);
|
||||
}
|
||||
}
|
||||
|
||||
public static function archiveBadSaveData(data:Dynamic):Int
|
||||
{
|
||||
// We want to save this somewhere so we can try to recover it for the user in the future!
|
||||
|
||||
final RECOVERY_SLOT_START = 1000;
|
||||
|
||||
return writeToAvailableSlot(RECOVERY_SLOT_START, data);
|
||||
}
|
||||
|
||||
public static function debug_queryBadSaveData():Void
|
||||
{
|
||||
final RECOVERY_SLOT_START = 1000;
|
||||
final RECOVERY_SLOT_END = 1100;
|
||||
var firstBadSaveData = querySlotRange(RECOVERY_SLOT_START, RECOVERY_SLOT_END);
|
||||
if (firstBadSaveData > 0)
|
||||
{
|
||||
trace('[SAVE] Found bad save data in slot ${firstBadSaveData}!');
|
||||
trace('We should look into recovery...');
|
||||
|
||||
trace(haxe.Json.stringify(fetchFromSlotRaw(firstBadSaveData)));
|
||||
}
|
||||
}
|
||||
|
||||
static function fetchFromSlotRaw(slot:Int):Null<Dynamic>
|
||||
{
|
||||
var targetSaveData = new FlxSave();
|
||||
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||
if (targetSaveData.isEmpty()) return null;
|
||||
return targetSaveData.data;
|
||||
}
|
||||
|
||||
static function writeToAvailableSlot(slot:Int, data:Dynamic):Int
|
||||
{
|
||||
trace('[SAVE] Finding slot to write data to (starting with ${slot})...');
|
||||
|
||||
var targetSaveData = new FlxSave();
|
||||
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||
while (!targetSaveData.isEmpty())
|
||||
{
|
||||
// Keep trying to bind to slots until we find an empty slot.
|
||||
trace('[SAVE] Slot ${slot} is taken, continuing...');
|
||||
slot++;
|
||||
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||
}
|
||||
|
||||
trace('[SAVE] Writing data to slot ${slot}...');
|
||||
targetSaveData.mergeData(data, true);
|
||||
|
||||
trace('[SAVE] Data written to slot ${slot}!');
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the given save slot is not empty.
|
||||
* @param slot The slot number to check.
|
||||
* @return Whether the slot is not empty.
|
||||
*/
|
||||
static function querySlot(slot:Int):Bool
|
||||
{
|
||||
var targetSaveData = new FlxSave();
|
||||
targetSaveData.bind('$SAVE_NAME${slot}', SAVE_PATH);
|
||||
return !targetSaveData.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if any of the slots in the given range is not empty.
|
||||
* @param start The starting slot number to check.
|
||||
* @param end The ending slot number to check.
|
||||
* @return The first slot in the range that is not empty, or `-1` if none are.
|
||||
*/
|
||||
static function querySlotRange(start:Int, end:Int):Int
|
||||
{
|
||||
for (i in start...end)
|
||||
{
|
||||
if (querySlot(i))
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static function fetchLegacySaveData():Null<RawSaveData_v1_0_0>
|
||||
{
|
||||
trace("[SAVE] Checking for legacy save data...");
|
||||
|
|
|
@ -5,6 +5,10 @@ All notable changes to this project 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).
|
||||
|
||||
## [2.0.5] - 2024-05-21
|
||||
### Fixed
|
||||
- Resolved an issue where HTML5 wouldn't store the semantic version properly, causing the game to fail to load the save.
|
||||
|
||||
## [2.0.4] - 2024-05-21
|
||||
### Added
|
||||
- `favoriteSongs:Array<String>` to `Save`
|
||||
|
|
|
@ -35,8 +35,9 @@ class SaveDataMigrator
|
|||
else
|
||||
{
|
||||
var message:String = 'Error migrating save data, expected ${Save.SAVE_DATA_VERSION}.';
|
||||
lime.app.Application.current.window.alert(message, "Save Data Failure");
|
||||
trace('[SAVE] ' + message);
|
||||
var slot:Int = Save.archiveBadSaveData(inputData);
|
||||
var fullMessage:String = 'An error occurred migrating your save data.\n${message}\nInvalid data has been moved to save slot ${slot}.';
|
||||
lime.app.Application.current.window.alert(fullMessage, "Save Data Failure");
|
||||
return new Save(Save.getDefault());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ class CreditsDataHandler
|
|||
body: [
|
||||
{line: 'ninjamuffin99'},
|
||||
{line: 'PhantomArcade'},
|
||||
{line: 'KawaiSprite'},
|
||||
{line: 'Kawai Sprite'},
|
||||
{line: 'evilsk8r'},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import flixel.text.FlxText;
|
|||
import flixel.util.FlxColor;
|
||||
import funkin.audio.FunkinSound;
|
||||
import flixel.FlxSprite;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import flixel.group.FlxSpriteGroup;
|
||||
|
||||
/**
|
||||
|
@ -199,7 +200,7 @@ class CreditsState extends MusicBeatState
|
|||
|
||||
function exit():Void
|
||||
{
|
||||
FlxG.switchState(funkin.ui.mainmenu.MainMenuState.new);
|
||||
FlxG.switchState(() -> new MainMenuState());
|
||||
}
|
||||
|
||||
public override function destroy():Void
|
||||
|
|
|
@ -62,7 +62,6 @@ class DebugMenuSubState extends MusicBeatSubState
|
|||
#if sys
|
||||
createItem("OPEN CRASH LOG FOLDER", openLogFolder);
|
||||
#end
|
||||
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y));
|
||||
FlxG.camera.focusOn(new FlxPoint(camFocusPoint.x, camFocusPoint.y + 500));
|
||||
}
|
||||
|
||||
|
|
|
@ -1270,7 +1270,7 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
var result:Null<SongMetadata> = songMetadata.get(selectedVariation);
|
||||
if (result == null)
|
||||
{
|
||||
result = new SongMetadata('DadBattle', 'Kawai Sprite', selectedVariation);
|
||||
result = new SongMetadata('Default Song Name', Constants.DEFAULT_ARTIST, selectedVariation);
|
||||
songMetadata.set(selectedVariation, result);
|
||||
}
|
||||
return result;
|
||||
|
@ -4566,8 +4566,8 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
|
|||
}
|
||||
|
||||
gridGhostHoldNote.visible = true;
|
||||
gridGhostHoldNote.noteData = gridGhostNote.noteData;
|
||||
gridGhostHoldNote.noteDirection = gridGhostNote.noteData.getDirection();
|
||||
gridGhostHoldNote.noteData = currentPlaceNoteData;
|
||||
gridGhostHoldNote.noteDirection = currentPlaceNoteData.getDirection();
|
||||
gridGhostHoldNote.setHeightDirectly(dragLengthPixels, true);
|
||||
|
||||
gridGhostHoldNote.updateHoldNotePosition(renderedHoldNotes);
|
||||
|
|
|
@ -36,6 +36,8 @@ class ChartEditorHoldNoteSprite extends SustainTrail
|
|||
zoom *= 0.7;
|
||||
zoom *= ChartEditorState.GRID_SIZE / Strumline.STRUMLINE_SIZE;
|
||||
|
||||
flipY = false;
|
||||
|
||||
setup();
|
||||
}
|
||||
|
||||
|
|
|
@ -384,17 +384,34 @@ class ChartEditorImportExportHandler
|
|||
if (variationId == '')
|
||||
{
|
||||
var variationMetadata:Null<SongMetadata> = state.songMetadata.get(variation);
|
||||
if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize()));
|
||||
if (variationMetadata != null)
|
||||
{
|
||||
variationMetadata.version = funkin.data.song.SongRegistry.SONG_METADATA_VERSION;
|
||||
variationMetadata.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY;
|
||||
zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata.json', variationMetadata.serialize()));
|
||||
}
|
||||
var variationChart:Null<SongChartData> = state.songChartData.get(variation);
|
||||
if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize()));
|
||||
if (variationChart != null)
|
||||
{
|
||||
variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION;
|
||||
variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY;
|
||||
zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart.json', variationChart.serialize()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var variationMetadata:Null<SongMetadata> = state.songMetadata.get(variation);
|
||||
if (variationMetadata != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json',
|
||||
variationMetadata.serialize()));
|
||||
if (variationMetadata != null)
|
||||
{
|
||||
zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-metadata-$variationId.json', variationMetadata.serialize()));
|
||||
}
|
||||
var variationChart:Null<SongChartData> = state.songChartData.get(variation);
|
||||
if (variationChart != null) zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize()));
|
||||
if (variationChart != null)
|
||||
{
|
||||
variationChart.version = funkin.data.song.SongRegistry.SONG_CHART_DATA_VERSION;
|
||||
variationChart.generatedBy = funkin.data.song.SongRegistry.DEFAULT_GENERATEDBY;
|
||||
zipEntries.push(FileUtil.makeZIPEntry('${state.currentSongId}-chart-$variationId.json', variationChart.serialize()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
{
|
||||
var inputSongName:TextField;
|
||||
var inputSongArtist:TextField;
|
||||
var inputSongCharter:TextField;
|
||||
var inputStage:DropDown;
|
||||
var inputNoteStyle:DropDown;
|
||||
var buttonCharacterPlayer:Button;
|
||||
|
@ -89,6 +90,20 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
}
|
||||
};
|
||||
|
||||
inputSongCharter.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.target.text != null && event.target.text != '';
|
||||
|
||||
if (valid)
|
||||
{
|
||||
inputSongCharter.removeClass('invalid-value');
|
||||
chartEditorState.currentSongMetadata.charter = event.target.text;
|
||||
}
|
||||
else
|
||||
{
|
||||
chartEditorState.currentSongMetadata.charter = null;
|
||||
}
|
||||
};
|
||||
|
||||
inputStage.onChange = function(event:UIEvent) {
|
||||
var valid:Bool = event.data != null && event.data.id != null;
|
||||
|
||||
|
@ -178,6 +193,7 @@ class ChartEditorMetadataToolbox extends ChartEditorBaseToolbox
|
|||
|
||||
inputSongName.value = chartEditorState.currentSongMetadata.songName;
|
||||
inputSongArtist.value = chartEditorState.currentSongMetadata.artist;
|
||||
inputSongCharter.value = chartEditorState.currentSongMetadata.charter;
|
||||
inputStage.value = chartEditorState.currentSongMetadata.playData.stage;
|
||||
inputNoteStyle.value = chartEditorState.currentSongMetadata.playData.noteStyle;
|
||||
inputBPM.value = chartEditorState.currentSongMetadata.timeChanges[0].bpm;
|
||||
|
|
|
@ -66,7 +66,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
add(newAlbumArt);
|
||||
|
||||
difficultyStars = new DifficultyStars(140, 39);
|
||||
difficultyStars.stars.visible = false;
|
||||
difficultyStars.visible = false;
|
||||
add(difficultyStars);
|
||||
}
|
||||
|
||||
|
@ -131,7 +131,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
|
||||
if (exitMovers == null) return;
|
||||
|
||||
exitMovers.set([newAlbumArt],
|
||||
exitMovers.set([newAlbumArt, difficultyStars],
|
||||
{
|
||||
x: FlxG.width,
|
||||
speed: 0.4,
|
||||
|
@ -149,7 +149,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
newAlbumArt.visible = true;
|
||||
newAlbumArt.playAnimation(animNames.get('$albumId-active'), false, false, false);
|
||||
|
||||
difficultyStars.stars.visible = false;
|
||||
difficultyStars.visible = false;
|
||||
new FlxTimer().start(0.75, function(_) {
|
||||
// showTitle();
|
||||
showStars();
|
||||
|
@ -172,6 +172,7 @@ class AlbumRoll extends FlxSpriteGroup
|
|||
*/
|
||||
public function showStars():Void
|
||||
{
|
||||
difficultyStars.stars.visible = true; // true;
|
||||
difficultyStars.visible = true; // true;
|
||||
difficultyStars.flameCheck();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -58,12 +58,24 @@ class CapsuleText extends FlxSpriteGroup
|
|||
function set_clipWidth(value:Int):Int
|
||||
{
|
||||
resetText();
|
||||
if (whiteText.width > value)
|
||||
checkClipWidth(value);
|
||||
return clipWidth = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the text if it's too long, and clips if it is
|
||||
* @param wid
|
||||
*/
|
||||
function checkClipWidth(?wid:Int):Void
|
||||
{
|
||||
if (wid == null) wid = clipWidth;
|
||||
|
||||
if (whiteText.width > wid)
|
||||
{
|
||||
tooLong = true;
|
||||
|
||||
blurredText.clipRect = new FlxRect(0, 0, value, blurredText.height);
|
||||
whiteText.clipRect = new FlxRect(0, 0, value, whiteText.height);
|
||||
blurredText.clipRect = new FlxRect(0, 0, wid, blurredText.height);
|
||||
whiteText.clipRect = new FlxRect(0, 0, wid, whiteText.height);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -72,7 +84,6 @@ class CapsuleText extends FlxSpriteGroup
|
|||
blurredText.clipRect = null;
|
||||
whiteText.clipRect = null;
|
||||
}
|
||||
return clipWidth = value;
|
||||
}
|
||||
|
||||
function set_text(value:String):String
|
||||
|
@ -86,6 +97,7 @@ class CapsuleText extends FlxSpriteGroup
|
|||
|
||||
blurredText.text = value;
|
||||
whiteText.text = value;
|
||||
checkClipWidth();
|
||||
whiteText.textField.filters = [
|
||||
new openfl.filters.GlowFilter(0x00ccff, 1, 5, 5, 210, BitmapFilterQuality.MEDIUM),
|
||||
// new openfl.filters.BlurFilter(5, 5, BitmapFilterQuality.LOW)
|
||||
|
|
|
@ -27,8 +27,8 @@ class DJBoyfriend extends FlxAtlasSprite
|
|||
|
||||
var gotSpooked:Bool = false;
|
||||
|
||||
static final SPOOK_PERIOD:Float = 120.0;
|
||||
static final TV_PERIOD:Float = 180.0;
|
||||
static final SPOOK_PERIOD:Float = 60.0;
|
||||
static final TV_PERIOD:Float = 120.0;
|
||||
|
||||
// Time since dad last SPOOKED you.
|
||||
var timeSinceSpook:Float = 0;
|
||||
|
|
|
@ -19,7 +19,7 @@ class DifficultyStars extends FlxSpriteGroup
|
|||
|
||||
public var stars:FlxAtlasSprite;
|
||||
|
||||
var flames:FreeplayFlames;
|
||||
public var flames:FreeplayFlames;
|
||||
|
||||
var hsvShader:HSVShader;
|
||||
|
||||
|
@ -80,11 +80,16 @@ class DifficultyStars extends FlxSpriteGroup
|
|||
curDifficulty = difficulty - 1;
|
||||
}
|
||||
|
||||
flameCheck();
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
public function flameCheck():Void
|
||||
{
|
||||
if (difficulty > 10) flames.flameCount = difficulty - 10;
|
||||
else
|
||||
flames.flameCount = 0;
|
||||
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
function set_curDifficulty(value:Int):Int
|
||||
|
|
|
@ -6,6 +6,7 @@ import flixel.addons.ui.FlxInputText;
|
|||
import flixel.FlxCamera;
|
||||
import flixel.FlxSprite;
|
||||
import flixel.group.FlxGroup;
|
||||
import funkin.graphics.shaders.GaussianBlurShader;
|
||||
import flixel.group.FlxGroup.FlxTypedGroup;
|
||||
import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
|
||||
import flixel.input.touch.FlxTouch;
|
||||
|
@ -31,9 +32,12 @@ import funkin.graphics.shaders.StrokeShader;
|
|||
import funkin.input.Controls;
|
||||
import funkin.play.PlayStatePlaylist;
|
||||
import funkin.play.song.Song;
|
||||
import funkin.ui.story.Level;
|
||||
import funkin.save.Save;
|
||||
import funkin.save.Save.SaveScoreData;
|
||||
import funkin.ui.AtlasText;
|
||||
import funkin.play.scoring.Scoring;
|
||||
import funkin.play.scoring.Scoring.ScoringRank;
|
||||
import funkin.ui.mainmenu.MainMenuState;
|
||||
import funkin.ui.MusicBeatSubState;
|
||||
import funkin.ui.transition.LoadingState;
|
||||
|
@ -42,6 +46,7 @@ import funkin.util.MathUtil;
|
|||
import lime.utils.Assets;
|
||||
import flixel.tweens.misc.ShakeTween;
|
||||
import funkin.effects.IntervalShake;
|
||||
import funkin.ui.freeplay.SongMenuItem.FreeplayRank;
|
||||
|
||||
/**
|
||||
* Parameters used to initialize the FreeplayState.
|
||||
|
@ -49,6 +54,39 @@ import funkin.effects.IntervalShake;
|
|||
typedef FreeplayStateParams =
|
||||
{
|
||||
?character:String,
|
||||
|
||||
?fromResults:FromResultsParams,
|
||||
};
|
||||
|
||||
/**
|
||||
* A set of parameters for transitioning to the FreeplayState from the ResultsState.
|
||||
*/
|
||||
typedef FromResultsParams =
|
||||
{
|
||||
/**
|
||||
* The previous rank the song hand, if any. Null if it had no score before.
|
||||
*/
|
||||
var ?oldRank:ScoringRank;
|
||||
|
||||
/**
|
||||
* Whether or not to play the rank animation on returning to freeplay.
|
||||
*/
|
||||
var playRankAnim:Bool;
|
||||
|
||||
/**
|
||||
* The new rank the song has.
|
||||
*/
|
||||
var newRank:ScoringRank;
|
||||
|
||||
/**
|
||||
* The song ID to play the animation on.
|
||||
*/
|
||||
var songId:String;
|
||||
|
||||
/**
|
||||
* The difficulty ID to play the animation on.
|
||||
*/
|
||||
var difficultyId:String;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -73,6 +111,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
/**
|
||||
* For the audio preview, the duration of the fade-out effect.
|
||||
*
|
||||
*/
|
||||
public static final FADE_OUT_DURATION:Float = 0.25;
|
||||
|
||||
|
@ -160,10 +199,21 @@ class FreeplayState extends MusicBeatSubState
|
|||
var bgDad:FlxSprite;
|
||||
var cardGlow:FlxSprite;
|
||||
|
||||
var fromResultsParams:Null<FromResultsParams> = null;
|
||||
|
||||
var prepForNewRank:Bool = false;
|
||||
|
||||
public function new(?params:FreeplayStateParams, ?stickers:StickerSubState)
|
||||
{
|
||||
currentCharacter = params?.character ?? Constants.DEFAULT_CHARACTER;
|
||||
|
||||
fromResultsParams = params?.fromResults;
|
||||
|
||||
if (fromResultsParams?.playRankAnim == true)
|
||||
{
|
||||
prepForNewRank = true;
|
||||
}
|
||||
|
||||
if (stickers != null)
|
||||
{
|
||||
stickerSubState = stickers;
|
||||
|
@ -200,11 +250,14 @@ class FreeplayState extends MusicBeatSubState
|
|||
isDebug = true;
|
||||
#end
|
||||
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
if (prepForNewRank == false)
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
}
|
||||
|
||||
// Add a null entry that represents the RANDOM option
|
||||
songs.push(null);
|
||||
|
@ -212,10 +265,24 @@ class FreeplayState extends MusicBeatSubState
|
|||
// programmatically adds the songs via LevelRegistry and SongRegistry
|
||||
for (levelId in LevelRegistry.instance.listSortedLevelIds())
|
||||
{
|
||||
for (songId in LevelRegistry.instance.parseEntryData(levelId).songs)
|
||||
var level:Level = LevelRegistry.instance.fetchEntry(levelId);
|
||||
|
||||
if (level == null)
|
||||
{
|
||||
trace('[WARN] Could not find level with id (${levelId})');
|
||||
continue;
|
||||
}
|
||||
|
||||
for (songId in level.getSongs())
|
||||
{
|
||||
var song:Song = SongRegistry.instance.fetchEntry(songId);
|
||||
|
||||
if (song == null)
|
||||
{
|
||||
trace('[WARN] Could not find song with id (${songId})');
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only display songs which actually have available difficulties for the current character.
|
||||
var displayedVariations = song.getVariationsByCharId(currentCharacter);
|
||||
var availableDifficultiesForSong:Array<String> = song.listDifficulties(displayedVariations, false);
|
||||
|
@ -587,6 +654,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
cardGlow.visible = true;
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.45, {ease: FlxEase.sineOut});
|
||||
|
||||
if (prepForNewRank)
|
||||
{
|
||||
rankAnimStart(fromResultsParams);
|
||||
}
|
||||
});
|
||||
|
||||
generateSongList(null, false);
|
||||
|
@ -613,6 +685,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
FlxG.cameras.add(rankCamera, false);
|
||||
rankBg.cameras = [rankCamera];
|
||||
rankBg.alpha = 0;
|
||||
|
||||
if (prepForNewRank)
|
||||
{
|
||||
rankCamera.fade(0xFF000000, 0, false, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
var currentFilter:SongFilter = null;
|
||||
|
@ -680,6 +757,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
randomCapsule.alpha = 0;
|
||||
randomCapsule.songText.visible = false;
|
||||
randomCapsule.favIcon.visible = false;
|
||||
randomCapsule.favIconBlurred.visible = false;
|
||||
randomCapsule.ranking.visible = false;
|
||||
randomCapsule.blurredRanking.visible = false;
|
||||
randomCapsule.initJumpIn(0, force);
|
||||
|
@ -702,13 +780,12 @@ class FreeplayState extends MusicBeatSubState
|
|||
funnyMenu.capsule.alpha = 0.5;
|
||||
funnyMenu.songText.visible = false;
|
||||
funnyMenu.favIcon.visible = tempSongs[i].isFav;
|
||||
funnyMenu.favIconBlurred.visible = tempSongs[i].isFav;
|
||||
funnyMenu.hsvShader = hsvShader;
|
||||
|
||||
funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45);
|
||||
|
||||
funnyMenu.forcePosition();
|
||||
|
||||
funnyMenu.checkClip();
|
||||
funnyMenu.forcePosition();
|
||||
|
||||
grpCapsules.add(funnyMenu);
|
||||
}
|
||||
|
@ -729,6 +806,13 @@ class FreeplayState extends MusicBeatSubState
|
|||
*/
|
||||
public function sortSongs(songsToFilter:Array<FreeplaySongData>, songFilter:SongFilter):Array<FreeplaySongData>
|
||||
{
|
||||
var filterAlphabetically = function(a:FreeplaySongData, b:FreeplaySongData):Int {
|
||||
if (a?.songName.toLowerCase() < b?.songName.toLowerCase()) return -1;
|
||||
else if (a?.songName.toLowerCase() > b?.songName.toLowerCase()) return 1;
|
||||
else
|
||||
return 0;
|
||||
};
|
||||
|
||||
switch (songFilter.filterType)
|
||||
{
|
||||
case REGEXP:
|
||||
|
@ -743,6 +827,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
return filterRegexp.match(str.songName);
|
||||
});
|
||||
|
||||
songsToFilter.sort(filterAlphabetically);
|
||||
|
||||
case STARTSWITH:
|
||||
// extra note: this is essentially a "search"
|
||||
|
||||
|
@ -757,25 +843,93 @@ class FreeplayState extends MusicBeatSubState
|
|||
if (str == null) return true; // Random
|
||||
return str.isFav;
|
||||
});
|
||||
|
||||
songsToFilter.sort(filterAlphabetically);
|
||||
|
||||
default:
|
||||
// return all on default
|
||||
}
|
||||
|
||||
return songsToFilter;
|
||||
}
|
||||
|
||||
function rankAnimStart()
|
||||
var sparks:FlxSprite;
|
||||
var sparksADD:FlxSprite;
|
||||
|
||||
function rankAnimStart(fromResults:Null<FromResultsParams>):Void
|
||||
{
|
||||
busy = true;
|
||||
grpCapsules.members[curSelected].sparkle.alpha = 0;
|
||||
// grpCapsules.members[curSelected].forcePosition();
|
||||
|
||||
if (fromResults != null)
|
||||
{
|
||||
rememberedSongId = fromResults.songId;
|
||||
rememberedDifficulty = fromResults.difficultyId;
|
||||
changeSelection();
|
||||
changeDiff();
|
||||
}
|
||||
|
||||
dj.fistPump();
|
||||
// rankCamera.fade(FlxColor.BLACK, 0.5, true);
|
||||
rankCamera.fade(0xFF000000, 0.5, true, null, true);
|
||||
FlxG.sound.music.volume = 0;
|
||||
if (FlxG.sound.music != null) FlxG.sound.music.volume = 0;
|
||||
rankBg.alpha = 1;
|
||||
|
||||
originalPos.x = grpCapsules.members[curSelected].x;
|
||||
originalPos.y = grpCapsules.members[curSelected].y;
|
||||
if (fromResults?.oldRank != null)
|
||||
{
|
||||
grpCapsules.members[curSelected].fakeRanking.rank = fromResults.oldRank;
|
||||
grpCapsules.members[curSelected].fakeBlurredRanking.rank = fromResults.oldRank;
|
||||
|
||||
grpCapsules.members[curSelected].ranking.alpha = 0;
|
||||
grpCapsules.members[curSelected].blurredRanking.alpha = 0;
|
||||
sparks = new FlxSprite(0, 0);
|
||||
sparks.frames = Paths.getSparrowAtlas('freeplay/sparks');
|
||||
sparks.animation.addByPrefix('sparks', 'sparks', 24, false);
|
||||
sparks.visible = false;
|
||||
sparks.blend = BlendMode.ADD;
|
||||
sparks.setPosition(517, 134);
|
||||
sparks.scale.set(0.5, 0.5);
|
||||
add(sparks);
|
||||
sparks.cameras = [rankCamera];
|
||||
|
||||
sparksADD = new FlxSprite(0, 0);
|
||||
sparksADD.visible = false;
|
||||
sparksADD.frames = Paths.getSparrowAtlas('freeplay/sparksadd');
|
||||
sparksADD.animation.addByPrefix('sparks add', 'sparks add', 24, false);
|
||||
sparksADD.setPosition(498, 116);
|
||||
sparksADD.blend = BlendMode.ADD;
|
||||
sparksADD.scale.set(0.5, 0.5);
|
||||
add(sparksADD);
|
||||
sparksADD.cameras = [rankCamera];
|
||||
|
||||
switch (fromResults.oldRank)
|
||||
{
|
||||
case SHIT:
|
||||
sparksADD.color = 0xFF6044FF;
|
||||
case GOOD:
|
||||
sparksADD.color = 0xFFEF8764;
|
||||
case GREAT:
|
||||
sparksADD.color = 0xFFEAF6FF;
|
||||
case EXCELLENT:
|
||||
sparksADD.color = 0xFFFDCB42;
|
||||
case PERFECT:
|
||||
sparksADD.color = 0xFFFF58B4;
|
||||
case PERFECT_GOLD:
|
||||
sparksADD.color = 0xFFFFB619;
|
||||
}
|
||||
// sparksADD.color = sparks.color;
|
||||
}
|
||||
|
||||
grpCapsules.members[curSelected].doLerp = false;
|
||||
|
||||
// originalPos.x = grpCapsules.members[curSelected].x;
|
||||
// originalPos.y = grpCapsules.members[curSelected].y;
|
||||
|
||||
originalPos.x = 320.488;
|
||||
originalPos.y = 235.6;
|
||||
trace(originalPos);
|
||||
|
||||
grpCapsules.members[curSelected].ranking.visible = false;
|
||||
grpCapsules.members[curSelected].blurredRanking.visible = false;
|
||||
|
||||
rankCamera.zoom = 1.85;
|
||||
FlxTween.tween(rankCamera, {"zoom": 1.8}, 0.6, {ease: FlxEase.sineIn});
|
||||
|
@ -784,40 +938,57 @@ class FreeplayState extends MusicBeatSubState
|
|||
FlxTween.tween(funnyCam, {"zoom": 1.1}, 0.6, {ease: FlxEase.sineIn});
|
||||
|
||||
grpCapsules.members[curSelected].cameras = [rankCamera];
|
||||
grpCapsules.members[curSelected].targetPos.set((FlxG.width / 2) - (grpCapsules.members[curSelected].width / 2),
|
||||
// grpCapsules.members[curSelected].targetPos.set((FlxG.width / 2) - (grpCapsules.members[curSelected].width / 2),
|
||||
// (FlxG.height / 2) - (grpCapsules.members[curSelected].height / 2));
|
||||
|
||||
grpCapsules.members[curSelected].setPosition((FlxG.width / 2) - (grpCapsules.members[curSelected].width / 2),
|
||||
(FlxG.height / 2) - (grpCapsules.members[curSelected].height / 2));
|
||||
|
||||
new FlxTimer().start(0.5, _ -> {
|
||||
grpCapsules.members[curSelected].doLerp = false;
|
||||
rankDisplayNew();
|
||||
rankDisplayNew(fromResults);
|
||||
});
|
||||
}
|
||||
|
||||
function rankDisplayNew()
|
||||
function rankDisplayNew(fromResults:Null<FromResultsParams>):Void
|
||||
{
|
||||
grpCapsules.members[curSelected].ranking.alpha = 1;
|
||||
grpCapsules.members[curSelected].blurredRanking.alpha = 1;
|
||||
|
||||
grpCapsules.members[curSelected].ranking.visible = true;
|
||||
grpCapsules.members[curSelected].blurredRanking.visible = true;
|
||||
grpCapsules.members[curSelected].ranking.scale.set(20, 20);
|
||||
grpCapsules.members[curSelected].blurredRanking.scale.set(20, 20);
|
||||
// var tempr:Int = FlxG.random.int(0, 4);
|
||||
|
||||
// grpCapsules.members[curSelected].ranking.rank = tempr;
|
||||
grpCapsules.members[curSelected].ranking.animation.play(grpCapsules.members[curSelected].ranking.animation.curAnim.name, true);
|
||||
grpCapsules.members[curSelected].ranking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
// grpCapsules.members[curSelected].ranking.animation.curAnim.name, true);
|
||||
|
||||
FlxTween.tween(grpCapsules.members[curSelected].ranking, {"scale.x": 1, "scale.y": 1}, 0.1);
|
||||
|
||||
grpCapsules.members[curSelected].blurredRanking.animation.play(grpCapsules.members[curSelected].blurredRanking.animation.curAnim.name, true);
|
||||
grpCapsules.members[curSelected].blurredRanking.animation.play(fromResults.newRank.getFreeplayRankIconAsset(), true);
|
||||
FlxTween.tween(grpCapsules.members[curSelected].blurredRanking, {"scale.x": 1, "scale.y": 1}, 0.1);
|
||||
|
||||
new FlxTimer().start(0.1, _ -> {
|
||||
trace(grpCapsules.members[curSelected].ranking.rank);
|
||||
switch (grpCapsules.members[curSelected].tempr)
|
||||
// trace(grpCapsules.members[curSelected].ranking.rank);
|
||||
if (fromResults?.oldRank != null)
|
||||
{
|
||||
case 0:
|
||||
grpCapsules.members[curSelected].fakeRanking.visible = false;
|
||||
grpCapsules.members[curSelected].fakeBlurredRanking.visible = false;
|
||||
|
||||
sparks.visible = true;
|
||||
sparksADD.visible = true;
|
||||
sparks.animation.play('sparks', true);
|
||||
sparksADD.animation.play('sparks add', true);
|
||||
|
||||
sparks.animation.finishCallback = anim -> {
|
||||
sparks.visible = false;
|
||||
sparksADD.visible = false;
|
||||
};
|
||||
}
|
||||
|
||||
switch (fromResultsParams?.newRank)
|
||||
{
|
||||
case SHIT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/rankinbad'));
|
||||
case 4:
|
||||
case PERFECT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/rankinperfect'));
|
||||
case 5:
|
||||
case PERFECT_GOLD:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/rankinperfect'));
|
||||
default:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/rankinnormal'));
|
||||
|
@ -846,31 +1017,31 @@ class FreeplayState extends MusicBeatSubState
|
|||
});
|
||||
|
||||
new FlxTimer().start(0.6, _ -> {
|
||||
rankAnimSlam();
|
||||
rankAnimSlam(fromResults);
|
||||
// IntervalShake.shake(grpCapsules.members[curSelected].capsule, 0.3, 1 / 30, 0, 0.3, FlxEase.quartIn);
|
||||
});
|
||||
}
|
||||
|
||||
function rankAnimSlam()
|
||||
function rankAnimSlam(fromResultsParams:Null<FromResultsParams>)
|
||||
{
|
||||
// FlxTween.tween(rankCamera, {"zoom": 1.9}, 0.5, {ease: FlxEase.backOut});
|
||||
FlxTween.tween(rankBg, {alpha: 0}, 0.5, {ease: FlxEase.expoIn});
|
||||
|
||||
// FlxTween.tween(grpCapsules.members[curSelected], {angle: 5}, 0.5, {ease: FlxEase.backIn});
|
||||
|
||||
switch (grpCapsules.members[curSelected].tempr)
|
||||
switch (fromResultsParams?.newRank)
|
||||
{
|
||||
case 0:
|
||||
case SHIT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/loss'));
|
||||
case 1:
|
||||
case GOOD:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/good'));
|
||||
case 2:
|
||||
case GREAT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/great'));
|
||||
case 3:
|
||||
case EXCELLENT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/excellent'));
|
||||
case 4:
|
||||
case PERFECT:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/perfect'));
|
||||
case 5:
|
||||
case PERFECT_GOLD:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/perfect'));
|
||||
default:
|
||||
FunkinSound.playOnce(Paths.sound('ranks/loss'));
|
||||
|
@ -880,7 +1051,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
new FlxTimer().start(0.5, _ -> {
|
||||
funnyCam.shake(0.0045, 0.35);
|
||||
|
||||
if (grpCapsules.members[curSelected].tempr == 0)
|
||||
if (fromResultsParams?.newRank == SHIT)
|
||||
{
|
||||
dj.pumpFistBad();
|
||||
}
|
||||
|
@ -915,6 +1086,11 @@ class FreeplayState extends MusicBeatSubState
|
|||
IntervalShake.shake(capsule, 0.6, 1 / 24, 0.12, 0, FlxEase.quadOut, function(_) {
|
||||
capsule.doLerp = true;
|
||||
capsule.cameras = [funnyCam];
|
||||
|
||||
// NOW we can interact with the menu
|
||||
busy = false;
|
||||
grpCapsules.members[curSelected].sparkle.alpha = 0.7;
|
||||
playCurSongPreview(capsule);
|
||||
}, null);
|
||||
|
||||
// FlxTween.tween(capsule, {"targetPos.x": capsule.targetPos.x - 50}, 0.6,
|
||||
|
@ -963,7 +1139,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
new FlxTimer().start(2, _ -> {
|
||||
// dj.fistPump();
|
||||
FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
|
||||
prepForNewRank = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -981,7 +1157,10 @@ class FreeplayState extends MusicBeatSubState
|
|||
var spamTimer:Float = 0;
|
||||
var spamming:Bool = false;
|
||||
|
||||
var busy:Bool = false; // Set to true once the user has pressed enter to select a song.
|
||||
/**
|
||||
* If true, disable interaction with the interface.
|
||||
*/
|
||||
var busy:Bool = false;
|
||||
|
||||
var originalPos:FlxPoint = new FlxPoint();
|
||||
|
||||
|
@ -989,43 +1168,66 @@ class FreeplayState extends MusicBeatSubState
|
|||
{
|
||||
super.update(elapsed);
|
||||
|
||||
#if debug
|
||||
if (FlxG.keys.justPressed.T)
|
||||
{
|
||||
rankAnimStart();
|
||||
rankAnimStart(fromResultsParams);
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.H)
|
||||
{
|
||||
rankDisplayNew();
|
||||
}
|
||||
// if (FlxG.keys.justPressed.H)
|
||||
// {
|
||||
// rankDisplayNew(fromResultsParams);
|
||||
// }
|
||||
|
||||
// if (FlxG.keys.justPressed.G)
|
||||
// {
|
||||
// rankAnimSlam(fromResultsParams);
|
||||
// }
|
||||
|
||||
if (FlxG.keys.justPressed.G)
|
||||
{
|
||||
rankAnimSlam();
|
||||
sparks.y -= 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.V)
|
||||
{
|
||||
sparks.x -= 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.N)
|
||||
{
|
||||
sparks.x += 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.B)
|
||||
{
|
||||
sparks.y += 2;
|
||||
trace(sparks.x, sparks.y);
|
||||
}
|
||||
|
||||
if (FlxG.keys.justPressed.I)
|
||||
{
|
||||
confirmTextGlow.y -= 1;
|
||||
trace(confirmTextGlow.x, confirmTextGlow.y);
|
||||
sparksADD.y -= 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.J)
|
||||
{
|
||||
confirmTextGlow.x -= 1;
|
||||
trace(confirmTextGlow.x, confirmTextGlow.y);
|
||||
sparksADD.x -= 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.L)
|
||||
{
|
||||
confirmTextGlow.x += 1;
|
||||
trace(confirmTextGlow.x, confirmTextGlow.y);
|
||||
sparksADD.x += 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
if (FlxG.keys.justPressed.K)
|
||||
{
|
||||
confirmTextGlow.y += 1;
|
||||
trace(confirmTextGlow.x, confirmTextGlow.y);
|
||||
sparksADD.y += 2;
|
||||
trace(sparksADD.x, sparksADD.y);
|
||||
}
|
||||
#end
|
||||
|
||||
if (FlxG.keys.justPressed.F)
|
||||
if (FlxG.keys.justPressed.F && !busy)
|
||||
{
|
||||
var targetSong = grpCapsules.members[curSelected]?.songData;
|
||||
if (targetSong != null)
|
||||
|
@ -1034,24 +1236,52 @@ class FreeplayState extends MusicBeatSubState
|
|||
var isFav = targetSong.toggleFavorite();
|
||||
if (isFav)
|
||||
{
|
||||
FlxTween.tween(grpCapsules.members[realShit], {angle: 360}, 0.4,
|
||||
grpCapsules.members[realShit].favIcon.visible = true;
|
||||
grpCapsules.members[realShit].favIconBlurred.visible = true;
|
||||
grpCapsules.members[realShit].favIcon.animation.play('fav');
|
||||
grpCapsules.members[realShit].favIconBlurred.animation.play('fav');
|
||||
FunkinSound.playOnce(Paths.sound('fav'), 1);
|
||||
grpCapsules.members[realShit].checkClip();
|
||||
grpCapsules.members[realShit].selected = grpCapsules.members[realShit].selected; // set selected again, so it can run it's getter function to initialize movement
|
||||
busy = true;
|
||||
|
||||
grpCapsules.members[realShit].doLerp = false;
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y - 5}, 0.1, {ease: FlxEase.expoOut});
|
||||
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y + 5}, 0.1,
|
||||
{
|
||||
ease: FlxEase.elasticOut,
|
||||
onComplete: _ -> {
|
||||
grpCapsules.members[realShit].favIcon.visible = true;
|
||||
grpCapsules.members[realShit].favIcon.animation.play('fav');
|
||||
ease: FlxEase.expoIn,
|
||||
startDelay: 0.1,
|
||||
onComplete: function(_) {
|
||||
grpCapsules.members[realShit].doLerp = true;
|
||||
busy = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
grpCapsules.members[realShit].favIcon.animation.play('fav', false, true);
|
||||
new FlxTimer().start((1 / 24) * 14, _ -> {
|
||||
grpCapsules.members[realShit].favIcon.animation.play('fav', true, true, 9);
|
||||
grpCapsules.members[realShit].favIconBlurred.animation.play('fav', true, true, 9);
|
||||
FunkinSound.playOnce(Paths.sound('unfav'), 1);
|
||||
new FlxTimer().start(0.2, _ -> {
|
||||
grpCapsules.members[realShit].favIcon.visible = false;
|
||||
grpCapsules.members[realShit].favIconBlurred.visible = false;
|
||||
grpCapsules.members[realShit].checkClip();
|
||||
});
|
||||
new FlxTimer().start((1 / 24) * 24, _ -> {
|
||||
FlxTween.tween(grpCapsules.members[realShit], {angle: 0}, 0.4, {ease: FlxEase.elasticOut});
|
||||
});
|
||||
|
||||
busy = true;
|
||||
grpCapsules.members[realShit].doLerp = false;
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y + 5}, 0.1, {ease: FlxEase.expoOut});
|
||||
|
||||
FlxTween.tween(grpCapsules.members[realShit], {y: grpCapsules.members[realShit].y - 5}, 0.1,
|
||||
{
|
||||
ease: FlxEase.expoIn,
|
||||
startDelay: 0.1,
|
||||
onComplete: function(_) {
|
||||
grpCapsules.members[realShit].doLerp = true;
|
||||
busy = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1255,6 +1485,24 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
var longestTimer:Float = 0;
|
||||
|
||||
// FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut});
|
||||
FlxTween.color(pinkBack, 0.25, 0xFFFFD863, 0xFFFFD0D5, {ease: FlxEase.quadOut});
|
||||
|
||||
cardGlow.visible = true;
|
||||
cardGlow.alpha = 1;
|
||||
cardGlow.scale.set(1, 1);
|
||||
FlxTween.tween(cardGlow, {alpha: 0, "scale.x": 1.2, "scale.y": 1.2}, 0.25, {ease: FlxEase.sineOut});
|
||||
|
||||
orangeBackShit.visible = false;
|
||||
alsoOrangeLOL.visible = false;
|
||||
|
||||
moreWays.visible = false;
|
||||
funnyScroll.visible = false;
|
||||
txtNuts.visible = false;
|
||||
funnyScroll2.visible = false;
|
||||
moreWays2.visible = false;
|
||||
funnyScroll3.visible = false;
|
||||
|
||||
for (grpSpr in exitMovers.keys())
|
||||
{
|
||||
var moveData:MoveData = exitMovers.get(grpSpr);
|
||||
|
@ -1299,6 +1547,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
|
||||
close();
|
||||
}
|
||||
else
|
||||
|
@ -1394,6 +1643,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
{
|
||||
songCapsule.songData.currentDifficulty = currentDifficulty;
|
||||
songCapsule.init(null, null, songCapsule.songData);
|
||||
songCapsule.checkClip();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1485,6 +1735,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
FunkinSound.playOnce(Paths.sound('confirmMenu'));
|
||||
dj.confirm();
|
||||
|
||||
grpCapsules.members[curSelected].forcePosition();
|
||||
grpCapsules.members[curSelected].songText.flickerText();
|
||||
|
||||
// FlxTween.color(bgDad, 0.33, 0xFFFFFFFF, 0xFF555555, {ease: FlxEase.quadOut});
|
||||
|
@ -1522,6 +1773,8 @@ class FreeplayState extends MusicBeatSubState
|
|||
funnyScroll3.visible = false;
|
||||
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
FunkinSound.emptyPartialQueue();
|
||||
|
||||
Paths.setCurrentLevel(cap.songData.levelId);
|
||||
LoadingState.loadPlayState(
|
||||
{
|
||||
|
@ -1564,7 +1817,7 @@ class FreeplayState extends MusicBeatSubState
|
|||
|
||||
function changeSelection(change:Int = 0):Void
|
||||
{
|
||||
FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
if (!prepForNewRank) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
|
||||
|
||||
var prevSelected:Int = curSelected;
|
||||
|
||||
|
@ -1605,49 +1858,58 @@ class FreeplayState extends MusicBeatSubState
|
|||
if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure
|
||||
}
|
||||
|
||||
if (grpCapsules.countLiving() > 0)
|
||||
if (grpCapsules.countLiving() > 0 && !prepForNewRank)
|
||||
{
|
||||
if (curSelected == 0)
|
||||
{
|
||||
FunkinSound.playMusic('freeplayRandom',
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: true
|
||||
});
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Stream the instrumental of the selected song?
|
||||
var didReplace:Bool = FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
if (didReplace)
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
}
|
||||
}
|
||||
playCurSongPreview(daSongCapsule);
|
||||
grpCapsules.members[curSelected].selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
public function playCurSongPreview(daSongCapsule:SongMenuItem):Void
|
||||
{
|
||||
if (curSelected == 0)
|
||||
{
|
||||
FunkinSound.playMusic('freeplayRandom',
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
});
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.8);
|
||||
}
|
||||
else
|
||||
{
|
||||
var potentiallyErect:String = (currentDifficulty == "erect") || (currentDifficulty == "nightmare") ? "-erect" : "";
|
||||
FunkinSound.playMusic(daSongCapsule.songData.songId,
|
||||
{
|
||||
startingVolume: 0.0,
|
||||
overrideExisting: true,
|
||||
restartTrack: false,
|
||||
pathsFunction: INST,
|
||||
suffix: potentiallyErect,
|
||||
partialParams:
|
||||
{
|
||||
loadPartial: true,
|
||||
start: 0.05,
|
||||
end: 0.25
|
||||
},
|
||||
onLoad: function() {
|
||||
FlxG.sound.music.fadeIn(2, 0, 0.4);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build an instance of `FreeplayState` that is above the `MainMenuState`.
|
||||
* @return The MainMenuState with the FreeplayState as a substate.
|
||||
*/
|
||||
public static function build(?params:FreeplayStateParams, ?stickers:StickerSubState):MusicBeatState
|
||||
{
|
||||
var result = new MainMenuState();
|
||||
var result:MainMenuState;
|
||||
if (params?.fromResults.playRankAnim) result = new MainMenuState(true);
|
||||
else
|
||||
result = new MainMenuState(false);
|
||||
|
||||
result.openSubState(new FreeplayState(params, stickers));
|
||||
result.persistentUpdate = false;
|
||||
|
@ -1750,6 +2012,8 @@ class FreeplaySongData
|
|||
*/
|
||||
public var isFav:Bool = false;
|
||||
|
||||
public var isNew:Bool = false;
|
||||
|
||||
var song:Song;
|
||||
|
||||
public var levelId(default, null):String = '';
|
||||
|
@ -1765,12 +2029,12 @@ class FreeplaySongData
|
|||
|
||||
public var currentDifficulty(default, set):String = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
public var scoringRank:Null<ScoringRank> = null;
|
||||
|
||||
var displayedVariations:Array<String> = [Constants.DEFAULT_VARIATION];
|
||||
|
||||
function set_currentDifficulty(value:String):String
|
||||
{
|
||||
if (currentDifficulty == value) return value;
|
||||
|
||||
currentDifficulty = value;
|
||||
updateValues(displayedVariations);
|
||||
return value;
|
||||
|
@ -1809,7 +2073,7 @@ class FreeplaySongData
|
|||
|
||||
function updateValues(variations:Array<String>):Void
|
||||
{
|
||||
this.songDifficulties = song.listDifficulties(variations, false, false);
|
||||
this.songDifficulties = song.listDifficulties(null, variations, false, false);
|
||||
if (!this.songDifficulties.contains(currentDifficulty)) currentDifficulty = Constants.DEFAULT_DIFFICULTY;
|
||||
|
||||
var songDifficulty:SongDifficulty = song.getDifficulty(currentDifficulty, variations);
|
||||
|
@ -1827,6 +2091,10 @@ class FreeplaySongData
|
|||
{
|
||||
this.albumId = songDifficulty.album;
|
||||
}
|
||||
|
||||
this.scoringRank = Save.instance.getSongRank(songId, currentDifficulty);
|
||||
|
||||
this.isNew = song.isSongNew(currentDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@ import funkin.graphics.FunkinSprite;
|
|||
import flixel.tweens.FlxEase;
|
||||
import flixel.tweens.FlxTween;
|
||||
import flixel.addons.effects.FlxTrail;
|
||||
import funkin.play.scoring.Scoring.ScoringRank;
|
||||
import funkin.save.Save;
|
||||
import funkin.save.Save.SaveScoreData;
|
||||
import flixel.util.FlxColor;
|
||||
|
||||
class SongMenuItem extends FlxSpriteGroup
|
||||
|
@ -37,10 +40,15 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
public var selected(default, set):Bool;
|
||||
|
||||
public var songText:CapsuleText;
|
||||
public var favIconBlurred:FlxSprite;
|
||||
public var favIcon:FlxSprite;
|
||||
|
||||
public var ranking:FreeplayRank;
|
||||
public var blurredRanking:FreeplayRank;
|
||||
|
||||
public var fakeRanking:FreeplayRank;
|
||||
public var fakeBlurredRanking:FreeplayRank;
|
||||
|
||||
var ranks:Array<String> = ["fail", "average", "great", "excellent", "perfect", "perfectsick"];
|
||||
|
||||
public var targetPos:FlxPoint = new FlxPoint();
|
||||
|
@ -70,7 +78,9 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
|
||||
var impactThing:FunkinSprite;
|
||||
|
||||
public var tempr:Int;
|
||||
public var sparkle:FlxSprite;
|
||||
|
||||
var sparkleTimer:FlxTimer;
|
||||
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
|
@ -106,7 +116,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
newText.animation.play('newAnim', true);
|
||||
newText.setGraphicSize(Std.int(newText.width * 0.9));
|
||||
|
||||
newText.visible = false;
|
||||
// newText.visible = false;
|
||||
|
||||
add(newText);
|
||||
|
||||
|
@ -132,15 +142,35 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
// doesn't get added, simply is here to help with visibility of things for the pop in!
|
||||
grpHide = new FlxGroup();
|
||||
|
||||
var rank:String = FlxG.random.getObject(ranks);
|
||||
fakeRanking = new FreeplayRank(420, 41);
|
||||
add(fakeRanking);
|
||||
|
||||
tempr = FlxG.random.int(0, 5);
|
||||
ranking = new FreeplayRank(420, 41, tempr);
|
||||
fakeBlurredRanking = new FreeplayRank(fakeRanking.x, fakeRanking.y);
|
||||
fakeBlurredRanking.shader = new GaussianBlurShader(1);
|
||||
add(fakeBlurredRanking);
|
||||
|
||||
fakeRanking.visible = false;
|
||||
fakeBlurredRanking.visible = false;
|
||||
|
||||
ranking = new FreeplayRank(420, 41);
|
||||
add(ranking);
|
||||
|
||||
blurredRanking = new FreeplayRank(ranking.x, ranking.y, tempr);
|
||||
blurredRanking = new FreeplayRank(ranking.x, ranking.y);
|
||||
blurredRanking.shader = new GaussianBlurShader(1);
|
||||
add(blurredRanking);
|
||||
|
||||
sparkle = new FlxSprite(ranking.x, ranking.y);
|
||||
sparkle.frames = Paths.getSparrowAtlas('freeplay/sparkle');
|
||||
sparkle.animation.addByPrefix('sparkle', 'sparkle', 24, false);
|
||||
sparkle.animation.play('sparkle', true);
|
||||
sparkle.scale.set(0.8, 0.8);
|
||||
sparkle.blend = BlendMode.ADD;
|
||||
|
||||
sparkle.visible = false;
|
||||
sparkle.alpha = 0.7;
|
||||
|
||||
add(sparkle);
|
||||
|
||||
// ranking.loadGraphic(Paths.image('freeplay/ranks/' + rank));
|
||||
// ranking.scale.x = ranking.scale.y = realScaled;
|
||||
// ranking.alpha = 0.75;
|
||||
|
@ -179,6 +209,16 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
add(pixelIcon);
|
||||
grpHide.add(pixelIcon);
|
||||
|
||||
favIconBlurred = new FlxSprite(380, 40);
|
||||
favIconBlurred.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
||||
favIconBlurred.animation.addByPrefix('fav', 'favorite heart', 24, false);
|
||||
favIconBlurred.animation.play('fav');
|
||||
favIconBlurred.setGraphicSize(50, 50);
|
||||
favIconBlurred.blend = BlendMode.ADD;
|
||||
favIconBlurred.shader = new GaussianBlurShader(1.2);
|
||||
favIconBlurred.visible = false;
|
||||
add(favIconBlurred);
|
||||
|
||||
favIcon = new FlxSprite(380, 40);
|
||||
favIcon.frames = Paths.getSparrowAtlas('freeplay/favHeart');
|
||||
favIcon.animation.addByPrefix('fav', 'favorite heart', 24, false);
|
||||
|
@ -196,6 +236,13 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
setVisibleGrp(false);
|
||||
}
|
||||
|
||||
function sparkleEffect(timer:FlxTimer):Void
|
||||
{
|
||||
sparkle.setPosition(FlxG.random.float(ranking.x - 20, ranking.x + 3), FlxG.random.float(ranking.y - 29, ranking.y + 4));
|
||||
sparkle.animation.play('sparkle', true);
|
||||
sparkleTimer = new FlxTimer().start(FlxG.random.float(1.2, 4.5), sparkleEffect);
|
||||
}
|
||||
|
||||
// no way to grab weeks rn, so this needs to be done :/
|
||||
// negative values mean weekends
|
||||
function checkWeek(name:String):Void
|
||||
|
@ -255,7 +302,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
var clipType:Int = 0;
|
||||
|
||||
if (ranking.visible == true) clipType += 1;
|
||||
if (favIcon.visible == true) clipType += 1;
|
||||
if (favIcon.visible == true) clipType = 2;
|
||||
switch (clipType)
|
||||
{
|
||||
case 2:
|
||||
|
@ -268,8 +315,6 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
|
||||
function updateBPM(newBPM:Int):Void
|
||||
{
|
||||
trace(newBPM);
|
||||
|
||||
var shiftX:Float = 191;
|
||||
var tempShift:Float = 0;
|
||||
|
||||
|
@ -317,7 +362,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
|
||||
var evilTrail:FlxTrail;
|
||||
|
||||
public function fadeAnim()
|
||||
public function fadeAnim():Void
|
||||
{
|
||||
impactThing = new FunkinSprite(0, 0);
|
||||
impactThing.frames = capsule.frames;
|
||||
|
@ -344,19 +389,19 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
});
|
||||
add(evilTrail);
|
||||
|
||||
switch (tempr)
|
||||
switch (ranking.rank)
|
||||
{
|
||||
case 0:
|
||||
case SHIT:
|
||||
evilTrail.color = 0xFF6044FF;
|
||||
case 1:
|
||||
case GOOD:
|
||||
evilTrail.color = 0xFFEF8764;
|
||||
case 2:
|
||||
case GREAT:
|
||||
evilTrail.color = 0xFFEAF6FF;
|
||||
case 3:
|
||||
case EXCELLENT:
|
||||
evilTrail.color = 0xFFFDCB42;
|
||||
case 4:
|
||||
case PERFECT:
|
||||
evilTrail.color = 0xFFFF58B4;
|
||||
case 5:
|
||||
case PERFECT_GOLD:
|
||||
evilTrail.color = 0xFFFFB619;
|
||||
}
|
||||
}
|
||||
|
@ -375,7 +420,7 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
switch (i)
|
||||
{
|
||||
case 0:
|
||||
if (newRating > 10)
|
||||
if (newRating < 10)
|
||||
{
|
||||
bigNumbers[i].digit = 0;
|
||||
}
|
||||
|
@ -393,6 +438,21 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
// diffRatingSprite.visible = false;
|
||||
}
|
||||
|
||||
function updateScoringRank(newRank:Null<ScoringRank>):Void
|
||||
{
|
||||
if (sparkleTimer != null) sparkleTimer.cancel();
|
||||
sparkle.visible = false;
|
||||
|
||||
this.ranking.rank = newRank;
|
||||
this.blurredRanking.rank = newRank;
|
||||
|
||||
if (newRank == PERFECT_GOLD)
|
||||
{
|
||||
sparkleTimer = new FlxTimer().start(1, sparkleEffect);
|
||||
sparkle.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
function set_hsvShader(value:HSVShader):HSVShader
|
||||
{
|
||||
this.hsvShader = value;
|
||||
|
@ -441,6 +501,8 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
if (songData?.songCharacter != null) setCharacter(songData.songCharacter);
|
||||
updateBPM(Std.int(songData?.songStartingBpm) ?? 0);
|
||||
updateDifficultyRating(songData?.difficultyRating ?? 0);
|
||||
updateScoringRank(songData?.scoringRank);
|
||||
newText.visible = songData?.isNew;
|
||||
// Update opacity, offsets, etc.
|
||||
updateSelected();
|
||||
|
||||
|
@ -653,56 +715,66 @@ class SongMenuItem extends FlxSpriteGroup
|
|||
capsule.offset.x = this.selected ? 0 : -5;
|
||||
capsule.animation.play(this.selected ? "selected" : "unselected");
|
||||
ranking.alpha = this.selected ? 1 : 0.7;
|
||||
favIcon.alpha = this.selected ? 1 : 0.6;
|
||||
favIconBlurred.alpha = this.selected ? 1 : 0;
|
||||
ranking.color = this.selected ? 0xFFFFFFFF : 0xFFAAAAAA;
|
||||
|
||||
if (selected)
|
||||
{
|
||||
if (songText.tooLong == true) songText.initMove();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (songText.tooLong == true) songText.resetText();
|
||||
}
|
||||
if (songText.tooLong) songText.resetText();
|
||||
|
||||
if (selected && songText.tooLong) songText.initMove();
|
||||
}
|
||||
}
|
||||
|
||||
class FreeplayRank extends FlxSprite
|
||||
{
|
||||
public var rank(default, set):Int = 0;
|
||||
public var rank(default, set):Null<ScoringRank> = null;
|
||||
|
||||
var numToRank:Array<String> = ["LOSS", "GOOD", "GREAT", "EXCELLENT", "PERFECT", "PERFECTSICK"];
|
||||
|
||||
function set_rank(val):Int
|
||||
function set_rank(val:Null<ScoringRank>):Null<ScoringRank>
|
||||
{
|
||||
animation.play(numToRank[val], true, false);
|
||||
rank = val;
|
||||
|
||||
centerOffsets(false);
|
||||
|
||||
switch (val)
|
||||
if (rank == null || val == null)
|
||||
{
|
||||
case 0:
|
||||
// offset.x -= 1;
|
||||
case 1:
|
||||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
case 2:
|
||||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
case 3:
|
||||
// offset.y += 5;
|
||||
case 4:
|
||||
// offset.y += 5;
|
||||
default:
|
||||
centerOffsets(false);
|
||||
this.visible = false;
|
||||
}
|
||||
updateHitbox();
|
||||
return val;
|
||||
else
|
||||
{
|
||||
this.visible = true;
|
||||
|
||||
animation.play(val.getFreeplayRankIconAsset(), true, false);
|
||||
|
||||
centerOffsets(false);
|
||||
|
||||
switch (val)
|
||||
{
|
||||
case SHIT:
|
||||
// offset.x -= 1;
|
||||
case GOOD:
|
||||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
case GREAT:
|
||||
// offset.x -= 1;
|
||||
offset.y -= 8;
|
||||
case EXCELLENT:
|
||||
// offset.y += 5;
|
||||
case PERFECT:
|
||||
// offset.y += 5;
|
||||
case PERFECT_GOLD:
|
||||
// offset.y += 5;
|
||||
default:
|
||||
centerOffsets(false);
|
||||
this.visible = false;
|
||||
}
|
||||
updateHitbox();
|
||||
}
|
||||
|
||||
return rank = val;
|
||||
}
|
||||
|
||||
public var baseY:Float = 0;
|
||||
public var baseX:Float = 0;
|
||||
public var baseY:Float = 0;
|
||||
|
||||
public function new(x:Float, y:Float, ?initRank:Int = 0)
|
||||
public function new(x:Float, y:Float)
|
||||
{
|
||||
super(x, y);
|
||||
|
||||
|
@ -717,9 +789,7 @@ class FreeplayRank extends FlxSprite
|
|||
|
||||
blend = BlendMode.ADD;
|
||||
|
||||
this.rank = initRank;
|
||||
|
||||
animation.play(numToRank[initRank], true);
|
||||
this.rank = null;
|
||||
|
||||
// setGraphicSize(Std.int(width * 0.9));
|
||||
scale.set(0.9, 0.9);
|
||||
|
|
|
@ -42,6 +42,15 @@ class MainMenuState extends MusicBeatState
|
|||
var magenta:FlxSprite;
|
||||
var camFollow:FlxObject;
|
||||
|
||||
var overrideMusic:Bool = false;
|
||||
|
||||
public function new(?_overrideMusic:Bool = false)
|
||||
{
|
||||
super();
|
||||
overrideMusic = _overrideMusic;
|
||||
|
||||
}
|
||||
|
||||
override function create():Void
|
||||
{
|
||||
#if discord_rpc
|
||||
|
@ -49,10 +58,12 @@ class MainMenuState extends MusicBeatState
|
|||
DiscordClient.changePresence("In the Menus", null);
|
||||
#end
|
||||
|
||||
FlxG.cameras.reset(new FunkinCamera('mainMenu'));
|
||||
|
||||
transIn = FlxTransitionableState.defaultTransIn;
|
||||
transOut = FlxTransitionableState.defaultTransOut;
|
||||
|
||||
playMenuMusic();
|
||||
if(overrideMusic == false) playMenuMusic();
|
||||
|
||||
// We want the state to always be able to begin with being able to accept inputs and show the anims of the menu items.
|
||||
persistentUpdate = true;
|
||||
|
@ -161,7 +172,7 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
function playMenuMusic():Void
|
||||
{
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
FunkinSound.playMusic('freakyMenu',
|
||||
{
|
||||
overrideExisting: true,
|
||||
restartTrack: false
|
||||
|
@ -170,7 +181,6 @@ class MainMenuState extends MusicBeatState
|
|||
|
||||
function resetCamStuff():Void
|
||||
{
|
||||
FlxG.cameras.reset(new FunkinCamera('mainMenu'));
|
||||
FlxG.camera.follow(camFollow, null, 0.06);
|
||||
FlxG.camera.snapToTarget();
|
||||
}
|
||||
|
@ -329,6 +339,8 @@ class MainMenuState extends MusicBeatState
|
|||
persistentUpdate = false;
|
||||
|
||||
FlxG.state.openSubState(new DebugMenuSubState());
|
||||
// reset camera when debug menu is closed
|
||||
subStateClosed.addOnce(_ -> resetCamStuff());
|
||||
}
|
||||
#end
|
||||
|
||||
|
|
|
@ -11,12 +11,15 @@ class LevelProp extends Bopper
|
|||
function set_propData(value:LevelPropData):LevelPropData
|
||||
{
|
||||
// Only reset the prop if the asset path has changed.
|
||||
if (propData == null || value?.assetPath != propData?.assetPath)
|
||||
if (propData == null || !(thx.Dynamics.equals(value, propData)))
|
||||
{
|
||||
this.propData = value;
|
||||
|
||||
this.visible = this.propData != null;
|
||||
danceEvery = this.propData?.danceEvery ?? 0;
|
||||
|
||||
applyData();
|
||||
}
|
||||
this.visible = (value != null);
|
||||
danceEvery = this.propData?.danceEvery ?? 0;
|
||||
|
||||
return this.propData;
|
||||
}
|
||||
|
|
|
@ -306,7 +306,7 @@ class StoryMenuState extends MusicBeatState
|
|||
{
|
||||
Conductor.instance.update();
|
||||
|
||||
highScoreLerp = Std.int(MathUtil.smoothLerp(highScoreLerp, highScore, elapsed, 0.5));
|
||||
highScoreLerp = Std.int(MathUtil.smoothLerp(highScoreLerp, highScore, elapsed, 0.25));
|
||||
|
||||
scoreText.text = 'LEVEL SCORE: ${Math.round(highScoreLerp)}';
|
||||
|
||||
|
@ -466,6 +466,9 @@ class StoryMenuState extends MusicBeatState
|
|||
// Disable the funny music thing for now.
|
||||
// funnyMusicThing();
|
||||
}
|
||||
|
||||
updateText();
|
||||
refresh();
|
||||
}
|
||||
|
||||
final FADE_OUT_TIME:Float = 1.5;
|
||||
|
|
|
@ -67,9 +67,11 @@ class TitleState extends MusicBeatState
|
|||
// DEBUG BULLSHIT
|
||||
|
||||
// netConnection.addEventListener(MouseEvent.MOUSE_DOWN, overlay_onMouseDown);
|
||||
new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
if (!initialized) new FlxTimer().start(1, function(tmr:FlxTimer) {
|
||||
startIntro();
|
||||
});
|
||||
else
|
||||
startIntro();
|
||||
}
|
||||
|
||||
function client_onMetaData(metaData:Dynamic)
|
||||
|
@ -118,11 +120,11 @@ class TitleState extends MusicBeatState
|
|||
|
||||
function startIntro():Void
|
||||
{
|
||||
playMenuMusic();
|
||||
if (!initialized || FlxG.sound.music == null) playMenuMusic();
|
||||
|
||||
persistentUpdate = true;
|
||||
|
||||
var bg:FunkinSprite = new FunkinSprite().makeSolidColor(FlxG.width, FlxG.height, FlxColor.BLACK);
|
||||
var bg:FunkinSprite = new FunkinSprite(-1).makeSolidColor(FlxG.width + 2, FlxG.height, FlxColor.BLACK);
|
||||
bg.screenCenter();
|
||||
add(bg);
|
||||
|
||||
|
@ -231,7 +233,7 @@ class TitleState extends MusicBeatState
|
|||
overrideExisting: true,
|
||||
restartTrack: true
|
||||
});
|
||||
// Fade from 0.0 to 0.7 over 4 seconds
|
||||
// Fade from 0.0 to 1 over 4 seconds
|
||||
if (shouldFadeIn) FlxG.sound.music.fadeIn(4.0, 0.0, 1.0);
|
||||
}
|
||||
|
||||
|
|
|
@ -136,6 +136,8 @@ class FunkinPreloader extends FlxBasePreloader
|
|||
// We can't even call trace() yet, until Flixel loads.
|
||||
trace('Initializing custom preloader...');
|
||||
|
||||
funkin.util.CLIUtil.resetWorkingDir();
|
||||
|
||||
this.siteLockTitleText = Constants.SITE_LOCK_TITLE;
|
||||
this.siteLockBodyText = Constants.SITE_LOCK_DESC;
|
||||
}
|
||||
|
|
|
@ -248,6 +248,11 @@ class Constants
|
|||
*/
|
||||
public static final DEFAULT_ARTIST:String = 'Unknown';
|
||||
|
||||
/**
|
||||
* The default charter for songs.
|
||||
*/
|
||||
public static final DEFAULT_CHARTER:String = 'Unknown';
|
||||
|
||||
/**
|
||||
* The default note style for songs.
|
||||
*/
|
||||
|
@ -462,7 +467,7 @@ class Constants
|
|||
// % Hit
|
||||
public static final RANK_PERFECT_THRESHOLD:Float = 1.00;
|
||||
public static final RANK_EXCELLENT_THRESHOLD:Float = 0.90;
|
||||
public static final RANK_GREAT_THRESHOLD:Float = 0.75;
|
||||
public static final RANK_GREAT_THRESHOLD:Float = 0.80;
|
||||
public static final RANK_GOOD_THRESHOLD:Float = 0.60;
|
||||
|
||||
// public static final RANK_SHIT_THRESHOLD:Float = 0.00;
|
||||
|
|
|
@ -23,6 +23,8 @@ class VersionUtil
|
|||
{
|
||||
try
|
||||
{
|
||||
var versionRaw:thx.semver.Version.SemVer = version;
|
||||
trace('${versionRaw} satisfies (${versionRule})? ${version.satisfies(versionRule)}');
|
||||
return version.satisfies(versionRule);
|
||||
}
|
||||
catch (e)
|
||||
|
@ -39,13 +41,28 @@ class VersionUtil
|
|||
if (thx.Types.isAnonymousObject(versionData.version))
|
||||
{
|
||||
// This is bad! versionData.version should be an array!
|
||||
trace('[SAVE] Version data repair required! (got ${versionData.version})');
|
||||
// Turn the objects back into arrays.
|
||||
// I'd use DynamicsT.values but IDK if it maintains order
|
||||
versionData.version = [versionData.version[0], versionData.version[1], versionData.version[2]];
|
||||
|
||||
// This is so jank but it should work.
|
||||
var buildData:Dynamic<String> = cast versionData.build;
|
||||
var buildDataFixed:Array<thx.semver.Version.Identifier> = thx.Dynamics.DynamicsT.values(buildData)
|
||||
.map(function(d:Dynamic) return StringId(d.toString()));
|
||||
versionData.build = buildDataFixed;
|
||||
|
||||
var preData:Dynamic<String> = cast versionData.pre;
|
||||
var preDataFixed:Array<thx.semver.Version.Identifier> = thx.Dynamics.DynamicsT.values(preData).map(function(d:Dynamic) return StringId(d.toString()));
|
||||
versionData.pre = preDataFixed;
|
||||
|
||||
var fixedVersion:thx.semver.Version = versionData;
|
||||
trace('[SAVE] Fixed version: ${fixedVersion}');
|
||||
return fixedVersion;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace('[SAVE] Version data repair not required (got ${version})');
|
||||
// No need for repair.
|
||||
return version;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue