diff --git a/1.6/1.6/Defs/BackstoryDefs/BackstoryDef.xml b/1.6/1.6/Defs/BackstoryDefs/BackstoryDef.xml
new file mode 100644
index 0000000..3854c9c
--- /dev/null
+++ b/1.6/1.6/Defs/BackstoryDefs/BackstoryDef.xml
@@ -0,0 +1,39 @@
+
+
+
+
+ ARA_QUEEN_Thin
+ ARA_QUEEN_Thin
+ true
+
+
+
+
+
+ Arachnae_BS
+ 阿拉克涅虫族
+ 阿拉克涅虫族
+ [PAWN_nameDef]是阿拉克涅虫族.
+ Childhood
+
+
+ ArachnaeQueen_spawnCategoriesA
+
+
+ true
+
+
+
+
+ Arachnae_BS_B
+ 阿拉克涅女皇种
+ 阿拉克涅女皇种
+ [PAWN_nameDef]是阿拉克涅女皇种.
+ Adulthood
+
+
+ ArachnaeQueen_spawnCategoriesB
+
+
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/BodyTypeDefs/ARA_BodyTypes.xml b/1.6/1.6/Defs/BodyTypeDefs/ARA_BodyTypes.xml
index 8471815..94bc418 100644
--- a/1.6/1.6/Defs/BodyTypeDefs/ARA_BodyTypes.xml
+++ b/1.6/1.6/Defs/BodyTypeDefs/ARA_BodyTypes.xml
@@ -1,7 +1,7 @@
-
+
+
+
+ ARA_QUEEN_Thin
+ (0.09, 0.34)
+ Pawns/ARA_HiveQueen/Bodies/Naked_Thin
+ Pawns/ARA_HiveQueen/Bodies/Naked_Thin
+ (4, 4)
+
+
+ South
+ Torso
+ (0,0,-0.15)
+ 0.15
+ (1,0,0,1)
+
+
+ South
+ LeftLeg
+ (0.075,0,-0.375)
+ 0.1
+ (0,1,0,1)
+
+
+ South
+ RightLeg
+ (-0.075,0,-0.375)
+ 0.1
+ (0,0,1,1)
+
+
+ South
+ LeftShoulder
+ (0.1,0,0.05)
+ 0.075
+ (1,1,0,1)
+
+
+ South
+ RightShoulder
+ (-0.1,0,0.05)
+ 0.075
+ (1,0,1,1)
+
+
+ South
+ FullHead
+ (0, 0, 0)
+ 0.18
+ Head
+ (0,1,1,1)
+
+
+ South
+ RightEye
+ false
+ (-0.092, 0, 0.18)
+ 0
+ Head
+ (1,1,1,1)
+
+
+ South
+ LeftEye
+ false
+ (0.092, 0, 0.18)
+ 0
+ Head
+ (0,0,0,1)
+
+
+ West
+ FullHead
+ (0, 0, 0)
+ 0.2
+ Head
+ (0,1,1,1)
+
+
+ West
+ Torso
+ (0,0,-0.13)
+ 0.2
+ (1,0,0,1)
+
+
+ West
+ LeftShoulder
+ (-0.05,0,0.1)
+ 0.15
+ false
+ (1,1,0,1)
+
+
+ East
+ LeftLeg
+ (-0.05,0,-0.3)
+ 0.15
+ false
+ (0,1,0,1)
+
+
+ East
+ RightShoulder
+ (0.05,0,0.1)
+ 0.15
+ false
+ (1,0,1,1)
+
+
+ West
+ RightLeg
+ (0.05,0,-0.3)
+ 0.15
+ false
+ (0,0,1,1)
+
+
+ East
+ RightEye
+ false
+ (0.1, 0, 0.18)
+ 0
+ Head
+ (1,1,1,1)
+
+
+ East
+ RightEye
+ false
+ true
+ (0.1, 0, 0.18)
+ 0
+ Head
+ (1,1,1,1)
+
+
+ West
+ LeftEye
+ false
+ (-0.1, 0, 0.18)
+ 0
+ Head
+ (0,0,0,1)
+
+
+ West
+ LeftEye
+ false
+ (-0.1, 0, 0.18)
+ true
+ 0
+ Head
+ (0,0,0,1)
+
+
+
+
+ (-.15, 0, 0)
+ PlatformRestraint0
+
+
+ (.15, 0, 0)
+ PlatformRestraint1
+
+
+ (.1, 0, -.5)
+ PlatformRestraint2
+
+
+ (-.1, 0, -.5)
+ PlatformRestraint3
+
+
+
+
+ (-.15, 0, 0)
+ PlatformRestraint0
+
+
+ (.15, 0, 0)
+ PlatformRestraint1
+
+
+ (.1, 0, -.5)
+ PlatformRestraint2
+
+
+ (-.1, 0, -.5)
+ PlatformRestraint3
+
+
\ No newline at end of file
diff --git a/1.6/1.6/Defs/HeadType_Defs/ARA_HeadTypeDefs.xml b/1.6/1.6/Defs/HeadType_Defs/ARA_HeadTypeDefs.xml
index 800929e..ea612ad 100644
--- a/1.6/1.6/Defs/HeadType_Defs/ARA_HeadTypeDefs.xml
+++ b/1.6/1.6/Defs/HeadType_Defs/ARA_HeadTypeDefs.xml
@@ -6,19 +6,10 @@
-
- Male
-
-
Female
-
- ARA_QUEEN_Male_AverageNormalA
- Textures/Pawns/General/Invisible/Inv
-
-
ARA_QUEEN_Female_AverageNormalA
Textures/Pawns/General/Invisible/Inv
diff --git a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceDefBase.xml b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceDefBase.xml
index c3839f4..e78a058 100644
--- a/1.6/1.6/Defs/ThingDef_Races/ARA_RaceDefBase.xml
+++ b/1.6/1.6/Defs/ThingDef_Races/ARA_RaceDefBase.xml
@@ -25,16 +25,29 @@
- ArachnaeQueen_Race
-
- 女皇种是阿拉克涅虫巢中最庞大的生命形态,肩负着领导整个阿拉克涅虫巢的任务,并根据虫巢的需求诞下不同类型的子嗣。她是虫群蜂巢意识金字塔中顶端的存在,如果死亡则会导致殖民地中所有阿拉克涅虫族的覆灭!
-
+
+
+ 15000
+ ARA_EggSpew
+
+
+
+ You've discovered the center of the nest. It's guarded by a massive insect queen!
+ ThreatBig
+
+
+ ArachnaeQueen_Race
+
+ 女皇种是阿拉克涅虫巢中最庞大的生命形态,肩负着领导整个阿拉克涅虫巢的任务,并根据虫巢的需求诞下不同类型的子嗣。她是虫群蜂巢意识金字塔中顶端的存在,如果死亡则会导致殖民地中所有阿拉克涅虫族的覆灭!
+
+
+
@@ -219,26 +232,6 @@
(0,0)
-
- (0, -0.125)
-
-
- (0, -0.125)
-
-
- (0, -0.125)
-
- (-0.1, 0)
- (-0.055,0)
-
-
-
- (0, -0.125)
-
- (0.1, 0)
- (0.055,0)
-
-
@@ -412,7 +405,6 @@
SleptInCold
SleptInHeat
Ugly
-
AteKibble
AteInsectMeatDirect
AteInsectMeatAsIngredient
diff --git a/Source/Documents/ThingDef_AlienRace.cs b/Source/Documents/ThingDef_AlienRace.cs
new file mode 100644
index 0000000..3dc408c
--- /dev/null
+++ b/Source/Documents/ThingDef_AlienRace.cs
@@ -0,0 +1,1182 @@
+namespace AlienRace
+{
+ using HarmonyLib;
+ using RimWorld;
+ using System;
+ using System.Collections;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using UnityEngine;
+ using Verse;
+ using System.Xml;
+ using ExtendedGraphics;
+ using JetBrains.Annotations;
+
+ public class ThingDef_AlienRace : ThingDef
+ {
+ public AlienSettings alienRace;
+
+ public override void ResolveReferences()
+ {
+ if(!this.HasComp())
+ this.comps.Add(new CompProperties(typeof(AlienPartGenerator.AlienComp)));
+ base.ResolveReferences();
+
+ if (this.alienRace.generalSettings.alienPartGenerator.customHeadDrawSize.Equals(Vector2.zero))
+ this.alienRace.generalSettings.alienPartGenerator.customHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customDrawSize;
+ if (this.alienRace.generalSettings.alienPartGenerator.customPortraitHeadDrawSize.Equals(Vector2.zero))
+ this.alienRace.generalSettings.alienPartGenerator.customPortraitHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customPortraitDrawSize;
+ if (this.alienRace.generalSettings.alienPartGenerator.headFemaleOffset.Equals(Vector2.negativeInfinity))
+ this.alienRace.generalSettings.alienPartGenerator.headFemaleOffset = this.alienRace.generalSettings.alienPartGenerator.headOffset;
+ this.alienRace.generalSettings.alienPartGenerator.headFemaleOffsetDirectional ??= this.alienRace.generalSettings.alienPartGenerator.headOffsetDirectional;
+
+ this.alienRace.generalSettings.alienPartGenerator.alienProps = this;
+
+ foreach (Type type in typeof(StyleItemDef).AllSubclassesNonAbstract())
+ if(!this.alienRace.styleSettings.ContainsKey(type))
+ this.alienRace.styleSettings.Add(type, new StyleSettings());
+
+ foreach (AlienPartGenerator.BodyAddon bodyAddon in this.alienRace.generalSettings.alienPartGenerator.bodyAddons)
+ bodyAddon.offsets.west ??= bodyAddon.offsets.east;
+
+ if (this.alienRace.generalSettings.minAgeForAdulthood < 0)
+ this.alienRace.generalSettings.minAgeForAdulthood = (float) AccessTools.Field(typeof(PawnBioAndNameGenerator), name: "MinAgeForAdulthood").GetValue(obj: null);
+
+ foreach (StatPartAgeOverride spao in this.alienRace.generalSettings.ageStatOverrides)
+ this.alienRace.generalSettings.ageStatOverride[spao.stat] = spao.overridePart;
+
+ for (int i = 0; i < this.race.lifeStageAges.Count; i++)
+ {
+ LifeStageAge lsa = this.race.lifeStageAges[i];
+
+ if (lsa is not LifeStageAgeAlien lsaa)
+ {
+ lsaa = new LifeStageAgeAlien
+ {
+ def = lsa.def,
+ minAge = lsa.minAge,
+ soundAmbience = lsa.soundAmbience,
+ soundAngry = lsa.soundAngry,
+ soundCall = lsa.soundCall,
+ soundDeath = lsa.soundDeath,
+ soundWounded = lsa.soundWounded
+ };
+
+ this.race.lifeStageAges[i] = lsaa;
+ }
+
+ if (lsaa.customDrawSize.Equals(Vector2.zero))
+ lsaa.customDrawSize = this.alienRace.generalSettings.alienPartGenerator.customDrawSize;
+
+ if (lsaa.customPortraitDrawSize.Equals(Vector2.zero))
+ lsaa.customPortraitDrawSize = this.alienRace.generalSettings.alienPartGenerator.customPortraitDrawSize;
+
+ if (lsaa.customHeadDrawSize.Equals(Vector2.zero))
+ lsaa.customHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customHeadDrawSize;
+
+ if (lsaa.customPortraitHeadDrawSize.Equals(Vector2.zero))
+ lsaa.customPortraitHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customPortraitHeadDrawSize;
+
+ if (lsaa.customFemaleDrawSize.Equals(Vector2.zero))
+ lsaa.customFemaleDrawSize = this.alienRace.generalSettings.alienPartGenerator.customFemaleDrawSize;
+
+ if (lsaa.customFemalePortraitDrawSize.Equals(Vector2.zero))
+ lsaa.customFemalePortraitDrawSize = this.alienRace.generalSettings.alienPartGenerator.customFemalePortraitDrawSize;
+
+ if (lsaa.customFemaleHeadDrawSize.Equals(Vector2.zero))
+ lsaa.customFemaleHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customFemaleHeadDrawSize;
+
+ if (lsaa.customFemalePortraitHeadDrawSize.Equals(Vector2.zero))
+ lsaa.customFemalePortraitHeadDrawSize = this.alienRace.generalSettings.alienPartGenerator.customFemalePortraitHeadDrawSize;
+
+ if (lsaa.headOffset.Equals(Vector2.zero))
+ lsaa.headOffset = this.alienRace.generalSettings.alienPartGenerator.headOffset;
+
+ if (lsaa.headFemaleOffset.Equals(Vector2.negativeInfinity))
+ lsaa.headFemaleOffset = this.alienRace.generalSettings.alienPartGenerator.headFemaleOffset;
+
+ lsaa.headOffsetDirectional ??= this.alienRace.generalSettings.alienPartGenerator.headOffsetDirectional;
+ lsaa.headOffsetDirectional.west ??= lsaa.headOffsetDirectional.east;
+
+ lsaa.headFemaleOffsetDirectional ??= this.alienRace.generalSettings.alienPartGenerator.headFemaleOffsetDirectional;
+ lsaa.headFemaleOffsetDirectional.west ??= lsaa.headFemaleOffsetDirectional.east;
+ }
+
+ //if (this.alienRace.graphicPaths.body.path == GraphicPaths.VANILLA_BODY_PATH && !this.alienRace.graphicPaths.body.GetSubGraphics().MoveNext())
+ //this.alienRace.graphicPaths.body.debug = false;
+
+ if (this.alienRace.graphicPaths.head.path == GraphicPaths.VANILLA_HEAD_PATH && !this.alienRace.graphicPaths.head.GetSubGraphics().Any())
+ {
+ foreach (HeadTypeDef headType in DefDatabase.AllDefs)
+ {
+ AlienPartGenerator.ExtendedConditionGraphic headtypeGraphic = new()
+ {
+ conditions = [new ConditionHeadType {headType = headType}],
+ path = headType.graphicPath
+ };
+
+ this.alienRace.graphicPaths.head.extendedGraphics.Add(headtypeGraphic);
+ //this.alienRace.graphicPaths.head.debug = false;
+ }
+ }
+
+ if (this.alienRace.graphicPaths.skeleton.path == GraphicPaths.VANILLA_SKELETON_PATH && !this.alienRace.graphicPaths.skeleton.GetSubGraphics().Any())
+ {
+ this.alienRace.graphicPaths.skeleton.path = string.Empty;
+
+ foreach (BodyTypeDef bodyType in this.alienRace.generalSettings.alienPartGenerator.bodyTypes)
+ this.alienRace.graphicPaths.skeleton.extendedGraphics.Add(new AlienPartGenerator.ExtendedConditionGraphic()
+ {
+ conditions = [new ConditionBodyType { bodyType = bodyType }],
+ path = bodyType.bodyDessicatedGraphicPath
+ });
+ }
+
+ void RecursiveAttributeCheck(Type type, Traverse instance, string debug)
+ {
+ if (type == typeof(ThingDef_AlienRace))
+ return;
+ try
+ {
+ debug += ".";
+ string debugBackup = debug;
+
+ foreach (FieldInfo field in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
+ {
+ debug = debugBackup;
+ debug += $"({field.FieldType.FullName}) {field.Name}";
+
+ Traverse instanceNew = instance.Field(field.Name);
+
+ if (typeof(IList).IsAssignableFrom(field.FieldType))
+ {
+ object value = instanceNew.GetValue();
+ if (value != null)
+ foreach (object o in (IList)value)
+ {
+ if (o.GetType().Assembly == typeof(ThingDef_AlienRace).Assembly)
+ RecursiveAttributeCheck(o.GetType(), Traverse.Create(o), debug);
+ }
+ }
+
+ if (field.FieldType.Assembly == typeof(ThingDef_AlienRace).Assembly)
+ RecursiveAttributeCheck(field.FieldType, instanceNew, debug);
+
+ LoadDefFromField attribute = field.GetCustomAttribute();
+ if (attribute != null)
+ if (instanceNew.GetValue() == null)
+ instanceNew.SetValue(attribute.defName == "this" ? this : attribute.GetDef(field.FieldType));
+ }
+ }
+ catch (InvalidOperationException ex)
+ {
+ Log.Error($"RecursiveAttribute Error: {debug}\n{ex}");
+ }
+ }
+ RecursiveAttributeCheck(typeof(AlienSettings), Traverse.Create(this.alienRace), this.defName);
+
+
+ foreach (ThingDef bedDef in this.alienRace.generalSettings.validBeds)
+ GeneralSettings.lockedBeds.Add(bedDef);
+
+ foreach (ThoughtDef thoughtDef in this.alienRace.thoughtSettings.restrictedThoughts)
+ {
+ if (!ThoughtSettings.thoughtRestrictionDict.ContainsKey(thoughtDef))
+ ThoughtSettings.thoughtRestrictionDict.Add(thoughtDef, []);
+ ThoughtSettings.thoughtRestrictionDict[thoughtDef].Add(this);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.apparelList)
+ {
+ RaceRestrictionSettings.apparelRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whiteApparelList.Add(thingDef);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.weaponList)
+ {
+ RaceRestrictionSettings.weaponRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whiteWeaponList.Add(thingDef);
+ }
+
+ foreach (BuildableDef thingDef in this.alienRace.raceRestriction.buildingList)
+ {
+ RaceRestrictionSettings.buildingRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whiteBuildingList.Add(thingDef);
+ }
+
+ foreach (RecipeDef recipeDef in this.alienRace.raceRestriction.recipeList)
+ {
+ RaceRestrictionSettings.recipeRestricted.Add(recipeDef);
+ this.alienRace.raceRestriction.whiteRecipeList.Add(recipeDef);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.plantList)
+ {
+ RaceRestrictionSettings.plantRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whitePlantList.Add(thingDef);
+ }
+
+ foreach (TraitDef traitDef in this.alienRace.raceRestriction.traitList)
+ {
+ RaceRestrictionSettings.traitRestricted.Add(traitDef);
+ this.alienRace.raceRestriction.whiteTraitList.Add(traitDef);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.foodList)
+ {
+ RaceRestrictionSettings.foodRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whiteFoodList.Add(thingDef);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.petList)
+ {
+ RaceRestrictionSettings.petRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whitePetList.Add(thingDef);
+ }
+
+ foreach (ResearchProjectDef projectDef in this.alienRace.raceRestriction.researchList.SelectMany(selector: rl => rl?.projects))
+ {
+ if (!RaceRestrictionSettings.researchRestrictionDict.ContainsKey(projectDef))
+ RaceRestrictionSettings.researchRestrictionDict.Add(projectDef, []);
+ RaceRestrictionSettings.researchRestrictionDict[projectDef].Add(this);
+ }
+
+ foreach (GeneDef geneDef in this.alienRace.raceRestriction.geneList)
+ {
+ RaceRestrictionSettings.geneRestricted.Add(geneDef);
+ this.alienRace.raceRestriction.whiteGeneList.Add(geneDef);
+ }
+
+ foreach (GeneDef geneDef in this.alienRace.raceRestriction.geneListEndo)
+ {
+ RaceRestrictionSettings.geneRestrictedEndo.Add(geneDef);
+ this.alienRace.raceRestriction.whiteGeneListEndo.Add(geneDef);
+ }
+
+ foreach (GeneDef geneDef in this.alienRace.raceRestriction.geneListXeno)
+ {
+ RaceRestrictionSettings.geneRestrictedXeno.Add(geneDef);
+ this.alienRace.raceRestriction.whiteGeneListXeno.Add(geneDef);
+ }
+
+ foreach (XenotypeDef xenotypeDef in this.alienRace.raceRestriction.xenotypeList)
+ {
+ RaceRestrictionSettings.xenotypeRestricted.Add(xenotypeDef);
+ this.alienRace.raceRestriction.whiteXenotypeList.Add(xenotypeDef);
+ }
+
+ foreach (ThingDef thingDef in this.alienRace.raceRestriction.reproductionList)
+ {
+ RaceRestrictionSettings.reproductionRestricted.Add(thingDef);
+ this.alienRace.raceRestriction.whiteReproductionList.Add(thingDef);
+ }
+
+ if (this.race.hasCorpse && this.alienRace.generalSettings.corpseCategory != ThingCategoryDefOf.CorpsesHumanlike)
+ {
+ ThingCategoryDefOf.CorpsesHumanlike.childThingDefs.Remove(this.race.corpseDef);
+ if (this.alienRace.generalSettings.corpseCategory != null)
+ {
+ this.race.corpseDef.thingCategories = [this.alienRace.generalSettings.corpseCategory];
+ this.alienRace.generalSettings.corpseCategory.childThingDefs.Add(this.race.corpseDef);
+ this.alienRace.generalSettings.corpseCategory.ResolveReferences();
+ }
+ ThingCategoryDefOf.CorpsesHumanlike.ResolveReferences();
+ }
+
+ this.alienRace.generalSettings.alienPartGenerator.GenerateMeshsAndMeshPools();
+
+ if (this.alienRace.generalSettings.humanRecipeImport && this != ThingDefOf.Human)
+ {
+ (this.recipes ??= []).AddRange(ThingDefOf.Human.recipes.Where(rd => !rd.targetsBodyPart ||
+ rd.appliedOnFixedBodyParts.NullOrEmpty() ||
+ rd.appliedOnFixedBodyParts.Any(bpd => this.race.body.AllParts.Any(bpr => bpr.def == bpd))));
+
+ DefDatabase.AllDefsListForReading.ForEach(rd =>
+ {
+ if (rd.recipeUsers?.Contains(ThingDefOf.Human) ?? false)
+ rd.recipeUsers.Add(this);
+ if (!rd.defaultIngredientFilter?.Allows(ThingDefOf.Meat_Human) ?? false)
+ rd.defaultIngredientFilter.SetAllow(this.race.meatDef, allow: false);
+ });
+ this.recipes.RemoveDuplicates();
+ }
+ }
+
+ public class AlienSettings
+ {
+ public GeneralSettings generalSettings = new();
+ public GraphicPaths graphicPaths = new();
+ public Dictionary styleSettings = new();
+ public ThoughtSettings thoughtSettings = new();
+ public RelationSettings relationSettings = new();
+ public RaceRestrictionSettings raceRestriction = new();
+ public CompatibilityInfo compatibility = new();
+ }
+ }
+
+ public class GeneralSettings
+ {
+ public float maleGenderProbability = 0.5f;
+ public bool immuneToAge = false;
+ public bool canLayDown = true;
+ public float minAgeForAdulthood = -1f;
+
+ public List validBeds = [];
+ public static HashSet lockedBeds = [];
+
+ public bool CanUseBed(ThingDef bedDef) =>
+ this.validBeds.Contains(bedDef) ||
+ (this.validBeds.NullOrEmpty() &&
+ !lockedBeds.Contains(bedDef));
+
+ public List chemicalSettings;
+
+ public bool CanUseChemical(ChemicalDef chemical)
+ {
+ if (this.chemicalSettings.NullOrEmpty() || chemical == null)
+ return true;
+
+ foreach (ChemicalSettings cs in this.chemicalSettings)
+ if (cs.chemical == chemical && !cs.ingestible)
+ return false;
+
+ return true;
+ }
+
+ public List> forcedRaceTraitEntries;
+ public List> disallowedTraits;
+
+ public IntRange additionalTraits = IntRange.Zero;
+ public AlienPartGenerator alienPartGenerator = new();
+ public List passions = new();
+ public List> abilities = new();
+
+ public List factionRelations = [];
+ public int maxDamageForSocialfight = int.MaxValue;
+ public bool allowHumanBios = false;
+ public bool immuneToXenophobia = false;
+ public List notXenophobistTowards = new();
+ public bool humanRecipeImport = false;
+
+ [LoadDefFromField(nameof(AlienDefOf.HAR_AlienCorpseCategory))]
+ public ThingCategoryDef corpseCategory;
+
+ public SimpleCurve lovinIntervalHoursFromAge;
+ public List growthAges = new() { 7, 10, 13 };
+ public int[] GrowthAges => this.growthAges?.ToArray();
+
+ public SimpleCurve growthFactorByAge;
+ public SimpleCurve ageSkillFactorCurve;
+
+ public List childBackstoryFilter;
+ public List adultBackstoryFilter;
+ public List adultVatBackstoryFilter;
+ public List newbornBackstoryFilter;
+
+ public ReproductionSettings reproduction = new();
+
+ public List> raceGenes = new();
+
+ internal List ageStatOverrides = [];
+
+ [Unsaved]
+ public Dictionary ageStatOverride = [];
+
+ public List meditationFocii = [];
+ }
+
+ public class ReproductionSettings
+ {
+ public PawnKindDef childKindDef;
+
+ public SimpleCurve maleFertilityAgeFactor = new(new[]
+ {
+ new CurvePoint(14, 0),
+ new CurvePoint(18, 1),
+ new CurvePoint(50, 1),
+ new CurvePoint(90, 0)
+ });
+ public SimpleCurve femaleFertilityAgeFactor = new(new[]
+ {
+ new CurvePoint(14, 0),
+ new CurvePoint(20, 1),
+ new CurvePoint(28, 1),
+ new CurvePoint(35, 0.5f),
+ new CurvePoint(40, 0.1f),
+ new CurvePoint(45, 0.02f),
+ new CurvePoint(50, 0),
+ });
+
+ public List hybridSpecific = new();
+
+ public GenderPossibility fertilizingGender = GenderPossibility.Male;
+ public GenderPossibility gestatingGender = GenderPossibility.Female;
+
+ public static bool ApplicableGender(Pawn pawn, bool gestating)
+ {
+ ReproductionSettings reproduction = (pawn.def as ThingDef_AlienRace)?.alienRace.generalSettings.reproduction ?? new ReproductionSettings();
+ return ApplicableGender(pawn.gender, reproduction, gestating);
+ }
+
+ public static bool ApplicableGender(Gender gender, ReproductionSettings reproduction, bool gestating) =>
+ gestating switch
+ {
+ true when reproduction.gestatingGender.IsGenderApplicable(gender) => true,
+ false when reproduction.fertilizingGender.IsGenderApplicable(gender) => true,
+ _ => false
+ };
+
+ public static bool GenderReproductionCheck(Pawn pawn, Pawn partnerPawn)
+ {
+ ReproductionSettings pawnReproduction = (pawn.def as ThingDef_AlienRace)?.alienRace.generalSettings.reproduction ?? new ReproductionSettings();
+ ReproductionSettings partnerReproduction = (partnerPawn.def as ThingDef_AlienRace)?.alienRace.generalSettings.reproduction ?? new ReproductionSettings();
+
+ return (ApplicableGender(pawn.gender, pawnReproduction, false) &&
+ ApplicableGender(partnerPawn.gender, partnerReproduction, true)) ||
+ (ApplicableGender(pawn.gender, pawnReproduction, true) &&
+ ApplicableGender(partnerPawn.gender, partnerReproduction, false));
+ }
+ }
+
+ public class HybridSpecificSettings
+ {
+ public ThingDef partnerRace;
+ public float probability = 100;
+ public PawnKindDef childKindDef;
+ }
+
+ public class FactionRelationSettings
+ {
+ public List factions;
+ public IntRange goodwill;
+ }
+
+ public class ChemicalSettings
+ {
+ public ChemicalDef chemical;
+ public bool ingestible = true;
+ public List reactions;
+ }
+
+ public class AlienChanceEntry
+ {
+ [LoadAlias("defName")]
+ public T entry;
+ public List> options = [];
+ public int count = 1;
+ public float chance = 100;
+
+ public float commonalityMale = -1f;
+ public float commonalityFemale = -1f;
+
+ [Unsaved]
+ private readonly List> shuffledOptions = [];
+
+ public bool Approved() =>
+ Rand.Range(0, 100) < this.chance;
+
+ public bool Approved(Gender gender) =>
+ (gender == Gender.Male && (this.commonalityMale < 0 || Rand.Range(0, 100) < this.commonalityMale) ||
+ gender == Gender.Female && (this.commonalityFemale < 0 || Rand.Range(0, 100) < this.commonalityFemale) ||
+ gender == Gender.None) &&
+ this.Approved();
+
+ public bool Approved(Pawn pawn) =>
+ this.Approved(pawn.gender);
+
+ public IEnumerable Select(Pawn pawn)
+ {
+ if (pawn != null)
+ {
+ if (!this.Approved(pawn))
+ yield break;
+ } else if (!this.Approved())
+ {
+ yield break;
+ }
+
+ if (!Equals(this.entry, default(T)))
+ yield return this.entry;
+
+ // Doing this instead of GenCollection.TakeRandom because TakeRandom allows repeats
+ if (this.shuffledOptions.Count != this.options.Count)
+ {
+ this.shuffledOptions.Clear();
+ this.shuffledOptions.AddRange(this.options);
+ }
+
+ this.shuffledOptions.Shuffle();
+ int limit = Math.Min(this.shuffledOptions.Count, this.count);
+ for (int i = 0; i < limit; i ++)
+ {
+ foreach (T entryInner in this.shuffledOptions[i].Select(pawn))
+ yield return entryInner;
+ }
+ }
+
+ [UsedImplicitly]
+ public void LoadDataFromXmlCustom(XmlNode xmlRoot)
+ {
+ if (xmlRoot.ChildNodes.Count == 1 && xmlRoot.FirstChild.NodeType == XmlNodeType.Text)
+ {
+ if(typeof(T).IsSubclassOf(typeof(Def)))
+ DirectXmlCrossRefLoader.RegisterObjectWantsCrossRef(this, nameof(this.entry), xmlRoot.FirstChild.Value);
+ else
+ Utilities.SetFieldFromXmlNode(Traverse.Create(this), xmlRoot, this, nameof(this.entry));
+ }
+ else
+ {
+ Traverse traverse = Traverse.Create(this);
+ foreach (XmlNode xmlNode in xmlRoot.ChildNodes)
+ Utilities.SetFieldFromXmlNode(traverse, xmlNode, this, xmlNode.Name == "defName" ? nameof(this.entry) : xmlNode.Name);
+ }
+ }
+ }
+
+ public class TraitWithDegree
+ {
+ public TraitDef def;
+ public int degree = 0;
+
+ [UsedImplicitly]
+ public void LoadDataFromXmlCustom(XmlNode xmlRoot)
+ {
+ DirectXmlCrossRefLoader.RegisterObjectWantsCrossRef(this, nameof(this.def), xmlRoot?.FirstChild?.Value ?? xmlRoot?.Value ?? xmlRoot?.InnerText);
+ int.TryParse(xmlRoot.Attributes?["Degree"]?.Value, out this.degree);
+ }
+
+ public override string ToString() => $"{nameof(TraitWithDegree)}: {this.def?.defName} | {this.degree}";
+ }
+
+ public class GraphicPaths
+ {
+ public const string VANILLA_HEAD_PATH = "Things/Pawn/Humanlike/Heads/";
+ public const string VANILLA_BODY_PATH = "Things/Pawn/Humanlike/Bodies/";
+ public const string VANILLA_SKELETON_PATH = "Things/Pawn/Humanlike/HumanoidDessicated";
+
+ public AlienPartGenerator.ExtendedGraphicTop body = new() { path = VANILLA_BODY_PATH};
+ public AlienPartGenerator.ExtendedGraphicTop bodyMasks = new() { path = string.Empty };
+ public AlienPartGenerator.ExtendedGraphicTop head = new() { path = VANILLA_HEAD_PATH };
+ public AlienPartGenerator.ExtendedGraphicTop headMasks = new() { path = string.Empty };
+
+ public AlienPartGenerator.ExtendedGraphicTop skeleton = new() { path = VANILLA_SKELETON_PATH };
+ public AlienPartGenerator.ExtendedGraphicTop skull = new() { path = "Things/Pawn/Humanlike/Heads/None_Average_Skull" };
+ public AlienPartGenerator.ExtendedGraphicTop stump = new() { path = "Things/Pawn/Humanlike/Heads/None_Average_Stump" };
+ public AlienPartGenerator.ExtendedGraphicTop swaddle = new() { path = "Things/Pawn/Humanlike/Apparel/SwaddledBaby/Swaddled_Child" };
+
+ public ApparelGraphics.ApparelGraphicsOverrides apparel = new();
+
+ public ShaderTypeDef skinShader;
+ public Color skinColor = new(1f, 0f, 0f, 1f);
+
+ private List skinColoringParameter;
+ public List SkinColoringParameter
+ {
+ get
+ {
+ if (this.skinColoringParameter == null)
+ {
+ ShaderParameter parameter = new();
+ Traverse traverse = Traverse.Create(parameter);
+ traverse.Field("name").SetValue("_ShadowColor");
+ traverse.Field("value").SetValue(new Vector4(this.skinColor.r, this.skinColor.g, this.skinColor.b, this.skinColor.a));
+ traverse.Field("type").SetValue(1);
+ this.skinColoringParameter = [parameter];
+ }
+ return this.skinColoringParameter;
+ }
+ }
+ }
+
+ public class DirectionOffset
+ {
+ public Vector2 north = Vector2.zero;
+ public Vector2 west = Vector2.zero;
+ public Vector2 east = Vector2.zero;
+ public Vector2 south = Vector2.zero;
+
+ public Vector2 GetOffset(Rot4 rot) =>
+ rot == Rot4.North ? this.north : rot == Rot4.East ? this.east : rot == Rot4.West ? this.west : this.south;
+ }
+
+ public class StyleSettings
+ {
+ public bool hasStyle = true;
+ public bool genderRespected = true;
+ public List styleTags;
+ public List styleTagsOverride;
+ public List bannedTags;
+ public ShaderTypeDef shader;
+
+ public bool IsValidStyle(StyleItemDef styleItemDef, Pawn pawn, bool useOverrides = false) =>
+ !this.hasStyle ?
+ styleItemDef.styleTags.Contains("alienNoStyle") :
+
+ (useOverrides ?
+ this.styleTagsOverride.NullOrEmpty() || this.styleTagsOverride.Any(s => styleItemDef.styleTags.Contains(s)) :
+ this.styleTags.NullOrEmpty() || this.styleTags.Any(s => styleItemDef.styleTags.Contains(s))) &&
+ (this.bannedTags.NullOrEmpty() || !this.bannedTags.Any(s => styleItemDef.styleTags.Contains(s))) &&
+
+ (!this.genderRespected ||
+ pawn.gender == Gender.None ||
+ (pawn.Ideo?.style.GetGender(styleItemDef) ?? styleItemDef.styleGender, pawn.gender) switch
+ {
+ (StyleGender.Any or StyleGender.MaleUsually or StyleGender.FemaleUsually, _) or (StyleGender.Male, Gender.Male) or (StyleGender.Female, Gender.Female) => true,
+ _ => false
+ });
+ }
+
+ public class ThoughtSettings
+ {
+ public List cannotReceiveThoughts;
+ public bool cannotReceiveThoughtsAtAll = false;
+ public List canStillReceiveThoughts;
+
+ public static Dictionary> thoughtRestrictionDict = new();
+ public List restrictedThoughts = new();
+
+ public ThoughtDef ReplaceIfApplicable(ThoughtDef def)
+ {
+ if (this.replacerList == null || this.replacerList.Select(tr => tr.replacer).Contains(def))
+ return def;
+
+ for (int i = 0; i < this.replacerList.Count; i++)
+ {
+ if(this.replacerList[i].original == def)
+ return this.replacerList[i].replacer ?? def;
+ }
+
+ return def;
+ }
+
+ public ButcherThought butcherThoughtGeneral = new();
+ public List butcherThoughtSpecific = new();
+
+ public AteThought ateThoughtGeneral = new();
+ public List ateThoughtSpecific = new();
+
+ public ThoughtDef GetAteThought(ThingDef race, bool cannibal, bool ingredient) =>
+ (this.ateThoughtSpecific?.FirstOrDefault(predicate: at => at.raceList?.Contains(race) ?? false) ?? this.ateThoughtGeneral)?.GetThought(cannibal, ingredient);
+
+ public bool CanGetThought(ThoughtDef def)
+ {
+ def = this.ReplaceIfApplicable(def);
+
+ return (!this.cannotReceiveThoughtsAtAll || (this.canStillReceiveThoughts?.Contains(def) ?? false)) &&
+ (!(this.cannotReceiveThoughts?.Contains(def) ?? false));
+ }
+
+ private static readonly Dictionary canGetThoughtCache = [];
+
+ public static bool CanGetThought(ThoughtDef def, ThingDef race)
+ {
+ uint key = def.shortHash | ((uint)race.shortHash << 16);
+
+ if (!canGetThoughtCache.TryGetValue(key, out bool canGetThought))
+ {
+ bool result = !(thoughtRestrictionDict.TryGetValue(def, out List races));
+
+ canGetThoughtCache.Add(key, canGetThought = race is not ThingDef_AlienRace alienProps ?
+ result :
+ (races?.Contains(alienProps) ?? true) && alienProps.alienRace.thoughtSettings.CanGetThought(def));
+ }
+ return canGetThought;
+ }
+
+ public static bool CanGetThought(ThoughtDef def, Pawn pawn) =>
+ CanGetThought(def, pawn.def);
+
+ public List replacerList;
+ }
+
+ public class ButcherThought
+ {
+ public List raceList;
+
+ [LoadDefFromField(nameof(AlienDefOf.ButcheredHumanlikeCorpse))]
+ public ThoughtDef thought;
+
+ [LoadDefFromField(nameof(AlienDefOf.KnowButcheredHumanlikeCorpse))]
+ public ThoughtDef knowThought;
+ }
+
+ public class AteThought
+ {
+ public List raceList;
+ [LoadDefFromField(nameof(AlienDefOf.AteHumanlikeMeatDirect))]
+ public ThoughtDef thought;
+
+ [LoadDefFromField(nameof(AlienDefOf.AteHumanlikeMeatDirectCannibal))]
+ public ThoughtDef thoughtCannibal;
+
+ [LoadDefFromField(nameof(AlienDefOf.AteHumanlikeMeatAsIngredient))]
+ public ThoughtDef ingredientThought;
+
+ [LoadDefFromField(nameof(AlienDefOf.AteHumanlikeMeatAsIngredientCannibal))]
+ public ThoughtDef ingredientThoughtCannibal;
+
+ public ThoughtDef GetThought(bool cannibal, bool ingredient) =>
+ cannibal ? ingredient ? this.ingredientThoughtCannibal : this.thoughtCannibal : ingredient ? this.ingredientThought : this.thought;
+ }
+
+ public class ThoughtReplacer
+ {
+ public ThoughtDef original;
+ public ThoughtDef replacer;
+ }
+
+ public class RelationSettings
+ {
+ public float relationChanceModifierChild = 1f;
+ public float relationChanceModifierExLover = 1f;
+ public float relationChanceModifierExSpouse = 1f;
+ public float relationChanceModifierFiance = 1f;
+ public float relationChanceModifierLover = 1f;
+ public float relationChanceModifierParent = 1f;
+ public float relationChanceModifierSibling = 1f;
+ public float relationChanceModifierSpouse = 1f;
+
+ public List renamer;
+ }
+
+ public class RelationRenamer
+ {
+ public PawnRelationDef relation;
+ public string label;
+ public string femaleLabel;
+ }
+
+ public class RaceRestrictionSettings
+ {
+ public bool onlyUseRaceRestrictedApparel = false;
+ public List apparelList = new();
+ public List whiteApparelList = new();
+ public List blackApparelList = new();
+
+ public static HashSet apparelRestricted = new();
+
+ public static bool CanWear(ThingDef apparel, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (apparelRestricted.Contains(apparel) || (raceRestriction?.onlyUseRaceRestrictedApparel ?? false))
+ result = raceRestriction?.whiteApparelList.Contains(apparel) ?? false;
+
+ return result && !(raceRestriction?.blackApparelList.Contains(apparel) ?? false);
+ }
+
+
+ public List researchList = new();
+ public static Dictionary> researchRestrictionDict = new();
+
+ public static bool CanResearch(IEnumerable races, ResearchProjectDef project) =>
+ !researchRestrictionDict.ContainsKey(project) || races.Any(predicate: ar => researchRestrictionDict[project].Contains(ar));
+
+
+ public bool onlyUseRaceRestrictedWeapons = false;
+ public List weaponList = new();
+ public List whiteWeaponList = new();
+ public List blackWeaponList = new();
+
+ public static HashSet weaponRestricted = new();
+
+ public static bool CanEquip(ThingDef weapon, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (weaponRestricted.Contains(weapon) || (raceRestriction?.onlyUseRaceRestrictedWeapons ?? false))
+ result = raceRestriction?.whiteWeaponList.Contains(weapon) ?? false;
+
+ return result && !(raceRestriction?.blackWeaponList.Contains(weapon) ?? false);
+ }
+
+ public bool onlyBuildRaceRestrictedBuildings = false;
+ public List buildingList = [];
+ public List whiteBuildingList = [];
+ public List blackBuildingList = [];
+ public List hiddenBuildingList = [];
+
+ public static readonly HashSet buildingRestricted = [];
+
+ public static readonly HashSet buildingsRestrictedWithCurrentColony = [];
+
+ public static bool CanColonyBuild(BuildableDef building) =>
+ !buildingsRestrictedWithCurrentColony.Contains(building);
+
+ public static bool CanBuild(BuildableDef building, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (buildingRestricted.Contains(building) || (raceRestriction?.onlyBuildRaceRestrictedBuildings ?? false))
+ result = raceRestriction?.whiteBuildingList.Contains(building) ?? false;
+
+ return result && !(raceRestriction?.blackBuildingList.Contains(building) ?? false);
+ }
+
+
+
+
+ public bool onlyDoRaceRestrictedRecipes = false;
+ public List recipeList = new();
+ public List whiteRecipeList = new();
+ public List blackRecipeList = new();
+
+ public static HashSet recipeRestricted = new();
+
+ public static bool CanDoRecipe(RecipeDef recipe, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (recipeRestricted.Contains(recipe) || (raceRestriction?.onlyDoRaceRestrictedRecipes ?? false))
+ result = raceRestriction?.whiteRecipeList.Contains(recipe) ?? false;
+
+ return result && !(raceRestriction?.blackRecipeList.Contains(recipe) ?? false);
+ }
+
+ public bool onlyDoRaceRestrictedPlants = false;
+ public List plantList = new();
+ public List whitePlantList = new();
+ public List blackPlantList = new();
+
+ public static HashSet plantRestricted = new();
+
+ public static bool CanPlant(ThingDef plant, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (plantRestricted.Contains(plant) || (raceRestriction?.onlyDoRaceRestrictedPlants ?? false))
+ result = raceRestriction?.whitePlantList.Contains(plant) ?? false;
+
+ return result && !(raceRestriction?.blackPlantList.Contains(plant) ?? false);
+ }
+
+ public bool onlyGetRaceRestrictedTraits = false;
+ public List traitList = new();
+ public List whiteTraitList = new();
+ public List blackTraitList = new();
+
+ public static HashSet traitRestricted = new();
+
+ public static bool CanGetTrait(TraitDef trait, Pawn pawn, int degree = 0)
+ {
+ List> disallowedTraits = [];
+
+ foreach (BackstoryDef backstory in pawn.story.AllBackstories)
+ if (backstory is AlienBackstoryDef alienBackstory)
+ if(!alienBackstory.disallowedTraitsChance.NullOrEmpty())
+ disallowedTraits.AddRange(alienBackstory.disallowedTraitsChance);
+
+ return CanGetTrait(trait, pawn.def, degree, disallowedTraits);
+ }
+
+ public static bool CanGetTrait(TraitDef trait, ThingDef race, int degree = 0, List> disallowedTraits = null)
+ {
+ ThingDef_AlienRace.AlienSettings alienProps = (race as ThingDef_AlienRace)?.alienRace;
+ RaceRestrictionSettings raceRestriction = alienProps?.raceRestriction;
+ bool result = true;
+
+ if (traitRestricted.Contains(trait) || (raceRestriction?.onlyGetRaceRestrictedTraits ?? false))
+ result &= raceRestriction?.whiteTraitList.Contains(trait) ?? false;
+
+ disallowedTraits ??= [];
+
+ if (!(alienProps?.generalSettings.disallowedTraits.NullOrEmpty() ?? true))
+ disallowedTraits.AddRange(alienProps.generalSettings.disallowedTraits);
+
+ if (!disallowedTraits.NullOrEmpty())
+ result &= disallowedTraits.All(ace => ace.Select(null).All(traitEntry => traitEntry.def != trait || degree != traitEntry.degree));
+
+
+ return result && !(raceRestriction?.blackTraitList.Contains(trait) ?? false);
+ }
+
+ public bool onlyEatRaceRestrictedFood = false;
+ public List foodList = new();
+ public List whiteFoodList = new();
+ public List blackFoodList = new();
+
+ public static HashSet foodRestricted = new();
+
+ public static bool CanEat(ThingDef food, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (foodRestricted.Contains(food) || (raceRestriction?.onlyEatRaceRestrictedFood ?? false))
+ result = raceRestriction?.whiteFoodList.Contains(food) ?? false;
+
+ result &= !(raceRestriction?.blackFoodList.Contains(food) ?? false);
+
+ ChemicalDef chemical = food.GetCompProperties()?.chemical;
+ return result && (chemical == null || ((race as ThingDef_AlienRace)?.alienRace.generalSettings.CanUseChemical(chemical) ?? true));
+ }
+
+ public bool onlyTameRaceRestrictedPets = false;
+ public List petList = new();
+ public List whitePetList = new();
+ public List blackPetList = new();
+
+ public static HashSet petRestricted = new();
+
+ public static bool CanTame(ThingDef pet, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (petRestricted.Contains(pet) || (raceRestriction?.onlyTameRaceRestrictedPets ?? false))
+ result = raceRestriction?.whitePetList.Contains(pet) ?? false;
+
+ return result && !(raceRestriction?.blackPetList.Contains(pet) ?? false);
+ }
+
+ public List conceptList = new();
+
+ public List workGiverList = new();
+
+ public bool onlyHaveRaceRestrictedGenes = false;
+ public List geneList = [];
+ public List whiteGeneList = [];
+ public List whiteGeneTags = [];
+ public List blackGeneList = [];
+ public List blackGeneTags = [];
+ public List blackEndoCategories = [];
+
+ public static HashSet geneRestricted = [];
+
+ public bool onlyHaveRaceRestrictedGenesXeno = false;
+ public List geneListXeno = [];
+ public List whiteGeneListXeno = [];
+ public List whiteGeneTagsXeno = [];
+ public List blackGeneListXeno = [];
+ public List blackGeneTagsXeno = [];
+ public List blackEndoCategoriesXeno = [];
+
+ public static HashSet geneRestrictedXeno = [];
+
+ public bool onlyHaveRaceRestrictedGenesEndo = false;
+ public List geneListEndo = [];
+ public List whiteGeneListEndo = [];
+ public List whiteGeneTagsEndo = [];
+ public List blackGeneListEndo = [];
+ public List blackGeneTagsEndo = [];
+ public List blackEndoCategoriesEndo = [];
+
+ public static HashSet geneRestrictedEndo = [];
+
+ public static bool CanHaveGene(GeneDef gene, ThingDef race, bool xeno)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (geneRestricted.Contains(gene) || (raceRestriction?.onlyHaveRaceRestrictedGenes ?? false))
+ result = (raceRestriction?.whiteGeneList.Contains(gene) ?? false) ||
+ (gene.exclusionTags?.Any(t => raceRestriction?.whiteGeneTags.Any(t.StartsWith) ?? false) ?? false);
+
+ if (xeno)
+ {
+ if (geneRestrictedXeno.Contains(gene) || (raceRestriction?.onlyHaveRaceRestrictedGenesXeno ?? false))
+ result &= (raceRestriction?.whiteGeneListXeno.Contains(gene) ?? false) ||
+ (gene.exclusionTags?.Any(t => raceRestriction?.whiteGeneTagsXeno.Any(t.StartsWith) ?? false) ?? false);
+
+ result &= !(raceRestriction?.blackGeneListXeno.Contains(gene) ?? false) &&
+ !(gene.exclusionTags?.Any(t => raceRestriction?.blackGeneTagsXeno.Any(t.StartsWith) ?? false) ?? false) &&
+ !(raceRestriction?.blackEndoCategoriesXeno.Contains(gene.endogeneCategory) ?? false);
+
+ }
+ else
+ {
+ if (geneRestrictedEndo.Contains(gene) || (raceRestriction?.onlyHaveRaceRestrictedGenesEndo ?? false))
+ result &= (raceRestriction?.whiteGeneListEndo.Contains(gene) ?? false) ||
+ (gene.exclusionTags?.Any(t => raceRestriction?.whiteGeneTagsEndo.Any(t.StartsWith) ?? false) ?? false);
+
+ result &= !(raceRestriction?.blackGeneListEndo.Contains(gene) ?? false) &&
+ !(gene.exclusionTags?.Any(t => raceRestriction?.blackGeneTagsEndo.Any(t.StartsWith) ?? false) ?? false) &&
+ !(raceRestriction?.blackEndoCategoriesEndo.Contains(gene.endogeneCategory) ?? false);
+ }
+
+ if (gene.chemical != null)
+ result &= ((race as ThingDef_AlienRace)?.alienRace.generalSettings.CanUseChemical(gene.chemical) ?? true);
+
+ return result &&
+ !(raceRestriction?.blackGeneList.Contains(gene) ?? false) &&
+ !(gene.exclusionTags?.Any(t => raceRestriction?.blackGeneTags.Any(t.StartsWith) ?? false) ?? false) &&
+ !(raceRestriction?.blackEndoCategories.Contains(gene.endogeneCategory) ?? false);
+ }
+
+ public bool onlyUseRaceRestrictedXenotypes = false;
+ public List xenotypeList = new();
+ public List whiteXenotypeList = new();
+ public List blackXenotypeList = new();
+
+ public static HashSet xenotypeRestricted = new();
+
+ public static HashSet FilterXenotypes(IEnumerable xenotypes, ThingDef race, out HashSet removedXenotypes)
+ {
+ HashSet xenotypeDefs = new();
+ removedXenotypes = new HashSet();
+
+ foreach (XenotypeDef xenotypeDef in xenotypes)
+ if (CanUseXenotype(xenotypeDef, race))
+ xenotypeDefs.Add(xenotypeDef);
+ else
+ removedXenotypes.Add(xenotypeDef);
+
+ return xenotypeDefs;
+ }
+
+ public static bool CanUseXenotype(XenotypeDef xenotype, ThingDef race)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ bool result = true;
+
+ if (xenotypeRestricted.Contains(xenotype) || (raceRestriction?.onlyUseRaceRestrictedXenotypes ?? false))
+ result = raceRestriction?.whiteXenotypeList.Contains(xenotype) ?? false;
+
+ return result && !(raceRestriction?.blackXenotypeList.Contains(xenotype) ?? false);
+ }
+
+ public bool canReproduce = true;
+ public bool canReproduceWithSelf = true;
+ public bool onlyReproduceWithRestrictedRaces = false;
+ public List reproductionList = new();
+ public List whiteReproductionList = new();
+ public List blackReproductionList = new();
+
+ public static HashSet reproductionRestricted = new();
+
+ public static bool CanReproduce(Pawn pawn, Pawn partnerPawn) =>
+ ReproductionSettings.GenderReproductionCheck(pawn, partnerPawn) &&
+ CanReproduce(pawn.def, partnerPawn.def);
+
+ public static bool CanReproduce(ThingDef race, ThingDef partnerRace) =>
+ CanReproduceWith(race, partnerRace) && CanReproduceWith(partnerRace, race);
+
+ private static bool CanReproduceWith(ThingDef race, ThingDef partnerRace)
+ {
+ RaceRestrictionSettings raceRestriction = (race as ThingDef_AlienRace)?.alienRace.raceRestriction;
+ if (!(raceRestriction?.canReproduce ?? true))
+ return false;
+
+ if (race == partnerRace)
+ return raceRestriction?.canReproduceWithSelf ?? true;
+
+ bool result = true;
+ if (reproductionRestricted.Contains(partnerRace) || (raceRestriction?.onlyReproduceWithRestrictedRaces ?? false))
+ result = raceRestriction?.whiteReproductionList.Contains(partnerRace) ?? false;
+
+ return result && !(raceRestriction?.blackReproductionList.Contains(partnerRace) ?? false);
+ }
+ }
+
+ public class ResearchProjectRestrictions
+ {
+ public List projects = new();
+ public List apparelList;
+ }
+
+ public class Info : DefModExtension
+ {
+ public bool allowHumanBios = true;
+ public float maleGenderProbability = 0.5f;
+ }
+
+ public class LifeStageAgeAlien : LifeStageAge
+ {
+ public BodyDef body;
+
+ public Vector2 headOffset = Vector2.zero;
+ public AlienPartGenerator.DirectionalOffset headOffsetDirectional;
+
+ public Vector2 headFemaleOffset = Vector2.negativeInfinity;
+ public AlienPartGenerator.DirectionalOffset headFemaleOffsetDirectional;
+
+
+ public Vector2 customDrawSize = Vector2.zero;
+ public Vector2 customPortraitDrawSize = Vector2.zero;
+ public Vector2 customHeadDrawSize = Vector2.zero;
+ public Vector2 customPortraitHeadDrawSize = Vector2.zero;
+
+ public Vector2 customFemaleDrawSize = Vector2.zero;
+ public Vector2 customFemalePortraitDrawSize = Vector2.zero;
+ public Vector2 customFemaleHeadDrawSize = Vector2.zero;
+ public Vector2 customFemalePortraitHeadDrawSize = Vector2.zero;
+ }
+
+ public class StatPartAgeOverride
+ {
+ public StatDef stat;
+ public StatPart_Age overridePart;
+
+ public void LoadDataFromXmlCustom(XmlNode xmlRoot)
+ {
+ DirectXmlCrossRefLoader.RegisterObjectWantsCrossRef(this, "stat", xmlRoot.Name);
+ this.overridePart = DirectXmlToObject.ObjectFromXml(xmlRoot, false);
+ }
+ }
+
+ public class CompatibilityInfo
+ {
+ protected bool isFlesh = true;
+
+ public virtual bool IsFlesh
+ {
+ get => this.isFlesh;
+ set => this.isFlesh = value;
+ }
+
+ public virtual bool IsFleshPawn(Pawn pawn) => this.IsFlesh;
+
+ protected bool isSentient = true;
+
+ public virtual bool IsSentient
+ {
+ get => this.isSentient;
+ set => this.isSentient = value;
+ }
+
+ public virtual bool IsSentientPawn(Pawn pawn) => this.IsSentient;
+
+ protected bool hasBlood = true;
+
+ public virtual bool HasBlood
+ {
+ get => this.hasBlood;
+ set => this.hasBlood = value;
+ }
+
+ public virtual bool HasBloodPawn(Pawn pawn) => this.HasBlood;
+ }
+
+ public class HARStatueContainer : IExposable
+ {
+ public static readonly string loadKey = typeof(HARStatueContainer).FullName!;
+
+ public ThingDef_AlienRace alienRace;
+ public PawnKindDef kindDef;
+ public AlienPartGenerator.AlienComp alienComp;
+
+ public List> traits = [];
+
+
+ public void ExposeData()
+ {
+ Scribe_Defs.Look(ref this.alienRace, nameof(this.alienRace));
+ Scribe_Defs.Look(ref this.kindDef, nameof(this.kindDef));
+ Scribe_Collections.Look(ref this.traits, nameof(this.traits), LookMode.Deep);
+
+ if (Scribe.mode == LoadSaveMode.LoadingVars)
+ {
+ this.traits ??= [];
+ this.alienComp = Activator.CreateInstance();
+ }
+
+ this.alienComp.PostExposeData();
+ }
+ }
+}
\ No newline at end of file