@supalosa/chronodivide-bot 0.3.1 → 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.
Files changed (149) hide show
  1. package/.env.template +5 -0
  2. package/README.md +57 -39
  3. package/dist/bot/bot.js +27 -37
  4. package/dist/bot/bot.js.map +1 -1
  5. package/dist/bot/logic/awareness.js +13 -8
  6. package/dist/bot/logic/awareness.js.map +1 -1
  7. package/dist/bot/logic/awarenessImpl.js +132 -0
  8. package/dist/bot/logic/awarenessImpl.js.map +1 -0
  9. package/dist/bot/logic/building/ArtilleryUnit.js +2 -29
  10. package/dist/bot/logic/building/ArtilleryUnit.js.map +1 -0
  11. package/dist/bot/logic/building/antiAirStaticDefence.js +43 -0
  12. package/dist/bot/logic/building/antiAirStaticDefence.js.map +1 -0
  13. package/dist/bot/logic/building/antiGroundStaticDefence.js +8 -5
  14. package/dist/bot/logic/building/antiGroundStaticDefence.js.map +1 -1
  15. package/dist/bot/logic/building/basicAirUnit.js +2 -23
  16. package/dist/bot/logic/building/basicAirUnit.js.map +1 -1
  17. package/dist/bot/logic/building/basicBuilding.js +3 -2
  18. package/dist/bot/logic/building/basicBuilding.js.map +1 -1
  19. package/dist/bot/logic/building/basicGroundUnit.js +2 -43
  20. package/dist/bot/logic/building/basicGroundUnit.js.map +1 -1
  21. package/dist/bot/logic/building/building.js +55 -11
  22. package/dist/bot/logic/building/building.js.map +1 -0
  23. package/dist/bot/logic/building/buildingRules.js +62 -50
  24. package/dist/bot/logic/building/buildingRules.js.map +1 -1
  25. package/dist/bot/logic/building/common.js +19 -0
  26. package/dist/bot/logic/building/common.js.map +1 -0
  27. package/dist/bot/logic/building/harvester.js +2 -1
  28. package/dist/bot/logic/building/harvester.js.map +1 -1
  29. package/dist/bot/logic/building/queueController.js +73 -41
  30. package/dist/bot/logic/building/queueController.js.map +1 -1
  31. package/dist/bot/logic/common/utils.js +35 -0
  32. package/dist/bot/logic/common/utils.js.map +1 -1
  33. package/dist/bot/logic/composition/alliedCompositions.js +13 -0
  34. package/dist/bot/logic/composition/alliedCompositions.js.map +1 -0
  35. package/dist/bot/logic/composition/common.js +2 -0
  36. package/dist/bot/logic/composition/common.js.map +1 -0
  37. package/dist/bot/logic/composition/sovietCompositions.js +13 -0
  38. package/dist/bot/logic/composition/sovietCompositions.js.map +1 -0
  39. package/dist/bot/logic/mission/actionBatcher.js +92 -0
  40. package/dist/bot/logic/mission/actionBatcher.js.map +1 -0
  41. package/dist/bot/logic/mission/behaviours/combatSquad.js +124 -0
  42. package/dist/bot/logic/mission/behaviours/combatSquad.js.map +1 -0
  43. package/dist/bot/logic/mission/behaviours/common.js +58 -0
  44. package/dist/bot/logic/mission/behaviours/common.js.map +1 -0
  45. package/dist/bot/logic/mission/behaviours/engineerSquad.js +39 -0
  46. package/dist/bot/logic/mission/behaviours/engineerSquad.js.map +1 -0
  47. package/dist/bot/logic/mission/behaviours/expansionSquad.js +46 -0
  48. package/dist/bot/logic/mission/behaviours/expansionSquad.js.map +1 -0
  49. package/dist/bot/logic/mission/behaviours/retreatSquad.js +31 -0
  50. package/dist/bot/logic/mission/behaviours/retreatSquad.js.map +1 -0
  51. package/{src/bot/logic/squad/behaviours/scoutingSquad.ts → dist/bot/logic/mission/behaviours/scoutingSquad.js} +29 -47
  52. package/dist/bot/logic/mission/behaviours/scoutingSquad.js.map +1 -0
  53. package/dist/bot/logic/mission/mission.js +91 -19
  54. package/dist/bot/logic/mission/mission.js.map +1 -1
  55. package/dist/bot/logic/mission/missionController.js +262 -21
  56. package/dist/bot/logic/mission/missionController.js.map +1 -1
  57. package/dist/bot/logic/mission/missions/attackMission.js +159 -52
  58. package/dist/bot/logic/mission/missions/attackMission.js.map +1 -1
  59. package/dist/bot/logic/mission/missions/basicMission.js +13 -0
  60. package/dist/bot/logic/mission/missions/basicMission.js.map +1 -0
  61. package/dist/bot/logic/mission/missions/defenceMission.js +43 -28
  62. package/dist/bot/logic/mission/missions/defenceMission.js.map +1 -1
  63. package/dist/bot/logic/mission/missions/engineerMission.js +37 -7
  64. package/dist/bot/logic/mission/missions/engineerMission.js.map +1 -1
  65. package/dist/bot/logic/mission/missions/expansionMission.js +42 -6
  66. package/dist/bot/logic/mission/missions/expansionMission.js.map +1 -1
  67. package/dist/bot/logic/mission/missions/missionBehaviour.js +2 -0
  68. package/dist/bot/logic/mission/missions/missionBehaviour.js.map +1 -0
  69. package/dist/bot/logic/mission/missions/retreatMission.js +31 -5
  70. package/dist/bot/logic/mission/missions/retreatMission.js.map +1 -1
  71. package/dist/bot/logic/mission/missions/scoutingMission.js +103 -6
  72. package/dist/bot/logic/mission/missions/scoutingMission.js.map +1 -1
  73. package/dist/bot/logic/mission/missions/squads/combatSquad.js +116 -0
  74. package/dist/bot/logic/mission/missions/squads/combatSquad.js.map +1 -0
  75. package/dist/bot/logic/mission/missions/squads/common.js +58 -0
  76. package/dist/bot/logic/mission/missions/squads/common.js.map +1 -0
  77. package/dist/bot/logic/mission/missions/squads/squad.js +2 -0
  78. package/dist/bot/logic/mission/missions/squads/squad.js.map +1 -0
  79. package/dist/bot/logic/squad/behaviours/attackSquad.js +63 -56
  80. package/dist/bot/logic/squad/behaviours/combatSquad.js +19 -18
  81. package/dist/bot/logic/squad/behaviours/combatSquad.js.map +1 -1
  82. package/dist/bot/logic/squad/behaviours/common.js +2 -19
  83. package/dist/bot/logic/squad/behaviours/common.js.map +1 -1
  84. package/dist/bot/logic/squad/behaviours/defenceSquad.js +15 -2
  85. package/dist/bot/logic/squad/behaviours/retreatSquad.js.map +1 -1
  86. package/dist/bot/logic/squad/behaviours/scoutingSquad.js +17 -21
  87. package/dist/bot/logic/squad/behaviours/scoutingSquad.js.map +1 -1
  88. package/dist/bot/logic/squad/squad.js +8 -5
  89. package/dist/bot/logic/squad/squad.js.map +1 -1
  90. package/dist/bot/logic/squad/squadBehaviour.js.map +1 -1
  91. package/dist/bot/logic/squad/squadController.js +3 -2
  92. package/dist/bot/logic/squad/squadController.js.map +1 -1
  93. package/dist/bot/logic/threat/threatCalculator.js +5 -5
  94. package/dist/bot/logic/threat/threatCalculator.js.map +1 -1
  95. package/dist/exampleBot.js +53 -16
  96. package/dist/exampleBot.js.map +1 -1
  97. package/package.json +5 -4
  98. package/src/bot/bot.ts +38 -53
  99. package/src/bot/logic/awareness.ts +34 -22
  100. package/src/bot/logic/building/antiAirStaticDefence.ts +64 -0
  101. package/src/bot/logic/building/antiGroundStaticDefence.ts +7 -20
  102. package/src/bot/logic/building/artilleryUnit.ts +2 -28
  103. package/src/bot/logic/building/basicAirUnit.ts +2 -33
  104. package/src/bot/logic/building/basicBuilding.ts +8 -6
  105. package/src/bot/logic/building/basicGroundUnit.ts +2 -46
  106. package/src/bot/logic/building/buildingRules.ts +73 -57
  107. package/src/bot/logic/building/common.ts +23 -0
  108. package/src/bot/logic/building/harvester.ts +2 -1
  109. package/src/bot/logic/building/queueController.ts +105 -42
  110. package/src/bot/logic/common/utils.ts +47 -0
  111. package/src/bot/logic/composition/alliedCompositions.ts +22 -0
  112. package/src/bot/logic/composition/common.ts +3 -0
  113. package/src/bot/logic/composition/sovietCompositions.ts +21 -0
  114. package/src/bot/logic/mission/actionBatcher.ts +124 -0
  115. package/src/bot/logic/mission/mission.ts +186 -37
  116. package/src/bot/logic/mission/missionController.ts +340 -31
  117. package/src/bot/logic/mission/missionFactories.ts +3 -3
  118. package/src/bot/logic/mission/missions/attackMission.ts +234 -56
  119. package/src/bot/logic/mission/missions/defenceMission.ts +72 -45
  120. package/src/bot/logic/mission/missions/engineerMission.ts +67 -15
  121. package/src/bot/logic/mission/missions/expansionMission.ts +67 -14
  122. package/src/bot/logic/mission/missions/retreatMission.ts +50 -6
  123. package/src/bot/logic/mission/missions/scoutingMission.ts +138 -14
  124. package/src/bot/logic/mission/missions/squads/combatSquad.ts +160 -0
  125. package/src/bot/logic/{squad/behaviours → mission/missions/squads}/common.ts +14 -20
  126. package/src/bot/logic/mission/missions/squads/squad.ts +19 -0
  127. package/src/bot/logic/threat/threat.ts +15 -15
  128. package/src/bot/logic/threat/threatCalculator.ts +10 -10
  129. package/src/exampleBot.ts +59 -19
  130. package/.prettierrc +0 -5
  131. package/TODO.md +0 -18
  132. package/dist/bot/logic/building/artilleryUnit.js.map +0 -1
  133. package/dist/bot/logic/building/massedAntiGroundUnit.js +0 -20
  134. package/dist/bot/logic/building/queues.js +0 -19
  135. package/dist/bot/logic/knowledge.js +0 -1
  136. package/dist/bot/logic/mission/basicMission.js +0 -26
  137. package/dist/bot/logic/mission/expansionMission.js +0 -32
  138. package/dist/bot/logic/squad/behaviours/squadExpansion.js +0 -31
  139. package/dist/bot/logic/squad/behaviours/squadScouters.js +0 -8
  140. package/rules.ini +0 -23126
  141. package/src/bot/logic/mission/missions/oneTimeMission.ts +0 -33
  142. package/src/bot/logic/squad/behaviours/combatSquad.ts +0 -127
  143. package/src/bot/logic/squad/behaviours/engineerSquad.ts +0 -53
  144. package/src/bot/logic/squad/behaviours/expansionSquad.ts +0 -59
  145. package/src/bot/logic/squad/behaviours/retreatSquad.ts +0 -44
  146. package/src/bot/logic/squad/squad.ts +0 -159
  147. package/src/bot/logic/squad/squadBehaviour.ts +0 -62
  148. package/src/bot/logic/squad/squadBehaviours.ts +0 -8
  149. package/src/bot/logic/squad/squadController.ts +0 -254
@@ -1,33 +0,0 @@
1
- import { GameApi, PlayerData } from "@chronodivide/game-api";
2
- import { GlobalThreat } from "../../threat/threat.js";
3
- import { Mission, MissionAction, disbandMission, noop } from "../mission.js";
4
- import { ExpansionSquad } from "../../squad/behaviours/expansionSquad.js";
5
- import { Squad } from "../../squad/squad.js";
6
- import { MissionFactory } from "../missionFactories.js";
7
- import { SquadBehaviour } from "../../squad/squadBehaviour.js";
8
- import { MatchAwareness } from "../../awareness.js";
9
- import { DebugLogger } from "../../common/utils.js";
10
-
11
- /**
12
- * A mission that gets dispatched once, and once the squad decides to disband, the mission is disbanded.
13
- */
14
- export abstract class OneTimeMission<T = undefined> extends Mission<T> {
15
- private hadSquad = false;
16
-
17
- constructor(uniqueName: string, priority: number, private behaviourFactory: () => SquadBehaviour, logger: DebugLogger) {
18
- super(uniqueName, priority, logger);
19
- }
20
-
21
- onAiUpdate(gameApi: GameApi, playerData: PlayerData, matchAwareness: MatchAwareness): MissionAction {
22
- if (this.getSquad() === null) {
23
- if (!this.hadSquad) {
24
- this.hadSquad = true;
25
- return this.setSquad(new Squad(this.getUniqueName(), this.behaviourFactory(), this));
26
- } else {
27
- return disbandMission();
28
- }
29
- } else {
30
- return noop();
31
- }
32
- }
33
- }
@@ -1,127 +0,0 @@
1
- import { ActionsApi, GameApi, GameMath, MovementZone, PlayerData, UnitData, Vector2 } from "@chronodivide/game-api";
2
- import { Squad } from "../squad.js";
3
- import { SquadAction, SquadBehaviour, grabCombatants, noop } from "../squadBehaviour.js";
4
- import { MatchAwareness } from "../../awareness.js";
5
- import { getAttackWeight, manageAttackMicro, manageMoveMicro } from "./common.js";
6
- import { DebugLogger, maxBy } from "../../common/utils.js";
7
-
8
- const TARGET_UPDATE_INTERVAL_TICKS = 10;
9
- const GRAB_INTERVAL_TICKS = 10;
10
-
11
- const GRAB_RADIUS = 20;
12
-
13
- // Units must be in a certain radius of the center of mass before attacking.
14
- // This scales for number of units in the squad though.
15
- const MIN_GATHER_RADIUS = 5;
16
-
17
- // If the radius expands beyond this amount then we should switch back to gathering mode.
18
- const MAX_GATHER_RADIUS = 15;
19
-
20
- const GATHER_RATIO = 10;
21
-
22
- enum SquadState {
23
- Gathering,
24
- Attacking,
25
- }
26
-
27
- export class CombatSquad implements SquadBehaviour {
28
- private lastGrab: number | null = null;
29
- private lastCommand: number | null = null;
30
- private state = SquadState.Gathering;
31
-
32
- /**
33
- *
34
- * @param rallyArea the initial location to grab combatants
35
- * @param targetArea
36
- * @param radius
37
- */
38
- constructor(
39
- private rallyArea: Vector2,
40
- private targetArea: Vector2,
41
- private radius: number,
42
- ) {}
43
-
44
- public setAttackArea(targetArea: Vector2) {
45
- this.targetArea = targetArea;
46
- }
47
-
48
- public onAiUpdate(
49
- gameApi: GameApi,
50
- actionsApi: ActionsApi,
51
- playerData: PlayerData,
52
- squad: Squad,
53
- matchAwareness: MatchAwareness,
54
- logger: DebugLogger,
55
- ): SquadAction {
56
- if (
57
- squad.getUnitIds().length > 0 &&
58
- (!this.lastCommand || gameApi.getCurrentTick() > this.lastCommand + TARGET_UPDATE_INTERVAL_TICKS)
59
- ) {
60
- this.lastCommand = gameApi.getCurrentTick();
61
- const centerOfMass = squad.getCenterOfMass();
62
- const maxDistance = squad.getMaxDistanceToCenterOfMass();
63
- const units = squad.getUnitsMatching(gameApi, (r) => r.rules.isSelectableCombatant);
64
-
65
- // Only use ground units for center of mass.
66
- const groundUnits = squad.getUnitsMatching(
67
- gameApi,
68
- (r) =>
69
- r.rules.isSelectableCombatant &&
70
- (r.rules.movementZone === MovementZone.Infantry ||
71
- r.rules.movementZone === MovementZone.Normal ||
72
- r.rules.movementZone === MovementZone.InfantryDestroyer),
73
- );
74
-
75
- if (this.state === SquadState.Gathering) {
76
- const requiredGatherRadius = GameMath.sqrt(groundUnits.length) * GATHER_RATIO + MIN_GATHER_RADIUS;
77
- if (
78
- centerOfMass &&
79
- maxDistance &&
80
- gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
81
- maxDistance > requiredGatherRadius
82
- ) {
83
- units.forEach((unit) => {
84
- manageMoveMicro(actionsApi, unit, centerOfMass);
85
- });
86
- } else {
87
- logger(`CombatSquad ${squad.getName()} switching back to attack mode (${maxDistance})`);
88
- this.state = SquadState.Attacking;
89
- }
90
- } else {
91
- const targetPoint = this.targetArea || playerData.startLocation;
92
- const requiredGatherRadius = GameMath.sqrt(groundUnits.length) * GATHER_RATIO + MAX_GATHER_RADIUS;
93
- if (
94
- centerOfMass &&
95
- maxDistance &&
96
- gameApi.mapApi.getTile(centerOfMass.x, centerOfMass.y) !== undefined &&
97
- maxDistance > requiredGatherRadius
98
- ) {
99
- // Switch back to gather mode
100
- logger(`CombatSquad ${squad.getName()} switching back to gather (${maxDistance})`);
101
- this.state = SquadState.Gathering;
102
- return noop();
103
- }
104
- for (const unit of units) {
105
- const { rx: x, ry: y } = unit.tile;
106
- const range = unit.primaryWeapon?.maxRange ?? unit.secondaryWeapon?.maxRange ?? 5;
107
- const nearbyHostiles = matchAwareness
108
- .getHostilesNearPoint(x, y, range * 2)
109
- .map(({ unitId }) => gameApi.getUnitData(unitId)) as UnitData[];
110
- const bestUnit = maxBy(nearbyHostiles, (target) => getAttackWeight(unit, target));
111
- if (bestUnit) {
112
- manageAttackMicro(actionsApi, unit, bestUnit);
113
- } else {
114
- manageMoveMicro(actionsApi, unit, targetPoint);
115
- }
116
- }
117
- }
118
- }
119
-
120
- if (!this.lastGrab || gameApi.getCurrentTick() > this.lastGrab + GRAB_INTERVAL_TICKS) {
121
- this.lastGrab = gameApi.getCurrentTick();
122
- return grabCombatants(squad.getCenterOfMass() ?? this.rallyArea, GRAB_RADIUS);
123
- } else {
124
- return noop();
125
- }
126
- }
127
- }
@@ -1,53 +0,0 @@
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
- }
@@ -1,59 +0,0 @@
1
- import { ActionsApi, GameApi, OrderType, PlayerData, SideType } from "@chronodivide/game-api";
2
- import { GlobalThreat } from "../../threat/threat.js";
3
- import { Squad } from "../squad.js";
4
- import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
5
- import { MatchAwareness } from "../../awareness.js";
6
-
7
- const DEPLOY_COOLDOWN_TICKS = 30;
8
-
9
- // Expansion or initial base.
10
- export class ExpansionSquad implements SquadBehaviour {
11
- private hasAttemptedDeployWith: {
12
- unitId: number;
13
- gameTick: number;
14
- } | null = null;
15
-
16
- /**
17
- * @param selectedMcv ID of the MCV to try to expand with. If that unit dies, the squad will disband. If no value is provided,
18
- * the mission requests an MCV.
19
- */
20
- constructor(private selectedMcv: number | null) {
21
- };
22
-
23
- public onAiUpdate(
24
- gameApi: GameApi,
25
- actionsApi: ActionsApi,
26
- playerData: PlayerData,
27
- squad: Squad,
28
- matchAwareness: MatchAwareness
29
- ): SquadAction {
30
- const mcvTypes = ["AMCV", "SMCV"];
31
- const mcvs = squad.getUnitsOfTypes(gameApi, ...mcvTypes);
32
- if (mcvs.length === 0) {
33
- // Perhaps we deployed already (or the unit was destroyed), end the mission.
34
- if (this.hasAttemptedDeployWith !== null) {
35
- return disband();
36
- }
37
- // We need an mcv!
38
- if (this.selectedMcv) {
39
- return requestSpecificUnits([this.selectedMcv], 100);
40
- } else {
41
- return requestUnits(mcvTypes, 100);
42
- }
43
- } else if (
44
- !this.hasAttemptedDeployWith ||
45
- gameApi.getCurrentTick() > this.hasAttemptedDeployWith.gameTick + DEPLOY_COOLDOWN_TICKS
46
- ) {
47
- actionsApi.orderUnits(
48
- mcvs.map((mcv) => mcv.id),
49
- OrderType.DeploySelected
50
- );
51
- // Add a cooldown to deploy attempts.
52
- this.hasAttemptedDeployWith = {
53
- unitId: mcvs[0].id,
54
- gameTick: gameApi.getCurrentTick(),
55
- };
56
- }
57
- return noop();
58
- }
59
- }
@@ -1,44 +0,0 @@
1
- import { ActionsApi, GameApi, OrderType, PlayerData, Vector2 } from "@chronodivide/game-api";
2
- import { GlobalThreat } from "../../threat/threat.js";
3
- import { Squad } from "../squad.js";
4
- import { SquadAction, SquadBehaviour, disband, noop, requestSpecificUnits, requestUnits } from "../squadBehaviour.js";
5
- import { MatchAwareness } from "../../awareness.js";
6
-
7
- const SCOUT_MOVE_COOLDOWN_TICKS = 30;
8
-
9
- export class RetreatSquad implements SquadBehaviour {
10
- private createdAt: number | null = null;
11
-
12
- constructor(
13
- private unitIds: number[],
14
- private retreatToPoint: Vector2,
15
- ) {}
16
-
17
- public onAiUpdate(
18
- gameApi: GameApi,
19
- actionsApi: ActionsApi,
20
- playerData: PlayerData,
21
- squad: Squad,
22
- matchAwareness: MatchAwareness,
23
- ): SquadAction {
24
- if (!this.createdAt) {
25
- this.createdAt = gameApi.getCurrentTick();
26
- }
27
- if (squad.getUnitIds().length > 0) {
28
- // Only send the order once we have managed to claim some units.
29
- actionsApi.orderUnits(
30
- squad.getUnitIds(),
31
- OrderType.AttackMove,
32
- this.retreatToPoint.x,
33
- this.retreatToPoint.y,
34
- );
35
- return disband();
36
- }
37
- if (this.createdAt && gameApi.getCurrentTick() > this.createdAt + 240) {
38
- // Disband automatically after 240 ticks in case we couldn't actually claim any units.
39
- return disband();
40
- } else {
41
- return requestSpecificUnits(this.unitIds, 1000);
42
- }
43
- }
44
- }
@@ -1,159 +0,0 @@
1
- import { ActionsApi, GameApi, PlayerData, TechnoRules, Tile, UnitData, Vector2 } from "@chronodivide/game-api";
2
- import { Mission } from "../mission/mission.js";
3
- import { SquadAction, SquadBehaviour, disband } from "./squadBehaviour.js";
4
- import { MatchAwareness } from "../awareness.js";
5
- import { getDistanceBetweenTileAndPoint } from "../map/map.js";
6
- import { DebugLogger } from "../common/utils.js";
7
-
8
- export enum SquadLiveness {
9
- SquadDead,
10
- SquadActive,
11
- }
12
-
13
- export type SquadConstructionRequest = {
14
- squad: Squad;
15
- unitType: TechnoRules;
16
- priority: number;
17
- };
18
-
19
- const calculateCenterOfMass: (unitTiles: Tile[]) => {
20
- centerOfMass: Vector2;
21
- maxDistance: number;
22
- } | null = (unitTiles) => {
23
- if (unitTiles.length === 0) {
24
- return null;
25
- }
26
- // TODO: use median here
27
- const sums = unitTiles.reduce(
28
- ({ x, y }, tile) => {
29
- return {
30
- x: x + (tile?.rx || 0),
31
- y: y + (tile?.ry || 0),
32
- };
33
- },
34
- { x: 0, y: 0 },
35
- );
36
- const centerOfMass = new Vector2(Math.round(sums.x / unitTiles.length), Math.round(sums.y / unitTiles.length));
37
-
38
- // max distance of units to the center of mass
39
- const distances = unitTiles.map((tile) => getDistanceBetweenTileAndPoint(tile, centerOfMass));
40
- const maxDistance = Math.max(...distances);
41
- return { centerOfMass, maxDistance };
42
- };
43
-
44
- export class Squad {
45
- private unitIds: number[] = [];
46
- private liveness: SquadLiveness = SquadLiveness.SquadActive;
47
- private lastLivenessUpdateTick: number = 0;
48
- private centerOfMass: Vector2 | null = null;
49
- private maxDistanceToCenterOfMass: number | null = null;
50
-
51
- constructor(
52
- private name: string,
53
- private behaviour: SquadBehaviour,
54
- private mission: Mission<any> | null,
55
- private killable = false,
56
- ) {}
57
-
58
- public getName(): string {
59
- return this.name;
60
- }
61
-
62
- public getCenterOfMass() {
63
- return this.centerOfMass;
64
- }
65
-
66
- public getMaxDistanceToCenterOfMass() {
67
- return this.maxDistanceToCenterOfMass;
68
- }
69
-
70
- public onAiUpdate(
71
- gameApi: GameApi,
72
- actionsApi: ActionsApi,
73
- playerData: PlayerData,
74
- matchAwareness: MatchAwareness,
75
- logger: DebugLogger,
76
- ): SquadAction {
77
- this.updateLiveness(gameApi);
78
- const movableUnitTiles = this.unitIds
79
- .map((unitId) => gameApi.getUnitData(unitId))
80
- .filter((unit) => unit?.canMove)
81
- .map((unit) => unit?.tile)
82
- .filter((tile) => !!tile) as Tile[];
83
- const tileMetrics = calculateCenterOfMass(movableUnitTiles);
84
- if (tileMetrics) {
85
- this.centerOfMass = tileMetrics.centerOfMass;
86
- this.maxDistanceToCenterOfMass = tileMetrics.maxDistance;
87
- } else {
88
- this.centerOfMass = null;
89
- this.maxDistanceToCenterOfMass = null;
90
- }
91
-
92
- if (this.mission && this.mission.isActive() == false) {
93
- // Orphaned squad, might get picked up later.
94
- this.mission.removeSquad();
95
- this.mission = null;
96
- return disband();
97
- } else if (!this.mission) {
98
- return disband();
99
- }
100
- return this.behaviour.onAiUpdate(gameApi, actionsApi, playerData, this, matchAwareness, logger);
101
- }
102
- public getMission(): Mission | null {
103
- return this.mission;
104
- }
105
-
106
- public setMission(mission: Mission | null) {
107
- if (this.mission != undefined && this.mission != mission) {
108
- this.mission.removeSquad();
109
- }
110
- this.mission = mission;
111
- }
112
-
113
- public getUnitIds(): number[] {
114
- return this.unitIds;
115
- }
116
-
117
- public getUnits(gameApi: GameApi): UnitData[] {
118
- return this.unitIds
119
- .map((unitId) => gameApi.getUnitData(unitId))
120
- .filter((unit) => unit != null)
121
- .map((unit) => unit!);
122
- }
123
-
124
- public getUnitsOfTypes(gameApi: GameApi, ...names: string[]): UnitData[] {
125
- return this.unitIds
126
- .map((unitId) => gameApi.getUnitData(unitId))
127
- .filter((unit) => !!unit && names.includes(unit.name))
128
- .map((unit) => unit!);
129
- }
130
-
131
- public getUnitsMatching(gameApi: GameApi, filter: (r: UnitData) => boolean): UnitData[] {
132
- return this.unitIds
133
- .map((unitId) => gameApi.getUnitData(unitId))
134
- .filter((unit) => !!unit && filter(unit))
135
- .map((unit) => unit!);
136
- }
137
-
138
- public removeUnit(unitIdToRemove: number): void {
139
- this.unitIds = this.unitIds.filter((unitId) => unitId != unitIdToRemove);
140
- }
141
-
142
- public addUnit(unitIdToAdd: number): void {
143
- this.unitIds.push(unitIdToAdd);
144
- }
145
-
146
- private updateLiveness(gameApi: GameApi) {
147
- this.unitIds = this.unitIds.filter((unitId) => gameApi.getUnitData(unitId));
148
- this.lastLivenessUpdateTick = gameApi.getCurrentTick();
149
- if (this.killable && this.unitIds.length == 0) {
150
- if (this.liveness == SquadLiveness.SquadActive) {
151
- this.liveness = SquadLiveness.SquadDead;
152
- }
153
- }
154
- }
155
-
156
- public getLiveness() {
157
- return this.liveness;
158
- }
159
- }
@@ -1,62 +0,0 @@
1
- import { ActionsApi, GameApi, PlayerData, Vector2 } from "@chronodivide/game-api";
2
- import { Squad } from "./squad.js";
3
- import { MatchAwareness } from "../awareness.js";
4
- import { DebugLogger } from "../common/utils.js";
5
-
6
- export type SquadActionNoop = {
7
- type: "noop";
8
- };
9
- export type SquadActionDisband = {
10
- type: "disband";
11
- };
12
- export type SquadActionMergeInto = {
13
- type: "mergeInto";
14
- mergeInto: Squad;
15
- };
16
- export type SquadActionRequestUnits = {
17
- type: "request";
18
- unitNames: string[];
19
- priority: number;
20
- };
21
- export type SquadActionRequestSpecificUnits = {
22
- type: "requestSpecific";
23
- unitIds: number[];
24
- priority: number;
25
- };
26
- export type SquadActionGrabFreeCombatants = {
27
- type: "requestCombatants";
28
- point: Vector2;
29
- radius: number;
30
- };
31
-
32
- export const noop = () => ({ type: "noop" }) as SquadActionNoop;
33
-
34
- export const disband = () => ({ type: "disband" }) as SquadActionDisband;
35
-
36
- export const requestUnits = (unitNames: string[], priority: number) =>
37
- ({ type: "request", unitNames, priority }) as SquadActionRequestUnits;
38
-
39
- export const requestSpecificUnits = (unitIds: number[], priority: number) =>
40
- ({ type: "requestSpecific", unitIds, priority }) as SquadActionRequestSpecificUnits;
41
-
42
- export const grabCombatants = (point: Vector2, radius: number) =>
43
- ({ type: "requestCombatants", point, radius }) as SquadActionGrabFreeCombatants;
44
-
45
- export type SquadAction =
46
- | SquadActionNoop
47
- | SquadActionDisband
48
- | SquadActionMergeInto
49
- | SquadActionRequestUnits
50
- | SquadActionRequestSpecificUnits
51
- | SquadActionGrabFreeCombatants;
52
-
53
- export interface SquadBehaviour {
54
- onAiUpdate(
55
- gameApi: GameApi,
56
- actionsApi: ActionsApi,
57
- playerData: PlayerData,
58
- squad: Squad,
59
- matchAwareness: MatchAwareness,
60
- logger: DebugLogger,
61
- ): SquadAction;
62
- }
@@ -1,8 +0,0 @@
1
- import { ExpansionSquad } from "./behaviours/expansionSquad.js";
2
- import { SquadBehaviour } from "./squadBehaviour.js";
3
-
4
- /*
5
- export const ALL_SQUAD_BEHAVIOURS: SquadBehaviour[] = [
6
- //new SquadScouters(),
7
- new SquadExpansion(),
8
- ];*/