import { ECellType } from "../Simulation/index.js"; import { Hex, Vector2 } from "../Util/index.js"; import { VisEnemy, VisProjectile } from "./index.js"; export class VisLevel { _screenCellWidth = -1; _screenCellHeight = -1; _screenXOffset = -1; _screenYOffset = -1; _hexSize = -1; _lastStep = -1; _projectileMap; _enemyMap; _background = null; _simLevel = null; _visMain; _simMain; _gdRoot; constructor(visMain, simMain, gdRoot) { this._visMain = visMain; this._simMain = visMain.simMain; this._gdRoot = gdRoot; this._enemyMap = new Map(); this._projectileMap = new Map(); this.reset(); } reset() { this._projectileMap.clear(); this._enemyMap.clear(); this._background = null; } draw() { const ctx = this._visMain.context; const simLevel = this._simMain.currentLevel; if (simLevel != this._simLevel) { this.reset(); this._simLevel = simLevel; } this.drawBackground(); ctx.globalCompositeOperation = "source-over"; simLevel.simCells.forEach((cell) => { if (cell.distance > simLevel.gdLevel.radius) { return; } this.drawCell(cell); }); simLevel.simEnemies.forEach((enemy) => { this.drawEnemy(enemy); }); simLevel.simProjectiles.forEach((projectile) => { this.drawProjectile(projectile); }); ctx.fillStyle = "white"; ctx.fillText("Currency: " + simLevel.currency, 5, 15); ctx.fillText("Current wave: " + simLevel.currentWave, 5, 35); ctx.fillText("Enemies left: " + simLevel.enemiesLeftToSpawn, 5, 55); ctx.fillText("Current step: " + simLevel.currentStep, 5, 75); } updateSize() { const gdLevel = this._simMain.currentLevel.gdLevel; const minSize = Math.min(this._visMain.canvas.height, this._visMain.canvas.width * Math.sqrt(3) / 2); this._hexSize = Math.floor(minSize / (4 * (gdLevel.radius - 1) - 4)); this._screenCellHeight = Math.ceil(2 * this._hexSize); this._screenCellHeight = Math.ceil(this._screenCellHeight * 0.5) * 2; this._screenCellWidth = Math.ceil(Math.sqrt(3) / 2 * this._screenCellHeight); this._screenCellWidth = Math.ceil(this._screenCellWidth * 0.5) * 2; this._screenXOffset = this._screenCellWidth * (gdLevel.radius + 0.5); this._screenYOffset = this._screenCellHeight * (gdLevel.radius + 0.5); const width = this._screenCellWidth * (gdLevel.radius * 2 + 1); const height = this._screenCellHeight * (gdLevel.radius * 2 + 1); this._screenXOffset += (this._visMain.canvas.width - width) * 0.5; this._screenYOffset += (this._visMain.canvas.height - height) * 0.5; this._screenXOffset = Math.floor(this._screenXOffset); this._screenYOffset = Math.floor(this._screenYOffset); this._background = null; this._enemyMap.clear(); } updateEveryFrame(currentStep) { const simLevel = this._simMain.currentLevel; const t = currentStep - Math.floor(currentStep); const deadEnemies = []; simLevel.simEnemies.forEach((simEnemy) => { if (simEnemy.dead) { deadEnemies.push(simEnemy); return; } const visEnemy = this._enemyMap.get(simEnemy); if (!visEnemy) { this._enemyMap.set(simEnemy, new VisEnemy(this._gdRoot, simEnemy, this._screenCellWidth, this._screenCellHeight)); } else if (Math.floor(currentStep) != Math.floor(this._lastStep)) { visEnemy.advanceStep(); } else { visEnemy.update(t); } }); for (const deadEnemy of deadEnemies) { this._enemyMap.delete(deadEnemy); } const deadProjectiles = []; simLevel.simProjectiles.forEach((simProjectile) => { if (simProjectile.dead) { deadProjectiles.push(simProjectile); return; } const visProjectile = this._projectileMap.get(simProjectile); if (!visProjectile) { this._projectileMap.set(simProjectile, new VisProjectile(simProjectile)); } else if (Math.floor(currentStep) != Math.floor(this._lastStep)) { visProjectile.advanceStep(); } }); for (const deadProjectile of deadProjectiles) { this._projectileMap.delete(deadProjectile); } this._lastStep = currentStep; } getScreenCoords(hex) { const coord = Hex.toPixel(hex, this._hexSize); return new Vector2(coord.x + this._screenXOffset - this._screenCellWidth / 2, coord.y + this._screenYOffset - this._screenCellHeight / 2); } getHexFromScreenCoords(coords) { const x = coords.x - this._screenXOffset; const y = coords.y - this._screenYOffset; return Hex.fromPixel(new Vector2(x, y), this._hexSize); } drawBackground() { const ctx = this._visMain.context; const simLevel = this._simMain.currentLevel; if (this._background == null) { const backgroundCanvas = this._visMain.canvas.cloneNode(); const backgroundContext = backgroundCanvas.getContext("2d"); this._background = backgroundCanvas; simLevel.simCells.forEach((cell) => { if (cell.distance > simLevel.gdLevel.radius) { return; } if (cell.blockedType != -1 && cell.type == ECellType.Blocked) { this.drawCellImage(backgroundContext, cell, "cell-blocked-" + (cell.blockedType | 0) + ".svg"); } }); backgroundContext.globalCompositeOperation = "source-atop"; backgroundContext.fillStyle = this._visMain.wallPattern; backgroundContext.fillRect(0, 0, this._visMain.canvas.width, this._visMain.canvas.height); const cellCanvas = this._visMain.canvas.cloneNode(); const cellContext = cellCanvas.getContext("2d"); simLevel.simCells.forEach((cell) => { if (cell.distance > simLevel.gdLevel.radius) { return; } if (cell.type != ECellType.Entry) { this.drawCellImage(cellContext, cell, "cell.svg"); } }); backgroundContext.globalCompositeOperation = "destination-over"; backgroundContext.drawImage(cellCanvas, 0, 0); backgroundContext.globalCompositeOperation = "source-over"; } ctx.drawImage(this._background, 0, 0); } drawProjectile(simProjectile) { const visProjectile = this._projectileMap.get(simProjectile); if (!visProjectile) { return; } const t = this._lastStep - Math.floor(this._lastStep); const positions = visProjectile.positions; if (!positions) { return; } const pos = Vector2.lerp(positions[0], positions[1], t); const width = this._screenCellWidth * simProjectile.size; const height = this._screenCellHeight * simProjectile.size; this._visMain.context.drawImage(this._gdRoot.image("projectile.svg"), this._screenXOffset + pos.x * this._hexSize - width / 2, this._screenYOffset + pos.y * this._hexSize - height / 2, width, height); } drawEnemy(simEnemy) { const visEnemy = this._enemyMap.get(simEnemy); if (!visEnemy) { return; } const t = this._lastStep - Math.floor(this._lastStep); const positions = visEnemy.positions; if (!positions) { return; } const pos = Vector2.lerp(positions[0], positions[1], t); this._visMain.context.drawImage(visEnemy.image, this._screenXOffset + pos.x * this._hexSize - this._screenCellWidth / 2, this._screenYOffset + pos.y * this._hexSize - this._screenCellHeight / 2, this._screenCellWidth, this._screenCellHeight); } drawCell(cell) { this._visMain.context.fillStyle = "rgba(192, 192, 192, 0.25)"; if (cell.type == ECellType.Entry) { this.drawCellImage(this._visMain.context, cell, "cell-entry-" + (cell.blockedType | 0) + ".svg"); } const simLevel = this._simMain.currentLevel; const highlightedCell = simLevel.simCells[simLevel.highlightedIndex]; if (!!highlightedCell) { let draw = highlightedCell.index == cell.index; if (draw && highlightedCell.pathsToTarget != null) { for (const routeIdx in highlightedCell.pathsToTarget) { for (const idx in highlightedCell.pathsToTarget[routeIdx]) { if (highlightedCell.pathsToTarget[routeIdx][idx] == highlightedCell.index) { draw = true; break; } } } } if (draw) { this.drawCellImage(this._visMain.context, highlightedCell, "cell-highlighted.svg"); this._visMain.context.fillStyle = "rgba(0, 0, 0, 1)"; } } if (cell.simTower != null) { this.drawCellImage(this._visMain.context, cell, "tower-" + (cell.simTower.index | 0) + ".svg"); } const coords = this.getScreenCoords(cell.hex); this._visMain.context.fillText("(" + cell.hex.col + ", " + cell.hex.row + ")", coords.x + 10, coords.y + this._screenCellHeight / 2 + 5); } drawCellImage(context, cell, name) { const coords = this.getScreenCoords(cell.hex); context.drawImage(this._gdRoot.image(name), coords.x, coords.y, this._screenCellWidth, this._screenCellHeight); } } //# sourceMappingURL=VisLevel.js.map