Merge branch 'rewrite/master' into funkin-mobile/main

This commit is contained in:
EliteMasterEric
2025-05-01 21:46:20 -04:00
33 changed files with 347 additions and 124 deletions
+9 -8
View File
@@ -74,14 +74,6 @@ class Main extends Sprite
function init(?event:Event):Void
{
#if web
// set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx
// The framerate cap will more thoroughly initialize via Preferences in InitState.hx
funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
#end
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
if (hasEventListener(Event.ADDED_TO_STAGE))
{
removeEventListener(Event.ADDED_TO_STAGE, init);
@@ -125,6 +117,15 @@ class Main extends Sprite
// George recommends binding the save before FlxGame is created.
Save.load();
// Don't call anything from the preferences until the save is loaded!
#if web
// set this variable (which is a function) from the lime version at lime/_internal/backend/html5/HTML5Application.hx
// The framerate cap will more thoroughly initialize via Preferences in InitState.hx
funkin.Preferences.lockedFramerateFunction = untyped js.Syntax.code("window.requestAnimationFrame");
#end
WindowUtil.setVSyncMode(funkin.Preferences.vsyncMode);
var game:FlxGame = new FlxGame(gameWidth, gameHeight, initialState, Preferences.framerate, Preferences.framerate, skipSplash, startFullscreen);
// FlxG.game._customSoundTray wants just the class, it calls new from
+28 -2
View File
@@ -213,15 +213,41 @@ class Preferences
static function get_vsyncMode():lime.ui.WindowVSyncMode
{
return Save?.instance?.options?.vsyncMode ?? lime.ui.WindowVSyncMode.OFF;
var value = Save?.instance?.options?.vsyncMode ?? "Off";
return switch (value)
{
case "Off":
lime.ui.WindowVSyncMode.OFF;
case "On":
lime.ui.WindowVSyncMode.ON;
case "Adaptive":
lime.ui.WindowVSyncMode.ADAPTIVE;
default:
lime.ui.WindowVSyncMode.OFF;
};
}
static function set_vsyncMode(value:lime.ui.WindowVSyncMode):lime.ui.WindowVSyncMode
{
var string;
switch (value)
{
case lime.ui.WindowVSyncMode.OFF:
string = "Off";
case lime.ui.WindowVSyncMode.ON:
string = "On";
case lime.ui.WindowVSyncMode.ADAPTIVE:
string = "Adaptive";
default:
string = "Off";
};
WindowUtil.setVSyncMode(value);
var save:Save = Save.instance;
save.options.vsyncMode = value;
save.options.vsyncMode = string;
save.flush();
return value;
}
+1 -1
View File
@@ -67,7 +67,7 @@ class Medals
NewgroundsMedalPlugin.play(medalData.value, medalData.name, medalGraphic);
});
#else
if (medalJSON == null) loadMedalJSON();
if ((medalJSON?.length ?? 0) == 0) loadMedalJSON();
// We have to use a medal image from the game files. We use a Base64 encoded image that NG spits out.
// TODO: Wait, don't they give us the medal icon?
+58 -4
View File
@@ -14,6 +14,10 @@ import funkin.util.tools.ICloneable;
import funkin.util.flixel.sound.FlxPartialSound;
import funkin.Paths.PathsFunction;
import lime.app.Promise;
import lime.media.AudioSource;
import openfl.events.Event;
import openfl.media.Sound;
import openfl.media.SoundChannel;
import openfl.media.SoundMixer;
/**
@@ -105,6 +109,11 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
return _waveformData;
}
/**
* If true, the game will forcefully add this sound's channel to the list of playing sounds.
*/
public var important:Bool = false;
/**
* Are we in a state where the song should play but time is negative?
*/
@@ -142,6 +151,14 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
else
{
super.update(elapsedSec);
@:privateAccess
{
if (important && _channel != null && !SoundMixer.__soundChannels.contains(_channel))
{
SoundMixer.__soundChannels.push(_channel);
}
}
}
}
@@ -428,13 +445,14 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
* @param persist Whether to keep this `FunkinSound` between states, or destroy it.
* @param onComplete Called when the sound finished playing.
* @param onLoad Called when the sound finished loading. Called immediately for succesfully loaded embedded sounds.
* @param important If `true`, the sound channel will forcefully be added onto the channel array, even if full. Use sparingly!
* @return A `FunkinSound` object, or `null` if the sound could not be loaded.
*/
public static function load(embeddedSound:FlxSoundAsset, volume:Float = 1.0, looped:Bool = false, autoDestroy:Bool = false, autoPlay:Bool = false,
persist:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound>
persist:Bool = false, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null<FunkinSound>
{
@:privateAccess
if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS)
if (SoundMixer.__soundChannels.length >= SoundMixer.MAX_ACTIVE_CHANNELS && !important)
{
FlxG.log.error('FunkinSound could not play sound, channels exhausted! Found ${SoundMixer.__soundChannels.length} active sound channels.');
return null;
@@ -459,6 +477,7 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
sound.volume = volume;
sound.group = FlxG.sound.defaultSoundGroup;
sound.persist = persist;
sound.important = important;
// Make sure to add the sound to the list.
// If it's already in, it won't get re-added.
@@ -531,15 +550,50 @@ class FunkinSound extends FlxSound implements ICloneable<FunkinSound>
this._label = 'unknown';
}
@:access(openfl.media.Sound)
@:access(openfl.media.SoundChannel)
@:access(openfl.media.SoundMixer)
override function startSound(startTime:Float)
{
if (!important)
{
super.startSound(startTime);
return;
}
_time = startTime;
_paused = false;
if (_sound == null) return;
// Create a channel manually if the sound is considered important.
var pan:Float = FlxMath.bound(SoundMixer.__soundTransform.pan + _transform.pan, -1, 1);
var volume:Float = FlxMath.bound(SoundMixer.__soundTransform.volume * _transform.volume, 0, MAX_VOLUME);
var audioSource:AudioSource = new AudioSource(_sound.__buffer);
audioSource.offset = Std.int(startTime);
audioSource.gain = volume;
var position:lime.math.Vector4 = audioSource.position;
position.x = pan;
position.z = -1 * Math.sqrt(1 - Math.pow(pan, 2));
audioSource.position = position;
_channel = new SoundChannel(_sound, audioSource, _transform);
_channel.addEventListener(Event.SOUND_COMPLETE, stopped);
pitch = _pitch;
active = true;
}
/**
* Play a sound effect once, then destroy it.
* @param key
* @param volume
* @return A `FunkinSound` object, or `null` if the sound could not be loaded.
*/
public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void):Null<FunkinSound>
public static function playOnce(key:String, volume:Float = 1.0, ?onComplete:Void->Void, ?onLoad:Void->Void, important:Bool = false):Null<FunkinSound>
{
var result:Null<FunkinSound> = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad);
var result:Null<FunkinSound> = FunkinSound.load(key, volume, false, true, true, false, onComplete, onLoad, important);
return result;
}
+1 -1
View File
@@ -255,7 +255,7 @@ class Countdown
var path = noteStyle.getCountdownSoundPath(step);
if (path == null) return null;
return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME);
return FunkinSound.playOnce(path, Constants.COUNTDOWN_VOLUME, null, null, true);
}
public static function decrement(step:CountdownStep):CountdownStep
+7 -10
View File
@@ -961,14 +961,8 @@ class PlayState extends MusicBeatSubState
});
// Reset the health icons.
if (currentStage.getBoyfriend() != null)
{
currentStage.getBoyfriend().initHealthIcon(false);
}
if (currentStage.getDad() != null)
{
currentStage.getDad().initHealthIcon(true);
}
currentStage?.getBoyfriend()?.initHealthIcon(false);
currentStage?.getDad()?.initHealthIcon(true);
needsReset = false;
}
@@ -1518,9 +1512,10 @@ class PlayState extends MusicBeatSubState
*/
override function reloadAssets():Void
{
performCleanup();
funkin.modding.PolymodHandler.forceReloadAssets();
lastParams.targetSong = SongRegistry.instance.fetchEntry(currentSong.id);
this.remove(currentStage);
LoadingState.loadPlayState(lastParams);
}
@@ -2577,7 +2572,7 @@ class PlayState extends MusicBeatSubState
dispatchEvent(event);
trace('Penalizing score by ${event.score} and health by ${event.healthChange} for dropping hold note (is combo break: ${event.isComboBreak})!');
applyScore(event.score, 'miss', event.healthChange, event.isComboBreak);
applyScore(event.score, '', event.healthChange, event.isComboBreak);
// Play the miss sound.
vocals.playerVolume = 0;
@@ -2923,6 +2918,8 @@ class PlayState extends MusicBeatSubState
Highscore.tallies.shit += 1;
case 'miss':
Highscore.tallies.missed += 1;
default:
// Nothing!
}
health += healthChange;
if (isComboBreak)
+2 -1
View File
@@ -510,7 +510,8 @@ class ResultState extends MusicBeatSubState
FlxTween.tween(bgFlash, {alpha: 0}, 5 / 24);
// NOTE: Only divide if totalNotes > 0 to prevent divide-by-zero errors.
var clearPercentFloat = params.scoreData.tallies.totalNotes == 0 ? 0.0 : (params.scoreData.tallies.sick +
params.scoreData.tallies.good) / params.scoreData.tallies.totalNotes * 100;
params.scoreData.tallies.good
- params.scoreData.tallies.missed) / params.scoreData.tallies.totalNotes * 100;
clearPercentTarget = Math.floor(clearPercentFloat);
// Prevent off-by-one errors.
@@ -65,7 +65,7 @@ class ClearPercentCounter extends FlxTypedSpriteGroup<FlxSprite>
var seperatedScore:Array<Int> = [];
var tempCombo:Int = Math.round(curNumber);
while (tempCombo != 0)
while (tempCombo > 0 && tempCombo != 0)
{
seperatedScore.push(tempCombo % 10);
tempCombo = Math.floor(tempCombo / 10);
+1 -1
View File
@@ -384,7 +384,7 @@ class Scoring
// Final Grade = (Sick + Good - Miss) / (Total Notes)
var grade = (scoreData.tallies.sick + scoreData.tallies.good) / scoreData.tallies.totalNotes;
var grade = (scoreData.tallies.sick + scoreData.tallies.good - scoreData.tallies.missed) / scoreData.tallies.totalNotes;
if (grade == Constants.RANK_PERFECT_THRESHOLD)
{
+8 -3
View File
@@ -801,7 +801,7 @@ class SongDifficulty
{
var suffix:String = (instId != '') ? '-$instId' : '';
FlxG.sound.music = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, true);
FlxG.sound.music = FunkinSound.load(Paths.inst(this.song.id, suffix), volume, looped, false, true, false, null, null, true);
// Workaround for a bug where FlxG.sound.music.update() was being called twice.
FlxG.sound.list.remove(FlxG.sound.music);
@@ -948,16 +948,21 @@ class SongDifficulty
for (playerVoice in playerVoiceList)
{
if (!Assets.exists(playerVoice)) continue;
result.addPlayerVoice(FunkinSound.load(playerVoice));
result.addPlayerVoice(FunkinSound.load(playerVoice, 1.0, false, false, false, false, null, null, true));
}
// Add opponent vocals.
for (opponentVoice in opponentVoiceList)
{
if (!Assets.exists(opponentVoice)) continue;
result.addOpponentVoice(FunkinSound.load(opponentVoice));
result.addOpponentVoice(FunkinSound.load(opponentVoice, 1.0, false, false, false, false, null, null, true));
}
// Sometimes the sounds don't set their important value to true, so we have to do this manually.
result.forEach(function(snd:FunkinSound) {
snd.important = true;
});
result.playerVoicesOffset = offsets.getVocalOffset(characters.player, instId);
result.opponentVoicesOffset = offsets.getVocalOffset(characters.opponent, instId);
+3 -3
View File
@@ -123,7 +123,7 @@ class Save
debugDisplay: false,
vibration: true,
autoPause: true,
vsyncMode: lime.ui.WindowVSyncMode.OFF,
vsyncMode: 'Off',
strumlineBackgroundOpacity: 0,
autoFullscreen: false,
inputOffset: 0,
@@ -1450,9 +1450,9 @@ typedef SaveDataOptions =
/**
* If enabled, the game will utilize VSync (or adaptive VSync) on startup.
* @default `OFF`
* @default `Off`
*/
var vsyncMode:lime.ui.WindowVSyncMode;
var vsyncMode:String;
/**
* If >0, the game will display a semi-opaque background under the notes.
@@ -1334,7 +1334,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
result = [];
trace('Initializing blank chart for difficulty ' + selectedDifficulty);
currentSongChartData.notes.set(selectedDifficulty, result);
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
return result;
}
return result;
@@ -1343,7 +1342,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
function set_currentSongChartNoteData(value:Array<SongNoteData>):Array<SongNoteData>
{
currentSongChartData.notes.set(selectedDifficulty, value);
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
return value;
}
@@ -1636,9 +1634,6 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
noteTooltipsDirty = true;
notePreviewViewportBoundsDirty = true;
// Make sure the difficulty we selected is in the list of difficulties.
currentSongMetadata.playData.difficulties.pushUnique(selectedDifficulty);
return selectedDifficulty;
}
@@ -117,6 +117,12 @@ class ChartEditorImportExportHandler
{
state.songMetadata = newSongMetadata;
state.songChartData = newSongChartData;
state.selectedDifficulty = state.availableDifficulties[0];
if (!newSongMetadata.exists(state.selectedVariation))
{
state.selectedVariation = Constants.DEFAULT_VARIATION;
}
Conductor.instance.forceBPM(null); // Disable the forced BPM.
Conductor.instance.instrumentalOffset = state.currentInstrumentalOffset; // Loads from the metadata.
@@ -343,6 +349,12 @@ class ChartEditorImportExportHandler
var variations = state.availableVariations;
if (state.currentSongMetadata.playData.difficulties.pushUnique(state.selectedDifficulty))
{
// Just in case the user deleted all or didn't add a difficulty
state.difficultySelectDirty = true;
}
for (variation in variations)
{
var variationId:String = variation;
@@ -29,6 +29,10 @@ class CapsuleOptionsMenu extends FlxSpriteGroup
var leftArrow:InstrumentalSelector;
var rightArrow:InstrumentalSelector;
var leftArrow:InstrumentalSelector;
var rightArrow:InstrumentalSelector;
public function new(parent:FreeplayState, x:Float = 0, y:Float = 0, instIds:Array<String>):Void
{
super(x, y);
@@ -126,6 +130,8 @@ class CapsuleOptionsMenu extends FlxSpriteGroup
{
// Play in reverse.
capsuleMenuBG.animation.play('open', true, true);
if (leftArrow.moveShitDownTimer != null) leftArrow.moveShitDownTimer.cancel();
if (rightArrow.moveShitDownTimer != null) rightArrow.moveShitDownTimer.cancel();
capsuleMenuBG.animation.finishCallback = function(_) {
parent.cleanupCapsuleOptionsMenu();
queueDestroy = true;
@@ -153,6 +159,8 @@ class InstrumentalSelector extends FunkinSprite
var baseScale:Float = 0.6;
public var moveShitDownTimer:FlxTimer;
public function new(parent:FreeplayState, x:Float, y:Float, flipped:Bool, controls:Controls)
{
super(x, y);
@@ -191,7 +199,7 @@ class InstrumentalSelector extends FunkinSprite
scale.x = scale.y = 0.5 * baseScale;
new FlxTimer().start(2 / 24, function(tmr) {
moveShitDownTimer = new FlxTimer().start(2 / 24, function(tmr) {
scale.x = scale.y = 1 * baseScale;
whiteShader.colorSet = false;
updateHitbox();
+2
View File
@@ -168,6 +168,8 @@ class CapsuleText extends FlxSpriteGroup
public function resetText():Void
{
scale.x = 1;
scale.y = 1;
if (moveTween != null) moveTween.cancel();
if (moveTimer != null) moveTimer.cancel();
whiteText.offset.x = 0;
+1 -1
View File
@@ -87,7 +87,7 @@ class FreeplayDJ extends FlxAtlasSprite
case Intro:
// Play the intro animation then leave this state immediately.
var animPrefix = playableCharData.getAnimationPrefix('intro');
if (getCurrentAnimation() != animPrefix) playFlashAnimation(animPrefix, true);
if (getCurrentAnimation() != animPrefix || !this.anim.isPlaying) playFlashAnimation(animPrefix, true);
timeIdling = 0;
case Idle:
// We are in this state the majority of the time.
+40 -26
View File
@@ -576,10 +576,10 @@ class FreeplayState extends MusicBeatSubState
generateSongList({filterType: REGEXP, filterData: str}, true);
}
// If the current song is still in the list (aka not null?), we'll land on it
// If the current song is still in the list, or if it was random, we'll land on it
// Otherwise we want to land on the first song of the group, rather than random song when changing letter sorts
// that is, only if there's more than one song in the group!
if (curSong != null && currentFilteredSongs.length > 1)
if (curSong == null || currentFilteredSongs.contains(curSong))
{
changeSelection();
}
@@ -733,7 +733,8 @@ class FreeplayState extends MusicBeatSubState
allDifficulties = SongRegistry.instance.listAllDifficulties(currentCharacterId);
// Generates song list with the starter params (who our current character is, last remembered difficulty, etc.)
generateSongList(null, false);
// Set this to false if you prefer the 50% transparency on the capsules when they first appear.
generateSongList(null, true);
// dedicated camera for the state so we don't need to fuk around with camera scrolls from the mainmenu / elsewhere
funnyCam.bgColor = FlxColor.TRANSPARENT;
@@ -835,10 +836,10 @@ class FreeplayState extends MusicBeatSubState
// Initialize the random capsule, with empty/blank info (which we display once bf/pico does his hand)
var randomCapsule:SongMenuItem = grpCapsules.recycle(SongMenuItem);
randomCapsule.initPosition(FlxG.width, 0);
randomCapsule.initData(null, styleData);
randomCapsule.initData(null, styleData, 1);
randomCapsule.y = randomCapsule.intendedY(0) + 10;
randomCapsule.targetPos.x = randomCapsule.x;
randomCapsule.alpha = 0;
randomCapsule.alpha = 0.5;
randomCapsule.songText.visible = false;
randomCapsule.favIcon.visible = false;
randomCapsule.favIconBlurred.visible = false;
@@ -850,8 +851,7 @@ class FreeplayState extends MusicBeatSubState
if (fromCharSelect) randomCapsule.forcePosition();
else
{
// randomCapsule.initJumpIn(0, force);
randomCapsule.forcePosition();
randomCapsule.initJumpIn(0, force);
}
var hsvShader:HSVShader = new HSVShader();
@@ -866,7 +866,7 @@ class FreeplayState extends MusicBeatSubState
var funnyMenu:SongMenuItem = grpCapsules.recycle(SongMenuItem);
funnyMenu.initPosition(FlxG.width, 0);
funnyMenu.initData(tempSong, styleData);
funnyMenu.initData(tempSong, styleData, i + 1);
funnyMenu.onConfirm = function() {
capsuleOnOpenDefault(funnyMenu);
};
@@ -877,10 +877,10 @@ class FreeplayState extends MusicBeatSubState
funnyMenu.hsvShader = hsvShader;
funnyMenu.newText.animation.curAnim.curFrame = 45 - ((i * 4) % 45);
// This stops the bounce-in animation.
funnyMenu.forcePosition();
// funnyMenu.initJumpIn(0, force);
// Stop the bounce-in animation when returning to freeplay from the character selection screen.
if (fromCharSelect) funnyMenu.forcePosition();
else
funnyMenu.initJumpIn(0, force);
grpCapsules.add(funnyMenu);
// add(funnyMenu.theActualHitbox);
@@ -1983,14 +1983,14 @@ class FreeplayState extends MusicBeatSubState
if (change != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
var previousVariation:String = currentVariation;
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
// Available variations for current character. We get this since bf is usually `default` variation, and `pico` is `pico`
// but sometimes pico can be the default variation (weekend 1 songs), and bf can be `bf` variation (darnell)
var characterVariations:Array<String> = grpCapsules.members[curSelected].freeplayData?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
var characterVariations:Array<String> = daSong?.data.getVariationsByCharacter(currentCharacter) ?? Constants.DEFAULT_VARIATION_LIST;
var difficultiesAvailable:Array<String> = allDifficulties;
// Gets all available difficulties for our character, via our available variations
var songDifficulties:Array<String> = grpCapsules.members[curSelected].freeplayData?.data.listDifficulties(null,
characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST;
var songDifficulties:Array<String> = daSong?.data.listDifficulties(null, characterVariations) ?? Constants.DEFAULT_DIFFICULTY_LIST;
var currentDifficultyIndex:Int = difficultiesAvailable.indexOf(currentDifficulty);
@@ -2003,15 +2003,16 @@ class FreeplayState extends MusicBeatSubState
// Update the current difficulty
currentDifficulty = difficultiesAvailable[currentDifficultyIndex];
// For when we change the difficulty, but the song doesn't have that difficulty!
if (!songDifficulties.contains(difficultiesAvailable[currentDifficultyIndex]))
if (daSong != null && !songDifficulties.contains(difficultiesAvailable[currentDifficultyIndex]))
{
curSelected = findClosestDiff(characterVariations, difficultiesAvailable[currentDifficultyIndex]);
rememberedSongId = grpCapsules.members[curSelected].freeplayData?.data.id;
daSong = grpCapsules.members[curSelected].freeplayData;
rememberedSongId = daSong?.data.id;
}
for (variation in characterVariations)
{
if (grpCapsules.members[curSelected].freeplayData?.data.hasDifficulty(currentDifficulty, variation) ?? false)
if (daSong?.data.hasDifficulty(currentDifficulty, variation) ?? false)
{
currentVariation = variation;
rememberedVariation = variation;
@@ -2019,7 +2020,6 @@ class FreeplayState extends MusicBeatSubState
}
}
var daSong:Null<FreeplaySongData> = grpCapsules.members[curSelected].freeplayData;
if (daSong != null)
{
var targetSong:Null<Song> = SongRegistry.instance.fetchEntry(daSong.data.id);
@@ -2033,7 +2033,7 @@ class FreeplayState extends MusicBeatSubState
intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
rememberedDifficulty = currentDifficulty;
grpCapsules.members[curSelected].refreshDisplay();
grpCapsules.members[curSelected].refreshDisplay((prepForNewRank == true) ? false : true);
}
else
{
@@ -2172,11 +2172,15 @@ class FreeplayState extends MusicBeatSubState
*/
function capsuleOnOpenDefault(cap:SongMenuItem):Void
{
busy = true;
letterSort.inputEnabled = false;
var targetSongId:String = cap?.freeplayData?.data.id ?? 'unknown';
var targetSongNullable:Null<Song> = SongRegistry.instance.fetchEntry(targetSongId);
if (targetSongNullable == null)
{
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
busy = false;
letterSort.inputEnabled = true;
return;
}
var targetSong:Song = targetSongNullable;
@@ -2190,6 +2194,8 @@ class FreeplayState extends MusicBeatSubState
if (targetDifficulty == null)
{
FlxG.log.warn('WARN: could not find difficulty with id (${targetDifficultyId})');
busy = false;
letterSort.inputEnabled = true;
return;
}
@@ -2219,9 +2225,7 @@ class FreeplayState extends MusicBeatSubState
function openInstrumentalList(cap:SongMenuItem, instrumentalIds:Array<String>):Void
{
busy = true;
capsuleOptionsMenu = new CapsuleOptionsMenu(this, cap.x + 175, cap.y + 115, instrumentalIds);
capsuleOptionsMenu = new CapsuleOptionsMenu(this, cap.targetPos.x + 175, cap.targetPos.y + 115, instrumentalIds);
capsuleOptionsMenu.cameras = [funnyCam];
capsuleOptionsMenu.zIndex = 10000;
add(capsuleOptionsMenu);
@@ -2236,6 +2240,7 @@ class FreeplayState extends MusicBeatSubState
public function cleanupCapsuleOptionsMenu():Void
{
this.busy = false;
letterSort.inputEnabled = true;
if (capsuleOptionsMenu != null)
{
@@ -2261,6 +2266,8 @@ class FreeplayState extends MusicBeatSubState
if (targetSongNullable == null)
{
FlxG.log.warn('WARN: could not find song with id (${targetSongId})');
busy = false;
letterSort.inputEnabled = true;
return;
}
var targetSong:Song = targetSongNullable;
@@ -2272,6 +2279,8 @@ class FreeplayState extends MusicBeatSubState
if (targetDifficulty == null)
{
FlxG.log.warn('WARN: could not find difficulty with id (${currentDifficulty})');
busy = false;
letterSort.inputEnabled = true;
return;
}
@@ -2414,10 +2423,11 @@ class FreeplayState extends MusicBeatSubState
{
var songScore:Null<SaveScoreData> = Save.instance.getSongScore(daSongCapsule.freeplayData.data.id, currentDifficulty, currentVariation);
intendedScore = songScore?.score ?? 0;
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick + songScore.tallies.good) / songScore.tallies.totalNotes);
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick +
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes);
rememberedSongId = daSongCapsule.freeplayData.data.id;
changeDiff();
daSongCapsule.refreshDisplay();
daSongCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
}
else
{
@@ -2425,6 +2435,8 @@ class FreeplayState extends MusicBeatSubState
intendedCompletion = 0.0;
rememberedSongId = null;
albumRoll.albumId = null;
changeDiff();
daSongCapsule.refreshDisplay();
}
for (index => capsule in grpCapsules.members)
@@ -2434,8 +2446,10 @@ class FreeplayState extends MusicBeatSubState
capsule.forceHighlight = false;
capsule.selected = index == curSelected + 1;
capsule.curSelected = curSelected;
capsule.targetPos.y = capsule.intendedY(index - curSelected);
capsule.targetPos.x = (270 + (60 * (Math.sin(index - curSelected)))) + (CUTOUT_WIDTH * SONGS_POS_MULTI);
capsule.targetPos.x = capsule.intendedX(index - curSelected) + (CUTOUT_WIDTH * SONGS_POS_MULTI);
if (index < curSelected) capsule.targetPos.y -= 100; // another 100 for good measure
}
+4 -1
View File
@@ -284,6 +284,9 @@ class FreeplayLetter extends FlxAtlasSprite
this.anim.play(animLetters[letterInd] + " move");
this.anim.pause();
curLetter = letterInd;
this.anim.onComplete.add(function() {
this.anim.play(animLetters[curLetter] + " move");
});
}
}
@@ -311,7 +314,7 @@ class FreeplayLetter extends FlxAtlasSprite
animName = "T move";
}
this.anim.play(animName);
this.anim.play(animName, true);
if (curSelection != curLetter)
{
this.anim.pause();
+30 -11
View File
@@ -82,6 +82,10 @@ class SongMenuItem extends FlxSpriteGroup
public var theActualHitbox:FlxObject;
var index:Int;
public var curSelected:Int;
public function new(x:Float, y:Float)
{
super(x, y);
@@ -402,7 +406,7 @@ class SongMenuItem extends FlxSpriteGroup
return evilTrail.color;
}
public function refreshDisplay():Void
public function refreshDisplay(updateRank:Bool = true):Void
{
if (freeplayData == null)
{
@@ -420,7 +424,7 @@ class SongMenuItem extends FlxSpriteGroup
pixelIcon.visible = true;
updateBPM(Std.int(freeplayData.songStartingBpm) ?? 0);
updateDifficultyRating(freeplayData.difficultyRating ?? 0);
updateScoringRank(freeplayData.scoringRank);
if (updateRank) updateScoringRank(freeplayData.scoringRank);
newText.visible = freeplayData.isNew;
favIcon.visible = freeplayData.isFav;
favIconBlurred.visible = freeplayData.isFav;
@@ -501,7 +505,7 @@ class SongMenuItem extends FlxSpriteGroup
spr.visible = value;
}
if (value) textAppear();
textAppear();
updateSelected();
}
@@ -512,10 +516,12 @@ class SongMenuItem extends FlxSpriteGroup
this.y = y;
}
public function initData(freeplayData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null):Void
public function initData(freeplayData:Null<FreeplaySongData>, ?styleData:FreeplayStyle = null, index:Int = null):Void
{
this.freeplayData = freeplayData;
if (index != null) this.index = index;
// im so mad i have to do this but im pretty sure with the capsules recycling i cant call the new function properly :/
// if thats possible someone Please change the new function to be something like
// capsule.frames = Paths.getSparrowAtlas(styleData == null ? 'freeplay/freeplayCapsule/capsule/freeplayCapsule' : styleData.getCapsuleAssetKey()); thank u luv u
@@ -543,7 +549,7 @@ class SongMenuItem extends FlxSpriteGroup
var frameOutTypeBeat:Int = 0;
var xFrames:Array<Float> = [1.7, 1.8, 0.85, 0.85, 0.97, 0.97, 1];
var xPosLerpLol:Array<Float> = [0.9, 0.4, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
var xPosLerpLol:Array<Float> = [0, 0, 0.16, 0.16, 0.22, 0.22, 0.245]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
var xPosOutLerpLol:Array<Float> = [0.245, 0.75, 0.98, 0.98, 1.2]; // NUMBERS ARE JANK CUZ THE SCALING OR WHATEVER
public var realScaled:Float = 0.8;
@@ -554,9 +560,6 @@ class SongMenuItem extends FlxSpriteGroup
new FlxTimer().start((1 / 24) * maxTimer, function(doShit) {
doJumpIn = true;
});
new FlxTimer().start((0.09 * maxTimer) + 0.85, function(lerpTmr) {
doLerp = true;
});
@@ -617,12 +620,19 @@ class SongMenuItem extends FlxSpriteGroup
capsule.scale.x = xFrames[frameInTypeBeat];
capsule.scale.y = 1 / xFrames[frameInTypeBeat];
x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
targetPos.x = FlxG.width * xPosLerpLol[Std.int(Math.min(frameInTypeBeat, xPosLerpLol.length - 1))];
capsule.scale.x *= realScaled;
capsule.scale.y *= realScaled;
frameInTypeBeat += 1;
// Move the targetPos set to the if statement below if you want them to shift to their target positions after jumping in instead
// I have no idea why this if instead of frameInTypeBeat == xFrames.length works even though they're the same thing
if (targetPos.x <= 320) targetPos.x = intendedX(index - curSelected);
}
else if (frameInTypeBeat == xFrames.length)
{
doJumpIn = false;
}
}
@@ -636,13 +646,17 @@ class SongMenuItem extends FlxSpriteGroup
capsule.scale.x = xFrames[frameOutTypeBeat];
capsule.scale.y = 1 / xFrames[frameOutTypeBeat];
x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
this.x = FlxG.width * xPosOutLerpLol[Std.int(Math.min(frameOutTypeBeat, xPosOutLerpLol.length - 1))];
capsule.scale.x *= realScaled;
capsule.scale.y *= realScaled;
frameOutTypeBeat += 1;
}
else if (frameOutTypeBeat == xFrames.length)
{
doJumpOut = false;
}
}
if (doLerp)
@@ -669,7 +683,12 @@ class SongMenuItem extends FlxSpriteGroup
}
}
public function intendedY(index:Float):Float
public function intendedX(index:Int):Float
{
return 270 + (60 * (Math.sin(index)));
}
public function intendedY(index:Int):Float
{
return (index * ((height * realScaled) + 10)) + 120;
}