From 45b548b478ca6b244094a252e6dd24dc7804f63a Mon Sep 17 00:00:00 2001 From: Kai-Li Date: Fri, 7 Apr 2023 22:51:06 +0200 Subject: [PATCH] GENDER: Enable the user to ignore the gender of all players - inspired by https://github.com/PadWorld-Entertainment/worldofpadman-fork/pull/41 - add "none" as a new default gender which will force "they/them" to be used instead of "male" with "he/him" - also "none" is the new default gender for a model if undefined in animations.cfg - add an "ignore gender" option in game settings menu; if enabled, the gender normally set by the player model (male/female/neuter) via animations.cfg is ignored and set to (none) - add gender neutral event messages - fix minor typos in event messages - TODO: Make the user selected option also work without model change, right now it only works if animation.cfg is reloaded. --- code/cgame/cg_event.c | 45 ++++++++++++++++++++-------------------- code/cgame/cg_local.h | 1 + code/cgame/cg_main.c | 2 ++ code/cgame/cg_players.c | 16 +++++++------- code/client/cl_main.c | 2 +- code/game/bg_public.h | 2 +- code/game/g_bot.c | 2 +- code/ui/ui_main.c | 2 ++ code/ui/ui_preferences.c | 22 ++++++++++++++++++++ 9 files changed, 61 insertions(+), 33 deletions(-) diff --git a/code/cgame/cg_event.c b/code/cgame/cg_event.c index 79d4e6787..15d7fbf58 100644 --- a/code/cgame/cg_event.c +++ b/code/cgame/cg_event.c @@ -156,40 +156,39 @@ static void CG_Obituary(entityState_t *ent) { gender = ci->gender; switch (mod) { case MOD_BALLOONY_SPLASH: - if (gender == GENDER_FEMALE) + if (gender == GENDER_MALE) + message = "tripped on his own water bomb"; + else if (gender == GENDER_FEMALE) message = "tripped on her own water bomb"; else if (gender == GENDER_NEUTER) message = "tripped on its own water bomb"; else - message = "tripped on his own water bomb"; + message = "tripped on their own water bomb"; break; case MOD_BETTY_SPLASH: - if (gender == GENDER_FEMALE) + if (gender == GENDER_MALE) + message = "blew himself up"; + else if (gender == GENDER_FEMALE) message = "blew herself up"; else if (gender == GENDER_NEUTER) message = "blew itself up"; else - message = "blew himself up"; + message = "blew themselves up"; break; case MOD_BUBBLEG_SPLASH: - if (gender == GENDER_FEMALE) + if (gender == GENDER_MALE) + message = "melted himself"; + else if (gender == GENDER_FEMALE) message = "melted herself"; else if (gender == GENDER_NEUTER) message = "melted itself"; else - message = "melted himself"; + message = "melted themselves"; break; case MOD_IMPERIUS_SPLASH: message = "should have used a smaller gun"; break; - default: /* - if ( gender == GENDER_FEMALE ) - message = "killed herself"; - else if ( gender == GENDER_NEUTER ) - message = "killed itself"; - else - message = "killed himself"; - break;*/ + default: message = "did the lemming thing"; break; } @@ -206,19 +205,19 @@ static void CG_Obituary(entityState_t *ent) { if (cgs.gametype < GT_TEAM) { if (cgs.gametype == GT_LPS) { - const char *gender_strings[] = {"he", "she", "it", 0}; - CASSERT(ARRAY_LEN(gender_strings) == GENDER_MAX + 1); + const char *gender_strings[] = {"they have", "he has", "she has", "it has", NULL}; +// CASSERT(ARRAY_LEN(gender_strings) == GENDER_MAX + 1); gender = ci->gender; - if (gender > 2 || gender < 0) - gender = 2; + if (gender >= GENDER_MAX || gender < GENDER_NONE) + gender = GENDER_NONE; if (ent->generic1 == 0) - s = va("You fragged %s\n%s has no lives left.", targetName, gender_strings[gender]); + s = va("You fragged %s,\n%s no lives left.", targetName, gender_strings[gender]); else if (ent->generic1 == 1) - s = va("You fragged %s\n%s has 1 live left.", targetName, gender_strings[gender]); + s = va("You fragged %s,\n%s 1 life left.", targetName, gender_strings[gender]); else - s = va("You fragged %s\n%s has %i lives left.", targetName, gender_strings[gender], ent->generic1); + s = va("You fragged %s,\n%s %i lives left.", targetName, gender_strings[gender], ent->generic1); } else s = va("You fragged %s\n%s place with %i", targetName, CG_PlaceString(cg.snap->ps.persistant[PERS_RANK] + 1), cg.snap->ps.persistant[PERS_SCORE]); @@ -282,7 +281,7 @@ static void CG_Obituary(entityState_t *ent) { break; case MOD_SPLASHER: message = "was splashed by"; - message2 = "'s splasher"; + message2 = "'s Splasher"; break; case MOD_BOASTER: message = "was showered by"; @@ -294,7 +293,7 @@ static void CG_Obituary(entityState_t *ent) { break; case MOD_KILLERDUCKS: message = "was hunted & bitten to death by"; - message2 = "'s KillerDuck"; + message2 = "'s Killerduck"; break; case MOD_TELEFRAG: message = "tried to invade"; diff --git a/code/cgame/cg_local.h b/code/cgame/cg_local.h index 14c01496d..7ffe198db 100644 --- a/code/cgame/cg_local.h +++ b/code/cgame/cg_local.h @@ -1317,6 +1317,7 @@ extern vmCvar_t cg_cineDrawLetterBox; extern vmCvar_t cg_glowModel; extern vmCvar_t cg_glowModelTeam; +extern vmCvar_t cg_ignoreGender; extern vmCvar_t cg_warmupReady; extern vmCvar_t cg_curWarmupReady; diff --git a/code/cgame/cg_main.c b/code/cgame/cg_main.c index 16d2fe7d3..0520ab31b 100644 --- a/code/cgame/cg_main.c +++ b/code/cgame/cg_main.c @@ -186,6 +186,7 @@ vmCvar_t cg_glowModelTeam; vmCvar_t cg_warmupReady; vmCvar_t cg_curWarmupReady; +vmCvar_t cg_ignoreGender; vmCvar_t cg_sky; vmCvar_t cg_skyLensflare; @@ -320,6 +321,7 @@ static cvarTable_t cvarTable[] = { // bk001129 {&cg_glowModel, "cg_glowModel", "", CVAR_ARCHIVE}, {&cg_glowModelTeam, "cg_glowModelTeam", "", CVAR_ARCHIVE}, + {&cg_ignoreGender, "cg_ignoreGender", "0", CVAR_ARCHIVE}, /* NOTE: We can't easily extend CS_WARMUP as SV_MapRestart_f() directly sets it */ {&cg_warmupReady, "g_warmupReady", "", CVAR_SYSTEMINFO}, diff --git a/code/cgame/cg_players.c b/code/cgame/cg_players.c index 2ceec2a3b..a2dc8c606 100644 --- a/code/cgame/cg_players.c +++ b/code/cgame/cg_players.c @@ -108,7 +108,7 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t *ci) { ci->footsteps = FOOTSTEP_NORMAL; VectorClear(ci->headOffset); ci->headScale = 1.0f; - ci->gender = GENDER_MALE; + ci->gender = GENDER_NONE; ci->fixedlegs = qfalse; ci->fixedtorso = qfalse; @@ -169,12 +169,14 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t *ci) { if (!token[0]) { break; } - if (token[0] == 'f' || token[0] == 'F') { - ci->gender = GENDER_FEMALE; - } else if (token[0] == 'n' || token[0] == 'N') { - ci->gender = GENDER_NEUTER; - } else { - ci->gender = GENDER_MALE; + if (!cg_ignoreGender.integer) { + if (token[0] == 'm' || token[0] == 'M') { + ci->gender = GENDER_MALE; + } else if (token[0] == 'f' || token[0] == 'F') { + ci->gender = GENDER_FEMALE; + } else if (token[0] == 'n' || token[0] == 'N') { + ci->gender = GENDER_NEUTER; + } } continue; } else if (!Q_stricmp(token, "fixedlegs")) { diff --git a/code/client/cl_main.c b/code/client/cl_main.c index 475eb7a0a..a6c837c4b 100644 --- a/code/client/cl_main.c +++ b/code/client/cl_main.c @@ -3394,7 +3394,7 @@ void CL_Init(void) { Cvar_Get("team_model", "padman", CVAR_USERINFO | CVAR_ARCHIVE); Cvar_Get("team_headmodel", "padman", CVAR_USERINFO | CVAR_ARCHIVE); Cvar_Get("handicap", "100", CVAR_USERINFO | CVAR_ARCHIVE); - Cvar_Get("sex", "male", CVAR_USERINFO | CVAR_ARCHIVE); + Cvar_Get("sex", "none", CVAR_USERINFO | CVAR_ARCHIVE); Cvar_Get("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE); Cvar_Get("password", "", CVAR_USERINFO); diff --git a/code/game/bg_public.h b/code/game/bg_public.h index fb81a9483..3e3d8a4e2 100644 --- a/code/game/bg_public.h +++ b/code/game/bg_public.h @@ -160,7 +160,7 @@ typedef enum { int convertGTStringToGTNumber(const char *argStr); -typedef enum { GENDER_MALE, GENDER_FEMALE, GENDER_NEUTER, GENDER_MAX } gender_t; +typedef enum { GENDER_NONE, GENDER_MALE, GENDER_FEMALE, GENDER_NEUTER, GENDER_MAX } gender_t; /* =================================================================================== diff --git a/code/game/g_bot.c b/code/game/g_bot.c index 62e257801..35ec10d4f 100644 --- a/code/game/g_bot.c +++ b/code/game/g_bot.c @@ -549,7 +549,7 @@ static void G_AddBot(const char *name, float skill, const char *team, int delay, key = "gender"; s = Info_ValueForKey(botinfo, key); if (!*s) { - s = "male"; + s = "none"; } Info_SetValueForKey(userinfo, "sex", s); diff --git a/code/ui/ui_main.c b/code/ui/ui_main.c index d8afff14a..9ed86e6db 100644 --- a/code/ui/ui_main.c +++ b/code/ui/ui_main.c @@ -164,6 +164,7 @@ vmCvar_t wop_AutoBindUnusedKeys; vmCvar_t cg_drawChatIcon; vmCvar_t cg_chatBeep; vmCvar_t cg_glowModel; +vmCvar_t cg_ignoreGender; vmCvar_t cl_renderer; // bk001129 - made static to avoid aliasing. @@ -230,6 +231,7 @@ static cvarTable_t cvarTable[] = {{&ui_ffa_fraglimit, "ui_ffa_fraglimit", "20", {&cg_drawChatIcon, "cg_drawChatIcon", "1", CVAR_ARCHIVE}, {&cg_chatBeep, "cg_chatBeep", "7", CVAR_ARCHIVE}, {&cg_glowModel, "cg_glowModel", "", CVAR_ARCHIVE}, + {&cg_ignoreGender, "cg_ignoreGender", "0", CVAR_ARCHIVE}, {&wop_AutoswitchSongByNextMap, "wop_AutoswitchSongByNextMap", "0", CVAR_ARCHIVE}, {&wop_AutoBindUnusedKeys, "wop_AutoBindUnusedKeys", "1", CVAR_ARCHIVE}, {&cl_renderer, "cl_renderer", "opengl1", CVAR_ARCHIVE}}; diff --git a/code/ui/ui_preferences.c b/code/ui/ui_preferences.c index df4ba6210..c9154a921 100644 --- a/code/ui/ui_preferences.c +++ b/code/ui/ui_preferences.c @@ -77,6 +77,7 @@ PREFERENCES MENU #define ID_SYNCEVERYFRAME 37 #define ID_FORCEMODEL 38 #define ID_GLOWMODEL 39 +#define ID_IGNOREGENDER 40 #define ID_CONNOTIFY 50 #define ID_CHATHEIGHT 51 @@ -133,6 +134,7 @@ typedef struct { menuradiobutton_s synceveryframe; menuradiobutton_s forcemodel; menulist_s glowmodel; + menuradiobutton_s ignoregender; menulist_s connotify; menulist_s chatheight; @@ -190,6 +192,7 @@ static menucommon_s *g_game_options[] = { (menucommon_s *)&s_preferences.synceveryframe, (menucommon_s *)&s_preferences.forcemodel, (menucommon_s *)&s_preferences.glowmodel, + (menucommon_s *)&s_preferences.ignoregender, NULL }; @@ -279,6 +282,8 @@ static void UI_Preferences_SetMenuItems(void) { s_preferences.glowmodel.curvalue = (trap_Cvar_VariableValue("cg_glowModel") + 1); } + s_preferences.ignoregender.curvalue = trap_Cvar_VariableValue("cg_ignoreGender") != 0; + notify = UI_GetCvarInt("con_notifytime"); if (notify < 0) { notify *= -1; @@ -523,6 +528,10 @@ static void UI_Preferences_Event(void *ptr, int notification) { UI_Preferences_UpdateMenuItems(); break; + case ID_IGNOREGENDER: + trap_Cvar_SetValue("cg_ignoreGender", s_preferences.ignoregender.curvalue); + break; + case ID_CONNOTIFY: switch (s_preferences.connotify.curvalue) { case 0: @@ -1028,6 +1037,18 @@ static void UI_Preferences_MenuInit(void) { "Enable to force all players to be displayed with glowing player models in the desired skin color. " "Default is none. NOTE: In team gametypes, the glowing color is always set to red or blue."; + y += (BIGCHAR_HEIGHT + 2); + s_preferences.ignoregender.generic.type = MTYPE_RADIOBUTTON; + s_preferences.ignoregender.generic.name = "Ignore Gender:"; + s_preferences.ignoregender.generic.flags = QMF_SMALLFONT; + s_preferences.ignoregender.generic.id = ID_IGNOREGENDER; + s_preferences.ignoregender.generic.callback = UI_Preferences_Event; + s_preferences.ignoregender.generic.x = XPOSITION; + s_preferences.ignoregender.generic.y = y; + s_preferences.ignoregender.generic.toolTip = + "Enable to ignore the gender of all players, which is normally set by the selected player model. " + "This forces the game to replace he/him, she/her and it/its with they/them. Default is off."; + // chat options y = YPOSITION; s_preferences.connotify.generic.type = MTYPE_SPINCONTROL; @@ -1287,6 +1308,7 @@ static void UI_Preferences_MenuInit(void) { Menu_AddItem(&s_preferences.menu, &s_preferences.synceveryframe); Menu_AddItem(&s_preferences.menu, &s_preferences.forcemodel); Menu_AddItem(&s_preferences.menu, &s_preferences.glowmodel); + Menu_AddItem(&s_preferences.menu, &s_preferences.ignoregender); Menu_AddItem(&s_preferences.menu, &s_preferences.connotify); Menu_AddItem(&s_preferences.menu, &s_preferences.chatheight);