@supalosa/chronodivide-bot 0.2.0 → 0.2.2-a
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/README.md +11 -3
- package/dist/bot/bot.js +17 -9
- package/dist/bot/bot.js.map +1 -0
- package/dist/bot/logic/awareness.js +12 -2
- package/dist/bot/logic/awareness.js.map +1 -0
- package/dist/bot/logic/building/ArtilleryUnit.js +29 -8
- package/dist/bot/logic/building/antiGroundStaticDefence.js +3 -2
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -0
- package/dist/bot/logic/building/artilleryUnit.js.map +1 -0
- package/dist/bot/logic/building/basicAirUnit.js +2 -1
- package/dist/bot/logic/building/basicAirUnit.js.map +1 -0
- package/dist/bot/logic/building/basicBuilding.js +2 -1
- package/dist/bot/logic/building/basicBuilding.js.map +1 -0
- package/dist/bot/logic/building/basicGroundUnit.js +2 -1
- package/dist/bot/logic/building/basicGroundUnit.js.map +1 -0
- package/dist/bot/logic/building/building.js +2 -0
- package/dist/bot/logic/building/buildingRules.js +168 -0
- package/dist/bot/logic/building/buildingRules.js.map +1 -0
- package/dist/bot/logic/building/harvester.js +1 -0
- package/dist/bot/logic/building/harvester.js.map +1 -0
- package/dist/bot/logic/building/powerPlant.js +2 -1
- package/dist/bot/logic/building/powerPlant.js.map +1 -0
- package/dist/bot/logic/building/queueController.js +2 -1
- package/dist/bot/logic/building/queueController.js.map +1 -0
- package/dist/bot/logic/building/resourceCollectionBuilding.js +2 -1
- package/dist/bot/logic/building/resourceCollectionBuilding.js.map +1 -0
- package/dist/bot/logic/common/scout.js +100 -0
- package/dist/bot/logic/common/scout.js.map +1 -0
- package/dist/bot/logic/common/utils.js +14 -0
- package/dist/bot/logic/common/utils.js.map +1 -0
- package/dist/bot/logic/map/map.js +9 -25
- package/dist/bot/logic/map/map.js.map +1 -0
- package/dist/bot/logic/map/sector.js +33 -1
- package/dist/bot/logic/map/sector.js.map +1 -0
- package/dist/bot/logic/mission/mission.js +3 -1
- package/dist/bot/logic/mission/mission.js.map +1 -0
- package/dist/bot/logic/mission/missionController.js +5 -4
- package/dist/bot/logic/mission/missionController.js.map +1 -0
- package/dist/bot/logic/mission/missionFactories.js +3 -0
- package/dist/bot/logic/mission/missionFactories.js.map +1 -0
- package/dist/bot/logic/mission/missions/attackMission.js +9 -10
- package/dist/bot/logic/mission/missions/attackMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/defenceMission.js +12 -7
- package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/engineerMission.js +34 -0
- package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/expansionMission.js +5 -4
- package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/oneTimeMission.js +3 -2
- package/dist/bot/logic/mission/missions/oneTimeMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/retreatMission.js +3 -2
- package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -0
- package/dist/bot/logic/mission/missions/scoutingMission.js +8 -9
- package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/combatSquad.js +20 -18
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/common.js +25 -5
- package/dist/bot/logic/squad/behaviours/common.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/engineerSquad.js +36 -0
- package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +1 -0
- package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +6 -10
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -0
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +66 -18
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -0
- package/dist/bot/logic/squad/squad.js +4 -5
- package/dist/bot/logic/squad/squad.js.map +1 -0
- package/dist/bot/logic/squad/squadBehaviour.js +1 -0
- package/dist/bot/logic/squad/squadBehaviour.js.map +1 -0
- package/dist/bot/logic/squad/squadBehaviours.js +1 -0
- package/dist/bot/logic/squad/squadBehaviours.js.map +1 -0
- package/dist/bot/logic/squad/squadController.js +73 -23
- package/dist/bot/logic/squad/squadController.js.map +1 -0
- package/dist/bot/logic/threat/threat.js +1 -0
- package/dist/bot/logic/threat/threat.js.map +1 -0
- package/dist/bot/logic/threat/threatCalculator.js +1 -0
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -0
- package/dist/exampleBot.js +29 -12
- package/dist/exampleBot.js.map +1 -0
- package/package.json +15 -7
- package/src/bot/bot.ts +21 -16
- package/src/bot/logic/awareness.ts +22 -5
- package/src/bot/logic/building/antiGroundStaticDefence.ts +60 -60
- package/src/bot/logic/building/artilleryUnit.ts +68 -0
- package/src/bot/logic/building/basicAirUnit.ts +68 -68
- package/src/bot/logic/building/basicBuilding.ts +47 -47
- package/src/bot/logic/building/basicGroundUnit.ts +1 -1
- package/src/bot/logic/building/buildingRules.ts +233 -0
- package/src/bot/logic/building/powerPlant.ts +32 -32
- package/src/bot/logic/building/queueController.ts +1 -1
- package/src/bot/logic/building/resourceCollectionBuilding.ts +56 -56
- package/src/bot/logic/common/scout.ts +127 -1
- package/src/bot/logic/common/utils.ts +17 -0
- package/src/bot/logic/map/map.ts +70 -84
- package/src/bot/logic/map/sector.ts +46 -4
- package/src/bot/logic/mission/mission.ts +2 -2
- package/src/bot/logic/mission/missionController.ts +7 -6
- package/src/bot/logic/mission/missionFactories.ts +5 -0
- package/src/bot/logic/mission/missions/attackMission.ts +19 -12
- package/src/bot/logic/mission/missions/defenceMission.ts +35 -15
- package/src/bot/logic/mission/missions/engineerMission.ts +61 -0
- package/src/bot/logic/mission/missions/expansionMission.ts +6 -4
- package/src/bot/logic/mission/missions/oneTimeMission.ts +3 -2
- package/src/bot/logic/mission/missions/retreatMission.ts +3 -2
- package/src/bot/logic/mission/missions/scoutingMission.ts +9 -6
- package/src/bot/logic/squad/behaviours/combatSquad.ts +22 -18
- package/src/bot/logic/squad/behaviours/common.ts +38 -5
- package/src/bot/logic/squad/behaviours/engineerSquad.ts +53 -0
- package/src/bot/logic/squad/behaviours/retreatSquad.ts +10 -12
- package/src/bot/logic/squad/behaviours/scoutingSquad.ts +79 -26
- package/src/bot/logic/squad/squad.ts +4 -4
- package/src/bot/logic/squad/squadBehaviour.ts +3 -1
- package/src/bot/logic/squad/squadController.ts +135 -70
- package/src/exampleBot.ts +31 -12
- package/tsconfig.json +73 -73
- package/src/bot/logic/building/ArtilleryUnit.ts +0 -43
- package/src/bot/logic/building/building.ts +0 -125
|
@@ -1,16 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { OneTimeMission } from "./oneTimeMission.js";
|
|
1
|
+
import { GameApi, ObjectType, PlayerData, Point2D, UnitData } from "@chronodivide/game-api";
|
|
3
2
|
import { CombatSquad } from "../../squad/behaviours/combatSquad.js";
|
|
4
3
|
import { Mission, MissionAction, disbandMission, noop } from "../mission.js";
|
|
5
|
-
import { GlobalThreat } from "../../threat/threat.js";
|
|
6
4
|
import { Squad } from "../../squad/squad.js";
|
|
7
|
-
import { getDistanceBetweenPoints, getDistanceBetweenUnits } from "../../map/map.js";
|
|
8
5
|
import { MissionFactory } from "../missionFactories.js";
|
|
9
6
|
import { MatchAwareness } from "../../awareness.js";
|
|
10
7
|
import { MissionController } from "../missionController.js";
|
|
11
|
-
import { match } from "assert";
|
|
12
8
|
import { RetreatMission } from "./retreatMission.js";
|
|
13
|
-
import
|
|
9
|
+
import maxBy from "lodash.maxby";
|
|
10
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
14
11
|
|
|
15
12
|
export enum AttackFailReason {
|
|
16
13
|
NoTargets = 0,
|
|
@@ -31,8 +28,9 @@ export class AttackMission extends Mission<AttackFailReason> {
|
|
|
31
28
|
private rallyArea: Point2D,
|
|
32
29
|
private attackArea: Point2D,
|
|
33
30
|
private radius: number,
|
|
31
|
+
logger: DebugLogger,
|
|
34
32
|
) {
|
|
35
|
-
super(uniqueName, priority);
|
|
33
|
+
super(uniqueName, priority, logger);
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
onAiUpdate(gameApi: GameApi, playerData: PlayerData, matchAwareness: MatchAwareness): MissionAction {
|
|
@@ -88,7 +86,7 @@ export class AttackMissionFactory implements MissionFactory {
|
|
|
88
86
|
.map((unitId) => gameApi.getUnitData(unitId))
|
|
89
87
|
.filter((u) => !!u && gameApi.getPlayerData(u.owner).isCombatant) as UnitData[];
|
|
90
88
|
|
|
91
|
-
const maxUnit =
|
|
89
|
+
const maxUnit = maxBy(enemyUnits, (u) => getTargetWeight(u, tryFocusHarvester));
|
|
92
90
|
if (maxUnit) {
|
|
93
91
|
return { x: maxUnit.tile.rx, y: maxUnit.tile.ry };
|
|
94
92
|
}
|
|
@@ -104,6 +102,7 @@ export class AttackMissionFactory implements MissionFactory {
|
|
|
104
102
|
playerData: PlayerData,
|
|
105
103
|
matchAwareness: MatchAwareness,
|
|
106
104
|
missionController: MissionController,
|
|
105
|
+
logger: DebugLogger,
|
|
107
106
|
): void {
|
|
108
107
|
if (!matchAwareness.shouldAttack()) {
|
|
109
108
|
return;
|
|
@@ -124,18 +123,26 @@ export class AttackMissionFactory implements MissionFactory {
|
|
|
124
123
|
// TODO: not using a fixed value here. But performance slows to a crawl when this is unique.
|
|
125
124
|
const squadName = "globalAttack";
|
|
126
125
|
|
|
127
|
-
const tryAttack = missionController
|
|
128
|
-
|
|
129
|
-
|
|
126
|
+
const tryAttack = missionController.addMission(
|
|
127
|
+
new AttackMission(
|
|
128
|
+
squadName,
|
|
129
|
+
100,
|
|
130
|
+
matchAwareness.getMainRallyPoint(),
|
|
131
|
+
attackArea,
|
|
132
|
+
attackRadius,
|
|
133
|
+
logger,
|
|
134
|
+
).then((reason, squad) => {
|
|
130
135
|
missionController.addMission(
|
|
131
136
|
new RetreatMission(
|
|
132
137
|
"retreat-from-" + squadName + gameApi.getCurrentTick(),
|
|
133
138
|
100,
|
|
134
139
|
matchAwareness.getMainRallyPoint(),
|
|
135
140
|
squad?.getUnitIds() ?? [],
|
|
141
|
+
logger,
|
|
136
142
|
),
|
|
137
143
|
);
|
|
138
|
-
})
|
|
144
|
+
}),
|
|
145
|
+
);
|
|
139
146
|
if (tryAttack) {
|
|
140
147
|
this.lastAttackAt = gameApi.getCurrentTick();
|
|
141
148
|
}
|
|
@@ -6,6 +6,7 @@ import { MissionFactory } from "../missionFactories.js";
|
|
|
6
6
|
import { Squad } from "../../squad/squad.js";
|
|
7
7
|
import { CombatSquad } from "../../squad/behaviours/combatSquad.js";
|
|
8
8
|
import { RetreatMission } from "./retreatMission.js";
|
|
9
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
9
10
|
|
|
10
11
|
export enum DefenceFailReason {
|
|
11
12
|
NoTargets,
|
|
@@ -22,8 +23,9 @@ export class DefenceMission extends Mission<DefenceFailReason> {
|
|
|
22
23
|
priority: number,
|
|
23
24
|
private defenceArea: Point2D,
|
|
24
25
|
private radius: number,
|
|
26
|
+
logger: DebugLogger,
|
|
25
27
|
) {
|
|
26
|
-
super(uniqueName, priority);
|
|
28
|
+
super(uniqueName, priority, logger);
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
onAiUpdate(gameApi: GameApi, playerData: PlayerData, matchAwareness: MatchAwareness): MissionAction {
|
|
@@ -35,8 +37,15 @@ export class DefenceMission extends Mission<DefenceFailReason> {
|
|
|
35
37
|
const foundTargets = matchAwareness.getHostilesNearPoint2d(this.defenceArea, this.radius);
|
|
36
38
|
|
|
37
39
|
if (foundTargets.length === 0) {
|
|
40
|
+
this.logger(`(Defence Mission ${this.getUniqueName()}): No targets found, disbanding.`);
|
|
38
41
|
return disbandMission(DefenceFailReason.NoTargets);
|
|
39
42
|
} else {
|
|
43
|
+
const targetUnit = gameApi.getUnitData(foundTargets[0].unitId);
|
|
44
|
+
this.logger(
|
|
45
|
+
`(Defence Mission ${this.getUniqueName()}): Focused on target ${targetUnit?.name} (${
|
|
46
|
+
foundTargets.length
|
|
47
|
+
} found in area ${this.radius})`,
|
|
48
|
+
);
|
|
40
49
|
this.combatSquad?.setAttackArea({ x: foundTargets[0].x, y: foundTargets[0].y });
|
|
41
50
|
}
|
|
42
51
|
}
|
|
@@ -47,9 +56,9 @@ export class DefenceMission extends Mission<DefenceFailReason> {
|
|
|
47
56
|
const DEFENCE_CHECK_TICKS = 30;
|
|
48
57
|
|
|
49
58
|
// Starting radius around the player's base to trigger defense.
|
|
50
|
-
const DEFENCE_STARTING_RADIUS =
|
|
59
|
+
const DEFENCE_STARTING_RADIUS = 10;
|
|
51
60
|
// Every game tick, we increase the defendable area by this amount.
|
|
52
|
-
const DEFENCE_RADIUS_INCREASE_PER_GAME_TICK = 0.
|
|
61
|
+
const DEFENCE_RADIUS_INCREASE_PER_GAME_TICK = 0.001;
|
|
53
62
|
|
|
54
63
|
export class DefenceMissionFactory implements MissionFactory {
|
|
55
64
|
private lastDefenceCheckAt = 0;
|
|
@@ -65,6 +74,7 @@ export class DefenceMissionFactory implements MissionFactory {
|
|
|
65
74
|
playerData: PlayerData,
|
|
66
75
|
matchAwareness: MatchAwareness,
|
|
67
76
|
missionController: MissionController,
|
|
77
|
+
logger: DebugLogger,
|
|
68
78
|
): void {
|
|
69
79
|
if (gameApi.getCurrentTick() < this.lastDefenceCheckAt + DEFENCE_CHECK_TICKS) {
|
|
70
80
|
return;
|
|
@@ -76,19 +86,29 @@ export class DefenceMissionFactory implements MissionFactory {
|
|
|
76
86
|
const enemiesNearSpawn = matchAwareness.getHostilesNearPoint2d(playerData.startLocation, defendableRadius);
|
|
77
87
|
|
|
78
88
|
if (enemiesNearSpawn.length > 0) {
|
|
89
|
+
logger(
|
|
90
|
+
`Starting defence mission, ${
|
|
91
|
+
enemiesNearSpawn.length
|
|
92
|
+
} found in radius ${defendableRadius} (tick ${gameApi.getCurrentTick()})`,
|
|
93
|
+
);
|
|
79
94
|
missionController.addMission(
|
|
80
|
-
new DefenceMission(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
95
|
+
new DefenceMission(
|
|
96
|
+
"globalDefence",
|
|
97
|
+
1000,
|
|
98
|
+
playerData.startLocation,
|
|
99
|
+
defendableRadius * 1.2,
|
|
100
|
+
logger,
|
|
101
|
+
).then((reason, squad) => {
|
|
102
|
+
missionController.addMission(
|
|
103
|
+
new RetreatMission(
|
|
104
|
+
"retreat-from-globalDefence" + gameApi.getCurrentTick(),
|
|
105
|
+
100,
|
|
106
|
+
matchAwareness.getMainRallyPoint(),
|
|
107
|
+
squad?.getUnitIds() ?? [],
|
|
108
|
+
logger,
|
|
109
|
+
),
|
|
110
|
+
);
|
|
111
|
+
}),
|
|
92
112
|
);
|
|
93
113
|
}
|
|
94
114
|
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { GameApi, PlayerData } from "@chronodivide/game-api";
|
|
2
|
+
import { GlobalThreat } from "../../threat/threat.js";
|
|
3
|
+
import { Mission } from "../mission.js";
|
|
4
|
+
import { ExpansionSquad } from "../../squad/behaviours/expansionSquad.js";
|
|
5
|
+
import { MissionFactory } from "../missionFactories.js";
|
|
6
|
+
import { OneTimeMission } from "./oneTimeMission.js";
|
|
7
|
+
import { MatchAwareness } from "../../awareness.js";
|
|
8
|
+
import { MissionController } from "../missionController.js";
|
|
9
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
10
|
+
import { EngineerSquad } from "../../squad/behaviours/engineerSquad.js";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* A mission that tries to send an engineer into a building (e.g. to capture tech building or repair bridge)
|
|
14
|
+
*/
|
|
15
|
+
export class EngineerMission extends OneTimeMission {
|
|
16
|
+
constructor(uniqueName: string, priority: number, selectedTechBuilding: number,
|
|
17
|
+
logger: DebugLogger) {
|
|
18
|
+
super(uniqueName, priority, () => new EngineerSquad(selectedTechBuilding), logger);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Only try to capture tech buildings within this radius of the starting point.
|
|
23
|
+
const MAX_TECH_CAPTURE_RADIUS = 50;
|
|
24
|
+
|
|
25
|
+
const TECH_CHECK_INTERVAL_TICKS = 300;
|
|
26
|
+
|
|
27
|
+
export class EngineerMissionFactory implements MissionFactory {
|
|
28
|
+
private lastCheckAt = 0;
|
|
29
|
+
|
|
30
|
+
getName(): string {
|
|
31
|
+
return "EngineerMissionFactory";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
maybeCreateMissions(
|
|
35
|
+
gameApi: GameApi,
|
|
36
|
+
playerData: PlayerData,
|
|
37
|
+
matchAwareness: MatchAwareness,
|
|
38
|
+
missionController: MissionController,
|
|
39
|
+
logger: DebugLogger
|
|
40
|
+
): void {
|
|
41
|
+
if (!(gameApi.getCurrentTick() > this.lastCheckAt + TECH_CHECK_INTERVAL_TICKS)) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.lastCheckAt = gameApi.getCurrentTick();
|
|
45
|
+
const eligibleTechBuildings = gameApi.getVisibleUnits(playerData.name, "hostile", (r) => r.capturable && r.produceCashAmount > 0);
|
|
46
|
+
|
|
47
|
+
eligibleTechBuildings.forEach((techBuildingId) => {
|
|
48
|
+
missionController.addMission(new EngineerMission("capture-" + techBuildingId, 100, techBuildingId, logger));
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onMissionFailed(
|
|
53
|
+
gameApi: GameApi,
|
|
54
|
+
playerData: PlayerData,
|
|
55
|
+
matchAwareness: MatchAwareness,
|
|
56
|
+
failedMission: Mission,
|
|
57
|
+
failureReason: undefined,
|
|
58
|
+
missionController: MissionController,
|
|
59
|
+
): void {
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -5,15 +5,16 @@ import { ExpansionSquad } from "../../squad/behaviours/expansionSquad.js";
|
|
|
5
5
|
import { MissionFactory } from "../missionFactories.js";
|
|
6
6
|
import { OneTimeMission } from "./oneTimeMission.js";
|
|
7
7
|
import { MatchAwareness } from "../../awareness.js";
|
|
8
|
-
import { AttackFailReason, AttackMission } from "./attackMission.js";
|
|
9
8
|
import { MissionController } from "../missionController.js";
|
|
9
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
10
10
|
|
|
11
11
|
/**
|
|
12
12
|
* A mission that tries to create an MCV (if it doesn't exist) and deploy it somewhere it can be deployed.
|
|
13
13
|
*/
|
|
14
14
|
export class ExpansionMission extends OneTimeMission {
|
|
15
|
-
constructor(uniqueName: string, priority: number, selectedMcv: number | null
|
|
16
|
-
|
|
15
|
+
constructor(uniqueName: string, priority: number, selectedMcv: number | null,
|
|
16
|
+
logger: DebugLogger) {
|
|
17
|
+
super(uniqueName, priority, () => new ExpansionSquad(selectedMcv), logger);
|
|
17
18
|
}
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -27,13 +28,14 @@ export class ExpansionMissionFactory implements MissionFactory {
|
|
|
27
28
|
playerData: PlayerData,
|
|
28
29
|
matchAwareness: MatchAwareness,
|
|
29
30
|
missionController: MissionController,
|
|
31
|
+
logger: DebugLogger
|
|
30
32
|
): void {
|
|
31
33
|
// At this point, only expand if we have a loose MCV.
|
|
32
34
|
const mcvs = gameApi.getVisibleUnits(playerData.name, "self", (r) =>
|
|
33
35
|
gameApi.getGeneralRules().baseUnit.includes(r.name)
|
|
34
36
|
);
|
|
35
37
|
mcvs.forEach((mcv) => {
|
|
36
|
-
missionController.addMission(new ExpansionMission("expand-with-" + mcv, 100, mcv));
|
|
38
|
+
missionController.addMission(new ExpansionMission("expand-with-" + mcv, 100, mcv, logger));
|
|
37
39
|
});
|
|
38
40
|
}
|
|
39
41
|
|
|
@@ -6,6 +6,7 @@ import { Squad } from "../../squad/squad.js";
|
|
|
6
6
|
import { MissionFactory } from "../missionFactories.js";
|
|
7
7
|
import { SquadBehaviour } from "../../squad/squadBehaviour.js";
|
|
8
8
|
import { MatchAwareness } from "../../awareness.js";
|
|
9
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* A mission that gets dispatched once, and once the squad decides to disband, the mission is disbanded.
|
|
@@ -13,8 +14,8 @@ import { MatchAwareness } from "../../awareness.js";
|
|
|
13
14
|
export abstract class OneTimeMission<T = undefined> extends Mission<T> {
|
|
14
15
|
private hadSquad = false;
|
|
15
16
|
|
|
16
|
-
constructor(uniqueName: string, priority: number, private behaviourFactory: () => SquadBehaviour) {
|
|
17
|
-
super(uniqueName, priority);
|
|
17
|
+
constructor(uniqueName: string, priority: number, private behaviourFactory: () => SquadBehaviour, logger: DebugLogger) {
|
|
18
|
+
super(uniqueName, priority, logger);
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
onAiUpdate(gameApi: GameApi, playerData: PlayerData, matchAwareness: MatchAwareness): MissionAction {
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { Point2D } from "@chronodivide/game-api";
|
|
2
2
|
import { OneTimeMission } from "./oneTimeMission.js";
|
|
3
3
|
import { RetreatSquad } from "../../squad/behaviours/retreatSquad.js";
|
|
4
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
4
5
|
|
|
5
6
|
export class RetreatMission extends OneTimeMission {
|
|
6
|
-
constructor(uniqueName: string, priority: number, retreatToPoint: Point2D, unitIds: number[]) {
|
|
7
|
-
super(uniqueName, priority, () => new RetreatSquad(unitIds, retreatToPoint));
|
|
7
|
+
constructor(uniqueName: string, priority: number, retreatToPoint: Point2D, unitIds: number[], logger: DebugLogger) {
|
|
8
|
+
super(uniqueName, priority, () => new RetreatSquad(unitIds, retreatToPoint), logger);
|
|
8
9
|
}
|
|
9
10
|
}
|
|
@@ -7,13 +7,15 @@ import { Mission } from "../mission.js";
|
|
|
7
7
|
import { AttackMission } from "./attackMission.js";
|
|
8
8
|
import { MissionController } from "../missionController.js";
|
|
9
9
|
import { getUnseenStartingLocations } from "../../common/scout.js";
|
|
10
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* A mission that tries to scout around the map with a cheap, fast unit (usually attack dogs)
|
|
13
14
|
*/
|
|
14
15
|
export class ScoutingMission extends OneTimeMission {
|
|
15
|
-
constructor(uniqueName: string, priority: number
|
|
16
|
-
|
|
16
|
+
constructor(uniqueName: string, priority: number,
|
|
17
|
+
logger: DebugLogger) {
|
|
18
|
+
super(uniqueName, priority, () => new ScoutingSquad(), logger);
|
|
17
19
|
}
|
|
18
20
|
}
|
|
19
21
|
|
|
@@ -31,15 +33,15 @@ export class ScoutingMissionFactory implements MissionFactory {
|
|
|
31
33
|
playerData: PlayerData,
|
|
32
34
|
matchAwareness: MatchAwareness,
|
|
33
35
|
missionController: MissionController,
|
|
36
|
+
logger: DebugLogger
|
|
34
37
|
): void {
|
|
35
38
|
if (gameApi.getCurrentTick() < this.lastScoutAt + SCOUT_COOLDOWN_TICKS) {
|
|
36
39
|
return;
|
|
37
40
|
}
|
|
38
|
-
|
|
39
|
-
if (candidatePoints.length === 0) {
|
|
41
|
+
if (!matchAwareness.getScoutingManager().hasScoutTargets()) {
|
|
40
42
|
return;
|
|
41
43
|
}
|
|
42
|
-
if (!missionController.addMission(new ScoutingMission("globalScout", 100))) {
|
|
44
|
+
if (!missionController.addMission(new ScoutingMission("globalScout", 100, logger))) {
|
|
43
45
|
this.lastScoutAt = gameApi.getCurrentTick();
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -51,9 +53,10 @@ export class ScoutingMissionFactory implements MissionFactory {
|
|
|
51
53
|
failedMission: Mission,
|
|
52
54
|
failureReason: undefined,
|
|
53
55
|
missionController: MissionController,
|
|
56
|
+
logger: DebugLogger
|
|
54
57
|
): void {
|
|
55
58
|
if (failedMission instanceof AttackMission) {
|
|
56
|
-
missionController.addMission(new ScoutingMission("globalScout", 100));
|
|
59
|
+
missionController.addMission(new ScoutingMission("globalScout", 100, logger));
|
|
57
60
|
}
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { ActionsApi, GameApi, MovementZone, PlayerData, Point2D } from "@chronodivide/game-api";
|
|
1
|
+
import maxBy from "lodash.maxby";
|
|
2
|
+
import { ActionsApi, GameApi, MovementZone, PlayerData, Point2D, UnitData } from "@chronodivide/game-api";
|
|
3
3
|
import { Squad } from "../squad.js";
|
|
4
4
|
import { SquadAction, SquadBehaviour, grabCombatants, noop } from "../squadBehaviour.js";
|
|
5
5
|
import { MatchAwareness } from "../../awareness.js";
|
|
6
6
|
import { getDistanceBetweenPoints } from "../../map/map.js";
|
|
7
|
-
import { manageAttackMicro, manageMoveMicro } from "./common.js";
|
|
7
|
+
import { getAttackWeight, manageAttackMicro, manageMoveMicro } from "./common.js";
|
|
8
|
+
import { DebugLogger } from "../../common/utils.js";
|
|
8
9
|
|
|
9
10
|
const TARGET_UPDATE_INTERVAL_TICKS = 10;
|
|
10
11
|
const GRAB_INTERVAL_TICKS = 10;
|
|
@@ -52,8 +53,12 @@ export class CombatSquad implements SquadBehaviour {
|
|
|
52
53
|
playerData: PlayerData,
|
|
53
54
|
squad: Squad,
|
|
54
55
|
matchAwareness: MatchAwareness,
|
|
56
|
+
logger: DebugLogger,
|
|
55
57
|
): SquadAction {
|
|
56
|
-
if (
|
|
58
|
+
if (
|
|
59
|
+
squad.getUnitIds().length > 0 &&
|
|
60
|
+
(!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS)
|
|
61
|
+
) {
|
|
57
62
|
this.lastCommand = gameApi.getCurrentTick();
|
|
58
63
|
const centerOfMass = squad.getCenterOfMass();
|
|
59
64
|
const maxDistance = squad.getMaxDistanceToCenterOfMass();
|
|
@@ -81,6 +86,7 @@ export class CombatSquad implements SquadBehaviour {
|
|
|
81
86
|
manageMoveMicro(actionsApi, unit, centerOfMass);
|
|
82
87
|
});
|
|
83
88
|
} else {
|
|
89
|
+
logger(`CombatSquad ${squad.getName()} switching back to attack mode (${maxDistance})`);
|
|
84
90
|
this.state = SquadState.Attacking;
|
|
85
91
|
}
|
|
86
92
|
} else {
|
|
@@ -92,24 +98,22 @@ export class CombatSquad implements SquadBehaviour {
|
|
|
92
98
|
gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
|
|
93
99
|
maxDistance > requiredGatherRadius
|
|
94
100
|
) {
|
|
95
|
-
// Switch back to gather mode
|
|
101
|
+
// Switch back to gather mode
|
|
102
|
+
logger(`CombatSquad ${squad.getName()} switching back to gather (${maxDistance})`);
|
|
96
103
|
this.state = SquadState.Gathering;
|
|
97
104
|
return noop();
|
|
98
105
|
}
|
|
99
106
|
for (const unit of units) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
} else {
|
|
111
|
-
manageMoveMicro(actionsApi, unit, targetPoint);
|
|
112
|
-
}
|
|
107
|
+
const { rx: x, ry: y } = unit.tile;
|
|
108
|
+
const range = unit.primaryWeapon?.maxRange ?? unit.secondaryWeapon?.maxRange ?? 5;
|
|
109
|
+
const nearbyHostiles = matchAwareness
|
|
110
|
+
.getHostilesNearPoint(x, y, range * 2)
|
|
111
|
+
.map(({ unitId }) => gameApi.getUnitData(unitId)) as UnitData[];
|
|
112
|
+
const bestUnit = maxBy(nearbyHostiles, (target) => getAttackWeight(unit, target));
|
|
113
|
+
if (bestUnit) {
|
|
114
|
+
manageAttackMicro(actionsApi, unit, bestUnit);
|
|
115
|
+
} else {
|
|
116
|
+
manageMoveMicro(actionsApi, unit, targetPoint);
|
|
113
117
|
}
|
|
114
118
|
}
|
|
115
119
|
}
|
|
@@ -1,10 +1,21 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
1
|
+
import {
|
|
2
|
+
ActionsApi,
|
|
3
|
+
AttackState,
|
|
4
|
+
ObjectType,
|
|
5
|
+
OrderType,
|
|
6
|
+
Point2D,
|
|
7
|
+
StanceType,
|
|
8
|
+
UnitData,
|
|
9
|
+
ZoneType,
|
|
10
|
+
} from "@chronodivide/game-api";
|
|
11
|
+
import { getDistanceBetweenPoints, getDistanceBetweenUnits } from "../../map/map.js";
|
|
12
|
+
import { Zone } from "luxon";
|
|
3
13
|
|
|
4
14
|
// Micro methods
|
|
5
15
|
export function manageMoveMicro(actionsApi: ActionsApi, attacker: UnitData, attackPoint: Point2D) {
|
|
6
16
|
if (attacker.name === "E1") {
|
|
7
|
-
|
|
17
|
+
const isDeployed = attacker.stance === StanceType.Deployed;
|
|
18
|
+
if (isDeployed) {
|
|
8
19
|
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
9
20
|
}
|
|
10
21
|
}
|
|
@@ -16,10 +27,11 @@ export function manageAttackMicro(actionsApi: ActionsApi, attacker: UnitData, ta
|
|
|
16
27
|
if (attacker.name === "E1") {
|
|
17
28
|
// Para (deployed weapon) range is 5.
|
|
18
29
|
const deployedWeaponRange = attacker.secondaryWeapon?.maxRange || 5;
|
|
19
|
-
|
|
30
|
+
const isDeployed = attacker.stance === StanceType.Deployed;
|
|
31
|
+
if (!isDeployed && (distance <= deployedWeaponRange || attacker.attackState === AttackState.JustFired)) {
|
|
20
32
|
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
21
33
|
return;
|
|
22
|
-
} else if (
|
|
34
|
+
} else if (isDeployed && distance > deployedWeaponRange) {
|
|
23
35
|
actionsApi.orderUnits([attacker.id], OrderType.DeploySelected);
|
|
24
36
|
return;
|
|
25
37
|
}
|
|
@@ -35,3 +47,24 @@ export function manageAttackMicro(actionsApi: ActionsApi, attacker: UnitData, ta
|
|
|
35
47
|
}
|
|
36
48
|
actionsApi.orderUnits([attacker.id], orderType, target.id);
|
|
37
49
|
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
*
|
|
53
|
+
* @param attacker
|
|
54
|
+
* @param target
|
|
55
|
+
* @returns A number describing the weight of the given target for the attacker, or null if it should not attack it.
|
|
56
|
+
*/
|
|
57
|
+
export function getAttackWeight(attacker: UnitData, target: UnitData): number | null {
|
|
58
|
+
const { rx: x, ry: y } = attacker.tile;
|
|
59
|
+
const { rx: hX, ry: hY } = target.tile;
|
|
60
|
+
|
|
61
|
+
if (!attacker.primaryWeapon?.projectileRules.isAntiAir && target.zone === ZoneType.Air) {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (!attacker.primaryWeapon?.projectileRules.isAntiGround && target.zone === ZoneType.Ground) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return 1000000 - getDistanceBetweenPoints({ x, y }, { x: hX, y: hY });
|
|
70
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { ActionsApi, GameApi, OrderType, PlayerData, SideType } from "@chronodivide/game-api";
|
|
2
|
+
import { Squad } from "../squad.js";
|
|
3
|
+
import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
|
|
4
|
+
import { MatchAwareness } from "../../awareness.js";
|
|
5
|
+
|
|
6
|
+
const CAPTURE_COOLDOWN_TICKS = 30;
|
|
7
|
+
|
|
8
|
+
// Capture squad
|
|
9
|
+
export class EngineerSquad implements SquadBehaviour {
|
|
10
|
+
private hasAttemptedCaptureWith: {
|
|
11
|
+
unitId: number;
|
|
12
|
+
gameTick: number;
|
|
13
|
+
} | null = null;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @param captureTarget ID of the target to try and capture/send engineer into.
|
|
17
|
+
*/
|
|
18
|
+
constructor(private captureTarget: number) {
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
public onAiUpdate(
|
|
22
|
+
gameApi: GameApi,
|
|
23
|
+
actionsApi: ActionsApi,
|
|
24
|
+
playerData: PlayerData,
|
|
25
|
+
squad: Squad,
|
|
26
|
+
matchAwareness: MatchAwareness
|
|
27
|
+
): SquadAction {
|
|
28
|
+
const engineerTypes = ["ENGINEER", "SENGINEER"];
|
|
29
|
+
const engineers = squad.getUnitsOfTypes(gameApi, ...engineerTypes);
|
|
30
|
+
if (engineers.length === 0) {
|
|
31
|
+
// Perhaps we deployed already (or the unit was destroyed), end the mission.
|
|
32
|
+
if (this.hasAttemptedCaptureWith !== null) {
|
|
33
|
+
return disband();
|
|
34
|
+
}
|
|
35
|
+
return requestUnits(engineerTypes, 100);
|
|
36
|
+
} else if (
|
|
37
|
+
!this.hasAttemptedCaptureWith ||
|
|
38
|
+
gameApi.getCurrentTick() > this.hasAttemptedCaptureWith.gameTick + CAPTURE_COOLDOWN_TICKS
|
|
39
|
+
) {
|
|
40
|
+
actionsApi.orderUnits(
|
|
41
|
+
engineers.map((engineer) => engineer.id),
|
|
42
|
+
OrderType.Capture,
|
|
43
|
+
this.captureTarget
|
|
44
|
+
);
|
|
45
|
+
// Add a cooldown to deploy attempts.
|
|
46
|
+
this.hasAttemptedCaptureWith = {
|
|
47
|
+
unitId: engineers[0].id,
|
|
48
|
+
gameTick: gameApi.getCurrentTick(),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
return noop();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -7,8 +7,6 @@ import { MatchAwareness } from "../../awareness.js";
|
|
|
7
7
|
const SCOUT_MOVE_COOLDOWN_TICKS = 30;
|
|
8
8
|
|
|
9
9
|
export class RetreatSquad implements SquadBehaviour {
|
|
10
|
-
private hasRequestedUnits: boolean = false;
|
|
11
|
-
private moveOrderSentAt: number | null = null;
|
|
12
10
|
private createdAt: number | null = null;
|
|
13
11
|
|
|
14
12
|
constructor(
|
|
@@ -28,19 +26,19 @@ export class RetreatSquad implements SquadBehaviour {
|
|
|
28
26
|
}
|
|
29
27
|
if (squad.getUnitIds().length > 0) {
|
|
30
28
|
// Only send the order once we have managed to claim some units.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
this.
|
|
35
|
-
|
|
29
|
+
actionsApi.orderUnits(
|
|
30
|
+
squad.getUnitIds(),
|
|
31
|
+
OrderType.AttackMove,
|
|
32
|
+
this.retreatToPoint.x,
|
|
33
|
+
this.retreatToPoint.y,
|
|
34
|
+
);
|
|
35
|
+
return disband();
|
|
36
36
|
}
|
|
37
|
-
if (
|
|
38
|
-
|
|
39
|
-
(this.createdAt && gameApi.getCurrentTick() > this.createdAt + 240)
|
|
40
|
-
) {
|
|
37
|
+
if (this.createdAt && gameApi.getCurrentTick() > this.createdAt + 240) {
|
|
38
|
+
// Disband automatically after 240 ticks in case we couldn't actually claim any units.
|
|
41
39
|
return disband();
|
|
42
40
|
} else {
|
|
43
|
-
return requestSpecificUnits(this.unitIds,
|
|
41
|
+
return requestSpecificUnits(this.unitIds, 1000);
|
|
44
42
|
}
|
|
45
43
|
}
|
|
46
44
|
}
|