move config to Game

This commit is contained in:
evanpelle
2024-08-24 12:12:46 -07:00
parent a9785741b7
commit bb8c24e230
11 changed files with 102 additions and 51 deletions
-1
View File
@@ -43,7 +43,6 @@
* BUG: boat doesn't work if on lake if other player not on same lake DONE 8/23/2024
* Allow boats to attack TerraNullius DONE 8/23/2024
* try vintage theme DONE 8/24/2023
* Boats can go diagonally
* Make lobby background the terrain map
* improve menu (keep highlighted when click, allow deselect lobby)
* add shader to dim border
+7 -9
View File
@@ -13,7 +13,7 @@ import {bfs, manhattanDist} from "../core/Util";
export function createClientGame(name: string, clientID: ClientID, gameID: GameID, config: Config, terrainMap: TerrainMap): ClientGame {
let eventBus = new EventBus()
let gs = createGame(terrainMap, eventBus)
let gs = createGame(terrainMap, eventBus, config)
let gameRenderer = new GameRenderer(gs, config.theme(), document.createElement("canvas"))
return new ClientGame(
@@ -24,8 +24,7 @@ export function createClientGame(name: string, clientID: ClientID, gameID: GameI
gs,
gameRenderer,
new InputHandler(eventBus),
new Executor(gs, config),
config
new Executor(gs)
)
}
@@ -52,8 +51,7 @@ export class ClientGame {
private gs: Game,
private renderer: GameRenderer,
private input: InputHandler,
private executor: Executor,
private config: Config
private executor: Executor
) { }
public join() {
@@ -117,7 +115,7 @@ export class ClientGame {
this.renderer.initialize()
this.input.initialize()
this.gs.addExecution(...this.executor.spawnBots(this.config.numBots()))
this.gs.addExecution(...this.executor.spawnBots(this.gs.config().numBots()))
this.intervalID = setInterval(() => this.tick(), 10);
}
@@ -197,15 +195,15 @@ export class ClientGame {
.flatMap(t => t.neighbors())
.filter(n => n.isShore())
if (tn.length > 0) {
this.sendBoatAttackIntent(targetID, tn[0].cell(), this.config.player().boatAttackAmount(this.myPlayer, owner))
this.sendBoatAttackIntent(targetID, tn[0].cell(), this.gs.config().player().boatAttackAmount(this.myPlayer, owner))
return
}
if (this.myPlayer.sharesBorderWith(tile.owner())) {
this.sendAttackIntent(targetID, cell, this.config.player().attackAmount(this.myPlayer, owner))
this.sendAttackIntent(targetID, cell, this.gs.config().player().attackAmount(this.myPlayer, owner))
} else if (owner.isPlayer()) {
console.log('going to send boat')
this.sendBoatAttackIntent(targetID, cell, this.config.player().boatAttackAmount(this.myPlayer, owner))
this.sendBoatAttackIntent(targetID, cell, this.gs.config().player().boatAttackAmount(this.myPlayer, owner))
}
}
}
+37
View File
@@ -0,0 +1,37 @@
import {inherits} from "util"
import {Game} from "../../core/Game";
export class TerrainRenderer {
private canvas: HTMLCanvasElement
private context: CanvasRenderingContext2D
private imageData: ImageData
constructor(private game: Game) { }
init() {
this.canvas = document.createElement('canvas');
this.context = this.canvas.getContext("2d")
this.imageData = this.context.getImageData(0, 0, this.game.width(), this.game.height())
this.initImageData()
this.canvas = document.createElement('canvas');
const backgroundCtx = this.canvas.getContext('2d');
this.canvas.width = this.game.width();
this.canvas.height = this.game.height();
backgroundCtx.putImageData(this.imageData, 0, 0);
}
initImageData() {
const theme = this.game.config().theme()
this.game.forEachTile((tile) => {
let terrainColor = theme.terrainColor(tile)
const index = (tile.cell().y * this.game.width()) + tile.cell().x
const offset = index * 4
this.imageData.data[offset] = terrainColor.rgba.r;
this.imageData.data[offset + 1] = terrainColor.rgba.g;
this.imageData.data[offset + 2] = terrainColor.rgba.b;
this.imageData.data[offset + 3] = terrainColor.rgba.a * 255 | 0
})
}
}
+2
View File
@@ -1,3 +1,4 @@
import {Config} from "./configuration/Config"
import {GameEvent} from "./EventBus"
import {ClientID, GameID} from "./Schemas"
@@ -120,6 +121,7 @@ export interface Game {
terraNullius(): TerraNullius
tick(): void
addExecution(...exec: Execution[]): void
config(): Config
}
export interface MutableGame extends Game {
+25 -3
View File
@@ -1,9 +1,10 @@
import {Config} from "./configuration/Config";
import {EventBus} from "./EventBus";
import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, MutableBoat, BoatEvent} from "./Game";
import {Terrain, TerrainMap, TerrainType} from "./TerrainMapLoader";
export function createGame(terrainMap: TerrainMap, eventBus: EventBus): Game {
return new GameImpl(terrainMap, eventBus)
export function createGame(terrainMap: TerrainMap, eventBus: EventBus, config: Config): Game {
return new GameImpl(terrainMap, eventBus, config)
}
type CellString = string
@@ -19,6 +20,7 @@ class TileImpl implements Tile {
private readonly _cell: Cell,
private readonly _terrain: Terrain
) { }
isLake(): boolean {
return !this.isLand() && !this.isOcean()
}
@@ -228,7 +230,7 @@ export class GameImpl implements MutableGame {
private _height: number
_terraNullius: TerraNulliusImpl
constructor(terrainMap: TerrainMap, private eventBus: EventBus) {
constructor(terrainMap: TerrainMap, private eventBus: EventBus, private _config: Config) {
this._terraNullius = new TerraNulliusImpl(this)
this._width = terrainMap.width();
this._height = terrainMap.height();
@@ -241,6 +243,9 @@ export class GameImpl implements MutableGame {
}
}
}
config(): Config {
return this._config
}
tick() {
this.executions().forEach(e => e.tick(this.ticks))
@@ -354,6 +359,23 @@ export class GameImpl implements MutableGame {
return ns
}
neighborsWithDiag(tile: Tile): Tile[] {
const x = tile.cell().x
const y = tile.cell().y
const ns: TileImpl[] = []
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
if (dx === 0 && dy === 0) continue // Skip the center tile
const newX = x + dx
const newY = y + dy
if (newX >= 0 && newX < this._width && newY >= 0 && newY < this._height) {
ns.push(this.map[newX][newY])
}
}
}
return ns
}
private assertIsOnMap(cell: Cell) {
if (!this.isOnMap(cell)) {
throw new Error(`cell ${cell.toString()} is not on map`)
+3 -4
View File
@@ -22,7 +22,6 @@ export class AttackExecution implements Execution {
private _ownerID: PlayerID,
private targetID: PlayerID | null,
private targetCell: Cell | null,
private config: Config
) { }
init(mg: MutableGame, ticks: number) {
@@ -68,11 +67,11 @@ export class AttackExecution implements Execution {
if (!this.active) {
return
}
if (ticks < this.config.turnsUntilGameStart()) {
if (ticks < this.mg.config().turnsUntilGameStart()) {
return
}
let numTilesPerTick = this.config.player().attackTilesPerTick(this._owner, this.target, this.numTilesWithEnemy)
let numTilesPerTick = this.mg.config().player().attackTilesPerTick(this._owner, this.target, this.numTilesWithEnemy)
if (this.targetCell != null) {
numTilesPerTick /= 2
}
@@ -107,7 +106,7 @@ export class AttackExecution implements Execution {
badTiles++
continue
}
const {attackerTroopLoss, defenderTroopLoss, tilesPerTickUsed} = this.config.player().attackLogic(this._owner, this.target, tileToConquer)
const {attackerTroopLoss, defenderTroopLoss, tilesPerTickUsed} = this.mg.config().player().attackLogic(this._owner, this.target, tileToConquer)
numTilesPerTick -= tilesPerTickUsed
this.troops -= attackerTroopLoss
if (this.target.isPlayer()) {
+1 -2
View File
@@ -36,7 +36,6 @@ export class BoatAttackExecution implements Execution {
private targetID: PlayerID | null,
private cell: Cell,
private troops: number,
private config: Config
) { }
init(mg: MutableGame, ticks: number) {
@@ -102,7 +101,7 @@ export class BoatAttackExecution implements Execution {
return
}
this.attacker.conquer(this.dst)
this.mg.addExecution(new AttackExecution(this.troops, this.attacker.id(), this.targetID, null, this.config))
this.mg.addExecution(new AttackExecution(this.troops, this.attacker.id(), this.targetID, null))
this.boat.delete()
this.active = false
return
+9 -11
View File
@@ -8,24 +8,23 @@ export class BotExecution implements Execution {
private active = true
private random: PseudoRandom;
private attackRate: number
private gs: MutableGame
private mg: MutableGame
private neighborsTerra = true
constructor(private bot: MutablePlayer, private config: Config) {
constructor(private bot: MutablePlayer) {
this.random = new PseudoRandom(bot.id())
this.attackRate = this.random.nextInt(10, 50)
}
init(gs: MutableGame, ticks: number) {
this.gs = gs
init(mg: MutableGame, ticks: number) {
this.mg = mg
// this.neighborsTerra = this.bot.neighbors().filter(n => n == this.gs.terraNullius()).length > 0
}
tick(ticks: number) {
if (ticks < this.config.turnsUntilGameStart()) {
if (ticks < this.mg.config().turnsUntilGameStart()) {
return
}
@@ -38,8 +37,8 @@ export class BotExecution implements Execution {
if (this.neighborsTerra) {
for (const b of this.bot.borderTiles()) {
for (const n of b.neighbors()) {
if (n.owner() == this.gs.terraNullius() && n.isLand()) {
this.sendAttack(this.gs.terraNullius())
if (n.owner() == this.mg.terraNullius() && n.isLand()) {
this.sendAttack(this.mg.terraNullius())
return
}
}
@@ -57,12 +56,11 @@ export class BotExecution implements Execution {
}
sendAttack(toAttack: Player | TerraNullius) {
this.gs.addExecution(new AttackExecution(
this.mg.addExecution(new AttackExecution(
this.bot.troops() / 20,
this.bot.id(),
toAttack.isPlayer() ? toAttack.id() : null,
null,
this.config
null
))
}
+4 -8
View File
@@ -5,12 +5,11 @@ import {AttackExecution} from "./AttackExecution";
import {SpawnExecution} from "./SpawnExecution";
import {BotSpawner} from "./BotSpawner";
import {BoatAttackExecution} from "./BoatAttackExecution";
import {Config, PlayerConfig} from "../configuration/Config";
export class Executor {
constructor(private gs: Game, private config: Config) {
constructor(private gs: Game) {
}
@@ -24,22 +23,19 @@ export class Executor {
intent.troops,
intent.attackerID,
intent.targetID,
new Cell(intent.targetX, intent.targetY),
this.config
new Cell(intent.targetX, intent.targetY)
)
} else if (intent.type == "spawn") {
return new SpawnExecution(
new PlayerInfo(intent.name, intent.isBot, intent.clientID),
new Cell(intent.x, intent.y),
this.config
new Cell(intent.x, intent.y)
)
} else if (intent.type == "boat") {
return new BoatAttackExecution(
intent.attackerID,
intent.targetID,
new Cell(intent.x, intent.y),
intent.troops,
this.config
intent.troops
)
} else {
throw new Error(`intent type ${intent} not found`)
+5 -3
View File
@@ -4,12 +4,14 @@ import {Execution, MutableGame, MutablePlayer, PlayerID} from "../Game"
export class PlayerExecution implements Execution {
private player: MutablePlayer
private config: Config
constructor(private playerID: PlayerID, private config: Config) {
constructor(private playerID: PlayerID) {
}
init(gs: MutableGame, ticks: number) {
this.player = gs.player(this.playerID)
init(mg: MutableGame, ticks: number) {
this.config = mg.config()
this.player = mg.player(this.playerID)
}
tick(ticks: number) {
+9 -10
View File
@@ -7,30 +7,29 @@ import {getSpawnCells} from "./Util"
export class SpawnExecution implements Execution {
active: boolean = true
private gs: MutableGame
private mg: MutableGame
constructor(
private playerInfo: PlayerInfo,
private cell: Cell,
private config: Config
private cell: Cell
) { }
init(gs: MutableGame, ticks: number) {
this.gs = gs
init(mg: MutableGame, ticks: number) {
this.mg = mg
}
tick(ticks: number) {
if (!this.isActive()) {
return
}
const player = this.gs.addPlayer(this.playerInfo, this.config.player().startTroops(this.playerInfo))
getSpawnCells(this.gs, this.cell).forEach(c => {
player.conquer(this.gs.tile(c))
const player = this.mg.addPlayer(this.playerInfo, this.mg.config().player().startTroops(this.playerInfo))
getSpawnCells(this.mg, this.cell).forEach(c => {
player.conquer(this.mg.tile(c))
})
this.gs.addExecution(new PlayerExecution(player.id(), this.config))
this.mg.addExecution(new PlayerExecution(player.id()))
if (player.info().isBot) {
this.gs.addExecution(new BotExecution(player, this.config))
this.mg.addExecution(new BotExecution(player))
}
this.active = false
}