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
+71
View File
@@ -162,6 +162,14 @@ public sealed class TraitSkillModifier
public int Offset { get; init; }
}
/// <summary>Biases topic pick toward a tag. Used on gossip and similar traits.</summary>
public sealed class TopicTagWeight
{
public required string Tag { get; init; }
public float Weight { get; init; } = 1f;
}
public sealed class TraitDef : Def
{
public int Weight { get; init; } = 1;
@@ -190,6 +198,39 @@ public sealed class TraitDef : Def
/// cold-loving is negative.
/// </summary>
public float ComfortTemperatureOffset { get; init; }
/// <summary>How often this person initiates talk. Quiet below 1, leader above.</summary>
public float TalkInitiative { get; init; } = 1f;
/// <summary>Extra seats preferred in a circle. Social trait raises it.</summary>
public int TalkCircleBonus { get; init; }
/// <summary>Multiplies opinion shift from talk. Gossip above 1.</summary>
public float TalkOpinionMultiplier { get; init; } = 1f;
/// <summary>Pull toward topics carrying these tags when this person picks the subject.</summary>
public IReadOnlyList<TopicTagWeight> TalkTagWeights { get; init; } = [];
}
/// <summary>Conversation subject. Tags gate appropriateness; language is optional skill help.</summary>
public sealed class TopicDef : Def
{
/// <summary>Vanilla tags: study, games, food, family, sport, gossip, rude, appearance.</summary>
public IReadOnlyList<string> Tags { get; init; } = [];
public IntRange? Age { get; init; }
/// <summary>Empty means every role.</summary>
public IReadOnlyList<string> Roles { get; init; } = [];
/// <summary>Allowed as whisper on lesson — phase 43 uses this; ordinary Chat does not.</summary>
public bool WhisperOnLesson { get; init; }
/// <summary>Skill that helps when shared above threshold. Null — any language works equally.</summary>
public string? Language { get; init; }
/// <summary>Base opinion shift toward each other participant when talk succeeds.</summary>
public int OpinionShift { get; init; } = 2;
}
public sealed class StaffingDef : Def
@@ -329,6 +370,36 @@ public sealed class BehaviorDef : Def
/// </summary>
public IReadOnlyList<OpinionBand> OpinionBands { get; init; } = DefaultOpinionBands;
/// <summary>Skill points Communication gains per game hour of talk — less than a lesson.</summary>
public float TalkSkillPerHour { get; init; } = 0.02f;
/// <summary>Language used in talk, when not already at skill max.</summary>
public float TalkLanguageSkillPerHour { get; init; } = 0.005f;
/// <summary>Minimum shared language skill for full opinion shift.</summary>
public int TalkLanguageThreshold { get; init; } = 25;
/// <summary>Opinion multiplier when participants share no language above threshold.</summary>
public float TalkNoLanguageOpinionMultiplier { get; init; } = 0.15f;
/// <summary>Opinion multiplier for phone chat vs live circle.</summary>
public float TalkPhoneOpinionMultiplier { get; init; } = 0.5f;
/// <summary>How much Communication scales opinion shift (per 100 points).</summary>
public float TalkCommunicationOpinionScale { get; init; } = 0.5f;
/// <summary>Minimum circle size. Below this Chat does not start.</summary>
public int TalkCircleMin { get; init; } = 2;
/// <summary>Maximum people in one live circle.</summary>
public int TalkCircleMax { get; init; } = 4;
/// <summary>Opinion at or above — friend for invites and node pull.</summary>
public int TalkFriendThreshold { get; init; } = 40;
/// <summary>Opinion at or below — enemy; never invited, lunch nodes avoided.</summary>
public int TalkEnemyThreshold { get; init; } = -40;
public static IReadOnlyList<OpinionBand> DefaultOpinionBands { get; } =
[
new() { Min = 70, Id = "OpinionCloseFriend" },