Review slice 9 C-D: persist clash opinions and the bully's victim.

FinishConflict never set RosterTalkDirty, so people.json skipped quarrel shifts until some other save — the same seam as the A-B talk-circle fix. First BullyVictimId write stayed in RAM only.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 14:27:49 +03:00
co-authored by Cursor
parent 1fd8ade69c
commit 19554d4362
6 changed files with 114 additions and 0 deletions
@@ -50,6 +50,51 @@ public class QuarrelFightTests
}
}
[Fact]
public void QuarrelFinish_MarksTickPeopleChangedSoSaveWritesOpinions()
{
var (school, first, second) = TwoPupilsOnBreak();
using (school)
{
const int baseline = -55;
OpinionStore.Set(first, second.Id, baseline);
OpinionStore.Set(second, first.Id, baseline);
PlaceAt(school, first.Id, "yard");
PlaceAt(school, second.Id, "yard");
Assert.True(school.TryStartAction(first.Id, TalkActions.QuarrelYard));
var peopleChanged = false;
for (var i = 0; i < 12 && ActivityOf(school, first.Id) == TalkActions.QuarrelYard; i++)
{
peopleChanged |= school.Tick(0.2d, 5d);
}
Assert.True(peopleChanged);
Assert.True((OpinionStore.Get(first, second.Id) ?? 0) < baseline);
}
}
[Fact]
public void BullyPickingAVictim_DirtiesRosterSoSaveKeepsTheId()
{
var (school, first, second) = TwoPupilsOnBreak();
using (school)
{
GiveTrait(school, first.Id, "Bully");
var bully = school.Roster!.People.First(person => person.Id == first.Id);
var other = school.Roster.People.First(person => person.Id == second.Id);
OpinionStore.Set(bully, other.Id, -55);
OpinionStore.Set(other, bully.Id, -55);
PlaceAt(school, bully.Id, "yard");
PlaceAt(school, other.Id, "yard");
school.RosterTalkDirty = false;
Assert.True(school.TryStartAction(bully.Id, TalkActions.QuarrelYard));
Assert.NotNull(bully.BullyVictimId);
Assert.True(school.RosterTalkDirty);
}
}
[Fact]
public void Apology_RaisesOpinion_ButNotAbovePreQuarrel()
{