diff --git a/TODO.txt b/TODO.txt index a6c902b6c..2689f4449 100644 --- a/TODO.txt +++ b/TODO.txt @@ -162,9 +162,9 @@ * disable double tap on mobile DONE 10/6/2024 * donate troops button DONE 10/7/2024 * Make fake humans spawn by their country +* Test on android * Increase disk size * NPC more likely to accept alliance fewer alliance player has -* rewrite mobile input handling * fake humans target enemies * create private lobby menu * block user inputs if too far behind server diff --git a/resources/World.bin b/resources/World.bin deleted file mode 100644 index 72b735041..000000000 Binary files a/resources/World.bin and /dev/null differ diff --git a/resources/WorldMap.bin b/resources/WorldMap.bin deleted file mode 100644 index 14c3da6b7..000000000 Binary files a/resources/WorldMap.bin and /dev/null differ diff --git a/resources/WorldSmall.bin b/resources/WorldSmall.bin deleted file mode 100644 index 14c3da6b7..000000000 Binary files a/resources/WorldSmall.bin and /dev/null differ diff --git a/resources/maps/WorldMap.json b/resources/maps/WorldMap.json index f2117e0bb..a7f40bd51 100644 --- a/resources/maps/WorldMap.json +++ b/resources/maps/WorldMap.json @@ -2,14 +2,14 @@ "name": "World", "width": 2000, "height": 1000, - "countries": [ + "nations": [ { "coordinates": [ 375, 272 ], "name": "USA", - "strength": 1 + "strength": 3 }, { "coordinates": [ @@ -17,7 +17,7 @@ 136 ], "name": "Canada", - "strength": 1 + "strength": 2 }, { "coordinates": [ @@ -73,7 +73,7 @@ 975 ], "name": "Antartica", - "strength": 1 + "strength": 3 }, { "coordinates": [ @@ -81,7 +81,7 @@ 57 ], "name": "Greenland", - "strength": 1 + "strength": 2 }, { "coordinates": [ @@ -209,7 +209,7 @@ 136 ], "name": "Russia", - "strength": 1 + "strength": 3 }, { "coordinates": [ @@ -225,7 +225,7 @@ 328 ], "name": "China", - "strength": 1 + "strength": 3 }, { "coordinates": [ @@ -233,7 +233,7 @@ 373 ], "name": "India", - "strength": 1 + "strength": 2 }, { "coordinates": [ @@ -265,7 +265,7 @@ 657 ], "name": "Australia", - "strength": 1 + "strength": 2 }, { "coordinates": [ @@ -273,7 +273,7 @@ 775 ], "name": "New Zealand", - "strength": 1 + "strength": 0.5 }, { "coordinates": [ @@ -353,7 +353,7 @@ 627 ], "name": "Madagascar", - "strength": 1 + "strength": 0.5 }, { "coordinates": [ diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index 793d1247a..455b4ccd0 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -18,9 +18,9 @@ export const devConfig = new class extends DefaultConfig { return 100 } - // numBots(): number { - // return 0 - // } + numBots(): number { + return 400 + } // allianceDuration(): Tick { // return 10 * 10 diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index 3af8f2149..128644869 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -26,7 +26,8 @@ export class Executor { private random: PseudoRandom = null constructor(private gs: Game, private gameID: GameID) { - this.random = new PseudoRandom(simpleHash(gameID)) + // Add one to avoid id collisions with bots. + this.random = new PseudoRandom(simpleHash(gameID) + 1) } createExecs(turn: Turn): Execution[] { @@ -86,15 +87,17 @@ export class Executor { fakeHumanExecutions(numFakes: number): Execution[] { const execs = [] - for (let i = 0; i < numFakes; i++) { - execs.push( - new FakeHumanExecution(new PlayerInfo( - this.usernames[this.random.nextInt(0, this.usernames.length)], + for (const nation of this.gs.nations()) { + execs.push(new FakeHumanExecution( + new PlayerInfo( + nation.name, PlayerType.FakeHuman, null, this.random.nextID() - )) - ) + ), + nation.cell, + nation.strength + )) } return execs } diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index ef3c361f1..cc1dc6f8d 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -21,7 +21,7 @@ export class FakeHumanExecution implements Execution { private isTraitor = false - constructor(private playerInfo: PlayerInfo) { + constructor(private playerInfo: PlayerInfo, private cell: Cell, private strength: number) { this.random = new PseudoRandom(simpleHash(playerInfo.id)) } @@ -41,12 +41,14 @@ export class FakeHumanExecution implements Execution { this.randomLand().cell() )) } + return } if (this.player == null) { this.player = this.mg.players().find(p => p.id() == this.playerInfo.id) if (this.player == null) { - //console.log(`player with id ${this.playerInfo.id} not found in FakeHumanExecution`) return + } else { + this.player.setTroops(this.player.troops() * this.strength) } } @@ -194,8 +196,15 @@ export class FakeHumanExecution implements Execution { } randomLand(): Tile { + const delta = 25 while (true) { - const cell = new Cell(this.random.nextInt(0, this.mg.width()), this.random.nextInt(0, this.mg.height())) + const cell = new Cell( + this.random.nextInt(this.cell.x - delta, this.cell.x + delta), + this.random.nextInt(this.cell.y - delta, this.cell.y + delta) + ) + if (!this.mg.isOnMap(cell)) { + continue + } const tile = this.mg.tile(cell) if (tile.isLand() && !tile.hasOwner()) { if (tile.terrain() == TerrainType.Mountain && this.random.chance(2)) { diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 75c5579a2..3d9a3724c 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -11,6 +11,14 @@ export type Tick = number export const AllPlayers = "AllPlayers" as const; +export class Nation { + constructor( + public readonly name: string, + public readonly cell: Cell, + public readonly strength: number, + ) { } +} + export class EmojiMessage { constructor( public readonly sender: Player, @@ -217,6 +225,7 @@ export interface Game { ticks(): Tick inSpawnPhase(): boolean addExecution(...exec: Execution[]): void + nations(): Nation[] config(): Config displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void } diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 1346f56f1..79e298f03 100644 --- a/src/core/game/GameImpl.ts +++ b/src/core/game/GameImpl.ts @@ -1,7 +1,7 @@ import {info} from "console"; import {Config} from "../configuration/Config"; import {EventBus} from "../EventBus"; -import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent} from "./Game"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent, Nation} from "./Game"; import {TerrainMap} from "./TerrainMapLoader"; import {PlayerImpl} from "./PlayerImpl"; import {TerraNulliusImpl} from "./TerraNulliusImpl"; @@ -24,6 +24,9 @@ export class GameImpl implements MutableGame { // idCounter: PlayerID = 1; // Zero reserved for TerraNullius map: TileImpl[][] + + private nations_: Nation[] = [] + _players: Map = new Map private execs: Execution[] = [] private _width: number @@ -47,6 +50,15 @@ export class GameImpl implements MutableGame { this.map[x][y] = new TileImpl(this, this._terraNullius, cell, terrainMap.terrain(cell)); } } + this.nations_ = terrainMap.nationMap.nations + .map(n => new Nation( + n.name, + new Cell(n.coordinates[0], n.coordinates[1]), + n.strength + )) + } + nations(): Nation[] { + return this.nations_ } createAllianceRequest(requestor: MutablePlayer, recipient: Player): MutableAllianceRequest { diff --git a/src/core/game/TerrainMapLoader.ts b/src/core/game/TerrainMapLoader.ts index 637bd844f..75b7bebab 100644 --- a/src/core/game/TerrainMapLoader.ts +++ b/src/core/game/TerrainMapLoader.ts @@ -1,8 +1,27 @@ import {Cell, TerrainType} from './Game'; import binAsString from "!!binary-loader!../../../resources/maps/WorldMap.bin"; +import worldMapInfo from "../../../resources/maps/WorldMap.json" + +export interface NationMap { + name: string; + width: number; + height: number; + nations: Nation[]; +} + +export interface Nation { + coordinates: [number, number]; + name: string; + strength: number; +} + export class TerrainMap { - constructor(public readonly tiles: Terrain[][], public readonly numLandTiles: number) { } + constructor( + public readonly tiles: Terrain[][], + public readonly numLandTiles: number, + public readonly nationMap: NationMap + ) { } terrain(cell: Cell): Terrain { return this.tiles[cell.x][cell.y] @@ -84,7 +103,7 @@ export async function loadTerrainMap(): Promise { } } - return new TerrainMap(terrain, numLand); + return new TerrainMap(terrain, numLand, worldMapInfo); } function logBinaryAsAscii(data: string, length: number = 8) { diff --git a/src/global.d.ts b/src/global.d.ts index ddb6cb6a8..d3eca7cf5 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -26,4 +26,8 @@ declare module '*.txt' { declare module '*.html' { const content: string; export default content; +} +declare module '*.json' { + const value: any; + export default value; } \ No newline at end of file diff --git a/src/scripts/TerrainMapGenerator.ts b/src/scripts/TerrainMapGenerator.ts index faf6eac6d..704db6559 100644 --- a/src/scripts/TerrainMapGenerator.ts +++ b/src/scripts/TerrainMapGenerator.ts @@ -1,7 +1,7 @@ import PImage from 'pureimage'; import path from 'path'; import fs from 'fs/promises'; -import {createReadStream, createWriteStream} from 'fs'; +import {createReadStream} from 'fs'; import {fileURLToPath} from 'url'; @@ -44,7 +44,7 @@ export class Terrain { } export async function loadTerrainMap(): Promise { - const imagePath = path.resolve(__dirname, '..', '..', 'resources', 'maps', 'TopoWorldMap.png'); + const imagePath = path.resolve(__dirname, '..', '..', 'resources', 'maps', 'WorldMap.png'); const readStream = createReadStream(imagePath); const img = await PImage.decodePNGFromStream(readStream); @@ -92,7 +92,7 @@ export async function loadTerrainMap(): Promise { processDistToLand(shorelineWaters, terrain) processOcean(terrain) const packed = packTerrain(terrain) - const outputPath = path.join(__dirname, '..', '..', 'resources', 'TopoWorldMap.bin'); + const outputPath = path.join(__dirname, '..', '..', 'resources', 'maps', 'WorldMap.bin'); fs.writeFile(outputPath, packed); }