Show director summons as info toasts and presence queue labels.

Corridor waiters need a summon-phase byte on presence (v11); EventDef DirectorSummoned is info-only so clocks keep running.
This commit is contained in:
Leonid Pershin
2026-08-21 13:07:47 +03:00
parent d3c2327e23
commit 9e429d0cd7
29 changed files with 453 additions and 28 deletions
@@ -112,7 +112,16 @@ public class DirectorSummonTests
name => name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
method => method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
method => method.Name.StartsWith("Set", StringComparison.Ordinal)
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
method => method.Name.StartsWith("Send", StringComparison.Ordinal)
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
Assert.DoesNotContain(
typeof(School).GetMethods(System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public),
method => method.Name.StartsWith("Enqueue", StringComparison.Ordinal)
&& method.Name.Contains("Summon", StringComparison.OrdinalIgnoreCase));
}
[Fact]
@@ -123,6 +132,67 @@ public class DirectorSummonTests
Assert.Equal("corridor-1", DirectorSummons.WaitNodeId(map));
}
[Fact]
public void QualifyingSummon_RaisesDirectorSummonWorldEvent()
{
var (school, first, _, _) = TwoPupilsPrincipalAndTeacherOnBreak();
using (school)
{
school.DrainWorldEvents();
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
var facts = school.DrainWorldEvents();
var fact = Assert.Single(facts, row => row.Trigger.Equals(EventTriggers.DirectorSummon, StringComparison.Ordinal));
Assert.Equal(first.Id, fact.PersonKey);
}
}
[Fact]
public void QueueAdvancesWithoutOpenClient()
{
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak();
using (school)
{
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
Assert.True(DirectorSummonSystem.TryEnqueue(school, second.Id, chance: 1f));
DirectorSummonSystem.Apply(school);
var head = school.DirectorSummons.OrderBy(row => row.Order).First();
var next = school.DirectorSummons.OrderBy(row => row.Order).Skip(1).First();
PlaceAt(school, head.PersonId, "principals-office");
DirectorSummonSystem.Apply(school);
Assert.True(ActivitySystem.IsPrincipalHearing(ActivityOf(school, head.PersonId)));
DirectorSummonSystem.CompleteHearing(school, head.PersonId);
DirectorSummonSystem.Apply(school);
Assert.False(DirectorSummonSystem.IsSummoned(school, head.PersonId));
Assert.True(DirectorSummonSystem.IsSummoned(school, next.PersonId));
PlaceAt(school, next.PersonId, "corridor-1");
Assert.Equal(DirectorSummons.PresenceWaiting, school.DirectorSummonPresencePhase(next.PersonId));
}
}
[Fact]
public void SummonPresencePhase_WaitingInCorridor_HearingInOffice()
{
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak();
using (school)
{
Assert.True(DirectorSummonSystem.TryEnqueue(school, first.Id, chance: 1f));
Assert.True(DirectorSummonSystem.TryEnqueue(school, second.Id, chance: 1f));
DirectorSummonSystem.Apply(school);
var ordered = school.DirectorSummons.OrderBy(row => row.Order).ToArray();
PlaceAt(school, ordered[0].PersonId, "principals-office");
PlaceAt(school, ordered[1].PersonId, "corridor-1");
DirectorSummonSystem.Apply(school);
Assert.Equal(DirectorSummons.PresenceHearing, school.DirectorSummonPresencePhase(ordered[0].PersonId));
Assert.Equal(DirectorSummons.PresenceWaiting, school.DirectorSummonPresencePhase(ordered[1].PersonId));
}
}
private static IReadOnlyList<string> QueueOrderFromFight(int seed)
{
var (school, first, second, _) = TwoPupilsPrincipalAndTeacherOnBreak(seed);