From 4a0030dd86b2f38dc2ae6d95d3902569f56a0375 Mon Sep 17 00:00:00 2001 From: Quinn Bast Date: Sun, 3 Sep 2023 14:02:45 -0600 Subject: [PATCH] Add tests for various combat scenarios --- .../Entities/Components/PositionManager.cs | 8 + .../Entities/Components/SpecialistManager.cs | 4 +- .../Core/Entities/EntityData.cs | 19 + .../Core/Entities/Specialists/Heroes/Queen.cs | 8 +- .../Core/Entities/Specialists/Specialist.cs | 16 - .../Entities/Specialists/SpecialistFactory.cs | 4 + .../Specialists/Specialists/Infiltrator.cs | 4 +- .../Specialists/Specialists/Martyr.cs | 4 +- .../Entities/Specialists/Specialists/Theif.cs | 4 +- .../Specialists/Specialists/Veteran.cs | 4 +- .../CombatResolve/FriendlyCombatResolution.cs | 2 +- .../CombatOwnershipTransferEffect.cs | 8 +- .../EntityExplodeEffect.cs | 35 +- .../Core/Combat/CombatTest.cs | 347 ++++++++++++++++++ .../Core/Components/SpecialistManagerTest.cs | 6 +- 15 files changed, 421 insertions(+), 52 deletions(-) create mode 100644 Core/SubterfugeCore/Core/Entities/EntityData.cs create mode 100644 Core/SubterfugeCoreTest/Core/Combat/CombatTest.cs diff --git a/Core/SubterfugeCore/Core/Entities/Components/PositionManager.cs b/Core/SubterfugeCore/Core/Entities/Components/PositionManager.cs index 2b24e0a..25554b1 100644 --- a/Core/SubterfugeCore/Core/Entities/Components/PositionManager.cs +++ b/Core/SubterfugeCore/Core/Entities/Components/PositionManager.cs @@ -115,6 +115,14 @@ private void CheckCombat(TimeMachine timeMachine, OnTickEventArgs onTick) CombatEvent = combat, }); + // Also include any defensive combat events + entityToFight.GetComponent().OnRegisterCombatEffects?.Invoke(this, new OnRegisterCombatEventArgs() + { + Direction = onTick.Direction, + CurrentState = timeMachine.GetState(), + CombatEvent = combat, + }); + timeMachine.AddEvent(combat); localCombatEvents.Add(combat); } diff --git a/Core/SubterfugeCore/Core/Entities/Components/SpecialistManager.cs b/Core/SubterfugeCore/Core/Entities/Components/SpecialistManager.cs index 085dbaa..d9324be 100644 --- a/Core/SubterfugeCore/Core/Entities/Components/SpecialistManager.cs +++ b/Core/SubterfugeCore/Core/Entities/Components/SpecialistManager.cs @@ -194,10 +194,10 @@ public bool TransferSpecialistsTo(SpecialistManager specialistManager, TimeMachi /// List of specialist Ids to transfer public bool TransferSpecialistsById(SpecialistManager destinationSpecialistManager, List specialistIds, TimeMachine timeMachine) { - var specialistsMatchingId = _specialists.Where(it => specialistIds.Contains(it.GetId())).ToList(); + var specialistsMatchingId = _specialists.Where(it => specialistIds.Contains(it.GetSpecialistId().ToString())).ToList(); if (destinationSpecialistManager.CanAddSpecialists(specialistsMatchingId.Count)) { - _specialists = _specialists.Where(it => !specialistIds.Contains(it.GetId())).ToList(); + _specialists = _specialists.Where(it => !specialistIds.Contains(it.GetSpecialistId().ToString())).ToList(); destinationSpecialistManager._specialists.AddRange(specialistsMatchingId); specialistsMatchingId.ForEach(spec => diff --git a/Core/SubterfugeCore/Core/Entities/EntityData.cs b/Core/SubterfugeCore/Core/Entities/EntityData.cs new file mode 100644 index 0000000..6843a62 --- /dev/null +++ b/Core/SubterfugeCore/Core/Entities/EntityData.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Subterfuge.Remake.Api.Network; +using Subterfuge.Remake.Core.Players; + +namespace Subterfuge.Remake.Core.Entities +{ + public class EntityData + { + public int DrillerCount { get; set; } = 0; + public int ShieldCount { get; set; } = 0; + public Player Owner { get; set; } = null; + public List Specialists + { + get; + set; + } = null; + + } +} \ No newline at end of file diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Heroes/Queen.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Heroes/Queen.cs index b61f526..d452994 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Heroes/Queen.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Heroes/Queen.cs @@ -7,7 +7,7 @@ namespace Subterfuge.Remake.Core.Entities.Specialists.Heroes { public class Queen: Specialist { - private int shieldDelta = 25; + public static int SHIELD_PROVIDED = 25; public Queen(Player owner) : base(owner, true) { @@ -18,7 +18,7 @@ public override void ArriveAtLocation(IEntity entity, TimeMachine timeMachine) if (!this._isCaptured) { entity.GetComponent().AllowHireFromLocation(); - entity.GetComponent().AlterShieldCapacity(shieldDelta); + entity.GetComponent().AlterShieldCapacity(SHIELD_PROVIDED); } } @@ -28,7 +28,7 @@ public override void LeaveLocation(IEntity entity, TimeMachine timeMachine) if (!this._isCaptured) { entity.GetComponent().DisallowHireFromLocation(); - entity.GetComponent().AlterShieldCapacity(shieldDelta * -1); + entity.GetComponent().AlterShieldCapacity(SHIELD_PROVIDED * -1); } } @@ -47,7 +47,7 @@ public override SpecialistTypeId GetSpecialistId() public override string GetDescription() { - return $"Adds ${shieldDelta} shields to the Queen's Location. If the Queen dies, the owner is eliminated."; + return $"Adds ${SHIELD_PROVIDED} shields to the Queen's Location. If the Queen dies, the owner is eliminated."; } } } \ No newline at end of file diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Specialist.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Specialist.cs index 25cf4eb..53a6aa4 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Specialist.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Specialist.cs @@ -13,12 +13,6 @@ namespace Subterfuge.Remake.Core.Entities.Specialists /// public abstract class Specialist { - /// - /// The name of the specialist - /// - /// TODO: Generate the id from a known seed. - private readonly string _specialistId = Guid.NewGuid().ToString(); - /// /// The player who owns the specialist /// @@ -50,16 +44,6 @@ bool isHero IsHero = isHero; } - /// - /// Returns the specialist id. - /// - /// The specialist's id - public string GetId() - { - // TODO: Do something else here. - return GetSpecialistId().ToString(); - } - /// /// Gets the owner of the specialist. /// diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/SpecialistFactory.cs b/Core/SubterfugeCore/Core/Entities/Specialists/SpecialistFactory.cs index 051c614..347a162 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/SpecialistFactory.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/SpecialistFactory.cs @@ -28,6 +28,10 @@ Player owner return new Smuggler(owner); case SpecialistTypeId.Veteran: return new Veteran(owner); + case SpecialistTypeId.Martyr: + return new Martyr(owner); + case SpecialistTypeId.Theif: + return new Theif(owner); default: return null; } diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Infiltrator.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Infiltrator.cs index c9f911d..780c8fa 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Infiltrator.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Infiltrator.cs @@ -51,8 +51,8 @@ private void OnRegisterCombatEffects(object positionManager, OnRegisterCombatEve registerCombatEventArgs.CombatEvent.AddEffectToCombat(new AlterShieldEffect( registerCombatEventArgs.CombatEvent, - friendlyEntity, - GetShieldDelta(enemyEntity) + enemyEntity, + GetShieldDelta(enemyEntity) * -1 )); if (GetLevel() >= 2) diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Martyr.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Martyr.cs index 821c4bd..02c3e21 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Martyr.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Martyr.cs @@ -15,7 +15,7 @@ public Martyr(Player owner) : base(owner, false) public override void ArriveAtLocation(IEntity entity, TimeMachine timeMachine) { - if (_isCaptured) + if (!_isCaptured) { entity.GetComponent().OnRegisterCombatEffects += ExplodeOnCombatEffects; } @@ -23,7 +23,7 @@ public override void ArriveAtLocation(IEntity entity, TimeMachine timeMachine) public override void LeaveLocation(IEntity entity, TimeMachine timeMachine) { - if (_isCaptured) + if (!_isCaptured) { entity.GetComponent().OnRegisterCombatEffects -= ExplodeOnCombatEffects; } diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Theif.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Theif.cs index 8011652..6f8c454 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Theif.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Theif.cs @@ -51,8 +51,8 @@ private void StealEnemyDrillers(object? combat, OnRegisterCombatEventArgs combat combatEventArgs.CombatEvent.AddEffectToCombat(new AlterDrillerEffect( combatEventArgs.CombatEvent, friendlyEntity, - (int)(enemyCarrier.GetDrillerCount() * _stealPerLevel[_level]), - (int)(enemyCarrier.GetDrillerCount() * _stealPerLevel[_level] * -1) + (int)(enemyCarrier.GetDrillerCount() * _stealPerLevel[_level - 1]), + (int)(enemyCarrier.GetDrillerCount() * _stealPerLevel[_level - 1] * -1) )); } diff --git a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Veteran.cs b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Veteran.cs index de9b81d..acbad8c 100644 --- a/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Veteran.cs +++ b/Core/SubterfugeCore/Core/Entities/Specialists/Specialists/Veteran.cs @@ -53,13 +53,13 @@ private void AlterDrilleresOnCombat(object sender, OnRegisterCombatEventArgs eve { var friendlyEntity = eventArgs.CombatEvent.GetEntityOwnedBy(_owner); var friendlyDrillers = friendlyEntity.GetComponent().GetDrillerCount(); - var extraDrillers = (int)(ExtraDrillersPerLevel[_level] * friendlyDrillers); + var extraDrillers = (int)(ExtraDrillersPerLevel[_level - 1] * friendlyDrillers); eventArgs.CombatEvent.AddEffectToCombat(new AlterDrillerEffect( eventArgs.CombatEvent, friendlyEntity, 0, - -1 * (drillersPerLevel[_level] + extraDrillers) + -1 * (drillersPerLevel[_level - 1] + extraDrillers) )); } } diff --git a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/CombatResolve/FriendlyCombatResolution.cs b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/CombatResolve/FriendlyCombatResolution.cs index d98c922..d5dca4f 100644 --- a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/CombatResolve/FriendlyCombatResolution.cs +++ b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/CombatResolve/FriendlyCombatResolution.cs @@ -43,7 +43,7 @@ public override bool BackwardAction(TimeMachine timeMachine) { _outpost.GetComponent().AlterDrillers(_drillersTransferred * -1); _outpost.GetComponent() - .TransferSpecialistsById(_sub.GetComponent(), _specialistsTransferred.Select(it => it.GetId()).ToList(), timeMachine); + .TransferSpecialistsById(_sub.GetComponent(), _specialistsTransferred.Select(it => it.GetSpecialistId().ToString()).ToList(), timeMachine); timeMachine.GetState().AddSub(_sub); return true; } diff --git a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/NaturalCombatEvents/CombatOwnershipTransferEffect.cs b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/NaturalCombatEvents/CombatOwnershipTransferEffect.cs index 97ddc9f..f81f938 100644 --- a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/NaturalCombatEvents/CombatOwnershipTransferEffect.cs +++ b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/NaturalCombatEvents/CombatOwnershipTransferEffect.cs @@ -81,7 +81,7 @@ public override bool BackwardAction(TimeMachine timeMachine) { // Put specialists back on the sub. _outpost.GetComponent() - .TransferSpecialistsById(_sub.GetComponent(), specialistsTransferred.Select(it => it.GetId()).ToList(), timeMachine); + .TransferSpecialistsById(_sub.GetComponent(), specialistsTransferred.Select(it => it.GetSpecialistId().ToString()).ToList(), timeMachine); // Put drillers back on the sub. _sub.GetComponent().AlterDrillers(drillersTransferred); @@ -94,7 +94,7 @@ public override bool BackwardAction(TimeMachine timeMachine) { // Put specialists back on the sub. _outpost.GetComponent() - .TransferSpecialistsById(_sub.GetComponent(), specialistsTransferred.Select(it => it.GetId()).ToList(), timeMachine); + .TransferSpecialistsById(_sub.GetComponent(), specialistsTransferred.Select(it => it.GetSpecialistId().ToString()).ToList(), timeMachine); } } @@ -112,8 +112,8 @@ public override bool WasEventSuccessful() var outpostDrillerCount = _outpost.GetComponent().GetDrillerCount(); if (subDrillerCount <= 0 && outpostDrillerCount <= 0) { - // Attacker wins in the case of a tie - return _sub; + // Defender wins in the case of a tie + return _outpost; } if (subDrillerCount <= 0) diff --git a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/SpecialCombatEvents/EntityExplodeEffect.cs b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/SpecialCombatEvents/EntityExplodeEffect.cs index 6a0c24f..1db1497 100644 --- a/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/SpecialCombatEvents/EntityExplodeEffect.cs +++ b/Core/SubterfugeCore/Core/GameEvents/Combat/CombatEvents/SpecialCombatEvents/EntityExplodeEffect.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; +using System.Linq; using Subterfuge.Remake.Core.Entities; using Subterfuge.Remake.Core.Entities.Components; -using Subterfuge.Remake.Core.Entities.Positions; using Subterfuge.Remake.Core.GameEvents.Base; using Subterfuge.Remake.Core.Timing; @@ -13,8 +13,8 @@ public class EntityExplodeEffect : PositionalGameEvent private IEntity _entityExploding; private GameState _state; - private List entitiesDestroyed; - + private Dictionary EntitiesBeforeExplosion; + public EntityExplodeEffect( GameTick occursAt, IEntity entityExploding, @@ -27,19 +27,26 @@ GameState currentState public override bool ForwardAction(TimeMachine timeMachine) { - entitiesDestroyed = _state.EntitesInRange(Constants.BaseSubVisionRadius, - _entityExploding.GetComponent().CurrentLocation); + EntitiesBeforeExplosion = _state.EntitesInRange(Constants.BaseSubVisionRadius, + _entityExploding.GetComponent().CurrentLocation) + .ToDictionary(entity => entity, entity => new EntityData() + { + DrillerCount = entity.GetComponent().GetDrillerCount(), + ShieldCount = entity.GetComponent().GetShields(), + Specialists = entity.GetComponent().GetSpecialists().ConvertAll(spec => spec.GetSpecialistId()) + }); // Remove all of the entities from the game state. - foreach(IEntity e in entitiesDestroyed) + foreach(KeyValuePair e in EntitiesBeforeExplosion) { - if (e is Sub) + if (e.Key is Sub) { - timeMachine.GetState().RemoveSub(e as Sub); + timeMachine.GetState().RemoveSub(e.Key as Sub); } else { - e.GetComponent().Destroy(); + e.Key.GetComponent().SetDrillerCount(0); + e.Key.GetComponent().Destroy(); } } @@ -48,16 +55,16 @@ public override bool ForwardAction(TimeMachine timeMachine) public override bool BackwardAction(TimeMachine timeMachine) { - foreach(IEntity e in entitiesDestroyed) + foreach(KeyValuePair e in EntitiesBeforeExplosion) { - if (e is Sub) + if (e.Key is Sub) { - timeMachine.GetState().AddSub(e as Sub); + timeMachine.GetState().AddSub(e.Key as Sub); } else { - // TODO: This is not perfect... Need to ensure that we can "redo" a destroy event somehow. - e.GetComponent().UndoDestroy(10); + e.Key.GetComponent().UndoDestroy(e.Value.DrillerCount); + e.Key.GetComponent().SetShields(e.Value.ShieldCount); } } diff --git a/Core/SubterfugeCoreTest/Core/Combat/CombatTest.cs b/Core/SubterfugeCoreTest/Core/Combat/CombatTest.cs new file mode 100644 index 0000000..ca072da --- /dev/null +++ b/Core/SubterfugeCoreTest/Core/Combat/CombatTest.cs @@ -0,0 +1,347 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; +using Subterfuge.Remake.Api.Network; +using Subterfuge.Remake.Core; +using Subterfuge.Remake.Core.Entities.Components; +using Subterfuge.Remake.Core.Entities.Positions; +using Subterfuge.Remake.Core.Entities.Specialists; +using Subterfuge.Remake.Core.Entities.Specialists.Heroes; +using Subterfuge.Remake.Core.GameEvents.PlayerTriggeredEvents; +using Subterfuge.Remake.Core.Players; +using Subterfuge.Remake.Core.Timing; +using Subterfuge.Remake.Core.Topologies; + +namespace Subterfuge.Remake.Test.Core.Combat +{ + [TestClass] + public class CombatTest + { + + private Game game = Game.Bare(); + private Player attackingPlayer; + private Outpost attackingOutpost; + + private Player defendingPlayer; + private Outpost defendingOutpost; + + + [TestInitialize] + public void Setup() + { + game = Game.Bare(); + attackingPlayer = new Player(new SimpleUser() { Id = "1", Username = "Attacker" }); + attackingOutpost = new Generator("1", new RftVector(0, 0), attackingPlayer, game.TimeMachine); + + defendingPlayer = new Player(new SimpleUser() { Id = "2", Username = "Defender" }); + defendingOutpost = new Generator("2", new RftVector(0, 1), defendingPlayer, game.TimeMachine); + game.TimeMachine.GetState().GetOutposts().AddRange(new List() { attackingOutpost, defendingOutpost }); + + Assert.AreEqual(2, game.TimeMachine.GetState().GetOutposts().Count); + Assert.AreEqual(2, game.TimeMachine.GetState().GetPlayers().Count); + } + + [TestMethod] + public void TestFailingDrillerCombat() + { + SetupDefence(50, 0, new List()); + Attack(30, new List()); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + } + + [TestMethod] + public void TestSuccessfulDrillerCombat() + { + SetupDefence(50, 0, new List()); + Attack(60, new List()); + + AssertOutpostOwnedBy(defendingOutpost, attackingPlayer); + AssertOutpostDrillers(defendingOutpost, 10); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 60); + } + + [TestMethod] + public void TestAttackFailsIfTie() + { + SetupDefence(50, 0, new List()); + Attack(50, new List()); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 0); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 50); + } + + [TestMethod] + public void TestDrillersAttackingLargerShields() + { + SetupDefence(20, 50, new List()); + Attack(20, new List()); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 30); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 20); + } + + [TestMethod] + public void TestAttackDrainingAllShieldsButNotDrillers() + { + SetupDefence(20, 50, new List()); + Attack(50, new List()); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 0); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 50); + } + + [TestMethod] + public void TestSuccessfulAttackAgainstShields() + { + SetupDefence(20, 50, new List()); + Attack(71, new List()); + + AssertOutpostOwnedBy(defendingOutpost, attackingPlayer); + AssertOutpostDrillers(defendingOutpost, 1); + AssertOutpostShields(defendingOutpost, 0); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 71); + } + + [TestMethod] + public void TestAttackingWithInfiltrator() + { + SetupDefence(20, 50, new List()); + Attack(10, new List() { SpecialistTypeId.Infiltrator }); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 50 - 10 - 15); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 20); + AssertOutpostShields(defendingOutpost, 50); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 10); + } + + [TestMethod] + public void CanBustShieldsWithInfiltrator() + { + SetupDefence(0, 15, new List()); + Attack(10, new List() { SpecialistTypeId.Infiltrator }); + + AssertOutpostOwnedBy(defendingOutpost, attackingPlayer); + AssertOutpostDrillers(defendingOutpost, 10); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 0); + AssertOutpostShields(defendingOutpost, 15); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 10); + } + + [TestMethod] + public void MartyrExplodesOnCombat() + { + SetupDefence(900, 15, new List() { SpecialistTypeId.Martyr }); + Attack(10, new List()); + + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 0); + Assert.AreEqual(defendingOutpost.GetComponent().IsDestroyed(), true); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + Assert.AreEqual(attackingOutpost.GetComponent().IsDestroyed(), true); + + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 900); + AssertOutpostShields(defendingOutpost, 15); + Assert.AreEqual(defendingOutpost.GetComponent().IsDestroyed(), false); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 10); + Assert.AreEqual(attackingOutpost.GetComponent().IsDestroyed(), false); + } + + [TestMethod] + public void TheifStealsDrillersOnCombat() + { + SetupDefence(100, 0, new List()); + Attack(100, new List() { SpecialistTypeId.Theif }); + + AssertOutpostOwnedBy(defendingOutpost, attackingPlayer); + // Steal 10 so enemy loses 10, we gain 10 = 90 vs. 110 + AssertOutpostDrillers(defendingOutpost, 20); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 100); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 100); + } + + [TestMethod] + public void VeteranDestroysDrillersOnCombat() + { + SetupDefence(100, 0, new List()); + Attack(100, new List() { SpecialistTypeId.Veteran }); + + AssertOutpostOwnedBy(defendingOutpost, attackingPlayer); + AssertOutpostDrillers(defendingOutpost, 10); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 0); + + game.TimeMachine.GoTo(new GameTick(0)); + AssertOutpostOwnedBy(defendingOutpost, defendingPlayer); + AssertOutpostDrillers(defendingOutpost, 100); + + AssertOutpostOwnedBy(attackingOutpost, attackingPlayer); + AssertOutpostDrillers(attackingOutpost, 100); + } + + private void SetupDefence( + int drillerCount = 10, + int shieldCount = 0, + List? defendingSpecialists = null + ) + { + defendingOutpost.GetComponent().SetShieldCapacity(shieldCount); + defendingOutpost.GetComponent().SetShields(shieldCount); + defendingOutpost.GetComponent().SetDrillerCount(drillerCount); + + defendingOutpost.GetComponent().AllowHireFromLocation(); + defendingSpecialists?.ForEach(spec => + { + defendingOutpost.GetComponent().HireSpecialist( + SpecialistFactory.CreateSpecialist(spec, 1, defendingPlayer), game.TimeMachine); + }); + } + + private void Attack( + int drillerCount = 10, + List attackingSpecialists = null + ) + { + attackingOutpost.GetComponent().SetDrillerCount(drillerCount); + + attackingOutpost.GetComponent().AllowHireFromLocation(); + attackingSpecialists?.ForEach(spec => + { + attackingOutpost.GetComponent().HireSpecialist( + SpecialistFactory.CreateSpecialist(spec, 1, attackingPlayer), game.TimeMachine); + }); + + var launchEventData = new LaunchEventData() + { + DestinationId = "2", + DrillerCount = drillerCount, + SourceId = "1", + SpecialistIds = attackingSpecialists?.ConvertAll(it => it.ToString()) + }; + var launchEvent = new LaunchEvent(new GameRoomEvent() + { + GameEventData = new GameEventData() + { + OccursAtTick = 1, + EventDataType = EventDataType.LaunchEventData, + SerializedEventData = JsonConvert.SerializeObject(launchEventData), + }, + Id = "asdf", + IssuedBy = attackingPlayer.PlayerInstance, + RoomId = "1", + TimeIssued = DateTime.UtcNow, + }); + game.TimeMachine.AddEvent(launchEvent); + game.TimeMachine.GoTo(launchEvent); + game.TimeMachine.Advance(1); + } + + private void AssertOutpostOwnedBy(Outpost outpost, Player owner) + { + Assert.AreEqual(owner.PlayerInstance.Username, outpost.GetComponent().GetOwner().PlayerInstance.Username); + } + + private void AssertOutpostDrillers(Outpost outpost, int expectedDrillers) + { + Assert.AreEqual(expectedDrillers, outpost.GetComponent().GetDrillerCount()); + } + + private void AssertOutpostShields(Outpost outpost, int expectedShields) + { + Assert.AreEqual(expectedShields, outpost.GetComponent().GetShields()); + } + } +} \ No newline at end of file diff --git a/Core/SubterfugeCoreTest/Core/Components/SpecialistManagerTest.cs b/Core/SubterfugeCoreTest/Core/Components/SpecialistManagerTest.cs index 406727f..efa5ce4 100644 --- a/Core/SubterfugeCoreTest/Core/Components/SpecialistManagerTest.cs +++ b/Core/SubterfugeCoreTest/Core/Components/SpecialistManagerTest.cs @@ -203,7 +203,7 @@ public void CannotRemoveASpecialistIfNotInCarrier() var specialist = new NoOpSpecialist(playerOne); bool removeResult = _mockEntity.Object.GetComponent().TransferSpecialistsById( _mockSecondEntity.Object.GetComponent(), - new List() { specialist.GetId() }, + new List() { specialist.GetSpecialistId().ToString() }, _timeMachine ); Assert.IsTrue(removeResult); @@ -387,7 +387,7 @@ public void CanTransferSpecialistsByIdFromOneCarrierToAnotherIfItHasCapacity() Assert.IsTrue(_mockEntity.Object.GetComponent().GetSpecialists().Contains(specialistOne)); Assert.IsTrue(_mockEntity.Object.GetComponent().GetSpecialists().Contains(specialistTwo)); - var listOfIds = specialists.Select(it => it.GetId()).ToList(); + var listOfIds = specialists.Select(it => it.GetSpecialistId().ToString()).ToList(); var transferResult = _mockEntity.Object.GetComponent() .TransferSpecialistsById(_mockSecondEntity.Object.GetComponent(), listOfIds, _timeMachine); @@ -428,7 +428,7 @@ public void CannotTransferSpecialistsByIdFromOneCarrierToAnotherIfNoCapacity() Assert.IsTrue(_mockEntity.Object.GetComponent().GetSpecialists().Contains(specialistOne)); Assert.IsTrue(_mockEntity.Object.GetComponent().GetSpecialists().Contains(specialistTwo)); - var listOfIds = specialists.Select(it => it.GetId()).ToList(); + var listOfIds = specialists.Select(it => it.GetSpecialistId().ToString()).ToList(); var transferResult = _mockEntity.Object.GetComponent() .TransferSpecialistsById(_mockSecondEntity.Object.GetComponent(), listOfIds, _timeMachine);