@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
|
@@ -20,6 +20,7 @@ import { PowerPlant } from "./powerPlant.js";
|
|
|
20
20
|
import { ResourceCollectionBuilding } from "./resourceCollectionBuilding.js";
|
|
21
21
|
import { Harvester } from "./harvester.js";
|
|
22
22
|
import { uniqBy } from "../common/utils.js";
|
|
23
|
+
import { AntiAirStaticDefence } from "./antiAirStaticDefence.js";
|
|
23
24
|
|
|
24
25
|
export interface AiBuildingRules {
|
|
25
26
|
getPriority(
|
|
@@ -183,7 +184,7 @@ export function getDefaultPlacementLocation(
|
|
|
183
184
|
// Priority 0 = don't build.
|
|
184
185
|
export type TechnoRulesWithPriority = { unit: TechnoRules; priority: number };
|
|
185
186
|
|
|
186
|
-
export const DEFAULT_BUILDING_PRIORITY =
|
|
187
|
+
export const DEFAULT_BUILDING_PRIORITY = 0;
|
|
187
188
|
|
|
188
189
|
export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
189
190
|
// Allied
|
|
@@ -192,7 +193,6 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
192
193
|
["GAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
193
194
|
["GAPILE", new BasicBuilding(12, 1)], // Barracks
|
|
194
195
|
["CMIN", new Harvester(15, 4, 2)], // Chrono Miner
|
|
195
|
-
["ENGINEER", new BasicBuilding(10, 1, 1000)], // Engineer
|
|
196
196
|
["GADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
197
197
|
["GAAIRC", new BasicBuilding(10, 1, 500)], // Airforce Command
|
|
198
198
|
["AMRADR", new BasicBuilding(10, 1, 500)], // Airforce Command (USA)
|
|
@@ -200,11 +200,13 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
200
200
|
["GATECH", new BasicBuilding(20, 1, 4000)], // Allied Battle Lab
|
|
201
201
|
["GAYARD", new BasicBuilding(0, 0, 0)], // Naval Yard, disabled
|
|
202
202
|
|
|
203
|
-
["GAPILL", new AntiGroundStaticDefence(
|
|
204
|
-
["ATESLA", new AntiGroundStaticDefence(
|
|
203
|
+
["GAPILL", new AntiGroundStaticDefence(2, 1, 5)], // Pillbox
|
|
204
|
+
["ATESLA", new AntiGroundStaticDefence(2, 1, 10)], // Prism Cannon
|
|
205
|
+
["NASAM", new AntiAirStaticDefence(2, 1, 5)], // Prism Cannon
|
|
205
206
|
["GAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
206
207
|
|
|
207
|
-
["E1", new BasicGroundUnit(2,
|
|
208
|
+
["E1", new BasicGroundUnit(2, 2, 0.2, 0)], // GI
|
|
209
|
+
["ENGINEER", new BasicGroundUnit(1, 0, 0)], // Engineer
|
|
208
210
|
["MTNK", new BasicGroundUnit(10, 3, 2, 0)], // Grizzly Tank
|
|
209
211
|
["MGTK", new BasicGroundUnit(10, 1, 2.5, 0)], // Mirage Tank
|
|
210
212
|
["FV", new BasicGroundUnit(5, 2, 0.5, 1)], // IFV
|
|
@@ -220,7 +222,6 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
220
222
|
["NAWEAP", new BasicBuilding(15, 1)], // War Factory
|
|
221
223
|
["NAHAND", new BasicBuilding(12, 1)], // Barracks
|
|
222
224
|
["HARV", new Harvester(15, 4, 2)], // War Miner
|
|
223
|
-
["SENGINEER", new BasicBuilding(10, 1, 1000)], // Soviet Engineer
|
|
224
225
|
["NADEPT", new BasicBuilding(1, 1, 10000)], // Repair Depot
|
|
225
226
|
["NARADR", new BasicBuilding(10, 1, 500)], // Radar
|
|
226
227
|
["NANRCT", new PowerPlant()], // Nuclear Reactor
|
|
@@ -228,11 +229,16 @@ export const BUILDING_NAME_TO_RULES = new Map<string, AiBuildingRules>([
|
|
|
228
229
|
|
|
229
230
|
["NATECH", new BasicBuilding(20, 1, 4000)], // Soviet Battle Lab
|
|
230
231
|
|
|
231
|
-
["NALASR", new AntiGroundStaticDefence(
|
|
232
|
-
["
|
|
232
|
+
["NALASR", new AntiGroundStaticDefence(2, 1, 5)], // Sentry Gun
|
|
233
|
+
["NAFLAK", new AntiAirStaticDefence(2, 1, 5)], // Flak Cannon
|
|
234
|
+
["TESLA", new AntiGroundStaticDefence(2, 1, 10)], // Tesla Coil
|
|
233
235
|
["NAWALL", new AntiGroundStaticDefence(0, 0, 0)], // Walls
|
|
234
236
|
|
|
235
|
-
["E2", new BasicGroundUnit(2,
|
|
237
|
+
["E2", new BasicGroundUnit(2, 2, 0.2, 0)], // Conscript
|
|
238
|
+
["SENGINEER", new BasicGroundUnit(1, 0, 0)], // Soviet Engineer
|
|
239
|
+
["FLAKT", new BasicGroundUnit(2, 2, 0.1, 0.3)], // Flak Trooper
|
|
240
|
+
["YURI", new BasicGroundUnit(1, 1, 1, 0)], // Yuri
|
|
241
|
+
["DOG", new BasicGroundUnit(1, 1, 0, 0)], // Soviet Attack Dog
|
|
236
242
|
["HTNK", new BasicGroundUnit(10, 3, 3, 0)], // Rhino Tank
|
|
237
243
|
["APOC", new BasicGroundUnit(6, 1, 5, 0)], // Apocalypse Tank
|
|
238
244
|
["HTK", new BasicGroundUnit(5, 2, 0.33, 1.5)], // Flak Track
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GameApi, PlayerData, TechnoRules, Vector2 } from "@chronodivide/game-api";
|
|
2
|
+
import { getPointTowardsOtherPoint } from "../map/map.js";
|
|
3
|
+
import { getDefaultPlacementLocation } from "./buildingRules.js";
|
|
4
|
+
|
|
5
|
+
export const getStaticDefencePlacement = (game: GameApi, playerData: PlayerData, technoRules: TechnoRules) => {
|
|
6
|
+
// Prefer front towards enemy.
|
|
7
|
+
let startLocation = playerData.startLocation;
|
|
8
|
+
let players = game.getPlayers();
|
|
9
|
+
let enemyFacingLocationCandidates: Vector2[] = [];
|
|
10
|
+
for (let i = 0; i < players.length; ++i) {
|
|
11
|
+
let playerName = players[i];
|
|
12
|
+
if (playerName == playerData.name) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
let enemyPlayer = game.getPlayerData(playerName);
|
|
16
|
+
enemyFacingLocationCandidates.push(
|
|
17
|
+
getPointTowardsOtherPoint(game, startLocation, enemyPlayer.startLocation, 4, 16, 1.5),
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
let selectedLocation =
|
|
21
|
+
enemyFacingLocationCandidates[Math.floor(game.generateRandom() * enemyFacingLocationCandidates.length)];
|
|
22
|
+
return getDefaultPlacementLocation(game, playerData, selectedLocation, technoRules, false, 0);
|
|
23
|
+
};
|
|
@@ -3,6 +3,7 @@ import { GlobalThreat } from "../threat/threat.js";
|
|
|
3
3
|
import { BasicGroundUnit } from "./basicGroundUnit.js";
|
|
4
4
|
|
|
5
5
|
const IDEAL_HARVESTERS_PER_REFINERY = 2;
|
|
6
|
+
const MAX_HARVESTERS_PER_REFINERY = 4;
|
|
6
7
|
|
|
7
8
|
export class Harvester extends BasicGroundUnit {
|
|
8
9
|
constructor(
|
|
@@ -23,7 +24,7 @@ export class Harvester extends BasicGroundUnit {
|
|
|
23
24
|
const refineries = game.getVisibleUnits(playerData.name, "self", (r) => r.refinery).length;
|
|
24
25
|
const harvesters = game.getVisibleUnits(playerData.name, "self", (r) => r.harvester).length;
|
|
25
26
|
|
|
26
|
-
const boost = harvesters < this.minNeeded ? 3 : 1;
|
|
27
|
+
const boost = harvesters < this.minNeeded ? 3 : harvesters > refineries * MAX_HARVESTERS_PER_REFINERY ? 0 : 1;
|
|
27
28
|
|
|
28
29
|
return this.basePriority * (refineries / Math.max(harvesters / IDEAL_HARVESTERS_PER_REFINERY, 1)) * boost;
|
|
29
30
|
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
DEFAULT_BUILDING_PRIORITY,
|
|
15
15
|
getDefaultPlacementLocation,
|
|
16
16
|
} from "./buildingRules.js";
|
|
17
|
+
import { DebugLogger } from "../common/utils";
|
|
17
18
|
|
|
18
19
|
export const QUEUES = [
|
|
19
20
|
QueueType.Structures,
|
|
@@ -43,9 +44,16 @@ export const queueTypeToName = (queue: QueueType) => {
|
|
|
43
44
|
}
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
|
|
47
|
+
type QueueState = {
|
|
48
|
+
queue: QueueType;
|
|
49
|
+
/** sorted in ascending order (last item is the topItem) */
|
|
50
|
+
items: TechnoRulesWithPriority[];
|
|
51
|
+
topItem: TechnoRulesWithPriority | undefined;
|
|
52
|
+
};
|
|
47
53
|
|
|
48
54
|
export class QueueController {
|
|
55
|
+
private queueStates: QueueState[] = [];
|
|
56
|
+
|
|
49
57
|
constructor() {}
|
|
50
58
|
|
|
51
59
|
public onAiUpdate(
|
|
@@ -54,23 +62,35 @@ export class QueueController {
|
|
|
54
62
|
actionsApi: ActionsApi,
|
|
55
63
|
playerData: PlayerData,
|
|
56
64
|
threatCache: GlobalThreat | null,
|
|
65
|
+
unitTypeRequests: Map<string, number>,
|
|
57
66
|
logger: (message: string) => void,
|
|
58
67
|
) {
|
|
59
|
-
|
|
68
|
+
this.queueStates = QUEUES.map((queueType) => {
|
|
60
69
|
const options = productionApi.getAvailableObjects(queueType);
|
|
70
|
+
const items = this.getPrioritiesForBuildingOptions(
|
|
71
|
+
game,
|
|
72
|
+
options,
|
|
73
|
+
threatCache,
|
|
74
|
+
playerData,
|
|
75
|
+
unitTypeRequests,
|
|
76
|
+
logger,
|
|
77
|
+
);
|
|
78
|
+
const topItem = items.length > 0 ? items[items.length - 1] : undefined;
|
|
61
79
|
return {
|
|
62
80
|
queue: queueType,
|
|
63
|
-
|
|
81
|
+
items,
|
|
82
|
+
// only if the top item has a priority above zero
|
|
83
|
+
topItem: topItem && topItem.priority > 0 ? topItem : undefined,
|
|
64
84
|
};
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
.map((decision) => decision.
|
|
85
|
+
});
|
|
86
|
+
const totalWeightAcrossQueues = this.queueStates
|
|
87
|
+
.map((decision) => decision.topItem?.priority!)
|
|
68
88
|
.reduce((pV, cV) => pV + cV, 0);
|
|
69
|
-
|
|
70
|
-
.map((decision) => decision.
|
|
89
|
+
const totalCostAcrossQueues = this.queueStates
|
|
90
|
+
.map((decision) => decision.topItem?.unit.cost!)
|
|
71
91
|
.reduce((pV, cV) => pV + cV, 0);
|
|
72
92
|
|
|
73
|
-
|
|
93
|
+
this.queueStates.forEach((decision) => {
|
|
74
94
|
this.updateBuildQueue(
|
|
75
95
|
game,
|
|
76
96
|
productionApi,
|
|
@@ -78,7 +98,7 @@ export class QueueController {
|
|
|
78
98
|
playerData,
|
|
79
99
|
threatCache,
|
|
80
100
|
decision.queue,
|
|
81
|
-
decision.
|
|
101
|
+
decision.topItem,
|
|
82
102
|
totalWeightAcrossQueues,
|
|
83
103
|
totalCostAcrossQueues,
|
|
84
104
|
logger,
|
|
@@ -86,15 +106,17 @@ export class QueueController {
|
|
|
86
106
|
});
|
|
87
107
|
|
|
88
108
|
// Repair is simple - just repair everything that's damaged.
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
109
|
+
if (playerData.credits > 0) {
|
|
110
|
+
game.getVisibleUnits(playerData.name, "self", (r) => r.repairable).forEach((unitId) => {
|
|
111
|
+
const unit = game.getUnitData(unitId);
|
|
112
|
+
if (!unit || !unit.hitPoints || !unit.maxHitPoints || unit.hasWrenchRepair) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (unit.hitPoints < unit.maxHitPoints) {
|
|
116
|
+
actionsApi.toggleRepairWrench(unitId);
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
private updateBuildQueue(
|
|
@@ -139,20 +161,24 @@ export class QueueController {
|
|
|
139
161
|
}
|
|
140
162
|
}
|
|
141
163
|
} else if (queueData.status == QueueStatus.Active && queueData.items.length > 0 && decision != null) {
|
|
142
|
-
// Consider cancelling if something else is significantly higher priority.
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
if (decision.unit != current) {
|
|
164
|
+
// Consider cancelling if something else is significantly higher priority than what is currently being produced.
|
|
165
|
+
const currentProduction = queueData.items[0].rules;
|
|
166
|
+
if (decision.unit != currentProduction) {
|
|
146
167
|
// Changing our mind.
|
|
147
|
-
let currentItemPriority = this.getPriorityForBuildingOption(
|
|
168
|
+
let currentItemPriority = this.getPriorityForBuildingOption(
|
|
169
|
+
currentProduction,
|
|
170
|
+
game,
|
|
171
|
+
playerData,
|
|
172
|
+
threatCache,
|
|
173
|
+
);
|
|
148
174
|
let newItemPriority = decision.priority;
|
|
149
175
|
if (newItemPriority > currentItemPriority * 2) {
|
|
150
176
|
logger(
|
|
151
|
-
`Dequeueing queue ${queueTypeToName(queueData.type)} unit ${
|
|
177
|
+
`Dequeueing queue ${queueTypeToName(queueData.type)} unit ${currentProduction.name} because ${
|
|
152
178
|
decision.unit.name
|
|
153
179
|
} has 2x higher priority.`,
|
|
154
180
|
);
|
|
155
|
-
actionsApi.unqueueFromProduction(queueData.type,
|
|
181
|
+
actionsApi.unqueueFromProduction(queueData.type, currentProduction.name, currentProduction.type, 1);
|
|
156
182
|
}
|
|
157
183
|
} else {
|
|
158
184
|
// Not changing our mind, but maybe other queues are more important for now.
|
|
@@ -181,32 +207,29 @@ export class QueueController {
|
|
|
181
207
|
}
|
|
182
208
|
}
|
|
183
209
|
|
|
184
|
-
private
|
|
210
|
+
private getPrioritiesForBuildingOptions(
|
|
185
211
|
game: GameApi,
|
|
186
212
|
options: TechnoRules[],
|
|
187
213
|
threatCache: GlobalThreat | null,
|
|
188
214
|
playerData: PlayerData,
|
|
189
|
-
|
|
190
|
-
|
|
215
|
+
unitTypeRequests: Map<string, number>,
|
|
216
|
+
logger: DebugLogger,
|
|
217
|
+
): TechnoRulesWithPriority[] {
|
|
191
218
|
let priorityQueue: TechnoRulesWithPriority[] = [];
|
|
192
219
|
options.forEach((option) => {
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
220
|
+
const calculatedPriority = this.getPriorityForBuildingOption(option, game, playerData, threatCache);
|
|
221
|
+
// Get the higher of the dynamic and the mission priority for the unit.
|
|
222
|
+
const actualPriority = Math.max(
|
|
223
|
+
calculatedPriority,
|
|
224
|
+
unitTypeRequests.get(option.name) ?? calculatedPriority,
|
|
225
|
+
);
|
|
226
|
+
if (actualPriority > 0) {
|
|
227
|
+
priorityQueue.push({ unit: option, priority: actualPriority });
|
|
196
228
|
}
|
|
197
229
|
});
|
|
198
230
|
|
|
199
|
-
priorityQueue = priorityQueue.sort((a, b) =>
|
|
200
|
-
|
|
201
|
-
});
|
|
202
|
-
if (priorityQueue.length > 0) {
|
|
203
|
-
if (DEBUG_BUILD_QUEUES && game.getCurrentTick() % 100 === 0) {
|
|
204
|
-
let queueString = priorityQueue.map((item) => item.unit.name + "(" + item.priority + ")").join(", ");
|
|
205
|
-
logger(`Build priority currently: ${queueString}`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
return priorityQueue.pop();
|
|
231
|
+
priorityQueue = priorityQueue.sort((a, b) => a.priority - b.priority);
|
|
232
|
+
return priorityQueue;
|
|
210
233
|
}
|
|
211
234
|
|
|
212
235
|
private getPriorityForBuildingOption(
|
|
@@ -239,4 +262,36 @@ export class QueueController {
|
|
|
239
262
|
return getDefaultPlacementLocation(game, playerData, playerData.startLocation, objectReady);
|
|
240
263
|
}
|
|
241
264
|
}
|
|
265
|
+
|
|
266
|
+
public getGlobalDebugText(gameApi: GameApi, productionApi: ProductionApi) {
|
|
267
|
+
const productionState = QUEUES.reduce((prev, queueType) => {
|
|
268
|
+
if (productionApi.getQueueData(queueType).size === 0) {
|
|
269
|
+
return prev;
|
|
270
|
+
}
|
|
271
|
+
const paused = productionApi.getQueueData(queueType).status === QueueStatus.OnHold;
|
|
272
|
+
return (
|
|
273
|
+
prev +
|
|
274
|
+
" [" +
|
|
275
|
+
queueTypeToName(queueType) +
|
|
276
|
+
(paused ? " PAUSED" : "") +
|
|
277
|
+
": " +
|
|
278
|
+
productionApi
|
|
279
|
+
.getQueueData(queueType)
|
|
280
|
+
.items.map((item) => item.rules.name + (item.quantity > 1 ? "x" + item.quantity : "")) +
|
|
281
|
+
"]"
|
|
282
|
+
);
|
|
283
|
+
}, "");
|
|
284
|
+
|
|
285
|
+
const queueStates = this.queueStates
|
|
286
|
+
.filter((queueState) => queueState.items.length > 0)
|
|
287
|
+
.map((queueState) => {
|
|
288
|
+
const queueString = queueState.items
|
|
289
|
+
.map((item) => item.unit.name + "(" + Math.round(item.priority * 10) / 10 + ")")
|
|
290
|
+
.join(", ");
|
|
291
|
+
return `${queueTypeToName(queueState.queue)} Prios: ${queueString}\n`;
|
|
292
|
+
})
|
|
293
|
+
.join("");
|
|
294
|
+
|
|
295
|
+
return `Production: ${productionState}\n${queueStates}`;
|
|
296
|
+
}
|
|
242
297
|
}
|
|
@@ -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
|
}
|