1 | Engine.LoadHelperScript("FSM.js");
|
---|
2 | Engine.LoadHelperScript("Player.js");
|
---|
3 | Engine.LoadHelperScript("Position.js");
|
---|
4 | Engine.LoadHelperScript("Sound.js");
|
---|
5 | Engine.LoadComponentScript("interfaces/Auras.js");
|
---|
6 | Engine.LoadComponentScript("interfaces/Builder.js");
|
---|
7 | Engine.LoadComponentScript("interfaces/BuildingAI.js");
|
---|
8 | Engine.LoadComponentScript("interfaces/Capturable.js");
|
---|
9 | Engine.LoadComponentScript("interfaces/Garrisonable.js");
|
---|
10 | Engine.LoadComponentScript("interfaces/Resistance.js");
|
---|
11 | Engine.LoadComponentScript("interfaces/Formation.js");
|
---|
12 | Engine.LoadComponentScript("interfaces/Heal.js");
|
---|
13 | Engine.LoadComponentScript("interfaces/Health.js");
|
---|
14 | Engine.LoadComponentScript("interfaces/Pack.js");
|
---|
15 | Engine.LoadComponentScript("interfaces/ResourceSupply.js");
|
---|
16 | Engine.LoadComponentScript("interfaces/ResourceGatherer.js");
|
---|
17 | Engine.LoadComponentScript("interfaces/Timer.js");
|
---|
18 | Engine.LoadComponentScript("interfaces/UnitAI.js");
|
---|
19 | Engine.LoadComponentScript("Formation.js");
|
---|
20 | Engine.LoadComponentScript("UnitAI.js");
|
---|
21 |
|
---|
22 | /**
|
---|
23 | * Fairly straightforward test that entity renaming is handled
|
---|
24 | * by unitAI states. These ought to be augmented with integration tests, ideally.
|
---|
25 | */
|
---|
26 | function TestTargetEntityRenaming(init_state, post_state, setup)
|
---|
27 | {
|
---|
28 | ResetState();
|
---|
29 | const player_ent = 5;
|
---|
30 | const target_ent = 6;
|
---|
31 |
|
---|
32 | AddMock(SYSTEM_ENTITY, IID_Timer, {
|
---|
33 | "SetInterval": () => {},
|
---|
34 | "SetTimeout": () => {}
|
---|
35 | });
|
---|
36 | AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
|
---|
37 | "IsInTargetRange": () => false
|
---|
38 | });
|
---|
39 |
|
---|
40 | let unitAI = ConstructComponent(player_ent, "UnitAI", {
|
---|
41 | "FormationController": "false",
|
---|
42 | "DefaultStance": "aggressive",
|
---|
43 | "FleeDistance": 10
|
---|
44 | });
|
---|
45 | unitAI.OnCreate();
|
---|
46 |
|
---|
47 | setup(unitAI, player_ent, target_ent);
|
---|
48 |
|
---|
49 | TS_ASSERT_EQUALS(unitAI.GetCurrentState(), init_state);
|
---|
50 |
|
---|
51 | unitAI.OnGlobalEntityRenamed({
|
---|
52 | "entity": target_ent,
|
---|
53 | "newentity": target_ent + 1
|
---|
54 | });
|
---|
55 |
|
---|
56 | TS_ASSERT_EQUALS(unitAI.GetCurrentState(), post_state);
|
---|
57 | }
|
---|
58 |
|
---|
59 | TestTargetEntityRenaming(
|
---|
60 | "INDIVIDUAL.GARRISON.APPROACHING", "INDIVIDUAL.IDLE",
|
---|
61 | (unitAI, player_ent, target_ent) => {
|
---|
62 | unitAI.CanGarrison = (target) => target == target_ent;
|
---|
63 | unitAI.MoveToTargetRange = (target) => target == target_ent;
|
---|
64 | unitAI.AbleToMove = () => true;
|
---|
65 |
|
---|
66 | unitAI.Garrison(target_ent, false);
|
---|
67 | }
|
---|
68 | );
|
---|
69 |
|
---|
70 | TestTargetEntityRenaming(
|
---|
71 | "INDIVIDUAL.REPAIR.REPAIRING", "INDIVIDUAL.REPAIR.REPAIRING",
|
---|
72 | (unitAI, player_ent, target_ent) => {
|
---|
73 |
|
---|
74 | AddMock(player_ent, IID_Builder, {
|
---|
75 | "StartRepairing": () => true,
|
---|
76 | "StopRepairing": () => {}
|
---|
77 | });
|
---|
78 |
|
---|
79 | QueryBuilderListInterface = () => {};
|
---|
80 | unitAI.CheckTargetRange = () => true;
|
---|
81 | unitAI.CanRepair = (target) => target == target_ent;
|
---|
82 |
|
---|
83 | unitAI.Repair(target_ent, false, false);
|
---|
84 | }
|
---|
85 | );
|
---|
86 |
|
---|
87 |
|
---|
88 | TestTargetEntityRenaming(
|
---|
89 | "INDIVIDUAL.FLEEING", "INDIVIDUAL.FLEEING",
|
---|
90 | (unitAI, player_ent, target_ent) => {
|
---|
91 | PositionHelper.DistanceBetweenEntities = () => 10;
|
---|
92 | unitAI.CheckTargetRangeExplicit = () => false;
|
---|
93 |
|
---|
94 | AddMock(player_ent, IID_UnitMotion, {
|
---|
95 | "MoveToTargetRange": () => true,
|
---|
96 | "GetRunMultiplier": () => 1,
|
---|
97 | "SetSpeedMultiplier": () => {},
|
---|
98 | "StopMoving": () => {}
|
---|
99 | });
|
---|
100 |
|
---|
101 | unitAI.Flee(target_ent, false);
|
---|
102 | }
|
---|
103 | );
|
---|
104 |
|
---|
105 | /* Regression test.
|
---|
106 | * Tests the FSM behaviour of a unit when walking as part of a formation,
|
---|
107 | * then exiting the formation.
|
---|
108 | * mode == 0: There is no enemy unit nearby.
|
---|
109 | * mode == 1: There is a live enemy unit nearby.
|
---|
110 | * mode == 2: There is a dead enemy unit nearby.
|
---|
111 | */
|
---|
112 | function TestFormationExiting(mode)
|
---|
113 | {
|
---|
114 | ResetState();
|
---|
115 |
|
---|
116 | var playerEntity = 5;
|
---|
117 | var unit = 10;
|
---|
118 | var enemy = 20;
|
---|
119 | var controller = 30;
|
---|
120 |
|
---|
121 |
|
---|
122 | AddMock(SYSTEM_ENTITY, IID_Timer, {
|
---|
123 | "SetInterval": function() { },
|
---|
124 | "SetTimeout": function() { },
|
---|
125 | });
|
---|
126 |
|
---|
127 | AddMock(SYSTEM_ENTITY, IID_RangeManager, {
|
---|
128 | "CreateActiveQuery": function(ent, minRange, maxRange, players, iid, flags, accountForSize) {
|
---|
129 | return 1;
|
---|
130 | },
|
---|
131 | "EnableActiveQuery": function(id) { },
|
---|
132 | "ResetActiveQuery": function(id) { if (mode == 0) return []; return [enemy]; },
|
---|
133 | "DisableActiveQuery": function(id) { },
|
---|
134 | "GetEntityFlagMask": function(identifier) { },
|
---|
135 | });
|
---|
136 |
|
---|
137 | AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
|
---|
138 | "GetCurrentTemplateName": function(ent) { return "special/formations/line_closed"; },
|
---|
139 | });
|
---|
140 |
|
---|
141 | AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
|
---|
142 | "GetPlayerByID": function(id) { return playerEntity; },
|
---|
143 | "GetNumPlayers": function() { return 2; },
|
---|
144 | });
|
---|
145 |
|
---|
146 | AddMock(playerEntity, IID_Player, {
|
---|
147 | "IsAlly": function() { return false; },
|
---|
148 | "IsEnemy": function() { return true; },
|
---|
149 | "GetEnemies": function() { return [2]; },
|
---|
150 | });
|
---|
151 |
|
---|
152 | AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
|
---|
153 | "IsInTargetRange": () => true,
|
---|
154 | "IsInPointRange": () => true
|
---|
155 | });
|
---|
156 |
|
---|
157 | var unitAI = ConstructComponent(unit, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
|
---|
158 |
|
---|
159 | AddMock(unit, IID_Identity, {
|
---|
160 | "GetClassesList": function() { return []; },
|
---|
161 | });
|
---|
162 |
|
---|
163 | AddMock(unit, IID_Ownership, {
|
---|
164 | "GetOwner": function() { return 1; },
|
---|
165 | });
|
---|
166 |
|
---|
167 | AddMock(unit, IID_Position, {
|
---|
168 | "GetTurretParent": function() { return INVALID_ENTITY; },
|
---|
169 | "GetPosition": function() { return new Vector3D(); },
|
---|
170 | "GetPosition2D": function() { return new Vector2D(); },
|
---|
171 | "GetRotation": function() { return { "y": 0 }; },
|
---|
172 | "IsInWorld": function() { return true; },
|
---|
173 | });
|
---|
174 |
|
---|
175 | AddMock(unit, IID_UnitMotion, {
|
---|
176 | "GetWalkSpeed": () => 1,
|
---|
177 | "MoveToFormationOffset": (target, x, z) => {},
|
---|
178 | "MoveToTargetRange": (target, min, max) => true,
|
---|
179 | "StopMoving": () => {},
|
---|
180 | "SetFacePointAfterMove": () => {},
|
---|
181 | "GetFacePointAfterMove": () => true,
|
---|
182 | "GetPassabilityClassName": () => "default"
|
---|
183 | });
|
---|
184 |
|
---|
185 | AddMock(unit, IID_Vision, {
|
---|
186 | "GetRange": function() { return 10; },
|
---|
187 | });
|
---|
188 |
|
---|
189 | AddMock(unit, IID_Attack, {
|
---|
190 | "GetRange": function() { return { "max": 10, "min": 0 }; },
|
---|
191 | "GetFullAttackRange": function() { return { "max": 40, "min": 0 }; },
|
---|
192 | "GetBestAttackAgainst": function(t) { return "melee"; },
|
---|
193 | "GetPreference": function(t) { return 0; },
|
---|
194 | "GetTimers": function() { return { "prepare": 500, "repeat": 1000 }; },
|
---|
195 | "CanAttack": function(v) { return true; },
|
---|
196 | "CompareEntitiesByPreference": function(a, b) { return 0; },
|
---|
197 | });
|
---|
198 |
|
---|
199 | unitAI.OnCreate();
|
---|
200 |
|
---|
201 | unitAI.SetupAttackRangeQuery(1);
|
---|
202 |
|
---|
203 |
|
---|
204 | if (mode == 1)
|
---|
205 | {
|
---|
206 | AddMock(enemy, IID_Health, {
|
---|
207 | "GetHitpoints": function() { return 10; },
|
---|
208 | });
|
---|
209 | AddMock(enemy, IID_UnitAI, {
|
---|
210 | "IsAnimal": () => "false",
|
---|
211 | "IsDangerousAnimal": () => "false"
|
---|
212 | });
|
---|
213 | }
|
---|
214 | else if (mode == 2)
|
---|
215 | AddMock(enemy, IID_Health, {
|
---|
216 | "GetHitpoints": function() { return 0; },
|
---|
217 | });
|
---|
218 |
|
---|
219 | let controllerFormation = ConstructComponent(controller, "Formation", {
|
---|
220 | "FormationName": "Line Closed",
|
---|
221 | "FormationShape": "square",
|
---|
222 | "ShiftRows": "false",
|
---|
223 | "SortingClasses": "",
|
---|
224 | "WidthDepthRatio": 1,
|
---|
225 | "UnitSeparationWidthMultiplier": 1,
|
---|
226 | "UnitSeparationDepthMultiplier": 1,
|
---|
227 | "SpeedMultiplier": 1,
|
---|
228 | "Sloppiness": 0
|
---|
229 | });
|
---|
230 | let controllerAI = ConstructComponent(controller, "UnitAI", {
|
---|
231 | "FormationController": "true",
|
---|
232 | "DefaultStance": "aggressive"
|
---|
233 | });
|
---|
234 |
|
---|
235 | AddMock(controller, IID_Position, {
|
---|
236 | "JumpTo": function(x, z) { this.x = x; this.z = z; },
|
---|
237 | "GetTurretParent": function() { return INVALID_ENTITY; },
|
---|
238 | "GetPosition": function() { return new Vector3D(this.x, 0, this.z); },
|
---|
239 | "GetPosition2D": function() { return new Vector2D(this.x, this.z); },
|
---|
240 | "GetRotation": function() { return { "y": 0 }; },
|
---|
241 | "IsInWorld": function() { return true; },
|
---|
242 | "MoveOutOfWorld": () => {}
|
---|
243 | });
|
---|
244 |
|
---|
245 | AddMock(controller, IID_UnitMotion, {
|
---|
246 | "GetWalkSpeed": () => 1,
|
---|
247 | "StopMoving": () => {},
|
---|
248 | "SetSpeedMultiplier": () => {},
|
---|
249 | "MoveToPointRange": () => true,
|
---|
250 | "SetFacePointAfterMove": () => {},
|
---|
251 | "GetFacePointAfterMove": () => true,
|
---|
252 | "GetPassabilityClassName": () => "default"
|
---|
253 | });
|
---|
254 |
|
---|
255 | controllerAI.OnCreate();
|
---|
256 |
|
---|
257 |
|
---|
258 | TS_ASSERT_EQUALS(controllerAI.fsmStateName, "FORMATIONCONTROLLER.IDLE");
|
---|
259 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
|
---|
260 |
|
---|
261 | controllerFormation.SetMembers([unit]);
|
---|
262 | controllerAI.Walk(100, 100, false);
|
---|
263 |
|
---|
264 | TS_ASSERT_EQUALS(controllerAI.fsmStateName, "FORMATIONCONTROLLER.WALKING");
|
---|
265 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "FORMATIONMEMBER.WALKING");
|
---|
266 |
|
---|
267 | controllerFormation.Disband();
|
---|
268 |
|
---|
269 | unitAI.UnitFsm.ProcessMessage(unitAI, { "type": "Timer" });
|
---|
270 |
|
---|
271 | if (mode == 0)
|
---|
272 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
|
---|
273 | else if (mode == 1)
|
---|
274 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
|
---|
275 | else if (mode == 2)
|
---|
276 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.IDLE");
|
---|
277 | else
|
---|
278 | TS_FAIL("invalid mode");
|
---|
279 | }
|
---|
280 |
|
---|
281 | function TestMoveIntoFormationWhileAttacking()
|
---|
282 | {
|
---|
283 | ResetState();
|
---|
284 |
|
---|
285 | var playerEntity = 5;
|
---|
286 | var controller = 10;
|
---|
287 | var enemy = 20;
|
---|
288 | var unit = 30;
|
---|
289 | var units = [];
|
---|
290 | var unitCount = 8;
|
---|
291 | var unitAIs = [];
|
---|
292 |
|
---|
293 | AddMock(SYSTEM_ENTITY, IID_Timer, {
|
---|
294 | "SetInterval": function() { },
|
---|
295 | "SetTimeout": function() { },
|
---|
296 | });
|
---|
297 |
|
---|
298 |
|
---|
299 | AddMock(SYSTEM_ENTITY, IID_RangeManager, {
|
---|
300 | "CreateActiveQuery": function(ent, minRange, maxRange, players, iid, flags, accountForSize) {
|
---|
301 | return 1;
|
---|
302 | },
|
---|
303 | "EnableActiveQuery": function(id) { },
|
---|
304 | "ResetActiveQuery": function(id) { return [enemy]; },
|
---|
305 | "DisableActiveQuery": function(id) { },
|
---|
306 | "GetEntityFlagMask": function(identifier) { },
|
---|
307 | });
|
---|
308 |
|
---|
309 | AddMock(SYSTEM_ENTITY, IID_TemplateManager, {
|
---|
310 | "GetCurrentTemplateName": function(ent) { return "special/formations/line_closed"; },
|
---|
311 | });
|
---|
312 |
|
---|
313 | AddMock(SYSTEM_ENTITY, IID_PlayerManager, {
|
---|
314 | "GetPlayerByID": function(id) { return playerEntity; },
|
---|
315 | "GetNumPlayers": function() { return 2; },
|
---|
316 | });
|
---|
317 |
|
---|
318 | AddMock(SYSTEM_ENTITY, IID_ObstructionManager, {
|
---|
319 | "IsInTargetRange": (ent, target, min, max) => true
|
---|
320 | });
|
---|
321 |
|
---|
322 | AddMock(playerEntity, IID_Player, {
|
---|
323 | "IsAlly": function() { return false; },
|
---|
324 | "IsEnemy": function() { return true; },
|
---|
325 | "GetEnemies": function() { return [2]; },
|
---|
326 | });
|
---|
327 |
|
---|
328 | // create units
|
---|
329 | for (var i = 0; i < unitCount; i++)
|
---|
330 | {
|
---|
331 |
|
---|
332 | units.push(unit + i);
|
---|
333 |
|
---|
334 | var unitAI = ConstructComponent(unit + i, "UnitAI", { "FormationController": "false", "DefaultStance": "aggressive" });
|
---|
335 |
|
---|
336 | AddMock(unit + i, IID_Identity, {
|
---|
337 | "GetClassesList": function() { return []; },
|
---|
338 | });
|
---|
339 |
|
---|
340 | AddMock(unit + i, IID_Ownership, {
|
---|
341 | "GetOwner": function() { return 1; },
|
---|
342 | });
|
---|
343 |
|
---|
344 | AddMock(unit + i, IID_Position, {
|
---|
345 | "GetTurretParent": function() { return INVALID_ENTITY; },
|
---|
346 | "GetPosition": function() { return new Vector3D(); },
|
---|
347 | "GetPosition2D": function() { return new Vector2D(); },
|
---|
348 | "GetRotation": function() { return { "y": 0 }; },
|
---|
349 | "IsInWorld": function() { return true; },
|
---|
350 | });
|
---|
351 |
|
---|
352 | AddMock(unit + i, IID_UnitMotion, {
|
---|
353 | "GetWalkSpeed": () => 1,
|
---|
354 | "MoveToFormationOffset": (target, x, z) => {},
|
---|
355 | "MoveToTargetRange": (target, min, max) => true,
|
---|
356 | "StopMoving": () => {},
|
---|
357 | "SetFacePointAfterMove": () => {},
|
---|
358 | "GetFacePointAfterMove": () => true,
|
---|
359 | "GetPassabilityClassName": () => "default"
|
---|
360 | });
|
---|
361 |
|
---|
362 | AddMock(unit + i, IID_Vision, {
|
---|
363 | "GetRange": function() { return 10; },
|
---|
364 | });
|
---|
365 |
|
---|
366 | AddMock(unit + i, IID_Attack, {
|
---|
367 | "GetRange": function() { return { "max": 10, "min": 0 }; },
|
---|
368 | "GetFullAttackRange": function() { return { "max": 40, "min": 0 }; },
|
---|
369 | "GetBestAttackAgainst": function(t) { return "melee"; },
|
---|
370 | "GetTimers": function() { return { "prepare": 500, "repeat": 1000 }; },
|
---|
371 | "CanAttack": function(v) { return true; },
|
---|
372 | "CompareEntitiesByPreference": function(a, b) { return 0; },
|
---|
373 | });
|
---|
374 |
|
---|
375 | unitAI.OnCreate();
|
---|
376 |
|
---|
377 | unitAI.SetupAttackRangeQuery(1);
|
---|
378 |
|
---|
379 | unitAIs.push(unitAI);
|
---|
380 | }
|
---|
381 |
|
---|
382 | // create enemy
|
---|
383 | AddMock(enemy, IID_Health, {
|
---|
384 | "GetHitpoints": function() { return 40; },
|
---|
385 | });
|
---|
386 |
|
---|
387 | let controllerFormation = ConstructComponent(controller, "Formation", {
|
---|
388 | "FormationName": "Line Closed",
|
---|
389 | "FormationShape": "square",
|
---|
390 | "ShiftRows": "false",
|
---|
391 | "SortingClasses": "",
|
---|
392 | "WidthDepthRatio": 1,
|
---|
393 | "UnitSeparationWidthMultiplier": 1,
|
---|
394 | "UnitSeparationDepthMultiplier": 1,
|
---|
395 | "SpeedMultiplier": 1,
|
---|
396 | "Sloppiness": 0
|
---|
397 | });
|
---|
398 | let controllerAI = ConstructComponent(controller, "UnitAI", {
|
---|
399 | "FormationController": "true",
|
---|
400 | "DefaultStance": "aggressive"
|
---|
401 | });
|
---|
402 |
|
---|
403 | AddMock(controller, IID_Position, {
|
---|
404 | "GetTurretParent": () => INVALID_ENTITY,
|
---|
405 | "JumpTo": function(x, z) { this.x = x; this.z = z; },
|
---|
406 | "GetPosition": function(){ return new Vector3D(this.x, 0, this.z); },
|
---|
407 | "GetPosition2D": function(){ return new Vector2D(this.x, this.z); },
|
---|
408 | "GetRotation": () => ({ "y": 0 }),
|
---|
409 | "IsInWorld": () => true,
|
---|
410 | "MoveOutOfWorld": () => {},
|
---|
411 | });
|
---|
412 |
|
---|
413 | AddMock(controller, IID_UnitMotion, {
|
---|
414 | "GetWalkSpeed": () => 1,
|
---|
415 | "SetSpeedMultiplier": (speed) => {},
|
---|
416 | "MoveToPointRange": (x, z, minRange, maxRange) => {},
|
---|
417 | "StopMoving": () => {},
|
---|
418 | "SetFacePointAfterMove": () => {},
|
---|
419 | "GetFacePointAfterMove": () => true,
|
---|
420 | "GetPassabilityClassName": () => "default"
|
---|
421 | });
|
---|
422 |
|
---|
423 | AddMock(controller, IID_Attack, {
|
---|
424 | "GetRange": function() { return { "max": 10, "min": 0 }; },
|
---|
425 | "CanAttackAsFormation": function() { return false; },
|
---|
426 | });
|
---|
427 |
|
---|
428 | controllerAI.OnCreate();
|
---|
429 |
|
---|
430 | controllerFormation.SetMembers(units);
|
---|
431 |
|
---|
432 | controllerAI.Attack(enemy, []);
|
---|
433 |
|
---|
434 | for (let ent of unitAIs)
|
---|
435 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
|
---|
436 |
|
---|
437 | controllerAI.MoveIntoFormation({ "name": "Circle" });
|
---|
438 |
|
---|
439 | // let all units be in position
|
---|
440 | for (let ent of unitAIs)
|
---|
441 | controllerFormation.SetWaitingOnController(ent);
|
---|
442 |
|
---|
443 | for (let ent of unitAIs)
|
---|
444 | TS_ASSERT_EQUALS(unitAI.fsmStateName, "INDIVIDUAL.COMBAT.ATTACKING");
|
---|
445 |
|
---|
446 | controllerFormation.Disband();
|
---|
447 | }
|
---|
448 |
|
---|
449 | TestFormationExiting(0);
|
---|
450 | TestFormationExiting(1);
|
---|
451 | TestFormationExiting(2);
|
---|
452 |
|
---|
453 | TestMoveIntoFormationWhileAttacking();
|
---|
454 |
|
---|
455 |
|
---|
456 | function TestWalkAndFightTargets()
|
---|
457 | {
|
---|
458 | const ent = 10;
|
---|
459 | let unitAI = ConstructComponent(ent, "UnitAI", {
|
---|
460 | "FormationController": "false",
|
---|
461 | "DefaultStance": "aggressive",
|
---|
462 | "FleeDistance": 10
|
---|
463 | });
|
---|
464 | unitAI.OnCreate();
|
---|
465 | unitAI.losAttackRangeQuery = true;
|
---|
466 |
|
---|
467 | // The result is stored here
|
---|
468 | let result;
|
---|
469 | unitAI.PushOrderFront = function(type, order)
|
---|
470 | {
|
---|
471 | if (type === "Attack" && order?.target)
|
---|
472 | result = order.target;
|
---|
473 | };
|
---|
474 |
|
---|
475 | // Create some targets.
|
---|
476 | AddMock(ent+1, IID_UnitAI, { "IsAnimal": () => true, "IsDangerousAnimal": () => false });
|
---|
477 | AddMock(ent+2, IID_Ownership, { "GetOwner": () => 2 });
|
---|
478 | AddMock(ent+3, IID_Ownership, { "GetOwner": () => 2 });
|
---|
479 | AddMock(ent+4, IID_Ownership, { "GetOwner": () => 2 });
|
---|
480 | AddMock(ent+5, IID_Ownership, { "GetOwner": () => 2 });
|
---|
481 | AddMock(ent+6, IID_Ownership, { "GetOwner": () => 2 });
|
---|
482 | AddMock(ent+7, IID_Ownership, { "GetOwner": () => 2 });
|
---|
483 |
|
---|
484 | unitAI.CanAttack = function(target)
|
---|
485 | {
|
---|
486 | return target !== ent+2 && target !== ent+7;
|
---|
487 | };
|
---|
488 |
|
---|
489 | AddMock(ent, IID_Attack, {
|
---|
490 | "GetPreference": (target) => ({
|
---|
491 | [ent+4]: 0,
|
---|
492 | [ent+5]: 1,
|
---|
493 | [ent+6]: 2,
|
---|
494 | [ent+7]: 0
|
---|
495 | }?.[target])
|
---|
496 | });
|
---|
497 |
|
---|
498 | let runTest = function(ents, res)
|
---|
499 | {
|
---|
500 | result = undefined;
|
---|
501 | AddMock(SYSTEM_ENTITY, IID_RangeManager, {
|
---|
502 | "ResetActiveQuery": () => ents
|
---|
503 | });
|
---|
504 | TS_ASSERT_EQUALS(unitAI.FindWalkAndFightTargets(), !!res);
|
---|
505 | TS_ASSERT_EQUALS(result, res);
|
---|
506 | };
|
---|
507 |
|
---|
508 | // No entities.
|
---|
509 | runTest([]);
|
---|
510 |
|
---|
511 | // Entities that cannot be attacked.
|
---|
512 | runTest([ent+1, ent+2, ent+7]);
|
---|
513 |
|
---|
514 | // No preference, one attackable entity.
|
---|
515 | runTest([ent+1, ent+2, ent+3], ent+3);
|
---|
516 |
|
---|
517 | // Check preferences.
|
---|
518 | runTest([ent+1, ent+2, ent+3, ent+4], ent+4);
|
---|
519 | runTest([ent+1, ent+2, ent+3, ent+4, ent+5], ent+4);
|
---|
520 | runTest([ent+1, ent+2, ent+6, ent+3, ent+4, ent+5], ent+4);
|
---|
521 | runTest([ent+1, ent+2, ent+7, ent+6, ent+3, ent+4, ent+5], ent+4);
|
---|
522 | runTest([ent+1, ent+2, ent+7, ent+6, ent+3, ent+5], ent+5);
|
---|
523 | runTest([ent+1, ent+2, ent+7, ent+6, ent+3], ent+6);
|
---|
524 | runTest([ent+1, ent+2, ent+7, ent+3], ent+3);
|
---|
525 | }
|
---|
526 |
|
---|
527 | TestWalkAndFightTargets();
|
---|