Merge branch 'feature/discord-rpc-resurrected' into rewrite/master

This commit is contained in:
Cameron Taylor 2024-09-27 17:47:59 -04:00
commit 7a3d983bba
14 changed files with 412 additions and 173 deletions

View file

@ -111,6 +111,11 @@
"target": "hl", "target": "hl",
"args": ["-debug"] "args": ["-debug"]
}, },
{
"label": "Windows / Debug (Discord)",
"target": "windows",
"args": ["-debug", "-DFEATURE_DEBUG_FUNCTIONS", "-DFEATURE_DISCORD_RPC"]
},
{ {
"label": "Windows / Debug (FlxAnimate Test)", "label": "Windows / Debug (FlxAnimate Test)",
"target": "windows", "target": "windows",

2
art

@ -1 +1 @@
Subproject commit 1f64f3e7403a090b164f4442d10152b2be5d3d0a Subproject commit fbd3e3df77734606d88516770b71b56e6fa04bce

View file

@ -7,13 +7,6 @@
"ref": "a1eab7b9bf507b87200a3341719054fe427f3b15", "ref": "a1eab7b9bf507b87200a3341719054fe427f3b15",
"url": "https://github.com/FunkinCrew/FlxPartialSound.git" "url": "https://github.com/FunkinCrew/FlxPartialSound.git"
}, },
{
"name": "discord_rpc",
"type": "git",
"dir": null,
"ref": "2d83fa863ef0c1eace5f1cf67c3ac315d1a3a8a5",
"url": "https://github.com/FunkinCrew/linc_discord-rpc"
},
{ {
"name": "flixel", "name": "flixel",
"type": "git", "type": "git",
@ -108,6 +101,13 @@
"ref": "147294123f983e35f50a966741474438069a7a8f", "ref": "147294123f983e35f50a966741474438069a7a8f",
"url": "https://github.com/FunkinCrew/hxcpp-debugger" "url": "https://github.com/FunkinCrew/hxcpp-debugger"
}, },
{
"name": "hxdiscord_rpc",
"type": "git",
"dir": null,
"ref": "82c47ecc1a454b7dd644e4fcac7e91155f176dec",
"url": "https://github.com/FunkinCrew/hxdiscord_rpc"
},
{ {
"name": "hxjsonast", "name": "hxjsonast",
"type": "git", "type": "git",

View file

@ -479,10 +479,9 @@ class Project extends HXProject {
// Should default to true on workspace builds and false on release builds. // Should default to true on workspace builds and false on release builds.
REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop()); REDIRECT_ASSETS_FOLDER.apply(this, isDebug() && isDesktop());
// Should be true on release, non-tester builds. // Should be true on desktop, release, non-tester builds.
// We don't want testers to accidentally leak songs to their Discord friends! // We don't want testers to accidentally leak songs to their Discord friends!
// TODO: Re-enable this. FEATURE_DISCORD_RPC.apply(this, isDesktop() && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
FEATURE_DISCORD_RPC.apply(this, false && !FEATURE_DEBUG_FUNCTIONS.isEnabled(this));
// Should be true only on web builds. // Should be true only on web builds.
// Audio context issues only exist there. // Audio context issues only exist there.
@ -654,7 +653,7 @@ class Project extends HXProject {
} }
if (FEATURE_DISCORD_RPC.isEnabled(this)) { if (FEATURE_DISCORD_RPC.isEnabled(this)) {
addHaxelib('discord_rpc'); // Discord API addHaxelib('hxdiscord_rpc'); // Discord API
} }
if (FEATURE_NEWGROUNDS.isEnabled(this)) { if (FEATURE_NEWGROUNDS.isEnabled(this)) {

View file

@ -1,42 +1,42 @@
package funkin; package funkin;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.ui.debug.charting.ChartEditorState;
import funkin.ui.transition.LoadingState;
import flixel.FlxState;
import flixel.addons.transition.FlxTransitionableState; import flixel.addons.transition.FlxTransitionableState;
import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond; import flixel.addons.transition.FlxTransitionSprite.GraphicTransTileDiamond;
import flixel.addons.transition.TransitionData; import flixel.addons.transition.TransitionData;
import flixel.FlxSprite;
import flixel.FlxState;
import flixel.graphics.FlxGraphic; import flixel.graphics.FlxGraphic;
import flixel.math.FlxPoint; import flixel.math.FlxPoint;
import flixel.math.FlxRect; import flixel.math.FlxRect;
import flixel.FlxSprite;
import flixel.system.debug.log.LogStyle; import flixel.system.debug.log.LogStyle;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import funkin.util.macro.MacroUtil;
import funkin.util.WindowUtil;
import funkin.play.PlayStatePlaylist;
import openfl.display.BitmapData;
import funkin.data.story.level.LevelRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.stage.StageRegistry;
import funkin.data.dialogue.conversation.ConversationRegistry; import funkin.data.dialogue.conversation.ConversationRegistry;
import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry; import funkin.data.dialogue.dialoguebox.DialogueBoxRegistry;
import funkin.data.dialogue.speaker.SpeakerRegistry; import funkin.data.dialogue.speaker.SpeakerRegistry;
import funkin.data.freeplay.album.AlbumRegistry; import funkin.data.freeplay.album.AlbumRegistry;
import funkin.data.freeplay.player.PlayerRegistry;
import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.notestyle.NoteStyleRegistry;
import funkin.data.song.SongRegistry; import funkin.data.song.SongRegistry;
import funkin.data.event.SongEventRegistry;
import funkin.data.stage.StageRegistry;
import funkin.data.story.level.LevelRegistry;
import funkin.modding.module.ModuleHandler;
import funkin.play.character.CharacterData.CharacterDataParser; import funkin.play.character.CharacterData.CharacterDataParser;
import funkin.play.notes.notekind.NoteKindManager; import funkin.play.notes.notekind.NoteKindManager;
import funkin.modding.module.ModuleHandler; import funkin.play.PlayStatePlaylist;
import funkin.ui.debug.charting.ChartEditorState;
import funkin.ui.title.TitleState; import funkin.ui.title.TitleState;
import funkin.ui.transition.LoadingState;
import funkin.util.CLIUtil; import funkin.util.CLIUtil;
import funkin.util.CLIUtil.CLIParams; import funkin.util.CLIUtil.CLIParams;
import funkin.util.macro.MacroUtil;
import funkin.util.TimerUtil; import funkin.util.TimerUtil;
import funkin.util.TrackerUtil; import funkin.util.TrackerUtil;
import funkin.util.WindowUtil;
import openfl.display.BitmapData;
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
import Discord.DiscordClient; import funkin.api.discord.DiscordClient;
#end #end
/** /**
@ -125,10 +125,10 @@ class InitState extends FlxState
// DISCORD API SETUP // DISCORD API SETUP
// //
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
DiscordClient.initialize(); DiscordClient.instance.init();
Application.current.onExit.add(function(exitCode) { lime.app.Application.current.onExit.add(function(exitCode) {
DiscordClient.shutdown(); DiscordClient.instance.shutdown();
}); });
#end #end

View file

@ -1,91 +0,0 @@
package funkin.api.discord;
import Sys.sleep;
#if FEATURE_DISCORD_RPC
import discord_rpc.DiscordRpc;
#end
class DiscordClient
{
#if FEATURE_DISCORD_RPC
public function new()
{
trace("Discord Client starting...");
DiscordRpc.start(
{
clientID: "814588678700924999",
onReady: onReady,
onError: onError,
onDisconnected: onDisconnected
});
trace("Discord Client started.");
while (true)
{
DiscordRpc.process();
sleep(2);
// trace("Discord Client Update");
}
DiscordRpc.shutdown();
}
public static function shutdown()
{
DiscordRpc.shutdown();
}
static function onReady()
{
DiscordRpc.presence(
{
details: "In the Menus",
state: null,
largeImageKey: 'icon',
largeImageText: "Friday Night Funkin'"
});
}
static function onError(_code:Int, _message:String)
{
trace('Error! $_code : $_message');
}
static function onDisconnected(_code:Int, _message:String)
{
trace('Disconnected! $_code : $_message');
}
public static function initialize()
{
var DiscordDaemon = sys.thread.Thread.create(() -> {
new DiscordClient();
});
trace("Discord Client initialized");
}
public static function changePresence(details:String, ?state:String, ?smallImageKey:String, ?hasStartTimestamp:Bool, ?endTimestamp:Float)
{
var startTimestamp:Float = if (hasStartTimestamp) Date.now().getTime() else 0;
if (endTimestamp > 0)
{
endTimestamp = startTimestamp + endTimestamp;
}
DiscordRpc.presence(
{
details: details,
state: state,
largeImageKey: 'icon',
largeImageText: "Friday Night Funkin'",
smallImageKey: smallImageKey,
// Obtained times are in milliseconds so they are divided so Discord can use it
startTimestamp: Std.int(startTimestamp / 1000),
endTimestamp: Std.int(endTimestamp / 1000)
});
// trace('Discord RPC Updated. Arguments: $details, $state, $smallImageKey, $hasStartTimestamp, $endTimestamp');
}
#end
}

View file

@ -0,0 +1,204 @@
package funkin.api.discord;
#if FEATURE_DISCORD_RPC
import hxdiscord_rpc.Discord;
import hxdiscord_rpc.Types;
import sys.thread.Thread;
class DiscordClient
{
static final CLIENT_ID:String = "816168432860790794";
public static var instance(get, never):DiscordClient;
static var _instance:Null<DiscordClient> = null;
static function get_instance():DiscordClient
{
if (DiscordClient._instance == null) _instance = new DiscordClient();
if (DiscordClient._instance == null) throw "Could not initialize singleton DiscordClient!";
return DiscordClient._instance;
}
var handlers:DiscordEventHandlers;
private function new()
{
trace('[DISCORD] Initializing event handlers...');
handlers = DiscordEventHandlers.create();
handlers.ready = cpp.Function.fromStaticFunction(onReady);
handlers.disconnected = cpp.Function.fromStaticFunction(onDisconnected);
handlers.errored = cpp.Function.fromStaticFunction(onError);
}
public function init():Void
{
trace('[DISCORD] Initializing connection...');
// Discord.initialize(CLIENT_ID, handlers, true, null);
Discord.Initialize(CLIENT_ID, cpp.RawPointer.addressOf(handlers), 1, null);
createDaemon();
}
var daemon:Thread = null;
function createDaemon():Void
{
daemon = Thread.create(doDaemonWork);
}
function doDaemonWork():Void
{
while (true)
{
trace('[DISCORD] Performing client update...');
#if DISCORD_DISABLE_IO_THREAD
Discord.updateConnection();
#end
Discord.runCallbacks();
Sys.sleep(2);
}
}
public function shutdown():Void
{
trace('[DISCORD] Shutting down...');
Discord.shutdown();
}
public function setPresence(params:DiscordClientPresenceParams):Void
{
trace('[DISCORD] Updating presence... (${params})');
Discord.updatePresence(buildPresence(params));
}
function buildPresence(params:DiscordClientPresenceParams):DiscordRichPresence
{
var presence = DiscordRichPresence.create();
// Presence should always be playing the game.
presence.type = DiscordActivityType_Playing;
// Text when hovering over the large image. We just leave this as the game name.
presence.largeImageText = "Friday Night Funkin'";
// State should be generally what the person is doing, like "In the Menus" or "Pico (Pico Mix) [Freeplay Hard]"
presence.state = cast(params.state, Null<String>);
// Details should be what the person is specifically doing, including stuff like timestamps (maybe something like "03:24 elapsed").
presence.details = cast(params.details, Null<String>);
// The large image displaying what the user is doing.
// This should probably be album art.
// IMPORTANT NOTE: This can be an asset key uploaded to Discord's developer panel OR any URL you like.
presence.largeImageKey = cast(params.largeImageKey, Null<String>) ?? "album-volume1";
trace('[DISCORD] largeImageKey: ${presence.largeImageKey}');
// TODO: Make this use the song's album art.
// presence.largeImageKey = "icon";
// presence.largeImageKey = "https://f4.bcbits.com/img/a0746694746_16.jpg";
// The small inset image for what the user is doing.
// This can be the opponent's health icon?
// NOTE: Like largeImageKey, this can be a URL, or an asset key.
presence.smallImageKey = cast(params.smallImageKey, Null<String>);
// NOTE: In previous versions, this showed as "Elapsed", but now shows as playtime and doesn't look good
// presence.startTimestamp = time - 10;
// presence.endTimestamp = time + 30;
final button1:DiscordButton = DiscordButton.create();
button1.label = "Play on Web";
button1.url = Constants.URL_NEWGROUNDS;
presence.buttons[0] = button1;
final button2:DiscordButton = DiscordButton.create();
button2.label = "Download";
button2.url = Constants.URL_ITCH;
presence.buttons[1] = button2;
return presence;
}
// TODO: WHAT THE FUCK get this pointer bullfuckery out of here
private static function onReady(request:cpp.RawConstPointer<DiscordUser>):Void
{
trace('[DISCORD] Client has connected!');
final username:String = request[0].username;
final globalName:String = request[0].username;
final discriminator:Int = Std.parseInt(request[0].discriminator);
if (discriminator != 0)
{
trace('[DISCORD] User: ${username}#${discriminator} (${globalName})');
}
else
{
trace('[DISCORD] User: @${username} (${globalName})');
}
}
private static function onDisconnected(errorCode:Int, message:cpp.ConstCharStar):Void
{
trace('[DISCORD] Client has disconnected! ($errorCode) "${cast (message, String)}"');
}
private static function onError(errorCode:Int, message:cpp.ConstCharStar):Void
{
trace('[DISCORD] Client has received an error! ($errorCode) "${cast (message, String)}"');
}
// public var type(get, set):DiscordActivityType;
// public var state(get, set):String;
// public var details(get, set):String;
// public var startTimestamp(get, set):Int;
// public var endTimestamp(get, set):Int;
// public var largeImageKey(get, set):String;
// public var largeImageText(get, set):String;
// public var smallImageKey(get, set):String;
// public var smallImageText(get, set):String;
//
//
// public var partyId(get, set)
// public var partySize(get, set)
// public var partyMax(get, set)
// public var partyPrivacy(get, set)
//
// public var buttons(get, set)
//
// public var matchSecret(get, set)
// public var joinSecret(get, set)
// public var spectateSecret(get, set)
}
typedef DiscordClientPresenceParams =
{
/**
* The first row of text below the game title.
*/
var state:String;
/**
* The second row of text below the game title.
* Use `null` to display no text.
*/
var details:Null<String>;
/**
* A large, 4-row high image to the left of the content.
*/
var ?largeImageKey:String;
/**
* A small, inset image to the bottom right of `largeImageKey`.
*/
var ?smallImageKey:String;
}
#end

View file

@ -15,8 +15,8 @@ import flixel.tweens.FlxEase;
import flixel.tweens.FlxTween; import flixel.tweens.FlxTween;
import flixel.ui.FlxBar; import flixel.ui.FlxBar;
import flixel.util.FlxColor; import flixel.util.FlxColor;
import flixel.util.FlxTimer;
import flixel.util.FlxStringUtil; import flixel.util.FlxStringUtil;
import flixel.util.FlxTimer;
import funkin.api.newgrounds.NGio; import funkin.api.newgrounds.NGio;
import funkin.audio.FunkinSound; import funkin.audio.FunkinSound;
import funkin.audio.VoicesGroup; import funkin.audio.VoicesGroup;
@ -44,12 +44,12 @@ import funkin.play.cutscene.dialogue.Conversation;
import funkin.play.cutscene.VanillaCutscenes; import funkin.play.cutscene.VanillaCutscenes;
import funkin.play.cutscene.VideoCutscene; import funkin.play.cutscene.VideoCutscene;
import funkin.play.notes.NoteDirection; import funkin.play.notes.NoteDirection;
import funkin.play.notes.notekind.NoteKindManager;
import funkin.play.notes.NoteSplash; import funkin.play.notes.NoteSplash;
import funkin.play.notes.NoteSprite; import funkin.play.notes.NoteSprite;
import funkin.play.notes.notestyle.NoteStyle; import funkin.play.notes.notestyle.NoteStyle;
import funkin.play.notes.Strumline; import funkin.play.notes.Strumline;
import funkin.play.notes.SustainTrail; import funkin.play.notes.SustainTrail;
import funkin.play.notes.notekind.NoteKindManager;
import funkin.play.scoring.Scoring; import funkin.play.scoring.Scoring;
import funkin.play.song.Song; import funkin.play.song.Song;
import funkin.play.stage.Stage; import funkin.play.stage.Stage;
@ -68,7 +68,7 @@ import openfl.display.BitmapData;
import openfl.geom.Rectangle; import openfl.geom.Rectangle;
import openfl.Lib; import openfl.Lib;
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
import Discord.DiscordClient; import funkin.api.discord.DiscordClient;
#end #end
/** /**
@ -447,10 +447,8 @@ class PlayState extends MusicBeatSubState
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Discord RPC variables // Discord RPC variables
var storyDifficultyText:String = ''; var discordRPCAlbum:String = '';
var iconRPC:String = ''; var discordRPCIcon:String = '';
var detailsText:String = '';
var detailsPausedText:String = '';
#end #end
/** /**
@ -994,7 +992,15 @@ class PlayState extends MusicBeatSubState
} }
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
DiscordClient.changePresence(detailsPausedText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); DiscordClient.instance.setPresence(
{
details: 'Paused - ${buildDiscordRPCDetails()}',
state: buildDiscordRPCState(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
#end #end
} }
} }
@ -1083,8 +1089,14 @@ class PlayState extends MusicBeatSubState
} }
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Game Over doesn't get his own variable because it's only used here DiscordClient.instance.setPresence(
DiscordClient.changePresence('Game Over - ' + detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); {
details: 'Game Over - ${buildDiscordRPCDetails()}',
state: buildDiscordRPCState(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
#end #end
} }
else if (isPlayerDying) else if (isPlayerDying)
@ -1295,14 +1307,29 @@ class PlayState extends MusicBeatSubState
Countdown.resumeCountdown(); Countdown.resumeCountdown();
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
if (startTimer.finished) if (Conductor.instance.songPosition > 0)
{ {
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, // DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true,
currentSongLengthMs - Conductor.instance.songPosition); // currentSongLengthMs - Conductor.instance.songPosition);
DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
} }
else else
{ {
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
} }
#end #end
@ -1328,16 +1355,32 @@ class PlayState extends MusicBeatSubState
#end #end
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) if (health > Constants.HEALTH_MIN && !isGamePaused && FlxG.autoPause)
{ {
if (Conductor.instance.songPosition > 0.0) DiscordClient.changePresence(detailsText, currentSong.song if (Conductor.instance.songPosition > 0.0)
+ ' (' {
+ storyDifficultyText DiscordClient.instance.setPresence(
+ ')', iconRPC, true, {
currentSongLengthMs state: buildDiscordRPCState(),
- Conductor.instance.songPosition); details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
}
else else
DiscordClient.changePresence(detailsText, currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); {
DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
// DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true,
// currentSongLengthMs - Conductor.instance.songPosition);
}
} }
#end #end
@ -1354,8 +1397,17 @@ class PlayState extends MusicBeatSubState
#end #end
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
if (health > Constants.HEALTH_MIN && !paused && FlxG.autoPause) DiscordClient.changePresence(detailsPausedText, if (health > Constants.HEALTH_MIN && !isGamePaused && FlxG.autoPause)
currentSong.song + ' (' + storyDifficultyText + ')', iconRPC); {
DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
}
#end #end
super.onFocusLost(); super.onFocusLost();
@ -1653,6 +1705,11 @@ class PlayState extends MusicBeatSubState
iconP2.zIndex = 850; iconP2.zIndex = 850;
add(iconP2); add(iconP2);
iconP2.cameras = [camHUD]; iconP2.cameras = [camHUD];
#if FEATURE_DISCORD_RPC
discordRPCAlbum = 'album-${currentChart.album}';
discordRPCIcon = 'icon-${currentCharacterData.opponent}';
#end
} }
// //
@ -1768,29 +1825,53 @@ class PlayState extends MusicBeatSubState
function initDiscord():Void function initDiscord():Void
{ {
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
storyDifficultyText = difficultyString(); // Determine the details strings once and reuse them.
iconRPC = currentSong.player2;
// To avoid having duplicate images in Discord assets
switch (iconRPC)
{
case 'senpai-angry':
iconRPC = 'senpai';
case 'monster-christmas':
iconRPC = 'monster';
case 'mom-car':
iconRPC = 'mom';
}
// String that contains the mode defined here so it isn't necessary to call changePresence for each mode
detailsText = isStoryMode ? 'Story Mode: Week $storyWeek' : 'Freeplay';
detailsPausedText = 'Paused - $detailsText';
// Updating Discord Rich Presence. // Updating Discord Rich Presence.
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC); DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
#end #end
} }
function buildDiscordRPCDetails():String
{
if (PlayStatePlaylist.isStoryMode)
{
return 'Story Mode: ${PlayStatePlaylist.campaignTitle}';
}
else
{
if (isChartingMode)
{
return 'Chart Editor [Playtest]';
}
else if (isPracticeMode)
{
return 'Freeplay [Practice]';
}
else if (isBotPlayMode)
{
return 'Freeplay [Bot Play]';
}
else
{
return 'Freeplay';
}
}
}
function buildDiscordRPCState():String
{
var discordRPCDifficulty = PlayState.instance.currentDifficulty.replace('-', ' ').toTitleCase();
return '${currentChart.songName} [${discordRPCDifficulty}]';
}
function initPreciseInputs():Void function initPreciseInputs():Void
{ {
PreciseInputManager.instance.onInputPressed.add(onKeyPress); PreciseInputManager.instance.onInputPressed.add(onKeyPress);
@ -1985,7 +2066,15 @@ class PlayState extends MusicBeatSubState
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Updating Discord Rich Presence (with Time Left) // Updating Discord Rich Presence (with Time Left)
DiscordClient.changePresence(detailsText, '${currentChart.songName} ($storyDifficultyText)', iconRPC, true, currentSongLengthMs); DiscordClient.instance.setPresence(
{
state: buildDiscordRPCState(),
details: buildDiscordRPCDetails(),
largeImageKey: discordRPCAlbum,
smallImageKey: discordRPCIcon
});
// DiscordClient.changePresence(detailsText, '${currentChart.songName} ($discordRPCDifficulty)', discordRPCIcon, true, currentSongLengthMs);
#end #end
if (startTimestamp > 0) if (startTimestamp > 0)

View file

@ -323,6 +323,11 @@ class BaseCharacter extends Bopper
this.cameraFocusPoint = new FlxPoint(charCenterX + _data.cameraOffsets[0], charCenterY + _data.cameraOffsets[1]); this.cameraFocusPoint = new FlxPoint(charCenterX + _data.cameraOffsets[0], charCenterY + _data.cameraOffsets[1]);
} }
public function getHealthIconId():String
{
return _data?.healthIcon?.id ?? Constants.DEFAULT_HEALTH_ICON;
}
public function initHealthIcon(isOpponent:Bool):Void public function initHealthIcon(isOpponent:Bool):Void
{ {
if (!isOpponent) if (!isOpponent)
@ -332,7 +337,7 @@ class BaseCharacter extends Bopper
trace('[WARN] Player 1 health icon not found!'); trace('[WARN] Player 1 health icon not found!');
return; return;
} }
PlayState.instance.iconP1.configure(_data.healthIcon); PlayState.instance.iconP1.configure(_data?.healthIcon);
PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way. PlayState.instance.iconP1.flipX = !PlayState.instance.iconP1.flipX; // BF is looking the other way.
} }
else else
@ -342,7 +347,7 @@ class BaseCharacter extends Bopper
trace('[WARN] Player 2 health icon not found!'); trace('[WARN] Player 2 health icon not found!');
return; return;
} }
PlayState.instance.iconP2.configure(_data.healthIcon); PlayState.instance.iconP2.configure(_data?.healthIcon);
} }
} }

View file

@ -2274,8 +2274,25 @@ class ChartEditorState extends UIState // UIState derives from MusicBeatState
this.openBackupAvailableDialog(welcomeDialog); this.openBackupAvailableDialog(welcomeDialog);
} }
} }
#if FEATURE_DISCORD_RPC
updateDiscordRPC();
#end
} }
#if FEATURE_DISCORD_RPC
function updateDiscordRPC():Void
{
funkin.api.discord.DiscordClient.instance.setPresence(
{
// TODO: Make this display the song name and update when it changes.
// state: '${currentSongName} [${selectedDifficulty}]',
state: null,
details: 'Chart Editor [Charting]'
});
}
#end
function setupWelcomeMusic() function setupWelcomeMusic()
{ {
this.welcomeMusic.loadEmbedded(Paths.music('chartEditorLoop/chartEditorLoop')); this.welcomeMusic.loadEmbedded(Paths.music('chartEditorLoop/chartEditorLoop'));

View file

@ -54,6 +54,9 @@ import funkin.util.SortUtil;
import openfl.display.BlendMode; import openfl.display.BlendMode;
import funkin.data.freeplay.style.FreeplayStyleRegistry; import funkin.data.freeplay.style.FreeplayStyleRegistry;
import funkin.data.song.SongData.SongMusicData; import funkin.data.song.SongData.SongMusicData;
#if FEATURE_DISCORD_RPC
import funkin.api.discord.DiscordClient;
#end
/** /**
* Parameters used to initialize the FreeplayState. * Parameters used to initialize the FreeplayState.
@ -338,7 +341,7 @@ class FreeplayState extends MusicBeatSubState
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Updating Discord Rich Presence // Updating Discord Rich Presence
DiscordClient.changePresence('In the Menus', null); DiscordClient.instance.setPresence({state: 'In the Menus', details: null});
#end #end
var isDebug:Bool = false; var isDebug:Bool = false;

View file

@ -28,7 +28,7 @@ import funkin.ui.story.StoryMenuState;
import funkin.ui.Prompt; import funkin.ui.Prompt;
import funkin.util.WindowUtil; import funkin.util.WindowUtil;
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
import Discord.DiscordClient; import funkin.api.discord.DiscordClient;
#end #end
#if newgrounds #if newgrounds
import funkin.ui.NgPrompt; import funkin.ui.NgPrompt;
@ -55,8 +55,7 @@ class MainMenuState extends MusicBeatState
override function create():Void override function create():Void
{ {
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Updating Discord Rich Presence DiscordClient.instance.setPresence({state: "In the Menus", details: null});
DiscordClient.changePresence("In the Menus", null);
#end #end
FlxG.cameras.reset(new FunkinCamera('mainMenu')); FlxG.cameras.reset(new FunkinCamera('mainMenu'));

View file

@ -23,6 +23,10 @@ import funkin.ui.MusicBeatState;
import funkin.ui.transition.LoadingState; import funkin.ui.transition.LoadingState;
import funkin.ui.transition.StickerSubState; import funkin.ui.transition.StickerSubState;
import funkin.util.MathUtil; import funkin.util.MathUtil;
import openfl.utils.Assets;
#if FEATURE_DISCORD_RPC
import funkin.api.discord.DiscordClient;
#end
class StoryMenuState extends MusicBeatState class StoryMenuState extends MusicBeatState
{ {
@ -217,7 +221,7 @@ class StoryMenuState extends MusicBeatState
#if FEATURE_DISCORD_RPC #if FEATURE_DISCORD_RPC
// Updating Discord Rich Presence // Updating Discord Rich Presence
DiscordClient.changePresence('In the Menus', null); DiscordClient.instance.setPresence({state: 'In the Menus', details: null});
#end #end
} }

View file

@ -78,7 +78,12 @@ class Constants
/** /**
* Link to download the game on Itch.io. * Link to download the game on Itch.io.
*/ */
public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin/purchase'; public static final URL_ITCH:String = 'https://ninja-muffin24.itch.io/funkin';
/**
* Link to play the game on Newgrounds.
*/
public static final URL_NEWGROUNDS:String = 'https://www.newgrounds.com/portal/view/770371';
/** /**
* Link to the game's page on Kickstarter. * Link to the game's page on Kickstarter.