Пример #1
0
        /// <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);
        }
Пример #2
0
        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;
        }
Пример #3
0
        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;
        }
Пример #4
0
        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;
        }