From 024f9690d3a01527324ad570e6d561c2da501b76 Mon Sep 17 00:00:00 2001 From: edbmods Date: Sat, 24 Dec 2022 08:37:50 -0800 Subject: [PATCH 1/5] Version 1.4 and Biotech support --- EdBPrepareCarefully.csproj | 14 +- EdBPrepareCarefully.sln | 31 +- Resources/About/About.xml | 3 +- Resources/About/Manifest.xml | 2 +- Resources/CHANGELOG.txt | 13 +- .../English/Keyed/EdBPrepareCarefully.xml | 23 +- Resources/LoadFolders.xml | 4 + Source/AgeModifier.cs | 124 ++++++++ Source/AlienRace.cs | 5 +- Source/Controller.cs | 4 + Source/ControllerPawns.cs | 140 +++++++-- Source/CustomHeadType.cs | 73 ----- Source/CustomPawn.cs | 246 +++++++++------- Source/DialogPawnKinds.cs | 94 ++++-- Source/ExtensionsBackstory.cs | 8 +- Source/ExtensionsPawn.cs | 1 + Source/Field.cs | 13 +- Source/FilterBackstoryMatchesFaction.cs | 4 +- Source/FilterBackstoryNoDisabledWorkTypes.cs | 6 +- Source/FilterBackstoryNoPenalties.cs | 8 +- Source/FilterBackstorySkillAdjustment.cs | 12 +- Source/HarmonyPatches.cs | 9 +- Source/Injury.cs | 7 +- Source/InjuryOption.cs | 1 + Source/OptionsHeadType.cs | 111 ++----- Source/OptionsHealth.cs | 5 + Source/Page_PrepareCarefully.cs | 48 +-- Source/PanelAge.cs | 224 +++++++++----- Source/PanelAppearance.cs | 187 +++--------- Source/PanelBackstory.cs | 70 +++-- Source/PanelFavoriteColor.cs | 24 ++ Source/PanelHealth.cs | 5 +- Source/PanelModule.cs | 7 +- Source/PanelPawnList.cs | 34 ++- Source/PanelRandomize.cs | 171 ++++++++++- Source/PanelRelationshipsParentChild.cs | 6 +- Source/PanelSkills.cs | 15 +- Source/PanelTraits.cs | 119 ++++---- Source/PanelXenotype.cs | 80 +++++ Source/PawnColorUtils.cs | 184 ------------ Source/PawnCompRules.cs | 22 +- Source/PawnGenerationRequestWrapper.cs | 223 ++++++-------- Source/PawnLayerBody.cs | 2 +- Source/PawnLayerHead.cs | 2 +- Source/PawnLayerOption.cs | 3 +- Source/PawnLayerOptionBody.cs | 2 +- Source/PawnLayerOptionHead.cs | 18 +- Source/PrepareCarefully.cs | 52 +++- Source/ProviderAgeLimits.cs | 114 ++++++-- Source/ProviderAlienRaces.cs | 89 +++--- Source/ProviderApparel.cs | 45 +-- Source/ProviderBackstories.cs | 46 +-- Source/ProviderBodyTypes.cs | 55 +--- Source/ProviderEquipment.cs | 4 +- Source/ProviderHair.cs | 16 - Source/ProviderHeadTypes.cs | 210 ++++---------- Source/ProviderHealthOptions.cs | 36 ++- Source/ProviderPawnLayers.cs | 25 +- Source/Randomizer.cs | 52 +++- Source/Reflection.cs | 15 +- Source/ReflectionCache.cs | 3 - Source/State.cs | 4 +- Source/TabViewPawns.cs | 58 +++- Source/Textures.cs | 11 +- Source/Version3/PresetLoaderVersion3.cs | 95 +++--- Source/Version3/SaveRecordInjuryV3.cs | 2 +- Source/Version3/SaveRecordPawnV3.cs | 6 +- Source/Version4/PresetLoaderVersion4.cs | 60 ++-- Source/Version4/SaveRecordPawnV4.cs | 11 +- Source/Version5/PawnLoaderV5.cs | 273 ++++++++++++------ Source/Version5/SaveRecordGenesV5.cs | 25 ++ Source/Version5/SaveRecordHediffV5.cs | 20 ++ Source/Version5/SaveRecordInjuryV5.cs | 55 ++++ Source/Version5/SaveRecordPawnV5.cs | 56 +++- Source/WidgetDropdown.cs | 44 ++- Source/WidgetNumberField.cs | 8 +- Source/WidgetTable.cs | 15 +- dist.bat | 5 +- 78 files changed, 2275 insertions(+), 1642 deletions(-) create mode 100644 Source/AgeModifier.cs delete mode 100644 Source/CustomHeadType.cs create mode 100644 Source/PanelXenotype.cs delete mode 100644 Source/PawnColorUtils.cs create mode 100644 Source/Version5/SaveRecordGenesV5.cs create mode 100644 Source/Version5/SaveRecordHediffV5.cs create mode 100644 Source/Version5/SaveRecordInjuryV5.cs diff --git a/EdBPrepareCarefully.csproj b/EdBPrepareCarefully.csproj index bbd81f4..99ab37e 100644 --- a/EdBPrepareCarefully.csproj +++ b/EdBPrepareCarefully.csproj @@ -34,7 +34,8 @@ - Libraries\Harmony\2.0\0Harmony.dll + False + Libraries\Harmony\2.2.2\0Harmony.dll False @@ -58,6 +59,7 @@ + @@ -80,6 +82,7 @@ + @@ -119,7 +122,6 @@ - @@ -219,7 +221,10 @@ + + + @@ -227,7 +232,6 @@ - @@ -283,4 +287,8 @@ + + cd $(SolutionDir) +deploy.bat + \ No newline at end of file diff --git a/EdBPrepareCarefully.sln b/EdBPrepareCarefully.sln index 17aa399..b740ae9 100644 --- a/EdBPrepareCarefully.sln +++ b/EdBPrepareCarefully.sln @@ -1,6 +1,8 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33103.184 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EdBPrepareCarefully", "EdBPrepareCarefully.csproj", "{86C9833E-894B-4633-ACFA-EFE4157BBBE6}" EndProject Global @@ -14,12 +16,18 @@ Global {86C9833E-894B-4633-ACFA-EFE4157BBBE6}.Release|Any CPU.ActiveCfg = Release|Any CPU {86C9833E-894B-4633-ACFA-EFE4157BBBE6}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {18BEA049-3BB9-4853-9F6A-E122019E4EB3} + EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution Policies = $0 $0.DotNetNamingPolicy = $1 $1.DirectoryNamespaceAssociation = None $1.ResourceNamePolicy = FileFormatDefault - $0.TextStylePolicy = $2 + $0.TextStylePolicy = $5 $2.FileWidth = 120 $2.inheritsSet = VisualStudio $2.inheritsScope = text/plain @@ -51,13 +59,11 @@ Global $3.NewLinesForBracesInTypes = False $3.NewLinesForBracesInMethods = False $3.SpaceAfterCast = True - $0.TextStylePolicy = $4 $4.FileWidth = 120 $4.TabsToSpaces = False $4.inheritsSet = null $4.inheritsScope = text/plain $4.scope = application/xml - $0.TextStylePolicy = $5 $5.inheritsSet = null $5.scope = text/x-csharp $0.XmlFormattingPolicy = $6 @@ -69,7 +75,7 @@ Global $7.IncludeInNewFiles = True $0.NameConventionPolicy = $8 $8.Rules = $9 - $9.NamingRule = $10 + $9.NamingRule = $29 $10.Name = Type Parameters $10.AffectedEntity = TypeParameter $10.VisibilityMask = VisibilityMask @@ -80,14 +86,12 @@ Global $32.String = T $10.RequiredSuffixes = $33 $33.String = Exception - $9.NamingRule = $11 $11.Name = Types $11.AffectedEntity = Class, Struct, Enum, Delegate $11.VisibilityMask = Public $11.NamingStyle = PascalCase $11.IncludeInstanceMembers = True $11.IncludeStaticEntities = True - $9.NamingRule = $12 $12.Name = Interfaces $12.RequiredPrefixes = $13 $13.String = I @@ -96,7 +100,6 @@ Global $12.NamingStyle = PascalCase $12.IncludeInstanceMembers = True $12.IncludeStaticEntities = True - $9.NamingRule = $14 $14.Name = Attributes $14.RequiredSuffixes = $15 $15.String = Attribute @@ -105,7 +108,6 @@ Global $14.NamingStyle = PascalCase $14.IncludeInstanceMembers = True $14.IncludeStaticEntities = True - $9.NamingRule = $16 $16.Name = Event Arguments $16.RequiredSuffixes = $17 $17.String = EventArgs @@ -114,7 +116,6 @@ Global $16.NamingStyle = PascalCase $16.IncludeInstanceMembers = True $16.IncludeStaticEntities = True - $9.NamingRule = $18 $18.Name = Exceptions $18.RequiredSuffixes = $19 $19.String = Exception @@ -123,70 +124,60 @@ Global $18.NamingStyle = PascalCase $18.IncludeInstanceMembers = True $18.IncludeStaticEntities = True - $9.NamingRule = $20 $20.Name = Methods $20.AffectedEntity = Methods $20.VisibilityMask = Protected, Public $20.NamingStyle = PascalCase $20.IncludeInstanceMembers = True $20.IncludeStaticEntities = True - $9.NamingRule = $21 $21.Name = Static Readonly Fields $21.AffectedEntity = ReadonlyField $21.VisibilityMask = Protected, Public $21.NamingStyle = PascalCase $21.IncludeInstanceMembers = False $21.IncludeStaticEntities = True - $9.NamingRule = $22 $22.Name = Fields $22.AffectedEntity = Field $22.VisibilityMask = Protected, Public $22.NamingStyle = PascalCase $22.IncludeInstanceMembers = True $22.IncludeStaticEntities = True - $9.NamingRule = $23 $23.Name = ReadOnly Fields $23.AffectedEntity = ReadonlyField $23.VisibilityMask = Protected, Public $23.NamingStyle = PascalCase $23.IncludeInstanceMembers = True $23.IncludeStaticEntities = False - $9.NamingRule = $24 $24.Name = Constant Fields $24.AffectedEntity = ConstantField $24.VisibilityMask = Protected, Public $24.NamingStyle = PascalCase $24.IncludeInstanceMembers = True $24.IncludeStaticEntities = True - $9.NamingRule = $25 $25.Name = Properties $25.AffectedEntity = Property $25.VisibilityMask = Protected, Public $25.NamingStyle = PascalCase $25.IncludeInstanceMembers = True $25.IncludeStaticEntities = True - $9.NamingRule = $26 $26.Name = Events $26.AffectedEntity = Event $26.VisibilityMask = Protected, Public $26.NamingStyle = PascalCase $26.IncludeInstanceMembers = True $26.IncludeStaticEntities = True - $9.NamingRule = $27 $27.Name = Enum Members $27.AffectedEntity = EnumMember $27.VisibilityMask = VisibilityMask $27.NamingStyle = PascalCase $27.IncludeInstanceMembers = True $27.IncludeStaticEntities = True - $9.NamingRule = $28 $28.Name = Parameters $28.AffectedEntity = Parameter $28.VisibilityMask = VisibilityMask $28.NamingStyle = CamelCase $28.IncludeInstanceMembers = True $28.IncludeStaticEntities = True - $9.NamingRule = $29 $29.Name = Type Parameters $29.RequiredPrefixes = $30 $30.String = T diff --git a/Resources/About/About.xml b/Resources/About/About.xml index 3fca111..78711f5 100644 --- a/Resources/About/About.xml +++ b/Resources/About/About.xml @@ -8,12 +8,13 @@
  • 1.1
  • 1.2
  • 1.3
  • +
  • 1.4
  • Customize your colonists, choose your gear and prepare carefully for your crash landing! If you get a set of starting colonists that you like, save them as a preset so that you can start your game the same way next time. -[Version 1.3.12] +[Version 1.4.1]
  • net.pardeike.rimworld.mod.harmony
  • diff --git a/Resources/About/Manifest.xml b/Resources/About/Manifest.xml index f297840..1769584 100644 --- a/Resources/About/Manifest.xml +++ b/Resources/About/Manifest.xml @@ -1,7 +1,7 @@ EdB.PrepareCarefully - 1.3.12 + 1.4.1 false https://github.com/edbmods/EdBPrepareCarefully/raw/master/Resources/About/Manifest.xml https://github.com/edbmods/EdBPrepareCarefully/releases/latest diff --git a/Resources/CHANGELOG.txt b/Resources/CHANGELOG.txt index ae321df..be07bcb 100644 --- a/Resources/CHANGELOG.txt +++ b/Resources/CHANGELOG.txt @@ -1,3 +1,14 @@ + _____________________________________________________________________________ + + Version 1.4.1 + _____________________________________________________________________________ + + - Compatibility for 1.4 and Biotech + - Added Xenotype panel + - Added "Days" field to age panel + - Added dropdown options to the randomize panel + - Added starting pawn possessions to the equipment list + _____________________________________________________________________________ Version 1.3.12 @@ -14,7 +25,7 @@ _____________________________________________________________________________ - Fixed issue with missing relationships when loading presets. - - Fixed apparel for alien race mods where the alien race uses restricted + - Fixed apparel for alien race mods where the alien race uses restricted apparel. _____________________________________________________________________________ diff --git a/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml b/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml index 9020b46..7a5f01d 100644 --- a/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml +++ b/Resources/Common/Languages/English/Keyed/EdBPrepareCarefully.xml @@ -1,5 +1,15 @@  + + + + + + + + + + @@ -281,6 +291,7 @@ Before reporting an error, please try to figure out which mod may be causing the Apparel: {0} {0}: {1} Implants: {0} + Disable Point Limits You've spent too many points. Points Remaining: {0} The starting colonists and resources that were randomly generated for this scenario were worth {0} points. @@ -294,7 +305,12 @@ Disable point limits to prepare your starting colonists with no limitations. Prepare Carefully No abilities - + + Age + Biological + Chronological + Years + Days Biological Age Chronological Age @@ -342,6 +358,11 @@ You can play the game with your colonists as they are, but you will be asked to ({0}) Select From a Faction Template + {0} +Click the randomize button to generate a colonist within this developmental stage's age range + {0} +Click the randomize button to generate a colonist with this xenotype + Other Relationships Parent, Child and Sibling Relationships diff --git a/Resources/LoadFolders.xml b/Resources/LoadFolders.xml index 1800c6e..4bb27c6 100644 --- a/Resources/LoadFolders.xml +++ b/Resources/LoadFolders.xml @@ -11,4 +11,8 @@
  • Common
  • 1.3
  • + +
  • Common
  • +
  • 1.4
  • +
    \ No newline at end of file diff --git a/Source/AgeModifier.cs b/Source/AgeModifier.cs new file mode 100644 index 0000000..8e5447c --- /dev/null +++ b/Source/AgeModifier.cs @@ -0,0 +1,124 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using RimWorld; +using Verse; +using Verse.AI; + +namespace EdB.PrepareCarefully { + public class AgeModifier { + public static long TicksPerYear = 3600000L; + public static long TicksPerDay = 60000L; + public static long DaysPerYear = 60L; + public static long TicksFromYearsAndDays(int years, int days) { + return ((long)years * TicksPerYear) + ((long)days * TicksPerDay); + } + + BackstoryDef newbornBackstory; + BackstoryDef childBackstory; + public void ModifyBiologicalAge(CustomPawn pawn, long ticks) { + if (pawn == null) { + return; + } + + int previousLifeStageIndex = pawn.Pawn.ageTracker.CurLifeStageIndex; + LifeStageDef previousLifeStage = pawn.Pawn.ageTracker.CurLifeStage; + LifeStageAge previousLifeStageAge = pawn.Pawn.ageTracker.CurLifeStageRace; + + bool wasNewborn = IsNewborn(pawn); + bool wasChild = IsChild(pawn); + bool hadChildhoodBackstory = HasChildhoodBackstory(pawn); + bool hadAdulthoodBackstory = HasAdulthoodBackstory(pawn); + + pawn.Pawn.ageTracker.AgeBiologicalTicks = ticks; + + bool hasChildhoodBackstory = HasChildhoodBackstory(pawn); + bool hasAdulthoodBackstory = HasAdulthoodBackstory(pawn); + bool isNewborn = IsNewborn(pawn); + bool isChild = IsChild(pawn); + + if (hasAdulthoodBackstory && !hadAdulthoodBackstory) { + pawn.Pawn.story.Adulthood = pawn.LastSelectedAdulthoodBackstory; + pawn.ResetBackstories(); + } + else if (!hasAdulthoodBackstory && hadAdulthoodBackstory) { + pawn.Pawn.story.Adulthood = null; + pawn.ResetBackstories(); + } + + if (isNewborn && pawn.Pawn.story.Childhood != NewbornBackstory) { + pawn.Pawn.story.Childhood = NewbornBackstory; + pawn.ResetBackstories(); + } + else if (isChild && pawn.Pawn.story.Childhood != ChildBackstory) { + pawn.Pawn.story.Childhood = ChildBackstory; + pawn.ResetBackstories(); + } + + pawn.Pawn.ClearCachedLifeStage(); + pawn.Pawn.ClearCachedHealth(); + pawn.MarkPortraitAsDirty(); + + int newLifeStageIndex = pawn.Pawn.ageTracker.CurLifeStageIndex; + LifeStageDef newLifeStage = pawn.Pawn.ageTracker.CurLifeStage; + LifeStageAge newLifeStageAge = pawn.Pawn.ageTracker.CurLifeStageRace; + + if (newLifeStage != previousLifeStage) { + Logger.Debug("Pawn life stage changes from " + previousLifeStage.defName + " to " + newLifeStage.defName); + Logger.Debug("Development stage was " + previousLifeStage.developmentalStage + " and now is " + newLifeStage.developmentalStage); + } + } + + public void ModifyChronologicalAge(CustomPawn pawn, long ticks) { + if (pawn == null) { + return; + } + pawn.Pawn.ageTracker.AgeChronologicalTicks = ticks; + } + + public bool IsNewborn(CustomPawn pawn) { + DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; + var value = d.HasValue ? d.Value.Baby() : false; + //Logger.Debug("IsNewborn = " + value); + return value; + } + public bool IsChild(CustomPawn pawn) { + DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; + var value = d.HasValue ? d.Value.Child() : false; + //Logger.Debug("IsChild = " + value); + return value; + } + public bool IsAdult(CustomPawn pawn) { + DevelopmentalStage? d = pawn.Pawn.ageTracker?.CurLifeStage?.developmentalStage; + return d.HasValue ? d.Value.Adult() : false; + } + + public bool HasChildhoodBackstory(CustomPawn pawn) { + return !IsNewborn(pawn) && !IsChild(pawn); + } + public bool HasAdulthoodBackstory(CustomPawn pawn) { + return IsAdult(pawn); + } + + public BackstoryDef NewbornBackstory { + get { + if (newbornBackstory== null) { + newbornBackstory = DefDatabase.AllDefs.Where(b => b.spawnCategories != null && b.spawnCategories.Contains("Newborn")).FirstOrDefault(); + } + return newbornBackstory; + } + } + public BackstoryDef ChildBackstory { + get { + if (childBackstory == null) { + childBackstory = DefDatabase.AllDefs.Where(b => b.spawnCategories != null && b.spawnCategories.Contains("Child")).FirstOrDefault(); + } + return childBackstory; + } + } + } + + +} diff --git a/Source/AlienRace.cs b/Source/AlienRace.cs index 3ae733b..e8df967 100644 --- a/Source/AlienRace.cs +++ b/Source/AlienRace.cs @@ -37,10 +37,7 @@ public List HairColors { public List BodyTypes { get; set; } - public List CrownTypes { - get; set; - } - public bool GenderSpecificHeads { + public List HeadTypes { get; set; } public string GraphicsPathForHeads { diff --git a/Source/Controller.cs b/Source/Controller.cs index 09eda35..ba07de5 100644 --- a/Source/Controller.cs +++ b/Source/Controller.cs @@ -149,6 +149,7 @@ protected void PrepareColonists() { } customPawn.Pawn.workSettings.EnableAndInitialize(); colonists.Add(customPawn.Pawn); + Find.GameInitData.startingPossessions[customPawn.Pawn] = new List(); } } Find.GameInitData.startingPawnCount = colonists.Count; @@ -158,6 +159,7 @@ protected void PrepareColonists() { protected void PrepareWorldPawns() { foreach (var customPawn in state.Pawns) { if (customPawn.Type == CustomPawnType.World) { + Find.GameInitData.startingPossessions[customPawn.Pawn] = new List(); AddPawnToWorld(customPawn); } } @@ -183,6 +185,7 @@ protected void PrepareRelatedPawns() { } protected void AddPawnToWorld(CustomPawn pawn) { + // Don't add colonists to the world if (pawn.Type == CustomPawnType.Colonist) { return; @@ -254,6 +257,7 @@ protected void AddPawnToWorld(CustomPawn pawn) { else { Find.GameInitData.startingAndOptionalPawns.Add(pawn.Pawn); } + } protected void MakePawnIntoFactionLeader(CustomPawn pawn) { diff --git a/Source/ControllerPawns.cs b/Source/ControllerPawns.cs index f81f580..d7cdbbf 100644 --- a/Source/ControllerPawns.cs +++ b/Source/ControllerPawns.cs @@ -19,6 +19,7 @@ public class ControllerPawns { private State state; private Randomizer randomizer = new Randomizer(); private ProviderAgeLimits ProviderAgeLimits = PrepareCarefully.Instance.Providers.AgeLimits; + private AgeModifier ageModifier = new AgeModifier(); public ControllerPawns(State state) { this.state = state; } @@ -49,13 +50,75 @@ public void CheckPawnCapabilities() { public void RandomizeAll() { // Create the pawn. - Pawn pawn = randomizer.GenerateKindOfPawn(state.CurrentPawn.Pawn.kindDef); + Pawn pawn = ModsConfig.BiotechActive ? CreateRandomizedPawnForBiotech() : randomizer.GenerateKindOfPawn(state.CurrentPawn.Pawn.kindDef); if (pawn.Faction != Faction.OfPlayer) { pawn.SetFactionDirect(Faction.OfPlayer); } + bool randomizeAny = state.CurrentPawn.RandomizeAnyNonArchite; state.CurrentPawn.InitializeWithPawn(pawn); state.CurrentPawn.GenerateId(); PawnReplaced(state.CurrentPawn); + if (randomizeAny) { + state.CurrentPawn.RandomizeAnyNonArchite = true; + state.CurrentPawn.RandomizeCustomXenotype = null; + state.CurrentPawn.RandomizeXenotype = null; + } + } + + public Pawn CreateRandomizedPawnForBiotech() { + var wrapper = new PawnGenerationRequestWrapper() { + Faction = Find.FactionManager.OfPlayer, + KindDef = state.CurrentPawn.Pawn.kindDef + }; + Ideo ideo = Find.FactionManager.OfPlayer.ideos.GetRandomIdeoForNewPawn(); + if (ideo != null) { + wrapper.FixedIdeology = ideo; + } + if (state.CurrentPawn.RandomizeDevelopmentalStage != DevelopmentalStage.Adult) { + wrapper.DevelopmentalStage = state.CurrentPawn.RandomizeDevelopmentalStage; + } + if (state.CurrentPawn.RandomizeXenotype != null) { + wrapper.ForcedXenotype = state.CurrentPawn.RandomizeXenotype; + } + else if (state.CurrentPawn.RandomizeCustomXenotype != null) { + wrapper.ForcedCustomXenotype = state.CurrentPawn.RandomizeCustomXenotype; + } + else { + wrapper.AllowedXenotypes = DefDatabase.AllDefs.Where((XenotypeDef x) => !x.Archite && x != XenotypeDefOf.Baseliner).ToList(); + wrapper.ForceBaselinerChance = 0.5f; + } + Pawn pawn = randomizer.AttemptToGeneratePawn(wrapper.Request); + + // Fix bad head type for Alien races + if (pawn.def.defName != "Human") { + var provider = PrepareCarefully.Instance.Providers.HeadTypes; + var headTypes = provider.GetHeadTypes(pawn); + if (headTypes.FirstOrDefault() != null && !headTypes.Contains(pawn.story.headType)) { + var replacement = provider.GetHeadTypes(pawn).First(); + Logger.Warning("Swapped out missing head type (" + pawn.story.headType?.defName + ") with first valid head type (" + replacement?.defName + ") for alien race (" + pawn.def.defName + ")"); + pawn.story.headType = replacement; + } + } + + // Fix bad body type for Alien races + if (pawn.def.defName != "Human") { + var provider = PrepareCarefully.Instance.Providers.BodyTypes; + var bodyTypes = provider.GetBodyTypesForPawn(pawn); + if (!bodyTypes.Contains(pawn.story.bodyType)) { + Logger.Warning("Alien race (" + pawn.def.defName + ") does not include the generated body type (" + pawn.story.bodyType?.defName + ")"); + if (bodyTypes.FirstOrDefault() != null) { + var replacement = provider.GetBodyTypesForPawn(pawn).First(); + Logger.Warning("Swapped out missing body type (" + pawn.story.bodyType?.defName + ") with first valid body type (" + replacement?.defName + ") for alien race (" + pawn.def.defName + ")"); + pawn.story.bodyType = replacement; + } + else { + Logger.Warning("No body types available in the alien race (" + pawn.def.defName + ") definition"); + } + } + + } + + return pawn; } // Name-related actions. @@ -82,7 +145,7 @@ public void RandomizeName() { } // Backstory-related actions. - public void UpdateBackstory(BackstorySlot slot, Backstory backstory) { + public void UpdateBackstory(BackstorySlot slot, BackstoryDef backstory) { if (slot == BackstorySlot.Childhood) { state.CurrentPawn.Childhood = backstory; } @@ -141,7 +204,7 @@ public void RemoveTrait(Trait trait) { } public void RandomizeTraits() { - Pawn pawn = randomizer.GenerateSameKindOfPawn(state.CurrentPawn); + Pawn pawn = randomizer.GeneratePawnAsCloseToAsPossible(state.CurrentPawn.Pawn); List traits = pawn.story.traits.allTraits; state.CurrentPawn.ClearTraits(); foreach (var trait in traits) { @@ -166,31 +229,41 @@ public void SetAbilities(IEnumerable abilities) { } // Age-related actions. - public void UpdateBiologicalAge(int age) { + public void UpdateBiologicalAge(int? ageYears, int? ageDays) { + int years = ageYears ?? state.CurrentPawn.BiologicalAgeInYears; + int days = ageDays ?? (int)(state.CurrentPawn.BiologicalAgeInDays % AgeModifier.DaysPerYear); int min = ProviderAgeLimits.MinAgeForPawn(state.CurrentPawn.Pawn); int max = ProviderAgeLimits.MaxAgeForPawn(state.CurrentPawn.Pawn); - if (age < min) { - age = min; + if (years < min) { + years = min; + days = 0; } - else if (age > max || age > state.CurrentPawn.ChronologicalAge) { - if (age > max) { - age = max; - } - else { - age = state.CurrentPawn.ChronologicalAge; - } + else if (years > max) { + years = max; + days = 59; + } + long ticks = AgeModifier.TicksFromYearsAndDays(years, days); + ticks += state.CurrentPawn.BiologicalAgeInTicks % AgeModifier.TicksPerDay; + ageModifier.ModifyBiologicalAge(state.CurrentPawn, ticks); + if (ticks > state.CurrentPawn.ChronologicalAgeInTicks) { + ageModifier.ModifyChronologicalAge(state.CurrentPawn, ticks); } - state.CurrentPawn.BiologicalAge = age; } - public void UpdateChronologicalAge(int age) { - if (age < state.CurrentPawn.BiologicalAge) { - age = state.CurrentPawn.BiologicalAge; + public void UpdateChronologicalAge(int? ageYears, int? ageDays) { + int years = ageYears ?? state.CurrentPawn.ChronologicalAgeInYears; + int days = ageDays ?? (int)(state.CurrentPawn.ChronologicalAgeInDays % AgeModifier.DaysPerYear); + int min = ProviderAgeLimits.MinAgeForPawn(state.CurrentPawn.Pawn); + if (years < min) { + years = min; + days = 0; } - if (age > Constraints.AgeChronologicalMax) { - age = Constraints.AgeChronologicalMax; + long ticks = AgeModifier.TicksFromYearsAndDays(years, days); + ticks += state.CurrentPawn.ChronologicalAgeInTicks % AgeModifier.TicksPerDay; + ageModifier.ModifyChronologicalAge(state.CurrentPawn, ticks); + if (ticks < state.CurrentPawn.BiologicalAgeInTicks) { + ageModifier.ModifyBiologicalAge(state.CurrentPawn, ticks); } - state.CurrentPawn.ChronologicalAge = age; } // Appearance-related actions. @@ -312,25 +385,33 @@ public void SaveCharacter(CustomPawn pawn, string filename) { ColonistSaver.SaveToFile(pawn, filename); state.AddMessage("SavedAs".Translate(filename)); } - public void AddFactionPawn(PawnKindDef kindDef, bool startingPawn) { + public void AddFactionPawn(PawnKindOption option, bool startingPawn) { Pawn pawn = null; try { - //Logger.Debug("Adding new pawn with kindDef = " + kindDef.defName); + //Logger.Debug("Adding new pawn " + option); var wrapper = new PawnGenerationRequestWrapper() { Faction = Find.World.factionManager.OfPlayer, - KindDef = kindDef, + KindDef = option.KindDef, Context = PawnGenerationContext.NonPlayer, - WorldPawnFactionDoesntMatter = true + WorldPawnFactionDoesntMatter = true, }; Ideo ideo = Find.FactionManager.OfPlayer?.ideos?.GetRandomIdeoForNewPawn(); - + //Logger.Debug("Pawn kind xenotype set: " + option?.KindDef?.xenotypeSet?.ToStringSafe()); + //Logger.Debug("Faction xenotype set: " + option?.FactionDef?.xenotypeSet?.ToStringSafe()); + XenotypeSet setToGenerateWith = option?.FactionDef?.xenotypeSet; + if (setToGenerateWith == null) { + setToGenerateWith = option?.KindDef?.xenotypeSet; + } + if (setToGenerateWith != null) { + wrapper.ForcedXenotype = this.randomizer.RandomXenotypeFromSet(setToGenerateWith); + } if (ideo != null) { wrapper.FixedIdeology = ideo; } pawn = randomizer.GeneratePawn(wrapper.Request); } catch (Exception e) { - Logger.Warning("Failed to create faction pawn of kind " + kindDef.defName, e); + Logger.Warning("Failed to create faction pawn: " + option, e); if (pawn != null) { pawn.Destroy(); } @@ -351,8 +432,11 @@ public void AddFactionPawn(PawnKindDef kindDef, bool startingPawn) { //} CustomPawn customPawn = new CustomPawn(pawn); - customPawn.OriginalKindDef = kindDef; - FactionDef factionDef = kindDef.defaultFactionType; + customPawn.OriginalKindDef = option.KindDef; + FactionDef factionDef = option.FactionDef; + if (factionDef == null) { + factionDef = option?.KindDef?.defaultFactionType; + } customPawn.OriginalFactionDef = factionDef; if (pawn.Faction != Faction.OfPlayer) { pawn.SetFaction(Faction.OfPlayer); diff --git a/Source/CustomHeadType.cs b/Source/CustomHeadType.cs deleted file mode 100644 index 85ef782..0000000 --- a/Source/CustomHeadType.cs +++ /dev/null @@ -1,73 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UnityEngine; -using Verse; -using Verse.Sound; - -namespace EdB.PrepareCarefully { - public class CustomHeadType { - private CrownType crownType; - private string graphicPath; - private string alternateGraphicPath; - public string AlienCrownType { - get; set; - } - private Gender? gender; - public CrownType CrownType { - get { - return crownType; - } - set { - crownType = value; - } - } - public string GraphicPath { - get { - return graphicPath; - } - set { - graphicPath = value; - } - } - public string AlternateGraphicPath { - get { - return alternateGraphicPath; - } - set { - alternateGraphicPath = value; - } - } - public Gender? Gender { - get { - return gender; - } - set { - gender = value; - } - } - public string Label { - get; set; - } - public CustomHeadType() { - - } - protected static string GetHeadLabel(string path) { - try { - string[] pathValues = path.Split('/'); - string crownType = pathValues[pathValues.Length - 1]; - string[] values = crownType.Split('_'); - return values[values.Count() - 2] + ", " + values[values.Count() - 1]; - } - catch (Exception) { - Logger.Warning("Could not determine head type label from graphics path: " + path); - return "EdB.PC.Common.Default".Translate(); - } - } - public override string ToString() { - return "{ label = \"" + Label + "\", graphicsPath = \"" + graphicPath + "\", crownType = " + crownType + "\", AlienCrownType = " + AlienCrownType + ", gender = " + gender + "}"; - } - } -} diff --git a/Source/CustomPawn.cs b/Source/CustomPawn.cs index 300f553..88b4eab 100644 --- a/Source/CustomPawn.cs +++ b/Source/CustomPawn.cs @@ -42,12 +42,13 @@ public class CustomPawn { // Keep track of the most recently selected adulthood option so that if the user updates the pawn's // age in a way that switches them back and forth from adult to child (which nulls out the adulthood // value in the Pawn), we can remember what the value was and restore it. - protected Backstory lastSelectedAdulthoodBackstory = null; + public BackstoryDef LastSelectedAdulthoodBackstory = null; + public BackstoryDef LastSelectedChildhoodBackstory = null; // A GUID provides a unique identifier for the CustomPawn. protected string id; - protected CustomHeadType headType; + //protected CustomHeadType headType; protected List implants = new List(); protected List injuries = new List(); @@ -94,6 +95,11 @@ public bool Hidden { } } + public XenotypeDef RandomizeXenotype { get; set; } + public CustomXenotype RandomizeCustomXenotype { get; set; } + public bool RandomizeAnyNonArchite { get; set; } + public DevelopmentalStage RandomizeDevelopmentalStage { get; set; } + public CustomFaction Faction { get { return faction; @@ -129,6 +135,12 @@ public AlienRace AlienRace { } } + public bool IsAlien { + get { + return alienRace != null && Pawn.def.defName != "Human"; + } + } + public bool HasCustomBodyParts { get { return bodyParts.Count > 0; @@ -162,10 +174,10 @@ public float Certainty { public Color HairColor { get { - return pawn.story.hairColor; + return pawn.story.HairColor; } set { - pawn.story.hairColor = value; + pawn.story.HairColor = value; MarkPortraitAsDirty(); } } @@ -225,7 +237,7 @@ public void UpdatePortrait() { } public RenderTexture GetPortrait(Vector2 size) { - return PortraitsCache.Get(Pawn, size, Rot4.South, new Vector3(0, 0, 0), 1.0f); + return PortraitsCache.Get(Pawn, size, Rot4.South, new Vector3(0, 0, 0), 1.0f, supersample: true, compensateForUIScale: true, renderClothes: true, renderHeadgear: true, overrideApparelColors: null, overrideHairColor: null, stylingStation: true); } public void InitializeWithPawn(Pawn pawn) { @@ -279,26 +291,22 @@ public void InitializeWithPawn(Pawn pawn) { } } - // Initialize head type. - CustomHeadType headType = PrepareCarefully.Instance.Providers.HeadTypes.FindHeadTypeForPawn(pawn); - if (headType != null) { - this.headType = headType; - } - else { - this.headType = null; - } - // Reset CustomPawn cached values. ResetApparel(); ResetCachedIncapableOf(); - ResetCachedHead(); // Copy the adulthood backstory or set a random one if it's null. - this.LastSelectedAdulthoodBackstory = pawn.story.adulthood; + this.LastSelectedAdulthoodBackstory = pawn.story.Adulthood; // Evaluate all hediffs. InitializeInjuriesAndImplantsFromPawn(pawn); + if (ModsConfig.BiotechActive) { + RandomizeXenotype = pawn.genes.Xenotype; + RandomizeCustomXenotype = pawn.genes.CustomXenotype; + RandomizeDevelopmentalStage = pawn.DevelopmentalStage; + } + // Set the alien race, if any. alienRace = PrepareCarefully.Instance.Providers.AlienRaces.GetAlienRace(pawn.def); @@ -340,6 +348,9 @@ public void InitializeInjuriesAndImplantsFromPawn(Pawn pawn) { if (getsPermanent != null) { injury.PainFactor = getsPermanent.PainFactor; } + + injury.Chemical = (hediff as Hediff_ChemicalDependency)?.chemical; + injuries.Add(injury); } else { @@ -428,20 +439,20 @@ protected void InitializeSkillLevelsAndPassions() { } } - public Backstory LastSelectedAdulthoodBackstory { - get { - if (lastSelectedAdulthoodBackstory != null) { - return lastSelectedAdulthoodBackstory; - } - else { - lastSelectedAdulthoodBackstory = Randomizer.RandomAdulthood(this); - return lastSelectedAdulthoodBackstory; - } - } - set { - lastSelectedAdulthoodBackstory = value; - } - } + //public BackstoryDef LastSelectedAdulthoodBackstory { + // get { + // if (lastSelectedAdulthoodBackstory != null) { + // return lastSelectedAdulthoodBackstory; + // } + // else { + // lastSelectedAdulthoodBackstory = Randomizer.RandomAdulthood(this); + // return lastSelectedAdulthoodBackstory; + // } + // } + // set { + // lastSelectedAdulthoodBackstory = value; + // } + //} public void ClearApparel() { this.pawn.apparel.DestroyAll(); @@ -449,15 +460,16 @@ public void ClearApparel() { public void CopyAppearance(Pawn pawn) { this.HairDef = pawn.story.hairDef; - this.pawn.story.hairColor = pawn.story.hairColor; + this.pawn.story.HairColor = pawn.story.HairColor; this.pawn.story.bodyType = pawn.story.bodyType; if (pawn.style != null && this.Pawn.style != null) { this.Beard = pawn.style.beardDef; this.FaceTattoo = pawn.style.FaceTattoo; this.BodyTattoo = pawn.style.BodyTattoo; } - this.HeadGraphicPath = pawn.story.HeadGraphicPath; - this.MelaninLevel = pawn.story.melanin; + //this.HeadGraphicPath = pawn.story.HeadGraphicPath; + this.HeadType = pawn.story.headType; + this.MelaninLevel = pawn.genes.GetMelaninGene().minMelanin; this.Pawn.apparel.DestroyAll(); foreach (var layer in PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(this)) { if (layer.Apparel) { @@ -510,14 +522,14 @@ public void ComputeSkillLevelModifiers() { protected int ComputeSkillModifier(SkillDef def) { int value = 0; - if (pawn.story != null && pawn.story.childhood != null && pawn.story.childhood.skillGainsResolved != null) { - if (pawn.story.childhood.skillGainsResolved.ContainsKey(def)) { - value += pawn.story.childhood.skillGainsResolved[def]; + if (pawn.story != null && pawn.story.Childhood != null && pawn.story.Childhood.skillGains != null) { + if (pawn.story.Childhood.skillGains.ContainsKey(def)) { + value += pawn.story.Childhood.skillGains[def]; } } - if (pawn.story != null && pawn.story.adulthood != null && pawn.story.adulthood.skillGainsResolved != null) { - if (pawn.story.adulthood.skillGainsResolved.ContainsKey(def)) { - value += pawn.story.adulthood.skillGainsResolved[def]; + if (pawn.story != null && pawn.story.Adulthood != null && pawn.story.Adulthood.skillGains != null) { + if (pawn.story.Adulthood.skillGains.ContainsKey(def)) { + value += pawn.story.Adulthood.skillGains[def]; } } foreach (Trait trait in this.Pawn.story.traits.allTraits) { @@ -749,10 +761,10 @@ public Pawn Pawn { public string Label { get { NameTriple name = pawn.Name as NameTriple; - if (pawn.story.adulthood == null) { + if (pawn.story.Adulthood == null) { return name.Nick; } - return name.Nick + ", " + pawn.story.adulthood.TitleShortFor(Gender); + return name.Nick + ", " + pawn.story.Adulthood.TitleShortFor(Gender); } } @@ -985,10 +997,10 @@ public string ProfessionLabel { public string ProfessionLabelShort { get { if (IsAdult) { - return Adulthood.TitleShortCapFor(Gender); + return Adulthood.TitleShortFor(Gender).CapitalizeFirst(); } else { - return Childhood.TitleShortCapFor(Gender); + return Childhood.TitleShortFor(Gender).CapitalizeFirst(); } } } @@ -1109,29 +1121,29 @@ public string ApparelConflict { } } - public Backstory Childhood { + public BackstoryDef Childhood { get { - return pawn.story.childhood; + return pawn.story.Childhood; } set { - pawn.story.childhood = value; + pawn.story.Childhood = value; ResetBackstories(); } } - public Backstory Adulthood { + public BackstoryDef Adulthood { get { - return pawn.story.adulthood; + return pawn.story.Adulthood; } set { if (value != null) { LastSelectedAdulthoodBackstory = value; } if (IsAdult) { - pawn.story.adulthood = value; + pawn.story.Adulthood = value; } else { - pawn.story.adulthood = null; + pawn.story.Adulthood = null; } ResetBackstories(); } @@ -1147,46 +1159,15 @@ public void ResetBackstories() { UpdateSkillLevelsForNewBackstoryOrTrait(); } - public CustomHeadType HeadType { - get { - return headType; - } - set { - this.headType = value; - //Logger.Debug("Setting pawn headType to " + value); - this.pawn.story.crownType = value.CrownType != CrownType.Undefined ? value.CrownType : CrownType.Average; - ThingComp alienComp = ProviderAlienRaces.FindAlienCompForPawn(pawn); - if (alienComp != null) { - ReflectionUtil.GetPublicField(alienComp, "crownType").SetValue(alienComp, headType.AlienCrownType); - } - ResetCachedHead(); - MarkPortraitAsDirty(); - } - } - - public string HeadGraphicPath { + public HeadTypeDef HeadType { get { - return pawn.story.HeadGraphicPath; + return pawn.story.headType; } set { - CustomHeadType headType = PrepareCarefully.Instance.Providers.HeadTypes.FindHeadType(pawn.def, value); - if (headType != null) { - HeadType = headType; - } - else { - Logger.Warning("Could not find a head type for the graphic path: " + value); - } - ResetCachedHead(); + pawn.story.headType = value; MarkPortraitAsDirty(); } } - - protected void SetHeadGraphicPathOnPawn(Pawn pawn, string value) { - // Need to use reflection to set the private field. - typeof(Pawn_StoryTracker).GetField("headGraphicPath", BindingFlags.Instance | BindingFlags.NonPublic) - .SetValue(pawn.story, value); - } - protected string FilterHeadPathForGender(string path) { if (pawn.gender == Gender.Male) { return path.Replace("Female", "Male"); @@ -1323,18 +1304,33 @@ public Color SkinColor { } } } + else { + bool removeOverride = false; + var melaninGeneDef = pawn.genes.GetMelaninGene(); + Gene activeSkinColorGene = null; + if (pawn?.genes?.GenesListForReading != null) { + activeSkinColorGene = pawn.genes.GenesListForReading.Where(g => g.Active && g.def.skinColorOverride.HasValue && g.overriddenByGene == null).FirstOrDefault(); + } + if (activeSkinColorGene == null && melaninGeneDef?.skinColorBase != null && melaninGeneDef.skinColorBase == value) { + removeOverride = true; + } + if (removeOverride) { + this.pawn.story.skinColorOverride = null; + } + else { + this.pawn.story.skinColorOverride = value; + } + MarkPortraitAsDirty(); + } } } public float MelaninLevel { get { - return pawn.story.melanin; + return pawn.genes.GetMelaninGene().minMelanin; } set { - pawn.story.melanin = value; - if (alienRace != null) { - SkinColor = PawnSkinColors.GetSkinColor(value); - } + SkinColor = PawnSkinColors.GetSkinColor(value); MarkPortraitAsDirty(); } } @@ -1367,15 +1363,20 @@ public int BiologicalAge { return pawn.ageTracker.AgeBiologicalYears; } set { + bool wasNewborn = IsNewborn(); + bool wasJuvenile = IsJuvenile(); + bool wasAdult = IsAdult; + bool hadBothBackstories = !wasNewborn && !wasJuvenile; + long years = pawn.ageTracker.AgeBiologicalYears; long diff = value - years; pawn.ageTracker.AgeBiologicalTicks += diff * 3600000L; - if (IsAdult && pawn.story.adulthood == null) { - pawn.story.adulthood = LastSelectedAdulthoodBackstory; + if (IsAdult && pawn.story.Adulthood == null) { + pawn.story.Adulthood = LastSelectedAdulthoodBackstory; ResetBackstories(); } - else if (!IsAdult && pawn.story.adulthood != null) { - pawn.story.adulthood = null; + else if (!IsAdult && pawn.story.Adulthood != null) { + pawn.story.Adulthood = null; ResetBackstories(); } pawn.ClearCachedLifeStage(); @@ -1383,19 +1384,53 @@ public int BiologicalAge { MarkPortraitAsDirty(); } } + public long BiologicalAgeInTicks { + get { + return pawn.ageTracker.AgeBiologicalTicks; + } + } - protected void ResetCachedHead() { - if (headType != null) { - // Get the matching head type for the pawn's current gender. We do this in case the user switches the - // gender, swapping to the correct head type if necessary. - CustomHeadType filteredHeadType = PrepareCarefully.Instance.Providers.HeadTypes.FindHeadTypeForGender(pawn.def, headType, Gender); - if (filteredHeadType == null) { - Logger.Warning("No filtered head type found"); //TODO - } - SetHeadGraphicPathOnPawn(pawn, filteredHeadType.GraphicPath); + public long ChronologicalAgeInTicks { + get { + return pawn.ageTracker.AgeChronologicalTicks; + } + } + public int BiologicalAgeInYears { + get { + return pawn.ageTracker.AgeBiologicalYears; } } + public int ChronologicalAgeInYears { + get { + return pawn.ageTracker.AgeChronologicalYears; + } + } + + public int ChronologicalAgeInDays { + get { + return (int)(pawn.ageTracker.AgeChronologicalTicks / AgeModifier.TicksPerDay); + } + } + public int BiologicalAgeInDays { + get { + return (int)(pawn.ageTracker.AgeBiologicalTicks / AgeModifier.TicksPerDay); + } + } + + public bool IsNewborn() { + DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; + return d.HasValue ? d.Value.Newborn() : false; + } + public bool IsJuvenile() { + DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; + return d.HasValue ? d.Value.Juvenile() : false; + } + //public bool IsAdult() { + // DevelopmentalStage? d = Pawn.ageTracker?.CurLifeStage?.developmentalStage; + // return d.HasValue ? d.Value.Adult() : false; + //} + protected void ResetGender() { List bodyTypes = PrepareCarefully.Instance.Providers.BodyTypes.GetBodyTypesForPawn(this); if (pawn.gender == Gender.Female) { @@ -1409,6 +1444,9 @@ protected void ResetGender() { BodyType = BodyTypeDefOf.Female; } } + if (HeadType.gender == Gender.Male) { + HeadType = PrepareCarefully.Instance.Providers.HeadTypes.FindMatchingHeadTypeForOtherGenderOrDefault(HeadType, pawn.gender); + } } else { if (HairDef.styleGender == StyleGender.Female) { @@ -1421,8 +1459,10 @@ protected void ResetGender() { BodyType = BodyTypeDefOf.Male; } } + if (HeadType.gender == Gender.Female) { + HeadType = PrepareCarefully.Instance.Providers.HeadTypes.FindMatchingHeadTypeForOtherGenderOrDefault(HeadType, pawn.gender); + } } - ResetCachedHead(); } public string ResetCachedIncapableOf() { diff --git a/Source/DialogPawnKinds.cs b/Source/DialogPawnKinds.cs index fab0412..17787e1 100644 --- a/Source/DialogPawnKinds.cs +++ b/Source/DialogPawnKinds.cs @@ -5,7 +5,39 @@ using Verse; using Verse.Sound; namespace EdB.PrepareCarefully { + public class PawnKindOption { + public PawnKindOption() { + this.FactionDef = null; + this.KindDef = null; + } + public PawnKindOption(FactionDef factionDef, PawnKindDef kindDef) { + this.FactionDef = factionDef; + this.KindDef = kindDef; + } + + public FactionDef FactionDef { get; set; } + public PawnKindDef KindDef { get; set; } + + public override bool Equals(object obj) { + return obj is PawnKindOption option && + EqualityComparer.Default.Equals(FactionDef, option.FactionDef) && + EqualityComparer.Default.Equals(KindDef, option.KindDef); + } + + public override int GetHashCode() { + var hashCode = 101273788; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(FactionDef); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(KindDef); + return hashCode; + } + + public override string ToString() { + return "{ FactionDef = " + FactionDef?.defName + ", KindDef = " + KindDef?.defName + "}"; + } + } + public class DialogPawnKinds : Window { + public Vector2 ContentMargin { get; protected set; } public Vector2 WindowSize { get; protected set; } public Vector2 ButtonSize { get; protected set; } @@ -25,7 +57,7 @@ public class DialogPawnKinds : Window { protected string headerLabel; protected bool resizeDirtyFlag = true; protected bool confirmed = false; - protected PawnKindDef scrollTo = null; + protected PawnKindOption scrollToWhenFirstOpened = null; protected LabelTrimmer labelTrimmer = new LabelTrimmer(); public DialogPawnKinds() { this.closeOnCancel = true; @@ -35,7 +67,7 @@ public DialogPawnKinds() { Resize(); } - public PawnKindDef Selected { + public PawnKindOption Selected { get; set; } @@ -56,22 +88,22 @@ public Action CloseAction { get; set; } - public Action SelectAction { + public Action SelectAction { get; set; } - public HashSet DisabledOptions { + public HashSet DisabledOptions { get; set; } - protected WidgetTable table = new WidgetTable(); + protected WidgetTable table = new WidgetTable(); public string ConfirmButtonLabel = "EdB.PC.Common.Select"; public string CancelButtonLabel = "EdB.PC.Common.Cancel"; - public void ScrollTo(PawnKindDef kindDef) { - this.scrollTo = kindDef; + public void ScrollToWhenOpened(PawnKindOption kindDef) { + this.scrollToWhenFirstOpened = kindDef; } protected void MarkResizeFlagDirty() { resizeDirtyFlag = true; @@ -123,7 +155,7 @@ protected void Resize() { labelTrimmer.Rect = new Rect(0, 0, nameSize.x, nameSize.y); - table = new WidgetTable(); + table = new WidgetTable(); table.Rect = new Rect(Vector2.zero, ContentRect.size); table.RowHeight = 42; table.RowGroupHeaderHeight = RowGroupHeaderHeight; @@ -131,48 +163,48 @@ protected void Resize() { table.AlternateRowColor = new Color(0, 0, 0, 0); table.SelectedRowColor = new Color(0, 0, 0, 0); table.SupportSelection = true; - table.SelectedAction = (PawnKindDef pawnKind) => { - if (!DisabledOptions.Contains(pawnKind)) { + table.SelectedAction = (PawnKindOption kind) => { + if (!DisabledOptions.Contains(kind)) { SoundDefOf.Tick_Tiny.PlayOneShotOnCamera(); - Select(pawnKind); + Select(kind); } }; - table.AddColumn(new WidgetTable.Column() { + table.AddColumn(new WidgetTable.Column() { Name = "Name", Width = nameSize.x, AdjustForScrollbars = true, - DrawAction = (PawnKindDef pawnKind, Rect rect, WidgetTable.Metadata metadata) => { + DrawAction = (PawnKindOption kind, Rect rect, WidgetTable.Metadata metadata) => { Text.Anchor = TextAnchor.MiddleLeft; Text.Font = GameFont.Small; - if (this.ShowRace && pawnKind.race != null) { + if (this.ShowRace && kind.KindDef.race != null) { Rect nameRect = new Rect(rect.x + nameOffset, rect.y + 5, rect.width, 22); - Widgets.Label(nameRect, labelTrimmer.TrimLabelIfNeeded(pawnKind.LabelCap)); + Widgets.Label(nameRect, labelTrimmer.TrimLabelIfNeeded(kind.KindDef.LabelCap)); Rect raceRect = new Rect(rect.x + nameOffset, nameRect.yMax - 5, rect.width, nameSize.y - 25); Text.Font = GameFont.Tiny; GUI.color = Style.ColorTextSecondary; - Widgets.Label(raceRect, labelTrimmer.TrimLabelIfNeeded(pawnKind.race.LabelCap)); + Widgets.Label(raceRect, labelTrimmer.TrimLabelIfNeeded(kind.KindDef.race.LabelCap)); GUI.color = Color.white; Text.Font = GameFont.Small; } else { - Widgets.Label(new Rect(rect.x + nameOffset, rect.y + 1, rect.width, nameSize.y), pawnKind.LabelCap); + Widgets.Label(new Rect(rect.x + nameOffset, rect.y + 1, rect.width, nameSize.y), kind.KindDef.LabelCap); } Text.Anchor = TextAnchor.UpperLeft; } }); - table.AddColumn(new WidgetTable.Column() { + table.AddColumn(new WidgetTable.Column() { Name = "RadioButton", Width = radioWidth, - DrawAction = (PawnKindDef pawnKind, Rect rect, WidgetTable.Metadata metadata) => { - if (DisabledOptions != null && DisabledOptions.Contains(pawnKind)) { + DrawAction = (PawnKindOption kind, Rect rect, WidgetTable.Metadata metadata) => { + if (DisabledOptions != null && DisabledOptions.Contains(kind)) { GUI.color = Style.ColorControlDisabled; GUI.color = new Color(1, 1, 1, 0.28f); GUI.DrawTexture(new Rect(rect.x, rect.MiddleY() - 12, 24, 24), Textures.TextureRadioButtonOff); GUI.color = Color.white; } else { - if (Widgets.RadioButton(new Vector2(rect.x, rect.MiddleY() - 12), this.Selected == pawnKind)) { - Select(pawnKind); + if (Widgets.RadioButton(new Vector2(rect.x, rect.MiddleY() - 12), EqualityComparer.Default.Equals(this.Selected, kind))) { + Select(kind); } } } @@ -180,18 +212,18 @@ protected void Resize() { resizeDirtyFlag = false; } - protected void Select(PawnKindDef pawnKind) { - this.Selected = pawnKind; + protected void Select(PawnKindOption kind) { + this.Selected = kind; if (SelectAction != null) { - SelectAction(pawnKind); + SelectAction(kind); } } - public IEnumerable PawnKinds { + public IEnumerable FactionPawnKinds { get; set; } - public IEnumerable.RowGroup> RowGroups { + public IEnumerable.RowGroup> RowGroups { get; set; } @@ -207,9 +239,9 @@ public override void DoWindowContents(Rect inRect) { if (resizeDirtyFlag) { Resize(); } - if (scrollTo != null) { - table.ScrollTo(scrollTo); - scrollTo = null; + if (scrollToWhenFirstOpened != null) { + table.ScrollTo(scrollToWhenFirstOpened); + scrollToWhenFirstOpened = null; } GUI.color = Color.white; Text.Font = GameFont.Medium; @@ -225,7 +257,7 @@ public override void DoWindowContents(Rect inRect) { table.Draw(RowGroups); } else { - table.Draw(PawnKinds); + table.Draw(FactionPawnKinds); } } finally { diff --git a/Source/ExtensionsBackstory.cs b/Source/ExtensionsBackstory.cs index 6d38c99..b4d44af 100644 --- a/Source/ExtensionsBackstory.cs +++ b/Source/ExtensionsBackstory.cs @@ -13,7 +13,7 @@ public static class ExtensionsBackstory { "pirate king" }; - public static string CheckedDescriptionFor(this Backstory backstory, Pawn pawn) { + public static string CheckedDescriptionFor(this BackstoryDef backstory, Pawn pawn) { if (ProblemBackstories.Contains(backstory.untranslatedTitle)) { return PartialDescriptionFor(backstory); } @@ -28,13 +28,13 @@ public static string CheckedDescriptionFor(this Backstory backstory, Pawn pawn) // EVERY RELEASE: // This is a copy of Backstory.FullDescriptionFor() that only includes the disabled work types and the skill adjustments. // Every release, we should evaluate that method to make sure that the logic has not changed. - public static string PartialDescriptionFor(this Backstory backstory) { + public static string PartialDescriptionFor(this BackstoryDef backstory) { StringBuilder stringBuilder = new StringBuilder(); List allDefsListForReading = DefDatabase.AllDefsListForReading; for (int i = 0; i < allDefsListForReading.Count; i++) { SkillDef skillDef = allDefsListForReading[i]; - if (backstory.skillGainsResolved.ContainsKey(skillDef)) { - stringBuilder.AppendLine(skillDef.skillLabel.CapitalizeFirst() + ": " + backstory.skillGainsResolved[skillDef].ToString("+##;-##")); + if (backstory.skillGains.ContainsKey(skillDef)) { + stringBuilder.AppendLine(skillDef.skillLabel.CapitalizeFirst() + ": " + backstory.skillGains[skillDef].ToString("+##;-##")); } } stringBuilder.AppendLine(); diff --git a/Source/ExtensionsPawn.cs b/Source/ExtensionsPawn.cs index 834ea37..98f2d38 100644 --- a/Source/ExtensionsPawn.cs +++ b/Source/ExtensionsPawn.cs @@ -123,6 +123,7 @@ public static void ClearCaches(this Pawn pawn) { pawn.ClearCachedHealth(); pawn.ClearCachedLifeStage(); pawn.ClearCachedDisabledSkillRecords(); + pawn.ClearCachedPortraits(); } public static void ClearCachedDisabledSkillRecords(this Pawn pawn) { diff --git a/Source/Field.cs b/Source/Field.cs index 5f5f890..daf9bb6 100644 --- a/Source/Field.cs +++ b/Source/Field.cs @@ -16,6 +16,7 @@ public class Field { private Action tipAction; private Color color = Style.ColorText; private bool enabled = true; + private bool nextPreviousButtonsHidden = false; public Field() { } public Rect Rect { @@ -90,6 +91,14 @@ public bool Enabled { enabled = value; } } + public bool NextPreviousButtonsHidden { + get { + return nextPreviousButtonsHidden; + } + set { + nextPreviousButtonsHidden = value; + } + } public Action DrawIconFunc = null; public Func IconSizeFunc = null; @@ -100,11 +109,11 @@ public void Draw() { try { // Adjust the width of the rectangle if the field has next and previous buttons. Rect fieldRect = rect; - if (previousAction != null) { + if (previousAction != null || nextPreviousButtonsHidden) { fieldRect.x += 12; fieldRect.width -= 12; } - if (nextAction != null) { + if (nextAction != null || nextPreviousButtonsHidden) { fieldRect.width -= 12; } diff --git a/Source/FilterBackstoryMatchesFaction.cs b/Source/FilterBackstoryMatchesFaction.cs index 3f9e6a2..9c96f71 100644 --- a/Source/FilterBackstoryMatchesFaction.cs +++ b/Source/FilterBackstoryMatchesFaction.cs @@ -6,10 +6,10 @@ using Verse; namespace EdB.PrepareCarefully { - class FilterBackstoryMatchesFaction : Filter { + class FilterBackstoryMatchesFaction : Filter { public FilterBackstoryMatchesFaction() { this.LabelShort = this.LabelFull = "EdB.PC.Dialog.Backstory.Filter.MatchesFaction".Translate(); - this.FilterFunction = (Backstory backstory) => { + this.FilterFunction = (BackstoryDef backstory) => { CustomPawn pawn = PrepareCarefully.Instance.State.CurrentPawn; PawnKindDef kindDef = pawn.OriginalKindDef; if (kindDef == null) { diff --git a/Source/FilterBackstoryNoDisabledWorkTypes.cs b/Source/FilterBackstoryNoDisabledWorkTypes.cs index f23d1c2..148a881 100644 --- a/Source/FilterBackstoryNoDisabledWorkTypes.cs +++ b/Source/FilterBackstoryNoDisabledWorkTypes.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -6,10 +6,10 @@ using Verse; namespace EdB.PrepareCarefully { - class FilterBackstoryNoDisabledWorkTypes : Filter { + class FilterBackstoryNoDisabledWorkTypes : Filter { public FilterBackstoryNoDisabledWorkTypes() { this.LabelShort = this.LabelFull = "EdB.PC.Dialog.Backstory.Filter.NoDisabledWorkTypes".Translate(); - this.FilterFunction = (Backstory backstory) => { + this.FilterFunction = (BackstoryDef backstory) => { return (backstory.DisabledWorkTypes.FirstOrDefault() == null); }; } diff --git a/Source/FilterBackstoryNoPenalties.cs b/Source/FilterBackstoryNoPenalties.cs index 86681ad..9eba173 100644 --- a/Source/FilterBackstoryNoPenalties.cs +++ b/Source/FilterBackstoryNoPenalties.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -6,14 +6,14 @@ using Verse; namespace EdB.PrepareCarefully { - class FilterBackstoryNoPenalties : Filter { + class FilterBackstoryNoPenalties : Filter { public FilterBackstoryNoPenalties() { this.LabelShort = this.LabelFull = "EdB.PC.Dialog.Backstory.Filter.NoSkillPenalties".Translate(); this.FilterFunction = (backstory) => { - if (backstory.skillGainsResolved.Count == 0) { + if (backstory.skillGains.Count == 0) { return true; } - foreach (var gain in backstory.skillGainsResolved.Values) { + foreach (var gain in backstory.skillGains.Values) { if (gain < 0) { return false; } diff --git a/Source/FilterBackstorySkillAdjustment.cs b/Source/FilterBackstorySkillAdjustment.cs index 07a731b..7e74589 100644 --- a/Source/FilterBackstorySkillAdjustment.cs +++ b/Source/FilterBackstorySkillAdjustment.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -6,7 +6,7 @@ using Verse; namespace EdB.PrepareCarefully { - class FilterBackstorySkillAdjustment : Filter { + class FilterBackstorySkillAdjustment : Filter { private int BonusOrPenalty { get; set; @@ -26,9 +26,9 @@ public FilterBackstorySkillAdjustment(SkillDef skillDef, int bonusOrPenalty) { this.LabelShort = "EdB.PC.Dialog.Backstory.Filter.SkillBonus".Translate(this.SkillDef.LabelCap, bonusOrPenalty); this.LabelFull = "EdB.PC.Dialog.Backstory.Filter.SkillBonusFull".Translate(this.SkillDef.LabelCap, bonusOrPenalty); } - this.FilterFunction = (Backstory backstory) => { - if (this.SkillDef != null && backstory.skillGainsResolved.ContainsKey(this.SkillDef)) { - int value = backstory.skillGainsResolved[skillDef]; + this.FilterFunction = (BackstoryDef backstory) => { + if (this.SkillDef != null && backstory.skillGains.ContainsKey(this.SkillDef)) { + int value = backstory.skillGains[skillDef]; if (bonusOrPenalty > 0) { return value >= bonusOrPenalty; } @@ -39,7 +39,7 @@ public FilterBackstorySkillAdjustment(SkillDef skillDef, int bonusOrPenalty) { return false; }; } - public override bool ConflictsWith(Filter filter) { + public override bool ConflictsWith(Filter filter) { if (filter as FilterBackstorySkillAdjustment == null) { return false; } diff --git a/Source/HarmonyPatches.cs b/Source/HarmonyPatches.cs index 1076258..3ac9930 100644 --- a/Source/HarmonyPatches.cs +++ b/Source/HarmonyPatches.cs @@ -65,8 +65,13 @@ static void Postfix() { class PrepareCarefullyButtonPatch { static void Postfix(Page_ConfigureStartingPawns __instance, ref Rect rect) { Vector2 BottomButSize = new Vector2(150f, 38f); - float num = rect.height + 45f; - Rect rect4 = new Rect(rect.x + rect.width / 2f - BottomButSize.x / 2f, num, BottomButSize.x, BottomButSize.y); + float halfButtonWidth = 75f; + float num = rect.height + 55f; + Rect rect4 = new Rect(rect.width / 2 - halfButtonWidth, num, BottomButSize.x, BottomButSize.y); + if (ModsConfig.BiotechActive) { + float w = (rect.width * 0.5f) - 16f - BottomButSize.HalfX(); + rect4 = new Rect(16f + (w * 0.5f), num, BottomButSize.x, BottomButSize.y); + } if (Widgets.ButtonText(rect4, "EdB.PC.Page.Button.PrepareCarefully".Translate(), true, false, true)) { // Version check if (VersionControl.CurrentVersion < PrepareCarefully.MinimumGameVersion) { diff --git a/Source/Injury.cs b/Source/Injury.cs index 677971c..61016c0 100644 --- a/Source/Injury.cs +++ b/Source/Injury.cs @@ -38,6 +38,8 @@ public Hediff Hediff { protected float? painFactor = null; + public ChemicalDef Chemical { get; set; } + public float Severity { get { return severity; @@ -111,7 +113,6 @@ public override void AddToPawn(CustomPawn customPawn, Pawn pawn) { if (getsPermanent != null) { getsPermanent.IsPermanent = true; Reflection.HediffComp_GetsPermanent.SetPainCategory(getsPermanent, PainCategoryForFloat(painFactor == null ? 0 : painFactor.Value)); - //ReflectionUtil.SetNonPublicField(getsPermanent, "painFactor", painFactor == null ? 0 : painFactor.Value); } pawn.health.AddHediff(hediff, BodyPartRecord); @@ -138,6 +139,9 @@ public override void AddToPawn(CustomPawn customPawn, Pawn pawn) { hediffLevel.SetLevelTo((int)Severity); } } + if (hediff is Hediff_ChemicalDependency chemicalDependency) { + chemicalDependency.chemical = Chemical; + } pawn.health.AddHediff(hediff); this.hediff = hediff; } @@ -147,6 +151,7 @@ public override void AddToPawn(CustomPawn customPawn, Pawn pawn) { // Check the PainCategory enum to verify that we still only have 4 values and that their int values match the logic here. // This method converts a float value into a PainCategory. It's here because we don't quite remember where that float // value comes from and if it contain a value that won't map to one of the PainCategory enum values. + // Unchanged for 1.14 protected PainCategory PainCategoryForFloat(float value) { int intValue = Mathf.FloorToInt(value); if (intValue == 2) { diff --git a/Source/InjuryOption.cs b/Source/InjuryOption.cs index 0f60992..353beec 100644 --- a/Source/InjuryOption.cs +++ b/Source/InjuryOption.cs @@ -12,6 +12,7 @@ public class InjuryOption { protected bool removesPart = false; protected bool wholeBody = false; protected string label = "?"; + public bool Selectable { get; set; } = true; public bool UsesSeverityPercentile { get { diff --git a/Source/OptionsHeadType.cs b/Source/OptionsHeadType.cs index ca9227a..5036007 100644 --- a/Source/OptionsHeadType.cs +++ b/Source/OptionsHeadType.cs @@ -9,113 +9,36 @@ using UnityEngine; using Verse; using Verse.Sound; +using System.Runtime.Remoting.Messaging; namespace EdB.PrepareCarefully { public class OptionsHeadType { - protected List heads = new List(); - protected List headPaths = new List(); - protected List maleHeadTypes = new List(); - protected List femaleHeadTypes = new List(); - protected List noGenderHeaderTypes = new List(); - public Dictionary pathDictionary = new Dictionary(); - public List headTypes = new List(); + protected List maleHeadTypes = new List(); + protected List femaleHeadTypes = new List(); + protected List noGenderHeaderTypes = new List(); + public List headTypes = new List(); protected int count = 0; public OptionsHeadType() { } - public void AddHeadType(CustomHeadType headType) { + public void AddHeadType(HeadTypeDef headType) { headTypes.Add(headType); - //Logger.Debug(headType.ToString()); - if (!headType.GraphicPath.NullOrEmpty() && !pathDictionary.ContainsKey(headType.GraphicPath)) { - pathDictionary.Add(headType.GraphicPath, headType); + if (headType.gender == Gender.Male) { + maleHeadTypes.Add(headType); } - if (!headType.AlternateGraphicPath.NullOrEmpty() && !pathDictionary.ContainsKey(headType.AlternateGraphicPath)) { - pathDictionary.Add(headType.AlternateGraphicPath, headType); - } - } - public IEnumerable GetHeadTypesForGender(Gender gender) { - return headTypes.Where((CustomHeadType headType) => { - return (headType.Gender == gender || headType.Gender == null); - }); - } - public CustomHeadType FindHeadTypeForPawn(Pawn pawn) { - var alienComp = ProviderAlienRaces.FindAlienCompForPawn(pawn); - if (alienComp == null) { - var result = FindHeadTypeByGraphicsPath(pawn.story.HeadGraphicPath); - if (result == null) { - Logger.Warning("Did not find head type for path: " + pawn.story.HeadGraphicPath); - } - return result; - } - else { - string crownType = ProviderAlienRaces.GetCrownTypeFromComp(alienComp); - var result = FindHeadTypeByCrownTypeAndGender(crownType, pawn.gender); - if (result == null) { - Logger.Warning("Did not find head type for alien crown type: " + crownType); - } - return result; - } - } - public CustomHeadType FindHeadTypeByGraphicsPath(string graphicsPath) { - if (pathDictionary.TryGetValue(graphicsPath, out CustomHeadType result)) { - return result; + else if (headType.gender == Gender.Female) { + femaleHeadTypes.Add(headType); } else { - return null; + noGenderHeaderTypes.Add(headType); } } - public CustomHeadType FindHeadTypeByCrownType(string crownType) { - return headTypes.Where(t => { - return t.AlienCrownType == crownType; - }).FirstOrDefault(); - } - public CustomHeadType FindHeadTypeByCrownTypeAndGender(string crownType, Gender gender) { - return headTypes.Where(t => { - return t.AlienCrownType == crownType && (t.Gender == null || t.Gender == gender); - }).FirstOrDefault(); + public IEnumerable GetHeadTypesForGender(Gender gender) { + return headTypes.Where((HeadTypeDef headType) => { + return (headType.gender == gender || headType.gender == Gender.None); + }); } - public CustomHeadType FindHeadTypeForGender(CustomHeadType headType, Gender gender) { - if (headType.Gender == null || headType.Gender == gender) { - return headType; - } - - if (headType.AlienCrownType.NullOrEmpty()) { - string graphicsPath = headType.GraphicPath; - string prefixReplacementString = "/" + gender.ToString() + "_"; - string directoryReplacementString = "/" + gender.ToString() + "/"; - if (headType.Gender == Gender.Male) { - graphicsPath = graphicsPath.Replace("/Male/", directoryReplacementString); - graphicsPath = graphicsPath.Replace("/Male_", prefixReplacementString); - } - if (headType.Gender == Gender.Female) { - graphicsPath = graphicsPath.Replace("/Female/", directoryReplacementString); - graphicsPath = graphicsPath.Replace("/Female_", prefixReplacementString); - } - else { - graphicsPath = graphicsPath.Replace("/None/", directoryReplacementString); - graphicsPath = graphicsPath.Replace("/None_", prefixReplacementString); - } - CustomHeadType result = FindHeadTypeByGraphicsPath(graphicsPath); - if (result == null) { - Logger.Warning("Could not find head type for gender: " + graphicsPath); - } - return result != null ? result : headType; - } - else { - Gender targetGender = gender; - if (headType.Gender == Gender.Male) { - targetGender = Gender.Female; - } - if (headType.Gender == Gender.Female) { - targetGender = Gender.Male; - } - else { - return headType; - } - var result = headTypes.Where(h => { - return h.AlienCrownType == headType.AlienCrownType && h.Gender == targetGender; - }).FirstOrDefault(); - return result != null ? result : headType; - } + public HeadTypeDef FindHeadTypeForPawn(Pawn pawn) { + return pawn.story.headType; } } } diff --git a/Source/OptionsHealth.cs b/Source/OptionsHealth.cs index 8ebc707..416fa23 100644 --- a/Source/OptionsHealth.cs +++ b/Source/OptionsHealth.cs @@ -232,6 +232,11 @@ public List InjuryOptions { return injuryOptions; } } + public IEnumerable SelectableInjuryOptions { + get { + return injuryOptions.Where(o => o.Selectable); + } + } public IEnumerable BodyPartsForInjury(InjuryOption option) { if (option.ValidParts == null || option.ValidParts.Count == 0) { return SkinCoveredBodyParts.Select((UniqueBodyPart p) => { return p.Record; }); diff --git a/Source/Page_PrepareCarefully.cs b/Source/Page_PrepareCarefully.cs index 079ee1b..5472162 100644 --- a/Source/Page_PrepareCarefully.cs +++ b/Source/Page_PrepareCarefully.cs @@ -35,13 +35,13 @@ public override Vector2 InitialSize { //Logger.Debug("Screen safe area: " + Screen.safeArea); //Logger.Debug("UI scale: " + Prefs.UIScale); - //Vector2 maxSize = new Vector2(Screen.safeArea.width / Prefs.UIScale, Screen.safeArea.height / Prefs.UIScale); - //Vector2 minSize = Page.StandardSize / Prefs.UIScale; + Vector2 maxSize = new Vector2(Screen.safeArea.width / Prefs.UIScale, Screen.safeArea.height / Prefs.UIScale); + Vector2 minSize = Page.StandardSize / Prefs.UIScale; - //Vector2 padding = new Vector2(64, 64) / Prefs.UIScale; - //maxSize -= padding; + Vector2 padding = new Vector2(64, 64) / Prefs.UIScale; + maxSize -= padding; - //Vector2 largeSize = new Vector2(1350, Page.StandardSize.y); + Vector2 largeSize = new Vector2(1350, Page.StandardSize.y); //if (maxSize.x >= largeSize.x && maxSize.y >= largeSize.y) { // LargeUI = true; @@ -94,7 +94,8 @@ public Page_PrepareCarefully() { currentTab.TabRecord = tabRecord; tabRecords.Add(tabRecord); } - + State.CurrentTab = tabViewPawns; + tabViewPawns.TabRecord.selected = true; } override public void OnAcceptKeyPressed() { @@ -140,9 +141,12 @@ public override void PreOpen() { public override void DoWindowContents(Rect inRect) { pawnListActionThisFrame = false; base.DrawPageTitle(inRect); - Rect mainRect = base.GetMainRect(inRect, 30f, false); + Rect mainRect = base.GetMainRect(inRect, 0, false).InsetBy(0, -6, 0, 0); Widgets.DrawMenuSection(mainRect); - TabDrawer.DrawTabs(mainRect, tabRecords); + float tabWidth = 180.0f; + float tabGroupWidth = tabRecords.Count * tabWidth; + float tabRectX = (mainRect.width * 0.5f) - (tabGroupWidth * 0.5f); + TabDrawer.DrawTabs(new Rect(tabRectX, mainRect.y, tabGroupWidth, mainRect.height), tabRecords, 180.0f); // Determine the size of the tab view and draw the current tab. Vector2 SizePageMargins = new Vector2(16, 16); @@ -278,7 +282,7 @@ protected void DrawPoints(Rect parentRect) { GUI.color = Style.ColorText; label = "EdB.PC.Page.Points.Spent".Translate(points); } - Rect rect = new Rect(parentRect.width - costLabelWidth.Value, 2, costLabelWidth.Value, 32); + Rect rect = new Rect(parentRect.width - costLabelWidth.Value - 40, 4, costLabelWidth.Value, 32); Widgets.Label(rect, label); string tooltipText = ""; @@ -298,15 +302,20 @@ protected void DrawPoints(Rect parentRect) { Text.Anchor = TextAnchor.UpperLeft; Text.Font = GameFont.Small; - string optionLabel; - float optionTop = rect.y; - optionLabel = "EdB.PC.Page.Points.UsePoints".Translate(); - Vector2 size = Text.CalcSize(optionLabel); - Rect optionRect = new Rect(parentRect.width - costLabelWidth.Value - size.x - 100, optionTop, size.x + 10, 32); - Widgets.Label(optionRect, optionLabel); - GUI.color = Color.white; - TooltipHandler.TipRegion(optionRect, "EdB.PC.Page.Points.UsePoints.Tip".Translate()); - Widgets.Checkbox(new Vector2(optionRect.x + optionRect.width, optionRect.y - 3), ref Config.pointsEnabled, 24, false); + if (WidgetDropdown.Button(new Rect(rect.xMax + 8, rect.yMin - 4, 31, 31), "", true, false, true)) { + List list = new List(); + if (Config.pointsEnabled) { + list.Add(new FloatMenuOption("EdB.PC.Page.Points.DisablePoints".Translate(), () => { + Config.pointsEnabled = false; + })); + } + else { + list.Add(new FloatMenuOption("EdB.PC.Page.Points.UsePoints".Translate(), () => { + Config.pointsEnabled = true; + })); + } + Find.WindowStack.Add(new FloatMenu(list, null, false)); + } } finally { Text.Anchor = TextAnchor.UpperLeft; @@ -365,7 +374,7 @@ protected void InstrumentPanels() { }; tabViewPawns.PanelBackstory.BackstoryUpdated += pawnController.UpdateBackstory; - tabViewPawns.PanelBackstory.BackstoryUpdated += (BackstorySlot slot, Backstory backstory) => { pawnController.CheckPawnCapabilities(); }; + tabViewPawns.PanelBackstory.BackstoryUpdated += (BackstorySlot slot, BackstoryDef backstory) => { pawnController.CheckPawnCapabilities(); }; tabViewPawns.PanelBackstory.BackstoriesRandomized += pawnController.RandomizeBackstories; tabViewPawns.PanelBackstory.BackstoriesRandomized += () => { pawnController.CheckPawnCapabilities(); }; @@ -409,6 +418,7 @@ protected void InstrumentPanels() { }; pawnController.PawnAdded += (CustomPawn pawn) => { pawnController.CheckPawnCapabilities(); }; pawnController.PawnReplaced += (CustomPawn pawn) => { pawnController.CheckPawnCapabilities(); }; + pawnController.PawnReplaced += (CustomPawn pawn) => { tabViewPawns.PanelAppearance.UpdatePawnLayers(); }; tabViewPawns.PanelHealth.InjuryAdded += pawnController.AddInjury; tabViewPawns.PanelHealth.ImplantAdded += pawnController.AddImplant; diff --git a/Source/PanelAge.cs b/Source/PanelAge.cs index 09362dd..a58ed61 100644 --- a/Source/PanelAge.cs +++ b/Source/PanelAge.cs @@ -1,131 +1,201 @@ -using RimWorld; +using EdB.PrepareCarefully.HarmonyPatches; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; +using System.Net; using UnityEngine; using Verse; using Verse.Sound; namespace EdB.PrepareCarefully { - public class PanelAge : PanelBase { - public delegate void UpdateAgeHandler(int age); + public class PanelAge : PanelModule { + public static readonly int DaysPerYear = 60; + + public delegate void UpdateAgeHandler(int? ageYears, int? ageDays); public event UpdateAgeHandler BiologicalAgeUpdated; public event UpdateAgeHandler ChronologicalAgeUpdated; private ProviderAgeLimits providerAgeLimits = PrepareCarefully.Instance.Providers.AgeLimits; - protected static Rect RectBiologicalAgeLabel; - protected static Rect RectBiologicalAgeField; - protected static Rect RectChronologicalAgeLabel; - protected static Rect RectChronologicalAgeField; - - private WidgetNumberField biologicalField; - private WidgetNumberField chronologicalField; + protected Rect RectDevelopmentStageButton; + protected Rect RectBiologicalAgeLabel; + protected Rect RectBiologicalAgeFieldYears; + protected Rect RectBiologicalAgeFieldDays; + protected Rect RectChronologicalAgeLabel; + protected Rect RectChronologicalAgeFieldYears; + protected Rect RectChronologicalAgeFieldDays; + protected Rect RectYearsLabel; + protected Rect RectDaysLabel; + protected Rect RectDevelopmentalStageDropdown; + protected Rect RectHeader; + + private WidgetNumberField biologicalFieldYears; + private WidgetNumberField biologicalFieldDays; + private WidgetNumberField chronologicalFieldYears; + private WidgetNumberField chronologicalFieldDays; + private AgeModifier ageModifier = new AgeModifier(); public PanelAge() { - biologicalField = new WidgetNumberField() { + biologicalFieldYears = new WidgetNumberField() { DragSlider = new DragSlider(0.4f, 20, 100), - MinValue = 14, + MinValue = 0, MaxValue = 99, UpdateAction = (int value) => { - UpdateBiologicalAge(value); + UpdateBiologicalAgeYears(value); + } + }; + biologicalFieldDays = new WidgetNumberField() { + DragSlider = new DragSlider(0.4f, 15, 100), + MinValue = 0, + MaxValue = 59, + UpdateAction = (int value) => { + UpdateBiologicalAgeDays(value); } }; - chronologicalField = new WidgetNumberField() { + chronologicalFieldYears = new WidgetNumberField() { DragSlider = new DragSlider(0.4f, 15, 100), - MinValue = 14, + MinValue = 0, MaxValue = Constraints.AgeChronologicalMax, UpdateAction = (int value) => { - UpdateChronologicalAge(value); + UpdateChronologicalAgeYears(value); + } + }; + chronologicalFieldDays = new WidgetNumberField() { + DragSlider = new DragSlider(0.4f, 15, 100), + MinValue = 0, + MaxValue = 59, + UpdateAction = (int value) => { + UpdateChronologicalAgeDays(value); } }; } - protected void UpdateBiologicalAge(int value) { - BiologicalAgeUpdated(value); + protected void UpdateBiologicalAgeYears(int? value) { + BiologicalAgeUpdated(value, null); + } + protected void UpdateBiologicalAgeDays(int? value) { + BiologicalAgeUpdated(null, value); } - protected void UpdateChronologicalAge(int value) { - ChronologicalAgeUpdated(value); + protected void UpdateChronologicalAgeYears(int? value) { + ChronologicalAgeUpdated(value, null); + } + protected void UpdateChronologicalAgeDays(int? value) { + ChronologicalAgeUpdated(null, value); } - public override void Resize(Rect rect) { - base.Resize(rect); + public override void Resize(float width) { + base.Resize(width); - Vector2 available = PanelRect.size - Style.SizePanelPadding; + float panelPadding = Style.SizePanelPadding.x; + float availableSize = width - panelPadding * 2; + + GameFont saveFont = Text.Font; + Text.Font = GameFont.Small; + Vector2 bioLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Label.BiologicalAge".Translate()); + Vector2 chronoLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Label.ChronologicalAge".Translate()); + Text.Font = saveFont; + float minimumLabelWidth = Mathf.Max(bioLabelSize.x, chronoLabelSize.x); float arrowPadding = 1; float arrowWidth = Textures.TextureButtonNext.width; - float bioWidth = 32; - float chronoWidth = 48; + float widthNeededForArrowButtons = 4 * (arrowWidth + arrowPadding); - float extendedArrowSize = arrowPadding + arrowWidth; - float extendedFieldSize = extendedArrowSize * 2; + float paddingBetweenLabelAndField = 8; + float paddingBetweenFields = 8; + float fieldHeight = 24; - float usedSpace = (extendedFieldSize * 2) + bioWidth + chronoWidth; + float widthNeededForFixedElements = minimumLabelWidth + paddingBetweenFields + paddingBetweenLabelAndField + widthNeededForArrowButtons; + float availableWidthForFields = availableSize - widthNeededForFixedElements; - float availableSpace = PanelRect.width - usedSpace; - float spacing = availableSpace / 3; + float yearFieldWidth = Mathf.Ceil(availableWidthForFields * 0.6f); + float dayFieldWidth = availableWidthForFields - yearFieldWidth; - float idealSpace = 15; - float extraFieldWidth = 0; - if (spacing > idealSpace) { - float extra = (spacing - idealSpace) * 3; - extraFieldWidth += Mathf.Floor(extra / 2); - spacing = idealSpace; - } - else { - spacing = Mathf.Floor(spacing); - } - float fieldHeight = 28; + float yearFieldOffset = panelPadding + minimumLabelWidth + paddingBetweenLabelAndField + arrowPadding + arrowWidth; + float dayFieldOffset = yearFieldOffset + yearFieldWidth + paddingBetweenFields + arrowPadding * 2 + arrowWidth * 2; - GameFont saveFont = Text.Font; - Text.Font = GameFont.Tiny; - Vector2 bioLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Biological".Translate()); - Vector2 chronoLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Chronological".Translate()); + float developmentStageDropdownWidth = 30; + RectDevelopmentalStageDropdown = new Rect(width - developmentStageDropdownWidth - panelPadding, 0, developmentStageDropdownWidth, 30); + + RectBiologicalAgeLabel = new Rect(panelPadding, 0, bioLabelSize.x, fieldHeight); + RectBiologicalAgeFieldYears = new Rect(yearFieldOffset, RectBiologicalAgeLabel.yMin, yearFieldWidth, fieldHeight); + RectBiologicalAgeFieldDays = new Rect(dayFieldOffset, RectBiologicalAgeLabel.yMin, dayFieldWidth, fieldHeight); + + RectChronologicalAgeLabel = new Rect(panelPadding, 0, chronoLabelSize.x, fieldHeight); + RectChronologicalAgeFieldYears = new Rect(yearFieldOffset, RectChronologicalAgeLabel.yMin, yearFieldWidth, fieldHeight); + RectChronologicalAgeFieldDays = new Rect(dayFieldOffset, RectBiologicalAgeLabel.yMin, dayFieldWidth, fieldHeight); + + saveFont = Text.Font; + Text.Font = GameFont.Small; + Vector2 yearsLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Label.Years".Translate()); + Vector2 daysLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Label.Days".Translate()); Text.Font = saveFont; - float labelHeight = Mathf.Max(bioLabelSize.y, chronoLabelSize.y); - float contentHeight = labelHeight + fieldHeight; - float top = PanelRect.HalfHeight() - (contentHeight * 0.5f); - float fieldTop = top + labelHeight; - - RectBiologicalAgeField = new Rect(spacing + extendedArrowSize, fieldTop, bioWidth + extraFieldWidth, fieldHeight); - RectChronologicalAgeField = new Rect(RectBiologicalAgeField.xMax + extendedArrowSize + - spacing + extendedArrowSize, fieldTop, chronoWidth + extraFieldWidth, fieldHeight); - - RectBiologicalAgeLabel = new Rect(RectBiologicalAgeField.MiddleX() - bioLabelSize.x / 2, - RectBiologicalAgeField.y - bioLabelSize.y, bioLabelSize.x, bioLabelSize.y); - RectChronologicalAgeLabel = new Rect(RectChronologicalAgeField.MiddleX() - chronoLabelSize.x / 2, - RectChronologicalAgeField.y - chronoLabelSize.y, chronoLabelSize.x, chronoLabelSize.y); + RectYearsLabel = new Rect(yearFieldOffset + yearFieldWidth * 0.5f - yearsLabelSize.x * 0.5f, 0, yearsLabelSize.x, yearsLabelSize.y); + RectDaysLabel = new Rect(dayFieldOffset + dayFieldWidth * 0.5f - daysLabelSize.x * 0.5f, 0, daysLabelSize.x, daysLabelSize.y); } - protected override void DrawPanelContent(State state) { - base.DrawPanelContent(state); + public override float Draw(State state, float y) { // Update field values. - CustomPawn customPawn = state.CurrentPawn; - int maxAge = providerAgeLimits.MaxAgeForPawn(customPawn.Pawn); - int minAge = providerAgeLimits.MinAgeForPawn(customPawn.Pawn); - chronologicalField.MinValue = customPawn.BiologicalAge; - biologicalField.MinValue = minAge; - biologicalField.MaxValue = customPawn.ChronologicalAge < maxAge ? customPawn.ChronologicalAge : maxAge; - - // Age labels. + CustomPawn pawn = state.CurrentPawn; + int maxAge = providerAgeLimits.MaxAgeForPawn(pawn.Pawn); + int minAge = providerAgeLimits.MinAgeForPawn(pawn.Pawn); + chronologicalFieldYears.MinValue = minAge; + biologicalFieldYears.MinValue = minAge; + biologicalFieldYears.MaxValue = maxAge; + + float top = y; + y += Margin.y; + + y += DrawHeader(y, Width, "EdB.PC.Panel.Age.Header".Translate().Resolve()); + Rect rect; + Text.Font = GameFont.Tiny; GUI.color = Style.ColorText; - Widgets.Label(RectBiologicalAgeLabel, "EdB.PC.Panel.Age.Biological".Translate()); - Widgets.Label(RectChronologicalAgeLabel, "EdB.PC.Panel.Age.Chronological".Translate()); + Text.Anchor = TextAnchor.MiddleCenter; + rect = RectYearsLabel.OffsetBy(0, y - RectYearsLabel.height); + Widgets.Label(rect, "EdB.PC.Panel.Age.Label.Years".Translate()); + rect = RectDaysLabel.OffsetBy(0, y - RectDaysLabel.height); + Widgets.Label(rect, "EdB.PC.Panel.Age.Label.Days".Translate()); + Text.Font = GameFont.Small; - GUI.color = Color.white; + GUI.color = Style.ColorText; + Text.Anchor = TextAnchor.MiddleLeft; + rect = RectBiologicalAgeLabel.OffsetBy(0, y); + Widgets.Label(rect, "EdB.PC.Panel.Age.Label.BiologicalAge".Translate()); + // Biological age years field. + rect = RectBiologicalAgeFieldYears.OffsetBy(0, y); + biologicalFieldYears.Draw(rect, state.CurrentPawn.BiologicalAgeInYears); + // Biological age days field. + rect = RectBiologicalAgeFieldDays.OffsetBy(0, y); + biologicalFieldDays.Draw(rect, state.CurrentPawn.BiologicalAgeInDays % DaysPerYear); + + y += RectBiologicalAgeLabel.height; + + // Vertical padding between the two age field pairs + y += 6; + + Text.Font = GameFont.Small; + GUI.color = Style.ColorText; + Text.Anchor = TextAnchor.MiddleLeft; + rect = RectChronologicalAgeLabel.OffsetBy(0, y); + Widgets.Label(rect, "EdB.PC.Panel.Age.Label.ChronologicalAge".Translate()); + // Chronological age years field. + rect = RectChronologicalAgeFieldYears.OffsetBy(0, y); + chronologicalFieldYears.Draw(rect, state.CurrentPawn.ChronologicalAgeInYears); + // Chronological age days field. + rect = RectChronologicalAgeFieldDays.OffsetBy(0, y); + chronologicalFieldDays.Draw(rect, state.CurrentPawn.ChronologicalAgeInDays % DaysPerYear); + + y += RectChronologicalAgeLabel.height; - // Biological age field. - Rect fieldRect = RectBiologicalAgeField; - biologicalField.Draw(fieldRect, customPawn.BiologicalAge); + Text.Font = GameFont.Small; + GUI.color = Color.white; + Text.Anchor = TextAnchor.UpperLeft; - // Chronological age field. - fieldRect = RectChronologicalAgeField; - chronologicalField.Draw(fieldRect, customPawn.ChronologicalAge); + return y - top; } } } diff --git a/Source/PanelAppearance.cs b/Source/PanelAppearance.cs index 4a3d0eb..ae9a08b 100644 --- a/Source/PanelAppearance.cs +++ b/Source/PanelAppearance.cs @@ -1,3 +1,4 @@ +using EdB.PrepareCarefully.Reflection; using RimWorld; using System; using System.Collections.Generic; @@ -106,10 +107,8 @@ public PanelAppearance() { } // Set up default skin colors - foreach (Color color in PawnColorUtils.Colors) { - skinColors.Add(color); - } - + skinColors = SkinColorsForPawn(null); + this.ChangePawnLayer(null); } @@ -117,6 +116,16 @@ public PanelAppearance() { private Rect RectPortrait; private Rect RectButtonRandomize; + public List SkinColorsForPawn(Verse.Pawn pawn) { + List result = new List(); + result.AddRange(RimWorld.PawnSkinColors.SkinColorGenesInOrder.ConvertAll(gene => RimWorld.PawnSkinColors.GetSkinColor(gene.minMelanin))); + if (pawn == null) { + return result; + } + result.AddRange(pawn.genes.GenesListForReading.Where(g => g.def.skinColorOverride.HasValue).Select(g => g.def.skinColorOverride.Value)); + return result; + } + public override void Resize(Rect rect) { base.Resize(rect); @@ -133,7 +142,11 @@ public override void Resize(Rect rect) { RectGenderMale = new Rect(RectGenderFemale.xMax + 2, RectPortrait.y + 6, 20, 24); } - public void ChangePawn(CustomPawn pawn) { + protected List currentPawnLayers = null; + public void UpdatePawnLayers() { + var pawn = PrepareCarefully.Instance.State.CurrentPawn; + currentPawnLayers = PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn); + // Make sure the selected layer is still valid for the new pawn. List layers = PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn); if (selectedPawnLayer != null) { @@ -141,47 +154,22 @@ public void ChangePawn(CustomPawn pawn) { } if (selectedPawnLayer == null) { selectedPawnLayer = layers.FirstOrDefault(); + selectedPawnLayer = layers.FirstOrDefault(); } - // Prepare the delegate actions for the layer selected dropdown. pawnLayerActions.Clear(); - foreach (var layer in layers) { + foreach (var layer in currentPawnLayers) { pawnLayerActions.Add(delegate { this.ChangePawnLayer(layer); }); } - - currentPawn = pawn; - } - - protected List currentPawnLayers = null; - public void UpdatePawnLayers() { - var pawn = PrepareCarefully.Instance.State.CurrentPawn; - currentPawnLayers = PrepareCarefully.Instance.Providers.PawnLayers.GetLayersForPawn(pawn); - if (selectedPawnLayer != null) { - foreach (var layer in currentPawnLayers) { - if (layer.Name == selectedPawnLayer.Name) { - selectedPawnLayer = layer; - /* - if (layer.Options != null) { - bool found = false; - foreach (var option in layer.Options) { - if (layer.IsOptionSelected(pawn, option)) { - found = true; - break; - } - } - } - */ - break; - } - } - } + skinColors = SkinColorsForPawn(PrepareCarefully.Instance.State.CurrentPawn.Pawn); } protected override void DrawPanelContent(State state) { base.DrawPanelContent(state); CustomPawn customPawn = state.CurrentPawn; if (currentPawn != state.CurrentPawn) { - ChangePawn(state.CurrentPawn); + UpdatePawnLayers(); + currentPawn = state.CurrentPawn; } if (currentPawnLayers == null) { UpdatePawnLayers(); @@ -189,7 +177,6 @@ protected override void DrawPanelContent(State state) { string label = this.selectedPawnLayer.Label; if (WidgetDropdown.Button(RectLayerSelector, label, true, false, true)) { List list = new List(); - int layerCount = this.pawnLayerActions.Count; int i = 0; foreach (var layer in currentPawnLayers) { label = layer.Label; @@ -340,11 +327,11 @@ protected override void DrawPanelContent(State state) { } else if (selectedPawnLayer.ColorSelectorType == ColorSelectorType.Skin) { AlienRace alienRace = customPawn.AlienRace; - if (alienRace == null || alienRace.UseMelaninLevels) { - DrawHumanlikeColorSelector(customPawn, cursorY); + if (alienRace == null || alienRace.UseMelaninLevels || alienRace.ThingDef?.defName == "Human") { + DrawSkinColorSelector(customPawn, cursorY, skinColors, true); } else if (alienRace.ChangeableColor) { - DrawAlienPawnColorSelector(customPawn, cursorY, alienRace.PrimaryColors, true); + DrawSkinColorSelector(customPawn, cursorY, alienRace.PrimaryColors, true); } } @@ -391,8 +378,14 @@ protected void DrawPawn(CustomPawn customPawn, Rect rect) { try { Vector2 pawnSize = new Vector2(128f, 180f); Rect pawnRect = new Rect(rect.width * 0.5f - pawnSize.x * 0.5f, 10 + rect.height * 0.5f - pawnSize.y * 0.5f, pawnSize.x, pawnSize.y); + RenderTexture texture = customPawn.GetPortrait(pawnSize); - GUI.DrawTexture(pawnRect, (Texture)texture); + if (texture != null) { + GUI.DrawTexture(pawnRect, (Texture)texture); + } + } + catch (Exception e) { + Logger.Error("Failed to draw pawn", e); } finally { GUI.EndGroup(); @@ -582,7 +575,7 @@ protected void DrawColorSelectorForPawnLayer(CustomPawn customPawn, float cursor GUI.color = Color.white; } - protected void DrawAlienPawnColorSelector(CustomPawn customPawn, float cursorY, List colors, bool allowAnyColor) { + protected void DrawSkinColorSelector(CustomPawn customPawn, float cursorY, List colors, bool allowAnyColor) { Color currentColor = customPawn.Pawn.story.SkinColor; Color clickedColor = currentColor; Rect rect = new Rect(SwatchPosition.x, cursorY, SwatchSize.x, SwatchSize.y); @@ -666,97 +659,6 @@ protected void SetColor(CustomPawn customPawn, Color color) { customPawn.SetColor(selectedPawnLayer, color); } - protected void DrawHumanlikeColorSelector(CustomPawn customPawn, float cursorY) { - float melanin = customPawn.MelaninLevel; - int currentSwatchIndex = PawnColorUtils.GetLeftIndexForValue(melanin); - Color currentSwatchColor = PawnColorUtils.Colors[currentSwatchIndex]; - - Rect swatchRect = new Rect(SwatchPosition.x, cursorY, SwatchSize.x, SwatchSize.y); - - // Draw the swatch selection boxes. - int colorCount = PawnColorUtils.Colors.Length - 1; - int clickedIndex = -1; - for (int i = 0; i < colorCount; i++) { - Color color = PawnColorUtils.Colors[i]; - - // If the swatch is selected, draw a heavier border around it. - bool isThisSwatchSelected = (i == currentSwatchIndex); - if (isThisSwatchSelected) { - Rect selectionRect = new Rect(swatchRect.x - 2, swatchRect.y - 2, SwatchSize.x + 4, SwatchSize.y + 4); - GUI.color = ColorSwatchSelection; - GUI.DrawTexture(selectionRect, BaseContent.WhiteTex); - } - - // Draw the border around the swatch. - Rect borderRect = new Rect(swatchRect.x - 1, swatchRect.y - 1, SwatchSize.x + 2, SwatchSize.y + 2); - GUI.color = ColorSwatchBorder; - GUI.DrawTexture(borderRect, BaseContent.WhiteTex); - - // Draw the swatch itself. - GUI.color = color; - GUI.DrawTexture(swatchRect, BaseContent.WhiteTex); - - if (!isThisSwatchSelected) { - if (Widgets.ButtonInvisible(swatchRect, false)) { - clickedIndex = i; - //currentSwatchColor = color; - } - } - - // Advance the swatch rect cursor position and wrap it if necessary. - swatchRect.x += SwatchSpacing.x; - if (swatchRect.x >= SwatchLimit - SwatchSize.x) { - swatchRect.y += SwatchSpacing.y; - swatchRect.x = SwatchPosition.x; - } - } - - // Draw the current color box. - GUI.color = Color.white; - Rect currentColorRect = new Rect(SwatchPosition.x, swatchRect.y + 4, 49, 49); - if (swatchRect.x != SwatchPosition.x) { - currentColorRect.y += SwatchSpacing.y; - } - GUI.color = ColorSwatchBorder; - GUI.DrawTexture(currentColorRect, BaseContent.WhiteTex); - GUI.color = customPawn.SkinColor; - GUI.DrawTexture(currentColorRect.ContractedBy(1), BaseContent.WhiteTex); - GUI.color = Color.white; - - // Figure out the lerp value so that we can draw the slider. - float minValue = 0.00f; - float maxValue = 0.99f; - float t = PawnColorUtils.GetRelativeLerpValue(customPawn.MelaninLevel); - if (t < minValue) { - t = minValue; - } - else if (t > maxValue) { - t = maxValue; - } - if (clickedIndex != -1) { - t = minValue; - } - - // Draw the slider. - float newValue = GUI.HorizontalSlider(new Rect(currentColorRect.x + 56, currentColorRect.y + 18, 136, 16), t, minValue, 1); - if (newValue < minValue) { - newValue = minValue; - } - else if (newValue > maxValue) { - newValue = maxValue; - } - GUI.color = Color.white; - - // If the user selected a new swatch or changed the lerp value, set a new color value. - if (t != newValue || clickedIndex != -1) { - if (clickedIndex != -1) { - currentSwatchIndex = clickedIndex; - } - float melaninLevel = PawnColorUtils.GetValueFromRelativeLerp(currentSwatchIndex, newValue); - customPawn.MelaninLevel = melaninLevel; - } - } - protected void DrawFieldSelector(Rect fieldRect, string label, Action previousAction, Action nextAction, Action clickAction) { DrawFieldSelector(fieldRect, label, previousAction, nextAction, clickAction, Style.ColorText); } @@ -814,14 +716,6 @@ protected string PawnLayerLabel { get { CustomPawn customPawn = PrepareCarefully.Instance.State.CurrentPawn; string label = "EdB.PC.Panel.Appearance.NoneSelected".Translate(); - /* - if (selectedPawnLayer == PrepareCarefully.Instance.Providers.PawnLayers.BodyLayer) { - label = PrepareCarefully.Instance.Providers.BodyTypes.GetBodyTypeLabel(customPawn.BodyType); - } - else if (selectedPawnLayer == PrepareCarefully.Instance.Providers.PawnLayers.HeadLayer) { - label = GetHeadLabel(customPawn); - } - */ if (selectedPawnLayer.Options != null) { PawnLayerOption option = selectedPawnLayer.GetSelectedOption(customPawn); if (option != null) { @@ -846,7 +740,7 @@ protected string PawnLayerLabel { } protected void SelectNextHead(CustomPawn customPawn, int direction) { - List heads = PrepareCarefully.Instance.Providers.HeadTypes.GetHeadTypes(customPawn.Pawn.def, customPawn.Gender).ToList(); + List heads = PrepareCarefully.Instance.Providers.HeadTypes.GetHeadTypes(customPawn.Pawn.def, customPawn.Gender).ToList(); int index = heads.IndexOf(customPawn.HeadType); if (index == -1) { return; @@ -862,9 +756,13 @@ protected void SelectNextHead(CustomPawn customPawn, int direction) { this.pawnLayerLabel = GetHeadLabel(customPawn); } - protected void SelectNextBodyType(CustomPawn customPawn, int direction) { + protected List BodyTypesForPawn(CustomPawn pawn) { ProviderBodyTypes provider = PrepareCarefully.Instance.Providers.BodyTypes; - List bodyTypes = provider.GetBodyTypesForPawn(customPawn); + return provider.GetBodyTypesForPawn(pawn).Where(b => b != BodyTypeDefOf.Baby && b != BodyTypeDefOf.Child).ToList(); + } + + protected void SelectNextBodyType(CustomPawn customPawn, int direction) { + List bodyTypes = BodyTypesForPawn(customPawn); int index = bodyTypes.IndexOf(customPawn.BodyType); if (index == -1) { Logger.Warning("Could not find the current pawn's body type in list of available options: " + customPawn.BodyType); @@ -878,6 +776,7 @@ protected void SelectNextBodyType(CustomPawn customPawn, int direction) { index = 0; } customPawn.BodyType = bodyTypes[index]; + ProviderBodyTypes provider = PrepareCarefully.Instance.Providers.BodyTypes; this.pawnLayerLabel = provider.GetBodyTypeLabel(customPawn.BodyType); } @@ -932,7 +831,7 @@ protected void SelectNextPawnLayerOption(CustomPawn customPawn, int direction) { protected string GetHeadLabel(CustomPawn pawn) { if (pawn.HeadType != null) { - return pawn.HeadType.Label; + return pawn.HeadType.LabelCap; } else { return "EdB.PC.Common.Default".Translate(); diff --git a/Source/PanelBackstory.cs b/Source/PanelBackstory.cs index 9e3c7b2..f635d23 100644 --- a/Source/PanelBackstory.cs +++ b/Source/PanelBackstory.cs @@ -15,10 +15,10 @@ public class PanelBackstory : PanelModule { protected Field FieldChildhood = new Field(); protected Field FieldAdulthood = new Field(); protected ProviderBackstories providerBackstories = PrepareCarefully.Instance.Providers.Backstories; - protected List> availableFilters = new List>(); - protected List> activeFilters = new List>(); + protected List> availableFilters = new List>(); + protected List> activeFilters = new List>(); - public delegate void UpdateBackstoryHandler(BackstorySlot slot, Backstory backstory); + public delegate void UpdateBackstoryHandler(BackstorySlot slot, BackstoryDef backstory); public delegate void RandomizeBackstoriesHandler(); public event UpdateBackstoryHandler BackstoryUpdated; @@ -74,15 +74,25 @@ protected float DrawChildhood(CustomPawn pawn, float y, float width) { FieldChildhood.Label = null; } FieldChildhood.Tip = pawn.Childhood.CheckedDescriptionFor(pawn.Pawn); - FieldChildhood.ClickAction = () => { - ShowBackstoryDialog(pawn, BackstorySlot.Childhood); - }; - FieldChildhood.PreviousAction = () => { - NextBackstory(pawn, BackstorySlot.Childhood, -1); - }; - FieldChildhood.NextAction = () => { - NextBackstory(pawn, BackstorySlot.Childhood, 1); - }; + if (pawn.IsNewborn() || pawn.IsJuvenile()) { + FieldChildhood.ClickAction = null; + FieldChildhood.PreviousAction = null; + FieldChildhood.NextAction = null; + FieldChildhood.NextPreviousButtonsHidden = true; + } + else { + FieldChildhood.NextPreviousButtonsHidden = false; + FieldChildhood.ClickAction = () => { + ShowBackstoryDialog(pawn, BackstorySlot.Childhood); + }; + FieldChildhood.PreviousAction = () => { + NextBackstory(pawn, BackstorySlot.Childhood, -1); + }; + FieldChildhood.NextAction = () => { + NextBackstory(pawn, BackstorySlot.Childhood, 1); + }; + } + FieldChildhood.Draw(); return FieldRect.height; @@ -157,7 +167,9 @@ public override float Draw(State state, float y) { y += Margin.y; CustomPawn pawn = state.CurrentPawn; - DrawRandomizeButton(y, Width); + if (pawn.Pawn.DevelopmentalStage == DevelopmentalStage.Adult) { + DrawRandomizeButton(y, Width); + } y += DrawHeader(y, Width, "Backstory".Translate().Resolve()); y += DrawChildhood(pawn, y, Width); y += 6; @@ -178,24 +190,24 @@ public override float Draw(State state, float y) { } protected void ShowBackstoryDialog(CustomPawn customPawn, BackstorySlot slot) { - Backstory originalBackstory = (slot == BackstorySlot.Childhood) ? customPawn.Childhood : customPawn.Adulthood; - Backstory selectedBackstory = originalBackstory; - Filter filterToRemove = null; + BackstoryDef originalBackstory = (slot == BackstorySlot.Childhood) ? customPawn.Childhood : customPawn.Adulthood; + BackstoryDef selectedBackstory = originalBackstory; + Filter filterToRemove = null; bool filterListDirtyFlag = true; - List fullOptionsList = slot == BackstorySlot.Childhood ? + List fullOptionsList = slot == BackstorySlot.Childhood ? this.providerBackstories.AllChildhookBackstories : this.providerBackstories.AllAdulthookBackstories; - List filteredBackstories = new List(fullOptionsList.Count); - Dialog_Options dialog = new Dialog_Options(filteredBackstories) { - NameFunc = (Backstory backstory) => { + List filteredBackstories = new List(fullOptionsList.Count); + Dialog_Options dialog = new Dialog_Options(filteredBackstories) { + NameFunc = (BackstoryDef backstory) => { return backstory.TitleCapFor(customPawn.Gender); }, - DescriptionFunc = (Backstory backstory) => { + DescriptionFunc = (BackstoryDef backstory) => { return backstory.CheckedDescriptionFor(customPawn.Pawn); }, - SelectedFunc = (Backstory backstory) => { + SelectedFunc = (BackstoryDef backstory) => { return selectedBackstory == backstory; }, - SelectAction = (Backstory backstory) => { + SelectAction = (BackstoryDef backstory) => { selectedBackstory = backstory; }, CloseAction = () => { @@ -294,8 +306,8 @@ protected void ShowBackstoryDialog(CustomPawn customPawn, BackstorySlot slot) { } protected void NextBackstory(CustomPawn pawn, BackstorySlot slot, int direction) { - Backstory backstory; - List backstories; + BackstoryDef backstory; + List backstories; PopulateBackstoriesFromSlot(pawn, slot, out backstories, out backstory); int currentIndex = FindBackstoryIndex(pawn, slot); @@ -310,14 +322,14 @@ protected void NextBackstory(CustomPawn pawn, BackstorySlot slot, int direction) } protected int FindBackstoryIndex(CustomPawn pawn, BackstorySlot slot) { - Backstory backstory; - List backstories; + BackstoryDef backstory; + List backstories; PopulateBackstoriesFromSlot(pawn, slot, out backstories, out backstory); return backstories.IndexOf(backstory); } - protected void PopulateBackstoriesFromSlot(CustomPawn pawn, BackstorySlot slot, out List backstories, - out Backstory backstory) { + protected void PopulateBackstoriesFromSlot(CustomPawn pawn, BackstorySlot slot, out List backstories, + out BackstoryDef backstory) { backstory = (slot == BackstorySlot.Childhood) ? pawn.Childhood : pawn.Adulthood; backstories = (slot == BackstorySlot.Childhood) ? providerBackstories.GetChildhoodBackstoriesForPawn(pawn) : providerBackstories.GetAdulthoodBackstoriesForPawn(pawn); diff --git a/Source/PanelFavoriteColor.cs b/Source/PanelFavoriteColor.cs index 54fccd9..29ff7b7 100644 --- a/Source/PanelFavoriteColor.cs +++ b/Source/PanelFavoriteColor.cs @@ -6,6 +6,8 @@ using UnityEngine; using Verse; using Verse.Sound; +using static UnityEngine.Random; + namespace EdB.PrepareCarefully { public class PanelFavoriteColor : PanelBase { public delegate void UpdateFavoriteColorHandler(Color? color); @@ -26,7 +28,13 @@ public override void Resize(Rect rect) { protected override void DrawPanelContent(State state) { base.DrawPanelContent(state); + if (state.CurrentPawn.Pawn.DevelopmentalStage.Baby()) { + DrawForBaby(); + return; + } + Rect rect = new Rect(8, 8, PanelRect.width - 16, PanelRect.height - 16); + Color favoriteColor = state.CurrentPawn.Pawn.story.favoriteColor.HasValue ? state.CurrentPawn.Pawn.story.favoriteColor.Value : new Color(0.5f, 0.5f, 0.5f); if (rect.Contains(Event.current.mousePosition)) { @@ -44,6 +52,22 @@ protected override void DrawPanelContent(State state) { Find.WindowStack.Add(dialog); } + Text.Font = GameFont.Small; + GUI.color = Color.white; + Text.Anchor = TextAnchor.UpperLeft; + } + protected void DrawForBaby() { + Rect rect = new Rect(8, 8, PanelRect.width - 16, PanelRect.height - 16); + + //if (rect.Contains(Event.current.mousePosition)) { + // GUI.DrawTexture(rect, BaseContent.WhiteTex); + // GUI.color = Color.white; + //} + + GUI.color = Style.ColorTabViewBackground; + Widgets.DrawBox(rect, 2); + Widgets.DrawLine(new Vector2(rect.xMin, rect.yMin), new Vector2(rect.xMax, rect.yMax), GUI.color, 2); + Text.Font = GameFont.Small; GUI.color = Color.white; Text.Anchor = TextAnchor.UpperLeft; diff --git a/Source/PanelHealth.cs b/Source/PanelHealth.cs index 820b8c7..cb4c0a8 100644 --- a/Source/PanelHealth.cs +++ b/Source/PanelHealth.cs @@ -292,12 +292,13 @@ public void DrawAddButton(float y, float width) { } }; - injuryOptionDialog = new Dialog_Options(healthOptions.InjuryOptions) { + injuryOptionDialog = new Dialog_Options(healthOptions.SelectableInjuryOptions) { ConfirmButtonLabel = "EdB.PC.Common.Next".Translate(), CancelButtonLabel = "EdB.PC.Common.Cancel".Translate(), HeaderLabel = "EdB.PC.Dialog.Injury.Header".Translate(), NameFunc = (InjuryOption option) => { - return option.Label; + return option.HediffDef.defName; + //return option.Label; }, DescriptionFunc = (InjuryOption option) => { return option.HediffDef?.description; diff --git a/Source/PanelModule.cs b/Source/PanelModule.cs index 213d851..cfb1c76 100644 --- a/Source/PanelModule.cs +++ b/Source/PanelModule.cs @@ -35,7 +35,12 @@ public virtual float Draw(State state, float y) { } public virtual float DrawHeader(float y, float width, string text) { - Rect headerLabelRect = new Rect(Margin.x, y, width - (Margin.x * 2), HeaderHeight); + DrawHeaderWithOffset(0, y, width, text); + return HeaderHeight; + } + + public virtual float DrawHeaderWithOffset(float offset, float y, float width, string text) { + Rect headerLabelRect = new Rect(Margin.x + offset, y, width - (Margin.x * 2) - offset, HeaderHeight); var fontValue = Text.Font; var anchorValue = Text.Anchor; var colorValue = GUI.color; diff --git a/Source/PanelPawnList.cs b/Source/PanelPawnList.cs index a3df88c..2973b50 100644 --- a/Source/PanelPawnList.cs +++ b/Source/PanelPawnList.cs @@ -14,7 +14,7 @@ public abstract class PanelPawnList : PanelBase { public delegate void MinimizeHandler(); public delegate void AddPawnHandler(bool startingPawn); public delegate void AddFactionPawnHandler(FactionDef def, bool startingPawn); - public delegate void AddPawnWithPawnKindHandler(PawnKindDef def, bool startingPawn); + public delegate void AddPawnWithPawnKindHandler(PawnKindOption def, bool startingPawn); public delegate void LoadCharacterHandler(string name); public event SelectPawnHandler PawnSelected; @@ -242,12 +242,16 @@ protected override void DrawPanelContent(State state) { Rect pawnRect = RectPortrait.OffsetBy(rect.position); GUI.color = Color.white; - RenderTexture pawnTexture = pawn.GetPortrait(pawnRect.size); + Rect clipRect = RectEntry.OffsetBy(rect.position); + RenderTexture pawnTexture = pawn.GetPortrait(pawnRect.size); try { GUI.BeginClip(clipRect); GUI.DrawTexture(RectPortrait, (Texture)pawnTexture); } + catch (Exception e) { + Logger.Error("Failed to draw pawn", e); + } finally { GUI.EndClip(); } @@ -268,11 +272,11 @@ protected override void DrawPanelContent(State state) { string description = null; if (pawn.IsAdult) { if (pawn.Adulthood != null) { - description = pawn.Adulthood.TitleShortCapFor(pawn.Gender); + description = pawn.Adulthood.TitleShortFor(pawn.Gender).CapitalizeFirst(); } } else { - description = pawn.Childhood.TitleShortCapFor(pawn.Gender); + description = pawn.Childhood.TitleShortFor(pawn.Gender).CapitalizeFirst(); } if (!description.NullOrEmpty()) { Widgets.Label(professionRect, descriptionTrimmer.TrimLabelIfNeeded(description)); @@ -383,12 +387,12 @@ public IEnumerable AllPawnKinds(PawnKindDef basicKind, IEnumerable< } } - protected List.RowGroup> rowGroups = new List.RowGroup>(); + protected List.RowGroup> rowGroups = new List.RowGroup>(); protected void ShowPawnKindDialog() { - HashSet disabled = new HashSet(); + var disabled = new HashSet(); rowGroups.Clear(); - PawnKindDef selected = PrepareCarefully.Instance.State.LastSelectedPawnKindDef; + PawnKindOption selected = PrepareCarefully.Instance.State.LastSelectedPawnKindDef; List factionPawnKindsList = new List(PrepareCarefully.Instance.Providers.PawnKinds.PawnKindsByFaction); // Sort the pawn kinds to put the colony faction at the top. @@ -407,21 +411,26 @@ protected void ShowPawnKindDialog() { // If no pawn kind has been selected, select the colony's basic pawn kind by default. if (selected == null) { - selected = factionPawnKindsList?.FirstOrDefault(f => f != null)?.PawnKinds?.FirstOrDefault(k => k != null); + var faction = factionPawnKindsList?.FirstOrDefault(f => f != null)?.Faction; + var kind = factionPawnKindsList?.FirstOrDefault(f => f != null)?.PawnKinds?.FirstOrDefault(k => k != null); + if (faction != null && kind != null) { + selected = new PawnKindOption(faction, kind); + } } foreach (var factionPawnKinds in factionPawnKindsList) { if (factionPawnKinds.PawnKinds.Count > 0) { - rowGroups.Add(new WidgetTable.RowGroup("" + factionPawnKinds.Faction.LabelCap.ToString() + "", factionPawnKinds.PawnKinds)); + rowGroups.Add(new WidgetTable.RowGroup("" + factionPawnKinds.Faction.LabelCap.ToString() + "", + factionPawnKinds.PawnKinds.ConvertAll(f => new PawnKindOption(factionPawnKinds.Faction, f)))); } } if (!PrepareCarefully.Instance.Providers.PawnKinds.PawnKindsWithNoFaction.EnumerableNullOrEmpty()) { - rowGroups.Add(new WidgetTable.RowGroup("Other", PrepareCarefully.Instance.Providers.PawnKinds.PawnKindsWithNoFaction)); + rowGroups.Add(new WidgetTable.RowGroup("Other", PrepareCarefully.Instance.Providers.PawnKinds.PawnKindsWithNoFaction.Select(k => new PawnKindOption(null, k)))); } DialogPawnKinds dialog = new DialogPawnKinds() { HeaderLabel = "EdB.PC.Panel.PawnList.SelectFaction".Translate(), - SelectAction = (PawnKindDef pawnKind) => { selected = pawnKind; }, + SelectAction = (PawnKindOption option) => { selected = option; }, RowGroups = rowGroups, DisabledOptions = disabled, CloseAction = () => { @@ -434,7 +443,8 @@ protected void ShowPawnKindDialog() { Selected = selected, ShowRace = PrepareCarefully.Instance.Providers.PawnKinds.AnyNonHumanPawnKinds && !PawnKindRaceDiversificationEnabled }; - dialog.ScrollTo(PrepareCarefully.Instance.State.LastSelectedPawnKindDef); + dialog.ScrollToWhenOpened(PrepareCarefully.Instance.State.LastSelectedPawnKindDef); + //Logger.Debug("ScrollToWhenOpened = " + PrepareCarefully.Instance.State.LastSelectedPawnKindDef); Find.WindowStack.Add(dialog); } diff --git a/Source/PanelRandomize.cs b/Source/PanelRandomize.cs index ca5d390..c19388e 100644 --- a/Source/PanelRandomize.cs +++ b/Source/PanelRandomize.cs @@ -1,5 +1,9 @@ using RimWorld; using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; using UnityEngine; using Verse; using Verse.Sound; @@ -10,8 +14,17 @@ public class PanelRandomize : PanelBase { public event RandomizeAllHandler RandomizeAllClicked; public override void Draw(State state) { - base.Draw(state); + if (ModsConfig.BiotechActive) { + DrawForBiotech(state); + return; + } + else { + DrawForNonBiotech(state); + } + } + public void DrawForNonBiotech(State state) { + base.Draw(state); Rect randomRect = new Rect( PanelRect.x + PanelRect.width / 2 - Textures.TextureButtonRandomLarge.width / 2 - 1, PanelRect.y + PanelRect.height / 2 - Textures.TextureButtonRandomLarge.height / 2, @@ -31,7 +44,163 @@ public override void Draw(State state) { } GUI.color = Color.white; + } + + public void DrawForBiotech(State state) { + base.Draw(state); + + var texture = DevelopmentalStageIconTexture(state.CurrentPawn.RandomizeDevelopmentalStage); + float buttonSize = 20f; + Rect dropdownRect = new Rect(PanelRect.x + 6, PanelRect.y + PanelRect.height * 0.25f - buttonSize * 0.5f, 34.0f, 18.0f); + if (WidgetDropdown.ImageButton(dropdownRect,texture, new Vector2(buttonSize, buttonSize), false, false, true)) { + List options = new List { + new FloatMenuOption("Adult".Translate().CapitalizeFirst(), delegate + { + state.CurrentPawn.RandomizeDevelopmentalStage = DevelopmentalStage.Adult; + }, Textures.TextureAdult, Color.white), + new FloatMenuOption("Child".Translate().CapitalizeFirst(), delegate + { + state.CurrentPawn.RandomizeDevelopmentalStage = DevelopmentalStage.Child; + }, Textures.TextureChild, Color.white), + new FloatMenuOption("Baby".Translate().CapitalizeFirst(), delegate + { + state.CurrentPawn.RandomizeDevelopmentalStage = DevelopmentalStage.Baby; + }, Textures.TextureBaby, Color.white) + }; + Find.WindowStack.Add(new FloatMenu(options)); + } + string tipTitle = ""; + if (state.CurrentPawn.Pawn.DevelopmentalStage == DevelopmentalStage.Adult) { + tipTitle = "Adult".Translate().CapitalizeFirst(); + } + else if (state.CurrentPawn.Pawn.DevelopmentalStage == DevelopmentalStage.Child) { + tipTitle = "Child".Translate().CapitalizeFirst(); + } + else { + tipTitle = "Baby".Translate().CapitalizeFirst(); + } + tipTitle = tipTitle.Colorize(ColoredText.TipSectionTitleColor); + TooltipHandler.TipRegion(dropdownRect, "EdB.PC.Panel.Randomize.Tip.DevelopmentalStage".Translate(tipTitle)); + + if (state.CurrentPawn.RandomizeXenotype != null) { + texture = state.CurrentPawn.RandomizeXenotype.Icon; + } + else if (state.CurrentPawn.RandomizeCustomXenotype != null) { + texture = state.CurrentPawn.RandomizeCustomXenotype.IconDef.Icon; + } + else if (state.CurrentPawn.RandomizeAnyNonArchite) { + texture = Textures.TextureButtonRandom; + } + else { + texture = null; + } + buttonSize = 22.0f; + dropdownRect = new Rect(PanelRect.x + 6, PanelRect.y + PanelRect.height * 0.75f - buttonSize * 0.5f, 32.0f, 18.0f); + if (WidgetDropdown.ImageButton(dropdownRect, texture, new Vector2(buttonSize, buttonSize), false, false, true)) { + List list = new List { + new FloatMenuOption("AnyNonArchite".Translate().CapitalizeFirst(), delegate + { + state.CurrentPawn.RandomizeXenotype = null; + state.CurrentPawn.RandomizeCustomXenotype = null; + state.CurrentPawn.RandomizeAnyNonArchite = true; + }), + new FloatMenuOption("XenotypeEditor".Translate() + "...", delegate + { + Find.WindowStack.Add(new Dialog_CreateXenotype(-1, delegate + { + CharacterCardUtility.cachedCustomXenotypes = null; + })); + }) + }; + foreach (XenotypeDef item in DefDatabase.AllDefs.OrderBy((XenotypeDef x) => 0f - x.displayPriority)) { + XenotypeDef xenotype = item; + list.Add(new FloatMenuOption(xenotype.LabelCap, delegate + { + state.CurrentPawn.RandomizeXenotype = xenotype; + state.CurrentPawn.RandomizeCustomXenotype = null; + state.CurrentPawn.RandomizeAnyNonArchite = false; + }, + xenotype.Icon, XenotypeDef.IconColor, MenuOptionPriority.Default, delegate (Rect r) + { + TooltipHandler.TipRegion(r, xenotype.descriptionShort ?? xenotype.description); + }, + null, 24f, (Rect r) => Widgets.InfoCardButton(r.x, r.y + 3f, xenotype) ? true : false, null, playSelectionSound: true, 0, HorizontalJustification.Left, extraPartRightJustified: true)); + } + var customXenotypes = ReflectionUtil.GetStaticPropertyValue>(typeof(CharacterCardUtility), "CustomXenotypes"); + if (customXenotypes != null) { + foreach (CustomXenotype customXenotype in customXenotypes) { + CustomXenotype customInner = customXenotype; + list.Add(new FloatMenuOption(customInner.name.CapitalizeFirst() + " (" + "Custom".Translate() + ")", delegate + { + Logger.Debug("Customized Xenotype, XenotypeDef: " + state.CurrentPawn.Pawn.genes.Xenotype?.defName); + Logger.Debug("Customized Xenotype: " + state.CurrentPawn.Pawn.genes.CustomXenotype?.name); + state.CurrentPawn.RandomizeCustomXenotype = customInner; + state.CurrentPawn.RandomizeXenotype = null; + state.CurrentPawn.RandomizeAnyNonArchite = false; + }, + customInner.IconDef.Icon, XenotypeDef.IconColor, MenuOptionPriority.Default, null, null, 24f, delegate (Rect r) { + if (Widgets.ButtonImage(new Rect(r.x, r.y + (r.height - r.width) / 2f, r.width, r.width), TexButton.DeleteX, GUI.color)) { + Find.WindowStack.Add(Dialog_MessageBox.CreateConfirmation("ConfirmDelete".Translate(customInner.name.CapitalizeFirst()), delegate { + string path = GenFilePaths.AbsFilePathForXenotype(customInner.name); + if (File.Exists(path)) { + File.Delete(path); + CharacterCardUtility.cachedCustomXenotypes = null; + } + }, destructive: true)); + return true; + } + return false; + }, null, playSelectionSound: true, 0, HorizontalJustification.Left, extraPartRightJustified: true)); + } + } + Find.WindowStack.Add(new FloatMenu(list)); + } + string xenotypeTipLabel = ""; + if (state.CurrentPawn.RandomizeXenotype != null) { + xenotypeTipLabel = state.CurrentPawn.RandomizeXenotype.LabelCap; + } + else if (state.CurrentPawn.RandomizeCustomXenotype != null) { + xenotypeTipLabel = state.CurrentPawn.RandomizeCustomXenotype.name; + } + else if (state.CurrentPawn.RandomizeAnyNonArchite) { + xenotypeTipLabel = "AnyNonArchite".Translate().CapitalizeFirst(); + } + xenotypeTipLabel = xenotypeTipLabel.Colorize(ColoredText.TipSectionTitleColor); + TooltipHandler.TipRegion(dropdownRect, "EdB.PC.Panel.Randomize.Tip.Xenotype".Translate(xenotypeTipLabel)); + + Rect randomRect = new Rect( + PanelRect.x + PanelRect.width - Textures.TextureButtonRandomLarge.width - 16, + PanelRect.y + PanelRect.HalfHeight() - Textures.TextureButtonRandomLarge.height * 0.5f, + Textures.TextureButtonRandomLarge.width, + Textures.TextureButtonRandomLarge.height + ); + if (randomRect.Contains(Event.current.mousePosition)) { + GUI.color = Style.ColorButtonHighlight; + } + else { + GUI.color = Style.ColorButton; + } + GUI.DrawTexture(randomRect, Textures.TextureButtonRandomLarge); + if (Widgets.ButtonInvisible(randomRect, false)) { + SoundDefOf.Tick_Low.PlayOneShotOnCamera(); + RandomizeAllClicked(); + } + GUI.color = Color.white; + GUI.color = Style.ColorTabViewBackground; + Widgets.DrawLineVertical(PanelRect.x + 48, PanelRect.y + 6, PanelRect.height - 12); + GUI.color = Color.white; + } + protected Texture2D DevelopmentalStageIconTexture(DevelopmentalStage developmentalStage) { + if (developmentalStage == DevelopmentalStage.Baby) { + return Textures.TextureBaby; + } + else if (developmentalStage == DevelopmentalStage.Child) { + return Textures.TextureChild; + } + else { + return Textures.TextureAdult; + } } } } diff --git a/Source/PanelRelationshipsParentChild.cs b/Source/PanelRelationshipsParentChild.cs index 91aa209..51a81ee 100644 --- a/Source/PanelRelationshipsParentChild.cs +++ b/Source/PanelRelationshipsParentChild.cs @@ -43,12 +43,12 @@ public class PanelRelationshipsParentChild : PanelBase { private Color ColorChild = new Color(22f / 255f, 22f / 255f, 23f / 255f); private Color ColorChildEmpty = new Color(22f / 255f, 22f / 255f, 23f / 255f, 0.40f); - private HashSet visibleBackstories = new HashSet(); + private HashSet visibleBackstories = new HashSet(); public PanelRelationshipsParentChild() { // TODO: Pull this out and put it in a utility somewhere, i.e. ProviderBackstory. - foreach (Backstory backstory in BackstoryDatabase.allBackstories.Values) { - if (backstory.identifier.StartsWith("FactionLeader")) { + foreach (BackstoryDef backstory in DefDatabase.AllDefs) { + if (backstory.identifier != null && backstory.identifier.StartsWith("FactionLeader")) { visibleBackstories.Add(backstory); } } diff --git a/Source/PanelSkills.cs b/Source/PanelSkills.cs index be40b36..0cd64f4 100644 --- a/Source/PanelSkills.cs +++ b/Source/PanelSkills.cs @@ -122,7 +122,7 @@ protected override void DrawPanelContent(State state) { Text.Font = GameFont.Small; foreach (var skill in customPawn.Pawn.skills.skills) { SkillDef def = skill.def; - bool disabled = skill.TotallyDisabled; + bool disabled = IsSkillDisabled(customPawn, skill); // Draw the label. GUI.color = Style.ColorText; @@ -222,9 +222,20 @@ public static void FillableBar(Rect rect, float fillPercent, Texture2D fillTex) GUI.DrawTexture(rect, fillTex); } + public static bool IsSkillDisabled(CustomPawn customPawn, SkillRecord skill) { + if (skill.TotallyDisabled) { + return true; + } + // TODO: Do we need to look at life stages to figure this out? + if (customPawn.Pawn.DevelopmentalStage == DevelopmentalStage.Newborn || customPawn.Pawn.DevelopmentalStage == DevelopmentalStage.Baby) { + return true; + } + return false; + } + private void DrawSkill(CustomPawn customPawn, SkillRecord skill, Rect rect) { int level = skill.Level; - bool disabled = skill.TotallyDisabled; + bool disabled = IsSkillDisabled(customPawn, skill); if (!disabled) { float barSize = (level > 0 ? (float)level : 0) / 20f; FillableBar(rect, barSize, Textures.TextureSkillBarFill); diff --git a/Source/PanelTraits.cs b/Source/PanelTraits.cs index d0605b8..d10ae34 100644 --- a/Source/PanelTraits.cs +++ b/Source/PanelTraits.cs @@ -67,10 +67,18 @@ public override float Draw(State state, float y) { if (trait != null) { field.Label = trait.LabelCap; field.Tip = GetTraitTip(trait, currentPawn); + field.Color = Style.ColorText; + if (trait.Suppressed) { + field.Color = ColoredText.SubtleGrayColor; + } + else if (trait.sourceGene != null) { + field.Color = ColoredText.GeneColor; + } } else { field.Label = null; field.Tip = null; + field.Color = Style.ColorText; } Trait localTrait = trait; int localIndex = index; @@ -147,7 +155,11 @@ public override float Draw(State state, float y) { // If the index is still zero, then the pawn has no traits. Draw the "none" label. if (index == 0) { GUI.color = Style.ColorText; - Widgets.Label(FieldRect.InsetBy(6, 0).OffsetBy(0, y - 4), "EdB.PC.Panel.Traits.None".Translate()); + string message = "EdB.PC.Panel.Traits.None".Translate(); + if (state.CurrentPawn.Pawn.DevelopmentalStage.Baby()) { + message = "TraitsDevelopLaterBaby".Translate(); + } + Widgets.Label(FieldRect.InsetBy(6, 0).OffsetBy(0, y - 4), message); y += FieldRect.height - 4; } @@ -159,59 +171,62 @@ public override float Draw(State state, float y) { clickAction = null; } - // Randomize traits button. - Rect randomizeRect = new Rect(Width - 32, top + 9, 22, 22); - if (randomizeRect.Contains(Event.current.mousePosition)) { - GUI.color = Style.ColorButtonHighlight; - } - else { - GUI.color = Style.ColorButton; - } - GUI.DrawTexture(randomizeRect, Textures.TextureButtonRandom); - if (Widgets.ButtonInvisible(randomizeRect, false)) { - SoundDefOf.Tick_Low.PlayOneShotOnCamera(); - tipCache.Invalidate(); - TraitsRandomized(); - } + // Don't show add or randomize buttons if the pawn is a baby + if (!state.CurrentPawn.Pawn.DevelopmentalStage.Baby()) { + // Randomize traits button. + Rect randomizeRect = new Rect(Width - 32, top + 9, 22, 22); + if (randomizeRect.Contains(Event.current.mousePosition)) { + GUI.color = Style.ColorButtonHighlight; + } + else { + GUI.color = Style.ColorButton; + } + GUI.DrawTexture(randomizeRect, Textures.TextureButtonRandom); + if (Widgets.ButtonInvisible(randomizeRect, false)) { + SoundDefOf.Tick_Low.PlayOneShotOnCamera(); + tipCache.Invalidate(); + TraitsRandomized(); + } - // Add trait button. - Rect addRect = new Rect(randomizeRect.x - 24, top + 12, 16, 16); - Style.SetGUIColorForButton(addRect); - int traitCount = state.CurrentPawn.Traits.Count(); - bool addButtonEnabled = (state.CurrentPawn != null && traitCount < Constraints.MaxTraits); - if (!addButtonEnabled) { - GUI.color = Style.ColorButtonDisabled; - } - GUI.DrawTexture(addRect, Textures.TextureButtonAdd); - if (addButtonEnabled && Widgets.ButtonInvisible(addRect, false)) { - ComputeDisallowedTraits(currentPawn, null); - SoundDefOf.Tick_Low.PlayOneShotOnCamera(); - Trait selectedTrait = null; - Dialog_Options dialog = new Dialog_Options(providerTraits.Traits) { - ConfirmButtonLabel = "EdB.PC.Common.Add".Translate(), - NameFunc = (Trait t) => { - return t.LabelCap; - }, - DescriptionFunc = (Trait t) => { - return GetTraitTip(t, state.CurrentPawn); - }, - SelectedFunc = (Trait t) => { - return selectedTrait == t; - }, - SelectAction = (Trait t) => { - selectedTrait = t; - }, - EnabledFunc = (Trait t) => { - return !disallowedTraitDefs.Contains(t.def); - }, - CloseAction = () => { - if (selectedTrait != null) { - TraitAdded(selectedTrait); - tipCache.Invalidate(); + // Add trait button. + Rect addRect = new Rect(randomizeRect.x - 24, top + 12, 16, 16); + Style.SetGUIColorForButton(addRect); + int traitCount = state.CurrentPawn.Traits.Count(); + bool addButtonEnabled = (state.CurrentPawn != null && traitCount < Constraints.MaxTraits); + if (!addButtonEnabled) { + GUI.color = Style.ColorButtonDisabled; + } + GUI.DrawTexture(addRect, Textures.TextureButtonAdd); + if (addButtonEnabled && Widgets.ButtonInvisible(addRect, false)) { + ComputeDisallowedTraits(currentPawn, null); + SoundDefOf.Tick_Low.PlayOneShotOnCamera(); + Trait selectedTrait = null; + Dialog_Options dialog = new Dialog_Options(providerTraits.Traits) { + ConfirmButtonLabel = "EdB.PC.Common.Add".Translate(), + NameFunc = (Trait t) => { + return t.LabelCap; + }, + DescriptionFunc = (Trait t) => { + return GetTraitTip(t, state.CurrentPawn); + }, + SelectedFunc = (Trait t) => { + return selectedTrait == t; + }, + SelectAction = (Trait t) => { + selectedTrait = t; + }, + EnabledFunc = (Trait t) => { + return !disallowedTraitDefs.Contains(t.def); + }, + CloseAction = () => { + if (selectedTrait != null) { + TraitAdded(selectedTrait); + tipCache.Invalidate(); + } } - } - }; - Find.WindowStack.Add(dialog); + }; + Find.WindowStack.Add(dialog); + } } // Remove any traits that were marked for deletion diff --git a/Source/PanelXenotype.cs b/Source/PanelXenotype.cs new file mode 100644 index 0000000..732e587 --- /dev/null +++ b/Source/PanelXenotype.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using UnityEngine; +using RimWorld; +using Verse; +using Verse.Sound; + +namespace EdB.PrepareCarefully { + public class PanelXenotype : PanelModule { + public static readonly Vector2 FieldPadding = new Vector2(6, 6); + + public Rect FieldRect; + protected Field FieldXenotype = new Field(); + protected LabelTrimmer labelTrimmer = new LabelTrimmer(); + + public override void Resize(float width) { + base.Resize(width); + FieldRect = new Rect(FieldPadding.x, 0, width - FieldPadding.x * 2, 30); + } + + public float Measure() { + return 0; + } + + public override bool IsVisible(State state) { + return ModsConfig.BiotechActive; + } + + public override float Draw(State state, float y) { + float top = y; + y += Margin.y; + + y += DrawHeader(y, Width, "Xenotype".Translate().CapitalizeFirst().Resolve()); + + CustomPawn pawn = state.CurrentPawn; + Pawn_GeneTracker geneTracker = pawn.Pawn.genes; + XenotypeDef xenotypeDef = geneTracker?.Xenotype; + CustomXenotype customXenotype = geneTracker?.CustomXenotype; + + FieldXenotype.Rect = FieldRect.OffsetBy(0, y); + labelTrimmer.Rect = FieldXenotype.Rect.InsetBy(8, 0); + + if (customXenotype != null) { + FieldXenotype.Label = labelTrimmer.TrimLabelIfNeeded(customXenotype.name); + } + else if (xenotypeDef != null) { + FieldXenotype.Label = labelTrimmer.TrimLabelIfNeeded(xenotypeDef.LabelCap); + } + FieldXenotype.Enabled = true; + FieldXenotype.ClickAction = () => { + Find.WindowStack.Add(new Dialog_ViewGenes(pawn.Pawn)); + }; + FieldXenotype.DrawIconFunc = (Rect rect) => { + if (xenotypeDef != null) { + GUI.DrawTexture(rect, xenotypeDef.Icon); + } + else if (customXenotype != null) { + GUI.DrawTexture(rect, customXenotype.IconDef?.Icon); + } + }; + FieldXenotype.IconSizeFunc = () => new Vector2(24, 24); + + FieldXenotype.Draw(); + + y += FieldRect.height; + + Text.Font = GameFont.Small; + GUI.color = Color.white; + Text.Anchor = TextAnchor.UpperLeft; + + y += Margin.y; + + return y - top; + } + + } +} diff --git a/Source/PawnColorUtils.cs b/Source/PawnColorUtils.cs deleted file mode 100644 index e58dfac..0000000 --- a/Source/PawnColorUtils.cs +++ /dev/null @@ -1,184 +0,0 @@ -using RimWorld; -using System; -using System.Collections.Generic; -using System.Reflection; -using UnityEngine; -using Verse; - -namespace EdB.PrepareCarefully { - public class PawnColorUtils { - // The values will match the color values in the PawnSkinColors class. We use a round-about way of getting at - // those color values in the InitializeColors() method. - public static Color[] Colors; - - // The same colors, but rounded to 3 fractional digits. When colors are saved to a preset, they are - // automatically rounded, so we need to use these rounded values when loading them. We could avoid this if - // we didn't store the color directly in the preset, and instead used the same value that the game stores in - // save files. Since we didn't do this from the beginning, we'll keeping using colors for now to keep presets - // backwards-compatibile. - public static Color[] RoundedColors; - - // The values will match the color values in the PawnSkinColors class. - public static float[] ColorValues; - - // Populates color arrays from PawnSkinColors.SkinColors. Uses an indirect method of getting the color values - // instead of just copying the color list via reflection. This will make it work better with mods that - // detour the color methods in that class--as long as they detour the GetSkinDataIndexOfMelanin() method. - public static void InitializeColors() { - List values = new List(); - - // Iterate all values from 0.0f to 1.0f, using increments of 0.01f, to get the left index for each value. - // Use this technique to construct a list of all of the indexes and their values. Once we have the list - // of indexes and their values, we can use the GetSkinColor() method to get the actual colors. - int currentIndex = 0; - values.Add(0.0f); - float f = 0.01f; - int counter = 1; - while (f < 1.0f) { - int result = Reflection.PawnSkinColors.GetSkinDataIndexOfMelanin(f); - if (result != currentIndex) { - currentIndex = result; - values.Add(f); - } - counter++; - double d = (double)counter / 100.0; - f = (float)d; - } - values.Add(1.0f); - - // Allocate the arrays and fill them with the correct values. - int length = values.Count; - Colors = new Color[length]; - ColorValues = new float[length]; - RoundedColors = new Color[length]; - for (int i = 0; i < length; i++) { - float v = values[i]; - Color color = PawnSkinColors.GetSkinColor(v); - Colors[i] = color; - RoundedColors[i] = color; - RoundedColors[i].r = (float)Math.Round(color.r, 3); - RoundedColors[i].g = (float)Math.Round(color.g, 3); - RoundedColors[i].b = (float)Math.Round(color.b, 3); - RoundedColors[i].a = (float)Math.Round(color.a, 3); - ColorValues[i] = v; - //Logger.Debug("Color added: (" + color.r + ", " + color.g + ", " + color.b + ")"); - } - } - - public static float GetRelativeLerpValue(float value) { - int leftIndex = GetLeftIndexForValue(value); - if (leftIndex == Colors.Length - 1) { - return 0.0f; - } - - int rightIndex = leftIndex + 1; - float t = Mathf.InverseLerp(ColorValues[leftIndex], ColorValues[rightIndex], value); - return t; - } - - public static float GetValueFromRelativeLerp(int leftIndex, float lerp) { - if (leftIndex >= Colors.Length - 1) { - return 1.0f; - } - else if (leftIndex < 0) { - return 0.0f; - } - - int rightIndex = leftIndex + 1; - float result = Mathf.Lerp(ColorValues[leftIndex], ColorValues[rightIndex], lerp); - - return result; - } - - public static int GetLeftIndexForValue(float value) { - int result = 0; - for (int i = 0; i < Colors.Length; i++) { - if (value < ColorValues[i]) { - break; - } - result = i; - } - return result; - } - - private static bool Between(Color color, Color a, Color b) { - if (color.r >= a.r && color.r <= b.r || color.r <= a.r && color.r >= b.r) { - if (color.g >= a.g && color.g <= b.g || color.g <= a.g && color.g >= b.g) { - if (color.b >= a.b && color.b <= b.b || color.b <= a.b && color.b >= b.b) { - return true; - } - } - } - return false; - } - - public static float FindMelaninValueFromColor(Color color) { - int colorCount = Colors.Length; - for (int i = 0; i < colorCount - 1; i++) { - Color a = RoundedColors[i]; - Color b = RoundedColors[i + 1]; - bool between = Between(color, a, b); - if (!between) { - continue; - } - Color c = a; - Color d = b; - bool ignorer = false; - bool ignoreg = false; - bool ignoreb = false; - if (c.r == d.r) { - ignorer = true; - } - if (c.g == d.g) { - ignoreg = true; - } - if (c.b == d.b) { - ignoreb = true; - } - float tr = (color.r - c.r) / (d.r - c.r); - float tg = (color.g - c.g) / (d.g - c.g); - float tb = (color.b - c.b) / (d.b - c.b); - bool invalid = false; - float count = 0.0f; - float total = 0.0f; - if (!ignorer) { - if (tr < 0.0f && tr > 1.0f) { - invalid = true; - } - else { - count += 1.0f; - total += tr; - } - } - if (!ignoreg) { - if (tg < 0.0f && tg > 1.0f) { - invalid = true; - } - else { - count += 1.0f; - total += tg; - } - } - if (!ignoreb) { - if (tb < 0.0f && tb > 1.0f) { - invalid = true; - } - else { - count += 1.0f; - total += tb; - } - } - if (invalid) { - continue; - } - float t = count > 0 ? total / count : 0; - float result = GetValueFromRelativeLerp(i, t); - - return result; - } - Logger.Warning("Could not find a valid matching value for the saved Color"); - return 0.5f; - } - } -} - diff --git a/Source/PawnCompRules.cs b/Source/PawnCompRules.cs index 3e0873a..9cc8f93 100644 --- a/Source/PawnCompRules.cs +++ b/Source/PawnCompRules.cs @@ -181,17 +181,17 @@ public static void RemoveDefaultFacialStuff(Pawn pawn, Dictionary compLookup, HashSet savedComps) { // If the pawn was saved when vanilla hair expanded was not enabled, but it was enabled when they load the pawn, // then they may end up with a beard by default. This post-load action clears out that default beard. - if (!savedComps.Contains("AlienRace.AlienPartGenerator+AlienComp")) { - if (compLookup.TryGetValue("AlienRace.AlienPartGenerator+AlienComp", out ThingComp c)) { - string crownType = ReflectionUtil.GetFieldValue(c, "crownType"); - if (crownType != null) { - CustomHeadType headType = pawn.HeadType; - if (headType != null && headType.AlienCrownType != crownType) { - ReflectionUtil.SetFieldValue(c, "crownType", null); - } - } - } - } + //if (!savedComps.Contains("AlienRace.AlienPartGenerator+AlienComp")) { + // if (compLookup.TryGetValue("AlienRace.AlienPartGenerator+AlienComp", out ThingComp c)) { + // string crownType = ReflectionUtil.GetFieldValue(c, "crownType"); + // if (crownType != null) { + // CustomHeadType headType = pawn.HeadType; + // if (headType != null && headType.AlienCrownType != crownType) { + // ReflectionUtil.SetFieldValue(c, "crownType", null); + // } + // } + // } + //} } } diff --git a/Source/PawnGenerationRequestWrapper.cs b/Source/PawnGenerationRequestWrapper.cs index c062966..6c34864 100644 --- a/Source/PawnGenerationRequestWrapper.cs +++ b/Source/PawnGenerationRequestWrapper.cs @@ -9,157 +9,122 @@ using Verse.Sound; namespace EdB.PrepareCarefully { - class PawnGenerationRequestWrapper { - private PawnKindDef kindDef = Faction.OfPlayer.def.basicMemberKind; - private Faction faction = Faction.OfPlayer; - private PawnGenerationContext context = PawnGenerationContext.PlayerStarter; - private float? fixedBiologicalAge = null; - private float? fixedChronologicalAge = null; - private Gender? fixedGender = null; - private bool worldPawnFactionDoesntMatter = false; - private bool mustBeCapableOfViolence = false; - private Ideo fixedIdeology = null; + public class PawnGenerationRequestWrapper { + public PawnKindDef KindDef { get; set; } = Faction.OfPlayer.def.basicMemberKind; + public Faction Faction { get; set; } = Faction.OfPlayer; + public PawnGenerationContext Context { get; set; } = PawnGenerationContext.PlayerStarter; + public float? FixedBiologicalAge { get; set; } = null; + public float? FixedChronologicalAge { get; set; } = null; + public Gender? FixedGender { get; set; } = null; + public bool WorldPawnFactionDoesntMatter { get; set; } = false; + public bool MustBeCapableOfViolence { get; set; } = false; + public Ideo FixedIdeology { get; set; } = null; + public XenotypeDef ForcedXenotype { get; set; } = null; + public CustomXenotype ForcedCustomXenotype { get; set; } = null; + public List AllowedXenotypes { get; set; } = null; + public float ForceBaselinerChance { get; set; } = 0f; + public IEnumerable ForcedTraits { get; set; } = Enumerable.Empty(); + public DevelopmentalStage DevelopmentalStage { get; set; } = DevelopmentalStage.Adult; + public List ForcedXenogenes { get; set; } = null; + public List ForcedEndogenes { get; set; } = null; + public string FixedLastName { get; set; } = null; + public string FixedBirthName { get; set; } = null; + public RoyalTitleDef FixedTitle { get; set; } = null; + public bool AllowDowned { get; set; } = false; public PawnGenerationRequestWrapper() { } private PawnGenerationRequest CreateRequest() { - //public PawnGenerationRequest ( - - //string fixedBirthName = null, - //RoyalTitleDef fixedTitle = null) - - /* - * PawnKindDef kind - * Faction faction = null - * PawnGenerationContext context = PawnGenerationContext.NonPlayer - * int tile = -1 - * bool forceGenerateNewPawn = false - * bool newborn = false - * bool allowDead = false - * bool allowDowned = false - * bool canGeneratePawnRelations = true - * bool mustBeCapableOfViolence = false - * float colonistRelationChanceFactor = 1 - * bool forceAddFreeWarmLayerIfNeeded = false - * bool allowGay = true - * bool allowFood = true - * bool allowAddictions = true - * bool inhabitant = false - * bool certainlyBeenInCryptosleep = false - * bool forceRedressWorldPawnIfFormerColonist = false - * bool worldPawnFactionDoesntMatter = false - * float biocodeWeaponChance = 0 - * float biocodeApparelChance = 0 - * Pawn extraPawnForExtraRelationChance = null - * float relationWithExtraPawnChanceFactor = 1 - * Predicate validatorPreGear = null - * Predicate validatorPostGear = null - * IEnumerable forcedTraits = null - * IEnumerable prohibitedTraits = null - * float? minChanceToRedressWorldPawn = null - * float? fixedBiologicalAge = null - * float? fixedChronologicalAge = null - * Gender? fixedGender = null - * float? fixedMelanin = null - * string fixedLastName = null - * string fixedBirthName = null - * RoyalTitleDef fixedTitle = null - * Ideo fixedIdeo = null - * bool forceNoIdeo = false - * bool forceNoBackstory = false); - */ - + // TODO: Should dynamically look at all of the life stages in the developmental stage to see if they have any "always downed" life stages like the "baby" life stage. + bool allowDowned = AllowDowned; + if (DevelopmentalStage == DevelopmentalStage.Baby) { + allowDowned = true; + } + var dedupedForcedXenogenes = RemoveDuplicateXenogenes(ForcedXenogenes); return new PawnGenerationRequest( - kindDef, // PawnKindDef kind - faction, // Faction faction = null - context, // PawnGenerationContext context = PawnGenerationContext.NonPlayer + KindDef, //PawnKindDef kind, + Faction, //Faction faction = null, + Context, //PawnGenerationContext context = PawnGenerationContext.NonPlayer, -1, //int tile = -1, true, //bool forceGenerateNewPawn = false, - false, //bool newborn = false, false, //bool allowDead = false, - false, //bool allowDowned = false, + allowDowned, //bool allowDowned = false, false, //bool canGeneratePawnRelations = true, - mustBeCapableOfViolence, //bool mustBeCapableOfViolence = false, + MustBeCapableOfViolence, //bool mustBeCapableOfViolence = false, 0f, //float colonistRelationChanceFactor = 1f, false, //bool forceAddFreeWarmLayerIfNeeded = false, true, //bool allowGay = true, + false, //bool allowPregnant = false, false, //bool allowFood = true, - false, //bool allowAddictions = true, - false, // bool inhabitant = false - false, // bool certainlyBeenInCryptosleep = false - false, // bool forceRedressWorldPawnIfFormerColonist = false - worldPawnFactionDoesntMatter, // bool worldPawnFactionDoesntMatter = false - 0f, //float biocodeWeaponChance = 0f, + false, //bool allowAddictions = true, + false, //bool inhabitant = false, + false, //bool certainlyBeenInCryptosleep = false, + false, //bool forceRedressWorldPawnIfFormerColonist = false, + WorldPawnFactionDoesntMatter, //bool worldPawnFactionDoesntMatter = false, + 0f, //float biocodeWeaponChance = 0f, 0f, //float biocodeApparelChance = 0f, - null, //Pawn extraPawnForExtraRelationChance = null, - 1f, //float relationWithExtraPawnChanceFactor = 1f, - null, // Predicate < Pawn > validatorPreGear = null - null, // Predicate < Pawn > validatorPostGear = null - Enumerable.Empty(), //IEnumerable forcedTraits = null, - Enumerable.Empty(), //IEnumerable prohibitedTraits = null, - null, // float ? minChanceToRedressWorldPawn = null - fixedBiologicalAge, // float ? fixedBiologicalAge = null - fixedChronologicalAge, // float ? fixedChronologicalAge = null - fixedGender, // Gender ? fixedGender = null - null, // float ? fixedMelanin = null - null, // string fixedLastName = null - null, //string fixedBirthName = null, - null, //RoyalTitleDef fixedTitle = null - fixedIdeology, //Ideo fixedIdeo = null - false, //bool forceNoIdeo = false - false //bool forceNoBackstory = false - ) { - ForbidAnyTitle = true - }; + null, //Pawn extraPawnForExtraRelationChance = null, + 1f, //float relationWithExtraPawnChanceFactor = 1f, + null, //Predicate < Pawn > validatorPreGear = null, + null, //Predicate < Pawn > validatorPostGear = null, + ForcedTraits, //IEnumerable < TraitDef > forcedTraits = null, + Enumerable.Empty(), //IEnumerable < TraitDef > prohibitedTraits = null, + null, //float ? minChanceToRedressWorldPawn = null, + FixedBiologicalAge, //float ? fixedBiologicalAge = null, + FixedChronologicalAge, //float ? fixedChronologicalAge = null, + FixedGender, //Gender ? fixedGender = null, + FixedLastName, //string fixedLastName = null, + FixedBirthName, //string fixedBirthName = null, + FixedTitle, //RoyalTitleDef fixedTitle = null, + null, //Ideo fixedIdeo = null, + false, //bool forceNoIdeo = false, + false, //bool forceNoBackstory = false, + true, //bool forbidAnyTitle = false, + false, //bool forceDead = false, + dedupedForcedXenogenes, //List < GeneDef > forcedXenogenes = null, + ForcedEndogenes, //List < GeneDef > forcedEndogenes = null, + ForcedXenotype, //XenotypeDef forcedXenotype = null, + ForcedCustomXenotype, //CustomXenotype forcedCustomXenotype = null, + AllowedXenotypes, //List < XenotypeDef > allowedXenotypes = null, + ForceBaselinerChance, //float forceBaselinerChance = 0f, + DevelopmentalStage, //DevelopmentalStage developmentalStages = DevelopmentalStage.Adult, + null, //Func < XenotypeDef, PawnKindDef > pawnKindDefGetter = null, + null, //FloatRange ? excludeBiologicalAgeRange = null, + null, //FloatRange ? biologicalAgeRange = null, + false //bool forceRecruitable = false + ); } public PawnGenerationRequest Request { get { return CreateRequest(); } } - public PawnKindDef KindDef { - set { - kindDef = value; - } - } - public Faction Faction { - set { - faction = value; - } - } - public PawnGenerationContext Context { - set { - context = value; - } - } - public bool WorldPawnFactionDoesntMatter { - set { - worldPawnFactionDoesntMatter = value; - } - } - public float? FixedBiologicalAge { - set { - fixedBiologicalAge = value; - } - } - public float? FixedChronologicalAge { - set { - fixedChronologicalAge = value; - } - } - public Gender? FixedGender { - set { - fixedGender = value; + + // If the generation request specifies a forced xenotype and force xenogenes, the default pawn generator will not de-duplicate + // the genes and will instead add multiple copies. We de-dupe them here by removing duplicates from the forced xenogenes before + // we do the generation. + // Note that the default pawn generator does de-duplicate endogenes. + protected List RemoveDuplicateXenogenes(List forcedGenes) { + if (forcedGenes == null || forcedGenes.Count == 0 || (ForcedXenotype == null && ForcedCustomXenotype == null)) { + return forcedGenes; } - } - public bool MustBeCapableOfViolence { - set { - mustBeCapableOfViolence = value; + HashSet geneSet = new HashSet(forcedGenes); + List genesToRemove = new List(); + Logger.Debug(" Xenotype " + ForcedXenotype?.defName + " is inheritable = " + ForcedXenotype?.inheritable); + if (ForcedXenotype != null && !ForcedXenotype.inheritable) { + foreach (var g in ForcedXenotype.genes) { + if (geneSet.Contains(g)) { + genesToRemove.Add(g); + } + } } - } - public Ideo FixedIdeology { - set { - fixedIdeology = value; + if (ForcedCustomXenotype != null && !ForcedCustomXenotype.inheritable) { + foreach (var g in ForcedCustomXenotype.genes) { + if (geneSet.Contains(g)) { + genesToRemove.Add(g); + } + } } + return new List(forcedGenes.Where(g => !genesToRemove.Contains(g))); } } } diff --git a/Source/PawnLayerBody.cs b/Source/PawnLayerBody.cs index 040642f..67dbdd9 100644 --- a/Source/PawnLayerBody.cs +++ b/Source/PawnLayerBody.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; diff --git a/Source/PawnLayerHead.cs b/Source/PawnLayerHead.cs index 9d3b845..8eb89a7 100644 --- a/Source/PawnLayerHead.cs +++ b/Source/PawnLayerHead.cs @@ -23,7 +23,7 @@ public override List Options { public override ColorSelectorType ColorSelectorType { get { - return ColorSelectorType.RGB; + return ColorSelectorType.Skin; } } diff --git a/Source/PawnLayerOption.cs b/Source/PawnLayerOption.cs index 8176a96..77cf3e5 100644 --- a/Source/PawnLayerOption.cs +++ b/Source/PawnLayerOption.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -9,5 +9,6 @@ public abstract string Label { get; set; } + public virtual bool Selectable { get; set; } = true; } } diff --git a/Source/PawnLayerOptionBody.cs b/Source/PawnLayerOptionBody.cs index 09ee766..ab3ead3 100644 --- a/Source/PawnLayerOptionBody.cs +++ b/Source/PawnLayerOptionBody.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; diff --git a/Source/PawnLayerOptionHead.cs b/Source/PawnLayerOptionHead.cs index 3a7cb9c..ca7179d 100644 --- a/Source/PawnLayerOptionHead.cs +++ b/Source/PawnLayerOptionHead.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -7,17 +7,25 @@ namespace EdB.PrepareCarefully { public class PawnLayerOptionHead : PawnLayerOption { + private string label; + private HeadTypeDef headType; public override string Label { get { - return HeadType.Label; + return label; } set { throw new NotImplementedException(); } } - public CustomHeadType HeadType { - get; - set; + public HeadTypeDef HeadType { + get { + return headType; + } + set { + headType = value; + // TODO: Better if we were setting the names when creating the options in the provider + label = PrepareCarefully.Instance.Providers.HeadTypes.GetHeadTypeLabel(headType); + } } } } diff --git a/Source/PrepareCarefully.cs b/Source/PrepareCarefully.cs index c218615..bf12be5 100644 --- a/Source/PrepareCarefully.cs +++ b/Source/PrepareCarefully.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reflection; using Verse; +using static RimWorld.ColonistBar; namespace EdB.PrepareCarefully { @@ -141,7 +142,6 @@ public void Initialize() { Textures.Reset(); Clear(); InitializeProviders(); - PawnColorUtils.InitializeColors(); InitializePawns(); InitializeRelationshipManager(this.pawns); InitializeDefaultEquipment(); @@ -186,8 +186,6 @@ protected void InitializeProviders() { Providers.Titles = new ProviderTitles(); } - // TODO: - // Think about whether or not this is the best approach. Might need to do a bug report for the vanilla game? // The tribal scenario adds a weapon with an invalid thing/stuff combination (jade knife). The // knife ThingDef should allow the jade material, but it does not. We need this workaround to // add the normally disallowed equipment to our equipment database. @@ -290,6 +288,28 @@ record = AddNonStandardScenarioEquipmentEntry(key); } } + // Go through starting possessions + foreach (var pair in Verse.Find.GameInitData.startingPossessions) { + foreach (var e in pair.Value) { + int count = e.Count; + ThingDef thingDef = e.ThingDef; + ThingDef stuffDef = null; + EquipmentKey key = new EquipmentKey(thingDef); + EquipmentRecord entry = equipmentDatabase.LookupEquipmentRecord(key); + if (entry == null) { + entry = AddNonStandardScenarioEquipmentEntry(key); + } + if (entry != null) { + AddEquipment(entry, count); + } + else { + Logger.Warning(String.Format("Couldn't initialize all scenario equipment. Didn't find an equipment entry for {0} ({1})", + thingDef, stuffDef != null ? stuffDef.defName : "no material")); + } + } + + } + //index = 0; //foreach (ScenPart part in Verse.Find.Scenario.AllParts) { // Logger.Debug(String.Format("[{0}] Replaced? {1}: {2} {3}", index, ReplacedScenarioParts.Contains(part), part.Label, String.Join(", ", part.GetSummaryListEntries("PlayerStartsWith")))); @@ -349,10 +369,11 @@ public void ClearPawns() { pawns.Clear(); } public void AddPawn(CustomPawn customPawn) { - PreloadPawnEquipment(customPawn.Pawn); + PreloadPawnEquipment(customPawn); pawns.Add(customPawn); } - protected void PreloadPawnEquipment(Pawn pawn) { + protected void PreloadPawnEquipment(CustomPawn customPawn) { + Pawn pawn = customPawn.Pawn; if (pawn.equipment != null) { foreach (var e in pawn.equipment.AllEquipmentListForReading) { if (e.Stuff != null) { @@ -361,6 +382,27 @@ protected void PreloadPawnEquipment(Pawn pawn) { equipmentDatabase.PreloadDefinition(e.def); } } + if (pawn.inventory != null) { + foreach (var e in pawn.inventory.GetDirectlyHeldThings()) { + if (e.Stuff != null) { + equipmentDatabase.PreloadDefinition(e.Stuff); + } + equipmentDatabase.PreloadDefinition(e.def); + + // If the pawn is a colonist, add their inventory to the colony equipment + if (customPawn.Type == CustomPawnType.Colonist) { + EquipmentRecord entry = equipmentDatabase.LookupEquipmentRecord(new EquipmentKey(e.def, e.Stuff)); + if (entry != null) { + if (e.stackCount > 1) { + AddEquipment(entry, e.stackCount); + } + else { + AddEquipment(entry); + } + } + } + } + } if (pawn.apparel != null) { foreach (var e in pawn.apparel.WornApparel) { if (e.Stuff != null) { diff --git a/Source/ProviderAgeLimits.cs b/Source/ProviderAgeLimits.cs index 6aead9f..81b589e 100644 --- a/Source/ProviderAgeLimits.cs +++ b/Source/ProviderAgeLimits.cs @@ -3,8 +3,10 @@ using System.Linq; using System.Reflection; using System.Text; +using EdB.PrepareCarefully.HarmonyPatches; using UnityEngine; using Verse; +using Verse.Noise; namespace EdB.PrepareCarefully { @@ -14,50 +16,100 @@ public class ProviderAgeLimits { private Dictionary minAgeLookup = new Dictionary(); private Dictionary maxAgeLookup = new Dictionary(); + private Dictionary, int> minAgeTicksLookup = new Dictionary, int>(); + private Dictionary, int> maxAgeTicksLookup = new Dictionary, int>(); + public int MinAgeForPawn(Pawn pawn) { - if (!minAgeLookup.TryGetValue(pawn.def, out int age)) { - SimpleCurve simpleCurve = pawn.def.race.ageGenerationCurve; - if (simpleCurve == null) { - Logger.Warning("No age generation curve defined for " + pawn.def.defName + ". Using default age generation curve to determine minimum age."); - simpleCurve = DefaultAgeGenerationCurve; - if (simpleCurve == null) { - Logger.Warning("Failed to get default age generation curve. Using default minimum age of " + DEFAULT_MIN_AGE); - age = DEFAULT_MIN_AGE; - } - else { - age = Mathf.CeilToInt(pawn.def.race.lifeExpectancy * simpleCurve.First().x); + // TODO: This probably doesn't work for alien races + if (!minAgeTicksLookup.TryGetValue(ValueTuple.Create(pawn.def, pawn.DevelopmentalStage), out int years)) { + float min = float.MaxValue; + foreach (var a in pawn.def.race.lifeStageAges) { + if (a.def.developmentalStage == pawn.DevelopmentalStage && a.minAge < min) { + min = a.minAge; } } - else { - CurvePoint point = simpleCurve.First(); - age = (int)point.x; + if (min == float.MaxValue) { + min = 0; } - minAgeLookup.Add(pawn.def, age); + int value = Mathf.FloorToInt(min); + minAgeTicksLookup.Add(ValueTuple.Create(pawn.def, pawn.DevelopmentalStage), value); + //Logger.Debug("Min age for " + pawn.DevelopmentalStage + " = " + value); + return value; + } + else { + return years; } - return age; + + //if (!minAgeLookup.TryGetValue(pawn.def, out int age)) { + // SimpleCurve simpleCurve = pawn.def.race.ageGenerationCurve; + // if (simpleCurve == null) { + // Logger.Warning("No age generation curve defined for " + pawn.def.defName + ". Using default age generation curve to determine minimum age."); + // simpleCurve = DefaultAgeGenerationCurve; + // if (simpleCurve == null) { + // Logger.Warning("Failed to get default age generation curve. Using default minimum age of " + DEFAULT_MIN_AGE); + // age = DEFAULT_MIN_AGE; + // } + // else { + // age = Mathf.CeilToInt(pawn.def.race.lifeExpectancy * simpleCurve.First().x); + // } + // } + // else { + // CurvePoint point = simpleCurve.First(); + // age = (int)point.x; + // } + // minAgeLookup.Add(pawn.def, age); + //} + //return age; } public int MaxAgeForPawn(Pawn pawn) { - if (!maxAgeLookup.TryGetValue(pawn.def, out int age)) { - SimpleCurve simpleCurve = pawn.def.race.ageGenerationCurve; - if (simpleCurve == null) { - Logger.Warning("No age generation curve defined for " + pawn.def.defName + ". Using default age generation curve to determine maximum age."); - simpleCurve = DefaultAgeGenerationCurve; - if (simpleCurve == null) { - Logger.Warning("Failed to get default age generation curve. Using default maximum age of " + DEFAULT_MAX_AGE); - age = DEFAULT_MAX_AGE; - } - else { - age = Mathf.CeilToInt(pawn.def.race.lifeExpectancy * simpleCurve.Last().x); + // TODO: This probably doesn't work for alien races + if (!maxAgeTicksLookup.TryGetValue(ValueTuple.Create(pawn.def, pawn.DevelopmentalStage), out int years)) { + float max = float.MinValue; + int count = pawn.def.race.lifeStageAges.Count; + float previousValue = float.MinValue; + for(int i=count-1; i>=0; i--) { + var a = pawn.def.race.lifeStageAges[i]; + if (a.def.developmentalStage == pawn.DevelopmentalStage) { + max = previousValue; + break; } + previousValue = a.minAge; + } + if (max == float.MinValue) { + max = pawn.def.race.lifeExpectancy * 1.5f; } else { - CurvePoint point = simpleCurve.Last(); - age = (int)(point.x * 1.2f); + max--; } - maxAgeLookup.Add(pawn.def, age); + int value = Mathf.FloorToInt(max); + maxAgeTicksLookup.Add(ValueTuple.Create(pawn.def, pawn.DevelopmentalStage), value); + //Logger.Debug("Max age for " + pawn.DevelopmentalStage + " " + pawn.def.defName + " = " + value); + return value; + } + else { + return years; } - return age; + //if (!maxAgeLookup.TryGetValue(pawn.def, out int age)) { + // SimpleCurve simpleCurve = pawn.def.race.ageGenerationCurve; + // if (simpleCurve == null) { + // Logger.Warning("No age generation curve defined for " + pawn.def.defName + ". Using default age generation curve to determine maximum age."); + // simpleCurve = DefaultAgeGenerationCurve; + // if (simpleCurve == null) { + // Logger.Warning("Failed to get default age generation curve. Using default maximum age of " + DEFAULT_MAX_AGE); + // age = DEFAULT_MAX_AGE; + // } + // else { + // age = Mathf.CeilToInt(pawn.def.race.lifeExpectancy * simpleCurve.Last().x); + // } + // } + // else { + // CurvePoint point = simpleCurve.Last(); + // age = (int)(point.x * 1.2f); + // } + // maxAgeLookup.Add(pawn.def, age); + //} + //return age; } protected SimpleCurve DefaultAgeGenerationCurve { diff --git a/Source/ProviderAlienRaces.cs b/Source/ProviderAlienRaces.cs index e540a5c..012cc56 100644 --- a/Source/ProviderAlienRaces.cs +++ b/Source/ProviderAlienRaces.cs @@ -30,13 +30,18 @@ public AlienRace GetAlienRace(ThingDef def) { } else { if (IsAlienRace(def)) { + //Logger.Debug(def.defName + " is an alien race"); result = InitializeAlienRace(def); if (result != null) { lookup.Add(def, result); } + else { + Logger.Debug("Failed to initialize " + def.defName + " alien race"); + } return result; } else { + //Logger.Debug(def.defName + " is not an alien race"); return null; } } @@ -118,7 +123,6 @@ protected ColorGenerator FindSecondaryColorGenerator(ThingDef raceDef, object al protected ColorGenerator FindColorGenerator(ThingDef raceDef, object alienPartGeneratorObject, string channelName, string generatorFieldName) { object colorChannelsObject = GetFieldValue(raceDef, alienPartGeneratorObject, "colorChannels", true); if (colorChannelsObject == null) { - Logger.Warning("didn't find colorChannels field"); return null; } System.Collections.IList colorChannelList = colorChannelsObject as System.Collections.IList; @@ -136,25 +140,34 @@ protected ColorGenerator FindColorGenerator(ThingDef raceDef, object alienPartGe if (foundGenerator == null) { return null; } - return GetFieldValue(raceDef, foundGenerator, generatorFieldName, true) as ColorGenerator; + System.Collections.IList colorChannelGeneratorCategoryList = GetFieldValueAsCollection(raceDef, foundGenerator, "entries") as System.Collections.IList; + if (colorChannelGeneratorCategoryList == null || colorChannelGeneratorCategoryList.Count == 0) { + return null; + } + object colorChannelGeneratorCategory = colorChannelGeneratorCategoryList[0]; + return GetFieldValue(raceDef, colorChannelGeneratorCategory, generatorFieldName, true) as ColorGenerator; } protected AlienRace InitializeAlienRace(ThingDef raceDef) { try { - object alienRaceObject = GetFieldValue(raceDef, raceDef, "alienRace"); - if (alienRaceObject == null) { + object alienSettingsObject = GetFieldValue(raceDef, raceDef, "alienRace"); + if (alienSettingsObject == null) { + Logger.Debug("Didn't find AlienSettings object in ThingDef_AlienRace.alienRace field"); return null; } - object generalSettingsObject = GetFieldValue(raceDef, alienRaceObject, "generalSettings"); + object generalSettingsObject = GetFieldValue(raceDef, alienSettingsObject, "generalSettings"); if (generalSettingsObject == null) { + Logger.Debug("Didn't find GeneralSettings object in ThingDef_AlienRace.AlienSettings.generalSettings field"); return null; } object alienPartGeneratorObject = GetFieldValue(raceDef, generalSettingsObject, "alienPartGenerator"); if (alienPartGeneratorObject == null) { + Logger.Debug("Didn't find AlienPartGenerator object in GeneralSettings.alienPartGenerator field"); return null; } - System.Collections.ICollection graphicPathsCollection = GetFieldValueAsCollection(raceDef, alienRaceObject, "graphicPaths"); - if (graphicPathsCollection == null) { + object graphicsPathsObject = GetFieldValue(raceDef, alienSettingsObject, "graphicPaths"); + if (graphicsPathsObject == null) { + Logger.Debug("Didn't find GraphicsPaths object in ThingDef_AlienRace.graphicPaths field"); return null; } @@ -170,6 +183,7 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { } */ + // We have enough to start putting together the result object, so we instantiate it now. AlienRace result = new AlienRace(); result.ThingDef = raceDef; @@ -183,7 +197,7 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { result.MinAgeForAdulthood = minAgeForAdulthood; // Get the list of body types. - System.Collections.ICollection alienBodyTypesCollection = GetFieldValueAsCollection(raceDef, alienPartGeneratorObject, "alienbodytypes"); + System.Collections.ICollection alienBodyTypesCollection = GetFieldValueAsCollection(raceDef, alienPartGeneratorObject, "bodyTypes"); if (alienBodyTypesCollection == null) { return null; } @@ -196,56 +210,25 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { } } } - else { - //Logger.Debug(" none"); - } //Logger.Debug($"Body types for alien race {raceDef.defName}: {string.Join(", ", bodyTypes.Select(b => b.defName + ", " + b.LabelCap))}"); result.BodyTypes = bodyTypes; - // Determine if the alien races uses gender-specific heads. - bool? useGenderedHeads = GetFieldValueAsBool(raceDef, alienPartGeneratorObject, "useGenderedHeads"); - if (useGenderedHeads == null) { + // Get the list of head types. + System.Collections.ICollection alienHeadTypesCollection = GetFieldValueAsCollection(raceDef, alienPartGeneratorObject, "headTypes"); + if (alienHeadTypesCollection == null) { + Logger.Debug("Didn't find List object in AlienPartGenerator.headTypes field"); return null; } - result.GenderSpecificHeads = useGenderedHeads.Value; - - // Get the list of crown types. - System.Collections.ICollection alienCrownTypesCollection = GetFieldValueAsCollection(raceDef, alienPartGeneratorObject, "aliencrowntypes"); - if (alienCrownTypesCollection == null) { - return null; - } - List crownTypes = new List(); - //Logger.Debug("Crown Types for " + raceDef.defName + ":"); - if (alienCrownTypesCollection.Count > 0) { - foreach (object o in alienCrownTypesCollection) { - string crownTypeString = o as string; - if (crownTypeString != null) { - crownTypes.Add(crownTypeString); - //Logger.Debug(" " + crownTypeString); - } - } - } - result.CrownTypes = crownTypes; - - // Go through the graphics paths and find the heads path. - // TODO: What is this? - string graphicsPathForHeads = null; - string graphicsPathForBodyTypes = null; - foreach (var graphicsPath in graphicPathsCollection) { - System.Collections.ICollection lifeStageCollection = GetFieldValueAsCollection(raceDef, graphicsPath, "lifeStageDefs"); - if (lifeStageCollection == null || lifeStageCollection.Count == 0) { - string headsPath = GetFieldValueAsString(raceDef, graphicsPath, "head"); - string bodyTypesPath = GetFieldValueAsString(raceDef, graphicsPath, "body"); - if (headsPath != null) { - graphicsPathForHeads = headsPath; - } - if (bodyTypesPath != null) { - graphicsPathForBodyTypes = bodyTypesPath; + List headTypes = new List(); + if (alienHeadTypesCollection.Count > 0) { + foreach (object o in alienHeadTypesCollection) { + if (o.GetType() == typeof(HeadTypeDef)) { + HeadTypeDef def = o as HeadTypeDef; + headTypes.Add((HeadTypeDef)o); } } } - result.GraphicsPathForHeads = graphicsPathForHeads; - result.GraphicsPathForBodyTypes = graphicsPathForBodyTypes; + result.HeadTypes = headTypes; // Figure out colors. ColorGenerator primaryGenerator = FindPrimarySkinColorGenerator(raceDef, alienPartGeneratorObject); @@ -275,7 +258,7 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { } // Style settings - object styleSettingsValue = GetFieldValue(raceDef, alienRaceObject, "styleSettings", true); + object styleSettingsValue = GetFieldValue(raceDef, alienSettingsObject, "styleSettings", true); result.HasHair = true; result.HasBeards = true; @@ -333,7 +316,7 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { } // Apparel properties. - object restrictionSettingsValue = GetFieldValue(raceDef, alienRaceObject, "raceRestriction", true); + object restrictionSettingsValue = GetFieldValue(raceDef, alienSettingsObject, "raceRestriction", true); result.RaceSpecificApparelOnly = false; result.RaceSpecificApparel = new HashSet(); result.AllowedApparel = new HashSet(); @@ -406,7 +389,7 @@ protected AlienRace InitializeAlienRace(ThingDef raceDef) { return result; } catch (Exception e) { - throw new InitializationException("Failed to initialize an alien race: " + raceDef.defName, e); + throw new InitializationException("Exception when trying to initialize an alien race: " + raceDef.defName, e); } } protected string ParseAddonName(string path) { diff --git a/Source/ProviderApparel.cs b/Source/ProviderApparel.cs index 7190036..b9504e0 100644 --- a/Source/ProviderApparel.cs +++ b/Source/ProviderApparel.cs @@ -10,38 +10,38 @@ namespace EdB.PrepareCarefully { public class ProviderApparel { - protected Dictionary apparelLookup = new Dictionary(); + protected Dictionary, OptionsApparel> apparelLookup = new Dictionary, OptionsApparel>(); protected OptionsApparel humanlikeApparel; protected OptionsApparel noApparel = new OptionsApparel(); public ProviderAlienRaces AlienRaceProvider { get; set; } public List GetApparel(CustomPawn pawn, PawnLayer layer) { - return GetApparel(pawn.Pawn.def, layer); + return GetApparel(pawn.Pawn.def, pawn.Pawn.DevelopmentalStage, layer); } - public List GetApparel(ThingDef raceDef, PawnLayer layer) { - OptionsApparel apparel = GetApparelForRace(raceDef); + public List GetApparel(ThingDef raceDef, DevelopmentalStage stage, PawnLayer layer) { + OptionsApparel apparel = GetApparelForRaceAndDevelopmentalStage(raceDef, stage); return apparel.GetApparel(layer); } public OptionsApparel GetApparelForRace(CustomPawn pawn) { - return GetApparelForRace(pawn.Pawn.def); + return GetApparelForRaceAndDevelopmentalStage(pawn.Pawn.def, pawn.Pawn.DevelopmentalStage); } - public OptionsApparel GetApparelForRace(ThingDef raceDef) { + public OptionsApparel GetApparelForRaceAndDevelopmentalStage(ThingDef raceDef, DevelopmentalStage stage) { OptionsApparel apparel; - if (apparelLookup.TryGetValue(raceDef, out apparel)) { + if (apparelLookup.TryGetValue(ValueTuple.Create(raceDef, stage), out apparel)) { return apparel; } - apparel = InitializeApparel(raceDef); + apparel = InitializeApparel(raceDef, stage); if (apparel == null) { if (raceDef != ThingDefOf.Human) { - return GetApparelForRace(ThingDefOf.Human); + return GetApparelForRaceAndDevelopmentalStage(ThingDefOf.Human, stage); } else { return null; } } else { - apparelLookup.Add(raceDef, apparel); + apparelLookup.Add(ValueTuple.Create(raceDef, stage), apparel); return apparel; } } @@ -65,10 +65,10 @@ protected void AddApparelToOptions(OptionsApparel options, ThingDef def) { options.Add(layer, def); } } - protected OptionsApparel InitializeApparel(ThingDef raceDef) { + protected OptionsApparel InitializeApparel(ThingDef raceDef, DevelopmentalStage stage) { AlienRace alienRace = AlienRaceProvider.GetAlienRace(raceDef); if (alienRace == null) { - return HumanlikeApparel; + return HumanlikeApparelForDevelopmentStage(stage); } OptionsApparel result = new OptionsApparel(); HashSet addedAlready = new HashSet(); @@ -82,7 +82,7 @@ protected OptionsApparel InitializeApparel(ThingDef raceDef) { // Even if we're only allowed to use race-specific apparel, we're also allowed to use anything in the allowed list. if (alienRace.RaceSpecificApparelOnly) { HashSet allowed = alienRace.AllowedApparel ?? new HashSet(); - foreach (var def in HumanlikeApparel.AllApparel ?? Enumerable.Empty()) { + foreach (var def in HumanlikeApparelForDevelopmentStage(stage).AllApparel ?? Enumerable.Empty()) { if (allowed.Contains(def.defName) && !addedAlready.Contains(def.defName)) { AddApparelToOptions(result, def); addedAlready.Add(def.defName); @@ -92,7 +92,7 @@ protected OptionsApparel InitializeApparel(ThingDef raceDef) { // Even if we're allowed to use more than just race-specific apparel, we can't use anything in the disallowed list. else { HashSet disallowed = alienRace.DisallowedApparel ?? new HashSet(); - foreach (var def in HumanlikeApparel.AllApparel ?? Enumerable.Empty()) { + foreach (var def in HumanlikeApparelForDevelopmentStage(stage).AllApparel ?? Enumerable.Empty()) { if (!addedAlready.Contains(def.defName) && !disallowed.Contains(def.defName)) { AddApparelToOptions(result, def); addedAlready.Add(def.defName); @@ -102,15 +102,15 @@ protected OptionsApparel InitializeApparel(ThingDef raceDef) { result.Sort(); return result; } - protected OptionsApparel HumanlikeApparel { - get { - if (humanlikeApparel == null) { - humanlikeApparel = InitializeHumanlikeApparel(); - } - return humanlikeApparel; + + protected OptionsApparel HumanlikeApparelForDevelopmentStage(DevelopmentalStage stage) { + if (apparelLookup.TryGetValue(ValueTuple.Create(ThingDefOf.Human, stage), out var options)) { + return options; } + return InitializeHumanlikeApparel(stage); } - protected OptionsApparel InitializeHumanlikeApparel() { + + protected OptionsApparel InitializeHumanlikeApparel(DevelopmentalStage stage) { HashSet nonHumanApparel = new HashSet(); IEnumerable alienRaces = DefDatabase.AllDefs.Where((ThingDef def) => { return def.race != null && ProviderAlienRaces.IsAlienRace(def); @@ -134,6 +134,9 @@ protected OptionsApparel InitializeHumanlikeApparel() { if (apparelDef.apparel == null) { continue; } + if (!apparelDef.apparel.developmentalStageFilter.Has(stage)) { + continue; + } if (!nonHumanApparel.Contains(apparelDef.defName)) { AddApparelToOptions(result, apparelDef); } diff --git a/Source/ProviderBackstories.cs b/Source/ProviderBackstories.cs index 3b8127a..cc47b82 100644 --- a/Source/ProviderBackstories.cs +++ b/Source/ProviderBackstories.cs @@ -5,55 +5,57 @@ using Verse; namespace EdB.PrepareCarefully { public class ProviderBackstories { - protected List childhoodBackstories = new List(); - protected List adulthoodBackstories = new List(); - protected List sortedChildhoodBackstories; - protected List sortedAdulthoodBackstories; + protected List childhoodBackstories = new List(); + protected List adulthoodBackstories = new List(); + protected List sortedChildhoodBackstories; + protected List sortedAdulthoodBackstories; - protected Dictionary> childhoodBackstoryLookup = new Dictionary>(); - protected Dictionary> adulthoodBackstoryLookup = new Dictionary>(); - protected Dictionary> backstoryHashSetLookup = new Dictionary>(); + protected Dictionary> childhoodBackstoryLookup = new Dictionary>(); + protected Dictionary> adulthoodBackstoryLookup = new Dictionary>(); + protected Dictionary> backstoryHashSetLookup = new Dictionary>(); - public List GetChildhoodBackstoriesForPawn(CustomPawn pawn) { + public List GetChildhoodBackstoriesForPawn(CustomPawn pawn) { return GetChildhoodBackstoriesForPawnKindDef(pawn.OriginalKindDef); } - public List GetAdulthoodBackstoriesForPawn(CustomPawn pawn) { + public List GetAdulthoodBackstoriesForPawn(CustomPawn pawn) { return GetAdulthoodBackstoriesForPawnKindDef(pawn.OriginalKindDef); } - public List GetChildhoodBackstoriesForPawnKindDef(PawnKindDef kindDef) { + public List GetChildhoodBackstoriesForPawnKindDef(PawnKindDef kindDef) { if (!backstoryHashSetLookup.ContainsKey(kindDef.defName)) { InitializeBackstoriesForPawnKind(kindDef); } return childhoodBackstoryLookup[kindDef.defName]; } - public List GetAdulthoodBackstoriesForPawnKindDef(PawnKindDef kindDef) { + public List GetAdulthoodBackstoriesForPawnKindDef(PawnKindDef kindDef) { if (!backstoryHashSetLookup.ContainsKey(kindDef.defName)) { InitializeBackstoriesForPawnKind(kindDef); } return adulthoodBackstoryLookup[kindDef.defName]; } - public HashSet BackstoriesForPawnKindDef(PawnKindDef kindDef) { + public HashSet BackstoriesForPawnKindDef(PawnKindDef kindDef) { if (!backstoryHashSetLookup.ContainsKey(kindDef.defName)) { InitializeBackstoriesForPawnKind(kindDef); } return backstoryHashSetLookup[kindDef.defName]; } - public List AllChildhookBackstories { + public List AllChildhookBackstories { get { return sortedChildhoodBackstories; } } - public List AllAdulthookBackstories { + public List AllAdulthookBackstories { get { return sortedAdulthoodBackstories; } } public ProviderBackstories() { // Go through all of the backstories and mark them as childhood or adult. - List backstories = BackstoryDatabase.allBackstories.Values.ToList(); - foreach (Backstory backstory in backstories) { + List backstories = DefDatabase.AllDefs.ToList(); + foreach (BackstoryDef backstory in backstories) { if (backstory.slot == BackstorySlot.Childhood) { - childhoodBackstories.Add(backstory); + if (!backstory.spawnCategories.Contains("Child") && !backstory.spawnCategories.Contains("Newborn")) { + childhoodBackstories.Add(backstory); + } } else { adulthoodBackstories.Add(backstory); @@ -61,15 +63,15 @@ public ProviderBackstories() { } // Create sorted versions of the backstory lists - sortedChildhoodBackstories = new List(childhoodBackstories); + sortedChildhoodBackstories = new List(childhoodBackstories); sortedChildhoodBackstories.Sort((b1, b2) => b1.TitleCapFor(Gender.Male).CompareTo(b2.TitleCapFor(Gender.Male))); - sortedAdulthoodBackstories = new List(adulthoodBackstories); + sortedAdulthoodBackstories = new List(adulthoodBackstories); sortedAdulthoodBackstories.Sort((b1, b2) => b1.TitleCapFor(Gender.Male).CompareTo(b2.TitleCapFor(Gender.Male))); } private void InitializeBackstoriesForPawnKind(PawnKindDef def) { HashSet categories = BackstoryCategoriesForPawnKindDef(def); - List childhood = BackstoryDatabase.allBackstories.Values.Where((b) => { + List childhood = DefDatabase.AllDefs.Where((b) => { if (b.slot != BackstorySlot.Childhood) { return false; } @@ -83,7 +85,7 @@ private void InitializeBackstoriesForPawnKind(PawnKindDef def) { childhood.Sort((b1, b2) => b1.TitleCapFor(Gender.Male).CompareTo(b2.TitleCapFor(Gender.Male))); childhoodBackstoryLookup[def.defName] = childhood; - List adulthood = BackstoryDatabase.allBackstories.Values.Where((b) => { + List adulthood = DefDatabase.AllDefs.Where((b) => { if (b.slot != BackstorySlot.Adulthood) { return false; } @@ -97,7 +99,7 @@ private void InitializeBackstoriesForPawnKind(PawnKindDef def) { adulthood.Sort((b1, b2) => b1.TitleCapFor(Gender.Male).CompareTo(b2.TitleCapFor(Gender.Male))); adulthoodBackstoryLookup[def.defName] = adulthood; - HashSet backstorySet = new HashSet(childhood); + HashSet backstorySet = new HashSet(childhood); backstorySet.AddRange(adulthood); this.backstoryHashSetLookup[def.defName] = backstorySet; } diff --git a/Source/ProviderBodyTypes.cs b/Source/ProviderBodyTypes.cs index 9a52afb..c0603d9 100644 --- a/Source/ProviderBodyTypes.cs +++ b/Source/ProviderBodyTypes.cs @@ -50,7 +50,7 @@ public string GetBodyTypeLabel(BodyTypeDef bodyType) { return label; } else { - return "EdB.PC.Pawn.BodyType.Unnamed".Translate(); + return bodyType.defName; } } else { @@ -123,62 +123,9 @@ protected OptionsBodyType InitializeAlienRaceBodyTypes(ThingDef def) { if (result.MaleBodyTypes.Count == 0 && result.FemaleBodyTypes.Count == 0) { result = InitializeNonModdedDefaultHumanlikeBodyTypes(); - //if (alienRace.GraphicsPathForBodyTypes != null && !defaultBodyTypesPaths.Contains(alienRace.GraphicsPathForBodyTypes)) { - // result = InitializeHumanlikeBodyTypes(); - // result.MaleBodyTypes = result.MaleBodyTypes.Where(d => ValidateBodyTypeForAlienRace(alienRace, d)).ToList(); - // result.FemaleBodyTypes = result.FemaleBodyTypes.Where(d => ValidateBodyTypeForAlienRace(alienRace, d)).ToList(); - //} - //else { - // result = InitializeNonModdedDefaultHumanlikeBodyTypes(); - //} } - /* - // TODO: Is this right? - // Was this trying to guard against mod developers only defining male and not female body types? - if (result.MaleBodyTypes.Count == 0 && result.FemaleBodyTypes.Count > 0) { - result.MaleBodyTypes = result.FemaleBodyTypes; - } - else if (result.FemaleBodyTypes.Count == 0 && result.MaleBodyTypes.Count > 0) { - result.FemaleBodyTypes = result.MaleBodyTypes; - } - - if (result.MaleBodyTypes.Count == 0 && result.FemaleBodyTypes.Count == 0) { - result = InitializeHumanlikeBodyTypes(); - } - */ - return result; } - - // TODO: Evaluate this. Disabled for now, but this method looks in the alien body graphics path for the specified - // body def. This was to try to guard against the addition of modded BodyTypeDefs that work for vanilla, but that - // the alien race mod maker has not provided a texture for. - /// Instead of calling this, we're just assigning only vanilla body type defs for alien races without explicit body types - /// and with a custom graphic path for body textures. - //protected bool ValidateBodyTypeForAlienRace(AlienRace race, BodyTypeDef def) { - // string path = race.GraphicsPathForBodyTypes + "/" + def.bodyNakedGraphicPath.Split('/').Last(); - // path = path.Replace("//", "/"); - // path += "_south"; - // Logger.Debug("ValidateBodyTypeForRace(" + race + ", " + def.defName + "), path = " + path); - // try { - // // TODO: Figure out which mod we're dealing with and only go through that content pack. - // List modsListForReading = LoadedModManager.RunningModsListForReading; - // for (int index = modsListForReading.Count - 1; index >= 0; --index) { - // ModContentPack pack = modsListForReading[index]; - // Logger.Debug("Looking for path in " + pack.Identifier); - // var contentHolder = pack.GetContentHolder(); - // if (contentHolder.contentList.ContainsKey(path)) { - // Logger.Warning("Found it"); - // return true; - // } - // } - // Logger.Debug("Didn't find it"); - // return false; - // } - // catch (Exception) { - // return false; - // } - //} } } diff --git a/Source/ProviderEquipment.cs b/Source/ProviderEquipment.cs index 55bacf6..3aa994a 100644 --- a/Source/ProviderEquipment.cs +++ b/Source/ProviderEquipment.cs @@ -1,10 +1,11 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; using Verse; namespace EdB.PrepareCarefully { public class ProviderEquipmentTypes { + protected List types = new List(); protected Dictionary> equipmentDictionary = new Dictionary>(); @@ -54,5 +55,6 @@ public IEnumerable AllEquipmentOfType(EquipmentType type) { return null; } } + } } diff --git a/Source/ProviderHair.cs b/Source/ProviderHair.cs index 3c3c622..74ff461 100644 --- a/Source/ProviderHair.cs +++ b/Source/ProviderHair.cs @@ -109,22 +109,6 @@ protected OptionsHair InitializeHumanlikeHairs() { // all hair def (both alien and non-alien) in the list of available hairs for non-aliens. // TODO: Implement filtering in the hair selection to make it easier to find appropriate hairs when there // are a lot of mods that add hairs. - /* - IEnumerable alienRaces = DefDatabase.AllDefs.Where((ThingDef def) => { - return def.race != null && ProviderAlienRaces.IsAlienRace(def); - }); - foreach (var alienRaceDef in alienRaces) { - AlienRace alienRace = AlienRaceProvider.GetAlienRace(alienRaceDef); - if (alienRace == null) { - continue; - } - if (alienRace.HairTags != null) { - foreach (var tag in alienRace.HairTags) { - nonHumanHairTags.Add(tag); - } - } - } - */ OptionsHair result = new OptionsHair(); foreach (HairDef hairDef in DefDatabase.AllDefs.Where((HairDef def) => { foreach (var tag in def.styleTags) { diff --git a/Source/ProviderHeadTypes.cs b/Source/ProviderHeadTypes.cs index 576b7aa..0501070 100644 --- a/Source/ProviderHeadTypes.cs +++ b/Source/ProviderHeadTypes.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Reflection; +using System.Reflection.Emit; using System.Text; using System.Text.RegularExpressions; using UnityEngine; @@ -12,23 +13,27 @@ namespace EdB.PrepareCarefully { public class ProviderHeadTypes { + protected List defs = new List(); protected List heads = new List(); protected List headPaths = new List(); - public Dictionary pathDictionary = new Dictionary(); + public Dictionary pathDictionary = new Dictionary(); protected Dictionary headTypeLookup = new Dictionary(); public ProviderHeadTypes() { } public ProviderAlienRaces AlienRaceProvider { get; set; } - public IEnumerable GetHeadTypes(CustomPawn pawn) { + public IEnumerable GetHeadTypes(CustomPawn pawn) { return GetHeadTypes(pawn.Pawn.def, pawn.Gender); } - public IEnumerable GetHeadTypes(ThingDef race, Gender gender) { + public IEnumerable GetHeadTypes(Pawn pawn) { + return GetHeadTypes(pawn.def, pawn.gender); + } + public IEnumerable GetHeadTypes(ThingDef race, Gender gender) { OptionsHeadType headTypes = GetHeadTypesForRace(race); return headTypes.GetHeadTypesForGender(gender); } - public CustomHeadType FindHeadTypeForPawn(Pawn pawn) { + public HeadTypeDef FindHeadTypeForPawn(Pawn pawn) { OptionsHeadType headTypes = GetHeadTypesForRace(pawn.def); var result = headTypes.FindHeadTypeForPawn(pawn); if (result == null) { @@ -36,15 +41,6 @@ public CustomHeadType FindHeadTypeForPawn(Pawn pawn) { } return result; } - public CustomHeadType FindHeadType(ThingDef race, string graphicsPath) { - OptionsHeadType headTypes = GetHeadTypesForRace(race); - //Logger.Debug("headTypes: \n" + String.Join("\n", headTypes.headTypes.ToList().ConvertAll(t => t.GraphicPath))); - return headTypes.FindHeadTypeByGraphicsPath(graphicsPath); - } - public CustomHeadType FindHeadTypeForGender(ThingDef race, CustomHeadType headType, Gender gender) { - OptionsHeadType headTypes = GetHeadTypesForRace(race); - return headTypes.FindHeadTypeForGender(headType, gender); - } protected OptionsHeadType GetHeadTypesForRace(ThingDef race) { if (!headTypeLookup.TryGetValue(race, out OptionsHeadType headTypes)) { headTypes = InitializeHeadTypes(race); @@ -82,171 +78,91 @@ protected OptionsHeadType InitializeHeadTypes(ThingDef race) { return result; } protected OptionsHeadType InitializeHumanHeadTypes() { - //Logger.Debug("InitializeHumanHeadTypes()"); - Reflection.GraphicDatabaseHeadRecords.BuildDatabaseIfNecessary(); - string[] headsFolderPaths = new string[] { - "Things/Pawn/Humanlike/Heads/Male", - "Things/Pawn/Humanlike/Heads/Female" - }; OptionsHeadType result = new OptionsHeadType(); - for (int i = 0; i < headsFolderPaths.Length; i++) { - string text = headsFolderPaths[i]; - IEnumerable graphicsInFolder = GraphicDatabaseUtility.GraphicNamesInFolder(text); - foreach (string current in GraphicDatabaseUtility.GraphicNamesInFolder(text)) { - string fullPath = text + "/" + current; - CustomHeadType headType = CreateHumanHeadTypeFromGenderedGraphicPath(fullPath); - result.AddHeadType(headType); - if (!pathDictionary.ContainsKey(fullPath)) { - pathDictionary.Add(fullPath, headType); - } + foreach (var d in DefDatabase.AllDefs) { + if (d == HeadTypeDefOf.Skull || d== HeadTypeDefOf.Stump) { + continue; } + result.AddHeadType(d); } return result; } protected OptionsHeadType InitializeAlienHeadTypes(ThingDef raceDef) { + OptionsHeadType result = new OptionsHeadType(); //Logger.Debug("InitializeAlienHeadTypes(" + raceDef.defName + ")"); AlienRace alienRace = AlienRaceProvider.GetAlienRace(raceDef); - OptionsHeadType result = new OptionsHeadType(); if (alienRace == null) { Logger.Warning("Could not initialize head types for alien race, " + raceDef + ", because the race's thing definition was missing"); return result; } - //Logger.Debug("alienRace.GraphicsPathForHeads = " + alienRace.GraphicsPathForHeads); - if (alienRace.GraphicsPathForHeads == null) { - Logger.Warning("Could not initialize head types for alien race, " + raceDef + ", because no path for head graphics was found."); - return result; - } - foreach (var crownType in alienRace.CrownTypes) { - //Logger.Debug(" - " + crownType); - if (alienRace.GenderSpecificHeads) { - CustomHeadType maleHead = CreateGenderedAlienHeadTypeFromCrownType(alienRace.GraphicsPathForHeads, crownType, Gender.Male); - CustomHeadType femaleHead = CreateGenderedAlienHeadTypeFromCrownType(alienRace.GraphicsPathForHeads, crownType, Gender.Female); - if (maleHead != null) { - //Logger.Debug(" - MALE: " + maleHead.GraphicPath); - result.AddHeadType(maleHead); - } - if (femaleHead != null) { - //Logger.Debug(" - FEMALE: " + femaleHead.GraphicPath); - result.AddHeadType(femaleHead); - } - } - else { - CustomHeadType head = CreateMultiGenderAlienHeadTypeFromCrownType(alienRace.GraphicsPathForHeads, crownType); - if (head != null) { - //Logger.Debug(" - MULTIGENDER: " + head.GraphicPath); - result.AddHeadType(head); - } - } + foreach (var headType in alienRace.HeadTypes) { + result.AddHeadType(headType); } return result; } - protected CrownType FindCrownTypeEnumValue(string crownType) { - if (crownType.Contains(CrownType.Average.ToString() + "_")) { - return CrownType.Average; - } - else if (crownType.Contains(CrownType.Narrow.ToString() + "_")) { - return CrownType.Narrow; + public HeadTypeDef FindMatchingHeadTypeForOtherGenderOrDefault(HeadTypeDef def, Gender targetGender) { + if (def.gender == targetGender) { + return def; } - else { - return CrownType.Undefined; - } - } - - protected CustomHeadType CreateGenderedAlienHeadTypeFromCrownType(string graphicsPath, string crownType, Gender gender) { - CustomHeadType result = new CustomHeadType(); - result.Gender = gender; - result.Label = LabelFromCrownType(crownType); - - // Build the full graphics path for this head type - string pathValue = string.Copy(graphicsPath); - if (!pathValue.EndsWith("/")) { - pathValue += "/"; + if (targetGender == Gender.None) { + return def; } - string genderPrefix; ; - if (gender == Gender.Female) { - genderPrefix = "Female_"; - } - else if (gender == Gender.Male) { - genderPrefix = "Male_"; + string replaceWith = targetGender.ToString(); + string stringToReplace = targetGender.Equals(Gender.Male) ? Gender.Female.ToString() : Gender.Male.ToString(); + string targetDefName = def.defName.Replace(stringToReplace + "_", replaceWith + "_"); + HeadTypeDef matchingDef = DefDatabase.GetNamedSilentFail(targetDefName); + if (matchingDef != null) { + Logger.Debug("Swapped gender-specific head type with matching: " + matchingDef.defName); + return matchingDef; + } + HeadTypeDef result = DefDatabase.AllDefs.Where(d => d.gender == targetGender).FirstOrDefault(null); + if (result != null) { + Logger.Debug("Swapped gender-specific head type with first found: " + result.defName); + return result; } else { - genderPrefix = "None_"; - } - - string altGenderPrefix; - if (gender == Gender.Female) { - altGenderPrefix = "Female/"; + return def; } - else if (gender == Gender.Male) { - altGenderPrefix = "Male/"; + } + public string GetHeadTypeLabel(HeadTypeDef headType) { + if (headType.label.NullOrEmpty()) { + return ConvertHeadTypeDefNameToLabel(headType.defName); } else { - altGenderPrefix = "None/"; + return headType.LabelCap; } - - result.GraphicPath = pathValue + genderPrefix + crownType; - result.AlternateGraphicPath = pathValue + altGenderPrefix + genderPrefix + crownType; - result.CrownType = FindCrownTypeEnumValue(crownType); - result.AlienCrownType = crownType; - return result; } - protected CustomHeadType CreateMultiGenderAlienHeadTypeFromCrownType(string graphicsPath, string crownType) { - CustomHeadType result = new CustomHeadType(); - string pathValue = string.Copy(graphicsPath); - if (!pathValue.EndsWith("/")) { - pathValue += "/"; - } - pathValue += crownType; - result.GraphicPath = pathValue; - result.AlternateGraphicPath = null; - result.Label = LabelFromCrownType(crownType); - result.Gender = null; - result.CrownType = FindCrownTypeEnumValue(crownType); - result.AlienCrownType = crownType; + protected string ConvertHeadTypeDefNameToLabel(string defName) { + string result = defName + .Replace("StarWarsRaces_", "") + .Replace("_", " ") + ; return result; } - protected CustomHeadType CreateHumanHeadTypeFromGenderedGraphicPath(string graphicPath) { - CustomHeadType result = new CustomHeadType(); - result.GraphicPath = graphicPath; - result.AlternateGraphicPath = null; - result.Label = LabelFromGraphicsPath(graphicPath); - string[] strArray = Path.GetFileNameWithoutExtension(graphicPath).Split('_'); - try { - result.CrownType = (CrownType)ParseHelper.FromString(strArray[strArray.Length - 2], typeof(CrownType)); - result.Gender = (Gender)ParseHelper.FromString(strArray[strArray.Length - 3], typeof(Gender)); - } - catch (Exception ex) { - Logger.Warning("Parse error with head graphic at " + graphicPath + ": " + ex.Message); - result.CrownType = CrownType.Undefined; - result.Gender = Gender.None; - } - return result; - } - - protected string LabelFromGraphicsPath(string path) { - try { - string[] pathValues = path.Split('/'); - string crownType = pathValues[pathValues.Length - 1]; - string[] values = crownType.Split('_'); - return values[values.Count() - 2] + ", " + values[values.Count() - 1]; - } - catch (Exception) { - Logger.Warning("Could not determine head type label from graphics path: " + path); - return "EdB.PC.Common.Default".Translate(); - } - } + //protected string LabelFromGraphicsPath(string path) { + // try { + // string[] pathValues = path.Split('/'); + // string crownType = pathValues[pathValues.Length - 1]; + // string[] values = crownType.Split('_'); + // return values[values.Count() - 2] + ", " + values[values.Count() - 1]; + // } + // catch (Exception) { + // Logger.Warning("Could not determine head type label from graphics path: " + path); + // return "EdB.PC.Common.Default".Translate(); + // } + //} - protected string LabelFromCrownType(string crownType) { - string value; - value = Regex.Replace(crownType, "(\\B[A-Z]+?(?=[A-Z][^A-Z])|\\B[A-Z]+?(?=[^A-Z]))", " $1"); - value = value.Replace("_", " "); - value = Regex.Replace(value, "\\s+", " "); - return value; - } + //protected string LabelFromCrownType(string crownType) { + // string value; + // value = Regex.Replace(crownType, "(\\B[A-Z]+?(?=[A-Z][^A-Z])|\\B[A-Z]+?(?=[^A-Z]))", " $1"); + // value = value.Replace("_", " "); + // value = Regex.Replace(value, "\\s+", " "); + // return value; + //} } } diff --git a/Source/ProviderHealthOptions.cs b/Source/ProviderHealthOptions.cs index fc00479..d84a216 100644 --- a/Source/ProviderHealthOptions.cs +++ b/Source/ProviderHealthOptions.cs @@ -11,14 +11,25 @@ namespace EdB.PrepareCarefully { public class ProviderHealthOptions { protected Dictionary optionsLookup = new Dictionary(); + protected HashSet excludedOptions = new HashSet() { + "VatLearning", "VatGrowing", "Pregnant", "PsychicBond", "PsychicBondTorn", "ResearchCommand", "Animal_Flu", "Stillborn" + }; + public OptionsHealth GetOptions(CustomPawn pawn) { - OptionsHealth result = null; - if (!optionsLookup.TryGetValue(pawn.Pawn.def, out result)) { + if (!optionsLookup.TryGetValue(pawn.Pawn.def, out var result)) { result = InitializeHealthOptions(pawn.Pawn.def); optionsLookup.Add(pawn.Pawn.def, result); } return result; } + + public OptionsHealth GetOptions(Pawn pawn) { + if (!optionsLookup.TryGetValue(pawn.def, out var result)) { + result = InitializeHealthOptions(pawn.def); + optionsLookup.Add(pawn.def, result); + } + return result; + } protected OptionsHealth InitializeHealthOptions(ThingDef pawnThingDef) { OptionsHealth result = new OptionsHealth(); BodyDef bodyDef = pawnThingDef.race.body; @@ -317,6 +328,11 @@ protected void InitializeInjuryOptions(OptionsHealth options, ThingDef pawnThing continue; } } + + // Mark whether or not the injury can be added to a pawn in the health panel + foreach (var o in options.InjuryOptions) { + o.Selectable = IsInjuryOptionSelectable(o.HediffDef.defName); + } // Disambiguate duplicate injury labels. HashSet labels = new HashSet(); @@ -353,5 +369,21 @@ protected void InitializeInjuryOptions(OptionsHealth options, ThingDef pawnThing option.UniqueParts = uniqueParts; } } + + public bool IsInjuryOptionSelectable(string defName) { + if (excludedOptions.Contains(defName)) { + return false; + } + if (defName.EndsWith("InEyes")) { + return false; + } + if (defName.EndsWith("Command")) { + return false; + } + if (defName.EndsWith("CommandBuff")) { + return false; + } + return true; + } } } diff --git a/Source/ProviderPawnLayers.cs b/Source/ProviderPawnLayers.cs index d2d4334..239b3c9 100644 --- a/Source/ProviderPawnLayers.cs +++ b/Source/ProviderPawnLayers.cs @@ -1,6 +1,7 @@ using RimWorld; using System; using System.Collections.Generic; +using System.Linq; using Verse; namespace EdB.PrepareCarefully { public class ProviderPawnLayers { @@ -13,34 +14,35 @@ public class ProviderPawnLayers { private PawnLayer hatLayer = new PawnLayer() { Name = "Hat", Apparel = true, ApparelLayer = ApparelLayerDefOf.Overhead, Label = ("EdB.PC.Pawn.PawnLayer.Hat").Translate() }; private PawnLayer accessoryLayer = new PawnLayer() { Name = "Accessory", Apparel = true, ApparelLayer = ApparelLayerDefOf.Belt, Label = ("EdB.PC.Pawn.PawnLayer.Accessory").Translate() }; private PawnLayer eyeCoveringLayer = new PawnLayer() { Name = "EyeCovering", Apparel = true, ApparelLayer = ApparelLayerDefOf.EyeCover, Label = ("EdB.PC.Pawn.PawnLayer.EyeCovering").Translate() }; - private Dictionary, List> pawnLayerCache = new Dictionary, List>(); + private Dictionary, List> pawnLayerCache = new Dictionary, List>(); public ProviderPawnLayers() { } public List GetLayersForPawn(CustomPawn pawn) { - List result = null; - if (!pawnLayerCache.TryGetValue(new Pair(pawn.Pawn.def, pawn.Gender), out result)) { - result = InitializePawnLayers(pawn.Pawn.def, pawn.Gender); - pawnLayerCache.Add(new Pair(pawn.Pawn.def, pawn.Gender), result); + if (!pawnLayerCache.TryGetValue(ValueTuple.Create(pawn.Pawn.def, pawn.Gender, pawn.Pawn.DevelopmentalStage), out var result)) { + result = InitializePawnLayers(pawn.Pawn.def, pawn.Gender, pawn.Pawn.DevelopmentalStage); + pawnLayerCache.Add(ValueTuple.Create(pawn.Pawn.def, pawn.Gender, pawn.Pawn.DevelopmentalStage), result); } return result; } - public List InitializePawnLayers(ThingDef pawnDef, Gender gender) { + public List InitializePawnLayers(ThingDef pawnDef, Gender gender, DevelopmentalStage stage) { AlienRace race = PrepareCarefully.Instance.Providers.AlienRaces.GetAlienRace(pawnDef); if (race == null) { - return InitializeDefaultPawnLayers(pawnDef, gender); + return InitializeDefaultPawnLayers(pawnDef, gender, stage); } else { return InitializeAlienPawnLayers(pawnDef, gender, race); } } - private List InitializeDefaultPawnLayers(ThingDef pawnDef, Gender gender) { + private List InitializeDefaultPawnLayers(ThingDef pawnDef, Gender gender, DevelopmentalStage stage) { List defaultLayers = new List() { InitializeHairLayer(pawnDef, gender), InitializeBeardLayer(pawnDef, gender), - InitializeHeadLayer(pawnDef, gender), - InitializeBodyLayer(pawnDef, gender) + InitializeHeadLayer(pawnDef, gender) }; + if (stage != DevelopmentalStage.Baby && stage != DevelopmentalStage.Child && stage != DevelopmentalStage.Newborn) { + defaultLayers.Add(InitializeBodyLayer(pawnDef, gender)); + } if (ModLister.IdeologyInstalled) { defaultLayers.Add(InitializeFaceTattooLayer(pawnDef, gender)); @@ -194,9 +196,10 @@ private PawnLayer InitializeBodyLayer(ThingDef pawnDef, Gender gender) { } private List InitializeBodyOptions(ThingDef pawnDef, Gender gender) { List options = new List(); - foreach (var bodyType in PrepareCarefully.Instance.Providers.BodyTypes.GetBodyTypesForPawn(pawnDef, gender)) { + foreach (var bodyType in PrepareCarefully.Instance.Providers.BodyTypes.GetBodyTypesForPawn(pawnDef, gender).Where(b => b != BodyTypeDefOf.Baby && b != BodyTypeDefOf.Child).ToList()) { PawnLayerOptionBody option = new PawnLayerOptionBody(); option.BodyTypeDef = bodyType; + option.Selectable = bodyType != BodyTypeDefOf.Baby && bodyType != BodyTypeDefOf.Child; options.Add(option); } return options; diff --git a/Source/Randomizer.cs b/Source/Randomizer.cs index 6b9c5ba..01d3f80 100644 --- a/Source/Randomizer.cs +++ b/Source/Randomizer.cs @@ -16,7 +16,7 @@ public System.Random Random { } } - protected Pawn AttemptToGeneratePawn(PawnGenerationRequest request) { + public Pawn AttemptToGeneratePawn(PawnGenerationRequest request) { Exception lastException = null; Faction savedRequestFaction = request.Faction; for (int i = 0; i < MaxAttempts; i++) { @@ -63,14 +63,17 @@ public Pawn GenerateColonist() { return result; } - public Pawn GenerateColonistAsCloseToAsPossible(Pawn pawn) { + public Pawn GeneratePawnAsCloseToAsPossible(Pawn pawn) { var request = new PawnGenerationRequestWrapper() { KindDef = pawn.kindDef, Faction = pawn.Faction, FixedBiologicalAge = pawn.ageTracker.AgeBiologicalYears, FixedChronologicalAge = pawn.ageTracker.AgeChronologicalYears, FixedGender = pawn.gender, - FixedIdeology = pawn.Ideo + FixedIdeology = pawn.Ideo, + DevelopmentalStage = pawn.DevelopmentalStage, + ForcedXenotype = pawn.genes?.Xenotype, + ForcedCustomXenotype = pawn.genes?.CustomXenotype }; Pawn result = AttemptToGeneratePawn(request.Request); return result; @@ -78,9 +81,37 @@ public Pawn GenerateColonistAsCloseToAsPossible(Pawn pawn) { public Pawn GeneratePawn(PawnGenerationRequest request) { Pawn result = AttemptToGeneratePawn(request); + + RemoveUnexpectedEndogenesFromPawn(result, request.ForcedEndogenes); + return result; } + public static void RemoveUnexpectedEndogenesFromPawn(Pawn pawn, List expectedEndogenes) { + //Logger.Debug("RemoveUnexpectedEndogenesFromPawn()"); + if (expectedEndogenes == null || expectedEndogenes.Count == 0 || pawn == null) { + return; + } + List pawnEndogenes = pawn?.genes?.Endogenes; + if (pawnEndogenes == null || pawnEndogenes.Count == 0) { + return; + } + HashSet expectedDefs = new HashSet(expectedEndogenes); + List genesToRemove = new List(); + foreach (var g in pawnEndogenes) { + if (!expectedDefs.Contains(g.def)) { + //Logger.Debug("Gene " + g.def.defName + " is not expected"); + genesToRemove.Add(g); + } + else { + //Logger.Debug("Gene " + g.def.defName + " is expected"); + } + } + foreach (var g in genesToRemove) { + pawn.genes.RemoveGene(g); + } + } + public Pawn GenerateKindOfColonist(PawnKindDef kindDef) { Pawn result = AttemptToGeneratePawn(new PawnGenerationRequestWrapper() { Faction = Find.FactionManager.OfPlayer, @@ -128,7 +159,7 @@ public Pawn GenerateSameKindOfPawn(Pawn pawn) { return GenerateKindOfPawn(pawn.kindDef); } - public static Backstory RandomAdulthood(CustomPawn customPawn) { + public static BackstoryDef RandomAdulthood(CustomPawn customPawn) { return PrepareCarefully.Instance.Providers.Backstories.GetAdulthoodBackstoriesForPawn(customPawn).RandomElement(); } @@ -174,6 +205,19 @@ public Pawn EmptyPawn(PawnKindDef kindDef) { PawnComponentsUtility.CreateInitialComponents(result); return result; } + + public XenotypeDef RandomXenotypeFromSet(XenotypeSet set) { + double value = 0f; + double r = this.Random.NextDouble(); + for (int i= set.Count - 1; i>=0; i--) { + var chance = set[i].chance; + value += chance; + if (r < value) { + return set[i].xenotype; + } + } + return XenotypeDefOf.Baseliner; + } } } diff --git a/Source/Reflection.cs b/Source/Reflection.cs index 1d62030..6eba975 100644 --- a/Source/Reflection.cs +++ b/Source/Reflection.cs @@ -29,16 +29,11 @@ public static IEnumerable PossibleHediffs(RimWorld.ScenPart_ForcedHed return (IEnumerable)ReflectionCache.Instance.ScenPart_ForcedHediff_PossibleHediffs.Invoke(scenPart, null); } } - public static class PawnSkinColors { - public static int GetSkinDataIndexOfMelanin(float value) { - return (int)ReflectionCache.Instance.PawnSkinColors_GetSkinDataIndexOfMelanin.Invoke(null, new object[] { value }); - } - } - public static class GraphicDatabaseHeadRecords { - public static void BuildDatabaseIfNecessary() { - ReflectionCache.Instance.GraphicDatabaseHeadRecords_BuildDatabaseIfNecessary.Invoke(null, null); - } - } + //public static class GraphicDatabaseHeadRecords { + // public static void BuildDatabaseIfNecessary() { + // ReflectionCache.Instance.GraphicDatabaseHeadRecords_BuildDatabaseIfNecessary.Invoke(null, null); + // } + //} public static class Pawn { public static void ClearCachedDisabledWorkTypes(Verse.Pawn pawn) { ReflectionCache.Instance.Pawn_CachedDisabledWorkTypes.SetValue(pawn, null); diff --git a/Source/ReflectionCache.cs b/Source/ReflectionCache.cs index 5cfcb95..9646535 100644 --- a/Source/ReflectionCache.cs +++ b/Source/ReflectionCache.cs @@ -18,7 +18,6 @@ public class ReflectionCache { public MethodInfo PawnBioAndNameGenerator_GetBackstoryCategoryFiltersFor { get; set; } public MethodInfo PawnBioAndNameGenerator_IsBioUseable { get; set; } public MethodInfo PawnBioAndNameGenerator_TryGetRandomUnusedSolidBioFor { get; set; } - public MethodInfo PawnSkinColors_GetSkinDataIndexOfMelanin { get; set; } public MethodInfo ScenPart_ForcedHediff_PossibleHediffs { get; set; } public MethodInfo ScenPart_StartingAnimal_RandomPets { get; set; } @@ -40,7 +39,6 @@ public static ReflectionCache Instance { public void Initialize() { CharacterCardUtility_WorkTagsFrom = ReflectionUtil.RequiredMethod(typeof(CharacterCardUtility), "WorkTagsFrom"); - GraphicDatabaseHeadRecords_BuildDatabaseIfNecessary = ReflectionUtil.RequiredMethod(typeof(GraphicDatabaseHeadRecords), "BuildDatabaseIfNecessary"); GenFilePaths_FolderUnderSaveData = ReflectionUtil.RequiredMethod(typeof(GenFilePaths), "FolderUnderSaveData", new Type[] { typeof(string) }); PawnBioAndNameGenerator_GetBackstoryCategoryFiltersFor = ReflectionUtil.RequiredMethod(typeof(PawnBioAndNameGenerator), "GetBackstoryCategoryFiltersFor", @@ -51,7 +49,6 @@ public void Initialize() { PawnBioAndNameGenerator_IsBioUseable = ReflectionUtil.RequiredMethod(typeof(PawnBioAndNameGenerator), "IsBioUseable"); PawnBioAndNameGenerator_BioSelectionWeight = ReflectionUtil.RequiredMethod(typeof(PawnBioAndNameGenerator), "BioSelectionWeight"); - PawnSkinColors_GetSkinDataIndexOfMelanin = ReflectionUtil.RequiredMethod(typeof(PawnSkinColors), "GetSkinDataIndexOfMelanin", new Type[] { typeof(float) }); ScenPart_StartingAnimal_RandomPets = ReflectionUtil.RequiredMethod(typeof(ScenPart_StartingAnimal), "RandomPets"); ScenPart_ForcedHediff_PossibleHediffs = ReflectionUtil.RequiredMethod(typeof(ScenPart_ForcedHediff), "PossibleHediffs"); diff --git a/Source/State.cs b/Source/State.cs index 42fe7b0..2bbfbd8 100644 --- a/Source/State.cs +++ b/Source/State.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -68,7 +68,7 @@ public FactionDef LastSelectedFactionDef { set; } - public PawnKindDef LastSelectedPawnKindDef { + public PawnKindOption LastSelectedPawnKindDef { get; set; } diff --git a/Source/TabViewPawns.cs b/Source/TabViewPawns.cs index 1eba5f4..890d98a 100644 --- a/Source/TabViewPawns.cs +++ b/Source/TabViewPawns.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using RimWorld; using UnityEngine; using Verse; namespace EdB.PrepareCarefully { @@ -17,6 +18,7 @@ public class TabViewPawns : TabViewBase { public PanelFavoriteColor PanelFavoriteColor { get; set; } public PanelBackstory PanelBackstory { get; set; } public PanelTraits PanelTraits { get; set; } + public PanelXenotype PanelXenotype { get; set; } public PanelHealth PanelHealth { get; set; } public PanelFaction PanelFaction { get; set; } public PanelIdeo PanelIdeo { get; set; } @@ -54,6 +56,7 @@ protected void InitializePanels(bool largeUI) { PanelIdeo = new PanelIdeo(); PanelAbilities = new PanelAbilities(); PanelAge = new PanelAge(); + PanelXenotype = new PanelXenotype(); //PanelAge = new PanelModuleAge(); PanelTitles = new PanelTitles(); if (largeUI) { @@ -70,9 +73,13 @@ public void OneColumnLayout() { }; //PanelColumn1.Modules.Add(PanelAge); PanelColumn1.Modules.Add(PanelFaction); - if (ModsConfig.IdeologyActive) { + if (ModsConfig.IdeologyActive && !Find.IdeoManager.classicMode) { PanelColumn1.Modules.Add(PanelIdeo); } + PanelColumn1.Modules.Add(PanelAge); + if (ModsConfig.BiotechActive) { + PanelColumn1.Modules.Add(PanelXenotype); + } PanelColumn1.Modules.Add(PanelBackstory); PanelColumn1.Modules.Add(PanelTraits); //PanelColumn1.Modules.Add(PanelTitles); @@ -81,10 +88,17 @@ public void OneColumnLayout() { } public void TwoColumnLayout() { + List column1Modules = new List(); + column1Modules.Add(PanelFaction); + if (ModsConfig.IdeologyActive && !Find.IdeoManager.classicMode) { + column1Modules.Add(PanelIdeo); + } + if (ModsConfig.BiotechActive) { + column1Modules.Add(PanelXenotype); + } + column1Modules.Add(PanelAbilities); PanelColumn1 = new PanelScrollingContent() { - Modules = new List() { - /*PanelAge,*/ PanelFaction, PanelIdeo, PanelAbilities - } + Modules = column1Modules }; PanelColumn2 = new PanelScrollingContent() { Modules = new List() { @@ -100,18 +114,24 @@ public override void Draw(State state, Rect rect) { PanelColonyPawns.Draw(state); PanelWorldPawns.Draw(state); if (state.CurrentPawn != null) { - PanelRandomize.Draw(state); PanelName.Draw(state); if (ModsConfig.IdeologyActive) { PanelFavoriteColor.Draw(state); } PanelSaveLoad.Draw(state); - PanelAge.Draw(state); PanelAppearance.Draw(state); PanelColumn1.Draw(state); PanelColumn2?.Draw(state); PanelSkills.Draw(state); PanelIncapable.Draw(state); + + // Always draw the randomize panel last to avoid panels that trigger the regeneration of the pawn portrait + // before drawing that pawn during the same frame. If the generated pawn is in an error state caused by missing + // graphics, the game can completely freeze. + // Example: Randomizing an alien race with a non-baseliner zenotype can generate a pawn with a body type that is + // missing from the alien race mod. This broken pawn graphics state can freeze the mod! + // TODO: Find a better way to avoid this--maybe by not clearing pawn portrait caches until the end of the frame. + PanelRandomize.Draw(state); } } @@ -140,17 +160,26 @@ protected override void Resize(Rect rect) { } // Randomize, Name and Save/Load - PanelRandomize.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, - PanelColonyPawns.PanelRect.yMin, 64, 64)); - float namePanelWidth = 532; + float randomizeWidth = ModsConfig.BiotechActive ? 110 : 64; + float saveButtonWidth = 154; + float favoriteColorWidth = 64; + + float availableWidth = rect.width - panelMargin.x * 2.0f - pawnListWidth; + availableWidth -= randomizeWidth; if (ModsConfig.IdeologyActive) { - namePanelWidth -= 88; + availableWidth -= favoriteColorWidth + panelMargin.x; } + availableWidth -= saveButtonWidth + panelMargin.x; + float namePanelWidth = availableWidth; + + PanelRandomize.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, + PanelColonyPawns.PanelRect.yMin, randomizeWidth, 64)); PanelName.Resize(new Rect(PanelRandomize.PanelRect.xMax + panelMargin.x, PanelRandomize.PanelRect.yMin, namePanelWidth, 64)); - bool favoriteColor = ModsConfig.IdeologyActive; - PanelFavoriteColor.Resize(new Rect(PanelName.PanelRect.xMax + panelMargin.x, PanelName.PanelRect.yMin, favoriteColor ? 64 : 0, favoriteColor ? 64 : 0)); - float panelSaveLoadLeft = favoriteColor ? PanelFavoriteColor.PanelRect.xMax : PanelName.PanelRect.xMax; + if (ModsConfig.IdeologyActive) { + PanelFavoriteColor.Resize(new Rect(PanelName.PanelRect.xMax + panelMargin.x, PanelName.PanelRect.yMin, favoriteColorWidth, 64)); + } + float panelSaveLoadLeft = ModsConfig.IdeologyActive ? PanelFavoriteColor.PanelRect.xMax : PanelName.PanelRect.xMax; PanelSaveLoad.Resize(new Rect(panelSaveLoadLeft + panelMargin.x, PanelName.PanelRect.yMin, 154, 64)); float x = PanelColonyPawns.PanelRect.xMax + panelMargin.x; @@ -158,8 +187,7 @@ protected override void Resize(Rect rect) { // Age and Appearance float columnSize1 = 226; - PanelAge.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, PanelRandomize.PanelRect.yMax + panelMargin.y, columnSize1, 64)); - PanelAppearance.Resize(new Rect(PanelAge.PanelRect.xMin, PanelAge.PanelRect.yMax + panelMargin.y, columnSize1, 414)); + PanelAppearance.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, PanelRandomize.PanelRect.yMax + panelMargin.y, columnSize1, 414)); //PanelAppearance.Resize(new Rect(x, top, columnSize1, 490)); x += columnSize1 + panelMargin.x; diff --git a/Source/Textures.cs b/Source/Textures.cs index 07c6b44..e013ec1 100644 --- a/Source/Textures.cs +++ b/Source/Textures.cs @@ -57,6 +57,9 @@ public static class Textures { public static Texture2D TextureFilterAtlas1; public static Texture2D TextureFilterAtlas2; public static Texture2D TextureButtonCloseSmall; + public static Texture2D TextureBaby; + public static Texture2D TextureChild; + public static Texture2D TextureAdult; public static Texture2D TextureWhite { get { @@ -139,8 +142,12 @@ private static void LoadTextures() { TextureAlternateRow = SolidColorMaterials.NewSolidColorTexture(new Color(1, 1, 1, 0.05f)); TextureSkillBarFill = SolidColorMaterials.NewSolidColorTexture(new Color(1f, 1f, 1f, 0.1f)); - - loaded = true; + + TextureBaby = ContentFinder.Get("UI/Icons/DevelopmentalStages/Baby", false); + TextureChild = ContentFinder.Get("UI/Icons/DevelopmentalStages/Child", false); + TextureAdult = ContentFinder.Get("UI/Icons/DevelopmentalStages/Adult", false); + + loaded = true; } } } diff --git a/Source/Version3/PresetLoaderVersion3.cs b/Source/Version3/PresetLoaderVersion3.cs index d25e211..4cb76ee 100644 --- a/Source/Version3/PresetLoaderVersion3.cs +++ b/Source/Version3/PresetLoaderVersion3.cs @@ -438,22 +438,38 @@ public CustomPawn LoadPawn(SaveRecordPawnV3 record) { Logger.Warning("Could not load hair definition \"" + record.hairDef + "\""); Failed = true; } - - pawn.HeadGraphicPath = record.headGraphicPath; - pawn.Pawn.story.hairColor = record.hairColor; + + if (!String.IsNullOrEmpty(record.headGraphicPath)) { + string[] pathParts = record.headGraphicPath.Split(new char[] { '/' }, 20); + string lastPath = pathParts[pathParts.Length - 1]; + string[] parts = lastPath.Split('_'); + string headTypeDefName = parts[0] + "_"; + for (int i = 1; i < parts.Count(); i++) { + headTypeDefName += parts[i]; + } + var headType = DefDatabase.GetNamedSilentFail(headTypeDefName); + if (headType != null) { + pawn.HeadType = headType; + Logger.Warning("Successfully converted headGraphicPath {" + record.headGraphicPath + "} to head type definition {" + headType.defName + "}"); + } + else { + Logger.Warning("Couldn't find head type {" + headTypeDefName + "} for converted headGraphicPath {" + record.headGraphicPath + "}"); + } + } + pawn.Pawn.story.HairColor = record.hairColor; if (record.melanin >= 0.0f) { pawn.MelaninLevel = record.melanin; } else { - pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor); + pawn.SkinColor = record.skinColor; } // Set the skin color (only for Alien Races). if (pawn.AlienRace != null) { pawn.SkinColor = record.skinColor; } - Backstory backstory = FindBackstory(record.childhood); + BackstoryDef backstory = FindBackstory(record.childhood); if (backstory != null) { pawn.Childhood = backstory; } @@ -472,28 +488,6 @@ public CustomPawn LoadPawn(SaveRecordPawnV3 record) { } } - // Get the body type from the save record. If there's no value in the save, then assign the - // default body type from the pawn's backstories. - // TODO: 1.0 - /* - BodyType? bodyType = null; - try { - bodyType = (BodyType)Enum.Parse(typeof(BodyType), record.bodyType); - } - catch (Exception) { - } - if (!bodyType.HasValue) { - if (pawn.Adulthood != null) { - bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender); - } - else { - bodyType = pawn.Childhood.BodyTypeFor(pawn.Gender); - } - } - if (bodyType.HasValue) { - pawn.BodyType = bodyType.Value; - } - */ BodyTypeDef bodyType = null; try { bodyType = DefDatabase.GetNamedSilentFail(record.bodyType); @@ -742,22 +736,39 @@ public HairDef FindHairDef(string name) { return DefDatabase.GetNamedSilentFail(name); } - public Backstory FindBackstory(string name) { - Backstory matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.Equals(name); - }); - // If we couldn't find a matching backstory, look for one with the same identifier, but a different version number at the end. - if (matchingBackstory == null) { - Regex expression = new Regex("\\d+$"); - string backstoryMinusVersioning = expression.Replace(name, ""); - matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.StartsWith(backstoryMinusVersioning); - }); - if (matchingBackstory != null) { - Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); - } + public BackstoryDef FindBackstory(string name) { + // Assume the name is a definition name. Look it up based on that and return it if we find it. + BackstoryDef matchingBackstory = DefDatabase.GetNamedSilentFail(name); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the name is probably a backstory identifier. Try to find it by matching the id. + matchingBackstory = DefDatabase.AllDefs.Where((BackstoryDef b) => { return b.identifier != null && b.identifier.Equals(name); }).FirstOrDefault(); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the identifier probably changed. Try to find another backstory with the same identifier prefix but with a different numeric suffix at the end + Regex expression = new Regex("\\d+$"); + string backstoryMinusVersioning = expression.Replace(name, ""); + if (backstoryMinusVersioning == name) { + return null; + } + matchingBackstory = DefDatabase.AllDefs + .Where(b => { return b.identifier != null && b.identifier.StartsWith(backstoryMinusVersioning); }) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); + return matchingBackstory; + } + // If we still didn't find it, look for a def name that starts with the identifier but with the numeric suffix removed + matchingBackstory = DefDatabase.AllDefs + .Where(b => b.defName.StartsWith(backstoryMinusVersioning)) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.defName + " in place of " + name); + return matchingBackstory; } - return matchingBackstory; + return null; } public Trait FindTrait(string name, int degree) { diff --git a/Source/Version3/SaveRecordInjuryV3.cs b/Source/Version3/SaveRecordInjuryV3.cs index 5de4254..e56f7c4 100644 --- a/Source/Version3/SaveRecordInjuryV3.cs +++ b/Source/Version3/SaveRecordInjuryV3.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; diff --git a/Source/Version3/SaveRecordPawnV3.cs b/Source/Version3/SaveRecordPawnV3.cs index b7bdf87..dbd33b6 100644 --- a/Source/Version3/SaveRecordPawnV3.cs +++ b/Source/Version3/SaveRecordPawnV3.cs @@ -97,10 +97,8 @@ public HairDef FindHairDef(string name) { return DefDatabase.GetNamedSilentFail(name); } - public Backstory FindBackstory(string name) { - return BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.Equals(name); - }); + public BackstoryDef FindBackstory(string name) { + return DefDatabase.AllDefs.Where((BackstoryDef b) => { return b.identifier.Equals(name); }).FirstOrDefault(); } public Trait FindTrait(string name, int degree) { diff --git a/Source/Version4/PresetLoaderVersion4.cs b/Source/Version4/PresetLoaderVersion4.cs index ececec4..3f6723a 100644 --- a/Source/Version4/PresetLoaderVersion4.cs +++ b/Source/Version4/PresetLoaderVersion4.cs @@ -425,17 +425,12 @@ public CustomPawn LoadPawn(SaveRecordPawnV4 record) { Failed = true; } - pawn.HeadGraphicPath = record.headGraphicPath; if (pawn.Pawn.story != null) { - pawn.Pawn.story.hairColor = record.hairColor; + pawn.Pawn.story.HairColor = record.hairColor; } - if (record.melanin >= 0.0f) { - pawn.MelaninLevel = record.melanin; - } - else { - pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor); - } + pawn.SkinColor = record.skinColor; + // Set the skin color for Alien Races and alien comp values. if (pawn.AlienRace != null) { pawn.SkinColor = record.skinColor; @@ -450,7 +445,7 @@ public CustomPawn LoadPawn(SaveRecordPawnV4 record) { } } - Backstory backstory = FindBackstory(record.childhood); + BackstoryDef backstory = FindBackstory(record.childhood); if (backstory != null) { pawn.Childhood = backstory; } @@ -715,22 +710,39 @@ public HairDef FindHairDef(string name) { return DefDatabase.GetNamedSilentFail(name); } - public Backstory FindBackstory(string name) { - Backstory matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.Equals(name); - }); - // If we couldn't find a matching backstory, look for one with the same identifier, but a different version number at the end. - if (matchingBackstory == null) { - Regex expression = new Regex("\\d+$"); - string backstoryMinusVersioning = expression.Replace(name, ""); - matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.StartsWith(backstoryMinusVersioning); - }); - if (matchingBackstory != null) { - Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); - } + public BackstoryDef FindBackstory(string name) { + // Assume the name is a definition name. Look it up based on that and return it if we find it. + BackstoryDef matchingBackstory = DefDatabase.GetNamedSilentFail(name); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the name is probably a backstory identifier. Try to find it by matching the id. + matchingBackstory = DefDatabase.AllDefs.Where((BackstoryDef b) => { return b.identifier != null && b.identifier.Equals(name); }).FirstOrDefault(); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the identifier probably changed. Try to find another backstory with the same identifier prefix but with a different numeric suffix at the end + Regex expression = new Regex("\\d+$"); + string backstoryMinusVersioning = expression.Replace(name, ""); + if (backstoryMinusVersioning == name) { + return null; } - return matchingBackstory; + matchingBackstory = DefDatabase.AllDefs + .Where(b => { return b.identifier != null && b.identifier.StartsWith(backstoryMinusVersioning); }) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); + return matchingBackstory; + } + // If we still didn't find it, look for a def name that starts with the identifier but with the numeric suffix removed + matchingBackstory = DefDatabase.AllDefs + .Where(b => b.defName.StartsWith(backstoryMinusVersioning)) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.defName + " in place of " + name); + return matchingBackstory; + } + return null; } public Trait FindTrait(string name, int degree) { diff --git a/Source/Version4/SaveRecordPawnV4.cs b/Source/Version4/SaveRecordPawnV4.cs index 98b5513..cfcb531 100644 --- a/Source/Version4/SaveRecordPawnV4.cs +++ b/Source/Version4/SaveRecordPawnV4.cs @@ -66,10 +66,9 @@ public SaveRecordPawnV4(CustomPawn pawn) { } this.childhood = pawn.Childhood.identifier; this.skinColor = pawn.Pawn.story.SkinColor; - this.melanin = pawn.Pawn.story.melanin; this.hairDef = pawn.HairDef.defName; - this.hairColor = pawn.Pawn.story.hairColor; - this.headGraphicPath = pawn.HeadGraphicPath; + this.hairColor = pawn.Pawn.story.HairColor; + this.bodyType = pawn.BodyType.defName; this.firstName = pawn.FirstName; this.nickName = pawn.NickName; @@ -190,10 +189,8 @@ public HairDef FindHairDef(string name) { return DefDatabase.GetNamedSilentFail(name); } - public Backstory FindBackstory(string name) { - return BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.Equals(name); - }); + public BackstoryDef FindBackstory(string name) { + return DefDatabase.AllDefs.Where((BackstoryDef b) => { return b.identifier.Equals(name); }).FirstOrDefault(); } public Trait FindTrait(string name, int degree) { diff --git a/Source/Version5/PawnLoaderV5.cs b/Source/Version5/PawnLoaderV5.cs index 1bb50f3..78577ed 100644 --- a/Source/Version5/PawnLoaderV5.cs +++ b/Source/Version5/PawnLoaderV5.cs @@ -5,6 +5,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; +using System.Xml.Linq; using UnityEngine; using Verse; @@ -159,23 +160,63 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { FixedChronologicalAge = record.chronologicalAge, FixedGender = record.gender, Context = PawnGenerationContext.NonPlayer, - WorldPawnFactionDoesntMatter = true + WorldPawnFactionDoesntMatter = true, + AllowDowned = true }; Faction playerFaction = Find.FactionManager.OfPlayer; Ideo ideology = playerFaction?.ideos?.PrimaryIdeo; if (record.ideo != null) { if (!record.ideo.sameAsColony && IdeoMap != null) { - if (IdeoMap.TryGetValue(record.ideo.name, out Ideo ideo)) { - ideology = ideo; + if (!record.ideo.name.NullOrEmpty()) { + if (IdeoMap.TryGetValue(record.ideo.name, out Ideo ideo)) { + ideology = ideo; + } + } + else { + ideology = null; } } } - if (ideology != null) { generationRequest.FixedIdeology = ideology; } + if (record.genes != null) { + if (!record.genes.xenotypeDef.NullOrEmpty()) { + XenotypeDef xenotypeDef = DefDatabase.GetNamedSilentFail(record.genes.xenotypeDef); + if (xenotypeDef != null) { + generationRequest.ForcedXenotype = xenotypeDef; + } + } + else if (!record.genes.customXenotypeName.NullOrEmpty()) { + var customXenotypes = ReflectionUtil.GetStaticPropertyValue>(typeof(CharacterCardUtility), "CustomXenotypes"); + CustomXenotype xenotype = customXenotypes?.Where(x => { return x.name == record.genes.customXenotypeName; }).FirstOrDefault(); + if (xenotype != null) { + generationRequest.ForcedCustomXenotype = xenotype; + } + } + + if (record.genes?.endogenes != null && record.genes?.endogenes.Count > 0) { + List genes = record.genes.endogenes.Select(g => { + GeneDef def = DefDatabase.GetNamedSilentFail(g); + return def; + }).Where(g => g != null).ToList(); + if (genes.Count > 0) { + generationRequest.ForcedEndogenes = genes; + } + } + if (record.genes?.xenogenes != null && record.genes?.xenogenes.Count > 0) { + List genes = record.genes.xenogenes.Select(g => { + GeneDef def = DefDatabase.GetNamedSilentFail(g); + return def; + }).Where(g => g != null).ToList(); + if (genes.Count > 0) { + generationRequest.ForcedXenogenes = genes; + } + } + } + // Add a pawn kind definition to the generation request, if possible. if (pawnKindDef != null) { generationRequest.KindDef = pawnKindDef; @@ -185,13 +226,15 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { Pawn source = null; try { source = PawnGenerator.GeneratePawn(generationRequest.Request); + Randomizer.RemoveUnexpectedEndogenesFromPawn(source, generationRequest.Request.ForcedEndogenes); } catch (Exception e) { Logger.Warning("Failed to generate a pawn from preset for pawn {" + (record.nickName) + "}. Will try to create it using fallback settings", e); generationRequest = new PawnGenerationRequestWrapper() { FixedBiologicalAge = record.biologicalAge, FixedChronologicalAge = record.chronologicalAge, - FixedGender = record.gender + FixedGender = record.gender, + AllowDowned = true }; try { source = PawnGenerator.GeneratePawn(generationRequest.Request); @@ -202,6 +245,14 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } } + // Adjust the age using the tick value + if (record.biologicalAgeInTicks != null) { + source.ageTracker.AgeBiologicalTicks = record.biologicalAgeInTicks.Value; + } + if (record.chronologicalAgeInTicks != null) { + source.ageTracker.AgeChronologicalTicks = record.chronologicalAgeInTicks.Value; + } + if (source.health != null) { source.health.Reset(); } @@ -258,40 +309,38 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } } else if (pawn.Type == CustomPawnType.World) { - if (record.faction != null) { - if (record.faction.def != null) { - FactionDef factionDef = DefDatabase.GetNamedSilentFail(record.faction.def); - if (factionDef != null) { - bool randomFaction = false; - if (record.faction.index != null) { - CustomFaction customFaction = null; - if (!record.faction.leader) { - customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionByIndex(factionDef, record.faction.index.Value); - } - else { - customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionWithLeaderOptionByIndex(factionDef, record.faction.index.Value); - } - if (customFaction != null) { - pawn.Faction = customFaction; - } - else { - Logger.Warning("Could not place at least one preset character into a saved faction because there were not enough available factions of that type in the world"); - randomFaction = true; - } + if (record?.faction?.def != null) { + FactionDef factionDef = DefDatabase.GetNamedSilentFail(record.faction.def); + if (factionDef != null) { + bool randomFaction = false; + if (record.faction.index != null) { + CustomFaction customFaction = null; + if (!record.faction.leader) { + customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionByIndex(factionDef, record.faction.index.Value); } else { - randomFaction = true; + customFaction = PrepareCarefully.Instance.Providers.Factions.FindCustomFactionWithLeaderOptionByIndex(factionDef, record.faction.index.Value); } - if (randomFaction) { - CustomFaction customFaction = PrepareCarefully.Instance.Providers.Factions.FindRandomCustomFactionByDef(factionDef); - if (customFaction != null) { - pawn.Faction = customFaction; - } + if (customFaction != null) { + pawn.Faction = customFaction; + } + else { + Logger.Warning("Could not place at least one preset character into a saved faction because there were not enough available factions of that type in the world"); + randomFaction = true; } } else { - Logger.Warning("Could not place at least one preset character into a saved faction because that faction is not available in the world"); + randomFaction = true; } + if (randomFaction) { + CustomFaction customFaction = PrepareCarefully.Instance.Providers.Factions.FindRandomCustomFactionByDef(factionDef); + if (customFaction != null) { + pawn.Faction = customFaction; + } + } + } + else { + Logger.Warning("Could not place at least one preset character into a saved faction because that faction is not available in the world"); } } } @@ -305,29 +354,48 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { partialFailure = true; } - if (!String.IsNullOrWhiteSpace(record.headGraphicPath)) { - pawn.HeadGraphicPath = record.headGraphicPath; + if (!String.IsNullOrWhiteSpace(record.headType)) { + var headType = DefDatabase.GetNamedSilentFail(record.headType); + if (headType != null) { + pawn.HeadType = headType; + } + else { + Logger.Warning("Could not load head type definition \"" + record.headType + "\""); + } + } + else if (!String.IsNullOrEmpty(record.headGraphicPath)) { + string[] pathParts = record.headGraphicPath.Split(new char[] { '/' }, 20); + string lastPath = pathParts[pathParts.Length - 1]; + string[] parts = lastPath.Split('_'); + string headTypeDefName = parts[0] + "_"; + for (int i=1; i.GetNamedSilentFail(headTypeDefName); + if (headType != null) { + pawn.HeadType = headType; + Logger.Warning("Successfully converted headGraphicPath {" + record.headGraphicPath + "} to head type definition {" + headType.defName + "}"); + } + else { + Logger.Warning("Couldn't find head type {" + headTypeDefName + "} converted from headGraphicPath {" + record.headGraphicPath + "}"); + } } pawn.HairColor = record.hairColor; + pawn.SkinColor = record.skinColor; - if (record.melanin >= 0.0f) { - pawn.MelaninLevel = record.melanin; - } - else { - pawn.MelaninLevel = PawnColorUtils.FindMelaninValueFromColor(record.skinColor); - } - - Backstory backstory = FindBackstory(record.childhood); - if (backstory != null) { - pawn.Childhood = backstory; - } - else { - Logger.Warning("Could not load childhood backstory definition \"" + record.childhood + "\""); - partialFailure = true; + if (!record.childhood.NullOrEmpty()) { + BackstoryDef backstory = record.childhood != null ? FindBackstory(record.childhood) : null; + if (backstory != null) { + pawn.Childhood = backstory; + } + else { + Logger.Warning("Could not load childhood backstory definition \"" + record.childhood + "\""); + partialFailure = true; + } } - if (record.adulthood != null) { - backstory = FindBackstory(record.adulthood); + if (!record.adulthood.NullOrEmpty()) { + BackstoryDef backstory = FindBackstory(record.adulthood); if (backstory != null) { pawn.Adulthood = backstory; } @@ -337,12 +405,7 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } } - BodyTypeDef bodyType = null; - try { - bodyType = DefDatabase.GetNamedSilentFail(record.bodyType); - } - catch (Exception) { - } + BodyTypeDef bodyType = DefDatabase.GetNamedSilentFail(record.bodyType); if (bodyType == null) { if (pawn.Adulthood != null) { bodyType = pawn.Adulthood.BodyTypeFor(pawn.Gender); @@ -426,17 +489,22 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } } - foreach (var skill in record.skills) { - SkillDef def = FindSkillDef(pawn.Pawn, skill.name); - if (def == null) { - Logger.Warning("Could not load skill definition \"" + skill.name + "\" from saved preset"); - partialFailure = true; - continue; + if (record.skills != null) { + foreach (var skill in record.skills) { + SkillDef def = FindSkillDef(pawn.Pawn, skill.name); + if (def == null) { + Logger.Warning("Could not load skill definition \"" + skill.name + "\" from saved preset"); + partialFailure = true; + continue; + } + pawn.currentPassions[def] = skill.passion; + pawn.originalPassions[def] = skill.passion; + pawn.SetOriginalSkillLevel(def, skill.value); + pawn.SetUnmodifiedSkillLevel(def, skill.value); } - pawn.currentPassions[def] = skill.passion; - pawn.originalPassions[def] = skill.passion; - pawn.SetOriginalSkillLevel(def, skill.value); - pawn.SetUnmodifiedSkillLevel(def, skill.value); + } + else { + Logger.Warning("Could not load skills definitions for the saved preset. No valid skill definitions found"); } pawn.ClearApparel(); @@ -554,6 +622,7 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } bodyPart = uniquePart.Record; } + bool isValid = true; Injury injury = new Injury(); injury.Option = option; injury.BodyPartRecord = bodyPart; @@ -563,13 +632,30 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { if (injuryRecord.painFactor != null) { injury.PainFactor = injuryRecord.PainFactor; } - pawn.AddInjury(injury); + if (injuryRecord.chemical != null) { + injury.Chemical = DefDatabase.GetNamedSilentFail(injuryRecord.chemical); + if (injury.Chemical == null) { + Logger.Debug("Could not load injury from saved pawn. Chemical definition (" + injuryRecord.chemical + ") was not found"); + isValid = false; + } + } + if (isValid) { + pawn.AddInjury(injury); + } + else { + Logger.Debug("Did not add invalid injury"); + } } // Ideoligion Certainty try { - if (record.ideo != null && ModsConfig.IdeologyActive && pawn.Pawn?.ideo != null) { - pawn.Certainty = record.ideo.certainty; + if (ideology != null) { + if (record.ideo != null && ModsConfig.IdeologyActive && pawn.Pawn?.ideo != null) { + pawn.Certainty = record.ideo.certainty; + } + } + else { + pawn.Certainty = 0.0f; } } catch (Exception e) { @@ -623,22 +709,39 @@ protected UniqueBodyPart FindReplacementBodyPart(OptionsHealth healthOptions, st return null; } - private Backstory FindBackstory(string name) { - Backstory matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.Equals(name); - }); - // If we couldn't find a matching backstory, look for one with the same identifier, but a different version number at the end. - if (matchingBackstory == null) { - Regex expression = new Regex("\\d+$"); - string backstoryMinusVersioning = expression.Replace(name, ""); - matchingBackstory = BackstoryDatabase.allBackstories.Values.ToList().Find((Backstory b) => { - return b.identifier.StartsWith(backstoryMinusVersioning); - }); - if (matchingBackstory != null) { - Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); - } - } - return matchingBackstory; + public BackstoryDef FindBackstory(string name) { + // Assume the name is a definition name. Look it up based on that and return it if we find it. + BackstoryDef matchingBackstory = DefDatabase.GetNamedSilentFail(name); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the name is probably a backstory identifier. Try to find it by matching the id. + matchingBackstory = DefDatabase.AllDefs.Where((BackstoryDef b) => { return b.identifier != null && b.identifier.Equals(name); }).FirstOrDefault(); + if (matchingBackstory != null) { + return matchingBackstory; + } + // If we didn't find it, the identifier probably changed. Try to find another backstory with the same identifier prefix but with a different numeric suffix at the end + Regex expression = new Regex("\\d+$"); + string backstoryMinusVersioning = expression.Replace(name, ""); + if (backstoryMinusVersioning == name) { + return null; + } + matchingBackstory = DefDatabase.AllDefs + .Where(b => { return b.identifier != null && b.identifier.StartsWith(backstoryMinusVersioning); }) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.identifier + " in place of " + name); + return matchingBackstory; + } + // If we still didn't find it, look for a def name that starts with the identifier but with the numeric suffix removed + matchingBackstory = DefDatabase.AllDefs + .Where(b => b.defName.StartsWith(backstoryMinusVersioning)) + .FirstOrDefault(); + if (matchingBackstory != null) { + Logger.Message("Found replacement backstory. Using " + matchingBackstory.defName + " in place of " + name); + return matchingBackstory; + } + return null; } protected Trait FindTrait(string name, int degree) { diff --git a/Source/Version5/SaveRecordGenesV5.cs b/Source/Version5/SaveRecordGenesV5.cs new file mode 100644 index 0000000..d88c46e --- /dev/null +++ b/Source/Version5/SaveRecordGenesV5.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace EdB.PrepareCarefully { + public class SaveRecordGenesV5 : IExposable { + public string xenotypeDef; + public string customXenotypeName; + public List endogenes; + public List xenogenes; + + public SaveRecordGenesV5() { + } + + public void ExposeData() { + Scribe_Values.Look(ref this.xenotypeDef, "xenotypeDef", null, false); + Scribe_Values.Look(ref this.customXenotypeName, "customXenotypeName", null, false); + Scribe_Collections.Look(ref this.endogenes, "endogenes"); + Scribe_Collections.Look(ref this.xenogenes, "xenogenes"); + } + } +} diff --git a/Source/Version5/SaveRecordHediffV5.cs b/Source/Version5/SaveRecordHediffV5.cs new file mode 100644 index 0000000..065995d --- /dev/null +++ b/Source/Version5/SaveRecordHediffV5.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Verse; + +namespace EdB.PrepareCarefully { + public class SaveRecordHediffV5 : IExposable { + public Pawn Pawn { get; set; } + public Hediff Hediff { get; set; } + public void ExposeData() { + if (Scribe.mode == LoadSaveMode.Saving && Hediff != null) { + if (Hediff != null) { + Hediff.ExposeData(); + } + } + } + } +} diff --git a/Source/Version5/SaveRecordInjuryV5.cs b/Source/Version5/SaveRecordInjuryV5.cs new file mode 100644 index 0000000..6ce5f7f --- /dev/null +++ b/Source/Version5/SaveRecordInjuryV5.cs @@ -0,0 +1,55 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + public class SaveRecordInjuryV5 : IExposable { + public string bodyPart = null; + public int? bodyPartIndex = null; + public string hediffDef = null; + public string severity = null; + public string painFactor = null; + public string chemical = null; + + public SaveRecordInjuryV5() { + } + + public SaveRecordInjuryV5(Injury injury) { + this.bodyPart = injury?.BodyPartRecord?.def?.defName; + this.hediffDef = injury?.Option?.HediffDef?.defName; + if (injury.Severity != 0) { + this.severity = injury.Severity.ToString(); + } + if (injury.PainFactor != null) { + this.painFactor = injury.PainFactor.Value.ToString(); + } + if (injury.Chemical != null) { + this.chemical = injury.Chemical.defName; + } + } + + public void ExposeData() { + Scribe_Values.Look(ref this.hediffDef, "hediffDef", null, false); + Scribe_Values.Look(ref this.bodyPart, "bodyPart", null, false); + Scribe_Values.Look(ref this.bodyPartIndex, "bodyPartIndex", null, false); + Scribe_Values.Look(ref this.severity, "severity", null, false); + Scribe_Values.Look(ref this.painFactor, "painFactor", null, false); + Scribe_Values.Look(ref this.chemical, "chemical", null, false); + } + + public float Severity { + get { + return float.Parse(severity); + } + } + public float PainFactor { + get { + return float.Parse(painFactor); + } + } + } +} + diff --git a/Source/Version5/SaveRecordPawnV5.cs b/Source/Version5/SaveRecordPawnV5.cs index d3c3cdc..0b47a09 100644 --- a/Source/Version5/SaveRecordPawnV5.cs +++ b/Source/Version5/SaveRecordPawnV5.cs @@ -7,6 +7,7 @@ namespace EdB.PrepareCarefully { public class SaveRecordPawnV5 : IExposable { + private Pawn pawn = null; public string id; public string type; public SaveRecordFactionV4 faction; @@ -23,6 +24,7 @@ public class SaveRecordPawnV5 : IExposable { public Color hairColor; public string headGraphicPath; public string bodyType; + public string headType; public string beard; public string faceTattoo; public string bodyTattoo; @@ -33,6 +35,9 @@ public class SaveRecordPawnV5 : IExposable { public int age; public int biologicalAge; public int chronologicalAge; + public long? biologicalAgeInTicks; + public long? chronologicalAgeInTicks; + public string developmentalStage; public List skills = new List(); public List apparel = new List(); public List apparelLayers = new List(); @@ -41,11 +46,14 @@ public class SaveRecordPawnV5 : IExposable { public bool randomInjuries = true; public bool randomRelations = false; public List implants = new List(); - public List injuries = new List(); + public List injuries = new List(); public SaveRecordIdeoV5 ideo; public List abilities = new List(); public string compsXml = null; public List savedComps = new List(); + public List hediffXmls = null; + public SaveRecordGenesV5 genes; + public List hediffs = null; // Deprecated. Here for backwards compatibility with V4 public List traitNames = new List(); @@ -57,6 +65,7 @@ public SaveRecordPawnV5() { } public SaveRecordPawnV5(CustomPawn pawn) { + this.pawn = pawn.Pawn; this.id = pawn.Id; this.thingDef = pawn.Pawn.def.defName; this.type = pawn.Type.ToString(); @@ -70,14 +79,14 @@ public SaveRecordPawnV5(CustomPawn pawn) { this.pawnKindDef = pawn.OriginalKindDef?.defName ?? pawn.Pawn.kindDef.defName; this.originalFactionDef = pawn.OriginalFactionDef?.defName; this.gender = pawn.Gender; - this.adulthood = pawn.Adulthood?.identifier ?? pawn.LastSelectedAdulthoodBackstory?.identifier; - this.childhood = pawn.Childhood?.identifier; + this.adulthood = pawn.Adulthood?.defName ?? pawn.LastSelectedAdulthoodBackstory?.defName; + this.childhood = pawn.Childhood?.defName; this.skinColor = pawn.Pawn.story.SkinColor; - this.melanin = pawn.Pawn.story.melanin; + this.melanin = pawn.MelaninLevel; this.hairDef = pawn.HairDef.defName; - this.hairColor = pawn.Pawn.story.hairColor; - this.headGraphicPath = pawn.HeadGraphicPath; + this.hairColor = pawn.Pawn.story.HairColor; this.bodyType = pawn.BodyType.defName; + this.headType = pawn.HeadType.defName; this.beard = pawn.Beard?.defName; this.faceTattoo = pawn.FaceTattoo?.defName; this.bodyTattoo = pawn.BodyTattoo?.defName; @@ -88,6 +97,9 @@ public SaveRecordPawnV5(CustomPawn pawn) { this.age = 0; this.biologicalAge = pawn.BiologicalAge; this.chronologicalAge = pawn.ChronologicalAge; + this.biologicalAgeInTicks = pawn.BiologicalAgeInTicks; + this.chronologicalAgeInTicks = pawn.ChronologicalAgeInTicks; + foreach (var trait in pawn.Traits) { if (trait != null) { this.traits.Add(new SaveRecordTraitV5() { @@ -130,7 +142,7 @@ public SaveRecordPawnV5(CustomPawn pawn) { this.implants.Add(saveRecord); } foreach (Injury injury in pawn.Injuries) { - var saveRecord = new SaveRecordInjuryV3(injury); + var saveRecord = new SaveRecordInjuryV5(injury); if (injury.BodyPartRecord != null) { UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(injury.BodyPartRecord); if (part != null && part.Index > 0) { @@ -155,6 +167,24 @@ public SaveRecordPawnV5(CustomPawn pawn) { } //Logger.Debug(string.Join(", ", pawn.Pawn.ideo.Ideo?.memes.Select(m => m.defName))); } + if (ModsConfig.BiotechActive) { + this.genes = new SaveRecordGenesV5() { + xenotypeDef = pawn.Pawn.genes?.Xenotype?.defName, + customXenotypeName = pawn.Pawn.genes?.CustomXenotype?.name, + endogenes = pawn.Pawn.genes?.Endogenes.ConvertAll(g => g.def.defName), + xenogenes = pawn.Pawn.genes?.Xenogenes.ConvertAll(g => g.def.defName) + }; + } + + if (pawn.Pawn?.health?.hediffSet?.hediffs != null) { + hediffs = new List(); + foreach (var hediff in pawn.Pawn.health.hediffSet.hediffs) { + hediffs.Add(new SaveRecordHediffV5() { + Pawn = pawn.Pawn, + Hediff = hediff + }); + } + } pawnCompsSaver = new PawnCompsSaver(pawn.Pawn, DefaultPawnCompRules.RulesForSaving); } @@ -175,6 +205,7 @@ public void ExposeData() { Scribe_Values.Look(ref this.skinColor, "skinColor", Color.white, false); Scribe_Values.Look(ref this.melanin, "melanin", -1.0f, false); Scribe_Values.Look(ref this.bodyType, "bodyType", null, false); + Scribe_Values.Look(ref this.headType, "headType", null, false); Scribe_Values.Look(ref this.headGraphicPath, "headGraphicPath", null, false); Scribe_Values.Look(ref this.hairDef, "hairDef", null, false); Scribe_Values.Look(ref this.hairColor, "hairColor", Color.white, false); @@ -188,9 +219,12 @@ public void ExposeData() { Scribe_Values.Look(ref this.favoriteColor, "favoriteColor", null, false); Scribe_Values.Look(ref this.biologicalAge, "biologicalAge", 0, false); Scribe_Values.Look(ref this.chronologicalAge, "chronologicalAge", 0, false); + Scribe_Values.Look(ref this.biologicalAgeInTicks, "biologicalAgeInTicks", null, false); + Scribe_Values.Look(ref this.chronologicalAgeInTicks, "chronologicalAgeInTicks", null, false); Scribe_Collections.Look(ref this.skills, "skills", LookMode.Deep, null); Scribe_Collections.Look(ref this.apparel, "apparel", LookMode.Deep, null); Scribe_Deep.Look(ref this.ideo, "ideo"); + Scribe_Deep.Look(ref this.genes, "genes"); Scribe_Collections.Look(ref this.abilities, "abilities", LookMode.Value, null); if (Scribe.mode == LoadSaveMode.Saving) { @@ -203,11 +237,11 @@ public void ExposeData() { } if (Scribe.mode == LoadSaveMode.Saving) { - Scribe_Collections.Look(ref this.injuries, "injuries", LookMode.Deep, null); + Scribe_Collections.Look(ref this.injuries, "injuries", LookMode.Deep, null); } else { if (Scribe.loader.curXmlParent["injuries"] != null) { - Scribe_Collections.Look(ref this.injuries, "injuries", LookMode.Deep, null); + Scribe_Collections.Look(ref this.injuries, "injuries", LookMode.Deep, null); } } @@ -227,6 +261,10 @@ public void ExposeData() { Scribe_Collections.Look(ref this.savedComps, "savedComps"); } + if (Scribe.mode == LoadSaveMode.Saving) { + Scribe_Collections.Look(ref this.hediffs, "hediffs", LookMode.Deep, null); + } + } } } diff --git a/Source/WidgetDropdown.cs b/Source/WidgetDropdown.cs index 5d4a654..ab4fff5 100644 --- a/Source/WidgetDropdown.cs +++ b/Source/WidgetDropdown.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using UnityEngine; @@ -45,5 +45,47 @@ public static bool Button(Rect rect, string label, bool drawBackground, bool doM GUI.color = color; return active && Widgets.ButtonInvisible(rect, false); } + public static bool ImageButton(Rect rect, Texture2D image, Vector2 imageSize, bool drawBackground = true, bool doMouseoverSound = false, bool active = true) { + TextAnchor anchor = Text.Anchor; + Color color = GUI.color; + if (drawBackground) { + Texture2D atlas = Textures.TextureButtonBGAtlas; + if (Mouse.IsOver(rect)) { + atlas = Textures.TextureButtonBGAtlasMouseover; + if (Input.GetMouseButton(0)) { + atlas = Textures.TextureButtonBGAtlasClick; + } + } + Widgets.DrawAtlas(rect, atlas); + Rect indicator = new Rect(rect.xMax - 21, rect.MiddleY() - 4, 11, 8); + GUI.DrawTexture(indicator, Textures.TextureDropdownIndicator); + } + if (doMouseoverSound) { + MouseoverSounds.DoRegion(rect); + } + if (!drawBackground) { + GUI.color = Style.ColorButton; + if (Mouse.IsOver(rect)) { + GUI.color = Style.ColorButtonHighlight; + } + } + if (drawBackground) { + Text.Anchor = TextAnchor.MiddleCenter; + } + else { + Text.Anchor = TextAnchor.MiddleLeft; + } + if (drawBackground) { + GUI.DrawTexture(new Rect(rect.x + 8, rect.MiddleY() - imageSize.y * 0.5f, imageSize.x, imageSize.y), image); + } + else { + GUI.DrawTexture(new Rect(rect.x, rect.MiddleY() - imageSize.y * 0.5f, imageSize.x, imageSize.y), image); + Rect indicator = new Rect(rect.xMax - 11, rect.MiddleY() - 4, 11, 8); + GUI.DrawTexture(indicator, Textures.TextureDropdownIndicator); + } + Text.Anchor = anchor; + GUI.color = color; + return active && Widgets.ButtonInvisible(rect, false); + } } } diff --git a/Source/WidgetNumberField.cs b/Source/WidgetNumberField.cs index 7babb88..092adbe 100644 --- a/Source/WidgetNumberField.cs +++ b/Source/WidgetNumberField.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -162,7 +162,7 @@ public void Draw(Rect rect, int value) { } // Draw the decrement button. - Rect buttonRect = new Rect(rect.x - 17, rect.y + 6, 16, 16); + Rect buttonRect = new Rect(rect.x - 17, rect.MiddleY() - 8, 16, 16); if (value == minValue) { GUI.color = Style.ColorButtonDisabled; } @@ -185,7 +185,7 @@ public void Draw(Rect rect, int value) { } // Draw the increment button. - buttonRect = new Rect(rect.x + rect.width + 1, rect.y + 6, 16, 16); + buttonRect = new Rect(rect.x + rect.width + 1, rect.MiddleY() - 8, 16, 16); if (value == maxValue) { GUI.color = Style.ColorButtonDisabled; } @@ -197,8 +197,8 @@ public void Draw(Rect rect, int value) { GUI.color = Style.ColorButton; } } + GUI.DrawTexture(buttonRect, Textures.TextureButtonNext); if (value != maxValue) { - GUI.DrawTexture(buttonRect, Textures.TextureButtonNext); if (Widgets.ButtonInvisible(buttonRect, false)) { SoundDefOf.Tick_Tiny.PlayOneShotOnCamera(); int amount = Event.current.shift ? 10 : 1; diff --git a/Source/WidgetTable.cs b/Source/WidgetTable.cs index 2b2a27d..dca5682 100644 --- a/Source/WidgetTable.cs +++ b/Source/WidgetTable.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; @@ -250,11 +250,12 @@ public void Draw(IEnumerable rowGroups) { } foreach (T row in group.Rows) { - if (scrollTo != null && row == scrollTo) { + bool setScrollToCursors = (scrollTo != null && EqualityComparer.Default.Equals(row, scrollTo)); + if (setScrollToCursors) { scrollToCursorTop = cursor; } cursor = DrawRow(cursor, row, index); - if (scrollTo != null && row == scrollTo) { + if (setScrollToCursors) { scrollToCursorBottom = cursor; } index++; @@ -268,9 +269,13 @@ public void Draw(IEnumerable rowGroups) { // Scroll to the specific row, if any. Need to do this after all of the rows have been drawn. if (scrollTo != null) { - ScrollTo(scrollToCursorTop.Value, scrollToCursorBottom.Value); + if (scrollToCursorBottom.HasValue && scrollToCursorTop.HasValue) { + ScrollTo(scrollToCursorTop.Value, scrollToCursorBottom.Value); + } scrollTo = null; } + + scrollTo = null; } protected void ScrollTo(float top, float bottom) { float contentHeight = bottom - top; @@ -312,7 +317,7 @@ protected float DrawRow(float cursor, T row, int index) { if (cursor + rowRect.height >= scrollView.Position.y && cursor <= scrollView.Position.y + scrollView.ViewHeight) { GUI.color = (index % 2 == 0) ? RowColor : AlternateRowColor; - if (row == Selected && SelectedRowColor.a != 0) { + if (EqualityComparer.Default.Equals(row, Selected) && SelectedRowColor.a != 0) { GUI.color = SelectedRowColor; } if (GUI.color.a != 0) { diff --git a/dist.bat b/dist.bat index 639552a..b6f87c8 100644 --- a/dist.bat +++ b/dist.bat @@ -2,9 +2,10 @@ if exist bin\Release\EdBPrepareCarefully.dll ( robocopy Resources dist\EdBPrepareCarefully\ /e /MIR xcopy LICENSE dist\EdBPrepareCarefully\ /Y - xcopy bin\Release\EdBPrepareCarefully.dll dist\EdBPrepareCarefully\1.3\Assemblies\ /Y - xcopy Libraries\Harmony\2.0\0Harmony.dll dist\EdBPrepareCarefully\Common\Assemblies\ /Y + xcopy bin\Release\EdBPrepareCarefully.dll dist\EdBPrepareCarefully\1.4\Assemblies\ /Y + xcopy Libraries\Harmony\2.2.2\0Harmony.dll dist\EdBPrepareCarefully\Common\Assemblies\ /Y xcopy Libraries\EdBPrepareCarefully\1.2\EdBPrepareCarefully.dll dist\EdBPrepareCarefully\1.2\Assemblies\ /Y + xcopy Libraries\EdBPrepareCarefully\1.3\EdBPrepareCarefully.dll dist\EdBPrepareCarefully\1.3\Assemblies\ /Y xcopy THIRD-PARTY-LICENSES dist\EdBPrepareCarefully\Common\Assemblies\ /Y del dist\EdBPrepareCarefully\Assemblies\README.md del dist\EdBPrepareCarefully\Latest\Assemblies\README.md From 7fe078ff9f3b95e0f3720da6e663974050b68e9a Mon Sep 17 00:00:00 2001 From: edbmods Date: Sat, 24 Dec 2022 08:59:12 -0800 Subject: [PATCH 2/5] Updated assembly info and libraries readme --- Libraries/README.md | 4 ++-- Properties/AssemblyInfo.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Libraries/README.md b/Libraries/README.md index 9e8af8f..a9f121e 100644 --- a/Libraries/README.md +++ b/Libraries/README.md @@ -10,6 +10,6 @@ The solution has dependencies on the following RimWorld DLLs: Copy those dependencies from the RimWorld game directory into the "Libraries" directory. Be sure to make _copies_ of the originals--don't accidentally move/delete them from the original game directory. The solution also has a dependency on the following third-party DLL: -- 0Harmony.dll (version 2.0.2) +- 0Harmony.dll (version 2.2.2) -The Harmony DLL is available from https://github.com/pardeike/Harmony/releases/tag/v2.0.2.0 and should be placed in the "Libraries/Harmony/2.0" directory. Be sure to use the "Release/net472" version. +The Harmony DLL is available from https://github.com/pardeike/Harmony/releases/tag/v2.2.2.0 and should be placed in the "Libraries/Harmony/2.2.2" directory. Be sure to use the "net472" version. diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index aa7f086..7bc2d62 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -6,7 +6,7 @@ [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("EdB Prepare Carefully Mod")] -[assembly: AssemblyCopyright("Copyright © 2014-2021")] +[assembly: AssemblyCopyright("Copyright © 2014-2023")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -14,4 +14,4 @@ [assembly: AssemblyVersion("1.1.1")] // Increment for each new release -[assembly: AssemblyFileVersion("1.3.12")] +[assembly: AssemblyFileVersion("1.4.1")] From 8666d8bd7944d56dd52f8561dda0247b56a0f476 Mon Sep 17 00:00:00 2001 From: edbmods Date: Sat, 24 Dec 2022 11:59:10 -0800 Subject: [PATCH 3/5] Fixed broken Ideo panel for Baby developmental stage --- Source/PanelIdeo.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Source/PanelIdeo.cs b/Source/PanelIdeo.cs index ae99a88..c93fee5 100644 --- a/Source/PanelIdeo.cs +++ b/Source/PanelIdeo.cs @@ -45,7 +45,7 @@ public float Measure() { } public override bool IsVisible(State state) { - return ModsConfig.IdeologyActive; + return ModsConfig.IdeologyActive && !state.CurrentPawn.Pawn.DevelopmentalStage.Baby(); } public override float Draw(State state, float y) { @@ -73,7 +73,9 @@ public override float Draw(State state, float y) { Find.WindowStack.Add(dialog); }; FieldIdeo.DrawIconFunc = (Rect rect) => { - ideo.DrawIcon(rect); + if (ideo != null) { + ideo.DrawIcon(rect); + } }; FieldIdeo.IconSizeFunc = () => new Vector2(32, 32); From ce7751d0e28b40cbd689d653d2df31022f3f199b Mon Sep 17 00:00:00 2001 From: edbmods Date: Sat, 24 Dec 2022 15:45:26 -0800 Subject: [PATCH 4/5] Fixed problem with MechlinkImplant hediff --- EdBPrepareCarefully.csproj | 1 + Source/CustomPawn.cs | 28 ++++++++++++++---- Source/Implant.cs | 40 +++++++++++++++++++------- Source/Version3/SaveRecordImplantV3.cs | 2 +- Source/Version5/PawnLoaderV5.cs | 21 ++++++++++++-- Source/Version5/SaveRecordImplantV5.cs | 32 +++++++++++++++++++++ Source/Version5/SaveRecordPawnV5.cs | 8 +++--- 7 files changed, 108 insertions(+), 24 deletions(-) create mode 100644 Source/Version5/SaveRecordImplantV5.cs diff --git a/EdBPrepareCarefully.csproj b/EdBPrepareCarefully.csproj index 99ab37e..ce0f28e 100644 --- a/EdBPrepareCarefully.csproj +++ b/EdBPrepareCarefully.csproj @@ -224,6 +224,7 @@ + diff --git a/Source/CustomPawn.cs b/Source/CustomPawn.cs index 88b4eab..da44677 100644 --- a/Source/CustomPawn.cs +++ b/Source/CustomPawn.cs @@ -361,9 +361,17 @@ public void InitializeInjuriesAndImplantsFromPawn(Pawn pawn) { implant.recipe = implantRecipe; implant.BodyPartRecord = hediff.Part; implant.Hediff = hediff; + implant.HediffDef = hediff?.def; implants.Add(implant); //Logger.Debug("Found implant recipes for {" + hediff.def.defName + "} for part {" + hediff.Part?.LabelCap + "}"); } + else if (hediff.def.defName == "MechlinkImplant") { + Implant implant = new Implant(); + implant.BodyPartRecord = hediff.Part; + implant.Hediff = hediff; + implant.HediffDef = hediff?.def; + implants.Add(implant); + } else if (hediff.def.defName != "MissingBodyPart") { Logger.Warning("Could not add hediff {" + hediff.def.defName + "} to the pawn because no recipe adds it to the body part {" + (hediff.Part?.def?.defName ?? "WholeBody") + "}"); } @@ -1551,12 +1559,16 @@ public void AddApparelToPawn(Pawn targetPawn, PawnLayer layer) { } public void AddInjury(Injury injury) { - injuries.Add(injury); - bodyParts.Add(injury); + AddInjuryDirect(injury); ApplyInjuriesAndImplantsToPawn(); InitializeInjuriesAndImplantsFromPawn(this.pawn); } + public void AddInjuryDirect(Injury injury) { + injuries.Add(injury); + bodyParts.Add(injury); + } + public void UpdateImplants(List implants) { List implantsToRemove = new List(); foreach (var bodyPart in bodyParts) { @@ -1575,7 +1587,7 @@ public void UpdateImplants(List implants) { InitializeInjuriesAndImplantsFromPawn(this.pawn); } - protected void ApplyInjuriesAndImplantsToPawn() { + public void ApplyInjuriesAndImplantsToPawn() { this.pawn.health.Reset(); List injuriesToRemove = new List(); foreach (var injury in injuries) { @@ -1631,14 +1643,20 @@ public void RemoveCustomBodyParts(BodyPartRecord part) { } public void AddImplant(Implant implant) { + if (AddImplantDirect(implant)) { + ApplyInjuriesAndImplantsToPawn(); + InitializeInjuriesAndImplantsFromPawn(this.pawn); + } + } + public bool AddImplantDirect(Implant implant) { if (implant != null && implant.BodyPartRecord != null) { implants.Add(implant); bodyParts.Add(implant); - ApplyInjuriesAndImplantsToPawn(); - InitializeInjuriesAndImplantsFromPawn(this.pawn); + return true; } else { Logger.Warning("Discarding implant because of missing body part: " + implant.BodyPartRecord.def.defName); + return false; } } diff --git a/Source/Implant.cs b/Source/Implant.cs index da3c6b8..f8efab2 100644 --- a/Source/Implant.cs +++ b/Source/Implant.cs @@ -12,6 +12,7 @@ public class Implant : CustomBodyPart { public string label = ""; public RecipeDef recipe = null; protected Hediff hediff = null; + protected HediffDef hediffDef = null; protected string tooltip; @@ -33,6 +34,12 @@ public Hediff Hediff { set => hediff = value; } + public HediffDef HediffDef { + get => hediffDef; + set => hediffDef = value; + } + + override public string ChangeName { get { return Label; @@ -84,7 +91,7 @@ public override bool Equals(System.Object obj) { return false; } - return (BodyPartRecord == option.BodyPartRecord) && (recipe == option.recipe); + return (BodyPartRecord == option.BodyPartRecord) && (recipe == option.recipe) && (hediffDef == option.hediffDef); } public bool Equals(Implant option) { @@ -92,22 +99,25 @@ public bool Equals(Implant option) { return false; } - return (BodyPartRecord == option.BodyPartRecord) && (recipe == option.recipe); - } - - public override int GetHashCode() { - unchecked { - int a = BodyPartRecord != null ? BodyPartRecord.GetHashCode() : 0; - int b = recipe != null ? recipe.GetHashCode() : 0; - return 31 * a + b; - } + return (BodyPartRecord == option.BodyPartRecord) && (recipe == option.recipe) && (hediffDef == option.hediffDef); } public override void AddToPawn(CustomPawn customPawn, Pawn pawn) { - if (recipe != null && BodyPartRecord != null) { + Logger.Debug("Adding implant to pawn, recipe = " + this.recipe?.defName + ", hediff = " + this.hediffDef ?.defName); + if (BodyPartRecord == null) { + Logger.Warning("Could not add implant to pawn because no BodyPartRecord is defined"); + } + if (recipe != null) { this.hediff = HediffMaker.MakeHediff(recipe.addsHediff, pawn, BodyPartRecord); pawn.health.AddHediff(hediff, BodyPartRecord, new DamageInfo?()); } + else if (hediffDef != null) { + this.hediff = HediffMaker.MakeHediff(hediffDef, pawn, BodyPartRecord); + pawn.health.AddHediff(hediff, BodyPartRecord, new DamageInfo?()); + } + else { + Logger.Warning("Could not add implant to pawn because no RecipeDef or HediffDef is defined"); + } } public bool ReplacesPart { @@ -153,6 +163,14 @@ protected void InitializeTooltip() { } tooltip = stringBuilder.ToString(); } + + public override int GetHashCode() { + var hashCode = -775691452; + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(BodyPartRecord); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(recipe); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(hediffDef); + return hashCode; + } } } diff --git a/Source/Version3/SaveRecordImplantV3.cs b/Source/Version3/SaveRecordImplantV3.cs index 5f451f6..b88cb37 100644 --- a/Source/Version3/SaveRecordImplantV3.cs +++ b/Source/Version3/SaveRecordImplantV3.cs @@ -1,4 +1,4 @@ -using RimWorld; +using RimWorld; using System; using System.Collections.Generic; using System.Linq; diff --git a/Source/Version5/PawnLoaderV5.cs b/Source/Version5/PawnLoaderV5.cs index 78577ed..25f5f8c 100644 --- a/Source/Version5/PawnLoaderV5.cs +++ b/Source/Version5/PawnLoaderV5.cs @@ -555,7 +555,7 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn); for (int i = 0; i < record.implants.Count; i++) { - SaveRecordImplantV3 implantRecord = record.implants[i]; + SaveRecordImplantV5 implantRecord = record.implants[i]; UniqueBodyPart uniqueBodyPart = healthOptions.FindBodyPartByName(implantRecord.bodyPart, implantRecord.bodyPartIndex != null ? implantRecord.bodyPartIndex.Value : 0); if (uniqueBodyPart == null) { uniqueBodyPart = FindReplacementBodyPart(healthOptions, implantRecord.bodyPart); @@ -590,7 +590,20 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { implant.BodyPartRecord = bodyPart; implant.recipe = recipeDef; implant.label = implant.Label; - pawn.AddImplant(implant); + pawn.AddImplantDirect(implant); + } + else if (implantRecord.hediffDef != null) { + HediffDef hediffDef = DefDatabase.GetNamedSilentFail(implantRecord.hediffDef); + if (hediffDef != null) { + Implant implant = new Implant(); + implant.BodyPartRecord = bodyPart; + implant.label = implant.Label; + implant.HediffDef = hediffDef; + pawn.AddImplantDirect(implant); + } + else { + Logger.Warning("Could not add implant to pawn because the specified HediffDef {" + implantRecord.hediffDef + "} for the implant was not found"); + } } } @@ -640,12 +653,14 @@ public CustomPawn ConvertSaveRecordToPawn(SaveRecordPawnV5 record) { } } if (isValid) { - pawn.AddInjury(injury); + pawn.AddInjuryDirect(injury); } else { Logger.Debug("Did not add invalid injury"); } } + pawn.ApplyInjuriesAndImplantsToPawn(); + pawn.InitializeInjuriesAndImplantsFromPawn(pawn.Pawn); // Ideoligion Certainty try { diff --git a/Source/Version5/SaveRecordImplantV5.cs b/Source/Version5/SaveRecordImplantV5.cs new file mode 100644 index 0000000..67690a7 --- /dev/null +++ b/Source/Version5/SaveRecordImplantV5.cs @@ -0,0 +1,32 @@ +using RimWorld; +using System; +using System.Collections.Generic; +using System.Linq; +using UnityEngine; +using Verse; + +namespace EdB.PrepareCarefully { + public class SaveRecordImplantV5 : IExposable { + public string bodyPart = null; + public int? bodyPartIndex = null; + public string recipe = null; + public string hediffDef = null; + + public SaveRecordImplantV5() { + } + + public SaveRecordImplantV5(Implant option) { + this.bodyPart = option.BodyPartRecord.def.defName; + this.recipe = option.recipe != null ? option.recipe.defName : null; + this.hediffDef = option?.Hediff?.def?.defName; + } + + public void ExposeData() { + Scribe_Values.Look(ref this.bodyPart, "bodyPart", null, false); + Scribe_Values.Look(ref this.bodyPartIndex, "bodyPartIndex", null, false); + Scribe_Values.Look(ref recipe, "recipe", null, false); + Scribe_Values.Look(ref hediffDef, "hediff", null, false); + } + } +} + diff --git a/Source/Version5/SaveRecordPawnV5.cs b/Source/Version5/SaveRecordPawnV5.cs index 0b47a09..e7362e6 100644 --- a/Source/Version5/SaveRecordPawnV5.cs +++ b/Source/Version5/SaveRecordPawnV5.cs @@ -45,7 +45,7 @@ public class SaveRecordPawnV5 : IExposable { public List apparelColors = new List(); public bool randomInjuries = true; public bool randomRelations = false; - public List implants = new List(); + public List implants = new List(); public List injuries = new List(); public SaveRecordIdeoV5 ideo; public List abilities = new List(); @@ -132,7 +132,7 @@ public SaveRecordPawnV5(CustomPawn pawn) { } OptionsHealth healthOptions = PrepareCarefully.Instance.Providers.Health.GetOptions(pawn); foreach (Implant implant in pawn.Implants) { - var saveRecord = new SaveRecordImplantV3(implant); + var saveRecord = new SaveRecordImplantV5(implant); if (implant.BodyPartRecord != null) { UniqueBodyPart part = healthOptions.FindBodyPartsForRecord(implant.BodyPartRecord); if (part != null && part.Index > 0) { @@ -228,11 +228,11 @@ public void ExposeData() { Scribe_Collections.Look(ref this.abilities, "abilities", LookMode.Value, null); if (Scribe.mode == LoadSaveMode.Saving) { - Scribe_Collections.Look(ref this.implants, "implants", LookMode.Deep, null); + Scribe_Collections.Look(ref this.implants, "implants", LookMode.Deep, null); } else { if (Scribe.loader.curXmlParent["implants"] != null) { - Scribe_Collections.Look(ref this.implants, "implants", LookMode.Deep, null); + Scribe_Collections.Look(ref this.implants, "implants", LookMode.Deep, null); } } From 75dd732b25b2d2a1352bd10e2eafe7d56ce1c9f4 Mon Sep 17 00:00:00 2001 From: edbmods Date: Sat, 24 Dec 2022 22:29:27 -0800 Subject: [PATCH 5/5] Fixed panel heights to fill the vertical space. Fixed health option labels. Removed class for old Age panel --- EdBPrepareCarefully.csproj | 1 - Source/PanelHealth.cs | 4 +- Source/PanelModuleAge.cs | 147 -------------------------------- Source/ProviderHealthOptions.cs | 2 +- Source/TabViewPawns.cs | 9 +- 5 files changed, 6 insertions(+), 157 deletions(-) delete mode 100644 Source/PanelModuleAge.cs diff --git a/EdBPrepareCarefully.csproj b/EdBPrepareCarefully.csproj index ce0f28e..d07e32e 100644 --- a/EdBPrepareCarefully.csproj +++ b/EdBPrepareCarefully.csproj @@ -89,7 +89,6 @@ - diff --git a/Source/PanelHealth.cs b/Source/PanelHealth.cs index cb4c0a8..eb23752 100644 --- a/Source/PanelHealth.cs +++ b/Source/PanelHealth.cs @@ -297,8 +297,8 @@ public void DrawAddButton(float y, float width) { CancelButtonLabel = "EdB.PC.Common.Cancel".Translate(), HeaderLabel = "EdB.PC.Dialog.Injury.Header".Translate(), NameFunc = (InjuryOption option) => { - return option.HediffDef.defName; - //return option.Label; + //return option.HediffDef.defName; + return option.Label; }, DescriptionFunc = (InjuryOption option) => { return option.HediffDef?.description; diff --git a/Source/PanelModuleAge.cs b/Source/PanelModuleAge.cs deleted file mode 100644 index 38c603f..0000000 --- a/Source/PanelModuleAge.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using UnityEngine; -using RimWorld; -using Verse; -using Verse.Sound; - -namespace EdB.PrepareCarefully { - public class PanelModuleAge : PanelModule { - public static readonly Vector2 FieldPadding = new Vector2(6, 6); - public delegate void UpdateAgeHandler(int age); - - public event UpdateAgeHandler BiologicalAgeUpdated; - public event UpdateAgeHandler ChronologicalAgeUpdated; - - private ProviderAgeLimits providerAgeLimits = PrepareCarefully.Instance.Providers.AgeLimits; - - protected static Rect RectBiologicalAgeLabel; - protected static Rect RectBiologicalAgeField; - protected static Rect RectChronologicalAgeLabel; - protected static Rect RectChronologicalAgeField; - - private WidgetNumberField biologicalField; - private WidgetNumberField chronologicalField; - - public PanelModuleAge() { - biologicalField = new WidgetNumberField() { - DragSlider = new DragSlider(0.4f, 20, 100), - MinValue = 14, - MaxValue = 99, - UpdateAction = (int value) => { - UpdateBiologicalAge(value); - } - }; - chronologicalField = new WidgetNumberField() { - DragSlider = new DragSlider(0.4f, 15, 100), - MinValue = 14, - MaxValue = Constraints.AgeChronologicalMax, - UpdateAction = (int value) => { - UpdateChronologicalAge(value); - } - }; - } - - protected void UpdateBiologicalAge(int value) { - BiologicalAgeUpdated(value); - } - - protected void UpdateChronologicalAge(int value) { - ChronologicalAgeUpdated(value); - } - - public override void Resize(float width) { - base.Resize(width); - - float arrowPadding = 1; - float arrowWidth = Textures.TextureButtonNext.width; - float bioWidth = 32; - float chronoWidth = 48; - - float extendedArrowSize = arrowPadding + arrowWidth; - float extendedFieldSize = extendedArrowSize * 2; - - float usedSpace = (extendedFieldSize * 2) + bioWidth + chronoWidth; - - float availableSpace = width - usedSpace; - float spacing = availableSpace / 3; - - float idealSpace = 15; - float extraFieldWidth = 0; - if (spacing > idealSpace) { - float extra = (spacing - idealSpace) * 3; - extraFieldWidth += Mathf.Floor(extra / 2); - spacing = idealSpace; - } - else { - spacing = Mathf.Floor(spacing); - } - float fieldHeight = 28; - - GameFont saveFont = Text.Font; - Text.Font = GameFont.Tiny; - Vector2 bioLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Biological".Translate()); - Vector2 chronoLabelSize = Text.CalcSize("EdB.PC.Panel.Age.Chronological".Translate()); - Text.Font = saveFont; - - float labelHeight = Mathf.Max(bioLabelSize.y, chronoLabelSize.y); - float top = 0; - float fieldTop = top + labelHeight; - - RectBiologicalAgeField = new Rect(spacing + extendedArrowSize, fieldTop, bioWidth + extraFieldWidth, fieldHeight); - RectChronologicalAgeField = new Rect(RectBiologicalAgeField.xMax + extendedArrowSize + - spacing + extendedArrowSize, fieldTop, chronoWidth + extraFieldWidth, fieldHeight); - - RectBiologicalAgeLabel = new Rect(RectBiologicalAgeField.MiddleX() - bioLabelSize.x / 2, - RectBiologicalAgeField.y - bioLabelSize.y, bioLabelSize.x, bioLabelSize.y); - RectChronologicalAgeLabel = new Rect(RectChronologicalAgeField.MiddleX() - chronoLabelSize.x / 2, - RectChronologicalAgeField.y - chronoLabelSize.y, chronoLabelSize.x, chronoLabelSize.y); - } - - public float Measure() { - return 0; - } - - public override bool IsVisible(State state) { - return base.IsVisible(state); - } - - public override float Draw(State state, float y) { - float top = y; - y += Margin.y; - - // Update field values. - CustomPawn pawn = state.CurrentPawn; - int maxAge = providerAgeLimits.MaxAgeForPawn(pawn.Pawn); - int minAge = providerAgeLimits.MinAgeForPawn(pawn.Pawn); - chronologicalField.MinValue = pawn.BiologicalAge; - biologicalField.MinValue = minAge; - biologicalField.MaxValue = pawn.ChronologicalAge < maxAge ? pawn.ChronologicalAge : maxAge; - - // Age labels. - Text.Font = GameFont.Tiny; - GUI.color = Style.ColorText; - Widgets.Label(RectBiologicalAgeLabel.OffsetBy(0, y), "EdB.PC.Panel.Age.Biological".Translate()); - Widgets.Label(RectChronologicalAgeLabel.OffsetBy(0, y), "EdB.PC.Panel.Age.Chronological".Translate()); - Text.Font = GameFont.Small; - GUI.color = Color.white; - - // Biological age field. - Rect fieldRect = RectBiologicalAgeField; - biologicalField.Draw(fieldRect.OffsetBy(0, y), pawn.BiologicalAge); - - // Chronological age field. - fieldRect = RectChronologicalAgeField; - chronologicalField.Draw(fieldRect.OffsetBy(0, y), pawn.ChronologicalAge); - - y += RectBiologicalAgeLabel.height; - y += fieldRect.height; - - y += Margin.y; - return y - top; - } - } -} diff --git a/Source/ProviderHealthOptions.cs b/Source/ProviderHealthOptions.cs index d84a216..038f3f7 100644 --- a/Source/ProviderHealthOptions.cs +++ b/Source/ProviderHealthOptions.cs @@ -12,7 +12,7 @@ namespace EdB.PrepareCarefully { public class ProviderHealthOptions { protected Dictionary optionsLookup = new Dictionary(); protected HashSet excludedOptions = new HashSet() { - "VatLearning", "VatGrowing", "Pregnant", "PsychicBond", "PsychicBondTorn", "ResearchCommand", "Animal_Flu", "Stillborn" + "VatLearning", "VatGrowing", "Pregnant", "PsychicBond", "PsychicBondTorn", "ResearchCommand", "Animal_Flu", "Stillborn", "Animal_Plague" }; public OptionsHealth GetOptions(CustomPawn pawn) { diff --git a/Source/TabViewPawns.cs b/Source/TabViewPawns.cs index 890d98a..df379d5 100644 --- a/Source/TabViewPawns.cs +++ b/Source/TabViewPawns.cs @@ -25,7 +25,6 @@ public class TabViewPawns : TabViewBase { public PanelAbilities PanelAbilities { get; set; } public PanelScrollingContent PanelColumn1 { get; set; } public PanelScrollingContent PanelColumn2 { get; set; } - //public PanelModuleAge PanelAge { get; set; } public PanelTitles PanelTitles { get; set; } public TabViewPawns(bool largeUI) { @@ -57,7 +56,6 @@ protected void InitializePanels(bool largeUI) { PanelAbilities = new PanelAbilities(); PanelAge = new PanelAge(); PanelXenotype = new PanelXenotype(); - //PanelAge = new PanelModuleAge(); PanelTitles = new PanelTitles(); if (largeUI) { TwoColumnLayout(); @@ -185,10 +183,9 @@ protected override void Resize(Rect rect) { float x = PanelColonyPawns.PanelRect.xMax + panelMargin.x; float top = PanelRandomize.PanelRect.yMax + panelMargin.y; - // Age and Appearance + // Appearance float columnSize1 = 226; - PanelAppearance.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, PanelRandomize.PanelRect.yMax + panelMargin.y, columnSize1, 414)); - //PanelAppearance.Resize(new Rect(x, top, columnSize1, 490)); + PanelAppearance.Resize(new Rect(PanelColonyPawns.PanelRect.xMax + panelMargin.x, PanelRandomize.PanelRect.yMax + panelMargin.y, columnSize1, 526)); x += columnSize1 + panelMargin.x; float columnSize2 = 304; @@ -204,7 +201,7 @@ protected override void Resize(Rect rect) { float columnSize3 = 218; PanelSkills.Resize(new Rect(x, top, columnSize3, 362)); PanelIncapable.Resize(new Rect(PanelSkills.PanelRect.xMin, PanelSkills.PanelRect.yMax + panelMargin.y, - columnSize3, 116)); + columnSize3, 152)); } public void ResizeTabView() {