Keep defender-victim opinions as allies after a quarrel.

The clash penalty applied to every pair except defender looking at the victim, so the person being helped disliked the defender like a second enemy.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-20 18:15:55 +03:00
co-authored by Cursor
parent 15017ad9a6
commit 9972b30257
4 changed files with 83 additions and 5 deletions
+10
View File
@@ -52,6 +52,16 @@ public class ConflictTests
Assert.True(restored <= baseline);
}
[Fact]
public void QuarrelOpinion_IsStrongerThanRudeTalk()
{
var (catalog, _) = Fixtures.Vanilla();
var quarrel = -Conflict.QuarrelOpinionDelta(catalog.BehaviorRules);
var rude = Math.Abs(catalog.Topics["TopicRude"].OpinionShift);
Assert.True(quarrel > rude);
Assert.True(-Conflict.FightOpinionDelta(catalog.BehaviorRules) > quarrel);
}
[Fact]
public void Catalog_HasNoHealthNeed()
{
@@ -122,6 +122,30 @@ public class AffinityTests
Assert.True((OpinionStore.Get(pupil, teacher.Id) ?? 0) < before);
}
[Fact]
public void RosterJson_RoundTripsCrushesAndPartner()
{
var catalog = Fixtures.RomanceCatalog();
var left = Staff("s1", female: false, "Bisexual");
var right = Staff("s2", female: true, "Bisexual");
SetOpinion(left, right.Id, 80);
SetOpinion(right, left.Id, 80);
var roster = new Roster([left, right], [], []);
Affinity.Refresh(catalog, roster, AsOf);
Assert.Equal(right.Id, left.Bonds!.PartnerId);
var json = RosterJson.Serialize(RosterDocument.From(1, roster));
Assert.Contains("\"partnerId\"", json, StringComparison.Ordinal);
Assert.Contains("\"crushes\"", json, StringComparison.Ordinal);
var loaded = RosterJson.Parse(json).ToRoster();
var loadedLeft = loaded.People.First(person => person.Id.Equals(left.Id, StringComparison.Ordinal));
var loadedRight = loaded.People.First(person => person.Id.Equals(right.Id, StringComparison.Ordinal));
Assert.Equal(right.Id, loadedLeft.Bonds?.PartnerId);
Assert.Equal(left.Id, loadedRight.Bonds?.PartnerId);
Assert.Contains(right.Id, loadedLeft.Bonds!.Crushes);
Assert.Contains(left.Id, loadedRight.Bonds!.Crushes);
}
[Fact]
public void AdjacentYearStudents_CanHaveSympathy()
{
@@ -230,6 +230,34 @@ public class QuarrelFightTests
}
}
[Fact]
public void Defender_AndVictim_OpinionsMoveAsAllies()
{
var (school, pupils) = ThreePupilsOnBreak();
using (school)
{
var bully = pupils[0];
var victim = pupils[1];
var defender = pupils[2];
OpinionStore.Set(bully, victim.Id, -55);
OpinionStore.Set(victim, bully.Id, -55);
OpinionStore.Set(defender, victim.Id, 70);
PlaceAt(school, bully.Id, "corridor-1");
PlaceAt(school, victim.Id, "corridor-1");
PlaceAt(school, defender.Id, "corridor-1");
Assert.True(school.TryStartAction(bully.Id, TalkActions.Quarrel));
TickWhile(school, personId => ActivityOf(school, personId) == TalkActions.Quarrel, bully.Id, victim.Id, defender.Id);
Assert.True((OpinionStore.Get(defender, victim.Id) ?? 0) > 70);
Assert.True((OpinionStore.Get(victim, defender.Id) ?? 0) > 0);
Assert.True((OpinionStore.Get(defender, bully.Id) ?? 0) < 0);
Assert.True((OpinionStore.Get(victim, bully.Id) ?? 0) < -55);
Assert.True((OpinionStore.Get(bully, victim.Id) ?? 0) < -55);
Assert.True((OpinionStore.Get(bully, defender.Id) ?? 0) < 0);
}
}
private static (School School, Person First, Person Second) TwoPupilsOnBreak()
{
var (catalog, map) = Vanilla();