Merge branch 'phase/73-lesson-marks'

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-21 12:00:53 +03:00
co-authored by Cursor
17 changed files with 703 additions and 22 deletions
+39
View File
@@ -683,6 +683,45 @@ internal static class PeopleDefValidator
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' directorHearingMinutes must be positive.");
}
if (behavior.LessonMarkMax < 0)
{
throw new ContentLoadException($"BehaviorDef '{behavior.DefName}' lessonMarkMax cannot be negative.");
}
if (behavior.LessonMarkWhenNoTeacher is { } absent
&& absent is < 2 or > 5)
{
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' lessonMarkWhenNoTeacher must be 25 or null.");
}
if (behavior.LessonMarkThresholds.Count > 0)
{
if (behavior.LessonMarkThresholds.Count != 3)
{
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' lessonMarkThresholds must list three floors (5, 4, 3).");
}
float? previous = null;
foreach (var floor in behavior.LessonMarkThresholds)
{
if (floor is < 0f or > 1f)
{
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' lessonMarkThresholds must be 01.");
}
if (previous is { } prior && floor > prior)
{
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' lessonMarkThresholds must be descending.");
}
previous = floor;
}
}
}
private static void ValidateTopic(TopicDef topic, DefCatalog catalog)
+24
View File
@@ -602,6 +602,30 @@ public sealed class BehaviorDef : Def
"reprimand",
];
/// <summary>
/// How many recent lesson marks a person keeps. Oldest drop first. Zero disables marks.
/// </summary>
public int LessonMarkMax { get; init; } = 40;
/// <summary>
/// Inclusive quality floors for marks 5, 4, 3 (descending). Below the last → 2.
/// Empty falls back to <see cref="DefaultLessonMarkThresholds"/>.
/// </summary>
public IReadOnlyList<float> LessonMarkThresholds { get; init; } = DefaultLessonMarkThresholds;
/// <summary>
/// Mark written when the assigned teacher is not standing in the lesson room.
/// Null = skip the mark (no grade for air). Vanilla writes 2.
/// </summary>
public int? LessonMarkWhenNoTeacher { get; init; } = 2;
public static IReadOnlyList<float> DefaultLessonMarkThresholds { get; } =
[
0.85f,
0.6f,
0.35f,
];
public static IReadOnlyList<OpinionBand> DefaultOpinionBands { get; } =
[
new() { Min = 70, Id = "OpinionCloseFriend" },