mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 22:24:16 +00:00
492 lines
12 KiB
TypeScript
492 lines
12 KiB
TypeScript
import { Config } from "../configuration/Config"
|
|
import { GameEvent } from "../EventBus"
|
|
import { ClientID, GameConfig, GameID } from "../Schemas"
|
|
import { GameMap, GameMapImpl, TileRef, TileUpdate } from "./GameMap"
|
|
|
|
export type PlayerID = string
|
|
export type Tick = number
|
|
export type Gold = number
|
|
|
|
export const AllPlayers = "AllPlayers" as const;
|
|
|
|
// export type GameUpdates = Record<GameUpdateType, GameUpdate[]>;
|
|
// Create a type that maps GameUpdateType to its corresponding update type
|
|
type UpdateTypeMap<T extends GameUpdateType> = Extract<GameUpdate, { type: T }>;
|
|
|
|
// Then use it to create the record type
|
|
export type GameUpdates = {
|
|
[K in GameUpdateType]: UpdateTypeMap<K>[]
|
|
}
|
|
|
|
export interface MapPos {
|
|
x: number
|
|
y: number
|
|
}
|
|
|
|
export enum Difficulty {
|
|
Easy = "Easy",
|
|
Medium = "Medium",
|
|
Hard = "Hard",
|
|
Impossible = "Impossible",
|
|
}
|
|
|
|
export enum GameMapType {
|
|
World = "World",
|
|
Europe = "Europe",
|
|
Mena = "Mena",
|
|
NorthAmerica = "North America",
|
|
Oceania = "Oceania",
|
|
BlackSea = "Black Sea"
|
|
}
|
|
|
|
export enum GameType {
|
|
Singleplayer = "Singleplayer",
|
|
Public = "Public",
|
|
Private = "Private",
|
|
}
|
|
|
|
export interface UnitInfo {
|
|
cost: (player: Player) => Gold
|
|
// Determines if its owner changes when its tile is conquered.
|
|
territoryBound: boolean
|
|
maxHealth?: number,
|
|
damage?: number
|
|
}
|
|
|
|
export enum UnitType {
|
|
TransportShip = "Transport",
|
|
Destroyer = "Destroyer",
|
|
Battleship = "Battleship",
|
|
Shell = "Shell",
|
|
Port = "Port",
|
|
AtomBomb = "Atom Bomb",
|
|
HydrogenBomb = "Hydrogen Bomb",
|
|
TradeShip = "Trade Ship",
|
|
MissileSilo = "Missile Silo",
|
|
DefensePost = "Defense Post",
|
|
City = "City"
|
|
}
|
|
|
|
export enum Relation {
|
|
Hostile = 0,
|
|
Distrustful = 1,
|
|
Neutral = 2,
|
|
Friendly = 3
|
|
}
|
|
|
|
export class Nation {
|
|
constructor(
|
|
public readonly name: string,
|
|
public readonly cell: Cell,
|
|
public readonly strength: number,
|
|
) { }
|
|
}
|
|
|
|
export class EmojiMessage {
|
|
constructor(
|
|
public readonly sender: Player,
|
|
public readonly recipient: Player | typeof AllPlayers,
|
|
public readonly emoji: string,
|
|
public readonly createdAt: Tick
|
|
) { }
|
|
}
|
|
|
|
export class Cell {
|
|
public index: number
|
|
|
|
private strRepr: string
|
|
|
|
constructor(
|
|
public readonly x,
|
|
public readonly y
|
|
) {
|
|
this.strRepr = `Cell[${this.x},${this.y}]`
|
|
}
|
|
|
|
pos(): MapPos {
|
|
return {
|
|
x: this.x,
|
|
y: this.y
|
|
}
|
|
}
|
|
|
|
toString(): string { return this.strRepr }
|
|
}
|
|
|
|
export enum TerrainType {
|
|
Plains,
|
|
Highland,
|
|
Mountain,
|
|
Lake,
|
|
Ocean
|
|
}
|
|
|
|
export enum PlayerType {
|
|
Bot = "BOT",
|
|
Human = "HUMAN",
|
|
FakeHuman = "FAKEHUMAN",
|
|
}
|
|
|
|
export interface ExecutionView {
|
|
isActive(): boolean
|
|
// TODO: remove owner
|
|
owner(): Player
|
|
activeDuringSpawnPhase(): boolean
|
|
}
|
|
|
|
export interface Execution extends ExecutionView {
|
|
init(mg: MutableGame, ticks: number): void
|
|
tick(ticks: number): void
|
|
owner(): MutablePlayer
|
|
}
|
|
|
|
export interface AllianceRequest {
|
|
requestor(): Player
|
|
recipient(): Player
|
|
createdAt(): Tick
|
|
}
|
|
|
|
export interface MutableAllianceRequest extends AllianceRequest {
|
|
accept(): void
|
|
reject(): void
|
|
requestor(): MutablePlayer
|
|
recipient(): MutablePlayer
|
|
}
|
|
|
|
export interface Alliance {
|
|
requestor(): Player
|
|
recipient(): Player
|
|
createdAt(): Tick
|
|
other(player: Player): Player
|
|
}
|
|
|
|
export interface MutableAlliance extends Alliance {
|
|
expire(): void
|
|
other(player: Player): MutablePlayer
|
|
}
|
|
|
|
export class PlayerInfo {
|
|
constructor(
|
|
public readonly name: string,
|
|
public readonly playerType: PlayerType,
|
|
// null if bot.
|
|
public readonly clientID: ClientID | null,
|
|
// TODO: make player id the small id
|
|
public readonly id: PlayerID
|
|
) { }
|
|
}
|
|
|
|
export interface DefenseBonus {
|
|
// Unit providing the defense bonus
|
|
unit: Unit
|
|
amount: number
|
|
tile: TileRef
|
|
}
|
|
|
|
export interface Unit {
|
|
type(): UnitType
|
|
troops(): number
|
|
tile(): TileRef
|
|
owner(): Player
|
|
isActive(): boolean
|
|
hasHealth(): boolean
|
|
health(): number
|
|
lastTile(): TileRef
|
|
}
|
|
|
|
export interface MutableUnit extends Unit {
|
|
move(tile: TileRef): void
|
|
owner(): MutablePlayer
|
|
setTroops(troops: number): void
|
|
info(): UnitInfo
|
|
delete(displayerMessage?: boolean): void
|
|
modifyHealth(delta: number): void
|
|
toUpdate(): UnitUpdate
|
|
}
|
|
|
|
export interface TerraNullius {
|
|
isPlayer(): false
|
|
id(): PlayerID // always zero, maybe make it TerraNulliusID?
|
|
clientID(): ClientID
|
|
smallID(): number
|
|
}
|
|
|
|
export interface Player {
|
|
smallID(): number
|
|
info(): PlayerInfo
|
|
name(): string
|
|
displayName(): string
|
|
clientID(): ClientID
|
|
id(): PlayerID
|
|
type(): PlayerType
|
|
units(...types: UnitType[]): Unit[]
|
|
isAlive(): boolean
|
|
borderTiles(): ReadonlySet<TileRef>
|
|
isPlayer(): this is Player
|
|
numTilesOwned(): number
|
|
sharesBorderWith(other: Player | TerraNullius): boolean
|
|
incomingAllianceRequests(): AllianceRequest[]
|
|
outgoingAllianceRequests(): AllianceRequest[]
|
|
alliances(): Alliance[]
|
|
allies(): Player[]
|
|
isAlliedWith(other: Player): boolean
|
|
allianceWith(other: Player): Alliance | null
|
|
// Includes recent requests that are in cooldown
|
|
// TODO: why can't I have "canSendAllyRequest" function instead?
|
|
recentOrPendingAllianceRequestWith(other: Player): boolean
|
|
// How this player feels about other player.
|
|
relation(other: Player): Relation
|
|
// Sorted from most hated to most liked
|
|
allRelationsSorted(): { player: Player, relation: Relation }[]
|
|
transitiveTargets(): Player[]
|
|
isTraitor(): boolean
|
|
canTarget(other: Player): boolean
|
|
toString(): string
|
|
canSendEmoji(recipient: Player | typeof AllPlayers): boolean
|
|
outgoingEmojis(): EmojiMessage[]
|
|
canDonate(recipient: Player): boolean
|
|
gold(): Gold
|
|
// Population = troops + workers
|
|
population(): number
|
|
workers(): number
|
|
// Number between 0, 1
|
|
targetTroopRatio(): number
|
|
troops(): number
|
|
|
|
// If can build returns the spawn tile, false otherwise
|
|
canBuild(type: UnitType, targetTile: TileRef): TileRef | false
|
|
lastTileChange(): Tick
|
|
}
|
|
|
|
export interface MutablePlayer extends Player {
|
|
// Targets for this player
|
|
targets(): Player[]
|
|
// Targets of player and all allies.
|
|
neighbors(): (Player | TerraNullius)[]
|
|
tiles(): ReadonlySet<TileRef>
|
|
conquer(tile: TileRef): void
|
|
relinquish(tile: TileRef): void
|
|
executions(): Execution[]
|
|
neighbors(): (MutablePlayer | TerraNullius)[]
|
|
units(...types: UnitType[]): MutableUnit[]
|
|
incomingAllianceRequests(): MutableAllianceRequest[]
|
|
outgoingAllianceRequests(): MutableAllianceRequest[]
|
|
alliances(): MutableAlliance[]
|
|
allies(): MutablePlayer[]
|
|
allianceWith(other: Player): MutableAlliance | null
|
|
breakAlliance(alliance: Alliance): void
|
|
createAllianceRequest(recipient: Player): MutableAllianceRequest
|
|
updateRelation(other: Player, delta: number): void
|
|
decayRelations(): void
|
|
target(other: Player): void
|
|
targets(): MutablePlayer[]
|
|
transitiveTargets(): MutablePlayer[]
|
|
sendEmoji(recipient: Player | typeof AllPlayers, emoji: string): void
|
|
donate(recipient: MutablePlayer, troops: number): void
|
|
|
|
addGold(toAdd: Gold): void
|
|
removeGold(toRemove: Gold): void
|
|
|
|
addWorkers(toAdd: number): void
|
|
removeWorkers(toRemove: number): void
|
|
setTargetTroopRatio(target: number): void
|
|
setTroops(troops: number): void
|
|
addTroops(troops: number): void
|
|
removeTroops(troops: number): number
|
|
|
|
buildUnit(type: UnitType, troops: number, tile: TileRef): MutableUnit
|
|
captureUnit(unit: MutableUnit): void
|
|
|
|
toUpdate(): PlayerUpdate
|
|
}
|
|
|
|
export interface Game extends GameMap {
|
|
// Throws exception is player not found
|
|
player(id: PlayerID): Player
|
|
playerByClientID(id: ClientID): Player | null
|
|
hasPlayer(id: PlayerID): boolean
|
|
players(): Player[]
|
|
isOnMap(cell: Cell): boolean
|
|
width(): number
|
|
height(): number
|
|
forEachTile(fn: (tile: TileRef) => void): void
|
|
executions(): ExecutionView[]
|
|
terraNullius(): TerraNullius
|
|
executeNextTick(): GameUpdates
|
|
ticks(): Tick
|
|
inSpawnPhase(): boolean
|
|
addExecution(...exec: Execution[]): void
|
|
nations(): Nation[]
|
|
config(): Config
|
|
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void
|
|
units(...types: UnitType[]): Unit[]
|
|
unitInfo(type: UnitType): UnitInfo
|
|
playerBySmallID(id: number): Player | TerraNullius
|
|
map(): GameMap
|
|
miniMap(): GameMap
|
|
owner(ref: TileRef): Player | TerraNullius
|
|
}
|
|
|
|
export interface MutableGame extends Game {
|
|
player(id: PlayerID): MutablePlayer
|
|
playerByClientID(id: ClientID): MutablePlayer | null
|
|
players(): MutablePlayer[]
|
|
allPlayers(): MutablePlayer[]
|
|
addPlayer(playerInfo: PlayerInfo, manpower: number): MutablePlayer
|
|
executions(): Execution[]
|
|
units(...types: UnitType[]): MutableUnit[]
|
|
addTileDefenseBonus(tile: TileRef, unit: Unit, amount: number): DefenseBonus
|
|
removeTileDefenseBonus(bonus: DefenseBonus): void
|
|
addFallout(tile: TileRef): void
|
|
setWinner(winner: Player): void
|
|
}
|
|
|
|
export enum GameUpdateType {
|
|
Tile,
|
|
Unit,
|
|
Player,
|
|
DisplayEvent,
|
|
AllianceRequest,
|
|
AllianceRequestReply,
|
|
BrokeAlliance,
|
|
AllianceExpired,
|
|
TargetPlayer,
|
|
EmojiUpdate,
|
|
WinUpdate
|
|
}
|
|
|
|
export interface NameViewData {
|
|
x: number,
|
|
y: number,
|
|
size: number,
|
|
}
|
|
|
|
export interface PlayerActions {
|
|
canBoat: boolean
|
|
canAttack: boolean
|
|
buildableUnits: UnitType[]
|
|
interaction?: PlayerInteraction
|
|
}
|
|
|
|
export interface PlayerProfile {
|
|
relations: Record<number, Relation>
|
|
// TODO: add alliances etc
|
|
}
|
|
|
|
export interface PlayerInteraction {
|
|
sharedBorder: boolean
|
|
canSendEmoji: boolean
|
|
canSendAllianceRequest: boolean
|
|
canBreakAlliance: boolean
|
|
canTarget: boolean
|
|
canDonate: boolean
|
|
}
|
|
|
|
export type GameUpdate = TileUpdateWrapper
|
|
| UnitUpdate
|
|
| PlayerUpdate
|
|
| AllianceRequestUpdate
|
|
| AllianceRequestReplyUpdate
|
|
| BrokeAllianceUpdate
|
|
| AllianceExpiredUpdate
|
|
| DisplayMessageUpdate
|
|
| TargetPlayerUpdate
|
|
| EmojiUpdate
|
|
| WinUpdate
|
|
|
|
export interface TileUpdateWrapper {
|
|
type: GameUpdateType.Tile,
|
|
update: TileUpdate
|
|
}
|
|
|
|
export interface UnitUpdate {
|
|
type: GameUpdateType.Unit
|
|
unitType: UnitType
|
|
troops: number
|
|
id: number
|
|
ownerID: number
|
|
pos: MapPos
|
|
lastPos: MapPos
|
|
isActive: boolean
|
|
health?: number
|
|
}
|
|
|
|
export interface PlayerUpdate {
|
|
type: GameUpdateType.Player
|
|
nameViewData?: NameViewData,
|
|
clientID: ClientID,
|
|
name: string,
|
|
displayName: string,
|
|
id: PlayerID,
|
|
smallID: number,
|
|
playerType: PlayerType,
|
|
isAlive: boolean,
|
|
tilesOwned: number,
|
|
allies: PlayerID[],
|
|
gold: number,
|
|
population: number,
|
|
workers: number,
|
|
troops: number,
|
|
targetTroopRatio: number
|
|
}
|
|
|
|
|
|
export interface AllianceRequestUpdate {
|
|
type: GameUpdateType.AllianceRequest
|
|
requestorID: number,
|
|
recipientID: number,
|
|
createdAt: Tick,
|
|
}
|
|
|
|
export interface AllianceRequestReplyUpdate {
|
|
type: GameUpdateType.AllianceRequestReply
|
|
request: AllianceRequestUpdate
|
|
accepted: boolean
|
|
}
|
|
|
|
export interface BrokeAllianceUpdate {
|
|
type: GameUpdateType.BrokeAlliance
|
|
traitorID: number
|
|
betrayedID: number
|
|
}
|
|
|
|
export interface AllianceExpiredUpdate {
|
|
type: GameUpdateType.AllianceExpired
|
|
player1ID: number
|
|
player2ID: number
|
|
}
|
|
|
|
export interface TargetPlayerUpdate {
|
|
type: GameUpdateType.TargetPlayer
|
|
playerID: number
|
|
targetID: number
|
|
}
|
|
|
|
export interface EmojiUpdate {
|
|
type: GameUpdateType.EmojiUpdate
|
|
message: string
|
|
senderID: number
|
|
recipientID: number | typeof AllPlayers
|
|
createdAt: Tick
|
|
}
|
|
|
|
export interface DisplayMessageUpdate {
|
|
type: GameUpdateType.DisplayEvent
|
|
message: string
|
|
messageType: MessageType
|
|
playerID: number | null
|
|
}
|
|
|
|
export interface WinUpdate {
|
|
type: GameUpdateType.WinUpdate
|
|
winnerID: number,
|
|
}
|
|
|
|
export enum MessageType {
|
|
SUCCESS,
|
|
INFO,
|
|
WARN,
|
|
ERROR
|
|
}
|
|
|