106 lines
3.2 KiB
C#
106 lines
3.2 KiB
C#
using HSchool.Content;
|
|
using HSchool.People;
|
|
|
|
namespace HSchool.Ai;
|
|
|
|
/// <summary>
|
|
/// Pure map / roster helpers for auto director summons. Queue state lives on the school worker.
|
|
/// </summary>
|
|
public static class DirectorSummons
|
|
{
|
|
public const string HearingAction = "PrincipalHearing";
|
|
|
|
public const string OfficeRoomDef = "PrincipalsOffice";
|
|
|
|
public const string CorridorRoomDef = "Corridor";
|
|
|
|
public static string? OfficeNodeId(MapLayout map)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(map);
|
|
return map.Rooms
|
|
.Where(room => room.Def.Equals(OfficeRoomDef, StringComparison.Ordinal))
|
|
.Select(room => room.Id)
|
|
.OrderBy(id => id, StringComparer.Ordinal)
|
|
.FirstOrDefault();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Corridor (or other neighbour) where the queue waits — never inside the office.
|
|
/// Prefer a <see cref="CorridorRoomDef"/> link; tie-break by ordinal id.
|
|
/// </summary>
|
|
public static string? WaitNodeId(MapLayout map, string? officeNodeId = null)
|
|
{
|
|
ArgumentNullException.ThrowIfNull(map);
|
|
officeNodeId ??= OfficeNodeId(map);
|
|
if (officeNodeId is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
string? bestCorridor = null;
|
|
string? bestAny = null;
|
|
foreach (var link in map.Links)
|
|
{
|
|
var other = OtherEnd(link, officeNodeId);
|
|
if (other is null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
bestAny = PickOrdinal(bestAny, other);
|
|
if (map.NodeDef(other) is { } def
|
|
&& def.Equals(CorridorRoomDef, StringComparison.Ordinal))
|
|
{
|
|
bestCorridor = PickOrdinal(bestCorridor, other);
|
|
}
|
|
}
|
|
|
|
return bestCorridor ?? bestAny;
|
|
}
|
|
|
|
public static bool HasHiredPrincipal(Roster roster) =>
|
|
roster.People.Any(person =>
|
|
person.IsStaff
|
|
&& Staffing.PrincipalPosition.Equals(person.Position, StringComparison.Ordinal));
|
|
|
|
public static string? PrincipalId(Roster roster) =>
|
|
roster.People
|
|
.Where(person =>
|
|
person.IsStaff
|
|
&& Staffing.PrincipalPosition.Equals(person.Position, StringComparison.Ordinal))
|
|
.Select(person => person.Id)
|
|
.OrderBy(id => id, StringComparer.Ordinal)
|
|
.FirstOrDefault();
|
|
|
|
public static bool CanStart(Roster roster, MapLayout map) =>
|
|
HasHiredPrincipal(roster) && OfficeNodeId(map) is not null && WaitNodeId(map) is not null;
|
|
|
|
public static bool RollSummon(float chance, int seed) =>
|
|
chance >= 1f || (chance > 0f && TalkCircles.Roll01(seed) < chance);
|
|
|
|
private static string? OtherEnd(MapLink link, string nodeId)
|
|
{
|
|
if (link.A.Equals(nodeId, StringComparison.Ordinal))
|
|
{
|
|
return link.B;
|
|
}
|
|
|
|
if (link.B.Equals(nodeId, StringComparison.Ordinal))
|
|
{
|
|
return link.A;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private static string PickOrdinal(string? current, string candidate)
|
|
{
|
|
if (current is null)
|
|
{
|
|
return candidate;
|
|
}
|
|
|
|
return string.CompareOrdinal(candidate, current) < 0 ? candidate : current;
|
|
}
|
|
}
|