79 lines
3.1 KiB
C#
79 lines
3.1 KiB
C#
|
|
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<BrigandBehavior>(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);
|
||
|
|
}
|
||
|
|
}
|