Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable note hit sounds & effects #4112

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions source/funkin/Preferences.hx
Original file line number Diff line number Diff line change
@@ -1,12 +1,89 @@
package funkin;

import funkin.save.Save;
import funkin.play.notes.notesound.NoteSoundType;

/**
* A core class which provides a store of user-configurable, globally relevant values.
*/
class Preferences
{
/**
* The type of sound to play when hitting a note
* @default `NoteSoundType.None`
*/
public static var noteSoundType(get, set):NoteSoundType;

static function get_noteSoundType():NoteSoundType
{
return Save?.instance?.options?.noteSoundType;
}

static function set_noteSoundType(value:NoteSoundType):NoteSoundType
{
var save:Save = Save.instance;
save.options.noteSoundType = value;
save.flush();
return value;
}

/**
* A value ranging from 0 to 100 representing the volume for note hit sounds
* @default `100`
*/
public static var noteSoundVolume(get, set):Int;

static function get_noteSoundVolume():Int
{
return Save?.instance?.options?.noteSoundVolume;
}

static function set_noteSoundVolume(value:Int):Int
{
var save:Save = Save.instance;
save.options.noteSoundVolume = value;
save.flush();
return value;
}

/**
* If enabled, plays a highlight animation when notes are hit.
* @default `true`
*/
public static var noteHighlights(get, set):Bool;

static function get_noteHighlights():Bool
{
return Save?.instance?.options?.noteHighlights;
}

static function set_noteHighlights(value:Bool):Bool
{
var save:Save = Save.instance;
save.options.noteHighlights = value;
save.flush();
return value;
}

/**
* If enabled, plays a splash particle effect when sick notes are hit.
* @default `true`
*/
public static var noteSplashes(get, set):Bool;

static function get_noteSplashes():Bool
{
return Save?.instance?.options?.noteSplashes;
}

static function set_noteSplashes(value:Bool):Bool
{
var save:Save = Save.instance;
save.options.noteSplashes = value;
save.flush();
return value;
}

/**
* FPS
* @default `60`
Expand Down
3 changes: 3 additions & 0 deletions source/funkin/play/PlayState.hx
Original file line number Diff line number Diff line change
Expand Up @@ -2563,6 +2563,9 @@ class PlayState extends MusicBeatSubState
if (note.isHoldNote && note.holdNoteSprite != null) playerStrumline.playNoteHoldCover(note.holdNoteSprite);
vocals.playerVolume = 1;

// Play a note hit sound
playerStrumline.noteSoundPlayer.begin(note.isHoldNote);

// Display the combo meter and add the calculation to the score.
applyScore(event.score, event.judgement, event.healthChange, event.isComboBreak);
popUpScore(event.judgement);
Expand Down
15 changes: 7 additions & 8 deletions source/funkin/play/notes/Strumline.hx
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,10 @@ import flixel.group.FlxSpriteGroup.FlxTypedSpriteGroup;
import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween;
import flixel.util.FlxSort;
import funkin.play.notes.NoteHoldCover;
import funkin.play.notes.NoteSplash;
import funkin.play.notes.NoteSprite;
import funkin.play.notes.SustainTrail;
import funkin.data.song.SongData.SongNoteData;
import funkin.ui.options.PreferencesMenu;
import funkin.util.SortUtil;
import funkin.modding.events.ScriptEvent;
import funkin.play.notes.notekind.NoteKindManager;
import funkin.play.notes.notesound.NoteSoundPlayer;

/**
* A group of sprites which handles the receptor, the note splashes, and the notes (with sustains) for a given player.
Expand Down Expand Up @@ -133,6 +128,8 @@ class Strumline extends FlxSpriteGroup

var heldKeys:Array<Bool> = [];

public var noteSoundPlayer:NoteSoundPlayer;

public function new(noteStyle:NoteStyle, isPlayer:Bool)
{
super();
Expand Down Expand Up @@ -171,6 +168,8 @@ class Strumline extends FlxSpriteGroup

this.refresh();

this.noteSoundPlayer = new NoteSoundPlayer();

this.onNoteIncoming = new FlxTypedSignal<NoteSprite->Void>();
resetScrollSpeed();

Expand Down Expand Up @@ -742,7 +741,7 @@ class Strumline extends FlxSpriteGroup

public function playNoteSplash(direction:NoteDirection):Void
{
if (!showNotesplash) return;
if (!Preferences.noteSplashes || !showNotesplash) return;
if (!noteStyle.isNoteSplashEnabled()) return;

var splash:NoteSplash = this.constructNoteSplash();
Expand All @@ -762,7 +761,7 @@ class Strumline extends FlxSpriteGroup

public function playNoteHoldCover(holdNote:SustainTrail):Void
{
if (!showNotesplash) return;
if (!Preferences.noteSplashes || !showNotesplash) return;
if (!noteStyle.isHoldNoteCoverEnabled()) return;

var cover:NoteHoldCover = this.constructNoteHoldCover();
Expand Down
3 changes: 2 additions & 1 deletion source/funkin/play/notes/StrumlineNote.hx
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,9 @@ class StrumlineNote extends FunkinSprite

public function playAnimation(name:String = 'static', force:Bool = false, reversed:Bool = false, startFrame:Int = 0):Void
{
this.animation.play(name, force, reversed, startFrame);
if (!Preferences.noteHighlights && (name == 'press' || name == 'confirm' || name == 'confirm-hold')) return;

this.animation.play(name, force, reversed, startFrame);
centerOffsets();
centerOrigin();
}
Expand Down
33 changes: 33 additions & 0 deletions source/funkin/play/notes/notesound/NoteSoundPlayer.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package funkin.play.notes.notesound;

import funkin.audio.FunkinSound;

/// Class that handles playing note hit sounds
class NoteSoundPlayer
{
var sound:FunkinSound = null;

public function new() {}

/// Should be called to play the hit sound, or additionally start a sustain "roll" sound if `sustain` is true
public function begin(sustain:Bool):Void
{
if (Preferences.noteSoundType == NoteSoundType.None) return;

final folderPath:String = 'noteSounds/${Preferences.noteSoundType}';
final noteType:String = (sustain && Assets.exists(Paths.sound('${folderPath}/beginSustain'))) ? "beginSustain" : "begin";
final volume:Float = Preferences.noteSoundVolume / 100.0;

// TODO: Add support for sustain notes and play them here
// also note that in "how to.txt" there is some text saying sustain sounds aren't implemented yet
if (sound != null && sound.isPlaying) sound.destroy();
sound = FunkinSound.load(Paths.sound('${folderPath}/${noteType}'), volume, false, true);
sound.play(true);
}

/// Should be called to stop sustain note "roll" sounds
public function end(sustain:Bool):Void
{
// Doesn't currently get called anywhere, not sure where to call this from
}
}
13 changes: 13 additions & 0 deletions source/funkin/play/notes/notesound/NoteSoundType.hx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package funkin.play.notes.notesound;

/*
* Any new hit sound should generally be added here.
* The string is used to identify the sound in the user's settings, as well as in the game's asset folder.
* Syntax: `SoundName = "soundFileName"`
*/
abstract NoteSoundType(String) from String to String
{
public static inline var None:String = ""; // Special case
public static inline var PingPong:String = "pingPong";
public static inline var PoolBall:String = "poolBall";
}
30 changes: 29 additions & 1 deletion source/funkin/save/Save.hx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ import flixel.util.FlxSave;
import funkin.input.Controls.Device;
import funkin.play.scoring.Scoring;
import funkin.play.scoring.Scoring.ScoringRank;
import funkin.play.notes.notesound.NoteSoundType;
import funkin.save.migrator.RawSaveData_v1_0_0;
import funkin.save.migrator.SaveDataMigrator;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorLiveInputStyle;
import funkin.ui.debug.charting.ChartEditorState.ChartEditorTheme;
import funkin.ui.debug.stageeditor.StageEditorState.StageEditorTheme;
import funkin.util.FileUtil;
import funkin.util.SerializerUtil;
import thx.semver.Version;

@:nullSafety
Expand Down Expand Up @@ -90,6 +90,10 @@ class Save
options:
{
// Reasonable defaults.
noteHighlights: true,
noteSplashes: true,
noteSoundType: NoteSoundType.None,
noteSoundVolume: 100,
framerate: 60,
naughtyness: true,
downscroll: false,
Expand Down Expand Up @@ -1298,6 +1302,30 @@ typedef SaveScoreTallyData =
*/
typedef SaveDataOptions =
{
/**
* The type of sound to play when hitting a note
* @default `NoteSoundType.None`
*/
var noteSoundType:NoteSoundType;

/**
* A value ranging from 0 to 100 representing the volume for note hit sounds
* @default `100`
*/
var noteSoundVolume:Int;

/**
* If enabled, plays a highlight animation when notes are hit.
* @default `true`
*/
var noteHighlights:Bool;

/**
* If enabled, plays a splash particle effect when sick notes are hit.
* @default `true`
*/
var noteSplashes:Bool;

/**
* FPS
* @default `60`
Expand Down
24 changes: 22 additions & 2 deletions source/funkin/ui/options/PreferencesMenu.hx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ import funkin.ui.options.OptionsState.Page;
import funkin.graphics.FunkinCamera;
import funkin.graphics.FunkinSprite;
import funkin.ui.TextMenuList.TextMenuItem;
import funkin.audio.FunkinSound;
import funkin.ui.options.MenuItemEnums;
import funkin.ui.options.items.CheckboxPreferenceItem;
import funkin.ui.options.items.NumberPreferenceItem;
import funkin.ui.options.items.EnumPreferenceItem;
import funkin.audio.FunkinSound;
import funkin.play.notes.notesound.NoteSoundType;

class PreferencesMenu extends Page
{
Expand Down Expand Up @@ -101,6 +101,26 @@ class PreferencesMenu extends Page
createPrefItemCheckbox('Downscroll', 'If enabled, this will make the notes move downwards.', function(value:Bool):Void {
Preferences.downscroll = value;
}, Preferences.downscroll);
createPrefItemCheckbox('Note Highlights', 'If enabled, this will show a highlight effect while hitting a note', function(value:Bool):Void {
Preferences.noteHighlights = value;
}, Preferences.noteHighlights);
createPrefItemCheckbox('Note Splashes', 'If enabled, this will show a splash particle effect when hitting a note', function(value:Bool):Void {
Preferences.noteSplashes = value;
}, Preferences.noteSplashes);
createPrefItemEnum('Note Sound', 'If enabled, plays a sound effect when a note is hit', [
NoteSoundType.None => 'None',
NoteSoundType.PingPong => 'Ping Pong',
NoteSoundType.PoolBall => 'Pool Ball',
], function(value:String):Void {
Preferences.noteSoundType = value;
if (Preferences.noteSoundType == NoteSoundType.None) return;
FunkinSound.playOnce(Paths.sound('noteSounds/${Preferences.noteSoundType}/begin'));
}, Preferences.noteSoundType);
createPrefItemPercentage('Note Sound Volume', 'The volume to play the note hit sound effects at', function(value:Int) {
Preferences.noteSoundVolume = value;
if (Preferences.noteSoundType == NoteSoundType.None) return;
FunkinSound.playOnce(Paths.sound('noteSounds/${Preferences.noteSoundType}/begin'), Preferences.noteSoundVolume / 100.0);
}, Preferences.noteSoundVolume);
createPrefItemCheckbox('Flashing Lights', 'If disabled, it will dampen flashing effects. Useful for people with photosensitive epilepsy.',
function(value:Bool):Void {
Preferences.flashingLights = value;
Expand Down