Add talk circles with TopicDef, staff/phone chat, and opinion outcomes.

This commit is contained in:
Leonid Pershin
2026-08-20 08:55:55 +03:00
parent 9d96108516
commit 47fc10da02
26 changed files with 1948 additions and 175 deletions
+64
View File
@@ -65,6 +65,13 @@ internal static class PeopleDefValidator
ValidateBehavior(behavior, log);
}
foreach (var topic in catalog.Topics.Values)
{
ValidateTopic(topic, catalog);
}
RequireTalkTopics(catalog);
if (catalog.Staffing.Values.Count(def => !def.Abstract) > 1)
{
throw new ContentLoadException("A catalog may only have one concrete StaffingDef.");
@@ -550,6 +557,63 @@ internal static class PeopleDefValidator
log.Warning(
$"BehaviorDef '{behavior.DefName}' lunchWeight {behavior.LunchWeight} is above dutyLessonWeight {behavior.DutyLessonWeight}; pupils will leave class for lunch.");
}
if (behavior.TalkCircleMin < 2 || behavior.TalkCircleMax < behavior.TalkCircleMin)
{
throw new ContentLoadException(
$"BehaviorDef '{behavior.DefName}' talk circle size must be at least 2 with max ≥ min.");
}
}
private static void ValidateTopic(TopicDef topic, DefCatalog catalog)
{
if (topic.Abstract)
{
return;
}
if (topic.Tags.Count == 0)
{
throw new ContentLoadException($"TopicDef '{topic.DefName}' needs at least one tag.");
}
if (topic.Age is { Min: var min, Max: var max } && min > max)
{
throw new ContentLoadException($"TopicDef '{topic.DefName}' age min cannot exceed max.");
}
foreach (var role in topic.Roles)
{
if (!PersonRoles.IsKnown(role))
{
throw new ContentLoadException($"TopicDef '{topic.DefName}' has unknown role '{role}'.");
}
}
if (topic.Language is not null && !catalog.Skills.ContainsKey(topic.Language))
{
throw new ContentLoadException($"TopicDef '{topic.DefName}' references unknown SkillDef '{topic.Language}'.");
}
}
/// <summary>A catalog with circle actions but no topics would silently solo-chat — refuse load.</summary>
private static void RequireTalkTopics(DefCatalog catalog)
{
var hasTalkAction = catalog.Actions.Values.Any(action =>
!action.Abstract
&& (action.DefName.Equals(TalkActions.Chat, StringComparison.Ordinal)
|| action.DefName.Equals(TalkActions.StaffChat, StringComparison.Ordinal)
|| action.DefName.Equals(TalkActions.PhoneChat, StringComparison.Ordinal)));
if (!hasTalkAction)
{
return;
}
if (!catalog.Topics.Values.Any(topic => !topic.Abstract))
{
throw new ContentLoadException(
"A catalog with talk actions requires at least one concrete TopicDef.");
}
}
private static void ValidateConditionBands(BehaviorDef behavior)