Halve lesson gain when the bag has no textbook for the subject.

Locker and home do not count. A pack without the field keeps vanilla 0.5.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 09:27:46 +03:00
co-authored by Cursor
parent f4a793aca4
commit 1b3479f984
13 changed files with 252 additions and 20 deletions
@@ -238,6 +238,96 @@ public class LessonTeacherPresentTests
}
}
[Fact]
public void TextbookInBag_GainsFull()
{
var (school, homeroom, pupilId, teacherId) = StaffedMath();
using (school)
{
AdvanceTo(school, LessonStart);
SetPlace(school, pupilId, homeroom);
SetPlace(school, teacherId, homeroom);
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
var before = SkillOf(school, pupilId, "Mathematics");
LessonLearningSystem.Apply(school, 45);
Assert.True(SkillOf(school, pupilId, "Mathematics") > before);
}
}
[Theory]
[InlineData(ItemLocations.Locker)]
[InlineData(ItemLocations.Home)]
public void TextbookNotInBag_GainsHalf(string location)
{
var (school, homeroom, pupilId, teacherId) = StaffedMath();
using (school)
{
AdvanceTo(school, LessonStart);
SetPlace(school, pupilId, homeroom);
SetPlace(school, teacherId, homeroom);
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
var start = SkillOf(school, pupilId, "Mathematics");
LessonLearningSystem.Apply(school, 45);
var bagGain = SkillOf(school, pupilId, "Mathematics") - start;
SetSkill(school, pupilId, "Mathematics", start);
PlaceTextbook(school, pupilId, "Mathematics", location);
LessonLearningSystem.Apply(school, 45);
var awayGain = SkillOf(school, pupilId, "Mathematics") - start;
Assert.True(bagGain > 0);
Assert.Equal(bagGain * 0.5f, awayGain, precision: 4);
}
}
[Fact]
public void OtherSubjectInBag_DoesNotCoverThisLesson()
{
var (school, homeroom, pupilId, teacherId) = StaffedMath();
using (school)
{
AdvanceTo(school, LessonStart);
SetPlace(school, pupilId, homeroom);
SetPlace(school, teacherId, homeroom);
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Bag);
var start = SkillOf(school, pupilId, "Mathematics");
LessonLearningSystem.Apply(school, 45);
var mathGain = SkillOf(school, pupilId, "Mathematics") - start;
SetSkill(school, pupilId, "Mathematics", start);
PlaceTextbook(school, pupilId, "Literature", ItemLocations.Bag);
LessonLearningSystem.Apply(school, 45);
var otherGain = SkillOf(school, pupilId, "Mathematics") - start;
Assert.Equal(mathGain * 0.5f, otherGain, precision: 4);
}
}
[Fact]
public void NoTextbook_LogsOncePerSubject()
{
var (school, homeroom, pupilId, teacherId) = StaffedMath();
using (school)
{
AdvanceTo(school, LessonStart);
SetPlace(school, pupilId, homeroom);
SetPlace(school, teacherId, homeroom);
PlaceTextbook(school, pupilId, "Mathematics", ItemLocations.Home);
LessonLearningSystem.Apply(school, 5);
LessonLearningSystem.Apply(school, 5);
var rows = school.DayLog
.Where(row => row.PersonId == pupilId && row.Type == PersonLogTypes.LessonNoTextbook)
.ToArray();
Assert.Single(rows);
Assert.Equal("Mathematics", rows[0].ThingDef);
Assert.Equal("нет учебника: Математика", rows[0].Caption(school.Catalog!, "ru"));
}
}
private static (School School, string Homeroom, string PupilId, string TeacherId) StaffedMath(
string? teacherId = null)
{
@@ -263,6 +353,25 @@ public class LessonTeacherPresentTests
return (school, schoolClass.RoomId, pupil.Id, hiredId);
}
private static void PlaceTextbook(School school, string personId, string subject, string location)
{
var person = school.Roster!.People.First(row => row.Id.Equals(personId, StringComparison.Ordinal));
if (person.Items is not IList<InventoryItem> items || items.IsReadOnly)
{
throw new InvalidOperationException($"Cannot mutate items for {personId}.");
}
for (var i = items.Count - 1; i >= 0; i--)
{
if (items[i].Subject is not null)
{
items.RemoveAt(i);
}
}
items.Add(new InventoryItem("Textbook", Color: null, Condition: 1f, location, subject));
}
private static void AdvanceTo(School school, DateTime until)
{
while (school.Clock.Time < until)