herd / commit
Add level progression locking and enlarge level-select buttons
src/game/GameState.ts | 48 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/src/game/GameState.ts b/src/game/GameState.ts index d9a6fe4..2822bfc 100644 --- a/src/game/GameState.ts +++ b/src/game/GameState.ts @@ -29,8 +29,14 @@ export class GameState { private penW0 = 0 private penH0 = 0 + private unlockedUpTo = 0 // highest level index the player may enter + private canvas: Canvas - constructor(canvas: Canvas) { this.canvas = canvas } + constructor(canvas: Canvas) { + this.canvas = canvas + const saved = localStorage.getItem('herd_progress') + if (saved !== null) this.unlockedUpTo = parseInt(saved, 10) + } get currentDog() { return this.dog } @@ -145,6 +151,10 @@ export class GameState { // check win if (inPen >= cfg.winThreshold) { this.phase = 'WIN' + if (this.levelIndex + 1 > this.unlockedUpTo) { + this.unlockedUpTo = this.levelIndex + 1 + localStorage.setItem('herd_progress', String(this.unlockedUpTo)) + } return } @@ -357,29 +367,37 @@ export class GameState { ctx.fillText('Tap a level to begin', W / 2, H * 0.31) const cols = 5, rows = 2 - const cellW = Math.min(90, W / (cols + 1)) - const cellH = 64 + const cellW = Math.min(160, W / (cols + 0.5)) + const cellH = 88 const startX = W / 2 - (cols * cellW * 1.2) / 2 - const startY = H * 0.42 + const startY = H * 0.38 LEVELS.forEach((level, i) => { const col = i % cols const row = Math.floor(i / cols) const cx = startX + col * cellW * 1.2 + cellW / 2 const cy = startY + row * (cellH + 20) + const locked = i > this.unlockedUpTo - ctx.fillStyle = 'rgba(255,255,255,0.15)' + ctx.fillStyle = locked ? 'rgba(255,255,255,0.06)' : 'rgba(255,255,255,0.15)' ctx.beginPath() roundRect(ctx, cx - cellW / 2, cy - cellH / 2, cellW, cellH, 10) ctx.fill() - ctx.fillStyle = '#fff' - ctx.font = `bold ${Math.round(cellW * 0.28)}px system-ui` - ctx.textAlign = 'center' - ctx.fillText(`${level.id}`, cx, cy + 5) + if (locked) { + ctx.fillStyle = 'rgba(255,255,255,0.2)' + ctx.font = `${Math.round(cellW * 0.30)}px system-ui` + ctx.textAlign = 'center' + ctx.fillText('🔒', cx, cy + 8) + } else { + ctx.fillStyle = '#fff' + ctx.font = `bold ${Math.round(cellW * 0.22)}px system-ui` + ctx.textAlign = 'center' + ctx.fillText(`${level.id}`, cx, cy + 5) + } - ctx.fillStyle = 'rgba(255,255,255,0.6)' - ctx.font = `${Math.round(cellW * 0.15)}px system-ui` + ctx.fillStyle = locked ? 'rgba(255,255,255,0.2)' : 'rgba(255,255,255,0.6)' + ctx.font = `${Math.round(cellW * 0.11)}px system-ui` ctx.fillText(level.title, cx, cy + cellH / 2 - 6) }) @@ -415,10 +433,10 @@ export class GameState { // hit test level buttons const cols = 5 - const cellW = Math.min(90, W / (cols + 1)) - const cellH = 64 + const cellW = Math.min(160, W / (cols + 0.5)) + const cellH = 88 const startX = W / 2 - (cols * cellW * 1.2) / 2 - const startY = H * 0.42 + const startY = H * 0.38 for (let i = 0; i < LEVELS.length; i++) { const col = i % cols @@ -426,7 +444,7 @@ export class GameState { const cx = startX + col * cellW * 1.2 + cellW / 2 const cy = startY + row * (cellH + 20) if (Math.abs(x - cx) < cellW / 2 && Math.abs(y - cy) < cellH / 2) { - this.startLevel(i) + if (i <= this.unlockedUpTo) this.startLevel(i) return } }