port spawns on coast

This commit is contained in:
evanpelle
2024-11-13 16:32:40 -08:00
committed by Evan
parent febabf00db
commit 1c8489f099
6 changed files with 73 additions and 24 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import { Config } from "../core/configuration/Config" import { Config } from "../core/configuration/Config"
import { EventBus, GameEvent } from "../core/EventBus" import { EventBus, GameEvent } from "../core/EventBus"
import { AllianceRequest, AllPlayers, Cell, Item, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game" import { AllianceRequest, AllPlayers, Cell, BuildItem, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game"
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, BuildUnitIntentSchema, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas" import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientLeaveMessageSchema, BuildUnitIntentSchema, GameID, Intent, ServerMessage, ServerMessageSchema } from "../core/Schemas"
import { LocalServer } from "./LocalServer" import { LocalServer } from "./LocalServer"
+2 -1
View File
@@ -15,6 +15,7 @@ import { ControlPanel } from "./layers/ControlPanel";
import { UIState } from "./UIState"; import { UIState } from "./UIState";
import { BuildMenu } from "./layers/radial/BuildMenu"; import { BuildMenu } from "./layers/radial/BuildMenu";
import { UnitLayer } from "./layers/UnitLayer"; import { UnitLayer } from "./layers/UnitLayer";
import { BuildValidator } from "../../core/game/BuildValidator";
export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer { export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus: EventBus, clientID: ClientID): GameRenderer {
@@ -33,7 +34,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: Game, eventBus:
} }
buildMenu.game = game buildMenu.game = game
buildMenu.eventBus = eventBus buildMenu.eventBus = eventBus
buildMenu.init() buildMenu.buildValidator = new BuildValidator(game)
const leaderboard = document.querySelector('leader-board') as Leaderboard; const leaderboard = document.querySelector('leader-board') as Leaderboard;
if (!emojiTable || !(leaderboard instanceof Leaderboard)) { if (!emojiTable || !(leaderboard instanceof Leaderboard)) {
+11 -15
View File
@@ -1,21 +1,21 @@
import { LitElement, html, css } from 'lit'; import { LitElement, html, css } from 'lit';
import { customElement, state } from 'lit/decorators.js'; import { customElement, state } from 'lit/decorators.js';
import { EventBus } from '../../../../core/EventBus'; import { EventBus } from '../../../../core/EventBus';
import { Cell, Game, Item, BuildItems, Player, UnitType } from '../../../../core/game/Game'; import { Cell, Game, BuildItem, BuildItems, Player, UnitType } from '../../../../core/game/Game';
import { BuildUnitIntentEvent as BuildItemIntentEvent, BuildUnitIntentEvent, SendNukeIntentEvent } from '../../../Transport'; import { BuildUnitIntentEvent, SendNukeIntentEvent } from '../../../Transport';
import nukeIcon from '../../../../../resources/images/NukeIconWhite.svg'; import nukeIcon from '../../../../../resources/images/NukeIconWhite.svg';
import destroyerIcon from '../../../../../resources/images/DestroyerIconWhite.svg'; import destroyerIcon from '../../../../../resources/images/DestroyerIconWhite.svg';
import goldCoinIcon from '../../../../../resources/images/GoldCoinIcon.svg'; import goldCoinIcon from '../../../../../resources/images/GoldCoinIcon.svg';
import portIcon from '../../../../../resources/images/PortIcon.svg'; import portIcon from '../../../../../resources/images/PortIcon.svg';
import { renderNumber } from '../../Utils'; import { renderNumber } from '../../Utils';
import { ContextMenuEvent } from '../../../InputHandler'; import { BuildValidator } from '../../../../core/game/BuildValidator';
interface BuildItem { interface BuildItemDisplay {
item: Item item: BuildItem
icon: string; icon: string;
} }
const buildTable: BuildItem[][] = [ const buildTable: BuildItemDisplay[][] = [
[ [
{ item: BuildItems.Nuke, icon: nukeIcon }, { item: BuildItems.Nuke, icon: nukeIcon },
{ item: BuildItems.Destroyer, icon: destroyerIcon }, { item: BuildItems.Destroyer, icon: destroyerIcon },
@@ -27,6 +27,7 @@ const buildTable: BuildItem[][] = [
export class BuildMenu extends LitElement { export class BuildMenu extends LitElement {
public game: Game; public game: Game;
public eventBus: EventBus; public eventBus: EventBus;
public buildValidator: BuildValidator;
private myPlayer: Player; private myPlayer: Player;
private clickedCell: Cell; private clickedCell: Cell;
@@ -145,19 +146,14 @@ export class BuildMenu extends LitElement {
@state() @state()
private _hidden = true; private _hidden = true;
private canBuild(item: BuildItem): boolean { private canBuild(item: BuildItemDisplay): boolean {
if (!this.myPlayer || this.myPlayer.gold() < item.item.cost) { if(this.myPlayer == null) {
return false return false
} }
switch (item.item) { return this.buildValidator.canBuild(this.myPlayer, this.game.tile(this.clickedCell), item.item)
case BuildItems.Destroyer:
return this.myPlayer.units(UnitType.Port).length > 0
default:
return true
}
} }
public onBuildSelected = (item: BuildItem) => { public onBuildSelected = (item: BuildItemDisplay) => {
switch (item.item) { switch (item.item) {
case BuildItems.Nuke: case BuildItems.Nuke:
this.eventBus.emit(new SendNukeIntentEvent(this.myPlayer, this.clickedCell, null)) this.eventBus.emit(new SendNukeIntentEvent(this.myPlayer, this.clickedCell, null))
+20 -2
View File
@@ -1,4 +1,6 @@
import { AllPlayers, Cell, Execution, MutableGame, MutablePlayer, PlayerID, UnitType } from "../game/Game"; import { BuildValidator } from "../game/BuildValidator";
import { AllPlayers, BuildItem, BuildItems, Cell, Execution, MutableGame, MutablePlayer, PlayerID, UnitType } from "../game/Game";
import { bfs, dist, manhattanDist } from "../Util";
export class PortExecution implements Execution { export class PortExecution implements Execution {
@@ -18,7 +20,23 @@ export class PortExecution implements Execution {
} }
tick(ticks: number): void { tick(ticks: number): void {
this.player.addUnit(UnitType.Port, 0, this.mg.tile(this.cell)) const tile = this.mg.tile(this.cell)
if (!new BuildValidator(this.mg).canBuild(this.player, tile, BuildItems.Port)) {
console.warn(`player ${this.player} cannot build port at ${this.cell}`)
this.active = false
return
}
const spawns = Array.from(bfs(tile, dist(tile, 20)))
.filter(t => t.isOceanShore() && t.owner() == this.player)
.sort((a, b) => manhattanDist(a.cell(), tile.cell()) - manhattanDist(b.cell(), tile.cell()))
if (spawns.length == 0) {
console.warn(`cannot find spawn for port`)
this.active = false
return
}
this.player.addUnit(UnitType.Port, 0, spawns[0])
this.active = false this.active = false
} }
+33
View File
@@ -0,0 +1,33 @@
import { bfs, dist, manhattanDist } from "../Util";
import { BuildItem, BuildItems, 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) {
return false
}
switch (item) {
case BuildItems.Nuke:
return true
case BuildItems.Port:
return this.canBuildPort(player, tile)
case BuildItems.Destroyer:
return this.canBuildDestroyer(player, tile)
default:
throw Error(`item ${item.type} not supported`)
}
}
canBuildPort(player: Player, tile: Tile): boolean {
return Array.from(bfs(tile, dist(tile, 20)))
.filter(t => t.owner() == player && t.isOceanShore()).length > 0
}
canBuildDestroyer(player: Player, tile: Tile): boolean {
return player.units(UnitType.Port)
.filter(u => manhattanDist(u.tile().cell(), tile.cell()) < this.game.config().boatMaxDistance()).length > 0
}
}
+6 -5
View File
@@ -32,16 +32,17 @@ export enum UnitType {
Nuke = "Nuke", Nuke = "Nuke",
} }
export class Item { export class BuildItem {
constructor(public readonly type: UnitType, constructor(
public readonly type: UnitType,
public readonly cost: Gold public readonly cost: Gold
) { } ) { }
} }
export const BuildItems = { export const BuildItems = {
Nuke: new Item(UnitType.Nuke, 1_000_000), Nuke: new BuildItem(UnitType.Nuke, 1_000_000),
Destroyer: new Item(UnitType.Destroyer, 10), Destroyer: new BuildItem(UnitType.Destroyer, 10),
Port: new Item(UnitType.Port, 0) Port: new BuildItem(UnitType.Port, 0)
} as const; } as const;
export class Nation { export class Nation {