@supalosa/chronodivide-bot 0.5.4 → 0.6.4
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 +4 -4
- package/.github/workflows/npm-publish.yml +24 -0
- package/README.md +108 -103
- package/dist/bot/bot.js +105 -105
- package/dist/bot/logic/awareness.js +136 -136
- package/dist/bot/logic/building/antiAirStaticDefence.js +42 -42
- package/dist/bot/logic/building/antiGroundStaticDefence.js +34 -34
- package/dist/bot/logic/building/{ArtilleryUnit.js → artilleryUnit.js} +18 -18
- package/dist/bot/logic/building/basicAirUnit.js +19 -19
- package/dist/bot/logic/building/basicBuilding.js +26 -26
- package/dist/bot/logic/building/basicGroundUnit.js +19 -19
- package/dist/bot/logic/building/buildingRules.js +175 -175
- package/dist/bot/logic/building/common.js +19 -19
- package/dist/bot/logic/building/harvester.js +16 -16
- package/dist/bot/logic/building/powerPlant.js +20 -20
- package/dist/bot/logic/building/queueController.js +183 -183
- package/dist/bot/logic/building/resourceCollectionBuilding.js +36 -36
- package/dist/bot/logic/common/scout.js +126 -126
- package/dist/bot/logic/common/utils.js +95 -95
- package/dist/bot/logic/composition/alliedCompositions.js +12 -12
- package/dist/bot/logic/composition/common.js +1 -1
- package/dist/bot/logic/composition/sovietCompositions.js +12 -12
- package/dist/bot/logic/map/map.js +44 -44
- package/dist/bot/logic/map/sector.js +137 -137
- package/dist/bot/logic/mission/actionBatcher.js +91 -91
- package/dist/bot/logic/mission/mission.js +122 -122
- package/dist/bot/logic/mission/missionController.js +321 -321
- package/dist/bot/logic/mission/missionFactories.js +12 -12
- package/dist/bot/logic/mission/missions/attackMission.js +214 -214
- package/dist/bot/logic/mission/missions/defenceMission.js +82 -82
- package/dist/bot/logic/mission/missions/engineerMission.js +63 -63
- package/dist/bot/logic/mission/missions/expansionMission.js +60 -60
- package/dist/bot/logic/mission/missions/retreatMission.js +33 -33
- package/dist/bot/logic/mission/missions/scoutingMission.js +133 -133
- package/dist/bot/logic/mission/missions/squads/combatSquad.js +115 -115
- package/dist/bot/logic/mission/missions/squads/common.js +57 -57
- package/dist/bot/logic/mission/missions/squads/squad.js +1 -1
- package/dist/bot/logic/threat/threat.js +22 -22
- package/dist/bot/logic/threat/threatCalculator.js +73 -73
- package/dist/exampleBot.js +100 -100
- package/package.json +32 -29
- package/src/bot/bot.ts +161 -161
- package/src/bot/logic/awareness.ts +245 -245
- package/src/bot/logic/building/antiAirStaticDefence.ts +64 -64
- package/src/bot/logic/building/antiGroundStaticDefence.ts +55 -55
- package/src/bot/logic/building/artilleryUnit.ts +39 -39
- package/src/bot/logic/building/basicAirUnit.ts +39 -39
- package/src/bot/logic/building/basicBuilding.ts +49 -49
- package/src/bot/logic/building/basicGroundUnit.ts +39 -39
- package/src/bot/logic/building/buildingRules.ts +250 -250
- package/src/bot/logic/building/common.ts +21 -21
- package/src/bot/logic/building/harvester.ts +31 -31
- package/src/bot/logic/building/powerPlant.ts +32 -32
- package/src/bot/logic/building/queueController.ts +297 -297
- package/src/bot/logic/building/resourceCollectionBuilding.ts +52 -52
- package/src/bot/logic/common/scout.ts +183 -183
- package/src/bot/logic/common/utils.ts +120 -120
- package/src/bot/logic/composition/alliedCompositions.ts +22 -22
- package/src/bot/logic/composition/common.ts +3 -3
- package/src/bot/logic/composition/sovietCompositions.ts +21 -21
- package/src/bot/logic/map/map.ts +66 -66
- package/src/bot/logic/map/sector.ts +174 -174
- package/src/bot/logic/mission/actionBatcher.ts +124 -124
- package/src/bot/logic/mission/mission.ts +232 -232
- package/src/bot/logic/mission/missionController.ts +413 -413
- package/src/bot/logic/mission/missionFactories.ts +51 -51
- package/src/bot/logic/mission/missions/attackMission.ts +336 -336
- package/src/bot/logic/mission/missions/defenceMission.ts +151 -151
- package/src/bot/logic/mission/missions/engineerMission.ts +113 -113
- package/src/bot/logic/mission/missions/expansionMission.ts +104 -104
- package/src/bot/logic/mission/missions/retreatMission.ts +54 -54
- package/src/bot/logic/mission/missions/scoutingMission.ts +186 -186
- package/src/bot/logic/mission/missions/squads/combatSquad.ts +160 -160
- package/src/bot/logic/mission/missions/squads/common.ts +63 -63
- package/src/bot/logic/mission/missions/squads/squad.ts +19 -19
- package/src/bot/logic/threat/threat.ts +15 -15
- package/src/bot/logic/threat/threatCalculator.ts +100 -100
- package/src/exampleBot.ts +111 -111
- package/tsconfig.json +73 -73
- package/dist/bot/logic/awarenessImpl.js +0 -132
- package/dist/bot/logic/awarenessImpl.js.map +0 -1
- package/dist/bot/logic/building/building.js +0 -126
- package/dist/bot/logic/building/building.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/combatSquad.js +0 -124
- package/dist/bot/logic/mission/behaviours/combatSquad.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/common.js +0 -58
- package/dist/bot/logic/mission/behaviours/common.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/engineerSquad.js +0 -39
- package/dist/bot/logic/mission/behaviours/engineerSquad.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/expansionSquad.js +0 -46
- package/dist/bot/logic/mission/behaviours/expansionSquad.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/retreatSquad.js +0 -31
- package/dist/bot/logic/mission/behaviours/retreatSquad.js.map +0 -1
- package/dist/bot/logic/mission/behaviours/scoutingSquad.js +0 -94
- package/dist/bot/logic/mission/behaviours/scoutingSquad.js.map +0 -1
- package/dist/bot/logic/mission/missions/basicMission.js +0 -13
- package/dist/bot/logic/mission/missions/basicMission.js.map +0 -1
- package/dist/bot/logic/mission/missions/missionBehaviour.js +0 -2
- package/dist/bot/logic/mission/missions/missionBehaviour.js.map +0 -1
- package/dist/bot/logic/mission/missions/oneTimeMission.js +0 -27
- package/dist/bot/logic/mission/missions/oneTimeMission.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/attackSquad.js +0 -89
- package/dist/bot/logic/squad/behaviours/combatSquad.js +0 -102
- package/dist/bot/logic/squad/behaviours/combatSquad.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/common.js +0 -40
- package/dist/bot/logic/squad/behaviours/common.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/defenceSquad.js +0 -61
- package/dist/bot/logic/squad/behaviours/engineerSquad.js +0 -36
- package/dist/bot/logic/squad/behaviours/engineerSquad.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/expansionSquad.js +0 -43
- package/dist/bot/logic/squad/behaviours/expansionSquad.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/retreatSquad.js +0 -28
- package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +0 -1
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js +0 -86
- package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +0 -1
- package/dist/bot/logic/squad/squad.js +0 -126
- package/dist/bot/logic/squad/squad.js.map +0 -1
- package/dist/bot/logic/squad/squadBehaviour.js +0 -6
- package/dist/bot/logic/squad/squadBehaviour.js.map +0 -1
- package/dist/bot/logic/squad/squadBehaviours.js +0 -7
- package/dist/bot/logic/squad/squadBehaviours.js.map +0 -1
- package/dist/bot/logic/squad/squadController.js +0 -199
- package/dist/bot/logic/squad/squadController.js.map +0 -1
- /package/dist/bot/logic/building/{ArtilleryUnit.js.map → artilleryUnit.js.map} +0 -0
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
import { ActionsApi, GameApi, PlayerData, Tile, UnitData, Vector2 } from "@chronodivide/game-api";
|
|
2
|
-
import { MatchAwareness } from "../awareness.js";
|
|
3
|
-
import { DebugLogger } from "../common/utils.js";
|
|
4
|
-
import { ActionBatcher } from "./actionBatcher.js";
|
|
5
|
-
import { getDistanceBetweenTileAndPoint } from "../map/map.js";
|
|
6
|
-
|
|
7
|
-
const calculateCenterOfMass: (unitTiles: Tile[]) => {
|
|
8
|
-
centerOfMass: Vector2;
|
|
9
|
-
maxDistance: number;
|
|
10
|
-
} | null = (unitTiles) => {
|
|
11
|
-
if (unitTiles.length === 0) {
|
|
12
|
-
return null;
|
|
13
|
-
}
|
|
14
|
-
// TODO: use median here
|
|
15
|
-
const sums = unitTiles.reduce(
|
|
16
|
-
({ x, y }, tile) => {
|
|
17
|
-
return {
|
|
18
|
-
x: x + (tile?.rx || 0),
|
|
19
|
-
y: y + (tile?.ry || 0),
|
|
20
|
-
};
|
|
21
|
-
},
|
|
22
|
-
{ x: 0, y: 0 },
|
|
23
|
-
);
|
|
24
|
-
const centerOfMass = new Vector2(Math.round(sums.x / unitTiles.length), Math.round(sums.y / unitTiles.length));
|
|
25
|
-
|
|
26
|
-
// max distance of units to the center of mass
|
|
27
|
-
const distances = unitTiles.map((tile) => getDistanceBetweenTileAndPoint(tile, centerOfMass));
|
|
28
|
-
const maxDistance = Math.max(...distances);
|
|
29
|
-
return { centerOfMass, maxDistance };
|
|
30
|
-
};
|
|
31
|
-
// AI starts Missions based on heuristics.
|
|
32
|
-
export abstract class Mission<FailureReasons = undefined> {
|
|
33
|
-
private active = true;
|
|
34
|
-
private unitIds: number[] = [];
|
|
35
|
-
private centerOfMass: Vector2 | null = null;
|
|
36
|
-
private maxDistanceToCenterOfMass: number | null = null;
|
|
37
|
-
|
|
38
|
-
private onFinish: (unitIds: number[], reason: FailureReasons) => void = () => {};
|
|
39
|
-
|
|
40
|
-
constructor(
|
|
41
|
-
private uniqueName: string,
|
|
42
|
-
protected logger: DebugLogger,
|
|
43
|
-
) {}
|
|
44
|
-
|
|
45
|
-
// TODO call this
|
|
46
|
-
protected updateCenterOfMass(gameApi: GameApi) {
|
|
47
|
-
const movableUnitTiles = this.unitIds
|
|
48
|
-
.map((unitId) => gameApi.getUnitData(unitId))
|
|
49
|
-
.filter((unit) => unit?.canMove)
|
|
50
|
-
.map((unit) => unit?.tile)
|
|
51
|
-
.filter((tile) => !!tile) as Tile[];
|
|
52
|
-
const tileMetrics = calculateCenterOfMass(movableUnitTiles);
|
|
53
|
-
if (tileMetrics) {
|
|
54
|
-
this.centerOfMass = tileMetrics.centerOfMass;
|
|
55
|
-
this.maxDistanceToCenterOfMass = tileMetrics.maxDistance;
|
|
56
|
-
} else {
|
|
57
|
-
this.centerOfMass = null;
|
|
58
|
-
this.maxDistanceToCenterOfMass = null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public onAiUpdate(
|
|
63
|
-
gameApi: GameApi,
|
|
64
|
-
actionsApi: ActionsApi,
|
|
65
|
-
playerData: PlayerData,
|
|
66
|
-
matchAwareness: MatchAwareness,
|
|
67
|
-
actionBatcher: ActionBatcher,
|
|
68
|
-
): MissionAction {
|
|
69
|
-
this.updateCenterOfMass(gameApi);
|
|
70
|
-
return this._onAiUpdate(gameApi, actionsApi, playerData, matchAwareness, actionBatcher);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// TODO: fix this weird indirection
|
|
74
|
-
abstract _onAiUpdate(
|
|
75
|
-
gameApi: GameApi,
|
|
76
|
-
actionsApi: ActionsApi,
|
|
77
|
-
playerData: PlayerData,
|
|
78
|
-
matchAwareness: MatchAwareness,
|
|
79
|
-
actionBatcher: ActionBatcher,
|
|
80
|
-
): MissionAction;
|
|
81
|
-
|
|
82
|
-
isActive(): boolean {
|
|
83
|
-
return this.active;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
public getUnitIds(): number[] {
|
|
87
|
-
return this.unitIds;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
public removeUnit(unitIdToRemove: number): void {
|
|
91
|
-
this.unitIds = this.unitIds.filter((unitId) => unitId != unitIdToRemove);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
public addUnit(unitIdToAdd: number): void {
|
|
95
|
-
this.unitIds.push(unitIdToAdd);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
public getUnits(gameApi: GameApi): UnitData[] {
|
|
99
|
-
return this.unitIds
|
|
100
|
-
.map((unitId) => gameApi.getUnitData(unitId))
|
|
101
|
-
.filter((unit) => unit != null)
|
|
102
|
-
.map((unit) => unit!);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
public getUnitsOfTypes(gameApi: GameApi, ...names: string[]): UnitData[] {
|
|
106
|
-
return this.unitIds
|
|
107
|
-
.map((unitId) => gameApi.getUnitData(unitId))
|
|
108
|
-
.filter((unit) => !!unit && names.includes(unit.name))
|
|
109
|
-
.map((unit) => unit!);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public getUnitsMatching(gameApi: GameApi, filter: (r: UnitData) => boolean): UnitData[] {
|
|
113
|
-
return this.unitIds
|
|
114
|
-
.map((unitId) => gameApi.getUnitData(unitId))
|
|
115
|
-
.filter((unit) => !!unit && filter(unit))
|
|
116
|
-
.map((unit) => unit!);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public getCenterOfMass() {
|
|
120
|
-
return this.centerOfMass;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public getMaxDistanceToCenterOfMass() {
|
|
124
|
-
return this.maxDistanceToCenterOfMass;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
getUniqueName(): string {
|
|
128
|
-
return this.uniqueName;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
// Don't call this from the mission itself
|
|
132
|
-
endMission(reason: FailureReasons): void {
|
|
133
|
-
this.onFinish(this.unitIds, reason);
|
|
134
|
-
this.active = false;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* Declare a callback that is executed when the mission is disbanded for whatever reason.
|
|
139
|
-
*/
|
|
140
|
-
then(onFinish: (unitIds: number[], reason: FailureReasons) => void): Mission<FailureReasons> {
|
|
141
|
-
this.onFinish = onFinish;
|
|
142
|
-
return this;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
abstract getGlobalDebugText(): string | undefined;
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Determines whether units can be stolen from this mission by other missions with higher priority.
|
|
149
|
-
*/
|
|
150
|
-
public isUnitsLocked(): boolean {
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
abstract getPriority(): number;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export type MissionWithAction<T extends MissionAction> = {
|
|
158
|
-
mission: Mission<any>;
|
|
159
|
-
action: T;
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
export type MissionActionNoop = {
|
|
163
|
-
type: "noop";
|
|
164
|
-
};
|
|
165
|
-
|
|
166
|
-
export type MissionActionDisband = {
|
|
167
|
-
type: "disband";
|
|
168
|
-
reason: any | null;
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
export type MissionActionRequestUnits = {
|
|
172
|
-
type: "request";
|
|
173
|
-
unitNames: string[];
|
|
174
|
-
priority: number;
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
export type MissionActionRequestSpecificUnits = {
|
|
178
|
-
type: "requestSpecific";
|
|
179
|
-
unitIds: number[];
|
|
180
|
-
priority: number;
|
|
181
|
-
};
|
|
182
|
-
|
|
183
|
-
export type MissionActionGrabFreeCombatants = {
|
|
184
|
-
type: "requestCombatants";
|
|
185
|
-
point: Vector2;
|
|
186
|
-
radius: number;
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
export type MissionActionReleaseUnits = {
|
|
190
|
-
type: "releaseUnits";
|
|
191
|
-
unitIds: number[];
|
|
192
|
-
};
|
|
193
|
-
|
|
194
|
-
export const noop = () =>
|
|
195
|
-
({
|
|
196
|
-
type: "noop",
|
|
197
|
-
}) as MissionActionNoop;
|
|
198
|
-
|
|
199
|
-
export const disbandMission = (reason?: any) => ({ type: "disband", reason }) as MissionActionDisband;
|
|
200
|
-
export const isDisbandMission = (a: MissionWithAction<MissionAction>): a is MissionWithAction<MissionActionDisband> =>
|
|
201
|
-
a.action.type === "disband";
|
|
202
|
-
|
|
203
|
-
export const requestUnits = (unitNames: string[], priority: number) =>
|
|
204
|
-
({ type: "request", unitNames, priority }) as MissionActionRequestUnits;
|
|
205
|
-
export const isRequestUnits = (
|
|
206
|
-
a: MissionWithAction<MissionAction>,
|
|
207
|
-
): a is MissionWithAction<MissionActionRequestUnits> => a.action.type === "request";
|
|
208
|
-
|
|
209
|
-
export const requestSpecificUnits = (unitIds: number[], priority: number) =>
|
|
210
|
-
({ type: "requestSpecific", unitIds, priority }) as MissionActionRequestSpecificUnits;
|
|
211
|
-
export const isRequestSpecificUnits = (
|
|
212
|
-
a: MissionWithAction<MissionAction>,
|
|
213
|
-
): a is MissionWithAction<MissionActionRequestSpecificUnits> => a.action.type === "requestSpecific";
|
|
214
|
-
|
|
215
|
-
export const grabCombatants = (point: Vector2, radius: number) =>
|
|
216
|
-
({ type: "requestCombatants", point, radius }) as MissionActionGrabFreeCombatants;
|
|
217
|
-
export const isGrabCombatants = (
|
|
218
|
-
a: MissionWithAction<MissionAction>,
|
|
219
|
-
): a is MissionWithAction<MissionActionGrabFreeCombatants> => a.action.type === "requestCombatants";
|
|
220
|
-
|
|
221
|
-
export const releaseUnits = (unitIds: number[]) => ({ type: "releaseUnits", unitIds }) as MissionActionReleaseUnits;
|
|
222
|
-
export const isReleaseUnits = (
|
|
223
|
-
a: MissionWithAction<MissionAction>,
|
|
224
|
-
): a is MissionWithAction<MissionActionReleaseUnits> => a.action.type === "releaseUnits";
|
|
225
|
-
|
|
226
|
-
export type MissionAction =
|
|
227
|
-
| MissionActionNoop
|
|
228
|
-
| MissionActionDisband
|
|
229
|
-
| MissionActionRequestUnits
|
|
230
|
-
| MissionActionRequestSpecificUnits
|
|
231
|
-
| MissionActionGrabFreeCombatants
|
|
232
|
-
| MissionActionReleaseUnits;
|
|
1
|
+
import { ActionsApi, GameApi, PlayerData, Tile, UnitData, Vector2 } from "@chronodivide/game-api";
|
|
2
|
+
import { MatchAwareness } from "../awareness.js";
|
|
3
|
+
import { DebugLogger } from "../common/utils.js";
|
|
4
|
+
import { ActionBatcher } from "./actionBatcher.js";
|
|
5
|
+
import { getDistanceBetweenTileAndPoint } from "../map/map.js";
|
|
6
|
+
|
|
7
|
+
const calculateCenterOfMass: (unitTiles: Tile[]) => {
|
|
8
|
+
centerOfMass: Vector2;
|
|
9
|
+
maxDistance: number;
|
|
10
|
+
} | null = (unitTiles) => {
|
|
11
|
+
if (unitTiles.length === 0) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
// TODO: use median here
|
|
15
|
+
const sums = unitTiles.reduce(
|
|
16
|
+
({ x, y }, tile) => {
|
|
17
|
+
return {
|
|
18
|
+
x: x + (tile?.rx || 0),
|
|
19
|
+
y: y + (tile?.ry || 0),
|
|
20
|
+
};
|
|
21
|
+
},
|
|
22
|
+
{ x: 0, y: 0 },
|
|
23
|
+
);
|
|
24
|
+
const centerOfMass = new Vector2(Math.round(sums.x / unitTiles.length), Math.round(sums.y / unitTiles.length));
|
|
25
|
+
|
|
26
|
+
// max distance of units to the center of mass
|
|
27
|
+
const distances = unitTiles.map((tile) => getDistanceBetweenTileAndPoint(tile, centerOfMass));
|
|
28
|
+
const maxDistance = Math.max(...distances);
|
|
29
|
+
return { centerOfMass, maxDistance };
|
|
30
|
+
};
|
|
31
|
+
// AI starts Missions based on heuristics.
|
|
32
|
+
export abstract class Mission<FailureReasons = undefined> {
|
|
33
|
+
private active = true;
|
|
34
|
+
private unitIds: number[] = [];
|
|
35
|
+
private centerOfMass: Vector2 | null = null;
|
|
36
|
+
private maxDistanceToCenterOfMass: number | null = null;
|
|
37
|
+
|
|
38
|
+
private onFinish: (unitIds: number[], reason: FailureReasons) => void = () => {};
|
|
39
|
+
|
|
40
|
+
constructor(
|
|
41
|
+
private uniqueName: string,
|
|
42
|
+
protected logger: DebugLogger,
|
|
43
|
+
) {}
|
|
44
|
+
|
|
45
|
+
// TODO call this
|
|
46
|
+
protected updateCenterOfMass(gameApi: GameApi) {
|
|
47
|
+
const movableUnitTiles = this.unitIds
|
|
48
|
+
.map((unitId) => gameApi.getUnitData(unitId))
|
|
49
|
+
.filter((unit) => unit?.canMove)
|
|
50
|
+
.map((unit) => unit?.tile)
|
|
51
|
+
.filter((tile) => !!tile) as Tile[];
|
|
52
|
+
const tileMetrics = calculateCenterOfMass(movableUnitTiles);
|
|
53
|
+
if (tileMetrics) {
|
|
54
|
+
this.centerOfMass = tileMetrics.centerOfMass;
|
|
55
|
+
this.maxDistanceToCenterOfMass = tileMetrics.maxDistance;
|
|
56
|
+
} else {
|
|
57
|
+
this.centerOfMass = null;
|
|
58
|
+
this.maxDistanceToCenterOfMass = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public onAiUpdate(
|
|
63
|
+
gameApi: GameApi,
|
|
64
|
+
actionsApi: ActionsApi,
|
|
65
|
+
playerData: PlayerData,
|
|
66
|
+
matchAwareness: MatchAwareness,
|
|
67
|
+
actionBatcher: ActionBatcher,
|
|
68
|
+
): MissionAction {
|
|
69
|
+
this.updateCenterOfMass(gameApi);
|
|
70
|
+
return this._onAiUpdate(gameApi, actionsApi, playerData, matchAwareness, actionBatcher);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// TODO: fix this weird indirection
|
|
74
|
+
abstract _onAiUpdate(
|
|
75
|
+
gameApi: GameApi,
|
|
76
|
+
actionsApi: ActionsApi,
|
|
77
|
+
playerData: PlayerData,
|
|
78
|
+
matchAwareness: MatchAwareness,
|
|
79
|
+
actionBatcher: ActionBatcher,
|
|
80
|
+
): MissionAction;
|
|
81
|
+
|
|
82
|
+
isActive(): boolean {
|
|
83
|
+
return this.active;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
public getUnitIds(): number[] {
|
|
87
|
+
return this.unitIds;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public removeUnit(unitIdToRemove: number): void {
|
|
91
|
+
this.unitIds = this.unitIds.filter((unitId) => unitId != unitIdToRemove);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
public addUnit(unitIdToAdd: number): void {
|
|
95
|
+
this.unitIds.push(unitIdToAdd);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
public getUnits(gameApi: GameApi): UnitData[] {
|
|
99
|
+
return this.unitIds
|
|
100
|
+
.map((unitId) => gameApi.getUnitData(unitId))
|
|
101
|
+
.filter((unit) => unit != null)
|
|
102
|
+
.map((unit) => unit!);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public getUnitsOfTypes(gameApi: GameApi, ...names: string[]): UnitData[] {
|
|
106
|
+
return this.unitIds
|
|
107
|
+
.map((unitId) => gameApi.getUnitData(unitId))
|
|
108
|
+
.filter((unit) => !!unit && names.includes(unit.name))
|
|
109
|
+
.map((unit) => unit!);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public getUnitsMatching(gameApi: GameApi, filter: (r: UnitData) => boolean): UnitData[] {
|
|
113
|
+
return this.unitIds
|
|
114
|
+
.map((unitId) => gameApi.getUnitData(unitId))
|
|
115
|
+
.filter((unit) => !!unit && filter(unit))
|
|
116
|
+
.map((unit) => unit!);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public getCenterOfMass() {
|
|
120
|
+
return this.centerOfMass;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public getMaxDistanceToCenterOfMass() {
|
|
124
|
+
return this.maxDistanceToCenterOfMass;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
getUniqueName(): string {
|
|
128
|
+
return this.uniqueName;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Don't call this from the mission itself
|
|
132
|
+
endMission(reason: FailureReasons): void {
|
|
133
|
+
this.onFinish(this.unitIds, reason);
|
|
134
|
+
this.active = false;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Declare a callback that is executed when the mission is disbanded for whatever reason.
|
|
139
|
+
*/
|
|
140
|
+
then(onFinish: (unitIds: number[], reason: FailureReasons) => void): Mission<FailureReasons> {
|
|
141
|
+
this.onFinish = onFinish;
|
|
142
|
+
return this;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
abstract getGlobalDebugText(): string | undefined;
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Determines whether units can be stolen from this mission by other missions with higher priority.
|
|
149
|
+
*/
|
|
150
|
+
public isUnitsLocked(): boolean {
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
abstract getPriority(): number;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export type MissionWithAction<T extends MissionAction> = {
|
|
158
|
+
mission: Mission<any>;
|
|
159
|
+
action: T;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export type MissionActionNoop = {
|
|
163
|
+
type: "noop";
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
export type MissionActionDisband = {
|
|
167
|
+
type: "disband";
|
|
168
|
+
reason: any | null;
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
export type MissionActionRequestUnits = {
|
|
172
|
+
type: "request";
|
|
173
|
+
unitNames: string[];
|
|
174
|
+
priority: number;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export type MissionActionRequestSpecificUnits = {
|
|
178
|
+
type: "requestSpecific";
|
|
179
|
+
unitIds: number[];
|
|
180
|
+
priority: number;
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export type MissionActionGrabFreeCombatants = {
|
|
184
|
+
type: "requestCombatants";
|
|
185
|
+
point: Vector2;
|
|
186
|
+
radius: number;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
export type MissionActionReleaseUnits = {
|
|
190
|
+
type: "releaseUnits";
|
|
191
|
+
unitIds: number[];
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const noop = () =>
|
|
195
|
+
({
|
|
196
|
+
type: "noop",
|
|
197
|
+
}) as MissionActionNoop;
|
|
198
|
+
|
|
199
|
+
export const disbandMission = (reason?: any) => ({ type: "disband", reason }) as MissionActionDisband;
|
|
200
|
+
export const isDisbandMission = (a: MissionWithAction<MissionAction>): a is MissionWithAction<MissionActionDisband> =>
|
|
201
|
+
a.action.type === "disband";
|
|
202
|
+
|
|
203
|
+
export const requestUnits = (unitNames: string[], priority: number) =>
|
|
204
|
+
({ type: "request", unitNames, priority }) as MissionActionRequestUnits;
|
|
205
|
+
export const isRequestUnits = (
|
|
206
|
+
a: MissionWithAction<MissionAction>,
|
|
207
|
+
): a is MissionWithAction<MissionActionRequestUnits> => a.action.type === "request";
|
|
208
|
+
|
|
209
|
+
export const requestSpecificUnits = (unitIds: number[], priority: number) =>
|
|
210
|
+
({ type: "requestSpecific", unitIds, priority }) as MissionActionRequestSpecificUnits;
|
|
211
|
+
export const isRequestSpecificUnits = (
|
|
212
|
+
a: MissionWithAction<MissionAction>,
|
|
213
|
+
): a is MissionWithAction<MissionActionRequestSpecificUnits> => a.action.type === "requestSpecific";
|
|
214
|
+
|
|
215
|
+
export const grabCombatants = (point: Vector2, radius: number) =>
|
|
216
|
+
({ type: "requestCombatants", point, radius }) as MissionActionGrabFreeCombatants;
|
|
217
|
+
export const isGrabCombatants = (
|
|
218
|
+
a: MissionWithAction<MissionAction>,
|
|
219
|
+
): a is MissionWithAction<MissionActionGrabFreeCombatants> => a.action.type === "requestCombatants";
|
|
220
|
+
|
|
221
|
+
export const releaseUnits = (unitIds: number[]) => ({ type: "releaseUnits", unitIds }) as MissionActionReleaseUnits;
|
|
222
|
+
export const isReleaseUnits = (
|
|
223
|
+
a: MissionWithAction<MissionAction>,
|
|
224
|
+
): a is MissionWithAction<MissionActionReleaseUnits> => a.action.type === "releaseUnits";
|
|
225
|
+
|
|
226
|
+
export type MissionAction =
|
|
227
|
+
| MissionActionNoop
|
|
228
|
+
| MissionActionDisband
|
|
229
|
+
| MissionActionRequestUnits
|
|
230
|
+
| MissionActionRequestSpecificUnits
|
|
231
|
+
| MissionActionGrabFreeCombatants
|
|
232
|
+
| MissionActionReleaseUnits;
|