mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-07-02 22:38:06 +00:00
thread_split: convert all tile to tileref
This commit is contained in:
@@ -4,8 +4,7 @@ import { EventBus } from "../core/EventBus";
|
||||
import { createRenderer, GameRenderer } from "./graphics/GameRenderer";
|
||||
import { InputHandler, MouseUpEvent, ZoomEvent, DragEvent, MouseDownEvent } from "./InputHandler"
|
||||
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, ClientMessageSchema, GameConfig, GameID, Intent, ServerMessage, ServerMessageSchema, ServerSyncMessage, Turn } from "../core/Schemas";
|
||||
import { loadTerrainFromFile, loadTerrainMap, TerrainMapImpl } from "../core/game/TerrainMapLoader";
|
||||
import { and, bfs, dist, generateID, manhattanDist } from "../core/Util";
|
||||
import { loadTerrainFromFile, loadTerrainMap } from "../core/game/TerrainMapLoader";
|
||||
import { SendAttackIntentEvent, SendSpawnIntentEvent, Transport } from "./Transport";
|
||||
import { createCanvas } from "./Utils";
|
||||
import { MessageType } from '../core/game/Game';
|
||||
@@ -72,10 +71,10 @@ export function joinLobby(lobbyConfig: LobbyConfig, onjoin: () => void): () => v
|
||||
export async function createClientGame(lobbyConfig: LobbyConfig, gameConfig: GameConfig, eventBus: EventBus, transport: Transport): Promise<ClientGameRunner> {
|
||||
const config = getConfig(gameConfig)
|
||||
|
||||
const terrainMap = await loadTerrainMap(gameConfig.gameMap);
|
||||
const gameMap = await loadTerrainMap(gameConfig.gameMap);
|
||||
const worker = new WorkerClient(lobbyConfig.gameID, gameConfig)
|
||||
await worker.initialize()
|
||||
const gameView = new GameView(worker, config, terrainMap.terrain)
|
||||
const gameView = new GameView(worker, config, gameMap.gameMap)
|
||||
|
||||
|
||||
consolex.log('going to init path finder')
|
||||
@@ -177,12 +176,12 @@ export class ClientGameRunner {
|
||||
return
|
||||
}
|
||||
const cell = this.renderer.transformHandler.screenToWorldCoordinates(event.x, event.y)
|
||||
if (!this.gameView.isOnMap(cell)) {
|
||||
if (!this.gameView.isValidCoord(cell.x, cell.y)) {
|
||||
return
|
||||
}
|
||||
consolex.log(`clicked cell ${cell}`)
|
||||
const tile = this.gameView.tile(cell)
|
||||
if (tile.terrain().isLand() && !tile.hasOwner() && this.gameView.inSpawnPhase()) {
|
||||
const tile = this.gameView.ref(cell.x, cell.y)
|
||||
if (this.gameView.isLand(tile) && !this.gameView.hasOwner(tile) && this.gameView.inSpawnPhase()) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(cell))
|
||||
return
|
||||
}
|
||||
@@ -200,7 +199,7 @@ export class ClientGameRunner {
|
||||
if (actions.canAttack) {
|
||||
this.eventBus.emit(
|
||||
new SendAttackIntentEvent(
|
||||
tile.owner().id(),
|
||||
this.gameView.owner(tile).id(),
|
||||
this.myPlayer.troops() * this.renderer.uiState.attackRatio
|
||||
)
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Config, ServerConfig } from "../core/configuration/Config"
|
||||
import { SendLogEvent } from "../core/Consolex"
|
||||
import { EventBus, GameEvent } from "../core/EventBus"
|
||||
import { AllianceRequest, AllPlayers, Cell, GameType, Player, PlayerID, PlayerType, Tile, UnitType } from "../core/game/Game"
|
||||
import { AllianceRequest, AllPlayers, Cell, GameType, Player, PlayerID, PlayerType, UnitType } from "../core/game/Game"
|
||||
import { ClientID, ClientIntentMessageSchema, ClientJoinMessageSchema, GameID, Intent, ServerMessage, ServerMessageSchema, ClientPingMessageSchema, GameConfig, ClientLogMessageSchema } from "../core/Schemas"
|
||||
import { LobbyConfig } from "./ClientGameRunner"
|
||||
import { LocalServer } from "./LocalServer"
|
||||
@@ -298,10 +298,6 @@ export class Transport {
|
||||
attackerID: this.lobbyConfig.playerID,
|
||||
targetID: event.targetID,
|
||||
troops: event.troops,
|
||||
sourceX: null,
|
||||
sourceY: null,
|
||||
targetX: null,
|
||||
targetY: null,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { Game, Player, Tile, Cell, NameViewData } from '../../core/game/Game';
|
||||
import { GameView } from '../../core/GameView';
|
||||
import { Game, Player, Cell, NameViewData } from '../../core/game/Game';
|
||||
import { calculateBoundingBox, within } from '../../core/Util';
|
||||
|
||||
export interface Point {
|
||||
@@ -17,12 +16,7 @@ export interface Rectangle {
|
||||
|
||||
|
||||
export function placeName(game: Game, player: Player): NameViewData {
|
||||
return {
|
||||
x: 0,
|
||||
y: 0,
|
||||
size: 0
|
||||
}
|
||||
const boundingBox = calculateBoundingBox(player.borderTiles());
|
||||
const boundingBox = calculateBoundingBox(game, player.borderTiles());
|
||||
|
||||
|
||||
const rawScalingFactor = (boundingBox.max.x - boundingBox.min.x) / 100
|
||||
@@ -72,8 +66,8 @@ export function createGrid(game: Game, player: Player, boundingBox: { min: Point
|
||||
for (let y = scaledBoundingBox.min.y; y <= scaledBoundingBox.max.y; y++) {
|
||||
const cell = new Cell(x * scalingFactor, y * scalingFactor);
|
||||
if (game.isOnMap(cell)) {
|
||||
const tile = game.tile(cell);
|
||||
grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = tile.terrain().isLake() || tile.owner() === player; // TODO: okay if lake
|
||||
const tile = game.ref(cell.x, cell.y);
|
||||
grid[x - scaledBoundingBox.min.x][y - scaledBoundingBox.min.y] = this.game.isLake(tile) || this.game.owner(tile) === player; // TODO: okay if lake
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { colord } from "colord";
|
||||
import { EventBus } from "../../core/EventBus"
|
||||
import { Cell, Game, Player } from "../../core/game/Game";
|
||||
import { calculateBoundingBox, calculateBoundingBoxCenter, manhattanDist } from "../../core/Util";
|
||||
import { calculateBoundingBox, calculateBoundingBoxCenter } from "../../core/Util";
|
||||
import { ZoomEvent, DragEvent } from "../InputHandler";
|
||||
import { GoToPlayerEvent } from "./layers/Leaderboard";
|
||||
import { placeName } from "./NameBoxCalculator";
|
||||
@@ -131,7 +131,7 @@ export class TransformHandler {
|
||||
const { screenX, screenY } = this.screenCenter()
|
||||
const screenMapCenter = new Cell(screenX, screenY)
|
||||
|
||||
if (manhattanDist(screenMapCenter, this.target) < 2) {
|
||||
if (this.game.manhattanDist(this.game.ref(screenX, screenY), this.game.ref(this.target.x, this.target.y)) < 2) {
|
||||
this.clearTarget()
|
||||
return
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { LitElement, html, css } from 'lit';
|
||||
import { customElement, property, state } from 'lit/decorators.js';
|
||||
import { Layer } from './Layer';
|
||||
import { Game, Player } from '../../../core/game/Game';
|
||||
import { ClientID } from '../../../core/Schemas';
|
||||
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
||||
import { EventBus, GameEvent } from '../../../core/EventBus';
|
||||
@@ -59,7 +58,7 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
.map((player, index) => ({
|
||||
name: player.displayName(),
|
||||
position: index + 1,
|
||||
score: formatPercentage(player.numTilesOwned() / this.game.terrainMap().numLandTiles()),
|
||||
score: formatPercentage(player.numTilesOwned() / this.game.numLandTiles()),
|
||||
gold: renderNumber(player.gold()),
|
||||
isMyPlayer: player == myPlayer,
|
||||
player: player
|
||||
@@ -78,7 +77,7 @@ export class Leaderboard extends LitElement implements Layer {
|
||||
this.players.push({
|
||||
name: myPlayer.displayName(),
|
||||
position: place,
|
||||
score: formatPercentage(myPlayer.numTilesOwned() / this.game.terrainMap().numLandTiles()),
|
||||
score: formatPercentage(myPlayer.numTilesOwned() / this.game.numLandTiles()),
|
||||
gold: renderNumber(myPlayer.gold()),
|
||||
isMyPlayer: true,
|
||||
player: myPlayer
|
||||
|
||||
@@ -6,10 +6,25 @@ import { ClientID } from '../../../core/Schemas';
|
||||
import { EventBus } from '../../../core/EventBus';
|
||||
import { TransformHandler } from '../TransformHandler';
|
||||
import { MouseMoveEvent } from '../../InputHandler';
|
||||
import { euclideanDist, distSortUnit } from '../../../core/Util';
|
||||
import { renderNumber, renderTroops } from '../../Utils';
|
||||
import { PauseGameEvent } from '../../Transport';
|
||||
import { GameView, PlayerView } from '../../../core/GameView';
|
||||
import { TileRef } from '../../../core/game/GameMap';
|
||||
import { PauseGameEvent } from '../../Transport';
|
||||
|
||||
function euclideanDistWorld(coord: { x: number, y: number }, tileRef: TileRef, game: GameView): number {
|
||||
const x = game.x(tileRef);
|
||||
const y = game.y(tileRef);
|
||||
const dx = coord.x - x;
|
||||
const dy = coord.y - y;
|
||||
return Math.sqrt(dx * dx + dy * dy);
|
||||
}
|
||||
|
||||
function distSortUnitWorld(coord: { x: number, y: number }, game: GameView) {
|
||||
return (a: Unit, b: Unit) => {
|
||||
const distA = euclideanDistWorld(coord, a.tile(), game);
|
||||
const distB = euclideanDistWorld(coord, b.tile(), game);
|
||||
return distA - distB;
|
||||
};
|
||||
}
|
||||
|
||||
@customElement('player-info-overlay')
|
||||
export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
@@ -29,13 +44,13 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
private player: Player | null = null;
|
||||
|
||||
@state()
|
||||
private playerProfile: PlayerProfile | null = null
|
||||
private playerProfile: PlayerProfile | null = null;
|
||||
|
||||
@state()
|
||||
private unit: Unit | null = null;
|
||||
|
||||
@state()
|
||||
private showPauseButton: boolean = true
|
||||
private showPauseButton: boolean = true;
|
||||
|
||||
@state()
|
||||
private _isInfoVisible: boolean = false;
|
||||
@@ -43,39 +58,40 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
@state()
|
||||
private _isPaused: boolean = false;
|
||||
|
||||
private _isActive = false
|
||||
private _isActive = false;
|
||||
|
||||
init() {
|
||||
this.eventBus.on(MouseMoveEvent, (e: MouseMoveEvent) => this.onMouseEvent(e));
|
||||
this._isActive = true
|
||||
this.showPauseButton = this.game.config().gameConfig().gameType == GameType.Singleplayer
|
||||
this._isActive = true;
|
||||
this.showPauseButton = this.game.config().gameConfig().gameType == GameType.Singleplayer;
|
||||
}
|
||||
|
||||
private onMouseEvent(event: MouseMoveEvent) {
|
||||
this.setVisible(false);
|
||||
this.unit = null;
|
||||
const lastPlayer = this.player
|
||||
this.player = null;
|
||||
|
||||
const worldCoord = this.transform.screenToWorldCoordinates(event.x, event.y);
|
||||
if (!this.game.isOnMap(worldCoord)) {
|
||||
if (!this.game.isValidCoord(worldCoord.x, worldCoord.y)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const tile = this.game.tile(worldCoord);
|
||||
const owner = tile.owner();
|
||||
const tile = this.game.ref(worldCoord.x, worldCoord.y);
|
||||
if (!tile) return;
|
||||
|
||||
if (owner.isPlayer()) {
|
||||
const owner = this.game.owner(tile);
|
||||
|
||||
if (owner && owner.isPlayer()) {
|
||||
this.player = owner;
|
||||
(this.player as PlayerView).profile().then(p => {
|
||||
console.log(`got profile ${JSON.stringify(p)}`)
|
||||
this.playerProfile = p
|
||||
})
|
||||
console.log(`got profile ${JSON.stringify(p)}`);
|
||||
this.playerProfile = p;
|
||||
});
|
||||
this.setVisible(true);
|
||||
} else if (!tile.terrain().isLand()) {
|
||||
} else if (!this.game.isLand(tile)) {
|
||||
const units = this.game.units(UnitType.Destroyer, UnitType.Battleship, UnitType.TradeShip)
|
||||
.filter(u => euclideanDist(worldCoord, u.tile().cell()) < 50)
|
||||
.sort(distSortUnit(tile));
|
||||
.filter(u => euclideanDistWorld(worldCoord, u.tile(), this.game) < 50)
|
||||
.sort(distSortUnitWorld(worldCoord, this.game));
|
||||
|
||||
if (units.length > 0) {
|
||||
this.unit = units[0];
|
||||
@@ -120,28 +136,28 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
private renderPlayerInfo(player: Player) {
|
||||
const myPlayer = this.myPlayer();
|
||||
const isAlly = (myPlayer?.isAlliedWith(player) || player == this.myPlayer()) ?? false;
|
||||
let relationHtml = null
|
||||
let relationHtml = null;
|
||||
if (player.type() == PlayerType.FakeHuman && myPlayer != null) {
|
||||
let classType = ''
|
||||
let relationName = ''
|
||||
const relation = this.playerProfile?.relations[myPlayer.smallID()] ?? Relation.Neutral
|
||||
let classType = '';
|
||||
let relationName = '';
|
||||
const relation = this.playerProfile?.relations[myPlayer.smallID()] ?? Relation.Neutral;
|
||||
switch (relation) {
|
||||
case Relation.Hostile:
|
||||
classType = 'hostile'
|
||||
relationName = 'Hostile'
|
||||
break
|
||||
classType = 'hostile';
|
||||
relationName = 'Hostile';
|
||||
break;
|
||||
case Relation.Distrustful:
|
||||
classType = 'distrustful'
|
||||
relationName = 'Distrustful'
|
||||
break
|
||||
classType = 'distrustful';
|
||||
relationName = 'Distrustful';
|
||||
break;
|
||||
case Relation.Neutral:
|
||||
classType = 'neutral'
|
||||
relationName = 'Neutral'
|
||||
break
|
||||
classType = 'neutral';
|
||||
relationName = 'Neutral';
|
||||
break;
|
||||
case Relation.Friendly:
|
||||
classType = 'friendly'
|
||||
relationName = 'Friendly'
|
||||
break
|
||||
classType = 'friendly';
|
||||
relationName = 'Friendly';
|
||||
break;
|
||||
}
|
||||
|
||||
relationHtml = html`<div class="type-label">Attitude: <span class="${classType}">${relationName}</span></div>`;
|
||||
@@ -149,8 +165,8 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
return html`
|
||||
<div class="info-content">
|
||||
<div class="player-name ${isAlly ? 'ally' : ''}">${player.name()}</div>
|
||||
<div class="type-label">Troops: ${renderTroops(player.troops())}</div>
|
||||
<div class="type-label">Gold: ${renderNumber(player.gold())}</div>
|
||||
<div class="type-label">Troops: ${player.troops()}</div>
|
||||
<div class="type-label">Gold: ${player.gold()}</div>
|
||||
${relationHtml == null ? '' : relationHtml}
|
||||
</div>
|
||||
`;
|
||||
@@ -173,7 +189,7 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
|
||||
render() {
|
||||
if (!this._isActive) {
|
||||
return html``
|
||||
return html``;
|
||||
}
|
||||
return html`
|
||||
<div class="container">
|
||||
@@ -277,6 +293,19 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.hostile {
|
||||
color: #ff4444;
|
||||
}
|
||||
.distrustful {
|
||||
color: #ff8888;
|
||||
}
|
||||
.neutral {
|
||||
color: #ffffff;
|
||||
}
|
||||
.friendly {
|
||||
color: #4CAF50;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
top: 5px;
|
||||
@@ -302,19 +331,6 @@ export class PlayerInfoOverlay extends LitElement implements Layer {
|
||||
.type-label {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
}
|
||||
.hostile {
|
||||
color: #ff4444;
|
||||
}
|
||||
.distrustful {
|
||||
color: #ff8888;
|
||||
}
|
||||
.neutral {
|
||||
color: #ffffff;
|
||||
}
|
||||
.friendly {
|
||||
color: #4CAF50;
|
||||
}
|
||||
`;
|
||||
}
|
||||
@@ -1,7 +1,5 @@
|
||||
import { colord, Colord } from "colord";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { Unit, Cell, Game, Tile, UnitType } from "../../../core/game/Game";
|
||||
import { bfs, dist, euclDist } from "../../../core/Util";
|
||||
import { Layer } from "./Layer";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
|
||||
@@ -10,6 +8,8 @@ import missileSiloIcon from '../../../../resources/images/MissileSiloUnit.png';
|
||||
import shieldIcon from '../../../../resources/images/ShieldIcon.png';
|
||||
import cityIcon from '../../../../resources/images/CityIcon.png';
|
||||
import { GameView } from "../../../core/GameView";
|
||||
import { Cell, Unit, UnitType } from "../../../core/game/Game";
|
||||
import { euclDistFN } from "../../../core/game/GameMap";
|
||||
|
||||
interface UnitRenderConfig {
|
||||
icon: string;
|
||||
@@ -17,14 +17,13 @@ interface UnitRenderConfig {
|
||||
territoryRadius: number;
|
||||
}
|
||||
|
||||
|
||||
export class StructureLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement;
|
||||
private context: CanvasRenderingContext2D;
|
||||
private unitImages: Map<string, HTMLImageElement> = new Map();
|
||||
private theme: Theme = null;
|
||||
|
||||
private seenUnits = new Set<Unit>()
|
||||
private seenUnits = new Set<Unit>();
|
||||
|
||||
// Configuration for supported unit types only
|
||||
private readonly unitConfigs: Partial<Record<UnitType, UnitRenderConfig>> = {
|
||||
@@ -70,20 +69,20 @@ export class StructureLayer implements Layer {
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.game.units().forEach(u => this.handleUnitRendering(u))
|
||||
this.game.units().forEach(u => this.handleUnitRendering(u));
|
||||
}
|
||||
|
||||
init() {
|
||||
this.redraw()
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
redraw() {
|
||||
console.log('structure layer redrawing')
|
||||
console.log('structure layer redrawing');
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext("2d", { alpha: true });
|
||||
this.canvas.width = this.game.width();
|
||||
this.canvas.height = this.game.height();
|
||||
this.game.units().forEach(u => this.handleUnitRendering(u))
|
||||
this.game.units().forEach(u => this.handleUnitRendering(u));
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
@@ -106,11 +105,11 @@ export class StructureLayer implements Layer {
|
||||
|
||||
if (unit.isActive() && this.seenUnits.has(unit)) {
|
||||
// Already rendered, so don't do anything.
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (!unit.isActive() && !this.seenUnits.has(unit)) {
|
||||
// Has been deleted and render is cleared so don't do anything.
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
const config = this.unitConfigs[unitType];
|
||||
@@ -119,14 +118,15 @@ export class StructureLayer implements Layer {
|
||||
if (!config || !unitImage) return;
|
||||
|
||||
// Clear previous rendering
|
||||
bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius))
|
||||
.forEach(t => this.clearCell(t.cell()));
|
||||
for (const tile of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), config.borderRadius))) {
|
||||
this.clearCell(new Cell(this.game.x(tile), this.game.y(tile)));
|
||||
}
|
||||
|
||||
if (!unit.isActive()) {
|
||||
this.seenUnits.delete(unit)
|
||||
this.seenUnits.delete(unit);
|
||||
return;
|
||||
}
|
||||
this.seenUnits.add(unit)
|
||||
this.seenUnits.add(unit);
|
||||
|
||||
// Create temporary canvas for icon processing
|
||||
const tempCanvas = document.createElement('canvas');
|
||||
@@ -138,16 +138,25 @@ export class StructureLayer implements Layer {
|
||||
tempContext.drawImage(unitImage, 0, 0);
|
||||
const iconData = tempContext.getImageData(0, 0, tempCanvas.width, tempCanvas.height);
|
||||
|
||||
const cell = unit.tile().cell();
|
||||
const startX = cell.x - Math.floor(tempCanvas.width / 2);
|
||||
const startY = cell.y - Math.floor(tempCanvas.height / 2);
|
||||
const startX = this.game.x(unit.tile()) - Math.floor(tempCanvas.width / 2);
|
||||
const startY = this.game.y(unit.tile()) - Math.floor(tempCanvas.height / 2);
|
||||
|
||||
// Draw border and territory
|
||||
bfs(unit.tile(), euclDist(unit.tile(), config.borderRadius))
|
||||
.forEach(t => this.paintCell(t.cell(), this.theme.borderColor(unit.owner().info()), 255));
|
||||
for (const tile of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), config.borderRadius))) {
|
||||
this.paintCell(
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
bfs(unit.tile(), euclDist(unit.tile(), config.territoryRadius))
|
||||
.forEach(t => this.paintCell(t.cell(), this.theme.territoryColor(unit.owner().info()), 130));
|
||||
for (const tile of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), config.territoryRadius))) {
|
||||
this.paintCell(
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
130
|
||||
);
|
||||
}
|
||||
|
||||
// Draw the icon
|
||||
this.renderIcon(iconData, startX, startY, tempCanvas.width, tempCanvas.height, unit);
|
||||
@@ -184,7 +193,7 @@ export class StructureLayer implements Layer {
|
||||
}
|
||||
|
||||
paintCell(cell: Cell, color: Colord, alpha: number) {
|
||||
this.clearCell(cell)
|
||||
this.clearCell(cell);
|
||||
this.context.fillStyle = color.alpha(alpha / 255).toRgbString();
|
||||
this.context.fillRect(cell.x, cell.y, 1, 1);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
import { inherits } from "util"
|
||||
import { Game } from "../../../core/game/Game";
|
||||
import { throws } from "assert";
|
||||
import { Layer } from "./Layer";
|
||||
import { TransformHandler } from "../TransformHandler";
|
||||
import { GameView } from "../../../core/GameView";
|
||||
|
||||
export class TerrainLayer implements Layer {
|
||||
@@ -37,8 +33,9 @@ export class TerrainLayer implements Layer {
|
||||
initImageData() {
|
||||
const theme = this.game.config().theme()
|
||||
this.game.forEachTile((tile) => {
|
||||
let terrainColor = theme.terrainColor(tile)
|
||||
const index = (tile.cell().y * this.game.width()) + tile.cell().x
|
||||
let terrainColor = theme.terrainColor(this.game, tile)
|
||||
// TODO: isn'te tileref and index the same?
|
||||
const index = (this.game.y(tile) * this.game.width()) + this.game.x(tile)
|
||||
const offset = index * 4
|
||||
this.imageData.data[offset] = terrainColor.rgba.r;
|
||||
this.imageData.data[offset + 1] = terrainColor.rgba.g;
|
||||
|
||||
@@ -1,90 +1,91 @@
|
||||
import { PriorityQueue } from "@datastructures-js/priority-queue";
|
||||
import { Cell, Game, Player, PlayerType, Tile, Unit, UnitType, UnitUpdate } from "../../../core/game/Game";
|
||||
import { Cell, Game, Player, PlayerType, Unit, UnitType, UnitUpdate } from "../../../core/game/Game";
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom";
|
||||
import { colord, Colord } from "colord";
|
||||
import { bfs, dist, euclDist, euclideanDist } from "../../../core/Util";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { Layer } from "./Layer";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { AlternateViewEvent, DragEvent, MouseDownEvent } from "../../InputHandler";
|
||||
import { GameView, PlayerView } from "../../../core/GameView";
|
||||
import { euclDistFN, TileRef } from "../../../core/game/GameMap";
|
||||
|
||||
export class TerritoryLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement
|
||||
private context: CanvasRenderingContext2D
|
||||
private imageData: ImageData
|
||||
private canvas: HTMLCanvasElement;
|
||||
private context: CanvasRenderingContext2D;
|
||||
private imageData: ImageData;
|
||||
|
||||
private tileToRenderQueue: PriorityQueue<{ tile: Tile, lastUpdate: number }> = new PriorityQueue((a, b) => { return a.lastUpdate - b.lastUpdate })
|
||||
private random = new PseudoRandom(123)
|
||||
private theme: Theme = null
|
||||
private tileToRenderQueue: PriorityQueue<{ tile: TileRef, lastUpdate: number }> = new PriorityQueue((a, b) => { return a.lastUpdate - b.lastUpdate });
|
||||
private random = new PseudoRandom(123);
|
||||
private theme: Theme = null;
|
||||
|
||||
// Used for spawn highlighting
|
||||
private highlightCanvas: HTMLCanvasElement
|
||||
private highlightContext: CanvasRenderingContext2D
|
||||
private highlightCanvas: HTMLCanvasElement;
|
||||
private highlightContext: CanvasRenderingContext2D;
|
||||
|
||||
private alternativeView = false
|
||||
private lastDragTime = 0
|
||||
private nodrawDragDuration = 200
|
||||
|
||||
private refreshRate = 50
|
||||
private lastRefresh = 0
|
||||
private alternativeView = false;
|
||||
private lastDragTime = 0;
|
||||
private nodrawDragDuration = 200;
|
||||
|
||||
private refreshRate = 50;
|
||||
private lastRefresh = 0;
|
||||
|
||||
constructor(private game: GameView, private eventBus: EventBus) {
|
||||
this.theme = game.config().theme()
|
||||
this.theme = game.config().theme();
|
||||
}
|
||||
|
||||
shouldTransform(): boolean {
|
||||
return true
|
||||
return true;
|
||||
}
|
||||
|
||||
tick() {
|
||||
this.game.recentlyUpdatedTiles()
|
||||
.forEach(t => this.enqueueTile(t))
|
||||
|
||||
.forEach(t => this.enqueueTile(t));
|
||||
|
||||
if (!this.game.inSpawnPhase()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
if (this.game.ticks() % 5 == 0) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
this.highlightContext.clearRect(0, 0, this.game.width(), this.game.height());
|
||||
const humans = this.game.playerViews()
|
||||
.filter(p => p.type() == PlayerType.Human)
|
||||
.filter(p => p.type() == PlayerType.Human);
|
||||
|
||||
for (const human of humans) {
|
||||
const center = human.nameLocation()
|
||||
const center = human.nameLocation();
|
||||
if (!center) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
const centerTile = this.game.tile(new Cell(center.x, center.y))
|
||||
const centerTile = this.game.ref(center.x, center.y)
|
||||
if (!centerTile) {
|
||||
continue
|
||||
continue;
|
||||
}
|
||||
for (const tile of bfs(centerTile, euclDist(centerTile, 9))) {
|
||||
if (!tile.hasOwner()) {
|
||||
this.paintHighlightCell(tile.cell(), this.theme.spawnHighlightColor(), 255)
|
||||
for (const tile of this.game.bfs(centerTile, euclDistFN(centerTile, 9))) {
|
||||
if (!this.game.hasOwner(tile)) {
|
||||
this.paintHighlightCell(
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.spawnHighlightColor(),
|
||||
255
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
this.eventBus.on(AlternateViewEvent, e => { this.alternativeView = e.alternateView })
|
||||
this.eventBus.on(DragEvent, e => { this.lastDragTime = Date.now() })
|
||||
this.redraw()
|
||||
this.eventBus.on(AlternateViewEvent, e => { this.alternativeView = e.alternateView; });
|
||||
this.eventBus.on(DragEvent, e => { this.lastDragTime = Date.now(); });
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
|
||||
redraw() {
|
||||
console.log('redrew territory layer')
|
||||
console.log('redrew territory layer');
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext("2d")
|
||||
this.context = this.canvas.getContext("2d");
|
||||
|
||||
this.imageData = this.context.getImageData(0, 0, this.game.width(), this.game.height())
|
||||
this.initImageData()
|
||||
this.imageData = this.context.getImageData(0, 0, this.game.width(), this.game.height());
|
||||
this.initImageData();
|
||||
this.canvas.width = this.game.width();
|
||||
this.canvas.height = this.game.height();
|
||||
this.context.putImageData(this.imageData, 0, 0);
|
||||
@@ -96,26 +97,27 @@ export class TerritoryLayer implements Layer {
|
||||
this.highlightCanvas.height = this.game.height();
|
||||
|
||||
this.game.forEachTile(t => {
|
||||
this.paintTerritory(t)
|
||||
})
|
||||
this.paintTerritory(t);
|
||||
});
|
||||
}
|
||||
|
||||
initImageData() {
|
||||
this.game.forEachTile((tile) => {
|
||||
const index = (tile.cell().y * this.game.width()) + tile.cell().x
|
||||
const offset = index * 4
|
||||
this.imageData.data[offset + 3] = 0
|
||||
})
|
||||
const cell = new Cell(this.game.x(tile), this.game.y(tile));
|
||||
const index = (cell.y * this.game.width()) + cell.x;
|
||||
const offset = index * 4;
|
||||
this.imageData.data[offset + 3] = 0;
|
||||
});
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
if (Date.now() > this.lastDragTime + this.nodrawDragDuration && Date.now() > this.lastRefresh + this.refreshRate) {
|
||||
this.lastRefresh = Date.now()
|
||||
this.renderTerritory()
|
||||
this.lastRefresh = Date.now();
|
||||
this.renderTerritory();
|
||||
this.context.putImageData(this.imageData, 0, 0);
|
||||
}
|
||||
if (this.alternativeView) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
|
||||
context.drawImage(
|
||||
@@ -124,7 +126,7 @@ export class TerritoryLayer implements Layer {
|
||||
-this.game.height() / 2,
|
||||
this.game.width(),
|
||||
this.game.height()
|
||||
)
|
||||
);
|
||||
if (this.game.inSpawnPhase()) {
|
||||
context.drawImage(
|
||||
this.highlightCanvas,
|
||||
@@ -137,62 +139,60 @@ export class TerritoryLayer implements Layer {
|
||||
}
|
||||
|
||||
renderTerritory() {
|
||||
let numToRender = Math.floor(this.tileToRenderQueue.size() / 5)
|
||||
let numToRender = Math.floor(this.tileToRenderQueue.size() / 5);
|
||||
if (numToRender == 0 || this.game.inSpawnPhase()) {
|
||||
numToRender = this.tileToRenderQueue.size()
|
||||
numToRender = this.tileToRenderQueue.size();
|
||||
}
|
||||
|
||||
while (numToRender > 0) {
|
||||
numToRender--
|
||||
const tile = this.tileToRenderQueue.pop().tile
|
||||
this.paintTerritory(tile)
|
||||
tile.neighbors().forEach(t => this.paintTerritory(t, true))
|
||||
numToRender--;
|
||||
const tile = this.tileToRenderQueue.pop().tile;
|
||||
this.paintTerritory(tile);
|
||||
for (const neighbor of this.game.neighbors(tile)) {
|
||||
this.paintTerritory(neighbor, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paintTerritory(tile: Tile, isBorder: boolean = false) {
|
||||
if (isBorder && !tile.hasOwner()) {
|
||||
return
|
||||
paintTerritory(tile: TileRef, isBorder: boolean = false) {
|
||||
if (isBorder && !this.game.hasOwner(tile)) {
|
||||
return;
|
||||
}
|
||||
if (!tile.hasOwner()) {
|
||||
if (tile.hasFallout()) {
|
||||
this.paintCell(tile.cell(), this.theme.falloutColor(), 150)
|
||||
return
|
||||
if (!this.game.hasOwner(tile)) {
|
||||
if (this.game.hasFallout(tile)) {
|
||||
this.paintCell(
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.falloutColor(),
|
||||
150
|
||||
);
|
||||
return;
|
||||
}
|
||||
this.clearCell(tile.cell())
|
||||
return
|
||||
this.clearCell(new Cell(this.game.x(tile), this.game.y(tile)));
|
||||
return;
|
||||
}
|
||||
const owner = tile.owner() as Player
|
||||
if (tile.isBorder()) {
|
||||
if (tile.hasDefenseBonus()) {
|
||||
this.paintCell(
|
||||
tile.cell(),
|
||||
this.theme.defendedBorderColor(owner.info()),
|
||||
255
|
||||
)
|
||||
} else {
|
||||
this.paintCell(
|
||||
tile.cell(),
|
||||
this.theme.borderColor(owner.info()),
|
||||
255
|
||||
)
|
||||
}
|
||||
const owner = this.game.owner(tile) as Player;
|
||||
if (this.game.isBorder(tile)) {
|
||||
this.paintCell(
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.borderColor(owner.info()),
|
||||
255
|
||||
);
|
||||
} else {
|
||||
this.paintCell(
|
||||
tile.cell(),
|
||||
new Cell(this.game.x(tile), this.game.y(tile)),
|
||||
this.theme.territoryColor(owner.info()),
|
||||
150
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
paintCell(cell: Cell, color: Colord, alpha: number) {
|
||||
const index = (cell.y * this.game.width()) + cell.x
|
||||
const offset = index * 4
|
||||
const index = (cell.y * this.game.width()) + cell.x;
|
||||
const offset = index * 4;
|
||||
this.imageData.data[offset] = color.rgba.r;
|
||||
this.imageData.data[offset + 1] = color.rgba.g;
|
||||
this.imageData.data[offset + 2] = color.rgba.b;
|
||||
this.imageData.data[offset + 3] = alpha
|
||||
this.imageData.data[offset + 3] = alpha;
|
||||
}
|
||||
|
||||
clearCell(cell: Cell) {
|
||||
@@ -201,12 +201,15 @@ export class TerritoryLayer implements Layer {
|
||||
this.imageData.data[offset + 3] = 0; // Set alpha to 0 (fully transparent)
|
||||
}
|
||||
|
||||
enqueueTile(tile: Tile) {
|
||||
this.tileToRenderQueue.push({ tile: tile, lastUpdate: this.game.ticks() + this.random.nextFloat(0, .5) })
|
||||
enqueueTile(tile: TileRef) {
|
||||
this.tileToRenderQueue.push({
|
||||
tile: tile,
|
||||
lastUpdate: this.game.ticks() + this.random.nextFloat(0, .5)
|
||||
});
|
||||
}
|
||||
|
||||
paintHighlightCell(cell: Cell, color: Colord, alpha: number) {
|
||||
this.clearCell(cell)
|
||||
this.clearCell(cell);
|
||||
this.highlightContext.fillStyle = color.alpha(alpha / 255).toRgbString();
|
||||
this.highlightContext.fillRect(cell.x, cell.y, 1, 1);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { Colord } from "colord";
|
||||
import { Theme } from "../../../core/configuration/Config";
|
||||
import { Unit, Cell, Game, Tile, UnitType, Player, UnitUpdate } from "../../../core/game/Game";
|
||||
import { bfs, dist, euclDist } from "../../../core/Util";
|
||||
import { Unit, UnitType, Player, UnitUpdate } from "../../../core/game/Game";
|
||||
import { Layer } from "./Layer";
|
||||
import { EventBus } from "../../../core/EventBus";
|
||||
import { AlternateViewEvent } from "../../InputHandler";
|
||||
import { ClientID } from "../../../core/Schemas";
|
||||
import { GameView } from "../../../core/GameView";
|
||||
import { euclDistFN, manhattanDistFN, TileRef } from "../../../core/game/GameMap";
|
||||
|
||||
enum Relationship {
|
||||
Self,
|
||||
@@ -18,15 +18,15 @@ export class UnitLayer implements Layer {
|
||||
private canvas: HTMLCanvasElement;
|
||||
private context: CanvasRenderingContext2D;
|
||||
|
||||
private boatToTrail = new Map<Unit, Set<Tile>>();
|
||||
private boatToTrail = new Map<Unit, Set<TileRef>>();
|
||||
|
||||
private theme: Theme = null;
|
||||
|
||||
private alternateView = false
|
||||
private alternateView = false;
|
||||
|
||||
private myPlayer: Player | null = null
|
||||
private myPlayer: Player | null = null;
|
||||
|
||||
private oldShellTile = new Map<Unit, Tile>()
|
||||
private oldShellTile = new Map<Unit, TileRef>();
|
||||
|
||||
constructor(private game: GameView, private eventBus: EventBus, private clientID: ClientID) {
|
||||
this.theme = game.config().theme();
|
||||
@@ -38,17 +38,17 @@ export class UnitLayer implements Layer {
|
||||
|
||||
tick() {
|
||||
if (this.myPlayer == null) {
|
||||
this.myPlayer = this.game.playerByClientID(this.clientID)
|
||||
this.myPlayer = this.game.playerByClientID(this.clientID);
|
||||
}
|
||||
for (const unit of this.game.units()) {
|
||||
if (unit.wasUpdated())
|
||||
this.onUnitEvent(unit)
|
||||
this.onUnitEvent(unit);
|
||||
}
|
||||
}
|
||||
|
||||
init() {
|
||||
this.eventBus.on(AlternateViewEvent, e => this.onAlternativeViewEvent(e))
|
||||
this.redraw()
|
||||
this.eventBus.on(AlternateViewEvent, e => this.onAlternativeViewEvent(e));
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
renderLayer(context: CanvasRenderingContext2D) {
|
||||
@@ -62,11 +62,10 @@ export class UnitLayer implements Layer {
|
||||
}
|
||||
|
||||
onAlternativeViewEvent(event: AlternateViewEvent) {
|
||||
this.alternateView = event.alternateView
|
||||
this.redraw()
|
||||
this.alternateView = event.alternateView;
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
|
||||
redraw() {
|
||||
this.canvas = document.createElement('canvas');
|
||||
this.context = this.canvas.getContext("2d");
|
||||
@@ -80,15 +79,15 @@ export class UnitLayer implements Layer {
|
||||
|
||||
private relationship(unit: Unit): Relationship {
|
||||
if (this.myPlayer == null) {
|
||||
return Relationship.Enemy
|
||||
return Relationship.Enemy;
|
||||
}
|
||||
if (this.myPlayer == unit.owner()) {
|
||||
return Relationship.Self
|
||||
return Relationship.Self;
|
||||
}
|
||||
if (this.myPlayer.isAlliedWith(unit.owner())) {
|
||||
return Relationship.Ally
|
||||
return Relationship.Ally;
|
||||
}
|
||||
return Relationship.Enemy
|
||||
return Relationship.Enemy;
|
||||
}
|
||||
|
||||
onUnitEvent(unit: Unit) {
|
||||
@@ -103,137 +102,262 @@ export class UnitLayer implements Layer {
|
||||
this.handleBattleshipEvent(unit);
|
||||
break;
|
||||
case UnitType.Shell:
|
||||
this.handleShellEvent(unit)
|
||||
this.handleShellEvent(unit);
|
||||
break;
|
||||
case UnitType.TradeShip:
|
||||
this.handleTradeShipEvent(unit)
|
||||
this.handleTradeShipEvent(unit);
|
||||
break;
|
||||
case UnitType.AtomBomb:
|
||||
case UnitType.HydrogenBomb:
|
||||
this.handleNuke(unit)
|
||||
break
|
||||
this.handleNuke(unit);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private handleDestroyerEvent(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
bfs(unit.lastTile(), euclDist(unit.lastTile(), 4)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
if (!unit.isActive()) {
|
||||
return
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(unit.lastTile(), euclDistFN(unit.lastTile(), 4))) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
|
||||
if (!unit.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Paint border
|
||||
for (const t of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), 4))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
// Paint territory
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 3))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
bfs(unit.tile(), euclDist(unit.tile(), 4))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
|
||||
bfs(unit.tile(), dist(unit.tile(), 3))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
|
||||
}
|
||||
|
||||
private handleBattleshipEvent(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
bfs(unit.lastTile(), euclDist(unit.lastTile(), 6)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
if (!unit.isActive()) {
|
||||
return
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(unit.lastTile(), euclDistFN(unit.lastTile(), 6))) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
|
||||
if (!unit.isActive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Paint outer territory
|
||||
for (const t of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), 5))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
// Paint border
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 4))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
// Paint inner territory
|
||||
for (const t of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), 1))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
bfs(unit.tile(), euclDist(unit.tile(), 5))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
|
||||
bfs(unit.tile(), dist(unit.tile(), 4))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
|
||||
bfs(unit.tile(), euclDist(unit.tile(), 1))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
|
||||
}
|
||||
|
||||
private handleShellEvent(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
this.clearCell(unit.lastTile().cell())
|
||||
// Clear current and previous positions
|
||||
this.clearCell(this.game.x(unit.lastTile()), this.game.y(unit.lastTile()));
|
||||
if (this.oldShellTile.has(unit)) {
|
||||
this.clearCell(this.oldShellTile.get(unit).cell())
|
||||
const oldTile = this.oldShellTile.get(unit);
|
||||
this.clearCell(this.game.x(oldTile), this.game.y(oldTile));
|
||||
}
|
||||
|
||||
this.oldShellTile.set(unit, unit.lastTile())
|
||||
this.oldShellTile.set(unit, unit.lastTile());
|
||||
if (!unit.isActive()) {
|
||||
return
|
||||
return;
|
||||
}
|
||||
this.paintCell(unit.tile().cell(), rel, this.theme.borderColor(unit.owner().info()), 255)
|
||||
this.paintCell(unit.lastTile().cell(), rel, this.theme.borderColor(unit.owner().info()), 255)
|
||||
|
||||
// Paint current and previous positions
|
||||
this.paintCell(
|
||||
this.game.x(unit.tile()),
|
||||
this.game.y(unit.tile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
this.paintCell(
|
||||
this.game.x(unit.lastTile()),
|
||||
this.game.y(unit.lastTile()),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private handleNuke(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
bfs(unit.lastTile(), euclDist(unit.lastTile(), 2)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
if (unit.isActive()) {
|
||||
bfs(unit.tile(), euclDist(unit.tile(), 2))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(unit.lastTile(), euclDistFN(unit.lastTile(), 2))) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
|
||||
if (unit.isActive()) {
|
||||
// Paint area
|
||||
for (const t of this.game.bfs(unit.tile(), euclDistFN(unit.tile(), 2))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleTradeShipEvent(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
bfs(unit.lastTile(), euclDist(unit.lastTile(), 3)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
if (unit.isActive()) {
|
||||
bfs(unit.tile(), dist(unit.tile(), 2))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(unit.lastTile(), euclDistFN(unit.lastTile(), 3))) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
|
||||
if (unit.isActive()) {
|
||||
bfs(unit.tile(), dist(unit.tile(), 1))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
|
||||
// Paint territory
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 2))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
// Paint border
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 1))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private handleBoatEvent(unit: Unit) {
|
||||
const rel = this.relationship(unit)
|
||||
const rel = this.relationship(unit);
|
||||
|
||||
if (!this.boatToTrail.has(unit)) {
|
||||
this.boatToTrail.set(unit, new Set<Tile>());
|
||||
this.boatToTrail.set(unit, new Set<TileRef>());
|
||||
}
|
||||
const trail = this.boatToTrail.get(unit);
|
||||
trail.add(unit.lastTile());
|
||||
bfs(unit.lastTile(), dist(unit.lastTile(), 3)).forEach(t => {
|
||||
this.clearCell(t.cell());
|
||||
});
|
||||
|
||||
// Clear previous area
|
||||
for (const t of this.game.bfs(unit.lastTile(), manhattanDistFN(unit.lastTile(), 3))) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
|
||||
if (unit.isActive()) {
|
||||
// Paint trail
|
||||
for (const t of trail) {
|
||||
this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 150);
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
150
|
||||
);
|
||||
}
|
||||
|
||||
// Paint border
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 2))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.borderColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
|
||||
// Paint territory
|
||||
for (const t of this.game.bfs(unit.tile(), manhattanDistFN(unit.tile(), 1))) {
|
||||
this.paintCell(
|
||||
this.game.x(t),
|
||||
this.game.y(t),
|
||||
rel,
|
||||
this.theme.territoryColor(unit.owner().info()),
|
||||
255
|
||||
);
|
||||
}
|
||||
bfs(unit.tile(), dist(unit.tile(), 2))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.borderColor(unit.owner().info()), 255));
|
||||
bfs(unit.tile(), dist(unit.tile(), 1))
|
||||
.forEach(t => this.paintCell(t.cell(), rel, this.theme.territoryColor(unit.owner().info()), 255));
|
||||
} else {
|
||||
trail.forEach(t => this.clearCell(t.cell()));
|
||||
for (const t of trail) {
|
||||
this.clearCell(this.game.x(t), this.game.y(t));
|
||||
}
|
||||
this.boatToTrail.delete(unit);
|
||||
}
|
||||
}
|
||||
|
||||
paintCell(cell: Cell, relationship: Relationship, color: Colord, alpha: number) {
|
||||
this.clearCell(cell)
|
||||
paintCell(x: number, y: number, relationship: Relationship, color: Colord, alpha: number) {
|
||||
this.clearCell(x, y);
|
||||
if (this.alternateView) {
|
||||
switch (relationship) {
|
||||
case Relationship.Self:
|
||||
this.context.fillStyle = this.theme.selfColor().toRgbString()
|
||||
break
|
||||
this.context.fillStyle = this.theme.selfColor().toRgbString();
|
||||
break;
|
||||
case Relationship.Ally:
|
||||
this.context.fillStyle = this.theme.allyColor().toRgbString()
|
||||
break
|
||||
this.context.fillStyle = this.theme.allyColor().toRgbString();
|
||||
break;
|
||||
case Relationship.Enemy:
|
||||
this.context.fillStyle = this.theme.enemyColor().toRgbString()
|
||||
break
|
||||
this.context.fillStyle = this.theme.enemyColor().toRgbString();
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
this.context.fillStyle = color.alpha(alpha / 255).toRgbString();
|
||||
}
|
||||
this.context.fillRect(cell.x, cell.y, 1, 1);
|
||||
this.context.fillRect(x, y, 1, 1);
|
||||
}
|
||||
|
||||
clearCell(cell: Cell) {
|
||||
this.context.clearRect(cell.x, cell.y, 1, 1);
|
||||
clearCell(x: number, y: number) {
|
||||
this.context.clearRect(x, y, 1, 1);
|
||||
}
|
||||
}
|
||||
@@ -230,7 +230,7 @@ export class BuildMenu extends LitElement {
|
||||
}
|
||||
|
||||
showMenu(player: PlayerView, clickedCell: Cell) {
|
||||
player.actions(this.game.tile(clickedCell)).then(actions => {
|
||||
player.actions(this.game.ref(clickedCell.x, clickedCell.y)).then(actions => {
|
||||
console.log(`got actions: ${JSON.stringify(actions)}`)
|
||||
this.playerActions = actions
|
||||
this.myPlayer = player;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { EventBus } from "../../../../core/EventBus";
|
||||
import { AllPlayers, Cell, Game, Player, PlayerActions, Tile, UnitType } from "../../../../core/game/Game";
|
||||
import { AllPlayers, Cell, Game, Player, PlayerActions, } from "../../../../core/game/Game";
|
||||
import { ClientID } from "../../../../core/Schemas";
|
||||
import { and, bfs, dist, manhattanDist, manhattanDistWrapped, sourceDstOceanShore, targetTransportTile } from "../../../../core/Util";
|
||||
import { ContextMenuEvent, MouseUpEvent, ShowBuildMenuEvent } from "../../../InputHandler";
|
||||
import { SendAllianceRequestIntentEvent, SendAttackIntentEvent, SendBoatAttackIntentEvent, SendBreakAllianceIntentEvent, SendDonateIntentEvent, SendEmojiIntentEvent, SendSpawnIntentEvent, SendTargetPlayerIntentEvent } from "../../../Transport";
|
||||
import { TransformHandler } from "../../TransformHandler";
|
||||
@@ -21,6 +20,7 @@ import { UIState } from "../../UIState";
|
||||
import { BuildMenu } from "./BuildMenu";
|
||||
import { consolex } from "../../../../core/Consolex";
|
||||
import { GameView, PlayerView } from "../../../../core/GameView";
|
||||
import { TileRef } from "../../../../core/game/GameMap";
|
||||
|
||||
|
||||
enum Slot {
|
||||
@@ -54,7 +54,7 @@ export class RadialMenu implements Layer {
|
||||
|
||||
constructor(
|
||||
private eventBus: EventBus,
|
||||
private game: GameView,
|
||||
private g: GameView,
|
||||
private transformHandler: TransformHandler,
|
||||
private clientID: ClientID,
|
||||
private emojiTable: EmojiTable,
|
||||
@@ -70,10 +70,10 @@ export class RadialMenu implements Layer {
|
||||
if (clickedCell == null) {
|
||||
return
|
||||
}
|
||||
if (!this.game.isOnMap(clickedCell)) {
|
||||
if (!this.g.isValidCoord(clickedCell.x, clickedCell.y)) {
|
||||
return
|
||||
}
|
||||
const p = this.game.playerByClientID(this.clientID)
|
||||
const p = this.g.playerByClientID(this.clientID)
|
||||
if (p == null) {
|
||||
return
|
||||
}
|
||||
@@ -233,19 +233,19 @@ export class RadialMenu implements Layer {
|
||||
}
|
||||
|
||||
this.clickedCell = this.transformHandler.screenToWorldCoordinates(event.x, event.y)
|
||||
if (!this.game.isOnMap(this.clickedCell)) {
|
||||
if (!this.g.isValidCoord(this.clickedCell.x, this.clickedCell.y)) {
|
||||
return
|
||||
}
|
||||
const tile = this.game.tile(this.clickedCell)
|
||||
const tile = this.g.ref(this.clickedCell.x, this.clickedCell.y)
|
||||
|
||||
if (this.game.inSpawnPhase()) {
|
||||
if (tile.terrain().isLand() && !tile.hasOwner()) {
|
||||
if (this.g.inSpawnPhase()) {
|
||||
if (this.g.isLand(tile) && !this.g.hasOwner(tile)) {
|
||||
this.enableCenterButton(true)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
const myPlayer = this.game.playerViews().find(p => p.clientID() == this.clientID)
|
||||
const myPlayer = this.g.playerViews().find(p => p.clientID() == this.clientID)
|
||||
if (!myPlayer) {
|
||||
consolex.warn('my player not found')
|
||||
return
|
||||
@@ -255,13 +255,13 @@ export class RadialMenu implements Layer {
|
||||
})
|
||||
}
|
||||
|
||||
private handlePlayerActions(myPlayer: PlayerView, actions: PlayerActions, tile: Tile) {
|
||||
private handlePlayerActions(myPlayer: PlayerView, actions: PlayerActions, tile: TileRef) {
|
||||
this.activateMenuElement(Slot.Build, "#ebe250", buildIcon, () => {
|
||||
this.buildMenu.showMenu(myPlayer, this.clickedCell)
|
||||
})
|
||||
if (actions.interaction?.canSendEmoji) {
|
||||
this.activateMenuElement(Slot.Emoji, "#00a6a4", emojiIcon, () => {
|
||||
const target = tile.owner() == myPlayer ? AllPlayers : (tile.owner() as Player)
|
||||
const target = this.g.owner(tile) == myPlayer ? AllPlayers : (this.g.owner(tile) as Player)
|
||||
this.emojiTable.onEmojiClicked = (emoji: string) => {
|
||||
this.emojiTable.hideTable()
|
||||
this.eventBus.emit(new SendEmojiIntentEvent(target, emoji))
|
||||
@@ -274,7 +274,7 @@ export class RadialMenu implements Layer {
|
||||
this.activateMenuElement(Slot.Boat, "#3f6ab1", boatIcon, () => {
|
||||
this.eventBus.emit(
|
||||
new SendBoatAttackIntentEvent(
|
||||
tile.owner().id(),
|
||||
this.g.owner(tile).id(),
|
||||
this.clickedCell,
|
||||
this.uiState.attackRatio * myPlayer.troops()
|
||||
)
|
||||
@@ -285,10 +285,10 @@ export class RadialMenu implements Layer {
|
||||
this.enableCenterButton(true)
|
||||
}
|
||||
|
||||
if (!tile.hasOwner()) {
|
||||
if (!this.g.hasOwner(tile)) {
|
||||
return
|
||||
}
|
||||
const other = tile.owner() as Player
|
||||
const other = this.g.owner(tile) as Player
|
||||
|
||||
|
||||
if (actions?.interaction.canDonate) {
|
||||
@@ -351,13 +351,13 @@ export class RadialMenu implements Layer {
|
||||
return
|
||||
}
|
||||
consolex.log('Center button clicked');
|
||||
const clicked = this.game.tile(this.clickedCell)
|
||||
if (this.game.inSpawnPhase()) {
|
||||
const clicked = this.g.ref(this.clickedCell.x, this.clickedCell.y)
|
||||
if (this.g.inSpawnPhase()) {
|
||||
this.eventBus.emit(new SendSpawnIntentEvent(this.clickedCell))
|
||||
} else {
|
||||
const myPlayer = this.game.players().find(p => p.clientID() == this.clientID)
|
||||
if (myPlayer != null && clicked.owner() != myPlayer) {
|
||||
this.eventBus.emit(new SendAttackIntentEvent(clicked.owner().id(), this.uiState.attackRatio * myPlayer.troops()))
|
||||
const myPlayer = this.g.players().find(p => p.clientID() == this.clientID)
|
||||
if (myPlayer != null && this.g.owner(clicked) != myPlayer) {
|
||||
this.eventBus.emit(new SendAttackIntentEvent(this.g.owner(clicked).id(), this.uiState.attackRatio * myPlayer.troops()))
|
||||
}
|
||||
}
|
||||
this.hideRadialMenu();
|
||||
|
||||
Reference in New Issue
Block a user