Add "neutral" gender option and better obit formatting

This commit is contained in:
Marrub 2018-10-30 16:41:04 -04:00 committed by drfrag666
parent 3d66b9af09
commit 0f6f23350d
11 changed files with 91 additions and 60 deletions

View file

@ -63,7 +63,7 @@ struct SoundAndString
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void SexMessage (const char *from, char *to, int gender,
void PronounMessage (const char *from, char *to, int pronoun,
const char *victim, const char *killer);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
@ -270,7 +270,7 @@ bool AnnounceKill (AActor *killer, AActor *killee)
{
char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.GetGender(),
PronounMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.GetName(), killerName);
Printf (PRINT_MEDIUM, "%s\n", assembled);
}
@ -302,7 +302,7 @@ bool AnnounceTelefrag (AActor *killer, AActor *killee)
{
char assembled[1024];
SexMessage (message, assembled, killee->player->userinfo.GetGender(),
PronounMessage (message, assembled, killee->player->userinfo.GetGender(),
killee->player->userinfo.GetName(), killer->player->userinfo.GetName());
Printf (PRINT_MEDIUM, "%s\n", assembled);
}

View file

@ -36,8 +36,17 @@
#include "c_cvars.h"
enum
{
GENDER_MALE,
GENDER_FEMALE,
GENDER_NEUTER,
GENDER_OBJECT,
GENDER_MAX
};
int D_GenderToInt (const char *gender);
extern const char *GenderNames[3];
extern const char *GenderNames[GENDER_MAX];
int D_PlayerClassToInt (const char *classname);

View file

@ -92,7 +92,7 @@ enum
INFO_ClassicFlight,
};
const char *GenderNames[3] = { "male", "female", "other" };
const char *GenderNames[GENDER_MAX] = { "male", "female", "neutral", "other" };
// Replace \ with %/ and % with %%
FString D_EscapeUserInfo (const char *str)
@ -145,12 +145,14 @@ FString D_UnescapeUserInfo (const char *str, size_t len)
int D_GenderToInt (const char *gender)
{
if (!stricmp (gender, "female"))
if (gender[0] == 'f')
return GENDER_FEMALE;
else if (!stricmp (gender, "other") || !stricmp (gender, "cyborg"))
else if (gender[0] == 'm')
return GENDER_MALE;
else if (gender[0] == 'n')
return GENDER_NEUTER;
else
return GENDER_MALE;
return GENDER_OBJECT;
}
int D_PlayerClassToInt (const char *classname)
@ -735,7 +737,8 @@ void D_WriteUserInfoStrings (int pnum, uint8_t **stream, bool compact)
case NAME_Gender:
*stream += sprintf(*((char **)stream), "\\%s",
*static_cast<FIntCVar *>(pair->Value) == GENDER_FEMALE ? "female" :
*static_cast<FIntCVar *>(pair->Value) == GENDER_NEUTER ? "other" : "male");
*static_cast<FIntCVar *>(pair->Value) == GENDER_MALE ? "male" :
*static_cast<FIntCVar *>(pair->Value) == GENDER_NEUTER ? "neutral" : "other");
break;
case NAME_PlayerClass:

View file

@ -34,6 +34,8 @@
#include "a_weapons.h"
#include "d_netinf.h"
// The player data structure depends on a number
// of other structs: items (internal inventory),
// animation states (closely tied to the sprites
@ -261,13 +263,6 @@ public:
extern TArray<FPlayerClass> PlayerClasses;
// User info (per-player copies of each CVAR_USERINFO cvar)
enum
{
GENDER_MALE,
GENDER_FEMALE,
GENDER_NEUTER
};
struct userinfo_t : TMap<FName,FBaseCVar *>
{
~userinfo_t();

View file

@ -216,7 +216,13 @@ DEFINE_ACTION_FUNCTION(DPlayerMenu, GenderChanged)
// only allow if the menu is active to prevent abuse.
if (self == CurrentMenu)
{
cvar_set("gender", v == 0 ? "male" : v == 1 ? "female" : "other");
switch(v)
{
case 0: cvar_set("gender", "male"); break;
case 1: cvar_set("gender", "female"); break;
case 2: cvar_set("gender", "neutral"); break;
case 3: cvar_set("gender", "other"); break;
}
}
return 0;
}

View file

@ -117,30 +117,34 @@ void P_TouchSpecialThing (AActor *special, AActor *toucher)
// [RH]
// SexMessage: Replace parts of strings with gender-specific pronouns
// PronounMessage: Replace parts of strings with player-specific pronouns
//
// The following expansions are performed:
// %g -> he/she/it
// %h -> him/her/it
// %p -> his/her/its
// %g -> he/she/they/it
// %h -> him/her/them/it
// %p -> his/her/their/its
// %s -> his/hers/theirs/its
// %r -> he's/she's/they're/it's
// %o -> other (victim)
// %k -> killer
//
void SexMessage (const char *from, char *to, int gender, const char *victim, const char *killer)
void PronounMessage (const char *from, char *to, int pronoun, const char *victim, const char *killer)
{
static const char *genderstuff[3][3] =
static const char *pronouns[GENDER_MAX][5] =
{
{ "he", "him", "his" },
{ "she", "her", "her" },
{ "it", "it", "its" }
{ "he", "him", "his", "his", "he's" },
{ "she", "her", "her", "hers", "she's" },
{ "they", "them", "their", "theirs", "they're" },
{ "it", "it", "its", "its'", "it's" }
};
static const int gendershift[3][3] =
static const int pronounshift[GENDER_MAX][5] =
{
{ 2, 3, 3 },
{ 3, 3, 3 },
{ 2, 2, 3 }
{ 2, 3, 3, 3, 4 },
{ 3, 3, 3, 4, 5 },
{ 4, 4, 5, 6, 7 },
{ 2, 2, 3, 4, 4 }
};
const char *subst = NULL;
const char *substitute = NULL;
do
{
@ -150,32 +154,34 @@ void SexMessage (const char *from, char *to, int gender, const char *victim, con
}
else
{
int gendermsg = -1;
int grammarcase = -1;
switch (from[1])
{
case 'g': gendermsg = 0; break;
case 'h': gendermsg = 1; break;
case 'p': gendermsg = 2; break;
case 'o': subst = victim; break;
case 'k': subst = killer; break;
case 'g': grammarcase = 0; break; // Subject
case 'h': grammarcase = 1; break; // Object
case 'p': grammarcase = 2; break; // Possessive Determiner
case 's': grammarcase = 3; break; // Possessive Pronoun
case 'r': grammarcase = 4; break; // Perfective
case 'o': substitute = victim; break;
case 'k': substitute = killer; break;
}
if (subst != NULL)
if (substitute != nullptr)
{
size_t len = strlen (subst);
memcpy (to, subst, len);
size_t len = strlen (substitute);
memcpy (to, substitute, len);
to += len;
from++;
subst = NULL;
substitute = nullptr;
}
else if (gendermsg < 0)
else if (grammarcase < 0)
{
*to++ = '%';
}
else
{
strcpy (to, genderstuff[gender][gendermsg]);
to += gendershift[gender][gendermsg];
strcpy (to, pronouns[pronoun][grammarcase]);
to += pronounshift[pronoun][grammarcase];
from++;
}
}
@ -273,7 +279,7 @@ void ClientObituary (AActor *self, AActor *inflictor, AActor *attacker, int dmgf
if (message == NULL || strlen(message) <= 0)
return;
SexMessage (message, gendermessage, self->player->userinfo.GetGender(),
PronounMessage (message, gendermessage, self->player->userinfo.GetGender(),
self->player->userinfo.GetName(), attacker->player->userinfo.GetName());
Printf (PRINT_MEDIUM, "%s\n", gendermessage);
}
@ -407,7 +413,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
player->fragcount--;
if (deathmatch && player->spreecount >= 5 && cl_showsprees)
{
SexMessage (GStrings("SPREEKILLSELF"), buff,
PronounMessage (GStrings("SPREEKILLSELF"), buff,
player->userinfo.GetGender(), player->userinfo.GetName(),
player->userinfo.GetName());
StatusBar->AttachMessage (Create<DHUDMessageFadeOut>(SmallFont, buff,
@ -465,7 +471,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
{
if (!AnnounceSpreeLoss (this))
{
SexMessage (GStrings("SPREEOVER"), buff, player->userinfo.GetGender(),
PronounMessage (GStrings("SPREEOVER"), buff, player->userinfo.GetGender(),
player->userinfo.GetName(), source->player->userinfo.GetName());
StatusBar->AttachMessage (Create<DHUDMessageFadeOut> (SmallFont, buff,
1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R'));
@ -475,7 +481,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
{
if (!AnnounceSpree (source))
{
SexMessage (spreemsg, buff, player->userinfo.GetGender(),
PronounMessage (spreemsg, buff, player->userinfo.GetGender(),
player->userinfo.GetName(), source->player->userinfo.GetName());
StatusBar->AttachMessage (Create<DHUDMessageFadeOut> (SmallFont, buff,
1.5f, 0.2f, 0, 0, CR_WHITE, 3.f, 0.5f), MAKE_ID('K','S','P','R'));
@ -525,7 +531,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
if (!AnnounceMultikill (source))
{
SexMessage (multimsg, buff, player->userinfo.GetGender(),
PronounMessage (multimsg, buff, player->userinfo.GetGender(),
player->userinfo.GetName(), source->player->userinfo.GetName());
StatusBar->AttachMessage (Create<DHUDMessageFadeOut> (SmallFont, buff,
1.5f, 0.8f, 0, 0, CR_RED, 3.f, 0.5f), MAKE_ID('M','K','I','L'));

View file

@ -88,7 +88,7 @@ struct FRandomSoundList
struct FPlayerClassLookup
{
FString Name;
uint16_t ListIndex[3]; // indices into PlayerSounds (0xffff means empty)
uint16_t ListIndex[GENDER_MAX]; // indices into PlayerSounds (0xffff means empty)
};
// Used to lookup a sound like "*grunt". This contains all player sounds for
@ -1630,7 +1630,8 @@ static int S_AddPlayerClass (const char *name)
FPlayerClassLookup lookup;
lookup.Name = name;
lookup.ListIndex[2] = lookup.ListIndex[1] = lookup.ListIndex[0] = 0xffff;
for(int i = 0; i < GENDER_MAX; i++)
lookup.ListIndex[i] = 0xffff;
cnum = (int)PlayerClassLookups.Push (lookup);
PlayerClassesIsSorted = false;
@ -1778,11 +1779,11 @@ static int S_LookupPlayerSound (int classidx, int gender, FSoundID refid)
{
int g;
for (g = 0; g < 3 && listidx == 0xffff; ++g)
for (g = 0; g < GENDER_MAX && listidx == 0xffff; ++g)
{
listidx = PlayerClassLookups[classidx].ListIndex[g];
}
if (g == 3)
if (g == GENDER_MAX)
{ // No sounds defined at all for this class (can this happen?)
if (classidx != DefPlayerClass)
{
@ -1804,7 +1805,7 @@ static int S_LookupPlayerSound (int classidx, int gender, FSoundID refid)
{ // This sound is unavailable.
if (ingender != 0)
{ // Try "male"
return S_LookupPlayerSound (classidx, 0, refid);
return S_LookupPlayerSound (classidx, GENDER_MALE, refid);
}
if (classidx != DefPlayerClass)
{ // Try the default class.
@ -1920,7 +1921,7 @@ bool S_AreSoundsEquivalent (AActor *actor, int id1, int id2)
int S_FindSkinnedSound (AActor *actor, FSoundID refid)
{
const char *pclass;
int gender = GENDER_MALE;
int gender = 0;
if (actor != NULL && actor->IsKindOf(RUNTIME_CLASS(APlayerPawn)))
{
@ -2082,7 +2083,7 @@ void S_MarkPlayerSounds (const char *playerclass)
{
classidx = DefPlayerClass;
}
for (int g = 0; g < 3; ++g)
for (int g = 0; g < GENDER_MAX; ++g)
{
int listidx = PlayerClassLookups[classidx].ListIndex[0];
if (listidx != 0xffff)
@ -2186,7 +2187,7 @@ CCMD (playersounds)
for (i = 0; i < PlayerClassLookups.Size(); ++i)
{
for (j = 0; j < 3; ++j)
for (j = 0; j < GENDER_MAX; ++j)
{
if ((l = PlayerClassLookups[i].ListIndex[j]) != 0xffff)
{

View file

@ -28,7 +28,8 @@ of the following entries:
is only useful when playing Doom.
Gender (opt) The gender to use for deciding which player sounds
to use for sounds not defined in the [Sounds] section.
Can be male, female, or cyborg. The default is male.
Can be male, female, neutral or cyborg.
The default is male.
ColorRange (opt) The range of palette entries to recolor to match
the player's color. Specified as <first>,<last>.
For Doom, this defaults to 112,127. For Heretic,

View file

@ -2307,7 +2307,8 @@ OPTVAL_ON = "On";
OPTVAL_AUTO = "Auto";
OPTVAL_MALE = "Male";
OPTVAL_FEMALE = "Female";
OPTVAL_OTHER = "Robotic";
OPTVAL_NEUTRAL = "Neutral";
OPTVAL_OTHER = "Object";
OPTVAL_UPPERLEFT = "Upper left";
OPTVAL_UPPERRIGHT = "Upper right";
OPTVAL_LOWERLEFT = "Lower left";

View file

@ -369,7 +369,8 @@ OptionValue "Gender"
{
0, "$OPTVAL_MALE"
1, "$OPTVAL_FEMALE"
2, "$OPTVAL_OTHER"
2, "$OPTVAL_NEUTRAL"
3, "$OPTVAL_OTHER"
}
ListMenu "PlayerMenu"

View file

@ -1366,6 +1366,14 @@ enum EPlayerState
PST_GONE // Player has left the game
}
enum EPlayerGender
{
GENDER_MALE,
GENDER_FEMALE,
GENDER_NEUTRAL,
GENDER_OTHER
}
struct PlayerInfo native play // this is what internally is known as player_t
{
// technically engine constants but the only part of the playsim using them is the player.