1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2024-11-15 11:22:55 +00:00

Merge branch 'rewrite/master' into applehair/freeplay-stack-overflow

This commit is contained in:
Eric 2024-08-12 14:37:18 -04:00 committed by GitHub
commit f4e42375d1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1087 additions and 380 deletions

View file

@ -60,8 +60,8 @@ runs:
haxelib --debug --never deleterepo || true
haxelib --debug --never newrepo
echo "HAXEPATH=$(haxelib config)" >> "$GITHUB_ENV"
haxelib --debug --never git haxelib https://github.com/HaxeFoundation/haxelib.git master
haxelib --debug --global install hmm
haxelib --debug --always --global git haxelib https://github.com/FunkinCrew/haxelib.git funkin-patches --skip-dependencies
haxelib --debug --global --always git hmm https://github.com/FunkinCrew/hmm funkin-patches
echo "TIMER_DEPS=$(date +%s)" >> "$GITHUB_ENV"
- name: Restore cached dependencies

2
assets

@ -1 +1 @@
Subproject commit 005c96f85f4304865acb196e7cc4d6d83f9d76d8
Subproject commit 62c4a8203362c1b434de0d376046ebccb96da254

View file

@ -104,7 +104,7 @@
"type": "git",
"dir": null,
"url": "https://github.com/HaxeFoundation/hxcpp",
"ref": "01cfee282a9a783e10c5a7774a3baaf547e6b0a7"
"ref": "8dc8020f8465027de6c2aaaed90718bc693651ed"
},
{
"name": "hxcpp-debug-server",
@ -178,7 +178,7 @@
"name": "openfl",
"type": "git",
"dir": null,
"ref": "228c1b5063911e2ad75cef6e3168ef0a4b9f9134",
"ref": "8306425c497766739510ab29e876059c96f77bd2",
"url": "https://github.com/FunkinCrew/openfl"
},
{

View file

@ -263,7 +263,7 @@ abstract class BaseRegistry<T:(IRegistryEntry<J> & Constructible<EntryConstructo
* @param version The entry's version (use `fetchEntryVersion(id)`).
* @return The created entry.
*/
public function parseEntryDataWithMigration(id:String, version:thx.semver.Version):Null<J>
public function parseEntryDataWithMigration(id:String, version:Null<thx.semver.Version>):Null<J>
{
if (version == null)
{

View file

@ -0,0 +1,31 @@
# Note Style Data Schema Changelog
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).
## [1.1.0]
### Added
- Added several new `assets`:
- `countdownThree`
- `countdownTwo`
- `countdownOne`
- `countdownGo`
- `judgementSick`
- `judgementGood`
- `judgementBad`
- `judgementShit`
- `comboNumber0`
- `comboNumber1`
- `comboNumber2`
- `comboNumber3`
- `comboNumber4`
- `comboNumber5`
- `comboNumber6`
- `comboNumber7`
- `comboNumber8`
- `comboNumber9`
## [1.0.0]
Initial version.

View file

@ -74,6 +74,84 @@ typedef NoteStyleAssetsData =
*/
@:optional
var holdNoteCover:NoteStyleAssetData<NoteStyleData_HoldNoteCover>;
/**
* The THREE sound (and an optional pre-READY graphic).
*/
@:optional
var countdownThree:NoteStyleAssetData<NoteStyleData_Countdown>;
/**
* The TWO sound and READY graphic.
*/
@:optional
var countdownTwo:NoteStyleAssetData<NoteStyleData_Countdown>;
/**
* The ONE sound and SET graphic.
*/
@:optional
var countdownOne:NoteStyleAssetData<NoteStyleData_Countdown>;
/**
* The GO sound and GO! graphic.
*/
@:optional
var countdownGo:NoteStyleAssetData<NoteStyleData_Countdown>;
/**
* The SICK! judgement.
*/
@:optional
var judgementSick:NoteStyleAssetData<NoteStyleData_Judgement>;
/**
* The GOOD! judgement.
*/
@:optional
var judgementGood:NoteStyleAssetData<NoteStyleData_Judgement>;
/**
* The BAD! judgement.
*/
@:optional
var judgementBad:NoteStyleAssetData<NoteStyleData_Judgement>;
/**
* The SHIT! judgement.
*/
@:optional
var judgementShit:NoteStyleAssetData<NoteStyleData_Judgement>;
@:optional
var comboNumber0:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber1:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber2:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber3:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber4:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber5:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber6:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber7:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber8:NoteStyleAssetData<NoteStyleData_ComboNum>;
@:optional
var comboNumber9:NoteStyleAssetData<NoteStyleData_ComboNum>;
}
/**
@ -120,7 +198,8 @@ typedef NoteStyleAssetData<T> =
/**
* The structure of this data depends on the asset.
*/
var data:T;
@:optional
var data:Null<T>;
}
typedef NoteStyleData_Note =
@ -131,7 +210,14 @@ typedef NoteStyleData_Note =
var right:UnnamedAnimationData;
}
typedef NoteStyleData_Countdown =
{
var audioPath:String;
}
typedef NoteStyleData_HoldNote = {}
typedef NoteStyleData_Judgement = {}
typedef NoteStyleData_ComboNum = {}
/**
* Data on animations for each direction of the strumline.

View file

@ -11,9 +11,9 @@ class NoteStyleRegistry extends BaseRegistry<NoteStyle, NoteStyleData>
* Handle breaking changes by incrementing this value
* and adding migration to the `migrateNoteStyleData()` function.
*/
public static final NOTE_STYLE_DATA_VERSION:thx.semver.Version = "1.0.0";
public static final NOTE_STYLE_DATA_VERSION:thx.semver.Version = "1.1.0";
public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.0.x";
public static final NOTE_STYLE_DATA_VERSION_RULE:thx.semver.VersionRule = "1.1.x";
public static var instance(get, never):NoteStyleRegistry;
static var _instance:Null<NoteStyleRegistry> = null;

View file

@ -93,8 +93,8 @@ class StageRegistry extends BaseRegistry<Stage, StageData>
public function listBaseGameStageIds():Array<String>
{
return [
"mainStage", "spookyMansion", "phillyTrain", "limoRide", "mallXmas", "mallEvil", "school", "schoolEvil", "tankmanBattlefield", "phillyStreets",
"phillyBlazin",
"mainStage", "spookyMansion", "phillyTrain", "phillyTrainErect", "limoRide", "limoRideErect", "mallXmas", "mallEvil", "school", "schoolEvil",
"tankmanBattlefield", "phillyStreets", "phillyBlazin",
];
}

View file

@ -0,0 +1,55 @@
package funkin.graphics.shaders;
import flixel.addons.display.FlxRuntimeShader;
import funkin.Paths;
import openfl.utils.Assets;
class AdjustColorShader extends FlxRuntimeShader
{
public var hue(default, set):Float;
public var saturation(default, set):Float;
public var brightness(default, set):Float;
public var contrast(default, set):Float;
public function new()
{
super(Assets.getText(Paths.frag('adjustColor')));
// FlxG.debugger.addTrackerProfile(new TrackerProfile(HSVShader, ['hue', 'saturation', 'brightness', 'contrast']));
hue = 0;
saturation = 0;
brightness = 0;
contrast = 0;
}
function set_hue(value:Float):Float
{
this.setFloat('hue', value);
this.hue = value;
return this.hue;
}
function set_saturation(value:Float):Float
{
this.setFloat('saturation', value);
this.saturation = value;
return this.saturation;
}
function set_brightness(value:Float):Float
{
this.setFloat('brightness', value);
this.brightness = value;
return this.brightness;
}
function set_contrast(value:Float):Float
{
this.setFloat('contrast', value);
this.contrast = value;
return this.contrast;
}
}

View file

@ -2,6 +2,7 @@ package funkin.graphics.shaders;
import flixel.FlxCamera;
import flixel.FlxG;
import flixel.graphics.frames.FlxFrame;
import flixel.addons.display.FlxRuntimeShader;
import lime.graphics.opengl.GLProgram;
import lime.utils.Log;
@ -32,6 +33,9 @@ class RuntimePostEffectShader extends FlxRuntimeShader
// equals (camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom)
uniform vec4 uCameraBounds;
// equals (frame.left, frame.top, frame.right, frame.bottom)
uniform vec4 uFrameBounds;
// screen coord -> world coord conversion
// returns world coord in px
vec2 screenToWorld(vec2 screenCoord) {
@ -56,6 +60,25 @@ class RuntimePostEffectShader extends FlxRuntimeShader
return (worldCoord - offset) / scale;
}
// screen coord -> frame coord conversion
// returns normalized frame coord
vec2 screenToFrame(vec2 screenCoord) {
float left = uFrameBounds.x;
float top = uFrameBounds.y;
float right = uFrameBounds.z;
float bottom = uFrameBounds.w;
float width = right - left;
float height = bottom - top;
float clampedX = clamp(screenCoord.x, left, right);
float clampedY = clamp(screenCoord.y, top, bottom);
return vec2(
(clampedX - left) / (width),
(clampedY - top) / (height)
);
}
// internally used to get the maximum `openfl_TextureCoordv`
vec2 bitmapCoordScale() {
return openfl_TextureCoordv / screenCoord;
@ -80,6 +103,8 @@ class RuntimePostEffectShader extends FlxRuntimeShader
{
super(fragmentSource, null, glVersion);
uScreenResolution.value = [FlxG.width, FlxG.height];
uCameraBounds.value = [0, 0, FlxG.width, FlxG.height];
uFrameBounds.value = [0, 0, FlxG.width, FlxG.height];
}
// basically `updateViewInfo(FlxG.width, FlxG.height, FlxG.camera)` is good
@ -89,6 +114,12 @@ class RuntimePostEffectShader extends FlxRuntimeShader
uCameraBounds.value = [camera.viewLeft, camera.viewTop, camera.viewRight, camera.viewBottom];
}
public function updateFrameInfo(frame:FlxFrame)
{
// NOTE: uv.width is actually the right pos and uv.height is the bottom pos
uFrameBounds.value = [frame.uv.x, frame.uv.y, frame.uv.width, frame.uv.height];
}
override function __createGLProgram(vertexSource:String, fragmentSource:String):GLProgram
{
try

View file

@ -32,6 +32,14 @@ class RuntimeRainShader extends RuntimePostEffectShader
return time = value;
}
public var spriteMode(default, set):Bool = false;
function set_spriteMode(value:Bool):Bool
{
this.setBool('uSpriteMode', value);
return spriteMode = value;
}
// The scale of the rain depends on the world coordinate system, so higher resolution makes
// the raindrops smaller. This parameter can be used to adjust the total scale of the scene.
// The size of the raindrops is proportional to the value of this parameter.

View file

@ -11,6 +11,9 @@ import funkin.modding.events.ScriptEvent.CountdownScriptEvent;
import flixel.util.FlxTimer;
import funkin.util.EaseUtil;
import funkin.audio.FunkinSound;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle;
class Countdown
{
@ -19,6 +22,24 @@ class Countdown
*/
public static var countdownStep(default, null):CountdownStep = BEFORE;
/**
* Which alternate graphic/sound on countdown to use.
* This is set via the current notestyle.
* For example, in Week 6 it is `pixel`.
*/
public static var soundSuffix:String = '';
/**
* Which alternate graphic on countdown to use.
* You can set this via script.
* For example, in Week 6 it is `-pixel`.
*/
public static var graphicSuffix:String = '';
static var noteStyle:NoteStyle;
static var fallbackNoteStyle:Null<NoteStyle>;
/**
* The currently running countdown. This will be null if there is no countdown running.
*/
@ -30,7 +51,7 @@ class Countdown
* This will automatically stop and restart the countdown if it is already running.
* @returns `false` if the countdown was cancelled by a script.
*/
public static function performCountdown(isPixelStyle:Bool):Bool
public static function performCountdown():Bool
{
countdownStep = BEFORE;
var cancelled:Bool = propagateCountdownEvent(countdownStep);
@ -65,10 +86,10 @@ class Countdown
// PlayState.instance.dispatchEvent(new SongTimeScriptEvent(SONG_BEAT_HIT, 0, 0));
// Countdown graphic.
showCountdownGraphic(countdownStep, isPixelStyle);
showCountdownGraphic(countdownStep);
// Countdown sound.
playCountdownSound(countdownStep, isPixelStyle);
playCountdownSound(countdownStep);
// Event handling bullshit.
var cancelled:Bool = propagateCountdownEvent(countdownStep);
@ -177,122 +198,69 @@ class Countdown
}
/**
* Retrieves the graphic to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
*
* This is public so modules can do lol funny shit.
* Reset the countdown configuration to the default.
*/
public static function showCountdownGraphic(index:CountdownStep, isPixelStyle:Bool):Void
public static function reset()
{
var spritePath:String = null;
noteStyle = null;
}
/**
* Retrieve the note style data (if we haven't already)
* @param noteStyleId The id of the note style to fetch. Defaults to the one used by the current PlayState.
* @param force Fetch the note style from the registry even if we've already fetched it.
*/
static function fetchNoteStyle(?noteStyleId:String, force:Bool = false):Void
{
if (noteStyle != null && !force) return;
if (noteStyleId == null) noteStyleId = PlayState.instance?.currentChart?.noteStyle;
noteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId);
if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault();
}
/**
* Retrieves the graphic to use for this step of the countdown.
*/
public static function showCountdownGraphic(index:CountdownStep):Void
{
fetchNoteStyle();
var countdownSprite = noteStyle.buildCountdownSprite(index);
if (countdownSprite == null) return;
var fadeEase = FlxEase.cubeInOut;
if (isPixelStyle)
{
fadeEase = EaseUtil.stepped(8);
switch (index)
{
case TWO:
spritePath = 'weeb/pixelUI/ready-pixel';
case ONE:
spritePath = 'weeb/pixelUI/set-pixel';
case GO:
spritePath = 'weeb/pixelUI/date-pixel';
default:
// null
}
}
else
{
switch (index)
{
case TWO:
spritePath = 'ready';
case ONE:
spritePath = 'set';
case GO:
spritePath = 'go';
default:
// null
}
}
if (spritePath == null) return;
var countdownSprite:FunkinSprite = FunkinSprite.create(spritePath);
countdownSprite.scrollFactor.set(0, 0);
if (isPixelStyle) countdownSprite.setGraphicSize(Std.int(countdownSprite.width * Constants.PIXEL_ART_SCALE));
countdownSprite.antialiasing = !isPixelStyle;
countdownSprite.updateHitbox();
countdownSprite.screenCenter();
if (noteStyle.isCountdownSpritePixel(index)) fadeEase = EaseUtil.stepped(8);
// Fade sprite in, then out, then destroy it.
FlxTween.tween(countdownSprite, {y: countdownSprite.y += 100}, Conductor.instance.beatLengthMs / 1000,
FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000,
{
ease: FlxEase.cubeInOut,
ease: fadeEase,
onComplete: function(twn:FlxTween) {
countdownSprite.destroy();
}
});
FlxTween.tween(countdownSprite, {alpha: 0}, Conductor.instance.beatLengthMs / 1000,
{
ease: fadeEase
});
countdownSprite.cameras = [PlayState.instance.camHUD];
PlayState.instance.add(countdownSprite);
countdownSprite.screenCenter();
var offsets = noteStyle.getCountdownSpriteOffsets(index);
countdownSprite.x += offsets[0];
countdownSprite.y += offsets[1];
}
/**
* Retrieves the sound file to use for this step of the countdown.
* TODO: Make this less dumb. Unhardcode it? Use modules? Use notestyles?
*
* This is public so modules can do lol funny shit.
*/
public static function playCountdownSound(index:CountdownStep, isPixelStyle:Bool):Void
public static function playCountdownSound(step:CountdownStep):FunkinSound
{
var soundPath:String = null;
fetchNoteStyle();
var path = noteStyle.getCountdownSoundPath(step);
if (path == null) return null;
if (isPixelStyle)
{
switch (index)
{
case THREE:
soundPath = 'intro3-pixel';
case TWO:
soundPath = 'intro2-pixel';
case ONE:
soundPath = 'intro1-pixel';
case GO:
soundPath = 'introGo-pixel';
default:
// null
}
}
else
{
switch (index)
{
case THREE:
soundPath = 'intro3';
case TWO:
soundPath = 'intro2';
case ONE:
soundPath = 'intro1';
case GO:
soundPath = 'introGo';
default:
// null
}
}
if (soundPath == null) return;
FunkinSound.playOnce(Paths.sound(soundPath), Constants.COUNTDOWN_VOLUME);
return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME);
}
public static function decrement(step:CountdownStep):CountdownStep

View file

@ -306,7 +306,7 @@ class PauseSubState extends MusicBeatSubState
metadataDifficulty.setFormat(Paths.font('vcr.ttf'), 32, FlxColor.WHITE, FlxTextAlign.RIGHT);
if (PlayState.instance?.currentDifficulty != null)
{
metadataDifficulty.text += PlayState.instance.currentDifficulty.toTitleCase();
metadataDifficulty.text += PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase();
}
metadataDifficulty.scrollFactor.set(0, 0);
metadata.add(metadataDifficulty);

View file

@ -696,12 +696,7 @@ class PlayState extends MusicBeatSubState
initMinimalMode();
}
initStrumlines();
// Initialize the judgements and combo meter.
comboPopUps = new PopUpStuff();
comboPopUps.zIndex = 900;
add(comboPopUps);
comboPopUps.cameras = [camHUD];
initPopups();
#if discord_rpc
// Initialize Discord Rich Presence.
@ -902,7 +897,7 @@ class PlayState extends MusicBeatSubState
health = Constants.HEALTH_STARTING;
songScore = 0;
Highscore.tallies.combo = 0;
Countdown.performCountdown(currentStageId.startsWith('school'));
Countdown.performCountdown();
needsReset = false;
}
@ -1729,6 +1724,21 @@ class PlayState extends MusicBeatSubState
opponentStrumline.fadeInArrows();
}
/**
* Configures the judgement and combo popups.
*/
function initPopups():Void
{
var noteStyleId:String = currentChart.noteStyle;
var noteStyle:NoteStyle = NoteStyleRegistry.instance.fetchEntry(noteStyleId);
if (noteStyle == null) noteStyle = NoteStyleRegistry.instance.fetchDefault();
// Initialize the judgements and combo meter.
comboPopUps = new PopUpStuff(noteStyle);
comboPopUps.zIndex = 900;
add(comboPopUps);
comboPopUps.cameras = [camHUD];
}
/**
* Initializes the Discord Rich Presence.
*/
@ -1859,7 +1869,7 @@ class PlayState extends MusicBeatSubState
public function startCountdown():Void
{
// If Countdown.performCountdown returns false, then the countdown was canceled by a script.
var result:Bool = Countdown.performCountdown(currentStageId.startsWith('school'));
var result:Bool = Countdown.performCountdown();
if (!result) return;
isInCutscene = false;
@ -3010,6 +3020,7 @@ class PlayState extends MusicBeatSubState
GameOverSubState.reset();
PauseSubState.reset();
Countdown.reset();
// Clear the static reference to this state.
instance = null;

View file

@ -118,22 +118,6 @@ class BaseCharacter extends Bopper
*/
public var cameraFocusPoint(default, null):FlxPoint = new FlxPoint(0, 0);
override function set_animOffsets(value:Array<Float>):Array<Float>
{
if (animOffsets == null) value = [0, 0];
if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value;
// Make sure animOffets are halved when scale is 0.5.
var xDiff = (animOffsets[0] * this.scale.x / (this.isPixel ? 6 : 1)) - value[0];
var yDiff = (animOffsets[1] * this.scale.y / (this.isPixel ? 6 : 1)) - value[1];
// Call the super function so that camera focus point is not affected.
super.set_x(this.x + xDiff);
super.set_y(this.y + yDiff);
return animOffsets = value;
}
/**
* If the x position changes, other than via changing the animation offset,
* then we need to update the camera focus point.

View file

@ -150,13 +150,17 @@ class HealthIcon extends FunkinSprite
{
if (characterId == 'bf-old')
{
isPixel = PlayState.instance.currentStage.getBoyfriend().isPixel;
PlayState.instance.currentStage.getBoyfriend().initHealthIcon(false);
}
else
{
characterId = 'bf-old';
isPixel = false;
loadCharacter(characterId);
}
lerpIconSize(true);
}
/**
@ -199,6 +203,22 @@ class HealthIcon extends FunkinSprite
super.update(elapsed);
if (bopEvery != 0)
{
lerpIconSize();
// Lerp the health icon back to its normal angle.
this.angle = MathUtil.coolLerp(this.angle, 0, 0.15);
}
this.updatePosition();
}
/**
* Does the calculation to lerp the icon size. Usually called every frame, but can be forced to the target size.
* Mainly forced when changing to old icon to not have a weird lerp related to changing from pixel icon to non-pixel old icon
* @param force Force the icon immedialtely to be the target size. Defaults to false.
*/
function lerpIconSize(force:Bool = false):Void
{
// Lerp the health icon back to its normal size,
// while maintaining aspect ratio.
@ -207,24 +227,22 @@ class HealthIcon extends FunkinSprite
// Apply linear interpolation while accounting for frame rate.
var targetSize:Int = Std.int(MathUtil.coolLerp(this.width, HEALTH_ICON_SIZE * this.size.x, 0.15));
if (force) targetSize = Std.int(HEALTH_ICON_SIZE * this.size.x);
setGraphicSize(targetSize, 0);
}
else
{
var targetSize:Int = Std.int(MathUtil.coolLerp(this.height, HEALTH_ICON_SIZE * this.size.y, 0.15));
if (force) targetSize = Std.int(HEALTH_ICON_SIZE * this.size.y);
setGraphicSize(0, targetSize);
}
// Lerp the health icon back to its normal angle.
this.angle = MathUtil.coolLerp(this.angle, 0, 0.15);
this.updateHitbox();
}
this.updatePosition();
}
/**
* Update the position (and status) of the health icon.
*/
@ -412,6 +430,8 @@ class HealthIcon extends FunkinSprite
isLegacyStyle = !isNewSpritesheet(charId);
trace(' Loading health icon for character: $charId (legacy: $isLegacyStyle)');
if (!isLegacyStyle)
{
loadSparrow('icons/icon-$charId');

View file

@ -8,58 +8,51 @@ import funkin.graphics.FunkinSprite;
import funkin.play.PlayState;
import funkin.util.TimerUtil;
import funkin.util.EaseUtil;
import openfl.utils.Assets;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.play.notes.notestyle.NoteStyle;
@:nullSafety
class PopUpStuff extends FlxTypedGroup<FunkinSprite>
{
public var offsets:Array<Int> = [0, 0];
/**
* The current note style to use. This determines which graphics to display.
* For example, Week 6 uses the `pixel` note style, and mods can create their own.
*/
var noteStyle:NoteStyle;
override public function new()
override public function new(noteStyle:NoteStyle)
{
super();
this.noteStyle = noteStyle;
}
public function displayRating(daRating:String):Void
public function displayRating(daRating:Null<String>)
{
var perfStart:Float = TimerUtil.start();
if (daRating == null) daRating = "good";
var ratingPath:String = daRating;
if (PlayState.instance.currentStageId.startsWith('school')) ratingPath = "weeb/pixelUI/" + ratingPath + "-pixel";
var rating:FunkinSprite = FunkinSprite.create(0, 0, ratingPath);
rating.scrollFactor.set(0.2, 0.2);
var rating:Null<FunkinSprite> = noteStyle.buildJudgementSprite(daRating);
if (rating == null) return;
rating.zIndex = 1000;
rating.x = (FlxG.width * 0.474) + offsets[0];
// rating.x -= FlxG.camera.scroll.x * 0.2;
rating.y = (FlxG.camera.height * 0.45 - 60) + offsets[1];
rating.x = (FlxG.width * 0.474);
rating.x -= rating.width / 2;
rating.y = (FlxG.camera.height * 0.45 - 60);
rating.y -= rating.height / 2;
var offsets = noteStyle.getJudgementSpriteOffsets(daRating);
rating.x += offsets[0];
rating.y += offsets[1];
rating.acceleration.y = 550;
rating.velocity.y -= FlxG.random.int(140, 175);
rating.velocity.x -= FlxG.random.int(0, 10);
add(rating);
var fadeEase = null;
if (PlayState.instance.currentStageId.startsWith('school'))
{
rating.setGraphicSize(Std.int(rating.width * Constants.PIXEL_ART_SCALE * 0.7));
rating.antialiasing = false;
rating.pixelPerfectRender = true;
rating.pixelPerfectPosition = true;
fadeEase = EaseUtil.stepped(2);
}
else
{
rating.setGraphicSize(Std.int(rating.width * 0.65));
rating.antialiasing = true;
}
rating.updateHitbox();
rating.x -= rating.width / 2;
rating.y -= rating.height / 2;
var fadeEase = noteStyle.isJudgementSpritePixel(daRating) ? EaseUtil.stepped(2) : null;
FlxTween.tween(rating, {alpha: 0}, 0.2,
{
@ -70,62 +63,10 @@ class PopUpStuff extends FlxTypedGroup<FunkinSprite>
startDelay: Conductor.instance.beatLengthMs * 0.001,
ease: fadeEase
});
trace('displayRating took: ${TimerUtil.seconds(perfStart)}');
}
public function displayCombo(?combo:Int = 0):Int
public function displayCombo(combo:Int = 0):Void
{
var perfStart:Float = TimerUtil.start();
if (combo == null) combo = 0;
var pixelShitPart1:String = "";
var pixelShitPart2:String = '';
if (PlayState.instance.currentStageId.startsWith('school'))
{
pixelShitPart1 = 'weeb/pixelUI/';
pixelShitPart2 = '-pixel';
}
var comboSpr:FunkinSprite = FunkinSprite.create(pixelShitPart1 + 'combo' + pixelShitPart2);
comboSpr.y = (FlxG.camera.height * 0.44) + offsets[1];
comboSpr.x = (FlxG.width * 0.507) + offsets[0];
// comboSpr.x -= FlxG.camera.scroll.x * 0.2;
comboSpr.acceleration.y = 600;
comboSpr.velocity.y -= 150;
comboSpr.velocity.x += FlxG.random.int(1, 10);
// add(comboSpr);
var fadeEase = null;
if (PlayState.instance.currentStageId.startsWith('school'))
{
comboSpr.setGraphicSize(Std.int(comboSpr.width * Constants.PIXEL_ART_SCALE * 1));
comboSpr.antialiasing = false;
comboSpr.pixelPerfectRender = true;
comboSpr.pixelPerfectPosition = true;
fadeEase = EaseUtil.stepped(2);
}
else
{
comboSpr.setGraphicSize(Std.int(comboSpr.width * 0.7));
comboSpr.antialiasing = true;
}
comboSpr.updateHitbox();
FlxTween.tween(comboSpr, {alpha: 0}, 0.2,
{
onComplete: function(tween:FlxTween) {
remove(comboSpr, true);
comboSpr.destroy();
},
startDelay: Conductor.instance.beatLengthMs * 0.001,
ease: fadeEase
});
var seperatedScore:Array<Int> = [];
var tempCombo:Int = combo;
@ -140,31 +81,27 @@ class PopUpStuff extends FlxTypedGroup<FunkinSprite>
// seperatedScore.reverse();
var daLoop:Int = 1;
for (i in seperatedScore)
for (digit in seperatedScore)
{
var numScore:FunkinSprite = FunkinSprite.create(0, comboSpr.y, pixelShitPart1 + 'num' + Std.int(i) + pixelShitPart2);
var numScore:Null<FunkinSprite> = noteStyle.buildComboNumSprite(digit);
if (numScore == null) continue;
if (PlayState.instance.currentStageId.startsWith('school'))
{
numScore.setGraphicSize(Std.int(numScore.width * Constants.PIXEL_ART_SCALE * 1));
numScore.antialiasing = false;
numScore.pixelPerfectRender = true;
numScore.pixelPerfectPosition = true;
}
else
{
numScore.setGraphicSize(Std.int(numScore.width * 0.45));
numScore.antialiasing = true;
}
numScore.updateHitbox();
numScore.x = (FlxG.width * 0.507) - (36 * daLoop) - 65;
trace('numScore($daLoop) = ${numScore.x}');
numScore.y = (FlxG.camera.height * 0.44);
var offsets = noteStyle.getComboNumSpriteOffsets(digit);
numScore.x += offsets[0];
numScore.y += offsets[1];
numScore.x = comboSpr.x - (36 * daLoop) - 65; //- 90;
numScore.acceleration.y = FlxG.random.int(250, 300);
numScore.velocity.y -= FlxG.random.int(130, 150);
numScore.velocity.x = FlxG.random.float(-5, 5);
add(numScore);
var fadeEase = noteStyle.isComboNumSpritePixel(digit) ? EaseUtil.stepped(2) : null;
FlxTween.tween(numScore, {alpha: 0}, 0.2,
{
onComplete: function(tween:FlxTween) {
@ -177,9 +114,5 @@ class PopUpStuff extends FlxTypedGroup<FunkinSprite>
daLoop++;
}
trace('displayCombo took: ${TimerUtil.seconds(perfStart)}');
return combo;
}
}

View file

@ -75,6 +75,13 @@ class StrumlineNote extends FlxSprite
function setup(noteStyle:NoteStyle):Void
{
if (noteStyle == null)
{
// If you get an exception on this line, check the debug console.
// You probably have a parsing error in your note style's JSON file.
throw "FATAL ERROR: Attempted to initialize PlayState with an invalid NoteStyle.";
}
noteStyle.applyStrumlineFrames(this);
noteStyle.applyStrumlineAnimations(this, this.direction);

View file

@ -1,5 +1,6 @@
package funkin.play.notes.notestyle;
import funkin.play.Countdown;
import flixel.graphics.frames.FlxAtlasFrames;
import flixel.graphics.frames.FlxFramesCollection;
import funkin.data.animation.AnimationData;
@ -16,6 +17,7 @@ using funkin.data.animation.AnimationData.AnimationDataUtil;
* Holds the data for what assets to use for a note style,
* and provides convenience methods for building sprites based on them.
*/
@:nullSafety
class NoteStyle implements IRegistryEntry<NoteStyleData>
{
/**
@ -42,12 +44,8 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
this.id = id;
_data = _fetchData(id);
if (_data == null)
{
throw 'Could not parse note style data for id: $id';
}
this.fallback = NoteStyleRegistry.instance.fetchEntry(getFallbackID());
var fallbackID = _data.fallback;
if (fallbackID != null) this.fallback = NoteStyleRegistry.instance.fetchEntry(fallbackID);
}
/**
@ -72,7 +70,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
* Get the note style ID of the parent note style.
* @return The string ID, or `null` if there is no parent.
*/
function getFallbackID():Null<String>
public function getFallbackID():Null<String>
{
return _data.fallback;
}
@ -80,7 +78,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
public function buildNoteSprite(target:NoteSprite):Void
{
// Apply the note sprite frames.
var atlas:FlxAtlasFrames = buildNoteFrames(false);
var atlas:Null<FlxAtlasFrames> = buildNoteFrames(false);
if (atlas == null)
{
@ -89,7 +87,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
target.frames = atlas;
target.antialiasing = !_data.assets.note.isPixel;
target.antialiasing = !(_data.assets?.note?.isPixel ?? false);
// Apply the animations.
buildNoteAnimations(target);
@ -99,21 +97,30 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
target.updateHitbox();
}
var noteFrames:FlxAtlasFrames = null;
var noteFrames:Null<FlxAtlasFrames> = null;
function buildNoteFrames(force:Bool = false):FlxAtlasFrames
function buildNoteFrames(force:Bool = false):Null<FlxAtlasFrames>
{
if (!FunkinSprite.isTextureCached(Paths.image(getNoteAssetPath())))
var noteAssetPath = getNoteAssetPath();
if (noteAssetPath == null) return null;
if (!FunkinSprite.isTextureCached(Paths.image(noteAssetPath)))
{
FlxG.log.warn('Note texture is not cached: ${getNoteAssetPath()}');
FlxG.log.warn('Note texture is not cached: ${noteAssetPath}');
}
// Purge the note frames if the cached atlas is invalid.
@:nullSafety(Off)
{
if (noteFrames?.parent?.isDestroyed ?? false) noteFrames = null;
}
if (noteFrames != null && !force) return noteFrames;
noteFrames = Paths.getSparrowAtlas(getNoteAssetPath(), getNoteAssetLibrary());
var noteAssetPath = getNoteAssetPath();
if (noteAssetPath == null) return null;
noteFrames = Paths.getSparrowAtlas(noteAssetPath, getNoteAssetLibrary());
if (noteFrames == null)
{
@ -123,17 +130,18 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return noteFrames;
}
function getNoteAssetPath(raw:Bool = false):String
function getNoteAssetPath(raw:Bool = false):Null<String>
{
if (raw)
{
var rawPath:Null<String> = _data?.assets?.note?.assetPath;
if (rawPath == null) return fallback.getNoteAssetPath(true);
if (rawPath == null && fallback != null) return fallback.getNoteAssetPath(true);
return rawPath;
}
// library:path
var parts = getNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR);
var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length == 0) return null;
if (parts.length == 1) return getNoteAssetPath(true);
return parts[1];
}
@ -141,57 +149,63 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
function getNoteAssetLibrary():Null<String>
{
// library:path
var parts = getNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR);
var parts = getNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length == 0) return null;
if (parts.length == 1) return null;
return parts[0];
}
function buildNoteAnimations(target:NoteSprite):Void
{
var leftData:AnimationData = fetchNoteAnimationData(LEFT);
target.animation.addByPrefix('purpleScroll', leftData.prefix, leftData.frameRate, leftData.looped, leftData.flipX, leftData.flipY);
var downData:AnimationData = fetchNoteAnimationData(DOWN);
target.animation.addByPrefix('blueScroll', downData.prefix, downData.frameRate, downData.looped, downData.flipX, downData.flipY);
var upData:AnimationData = fetchNoteAnimationData(UP);
target.animation.addByPrefix('greenScroll', upData.prefix, upData.frameRate, upData.looped, upData.flipX, upData.flipY);
var rightData:AnimationData = fetchNoteAnimationData(RIGHT);
target.animation.addByPrefix('redScroll', rightData.prefix, rightData.frameRate, rightData.looped, rightData.flipX, rightData.flipY);
var leftData:Null<AnimationData> = fetchNoteAnimationData(LEFT);
if (leftData != null) target.animation.addByPrefix('purpleScroll', leftData.prefix ?? '', leftData.frameRate ?? 24, leftData.looped ?? false,
leftData.flipX, leftData.flipY);
var downData:Null<AnimationData> = fetchNoteAnimationData(DOWN);
if (downData != null) target.animation.addByPrefix('blueScroll', downData.prefix ?? '', downData.frameRate ?? 24, downData.looped ?? false,
downData.flipX, downData.flipY);
var upData:Null<AnimationData> = fetchNoteAnimationData(UP);
if (upData != null) target.animation.addByPrefix('greenScroll', upData.prefix ?? '', upData.frameRate ?? 24, upData.looped ?? false, upData.flipX,
upData.flipY);
var rightData:Null<AnimationData> = fetchNoteAnimationData(RIGHT);
if (rightData != null) target.animation.addByPrefix('redScroll', rightData.prefix ?? '', rightData.frameRate ?? 24, rightData.looped ?? false,
rightData.flipX, rightData.flipY);
}
public function isNoteAnimated():Bool
{
return _data.assets.note.animated;
return _data.assets?.note?.animated ?? false;
}
public function getNoteScale():Float
{
return _data.assets.note.scale;
return _data.assets?.note?.scale ?? 1.0;
}
function fetchNoteAnimationData(dir:NoteDirection):AnimationData
function fetchNoteAnimationData(dir:NoteDirection):Null<AnimationData>
{
var result:Null<AnimationData> = switch (dir)
{
case LEFT: _data.assets.note.data.left.toNamed();
case DOWN: _data.assets.note.data.down.toNamed();
case UP: _data.assets.note.data.up.toNamed();
case RIGHT: _data.assets.note.data.right.toNamed();
case LEFT: _data.assets?.note?.data?.left?.toNamed();
case DOWN: _data.assets?.note?.data?.down?.toNamed();
case UP: _data.assets?.note?.data?.up?.toNamed();
case RIGHT: _data.assets?.note?.data?.right?.toNamed();
};
return (result == null) ? fallback.fetchNoteAnimationData(dir) : result;
return (result == null && fallback != null) ? fallback.fetchNoteAnimationData(dir) : result;
}
public function getHoldNoteAssetPath(raw:Bool = false):String
public function getHoldNoteAssetPath(raw:Bool = false):Null<String>
{
if (raw)
{
// TODO: figure out why ?. didn't work here
var rawPath:Null<String> = (_data?.assets?.holdNote == null) ? null : _data?.assets?.holdNote?.assetPath;
return (rawPath == null) ? fallback.getHoldNoteAssetPath(true) : rawPath;
return (rawPath == null && fallback != null) ? fallback.getHoldNoteAssetPath(true) : rawPath;
}
// library:path
var parts = getHoldNoteAssetPath(true).split(Constants.LIBRARY_SEPARATOR);
var parts = getHoldNoteAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length == 0) return null;
if (parts.length == 1) return Paths.image(parts[0]);
return Paths.image(parts[1], parts[0]);
}
@ -199,15 +213,15 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
public function isHoldNotePixel():Bool
{
var data = _data?.assets?.holdNote;
if (data == null) return fallback.isHoldNotePixel();
return data.isPixel;
if (data == null && fallback != null) return fallback.isHoldNotePixel();
return data?.isPixel ?? false;
}
public function fetchHoldNoteScale():Float
{
var data = _data?.assets?.holdNote;
if (data == null) return fallback.fetchHoldNoteScale();
return data.scale;
if (data == null && fallback != null) return fallback.fetchHoldNoteScale();
return data?.scale ?? 1.0;
}
public function applyStrumlineFrames(target:StrumlineNote):Void
@ -215,7 +229,7 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
// TODO: Add support for multi-Sparrow.
// Will be less annoying after this is merged: https://github.com/HaxeFlixel/flixel/pull/2772
var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath(), getStrumlineAssetLibrary());
var atlas:FlxAtlasFrames = Paths.getSparrowAtlas(getStrumlineAssetPath() ?? '', getStrumlineAssetLibrary());
if (atlas == null)
{
@ -224,31 +238,30 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
target.frames = atlas;
target.scale.x = _data.assets.noteStrumline.scale;
target.scale.y = _data.assets.noteStrumline.scale;
target.antialiasing = !_data.assets.noteStrumline.isPixel;
target.scale.set(_data.assets.noteStrumline?.scale ?? 1.0);
target.antialiasing = !(_data.assets.noteStrumline?.isPixel ?? false);
}
function getStrumlineAssetPath(raw:Bool = false):String
function getStrumlineAssetPath(raw:Bool = false):Null<String>
{
if (raw)
{
var rawPath:Null<String> = _data?.assets?.noteStrumline?.assetPath;
if (rawPath == null) return fallback.getStrumlineAssetPath(true);
if (rawPath == null && fallback != null) return fallback.getStrumlineAssetPath(true);
return rawPath;
}
// library:path
var parts = getStrumlineAssetPath(true).split(Constants.LIBRARY_SEPARATOR);
if (parts.length == 1) return getStrumlineAssetPath(true);
var parts = getStrumlineAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length <= 1) return getStrumlineAssetPath(true);
return parts[1];
}
function getStrumlineAssetLibrary():Null<String>
{
// library:path
var parts = getStrumlineAssetPath(true).split(Constants.LIBRARY_SEPARATOR);
if (parts.length == 1) return null;
var parts = getStrumlineAssetPath(true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length <= 1) return null;
return parts[0];
}
@ -259,60 +272,592 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
function getStrumlineAnimationData(dir:NoteDirection):Array<AnimationData>
{
var result:Array<AnimationData> = switch (dir)
var result:Array<Null<AnimationData>> = switch (dir)
{
case NoteDirection.LEFT: [
_data.assets.noteStrumline.data.leftStatic.toNamed('static'),
_data.assets.noteStrumline.data.leftPress.toNamed('press'),
_data.assets.noteStrumline.data.leftConfirm.toNamed('confirm'),
_data.assets.noteStrumline.data.leftConfirmHold.toNamed('confirm-hold'),
_data.assets.noteStrumline?.data?.leftStatic?.toNamed('static'),
_data.assets.noteStrumline?.data?.leftPress?.toNamed('press'),
_data.assets.noteStrumline?.data?.leftConfirm?.toNamed('confirm'),
_data.assets.noteStrumline?.data?.leftConfirmHold?.toNamed('confirm-hold'),
];
case NoteDirection.DOWN: [
_data.assets.noteStrumline.data.downStatic.toNamed('static'),
_data.assets.noteStrumline.data.downPress.toNamed('press'),
_data.assets.noteStrumline.data.downConfirm.toNamed('confirm'),
_data.assets.noteStrumline.data.downConfirmHold.toNamed('confirm-hold'),
_data.assets.noteStrumline?.data?.downStatic?.toNamed('static'),
_data.assets.noteStrumline?.data?.downPress?.toNamed('press'),
_data.assets.noteStrumline?.data?.downConfirm?.toNamed('confirm'),
_data.assets.noteStrumline?.data?.downConfirmHold?.toNamed('confirm-hold'),
];
case NoteDirection.UP: [
_data.assets.noteStrumline.data.upStatic.toNamed('static'),
_data.assets.noteStrumline.data.upPress.toNamed('press'),
_data.assets.noteStrumline.data.upConfirm.toNamed('confirm'),
_data.assets.noteStrumline.data.upConfirmHold.toNamed('confirm-hold'),
_data.assets.noteStrumline?.data?.upStatic?.toNamed('static'),
_data.assets.noteStrumline?.data?.upPress?.toNamed('press'),
_data.assets.noteStrumline?.data?.upConfirm?.toNamed('confirm'),
_data.assets.noteStrumline?.data?.upConfirmHold?.toNamed('confirm-hold'),
];
case NoteDirection.RIGHT: [
_data.assets.noteStrumline.data.rightStatic.toNamed('static'),
_data.assets.noteStrumline.data.rightPress.toNamed('press'),
_data.assets.noteStrumline.data.rightConfirm.toNamed('confirm'),
_data.assets.noteStrumline.data.rightConfirmHold.toNamed('confirm-hold'),
_data.assets.noteStrumline?.data?.rightStatic?.toNamed('static'),
_data.assets.noteStrumline?.data?.rightPress?.toNamed('press'),
_data.assets.noteStrumline?.data?.rightConfirm?.toNamed('confirm'),
_data.assets.noteStrumline?.data?.rightConfirmHold?.toNamed('confirm-hold'),
];
default: [];
};
return result;
return thx.Arrays.filterNull(result);
}
public function applyStrumlineOffsets(target:StrumlineNote)
public function applyStrumlineOffsets(target:StrumlineNote):Void
{
target.x += _data.assets.noteStrumline.offsets[0];
target.y += _data.assets.noteStrumline.offsets[1];
var offsets = _data?.assets?.noteStrumline?.offsets ?? [0.0, 0.0];
target.x += offsets[0];
target.y += offsets[1];
}
public function getStrumlineScale():Float
{
return _data.assets.noteStrumline.scale;
return _data?.assets?.noteStrumline?.scale ?? 1.0;
}
public function isNoteSplashEnabled():Bool
{
var data = _data?.assets?.noteSplash?.data;
if (data == null) return fallback.isNoteSplashEnabled();
return data.enabled;
if (data == null) return fallback?.isNoteSplashEnabled() ?? false;
return data.enabled ?? false;
}
public function isHoldNoteCoverEnabled():Bool
{
var data = _data?.assets?.holdNoteCover?.data;
if (data == null) return fallback.isHoldNoteCoverEnabled();
return data.enabled;
if (data == null) return fallback?.isHoldNoteCoverEnabled() ?? false;
return data.enabled ?? false;
}
/**
* Build a sprite for the given step of the countdown.
* @param step
* @return A `FunkinSprite`, or `null` if no graphic is available for this step.
*/
public function buildCountdownSprite(step:Countdown.CountdownStep):Null<FunkinSprite>
{
var result = new FunkinSprite();
switch (step)
{
case THREE:
if (_data.assets.countdownThree == null) return fallback?.buildCountdownSprite(step);
var assetPath = buildCountdownSpritePath(step);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.countdownThree?.scale ?? 1.0;
result.scale.y = _data.assets.countdownThree?.scale ?? 1.0;
case TWO:
if (_data.assets.countdownTwo == null) return fallback?.buildCountdownSprite(step);
var assetPath = buildCountdownSpritePath(step);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.countdownTwo?.scale ?? 1.0;
result.scale.y = _data.assets.countdownTwo?.scale ?? 1.0;
case ONE:
if (_data.assets.countdownOne == null) return fallback?.buildCountdownSprite(step);
var assetPath = buildCountdownSpritePath(step);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.countdownOne?.scale ?? 1.0;
result.scale.y = _data.assets.countdownOne?.scale ?? 1.0;
case GO:
if (_data.assets.countdownGo == null) return fallback?.buildCountdownSprite(step);
var assetPath = buildCountdownSpritePath(step);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.countdownGo?.scale ?? 1.0;
result.scale.y = _data.assets.countdownGo?.scale ?? 1.0;
default:
// TODO: Do something here?
return null;
}
result.scrollFactor.set(0, 0);
result.antialiasing = !isCountdownSpritePixel(step);
result.updateHitbox();
return result;
}
function buildCountdownSpritePath(step:Countdown.CountdownStep):Null<String>
{
var basePath:Null<String> = null;
switch (step)
{
case THREE:
basePath = _data.assets.countdownThree?.assetPath;
case TWO:
basePath = _data.assets.countdownTwo?.assetPath;
case ONE:
basePath = _data.assets.countdownOne?.assetPath;
case GO:
basePath = _data.assets.countdownGo?.assetPath;
default:
basePath = null;
}
if (basePath == null) return fallback?.buildCountdownSpritePath(step);
var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length < 1) return null;
if (parts.length == 1) return parts[0];
return parts[1];
}
function buildCountdownSpriteLibrary(step:Countdown.CountdownStep):Null<String>
{
var basePath:Null<String> = null;
switch (step)
{
case THREE:
basePath = _data.assets.countdownThree?.assetPath;
case TWO:
basePath = _data.assets.countdownTwo?.assetPath;
case ONE:
basePath = _data.assets.countdownOne?.assetPath;
case GO:
basePath = _data.assets.countdownGo?.assetPath;
default:
basePath = null;
}
if (basePath == null) return fallback?.buildCountdownSpriteLibrary(step);
var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length <= 1) return null;
return parts[0];
}
public function isCountdownSpritePixel(step:Countdown.CountdownStep):Bool
{
switch (step)
{
case THREE:
var result = _data.assets.countdownThree?.isPixel;
if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step);
return result ?? false;
case TWO:
var result = _data.assets.countdownTwo?.isPixel;
if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step);
return result ?? false;
case ONE:
var result = _data.assets.countdownOne?.isPixel;
if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step);
return result ?? false;
case GO:
var result = _data.assets.countdownGo?.isPixel;
if (result == null && fallback != null) result = fallback.isCountdownSpritePixel(step);
return result ?? false;
default:
return false;
}
}
public function getCountdownSpriteOffsets(step:Countdown.CountdownStep):Array<Float>
{
switch (step)
{
case THREE:
var result = _data.assets.countdownThree?.offsets;
if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step);
return result ?? [0, 0];
case TWO:
var result = _data.assets.countdownTwo?.offsets;
if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step);
return result ?? [0, 0];
case ONE:
var result = _data.assets.countdownOne?.offsets;
if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step);
return result ?? [0, 0];
case GO:
var result = _data.assets.countdownGo?.offsets;
if (result == null && fallback != null) result = fallback.getCountdownSpriteOffsets(step);
return result ?? [0, 0];
default:
return [0, 0];
}
}
public function getCountdownSoundPath(step:Countdown.CountdownStep, raw:Bool = false):Null<String>
{
if (raw)
{
// TODO: figure out why ?. didn't work here
var rawPath:Null<String> = switch (step)
{
case Countdown.CountdownStep.THREE:
_data.assets.countdownThree?.data?.audioPath;
case Countdown.CountdownStep.TWO:
_data.assets.countdownTwo?.data?.audioPath;
case Countdown.CountdownStep.ONE:
_data.assets.countdownOne?.data?.audioPath;
case Countdown.CountdownStep.GO:
_data.assets.countdownGo?.data?.audioPath;
default:
null;
}
return (rawPath == null && fallback != null) ? fallback.getCountdownSoundPath(step, true) : rawPath;
}
// library:path
var parts = getCountdownSoundPath(step, true)?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length == 0) return null;
if (parts.length == 1) return Paths.image(parts[0]);
return Paths.sound(parts[1], parts[0]);
}
public function buildJudgementSprite(rating:String):Null<FunkinSprite>
{
var result = new FunkinSprite();
switch (rating)
{
case "sick":
if (_data.assets.judgementSick == null) return fallback?.buildJudgementSprite(rating);
var assetPath = buildJudgementSpritePath(rating);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.judgementSick?.scale ?? 1.0;
result.scale.y = _data.assets.judgementSick?.scale ?? 1.0;
case "good":
if (_data.assets.judgementGood == null) return fallback?.buildJudgementSprite(rating);
var assetPath = buildJudgementSpritePath(rating);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.judgementGood?.scale ?? 1.0;
result.scale.y = _data.assets.judgementGood?.scale ?? 1.0;
case "bad":
if (_data.assets.judgementBad == null) return fallback?.buildJudgementSprite(rating);
var assetPath = buildJudgementSpritePath(rating);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.judgementBad?.scale ?? 1.0;
result.scale.y = _data.assets.judgementBad?.scale ?? 1.0;
case "shit":
if (_data.assets.judgementShit == null) return fallback?.buildJudgementSprite(rating);
var assetPath = buildJudgementSpritePath(rating);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.judgementShit?.scale ?? 1.0;
result.scale.y = _data.assets.judgementShit?.scale ?? 1.0;
default:
return null;
}
result.scrollFactor.set(0.2, 0.2);
var isPixel = isJudgementSpritePixel(rating);
result.antialiasing = !isPixel;
result.pixelPerfectRender = isPixel;
result.pixelPerfectPosition = isPixel;
result.updateHitbox();
return result;
}
public function isJudgementSpritePixel(rating:String):Bool
{
switch (rating)
{
case "sick":
var result = _data.assets.judgementSick?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;
case "good":
var result = _data.assets.judgementGood?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;
case "bad":
var result = _data.assets.judgementBad?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;
case "GO":
var result = _data.assets.judgementShit?.isPixel;
if (result == null && fallback != null) result = fallback.isJudgementSpritePixel(rating);
return result ?? false;
default:
return false;
}
}
function buildJudgementSpritePath(rating:String):Null<String>
{
var basePath:Null<String> = null;
switch (rating)
{
case "sick":
basePath = _data.assets.judgementSick?.assetPath;
case "good":
basePath = _data.assets.judgementGood?.assetPath;
case "bad":
basePath = _data.assets.judgementBad?.assetPath;
case "shit":
basePath = _data.assets.judgementShit?.assetPath;
default:
basePath = null;
}
if (basePath == null) return fallback?.buildJudgementSpritePath(rating);
var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length < 1) return null;
if (parts.length == 1) return parts[0];
return parts[1];
}
public function getJudgementSpriteOffsets(rating:String):Array<Float>
{
switch (rating)
{
case "sick":
var result = _data.assets.judgementSick?.offsets;
if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating);
return result ?? [0, 0];
case "good":
var result = _data.assets.judgementGood?.offsets;
if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating);
return result ?? [0, 0];
case "bad":
var result = _data.assets.judgementBad?.offsets;
if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating);
return result ?? [0, 0];
case "shit":
var result = _data.assets.judgementShit?.offsets;
if (result == null && fallback != null) result = fallback.getJudgementSpriteOffsets(rating);
return result ?? [0, 0];
default:
return [0, 0];
}
}
public function buildComboNumSprite(digit:Int):Null<FunkinSprite>
{
var result = new FunkinSprite();
switch (digit)
{
case 0:
if (_data.assets.comboNumber0 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber0?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber0?.scale ?? 1.0;
case 1:
if (_data.assets.comboNumber1 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber1?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber1?.scale ?? 1.0;
case 2:
if (_data.assets.comboNumber2 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber2?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber2?.scale ?? 1.0;
case 3:
if (_data.assets.comboNumber3 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber3?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber3?.scale ?? 1.0;
case 4:
if (_data.assets.comboNumber4 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber4?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber4?.scale ?? 1.0;
case 5:
if (_data.assets.comboNumber5 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber5?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber5?.scale ?? 1.0;
case 6:
if (_data.assets.comboNumber6 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber6?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber6?.scale ?? 1.0;
case 7:
if (_data.assets.comboNumber7 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber7?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber7?.scale ?? 1.0;
case 8:
if (_data.assets.comboNumber8 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber8?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber8?.scale ?? 1.0;
case 9:
if (_data.assets.comboNumber9 == null) return fallback?.buildComboNumSprite(digit);
var assetPath = buildComboNumSpritePath(digit);
if (assetPath == null) return null;
result.loadTexture(assetPath);
result.scale.x = _data.assets.comboNumber9?.scale ?? 1.0;
result.scale.y = _data.assets.comboNumber9?.scale ?? 1.0;
default:
return null;
}
var isPixel = isComboNumSpritePixel(digit);
result.antialiasing = !isPixel;
result.pixelPerfectRender = isPixel;
result.pixelPerfectPosition = isPixel;
result.updateHitbox();
return result;
}
public function isComboNumSpritePixel(digit:Int):Bool
{
switch (digit)
{
case 0:
var result = _data.assets.comboNumber0?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 1:
var result = _data.assets.comboNumber1?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 2:
var result = _data.assets.comboNumber2?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 3:
var result = _data.assets.comboNumber3?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 4:
var result = _data.assets.comboNumber4?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 5:
var result = _data.assets.comboNumber5?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 6:
var result = _data.assets.comboNumber6?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 7:
var result = _data.assets.comboNumber7?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 8:
var result = _data.assets.comboNumber8?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
case 9:
var result = _data.assets.comboNumber9?.isPixel;
if (result == null && fallback != null) result = fallback.isComboNumSpritePixel(digit);
return result ?? false;
default:
return false;
}
}
function buildComboNumSpritePath(digit:Int):Null<String>
{
var basePath:Null<String> = null;
switch (digit)
{
case 0:
basePath = _data.assets.comboNumber0?.assetPath;
case 1:
basePath = _data.assets.comboNumber1?.assetPath;
case 2:
basePath = _data.assets.comboNumber2?.assetPath;
case 3:
basePath = _data.assets.comboNumber3?.assetPath;
case 4:
basePath = _data.assets.comboNumber4?.assetPath;
case 5:
basePath = _data.assets.comboNumber5?.assetPath;
case 6:
basePath = _data.assets.comboNumber6?.assetPath;
case 7:
basePath = _data.assets.comboNumber7?.assetPath;
case 8:
basePath = _data.assets.comboNumber8?.assetPath;
case 9:
basePath = _data.assets.comboNumber9?.assetPath;
default:
basePath = null;
}
if (basePath == null) return fallback?.buildComboNumSpritePath(digit);
var parts = basePath?.split(Constants.LIBRARY_SEPARATOR) ?? [];
if (parts.length < 1) return null;
if (parts.length == 1) return parts[0];
return parts[1];
}
public function getComboNumSpriteOffsets(digit:Int):Array<Float>
{
switch (digit)
{
case 0:
var result = _data.assets.comboNumber0?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 1:
var result = _data.assets.comboNumber1?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 2:
var result = _data.assets.comboNumber2?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 3:
var result = _data.assets.comboNumber3?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 4:
var result = _data.assets.comboNumber4?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 5:
var result = _data.assets.comboNumber5?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 6:
var result = _data.assets.comboNumber6?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 7:
var result = _data.assets.comboNumber7?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 8:
var result = _data.assets.comboNumber8?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
case 9:
var result = _data.assets.comboNumber9?.offsets;
if (result == null && fallback != null) result = fallback.getComboNumSpriteOffsets(digit);
return result ?? [0, 0];
default:
return [0, 0];
}
}
public function destroy():Void {}
@ -322,8 +867,17 @@ class NoteStyle implements IRegistryEntry<NoteStyleData>
return 'NoteStyle($id)';
}
static function _fetchData(id:String):Null<NoteStyleData>
static function _fetchData(id:String):NoteStyleData
{
return NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id));
var result = NoteStyleRegistry.instance.parseEntryDataWithMigration(id, NoteStyleRegistry.instance.fetchEntryVersion(id));
if (result == null)
{
throw 'Could not parse note style data for id: $id';
}
else
{
return result;
}
}
}

View file

@ -277,7 +277,8 @@ class Song implements IPlayStateScriptedClass implements IRegistryEntry<SongMeta
// If there are no difficulties in the metadata, there's a problem.
if (metadata.playData.difficulties.length == 0)
{
trace('[WARN] Song $id has no difficulties listed in metadata!');
trace('[SONG] Warning: Song $id (variation ${metadata.variation}) has no difficulties listed in metadata!');
continue;
}
// There may be more difficulties in the chart file than in the metadata,

View file

@ -1,6 +1,7 @@
package funkin.play.stage;
import flixel.FlxSprite;
import flixel.FlxCamera;
import flixel.math.FlxPoint;
import flixel.util.FlxTimer;
import funkin.modding.IScriptedClass.IPlayStateScriptedClass;
@ -79,11 +80,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
if (globalOffsets == null) globalOffsets = [0, 0];
if (globalOffsets == value) return value;
var xDiff = globalOffsets[0] - value[0];
var yDiff = globalOffsets[1] - value[1];
this.x += xDiff;
this.y += yDiff;
return globalOffsets = value;
}
@ -97,12 +93,6 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
if (animOffsets == null) animOffsets = [0, 0];
if ((animOffsets[0] == value[0]) && (animOffsets[1] == value[1])) return value;
var xDiff = animOffsets[0] - value[0];
var yDiff = animOffsets[1] - value[1];
this.x += xDiff;
this.y += yDiff;
return animOffsets = value;
}
@ -320,14 +310,7 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
function applyAnimationOffsets(name:String):Void
{
var offsets = animationOffsets.get(name);
if (offsets != null && !(offsets[0] == 0 && offsets[1] == 0))
{
this.animOffsets = [offsets[0] + globalOffsets[0], offsets[1] + globalOffsets[1]];
}
else
{
this.animOffsets = globalOffsets;
}
this.animOffsets = offsets;
}
public function isAnimationFinished():Bool
@ -351,6 +334,15 @@ class Bopper extends StageProp implements IPlayStateScriptedClass
return this.animation.curAnim.name;
}
// override getScreenPosition (used by FlxSprite's draw method) to account for animation offsets.
override function getScreenPosition(?result:FlxPoint, ?camera:FlxCamera):FlxPoint
{
var output:FlxPoint = super.getScreenPosition(result, camera);
output.x -= (animOffsets[0] - globalOffsets[0]) * this.scale.x;
output.y -= (animOffsets[1] - globalOffsets[1]) * this.scale.y;
return output;
}
public function onPause(event:PauseScriptEvent) {}
public function onResume(event:ScriptEvent) {}

View file

@ -5701,11 +5701,11 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
{
case 'mainStage':
PlayStatePlaylist.campaignId = 'week1';
case 'spookyMansion':
case 'spookyMansion' | 'spookyMansionErect':
PlayStatePlaylist.campaignId = 'week2';
case 'phillyTrain':
case 'phillyTrain' | 'phillyTrainErect':
PlayStatePlaylist.campaignId = 'week3';
case 'limoRide':
case 'limoRide' | 'limoRideErect':
PlayStatePlaylist.campaignId = 'week4';
case 'mallXmas' | 'mallEvil':
PlayStatePlaylist.campaignId = 'week5';

View file

@ -60,7 +60,9 @@ class ChartEditorHoldNoteSprite extends SustainTrail
override function setupHoldNoteGraphic(noteStyle:NoteStyle):Void
{
loadGraphic(noteStyle.getHoldNoteAssetPath());
var graphicPath = noteStyle.getHoldNoteAssetPath();
if (graphicPath == null) return;
loadGraphic(graphicPath);
antialiasing = true;

View file

@ -291,28 +291,47 @@ class LoadingState extends MusicBeatSubState
FunkinSprite.preparePurgeCache();
FunkinSprite.cacheTexture(Paths.image('healthBar'));
FunkinSprite.cacheTexture(Paths.image('menuDesat'));
FunkinSprite.cacheTexture(Paths.image('combo'));
FunkinSprite.cacheTexture(Paths.image('num0'));
FunkinSprite.cacheTexture(Paths.image('num1'));
FunkinSprite.cacheTexture(Paths.image('num2'));
FunkinSprite.cacheTexture(Paths.image('num3'));
FunkinSprite.cacheTexture(Paths.image('num4'));
FunkinSprite.cacheTexture(Paths.image('num5'));
FunkinSprite.cacheTexture(Paths.image('num6'));
FunkinSprite.cacheTexture(Paths.image('num7'));
FunkinSprite.cacheTexture(Paths.image('num8'));
FunkinSprite.cacheTexture(Paths.image('num9'));
// Lord have mercy on me and this caching -anysad
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/combo'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num0'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num1'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num2'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num3'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num4'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num5'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num6'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num7'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num8'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/funkin/num9'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/combo'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num0'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num1'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num2'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num3'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num4'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num5'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num6'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num7'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num8'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/num9'));
FunkinSprite.cacheTexture(Paths.image('notes', 'shared'));
FunkinSprite.cacheTexture(Paths.image('noteSplashes', 'shared'));
FunkinSprite.cacheTexture(Paths.image('noteStrumline', 'shared'));
FunkinSprite.cacheTexture(Paths.image('NOTE_hold_assets'));
FunkinSprite.cacheTexture(Paths.image('ready', 'shared'));
FunkinSprite.cacheTexture(Paths.image('set', 'shared'));
FunkinSprite.cacheTexture(Paths.image('go', 'shared'));
FunkinSprite.cacheTexture(Paths.image('sick', 'shared'));
FunkinSprite.cacheTexture(Paths.image('good', 'shared'));
FunkinSprite.cacheTexture(Paths.image('bad', 'shared'));
FunkinSprite.cacheTexture(Paths.image('shit', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/ready', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/set', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/funkin/go', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/ready', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/set', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/countdown/pixel/go', 'shared'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/sick'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/good'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/bad'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/normal/shit'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/sick'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/good'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/bad'));
FunkinSprite.cacheTexture(Paths.image('ui/popup/pixel/shit'));
FunkinSprite.cacheTexture(Paths.image('miss', 'shared')); // TODO: remove this
// List all image assets in the level's library.

View file

@ -258,6 +258,11 @@ class Constants
*/
public static final DEFAULT_NOTE_STYLE:String = 'funkin';
/**
* The default pixel note style for songs.
*/
public static final DEFAULT_PIXEL_NOTE_STYLE:String = 'pixel';
/**
* The default album for songs in Freeplay.
*/