diff --git a/binaries/data/mods/public/simulation/components/tests/test_Attack.js b/binaries/data/mods/public/simulation/components/tests/test_Attack.js new file mode 100644 index 0000000000..fd69000176 --- /dev/null +++ b/binaries/data/mods/public/simulation/components/tests/test_Attack.js @@ -0,0 +1,165 @@ +Engine.LoadHelperScript("Player.js"); +Engine.LoadHelperScript("ValueModification.js"); +Engine.LoadComponentScript("interfaces/Auras.js"); +Engine.LoadComponentScript("interfaces/AuraManager.js"); +Engine.LoadComponentScript("interfaces/Capturable.js"); +Engine.LoadComponentScript("interfaces/TechnologyManager.js"); +Engine.LoadComponentScript("interfaces/Formation.js"); +Engine.LoadComponentScript("interfaces/Attack.js"); +Engine.LoadComponentScript("Attack.js"); + +let entityID = 903; + +function attackComponentTest(defenderClass, test_function) +{ + ResetState(); + + { + let playerEnt1 = 5; + + AddMock(SYSTEM_ENTITY, IID_PlayerManager, { + "GetPlayerByID": () => playerEnt1 + }); + + AddMock(playerEnt1, IID_Player, { + "GetPlayerID": () => 1 + }); + } + + let attacker = entityID; + + AddMock(attacker, IID_Position, { + "IsInWorld": () => true, + "GetHeightOffset": () => 5 + }); + + AddMock(attacker, IID_Ownership, { + "GetOwner": owner => 1 + }); + + let cmpAttack = ConstructComponent(attacker, "Attack", { + "Melee" : { + "Hack": 11, + "Pierce": 5, + "Crush": 0, + "MinRange": 3, + "MaxRange": 5, + "PreferredClasses": { + "_string": "Female" + }, + "RestrictedClasses": { + "_string": "Elephant Archer" + }, + "Bonuses": + { + "BonusCav": { + "Classes": "Cavalry", + "Multiplier": 2 + } + } + }, + "Ranged" : { + "Hack": 0, + "Pierce": 10, + "Crush": 0, + "MinRange": 10, + "MaxRange": 80, + "PrepareTime": 300, + "RepeatTime": 500, + "PreferredClasses": { + "_string": "Archer" + }, + "RestrictedClasses": { + "_string": "Elephant" + } + }, + "Capture" : { + "Value": 8, + "MaxRange": 10, + }, + "Slaughter": {} + }); + + let defender = ++entityID; + + AddMock(defender, IID_Identity, { + "GetClassesList": () => [defenderClass], + "HasClass": className => className == defenderClass + }); + + AddMock(defender, IID_Position, { + "IsInWorld": () => true, + "GetHeightOffset": () => 0 + }); + + test_function(attacker, cmpAttack, defender); +} + +// Validate template getter functions +attackComponentTest(undefined, (attacker, cmpAttack, defender) => { + + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackTypes(), ["Melee", "Ranged", "Capture"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetPreferredClasses("Melee"), ["Female"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetRestrictedClasses("Melee"), ["Elephant", "Archer"]); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetFullAttackRange(), { "min": 0, "max": 80 }); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackStrengths("Capture"), { "value": 8 }); + + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackStrengths("Ranged"), { + "hack": 0, + "pierce": 10, + "crush": 0 + }); + + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetTimers("Ranged"), { + "prepare": 300, + "repeat": 500, + "recharge": 200 + }); + + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetTimers("Capture"), { + "prepare": 0, + "repeat": 1000, + "recharge": 1000 + }); +}); + +for (let className of ["Infantry", "Cavalry"]) + attackComponentTest(className, (attacker, cmpAttack, defender) => { + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Melee", defender), className == "Cavalry" ? 2 : 1); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Ranged", defender), 1); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Capture", defender), 1); + TS_ASSERT_UNEVAL_EQUALS(cmpAttack.GetAttackBonus("Slaughter", defender), 1); + }); + +// CanAttack rejects elephant attack due to RestrictedClasses +attackComponentTest("Elephant", (attacker, cmpAttack, defender) => { + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), false); +}); + +function testGetBestAttackAgainst(defenderClass, bestAttack, isBuilding = false) +{ + attackComponentTest(defenderClass, (attacker, cmpAttack, defender) => { + + if (isBuilding) + AddMock(defender, IID_Capturable, { + "CanCapture": playerID => { + TS_ASSERT_EQUALS(playerID, 1); + return true; + } + }); + + TS_ASSERT_EQUALS(cmpAttack.CanAttack(defender), true); + + let allowCapturing = [true]; + if (!isBuilding) + allowCapturing.push(false); + + for (let allowCapturing of allowCapturing) + TS_ASSERT_EQUALS(cmpAttack.GetBestAttackAgainst(defender, allowCapturing), bestAttack); + }); +} + +testGetBestAttackAgainst("Female", "Melee"); +testGetBestAttackAgainst("Archer", "Ranged"); +testGetBestAttackAgainst("Domestic", "Slaughter"); +testGetBestAttackAgainst("Structure", "Capture", true);