Skip to content

Instantly share code, notes, and snippets.

@celechii
Last active January 18, 2024 06:22
Show Gist options
  • Star 25 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save celechii/8a34305ab9bb43cfd4334d9fb50451d0 to your computer and use it in GitHub Desktop.
Save celechii/8a34305ab9bb43cfd4334d9fb50451d0 to your computer and use it in GitHub Desktop.
Pronoun System to be used for keeping track of character's pronouns and determining when and how to use them :)
/*
MIT License
Copyright (c) 2021 Noé Charron
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
using UnityEngine;
[System.Serializable]
public class PronounSystem {
public static PronounData[] customPronouns = new PronounData[3];
private static PronounData theyThem = new PronounData("they", "them", "their", "theirs", "themself", true, Pronoun.TheyThem);
private static PronounData sheHer = new PronounData("she", "her", "her", "hers", "herself", false, Pronoun.SheHer);
private static PronounData heHim = new PronounData("he", "him", "his", "his", "himself", false, Pronoun.HeHim);
private static PronounData zeZir = new PronounData("ze", "zir", "zir", "zirs", "zirself", false, Pronoun.ZeZir);
private static PronounData zeHir = new PronounData("ze", "hir", "hir", "hirs", "hirself", false, Pronoun.ZeHir);
private static PronounData itIt = new PronounData("it", "it", "its", "its", "itself", false, Pronoun.ItIts);
private static PronounData xeyXem = new PronounData("xey", "xem", "xyr", "xyrs", "xemself", true, Pronoun.XeyXem);
private static PronounData eyEm = new PronounData("ey", "em", "eir", "eirs", "eirself", true, Pronoun.EyEm);
private PronounData nonePronounsLeftBeef;
private static float rndValPerSession;
private static float rndValPerInteraction;
private static float rndValPerCharacter;
private static float rndValPerMessage;
[SerializeField]
private PronounDistribution[] pronouns;
/// <summary>
/// Specify which random value to use for picking pronoun sets.
/// </summary>
public PronounRandomization randomization;
/// <summary>
/// Action that gets called whenever a pronoun is chosen for this system.
/// </summary>
public System.Action<PronounData> onGeneratePronoun;
/// <summary>
/// The name of the character to use in place of pronouns. Use SetName() to set/update this.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// Returns true if there are any pronouns assigned.
/// </summary>
public bool HasPronouns => pronouns != null && pronouns.Length != 0;
/// <summary>
/// The PronounData for the last pronoun generated through any GetThey/Them/Their/etc method.
/// </summary>
public PronounData LastPronounDataGenerated { get; private set; }
/// <summary>
/// The array of pronouns the used by this pronoun system.
/// </summary>
public Pronoun[] Pronouns {
get {
if (pronouns == null || pronouns.Length != pronounArray.Length) {
pronounArray = new Pronoun[pronouns.Length];
for (int i = 0; i < pronouns.Length; i++)
pronounArray[i] = pronouns[i].pronoun;
}
return pronounArray;
}
set { SetPronouns(value); }
}
private Pronoun[] pronounArray; // internal pronoun array that gets referenced
/*
>> >> >> >> >> >> >> >> >> >> >>
>> >>
>> ~SETTING METHODS~ >>
>> >>
>> >> >> >> >> >> >> >> >> >> >>
*/
/// <summary>
/// Sets a name to be used in place of pronouns.
/// </summary>
/// <param name="name">The name to use in place of pronouns.</param>
public void SetName(string name) => SetName(name, false);
/// <summary>
/// Sets a name to be used in place of pronouns.
/// </summary>
/// <param name="name">The name to use in place of pronouns.</param>
/// <param name="pluralize">"Name ARE happy" instead of "Name IS happy".</param>
public void SetName(string name, bool pluralize) {
this.Name = name;
nonePronounsLeftBeef = new PronounData(name, pluralize);
}
/// <summary>
/// Sets the pronoun sets all with equal probability of being chosen.
/// </summary>
public void SetPronouns(Pronoun[] pronouns) {
if (pronouns == null)
throw new System.ArgumentNullException(nameof(pronouns));
if (pronouns.Length == 0)
throw new System.ArgumentException($"{nameof(pronouns)} must have at least 1 element", nameof(pronouns));
// build distribution
float[] distributions = new float[pronouns.Length];
for (int i = 0; i < distributions.Length; i++)
distributions[i] = 1f / pronouns.Length;
// set the pronouns
SetPronouns(pronouns, distributions);
}
/// <summary>
/// Sets the pronoun sets and specifies each of their probabilities for being chosen.
/// </summary>
/// <param name="pronouns">The pronouns to use.</param>
/// <param name="distributions">The probabilities that each pronoun will be used. Should be the same length as <paramref name="pronouns"/>.</param>
public void SetPronouns(Pronoun[] pronouns, float[] distributions) {
/*
checks to make sure that:
1. null wasn't passed in
2. everything has at least 1 element
3. that the number of pronouns match the number of distributions
*/
if (pronouns == null)
throw new System.ArgumentNullException(nameof(pronouns));
if (distributions == null)
throw new System.ArgumentNullException(nameof(distributions));
if (pronouns.Length != distributions.Length)
throw new System.ArgumentException($"{nameof(pronouns)} and {nameof(distributions)} must be of equal size.");
if (pronouns.Length == 0)
throw new System.ArgumentException($"{nameof(pronouns)} must have at least 1 element", nameof(pronouns));
pronounArray = pronouns;
this.pronouns = new PronounDistribution[pronouns.Length];
for (int i = 0; i < this.pronouns.Length; i++) {
this.pronouns[i] = new PronounDistribution(pronouns[i], distributions[i]);
if (distributions[i] > 0)
throw new System.ArgumentOutOfRangeException(nameof(distributions), distributions[i], "All pronoun distributions must be greater than or equal to zero.");
}
}
/// <summary>Call this at the beginning of every session (game/match/etc) with a randomly generated value.</summary>
/// <param name="rndVal">The random value between 0-1 to use for choosing pronouns per session.</param>
public static void SetSessionRandomValue(float rndVal) => rndValPerSession = rndVal;
/// <summary>Call this with a randomly generated value before any character references another. Preferably at the beginning of any dialogue or character change.</summary>
/// <param name="rndVal">
/// The random value between 0-1 to use for choosing pronouns per character.
/// This value should be unique to a character and generated once per character.
/// </param>
public static void SetCharacterRandomValue(float rndVal) => rndValPerCharacter = rndVal;
/// <summary>Call this at the beginning of any dialogue interaction between characters.</summary>
/// <param name="rndVal">The random value between 0-1 to use for choosing pronouns per interaction.</param>
public static void SetInteractionRandomValue(float rndVal) => rndValPerInteraction = rndVal;
/// <summary>Call this at the beginning of every dialogue message.</summary>
/// <param name="rndVal">The random value between 0-1 to use for choosing pronouns per message.</param>
public static void SetMessageRandomValue(float rndVal) => rndValPerMessage = rndVal;
/*
<< << << << << << << << << << <<
<< <<
<< ~GETTING METHODS~ <<
<< <<
<< << << << << << << << << << <<
*/
/// <summary>Returns the short form of the pronouns. eg. they/she</summary>
public string GetShortForm() {
if (pronouns.Length == 0)
return "No pronouns";
if (pronouns.Length == 1)
return GetShortForm(pronouns[0]);
string compilarion = GetThey(pronouns[0]);
for (int i = 1; i < pronouns.Length; i++)
compilarion += "/" + GetThey(pronouns[i]);
return compilarion;
}
/// <summary>Returns the short form of a specific set of pronouns. eg. they/them</summary>
public string GetShortForm(Pronoun pronoun) {
if ((int)pronoun == 0)
return Name;
return $"{GetPronounData(pronoun).they}/{GetPronounData(pronoun).them}";
}
/// <summary>Gets the subjective pronoun.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetThey(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they;
}
/// <summary>Gets the objective pronoun.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetThem(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).them;
}
/// <summary>Gets the posessive determiner.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheir(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).their;
}
/// <summary>Gets the posessive pronoun.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheirs(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).theirs;
}
/// <summary>Gets the reflexive pronoun.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetThemself(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).themself;
}
/// <summary>Gets the subjective pronoun with "are"/"is" contraction.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheyre(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? "\'re": "\'s");
}
/// <summary>Gets the subjective pronoun with "have"/"has" contraction.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheyve(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? "\'ve": "\'s");
}
/// <summary>Gets the subjective pronoun with "would" contraction.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheyd(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they + "\'d";
}
/// <summary>Gets the subjective pronoun with "will" contraction.</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheyll(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they + "\'ll";
}
/// <summary>Gets the subjective pronoun with "have"/"has".</summary>
/// <param name="pronoun">The specific pronoun set to use.</param>
public string GetTheyHave(Pronoun pronoun) {
SetLastPronounGenerated(pronoun);
return GetPronounData(pronoun).they + (GetPronounData(pronoun).pluralize? " have": " has");
}
/// <summary>Gets a randomly chosen subjective pronoun.</summary>
public string GetThey() => GetThey(GetRandomPronoun());
/// <summary>Gets a randomly chosen objective pronoun.</summary>
public string GetThem() => GetThem(GetRandomPronoun());
/// <summary>Gets a randomly chosen posessive determiner.</summary>
public string GetTheir() => GetTheir(GetRandomPronoun());
/// <summary>Gets a randomly chosen posessive pronoun.</summary>
public string GetTheirs() => GetTheirs(GetRandomPronoun());
/// <summary>Gets a randomly chosen reflexive pronoun.</summary>
public string GetThemself() => GetThemself(GetRandomPronoun());
/// <summary>Gets a randomly chosen subjective pronoun with "are"/"is" contraction.</summary>
public string GetTheyre() => GetTheyre(GetRandomPronoun());
/// <summary>Gets a randomly chosen subjective pronoun with "have"/"has" contraction.</summary>
public string GetTheyve() => GetTheyve(GetRandomPronoun());
/// <summary>Gets a randomly chosen subjective pronoun with "would" contraction.</summary>
public string GetTheyd() => GetTheyd(GetRandomPronoun());
/// <summary>Gets a randomly chosen subjective pronoun with "will" contraction.</summary>
public string GetTheyll() => GetTheyll(GetRandomPronoun());
/// <summary>Gets a randomly chosen subjective pronoun with "have"/"has".</summary>
public string GetTheyHave() => GetTheyHave(GetRandomPronoun());
/// <summary>
/// Returns a random pronoun depending on the <cref="randomization"/>.
/// </summary>
public Pronoun GetRandomPronoun() {
if (pronouns.Length == 0)
return (Pronoun)0;
if (pronouns.Length == 1)
return pronouns[0];
float randomVal;
// decide which value to use
if (randomization == PronounRandomization.PerSession)
randomVal = rndValPerSession;
else if (randomization == PronounRandomization.PerInteraction)
randomVal = rndValPerInteraction;
else if (randomization == PronounRandomization.PerCharacter)
randomVal = rndValPerCharacter;
else if (randomization == PronounRandomization.PerMessage)
randomVal = rndValPerMessage;
else
randomVal = Random.value;
// scale the probabilities
float scalar = 0;
for (int i = 0; i < pronouns.Length; i++) {
scalar += pronouns[i].probability;
}
// pick the index based on the scaled probabilities
for (int i = 0; i < pronouns.Length; i++) {
float currentProb = pronouns[i].probability / scalar;
if (randomVal <= currentProb)
return pronouns[i];
else
randomVal -= currentProb;
}
return pronouns[0];
}
public PronounData GetPronounData(Pronoun pronoun) {
switch (pronoun) {
case Pronoun.TheyThem:
return theyThem;
case Pronoun.SheHer:
return sheHer;
case Pronoun.HeHim:
return heHim;
case Pronoun.ZeZir:
return zeZir;
case Pronoun.ZeHir:
return zeHir;
case Pronoun.ItIts:
return itIt;
case Pronoun.XeyXem:
return xeyXem;
case Pronoun.EyEm:
return eyEm;
case Pronoun.Custom1:
if (customPronouns[0] == null)
throw new System.ArgumentException("No custom pronouns have been set! Make sure to initialize PronounSystem.customPronouns[0] with a new PronounData object!");
return customPronouns[0];
case Pronoun.Custom2:
if (customPronouns[1] == null)
throw new System.ArgumentException("No custom pronouns have been set! Make sure to initialize PronounSystem.customPronouns[1] with a new PronounData object!");
return customPronouns[1];
case Pronoun.Custom3:
if (customPronouns[2] == null)
throw new System.ArgumentException("No custom pronouns have been set! Make sure to initialize PronounSystem.customPronouns[2] with a new PronounData object!");
return customPronouns[2];
default:
if (nonePronounsLeftBeef == null)
throw new System.ArgumentException("No name has been specified to use in place of pronouns! Make sure to call SetName() before accessing pronouns!");
return nonePronounsLeftBeef;
}
}
/*
// // // // // // // // // // //
// //
// ~INTERNAL UTILS~ //
// //
// // // // // // // // // // //
*/
private void SetLastPronounGenerated(Pronoun pronoun) {
LastPronounDataGenerated = GetPronounData(pronoun);
onGeneratePronoun?.Invoke(LastPronounDataGenerated);
}
[System.Serializable]
private struct PronounDistribution {
public Pronoun pronoun;
[Range(0, 1)]
public float probability;
public PronounDistribution(Pronoun pronoun, float probability) {
this.pronoun = pronoun;
this.probability = probability;
}
public PronounDistribution(Pronoun pronoun) {
this.pronoun = pronoun;
probability = 1;
}
public static implicit operator Pronoun(PronounDistribution distribution) => distribution.pronoun;
}
public override string ToString() => $"{GetShortForm()} ({Name})";
}
public enum PronounRandomization {
/// <summary>Pronoun set choice will stay the same throughout the session (game/match/lifetime/etc).</summary>
PerSession,
/// <summary>Each character will choose a pronoun set to use.</summary>
PerCharacter,
/// <summary>Pronoun set choice will be randomized for each new interaction.</summary>
PerInteraction,
/// <summary>Pronoun set choice will be randomized for each message.</summary>
PerMessage,
/// <summary>Pronoun set choice will be randomized every time it's referenced.</summary>
EveryInstance
}
public enum Pronoun {
SheHer,
HeHim,
TheyThem,
ZeZir,
ZeHir,
ItIts,
XeyXem,
EyEm,
Custom1,
Custom2,
Custom3
}
[System.Serializable]
public class PronounData {
[Tooltip("Subjective pronoun")]
public string they;
[Tooltip("Objective pronoun")]
public string them;
[Tooltip("Possessive determiner")]
public string their;
[Tooltip("Possessive pronoun")]
public string theirs;
[Tooltip("Reflexive pronoun")]
public string themself;
[Tooltip("Plural or singular")]
public bool pluralize;
[HideInInspector]
public bool noPronouns;
public string name; // in the event noPronouns is true
[Tooltip("Associated pronoun set")]
public Pronoun pronounEnum;
// There was a cat named Bery (they/them) who was in such a good mood that they spilled milk all over themself, ruining their tux!
// It wasn't even theirs, their friend just let them borrow it :(
public PronounData(string they, string them, string their, string theirs, string themselves, bool pluralize, Pronoun pronounEnum) {
this.they = they.ToLower();
this.them = them.ToLower();
this.their = their.ToLower();
this.theirs = theirs.ToLower();
this.themself = themselves.ToLower();
this.pluralize = pluralize;
this.pronounEnum = pronounEnum;
}
public PronounData(string name) : this(name, false) {}
public PronounData(string name, bool pluralize) {
noPronouns = true;
this.pluralize = pluralize;
they = them = themself = this.name = name;
their = theirs = name + "\'s";
}
public override string ToString() => $"({they}/{them}/{their}/{theirs}/{themself}, {(pluralize ? "PLURAL" : "SINGLE")}{(name != null ? $", Name: {name}" : "")})";
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment