mirror of
https://github.com/ninjamuffin99/Funkin.git
synced 2025-09-03 20:28:04 +00:00
Compare commits
16 commits
7dd9471beb
...
78c9ad0997
Author | SHA1 | Date | |
---|---|---|---|
|
78c9ad0997 | ||
|
b99a4b3e65 | ||
|
d72601094a | ||
|
38fcea6773 | ||
|
3bccce6070 | ||
|
a454bc7f50 | ||
|
18829b5b47 | ||
|
1ae149caed | ||
|
a8e76e5ec4 | ||
|
843f4d767f | ||
|
03df863ea7 | ||
|
63971f730f | ||
|
61b5fa0fe1 | ||
|
4b82af37ff | ||
|
6eb07d8b17 | ||
|
ab7bfb24d9 |
|
@ -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.
|
||||
|
|
54
source/funkin/modding/ModStore.hx
Normal file
54
source/funkin/modding/ModStore.hx
Normal 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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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});
|
||||
|
|
|
@ -100,6 +100,7 @@ class DebugBoundingState extends FlxState
|
|||
offsetAnimationDropdown = offsetEditorDialog.findComponent("animationDropdown", DropDown);
|
||||
|
||||
offsetEditorDialog.cameras = [hudCam];
|
||||
offsetEditorDialog.closable = false;
|
||||
|
||||
add(offsetEditorDialog);
|
||||
offsetEditorDialog.showDialog(false);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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',
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue