mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-21 14:00:54 +00:00
use html for rendering text
This commit is contained in:
@@ -80,7 +80,7 @@ export function createRenderer(canvas: HTMLCanvasElement, game: GameView, eventB
|
||||
new TerritoryLayer(game, eventBus),
|
||||
new StructureLayer(game, eventBus),
|
||||
new UnitLayer(game, eventBus, clientID),
|
||||
new NameLayer(game, eventBus, game.config().theme(), transformHandler, clientID),
|
||||
new NameLayer(game, game.config().theme(), transformHandler, clientID),
|
||||
new UILayer(eventBus, game, clientID, transformHandler),
|
||||
eventsDisplay,
|
||||
new RadialMenu(eventBus, game, transformHandler, clientID, emojiTable as EmojiTable, buildMenu, uiState),
|
||||
|
||||
@@ -14,6 +14,7 @@ export class TransformHandler {
|
||||
|
||||
private target: Cell
|
||||
private intervalID = null
|
||||
private changed = false
|
||||
|
||||
constructor(private game: GameView, private eventBus: EventBus, private canvas: HTMLCanvasElement) {
|
||||
this.eventBus.on(ZoomEvent, (e) => this.onZoom(e))
|
||||
@@ -28,6 +29,9 @@ export class TransformHandler {
|
||||
width(): number {
|
||||
return this.boundingRect().width
|
||||
}
|
||||
hasChanged(): boolean {
|
||||
return this.changed
|
||||
}
|
||||
|
||||
handleTransform(context: CanvasRenderingContext2D) {
|
||||
// Disable image smoothing for pixelated effect
|
||||
@@ -43,6 +47,32 @@ export class TransformHandler {
|
||||
this.game.width() / 2 - this.offsetX * this.scale,
|
||||
this.game.height() / 2 - this.offsetY * this.scale
|
||||
);
|
||||
this.changed = false
|
||||
}
|
||||
|
||||
worldToScreenCoordinates(cell: Cell): { x: number, y: number } {
|
||||
// Step 1: Convert from Cell coordinates to game coordinates
|
||||
// (reverse of Math.floor operation - we'll use the exact values)
|
||||
const gameX = cell.x;
|
||||
const gameY = cell.y;
|
||||
|
||||
// Step 2: Reverse the game center offset calculation
|
||||
// Original: gameX = centerX + this.game.width() / 2
|
||||
// Therefore: centerX = gameX - this.game.width() / 2
|
||||
const centerX = gameX - this.game.width() / 2;
|
||||
const centerY = gameY - this.game.height() / 2;
|
||||
|
||||
// Step 3: Reverse the world point calculation
|
||||
// Original: centerX = (canvasX - this.game.width() / 2) / this.scale + this.offsetX
|
||||
// Therefore: canvasX = (centerX - this.offsetX) * this.scale + this.game.width() / 2
|
||||
const canvasX = (centerX - this.offsetX) * this.scale + this.game.width() / 2;
|
||||
const canvasY = (centerY - this.offsetY) * this.scale + this.game.height() / 2;
|
||||
|
||||
// Step 4: Convert canvas coordinates back to screen coordinates
|
||||
const canvasRect = this.boundingRect();
|
||||
const screenX = canvasX + canvasRect.left;
|
||||
const screenY = canvasY + canvasRect.top;
|
||||
return { x: screenX, y: screenY }
|
||||
}
|
||||
|
||||
screenToWorldCoordinates(screenX: number, screenY: number): Cell {
|
||||
@@ -78,6 +108,11 @@ export class TransformHandler {
|
||||
return [new Cell(Math.floor(gameLeftX), Math.floor(gameTopY)), new Cell(Math.floor(gameRightX), Math.floor(gameBottomY))]
|
||||
}
|
||||
|
||||
isOnScreen(cell: Cell): boolean {
|
||||
const [topLeft, bottomRight] = this.screenBoundingRect()
|
||||
return cell.x > topLeft.x && cell.x < bottomRight.x && cell.y > topLeft.y && cell.y < bottomRight.y
|
||||
}
|
||||
|
||||
screenCenter(): { screenX: number, screenY: number } {
|
||||
const [upperLeft, bottomRight] = this.screenBoundingRect()
|
||||
return {
|
||||
@@ -121,6 +156,7 @@ export class TransformHandler {
|
||||
this.offsetY += offsetDy
|
||||
}
|
||||
}
|
||||
this.changed = true
|
||||
}
|
||||
|
||||
onZoom(event: ZoomEvent) {
|
||||
@@ -143,12 +179,14 @@ export class TransformHandler {
|
||||
// Adjust the offset
|
||||
this.offsetX = zoomPointX - (canvasX - this.game.width() / 2) / this.scale;
|
||||
this.offsetY = zoomPointY - (canvasY - this.game.height() / 2) / this.scale;
|
||||
this.changed = true
|
||||
}
|
||||
|
||||
onMove(event: DragEvent) {
|
||||
this.clearTarget()
|
||||
this.offsetX -= event.deltaX / this.scale;
|
||||
this.offsetY -= event.deltaY / this.scale;
|
||||
this.changed = true
|
||||
}
|
||||
|
||||
private clearTarget() {
|
||||
|
||||
@@ -1,37 +1,39 @@
|
||||
import { AllPlayers, Cell, Game, Player, PlayerType, Tick } from "../../../core/game/Game"
|
||||
import { AllPlayers, Cell, Game, Player, PlayerType } from "../../../core/game/Game"
|
||||
import { PseudoRandom } from "../../../core/PseudoRandom"
|
||||
import { calculateBoundingBox } from "../../../core/Util"
|
||||
import { Theme } from "../../../core/configuration/Config"
|
||||
import { Layer } from "./Layer"
|
||||
import { placeName } from "../NameBoxCalculator"
|
||||
import { TransformHandler } from "../TransformHandler"
|
||||
import { renderTroops } from "../../Utils"
|
||||
import traitorIcon from '../../../../resources/images/TraitorIcon.png';
|
||||
import allianceIcon from '../../../../resources/images/AllianceIcon.png';
|
||||
import crownIcon from '../../../../resources/images/CrownIcon.png';
|
||||
import targetIcon from '../../../../resources/images/TargetIcon.png';
|
||||
import { ClientID } from "../../../core/Schemas"
|
||||
import { EventBus } from "../../../core/EventBus"
|
||||
import { AlternateViewEvent } from "../../InputHandler"
|
||||
import { GameView, PlayerView } from "../../../core/GameView"
|
||||
import { createCanvas, renderTroops } from "../../Utils"
|
||||
|
||||
|
||||
class RenderInfo {
|
||||
public isVisible = true
|
||||
public icons: Map<string, HTMLImageElement> = new Map() // Track icon elements
|
||||
|
||||
constructor(
|
||||
public player: Player,
|
||||
public lastRenderCalcTick: Tick,
|
||||
public lastBoundingCalculatedTick: Tick,
|
||||
public boundingBox: { min: Cell, max: Cell },
|
||||
public player: PlayerView,
|
||||
public lastRenderCalc: number,
|
||||
public location: Cell,
|
||||
public fontSize: number
|
||||
public fontSize: number,
|
||||
public element: HTMLElement
|
||||
) { }
|
||||
}
|
||||
|
||||
export class NameLayer implements Layer {
|
||||
|
||||
private canvas: HTMLCanvasElement
|
||||
private context: CanvasRenderingContext2D
|
||||
|
||||
private lastChecked = 0
|
||||
private refreshRate = 1000
|
||||
|
||||
private renderCheckRate = 100
|
||||
private renderRefreshRate = 500
|
||||
|
||||
private rand = new PseudoRandom(10)
|
||||
private renders: RenderInfo[] = []
|
||||
@@ -41,21 +43,19 @@ export class NameLayer implements Layer {
|
||||
private targetIconImage: HTMLImageElement;
|
||||
private crownIconImage: HTMLImageElement;
|
||||
|
||||
private container: HTMLDivElement
|
||||
|
||||
|
||||
private myPlayer: Player | null = null
|
||||
|
||||
private firstPlace: Player | null = null
|
||||
|
||||
private alternateView = false
|
||||
private lastUpdate = 0
|
||||
private updateFrequency = 250
|
||||
|
||||
constructor(
|
||||
private game: GameView,
|
||||
private eventBus: EventBus,
|
||||
private theme: Theme,
|
||||
private transformHandler: TransformHandler,
|
||||
private clientID: ClientID
|
||||
) {
|
||||
this.eventBus.on(AlternateViewEvent, e => { this.alternateView = e.alternateView })
|
||||
private lastRect = null;
|
||||
|
||||
constructor(private game: GameView, private theme: Theme, private transformHandler: TransformHandler, private clientID: ClientID) {
|
||||
this.traitorIconImage = new Image();
|
||||
this.traitorIconImage.src = traitorIcon;
|
||||
|
||||
@@ -69,159 +69,219 @@ export class NameLayer implements Layer {
|
||||
this.targetIconImage.src = targetIcon
|
||||
}
|
||||
|
||||
resizeCanvas() {
|
||||
this.canvas.width = window.innerWidth;
|
||||
this.canvas.height = window.innerHeight;
|
||||
//this.redraw()
|
||||
}
|
||||
|
||||
|
||||
shouldTransform(): boolean {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
public init() {
|
||||
// this.canvas = document.createElement('canvas');
|
||||
this.canvas = createCanvas()
|
||||
this.context = this.canvas.getContext("2d")
|
||||
|
||||
|
||||
|
||||
window.addEventListener('resize', () => this.resizeCanvas());
|
||||
this.resizeCanvas();
|
||||
|
||||
this.container = document.createElement('div')
|
||||
this.container.style.position = 'fixed'
|
||||
this.container.style.left = '50%'
|
||||
this.container.style.top = '50%'
|
||||
this.container.style.pointerEvents = 'none' // Don't interfere with game interaction
|
||||
this.container.style.zIndex = '1000' // Add this line
|
||||
document.body.appendChild(this.container)
|
||||
}
|
||||
|
||||
// TODO: remove tick, move this to render
|
||||
public tick() {
|
||||
const now = Date.now()
|
||||
if (now - this.lastChecked > this.refreshRate) {
|
||||
this.lastChecked = now
|
||||
|
||||
const sorted = this.game.players().sort((a, b) => b.numTilesOwned() - a.numTilesOwned())
|
||||
if (sorted.length > 0) {
|
||||
this.firstPlace = sorted[0]
|
||||
}
|
||||
|
||||
this.renders = this.renders.filter(r => r.player.isAlive())
|
||||
for (const player of this.game.players()) {
|
||||
if (player.isAlive()) {
|
||||
if (!this.seenPlayers.has(player)) {
|
||||
this.seenPlayers.add(player)
|
||||
this.renders.push(new RenderInfo(player, 0, 0, null, null, 0))
|
||||
}
|
||||
} else {
|
||||
this.seenPlayers.delete(player)
|
||||
}
|
||||
}
|
||||
if (this.game.ticks() % 10 != 0) {
|
||||
return
|
||||
}
|
||||
const currTick = this.game.ticks()
|
||||
const recalcRate = this.game.inSpawnPhase() ? 2 : 10
|
||||
for (const render of this.renders) {
|
||||
// const territoryUpdated = render.boundingBox == null || render.player.lastTileChange() > render.lastBoundingCalculatedTick
|
||||
// if (!territoryUpdated) {
|
||||
// continue
|
||||
// }
|
||||
if (currTick - render.lastBoundingCalculatedTick > recalcRate) {
|
||||
render.lastBoundingCalculatedTick = currTick
|
||||
render.boundingBox = calculateBoundingBox(render.player.borderTiles());
|
||||
}
|
||||
if (render.isVisible && currTick - render.lastRenderCalcTick > recalcRate) {
|
||||
render.lastRenderCalcTick = currTick
|
||||
this.calculateRenderInfo(render)
|
||||
const sorted = this.game.players().sort((a, b) => b.numTilesOwned() - a.numTilesOwned())
|
||||
if (sorted.length > 0) {
|
||||
this.firstPlace = sorted[0]
|
||||
}
|
||||
|
||||
for (const player of this.game.playerViews()) {
|
||||
if (player.isAlive()) {
|
||||
if (!this.seenPlayers.has(player)) {
|
||||
this.seenPlayers.add(player)
|
||||
this.renders.push(new RenderInfo(player, 0, null, 0, this.createPlayerElement(player)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public renderLayer(mainContex: CanvasRenderingContext2D) {
|
||||
const [upperLeft, bottomRight] = this.transformHandler.screenBoundingRect()
|
||||
for (const player of this.game.playerViews()) {
|
||||
if (player.isAlive()) {
|
||||
const screenPosOld = this.transformHandler.worldToScreenCoordinates(new Cell(0, 0))
|
||||
const screenPos = new Cell(screenPosOld.x - window.innerWidth / 2, screenPosOld.y - window.innerHeight / 2)
|
||||
|
||||
this.renderPlayerInfo(player, mainContex, this.transformHandler.scale, upperLeft, bottomRight)
|
||||
// render.element.style.fontSize = `${render.fontSize}px`
|
||||
this.container.style.transform = `translate(${screenPos.x}px, ${screenPos.y}px) scale(${this.transformHandler.scale})`
|
||||
|
||||
const now = Date.now()
|
||||
if (now + this.lastChecked > this.renderRefreshRate) {
|
||||
this.lastChecked = now
|
||||
for (const render of this.renders) {
|
||||
this.renderPlayerInfo(render)
|
||||
}
|
||||
}
|
||||
|
||||
mainContex.drawImage(
|
||||
this.canvas,
|
||||
0,
|
||||
0,
|
||||
mainContex.canvas.width,
|
||||
mainContex.canvas.height
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
calculateRenderInfo(render: RenderInfo) {
|
||||
if (render.player.numTilesOwned() == 0) {
|
||||
render.fontSize = 0
|
||||
return
|
||||
}
|
||||
// const [cell, size] = placeName(this.game, render.player)
|
||||
// render.location = cell
|
||||
// render.fontSize = Math.max(1, Math.floor(size))
|
||||
private createPlayerElement(player: Player): HTMLDivElement {
|
||||
const element = document.createElement('div')
|
||||
element.style.position = 'absolute'
|
||||
element.style.display = 'flex'
|
||||
element.style.flexDirection = 'column'
|
||||
element.style.alignItems = 'center'
|
||||
// Don't set initial transform, will be handled in renderPlayerInfo
|
||||
|
||||
const nameDiv = document.createElement('div')
|
||||
nameDiv.innerHTML = player.displayName()
|
||||
nameDiv.style.color = this.theme.playerInfoColor(player.id()).toHex()
|
||||
nameDiv.style.fontFamily = this.theme.font()
|
||||
element.appendChild(nameDiv)
|
||||
|
||||
const troopsDiv = document.createElement('div')
|
||||
troopsDiv.textContent = renderTroops(player.troops())
|
||||
troopsDiv.style.color = this.theme.playerInfoColor(player.id()).toHex()
|
||||
troopsDiv.style.fontFamily = this.theme.font()
|
||||
troopsDiv.style.fontWeight = 'bold'
|
||||
element.appendChild(troopsDiv)
|
||||
|
||||
const iconsDiv = document.createElement('div')
|
||||
iconsDiv.style.position = 'absolute'
|
||||
iconsDiv.style.display = 'flex'
|
||||
element.appendChild(iconsDiv)
|
||||
|
||||
this.container.appendChild(element)
|
||||
return element
|
||||
}
|
||||
|
||||
renderPlayerInfo(player: PlayerView, context: CanvasRenderingContext2D, scale: number, uppperLeft: Cell, bottomRight: Cell) {
|
||||
if (this.alternateView) {
|
||||
renderPlayerInfo(render: RenderInfo) {
|
||||
if (!render.player.nameLocation() || !render.player.isAlive()) {
|
||||
console.log(`remove ${render.player.name()}`)
|
||||
this.renders = this.renders.filter(r => r != render)
|
||||
render.element.remove()
|
||||
return
|
||||
}
|
||||
const name = player.nameLocation()
|
||||
if (!name) {
|
||||
const oldLocation = render.location
|
||||
render.location = new Cell(render.player.nameLocation().x, render.player.nameLocation().y)
|
||||
render.fontSize = Math.max(1, Math.floor(render.player.nameLocation().size))
|
||||
// console.log(`zoom ${this.transformHandler.scale}, size: ${render.player.nameLocation().size}`)
|
||||
const size = this.transformHandler.scale * render.player.nameLocation().size
|
||||
if (size < 5) {
|
||||
if (render.element.style.display != 'none') {
|
||||
render.element.style.display = 'none'
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!this.transformHandler.isOnScreen(render.location)) {
|
||||
if (render.element.style.display != 'none') {
|
||||
render.element.style.display = 'none'
|
||||
}
|
||||
return
|
||||
}
|
||||
if (render.element.style.display != 'flex') {
|
||||
render.element.style.display = 'flex'
|
||||
}
|
||||
const now = Date.now()
|
||||
if (now - render.lastRenderCalc > this.renderRefreshRate) {
|
||||
render.lastRenderCalc = now + this.rand.nextInt(0, 100)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
|
||||
// Update troops count
|
||||
const troopsDiv = render.element.children[1] as HTMLDivElement
|
||||
troopsDiv.textContent = renderTroops(render.player.troops())
|
||||
|
||||
const nameCenterX = Math.floor(name.x - this.game.width() / 2)
|
||||
const nameCenterY = Math.floor(name.y - this.game.height() / 2)
|
||||
|
||||
const iconSize = name.size * 2; // Adjust size as needed
|
||||
// const iconX = nameCenterX + render.fontSize * 2; // Position to the right of the name
|
||||
// const iconY = nameCenterY - render.fontSize / 2;
|
||||
|
||||
if (player == this.firstPlace) {
|
||||
context.drawImage(
|
||||
this.crownIconImage,
|
||||
nameCenterX - iconSize / 2,
|
||||
nameCenterY - iconSize / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
if (player.isTraitor() && this.traitorIconImage.complete) {
|
||||
context.drawImage(
|
||||
this.traitorIconImage,
|
||||
nameCenterX - iconSize / 2,
|
||||
nameCenterY - iconSize / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
|
||||
// Get icons container
|
||||
const iconsDiv = render.element.children[2] as HTMLDivElement
|
||||
const iconSize = Math.floor(render.fontSize * 2)
|
||||
const myPlayer = this.getPlayer()
|
||||
if (myPlayer != null && myPlayer.isAlliedWith(player)) {
|
||||
context.drawImage(
|
||||
this.allianceIconImage,
|
||||
nameCenterX - iconSize / 2,
|
||||
nameCenterY - iconSize / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
|
||||
if (myPlayer != null && new Set(myPlayer.transitiveTargets()).has(player)) {
|
||||
context.drawImage(
|
||||
this.targetIconImage,
|
||||
nameCenterX - iconSize / 2,
|
||||
nameCenterY - iconSize / 2,
|
||||
iconSize,
|
||||
iconSize
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
context.textRendering = "optimizeSpeed";
|
||||
|
||||
context.font = `${name.size}px ${this.theme.font()}`;
|
||||
context.fillStyle = this.theme.playerInfoColor(player.id()).toHex();
|
||||
context.textAlign = 'center';
|
||||
context.textBaseline = 'middle';
|
||||
|
||||
context.fillText(player.name(), nameCenterX, nameCenterY - name.size / 2);
|
||||
context.font = `bold ${name.size}px ${this.theme.font()}`;
|
||||
|
||||
context.fillText(renderTroops(player.troops()), nameCenterX, nameCenterY + name.size);
|
||||
|
||||
|
||||
if (myPlayer != null) {
|
||||
const emojis = player.outgoingEmojis().filter(e => e.recipient == AllPlayers || e.recipient == myPlayer)
|
||||
if (emojis.length > 0) {
|
||||
context.font = `${name.size * 4}px ${this.theme.font()}`;
|
||||
context.fillStyle = this.theme.playerInfoColor(player.id()).toHex();
|
||||
context.textAlign = 'center';
|
||||
context.textBaseline = 'middle';
|
||||
|
||||
context.fillText(emojis[0].emoji, nameCenterX, nameCenterY + name.size / 2);
|
||||
// Handle crown icon
|
||||
const existingCrown = iconsDiv.querySelector('[data-icon="crown"]')
|
||||
if (render.player === this.firstPlace) {
|
||||
if (!existingCrown) {
|
||||
iconsDiv.appendChild(this.createIconElement(this.crownIconImage.src, iconSize, 'crown'))
|
||||
}
|
||||
} else if (existingCrown) {
|
||||
existingCrown.remove()
|
||||
}
|
||||
|
||||
// Handle traitor icon
|
||||
const existingTraitor = iconsDiv.querySelector('[data-icon="traitor"]')
|
||||
if (render.player.isTraitor()) {
|
||||
if (!existingTraitor) {
|
||||
iconsDiv.appendChild(this.createIconElement(this.traitorIconImage.src, iconSize, 'traitor'))
|
||||
}
|
||||
} else if (existingTraitor) {
|
||||
existingTraitor.remove()
|
||||
}
|
||||
|
||||
// Handle alliance icon
|
||||
const existingAlliance = iconsDiv.querySelector('[data-icon="alliance"]')
|
||||
if (myPlayer != null && myPlayer.isAlliedWith(render.player)) {
|
||||
if (!existingAlliance) {
|
||||
iconsDiv.appendChild(this.createIconElement(this.allianceIconImage.src, iconSize, 'alliance'))
|
||||
}
|
||||
} else if (existingAlliance) {
|
||||
existingAlliance.remove()
|
||||
}
|
||||
|
||||
// Handle target icon
|
||||
const existingTarget = iconsDiv.querySelector('[data-icon="target"]')
|
||||
if (myPlayer != null && new Set(myPlayer.transitiveTargets()).has(render.player)) {
|
||||
if (!existingTarget) {
|
||||
iconsDiv.appendChild(this.createIconElement(this.targetIconImage.src, iconSize, 'target'))
|
||||
}
|
||||
} else if (existingTarget) {
|
||||
existingTarget.remove()
|
||||
}
|
||||
|
||||
// Update icon sizes based on scale
|
||||
const icons = iconsDiv.getElementsByTagName('img')
|
||||
for (const icon of icons) {
|
||||
icon.style.width = `${iconSize}px`
|
||||
icon.style.height = `${iconSize}px`
|
||||
icon.style.transform = `translateY(${iconSize / 4}px)`
|
||||
}
|
||||
|
||||
if (!render.location) {
|
||||
return
|
||||
}
|
||||
|
||||
if (render.location != oldLocation) {
|
||||
// Handle all positioning in a single transform
|
||||
render.element.style.transform = `translate(${render.location.x}px, ${render.location.y}px) translate(-50%, -50%) scale(${render.fontSize * 0.1})`
|
||||
}
|
||||
}
|
||||
|
||||
private createIconElement(src: string, size: number, id: string): HTMLImageElement {
|
||||
const icon = document.createElement('img')
|
||||
icon.src = src
|
||||
icon.style.width = `${size}px`
|
||||
icon.style.height = `${size}px`
|
||||
icon.setAttribute('data-icon', id)
|
||||
icon.style.transform = `translateY(${size / 4}px)`
|
||||
return icon
|
||||
}
|
||||
|
||||
private getPlayer(): Player | null {
|
||||
@@ -231,4 +291,4 @@ export class NameLayer implements Layer {
|
||||
this.myPlayer = this.game.players().find(p => p.clientID() == this.clientID)
|
||||
return this.myPlayer
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ export class GameRunner {
|
||||
this.currTurn++
|
||||
const updates = this.game.executeNextTick()
|
||||
|
||||
if (this.game.inSpawnPhase() || this.game.ticks() % 10 == 0) {
|
||||
if (this.game.inSpawnPhase() || this.game.ticks() % 20 == 0) {
|
||||
this.game.players()
|
||||
.forEach(p => this.playerToName.set(p.id(), placeName(this.game, p)))
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ export class DevConfig extends DefaultConfig {
|
||||
// return 100
|
||||
}
|
||||
|
||||
unitInfo(type: UnitType): UnitInfo {
|
||||
const info = super.unitInfo(type)
|
||||
const oldCost = info.cost
|
||||
info.cost = (p: Player) => oldCost(p) / 10000
|
||||
return info
|
||||
}
|
||||
// unitInfo(type: UnitType): UnitInfo {
|
||||
// const info = super.unitInfo(type)
|
||||
// const oldCost = info.cost
|
||||
// info.cost = (p: Player) => oldCost(p) / 10000
|
||||
// return info
|
||||
// }
|
||||
|
||||
// tradeShipSpawnRate(): number {
|
||||
// return 10
|
||||
|
||||
Reference in New Issue
Block a user