/// <summary> /// Allows generating a parent for this character as if generating a spouse for one of their existing parents. /// This allows generating correct gender and orientation matching for between the 'spouses.' /// </summary> /// <param name="spouse">The character to be used as the hypothetical spouse.</param> private void GenerateParentAsSpouse(CharacterNote spouse) { if (RootSaveFile?.Template?.CharacterTemplate?.Relationships == null) return; CharacterRelationshipOption relationship = RootSaveFile.Template.CharacterTemplate.Relationships.FindOption("Parent") as CharacterRelationshipOption; CharacterRelationshipOption spouseRelationship = RootSaveFile.Template.CharacterTemplate.Relationships.FindOption("Significant Other\\Spouse") as CharacterRelationshipOption; if (relationship == null || spouseRelationship == null) return; CharacterNote newNote = new CharacterNote(); AddChild(newNote); newNote.Relationship = relationship; int? spouseMinAge = spouseRelationship.MinAge; if (spouseRelationship.MinAgeOffset.HasValue) spouseMinAge = Math.Max(spouseMinAge ?? int.MinValue, spouse.AgeYears + spouseRelationship.MinAgeOffset.Value); int? spouseMaxAge = spouseRelationship.MaxAge; if (spouseRelationship.MaxAgeOffset.HasValue) spouseMaxAge = Math.Min(spouseMaxAge ?? int.MaxValue, spouse.AgeYears + spouseRelationship.MaxAgeOffset.Value); if (!newNote.ChooseAge(false, spouseMinAge, spouseMaxAge)) // False return means no valid age range exists for the relationship (min > max). { Ideas.Remove(newNote); return; } if (!spouseRelationship.RequiresOrientationMatch || !newNote.SetSignificantOtherGender(spouse, spouseRelationship)) newNote.ChooseGender(); newNote.SetInheritedRaces(); newNote.AssignFamilySurname(); newNote.AssignFamilyFirstName(); if (RootSaveFile?.Template?.CharacterTemplate?.Traits != null) newNote.Traits.MergeWithOption(RootSaveFile.Template.CharacterTemplate.Traits, true); newNote.Traits.Choose(); // There is a chance that a character of any orientation will be in a // heterosexual marriage (or ex-marriage), due to societal pressure. // Otherwise, the new character's orientation will be adjusted, if necessary, to // fit the relationship. if (spouseRelationship.RequiresOrientationMatch && (random.Next(101) > chanceOfForcedHeteroMarriage || spouse.EffectiveGender?.Archetype != newNote.EffectiveGender?.Opposite)) newNote.ReconcileOrientation(spouse, spouseRelationship); }
public CharacterNote AddNewFamilyMember(CharacterRelationshipOption relationship, bool manual) { if (relationship == null) return null; CharacterNote newNote = new CharacterNote(); AddChild(newNote); // First see if the relationship specified is a gender-neutral option with gender-specific child options. // If so, choose a gender first, then select the appropriate child relationship. bool genderChosen = false; if (relationship.Genders?.Count == 0 && relationship.ChildOptions.Cast<CharacterRelationshipOption>().Count(c => c.Genders?.Count > 0) > 1) { newNote.SetRelationshipMatchingGender(this, relationship); genderChosen = true; } // If no gender-specific options were selected, the choice will // fall back on the gender-neutral original below. if (newNote.Relationship == null) newNote.Relationship = relationship; // False return means no valid age range exists for the relationship (min > max). // Ignored when adding manually. if (!newNote.ChooseAge() && !manual) { Ideas.Remove(newNote); return null; } if (!genderChosen) newNote.ChooseGender(); newNote.SetInheritedRaces(); newNote.AssignFamilySurname(); newNote.AssignFamilyFirstName(); if (RootSaveFile?.Template?.CharacterTemplate?.Traits != null) newNote.Traits.MergeWithOption(RootSaveFile.Template.CharacterTemplate.Traits, true); newNote.Traits.Choose(); if (newNote.Relationship.RequiresOrientationMatch) { newNote.ReconcileOrientation(this, newNote.Relationship); CharacterRelationshipOption reciprocalRelationship = RootSaveFile.Template.CharacterTemplate.Relationships.FindOption(newNote.Relationship.ReciprocalRelationship) as CharacterRelationshipOption; ReconcileOrientation(newNote, reciprocalRelationship); } return newNote; }
private List<CharacterNote> GenerateChildren(bool woman, int minChildAge, int maxChildAge, bool married, bool divorced, bool siblings, int numAlready, bool manual) { List<CharacterNote> children = new List<CharacterNote>(); CharacterRelationshipOption relationship = RootSaveFile.Template.CharacterTemplate.Relationships.FindOption(siblings ? "Sibling" : "Child") as CharacterRelationshipOption; if (relationship == null) return children; // Constrain the age bounds. If there are no valid ages, stop now. minChildAge = Math.Max(0, minChildAge); maxChildAge = Math.Min(maxAge, maxChildAge); if (minChildAge > maxChildAge) return children; // If there are already some children, and this isn't manual selection of more, decide whether to generate more before proceeding. if (!manual) { if (numAlready >= relationship.Max) return children; else if (numAlready == 1) { if (random.Next(4) == 0) return children; } else if (numAlready > 1 && random.Next(1, (2 * (numAlready - 1)) + 1) != 1) return children; } // Decide whether to stop now based on the character's situation. // If the user is manually adding children, don't make these determinations. // If determining siblings there's at least 1 child already (the character), so these checks can be skipped. if (!manual && !siblings && !ShouldHaveChildren(relationship, woman, married, divorced)) return children; int childAge; bool first = true; int numTwins = 0; int numChildren = numAlready; while (maxChildAge >= 0 && maxChildAge >= minChildAge) { childAge = random.Next(minChildAge, maxChildAge + 1); if (!first) { if (childAge == maxChildAge) { // First time, 1/4 chance of allowing twins; second, 1/8; third, 1/16; &c. // Triplets, quadruplets, &c have the same increasingly small chance of being allowed. if (random.Next(4 * (numTwins + 1)) != 0) { maxChildAge--; continue; } else numTwins++; } // There's a 50% chance of adjusting a 1-year separation to 2, to simulate the relative // rarity of such closely-spaced children. else if (childAge == maxChildAge - 1 && random.Next(2) == 0) { childAge--; if (childAge < 0) return children; } } CharacterNote newNote = new CharacterNote(); AddChild(newNote); // Pick a child relationship (gender-specific variant) relationship.DeselectAllChildren(); relationship.Choose(); newNote.Relationship = relationship.LowestCheckedChild as CharacterRelationshipOption; newNote.AgeYears = childAge; if (newNote.AgeYears == 0) newNote.AgeMonths = (byte)random.Next(13); newNote.ChooseGender(); newNote.SetInheritedRaces(); newNote.AssignFamilySurname(); newNote.AssignFamilyFirstName(); if (RootSaveFile?.Template?.CharacterTemplate?.Traits != null) newNote.Traits.MergeWithOption(RootSaveFile.Template.CharacterTemplate.Traits, true); newNote.Traits.Choose(); children.Add(newNote); first = false; maxChildAge = childAge; numChildren++; if (numChildren >= relationship.Max) return children; // The chances of having multiple kids follows a different progression than the standard for other relationships: // Once a couple has a single child, there is a 3 in 4 chance that they will have a second. else if (numChildren == 1) { if (random.Next(1, 101) <= 25) return children; } // After two, the chances of having additional children drop off rapidly, // starting at 1 in 2 for a third, then 1 in 4 for a fourth, 1 in 6 for a fifth, etc. else if (random.Next(1, (2 * (numChildren - 1)) + 1) != 1) return children; } return children; }
private bool GenerateSignificantOther(CharacterRelationshipOption relationship, int numAlready) { if (relationship.IsInPath("Spouse")) { if (!CheckForSpouse(relationship, numAlready)) return false; } else if (relationship.IsInPath("Ex-Spouse")) { if (!CheckForExSpouse(relationship, numAlready)) return false; } else if (relationship.IsInPath("Lover")) { if (!CheckForLover(relationship, numAlready)) return false; } else if (!CheckForRelationship(relationship, numAlready)) return false; CharacterNote newNote = new CharacterNote(); AddChild(newNote); newNote.Relationship = relationship; if (!relationship.RequiresOrientationMatch || !newNote.SetSignificantOtherGender(this, relationship)) newNote.ChooseGender(); int? minimum, maximum; GetSignificantOtherAgeRange(newNote.EffectiveGender, out minimum, out maximum); if (!newNote.ChooseAge(false, minimum, maximum)) // False return means no valid age range exists for the relationship (min > max). { Ideas.Remove(newNote); return false; } newNote.SetInheritedRaces(); newNote.AssignFamilySurname(); newNote.AssignFamilyFirstName(); if (RootSaveFile?.Template?.CharacterTemplate?.Traits != null) newNote.Traits.MergeWithOption(RootSaveFile.Template.CharacterTemplate.Traits, true); newNote.Traits.Choose(); // There is a chance that a character of any orientation will be in a // heterosexual marriage (or ex-marriage), due to societal pressure. // Otherwise, the new character's orientation will be adjusted, if necessary, to // fit the relationship. if (relationship.RequiresOrientationMatch && (!relationship.Path.Contains("Spouse") || random.Next(101) > chanceOfForcedHeteroMarriage || EffectiveGender?.Archetype != newNote.EffectiveGender?.Opposite)) newNote.ReconcileOrientation(this, newNote.Relationship); return true; }