diff --git a/TODO.txt b/TODO.txt index 844f9ba44..469784e44 100644 --- a/TODO.txt +++ b/TODO.txt @@ -130,12 +130,13 @@ * BUG: left click lost after right click DONE 9/20/2024 * eventbox events dissapear after timeout DONE 9/20/2024 * auto reject alliance when event dissapears DONE 9/20/2024 -* first place has crown DONE 9/20/2024 -* can't attack ally DONE 9/20/2024 -* add updates to eventbox: boats (max count, too far) DONE 9/20/2024 -* BUG: boats landing doesn't launch attack DONE 9/20/2024 -* fake humans handle alliances +* first place has crown DONE 9/21/2024 +* can't attack ally DONE 9/21/2024 +* add updates to eventbox: boats (max count, too far) DONE 9/21/2024 +* BUG: boats landing doesn't launch attack DONE 9/21/2024 +* fake humans handle alliances DONE 9/22/2024 * make alliances expire +* make alliance request mobile friendly * make year clock * block user inputs if too far behind server * BUG: FakeHuman don't be enemy if don't share border diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 98cd0f28c..0986b6241 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -208,7 +208,7 @@ export class ClientGame { if (tile.owner() == this.myPlayer) { return } - if (tile.owner().isPlayer() && this.myPlayer.alliedWith(tile.owner() as Player)) { + if (tile.owner().isPlayer() && this.myPlayer.isAlliedWith(tile.owner() as Player)) { this.eventBus.emit(new DisplayMessageEvent("Cannot attack ally", MessageType.WARN)) return } diff --git a/src/client/graphics/layers/NameLayer.ts b/src/client/graphics/layers/NameLayer.ts index 4380930c2..a126c63c6 100644 --- a/src/client/graphics/layers/NameLayer.ts +++ b/src/client/graphics/layers/NameLayer.ts @@ -166,7 +166,7 @@ export class NameLayer implements Layer { } const myPlayer = this.getPlayer() - if (myPlayer != null && myPlayer.alliedWith(render.player)) { + if (myPlayer != null && myPlayer.isAlliedWith(render.player)) { context.drawImage( this.allianceIconImage, nameCenterX - iconSize / 2, diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 72facebfb..a8ad07881 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -260,7 +260,7 @@ export class UILayer implements Layer { return } - if (myPlayer.alliedWith(owner)) { + if (myPlayer.isAlliedWith(owner)) { options.push({ label: "Break Alliance", action: (): void => { diff --git a/src/core/execution/AttackExecution.ts b/src/core/execution/AttackExecution.ts index 3a4d6fcbf..453959c2a 100644 --- a/src/core/execution/AttackExecution.ts +++ b/src/core/execution/AttackExecution.ts @@ -86,7 +86,7 @@ export class AttackExecution implements Execution { } if (this.target.isPlayer()) { - if (this._owner.alliedWith(this.target)) { + if (this._owner.isAlliedWith(this.target)) { // No updates should happen in init. this.breakAlliance = true } @@ -109,11 +109,12 @@ export class AttackExecution implements Execution { if (ticks < this.mg.config().numSpawnPhaseTurns()) { return } - if (this.breakAlliance && this._owner.alliedWith(this.target as Player)) { + const alliance = this._owner.allianceWith(this.target as Player) + if (this.breakAlliance && alliance != null) { this.breakAlliance = false - this._owner.breakAllianceWith(this.target as Player) + this._owner.breakAlliance(alliance) } - if (this.target.isPlayer() && this._owner.alliedWith(this.target)) { + if (this.target.isPlayer() && this._owner.isAlliedWith(this.target)) { // In this case a new alliance was created AFTER the attack started. this._owner.addTroops(this.troops) this.active = false diff --git a/src/core/execution/BotExecution.ts b/src/core/execution/BotExecution.ts index c7f6d31c3..508022dc2 100644 --- a/src/core/execution/BotExecution.ts +++ b/src/core/execution/BotExecution.ts @@ -51,7 +51,7 @@ export class BotExecution implements Execution { const traitors = this.bot.neighbors().filter(n => n.isPlayer() && n.isTraitor()) as Player[] if (traitors.length > 0) { const toAttack = this.random.randElement(traitors) - const odds = this.bot.alliedWith(toAttack) ? 6 : 3 + const odds = this.bot.isAlliedWith(toAttack) ? 6 : 3 if (this.random.chance(odds)) { this.sendAttack(toAttack) return @@ -80,7 +80,7 @@ export class BotExecution implements Execution { const owner = toAttack.owner() if (owner.isPlayer()) { - if (this.bot.alliedWith(owner)) { + if (this.bot.isAlliedWith(owner)) { return } if (owner.type() == PlayerType.FakeHuman) { diff --git a/src/core/execution/FakeHumanExecution.ts b/src/core/execution/FakeHumanExecution.ts index 1d5c72d13..ac04d6deb 100644 --- a/src/core/execution/FakeHumanExecution.ts +++ b/src/core/execution/FakeHumanExecution.ts @@ -100,17 +100,17 @@ export class FakeHumanExecution implements Execution { if (this.random.chance(10)) { const toAlly = this.random.randElement(enemies) - if (!this.player.alliedWith(toAlly)) { + if (!this.player.isAlliedWith(toAlly)) { this.player.createAllianceRequest(toAlly) } } if (this.random.chance(2)) { - if (!this.player.alliedWith(enemies[0]) || this.random.chance(30)) { + if (!this.player.isAlliedWith(enemies[0]) || this.random.chance(30)) { this.sendAttack(enemies[0]) } } else { - if (!this.player.alliedWith(enemies[0]) || this.random.chance(60)) { + if (!this.player.isAlliedWith(enemies[0]) || this.random.chance(60)) { this.sendAttack(this.random.randElement(enemies)) } } diff --git a/src/core/execution/PlayerExecution.ts b/src/core/execution/PlayerExecution.ts index 91c65d182..507bab30c 100644 --- a/src/core/execution/PlayerExecution.ts +++ b/src/core/execution/PlayerExecution.ts @@ -52,7 +52,7 @@ export class PlayerExecution implements Execution { const main = clusters.shift() const surroundedBy = this.surroundedBySamePlayer(main) - if (surroundedBy && !this.player.alliedWith(surroundedBy)) { + if (surroundedBy && !this.player.isAlliedWith(surroundedBy)) { this.removeCluster(main) } diff --git a/src/core/execution/alliance/AllianceRequestExecution.ts b/src/core/execution/alliance/AllianceRequestExecution.ts index a245c1d30..7a0c0492b 100644 --- a/src/core/execution/alliance/AllianceRequestExecution.ts +++ b/src/core/execution/alliance/AllianceRequestExecution.ts @@ -15,7 +15,7 @@ export class AllianceRequestExecution implements Execution { } tick(ticks: number): void { - if (this.requestor.alliedWith(this.recipient)) { + if (this.requestor.isAlliedWith(this.recipient)) { console.warn('already allied') } else { this.requestor.createAllianceRequest(this.recipient) diff --git a/src/core/execution/alliance/AllianceRequestReplyExecution.ts b/src/core/execution/alliance/AllianceRequestReplyExecution.ts index d1d3d67b0..82afd0b6e 100644 --- a/src/core/execution/alliance/AllianceRequestReplyExecution.ts +++ b/src/core/execution/alliance/AllianceRequestReplyExecution.ts @@ -15,7 +15,7 @@ export class AllianceRequestReplyExecution implements Execution { } tick(ticks: number): void { - if (this.requestor.alliedWith(this.recipient)) { + if (this.requestor.isAlliedWith(this.recipient)) { console.warn('already allied') } else { const request = this.requestor.outgoingAllianceRequests().find(ar => ar.recipient() == this.recipient) diff --git a/src/core/execution/alliance/BreakAllianceExecution.ts b/src/core/execution/alliance/BreakAllianceExecution.ts index d1552091f..9f316c18a 100644 --- a/src/core/execution/alliance/BreakAllianceExecution.ts +++ b/src/core/execution/alliance/BreakAllianceExecution.ts @@ -13,10 +13,11 @@ export class BreakAllianceExecution implements Execution { } tick(ticks: number): void { - if (!this.requestor.alliedWith(this.recipient)) { + const alliance = this.requestor.allianceWith(this.recipient) + if (alliance == null) { console.warn('cant break alliance, not allied') } else { - this.requestor.breakAllianceWith(this.recipient) + this.requestor.breakAlliance(alliance) } this.active = false } diff --git a/src/core/game/AllianceImpl.ts b/src/core/game/AllianceImpl.ts index dfa187eb5..550afd2d1 100644 --- a/src/core/game/AllianceImpl.ts +++ b/src/core/game/AllianceImpl.ts @@ -1,11 +1,13 @@ -import {MutableAlliance, MutablePlayer, Player} from "./Game"; +import {MutableAlliance, MutableGame, MutablePlayer, Player, Tick} from "./Game"; +import {GameImpl} from "./GameImpl"; import {PlayerImpl} from "./PlayerImpl"; export class AllianceImpl implements MutableAlliance { constructor( + private readonly mg: GameImpl, readonly requestor_: PlayerImpl, - readonly recepient_: PlayerImpl, - readonly createdAtTick_: number, + readonly recipient_: PlayerImpl, + readonly createdAtTick_: Tick, ) { } requestor(): MutablePlayer { @@ -13,7 +15,15 @@ export class AllianceImpl implements MutableAlliance { } recipient(): MutablePlayer { - return this.recepient_ + return this.recipient_ + } + + createdAt(): Tick { + return this.createdAtTick_ + } + + expire(): void { + } } \ No newline at end of file diff --git a/src/core/game/Game.ts b/src/core/game/Game.ts index 480ca2aea..b670532d9 100644 --- a/src/core/game/Game.ts +++ b/src/core/game/Game.ts @@ -3,8 +3,10 @@ import {Config} from "../configuration/Config" import {GameEvent} from "../EventBus" import {ClientID, GameID} from "../Schemas" import {DisplayMessageEvent, MessageType} from "../../client/graphics/layers/EventsDisplay" +import {BreakAllianceExecution} from "../execution/alliance/BreakAllianceExecution" export type PlayerID = string +export type Tick = number export class Cell { @@ -60,10 +62,11 @@ export interface MutableAllianceRequest extends AllianceRequest { export interface Alliance { requestor(): Player recipient(): Player + createdAt(): Tick } export interface MutableAlliance extends Alliance { - + expire(): void } export class PlayerInfo { @@ -138,7 +141,8 @@ export interface Player { incomingAllianceRequests(): AllianceRequest[] outgoingAllianceRequests(): AllianceRequest[] alliances(): Alliance[] - alliedWith(other: Player): boolean + isAlliedWith(other: Player): boolean + allianceWith(other: Player): Alliance | null pendingAllianceRequestWith(other: Player): boolean isTraitor(): boolean toString(): string @@ -157,7 +161,8 @@ export interface MutablePlayer extends Player { incomingAllianceRequests(): MutableAllianceRequest[] outgoingAllianceRequests(): MutableAllianceRequest[] alliances(): MutableAlliance[] - breakAllianceWith(other: Player): void + allianceWith(other: Player): MutableAlliance | null + breakAlliance(alliance: Alliance): void createAllianceRequest(recipient: Player): MutableAllianceRequest addBoat(troops: number, tile: Tile, target: Player | TerraNullius): MutableBoat } diff --git a/src/core/game/GameImpl.ts b/src/core/game/GameImpl.ts index 2c831f4e6..747b364ef 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} from "./Game"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, BoatEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance} from "./Game"; import {TerrainMap} from "./TerrainMapLoader"; import {PlayerImpl} from "./PlayerImpl"; import {TerraNulliusImpl} from "./TerraNulliusImpl"; @@ -50,7 +50,7 @@ export class GameImpl implements MutableGame { } createAllianceRequest(requestor: MutablePlayer, recipient: Player): MutableAllianceRequest { - if (requestor.alliedWith(recipient)) { + if (requestor.isAlliedWith(recipient)) { console.log('cannot request alliance, already allied') return } @@ -72,7 +72,7 @@ export class GameImpl implements MutableGame { acceptAllianceRequest(request: AllianceRequestImpl) { this.allianceRequests = this.allianceRequests.filter(ar => ar != request) - const alliance = new AllianceImpl(request.requestor() as PlayerImpl, request.recipient() as PlayerImpl, this._ticks) + const alliance = new AllianceImpl(this, request.requestor() as PlayerImpl, request.recipient() as PlayerImpl, this._ticks) this.alliances_.push(alliance) this.eventBus.emit(new AllianceRequestReplyEvent(request, true)) } @@ -343,7 +343,20 @@ export class GameImpl implements MutableGame { this.eventBus.emit(new BoatEvent(boat, oldTile)) } - public breakAlliance(breaker: Player, other: Player) { + public breakAlliance(breaker: Player, alliance: Alliance) { + let other: Player = null + if (alliance.requestor() == breaker) { + other = alliance.recipient() + } else { + other = alliance.requestor() + } + if (!breaker.isAlliedWith(other)) { + throw new Error(`${breaker} not allied with ${other}, cannot break alliance`) + } + if (!other.isTraitor()) { + (breaker as PlayerImpl).isTraitor_ = true + } + const breakerSet = new Set(breaker.alliances()) const alliances = other.alliances().filter(a => breakerSet.has(a)) if (alliances.length != 1) { diff --git a/src/core/game/PlayerImpl.ts b/src/core/game/PlayerImpl.ts index 46bd28ee1..012cd2793 100644 --- a/src/core/game/PlayerImpl.ts +++ b/src/core/game/PlayerImpl.ts @@ -1,4 +1,4 @@ -import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance} from "./Game"; +import {MutablePlayer, Tile, PlayerInfo, PlayerID, PlayerType, Player, TerraNullius, Cell, MutableGame, Execution, AllianceRequest, MutableAllianceRequest, MutableAlliance, Alliance} from "./Game"; import {ClientID} from "../Schemas"; import {simpleHash} from "../Util"; import {CellString, GameImpl} from "./GameImpl"; @@ -9,7 +9,7 @@ import {threadId} from "worker_threads"; export class PlayerImpl implements MutablePlayer { - private isTraitor_ = false + isTraitor_ = false public _borderTiles: Set = new Set(); @@ -123,35 +123,35 @@ export class PlayerImpl implements MutablePlayer { return this.gs.alliances_.filter(a => a.requestor() == this || a.recipient() == this) } - alliedWith(other: Player): boolean { + isAlliedWith(other: Player): boolean { if (other == this) { return false } - return this.alliances().find(a => a.recipient() == other || a.requestor() == other) != null + return this.allianceWith(other) != null } + allianceWith(other: Player): MutableAlliance | null { + return this.alliances().find(a => a.recipient() == other || a.requestor() == other) + } + + pendingAllianceRequestWith(other: Player): boolean { return this.incomingAllianceRequests().find(ar => ar.requestor() == other) != null || this.outgoingAllianceRequests().find(ar => ar.recipient() == other) != null } + breakAlliance(alliance: Alliance): void { + this.gs.breakAlliance(this, alliance) + } + + isTraitor(): boolean { return this.isTraitor_ } - breakAllianceWith(other: Player): void { - if (!this.alliedWith(other)) { - throw new Error('cannot break alliance, already allied') - } - if (!other.isTraitor()) { - this.isTraitor_ = true - } - this.gs.breakAlliance(this, other) - } - createAllianceRequest(recipient: Player): MutableAllianceRequest { - if (this.alliedWith(recipient)) { + if (this.isAlliedWith(recipient)) { throw new Error(`cannot create alliance request, already allies`) } return this.gs.createAllianceRequest(this, recipient)