using System.Collections.Generic; using System.Linq; namespace Luneraria.Core.Tests { /// /// H2: Brunhild, the Valkyrie. Purchase and rules, hunter passive, spear with mark, the leap /// with area damage and stun (bosses immune), talents, skin, bot profile; reference untouched. /// public sealed class H2HeroSuite : CheckSuite { public override string Name { get { return "h2"; } } protected override void RunChecks() { TestPurchase(); TestSpear(); TestUltimate(); TestPassiveAndTalents(); TestBots(); } static LuneCombat Arena(HeroState hero, out LuneUnit brunhild, params KeyValuePair[] enemies) { var balance = LuneBalance.Default(); var combat = new LuneCombat(new LuneMap(balance), balance, 5); UnitDef definition = LuneContent.HeroDefinition(hero); definition.Role = "melee"; brunhild = combat.Spawn("hero", 0, "mid", definition, 40.0, 0.0, 0, "h0-1"); Vec2 forward = combat.Map.LaneForward(0, "mid", 40.0); foreach (KeyValuePair enemy in enemies) { bool isHero = enemy.Key == "held"; UnitDef def = isHero ? LuneContent.HeroDefinition(new HeroState { HeroId = "held_ringborke" }) : ContentData.Enemies[enemy.Key].Clone(); def.MaxHp = 50000.0; def.Speed = 0.0; LuneUnit unit = combat.Spawn(isHero ? "hero" : "monster", 1, "mid", def, 40.0, 0.0, isHero ? 3 : -1, isHero ? "h3-1" : ""); unit.Position = brunhild.Position + forward * enemy.Value; unit.PreviousPosition = unit.Position; } return combat; } static KeyValuePair At(string id, double distance) { return new KeyValuePair(id, distance); } static void Run(LuneCombat combat, double seconds) { int steps = (int)(seconds / combat.Balance.SimStep + 0.5); for (int i = 0; i < steps && !combat.Finished; i++) combat.Step(); } void TestPurchase() { var state = new LuneMatch(); state.Setup(new MatchOptions { Seed = 71, HumanSlot = -1 }); state.Players[0].Draft.Gold += 1000; Check(state.Submit(0, Command.HeroBuy("held_brunhild")).Ok, "Brunhild can be recruited for 260"); Check(LuneContent.UltimateRuleFor(state.Players[0].Draft.Hero) == "gruppe", "default rule Gruppe"); Check(state.Submit(0, Command.UltimateRule("held")).Ok && state.Submit(0, Command.HeroSkin("eiswalkuere")).Ok, "rule Heldenjagd and skin Eiswalküre"); Check(!state.Submit(0, Command.HeroSkin("blutmond")).Ok, "Sylvara's skin is not hers"); UnitDef definition = LuneContent.HeroDefinition(state.Players[0].Draft.Hero); Check(definition.Id == "brunhild" && definition.Skin == "eiswalkuere" && definition.UltimateRule == "held" && GdMath.IsEqualApprox(definition.AttackRange, 2.0), "definition carries model, skin, rule and the spear's reach"); } void TestSpear() { LuneUnit brunhild; LuneCombat combat = Arena(new HeroState { HeroId = "held_brunhild", Ultimate = "notfall" }, out brunhild, At("e01", 1.5), At("e01", 4.0), At("e04", 6.5)); LuneUnit far = combat.Units.First(u => u.Def.Id == "e04"); Run(combat, LuneCombat.BrunhildFirstSpear - 0.1); Check(brunhild.AbilitySerial == 0, "no spear before 3 s"); Run(combat, 0.2); Check(brunhild.AbilitySerial == 1 && far.Effects.ContainsKey("valkyrie_mark"), "the first spear marks the farthest enemy in 7 m"); double raw = 100.0; var probe = new LuneUnit { Def = far.Def }; Check(GdMath.IsEqualApprox(far.Mitigated(raw, 0), probe.Mitigated(raw, 0) * 1.15), "a marked enemy takes 15 % more damage"); } void TestUltimate() { LuneUnit brunhild; LuneCombat combat = Arena(new HeroState { HeroId = "held_brunhild", Ultimate = "held" }, out brunhild, At("e01", 1.5), At("held", 6.0), At("e01", 6.8), At("e07", 6.5)); LuneUnit hero = combat.Units.First(u => u.Kind == "hero" && u.Team == 1); LuneUnit boss = combat.Units.First(u => u.Def.Id == "e07"); Run(combat, LuneContent.UltimateDelay - 0.1); Check(brunhild.UltimateSerial == 0, "no ultimate before 2 s"); Run(combat, 0.1); Check(brunhild.UltimateSerial == 1, "Heldenjagd fires with an enemy hero in 7 m"); Check(brunhild.Position.DistanceTo(hero.Position) < 1.5, "she leaps next to the hero"); Check(hero.Stunned() && !boss.Stunned(), "the hero is stunned, the boss is immune"); Check(hero.AttackSpeedFactor() == 0.0 && hero.MovementFactor() == 0.0, "a stunned unit neither attacks nor moves"); Run(combat, 1.1); Check(!hero.Stunned(), "the stun ends after 1 s"); Run(combat, 8.0); Check(brunhild.UltimateSerial == 1, "once per combat"); LuneUnit calm; LuneCombat group = Arena(new HeroState { HeroId = "held_brunhild" }, out calm, At("e01", 3.0), At("e01", 3.5)); Run(group, 4.0); Check(calm.UltimateSerial == 0, "Gruppe waits for three enemies at the target"); } void TestPassiveAndTalents() { LuneUnit brunhild; LuneCombat combat = Arena(new HeroState { HeroId = "held_brunhild", Ultimate = "notfall" }, out brunhild, At("held", 1.5)); LuneUnit hero = combat.Units.First(u => u.Kind == "hero" && u.Team == 1); double before = hero.Hp; int guard = 0; while (brunhild.HitCount == 0 && guard++ < 200) combat.Step(); double expected = hero.Mitigated(brunhild.Def.Damage * (1.0 + brunhild.DamageBonus()) * LuneCombat.BrunhildHunter, brunhild.Def.AttackType); Check(GdMath.IsEqualApprox(before - hero.Hp, expected), "Heldenjägerin: +25 % against heroes"); UnitDef raven = LuneContent.HeroDefinition(new HeroState { HeroId = "held_brunhild", Xp = 1000, Talents = new List { "speerregen", "rabenschwinge" } }); Check(GdMath.IsEqualApprox(raven.AttackSpeedBonus, 0.20), "Rabenschwinge: +20 % attack tempo"); LuneUnit rain; LuneCombat spears = Arena(new HeroState { HeroId = "held_brunhild", Ultimate = "notfall", Talents = new List { "speerregen", "goetterdaemmerung" } }, out rain, At("e01", 4.0), At("e01", 5.0), At("e01", 6.0), At("e01", 1.0)); Run(spears, 3.1); Check(spears.Units.Count(u => u.Effects.ContainsKey("valkyrie_mark")) == 3, "Speerregen: three marked enemies"); LuneUnit dusk; LuneCombat stun = Arena(new HeroState { HeroId = "held_brunhild", Ultimate = "sofort", Talents = new List { "speerregen", "goetterdaemmerung" } }, out dusk, At("e01", 2.0)); Run(stun, 2.05); LuneUnit victim = stun.Units.First(u => u.Kind == "monster"); Run(stun, 1.5); Check(victim.Stunned(), "Götterdämmerung: the stun lasts 2 s"); } void TestBots() { bool picked = false; for (int seed = 1; seed <= 16 && !picked; seed++) { var probe = new LuneMatch(); probe.Setup(new MatchOptions { Seed = seed, HumanSlot = -1, Extended = true }); for (int slot = 0; slot < 6; slot++) picked |= LuneBot.ProfileFor(probe, slot) == "walkuere"; } Check(picked, "extended bots pick Brunhild's profile"); var reference = new LuneMatch(); reference.Setup(new MatchOptions { Seed = 3, HumanSlot = -1 }); bool clean = true; for (int slot = 0; slot < 6; slot++) clean &= !ContentData.ExtraBotProfiles.ContainsKey(LuneBot.ProfileFor(reference, slot)); Check(clean, "reference rotation untouched"); } } }