Add opinions storage, morning drift, and Connections card tab.
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
using HSchool.Content;
|
||||
|
||||
namespace HSchool.People;
|
||||
|
||||
/// <summary>Sparse A→B opinions on a person. Zero is not stored.</summary>
|
||||
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<string, int> 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<string, int> 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<string> 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<string>(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<string> ids, string id)
|
||||
{
|
||||
foreach (var candidate in ids)
|
||||
{
|
||||
if (candidate.Equals(id, StringComparison.Ordinal))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user