288 lines
9.7 KiB
C#
288 lines
9.7 KiB
C#
using Arch.Core;
|
|
using HSchool.Ai;
|
|
using HSchool.Content;
|
|
using HSchool.People;
|
|
using HSchool.Server.Api;
|
|
using HSchool.Simulation;
|
|
|
|
namespace HSchool.Server.Game;
|
|
|
|
/// <summary>
|
|
/// Builds a person card on the school's worker thread so live need values come from the World.
|
|
/// </summary>
|
|
internal static partial class PersonCardReader
|
|
{
|
|
private static readonly QueryDescription IdentityAndNeeds =
|
|
new QueryDescription().WithAll<PersonIdentity, PersonNeeds>();
|
|
|
|
private static readonly QueryDescription IdentityAndSkills =
|
|
new QueryDescription().WithAll<PersonIdentity, PersonSkills>();
|
|
|
|
private static readonly QueryDescription IdentityAndActivity =
|
|
new QueryDescription().WithAll<PersonIdentity, PersonActivity>();
|
|
|
|
private static readonly QueryDescription IdentityAndPresence =
|
|
new QueryDescription().WithAll<PersonIdentity, Presence>();
|
|
|
|
public static PersonCardResponse? Read(School school, string personId, string locale)
|
|
{
|
|
var roster = school.Roster;
|
|
if (roster is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var person = roster.People.FirstOrDefault(candidate => candidate.Id.Equals(personId, StringComparison.Ordinal));
|
|
person ??= school.Applicants?.Applicants
|
|
.FirstOrDefault(applicant => applicant.Person.Id.Equals(personId, StringComparison.Ordinal))
|
|
?.Person;
|
|
if (person is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var catalog = school.Catalog;
|
|
var classes = roster.Classes.ToDictionary(schoolClass => schoolClass.Id, StringComparer.Ordinal);
|
|
var people = roster.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
|
|
int? year = null;
|
|
string? letter = null;
|
|
string? classTeacherId = null;
|
|
string? classTeacherName = null;
|
|
if (person.ClassId is { } classId && classes.TryGetValue(classId, out var schoolClass))
|
|
{
|
|
year = schoolClass.Year;
|
|
letter = schoolClass.Letter;
|
|
classTeacherId = schoolClass.ClassTeacherId;
|
|
if (classTeacherId is { } teacherId && people.TryGetValue(teacherId, out var teacher))
|
|
{
|
|
classTeacherName = teacher.Name.Full;
|
|
}
|
|
}
|
|
|
|
var needs = LiveNeeds(school.World, personId) ?? person.Needs;
|
|
var skills = LiveSkills(school.World, personId);
|
|
var activityId = LiveActivity(school.World, personId)
|
|
?? school.DirectorSummonPresenceActionId(personId);
|
|
string? activityLabel = null;
|
|
if (activityId is not null && catalog is not null && catalog.Actions.TryGetValue(activityId, out var action))
|
|
{
|
|
activityLabel = catalog.Label(locale, action);
|
|
}
|
|
else if (activityId is not null)
|
|
{
|
|
activityLabel = activityId;
|
|
}
|
|
|
|
var circle = school.TalkCircleOf(personId);
|
|
|
|
return new PersonCardResponse(
|
|
person.Id,
|
|
person.Name.Full,
|
|
person.Name.Surname,
|
|
person.Name.Given,
|
|
person.Name.Patronymic,
|
|
person.Female,
|
|
person.AgeOn(school.Clock.Time),
|
|
person.BirthDate,
|
|
PeopleListMapper.RolesOf(person),
|
|
year,
|
|
letter,
|
|
person.ClassId,
|
|
person.Position,
|
|
PeopleListMapper.PositionLabel(catalog, locale, person.Position),
|
|
Body(person, catalog, locale),
|
|
Skills(person, skills, catalog, locale),
|
|
Traits(person, catalog, locale),
|
|
Needs(needs, catalog, locale),
|
|
activityId,
|
|
activityLabel,
|
|
Family(roster, person),
|
|
Worn(person, catalog, locale),
|
|
Carried(person, catalog, locale),
|
|
catalog is null ? 0f : CarryMass.Held(catalog, person.Items),
|
|
Capacity(person, skills, catalog),
|
|
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),
|
|
Orientation: OrientationOf(person, catalog, locale),
|
|
TalkCircleMemberIds: circle?.MemberIds ?? [],
|
|
TalkTopicId: circle?.TopicId,
|
|
ClassTeacherId: classTeacherId,
|
|
ClassTeacherName: classTeacherName,
|
|
Offenses: Offenses(roster, person, catalog, locale),
|
|
LessonMarks: LessonMarks(person, catalog, locale),
|
|
Attendance: Attendance(person, catalog, locale));
|
|
}
|
|
|
|
private static IReadOnlyList<LessonMarkEntryResponse> LessonMarks(
|
|
Person person,
|
|
DefCatalog? catalog,
|
|
string locale)
|
|
{
|
|
var rows = person.LessonMarks;
|
|
if (rows is null || rows.Count == 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var result = new LessonMarkEntryResponse[rows.Count];
|
|
for (var i = 0; i < rows.Count; i++)
|
|
{
|
|
var row = rows[i];
|
|
result[i] = new LessonMarkEntryResponse(
|
|
row.Subject,
|
|
SubjectLabel(catalog, locale, row.Subject),
|
|
row.Value,
|
|
row.Time,
|
|
row.Period);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static IReadOnlyList<AttendanceEntryResponse> Attendance(
|
|
Person person,
|
|
DefCatalog? catalog,
|
|
string locale)
|
|
{
|
|
var rows = person.Attendance;
|
|
if (rows is null || rows.Count == 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var result = new AttendanceEntryResponse[rows.Count];
|
|
for (var i = 0; i < rows.Count; i++)
|
|
{
|
|
var row = rows[i];
|
|
result[i] = new AttendanceEntryResponse(
|
|
row.Subject,
|
|
SubjectLabel(catalog, locale, row.Subject),
|
|
row.Status,
|
|
StatusLabel(catalog, locale, row.Status),
|
|
row.Time,
|
|
row.Period,
|
|
row.AbsenceReason);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static string SubjectLabel(DefCatalog? catalog, string locale, string subject)
|
|
{
|
|
if (catalog is not null && catalog.Subjects.TryGetValue(subject, out var def))
|
|
{
|
|
return catalog.Label(locale, def);
|
|
}
|
|
|
|
return subject;
|
|
}
|
|
|
|
private static string StatusLabel(DefCatalog? catalog, string locale, string status)
|
|
{
|
|
var key = AttendanceStatuses.LocaleKey(status);
|
|
if (catalog is not null && catalog.HasText(locale, key))
|
|
{
|
|
return catalog.Text(locale, key);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
private static IReadOnlyList<OffenseEntryResponse> Offenses(
|
|
Roster roster,
|
|
Person person,
|
|
DefCatalog? catalog,
|
|
string locale)
|
|
{
|
|
var rows = person.Offenses;
|
|
if (rows is null || rows.Count == 0)
|
|
{
|
|
return [];
|
|
}
|
|
|
|
var byId = roster.People.ToDictionary(candidate => candidate.Id, StringComparer.Ordinal);
|
|
var result = new OffenseEntryResponse[rows.Count];
|
|
for (var i = 0; i < rows.Count; i++)
|
|
{
|
|
var row = rows[i];
|
|
string? otherName = null;
|
|
if (row.OtherPersonId is { } otherId && byId.TryGetValue(otherId, out var other))
|
|
{
|
|
otherName = other.Name.Full;
|
|
}
|
|
|
|
var label = KindLabel(catalog, locale, row.Kind);
|
|
result[i] = new OffenseEntryResponse(row.Kind, label, row.Time, row.OtherPersonId, otherName);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
private static string KindLabel(DefCatalog? catalog, string locale, string kind)
|
|
{
|
|
var key = OffenseKinds.LocaleKey(kind);
|
|
if (catalog is not null && catalog.HasText(locale, key))
|
|
{
|
|
return catalog.Text(locale, key);
|
|
}
|
|
|
|
return kind;
|
|
}
|
|
|
|
private static IReadOnlyDictionary<string, float>? LiveNeeds(World world, string personId)
|
|
{
|
|
Dictionary<string, float>? found = null;
|
|
world.Query(in IdentityAndNeeds, (ref PersonIdentity identity, ref PersonNeeds needs) =>
|
|
{
|
|
if (identity.Id.Equals(personId, StringComparison.Ordinal))
|
|
{
|
|
found = new Dictionary<string, float>(needs.Values, StringComparer.Ordinal);
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
|
|
private static IReadOnlyDictionary<string, float>? LiveSkills(World world, string personId)
|
|
{
|
|
Dictionary<string, float>? found = null;
|
|
world.Query(in IdentityAndSkills, (ref PersonIdentity identity, ref PersonSkills skills) =>
|
|
{
|
|
if (identity.Id.Equals(personId, StringComparison.Ordinal))
|
|
{
|
|
found = new Dictionary<string, float>(skills.Values, StringComparer.Ordinal);
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
|
|
private static string? LiveActivity(World world, string personId)
|
|
{
|
|
string? found = null;
|
|
var query = IdentityAndActivity;
|
|
world.Query(in query, (ref PersonIdentity identity, ref PersonActivity activity) =>
|
|
{
|
|
if (identity.Id.Equals(personId, StringComparison.Ordinal) && activity.IsActive)
|
|
{
|
|
found = activity.ActionId;
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
|
|
public static string? NodeId(School school, string personId)
|
|
{
|
|
string? found = null;
|
|
var query = IdentityAndPresence;
|
|
school.World.Query(in query, (ref PersonIdentity identity, ref Presence presence) =>
|
|
{
|
|
if (identity.Id.Equals(personId, StringComparison.Ordinal))
|
|
{
|
|
found = presence.NodeId;
|
|
}
|
|
});
|
|
return found;
|
|
}
|
|
}
|