Merge branch 'discord-rp-update' into 'master'

Update Discord Rich Presence for Ring Racers

See merge request KartKrew/Kart!2155
This commit is contained in:
Gunla 2024-03-29 20:30:20 +00:00
commit 20d6dcda88
11 changed files with 538 additions and 166 deletions

View file

@ -1243,11 +1243,6 @@ static void Got_NameAndColor(const UINT8 **cp, INT32 playernum)
// set follower
K_SetFollowerByNum(playernum, follower);
#ifdef HAVE_DISCORDRPC
if (playernum == consoleplayer)
DRPC_UpdatePresence();
#endif
}
enum {

View file

@ -55,6 +55,7 @@
#include "filesrch.h" // refreshdirmenu
#include "k_follower.h"
#include "doomstat.h" // MAXMUSNAMES
#include "discord.h"
// Loops through every constant and operation in word and performs its calculations, returning the final value.
fixed_t get_number(const char *word)
@ -787,6 +788,7 @@ void readgametype(MYFILE *f, char *gtname)
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
UINT8 newgtinttype = 0;
SINT8 newgtspeed = KARTSPEED_AUTO; // KARTSPEED_EASY
char gtconst[MAXLINELEN];
char gppic[9];
char gppicmini[9];
@ -853,6 +855,29 @@ void readgametype(MYFILE *f, char *gtname)
// Case sensitive
newgtinttype = (int)get_number(word2lwr);
}
else if (fastcmp(word, "GAMESPEED"))
{
if (fasticmp(word2, "EASY"))
{
newgtspeed = KARTSPEED_EASY;
}
else if (fasticmp(word2, "NORMAL"))
{
newgtspeed = KARTSPEED_NORMAL;
}
else if (fasticmp(word2, "HARD"))
{
newgtspeed = KARTSPEED_HARD;
}
else if (fasticmp(word2, "ANY"))
{
newgtspeed = KARTSPEED_AUTO;
}
else
{
deh_warning("readgametype %s: unknown gamespeed name %s\n", gtname, word2);
}
}
// Type of level
else if (fastcmp(word, "TYPEOFLEVEL"))
{
@ -945,6 +970,7 @@ void readgametype(MYFILE *f, char *gtname)
newgametype->intermission = newgtinttype;
newgametype->pointlimit = newgtpointlimit;
newgametype->timelimit = newgttimelimit;
newgametype->speed = newgtspeed;
gametypes[numgametypes++] = newgametype;

View file

@ -28,6 +28,9 @@
#include "byteptr.h"
#include "stun.h"
#include "i_tcp.h" // current_port
#include "k_grandprix.h"
#include "k_battle.h"
#include "m_cond.h" // M_GameTrulyStarted
#include "discord.h"
#include "doomdef.h"
@ -35,6 +38,10 @@
// Feel free to provide your own, if you care enough to create another Discord app for this :P
#define DISCORD_APPID "977470696852684833"
#ifdef DEVELOP
#define DISCORD_SECRETIVE
#endif
// length of IP strings
#define IP_SIZE 21
@ -42,8 +49,31 @@ struct discordInfo_s discordInfo;
discordRequest_t *discordRequestList = NULL;
size_t g_discord_skins = 0;
static char self_ip[IP_SIZE];
/*--------------------------------------------------
const char *DRPC_HideUsername(const char *input)
See header file for description.
--------------------------------------------------*/
const char *DRPC_HideUsername(const char *input)
{
static char buffer[5];
int i;
buffer[0] = input[0];
for (i = 1; i < 4; ++i)
{
buffer[i] = '.';
}
buffer[4] = '\0';
return buffer;
}
/*--------------------------------------------------
static char *DRPC_XORIPString(const char *input)
@ -94,11 +124,11 @@ static void DRPC_HandleReady(const DiscordUser *user)
{
if (cv_discordstreamer.value)
{
CONS_Printf("Discord: connected to %s\n", user->username);
CONS_Printf("Discord: connected to %s\n", DRPC_HideUsername(user->username));
}
else
{
CONS_Printf("Discord: connected to %s#%s (%s)\n", user->username, user->discriminator, user->userId);
CONS_Printf("Discord: connected to %s (%s)\n", user->username, user->userId);
}
}
@ -234,8 +264,10 @@ static void DRPC_HandleJoinRequest(const DiscordUser *requestUser)
newRequest->username = Z_Calloc(344, PU_STATIC, NULL);
snprintf(newRequest->username, 344, "%s", requestUser->username);
#if 0
newRequest->discriminator = Z_Calloc(8, PU_STATIC, NULL);
snprintf(newRequest->discriminator, 8, "%s", requestUser->discriminator);
#endif
newRequest->userID = Z_Calloc(32, PU_STATIC, NULL);
snprintf(newRequest->userID, 32, "%s", requestUser->userId);
@ -301,7 +333,9 @@ void DRPC_RemoveRequest(discordRequest_t *removeRequest)
}
Z_Free(removeRequest->username);
#if 0
Z_Free(removeRequest->discriminator);
#endif
Z_Free(removeRequest->userID);
Z_Free(removeRequest);
}
@ -394,6 +428,68 @@ static void DRPC_EmptyRequests(void)
}
}
#ifndef DISCORD_SECRETIVE
/*--------------------------------------------------
static boolean DRPC_DisplayGonerSetup(void)
Returns true if we're in the initial
tutorial game state.
--------------------------------------------------*/
static boolean DRPC_DisplayGonerSetup(void)
{
if (M_GameTrulyStarted())
{
// We're past all that tutorial stuff.
return false;
}
if (Playing())
{
// Need to check a bunch of stuff manually,
// since with command line and/or console you
// can play a bit of the game without fully
// fully starting the game.
if (netgame)
{
// We smuggled into a netgame early,
// show the netgame's info.
return false;
}
if (tutorialchallenge == TUTORIALSKIP_INPROGRESS)
{
// Attempting the Dirty Bubble Challenge
return true;
}
// If it's not GT_TUTORIAL, it's directly
// command line into a specific map.
return (gametype == GT_TUTORIAL);
}
// If we're in a menu, and the game hasn't started,
// then we're definitely in goner setup.
return true;
}
#endif
enum {
DISCORD_GS_UNKNOWN,
DISCORD_GS_CUSTOM,
DISCORD_GS_RACE,
DISCORD_GS_BATTLE,
DISCORD_GS_TUTORIAL,
DISCORD_GS_TIMEATTACK,
DISCORD_GS_GRANDPRIX,
DISCORD_GS_VOTING,
DISCORD_GS_MENU,
DISCORD_GS_REPLAY,
DISCORD_GS_TITLE,
DISCORD_GS_CREDITS,
DISCORD_GS_GONER
};
/*--------------------------------------------------
void DRPC_UpdatePresence(void)
@ -401,17 +497,6 @@ static void DRPC_EmptyRequests(void)
--------------------------------------------------*/
void DRPC_UpdatePresence(void)
{
#ifdef USEMAPIMG
char mapimg[8+1];
#endif
#ifndef DEVELOP
char detailstr[48+1];
char mapname[5+21+21+2+1];
char charimg[4+SKINNAMESIZE+1];
char charname[11+SKINNAMESIZE+1];
#endif
boolean joinSecretSet = false;
char *clientJoinSecret = NULL;
@ -432,14 +517,12 @@ void DRPC_UpdatePresence(void)
return;
}
#ifdef DEVELOP
#ifdef DISCORD_SECRETIVE
// This way, we can use the invite feature in-dev, but not have snoopers seeing any potential secrets! :P
discordPresence.largeImageKey = "miscdevelop";
discordPresence.largeImageKey = "misc_develop";
discordPresence.largeImageText = "No peeking!";
discordPresence.state = "Development EXE";
#endif // DEVELOP
// Server info
if (netgame)
{
if (DRPC_InvitesAreAllowed() == true)
@ -459,7 +542,170 @@ void DRPC_UpdatePresence(void)
}
}
#ifndef DEVELOP
discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field!
discordPresence.partySize = D_NumPlayers(); // Players in server
discordPresence.partyMax = discordInfo.maxPlayers; // Max players
}
else
{
// Reset discord info if you're not in a place that uses it!
// Important for if you join a server that compiled without HAVE_DISCORDRPC,
// so that you don't ever end up using bad information from another server.
memset(&discordInfo, 0, sizeof(discordInfo));
}
#else
char detailstr[128];
char localstr[128];
char charimg[32];
char charname[128];
char gtname[128];
UINT8 gs = DISCORD_GS_UNKNOWN;
if (DRPC_DisplayGonerSetup())
{
gs = DISCORD_GS_GONER;
}
else if (demo.playback)
{
switch (demo.attract)
{
case DEMO_ATTRACT_TITLE:
{
gs = DISCORD_GS_TITLE;
break;
}
case DEMO_ATTRACT_CREDITS:
{
gs = DISCORD_GS_CREDITS;
break;
}
default:
{
gs = DISCORD_GS_REPLAY;
break;
}
}
}
else
{
switch (gamestate)
{
case GS_LEVEL:
case GS_INTERMISSION:
{
if (grandprixinfo.gp == true)
{
gs = DISCORD_GS_GRANDPRIX;
}
else if (modeattacking)
{
gs = DISCORD_GS_TIMEATTACK;
}
else if (gametype >= GT_FIRSTFREESLOT)
{
gs = DISCORD_GS_CUSTOM;
}
else
{
switch (gametype)
{
case GT_RACE:
{
gs = DISCORD_GS_RACE;
break;
}
case GT_BATTLE:
{
gs = DISCORD_GS_BATTLE;
break;
}
case GT_TUTORIAL:
{
gs = DISCORD_GS_TUTORIAL;
break;
}
case GT_SPECIAL:
case GT_VERSUS:
{
// You're using command line.
// Just patch over this for now.
gs = DISCORD_GS_GRANDPRIX;
break;
}
default:
{
break; // leave as UNKNOWN...
}
}
}
break;
}
case GS_CEREMONY:
{
gs = DISCORD_GS_GRANDPRIX;
break;
}
case GS_VOTING:
{
gs = DISCORD_GS_VOTING;
break;
}
case GS_TITLESCREEN:
{
gs = DISCORD_GS_TITLE;
break;
}
case GS_CREDITS:
{
gs = DISCORD_GS_CREDITS;
break;
}
default:
{
gs = DISCORD_GS_MENU;
break;
}
}
}
// Server info
if (gs == DISCORD_GS_GONER)
{
if (Playing())
{
discordPresence.state = "TRAINING DATA";
}
else if (gamedata->gonerlevel >= GDGONER_OUTRO)
{
discordPresence.state = "EVALUATION";
}
else
{
discordPresence.state = "MISSING DATA";
}
}
else if (netgame)
{
if (DRPC_InvitesAreAllowed() == true)
{
const char *join;
// Grab the host's IP for joining.
if ((join = DRPC_GetServerIP()) != NULL)
{
discordPresence.joinSecret = DRPC_XORIPString(join);
joinSecretSet = true;
}
else
{
return;
}
}
if (cv_advertise.value)
{
discordPresence.state = "Public";
@ -468,7 +714,6 @@ void DRPC_UpdatePresence(void)
{
discordPresence.state = "Private";
}
#endif // DEVELOP
discordPresence.partyId = server_context; // Thanks, whoever gave us Mumble support, for implementing the EXACT thing Discord wanted for this field!
discordPresence.partySize = D_NumPlayers(); // Players in server
@ -481,71 +726,115 @@ void DRPC_UpdatePresence(void)
// so that you don't ever end up using bad information from another server.
memset(&discordInfo, 0, sizeof(discordInfo));
#ifndef DEVELOP
// Offline info
if (Playing())
discordPresence.state = "Offline";
else if (demo.playback && !demo.attract)
discordPresence.state = "Watching Replay";
else
discordPresence.state = "Menu";
#endif // DEVELOP
}
#ifndef DEVELOP
// Gametype info
if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING) && Playing())
{
if (modeattacking)
discordPresence.details = "Time Attack";
{
snprintf(localstr, 128, "Local (%dP)", splitscreen + 1);
discordPresence.state = localstr;
}
else
{
snprintf(detailstr, 48, "%s%s%s",
gametypes[gametype]->name,
(gametyperules & GTR_CIRCUIT) ? va(" | %s", kartspeed_cons_t[gamespeed].strvalue) : "",
(encoremode == true) ? " | Encore" : ""
);
discordPresence.details = detailstr;
switch (gs)
{
case DISCORD_GS_REPLAY:
{
discordPresence.state = "Watching Replay";
break;
}
case DISCORD_GS_TITLE:
{
discordPresence.state = "Title Screen";
break;
}
case DISCORD_GS_CREDITS:
{
discordPresence.state = "Watching Credits";
break;
}
default:
{
discordPresence.state = "Menu";
break;
}
}
}
}
if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) // Map info
&& !(demo.playback && demo.attract))
if (gs == DISCORD_GS_GONER)
{
#ifdef USEMAPIMG
if ((gamemap >= 1 && gamemap <= 60) // supported race maps
|| (gamemap >= 136 && gamemap <= 164)) // supported battle maps
{
//FIXME
//snprintf(mapimg, 8, "%s", G_BuildMapName(gamemap));
strlwr(mapimg);
discordPresence.largeImageKey = mapimg; // Map image
}
else
#endif
if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
{
// Hell map, use the method that got you here :P
discordPresence.largeImageKey = "miscdice";
}
else
{
// This is probably a custom map!
discordPresence.largeImageKey = "mapcustom";
}
// Gametype info
discordPresence.details = "Setup";
if (mapheaderinfo[gamemap-1]->menuflags & LF2_HIDEINMENU)
discordPresence.largeImageKey = "gs_goner";
discordPresence.largeImageText = "NO SIGNAL";
}
else
{
// Gametype info
if ((gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_CEREMONY) && Playing())
{
// Hell map, hide the name
discordPresence.largeImageText = "Map: ???";
}
else
{
// Map name on tool tip
char *title = G_BuildMapTitle(gamemap);
snprintf(mapname, 48, "Map: %s", title);
Z_Free(title);
discordPresence.largeImageText = mapname;
if (grandprixinfo.gp)
{
char roundstr[32];
if (gamestate == GS_CEREMONY)
{
snprintf(roundstr, 32, " | Ceremony");
}
else
{
switch (grandprixinfo.eventmode)
{
case GPEVENT_BONUS:
{
snprintf(roundstr, 32, " | Bonus");
break;
}
case GPEVENT_SPECIAL:
{
snprintf(roundstr, 32, " | Special");
break;
}
case GPEVENT_NONE:
{
if (roundqueue.position > 0 && roundqueue.position <= roundqueue.size)
{
snprintf(roundstr, 32, " | Round %d", roundqueue.position);
}
break;
}
}
}
snprintf(detailstr, 128, "Grand Prix%s | %s",
roundstr,
grandprixinfo.masterbots ? "Master" : kartspeed_cons_t[grandprixinfo.gamespeed + 1].strvalue
);
discordPresence.details = detailstr;
}
else if (battleprisons == true)
{
discordPresence.details = "Prison Break";
}
else if (modeattacking)
{
if (modeattacking & ATTACKING_SPB)
{
discordPresence.details = "SPB Attack";
}
else
{
discordPresence.details = "Time Attack";
}
}
else
{
snprintf(detailstr, 128, "%s%s%s",
gametypes[gametype]->name,
(gametypes[gametype]->speed == KARTSPEED_AUTO) ? va(" | %s", kartspeed_cons_t[gamespeed + 1].strvalue) : "",
(encoremode == true) ? " | Encore" : ""
);
discordPresence.details = detailstr;
}
}
if (gamestate == GS_LEVEL && Playing())
@ -561,90 +850,105 @@ void DRPC_UpdatePresence(void)
discordPresence.endTimestamp = mapTimeEnd;
}
}
}
else if (gamestate == GS_VOTING)
{
discordPresence.largeImageKey = ((gametype == GT_BATTLE) ? "miscredplanet" : "miscblueplanet");
discordPresence.largeImageText = "Voting";
}
else
{
discordPresence.largeImageKey = "misctitle";
discordPresence.largeImageText = "Title Screen";
}
// Character info
if (Playing() && playeringame[consoleplayer] && !players[consoleplayer].spectator)
{
// Supported skin names
static const char *supportedSkins[] = {
// base game
"sonic",
"tails",
"knuckles",
"eggman",
"metalsonic",
// bonus chars
"flicky",
"motobug",
"amy",
"mighty",
"ray",
"espio",
"vector",
"chao",
"gamma",
"chaos",
"shadow",
"rouge",
"herochao",
"darkchao",
"cream",
"omega",
"blaze",
"silver",
"wonderboy",
"arle",
"nights",
"sakura",
"ulala",
"beat",
"vyse",
"aiai",
"kiryu",
"aigis",
"miku",
"doom",
NULL
};
boolean customChar = true;
UINT8 checkSkin = 0;
// Character image
while (supportedSkins[checkSkin] != NULL)
// Gametype image
switch (gs)
{
if (!strcmp(skins[players[consoleplayer].skin].name, supportedSkins[checkSkin]))
case DISCORD_GS_CUSTOM:
{
snprintf(charimg, 21, "char%s", supportedSkins[checkSkin]);
discordPresence.smallImageKey = charimg;
customChar = false;
discordPresence.largeImageKey = "custom_gs";
snprintf(gtname, 128, "%s", gametypes[gametype]->name);
discordPresence.largeImageText = gtname;
break;
}
case DISCORD_GS_RACE:
{
discordPresence.largeImageKey = "gs_race";
discordPresence.largeImageText = "Race";
break;
}
case DISCORD_GS_BATTLE:
{
discordPresence.largeImageKey = "gs_battle";
discordPresence.largeImageText = "Battle";
break;
}
case DISCORD_GS_TUTORIAL:
{
discordPresence.largeImageKey = "gs_tutorial";
discordPresence.largeImageText = "Tutorial";
break;
}
case DISCORD_GS_TIMEATTACK:
{
discordPresence.largeImageKey = "gs_timeattack";
discordPresence.largeImageText = "Time Attack";
break;
}
case DISCORD_GS_GRANDPRIX:
{
discordPresence.largeImageKey = "gs_grandprix";
discordPresence.largeImageText = "Grand Prix";
break;
}
case DISCORD_GS_VOTING:
{
discordPresence.largeImageKey = "gs_voting";
discordPresence.largeImageText = "Voting";
break;
}
case DISCORD_GS_MENU:
{
discordPresence.largeImageKey = "gs_menu";
discordPresence.largeImageText = "Menu";
break;
}
case DISCORD_GS_REPLAY:
{
discordPresence.largeImageKey = "gs_replay";
discordPresence.largeImageText = "Watching Replay";
break;
}
case DISCORD_GS_TITLE:
{
discordPresence.largeImageKey = "gs_title";
discordPresence.largeImageText = "Title Screen";
break;
}
case DISCORD_GS_CREDITS:
{
discordPresence.largeImageKey = "gs_credits";
discordPresence.largeImageText = "Credits";
break;
}
default:
{
discordPresence.largeImageKey = "misc_develop";
discordPresence.largeImageText = "Invalid DRPC state?";
break;
}
checkSkin++;
}
if (customChar == true)
// Character info
if (Playing() && playeringame[consoleplayer] && !players[consoleplayer].spectator)
{
// Use the custom character icon!
discordPresence.smallImageKey = "charcustom";
}
// Character image
if ((unsigned)players[consoleplayer].skin < g_discord_skins) // Supported skins
{
snprintf(charimg, 32, "char_%s", skins[ players[consoleplayer].skin ].name);
discordPresence.smallImageKey = charimg;
}
else
{
// Use the custom character icon!
discordPresence.smallImageKey = "custom_char";
}
snprintf(charname, 28, "Character: %s", skins[players[consoleplayer].skin].realname);
discordPresence.smallImageText = charname; // Character name
snprintf(charname, 128, "Character: %s", skins[players[consoleplayer].skin].realname);
discordPresence.smallImageText = charname; // Character name
}
}
#endif // DEVELOP
#endif // DISCORD_SECRETIVE
if (joinSecretSet == false)
{

View file

@ -33,7 +33,9 @@ extern struct discordInfo_s {
struct discordRequest_t {
char *username; // Discord user name.
#if 0 // Good night, sweet prince...
char *discriminator; // Discord discriminator (The little hashtag thing after the username). Separated for a "hide discriminators" cvar.
#endif
char *userID; // The ID of the Discord user, gets used with Discord_Respond()
// HAHAHA, no.
@ -48,6 +50,18 @@ struct discordRequest_t {
extern discordRequest_t *discordRequestList;
extern size_t g_discord_skins;
/*--------------------------------------------------
const char *DRPC_HideUsername(const char *input);
Handle usernames while cv_discordstreamer is activated.
(The loss of discriminators is still a dumbass regression
that I will never forgive the Discord developers for.)
--------------------------------------------------*/
const char *DRPC_HideUsername(const char *input);
/*--------------------------------------------------
void DRPC_RemoveRequest(void);

View file

@ -624,6 +624,7 @@ struct gametype_t
UINT32 rules;
UINT32 tol;
UINT8 intermission;
SINT8 speed;
INT32 pointlimit;
INT32 timelimit;
char gppic[9];

View file

@ -3181,6 +3181,7 @@ static gametype_t defaultgametypes[] =
GTR_CIRCUIT|GTR_BOTS|GTR_ENCORE,
TOL_RACE,
int_time,
KARTSPEED_AUTO,
0,
0,
"",
@ -3194,6 +3195,7 @@ static gametype_t defaultgametypes[] =
GTR_SPHERES|GTR_BUMPERS|GTR_PAPERITEMS|GTR_POWERSTONES|GTR_KARMA|GTR_ITEMARROWS|GTR_PRISONS|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME|GTR_CLOSERPLAYERS,
TOL_BATTLE,
int_scoreortimeattack,
KARTSPEED_EASY,
0,
3,
"TT_RNDB",
@ -3207,6 +3209,7 @@ static gametype_t defaultgametypes[] =
GTR_CATCHER|GTR_SPECIALSTART|GTR_ROLLINGSTART|GTR_CIRCUIT|GTR_NOPOSITION,
TOL_SPECIAL,
int_time,
KARTSPEED_AUTO,
0,
0,
"TT_RNDSS",
@ -3220,6 +3223,7 @@ static gametype_t defaultgametypes[] =
GTR_BOSS|GTR_SPHERES|GTR_BUMPERS|GTR_POINTLIMIT|GTR_CLOSERPLAYERS|GTR_NOCUPSELECT|GTR_ENCORE,
TOL_VERSUS,
int_scoreortimeattack,
KARTSPEED_EASY,
0,
0,
"",
@ -3233,6 +3237,7 @@ static gametype_t defaultgametypes[] =
GTR_CHECKPOINTS|GTR_NOMP|GTR_NOCUPSELECT|GTR_NOPOSITION,
TOL_TUTORIAL,
int_none,
KARTSPEED_EASY,
0,
0,
"",

View file

@ -2513,7 +2513,7 @@ static void HU_DrawRankings(void)
V_DrawCenteredString(256, 8, 0, "POINT LIMIT");
V_DrawCenteredString(256, 16, hilicol, va("%d", g_pointlimit));
}
else if (gametyperules & GTR_CIRCUIT)
else if (gametypes[gametype]->speed == KARTSPEED_AUTO)
{
V_DrawCenteredString(256, 8, 0, "GAME SPEED");
V_DrawCenteredString(256, 16, hilicol, (cv_4thgear.value) ? va("4th Gear") : kartspeed_cons_t[1+gamespeed].strvalue);

View file

@ -2567,6 +2567,7 @@ static int lib_gAddGametype(lua_State *L)
INT32 newgtpointlimit = 0;
INT32 newgttimelimit = 0;
UINT8 newgtinttype = 0;
SINT8 newgtspeed = KARTSPEED_AUTO;
INT16 j;
luaL_checktype(L, 1, LUA_TTABLE);
@ -2632,6 +2633,14 @@ static int lib_gAddGametype(lua_State *L)
if (!lua_isstring(L, 3))
TYPEERROR("gppicmini", LUA_TSTRING)
gppicmini = lua_tostring(L, 3);
} else if (i == 10 || (k && fasticmp(k, "speed"))) {
if (!lua_isnumber(L, 3))
TYPEERROR("speed", LUA_TNUMBER)
newgtspeed = (UINT32)lua_tointeger(L, 3);
if (newgtspeed < KARTSPEED_AUTO || newgtspeed > KARTSPEED_HARD)
{
newgtspeed = KARTSPEED_AUTO;
}
}
lua_pop(L, 1);
}
@ -2672,6 +2681,7 @@ static int lib_gAddGametype(lua_State *L)
newgametype->intermission = newgtinttype;
newgametype->pointlimit = newgtpointlimit;
newgametype->timelimit = newgttimelimit;
newgametype->speed = newgtspeed;
if (gppic != NULL)
{

View file

@ -107,9 +107,9 @@ const char *M_GetDiscordName(discordRequest_t *r)
return "";
if (cv_discordstreamer.value)
return r->username;
return DRPC_HideUsername(r->username);
return va("%s#%s", r->username, r->discriminator);
return r->username;
}
#endif // HAVE_DISCORDRPC

View file

@ -7679,7 +7679,8 @@ static void P_InitLevelSettings(void)
g_exit.retry = false;
// Gamespeed and frantic items
gamespeed = KARTSPEED_EASY;
const boolean multi_speed = (gametypes[gametype]->speed == KARTSPEED_AUTO);
gamespeed = multi_speed ? KARTSPEED_EASY : gametypes[gametype]->speed;
franticitems = false;
if (K_PodiumSequence() == true)
@ -7694,7 +7695,7 @@ static void P_InitLevelSettings(void)
}
else if (grandprixinfo.gp == true)
{
if (gametyperules & GTR_CIRCUIT)
if (multi_speed)
{
gamespeed = grandprixinfo.gamespeed;
}
@ -7704,18 +7705,21 @@ static void P_InitLevelSettings(void)
|| tutorialchallenge == TUTORIALSKIP_INPROGRESS
)
{
if ((gametyperules & GTR_CATCHER) && encoremode == false)
if (multi_speed)
{
gamespeed = KARTSPEED_NORMAL;
}
else if (gametyperules & GTR_CIRCUIT)
{
gamespeed = KARTSPEED_HARD;
if ((gametyperules & GTR_CATCHER) && encoremode == false)
{
gamespeed = KARTSPEED_NORMAL;
}
else
{
gamespeed = KARTSPEED_HARD;
}
}
}
else
{
if (gametyperules & GTR_CIRCUIT)
if (multi_speed)
{
if (cv_kartspeed.value == KARTSPEED_AUTO)
gamespeed = ((speedscramble == -1) ? KARTSPEED_NORMAL : (UINT8)speedscramble);

View file

@ -35,6 +35,7 @@
#include "k_kart.h" // K_KartResetPlayerColor
#endif
#include "k_grandprix.h" // K_CanChangeRules
#include "discord.h"
#ifdef HWRENDER
#include "hardware/hw_md2.h"
#endif
@ -184,6 +185,13 @@ void R_InitSkins(void)
R_AddSkins((UINT16)i, true);
R_PatchSkins((UINT16)i, true);
R_LoadSpriteInfoLumps(i, wadfiles[i]->numlumps);
#ifdef HAVE_DISCORDRPC
if (i == mainwads)
{
g_discord_skins = numskins;
}
#endif
}
ST_ReloadSkinFaceGraphics();
M_UpdateConditionSetsPending();
@ -430,6 +438,11 @@ static void SetSkin(player_t *player, INT32 skinnum)
// for replays: We have changed our skin mid-game; let the game know so it can do the same in the replay!
demo_extradata[(player-players)] |= DXD_SKIN;
#ifdef HAVE_DISCORDRPC
if (player - players == consoleplayer)
DRPC_UpdatePresence();
#endif
}
// Gets the player to the first usuable skin in the game.