#include "STDInc.hpp" #include namespace Components { DiscordRichPresence discord_presence; Discord::DiscordCustomInformation_t Discord::DiscordCustomInformation; bool Discord_Presence_Initialized = false; static const char* stock_sp_map_names[] = { "killhouse", "cargoship", "coup", "blackout", "armada", "bog_a", "hunted", "ac130", "bog_b", "airlift", "aftermath", "village_assault", "scoutsniper", "sniperescape", "village_defend", "ambush", "icbm", "launchfacility_a", "launchfacility_b", "jeepride", "simplecredits", "airplane", }; bool InMainMenu(std::string map_name_info) { if (map_name_info == "") return true; else return false; } bool VanillaMap(std::string map_name_info) { for (const auto& map_name : stock_sp_map_names) { if (map_name == map_name_info) return true; } return false; } std::string GetDifficultyForState(std::string map_name_info) { if (!InMainMenu(map_name_info)) { const auto difficulty = Dvars::Functions::Dvar_FindVar("g_gameskill")->current.integer; if (difficulty == 0) return "IW3SP_MOD_LOC_DISCORD_DIFFICULTY_EASY"; else if (difficulty == 1) return "IW3SP_MOD_LOC_DISCORD_DIFFICULTY_MEDIUM"; else if (difficulty == 2) return "IW3SP_MOD_LOC_DISCORD_DIFFICULTY_HARD"; else if (difficulty == 3) return "IW3SP_MOD_LOC_DISCORD_DIFFICULTY_FU"; else return "IW3SP_MOD_LOC_DISCORD_DIFFICULTY_UNKNOWN"; } else return ""; } std::string GetMapName(std::string map_name_info) { if (VanillaMap(map_name_info)) { if(map_name_info.contains("ac130") && Dvars::Functions::Dvar_FindVar("credits_active")->current.string == "1"s) return "IW3SP_MOD_LOC_DISCORD_SIMPLECREDITS"; return std::string("IW3SP_MOD_LOC_DISCORD_") + map_name_info.data(); } else { if (InMainMenu(map_name_info)) return "IW3SP_MOD_LOC_DISCORD_MAINMENU"; else return "custom_map"; } } void Discord::DiscordUpdate() { Discord_RunCallbacks(); std::string map_name_offset = reinterpret_cast(0x1290DD8); std::string mapname = GetMapName(map_name_offset.data()); std::string difficulty = GetDifficultyForState(map_name_offset.data()); if (InMainMenu(map_name_offset.data())) { DiscordCustomInformation.details.reset(); DiscordCustomInformation.state.reset(); DiscordCustomInformation.button = std::make_pair("", ""); DiscordCustomInformation.large_image_key.reset(); discord_presence.largeImageKey = "preview_mainmenu"; } else { if (DiscordCustomInformation.large_image_key.has_value()) { const auto& imageUrl = DiscordCustomInformation.large_image_key.value(); discord_presence.largeImageKey = imageUrl.data(); } else { if (VanillaMap(map_name_offset.data())) { std::string imageKey = std::string("preview_") + map_name_offset.data(); discord_presence.largeImageKey = imageKey.data(); } else discord_presence.largeImageKey = nullptr; } } if (DiscordCustomInformation.details.has_value()) { const auto& details_value = DiscordCustomInformation.details.value(); if (details_value.starts_with("@") && details_value.size() > 1) { const auto loc_string = details_value.substr(1); const auto value = Game::UI_SafeTranslateString(loc_string.data()); discord_presence.details = value; } else { discord_presence.details = details_value.data(); } } else { if(mapname.contains("custom_map")) discord_presence.details = Game::UI_ReplaceConversionString(Game::UI_SafeTranslateString("IW3SP_MOD_LOC_DISCORD_MAPUNKNOWN"), map_name_offset.c_str()); else discord_presence.details = Game::UI_SafeTranslateString(mapname.data()); } if (DiscordCustomInformation.state.has_value()) { const auto& state_value = DiscordCustomInformation.state.value(); if (state_value.starts_with("@") && state_value.size() > 1) { const auto loc_string = state_value.substr(1); const auto value = Game::UI_SafeTranslateString(loc_string.data()); discord_presence.state = value; } else discord_presence.state = state_value.data(); } else { if (difficulty.data()) discord_presence.state = Game::UI_SafeTranslateString(difficulty.data()); else discord_presence.state = nullptr; } if (!DiscordCustomInformation.button.first.empty() && !DiscordCustomInformation.button.second.empty()) { const auto& buttonLabel = DiscordCustomInformation.button.first; const auto& buttonUrl = DiscordCustomInformation.button.second; if (buttonLabel.starts_with("@") && buttonLabel.size() > 1) { const auto loc_string = buttonLabel.substr(1); const auto value = Game::UI_SafeTranslateString(loc_string.data()); discord_presence.button2Label = value; } else discord_presence.button2Label = buttonLabel.data(); discord_presence.button2Url = buttonUrl.data(); } else { discord_presence.button2Label = nullptr; discord_presence.button2Url = nullptr; } if (!discord_presence.startTimestamp) { discord_presence.startTimestamp = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); } Discord_UpdatePresence(&discord_presence); } static void ready([[maybe_unused]] const DiscordUser* request) { ZeroMemory(&discord_presence, sizeof(discord_presence)); discord_presence.instance = 1; discord_presence.state = ""; discord_presence.button1Label = "About IW3SP-MOD"; discord_presence.button1Url = "https://gitea.com/JerryALT/iw3sp_mod"; Discord_UpdatePresence(&discord_presence); } static void Errored(const int error_code, const char* message) { Game::Com_Printf(0, "Discord: (%i) %s", error_code, message); } void Discord::DiscordInit() { if (EnableDiscord_RPC && !Discord_Presence_Initialized) { DiscordEventHandlers handlers; ZeroMemory(&handlers, sizeof(handlers)); handlers.ready = ready; handlers.errored = Errored; handlers.disconnected = Errored; handlers.joinGame = nullptr; handlers.spectateGame = nullptr; handlers.joinRequest = nullptr; Discord_Initialize("1088866597940248626", &handlers, 1, nullptr); Discord_Presence_Initialized = true; Scheduler::Loop(Discord::DiscordUpdate, Scheduler::Pipeline::MAIN, 5s); } } void Discord::DiscordShutdown() { if (Discord_Presence_Initialized) { Discord_Shutdown(); Discord_Presence_Initialized = false; } } Discord::Discord() { Discord::DiscordInit(); GSC::AddFunction("setdiscorddetails", [] { if (Game::Scr_GetNumParam() != 1) { Game::Scr_Error("setdiscorddetails ( detail ) requires 1 string parameter"); return; } const char* details = Game::Scr_GetString(0); DiscordCustomInformation.details.emplace(details); Scheduler::Once(Discord::DiscordUpdate, Scheduler::Pipeline::ASYNC); Game::Scr_AddInt(true); }, false); GSC::AddFunction("setdiscordstate", [] { if (Game::Scr_GetNumParam() != 1) { Game::Scr_Error("setdiscordstate ( state ) requires 1 string parameter"); return; } const char* state = Game::Scr_GetString(0); DiscordCustomInformation.state.emplace(state); Scheduler::Once(Discord::DiscordUpdate, Scheduler::Pipeline::ASYNC); Game::Scr_AddInt(true); }, false); GSC::AddFunction("setbuttoninformation", [] { const char* label = Game::Scr_GetString(0); const char* url = Game::Scr_GetString(1); if (strlen(label) > 32) { Game::Scr_Error("URL label maximum is 32 characters"); return; } DiscordCustomInformation.button.first = label; DiscordCustomInformation.button.second = url; Scheduler::Once(Discord::DiscordUpdate, Scheduler::Pipeline::ASYNC); Game::Scr_AddInt(true); }, false); GSC::AddFunction("setdiscordimage", [] { if (Game::Scr_GetNumParam() != 1) { Game::Scr_Error("setdiscordimage ( url ) requires 1 string parameter"); return; } const char* imageLink = Game::Scr_GetString(0); DiscordCustomInformation.large_image_key.emplace(imageLink); Scheduler::Once(Discord::DiscordUpdate, Scheduler::Pipeline::ASYNC); Game::Scr_AddInt(true); }, false); } Discord::~Discord() { Discord::DiscordShutdown(); } }