1
0
Fork 0
mirror of https://github.com/ninjamuffin99/Funkin.git synced 2025-09-03 20:28:04 +00:00

Compare commits

...

16 commits

Author SHA1 Message Date
lemz 78c9ad0997
Merge d2e1d3c80d into b99a4b3e65 2025-08-02 09:07:13 -05:00
PurSnake b99a4b3e65 Seriously? 2025-08-02 09:07:08 -05:00
Hyper_ d72601094a me...................................................................................... 2025-08-02 08:29:08 -05:00
Kolo 38fcea6773 me......................................................................... 2025-08-02 08:04:46 -05:00
cyn0x8 3bccce6070 Create ModStore.hx 2025-08-02 19:04:07 +08:00
anysad a454bc7f50 feature: are you sure? 2025-08-02 19:00:03 +08:00
AwesomezPro99 18829b5b47 Update PreferencesMenu.hx 2025-08-02 18:58:04 +08:00
JVN-Pixels 1ae149caed Fix NOR in Freeplay after spamming the letter keybinds 2025-08-02 18:47:03 +08:00
anysad a8e76e5ec4 fix: no more animation editor crash 2025-08-02 18:44:33 +08:00
Hyper_ 843f4d767f Restore a lost change from #4787 2025-08-02 18:40:59 +08:00
Kolo 03df863ea7 tfw changeDiff wasn't even being called 2025-08-02 18:24:11 +08:00
JackXson-Real 63971f730f Add enter/exit animation for charSelect nametag part 2 2025-08-02 18:06:36 +08:00
Hyper_ 61b5fa0fe1 Make the Android back button close the game when in TitleState 2025-08-02 17:50:51 +08:00
ExcodeFNF 4b82af37ff Freeplay CoolColors Array Removal 2025-08-02 02:01:58 +08:00
AwesomezPro99 6eb07d8b17 Fixed DJ Flicker After New Rank 2025-08-01 12:28:42 -05:00
kade-github ab7bfb24d9 offset menu changes 2025-07-31 23:43:43 +08:00
16 changed files with 128 additions and 53 deletions

View file

@ -330,10 +330,10 @@ class Project extends HXProject
static final FEATURE_HAPTICS:FeatureFlag = "FEATURE_HAPTICS";
/**
* `-DFEATURE_INPUT_OFFSETS`
* `-DFEATURE_LAG_ADJUSTMENT`
* If this flag is enabled, the input offsets menu will be available to configure your audio and visual offsets.
*/
static final FEATURE_INPUT_OFFSETS:FeatureFlag = "FEATURE_INPUT_OFFSETS";
static final FEATURE_LAG_ADJUSTMENT:FeatureFlag = "FEATURE_LAG_ADJUSTMENT";
/**
* `-DFEATURE_LOG_TRACE`
@ -829,7 +829,7 @@ class Project extends HXProject
FEATURE_STAGE_EDITOR.apply(this, !(isWeb() || isMobile()));
// Should be true except on web builds (some asset stuff breaks it)
FEATURE_INPUT_OFFSETS.apply(this, !isWeb());
FEATURE_LAG_ADJUSTMENT.apply(this, !isWeb());
// Should be true except on web and mobile builds.
// Screenshots doesn't work there, and mobile has its own screenshots anyway.

View file

@ -0,0 +1,54 @@
package funkin.modding;
import haxe.ds.StringMap;
/**
* Temporary persistent data storage for mods to use.
*/
@:nullSafety
class ModStore
{
/**
* All registered stores for this session.
*/
public static final stores:StringMap<Dynamic> = new StringMap<Dynamic>();
/**
* Attempts to register a new store with the given ID and return it.
* If a store with the same ID already exists, that store will be returned instead (discards `data`).
*
* @id The unique ID for this store.
* @data Optional initial data, uses an empty object by default.
* @return The store data at the given ID.
*/
public static function register(id:String, ?data:Dynamic):Dynamic
{
if (stores.exists(id)) return stores.get(id);
stores.set(id, data ??= {});
return data;
}
/**
* Helper function to get a store by ID.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function get(id:String):Null<Dynamic>
{
return stores.get(id);
}
/**
* Helper function to remove a store by ID and return it.
*
* @id The target ID of the store.
* @return The store data, or `null` if the store did not exist.
*/
public static function remove(id:String):Null<Dynamic>
{
var data:Null<Dynamic> = stores.get(id);
stores.remove(id);
return data;
}
}

View file

@ -444,7 +444,7 @@ class PauseSubState extends MusicBeatSubState
offsetText.y = FlxG.height - (offsetText.height + offsetText.height + 40);
offsetTextInfo.y = offsetText.y + offsetText.height + 4;
#if !mobile
#if (!mobile && FEATURE_LAG_ADJUSTMENT)
metadata.add(offsetText);
metadata.add(offsetTextInfo);
#end

View file

@ -1502,7 +1502,9 @@ class PlayState extends MusicBeatSubState
musicPausedBySubState = false;
}
forEachPausedSound((s) -> needsReset ? s.destroy() : s.resume());
// The logic here is that if this sound doesn't auto-destroy
// then it's gonna be reused somewhere, so we just stop it instead.
forEachPausedSound(s -> needsReset ? (s.autoDestroy ? s.destroy() : s.stop()) : s.resume());
// Resume camera tweens if we paused any.
for (camTween in cameraTweensPausedBySubState)

View file

@ -271,6 +271,12 @@ class CharSelectSubState extends MusicBeatSubState
nametag.midpointX += cutoutSize;
add(nametag);
@:privateAccess
{
nametag.midpointY += 200;
FlxTween.tween(nametag, {midpointY: nametag.midpointY - 200}, 1, {ease: FlxEase.expoOut});
}
nametag.scrollFactor.set();
FlxG.debugger.addTrackerProfile(new TrackerProfile(FlxSprite, ["x", "y", "alpha", "scale", "blend"]));
@ -739,6 +745,7 @@ class CharSelectSubState extends MusicBeatSubState
FlxTween.tween(cursorConfirmed, {alpha: 0}, 0.8, {ease: FlxEase.expoOut});
FlxTween.tween(barthing, {y: barthing.y + 80}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(nametag, {y: nametag.y + 80}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(dipshitBacking, {y: dipshitBacking.y + 210}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(chooseDipshit, {y: chooseDipshit.y + 200}, 0.8, {ease: FlxEase.backIn});
FlxTween.tween(dipshitBlur, {y: dipshitBlur.y + 220}, 0.8, {ease: FlxEase.backIn});

View file

@ -100,6 +100,7 @@ class DebugBoundingState extends FlxState
offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown);
offsetEditorDialog.cameras = [hudCam];
offsetEditorDialog.closable = false;
add(offsetEditorDialog);
offsetEditorDialog.showDialog(false);

View file

@ -94,6 +94,9 @@ import haxe.ui.components.Button;
import haxe.ui.components.DropDown;
import haxe.ui.components.Label;
import haxe.ui.components.Slider;
import haxe.ui.containers.dialogs.Dialogs;
import haxe.ui.containers.dialogs.Dialog.DialogButton;
import haxe.ui.containers.dialogs.MessageBox.MessageBoxType;
import haxe.ui.containers.dialogs.CollapsibleDialog;
import haxe.ui.containers.menus.Menu;
import haxe.ui.containers.menus.MenuBar;
@ -5599,7 +5602,18 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
@:nullSafety(Off)
function quitChartEditor():Void
{
autoSave();
if (saveDataDirty) {
Dialogs.messageBox("You are about to leave the editor without saving.\n\nAre you sure?", "Leave Editor", MessageBoxType.TYPE_YESNO, true, function(button:DialogButton) {
if (button == DialogButton.YES)
{
autoSave();
quitChartEditor();
}
});
return;
}
stopWelcomeMusic();
// TODO: PR Flixel to make onComplete nullable.
if (audioInstTrack != null) audioInstTrack.onComplete = null;

View file

@ -192,7 +192,7 @@ class AssetDataHandler
for (daFrame in obj.frames.frames)
{
xml += ' <SubTexture name="${daFrame.name}" x="${daFrame.frame.x}" y="${daFrame.frame.y}" width="${daFrame.frame.width}" height="${daFrame.frame.height}" frameX="${- daFrame.offset.x}" frameY="${- daFrame.offset.y}" frameWidth="${daFrame.sourceSize.x}" frameHeight="${daFrame.sourceSize.y}" flipX="${daFrame.flipX}" flipY="${daFrame.flipY}"/>\n';
xml += ' <SubTexture name="${daFrame.name}" x="${daFrame.frame.x}" y="${daFrame.frame.y}" width="${daFrame.frame.width}" height="${daFrame.frame.height}" frameX="${- daFrame.offset.x}" frameY="${- daFrame.offset.y}" frameWidth="${daFrame.sourceSize.x}" frameHeight="${daFrame.sourceSize.y}" flipX="${daFrame.flipX}" flipY="${daFrame.flipY}" rotated="${daFrame.angle == -90}"/>\n';
}
xml += "</TextureAtlas>";

View file

@ -81,8 +81,6 @@ class FreeplayDJ extends FlxAtlasSprite
public override function update(elapsed:Float):Void
{
super.update(elapsed);
switch (currentState)
{
case Intro:
@ -185,6 +183,8 @@ class FreeplayDJ extends FlxAtlasSprite
default:
// I shit myself.
}
super.update(elapsed);
}
function onFinishAnim(name:String):Void

View file

@ -169,17 +169,6 @@ class FreeplayState extends MusicBeatSubState
return grpCapsules.members[curSelected];
}
var coolColors:Array<Int> = [
0xFF9271FD,
0xFF9271FD,
0xFF223344,
0xFF941653,
0xFFFC96D7,
0xFFA0D1FF,
0xFFFF78BF,
0xFFF6B604
];
var grpCapsules:FlxTypedGroup<SongMenuItem>;
var dj:Null<FreeplayDJ> = null;
@ -2179,7 +2168,7 @@ class FreeplayState extends MusicBeatSubState
*/
function changeDiff(change:Int = 0, force:Bool = false, capsuleAnim:Bool = false):Void
{
if (!controls.active) return;
if (!controls.active && !force) return;
if (capsuleAnim)
{
@ -2390,11 +2379,6 @@ class FreeplayState extends MusicBeatSubState
{
trace('RANDOM SELECTED');
controls.active = false;
#if NO_FEATURE_TOUCH_CONTROLS
letterSort.inputEnabled = false;
#end
var availableSongCapsules:Array<SongMenuItem> = grpCapsules.members.filter(function(cap:SongMenuItem) {
// Dead capsules are ones which were removed from the list when changing filters.
return cap.alive && cap.freeplayData != null;
@ -2420,6 +2404,10 @@ class FreeplayState extends MusicBeatSubState
// Seeing if I can do an animation...
curSelected = grpCapsules.members.indexOf(targetSong);
changeSelection(0); // Trigger an update.
controls.active = false;
#if NO_FEATURE_TOUCH_CONTROLS
letterSort.inputEnabled = false;
#end
// Act like we hit Confirm on that song.
capsuleOnConfirmDefault(targetSong);
@ -2701,7 +2689,7 @@ class FreeplayState extends MusicBeatSubState
intendedCompletion = songScore == null ? 0.0 : ((songScore.tallies.sick +
songScore.tallies.good - songScore.tallies.missed) / songScore.tallies.totalNotes);
rememberedSongId = daSongCapsule.freeplayData.data.id;
changeDiff();
changeDiff(0, true);
daSongCapsule.refreshDisplay((prepForNewRank == true) ? false : true);
}
else
@ -2710,7 +2698,7 @@ class FreeplayState extends MusicBeatSubState
intendedCompletion = 0.0;
rememberedSongId = null;
albumRoll.albumId = null;
changeDiff();
changeDiff(0, true);
daSongCapsule.refreshDisplay();
}

View file

@ -148,18 +148,22 @@ class LetterSort extends FlxSpriteGroup
public function changeSelection(diff:Int = 0, playSound:Bool = true):Void
{
doLetterChangeAnims(diff);
@:privateAccess
if (instance.controls.active)
{
doLetterChangeAnims(diff);
var multiPosOrNeg:Float = diff > 0 ? 1 : -1;
var multiPosOrNeg:Float = diff > 0 ? 1 : -1;
// if we're moving left (diff < 0), we want control of the right arrow, and vice versa
var arrowToMove:FlxSprite = diff < 0 ? leftArrow : rightArrow;
arrowToMove.offset.x = 3 * multiPosOrNeg;
// if we're moving left (diff < 0), we want control of the right arrow, and vice versa
var arrowToMove:FlxSprite = diff < 0 ? leftArrow : rightArrow;
arrowToMove.offset.x = 3 * multiPosOrNeg;
new FlxTimer().start(2 / 24, function(_) {
arrowToMove.offset.x = 0;
});
if (playSound && diff != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
new FlxTimer().start(2 / 24, function(_) {
arrowToMove.offset.x = 0;
});
if (playSound && diff != 0) FunkinSound.playOnce(Paths.sound('scrollMenu'), 0.4);
}
}
/**

View file

@ -268,10 +268,8 @@ class MainMenuState extends MusicBeatState
// reset camera when debug menu is closed
subStateClosed.add(_ -> resetCamStuff(false));
// TODO: Why does this specific function break with null safety?
@:nullSafety(Off)
subStateOpened.add((sub:FlxSubState) -> {
if (Type.getClass(sub) == FreeplayState)
if (Std.isOfType(sub, FreeplayState))
{
new FlxTimer().start(0.5, _ -> {
magenta.visible = false;

View file

@ -545,6 +545,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
if (FlxG.sound.music.time < _lastTime)
{
localConductor.update(FlxG.sound.music.time, !calibrating);
b = localConductor.currentBeatTime;
// Update arrows to be the correct distance away from the receptor.
var lastArrowBeat:Float = 0;
@ -558,7 +559,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
}
if (calibrating)
{
arrowBeat = lastArrowBeat + 2;
arrowBeat = lastArrowBeat;
}
else
arrowBeat = 4;
@ -566,6 +567,10 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
testStrumline.clean();
testStrumline.noteData = [];
testStrumline.nextNoteIndex = 0;
trace('Restarting conductor');
_lastTime = FlxG.sound.music.time;
return;
}
_lastBeat = b;
@ -608,7 +613,7 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
countText.text = 'Current Offset: ' + Std.int(appliedOffsetLerp) + 'ms';
var toRemove:Array<ArrowData> = [];
var _lastArrowBeat:Float = 0;
// Update arrows
for (i in 0...arrows.length)
{
@ -629,12 +634,13 @@ class OffsetMenu extends Page<OptionsState.OptionsMenuPageName>
arrow.sprite.alpha -= elapsed * 5;
}
if (arrow.sprite.alpha <= 0)
if (arrow.beat == _lastArrowBeat || arrow.sprite.alpha <= 0)
{
toRemove.push(arrow);
arrow.sprite.kill();
// arrow.debugText.kill();
continue;
}
_lastArrowBeat = arrow.beat;
}
// Remove arrows that are marked for removal.

View file

@ -73,7 +73,7 @@ class OptionsState extends MusicBeatState
var options:OptionsMenu = optionsCodex.addPage(Options, new OptionsMenu(saveData));
var preferences:PreferencesMenu = optionsCodex.addPage(Preferences, new PreferencesMenu());
var controls:ControlsMenu = optionsCodex.addPage(Controls, new ControlsMenu());
#if FEATURE_INPUT_OFFSETS
#if FEATURE_LAG_ADJUSTMENT
var offsets:OffsetMenu = optionsCodex.addPage(Offsets, new OffsetMenu());
#end
@ -82,7 +82,7 @@ class OptionsState extends MusicBeatState
options.onExit.add(exitToMainMenu);
controls.onExit.add(exitControls);
preferences.onExit.add(optionsCodex.switchPage.bind(Options));
#if FEATURE_INPUT_OFFSETS
#if FEATURE_LAG_ADJUSTMENT
offsets.onExit.add(exitOffsets);
#end
saveData.onExit.add(optionsCodex.switchPage.bind(Options));
@ -174,8 +174,8 @@ class OptionsMenu extends Page<OptionsMenuPageName>
// createItem("CONTROL SCHEMES", function() {
// FlxG.state.openSubState(new ControlsSchemeMenu());
// });
#if FEATURE_INPUT_OFFSETS
createItem("INPUT OFFSETS", function() {
#if FEATURE_LAG_ADJUSTMENT
createItem("LAG ADJUSTMENT", function() {
FlxG.sound.music.fadeOut(0.5, 0, function(tw) {
FunkinSound.playMusic('offsetsLoop',
{

View file

@ -65,7 +65,6 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
createPrefDescription();
camFollow = new FlxObject(FlxG.width / 2, 0, 140, 70);
if (items != null) camFollow.y = items.selectedItem.y;
menuCamera.follow(camFollow, null, 0.085);
var margin = 160;
@ -73,7 +72,6 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
menuCamera.minScrollY = 0;
items.onChange.add(function(selected) {
camFollow.y = selected.y;
itemDesc.text = preferenceDesc[items.selectedIndex];
});
@ -204,6 +202,9 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
{
super.update(elapsed);
// Positions the camera to the selected item.
if (items != null) camFollow.y = items.selectedItem.y;
// Indent the selected item.
items.forEach(function(daItem:TextMenuItem) {
var thyOffset:Int = 0;

View file

@ -228,11 +228,11 @@ class TitleState extends MusicBeatState
{
FlxG.bitmapLog.add(FlxG.camera.buffer);
#if desktop
#if (desktop || android)
// Pressing BACK on the title screen should close the game.
// This lets you exit without leaving fullscreen mode.
// Only applicable on desktop.
if (controls.BACK)
// Only applicable on desktop and Android.
if (#if android FlxG.android.justReleased.BACK || #end controls.BACK)
{
openfl.Lib.application.window.close();
}