Complete phase 47 romance pack on generic orientation and affinity hooks.

Vanilla catalogs stay without crushes; a content pack ships the overlay so later mods can reuse it.
This commit is contained in:
Leonid Pershin
2026-08-20 10:42:42 +03:00
parent cb869e4977
commit d2911c3daf
43 changed files with 1840 additions and 29 deletions
+97 -2
View File
@@ -89,7 +89,8 @@ internal static class PersonCardReader
person.LockerRoomId is not null
|| person.Items.Any(item => item.Location.Equals(ItemLocations.Locker, StringComparison.Ordinal)),
person.Items.Count(item => item.Location.Equals(ItemLocations.Home, StringComparison.Ordinal)),
Connections: Connections(roster, person, catalog, locale));
Connections: Connections(roster, person, catalog, locale),
Orientation: OrientationOf(person, catalog, locale));
}
private static IReadOnlyDictionary<string, float>? LiveNeeds(World world, string personId)
@@ -449,7 +450,101 @@ internal static class PersonCardReader
var top = catalog?.BehaviorRules?.OpinionTopCount ?? 5;
var friends = others.Where(row => row.Opinion > 0).Take(top).ToArray();
var enemies = others.Where(row => row.Opinion < 0).Take(top).ToArray();
return new PersonConnectionsResponse(family, friends, enemies, others);
var crushes = CrushesOf(person, people, catalog, locale);
var admirers = AdmirersOf(roster, person, people, catalog, locale);
var pair = PairOf(person, people, catalog, locale);
return new PersonConnectionsResponse(family, friends, enemies, others, crushes, admirers, pair);
}
private static DefLabelResponse? OrientationOf(Person person, DefCatalog? catalog, string locale)
{
if (person.Orientation is null)
{
return null;
}
var label = person.Orientation;
if (catalog is not null && catalog.Orientations.TryGetValue(person.Orientation, out var def))
{
label = catalog.Label(locale, def);
}
return new DefLabelResponse(person.Orientation, label);
}
private static IReadOnlyList<PersonOpinionLinkResponse> CrushesOf(
Person person,
IReadOnlyDictionary<string, Person> people,
DefCatalog? catalog,
string locale)
{
if (person.Bonds is null)
{
return [];
}
var rows = new List<PersonOpinionLinkResponse>();
foreach (var id in person.Bonds.Crushes)
{
if (!people.TryGetValue(id, out var target))
{
continue;
}
var opinion = OpinionStore.Get(person, id) ?? 0;
rows.Add(Link(target, opinion, catalog, locale));
}
return rows;
}
private static IReadOnlyList<PersonOpinionLinkResponse> AdmirersOf(
Roster roster,
Person person,
IReadOnlyDictionary<string, Person> people,
DefCatalog? catalog,
string locale)
{
var rows = new List<PersonOpinionLinkResponse>();
foreach (var other in roster.People)
{
if (other.Id.Equals(person.Id, StringComparison.Ordinal) || other.Bonds is null)
{
continue;
}
if (!other.Bonds.Crushes.Contains(person.Id, StringComparer.Ordinal))
{
continue;
}
var opinion = OpinionStore.Get(other, person.Id) ?? 0;
rows.Add(Link(other, opinion, catalog, locale));
}
return rows;
}
private static PersonRelResponse? PairOf(
Person person,
IReadOnlyDictionary<string, Person> people,
DefCatalog? catalog,
string locale)
{
if (person.Bonds?.PartnerId is not { } partnerId || !people.TryGetValue(partnerId, out var partner))
{
return null;
}
int? opinion = null;
string? label = null;
if (person.Opinions.TryGetValue(partnerId, out var value))
{
opinion = value;
label = catalog is null ? OpinionLabels.BandId(null, value) : OpinionLabels.Label(catalog, locale, value);
}
return new PersonRelResponse(partner.Id, partner.Name.Full, partner.Female, opinion, label);
}
private static PersonFamilyResponse FamilyWithOpinions(
+11
View File
@@ -996,6 +996,17 @@ internal sealed class SchoolWorker
generated = true;
}
var assigned = OrientationGenerator.Assign(catalog, roster, seed);
generated |= !ReferenceEquals(assigned, roster);
roster = assigned;
var assignedPool = OrientationGenerator.AssignPool(catalog, applicants, seed);
generated |= !ReferenceEquals(assignedPool, applicants);
applicants = assignedPool;
if (Affinity.Refresh(catalog, roster, school.Clock.Time).Count > 0)
{
generated = true;
}
roster = LockerAssigner.Apply(catalog, map, roster);
if (!RosterFit.Matches(roster, demand))