Enhance protocol and simulation features with activity tracking and behavior definitions
- Updated protocol documentation to include new `activity` and `activityLabel` fields in the person card response, reflecting real-time activity status. - Introduced `BehaviorDef` to define behavior rules, including need thresholds and lesson skill gains, enhancing AI decision-making. - Revised the `DefCatalog` to incorporate behavior definitions and updated validation logic to ensure proper behavior handling. - Enhanced the simulation to manage presence and activity states, allowing for more dynamic interactions within the school environment. - Updated tests to validate the new activity tracking and behavior functionalities, ensuring robust performance and reliability. - Improved localization strings to support new activity and behavior features, enhancing user experience.
This commit is contained in:
@@ -13,7 +13,7 @@ namespace HSchool.Simulation;
|
||||
internal static class PresenceSystem
|
||||
{
|
||||
private static readonly QueryDescription People =
|
||||
new QueryDescription().WithAll<PersonIdentity, PersonRoles, PersonTraits, Presence>();
|
||||
new QueryDescription().WithAll<PersonIdentity, PersonRoles, PersonTraits, Presence, PersonActivity>();
|
||||
|
||||
public static void Apply(School school, double gameMinutes)
|
||||
{
|
||||
@@ -48,7 +48,7 @@ internal static class PresenceSystem
|
||||
{
|
||||
var rows = new List<PresenceSnapshot>();
|
||||
var world = school.World;
|
||||
world.Query(in People, (ref PersonIdentity identity, ref Presence presence) =>
|
||||
world.Query(in People, (ref PersonIdentity identity, ref Presence presence, ref PersonActivity activity) =>
|
||||
{
|
||||
rows.Add(new PresenceSnapshot(
|
||||
identity.Id,
|
||||
@@ -56,7 +56,10 @@ internal static class PresenceSystem
|
||||
presence.RemainingMinutes,
|
||||
presence.DestinationId,
|
||||
presence.HeadingHome,
|
||||
presence.Path));
|
||||
presence.Path,
|
||||
activity.ActionId,
|
||||
activity.Thing,
|
||||
activity.RemainingMinutes));
|
||||
});
|
||||
rows.Sort((left, right) => StringComparer.Ordinal.Compare(left.PersonId, right.PersonId));
|
||||
return rows;
|
||||
@@ -74,17 +77,19 @@ internal static class PresenceSystem
|
||||
var byId = saved
|
||||
.Where(row => !string.IsNullOrWhiteSpace(row.PersonId))
|
||||
.ToDictionary(row => row.PersonId, StringComparer.Ordinal);
|
||||
ForEachPerson(school, (person, _, ref presence) =>
|
||||
ForEachPerson(school, (person, _, ref presence, ref activity) =>
|
||||
{
|
||||
if (!byId.TryGetValue(person.Id, out var row))
|
||||
{
|
||||
presence = PlaceByDuty(school, person);
|
||||
activity = PersonActivity.Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
if (row.NodeId is null)
|
||||
{
|
||||
presence = Presence.OffCampus;
|
||||
activity = PersonActivity.Idle;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -94,16 +99,20 @@ internal static class PresenceSystem
|
||||
row.DestinationId,
|
||||
row.HeadingHome,
|
||||
row.Path?.ToArray() ?? []);
|
||||
activity = string.IsNullOrWhiteSpace(row.ActionId)
|
||||
? PersonActivity.Idle
|
||||
: new PersonActivity(row.ActionId, row.ActionThing, row.ActionRemaining);
|
||||
});
|
||||
}
|
||||
|
||||
public static void PlaceMissingByDuty(School school)
|
||||
{
|
||||
ForEachPerson(school, (person, _, ref presence) =>
|
||||
ForEachPerson(school, (person, _, ref presence, ref activity) =>
|
||||
{
|
||||
if (!presence.IsOnCampus)
|
||||
{
|
||||
presence = PlaceByDuty(school, person);
|
||||
activity = PersonActivity.Idle;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -323,7 +332,7 @@ internal static class PresenceSystem
|
||||
}
|
||||
|
||||
var world = school.World;
|
||||
world.Query(in People, (ref Presence presence) =>
|
||||
world.Query(in People, (ref Presence presence, ref PersonActivity activity) =>
|
||||
{
|
||||
if (presence.HeadingHome
|
||||
&& presence.NodeId is not null
|
||||
@@ -332,6 +341,7 @@ internal static class PresenceSystem
|
||||
&& presence.NodeId.Equals(yard, StringComparison.Ordinal))
|
||||
{
|
||||
presence = Presence.OffCampus;
|
||||
activity = PersonActivity.Idle;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -349,17 +359,17 @@ internal static class PresenceSystem
|
||||
private static IReadOnlyList<Person> OrderedPeople(School school) =>
|
||||
school.Roster!.People.OrderBy(person => person.Id, StringComparer.Ordinal).ToArray();
|
||||
|
||||
private delegate void PersonAction(Person person, PersonIdentity identity, ref Presence presence);
|
||||
private delegate void PersonAction(Person person, PersonIdentity identity, ref Presence presence, ref PersonActivity activity);
|
||||
|
||||
private static void ForEachPerson(School school, PersonAction action)
|
||||
{
|
||||
var roster = school.Roster!.People.ToDictionary(person => person.Id, StringComparer.Ordinal);
|
||||
var world = school.World;
|
||||
world.Query(in People, (ref PersonIdentity identity, ref Presence presence) =>
|
||||
world.Query(in People, (ref PersonIdentity identity, ref Presence presence, ref PersonActivity activity) =>
|
||||
{
|
||||
if (roster.TryGetValue(identity.Id, out var person))
|
||||
{
|
||||
action(person, identity, ref presence);
|
||||
action(person, identity, ref presence, ref activity);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -371,4 +381,7 @@ public sealed record PresenceSnapshot(
|
||||
float RemainingMinutes,
|
||||
string? DestinationId,
|
||||
bool HeadingHome,
|
||||
IReadOnlyList<string> Path);
|
||||
IReadOnlyList<string> Path,
|
||||
string? ActionId = null,
|
||||
string? ActionThing = null,
|
||||
float ActionRemaining = 0f);
|
||||
|
||||
Reference in New Issue
Block a user