Add speech-topic policy from tomorrow and a morning family talk.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 13:53:12 +03:00
co-authored by Cursor
parent 5e147a8e47
commit 92c2143329
31 changed files with 909 additions and 35 deletions
+6
View File
@@ -472,6 +472,12 @@ public sealed class BehaviorDef : Def
/// <summary>Points each work morning moves an opinion toward family basis or zero.</summary>
public int OpinionDriftPerMorning { get; init; } = 3;
/// <summary>
/// Family-morning opinion uses this fraction of the topic's school-talk shift. Below 1 so
/// breakfast is quieter than a corridor circle.
/// </summary>
public float HomeTalkOpinionScale { get; init; } = 0.5f;
/// <summary>How many non-family friends or enemies the card lists at the top.</summary>
public int OpinionTopCount { get; init; } = 5;
+68
View File
@@ -0,0 +1,68 @@
namespace HSchool.Content;
/// <summary>School speech-topic strictness. Values are wire and save ids.</summary>
public static class SpeechPolicies
{
public const string Free = "free";
public const string NoRude = "noRude";
public const string StudyOnly = "studyOnly";
public static readonly IReadOnlyList<string> All = [Free, NoRude, StudyOnly];
public static bool IsKnown(string value) =>
All.Any(candidate => candidate.Equals(value, StringComparison.Ordinal));
/// <summary>
/// Whether this topic may start under the live policy. Already-running circles are not filtered.
/// </summary>
public static bool Allows(string policy, TopicDef topic)
{
ArgumentNullException.ThrowIfNull(topic);
if (policy.Equals(StudyOnly, StringComparison.Ordinal))
{
return topic.Tags.Contains(TopicTags.Study, StringComparer.Ordinal);
}
if (policy.Equals(NoRude, StringComparison.Ordinal))
{
return !topic.Tags.Contains(TopicTags.Rude, StringComparer.Ordinal);
}
return true;
}
}
/// <summary>Live speech rules plus optional next-day overrides waiting for a work morning.</summary>
public sealed record SchoolSpeechRules
{
public string Students { get; init; } = SpeechPolicies.Free;
public string Staff { get; init; } = SpeechPolicies.Free;
public string? PendingStudents { get; init; }
public string? PendingStaff { get; init; }
public string For(bool student) => student ? Students : Staff;
public SchoolSpeechRules WithPending(string? students, string? staff) =>
this with { PendingStudents = students, PendingStaff = staff };
public SchoolSpeechRules ApplyPending()
{
var next = this;
if (PendingStudents is { } students)
{
next = next with { Students = students, PendingStudents = null };
}
if (PendingStaff is { } staff)
{
next = next with { Staff = staff, PendingStaff = null };
}
return next;
}
public bool HasPending => PendingStudents is not null || PendingStaff is not null;
}