using HSchool.Content;
using HSchool.People;
namespace HSchool.Ai;
///
/// Pure map / roster helpers for nurse visits. Queue state lives on the school worker.
/// Simpler than director summons: no hearing opinion, tend while co-located.
///
public static class NurseCare
{
/// Presence / card label while walking to the wait node or office.
public const string GoingAction = "GoingToNurse";
/// Presence / card label while queued in the corridor.
public const string WaitingAction = "WaitForNurse";
/// Presence / card label while being tended in the office.
public const string CareAction = "NurseCare";
public const byte PresenceNone = 0;
public const byte PresenceGoing = 1;
public const byte PresenceWaiting = 2;
public const byte PresenceCare = 3;
public const string OfficeRoomDef = "MedicalOffice";
public const string CorridorRoomDef = "Corridor";
public const string MedicineSkill = "Medicine";
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();
}
///
/// Corridor (or other neighbour) where the queue waits — never inside the office.
///
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 HasHiredNurse(Roster roster) =>
roster.People.Any(person =>
person.IsStaff
&& Staffing.NursePosition.Equals(person.Position, StringComparison.Ordinal));
public static string? NurseId(Roster roster) =>
roster.People
.Where(person =>
person.IsStaff
&& Staffing.NursePosition.Equals(person.Position, StringComparison.Ordinal))
.Select(person => person.Id)
.OrderBy(id => id, StringComparer.Ordinal)
.FirstOrDefault();
public static float MedicineOf(Person? nurse) =>
nurse is not null && nurse.Skills.TryGetValue(MedicineSkill, out var level) ? level : 0f;
public static bool CanStart(Roster roster, MapLayout map) =>
HasHiredNurse(roster) && OfficeNodeId(map) is not null && WaitNodeId(map) is not null;
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;
}
}