import { Hex, PathFinding } from "../../Util/index.js"; import { ECellType, SimCell } from "../index.js"; export class SimLevel { static version = 2; _paused = true; _currentStep = -1; _gdLevel; _highlightedIndex = -1; _currency = -1; _stride = -1; _simCells; _simEnemies; _simProjectiles; _nextWaveStep = -1; _lastEnemySpawnStep = 0; _currentWave; _enemiesLeftToSpawn; _levelIdx; _gdRoot; constructor(gdRoot, levelIdx) { this._levelIdx = levelIdx; this._gdRoot = gdRoot; this._gdLevel = gdRoot.levels[levelIdx]; this._simCells = []; this._simEnemies = []; this._simProjectiles = []; this._currentStep = 0; this._currency = this._gdLevel.currency; this._currentWave = 0; this._enemiesLeftToSpawn = this._gdLevel.waves[0].amount; const radius = this._gdLevel.radius; this._stride = 2 * radius + 1; const h0 = new Hex(0, 0); for (let y = -radius; y <= radius; ++y) { for (let x = -radius; x <= radius; ++x) { const hex = new Hex(x, y); const distance = Hex.distance(hex, h0); const type = distance >= radius ? ECellType.Blocked : ECellType.Free; const cellIndex = this.getCellIndex(hex); this._simCells[cellIndex] = new SimCell(hex, distance, type, cellIndex); } } this._gdLevel.walls.forEach((wall) => { const cellIndex = this.getCellIndex(wall); this._simCells[cellIndex].type = ECellType.Blocked; }); this._gdLevel.enemySpawns.forEach((hex) => { const cellIndex = this.getCellIndex(hex); this._simCells[cellIndex].type = ECellType.Entry; }); this._gdLevel.enemyTargets.forEach((hex) => { const cellIndex = this.getCellIndex(hex); this._simCells[cellIndex].type = ECellType.Entry; }); this._simCells.forEach((cell) => { this.updateBlockedType(cell); }); this.updatePaths(); } ; get gdLevel() { return this._gdLevel; } get simCells() { return this._simCells; } get simEnemies() { return this._simEnemies; } get simProjectiles() { return this._simProjectiles; } get currentStep() { return this._currentStep; } set currentStep(value) { this._currentStep = value; } get currency() { return this._currency; } set currency(value) { this._currency = value; } get highlightedIndex() { return this._highlightedIndex; } set highlightedIndex(value) { this._highlightedIndex = value; } get paused() { return this._paused; } set paused(value) { this._paused = value; } get currentWave() { return this._currentWave; } set currentWave(value) { this._currentWave = value; } get nextWaveStep() { return this._nextWaveStep; } set nextWaveStep(value) { this._nextWaveStep = value; } get lastEnemySpawnStep() { return this._lastEnemySpawnStep; } set lastEnemySpawnStep(value) { this._lastEnemySpawnStep = value; } get enemiesLeftToSpawn() { return this._enemiesLeftToSpawn; } set enemiesLeftToSpawn(value) { this._enemiesLeftToSpawn = value; } serialize() { const data = { version: SimLevel.version, levelIdx: this._levelIdx, paused: this._paused, gdLevel: this._gdLevel, currentStep: this._currentStep, highlightedIndex: this._highlightedIndex, currency: this._currency, stride: this._stride, cells: this._simCells, enemies: this._simEnemies, projectiles: this._simProjectiles, nextWaveStep: this._nextWaveStep, lastEnemySpawnStep: this._lastEnemySpawnStep, currentWave: this._currentWave, enemiesLeftToSpawn: this._enemiesLeftToSpawn }; return btoa(JSON.stringify(data)); } static deserialize(gdRoot, data) { const parsedData = JSON.parse(atob(data)); if (parsedData.version != SimLevel.version) { console.warn('Unsupported version:', parsedData.version); return null; } let level = new SimLevel(gdRoot, parsedData.levelIdx); level._paused = parsedData.paused; level._gdLevel = parsedData.gdLevel; level._currentStep = parsedData.currentStep; level._highlightedIndex = parsedData.highlightedIndex; level._currency = parsedData.currency; level._stride = parsedData.stride; level._simCells = parsedData.cells; level._simEnemies = parsedData.enemies; level._simProjectiles = parsedData.projectiles; level._nextWaveStep = parsedData.nextWaveStep; level._lastEnemySpawnStep = parsedData.lastEnemySpawnStep; level._currentWave = parsedData.currentWave; level._enemiesLeftToSpawn = parsedData.enemiesLeftToSpawn; return level; } getCellIndex(hex) { const x = hex.col + this._gdLevel.radius; const y = hex.row + this._gdLevel.radius; if (x < 0 || x >= this._stride || y < 0 || y >= this._stride) { return -1; } return y * this._stride + x; } getNeighbourCell(cell, direction) { const hex = cell.hex; const neighbourHex = Hex.neighbour(hex, direction); const neighbourIndex = this.getCellIndex(neighbourHex); return this._simCells[neighbourIndex]; } updateBlockedType(cell) { if (cell.type == ECellType.Free) { cell.blockedType = -1; return; } if (cell.type == ECellType.Entry && cell.blockedType != -1) { return; } let blockedType = 0; for (let direction = 0; direction < 6; ++direction) { const neighbourCell = this.getNeighbourCell(cell, direction); if (!!neighbourCell && neighbourCell.type == ECellType.Free) { blockedType |= 1 << direction; } } cell.blockedType = blockedType; } reservePaths(hexToBlock) { try { if (!!hexToBlock) { const reservedCell = this._simCells[this.getCellIndex(hexToBlock)]; if (reservedCell.type != ECellType.Free || !!reservedCell.simTower) return false; reservedCell.type = ECellType.Reserved; } return this.updatePaths(); } finally { if (!!hexToBlock) { const reservedCell = this._simCells[this.getCellIndex(hexToBlock)]; if (reservedCell.type == ECellType.Reserved) reservedCell.type = ECellType.Free; } } } updatePaths() { const level = this._gdLevel; const newRoutePaths = []; const newEnemyPaths = []; let invalid = false; for (const routeIdx in level.enemyRoutes) { const route = level.enemyRoutes[routeIdx]; const enemySpawnHex = level.enemySpawns[route[0]]; const enemySpawnCell = this._simCells[this.getCellIndex(enemySpawnHex)]; const enemyTargetHex = level.enemyTargets[route[1]]; const enemyTargetCell = this._simCells[this.getCellIndex(enemyTargetHex)]; const path = PathFinding.bfs(this, enemySpawnCell.index, enemyTargetCell.index); if (path == null) { invalid = true; break; } newRoutePaths[routeIdx] = path; } if (invalid) { return false; } for (let idx in this._simEnemies) { const simEnemy = this._simEnemies[idx]; const hex = Hex.fromWorld(simEnemy.position); const startIndex = this.getCellIndex(hex); const endIndex = this.getCellIndex(simEnemy.endHex); const path = PathFinding.bfs(this, startIndex, endIndex); if (path == null) { invalid = true; break; } newEnemyPaths[idx] = path; } if (invalid) { return false; } for (const routeIdx in level.enemyRoutes) { const route = level.enemyRoutes[routeIdx]; const enemySpawnHex = level.enemySpawns[route[0]]; const enemySpawnCell = this._simCells[this.getCellIndex(enemySpawnHex)]; enemySpawnCell.pathsToTarget[routeIdx] = newRoutePaths[routeIdx]; } this._simEnemies.forEach((simEnemy, idx) => { simEnemy.path = newEnemyPaths[idx]; simEnemy.currentPathIndex = 0; simEnemy.onPathUpdated(); }); return true; } } //# sourceMappingURL=SimLevel.js.map