using HSchool.Content; namespace HSchool.People; /// Sparse A→B opinions on a person. Zero is not stored. public static class OpinionStore { public static int? Get(Person person, string targetId) => person.Opinions.TryGetValue(targetId, out var value) ? value : null; public static bool Set(Person person, string targetId, int value) { value = Math.Clamp(value, OpinionLabels.MinValue, OpinionLabels.MaxValue); if (value == 0) { return Remove(person, targetId); } if (person.Opinions is not IDictionary dict) { throw new InvalidOperationException($"Cannot mutate opinions for {person.Id}."); } if (dict.IsReadOnly) { throw new InvalidOperationException($"Cannot mutate opinions for {person.Id}."); } if (dict.TryGetValue(targetId, out var existing) && existing == value) { return false; } dict[targetId] = value; return true; } public static bool Remove(Person person, string targetId) { if (person.Opinions is not IDictionary dict) { throw new InvalidOperationException($"Cannot mutate opinions for {person.Id}."); } if (dict.IsReadOnly) { throw new InvalidOperationException($"Cannot mutate opinions for {person.Id}."); } return dict.Remove(targetId); } public static int FamilyBasis(Roster roster, BehaviorDef rules, Person from, Person to) { var family = roster.Families.FirstOrDefault(candidate => candidate.Id.Equals(from.FamilyId, StringComparison.Ordinal) && candidate.Id.Equals(to.FamilyId, StringComparison.Ordinal)); if (family is null) { return 0; } var fromParent = InFamily(family.ParentIds, from.Id); var toParent = InFamily(family.ParentIds, to.Id); var fromChild = InFamily(family.ChildIds, from.Id); var toChild = InFamily(family.ChildIds, to.Id); if (fromParent && toChild) { return rules.OpinionParentToChildStart; } if (fromChild && toParent) { return rules.OpinionChildToParentStart; } if (fromChild && toChild && !from.Id.Equals(to.Id, StringComparison.Ordinal)) { return rules.OpinionSiblingStart; } if (fromParent && toParent && !from.Id.Equals(to.Id, StringComparison.Ordinal)) { return rules.OpinionPartnerStart; } return 0; } public static int MoveToward(int current, int target, int step) { if (current == target) { return current; } if (current < target) { return Math.Min(current + step, target); } return Math.Max(current - step, target); } public static HashSet FamilyMemberIds(Roster roster, Person person) { var family = roster.Families.FirstOrDefault(candidate => candidate.Id.Equals(person.FamilyId, StringComparison.Ordinal)); if (family is null) { return []; } var ids = new HashSet(StringComparer.Ordinal); foreach (var id in family.ParentIds) { if (!id.Equals(person.Id, StringComparison.Ordinal)) { ids.Add(id); } } foreach (var id in family.ChildIds) { if (!id.Equals(person.Id, StringComparison.Ordinal)) { ids.Add(id); } } return ids; } private static bool InFamily(IReadOnlyList ids, string id) { foreach (var candidate in ids) { if (candidate.Equals(id, StringComparison.Ordinal)) { return true; } } return false; } }