mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-28 03:24:16 +00:00
move unit info to config
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { Config } from "../core/configuration/Config"
|
||||
import { EventBus, GameEvent } from "../core/EventBus"
|
||||
import { AllianceRequest, AllPlayers, Cell, BuildItem, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game"
|
||||
import { AllianceRequest, AllPlayers, Cell, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game"
|
||||
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, BuildUnitIntentSchema, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas"
|
||||
import { LocalServer } from "./LocalServer"
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { customElement, state } from 'lit/decorators.js';
|
||||
import { EventBus } from '../../../../core/EventBus';
|
||||
import { Cell, Game, BuildItem, BuildItems, Player, UnitType } from '../../../../core/game/Game';
|
||||
import { Cell, Game, Player, UnitType } from '../../../../core/game/Game';
|
||||
import { BuildUnitIntentEvent, SendNukeIntentEvent } from '../../../Transport';
|
||||
import nukeIcon from '../../../../../resources/images/NukeIconWhite.svg';
|
||||
import destroyerIcon from '../../../../../resources/images/DestroyerIconWhite.svg';
|
||||
@@ -13,16 +13,16 @@ import { BuildValidator } from '../../../../core/game/BuildValidator';
|
||||
import { ContextMenuEvent } from '../../../InputHandler';
|
||||
|
||||
interface BuildItemDisplay {
|
||||
item: BuildItem
|
||||
unitType: UnitType
|
||||
icon: string;
|
||||
}
|
||||
|
||||
const buildTable: BuildItemDisplay[][] = [
|
||||
[
|
||||
{ item: BuildItems.Nuke, icon: nukeIcon },
|
||||
{ item: BuildItems.Destroyer, icon: destroyerIcon },
|
||||
{ item: BuildItems.Port, icon: portIcon },
|
||||
{ item: BuildItems.MissileSilo, icon: missileSiloIcon }
|
||||
{ unitType: UnitType.Nuke, icon: nukeIcon },
|
||||
{ unitType: UnitType.Destroyer, icon: destroyerIcon },
|
||||
{ unitType: UnitType.Port, icon: portIcon },
|
||||
{ unitType: UnitType.MissileSilo, icon: missileSiloIcon }
|
||||
]
|
||||
];
|
||||
|
||||
@@ -152,21 +152,21 @@ export class BuildMenu extends LitElement {
|
||||
if (this.myPlayer == null) {
|
||||
return false
|
||||
}
|
||||
return this.buildValidator.canBuild(this.myPlayer, this.game.tile(this.clickedCell), item.item)
|
||||
return this.buildValidator.canBuild(this.myPlayer, this.game.tile(this.clickedCell), item.unitType)
|
||||
}
|
||||
|
||||
public onBuildSelected = (item: BuildItemDisplay) => {
|
||||
switch (item.item) {
|
||||
case BuildItems.Nuke:
|
||||
switch (item.unitType) {
|
||||
case UnitType.Nuke:
|
||||
this.eventBus.emit(new SendNukeIntentEvent(this.myPlayer, this.clickedCell, null))
|
||||
break
|
||||
case BuildItems.Destroyer:
|
||||
case UnitType.Destroyer:
|
||||
this.eventBus.emit(new BuildUnitIntentEvent(UnitType.Destroyer, this.clickedCell))
|
||||
break
|
||||
case BuildItems.Port:
|
||||
case UnitType.Port:
|
||||
this.eventBus.emit(new BuildUnitIntentEvent(UnitType.Port, this.clickedCell))
|
||||
break
|
||||
case BuildItems.MissileSilo:
|
||||
case UnitType.MissileSilo:
|
||||
this.eventBus.emit(new BuildUnitIntentEvent(UnitType.MissileSilo, this.clickedCell))
|
||||
}
|
||||
this.hideMenu()
|
||||
@@ -184,10 +184,10 @@ export class BuildMenu extends LitElement {
|
||||
?disabled=${!this.canBuild(item)}
|
||||
title=${!this.canBuild(item) ? 'Not enough money' : ''}
|
||||
>
|
||||
<img src=${item.icon} alt="${item.item.type}" width="40" height="40">
|
||||
<span class="build-name">${item.item.type}</span>
|
||||
<img src=${item.icon} alt="${item.unitType}" width="40" height="40">
|
||||
<span class="build-name">${item.unitType}</span>
|
||||
<span class="build-cost">
|
||||
${renderNumber(item.item.cost)}
|
||||
${renderNumber(this.game ? this.game.unitInfo(item.unitType).cost : 0)}
|
||||
<img src=${goldCoinIcon} alt="gold" width="12" height="12" style="vertical-align: middle;">
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -98,6 +98,7 @@ export class AStar {
|
||||
}
|
||||
|
||||
private heuristic(a: Tile, b: Tile): number {
|
||||
// TODO use wrapped
|
||||
return 1.1 * Math.abs(a.cell().x - b.cell().x) + Math.abs(a.cell().y - b.cell().y);
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -200,4 +200,7 @@ export function processName(name: string): string {
|
||||
ALLOWED_URI_REGEXP: /^https:\/\/cdn\.jsdelivr\.net\/gh\/twitter\/twemoji/,
|
||||
ADD_ATTR: ['style']
|
||||
});
|
||||
}
|
||||
}
|
||||
export function assertNever(x: never): never {
|
||||
throw new Error('Unexpected value: ' + x);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Player, PlayerID, PlayerInfo, TerraNullius, Tick, Tile } from "../game/Game";
|
||||
import { Player, PlayerID, PlayerInfo, TerraNullius, Tick, Tile, UnitInfo, UnitType } from "../game/Game";
|
||||
import { Colord, colord } from "colord";
|
||||
import { devConfig } from "./DevConfig";
|
||||
import { defaultConfig } from "./DefaultConfig";
|
||||
@@ -56,6 +56,7 @@ export interface Config {
|
||||
emojiMessageDuration(): Tick
|
||||
donateCooldown(): Tick
|
||||
defaultDonationAmount(sender: Player): number
|
||||
unitInfo(type: UnitType): UnitInfo
|
||||
}
|
||||
|
||||
export interface Theme {
|
||||
|
||||
@@ -1,12 +1,42 @@
|
||||
import { Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tick, Tile } from "../game/Game";
|
||||
import { Player, PlayerInfo, PlayerType, TerrainType, TerraNullius, Tick, Tile, UnitInfo, UnitType } from "../game/Game";
|
||||
import { GameID } from "../Schemas";
|
||||
import { simpleHash, within } from "../Util";
|
||||
import { assertNever, simpleHash, within } from "../Util";
|
||||
import { Config, Theme } from "./Config";
|
||||
import { pastelTheme } from "./PastelTheme";
|
||||
|
||||
|
||||
|
||||
export class DefaultConfig implements Config {
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
switch (type) {
|
||||
case UnitType.TransportShip:
|
||||
return {
|
||||
cost: 0,
|
||||
}
|
||||
case UnitType.Destroyer:
|
||||
return {
|
||||
cost: 100_000
|
||||
}
|
||||
case UnitType.Port:
|
||||
return {
|
||||
cost: 300_000
|
||||
}
|
||||
case UnitType.Nuke:
|
||||
return {
|
||||
cost: 1_000_000
|
||||
}
|
||||
case UnitType.TradeShip:
|
||||
return {
|
||||
cost: 0
|
||||
}
|
||||
case UnitType.MissileSilo:
|
||||
return {
|
||||
cost: 1_000_000
|
||||
}
|
||||
default:
|
||||
assertNever(type)
|
||||
}
|
||||
}
|
||||
defaultDonationAmount(sender: Player): number {
|
||||
return Math.floor(sender.troops() / 3)
|
||||
}
|
||||
@@ -178,6 +208,4 @@ export class DefaultConfig implements Config {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const defaultConfig = new DefaultConfig()
|
||||
@@ -1,12 +1,18 @@
|
||||
import { PlayerInfo } from "../game/Game";
|
||||
import { PlayerInfo, UnitInfo, UnitType } from "../game/Game";
|
||||
import { DefaultConfig } from "./DefaultConfig";
|
||||
|
||||
export const devConfig = new class extends DefaultConfig {
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
const info = super.unitInfo(type)
|
||||
info.cost = 0
|
||||
return info
|
||||
}
|
||||
|
||||
percentageTilesOwnedToWin(): number {
|
||||
return 95
|
||||
}
|
||||
numSpawnPhaseTurns(): number {
|
||||
return 80
|
||||
return 40
|
||||
}
|
||||
gameCreationRate(): number {
|
||||
return 20 * 1000
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, Tile, UnitType } from "../game/Game";
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, MutableUnit, PlayerID, Tile, UnitType } from "../game/Game";
|
||||
import { AStar, PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { distSort, distSortUnit, manhattanDist } from "../Util";
|
||||
@@ -44,8 +44,7 @@ export class DestroyerExecution implements Execution {
|
||||
this.active = false
|
||||
return
|
||||
}
|
||||
this.destroyer = this._owner.addUnit(UnitType.Destroyer, 0, spawns[0])
|
||||
this._owner.removeGold(BuildItems.Destroyer.cost)
|
||||
this.destroyer = this._owner.buildUnit(UnitType.Destroyer, 0, spawns[0])
|
||||
return
|
||||
}
|
||||
if (!this.destroyer.isActive()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BuildValidator } from "../game/BuildValidator";
|
||||
import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AllPlayers, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AStar, PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, manhattanDist } from "../Util";
|
||||
@@ -26,12 +26,12 @@ export class MissileSiloExecution implements Execution {
|
||||
tick(ticks: number): void {
|
||||
if (this.silo == null) {
|
||||
const tile = this.mg.tile(this.cell)
|
||||
if (!new BuildValidator(this.mg).canBuild(this.player, tile, BuildItems.MissileSilo)) {
|
||||
if (!new BuildValidator(this.mg).canBuild(this.player, tile, UnitType.MissileSilo)) {
|
||||
console.warn(`player ${this.player} cannot build port at ${this.cell}`)
|
||||
this.active = false
|
||||
return
|
||||
}
|
||||
this.silo = this.player.addUnit(UnitType.MissileSilo, 0, tile)
|
||||
this.silo = this.player.buildUnit(UnitType.MissileSilo, 0, tile)
|
||||
}
|
||||
|
||||
if (!this.silo.tile().hasOwner()) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BuildValidator } from "../game/BuildValidator";
|
||||
import { Cell, Execution, BuildItems, MutableGame, MutablePlayer, PlayerID, Tile, MutableUnit, UnitType } from "../game/Game";
|
||||
import { Cell, Execution, MutableGame, MutablePlayer, PlayerID, Tile, MutableUnit, UnitType } from "../game/Game";
|
||||
import { PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, distSortUnit, euclideanDist, manhattanDist } from "../Util";
|
||||
@@ -35,11 +35,10 @@ export class NukeExecution implements Execution {
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.nuke == null) {
|
||||
if (new BuildValidator(this.mg).canBuild(this.player, this.dst, BuildItems.Nuke)) {
|
||||
if (new BuildValidator(this.mg).canBuild(this.player, this.dst, UnitType.Nuke)) {
|
||||
const spawn = this.player.units(UnitType.MissileSilo)
|
||||
.sort((a, b) => manhattanDist(a.tile().cell(), this.cell) - manhattanDist(b.tile().cell(), this.cell))[0]
|
||||
this.nuke = this.player.addUnit(UnitType.Nuke, 0, spawn.tile())
|
||||
this.player.removeGold(BuildItems.Nuke.cost)
|
||||
this.nuke = this.player.buildUnit(UnitType.Nuke, 0, spawn.tile())
|
||||
this.pathFinder = new PathFinder(10_000, t => true)
|
||||
} else {
|
||||
console.warn(`cannot build Nuke`)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BuildValidator } from "../game/BuildValidator";
|
||||
import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AllPlayers, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AStar, PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, manhattanDist } from "../Util";
|
||||
@@ -30,7 +30,7 @@ export class PortExecution implements Execution {
|
||||
tick(ticks: number): void {
|
||||
if (this.port == null) {
|
||||
const tile = this.mg.tile(this.cell)
|
||||
if (!new BuildValidator(this.mg).canBuild(this.player, tile, BuildItems.Port)) {
|
||||
if (!new BuildValidator(this.mg).canBuild(this.player, tile, UnitType.Port)) {
|
||||
console.warn(`player ${this.player} cannot build port at ${this.cell}`)
|
||||
this.active = false
|
||||
return
|
||||
@@ -44,8 +44,7 @@ export class PortExecution implements Execution {
|
||||
this.active = false
|
||||
return
|
||||
}
|
||||
this.port = this.player.addUnit(UnitType.Port, 0, spawns[0])
|
||||
this.player.removeGold(BuildItems.Port.cost)
|
||||
this.port = this.player.buildUnit(UnitType.Port, 0, spawns[0])
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { BuildValidator } from "../game/BuildValidator";
|
||||
import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AllPlayers, Cell, Execution, MutableGame, MutablePlayer, MutableUnit, Player, PlayerID, Tile, Unit, UnitType } from "../game/Game";
|
||||
import { AStar, PathFinder } from "../PathFinding";
|
||||
import { PseudoRandom } from "../PseudoRandom";
|
||||
import { bfs, dist, manhattanDist } from "../Util";
|
||||
@@ -28,7 +28,7 @@ export class TradeShipExecution implements Execution {
|
||||
|
||||
tick(ticks: number): void {
|
||||
if (this.tradeShip == null) {
|
||||
this.tradeShip = this.player.addUnit(UnitType.TradeShip, 0, this.srcPort.tile())
|
||||
this.tradeShip = this.player.buildUnit(UnitType.TradeShip, 0, this.srcPort.tile())
|
||||
}
|
||||
if (this.index >= this.path.length) {
|
||||
this.active = false
|
||||
|
||||
@@ -79,7 +79,7 @@ export class TransportShipExecution implements Execution {
|
||||
}
|
||||
|
||||
|
||||
this.boat = this.attacker.addUnit(UnitType.TransportShip, this.troops, this.src)
|
||||
this.boat = this.attacker.buildUnit(UnitType.TransportShip, this.troops, this.src)
|
||||
}
|
||||
|
||||
tick(ticks: number) {
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { bfs, dist, manhattanDist } from "../Util";
|
||||
import { BuildItem, BuildItems, Game, Player, Tile, UnitType } from "./Game";
|
||||
import { Game, Player, Tile, UnitType } from "./Game";
|
||||
|
||||
export class BuildValidator {
|
||||
constructor(private game: Game) { }
|
||||
|
||||
canBuild(player: Player, tile: Tile, item: BuildItem): boolean {
|
||||
if (!player.isAlive() || player.gold() < item.cost) {
|
||||
canBuild(player: Player, tile: Tile, unitType: UnitType): boolean {
|
||||
const cost = this.game.unitInfo(unitType).cost
|
||||
if (!player.isAlive() || player.gold() < cost) {
|
||||
return false
|
||||
}
|
||||
switch (item) {
|
||||
case BuildItems.Nuke:
|
||||
switch (unitType) {
|
||||
case UnitType.Nuke:
|
||||
return player.units(UnitType.MissileSilo).length > 0
|
||||
case BuildItems.Port:
|
||||
case UnitType.Port:
|
||||
return this.canBuildPort(player, tile)
|
||||
case BuildItems.Destroyer:
|
||||
case UnitType.Destroyer:
|
||||
return this.canBuildDestroyer(player, tile)
|
||||
case BuildItems.MissileSilo:
|
||||
case UnitType.MissileSilo:
|
||||
return tile.owner() == player
|
||||
default:
|
||||
throw Error(`item ${item.type} not supported`)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-16
@@ -22,6 +22,10 @@ export enum GameMap {
|
||||
Mena
|
||||
}
|
||||
|
||||
export interface UnitInfo {
|
||||
cost: Gold
|
||||
}
|
||||
|
||||
export enum UnitType {
|
||||
TransportShip = "Transport",
|
||||
Destroyer = "Destroyer",
|
||||
@@ -31,20 +35,6 @@ export enum UnitType {
|
||||
MissileSilo = "Missile Silo",
|
||||
}
|
||||
|
||||
export class BuildItem {
|
||||
constructor(
|
||||
public readonly type: UnitType,
|
||||
public readonly cost: Gold
|
||||
) { }
|
||||
}
|
||||
|
||||
export const BuildItems = {
|
||||
Nuke: new BuildItem(UnitType.Nuke, 1_000_000),
|
||||
Destroyer: new BuildItem(UnitType.Destroyer, 100_000),
|
||||
Port: new BuildItem(UnitType.Port, 300_000),
|
||||
MissileSilo: new BuildItem(UnitType.MissileSilo, 1_000_000),
|
||||
} as const;
|
||||
|
||||
export class Nation {
|
||||
constructor(
|
||||
public readonly name: string,
|
||||
@@ -253,8 +243,7 @@ export interface MutablePlayer extends Player {
|
||||
addTroops(troops: number): void
|
||||
removeTroops(troops: number): number
|
||||
|
||||
// TODO: make addUnit require gold
|
||||
addUnit(type: UnitType, troops: number, tile: Tile): MutableUnit
|
||||
buildUnit(type: UnitType, troops: number, tile: Tile): MutableUnit
|
||||
}
|
||||
|
||||
export interface Game {
|
||||
@@ -280,6 +269,7 @@ export interface Game {
|
||||
config(): Config
|
||||
displayMessage(message: string, type: MessageType, playerID: PlayerID | null): void
|
||||
units(...types: UnitType[]): Unit[]
|
||||
unitInfo(type: UnitType): UnitInfo
|
||||
}
|
||||
|
||||
export interface MutableGame extends Game {
|
||||
|
||||
@@ -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, Unit, UnitEvent as UnitEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent, Nation, UnitType } from "./Game";
|
||||
import { Cell, Execution, MutableGame, Game, MutablePlayer, PlayerEvent, PlayerID, PlayerInfo, Player, TerraNullius, Tile, TileEvent, Unit, UnitEvent as UnitEvent, PlayerType, MutableAllianceRequest, AllianceRequestReplyEvent, AllianceRequestEvent, BrokeAllianceEvent, MutableAlliance, Alliance, AllianceExpiredEvent, Nation, UnitType, UnitInfo } from "./Game";
|
||||
import { TerrainMap } from "./TerrainMapLoader";
|
||||
import { PlayerImpl } from "./PlayerImpl";
|
||||
import { TerraNulliusImpl } from "./TerraNulliusImpl";
|
||||
@@ -61,6 +61,9 @@ export class GameImpl implements MutableGame {
|
||||
units(...types: UnitType[]): UnitImpl[] {
|
||||
return Array.from(this._players.values()).flatMap(p => p.units(...types))
|
||||
}
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
return this.config().unitInfo(type)
|
||||
}
|
||||
nations(): Nation[] {
|
||||
return this.nations_
|
||||
}
|
||||
|
||||
@@ -73,9 +73,10 @@ export class PlayerImpl implements MutablePlayer {
|
||||
}
|
||||
|
||||
|
||||
addUnit(type: UnitType, troops: number, tile: Tile): UnitImpl {
|
||||
buildUnit(type: UnitType, troops: number, tile: Tile): UnitImpl {
|
||||
const b = new UnitImpl(type, this.gs, tile, troops, this);
|
||||
this._units.push(b);
|
||||
this.removeGold(this.gs.unitInfo(type).cost)
|
||||
this.gs.fireUnitUpdateEvent(b, b.tile());
|
||||
return b;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user