Merge branch 'rewrite/master' into mob-96/week3-train-haptics

This commit is contained in:
sector-a
2025-06-06 14:29:58 +03:00
16 changed files with 148 additions and 41 deletions
+4
View File
@@ -767,6 +767,10 @@ class Project extends HXProject
// Enable OpenFL's error handler. Required for the crash logger.
setHaxedef("openfl-enable-handle-error");
// Disable a macro in Lime that generates a random number for Assets.cache.version every build.
// This doesn't seem to be of any use and it causes lime.utils.AssetCache to be re-compiled on every build.
setHaxedef("lime_disable_assets_version");
// Enable stack trace tracking. Good for debugging but has a (minor) performance impact.
setHaxedef("HXCPP_CHECK_POINTER");
setHaxedef("HXCPP_STACK_LINE");
+30 -7
View File
@@ -5,6 +5,7 @@ import funkin.mobile.ui.FunkinHitbox;
#end
import funkin.save.Save;
import funkin.util.WindowUtil;
import funkin.util.HapticUtil.HapticsMode;
/**
* A core class which provides a store of user-configurable, globally relevant values.
@@ -147,20 +148,42 @@ class Preferences
}
/**
* If enabled, vibration will be enabled.
* @default `true`
* If enabled, haptic feedback will be enabled.
* @default `All`
*/
public static var vibration(get, set):Bool;
public static var hapticsMode(get, set):HapticsMode;
static function get_vibration():Bool
static function get_hapticsMode():HapticsMode
{
return Save?.instance?.options?.vibration ?? true;
var value = Save?.instance?.options?.hapticsMode ?? "All";
return switch (value)
{
case "None":
HapticsMode.NONE;
case "Notes Only":
HapticsMode.NOTES_ONLY;
default:
HapticsMode.ALL;
};
}
static function set_vibration(value:Bool):Bool
static function set_hapticsMode(value:HapticsMode):HapticsMode
{
var string;
switch (value)
{
case HapticsMode.NONE:
string = "None";
case HapticsMode.NOTES_ONLY:
string = "Notes Only";
default:
string = "All";
};
var save:Save = Save.instance;
save.options.vibration = value;
save.options.hapticsMode = string;
save.flush();
return value;
}
+1 -1
View File
@@ -255,7 +255,7 @@ class HoldNoteScriptEvent extends NoteScriptEvent
*/
public var doesNotesplash:Bool = false;
public function new(type:ScriptEventType, holdNote:SustainTrail, healthChange:Float, score:Int, isComboBreak:Bool, cancelable:Bool = false):Void
public function new(type:ScriptEventType, holdNote:SustainTrail, healthChange:Float, score:Int, isComboBreak:Bool, comboCount:Int = 0, cancelable:Bool = false):Void
{
super(type, null, healthChange, comboCount, true);
this.holdNote = holdNote;
+2 -1
View File
@@ -213,7 +213,7 @@ class PauseSubState extends MusicBeatSubState
#if FEATURE_MOBILE_ADVERTISEMENTS
extension.admob.Admob.onEvent.add(onBannerEvent);
funkin.mobile.util.AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_LEFT);
funkin.mobile.util.AdMobUtil.addBanner(extension.admob.AdmobBannerSize.BANNER, extension.admob.AdmobBannerAlign.TOP_CENTER);
#end
super.create();
@@ -279,6 +279,7 @@ class PauseSubState extends MusicBeatSubState
failedAdPlaceHolder = new FunkinSprite(0, 0);
failedAdPlaceHolder.makeSolidColor(Math.floor(320 * scale), Math.floor(50 * scale), FlxColor.RED);
failedAdPlaceHolder.updateHitbox();
failedAdPlaceHolder.screenCenter(X);
failedAdPlaceHolder.scrollFactor.set(0, 0);
add(failedAdPlaceHolder);
}
+2 -2
View File
@@ -2517,7 +2517,7 @@ class PlayState extends MusicBeatSubState
{
// Call an event to allow canceling the note miss.
// NOTE: This is what handles the character animations!
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Constants.HEALTH_MISS_PENALTY, 0, true);
var event:NoteScriptEvent = new NoteScriptEvent(NOTE_MISS, note, Constants.HEALTH_MISS_PENALTY, Highscore.tallies.combo, true);
dispatchEvent(event);
// Calling event.cancelEvent() skips all the other logic! Neat!
@@ -2584,7 +2584,7 @@ class PlayState extends MusicBeatSubState
var healthChange = healthChangeUncapped.clamp(healthChangeMax, 0);
var scoreChange = Std.int(Constants.SCORE_HOLD_DROP_PENALTY_PER_SECOND * remainingLengthSec);
var event:HoldNoteScriptEvent = new HoldNoteScriptEvent(NOTE_HOLD_DROP, holdNote, healthChange, scoreChange, true);
var event:HoldNoteScriptEvent = new HoldNoteScriptEvent(NOTE_HOLD_DROP, holdNote, healthChange, scoreChange, true, Highscore.tallies.combo);
dispatchEvent(event);
trace('Penalizing score by ${event.score} and health by ${event.healthChange} for dropping hold note (is combo break: ${event.isComboBreak})!');
@@ -218,9 +218,21 @@ class FocusCameraSongEvent extends SongEvent
'Smooth Step In' => 'smoothStepIn',
'Smooth Step Out' => 'smoothStepOut',
'Smooth Step In/Out' => 'smoothStepInOut',
'Smoother Step In' => 'smootherStepIn',
'Smoother Step Out' => 'smootherStepOut',
'Smoother Step In/Out' => 'smootherStepInOut',
'Elastic In' => 'elasticIn',
'Elastic Out' => 'elasticOut',
'Elastic In/Out' => 'elasticInOut',
'Back In' => 'backIn',
'Back Out' => 'backOut',
'Back In/Out' => 'backInOut',
'Bounce In' => 'bounceIn',
'Bounce Out' => 'bounceOut',
'Bounce In/Out' => 'bounceInOut',
'Circ In' => 'circIn',
'Circ Out' => 'circOut',
'Circ In/Out' => 'circInOut',
'Instant (Ignores duration)' => 'INSTANT',
'Classic (Ignores duration)' => 'CLASSIC'
]
+13 -1
View File
@@ -149,9 +149,21 @@ class ScrollSpeedEvent extends SongEvent
'Smooth Step In' => 'smoothStepIn',
'Smooth Step Out' => 'smoothStepOut',
'Smooth Step In/Out' => 'smoothStepInOut',
'Smoother Step In' => 'smootherStepIn',
'Smoother Step Out' => 'smootherStepOut',
'Smoother Step In/Out' => 'smootherStepInOut',
'Elastic In' => 'elasticIn',
'Elastic Out' => 'elasticOut',
'Elastic In/Out' => 'elasticInOut'
'Elastic In/Out' => 'elasticInOut',
'Back In' => 'backIn',
'Back Out' => 'backOut',
'Back In/Out' => 'backInOut',
'Bounce In' => 'bounceIn',
'Bounce Out' => 'bounceOut',
'Bounce In/Out' => 'bounceInOut',
'Circ In' => 'circIn',
'Circ Out' => 'circOut',
'Circ In/Out' => 'circInOut'
]
},
{
@@ -158,9 +158,21 @@ class ZoomCameraSongEvent extends SongEvent
'Smooth Step In' => 'smoothStepIn',
'Smooth Step Out' => 'smoothStepOut',
'Smooth Step In/Out' => 'smoothStepInOut',
'Smoother Step In' => 'smootherStepIn',
'Smoother Step Out' => 'smootherStepOut',
'Smoother Step In/Out' => 'smootherStepInOut',
'Elastic In' => 'elasticIn',
'Elastic Out' => 'elasticOut',
'Elastic In/Out' => 'elasticInOut'
'Elastic In/Out' => 'elasticInOut',
'Back In' => 'backIn',
'Back Out' => 'backOut',
'Back In/Out' => 'backInOut',
'Bounce In' => 'bounceIn',
'Bounce Out' => 'bounceOut',
'Bounce In/Out' => 'bounceInOut',
'Circ In' => 'circIn',
'Circ Out' => 'circOut',
'Circ In/Out' => 'circInOut'
]
}
]);
@@ -1,6 +1,7 @@
package funkin.play.notes;
import funkin.util.HapticUtil;
import funkin.util.HapticUtil.HapticsMode;
/**
* Handles vibrations on note presses.
@@ -60,7 +61,7 @@ class NoteVibrationsHandler
if (stackingAmplitude > Constants.MAX_VIBRATION_AMPLITUDE) stackingAmplitude = Constants.MAX_VIBRATION_AMPLITUDE;
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude);
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
}
/**
@@ -83,7 +84,7 @@ class NoteVibrationsHandler
if (stackingAmplitude > Constants.MAX_VIBRATION_AMPLITUDE) stackingAmplitude = Constants.MAX_VIBRATION_AMPLITUDE;
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude);
if (stackingAmplitude > 0) HapticUtil.vibrate(0, 0.01, stackingAmplitude, [HapticsMode.ALL, HapticsMode.NOTES_ONLY]);
for (currentNoteStatus in noteStatuses)
{
+4 -4
View File
@@ -121,7 +121,7 @@ class Save
flashingLights: true,
zoomCamera: true,
debugDisplay: false,
vibration: true,
hapticsMode: 'All',
autoPause: true,
vsyncMode: 'Off',
strumlineBackgroundOpacity: 0,
@@ -1435,10 +1435,10 @@ typedef SaveDataOptions =
var debugDisplay:Bool;
/**
* If enabled, vibration will be enabled.
* @default `true`
* If enabled, haptic feedback will be enabled.
* @default `All`
*/
var vibration:Bool;
var hapticsMode:String;
/**
* If enabled, the game will automatically pause when tabbing out.
@@ -22,7 +22,8 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
public var charSelectScroll:ScrollView;
public var charIconName:Label;
var currentCharButton:Button;
var currentCharButton:Null<Button> = null;
var currentCharId:String = '';
public function new(chartEditorState2:ChartEditorState, charType:CharacterType, lockPosition:Bool = false)
{
@@ -36,14 +37,16 @@ class ChartEditorCharacterIconSelectorMenu extends ChartEditorBaseMenu
ease: FlxEase.quartOut,
onComplete: function(_) {
// Just focus the button FFS. Idk why, but the scrollbar doesn't update until after the tween finishes with this????
currentCharButton.focus = true;
if (currentCharButton != null) currentCharButton.focus = true;
else
chartEditorState.error('Failure', 'Could not find character of ${currentCharId} in registry (Is the character in the registry?)');
}
});
}
function initialize(charType:CharacterType, lockPosition:Bool)
{
var currentCharId:String = switch (charType)
currentCharId = switch (charType)
{
case BF: chartEditorState.currentSongMetadata.playData.characters.player;
case GF: chartEditorState.currentSongMetadata.playData.characters.girlfriend;
+15 -10
View File
@@ -833,10 +833,6 @@ class FreeplayState extends MusicBeatSubState
currentFilteredSongs = tempSongs;
curSelected = 0;
// If curSelected is 0, the result will be null and fall back to the rememberedSongId.
// We set this so if we change the filter, we'd remain on the same song if it's still in the list.
rememberedSongId = grpCapsules.members[curSelected]?.freeplayData?.data.id ?? rememberedSongId;
grpCapsules.killMembers();
// Initialize the random capsule, with empty/blank info (which we display once bf/pico does his hand)
@@ -963,6 +959,9 @@ class FreeplayState extends MusicBeatSubState
rememberedSongId = fromResults.songId;
rememberedDifficulty = fromResults.difficultyId;
capsuleToRank.fakeRanking.visible = true;
capsuleToRank.fakeRanking.alpha = 0; // If this isn't done, you'd see a tiny E being replaced for the first rank
changeSelection();
changeDiff();
@@ -1003,6 +1002,7 @@ class FreeplayState extends MusicBeatSubState
sparksADD.cameras = [rankCamera];
sparksADD.color = fromResults.oldRank.getRankingFreeplayColor();
// sparksADD.color = sparks.color;
capsuleToRank.fakeRanking.alpha = 1.0;
}
capsuleToRank.doLerp = false;
@@ -1015,7 +1015,6 @@ class FreeplayState extends MusicBeatSubState
trace(originalPos);
capsuleToRank.ranking.visible = false;
capsuleToRank.fakeRanking.visible = false;
// Rank animation vibrations.
HapticUtil.increasingVibrate(Constants.MIN_VIBRATION_AMPLITUDE, Constants.MAX_VIBRATION_AMPLITUDE, 0.6);
@@ -2290,16 +2289,22 @@ class FreeplayState extends MusicBeatSubState
var altInstrumentalIds:Array<String> = targetSong.listAltInstrumentalIds(targetDifficultyId,
targetDifficulty?.variation ?? Constants.DEFAULT_VARIATION) ?? [];
#if !mobile
if (altInstrumentalIds.length > 0)
{
var instrumentalIds = [baseInstrumentalId].concat(altInstrumentalIds);
openInstrumentalList(cap, instrumentalIds);
return;
}
else
{
trace('NO ALTS');
capsuleOnConfirmDefault(cap);
}
#end
#if mobile
trace('ALTS ARE DISABLED');
#else
trace('NO ALTS');
#end
capsuleOnConfirmDefault(cap);
}
public function getControls():Controls
+1 -1
View File
@@ -300,7 +300,7 @@ class SongMenuItem extends FlxSpriteGroup
var clipSize:Int = 290;
var clipType:Int = 0;
if (ranking.visible)
if (ranking.visible || fakeRanking.visible)
{
favIconBlurred.x = this.x + 370;
favIcon.x = favIconBlurred.x;
@@ -22,6 +22,7 @@ import funkin.mobile.ui.FunkinHitbox.FunkinHitboxControlSchemes;
import funkin.util.TouchUtil;
import funkin.util.SwipeUtil;
#end
import funkin.util.HapticUtil;
import lime.ui.WindowVSyncMode;
class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
@@ -124,6 +125,20 @@ class PreferencesMenu extends Page<OptionsState.OptionsMenuPageName>
createPrefItemCheckbox('Camera Zooms', 'If disabled, camera stops bouncing to the song.', function(value:Bool):Void {
Preferences.zoomCamera = value;
}, Preferences.zoomCamera);
#if FEATURE_HAPTICS
createPrefItemEnum('Haptics', 'If enabled, game will use haptic feedback effects.', [
"All" => HapticsMode.ALL,
"Notes Only" => HapticsMode.NOTES_ONLY,
"None" => HapticsMode.NONE,
], function(key:String, value:HapticsMode):Void {
Preferences.hapticsMode = value;
}, switch (Preferences.hapticsMode)
{
case HapticsMode.NOTES_ONLY: "Notes Only";
case HapticsMode.NONE: "None";
default: "All";
});
#end
createPrefItemCheckbox('Debug Display', 'If enabled, FPS and other debug stats will be displayed.', function(value:Bool):Void {
Preferences.debugDisplay = value;
}, Preferences.debugDisplay);
+27 -2
View File
@@ -35,11 +35,15 @@ class HapticUtil
* @param sharpness Controls the feel of vibration.
*/
public static function vibrate(period:Float = Constants.DEFAULT_VIBRATION_PERIOD, duration:Float = Constants.DEFAULT_VIBRATION_DURATION,
amplitude:Float = Constants.DEFAULT_VIBRATION_AMPLITUDE, sharpness:Float = Constants.DEFAULT_VIBRATION_SHARPNESS):Void
amplitude:Float = Constants.DEFAULT_VIBRATION_AMPLITUDE, sharpness:Float = Constants.DEFAULT_VIBRATION_SHARPNESS,
?targetHapticsModes:Array<HapticsMode>):Void
{
#if FEATURE_HAPTICS
if (!HapticUtil.hapticsAvailable) return;
final hapticsModes:Array<HapticsMode> = targetHapticsModes ?? [HapticsMode.ALL];
if (!hapticsModes.contains(Preferences.hapticsMode)) return;
#if ios
final amplitudeValue = FlxMath.bound(amplitude * 2.5, 0, Constants.MAX_VIBRATION_AMPLITUDE);
#else
@@ -122,7 +126,7 @@ class HapticUtil
static function get_hapticsAvailable():Bool
{
#if FEATURE_HAPTICS
if (Preferences.vibration) return true;
if (Preferences.hapticsMode != HapticsMode.NONE) return true;
#end
return false;
@@ -154,3 +158,24 @@ typedef VibrationPreset =
*/
var sharpness:Float;
}
/**
* An abstract for vibrations preference.
*/
enum abstract HapticsMode(Int) from Int to Int
{
/**
* Haptics are completely disabled.
*/
var NONE:Int = 0;
/**
* Only note haptics are enabled.
*/
var NOTES_ONLY:Int = 1;
/**
* All the haptics are enabled.
*/
var ALL:Int = 2;
}
-6
View File
@@ -85,12 +85,6 @@ class WindowUtil
*/
public static function initTracy():Void
{
// Apply a marker to indicate frame end for the Tracy profiler.
// Do this only if Tracy is configured to prevent lag.
openfl.Lib.current.stage.addEventListener(openfl.events.Event.EXIT_FRAME, (e:openfl.events.Event) -> {
cpp.vm.tracy.TracyProfiler.frameMark();
});
var appInfoMessage = funkin.util.logging.CrashHandler.buildSystemInfo();
trace("Friday Night Funkin': Connection to Tracy profiler successful.");