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:
@@ -1056,11 +1056,7 @@ internal static class TalkCircleSystem
|
|||||||
OpinionStore.Get(person, other.Id) ?? 0);
|
OpinionStore.Get(person, other.Id) ?? 0);
|
||||||
var now = OpinionStore.Get(person, other.Id) ?? 0;
|
var now = OpinionStore.Get(person, other.Id) ?? 0;
|
||||||
var shift = delta;
|
var shift = delta;
|
||||||
if (circle.VictimId is not null
|
if (AllyOfVictim(circle, person.Id, rules) && AllyOfVictim(circle, other.Id, rules))
|
||||||
&& other.Id.Equals(circle.VictimId, StringComparison.Ordinal)
|
|
||||||
&& circle.OpinionBaseline.TryGetValue(PairKey(person.Id, circle.VictimId), out var viewOfVictim)
|
|
||||||
&& Conflict.ShouldDefend(viewOfVictim, rules)
|
|
||||||
&& !person.Id.Equals(circle.VictimId, StringComparison.Ordinal))
|
|
||||||
{
|
{
|
||||||
shift = Math.Max(2, -delta / 4);
|
shift = Math.Max(2, -delta / 4);
|
||||||
}
|
}
|
||||||
@@ -1076,6 +1072,26 @@ internal static class TalkCircleSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Victim and anyone who joined on their side. Clash penalty is for opposite sides; allies
|
||||||
|
/// get a small plus instead — otherwise the victim's view of the defender falls like a fight.
|
||||||
|
/// </summary>
|
||||||
|
private static bool AllyOfVictim(ActiveTalkCircle circle, string personId, BehaviorDef? rules)
|
||||||
|
{
|
||||||
|
if (circle.VictimId is null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (personId.Equals(circle.VictimId, StringComparison.Ordinal))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return circle.OpinionBaseline.TryGetValue(PairKey(personId, circle.VictimId), out var viewOfVictim)
|
||||||
|
&& Conflict.ShouldDefend(viewOfVictim, rules);
|
||||||
|
}
|
||||||
|
|
||||||
private static void ApplyApology(School school, IReadOnlyList<Person> people, BehaviorDef? rules)
|
private static void ApplyApology(School school, IReadOnlyList<Person> people, BehaviorDef? rules)
|
||||||
{
|
{
|
||||||
foreach (var person in people)
|
foreach (var person in people)
|
||||||
|
|||||||
@@ -52,6 +52,16 @@ public class ConflictTests
|
|||||||
Assert.True(restored <= baseline);
|
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]
|
[Fact]
|
||||||
public void Catalog_HasNoHealthNeed()
|
public void Catalog_HasNoHealthNeed()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -122,6 +122,30 @@ public class AffinityTests
|
|||||||
Assert.True((OpinionStore.Get(pupil, teacher.Id) ?? 0) < before);
|
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]
|
[Fact]
|
||||||
public void AdjacentYearStudents_CanHaveSympathy()
|
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()
|
private static (School School, Person First, Person Second) TwoPupilsOnBreak()
|
||||||
{
|
{
|
||||||
var (catalog, map) = Vanilla();
|
var (catalog, map) = Vanilla();
|
||||||
|
|||||||
Reference in New Issue
Block a user