@supalosa/chronodivide-bot 0.4.0 → 0.5.2
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 -35
- 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/building/ArtilleryUnit.js +2 -29
- 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 +10 -20
- package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
- package/dist/bot/logic/building/artilleryUnit.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/buildingRules.js +15 -9
- 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 +56 -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/squadController.js +6 -2
- package/dist/bot/logic/squad/squadController.js.map +1 -1
- package/dist/bot/logic/threat/threatCalculator.js +9 -9
- package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
- package/dist/exampleBot.js +50 -18
- package/dist/exampleBot.js.map +1 -1
- package/package.json +5 -4
- package/src/bot/bot.ts +19 -51
- 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/threatCalculator.ts +10 -10
- package/src/exampleBot.ts +56 -24
- package/.prettierrc +0 -5
- package/TODO.md +0 -15
- 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,5 +1,18 @@
|
|
|
1
|
+
import { GameObjectData, TechnoRules, UnitData } from "@chronodivide/game-api";
|
|
2
|
+
|
|
1
3
|
export type DebugLogger = (message: string, sayInGame?: boolean) => void;
|
|
2
4
|
|
|
5
|
+
const SOVIET_COUNTRY_NAMES = ["Africans", "Arabs", "Confederation", "Russians"];
|
|
6
|
+
|
|
7
|
+
export const isSoviet = (countryName: string) => SOVIET_COUNTRY_NAMES.includes(countryName);
|
|
8
|
+
|
|
9
|
+
export const isOwnedByNeutral = (unitData: UnitData | undefined) => unitData?.owner === "@@NEUTRAL@@";
|
|
10
|
+
|
|
11
|
+
// Return if the given unit would have .isSelectableCombatant = true.
|
|
12
|
+
// Usable on GameObjectData (which is faster to get than TechnoRules)
|
|
13
|
+
export const isSelectableCombatant = (rules: GameObjectData | undefined) =>
|
|
14
|
+
!!(rules?.rules as any)?.isSelectableCombatant;
|
|
15
|
+
|
|
3
16
|
// Thanks use-strict!
|
|
4
17
|
export function formatTimeDuration(timeSeconds: number, skipZeroHours = false) {
|
|
5
18
|
let h = Math.floor(timeSeconds / 3600);
|
|
@@ -17,6 +30,21 @@ export function pad(n: any, format = "0000") {
|
|
|
17
30
|
}
|
|
18
31
|
|
|
19
32
|
// So we don't need lodash
|
|
33
|
+
export function minBy<T>(array: T[], predicate: (arg: T) => number | null): T | null {
|
|
34
|
+
if (array.length === 0) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
let minIdx = 0;
|
|
38
|
+
let minVal = predicate(array[0]);
|
|
39
|
+
for (let i = 1; i < array.length; ++i) {
|
|
40
|
+
const newVal = predicate(array[i]);
|
|
41
|
+
if (minVal === null || (newVal !== null && newVal < minVal)) {
|
|
42
|
+
minIdx = i;
|
|
43
|
+
minVal = newVal;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return array[minIdx];
|
|
47
|
+
}
|
|
20
48
|
|
|
21
49
|
export function maxBy<T>(array: T[], predicate: (arg: T) => number | null): T | null {
|
|
22
50
|
if (array.length === 0) {
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { GameApi, PlayerData } from "@chronodivide/game-api";
|
|
2
|
+
import { MatchAwareness } from "../awareness";
|
|
3
|
+
import { UnitComposition } from "./common";
|
|
4
|
+
|
|
5
|
+
export const getAlliedCompositions = (
|
|
6
|
+
gameApi: GameApi,
|
|
7
|
+
playerData: PlayerData,
|
|
8
|
+
matchAwareness: MatchAwareness,
|
|
9
|
+
): UnitComposition => {
|
|
10
|
+
const hasWarFactory = gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "GAWEAP").length > 0;
|
|
11
|
+
const hasAirforce =
|
|
12
|
+
gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "GAAIRC" || r.name === "AMRADR").length > 0;
|
|
13
|
+
const hasBattleLab = gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "GATECH").length > 0;
|
|
14
|
+
|
|
15
|
+
const includeInfantry = !hasAirforce && !hasBattleLab;
|
|
16
|
+
return {
|
|
17
|
+
...(includeInfantry && { E1: 5 }),
|
|
18
|
+
...(hasWarFactory && { MTNK: 3, FV: 2 }),
|
|
19
|
+
...(hasAirforce && { JUMPJET: 6 }),
|
|
20
|
+
...(hasBattleLab && { SREF: 2, MGTK: 3 }),
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { GameApi, PlayerData } from "@chronodivide/game-api";
|
|
2
|
+
import { MatchAwareness } from "../awareness";
|
|
3
|
+
import { UnitComposition } from "./common";
|
|
4
|
+
|
|
5
|
+
export const getSovietComposition = (
|
|
6
|
+
gameApi: GameApi,
|
|
7
|
+
playerData: PlayerData,
|
|
8
|
+
matchAwareness: MatchAwareness,
|
|
9
|
+
): UnitComposition => {
|
|
10
|
+
const hasWarFactory = gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "NAWEAP").length > 0;
|
|
11
|
+
const hasRadar = gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "NARADR").length > 0;
|
|
12
|
+
const hasBattleLab = gameApi.getVisibleUnits(playerData.name, "self", (r) => r.name === "NATECH").length > 0;
|
|
13
|
+
|
|
14
|
+
const includeInfantry = !hasBattleLab;
|
|
15
|
+
return {
|
|
16
|
+
...(includeInfantry && { E2: 10 }),
|
|
17
|
+
...(hasWarFactory && { HTNK: 3, HTK: 2 }),
|
|
18
|
+
...(hasRadar && { V3: 1 }),
|
|
19
|
+
...(hasBattleLab && { APOC: 2 }),
|
|
20
|
+
};
|
|
21
|
+
};
|
|
@@ -1,15 +1,66 @@
|
|
|
1
1
|
// Used to group related actions together to minimise actionApi calls. For example, if multiple units
|
|
2
2
|
|
|
3
3
|
import { ActionsApi, OrderType, Vector2 } from "@chronodivide/game-api";
|
|
4
|
-
import { groupBy } from "
|
|
4
|
+
import { groupBy } from "../common/utils.js";
|
|
5
5
|
|
|
6
6
|
// are ordered to move to the same location, all of them will be ordered to move in a single action.
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
export class BatchableAction {
|
|
8
|
+
private constructor(
|
|
9
|
+
private _unitId: number,
|
|
10
|
+
private _orderType: OrderType,
|
|
11
|
+
private _point?: Vector2,
|
|
12
|
+
private _targetId?: number,
|
|
13
|
+
// If you don't want this action to be swallowed by dedupe, provide a unique nonce
|
|
14
|
+
private _nonce: number = 0,
|
|
15
|
+
) {}
|
|
16
|
+
|
|
17
|
+
static noTarget(unitId: number, orderType: OrderType, nonce: number = 0) {
|
|
18
|
+
return new BatchableAction(unitId, orderType, undefined, undefined, nonce);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
static toPoint(unitId: number, orderType: OrderType, point: Vector2, nonce: number = 0) {
|
|
22
|
+
return new BatchableAction(unitId, orderType, point, undefined);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
static toTargetId(unitId: number, orderType: OrderType, targetId: number, nonce: number = 0) {
|
|
26
|
+
return new BatchableAction(unitId, orderType, undefined, targetId, nonce);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
public get unitId() {
|
|
30
|
+
return this._unitId;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
public get orderType() {
|
|
34
|
+
return this._orderType;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get point() {
|
|
38
|
+
return this._point;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public get targetId() {
|
|
42
|
+
return this._targetId;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
public isSameAs(other: BatchableAction) {
|
|
46
|
+
if (this._unitId !== other._unitId) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
if (this._orderType !== other._orderType) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
if (this._point !== other._point) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
if (this._targetId !== other._targetId) {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (this._nonce !== other._nonce) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
13
64
|
|
|
14
65
|
export class ActionBatcher {
|
|
15
66
|
private actions: BatchableAction[];
|
|
@@ -60,6 +111,14 @@ export class ActionBatcher {
|
|
|
60
111
|
vector.y,
|
|
61
112
|
);
|
|
62
113
|
});
|
|
114
|
+
// Actions with no targets
|
|
115
|
+
const noTargets = commands.filter((command) => !command.targetId && !command.point);
|
|
116
|
+
if (noTargets.length > 0) {
|
|
117
|
+
actionsApi.orderUnits(
|
|
118
|
+
noTargets.map((action) => action.unitId),
|
|
119
|
+
commandType,
|
|
120
|
+
);
|
|
121
|
+
}
|
|
63
122
|
});
|
|
64
123
|
}
|
|
65
124
|
}
|
|
@@ -1,36 +1,127 @@
|
|
|
1
|
-
import { GameApi, PlayerData } from "@chronodivide/game-api";
|
|
2
|
-
import { Squad } from "../squad/squad.js";
|
|
1
|
+
import { ActionsApi, GameApi, PlayerData, Tile, UnitData, Vector2 } from "@chronodivide/game-api";
|
|
3
2
|
import { MatchAwareness } from "../awareness.js";
|
|
4
3
|
import { DebugLogger } from "../common/utils.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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.
|
|
8
32
|
export abstract class Mission<FailureReasons = undefined> {
|
|
9
|
-
private squad: Squad | null = null;
|
|
10
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
|
+
}
|
|
11
61
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
+
}
|
|
15
72
|
|
|
16
|
-
|
|
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;
|
|
17
81
|
|
|
18
82
|
isActive(): boolean {
|
|
19
83
|
return this.active;
|
|
20
84
|
}
|
|
21
85
|
|
|
22
|
-
|
|
23
|
-
this.
|
|
24
|
-
|
|
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);
|
|
25
96
|
}
|
|
26
97
|
|
|
27
|
-
|
|
28
|
-
return this.
|
|
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!);
|
|
29
103
|
}
|
|
30
104
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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;
|
|
34
125
|
}
|
|
35
126
|
|
|
36
127
|
getUniqueName(): string {
|
|
@@ -39,27 +130,37 @@ export abstract class Mission<FailureReasons = undefined> {
|
|
|
39
130
|
|
|
40
131
|
// Don't call this from the mission itself
|
|
41
132
|
endMission(reason: FailureReasons): void {
|
|
42
|
-
this.onFinish(
|
|
43
|
-
this.squad = null;
|
|
133
|
+
this.onFinish(this.unitIds, reason);
|
|
44
134
|
this.active = false;
|
|
45
135
|
}
|
|
46
136
|
|
|
47
137
|
/**
|
|
48
138
|
* Declare a callback that is executed when the mission is disbanded for whatever reason.
|
|
49
139
|
*/
|
|
50
|
-
then(onFinish: (
|
|
140
|
+
then(onFinish: (unitIds: number[], reason: FailureReasons) => void): Mission<FailureReasons> {
|
|
51
141
|
this.onFinish = onFinish;
|
|
52
142
|
return this;
|
|
53
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;
|
|
54
155
|
}
|
|
55
156
|
|
|
56
|
-
export type
|
|
57
|
-
|
|
157
|
+
export type MissionWithAction<T extends MissionAction> = {
|
|
158
|
+
mission: Mission<any>;
|
|
159
|
+
action: T;
|
|
58
160
|
};
|
|
59
161
|
|
|
60
|
-
export type
|
|
61
|
-
type: "
|
|
62
|
-
squad: Squad;
|
|
162
|
+
export type MissionActionNoop = {
|
|
163
|
+
type: "noop";
|
|
63
164
|
};
|
|
64
165
|
|
|
65
166
|
export type MissionActionDisband = {
|
|
@@ -67,17 +168,65 @@ export type MissionActionDisband = {
|
|
|
67
168
|
reason: any | null;
|
|
68
169
|
};
|
|
69
170
|
|
|
70
|
-
export
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
171
|
+
export type MissionActionRequestUnits = {
|
|
172
|
+
type: "request";
|
|
173
|
+
unitNames: string[];
|
|
174
|
+
priority: number;
|
|
175
|
+
};
|
|
74
176
|
|
|
75
|
-
export
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
177
|
+
export type MissionActionRequestSpecificUnits = {
|
|
178
|
+
type: "requestSpecific";
|
|
179
|
+
unitIds: number[];
|
|
180
|
+
priority: number;
|
|
181
|
+
};
|
|
80
182
|
|
|
81
|
-
export
|
|
183
|
+
export type MissionActionGrabFreeCombatants = {
|
|
184
|
+
type: "requestCombatants";
|
|
185
|
+
point: Vector2;
|
|
186
|
+
radius: number;
|
|
187
|
+
};
|
|
82
188
|
|
|
83
|
-
export type
|
|
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;
|