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;
}
}