using Theriapolis.Core.Data; using Theriapolis.Core.Rules.Combat; using Theriapolis.Core.Rules.Stats; using Theriapolis.Core.Util; using Xunit; namespace Theriapolis.Tests.Combat; public sealed class InitiativeTests { private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory)); [Fact] public void NewEncounter_AppendsInitiativeLogEntry() { var combatants = MakeThree(); var enc = new Encounter(0xCAFEUL, 1, combatants); Assert.Contains(enc.Log, l => l.Type == CombatLogEntry.Kind.Initiative); } [Fact] public void InitiativeOrder_ContainsEveryCombatantExactlyOnce() { var combatants = MakeThree(); var enc = new Encounter(0xCAFEUL, 1, combatants); Assert.Equal(combatants.Count, enc.InitiativeOrder.Count); Assert.Equal(combatants.Count, enc.InitiativeOrder.Distinct().Count()); foreach (int idx in enc.InitiativeOrder) Assert.InRange(idx, 0, combatants.Count - 1); } [Fact] public void EndTurn_AdvancesToNextLivingCombatant() { var combatants = MakeThree(); var enc = new Encounter(0xCAFEUL, 1, combatants); var first = enc.CurrentActor; enc.EndTurn(); Assert.NotEqual(first.Id, enc.CurrentActor.Id); } [Fact] public void EndTurn_WrappingIncrementsRoundCounter() { var combatants = MakeThree(); var enc = new Encounter(0xCAFEUL, 1, combatants); Assert.Equal(1, enc.RoundNumber); // Advance N turns to wrap once. for (int i = 0; i < combatants.Count; i++) enc.EndTurn(); Assert.Equal(2, enc.RoundNumber); } [Fact] public void CheckForVictory_EndsWhenOnlyOneSideRemains() { var brigand = _content.Npcs.Templates.First(t => t.Id == "brigand_footpad"); var wolf = _content.Npcs.Templates.First(t => t.Id == "wolf"); var hero = Combatant.FromNpcTemplate(brigand, id: 1, new Vec2(0, 0)); var foe = Combatant.FromNpcTemplate(wolf, id: 2, new Vec2(1, 0)); // Force opposite allegiances. Brigand defaults Hostile; rebuild "hero" as Player-side. hero = Combatant.FromNpcTemplate(brigand with { DefaultAllegiance = "player" }, id: 1, new Vec2(0, 0)); var enc = new Encounter(1UL, 1, new[] { hero, foe }); // Knock out the foe. Resolver.ApplyDamage(foe, foe.MaxHp); Assert.True(enc.CheckForVictory()); Assert.True(enc.IsOver); Assert.Contains(enc.Log, l => l.Type == CombatLogEntry.Kind.EncounterEnd); } private List MakeThree() { var brigand = _content.Npcs.Templates.First(t => t.Id == "brigand_footpad"); var wolf = _content.Npcs.Templates.First(t => t.Id == "wolf"); var captain = _content.Npcs.Templates.First(t => t.Id == "brigand_captain"); // Mix allegiances so CheckForVictory doesn't end the encounter immediately. return new List { Combatant.FromNpcTemplate(brigand with { DefaultAllegiance = "player" }, id: 1, new Vec2(0, 0)), Combatant.FromNpcTemplate(wolf, id: 2, new Vec2(2, 0)), Combatant.FromNpcTemplate(captain, id: 3, new Vec2(4, 0)), }; } }