using Theriapolis.Core.Data; using Theriapolis.Core.Entities.Ai; using Theriapolis.Core.Rules.Character; using Theriapolis.Core.Rules.Combat; using Theriapolis.Core.Util; using Xunit; namespace Theriapolis.Tests.Combat; public sealed class BehaviorTests { private readonly ContentResolver _content = new(new ContentLoader(TestHelpers.DataDirectory)); [Fact] public void Brigand_MovesTowardTargetWhenOutOfReach() { var brigand = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile); var hero = MakeNpc("brigand_footpad", new Vec2(5, 0), Allegiance.Player); var enc = new Encounter(0xCAFEUL, 1, new[] { brigand, hero }); // Skip past initiative and start brigand's turn explicitly. while (enc.CurrentActor.Id != brigand.Id) enc.EndTurn(); new BrigandBehavior().TakeTurn(brigand, new AiContext(enc)); Assert.True((int)brigand.Position.X > 0); // moved toward hero } [Fact] public void WildAnimal_FleesBelowQuarterHp() { var wolf = MakeNpc("wolf", new Vec2(5, 0), Allegiance.Hostile); var hero = MakeNpc("wolf", new Vec2(0, 0), Allegiance.Player); wolf.CurrentHp = 1; // well below 25% of 11 var enc = new Encounter(0xCAFEUL, 1, new[] { wolf, hero }); while (enc.CurrentActor.Id != wolf.Id) enc.EndTurn(); var startX = (int)wolf.Position.X; new WildAnimalBehavior().TakeTurn(wolf, new AiContext(enc)); Assert.True((int)wolf.Position.X > startX, "Wounded wolf should flee away from hero (positive X)"); } [Fact] public void Behavior_NoTargetMakesNoMoves() { var lone = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile); var enc = new Encounter(0xCAFEUL, 1, new[] { lone }); var startPos = lone.Position; new BrigandBehavior().TakeTurn(lone, new AiContext(enc)); Assert.Equal(startPos.X, lone.Position.X); Assert.Equal(startPos.Y, lone.Position.Y); } [Fact] public void Behavior_AttacksWhenInReach() { var brigand = MakeNpc("brigand_footpad", new Vec2(0, 0), Allegiance.Hostile); var hero = MakeNpc("brigand_footpad", new Vec2(1, 0), Allegiance.Player); var enc = new Encounter(0xCAFEUL, 1, new[] { brigand, hero }); while (enc.CurrentActor.Id != brigand.Id) enc.EndTurn(); int logBefore = enc.Log.Count; new BrigandBehavior().TakeTurn(brigand, new AiContext(enc)); Assert.Contains(enc.Log.Skip(logBefore), e => e.Type == CombatLogEntry.Kind.Attack); } [Fact] public void Registry_UnknownIdFallsBackToBrigand() { var b = BehaviorRegistry.For("nonsense_behavior"); Assert.IsType(b); } private Combatant MakeNpc(string templateId, Vec2 pos, Allegiance side) { var t = _content.Npcs.Templates.First(x => x.Id == templateId); var swapped = t with { DefaultAllegiance = side.ToString().ToLowerInvariant() }; return Combatant.FromNpcTemplate(swapped, id: pos.X.GetHashCode() ^ pos.Y.GetHashCode(), pos); } }