@supalosa/chronodivide-bot 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.template +5 -0
- package/README.md +54 -47
- package/dist/bot/bot.js +14 -30
- package/dist/bot/bot.js.map +1 -1
- package/dist/bot/logic/awareness.js +13 -8
- package/dist/bot/logic/awareness.js.map +1 -1
- package/dist/bot/logic/awarenessImpl.js +132 -0
- package/dist/bot/logic/awarenessImpl.js.map +1 -0
- package/dist/bot/logic/building/ArtilleryUnit.js +2 -29
- package/dist/bot/logic/building/ArtilleryUnit.js.map +1 -0
- package/dist/bot/logic/building/antiAirStaticDefence.js +43 -0
- package/dist/bot/logic/building/antiAirStaticDefence.js.map +1 -0
- package/dist/bot/logic/building/antiGroundStaticDefence.js +7 -4
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
- package/dist/bot/logic/building/basicAirUnit.js +2 -23
- package/dist/bot/logic/building/basicAirUnit.js.map +1 -1
- package/dist/bot/logic/building/basicBuilding.js +3 -2
- package/dist/bot/logic/building/basicBuilding.js.map +1 -1
- package/dist/bot/logic/building/basicGroundUnit.js +2 -43
- package/dist/bot/logic/building/basicGroundUnit.js.map +1 -1
- package/dist/bot/logic/building/building.js +55 -11
- package/dist/bot/logic/building/building.js.map +1 -0
- package/dist/bot/logic/building/buildingRules.js +10 -5
- package/dist/bot/logic/building/buildingRules.js.map +1 -1
- package/dist/bot/logic/building/common.js +19 -0
- package/dist/bot/logic/building/common.js.map +1 -0
- package/dist/bot/logic/building/harvester.js +2 -1
- package/dist/bot/logic/building/harvester.js.map +1 -1
- package/dist/bot/logic/building/queueController.js +69 -42
- package/dist/bot/logic/building/queueController.js.map +1 -1
- package/dist/bot/logic/common/utils.js +21 -0
- package/dist/bot/logic/common/utils.js.map +1 -1
- package/dist/bot/logic/composition/alliedCompositions.js +13 -0
- package/dist/bot/logic/composition/alliedCompositions.js.map +1 -0
- package/dist/bot/logic/composition/common.js +2 -0
- package/dist/bot/logic/composition/common.js.map +1 -0
- package/dist/bot/logic/composition/sovietCompositions.js +13 -0
- package/dist/bot/logic/composition/sovietCompositions.js.map +1 -0
- package/dist/bot/logic/mission/actionBatcher.js +92 -0
- package/dist/bot/logic/mission/actionBatcher.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/combatSquad.js +124 -0
- package/dist/bot/logic/mission/behaviours/combatSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/common.js +58 -0
- package/dist/bot/logic/mission/behaviours/common.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/engineerSquad.js +39 -0
- package/dist/bot/logic/mission/behaviours/engineerSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/expansionSquad.js +46 -0
- package/dist/bot/logic/mission/behaviours/expansionSquad.js.map +1 -0
- package/dist/bot/logic/mission/behaviours/retreatSquad.js +31 -0
- package/dist/bot/logic/mission/behaviours/retreatSquad.js.map +1 -0
- package/{src/bot/logic/squad/behaviours/scoutingSquad.ts → dist/bot/logic/mission/behaviours/scoutingSquad.js} +27 -51
- package/dist/bot/logic/mission/behaviours/scoutingSquad.js.map +1 -0
- package/dist/bot/logic/mission/mission.js +91 -19
- package/dist/bot/logic/mission/mission.js.map +1 -1
- package/dist/bot/logic/mission/missionController.js +262 -21
- package/dist/bot/logic/mission/missionController.js.map +1 -1
- package/dist/bot/logic/mission/missions/attackMission.js +113 -39
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/basicMission.js +13 -0
- package/dist/bot/logic/mission/missions/basicMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/defenceMission.js +43 -28
- package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/engineerMission.js +37 -7
- package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/expansionMission.js +42 -6
- package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/missionBehaviour.js +2 -0
- package/dist/bot/logic/mission/missions/missionBehaviour.js.map +1 -0
- package/dist/bot/logic/mission/missions/retreatMission.js +31 -5
- package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/scoutingMission.js +103 -6
- package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -1
- package/dist/bot/logic/mission/missions/squads/combatSquad.js +116 -0
- package/dist/bot/logic/mission/missions/squads/combatSquad.js.map +1 -0
- package/dist/bot/logic/mission/missions/squads/common.js +58 -0
- package/dist/bot/logic/mission/missions/squads/common.js.map +1 -0
- package/dist/bot/logic/mission/missions/squads/squad.js +2 -0
- package/dist/bot/logic/mission/missions/squads/squad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/attackSquad.js +63 -56
- package/dist/bot/logic/squad/behaviours/combatSquad.js +21 -25
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/common.js +11 -26
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/defenceSquad.js +15 -2
- package/dist/bot/logic/squad/behaviours/engineerSquad.js +2 -4
- package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +2 -4
- package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +1 -4
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +18 -25
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
- package/dist/bot/logic/squad/squad.js +10 -10
- package/dist/bot/logic/squad/squad.js.map +1 -1
- package/dist/bot/logic/squad/squadBehaviour.js.map +1 -1
- package/dist/bot/logic/squad/squadController.js +5 -17
- package/dist/bot/logic/squad/squadController.js.map +1 -1
- package/dist/bot/logic/threat/threatCalculator.js +5 -5
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
- package/dist/exampleBot.js +45 -18
- package/dist/exampleBot.js.map +1 -1
- package/package.json +5 -4
- package/src/bot/bot.ts +19 -45
- package/src/bot/logic/awareness.ts +34 -22
- package/src/bot/logic/building/antiAirStaticDefence.ts +64 -0
- package/src/bot/logic/building/antiGroundStaticDefence.ts +7 -20
- package/src/bot/logic/building/artilleryUnit.ts +2 -28
- package/src/bot/logic/building/basicAirUnit.ts +2 -33
- package/src/bot/logic/building/basicBuilding.ts +8 -6
- package/src/bot/logic/building/basicGroundUnit.ts +2 -46
- package/src/bot/logic/building/buildingRules.ts +15 -9
- package/src/bot/logic/building/common.ts +23 -0
- package/src/bot/logic/building/harvester.ts +2 -1
- package/src/bot/logic/building/queueController.ts +98 -43
- package/src/bot/logic/common/utils.ts +28 -0
- package/src/bot/logic/composition/alliedCompositions.ts +22 -0
- package/src/bot/logic/composition/common.ts +3 -0
- package/src/bot/logic/composition/sovietCompositions.ts +21 -0
- package/src/bot/logic/{squad/behaviours → mission}/actionBatcher.ts +66 -7
- package/src/bot/logic/mission/mission.ts +186 -37
- package/src/bot/logic/mission/missionController.ts +340 -31
- package/src/bot/logic/mission/missionFactories.ts +3 -3
- package/src/bot/logic/mission/missions/attackMission.ts +181 -44
- package/src/bot/logic/mission/missions/defenceMission.ts +72 -45
- package/src/bot/logic/mission/missions/engineerMission.ts +67 -15
- package/src/bot/logic/mission/missions/expansionMission.ts +67 -14
- package/src/bot/logic/mission/missions/retreatMission.ts +50 -6
- package/src/bot/logic/mission/missions/scoutingMission.ts +138 -14
- package/src/bot/logic/{squad/behaviours → mission/missions/squads}/combatSquad.ts +56 -33
- package/src/bot/logic/{squad/behaviours → mission/missions/squads}/common.ts +11 -17
- package/src/bot/logic/mission/missions/squads/squad.ts +19 -0
- package/src/bot/logic/threat/threat.ts +15 -15
- package/src/bot/logic/threat/threatCalculator.ts +10 -10
- package/src/exampleBot.ts +50 -24
- package/.prettierrc +0 -5
- package/TODO.md +0 -15
- package/dist/bot/logic/building/artilleryUnit.js.map +0 -1
- package/dist/bot/logic/building/massedAntiGroundUnit.js +0 -20
- package/dist/bot/logic/building/queues.js +0 -19
- package/dist/bot/logic/knowledge.js +0 -1
- package/dist/bot/logic/mission/basicMission.js +0 -26
- package/dist/bot/logic/mission/expansionMission.js +0 -32
- package/dist/bot/logic/squad/behaviours/actionBatcher.js +0 -36
- package/dist/bot/logic/squad/behaviours/actionBatcher.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/squadExpansion.js +0 -31
- package/dist/bot/logic/squad/behaviours/squadScouters.js +0 -8
- package/rules.ini +0 -23126
- package/src/bot/logic/mission/missions/oneTimeMission.ts +0 -33
- package/src/bot/logic/squad/behaviours/engineerSquad.ts +0 -58
- package/src/bot/logic/squad/behaviours/expansionSquad.ts +0 -64
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +0 -50
- package/src/bot/logic/squad/squad.ts +0 -165
- package/src/bot/logic/squad/squadBehaviour.ts +0 -66
- package/src/bot/logic/squad/squadBehaviours.ts +0 -8
- package/src/bot/logic/squad/squadController.ts +0 -271
|
@@ -1,8 +1,34 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
export class RetreatMission extends
|
|
4
|
-
constructor(uniqueName,
|
|
5
|
-
super(uniqueName,
|
|
1
|
+
import { OrderType } from "@chronodivide/game-api";
|
|
2
|
+
import { Mission, disbandMission, requestSpecificUnits } from "../mission.js";
|
|
3
|
+
export class RetreatMission extends Mission {
|
|
4
|
+
constructor(uniqueName, retreatToPoint, withUnitIds, logger) {
|
|
5
|
+
super(uniqueName, logger);
|
|
6
|
+
this.retreatToPoint = retreatToPoint;
|
|
7
|
+
this.withUnitIds = withUnitIds;
|
|
8
|
+
this.createdAt = null;
|
|
9
|
+
}
|
|
10
|
+
_onAiUpdate(gameApi, actionsApi, playerData, matchAwareness, actionBatcher) {
|
|
11
|
+
if (!this.createdAt) {
|
|
12
|
+
this.createdAt = gameApi.getCurrentTick();
|
|
13
|
+
}
|
|
14
|
+
if (this.getUnitIds().length > 0) {
|
|
15
|
+
// Only send the order once we have managed to claim some units.
|
|
16
|
+
actionsApi.orderUnits(this.getUnitIds(), OrderType.AttackMove, this.retreatToPoint.x, this.retreatToPoint.y);
|
|
17
|
+
return disbandMission();
|
|
18
|
+
}
|
|
19
|
+
if (this.createdAt && gameApi.getCurrentTick() > this.createdAt + 240) {
|
|
20
|
+
// Disband automatically after 240 ticks in case we couldn't actually claim any units.
|
|
21
|
+
return disbandMission();
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
return requestSpecificUnits(this.withUnitIds, 1000);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
getGlobalDebugText() {
|
|
28
|
+
return `retreat with ${this.withUnitIds.length} units`;
|
|
29
|
+
}
|
|
30
|
+
getPriority() {
|
|
31
|
+
return 100;
|
|
6
32
|
}
|
|
7
33
|
}
|
|
8
34
|
//# sourceMappingURL=retreatMission.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retreatMission.js","sourceRoot":"","sources":["../../../../../src/bot/logic/mission/missions/retreatMission.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"retreatMission.js","sourceRoot":"","sources":["../../../../../src/bot/logic/mission/missions/retreatMission.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,SAAS,EAAuB,MAAM,wBAAwB,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAiB,cAAc,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAI7F,MAAM,OAAO,cAAe,SAAQ,OAAO;IAGvC,YACI,UAAkB,EACV,cAAuB,EACvB,WAAqB,EAC7B,MAAmB;QAEnB,KAAK,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAJlB,mBAAc,GAAd,cAAc,CAAS;QACvB,gBAAW,GAAX,WAAW,CAAU;QALzB,cAAS,GAAkB,IAAI,CAAC;IASxC,CAAC;IAEM,WAAW,CACd,OAAgB,EAChB,UAAsB,EACtB,UAAsB,EACtB,cAA8B,EAC9B,aAA4B;QAE5B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YACjB,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC;SAC7C;QACD,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE;YAC9B,gEAAgE;YAChE,UAAU,CAAC,UAAU,CACjB,IAAI,CAAC,UAAU,EAAE,EACjB,SAAS,CAAC,UAAU,EACpB,IAAI,CAAC,cAAc,CAAC,CAAC,EACrB,IAAI,CAAC,cAAc,CAAC,CAAC,CACxB,CAAC;YACF,OAAO,cAAc,EAAE,CAAC;SAC3B;QACD,IAAI,IAAI,CAAC,SAAS,IAAI,OAAO,CAAC,cAAc,EAAE,GAAG,IAAI,CAAC,SAAS,GAAG,GAAG,EAAE;YACnE,sFAAsF;YACtF,OAAO,cAAc,EAAE,CAAC;SAC3B;aAAM;YACH,OAAO,oBAAoB,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;SACvD;IACL,CAAC;IAEM,kBAAkB;QACrB,OAAO,gBAAgB,IAAI,CAAC,WAAW,CAAC,MAAM,QAAQ,CAAC;IAC3D,CAAC;IAEM,WAAW;QACd,OAAO,GAAG,CAAC;IACf,CAAC;CACJ"}
|
|
@@ -1,12 +1,102 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { OrderType } from "@chronodivide/game-api";
|
|
2
|
+
import { Mission, disbandMission, noop, requestUnits } from "../mission.js";
|
|
3
3
|
import { AttackMission } from "./attackMission.js";
|
|
4
|
+
import { getDistanceBetweenTileAndPoint } from "../../map/map.js";
|
|
5
|
+
const SCOUT_MOVE_COOLDOWN_TICKS = 30;
|
|
6
|
+
// Max units to spend on a particular scout target.
|
|
7
|
+
const MAX_ATTEMPTS_PER_TARGET = 5;
|
|
8
|
+
// Maximum ticks to spend trying to scout a target *without making progress towards it*.
|
|
9
|
+
// Every time a unit gets closer to the target, the timer refreshes.
|
|
10
|
+
const MAX_TICKS_PER_TARGET = 600;
|
|
4
11
|
/**
|
|
5
12
|
* A mission that tries to scout around the map with a cheap, fast unit (usually attack dogs)
|
|
6
13
|
*/
|
|
7
|
-
export class ScoutingMission extends
|
|
14
|
+
export class ScoutingMission extends Mission {
|
|
8
15
|
constructor(uniqueName, priority, logger) {
|
|
9
|
-
super(uniqueName,
|
|
16
|
+
super(uniqueName, logger);
|
|
17
|
+
this.priority = priority;
|
|
18
|
+
this.scoutTarget = null;
|
|
19
|
+
this.attemptsOnCurrentTarget = 0;
|
|
20
|
+
this.scoutTargetRefreshedAt = 0;
|
|
21
|
+
this.lastMoveCommandTick = 0;
|
|
22
|
+
this.scoutTargetIsPermanent = false;
|
|
23
|
+
this.hadUnit = false;
|
|
24
|
+
}
|
|
25
|
+
_onAiUpdate(gameApi, actionsApi, playerData, matchAwareness, actionBatcher) {
|
|
26
|
+
const scoutNames = ["ADOG", "DOG", "E1", "E2", "FV", "HTK"];
|
|
27
|
+
const scouts = this.getUnitsOfTypes(gameApi, ...scoutNames);
|
|
28
|
+
if ((matchAwareness.getSectorCache().getOverallVisibility() || 0) > 0.9) {
|
|
29
|
+
return disbandMission();
|
|
30
|
+
}
|
|
31
|
+
if (scouts.length === 0) {
|
|
32
|
+
// Count the number of times the scout dies trying to uncover the current scoutTarget.
|
|
33
|
+
if (this.scoutTarget && this.hadUnit) {
|
|
34
|
+
this.attemptsOnCurrentTarget++;
|
|
35
|
+
this.hadUnit = false;
|
|
36
|
+
}
|
|
37
|
+
return requestUnits(scoutNames, this.priority);
|
|
38
|
+
}
|
|
39
|
+
else if (this.scoutTarget) {
|
|
40
|
+
this.hadUnit = true;
|
|
41
|
+
if (!this.scoutTargetIsPermanent) {
|
|
42
|
+
if (this.attemptsOnCurrentTarget > MAX_ATTEMPTS_PER_TARGET) {
|
|
43
|
+
this.logger(`Scout target ${this.scoutTarget.x},${this.scoutTarget.y} took too many attempts, moving to next`);
|
|
44
|
+
this.setScoutTarget(null, 0);
|
|
45
|
+
return noop();
|
|
46
|
+
}
|
|
47
|
+
if (gameApi.getCurrentTick() > this.scoutTargetRefreshedAt + MAX_TICKS_PER_TARGET) {
|
|
48
|
+
this.logger(`Scout target ${this.scoutTarget.x},${this.scoutTarget.y} took too long, moving to next`);
|
|
49
|
+
this.setScoutTarget(null, 0);
|
|
50
|
+
return noop();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const targetTile = gameApi.mapApi.getTile(this.scoutTarget.x, this.scoutTarget.y);
|
|
54
|
+
if (!targetTile) {
|
|
55
|
+
throw new Error(`target tile ${this.scoutTarget.x},${this.scoutTarget.y} does not exist`);
|
|
56
|
+
}
|
|
57
|
+
if (gameApi.getCurrentTick() > this.lastMoveCommandTick + SCOUT_MOVE_COOLDOWN_TICKS) {
|
|
58
|
+
this.lastMoveCommandTick = gameApi.getCurrentTick();
|
|
59
|
+
scouts.forEach((unit) => {
|
|
60
|
+
if (this.scoutTarget) {
|
|
61
|
+
actionsApi.orderUnits([unit.id], OrderType.AttackMove, this.scoutTarget.x, this.scoutTarget.y);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
// Check that a scout is actually moving closer to the target.
|
|
65
|
+
const distances = scouts.map((unit) => getDistanceBetweenTileAndPoint(unit.tile, this.scoutTarget));
|
|
66
|
+
const newMinDistance = Math.min(...distances);
|
|
67
|
+
if (!this.scoutMinDistance || newMinDistance < this.scoutMinDistance) {
|
|
68
|
+
this.logger(`Scout timeout refreshed because unit moved closer to point (${newMinDistance} < ${this.scoutMinDistance})`);
|
|
69
|
+
this.scoutTargetRefreshedAt = gameApi.getCurrentTick();
|
|
70
|
+
this.scoutMinDistance = newMinDistance;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (gameApi.mapApi.isVisibleTile(targetTile, playerData.name)) {
|
|
74
|
+
this.logger(`Scout target ${this.scoutTarget.x},${this.scoutTarget.y} successfully scouted, moving to next`);
|
|
75
|
+
this.setScoutTarget(null, gameApi.getCurrentTick());
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
const nextScoutTarget = matchAwareness.getScoutingManager().getNewScoutTarget();
|
|
80
|
+
if (!nextScoutTarget) {
|
|
81
|
+
this.logger(`No more scouting targets available, disbanding.`);
|
|
82
|
+
return disbandMission();
|
|
83
|
+
}
|
|
84
|
+
this.setScoutTarget(nextScoutTarget, gameApi.getCurrentTick());
|
|
85
|
+
}
|
|
86
|
+
return noop();
|
|
87
|
+
}
|
|
88
|
+
setScoutTarget(target, currentTick) {
|
|
89
|
+
this.attemptsOnCurrentTarget = 0;
|
|
90
|
+
this.scoutTargetRefreshedAt = currentTick;
|
|
91
|
+
this.scoutTarget = target?.asVector2() ?? null;
|
|
92
|
+
this.scoutMinDistance = undefined;
|
|
93
|
+
this.scoutTargetIsPermanent = target?.isPermanent ?? false;
|
|
94
|
+
}
|
|
95
|
+
getGlobalDebugText() {
|
|
96
|
+
return "scouting";
|
|
97
|
+
}
|
|
98
|
+
getPriority() {
|
|
99
|
+
return this.priority;
|
|
10
100
|
}
|
|
11
101
|
}
|
|
12
102
|
const SCOUT_COOLDOWN_TICKS = 300;
|
|
@@ -24,13 +114,20 @@ export class ScoutingMissionFactory {
|
|
|
24
114
|
if (!matchAwareness.getScoutingManager().hasScoutTargets()) {
|
|
25
115
|
return;
|
|
26
116
|
}
|
|
27
|
-
if (!missionController.addMission(new ScoutingMission("globalScout",
|
|
117
|
+
if (!missionController.addMission(new ScoutingMission("globalScout", 10, logger))) {
|
|
28
118
|
this.lastScoutAt = gameApi.getCurrentTick();
|
|
29
119
|
}
|
|
30
120
|
}
|
|
31
121
|
onMissionFailed(gameApi, playerData, matchAwareness, failedMission, failureReason, missionController, logger) {
|
|
122
|
+
if (gameApi.getCurrentTick() < this.lastScoutAt + SCOUT_COOLDOWN_TICKS) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (!matchAwareness.getScoutingManager().hasScoutTargets()) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
32
128
|
if (failedMission instanceof AttackMission) {
|
|
33
|
-
missionController.addMission(new ScoutingMission("globalScout",
|
|
129
|
+
missionController.addMission(new ScoutingMission("globalScout", 10, logger));
|
|
130
|
+
this.lastScoutAt = gameApi.getCurrentTick();
|
|
34
131
|
}
|
|
35
132
|
}
|
|
36
133
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scoutingMission.js","sourceRoot":"","sources":["../../../../../src/bot/logic/mission/missions/scoutingMission.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"scoutingMission.js","sourceRoot":"","sources":["../../../../../src/bot/logic/mission/missions/scoutingMission.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { GameMath, MovementZone, } from "@chronodivide/game-api";
|
|
2
|
+
import { getAttackWeight, manageAttackMicro, manageMoveMicro } from "./common.js";
|
|
3
|
+
import { isOwnedByNeutral, maxBy, minBy } from "../../../common/utils.js";
|
|
4
|
+
import { noop } from "../../mission.js";
|
|
5
|
+
const TARGET_UPDATE_INTERVAL_TICKS = 10;
|
|
6
|
+
// Units must be in a certain radius of the center of mass before attacking.
|
|
7
|
+
// This scales for number of units in the squad though.
|
|
8
|
+
const MIN_GATHER_RADIUS = 5;
|
|
9
|
+
// If the radius expands beyond this amount then we should switch back to gathering mode.
|
|
10
|
+
const MAX_GATHER_RADIUS = 15;
|
|
11
|
+
const GATHER_RATIO = 10;
|
|
12
|
+
const ATTACK_SCAN_AREA = 15;
|
|
13
|
+
var SquadState;
|
|
14
|
+
(function (SquadState) {
|
|
15
|
+
SquadState[SquadState["Gathering"] = 0] = "Gathering";
|
|
16
|
+
SquadState[SquadState["Attacking"] = 1] = "Attacking";
|
|
17
|
+
})(SquadState || (SquadState = {}));
|
|
18
|
+
export class CombatSquad {
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @param rallyArea the initial location to grab combatants
|
|
22
|
+
* @param targetArea
|
|
23
|
+
* @param radius
|
|
24
|
+
*/
|
|
25
|
+
constructor(rallyArea, targetArea, radius) {
|
|
26
|
+
this.rallyArea = rallyArea;
|
|
27
|
+
this.targetArea = targetArea;
|
|
28
|
+
this.radius = radius;
|
|
29
|
+
this.lastCommand = null;
|
|
30
|
+
this.state = SquadState.Gathering;
|
|
31
|
+
this.lastOrderGiven = {};
|
|
32
|
+
}
|
|
33
|
+
getGlobalDebugText() {
|
|
34
|
+
return this.debugLastTarget ?? "<none>";
|
|
35
|
+
}
|
|
36
|
+
setAttackArea(targetArea) {
|
|
37
|
+
this.targetArea = targetArea;
|
|
38
|
+
}
|
|
39
|
+
onAiUpdate(gameApi, actionsApi, actionBatcher, playerData, mission, matchAwareness, logger) {
|
|
40
|
+
if (mission.getUnitIds().length > 0 &&
|
|
41
|
+
(!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS)) {
|
|
42
|
+
this.lastCommand = gameApi.getCurrentTick();
|
|
43
|
+
const centerOfMass = mission.getCenterOfMass();
|
|
44
|
+
const maxDistance = mission.getMaxDistanceToCenterOfMass();
|
|
45
|
+
const units = mission.getUnitsMatching(gameApi, (r) => r.rules.isSelectableCombatant);
|
|
46
|
+
// Only use ground units for center of mass.
|
|
47
|
+
const groundUnits = mission.getUnitsMatching(gameApi, (r) => r.rules.isSelectableCombatant &&
|
|
48
|
+
(r.rules.movementZone === MovementZone.Infantry ||
|
|
49
|
+
r.rules.movementZone === MovementZone.Normal ||
|
|
50
|
+
r.rules.movementZone === MovementZone.InfantryDestroyer));
|
|
51
|
+
if (this.state === SquadState.Gathering) {
|
|
52
|
+
const requiredGatherRadius = GameMath.sqrt(groundUnits.length) * GATHER_RATIO + MIN_GATHER_RADIUS;
|
|
53
|
+
if (centerOfMass &&
|
|
54
|
+
maxDistance &&
|
|
55
|
+
gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
|
|
56
|
+
maxDistance > requiredGatherRadius) {
|
|
57
|
+
units.forEach((unit) => {
|
|
58
|
+
this.submitActionIfNew(actionBatcher, manageMoveMicro(unit, centerOfMass));
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
logger(`CombatSquad ${mission.getUniqueName()} switching back to attack mode (${maxDistance})`);
|
|
63
|
+
this.state = SquadState.Attacking;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
const targetPoint = this.targetArea || playerData.startLocation;
|
|
68
|
+
const requiredGatherRadius = GameMath.sqrt(groundUnits.length) * GATHER_RATIO + MAX_GATHER_RADIUS;
|
|
69
|
+
if (centerOfMass &&
|
|
70
|
+
maxDistance &&
|
|
71
|
+
gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
|
|
72
|
+
maxDistance > requiredGatherRadius) {
|
|
73
|
+
// Switch back to gather mode
|
|
74
|
+
logger(`CombatSquad ${mission.getUniqueName()} switching back to gather (${maxDistance})`);
|
|
75
|
+
this.state = SquadState.Gathering;
|
|
76
|
+
return noop();
|
|
77
|
+
}
|
|
78
|
+
// The unit with the shortest range chooses the target. Otherwise, a base range of 5 is chosen.
|
|
79
|
+
const getRangeForUnit = (unit) => unit.primaryWeapon?.maxRange ?? unit.secondaryWeapon?.maxRange ?? 5;
|
|
80
|
+
const attackLeader = minBy(units, getRangeForUnit);
|
|
81
|
+
if (!attackLeader) {
|
|
82
|
+
return noop();
|
|
83
|
+
}
|
|
84
|
+
// Find units within double the range of the leader.
|
|
85
|
+
const nearbyHostiles = matchAwareness
|
|
86
|
+
.getHostilesNearPoint(attackLeader.tile.rx, attackLeader.tile.ry, ATTACK_SCAN_AREA)
|
|
87
|
+
.map(({ unitId }) => gameApi.getUnitData(unitId))
|
|
88
|
+
.filter((unit) => !isOwnedByNeutral(unit));
|
|
89
|
+
for (const unit of units) {
|
|
90
|
+
const bestUnit = maxBy(nearbyHostiles, (target) => getAttackWeight(unit, target));
|
|
91
|
+
if (bestUnit) {
|
|
92
|
+
this.submitActionIfNew(actionBatcher, manageAttackMicro(unit, bestUnit));
|
|
93
|
+
this.debugLastTarget = `Unit ${bestUnit.id.toString()}`;
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.submitActionIfNew(actionBatcher, manageMoveMicro(unit, targetPoint));
|
|
97
|
+
this.debugLastTarget = `@${targetPoint.x},${targetPoint.y}`;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return noop();
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Sends an action to the acitonBatcher if and only if the action is different from the last action we submitted to it.
|
|
106
|
+
* Prevents spamming redundant orders, which affects performance and can also ccause the unit to sit around doing nothing.
|
|
107
|
+
*/
|
|
108
|
+
submitActionIfNew(actionBatcher, action) {
|
|
109
|
+
const lastAction = this.lastOrderGiven[action.unitId];
|
|
110
|
+
if (!lastAction || !lastAction.isSameAs(action)) {
|
|
111
|
+
actionBatcher.push(action);
|
|
112
|
+
this.lastOrderGiven[action.unitId] = action;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=combatSquad.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"combatSquad.js","sourceRoot":"","sources":["../../../../../../src/bot/logic/mission/missions/squads/combatSquad.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { AttackState, ObjectType, OrderType, StanceType, Vector2, ZoneType } from "@chronodivide/game-api";
|
|
2
|
+
import { getDistanceBetweenPoints, getDistanceBetweenUnits } from "../../../map/map.js";
|
|
3
|
+
import { BatchableAction } from "../../actionBatcher.js";
|
|
4
|
+
const NONCE_GI_DEPLOY = 0;
|
|
5
|
+
const NONCE_GI_UNDEPLOY = 1;
|
|
6
|
+
// Micro methods
|
|
7
|
+
export function manageMoveMicro(attacker, attackPoint) {
|
|
8
|
+
if (attacker.name === "E1") {
|
|
9
|
+
const isDeployed = attacker.stance === StanceType.Deployed;
|
|
10
|
+
if (isDeployed) {
|
|
11
|
+
return BatchableAction.noTarget(attacker.id, OrderType.DeploySelected, NONCE_GI_UNDEPLOY);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return BatchableAction.toPoint(attacker.id, OrderType.AttackMove, attackPoint);
|
|
15
|
+
}
|
|
16
|
+
export function manageAttackMicro(attacker, target) {
|
|
17
|
+
const distance = getDistanceBetweenUnits(attacker, target);
|
|
18
|
+
if (attacker.name === "E1") {
|
|
19
|
+
// Para (deployed weapon) range is 5.
|
|
20
|
+
const deployedWeaponRange = attacker.secondaryWeapon?.maxRange || 5;
|
|
21
|
+
const isDeployed = attacker.stance === StanceType.Deployed;
|
|
22
|
+
if (!isDeployed && (distance <= deployedWeaponRange || attacker.attackState === AttackState.JustFired)) {
|
|
23
|
+
return BatchableAction.noTarget(attacker.id, OrderType.DeploySelected, NONCE_GI_DEPLOY);
|
|
24
|
+
}
|
|
25
|
+
else if (isDeployed && distance > deployedWeaponRange) {
|
|
26
|
+
return BatchableAction.noTarget(attacker.id, OrderType.DeploySelected, NONCE_GI_UNDEPLOY);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
let targetData = target;
|
|
30
|
+
let orderType = OrderType.Attack;
|
|
31
|
+
const primaryWeaponRange = attacker.primaryWeapon?.maxRange || 5;
|
|
32
|
+
if (targetData?.type == ObjectType.Building && distance < primaryWeaponRange * 0.8) {
|
|
33
|
+
orderType = OrderType.Attack;
|
|
34
|
+
}
|
|
35
|
+
else if (targetData?.rules.canDisguise) {
|
|
36
|
+
// Special case for mirage tank/spy as otherwise they just sit next to it.
|
|
37
|
+
orderType = OrderType.Attack;
|
|
38
|
+
}
|
|
39
|
+
return BatchableAction.toTargetId(attacker.id, orderType, target.id);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
*
|
|
43
|
+
* @param attacker
|
|
44
|
+
* @param target
|
|
45
|
+
* @returns A number describing the weight of the given target for the attacker, or null if it should not attack it.
|
|
46
|
+
*/
|
|
47
|
+
export function getAttackWeight(attacker, target) {
|
|
48
|
+
const { rx: x, ry: y } = attacker.tile;
|
|
49
|
+
const { rx: hX, ry: hY } = target.tile;
|
|
50
|
+
if (!attacker.primaryWeapon?.projectileRules.isAntiAir && target.zone === ZoneType.Air) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
if (!attacker.primaryWeapon?.projectileRules.isAntiGround && target.zone === ZoneType.Ground) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
return 1000000 - getDistanceBetweenPoints(new Vector2(x, y), new Vector2(hX, hY));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=common.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"common.js","sourceRoot":"","sources":["../../../../../../src/bot/logic/mission/missions/squads/common.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"squad.js","sourceRoot":"","sources":["../../../../../../src/bot/logic/mission/missions/squads/squad.ts"],"names":[],"mappings":""}
|
|
@@ -1,75 +1,45 @@
|
|
|
1
1
|
import _ from "lodash";
|
|
2
|
-
import {
|
|
2
|
+
import { ObjectType, OrderType, } from "@chronodivide/game-api";
|
|
3
3
|
import { grabCombatants, noop } from "../squadBehaviour.js";
|
|
4
|
-
import { getDistanceBetweenPoints } from "../../map/map.js";
|
|
5
|
-
|
|
6
|
-
const
|
|
4
|
+
import { getDistanceBetweenPoints, getDistanceBetweenUnits } from "../../map/map.js";
|
|
5
|
+
// If no enemies are seen in a circle IDLE_CHECK_RADIUS*radius for IDLE_COOLDOWN_TICKS ticks, the mission is disbanded.
|
|
6
|
+
const IDLE_CHECK_RADIUS_RATIO = 2;
|
|
7
|
+
const IDLE_COOLDOWN_TICKS = 15 * 30;
|
|
8
|
+
const TARGET_UPDATE_INTERVAL_TICKS = 4;
|
|
7
9
|
const GRAB_INTERVAL_TICKS = 10;
|
|
8
10
|
const GRAB_RADIUS = 30;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
const MIN_GATHER_RADIUS = 5;
|
|
12
|
-
const GATHER_RATIO = 10;
|
|
13
|
-
var SquadState;
|
|
14
|
-
(function (SquadState) {
|
|
15
|
-
SquadState[SquadState["Gathering"] = 0] = "Gathering";
|
|
16
|
-
SquadState[SquadState["Attacking"] = 1] = "Attacking";
|
|
17
|
-
})(SquadState || (SquadState = {}));
|
|
18
|
-
export class AttackOrDefenceSquad {
|
|
19
|
-
constructor(rallyArea, targetArea, radius) {
|
|
11
|
+
export class AttackSquad {
|
|
12
|
+
constructor(rallyArea, attackArea, radius) {
|
|
20
13
|
this.rallyArea = rallyArea;
|
|
21
|
-
this.
|
|
14
|
+
this.attackArea = attackArea;
|
|
22
15
|
this.radius = radius;
|
|
16
|
+
this.lastIdleCheck = null;
|
|
23
17
|
this.lastGrab = null;
|
|
24
18
|
this.lastCommand = null;
|
|
25
|
-
this.state = SquadState.Gathering;
|
|
26
19
|
}
|
|
27
|
-
setAttackArea(
|
|
28
|
-
this.
|
|
20
|
+
setAttackArea(attackArea) {
|
|
21
|
+
this.attackArea = attackArea;
|
|
29
22
|
}
|
|
30
23
|
onAiUpdate(gameApi, actionsApi, playerData, squad, matchAwareness) {
|
|
31
24
|
if (!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS) {
|
|
32
|
-
this.lastCommand = gameApi.getCurrentTick();
|
|
33
|
-
const centerOfMass = squad.getCenterOfMass();
|
|
34
|
-
const maxDistance = squad.getMaxDistanceToCenterOfMass();
|
|
35
25
|
const units = squad.getUnitsMatching(gameApi, (r) => r.rules.isSelectableCombatant);
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
manageMoveMicro(actionsApi,
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
else {
|
|
52
|
-
this.state = SquadState.Attacking;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const targetPoint = this.targetArea || playerData.startLocation;
|
|
57
|
-
for (const unit of units) {
|
|
58
|
-
if (unit.isIdle) {
|
|
59
|
-
const { rx: x, ry: y } = unit.tile;
|
|
60
|
-
const range = unit.primaryWeapon?.maxRange ?? unit.secondaryWeapon?.maxRange ?? 5;
|
|
61
|
-
const nearbyHostiles = matchAwareness.getHostilesNearPoint(x, y, range * 2);
|
|
62
|
-
const closest = _.minBy(nearbyHostiles, ({ x: hX, y: hY }) => getDistanceBetweenPoints({ x, y }, { x: hX, y: hY }));
|
|
63
|
-
const closestUnit = closest ? gameApi.getUnitData(closest.unitId) ?? null : null;
|
|
64
|
-
if (closestUnit) {
|
|
65
|
-
manageAttackMicro(actionsApi, unit, closestUnit);
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
manageMoveMicro(actionsApi, unit, targetPoint);
|
|
69
|
-
}
|
|
26
|
+
const attackPoint = this.attackArea || playerData.startLocation;
|
|
27
|
+
for (const attacker of units) {
|
|
28
|
+
if (attacker.isIdle) {
|
|
29
|
+
const { rx: x, ry: y } = attacker.tile;
|
|
30
|
+
const range = attacker.primaryWeapon?.maxRange ?? attacker.secondaryWeapon?.maxRange ?? 5;
|
|
31
|
+
const nearbyHostiles = matchAwareness.getHostilesInRadius2(x, y, range * 2);
|
|
32
|
+
const closest = _.minBy(nearbyHostiles, ({ x: hX, y: hY }) => getDistanceBetweenPoints({ x, y }, { x: hX, y: hY }));
|
|
33
|
+
const closestUnit = closest ? gameApi.getUnitData(closest.unitId) ?? null : null;
|
|
34
|
+
if (closestUnit) {
|
|
35
|
+
this.manageAttackMicro(actionsApi, attacker, closestUnit);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
this.manageMoveMicro(actionsApi, attacker, attackPoint);
|
|
70
39
|
}
|
|
71
40
|
}
|
|
72
41
|
}
|
|
42
|
+
this.lastCommand = gameApi.getCurrentTick();
|
|
73
43
|
}
|
|
74
44
|
if (!this.lastGrab || gameApi.getCurrentTick() > this.lastGrab + GRAB_INTERVAL_TICKS) {
|
|
75
45
|
this.lastGrab = gameApi.getCurrentTick();
|
|
@@ -79,4 +49,41 @@ export class AttackOrDefenceSquad {
|
|
|
79
49
|
return noop();
|
|
80
50
|
}
|
|
81
51
|
}
|
|
52
|
+
// Micro methods
|
|
53
|
+
manageMoveMicro(actionsApi, attacker, attackPoint) {
|
|
54
|
+
if (attacker.name === "E1") {
|
|
55
|
+
if (attacker.canMove === false) {
|
|
56
|
+
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
actionsApi.orderUnits([attacker.id], OrderType.AttackMove, attackPoint.x, attackPoint.y);
|
|
60
|
+
}
|
|
61
|
+
manageAttackMicro(actionsApi, attacker, target) {
|
|
62
|
+
const distance = getDistanceBetweenUnits(attacker, target);
|
|
63
|
+
if (attacker.name === "E1") {
|
|
64
|
+
// Para (deployed weapon) range is 5.
|
|
65
|
+
const deployedWeaponRange = attacker.secondaryWeapon?.maxRange || 5;
|
|
66
|
+
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
67
|
+
//console.dir({distance, attacker: attacker.name, canMove: attacker.canMove, primaryMaxRange: attacker.primaryWeapon?.maxRange, secondaryMaxRange: attacker.secondaryWeapon?.maxRange})
|
|
68
|
+
if (attacker.canMove && distance <= deployedWeaponRange * 0.8) {
|
|
69
|
+
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
else if (!attacker.canMove && distance > deployedWeaponRange) {
|
|
73
|
+
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
let targetData = target;
|
|
78
|
+
let orderType = OrderType.AttackMove;
|
|
79
|
+
const primaryWeaponRange = attacker.primaryWeapon?.maxRange || 5;
|
|
80
|
+
if (targetData?.type == ObjectType.Building && distance < primaryWeaponRange * 0.8) {
|
|
81
|
+
orderType = OrderType.Attack;
|
|
82
|
+
}
|
|
83
|
+
else if (targetData?.rules.canDisguise) {
|
|
84
|
+
// Special case for mirage tank/spy as otherwise they just sit next to it.
|
|
85
|
+
orderType = OrderType.Attack;
|
|
86
|
+
}
|
|
87
|
+
actionsApi.orderUnits([attacker.id], orderType, target.id);
|
|
88
|
+
}
|
|
82
89
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import _ from "lodash";
|
|
2
|
+
import { MovementZone } from "@chronodivide/game-api";
|
|
2
3
|
import { grabCombatants, noop } from "../squadBehaviour.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
4
|
+
import { getDistanceBetweenPoints } from "../../map/map.js";
|
|
5
|
+
import { manageAttackMicro, manageMoveMicro } from "./common.js";
|
|
5
6
|
const TARGET_UPDATE_INTERVAL_TICKS = 10;
|
|
6
7
|
const GRAB_INTERVAL_TICKS = 10;
|
|
7
8
|
const GRAB_RADIUS = 20;
|
|
@@ -31,15 +32,11 @@ export class CombatSquad {
|
|
|
31
32
|
this.lastCommand = null;
|
|
32
33
|
this.state = SquadState.Gathering;
|
|
33
34
|
}
|
|
34
|
-
getGlobalDebugText() {
|
|
35
|
-
return this.debugLastTarget ?? "<none>";
|
|
36
|
-
}
|
|
37
35
|
setAttackArea(targetArea) {
|
|
38
36
|
this.targetArea = targetArea;
|
|
39
37
|
}
|
|
40
|
-
onAiUpdate(gameApi, actionsApi,
|
|
41
|
-
if (squad.getUnitIds().length > 0 &&
|
|
42
|
-
(!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS)) {
|
|
38
|
+
onAiUpdate(gameApi, actionsApi, playerData, squad, matchAwareness, logger) {
|
|
39
|
+
if (squad.getUnitIds().length > 0 && (!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS)) {
|
|
43
40
|
this.lastCommand = gameApi.getCurrentTick();
|
|
44
41
|
const centerOfMass = squad.getCenterOfMass();
|
|
45
42
|
const maxDistance = squad.getMaxDistanceToCenterOfMass();
|
|
@@ -50,13 +47,13 @@ export class CombatSquad {
|
|
|
50
47
|
r.rules.movementZone === MovementZone.Normal ||
|
|
51
48
|
r.rules.movementZone === MovementZone.InfantryDestroyer));
|
|
52
49
|
if (this.state === SquadState.Gathering) {
|
|
53
|
-
const requiredGatherRadius =
|
|
50
|
+
const requiredGatherRadius = Math.sqrt(groundUnits.length) * GATHER_RATIO + MIN_GATHER_RADIUS;
|
|
54
51
|
if (centerOfMass &&
|
|
55
52
|
maxDistance &&
|
|
56
53
|
gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
|
|
57
54
|
maxDistance > requiredGatherRadius) {
|
|
58
55
|
units.forEach((unit) => {
|
|
59
|
-
|
|
56
|
+
manageMoveMicro(actionsApi, unit, centerOfMass);
|
|
60
57
|
});
|
|
61
58
|
}
|
|
62
59
|
else {
|
|
@@ -66,7 +63,7 @@ export class CombatSquad {
|
|
|
66
63
|
}
|
|
67
64
|
else {
|
|
68
65
|
const targetPoint = this.targetArea || playerData.startLocation;
|
|
69
|
-
const requiredGatherRadius =
|
|
66
|
+
const requiredGatherRadius = Math.sqrt(groundUnits.length) * GATHER_RATIO + MAX_GATHER_RADIUS;
|
|
70
67
|
if (centerOfMass &&
|
|
71
68
|
maxDistance &&
|
|
72
69
|
gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
|
|
@@ -77,19 +74,18 @@ export class CombatSquad {
|
|
|
77
74
|
return noop();
|
|
78
75
|
}
|
|
79
76
|
for (const unit of units) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
.getHostilesNearPoint(x, y, range * 2)
|
|
84
|
-
.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
this.debugLastTarget = `@${targetPoint.x},${targetPoint.y}`;
|
|
77
|
+
if (unit.isIdle) {
|
|
78
|
+
const { rx: x, ry: y } = unit.tile;
|
|
79
|
+
const range = unit.primaryWeapon?.maxRange ?? unit.secondaryWeapon?.maxRange ?? 5;
|
|
80
|
+
const nearbyHostiles = matchAwareness.getHostilesNearPoint(x, y, range * 2);
|
|
81
|
+
const closest = _.minBy(nearbyHostiles, ({ x: hX, y: hY }) => getDistanceBetweenPoints({ x, y }, { x: hX, y: hY }));
|
|
82
|
+
const closestUnit = closest ? gameApi.getUnitData(closest.unitId) ?? null : null;
|
|
83
|
+
if (closestUnit) {
|
|
84
|
+
manageAttackMicro(actionsApi, unit, closestUnit);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
manageMoveMicro(actionsApi, unit, targetPoint);
|
|
88
|
+
}
|
|
93
89
|
}
|
|
94
90
|
}
|
|
95
91
|
}
|