Skip to content

Commit

Permalink
GENDER: Enable the user to select the gender
Browse files Browse the repository at this point in the history
- inspired by PadWorld-Entertainment/worldofpadman-fork#41
- add "none" as a new gender which will force "they/them" to be used instead of "male" with "he/him"
- add "model" as the new default gender which will force always to apply the gender of the selected player model (to keep old behavior)
- also "none" is the fallback for a model if a gender is undefined in animations.cfg
- add gender neutral event messages
- fix minor typos in event messages
- add gender selection option (model/male/female/neuter/none) in player settings menu; if "model" the gender is normally set by the player model via animations.cfg, else taken from user info "sex"
  • Loading branch information
kai-li-wop committed Nov 9, 2023
1 parent 47e1ad5 commit 9796053
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 102 deletions.
43 changes: 21 additions & 22 deletions code/cgame/cg_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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};
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]);
Expand Down Expand Up @@ -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";
Expand All @@ -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";
Expand Down
3 changes: 2 additions & 1 deletion code/cgame/cg_local.h
Original file line number Diff line number Diff line change
Expand Up @@ -369,7 +369,8 @@ typedef struct {
float headScale;

footstep_t footsteps;
gender_t gender; // from model
gender_t gender; // user settings (can also be from model)
gender_t genderModel; // cached value from model (animation.cfg)

qhandle_t legsModel;
qhandle_t legsSkin;
Expand Down
56 changes: 34 additions & 22 deletions code/cgame/cg_players.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ 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->fixedlegs = qfalse;
ci->fixedtorso = qfalse;

Expand Down Expand Up @@ -167,14 +166,15 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t *ci) {
} else if (!Q_stricmp(token, "sex")) {
token = COM_Parse(&text_p);
if (!token[0]) {
ci->genderModel = GENDER_NONE;
break;
}
if (token[0] == 'f' || token[0] == 'F') {
ci->gender = GENDER_FEMALE;
if (token[0] == 'm' || token[0] == 'M') {
ci->genderModel = GENDER_MALE;
} else if (token[0] == 'f' || token[0] == 'F') {
ci->genderModel = GENDER_FEMALE;
} else if (token[0] == 'n' || token[0] == 'N') {
ci->gender = GENDER_NEUTER;
} else {
ci->gender = GENDER_MALE;
ci->genderModel = GENDER_NEUTER;
}
continue;
} else if (!Q_stricmp(token, "fixedlegs")) {
Expand All @@ -195,7 +195,6 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t *ci) {

// read information for each frame
for (i = 0; i < MAX_ANIMATIONS; i++) {

token = COM_Parse(&text_p);
if (!token[0]) {
if (i >= TORSO_GETFLAG && i <= TORSO_NEGATIVE) {
Expand Down Expand Up @@ -261,8 +260,8 @@ static qboolean CG_ParseAnimationFile(const char *filename, clientInfo_t *ci) {
if (fps == 0) {
fps = 1;
}
animations[i].frameLerp = 1000 / fps;
animations[i].initialLerp = 1000 / fps;
animations[i].frameLerp = 1000.0f / fps;
animations[i].initialLerp = 1000.0f / fps;
}

if (i != MAX_ANIMATIONS) {
Expand Down Expand Up @@ -668,18 +667,21 @@ static void CG_LoadClientInfo(int clientNum, clientInfo_t *ci) {
CG_ResetPlayerEntity(&cg_entities[i]);
}
}

if (ci->gender == GENDER_MAX) {
ci->gender = ci->genderModel;
}
}

/*
======================
CG_CopyClientInfoModel
======================
*/
static void CG_CopyClientInfoModel(clientInfo_t *from, clientInfo_t *to) {
/**
* @brief Copies the attributes of a model from its @c animation.cfg properties to the new @c clientInfo_t
* @sa CG_ParseAnimationFile()
*/
static void CG_CopyClientInfoModel(const clientInfo_t *from, clientInfo_t *to) {
VectorCopy(from->headOffset, to->headOffset);
to->headScale = from->headScale;
to->footsteps = from->footsteps;
to->gender = from->gender;
to->genderModel = from->genderModel;

to->legsModel = from->legsModel;
to->legsSkin = from->legsSkin;
Expand All @@ -700,7 +702,7 @@ CG_ScanForExistingClientInfo
*/
static qboolean CG_ScanForExistingClientInfo(clientInfo_t *ci) {
int i;
clientInfo_t *match;
const clientInfo_t *match;

for (i = 0; i < cgs.maxclients; i++) {
match = &cgs.clientinfo[i];
Expand Down Expand Up @@ -801,11 +803,9 @@ static void CG_SetDeferredClientInfo(int clientNum, clientInfo_t *ci) {
CG_LoadClientInfo(clientNum, ci);
}

/*
======================
CG_NewClientInfo
======================
*/
/**
* @sa ClientUserinfoChanged()
*/
void CG_NewClientInfo(int clientNum) {
clientInfo_t *ci;
clientInfo_t newInfo;
Expand Down Expand Up @@ -879,6 +879,17 @@ void CG_NewClientInfo(int clientNum) {
// team leader
v = Info_ValueForKey(configstring, "tl");
newInfo.teamLeader = atoi(v);

// sex
// if a sex option is set in the userinfo string we take this,
// otherwise we check for GENDER_MAX in CG_ParseAnimationFile() to use the
// setting from animation.cfg
newInfo.gender = GENDER_MAX;
v = Info_ValueForKey(configstring, "s");
if (v[0] != '\0') {
newInfo.gender = atoi(v);
}

oldTeam = ci->team;

// this may be run before the first snapshot arrives
Expand Down Expand Up @@ -987,6 +998,7 @@ void CG_NewClientInfo(int clientNum) {

// replace whatever was there with the new one
newInfo.infoValid = qtrue;

*ci = newInfo;

// if the local client changed teams in a team gametype, adjust team- and enemymodels
Expand Down
2 changes: 1 addition & 1 deletion code/client/cl_main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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", "model", CVAR_USERINFO | CVAR_ARCHIVE);
Cvar_Get("cl_anonymous", "0", CVAR_USERINFO | CVAR_ARCHIVE);

Cvar_Get("password", "", CVAR_USERINFO);
Expand Down
2 changes: 1 addition & 1 deletion code/game/bg_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/*
===================================================================================
Expand Down
2 changes: 1 addition & 1 deletion code/game/g_bot.c
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ static void G_AddBot(const char *name, float skill, const char *team, int delay,
// have the server allocate a client slot
clientNum = trap_BotAllocateClient();
if (clientNum == -1) {
G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n");
G_Printf(S_COLOR_RED "Unable to add bot. All player slots are in use.\n");
G_Printf(S_COLOR_RED "Start server with more 'open' slots (or check setting of sv_maxclients cvar).\n");
return;
}
Expand Down
46 changes: 29 additions & 17 deletions code/game/g_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -741,17 +741,15 @@ static void ClientCleanName(const char *in, char *out, int outSize) {
Q_strncpyz(out, "UnnamedPlayer", outSize);
}

/*
===========
ClientUserInfoChanged
Called from ClientConnect when the player first connects and
directly by the server system when the player updates a userinfo variable.
The game can override any of the settings and call trap_SetUserinfo
if desired.
============
*/
/**
* Called from ClientConnect when the player first connects and
* directly by the server system when the player updates a userinfo variable.
*
* The game can override any of the settings and call trap_SetUserinfo
* if desired.
*
* @see CG_NewClientInfo()
*/
void ClientUserinfoChanged(int clientNum) {
gentity_t *ent;
int teamLeader, team, health;
Expand All @@ -763,6 +761,7 @@ void ClientUserinfoChanged(int clientNum) {
char c1[MAX_INFO_STRING];
char c2[MAX_INFO_STRING];
char userinfo[MAX_INFO_STRING];
gender_t gender;

ent = g_entities + clientNum;
client = ent->client;
Expand Down Expand Up @@ -815,6 +814,20 @@ void ClientUserinfoChanged(int clientNum) {
}
client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;

// set gender
s = Info_ValueForKey(userinfo, "sex");
if (!Q_stricmp(s, "neuter")) {
gender = GENDER_NEUTER;
} else if (!Q_stricmp(s, "female")) {
gender = GENDER_FEMALE;
} else if (!Q_stricmp(s, "male")) {
gender = GENDER_MALE;
} else if (!Q_stricmp(s, "none")) {
gender = GENDER_NONE;
} else {
gender = GENDER_MAX;
}

// set model
if (g_gametype.integer >= GT_TEAM) {
Q_strncpyz(model, Info_ValueForKey(userinfo, "team_model"), sizeof(model));
Expand Down Expand Up @@ -849,16 +862,15 @@ void ClientUserinfoChanged(int clientNum) {
int rnd;
rnd = random() * 5.9;
rnd_str = va("%d", rnd);
s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tl\\%"
"d\\sl\\%s",
s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\skill\\%s\\tl\\%d"
"\\sl\\%s",
client->pers.netname, team, model, headModel, c1, rnd_str, client->pers.maxHealth, client->sess.wins,
client->sess.losses, Info_ValueForKey(userinfo, "skill"), teamLeader,
client->sess.selectedlogo);
client->sess.losses, Info_ValueForKey(userinfo, "skill"), teamLeader, client->sess.selectedlogo);
// cyr}
} else {
s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tl\\%d\\sl\\%s",
s = va("n\\%s\\t\\%i\\model\\%s\\hmodel\\%s\\c1\\%s\\c2\\%s\\hc\\%i\\w\\%i\\l\\%i\\tl\\%d\\sl\\%s\\s\\%i",
client->pers.netname, client->sess.sessionTeam, model, headModel, c1, c2, client->pers.maxHealth,
client->sess.wins, client->sess.losses, teamLeader, client->sess.selectedlogo);
client->sess.wins, client->sess.losses, teamLeader, client->sess.selectedlogo, (int)gender);
}

trap_SetConfigstring(CS_PLAYERS + clientNum, s);
Expand Down
Loading

0 comments on commit 9796053

Please sign in to comment.