diff --git a/CHANGELOG.md b/CHANGELOG.md
index 71cafbf..800221c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,10 @@
# Changelog
+## v1.6 — 2024-06-10
+Tested to work on game version 207.67
+
+* Fix off-by-one error in pronoun selection
+
## v1.5 — 2023-08-15
Tested to work on game versions 204.100 / 205.54 (beta)
diff --git a/NamePronoun.cs b/NamePronoun.cs
deleted file mode 100644
index efc03f8..0000000
--- a/NamePronoun.cs
+++ /dev/null
@@ -1,163 +0,0 @@
-using HarmonyLib;
-using XRL.World;
-using XRL.Language;
-using ConsoleLib.Console;
-
-namespace QudGendersUnleashed.NamePronoun
-{
- ///
- /// Wraps the returned by with a .
- ///
- [HarmonyPatch(typeof(GameObject))]
- [HarmonyPatch(nameof(GameObject.GetPronounProvider))]
- public static class NameOnlyPronounPatch
- {
- static IPronounProvider Postfix(IPronounProvider pronouns, GameObject __instance)
- => NamePronounWrapper.Wrap(pronouns, __instance);
- }
-
- ///
- /// Wraps a , replacing =name=/=name's= with the name of the pronouns' referent.
- ///
- public class NamePronounWrapper : IPronounProvider
- {
- private IPronounProvider BasePronouns;
- private GameObject Referent;
-
- private static bool CouldHaveName(string s) => s.Contains("=name");
-
- public static bool CouldBeNamePronouns(IPronounProvider pronouns) =>
- CouldHaveName(pronouns.Name)
- || CouldHaveName(pronouns.CapitalizedName)
- || CouldHaveName(pronouns.Subjective)
- || CouldHaveName(pronouns.CapitalizedSubjective)
- || CouldHaveName(pronouns.Objective)
- || CouldHaveName(pronouns.CapitalizedObjective)
- || CouldHaveName(pronouns.PossessiveAdjective)
- || CouldHaveName(pronouns.CapitalizedPossessiveAdjective)
- || CouldHaveName(pronouns.SubstantivePossessive)
- || CouldHaveName(pronouns.CapitalizedSubstantivePossessive)
- || CouldHaveName(pronouns.Reflexive)
- || CouldHaveName(pronouns.CapitalizedReflexive)
- || CouldHaveName(pronouns.PersonTerm)
- || CouldHaveName(pronouns.CapitalizedPersonTerm)
- || CouldHaveName(pronouns.ImmaturePersonTerm)
- || CouldHaveName(pronouns.CapitalizedImmaturePersonTerm)
- || CouldHaveName(pronouns.FormalAddressTerm)
- || CouldHaveName(pronouns.CapitalizedFormalAddressTerm)
- || CouldHaveName(pronouns.OffspringTerm)
- || CouldHaveName(pronouns.CapitalizedOffspringTerm)
- || CouldHaveName(pronouns.SiblingTerm)
- || CouldHaveName(pronouns.CapitalizedSiblingTerm)
- || CouldHaveName(pronouns.ParentTerm)
- || CouldHaveName(pronouns.CapitalizedParentTerm)
- || CouldHaveName(pronouns.IndicativeProximal)
- || CouldHaveName(pronouns.CapitalizedIndicativeProximal)
- || CouldHaveName(pronouns.IndicativeDistal)
- || CouldHaveName(pronouns.CapitalizedIndicativeDistal);
-
- public static IPronounProvider Wrap(IPronounProvider basePronouns, GameObject referent)
- {
- if (basePronouns is NamePronounWrapper p && p.Referent != referent)
- {
- p.Referent = referent;
- }
- else if (CouldBeNamePronouns(basePronouns))
- {
- return new NamePronounWrapper(basePronouns, referent);
- }
-
- return basePronouns;
- }
-
- public NamePronounWrapper(IPronounProvider basePronouns, GameObject referent)
- {
- this.BasePronouns = basePronouns;
- this.Referent = referent;
- }
-
- public string ReplaceWithName(string pronoun, bool capitalize = false)
- {
- if (pronoun.Contains("=name"))
- {
- string displayName = Referent.BaseDisplayNameStripped;
- if (capitalize) {
- displayName = ColorUtility.CapitalizeExceptFormatting(displayName);
- }
- string displayNamePossessive = Grammar.MakePossessive(displayName);
- return pronoun.Replace("=name=", displayName)
- .Replace("=name's=", displayNamePossessive);
- }
- else
- {
- return pronoun;
- }
- }
-
- public string Name => ReplaceWithName(BasePronouns.Name);
-
- public string CapitalizedName => ReplaceWithName(BasePronouns.CapitalizedName, true);
-
- public bool Generic => BasePronouns.Generic;
-
- public bool Generated => BasePronouns.Generated;
-
- public bool Plural => BasePronouns.Plural;
-
- public bool PseudoPlural => BasePronouns.PseudoPlural;
-
- public string Subjective => ReplaceWithName(BasePronouns.Subjective);
-
- public string CapitalizedSubjective => ReplaceWithName(BasePronouns.CapitalizedSubjective, true);
-
- public string Objective => ReplaceWithName(BasePronouns.Objective);
-
- public string CapitalizedObjective => ReplaceWithName(BasePronouns.CapitalizedObjective, true);
-
- public string PossessiveAdjective => ReplaceWithName(BasePronouns.PossessiveAdjective);
-
- public string CapitalizedPossessiveAdjective => ReplaceWithName(BasePronouns.CapitalizedPossessiveAdjective, true);
-
- public string SubstantivePossessive => ReplaceWithName(BasePronouns.SubstantivePossessive);
-
- public string CapitalizedSubstantivePossessive => ReplaceWithName(BasePronouns.CapitalizedSubstantivePossessive);
-
- public string Reflexive => ReplaceWithName(BasePronouns.Reflexive);
-
- public string CapitalizedReflexive => ReplaceWithName(BasePronouns.CapitalizedReflexive, true);
-
- public string PersonTerm => ReplaceWithName(BasePronouns.PersonTerm);
-
- public string CapitalizedPersonTerm => ReplaceWithName(BasePronouns.CapitalizedPersonTerm, true);
-
- public string ImmaturePersonTerm => ReplaceWithName(BasePronouns.ImmaturePersonTerm);
-
- public string CapitalizedImmaturePersonTerm => ReplaceWithName(BasePronouns.CapitalizedImmaturePersonTerm, true);
-
- public string FormalAddressTerm => ReplaceWithName(BasePronouns.FormalAddressTerm);
-
- public string CapitalizedFormalAddressTerm => ReplaceWithName(BasePronouns.CapitalizedFormalAddressTerm, true);
-
- public string OffspringTerm => ReplaceWithName(BasePronouns.OffspringTerm);
-
- public string CapitalizedOffspringTerm => ReplaceWithName(BasePronouns.CapitalizedOffspringTerm, true);
-
- public string SiblingTerm => ReplaceWithName(BasePronouns.SiblingTerm);
-
- public string CapitalizedSiblingTerm => ReplaceWithName(BasePronouns.CapitalizedSiblingTerm, true);
-
- public string ParentTerm => ReplaceWithName(BasePronouns.ParentTerm);
-
- public string CapitalizedParentTerm => ReplaceWithName(BasePronouns.CapitalizedParentTerm, true);
-
- public string IndicativeProximal => ReplaceWithName(BasePronouns.IndicativeProximal);
-
- public string CapitalizedIndicativeProximal => ReplaceWithName(BasePronouns.CapitalizedIndicativeProximal, true);
-
- public string IndicativeDistal => ReplaceWithName(BasePronouns.IndicativeDistal);
-
- public string CapitalizedIndicativeDistal => ReplaceWithName(BasePronouns.CapitalizedIndicativeDistal, true);
-
- public bool UseBareIndicative => BasePronouns.UseBareIndicative;
- }
-}
diff --git a/README.md b/README.md
index 9133be1..6a5fe95 100644
--- a/README.md
+++ b/README.md
@@ -4,9 +4,9 @@ A mod that works in conjunction with the [Gender and Pronoun Sets](https://steam
Specifically, this mod allows you to pick from all personal genders and pronouns defined in game (including pseudo-plural ones), with a number of additions.
-Additionally, all gender and pronoun prompts have been redesigned with inspirations from kernelmethod's [Better Pet Selector](https://steamcommunity.com/sharedfiles/filedetails/?id=3006503292) mod.
+Additionally, all gender and pronoun prompts have been redesigned with inspiration from kernelmethod's [Better Pet Selector](https://steamcommunity.com/sharedfiles/filedetails/?id=3006503292).
-Tested to work on game versions 204.100 / 205.54 (beta).
+Tested to work on game version 207.67.
[Source Code (GitHub)](https://github.com/librarianmage/QudGendersUnleashed) \| [Workshop Page](https://steamcommunity.com/sharedfiles/filedetails/?id=2815078000) \| [My Caves of Qud Mods (Steam Workshop)](https://steamcommunity.com/profiles/76561198836298826/myworkshopfiles/?appid=333640)
diff --git a/Scripts/Formatting.cs b/Scripts/Formatting.cs
new file mode 100644
index 0000000..4ce56b2
--- /dev/null
+++ b/Scripts/Formatting.cs
@@ -0,0 +1,56 @@
+using System.Linq;
+using ConsoleLib.Console;
+using XRL.World;
+
+namespace QudGendersUnleashed
+{
+ public static class Formatting
+ {
+ public static readonly string FromGenderText = Markup.Color("W", "");
+ public static readonly string CreateNewText = Markup.Color("W", "");
+
+ public static string FormatGender(Gender G)
+ {
+ var name = Markup.Color("M", G.Name);
+ var summary = Markup.Color("c", G.GetBasicSummary());
+
+ return $"{name}\n{summary}";
+ }
+
+ public static string FormatPronounSet(PronounSet P)
+ {
+ var name = Markup.Color("M", P.GetShortName());
+ var summary = Markup.Color("c", P.GetBasicSummary());
+
+ if (P.FromGender)
+ {
+ var suspects = Gender
+ .Find(G => !G.DoNotReplicateAsPronounSet)
+ .Where(G => new PronounSet(G).Name == P.Name)
+ .Select(G => Markup.Color("m", G.Name));
+
+ var from = suspects.Any() ? string.Join(" / ", suspects) : "unknown";
+ var extra = Markup.Color("m", $"(from {from})");
+
+ return $"{name} {extra}\n{summary}";
+ }
+
+ return $"{name}\n{summary}";
+ }
+
+ public static string FormatFromGenderPronounOption(Gender G)
+ {
+ var g1 = G ?? Gender.Get("nonspecific");
+ if (g1 is null)
+ {
+ return FromGenderText;
+ }
+
+ var name = Markup.Color("m", g1.Name);
+ var extra = Markup.Color("m", $"({name})");
+ var summary = Markup.Color("c", g1.GetBasicSummary());
+
+ return $"{FromGenderText} {extra}\n{summary}";
+ }
+ }
+}
diff --git a/Scripts/NamePronoun.cs b/Scripts/NamePronoun.cs
new file mode 100644
index 0000000..aee26a1
--- /dev/null
+++ b/Scripts/NamePronoun.cs
@@ -0,0 +1,166 @@
+using ConsoleLib.Console;
+using XRL.Language;
+using XRL.World;
+
+namespace QudGendersUnleashed.NamePronoun
+{
+ ///
+ /// Wraps a , replacing =name=/=name's= with the name of the pronouns' referent.
+ ///
+ public class NamePronounWrapper : IPronounProvider
+ {
+ private readonly IPronounProvider BasePronouns;
+ private readonly GameObject Referent;
+
+ private static bool HasName(string S) => S.Contains("=name=") || S.Contains("=name's=");
+
+ public static bool CouldBeNamePronouns(IPronounProvider Pronouns) =>
+ HasName(Pronouns.Name)
+ || HasName(Pronouns.CapitalizedName)
+ || HasName(Pronouns.Subjective)
+ || HasName(Pronouns.CapitalizedSubjective)
+ || HasName(Pronouns.Objective)
+ || HasName(Pronouns.CapitalizedObjective)
+ || HasName(Pronouns.PossessiveAdjective)
+ || HasName(Pronouns.CapitalizedPossessiveAdjective)
+ || HasName(Pronouns.SubstantivePossessive)
+ || HasName(Pronouns.CapitalizedSubstantivePossessive)
+ || HasName(Pronouns.Reflexive)
+ || HasName(Pronouns.CapitalizedReflexive)
+ || HasName(Pronouns.PersonTerm)
+ || HasName(Pronouns.CapitalizedPersonTerm)
+ || HasName(Pronouns.ImmaturePersonTerm)
+ || HasName(Pronouns.CapitalizedImmaturePersonTerm)
+ || HasName(Pronouns.FormalAddressTerm)
+ || HasName(Pronouns.CapitalizedFormalAddressTerm)
+ || HasName(Pronouns.OffspringTerm)
+ || HasName(Pronouns.CapitalizedOffspringTerm)
+ || HasName(Pronouns.SiblingTerm)
+ || HasName(Pronouns.CapitalizedSiblingTerm)
+ || HasName(Pronouns.ParentTerm)
+ || HasName(Pronouns.CapitalizedParentTerm)
+ || HasName(Pronouns.IndicativeProximal)
+ || HasName(Pronouns.CapitalizedIndicativeProximal)
+ || HasName(Pronouns.IndicativeDistal)
+ || HasName(Pronouns.CapitalizedIndicativeDistal);
+
+ public static IPronounProvider Wrap(IPronounProvider BasePronouns, GameObject Referent)
+ {
+ if (BasePronouns is NamePronounWrapper p && p.Referent != Referent)
+ {
+ return new NamePronounWrapper(p.BasePronouns, Referent);
+ }
+ else if (CouldBeNamePronouns(BasePronouns))
+ {
+ return new NamePronounWrapper(BasePronouns, Referent);
+ }
+
+ return BasePronouns;
+ }
+
+ public NamePronounWrapper(IPronounProvider BasePronouns, GameObject Referent)
+ {
+ this.BasePronouns = BasePronouns;
+ this.Referent = Referent;
+ }
+
+ public string ReplaceWithName(string Pronoun, bool Capitalize = false)
+ {
+ if (Pronoun.Contains("=name"))
+ {
+ var displayName = Referent.BaseDisplayName;
+ if (Capitalize)
+ {
+ displayName = ColorUtility.CapitalizeExceptFormatting(displayName);
+ }
+ var displayNamePossessive = Grammar.MakePossessive(displayName);
+ return Pronoun
+ .Replace("=name=", displayName)
+ .Replace("=name's=", displayNamePossessive);
+ }
+ else
+ {
+ return Pronoun;
+ }
+ }
+
+ public string Name => ReplaceWithName(BasePronouns.Name);
+
+ public string CapitalizedName => ReplaceWithName(BasePronouns.CapitalizedName, true);
+
+ public bool Generic => BasePronouns.Generic;
+
+ public bool Generated => BasePronouns.Generated;
+
+ public bool Plural => BasePronouns.Plural;
+
+ public bool PseudoPlural => BasePronouns.PseudoPlural;
+
+ public string Subjective => ReplaceWithName(BasePronouns.Subjective);
+
+ public string CapitalizedSubjective =>
+ ReplaceWithName(BasePronouns.CapitalizedSubjective, true);
+
+ public string Objective => ReplaceWithName(BasePronouns.Objective);
+
+ public string CapitalizedObjective =>
+ ReplaceWithName(BasePronouns.CapitalizedObjective, true);
+
+ public string PossessiveAdjective => ReplaceWithName(BasePronouns.PossessiveAdjective);
+
+ public string CapitalizedPossessiveAdjective =>
+ ReplaceWithName(BasePronouns.CapitalizedPossessiveAdjective, true);
+
+ public string SubstantivePossessive => ReplaceWithName(BasePronouns.SubstantivePossessive);
+
+ public string CapitalizedSubstantivePossessive =>
+ ReplaceWithName(BasePronouns.CapitalizedSubstantivePossessive);
+
+ public string Reflexive => ReplaceWithName(BasePronouns.Reflexive);
+
+ public string CapitalizedReflexive =>
+ ReplaceWithName(BasePronouns.CapitalizedReflexive, true);
+
+ public string PersonTerm => ReplaceWithName(BasePronouns.PersonTerm);
+
+ public string CapitalizedPersonTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedPersonTerm, true);
+
+ public string ImmaturePersonTerm => ReplaceWithName(BasePronouns.ImmaturePersonTerm);
+
+ public string CapitalizedImmaturePersonTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedImmaturePersonTerm, true);
+
+ public string FormalAddressTerm => ReplaceWithName(BasePronouns.FormalAddressTerm);
+
+ public string CapitalizedFormalAddressTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedFormalAddressTerm, true);
+
+ public string OffspringTerm => ReplaceWithName(BasePronouns.OffspringTerm);
+
+ public string CapitalizedOffspringTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedOffspringTerm, true);
+
+ public string SiblingTerm => ReplaceWithName(BasePronouns.SiblingTerm);
+
+ public string CapitalizedSiblingTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedSiblingTerm, true);
+
+ public string ParentTerm => ReplaceWithName(BasePronouns.ParentTerm);
+
+ public string CapitalizedParentTerm =>
+ ReplaceWithName(BasePronouns.CapitalizedParentTerm, true);
+
+ public string IndicativeProximal => ReplaceWithName(BasePronouns.IndicativeProximal);
+
+ public string CapitalizedIndicativeProximal =>
+ ReplaceWithName(BasePronouns.CapitalizedIndicativeProximal, true);
+
+ public string IndicativeDistal => ReplaceWithName(BasePronouns.IndicativeDistal);
+
+ public string CapitalizedIndicativeDistal =>
+ ReplaceWithName(BasePronouns.CapitalizedIndicativeDistal, true);
+
+ public bool UseBareIndicative => BasePronouns.UseBareIndicative;
+ }
+}
diff --git a/Scripts/Patches.cs b/Scripts/Patches.cs
new file mode 100644
index 0000000..2509a9e
--- /dev/null
+++ b/Scripts/Patches.cs
@@ -0,0 +1,73 @@
+using System.Threading.Tasks;
+using HarmonyLib;
+using QudGendersUnleashed.NamePronoun;
+using XRL;
+using XRL.CharacterBuilds.Qud.UI;
+using XRL.World;
+
+namespace QudGendersUnleashed.Patches
+{
+ /// Patch the gender selector for during character creation.
+ [HarmonyPatch(typeof(QudCustomizeCharacterModuleWindow))]
+ [HarmonyPatch(nameof(QudCustomizeCharacterModuleWindow.OnChooseGenderAsync))]
+ public static class GenderPatch
+ {
+ private static bool Prefix(
+ ref Task __result,
+ QudCustomizeCharacterModuleWindow __instance
+ )
+ {
+ __result = Selectors.SelectGenderAsync(__instance.module?.data?.gender);
+ return false;
+ }
+ }
+
+ /// Patch the pronoun set selector for during character creation.
+ [HarmonyPatch(typeof(QudCustomizeCharacterModuleWindow))]
+ [HarmonyPatch(nameof(QudCustomizeCharacterModuleWindow.OnChoosePronounSetAsync))]
+ public static class PronounPatch
+ {
+ private static bool Prefix(
+ ref Task __result,
+ QudCustomizeCharacterModuleWindow __instance
+ )
+ {
+ var fromGenderPlaceholder = Traverse
+ .Create(__instance)
+ .Field("fromGenderPlaceholder")
+ .Value;
+
+ var data = __instance.module?.data;
+
+ __result = Selectors.ChoosePronounSetAsync(
+ data?.gender,
+ data?.pronounSet,
+ fromGenderPlaceholder
+ );
+ return false;
+ }
+ }
+
+ /// Patch the pronoun set selector for the (old-style) character sheet.
+ [HarmonyPatch(typeof(PronounAndGenderSets))]
+ [HarmonyPatch(nameof(PronounAndGenderSets.ShowChangePronounSet))]
+ public static class PlaytimePronounPatch
+ {
+ private static bool Prefix()
+ {
+ Selectors.ChoosePronounSet();
+ return false;
+ }
+ }
+
+ ///
+ /// Wraps the returned by with a .
+ ///
+ [HarmonyPatch(typeof(GameObject))]
+ [HarmonyPatch(nameof(GameObject.GetPronounProvider))]
+ public static class NameOnlyPronounPatch
+ {
+ private static IPronounProvider Postfix(IPronounProvider Pronouns, GameObject __instance) =>
+ NamePronounWrapper.Wrap(Pronouns, __instance);
+ }
+}
diff --git a/Scripts/Selectors.cs b/Scripts/Selectors.cs
new file mode 100644
index 0000000..1a63400
--- /dev/null
+++ b/Scripts/Selectors.cs
@@ -0,0 +1,195 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using XRL;
+using XRL.UI;
+using XRL.World;
+
+namespace QudGendersUnleashed
+{
+ /// Selectors for gender and pronoun sets.
+ public static class Selectors
+ {
+ #region Genders
+ public static async Task SelectGenderAsync(Gender CurrentGender)
+ {
+ var genders = Gender.GetAllPersonal();
+ var options = genders.Select(G => Formatting.FormatGender(G)).ToList();
+ options.Add(Formatting.CreateNewText);
+
+ var initialSelection = genders.IndexOf(CurrentGender);
+ if (initialSelection < 0)
+ {
+ initialSelection = 0;
+ }
+
+ var idx = await Popup.ShowOptionListAsync(
+ Title: "Choose Gender",
+ Options: options.ToArray(),
+ AllowEscape: true,
+ DefaultSelected: initialSelection
+ );
+
+ if (idx <= -1)
+ {
+ return null;
+ }
+ else if (0 <= idx && idx < options.Count - 1)
+ {
+ return genders[idx];
+ }
+ else
+ {
+ var baseIdx = await Popup.ShowOptionListAsync(
+ Title: "Select Base Gender",
+ Options: genders.Select(G => G.Name).ToArray(),
+ AllowEscape: true,
+ DefaultSelected: initialSelection
+ );
+
+ if (baseIdx <= -1)
+ {
+ return null;
+ }
+
+ var baseGender = genders[baseIdx];
+ var newGender = new Gender(baseGender);
+
+ if (await newGender.CustomizeAsync())
+ {
+ return newGender;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ public static void ChooseGender(bool Message = false)
+ {
+ var newGender = SelectGenderAsync(The.Player.GetGender()).Result;
+ if (newGender is not null)
+ {
+ The.Player.SetGender(newGender.Register());
+ if (Message)
+ {
+ Popup.Show($"Set gender to {newGender.Name}");
+ }
+ }
+ }
+ #endregion
+
+ #region Pronouns
+ public static async Task ChoosePronounSetAsync(
+ Gender CurrentGender,
+ PronounSet CurrentPronounSet,
+ PronounSet Placeholder
+ )
+ {
+ var pronounSets = PronounSet.GetAllPersonal();
+ var options = new List
+ {
+ Formatting.FormatFromGenderPronounOption(CurrentGender)
+ };
+ options.AddRange(pronounSets.Select(P => Formatting.FormatPronounSet(P)));
+ options.Add(Formatting.CreateNewText);
+
+ int initialSelection,
+ newSelection;
+ if (CurrentPronounSet is null)
+ {
+ initialSelection = 0;
+ newSelection = 0;
+ }
+ else
+ {
+ var setIdx = pronounSets.IndexOf(CurrentPronounSet);
+
+ if (setIdx < 0)
+ {
+ initialSelection = 0;
+ newSelection = 0;
+ }
+ else
+ {
+ initialSelection = setIdx + 1;
+ newSelection = setIdx;
+ }
+ }
+
+ var n = await Popup.ShowOptionListAsync(
+ "Choose Pronoun Set",
+ options.ToArray(),
+ AllowEscape: true,
+ DefaultSelected: initialSelection
+ );
+
+ if (n <= -1)
+ {
+ return null;
+ }
+ else if (n == 0)
+ {
+ return Placeholder;
+ }
+ else if (1 <= n && n < options.Count - 1)
+ {
+ return pronounSets[n - 1];
+ }
+ else
+ {
+ var b = await Popup.ShowOptionListAsync(
+ "Select Base Set",
+ pronounSets.Select(P => P.Name).ToArray(),
+ AllowEscape: true,
+ DefaultSelected: newSelection
+ );
+ if (b <= -1)
+ {
+ return null;
+ }
+
+ var basePronounSet = pronounSets[b];
+ var newPronounSet = new PronounSet(basePronounSet);
+
+ if (await newPronounSet.CustomizeAsync())
+ {
+ return newPronounSet;
+ }
+ else
+ {
+ return null;
+ }
+ }
+ }
+
+ public static void ChoosePronounSet(bool Message = false)
+ {
+ var sentinel = new PronounSet();
+ var newPronounSet = ChoosePronounSetAsync(
+ The.Player.GetGender(),
+ The.Player.GetPronounSet(),
+ sentinel
+ ).Result;
+ if (newPronounSet == sentinel)
+ {
+ var set = new PronounSet(The.Player.GetGender());
+ The.Player.SetPronounSet(set);
+ if (Message)
+ {
+ Popup.Show($"set pronoun set from gender ({The.Player.GetGender().Name})", LogMessage: false);
+ }
+ }
+ else if (newPronounSet is not null)
+ {
+ The.Player.SetPronounSet(newPronounSet.Register());
+ if (Message)
+ {
+ Popup.Show($"set pronoun set to {newPronounSet.Name}", LogMessage: false);
+ }
+ }
+ }
+ #endregion
+ }
+}
diff --git a/Wishes.cs b/Scripts/Wishes.cs
similarity index 61%
rename from Wishes.cs
rename to Scripts/Wishes.cs
index 731d1b1..db02713 100644
--- a/Wishes.cs
+++ b/Scripts/Wishes.cs
@@ -10,21 +10,18 @@ public static class Wishes
/// Changes the user's gender.
/// Will not change the user's pronouns.
[WishCommand(Command = "changegender")]
- static public void ChangeGender() => Selectors.ChooseGender();
+ public static void ChangeGender() => Selectors.ChooseGender(true);
/// Changes the user's pronouns.
[WishCommand(Command = "changepronouns")]
- static public void ChangePronounSet() => Selectors.ChoosePronounSet();
+ public static void ChangePronounSet() => Selectors.ChoosePronounSet(true);
/// Useless wish used to generate the header image.
+ ///
[WishCommand(Command = "gendermeme")]
- static public void GenderUploadForm()
+ public static void GenderUploadForm()
{
- string[] opts = {
- "Male",
- "Female",
- "Custom [Upload custom gender (max 10MB)]"
- };
+ string[] opts = { "Male", "Female", "Custom [Upload custom gender (max 10MB)]" };
Popup.ShowOptionList("Gender", opts);
}
diff --git a/SelectorPatches.cs b/SelectorPatches.cs
deleted file mode 100644
index 98fa293..0000000
--- a/SelectorPatches.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-using HarmonyLib;
-using XRL;
-using XRL.World;
-using XRL.CharacterBuilds.Qud.UI;
-using System.Threading.Tasks;
-
-namespace QudGendersUnleashed.Patches
-{
- /// Patch the gender selector during character creation.
- [HarmonyPatch(typeof(QudCustomizeCharacterModuleWindow))]
- [HarmonyPatch(nameof(QudCustomizeCharacterModuleWindow.OnChooseGenderAsync))]
- public static class GenderPatch
- {
- static bool Prefix() => false;
-
- static Task Postfix(Task _, QudCustomizeCharacterModuleWindow __instance) => Selectors.OnChooseGenderAsync(__instance);
- }
-
- /// Patch the pronoun set selector during character creation.
- [HarmonyPatch(typeof(QudCustomizeCharacterModuleWindow))]
- [HarmonyPatch(nameof(QudCustomizeCharacterModuleWindow.OnChoosePronounSetAsync))]
- public static class PronounPatch
- {
- static bool Prefix() => false;
-
- static Task Postfix(Task _, PronounSet ___fromGenderPlaceholder, QudCustomizeCharacterModuleWindow __instance)
- => Selectors.OnChoosePronounSetAsync(__instance, ___fromGenderPlaceholder);
- }
-
- /// Patch the pronoun set selector from the character sheet.
- [HarmonyPatch(typeof(PronounAndGenderSets))]
- [HarmonyPatch(nameof(PronounAndGenderSets.ShowChangePronounSet))]
- public static class PlaytimePronounPatch
- {
- static bool Prefix() => false;
- static void Postfix() => Selectors.ChoosePronounSet();
- }
-}
diff --git a/Selectors.cs b/Selectors.cs
deleted file mode 100644
index ea51751..0000000
--- a/Selectors.cs
+++ /dev/null
@@ -1,210 +0,0 @@
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-using ConsoleLib.Console;
-using XRL;
-using XRL.CharacterBuilds.Qud.UI;
-using XRL.UI;
-using XRL.World;
-
-// TODO: refactor
-
-namespace QudGendersUnleashed
-{
- /// Selectors for gender and pronoun sets.
- [HasModSensitiveStaticCache]
- public static class Selectors
- {
- private static string FromGenderText = Markup.Color("W", "");
- private static string CreateNewText = Markup.Color("W", "");
-
- [ModSensitiveStaticCache]
- private static Dictionary _PronounGenderMapping;
-
- public static Dictionary PronounGenderMapping {
- get {
- if (_PronounGenderMapping == null)
- {
- _PronounGenderMapping = new Dictionary();
-
- var suspects = Gender.Find(g => !g.DoNotReplicateAsPronounSet);
- foreach (Gender g in suspects)
- {
- var setName = new PronounSet(g).Name;
- var set = PronounSet.GetIfExists(setName);
- if (set != null) _PronounGenderMapping.Add(set, g);
- }
-
- }
-
- return _PronounGenderMapping;
- }
- }
-
- private static string FormatName(string name, string color = "M")
- {
- if (ColorUtility.HasFormatting(name))
- {
- return Markup.Color("y", name);
- }
- else
- {
- return Markup.Color(color, name);
- }
- }
-
- private static string FormatGender(Gender g)
- {
- string name = FormatName(g.Name);
- string summary = Markup.Color("c", g.GetBasicSummary());
-
- return $"{name}\n{summary}";
- }
-
- private static string FormatPronounSet(PronounSet p)
- {
- string name = FormatName(p.GetShortName());
-
- string extra = "";
-
- Gender g;
- if (PronounGenderMapping.TryGetValue(p, out g))
- {
- extra = Markup.Color("m", $" (from {FormatName(g.Name, "m")})");
- }
-
- string summary = Markup.Color("c", p.GetBasicSummary());
-
- return $"{name}{extra}\n{summary}";
- }
-
- private static string FormatFromGenderPronounOption(Gender g1)
- {
- Gender g = g1 ?? Gender.Get("nonspecific");
- if (g == null) return FromGenderText;
-
- string gName = FormatName(g.Name, "m");
-
- string extra = Markup.Color("m", $"({gName})");
-
- string summary = Markup.Color("c", g.GetBasicSummary());
-
- return $"{FromGenderText} {extra}\n{summary}";
- }
-
- public static async Task ChooseGenderAsync(Gender current)
- {
- var availableGenders = Gender.GetAllPersonal();
- var options = availableGenders.Select(gender=>FormatGender(gender)).ToList();
- options.Add(CreateNewText);
-
- var initial = availableGenders.IndexOf(current);
- if (initial < 0) initial = 0;
-
- int index = await Popup.ShowOptionListAsync(
- Title: "Choose Gender",
- Options: options.ToArray(),
- AllowEscape: true,
- DefaultSelected: initial
- );
-
- if (index <= -1)
- return null;
- else if (0 <= index && index < options.Count - 1)
- return availableGenders[index];
- else
- {
- int baseIndex = await Popup.ShowOptionListAsync(
- Title: "Select Base Gender",
- Options: availableGenders.Select(gender => gender.Name).ToArray(),
- AllowEscape: true,
- DefaultSelected: initial
- );
-
- if (baseIndex <= -1) return null;
-
- var baseGender = availableGenders[baseIndex];
- var newGender = new Gender(baseGender);
-
- if(await newGender.CustomizeAsync())
- return newGender;
- else
- return null;
- }
- }
-
- public static async Task OnChooseGenderAsync(QudCustomizeCharacterModuleWindow window)
- => await ChooseGenderAsync(window?.module?.data?.gender);
-
- public static void ChooseGender()
- {
- var newGender = ChooseGenderAsync(The.Player.GetGender()).Result;
- The.Player.SetGender(newGender.Register());
- }
-
- public static async Task ChoosePronounSetAsync(Gender currentGender, PronounSet currentPronounSet, PronounSet placeholder)
- {
- var availablePronounSets = PronounSet.GetAllPersonal();
- var options = new List();
- options.Add(FormatFromGenderPronounOption(currentGender));
- options.AddRange(availablePronounSets.Select(pronounSet => FormatPronounSet(pronounSet)));
- options.Add(CreateNewText);
-
- int initialPos, newPos;
- if (currentPronounSet == null)
- {
- initialPos = 0;
- newPos = 0;
- }
- else
- {
- int setIndex = availablePronounSets.IndexOf(currentPronounSet);
-
- initialPos = setIndex + 1;
- newPos = setIndex;
- if (newPos < 0) newPos = 0;
- }
-
- int n = await Popup.ShowOptionListAsync("Choose Pronoun Set", options.ToArray(), AllowEscape: true, DefaultSelected: initialPos);
-
- if (n <= -1)
- return null;
- else if (n == 0)
- return placeholder;
- else if (1 <= n && n < options.Count - 1)
- return availablePronounSets[n];
- else
- {
- int b = await Popup.ShowOptionListAsync("Select Base Set", availablePronounSets.Select(PronounSet => PronounSet.Name).ToArray(), AllowEscape: true, DefaultSelected: newPos);
- if (b <= -1) return null;
-
- var basePronounSet = availablePronounSets[b];
- var newPronounSet = new PronounSet(basePronounSet);
-
- if( await newPronounSet.CustomizeAsync() ) return newPronounSet;
- else return null;
- }
- }
-
- public static async Task OnChoosePronounSetAsync(QudCustomizeCharacterModuleWindow window, PronounSet fromGenderPlaceholder)
- {
- var data = window?.module?.data;
- return await ChoosePronounSetAsync(data?.gender, data?.pronounSet, fromGenderPlaceholder);
- }
-
- public static void ChoosePronounSet()
- {
- var sentinel = new PronounSet();
- var newPronounSet = ChoosePronounSetAsync(The.Player.GetGender(), The.Player.GetPronounSet(), sentinel).Result;
- if (newPronounSet == sentinel)
- {
- var n = new PronounSet(The.Player.GetGender());
- The.Player.SetPronounSet(n);
- }
- else if (newPronounSet != null)
- {
- The.Player.SetPronounSet(newPronounSet.Register());
- }
- }
- }
-}
diff --git a/Genders.xml b/XML/Genders.xml
similarity index 100%
rename from Genders.xml
rename to XML/Genders.xml
diff --git a/PronounSets.xml b/XML/PronounSets.xml
similarity index 100%
rename from PronounSets.xml
rename to XML/PronounSets.xml
diff --git a/manifest.json b/manifest.json
index 9848aef..ea81060 100644
--- a/manifest.json
+++ b/manifest.json
@@ -3,7 +3,7 @@
"title": "{{m-Y-g alternation|Qud: Genders Unleashed}}",
"description": "Adds gendeers and pronounce",
"tags": "Gender,Pronouns",
- "version": "1.5",
+ "version": "1.6",
"author": "{{R|librarianmage}}",
"previewImage": "Assets/icon.png"
}
diff --git a/project.csproj b/project.csproj
index 221b665..cfd8da5 100644
--- a/project.csproj
+++ b/project.csproj
@@ -3,6 +3,7 @@
QudGendersUnleashed
QudGendersUnleashed
librarianmage
+ true
diff --git a/workshop.json b/workshop.json
index 847418d..627c439 100644
--- a/workshop.json
+++ b/workshop.json
@@ -1,8 +1,8 @@
{
"WorkshopId": 2815078000,
"Title": "Qud: Genders Unleashed",
- "Description": "A mod that works in conjunction with the [url=https://steamcommunity.com/sharedfiles/filedetails/?id=1735379738]Gender and Pronoun Sets[/url] mod to to provide a plethora of genders and pronouns for your game character.\n\nSpecifically, this mod allows you to pick from all personal genders and pronouns defined in game (including pseudo-plural ones), with a number of additions.\n\nAdditionally, all gender and pronoun prompts have been redesigned with inspirations from kernelmethod's [url=https://steamcommunity.com/sharedfiles/filedetails/?id=3006503292]Better Pet Selector[/url] mod.\n\nTested to work on game versions 204.100 / 205.54 (beta).\n\n[url=https://github.com/librarianmage/QudGendersUnleashed]Source Code (GitHub)[/url]\n\n[h1]Wishes[/h1]The wishes `changegender` and `changepronouns` have been added for quick access while playing.[h1]`=name=` Pronoun Sets[/h1]Starting with version 1.1, `=name=` in a pronoun set will be dynamically replaced with the player character's name (`=name's=` with the possessive version). To demonstrate this, a pronoun set consisting completely of `=name=` and `=name's=` has been added.",
+ "Description": "A mod that works in conjunction with the [url=https://steamcommunity.com/sharedfiles/filedetails/?id=1735379738]Gender and Pronoun Sets[/url] mod to to provide a plethora of genders and pronouns for your game character.\n\nSpecifically, this mod allows you to pick from all personal genders and pronouns defined in game (including pseudo-plural ones), with a number of additions.\n\nAdditionally, all gender and pronoun prompts have been redesigned with inspiration from kernelmethod's [url=https://steamcommunity.com/sharedfiles/filedetails/?id=3006503292]Better Pet Selector[/url].\n\nTested to work on game versions 207.67.\n\n[url=https://github.com/librarianmage/QudGendersUnleashed]Source Code (GitHub)[/url]\n\n[h1]Wishes[/h1]The wishes `changegender` and `changepronouns` have been added for quick access while playing.[h1]`=name=` Pronoun Sets[/h1]Starting with version 1.1, `=name=` in a pronoun set will be dynamically replaced with the player character's name (`=name's=` with the possessive version). To demonstrate this, a pronoun set consisting completely of `=name=` and `=name's=` has been added.",
"Tags": "Stable,Beta,Script,Gender,Pronouns",
"Visibility": "2",
"ImagePath": "Assets/icon.png"
-}
+}
\ No newline at end of file