fixed timeline controls

This commit is contained in:
2025-06-02 20:57:29 +02:00
parent 102c1f59dc
commit 61b86b7a60
6 changed files with 298 additions and 286 deletions

View File

@@ -7,9 +7,9 @@
</div> </div>
<div class="bottom-panel"> <div class="bottom-panel">
<button (click)="rewind()">⏮ Rewind</button> <button (click)="rewind()">⏮ Rewind</button>
<button (click)="start()">▶ Play</button> <button (click)="start()" [disabled]="visMain?.active">▶ Play</button>
<button (click)="stop()">⏸ Pause</button> <button (click)="stop()" [disabled]="!visMain?.active">⏸ Pause</button>
<button (click)="step()">Step</button> <button (click)="step()" [disabled]="visMain?.active">Step</button>
<button (click)="fastForward()">⏭ Fast Forward</button> <button (click)="fastForward()">⏭ Fast Forward</button>
<div>Step: {{ simMain.currentStep }}</div> <div>Step: {{ simMain.currentStep }}</div>
<button (click)="startNextWave()">Start next wave</button> <button (click)="startNextWave()">Start next wave</button>

View File

@@ -44,19 +44,25 @@ export class GameComponent {
} }
rewind() { rewind() {
if (this.simMain.currentLevel) const wasActive = this.visMain.active;
this.simMain.currentLevel.currentStep = -1; if (wasActive)
this.visMain.stop();
this.simMain.currentStep = -1; this.simMain.rewind();
if (wasActive)
this.visMain.start();
} }
startNextWave() { startNextWave() {
this.simMain.addCommand(new SimCommandStartNextWave()); this.simMain.addCommand(new SimCommandStartNextWave());
this.simMain.currentLevel!.paused = false;
} }
fastForward() { fastForward() {
const wasActive = this.visMain.active;
if (wasActive)
this.visMain.stop();
this.simMain.executeToEnd(); this.simMain.executeToEnd();
if (wasActive)
this.visMain.start();
} }
async loadGdRoot(): Promise<GdRoot> { async loadGdRoot(): Promise<GdRoot> {

View File

@@ -10,6 +10,7 @@ import { SimLevel } from './SimLevel';
export class SimMain { export class SimMain {
currentStep = -1; currentStep = -1;
maxStep = -1;
currentLevel: SimLevel | null = null; currentLevel: SimLevel | null = null;
commandHistory: SimCommand[] = []; commandHistory: SimCommand[] = [];
gdRoot: GdRoot = null!; gdRoot: GdRoot = null!;
@@ -30,9 +31,7 @@ export class SimMain {
} }
executeToEnd() { executeToEnd() {
this.executeUntilStep( this.executeUntilStep(this.maxStep);
this.commandHistory[this.commandHistory.length - 1].step
);
} }
executeUntilStep(target: number) { executeUntilStep(target: number) {
@@ -41,8 +40,14 @@ export class SimMain {
} }
} }
rewind() {
this.currentLevel = new SimLevel(this.gdRoot, this.currentLevel?.index ?? 0);
this.currentStep = -1;
}
step() { step() {
this.currentStep++; this.currentStep++;
this.maxStep = Math.max(this.maxStep, this.currentStep);
if (this.currentLevel && !this.currentLevel.paused) { if (this.currentLevel && !this.currentLevel.paused) {
this.currentLevel.currentStep++; this.currentLevel.currentStep++;
} }

View File

@@ -17,6 +17,7 @@ export class SimCommandStartNextWave extends SimCommand {
level.nextWaveStep = level.currentStep + simMain.gdRoot.simulation.waveDuration * simMain.gdRoot.simulation.stepsPerSecond - 1; level.nextWaveStep = level.currentStep + simMain.gdRoot.simulation.waveDuration * simMain.gdRoot.simulation.stepsPerSecond - 1;
level.lastEnemySpawnStep = level.currentStep; level.lastEnemySpawnStep = level.currentStep;
level.enemiesLeftToSpawn = data.waves[level.currentWave].amount; level.enemiesLeftToSpawn = data.waves[level.currentWave].amount;
level.paused = false;
} }
public check(simMain: SimMain): boolean { public check(simMain: SimMain): boolean {

View File

@@ -1,312 +1,305 @@
import { AssetPreloaderService } from "../../../assetPreloaderService"; import { AssetPreloaderService } from '../../../assetPreloaderService';
import { GdRoot } from "../data/GdRoot"; import { GdRoot } from '../data/GdRoot';
import { ECellType } from "../sim/ECellType"; import { ECellType } from '../sim/ECellType';
import { SimCell } from "../sim/SimCell"; import { SimCell } from '../sim/SimCell';
import { SimEnemy } from "../sim/SimEnemy"; import { SimEnemy } from '../sim/SimEnemy';
import { SimLevel } from "../sim/SimLevel"; import { SimLevel } from '../sim/SimLevel';
import { SimMain } from "../sim/SimMain"; import { SimMain } from '../sim/SimMain';
import { SimProjectile } from "../sim/SimProjectile"; import { SimProjectile } from '../sim/SimProjectile';
import { Hex } from "../util/Hex"; import { Hex } from '../util/Hex';
import { Vector2 } from "../util/Vector2"; import { Vector2 } from '../util/Vector2';
import { VisEnemy } from "./VisEnemy"; import { VisEnemy } from './VisEnemy';
import { VisMain } from "./VisMain"; import { VisMain } from './VisMain';
import { VisProjectile } from "./VisProjectile"; import { VisProjectile } from './VisProjectile';
export class VisLevel { export class VisLevel {
private screenCellWidth: number = -1; private screenCellWidth: number = -1;
private screenCellHeight: number = -1; private screenCellHeight: number = -1;
private screenXOffset: number = -1; private screenXOffset: number = -1;
private screenYOffset: number = -1; private screenYOffset: number = -1;
private hexSize: number = -1; private hexSize: number = -1;
private lastStep: number = -1; private lastStep: number = -1;
private projectileMap: Map<SimProjectile, VisProjectile>; private projectileMap: Map<SimProjectile, VisProjectile>;
private enemyMap: Map<SimEnemy, VisEnemy>; private enemyMap: Map<SimEnemy, VisEnemy>;
private background: HTMLCanvasElement | null = null; private background: HTMLCanvasElement | null = null;
private simLevel: SimLevel | null = null; private simLevel: SimLevel | null = null;
private visMain: VisMain; private visMain: VisMain;
private simMain: SimMain; private simMain: SimMain;
private gdRoot: GdRoot; private gdRoot: GdRoot;
assets: AssetPreloaderService; assets: AssetPreloaderService;
private hoveredHex: Hex | null = null; private hoveredHex: Hex | null = null;
constructor(visMain: VisMain, simMain: SimMain, gdRoot: GdRoot, assets: AssetPreloaderService) { constructor(visMain: VisMain, simMain: SimMain, gdRoot: GdRoot, assets: AssetPreloaderService) {
this.assets = assets; this.assets = assets;
this.visMain = visMain; this.visMain = visMain;
this.simMain = simMain; this.simMain = simMain;
this.gdRoot = gdRoot this.gdRoot = gdRoot;
this.enemyMap = new Map<SimEnemy, VisEnemy>(); this.enemyMap = new Map<SimEnemy, VisEnemy>();
this.projectileMap = new Map<SimProjectile, VisProjectile>(); this.projectileMap = new Map<SimProjectile, VisProjectile>();
this.reset(); this.reset();
} }
public reset() { public reset() {
this.projectileMap.clear(); this.projectileMap.clear();
this.enemyMap.clear(); this.enemyMap.clear();
this.background = null; this.background = null;
} }
public draw() { public draw() {
const ctx = this.visMain.context; const ctx = this.visMain.context;
const simLevel = this.simMain.currentLevel; const simLevel = this.simMain.currentLevel;
if (simLevel == null) { if (simLevel == null) {
return; return;
} }
const gdLevel = this.gdRoot.levels[simLevel.index]; const gdLevel = this.gdRoot.levels[simLevel.index];
if (simLevel != this.simLevel) { if (simLevel != this.simLevel) {
this.reset(); this.reset();
this.simLevel = simLevel; this.simLevel = simLevel;
} }
this.drawBackground(); this.drawBackground();
ctx.globalCompositeOperation = "source-over"; ctx.globalCompositeOperation = 'source-over';
// Highlight hovered cell first (under everything else) // Highlight hovered cell first (under everything else)
if (this.hoveredHex) { if (this.hoveredHex) {
const hoveredIdx = simLevel.getCellIndex(this.hoveredHex); const hoveredIdx = simLevel.getCellIndex(this.hoveredHex);
const hoveredCell = simLevel.cells[hoveredIdx]; const hoveredCell = simLevel.cells[hoveredIdx];
if (hoveredCell && hoveredCell.distance <= gdLevel.radius) { if (hoveredCell && hoveredCell.distance <= gdLevel.radius) {
this.drawCellImage(ctx, hoveredCell, "cell-highlighted.svg"); this.drawCellImage(ctx, hoveredCell, 'cell-highlighted.svg');
} }
} }
simLevel.cells.forEach((cell: SimCell) => { simLevel.cells.forEach((cell: SimCell) => {
if (cell.distance > gdLevel.radius) { if (cell.distance > gdLevel.radius) {
return; return;
} }
this.drawCell(cell); this.drawCell(cell);
}); });
simLevel.enemies.forEach((enemy: SimEnemy) => { simLevel.enemies.forEach((enemy: SimEnemy) => {
this.drawEnemy(enemy); this.drawEnemy(enemy);
}); });
simLevel.projectiles.forEach((projectile: SimProjectile) => { simLevel.projectiles.forEach((projectile: SimProjectile) => {
this.drawProjectile(projectile); this.drawProjectile(projectile);
}); });
ctx.fillStyle = "white"; ctx.fillStyle = 'white';
ctx.fillText("Currency: " + simLevel.currency, 5, 15); ctx.fillText('Currency: ' + simLevel.currency, 5, 15);
ctx.fillText("Current wave: " + simLevel.currentWave, 5, 35); ctx.fillText('Current wave: ' + simLevel.currentWave, 5, 35);
ctx.fillText("Enemies left: " + simLevel.enemiesLeftToSpawn, 5, 55); ctx.fillText('Enemies left: ' + simLevel.enemiesLeftToSpawn, 5, 55);
ctx.fillText("Current step: " + simLevel.currentStep, 5, 75); ctx.fillText('Current step: ' + simLevel.currentStep, 5, 75);
} }
public updateSize() { public updateSize() {
const simLevel = this.simMain.currentLevel; const simLevel = this.simMain.currentLevel;
if (simLevel == null) { if (simLevel == null) {
return; return;
} }
const gdLevel = this.gdRoot.levels[simLevel.index]; const gdLevel = this.gdRoot.levels[simLevel.index];
const minSize = Math.min(this.visMain.canvas.height, this.visMain.canvas.width * Math.sqrt(3) / 2); 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.hexSize = Math.floor(minSize / (4 * (gdLevel.radius - 1) - 4));
this.screenCellHeight = Math.ceil(2 * this.hexSize); this.screenCellHeight = Math.ceil(2 * this.hexSize);
this.screenCellHeight = Math.ceil(this.screenCellHeight * 0.5) * 2; this.screenCellHeight = Math.ceil(this.screenCellHeight * 0.5) * 2;
this.screenCellWidth = Math.ceil(Math.sqrt(3) / 2 * this.screenCellHeight); this.screenCellWidth = Math.ceil((Math.sqrt(3) / 2) * this.screenCellHeight);
this.screenCellWidth = Math.ceil(this.screenCellWidth * 0.5) * 2; this.screenCellWidth = Math.ceil(this.screenCellWidth * 0.5) * 2;
this.screenXOffset = this.screenCellWidth * (gdLevel.radius + 0.5); this.screenXOffset = this.screenCellWidth * (gdLevel.radius + 0.5);
this.screenYOffset = this.screenCellHeight * (gdLevel.radius + 0.5); this.screenYOffset = this.screenCellHeight * (gdLevel.radius + 0.5);
const width = this.screenCellWidth * (gdLevel.radius * 2 + 1); const width = this.screenCellWidth * (gdLevel.radius * 2 + 1);
const height = this.screenCellHeight * (gdLevel.radius * 2 + 1); const height = this.screenCellHeight * (gdLevel.radius * 2 + 1);
this.screenXOffset += (this.visMain.canvas.width - width) * 0.5; this.screenXOffset += (this.visMain.canvas.width - width) * 0.5;
this.screenYOffset += (this.visMain.canvas.height - height) * 0.5; this.screenYOffset += (this.visMain.canvas.height - height) * 0.5;
this.screenXOffset = Math.floor(this.screenXOffset); this.screenXOffset = Math.floor(this.screenXOffset);
this.screenYOffset = Math.floor(this.screenYOffset); this.screenYOffset = Math.floor(this.screenYOffset);
this.background = null; this.background = null;
this.enemyMap.clear(); this.enemyMap.clear();
} }
public updateEveryFrame(currentStep: number) { public updateEveryFrame(currentStep: number) {
const simLevel = this.simMain.currentLevel; if (currentStep < 0) {
if (simLevel == null) { this.lastStep = -1;
return; return;
} }
const t = currentStep - Math.floor(currentStep); const simLevel = this.simMain.currentLevel;
if (simLevel == null) {
return;
}
const deadEnemies: SimEnemy[] = []; const t = currentStep - Math.floor(currentStep);
simLevel.enemies.forEach((simEnemy: SimEnemy) => {
if (simEnemy.dead) {
deadEnemies.push(simEnemy);
return;
}
const visEnemy = this.enemyMap.get(simEnemy); const deadEnemies: SimEnemy[] = [];
if (!visEnemy) { simLevel.enemies.forEach((simEnemy: SimEnemy) => {
this.enemyMap.set(simEnemy, new VisEnemy(this.gdRoot, this.assets, simEnemy, this.screenCellWidth, this.screenCellHeight)); if (simEnemy.dead) {
} deadEnemies.push(simEnemy);
else if (Math.floor(currentStep) != Math.floor(this.lastStep)) { return;
visEnemy.advanceStep(); }
}
else {
visEnemy.update(t);
}
});
for (const deadEnemy of deadEnemies) {
this.enemyMap.delete(deadEnemy);
}
const deadProjectiles: SimProjectile[] = []; const visEnemy = this.enemyMap.get(simEnemy);
simLevel.projectiles.forEach((simProjectile: SimProjectile) => { if (!visEnemy) {
if (simProjectile.dead) { this.enemyMap.set(simEnemy, new VisEnemy(this.gdRoot, this.assets, simEnemy, this.screenCellWidth, this.screenCellHeight));
deadProjectiles.push(simProjectile); } else if (Math.floor(currentStep) != Math.floor(this.lastStep)) {
return; visEnemy.advanceStep();
} } else {
visEnemy.update(t);
}
});
for (const deadEnemy of deadEnemies) {
this.enemyMap.delete(deadEnemy);
}
const visProjectile = this.projectileMap.get(simProjectile); const deadProjectiles: SimProjectile[] = [];
if (!visProjectile) { simLevel.projectiles.forEach((simProjectile: SimProjectile) => {
this.projectileMap.set(simProjectile, new VisProjectile(simProjectile)); if (simProjectile.dead) {
} deadProjectiles.push(simProjectile);
else if (Math.floor(currentStep) != Math.floor(this.lastStep)) { return;
visProjectile.advanceStep(); }
}
});
for (const deadProjectile of deadProjectiles) {
this.projectileMap.delete(deadProjectile);
}
this.lastStep = currentStep; 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);
}
public setHoveredHex(hex: Hex | null) { this.lastStep = currentStep;
this.hoveredHex = hex; }
}
public getScreenCoords(hex: Hex): Vector2 { public setHoveredHex(hex: Hex | null) {
const coord = Hex.toPixel(hex, this.hexSize); this.hoveredHex = hex;
return new Vector2(coord.x + this.screenXOffset - this.screenCellWidth / 2, coord.y + this.screenYOffset - this.screenCellHeight / 2); }
}
public getHexFromScreenCoords(coords: Vector2): Hex { public getScreenCoords(hex: Hex): Vector2 {
const x = coords.x - this.screenXOffset; const coord = Hex.toPixel(hex, this.hexSize);
const y = coords.y - this.screenYOffset; return new Vector2(coord.x + this.screenXOffset - this.screenCellWidth / 2, coord.y + this.screenYOffset - this.screenCellHeight / 2);
return Hex.fromPixel(new Vector2(x, y), this.hexSize); }
}
private drawBackground() { public getHexFromScreenCoords(coords: Vector2): Hex {
const ctx = this.visMain.context; const x = coords.x - this.screenXOffset;
const simLevel = this.simMain.currentLevel; const y = coords.y - this.screenYOffset;
if (simLevel == null) { return Hex.fromPixel(new Vector2(x, y), this.hexSize);
return; }
}
const gdLevel = this.gdRoot.levels[simLevel.index]; private drawBackground() {
if (this.background == null) { const ctx = this.visMain.context;
const backgroundCanvas = this.visMain.canvas.cloneNode() as HTMLCanvasElement; const simLevel = this.simMain.currentLevel;
const backgroundContext = backgroundCanvas.getContext("2d")!; if (simLevel == null) {
this.background = backgroundCanvas; return;
simLevel.cells.forEach((cell: SimCell) => { }
if (cell.distance > 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"; const gdLevel = this.gdRoot.levels[simLevel.index];
backgroundContext.fillStyle = this.visMain.wallPattern; if (this.background == null) {
backgroundContext.fillRect(0, 0, this.visMain.canvas.width, this.visMain.canvas.height); const backgroundCanvas = this.visMain.canvas.cloneNode() as HTMLCanvasElement;
const backgroundContext = backgroundCanvas.getContext('2d')!;
this.background = backgroundCanvas;
simLevel.cells.forEach((cell: SimCell) => {
if (cell.distance > gdLevel.radius) {
return;
}
if (cell.blockedType != -1 && cell.type == ECellType.Blocked) {
this.drawCellImage(backgroundContext, cell, 'cell-blocked-' + (cell.blockedType | 0) + '.svg');
}
});
const cellCanvas = this.visMain.canvas.cloneNode() as HTMLCanvasElement; backgroundContext.globalCompositeOperation = 'source-atop';
const cellContext = cellCanvas.getContext("2d")!; backgroundContext.fillStyle = this.visMain.wallPattern;
simLevel.cells.forEach((cell: SimCell) => { backgroundContext.fillRect(0, 0, this.visMain.canvas.width, this.visMain.canvas.height);
if (cell.distance > 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); const cellCanvas = this.visMain.canvas.cloneNode() as HTMLCanvasElement;
} const cellContext = cellCanvas.getContext('2d')!;
simLevel.cells.forEach((cell: SimCell) => {
if (cell.distance > 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';
}
private drawProjectile(simProjectile: SimProjectile) { ctx.drawImage(this.background, 0, 0);
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.assets.getImage("images/projectile.svg"),
this.screenXOffset + pos.x * this.hexSize - width / 2,
this.screenYOffset + pos.y * this.hexSize - height / 2,
width,
height);
}
private drawEnemy(simEnemy: SimEnemy) { private drawProjectile(simProjectile: SimProjectile) {
const visEnemy = this.enemyMap.get(simEnemy); const visProjectile = this.projectileMap.get(simProjectile);
if (!visEnemy) { if (!visProjectile) {
return; return;
} }
const t = this.lastStep - Math.floor(this.lastStep); const t = this.lastStep - Math.floor(this.lastStep);
const positions = visEnemy.positions; const positions = visProjectile.positions;
if (!positions) { if (!positions) {
return; return;
} }
const pos = Vector2.lerp(positions[0], positions[1], t); const pos = Vector2.lerp(positions[0], positions[1], t);
this.visMain.context.drawImage( const width = this.screenCellWidth * simProjectile.size;
visEnemy.image, const height = this.screenCellHeight * simProjectile.size;
this.screenXOffset + pos.x * this.hexSize - this.screenCellWidth / 2, this.visMain.context.drawImage(this.assets.getImage('images/projectile.svg'), this.screenXOffset + pos.x * this.hexSize - width / 2, this.screenYOffset + pos.y * this.hexSize - height / 2, width, height);
this.screenYOffset + pos.y * this.hexSize - this.screenCellHeight / 2, }
this.screenCellWidth,
this.screenCellHeight);
}
private drawCell(cell: SimCell) { private drawEnemy(simEnemy: SimEnemy) {
const simLevel = this.simMain.currentLevel; const visEnemy = this.enemyMap.get(simEnemy);
if (simLevel == null) { if (!visEnemy) {
return; 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);
}
this.visMain.context.fillStyle = "rgba(192, 192, 192, 0.25)"; private drawCell(cell: SimCell) {
if (cell.type == ECellType.Entry) { const simLevel = this.simMain.currentLevel;
this.drawCellImage(this.visMain.context, cell, "cell-entry-" + (cell.blockedType | 0) + ".svg"); if (simLevel == null) {
} return;
}
const highlightedCell = simLevel.cells[simLevel.highlightedIndex]; this.visMain.context.fillStyle = 'rgba(192, 192, 192, 0.25)';
if (!!highlightedCell) { if (cell.type == ECellType.Entry) {
let draw = highlightedCell.index == cell.index; this.drawCellImage(this.visMain.context, cell, 'cell-entry-' + (cell.blockedType | 0) + '.svg');
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.tower != null) {
this.drawCellImage(this.visMain.context, cell, "tower-" + (cell.tower.index | 0) + ".svg");
}
const coords = this.getScreenCoords(cell.hex); const highlightedCell = simLevel.cells[simLevel.highlightedIndex];
this.visMain.context.fillText("(" + cell.hex.col + ", " + cell.hex.row + ")", coords.x + 10, coords.y + this.screenCellHeight / 2 + 5); 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.tower != null) {
this.drawCellImage(this.visMain.context, cell, 'tower-' + (cell.tower.index | 0) + '.svg');
}
private drawCellImage(context: CanvasRenderingContext2D, cell: SimCell, name: string) { const coords = this.getScreenCoords(cell.hex);
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);
context.drawImage(this.assets.getImage("images/" + name), coords.x, coords.y, this.screenCellWidth, this.screenCellHeight); }
}
private drawCellImage(context: CanvasRenderingContext2D, cell: SimCell, name: string) {
const coords = this.getScreenCoords(cell.hex);
context.drawImage(this.assets.getImage('images/' + name), coords.x, coords.y, this.screenCellWidth, this.screenCellHeight);
}
} }

View File

@@ -19,6 +19,7 @@ export class VisMain {
private startTimestamp: number = 0; private startTimestamp: number = 0;
private ready: boolean = false; private ready: boolean = false;
private gap: number = 0; private gap: number = 0;
private animationFrameId: number | null = null;
constructor(simMain: SimMain, assets: AssetPreloaderService, wrapper: HTMLDivElement, canvas: HTMLCanvasElement) { constructor(simMain: SimMain, assets: AssetPreloaderService, wrapper: HTMLDivElement, canvas: HTMLCanvasElement) {
this.assets = assets; this.assets = assets;
@@ -50,7 +51,7 @@ export class VisMain {
return; return;
} }
requestAnimationFrame((timestamp: number) => { this.animationFrameId = requestAnimationFrame((timestamp: number) => {
this.step(timestamp); this.step(timestamp);
}); });
@@ -105,11 +106,17 @@ export class VisMain {
stop() { stop() {
this.active = false; this.active = false;
this.startTimestamp = 0;
this.visLevel.updateEveryFrame(-1);
if (this.animationFrameId !== null) {
cancelAnimationFrame(this.animationFrameId);
this.animationFrameId = null;
}
} }
start() { start() {
this.active = true; this.active = true;
requestAnimationFrame((timestamp: number) => { this.animationFrameId = requestAnimationFrame((timestamp: number) => {
this.startTimestamp = timestamp - (this.simMain.currentStep / this.simMain.gdRoot.simulation.stepsPerSecond) * 1000; this.startTimestamp = timestamp - (this.simMain.currentStep / this.simMain.gdRoot.simulation.stepsPerSecond) * 1000;
this.step(timestamp); this.step(timestamp);
}); });