mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-01 03:03:33 +00:00
move config to Game
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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`)
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
@@ -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`)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user