namespace HSchool.Content.Tests;
public class VanillaCoreTests
{
[Fact]
public void CoreDefaultMap_PassesValidation()
{
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
Assert.True(Directory.Exists(root), $"Vanilla core pack was not copied to {root}.");
var documents = PackDocuments.FromDirectory(CatalogLoader.CorePackId, root);
var catalog = new CatalogLoader().Load([CatalogLoader.CorePackId], documents);
var map = CatalogLoader.LastDefaultMap([CatalogLoader.CorePackId], documents);
Assert.NotNull(map);
MapValidator.Validate(map, catalog);
Assert.Equal("SchoolYard", catalog.Territories["SchoolYard"].DefName);
Assert.True(catalog.Rooms["Corridor"].Slots.Count == 0);
Assert.Equal(["Principal"], catalog.PositionsFor(DefKind.Room, "PrincipalsOffice"));
Assert.Equal("Кабинет директора", catalog.Label("ru", catalog.Rooms["PrincipalsOffice"]));
Assert.Equal("Principal's office", catalog.Label("en", catalog.Rooms["PrincipalsOffice"]));
Assert.Equal(["Sit"], catalog.Things["DirectorsChair"].Actions);
Assert.True(catalog.Buildings.ContainsKey("GymBuilding"));
Assert.True(catalog.Rooms.ContainsKey("Classroom"));
Assert.True(catalog.Rooms["Classroom"].Homeroom);
Assert.False(catalog.Rooms["ComputerLab"].Homeroom);
Assert.Equal("Класс", catalog.Label("ru", catalog.Rooms["Classroom"]));
Assert.Empty(catalog.PositionsFor(DefKind.Room, "Classroom"));
Assert.Empty(catalog.PositionsFor(DefKind.Room, "ComputerLab"));
Assert.Empty(catalog.PositionsFor(DefKind.Room, "GymHall"));
Assert.False(catalog.Positions.ContainsKey("PETeacher"));
Assert.Equal("StudentDesk", catalog.Rooms["Classroom"].SeatThing);
Assert.Equal(16, catalog.Rooms["Classroom"].DefaultSeats);
Assert.Empty(catalog.Rooms["Classroom"].Slots);
Assert.Equal(1, catalog.Things["StudentDesk"].PupilSlots);
Assert.Equal(1, catalog.Things["Computer"].PupilSlots);
Assert.Equal(0, catalog.Things["Desk"].PupilSlots);
Assert.Equal(0, catalog.Things["Chair"].PupilSlots);
Assert.Empty(catalog.Things["Chair"].Layers);
Assert.False(catalog.Things["Chair"].Portable);
Assert.Empty(catalog.Things["StudentDesk"].Layers);
Assert.Equal([ApparelLayers.Bottom, ApparelLayers.Top], catalog.Things["Dress"].Layers);
Assert.Contains("Red", catalog.Things["TShirt"].Colors);
Assert.True(catalog.Things["PeShirt"].Pe);
Assert.True(catalog.Things["Textbook"].Portable);
Assert.Empty(catalog.Things["Textbook"].Layers);
Assert.Equal(0, catalog.Things["Textbook"].CarryChance);
Assert.Equal(1, catalog.Things["SchoolBag"].CarryChance);
Assert.Equal(11, catalog.Things["Phone"].Age?.Min);
Assert.Equal(1, catalog.Things.Values.Count(thing => thing.DefName == "Textbook"));
Assert.Equal([ColorTags.Bright], catalog.Colors["Red"].Tags);
Assert.Equal([ColorTags.Neutral, ColorTags.School], catalog.Colors["White"].Tags);
Assert.Empty(catalog.Colors["Beige"].Tags);
Assert.True(catalog.Subjects.ContainsKey("PrimarySchool"));
Assert.Equal(1, catalog.Subjects["PrimarySchool"].Grades.Min);
Assert.Equal(4, catalog.Subjects["PrimarySchool"].Grades.Max);
Assert.True(catalog.Subjects.ContainsKey("PhysicalEducation"));
Assert.NotNull(catalog.StaffingRules);
Assert.Equal(32, catalog.StaffingRules.PoolSize);
Assert.Equal(20, catalog.StaffingRules.BaseWeeklyHours);
Assert.Equal(36, catalog.StaffingRules.MaxWeeklyHours);
Assert.NotNull(catalog.DayFrame);
Assert.Equal("08:30", catalog.DayFrame.FirstLesson);
Assert.Equal(7, catalog.DayFrame.LessonCount);
Assert.Equal("GymHall", catalog.Subjects["PhysicalEducation"].Room);
Assert.Equal(2, map.Buildings.Count);
var homerooms = map.Rooms.Where(room => room.Def == "Classroom").ToList();
Assert.Equal(11, homerooms.Count);
Assert.Contains(map.Rooms, room => room.Id == "classroom-101" && room.Label == "101");
Assert.Contains(map.Rooms, room => room.Id == "classroom-207" && room.Label == "207");
Assert.DoesNotContain(map.Rooms, room => room.Label is "1A" or "1B" or "2A" or "2B");
Assert.Equal(16, map.Rooms.Single(room => room.Id == "classroom-101").Seats);
Assert.Empty(map.Rooms.Single(room => room.Id == "classroom-101").Slots);
Assert.Equal(
2,
catalog.Things.Values.Count(thing => !thing.Abstract && thing.PupilSlots > 0));
Assert.Equal(0.5f, catalog.Rooms["Classroom"].TravelMinutes);
Assert.Equal(1.5f, catalog.Rooms["Corridor"].TravelMinutes);
Assert.Equal(1.5f, catalog.Rooms["Stairwell"].TravelMinutes);
Assert.Equal(1f, catalog.Rooms["EntranceHall"].TravelMinutes);
Assert.Equal(3f, catalog.Territories["SchoolYard"].TravelMinutes);
Assert.Equal(4, catalog.Traits["Diligent"].CommuteMinutes);
Assert.Equal(-4, catalog.Traits["Lazy"].CommuteMinutes);
Assert.True(catalog.Actions["Sit"].Abstract);
Assert.Equal("Cafeteria", catalog.Actions["EatLunch"].Room);
Assert.Equal("Chair", catalog.Actions["EatLunch"].Thing);
Assert.Equal(15f, catalog.Actions["EatLunch"].Minutes);
Assert.Equal("Hunger", catalog.Actions["EatLunch"].Need);
Assert.Equal(0.5f, catalog.Actions["EatLunch"].NeedGain);
Assert.Equal("SchoolYard", catalog.Actions["WalkYard"].Room);
// Hunger falls fast enough to be felt inside one school day and, like sleep, comes back
// off campus — people eat at home. Before that pair of numbers the school went hungry and
// never recovered: half of it sat below the urgency threshold permanently.
Assert.Equal(0.2f, catalog.Needs["Hunger"].DecayPerHour);
Assert.True(catalog.Needs["Hunger"].RestoredOffCampus);
Assert.True(catalog.Needs["Sleep"].RestoredOffCampus);
Assert.NotNull(catalog.BehaviorRules);
Assert.Equal(0, catalog.BehaviorRules.CommuteSlackMin);
Assert.Equal(6, catalog.BehaviorRules.CommuteSlackMax);
Assert.Equal(0.35f, catalog.BehaviorRules.NeedThreshold);
Assert.Equal(10f, catalog.BehaviorRules.DutyLessonWeight);
Assert.Equal(5f, catalog.BehaviorRules.DutyTravelWeight);
Assert.Equal(20f, catalog.BehaviorRules.NeedWeightAtZero);
Assert.Equal(6f, catalog.BehaviorRules.LunchWeight);
Assert.Equal(5f, catalog.BehaviorRules.CarryMassBase);
Assert.Equal(0.4f, catalog.BehaviorRules.OptionalApparelChance);
Assert.Equal(0.01f, catalog.BehaviorRules.ApparelWearPerHour);
Assert.Equal(0.15f, catalog.BehaviorRules.ApparelReplaceBelow);
Assert.Equal(4, catalog.BehaviorRules.ApparelConditionBands.Count);
Assert.Equal("ApparelConditionIntact", catalog.BehaviorRules.ApparelConditionBands[0].Id);
}
///
/// Every name a card can print has to exist in both locales. Missing ones do not fail — they
/// fall through to the raw key, which reads as a label in English and as a leak in Russian:
/// the build row showed "Build: Обычное" for exactly this reason.
///
[Fact]
public void EveryVanillaLabel_ExistsInBothLocales()
{
var root = Path.Combine(AppContext.BaseDirectory, "vanilla");
var catalog = new CatalogLoader().Load(
[CatalogLoader.CorePackId],
PackDocuments.FromDirectory(CatalogLoader.CorePackId, root));
var keys = new List();
keys.AddRange(Names(catalog.Actions.Values));
keys.AddRange(Names(catalog.Things.Values));
keys.AddRange(Names(catalog.Positions.Values));
keys.AddRange(Names(catalog.Works.Values));
keys.AddRange(Names(catalog.Rooms.Values));
keys.AddRange(Names(catalog.Buildings.Values));
keys.AddRange(Names(catalog.Floors.Values));
keys.AddRange(Names(catalog.Territories.Values));
keys.AddRange(Names(catalog.Skills.Values));
keys.AddRange(Names(catalog.Traits.Values));
keys.AddRange(Names(catalog.BodyAttributes.Values));
keys.AddRange(Names(catalog.Needs.Values));
keys.AddRange(Names(catalog.Countries.Values));
keys.AddRange(Names(catalog.ClimatePresets.Values));
keys.AddRange(Names(catalog.Subjects.Values));
keys.AddRange(Names(catalog.Staffing.Values));
keys.AddRange(Names(catalog.DayFrames.Values));
keys.AddRange(Names(catalog.Holidays.Values));
keys.AddRange(Names(catalog.Behavior.Values));
keys.AddRange(Names(catalog.Colors.Values));
keys.AddRange(catalog.PackIds);
// Derived in code, so no def carries them.
keys.Add(BodyBuilds.Attribute);
keys.AddRange(BodyBuilds.Values);
keys.AddRange(ApparelLayers.All);
if (catalog.BehaviorRules is { } rules)
{
keys.AddRange(rules.ApparelConditionBands.Select(band => band.Id));
}
keys.Add("ApparelReplaced");
foreach (var value in catalog.BodyAttributes.Values.SelectMany(def => def.Options))
{
keys.Add(value.Value);
}
var missing = keys
.Distinct(StringComparer.Ordinal)
.SelectMany(key => new[] { ("ru", key), ("en", key) })
.Where(pair => !catalog.HasText(pair.Item1, pair.Item2))
.Select(pair => $"{pair.Item1}:{pair.Item2}")
.Order(StringComparer.Ordinal)
.ToArray();
Assert.Empty(missing);
}
private static IEnumerable Names(IEnumerable defs) =>
defs.Where(def => !def.Abstract).Select(def => def.DefName);
}