mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-08-04 23:37:09 +00:00
Merge branch 'main' of github.com:openfrontio/OpenFrontIO
This commit is contained in:
+325
-261
File diff suppressed because one or more lines are too long
+187
-7
@@ -6,38 +6,218 @@
|
|||||||
{
|
{
|
||||||
"coordinates": [1173, 1984],
|
"coordinates": [1173, 1984],
|
||||||
"name": "South Africa",
|
"name": "South Africa",
|
||||||
"strength": 3,
|
"strength": 2,
|
||||||
"flag": "za"
|
"flag": "za"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": [1681, 752],
|
"coordinates": [1218, 599],
|
||||||
"name": "Sudan",
|
"name": "Sudan",
|
||||||
"strength": 2,
|
"strength": 2,
|
||||||
"flag": "sd"
|
"flag": "sd"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": [1850, 1691],
|
"coordinates": [1850, 1648],
|
||||||
"name": "Madagascar",
|
"name": "Madagascar",
|
||||||
"strength": 1,
|
"strength": 1,
|
||||||
"flag": "mg"
|
"flag": "mg"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": [1111, 1019],
|
"coordinates": [1090, 866],
|
||||||
"name": "Central African Republic",
|
"name": "Central African Republic",
|
||||||
"strength": 2,
|
"strength": 2,
|
||||||
"flag": "cf"
|
"flag": "cf"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": [746, 124],
|
"coordinates": [768, 82],
|
||||||
"name": "Tunisia",
|
"name": "Tunisia",
|
||||||
"strength": 3,
|
"strength": 2,
|
||||||
"flag": "tn"
|
"flag": "tn"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"coordinates": [1397, 304],
|
"coordinates": [1397, 304],
|
||||||
"name": "Egypt",
|
"name": "Egypt",
|
||||||
"strength": 3,
|
"strength": 2,
|
||||||
"flag": "eg"
|
"flag": "eg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [305, 221],
|
||||||
|
"name": "Morocco",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ma"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [539, 234],
|
||||||
|
"name": "Algeria",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "dz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [975, 272],
|
||||||
|
"name": "Libya",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "dz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [787, 534],
|
||||||
|
"name": "Niger",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ne"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [213, 871],
|
||||||
|
"name": "Sierra Leon",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "sl"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [351, 550],
|
||||||
|
"name": "Mali",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [273, 881],
|
||||||
|
"name": "Liberia",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "lr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [745, 831],
|
||||||
|
"name": "Nigeria",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ng"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1122, 1145],
|
||||||
|
"name": "Democratic Republic of the Congo",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "cg"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1047, 1430],
|
||||||
|
"name": "Angola",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ao"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1038, 1737],
|
||||||
|
"name": "Namibia",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "na"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1204, 1720],
|
||||||
|
"name": "Botswana",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "bw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [622, 893],
|
||||||
|
"name": "Benin",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "bj"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1459, 1724],
|
||||||
|
"name": "Zimbabwa",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "zw"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1570, 1531],
|
||||||
|
"name": "Mozambique",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "na"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1452, 1250],
|
||||||
|
"name": "Tanzania",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "tz"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1633, 978],
|
||||||
|
"name": "Kenya",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ke"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1906, 831],
|
||||||
|
"name": "Somalia",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "so"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1623, 871],
|
||||||
|
"name": "Ethiopia",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "et"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1382, 808],
|
||||||
|
"name": "South Sudan",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ss"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1510, 216],
|
||||||
|
"name": "Israel",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "il"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1601, 66],
|
||||||
|
"name": "Syria",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "sy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1812, 195],
|
||||||
|
"name": "Iraq",
|
||||||
|
"strength": 1,
|
||||||
|
"flag": "iq"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1802, 399],
|
||||||
|
"name": "Saudi Arabia",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "sa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1811, 668],
|
||||||
|
"name": "Yemen",
|
||||||
|
"strength": 1,
|
||||||
|
"flag": "ye"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [290, 16],
|
||||||
|
"name": "Portugal",
|
||||||
|
"strength": 1,
|
||||||
|
"flag": "pt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [1152, 27],
|
||||||
|
"name": "Greece",
|
||||||
|
"strength": 1,
|
||||||
|
"flag": "gr"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [937, 24],
|
||||||
|
"name": "Italy",
|
||||||
|
"strength": 1,
|
||||||
|
"flag": "it"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [102, 674],
|
||||||
|
"name": "Senegal",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "sn"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"coordinates": [898, 1099],
|
||||||
|
"name": "Gabon",
|
||||||
|
"strength": 2,
|
||||||
|
"flag": "ga"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 4.1 MiB |
+126
-110
File diff suppressed because one or more lines are too long
@@ -165,6 +165,10 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
|||||||
const myPlayer = this.myPlayer();
|
const myPlayer = this.myPlayer();
|
||||||
const isAlly = myPlayer?.isAlliedWith(player);
|
const isAlly = myPlayer?.isAlliedWith(player);
|
||||||
let relationHtml = null;
|
let relationHtml = null;
|
||||||
|
const attackingTroops = player
|
||||||
|
.outgoingAttacks()
|
||||||
|
.map((a) => a.troops)
|
||||||
|
.reduce((a, b) => a + b, 0);
|
||||||
|
|
||||||
if (player.type() == PlayerType.FakeHuman && myPlayer != null) {
|
if (player.type() == PlayerType.FakeHuman && myPlayer != null) {
|
||||||
const relation =
|
const relation =
|
||||||
@@ -201,12 +205,29 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
|||||||
${player.name()}
|
${player.name()}
|
||||||
</div>
|
</div>
|
||||||
<div class="text-sm opacity-80">Type: ${playerType}</div>
|
<div class="text-sm opacity-80">Type: ${playerType}</div>
|
||||||
<div class="text-sm opacity-80" translate="no">
|
${player.troops() >= 1 &&
|
||||||
Troops: ${renderTroops(player.troops())}
|
html`<div class="text-sm opacity-80" translate="no">
|
||||||
</div>
|
Defending troops: ${renderTroops(player.troops())}
|
||||||
|
</div>`}
|
||||||
|
${attackingTroops >= 1 &&
|
||||||
|
html`<div class="text-sm opacity-80" translate="no">
|
||||||
|
Attacking troops: ${renderTroops(attackingTroops)}
|
||||||
|
</div>`}
|
||||||
<div class="text-sm opacity-80" translate="no">
|
<div class="text-sm opacity-80" translate="no">
|
||||||
Gold: ${renderNumber(player.gold())}
|
Gold: ${renderNumber(player.gold())}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="text-sm opacity-80" translate="no">
|
||||||
|
Ports: ${player.units(UnitType.Port).length}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm opacity-80" translate="no">
|
||||||
|
Cities: ${player.units(UnitType.City).length}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm opacity-80" translate="no">
|
||||||
|
Missile launchers: ${player.units(UnitType.MissileSilo).length}
|
||||||
|
</div>
|
||||||
|
<div class="text-sm opacity-80" translate="no">
|
||||||
|
SAMs: ${player.units(UnitType.SAMLauncher).length}
|
||||||
|
</div>
|
||||||
${relationHtml}
|
${relationHtml}
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -409,7 +409,7 @@ export class DefaultConfig implements Config {
|
|||||||
(defender.isTraitor() ? this.traitorDefenseDebuff() : 1),
|
(defender.isTraitor() ? this.traitorDefenseDebuff() : 1),
|
||||||
defenderTroopLoss: defender.troops() / defender.numTilesOwned(),
|
defenderTroopLoss: defender.troops() / defender.numTilesOwned(),
|
||||||
tilesPerTickUsed:
|
tilesPerTickUsed:
|
||||||
within(defender.troops() / (4 * attackTroops), 0.2, 1.5) *
|
within(defender.troops() / (5 * attackTroops), 0.2, 1.5) *
|
||||||
speed *
|
speed *
|
||||||
largeModifier,
|
largeModifier,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export class SAMLauncherExecution implements Execution {
|
|||||||
|
|
||||||
private target: Unit = null;
|
private target: Unit = null;
|
||||||
|
|
||||||
private searchRange = 100;
|
private searchRangeRadius = 75;
|
||||||
|
|
||||||
private missileAttackRate = 100; // 10 seconds
|
private missileAttackRate = 75; // 7.5 seconds
|
||||||
private lastMissileAttack = 0;
|
private lastMissileAttack = 0;
|
||||||
|
|
||||||
private pseudoRandom: PseudoRandom;
|
private pseudoRandom: PseudoRandom;
|
||||||
@@ -63,10 +63,16 @@ export class SAMLauncherExecution implements Execution {
|
|||||||
|
|
||||||
const nukes = this.mg
|
const nukes = this.mg
|
||||||
.units(UnitType.AtomBomb, UnitType.HydrogenBomb)
|
.units(UnitType.AtomBomb, UnitType.HydrogenBomb)
|
||||||
.filter(
|
.filter((u) => {
|
||||||
(u) =>
|
// (x - center_x)² + (y - center_y)² < radius²
|
||||||
this.mg.manhattanDist(u.tile(), this.post.tile()) < this.searchRange,
|
const x = this.mg.x(u.tile());
|
||||||
)
|
const y = this.mg.y(u.tile());
|
||||||
|
const centerX = this.mg.x(this.post.tile());
|
||||||
|
const centerY = this.mg.y(this.post.tile());
|
||||||
|
const isInRange =
|
||||||
|
(x - centerX) ** 2 + (y - centerY) ** 2 < this.searchRangeRadius ** 2;
|
||||||
|
return isInRange;
|
||||||
|
})
|
||||||
.filter((u) => u.owner() !== this.player)
|
.filter((u) => u.owner() !== this.player)
|
||||||
.filter((u) => !u.owner().isAlliedWith(this.player));
|
.filter((u) => !u.owner().isAlliedWith(this.player));
|
||||||
|
|
||||||
|
|||||||
@@ -23,10 +23,8 @@ export class SAMMissileExecution implements Execution {
|
|||||||
private target: Unit,
|
private target: Unit,
|
||||||
private mg: Game,
|
private mg: Game,
|
||||||
private pseudoRandom: number,
|
private pseudoRandom: number,
|
||||||
private speed: number = 6,
|
private speed: number = 12,
|
||||||
// Regular atom bomb or warhead of MIRV
|
|
||||||
private hittingChance: number = 0.75,
|
private hittingChance: number = 0.75,
|
||||||
private hittingChanceHydrogen: number = 0.1,
|
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
init(mg: Game, ticks: number): void {
|
init(mg: Game, ticks: number): void {
|
||||||
@@ -45,10 +43,13 @@ export class SAMMissileExecution implements Execution {
|
|||||||
this.active = false;
|
this.active = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Mirv warheads are too fast, and mirv shouldn't be stopped ever
|
||||||
|
const nukesWhitelist = [UnitType.AtomBomb, UnitType.HydrogenBomb];
|
||||||
if (
|
if (
|
||||||
!this.target.isActive() ||
|
!this.target.isActive() ||
|
||||||
!this.ownerUnit.isActive() ||
|
!this.ownerUnit.isActive() ||
|
||||||
this.target.owner() == this.SAMMissile.owner()
|
this.target.owner() == this.SAMMissile.owner() ||
|
||||||
|
!nukesWhitelist.includes(this.target.type())
|
||||||
) {
|
) {
|
||||||
this.SAMMissile.delete(false);
|
this.SAMMissile.delete(false);
|
||||||
this.active = false;
|
this.active = false;
|
||||||
@@ -63,22 +64,7 @@ export class SAMMissileExecution implements Execution {
|
|||||||
switch (result.type) {
|
switch (result.type) {
|
||||||
case PathFindResultType.Completed:
|
case PathFindResultType.Completed:
|
||||||
this.active = false;
|
this.active = false;
|
||||||
let hit = false;
|
if (this.pseudoRandom < this.hittingChance) {
|
||||||
if (
|
|
||||||
this.target.type() == UnitType.HydrogenBomb &&
|
|
||||||
this.pseudoRandom < this.hittingChanceHydrogen
|
|
||||||
) {
|
|
||||||
hit = true;
|
|
||||||
} else if (
|
|
||||||
[UnitType.MIRVWarhead, UnitType.AtomBomb].includes(
|
|
||||||
this.target.type(),
|
|
||||||
) &&
|
|
||||||
this.pseudoRandom < this.hittingChance
|
|
||||||
) {
|
|
||||||
hit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hit) {
|
|
||||||
this.target.delete();
|
this.target.delete();
|
||||||
|
|
||||||
this.mg.displayMessage(
|
this.mg.displayMessage(
|
||||||
@@ -88,7 +74,7 @@ export class SAMMissileExecution implements Execution {
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.mg.displayMessage(
|
this.mg.displayMessage(
|
||||||
`Missile failed to intercept ${this.target.type()}`,
|
`Missile failed to target ${this.target.type()}`,
|
||||||
MessageType.ERROR,
|
MessageType.ERROR,
|
||||||
this._owner.id(),
|
this._owner.id(),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ class Terrain {
|
|||||||
|
|
||||||
export async function generateMap(
|
export async function generateMap(
|
||||||
imageBuffer: Buffer,
|
imageBuffer: Buffer,
|
||||||
|
removeSmall = true,
|
||||||
): Promise<{ map: Uint8Array; miniMap: Uint8Array }> {
|
): Promise<{ map: Uint8Array; miniMap: Uint8Array }> {
|
||||||
const stream = Readable.from(imageBuffer);
|
const stream = Readable.from(imageBuffer);
|
||||||
const img = await decodePNGFromStream(stream);
|
const img = await decodePNGFromStream(stream);
|
||||||
@@ -56,8 +57,10 @@ export async function generateMap(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSmallIslands(terrain);
|
if (removeSmall) {
|
||||||
removeSmallLakes(terrain);
|
removeSmallIslands(terrain);
|
||||||
|
removeSmallLakes(terrain);
|
||||||
|
}
|
||||||
const shorelineWaters = processShore(terrain);
|
const shorelineWaters = processShore(terrain);
|
||||||
processDistToLand(shorelineWaters, terrain);
|
processDistToLand(shorelineWaters, terrain);
|
||||||
processOcean(terrain);
|
processOcean(terrain);
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
Game,
|
||||||
|
Player,
|
||||||
|
PlayerInfo,
|
||||||
|
PlayerType,
|
||||||
|
UnitType,
|
||||||
|
} from "../src/core/game/Game";
|
||||||
|
import { SpawnExecution } from "../src/core/execution/SpawnExecution";
|
||||||
|
import { setup } from "./util/Setup";
|
||||||
|
import { constructionExecution } from "./util/utils";
|
||||||
|
|
||||||
|
let game: Game;
|
||||||
|
let player1: Player;
|
||||||
|
let player2: Player;
|
||||||
|
|
||||||
|
describe("Warship", () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
game = await setup("half_land_half_ocean", {
|
||||||
|
infiniteGold: true,
|
||||||
|
instantBuild: true,
|
||||||
|
});
|
||||||
|
const player_1_info = new PlayerInfo(
|
||||||
|
"us",
|
||||||
|
"boat dude",
|
||||||
|
PlayerType.Human,
|
||||||
|
null,
|
||||||
|
"player_1_id",
|
||||||
|
);
|
||||||
|
game.addPlayer(player_1_info, 1000);
|
||||||
|
const player_2_info = new PlayerInfo(
|
||||||
|
"us",
|
||||||
|
"boat dude",
|
||||||
|
PlayerType.Human,
|
||||||
|
null,
|
||||||
|
"player_2_id",
|
||||||
|
);
|
||||||
|
game.addPlayer(player_2_info, 1000);
|
||||||
|
|
||||||
|
const spawnTile = game.map().ref(0, 0);
|
||||||
|
game.addExecution(
|
||||||
|
new SpawnExecution(game.player(player_1_info.id).info(), spawnTile),
|
||||||
|
new SpawnExecution(game.player(player_2_info.id).info(), spawnTile),
|
||||||
|
);
|
||||||
|
|
||||||
|
while (game.inSpawnPhase()) {
|
||||||
|
game.executeNextTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
player1 = game.player(player_1_info.id);
|
||||||
|
player2 = game.player(player_2_info.id);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Warship heals only if player has port", async () => {
|
||||||
|
const maxHealth = game.config().unitInfo(UnitType.Warship).maxHealth;
|
||||||
|
|
||||||
|
const port = player1.buildUnit(UnitType.Port, 0, game.ref(0, 0));
|
||||||
|
const warship = player1.buildUnit(UnitType.Warship, 0, game.ref(7, 7));
|
||||||
|
|
||||||
|
game.executeNextTick();
|
||||||
|
|
||||||
|
expect(warship.health()).toBe(maxHealth);
|
||||||
|
warship.modifyHealth(-10);
|
||||||
|
expect(warship.health()).toBe(maxHealth - 10);
|
||||||
|
game.executeNextTick();
|
||||||
|
expect(warship.health()).toBe(maxHealth - 9);
|
||||||
|
|
||||||
|
port.delete();
|
||||||
|
|
||||||
|
game.executeNextTick();
|
||||||
|
expect(warship.health()).toBe(maxHealth - 9);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Warship captures trade if player has port", async () => {
|
||||||
|
constructionExecution(game, player1.id(), 0, 0, UnitType.Port);
|
||||||
|
constructionExecution(game, player1.id(), 7, 7, UnitType.Warship);
|
||||||
|
// Warship need one more tick (for warship exec to actually build warship)
|
||||||
|
game.executeNextTick();
|
||||||
|
expect(player1.units(UnitType.Warship)).toHaveLength(1);
|
||||||
|
|
||||||
|
// Cannot buildExec with trade ship as it's not buildable (but
|
||||||
|
// we can obviously directly add it to the player)
|
||||||
|
const tradeShip = player2.buildUnit(UnitType.TradeShip, 0, game.ref(6, 6));
|
||||||
|
|
||||||
|
expect(tradeShip.owner().id()).toBe(player2.id());
|
||||||
|
// Let plenty of time for A* to execute
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
game.executeNextTick();
|
||||||
|
}
|
||||||
|
expect(tradeShip.owner().id()).toBe(player1.id());
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Warship do not capture trade if player has no port", async () => {
|
||||||
|
constructionExecution(game, player1.id(), 0, 0, UnitType.Port);
|
||||||
|
constructionExecution(game, player1.id(), 7, 7, UnitType.Warship);
|
||||||
|
expect(player1.units(UnitType.Warship)).toHaveLength(1);
|
||||||
|
|
||||||
|
player1.units(UnitType.Port)[0].delete();
|
||||||
|
// Cannot buildExec with trade ship as it's not buildable (but
|
||||||
|
// we can obviously directly add it to the player)
|
||||||
|
const tradeShip = player2.buildUnit(UnitType.TradeShip, 0, game.ref(6, 6));
|
||||||
|
|
||||||
|
expect(tradeShip.owner().id()).toBe(player2.id());
|
||||||
|
// Let plenty of time for A* to execute
|
||||||
|
for (let i = 0; i < 10; i++) {
|
||||||
|
game.executeNextTick();
|
||||||
|
}
|
||||||
|
expect(tradeShip.owner().id()).toBe(player2.id());
|
||||||
|
});
|
||||||
|
});
|
||||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 96 B |
+4
-2
@@ -7,12 +7,13 @@ import { TestConfig } from "./TestConfig";
|
|||||||
import { TestServerConfig } from "./TestServerConfig";
|
import { TestServerConfig } from "./TestServerConfig";
|
||||||
import { UserSettings } from "../../src/core/game/UserSettings";
|
import { UserSettings } from "../../src/core/game/UserSettings";
|
||||||
import { Difficulty, GameType } from "../../src/core/game/Game";
|
import { Difficulty, GameType } from "../../src/core/game/Game";
|
||||||
|
import { GameConfig } from "../../src/core/Schemas";
|
||||||
|
|
||||||
export async function setup(mapName: string) {
|
export async function setup(mapName: string, _gameConfig: GameConfig = {}) {
|
||||||
// Load the specified map
|
// Load the specified map
|
||||||
const mapPath = path.join(__dirname, "..", "testdata", `${mapName}.png`);
|
const mapPath = path.join(__dirname, "..", "testdata", `${mapName}.png`);
|
||||||
const imageBuffer = await fs.readFile(mapPath);
|
const imageBuffer = await fs.readFile(mapPath);
|
||||||
const { map, miniMap } = await generateMap(imageBuffer);
|
const { map, miniMap } = await generateMap(imageBuffer, false);
|
||||||
const gameMap = await genTerrainFromBin(String.fromCharCode.apply(null, map));
|
const gameMap = await genTerrainFromBin(String.fromCharCode.apply(null, map));
|
||||||
const miniGameMap = await genTerrainFromBin(
|
const miniGameMap = await genTerrainFromBin(
|
||||||
String.fromCharCode.apply(null, miniMap),
|
String.fromCharCode.apply(null, miniMap),
|
||||||
@@ -30,6 +31,7 @@ export async function setup(mapName: string) {
|
|||||||
infiniteGold: false,
|
infiniteGold: false,
|
||||||
infiniteTroops: false,
|
infiniteTroops: false,
|
||||||
instantBuild: false,
|
instantBuild: false,
|
||||||
|
..._gameConfig,
|
||||||
};
|
};
|
||||||
const config = new TestConfig(serverConfig, gameConfig, new UserSettings());
|
const config = new TestConfig(serverConfig, gameConfig, new UserSettings());
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
// Either someone can straight up call player.buildUnit. It's simpler and immediate (no tick required)
|
||||||
|
// Either someone can straight up call player.buildUnit. It's simpler and immediate (no tick required)
|
||||||
|
// However buildUnit do not create executions (e.g.: WarshipExecution)
|
||||||
|
// If you also need execution use function below. Does not work with things not
|
||||||
|
|
||||||
|
import { ConstructionExecution } from "../../src/core/execution/ConstructionExecution";
|
||||||
|
import { Game, PlayerID, UnitType } from "../../src/core/game/Game";
|
||||||
|
|
||||||
|
// built via UI (e.g.: trade ships)
|
||||||
|
export function constructionExecution(
|
||||||
|
game: Game,
|
||||||
|
playerID: PlayerID,
|
||||||
|
x: number,
|
||||||
|
y: number,
|
||||||
|
unit: UnitType,
|
||||||
|
) {
|
||||||
|
game.addExecution(new ConstructionExecution(playerID, game.ref(x, y), unit));
|
||||||
|
// Init
|
||||||
|
game.executeNextTick();
|
||||||
|
// Exec
|
||||||
|
game.executeNextTick();
|
||||||
|
game.executeNextTick();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user