diff --git a/src/client/ClientGame.ts b/src/client/ClientGame.ts index 9ebe99b80..42b39ed7a 100644 --- a/src/client/ClientGame.ts +++ b/src/client/ClientGame.ts @@ -10,6 +10,7 @@ import {TerrainMap} from "../core/TerrainMapLoader"; import {and, bfs, dist, manhattanDist} from "../core/Util"; import {TerrainLayer} from "./graphics/layers/TerrainLayer"; import {WinCheckExecution} from "../core/execution/WinCheckExecution"; +import {SendAllianceRequestEvent} from "./graphics/layers/UILayer"; @@ -121,6 +122,7 @@ export class ClientGame { this.eventBus.on(PlayerEvent, (e) => this.playerEvent(e)) this.eventBus.on(MouseUpEvent, (e) => this.inputEvent(e)) + this.eventBus.on(SendAllianceRequestEvent, (e) => this.onSendAllianceRequest(e)) this.renderer.initialize() this.input.initialize() @@ -269,6 +271,15 @@ export class ClientGame { } } + private onSendAllianceRequest(event: SendAllianceRequestEvent) { + this.sendIntent({ + type: "allianceRequest", + clientID: this.id, + requestor: event.requestor.id(), + recipient: event.recipient.id(), + }) + } + private sendSpawnIntent(cell: Cell) { this.sendIntent({ type: "spawn", diff --git a/src/client/graphics/layers/UILayer.ts b/src/client/graphics/layers/UILayer.ts index 6ada2fe1b..7cb7accd9 100644 --- a/src/client/graphics/layers/UILayer.ts +++ b/src/client/graphics/layers/UILayer.ts @@ -1,7 +1,7 @@ -import {Theme} from "../../../core/configuration/Config"; -import {EventBus} from "../../../core/EventBus"; +import {GameEnv, Theme} from "../../../core/configuration/Config"; +import {EventBus, GameEvent} from "../../../core/EventBus"; import {WinEvent} from "../../../core/execution/WinCheckExecution"; -import {Game, Player} from "../../../core/Game"; +import {AllianceRequest, Game, Player} from "../../../core/Game"; import {ClientID} from "../../../core/Schemas"; import {renderTroops} from "../Utils"; import winModalHtml from '../WinModal.html'; @@ -9,6 +9,12 @@ import {RightClickEvent} from "../../InputHandler"; import {Layer} from "./Layer"; import {TransformHandler} from "../TransformHandler"; +export class SendAllianceRequestEvent implements GameEvent { + constructor( + public readonly requestor: Player, + public readonly recipient: Player + ) { } +} interface MenuOption { label: string; @@ -228,6 +234,13 @@ export class UILayer implements Layer { if (owner.clientID() == this.clientID) { return } + // TODO: check if already allied with + + const myPlayer = this.game.players().find(p => p.clientID() == this.clientID) + if (!myPlayer) { + console.warn('my player not found') + return + } this.customMenu!.style.display = 'block'; this.customMenu!.style.left = `${e.x}px`; @@ -235,7 +248,11 @@ export class UILayer implements Layer { this.populateMenu([ { label: "Request Alliance", - action: (): void => { }, + action: (): void => { + this.eventBus.emit( + new SendAllianceRequestEvent(myPlayer, owner) + ) + }, } ]) } diff --git a/src/core/Game.ts b/src/core/Game.ts index 2f487d5ac..a62a8bd29 100644 --- a/src/core/Game.ts +++ b/src/core/Game.ts @@ -46,6 +46,24 @@ export interface Execution extends ExecutionView { owner(): MutablePlayer } +export interface AllianceRequest { + requestor(): Player + recipient(): Player +} + +export interface MutableAllianceRequest extends AllianceRequest { + accept(): void + reject(): void +} + +export class Alliance { + constructor( + public readonly requestor: Player, + public readonly recepient: Player + ) { } +} + + export class PlayerInfo { constructor( public readonly name: string, @@ -159,10 +177,10 @@ export interface MutableGame extends Game { addPlayer(playerInfo: PlayerInfo, troops: number): MutablePlayer executions(): Execution[] removeInactiveExecutions(): void - removeExecution(exec: Execution) + removeExecution(exec: Execution): void + allianceRequest(requestor: Player, recipient: Player): MutableAllianceRequest } - export class TileEvent implements GameEvent { constructor(public readonly tile: Tile) { } } diff --git a/src/core/GameImpl.ts b/src/core/GameImpl.ts index 0c35d7a6a..192f258fb 100644 --- a/src/core/GameImpl.ts +++ b/src/core/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, MutableBoat, BoatEvent, TerrainType, PlayerType} from "./Game"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Boat, MutableBoat, BoatEvent, TerrainType, PlayerType, MutableAllianceRequest} from "./Game"; import {ClientID} from "./Schemas"; import {Terrain, TerrainMap} from "./TerrainMapLoader"; import {simpleHash} from "./Util"; @@ -115,6 +115,27 @@ class TileImpl implements Tile { } } +export class AllianceRequestImpl implements MutableAllianceRequest { + + constructor(private requestor_, private recipient_, private game: GameImpl) { } + + requestor(): Player { + return this.requestor_ + } + + recipient(): Player { + return this.recipient_ + } + + accept(): void { + throw new Error("Method not implemented."); + } + reject(): void { + throw new Error("Method not implemented."); + } + +} + export class BoatImpl implements MutableBoat { private _active = true @@ -297,6 +318,8 @@ export class GameImpl implements MutableGame { private _numLandTiles: number _terraNullius: TerraNulliusImpl + private allianceRequests: AllianceRequestImpl[] = [] + constructor(terrainMap: TerrainMap, private eventBus: EventBus, private _config: Config) { this._terraNullius = new TerraNulliusImpl(this) this._width = terrainMap.width(); @@ -311,6 +334,21 @@ export class GameImpl implements MutableGame { } } } + + allianceRequest(requestor: Player, recipient: Player): MutableAllianceRequest { + const ar = new AllianceRequestImpl(requestor, recipient, this) + this.allianceRequests.push(ar) + return ar + } + + acceptAllianceRequest() { + + } + + rejectAllianceRequest() { + + } + numLandTiles(): number { return this._numLandTiles } diff --git a/src/core/Schemas.ts b/src/core/Schemas.ts index 78501519e..f64afe3f8 100644 --- a/src/core/Schemas.ts +++ b/src/core/Schemas.ts @@ -4,13 +4,13 @@ import {PlayerType} from './Game'; export type GameID = string export type ClientID = string -export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent | UpdateNameIntent +export type Intent = SpawnIntent | AttackIntent | BoatAttackIntent | UpdateNameIntent | AllianceRequestIntent export type AttackIntent = z.infer export type SpawnIntent = z.infer export type BoatAttackIntent = z.infer export type UpdateNameIntent = z.infer - +export type AllianceRequestIntent = z.infer export type Turn = z.infer @@ -76,7 +76,13 @@ export const UpdateNameIntentSchema = BaseIntentSchema.extend({ name: z.string(), }) -const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema, UpdateNameIntentSchema]); +export const AllianceRequestIntentSchema = BaseIntentSchema.extend({ + type: z.literal('allianceRequest'), + requestor: z.string(), + recipient: z.string(), +}) + +const IntentSchema = z.union([AttackIntentSchema, SpawnIntentSchema, BoatAttackIntentSchema, UpdateNameIntentSchema, AllianceRequestIntentSchema]); const TurnSchema = z.object({ turnNumber: z.number(), diff --git a/src/core/configuration/DevConfig.ts b/src/core/configuration/DevConfig.ts index 75382888b..b7d7cccb9 100644 --- a/src/core/configuration/DevConfig.ts +++ b/src/core/configuration/DevConfig.ts @@ -19,12 +19,12 @@ export const devConfig = new class extends DefaultConfig { } numBots(): number { - return 0 + return 400 } numFakeHumans(gameID: GameID): number { - return 0 + return 10 } // startTroops(playerInfo: PlayerInfo): number { diff --git a/src/core/execution/AllianceRequestExecution.ts b/src/core/execution/AllianceRequestExecution.ts new file mode 100644 index 000000000..c60fc4018 --- /dev/null +++ b/src/core/execution/AllianceRequestExecution.ts @@ -0,0 +1,35 @@ +import {AllianceRequest, Execution, MutableGame, MutablePlayer, Player, PlayerID} from "../Game"; + +export class AllianceRequestExecution implements Execution { + private active = true + private mg: MutableGame = null + private requestor: Player; + private recipient: Player + + + constructor(private requestorID: PlayerID, private recipientID: PlayerID) { } + + init(mg: MutableGame, ticks: number): void { + this.mg = mg + this.requestor = mg.player(this.requestorID) + this.recipient = mg.player(this.recipientID) + } + + tick(ticks: number): void { + alert('recied request') + + this.active = false + } + + owner(): MutablePlayer { + return null + } + + isActive(): boolean { + return this.active + } + + activeDuringSpawnPhase(): boolean { + return false + } +} \ No newline at end of file diff --git a/src/core/execution/ExecutionManager.ts b/src/core/execution/ExecutionManager.ts index fea66ce10..38222f323 100644 --- a/src/core/execution/ExecutionManager.ts +++ b/src/core/execution/ExecutionManager.ts @@ -1,4 +1,4 @@ -import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerInfo, TerraNullius, Tile, PlayerType} from "../Game"; +import {Cell, Execution, MutableGame, Game, MutablePlayer, PlayerInfo, TerraNullius, Tile, PlayerType, Alliance} from "../Game"; import {AttackIntent, BoatAttackIntentSchema, GameID, Intent, Turn} from "../Schemas"; import {AttackExecution} from "./AttackExecution"; import {SpawnExecution} from "./SpawnExecution"; @@ -9,6 +9,7 @@ import {UpdateNameExecution} from "./UpdateNameExecution"; import {FakeHumanExecution} from "./FakeHumanExecution"; import Usernames from '../../../resources/Usernames.txt' import {simpleHash} from "../Util"; +import {AllianceRequestExecution} from "./AllianceRequestExecution"; @@ -55,6 +56,8 @@ export class Executor { intent.name, intent.clientID ) + } else if (intent.type == "allianceRequest") { + return new AllianceRequestExecution(intent.requestor, intent.recipient) } else { throw new Error(`intent type ${intent} not found`) }