@hypersocial/cli-games 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -663,7 +663,7 @@ function run2048Game(terminal) {
663
663
  "\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2582\u2580\u2580\u2588 \u2580\u2580\u2588 \u2584 \u2588\u2580\u2580 \u2580\u2588\u2580",
664
664
  "\u2588\u2580\u2588 \u2588 \u2588\u2580\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584 \u2584\u2580\u2580\u2584 \u2580\u2580\u2584 \u2588\u2580\u2584 \u2588\u2588\u2584 \u2588 "
665
665
  ];
666
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
666
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
667
667
  for (let i = 0; i < count; i++) {
668
668
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
669
669
  const speed = 0.3 + Math.random() * 0.5;
@@ -683,12 +683,12 @@ function run2048Game(terminal) {
683
683
  const screenY = gridY * TILE_HEIGHT + TILE_HEIGHT / 2;
684
684
  const color = TILE_COLORS[value] || "\x1B[97m";
685
685
  const dataChars = ["\u2588", "\u2593", "\u2592", "\u2591", "\u25A0", "\u25A1"];
686
- spawnParticles2(screenX, screenY, 8, color, dataChars);
686
+ spawnParticles3(screenX, screenY, 8, color, dataChars);
687
687
  }
688
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
688
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
689
689
  scorePopups.push({ x, y, text, frames: 20, color });
690
690
  }
691
- function triggerShake(frames, intensity) {
691
+ function triggerShake2(frames, intensity) {
692
692
  shakeFrames = frames;
693
693
  shakeIntensity = intensity;
694
694
  }
@@ -754,7 +754,7 @@ function run2048Game(terminal) {
754
754
  for (let x = 0; x < GRID_SIZE; x++) {
755
755
  if (merged[x] && result[x] > 0) {
756
756
  spawnMergeParticles(x, y, result[x]);
757
- addScorePopup(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${result[x]}`, TILE_COLORS[result[x]]);
757
+ addScorePopup2(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${result[x]}`, TILE_COLORS[result[x]]);
758
758
  }
759
759
  }
760
760
  grid[y] = result;
@@ -770,7 +770,7 @@ function run2048Game(terminal) {
770
770
  for (let x = 0; x < GRID_SIZE; x++) {
771
771
  if (finalMerged[x] && finalRow[x] > 0) {
772
772
  spawnMergeParticles(x, y, finalRow[x]);
773
- addScorePopup(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${finalRow[x]}`, TILE_COLORS[finalRow[x]]);
773
+ addScorePopup2(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${finalRow[x]}`, TILE_COLORS[finalRow[x]]);
774
774
  }
775
775
  }
776
776
  grid[y] = finalRow;
@@ -785,7 +785,7 @@ function run2048Game(terminal) {
785
785
  for (let y = 0; y < GRID_SIZE; y++) {
786
786
  if (merged[y] && result[y] > 0) {
787
787
  spawnMergeParticles(x, y, result[y]);
788
- addScorePopup(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${result[y]}`, TILE_COLORS[result[y]]);
788
+ addScorePopup2(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${result[y]}`, TILE_COLORS[result[y]]);
789
789
  }
790
790
  grid[y][x] = result[y];
791
791
  }
@@ -803,7 +803,7 @@ function run2048Game(terminal) {
803
803
  for (let y = 0; y < GRID_SIZE; y++) {
804
804
  if (finalMerged[y] && finalCol[y] > 0) {
805
805
  spawnMergeParticles(x, y, finalCol[y]);
806
- addScorePopup(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${finalCol[y]}`, TILE_COLORS[finalCol[y]]);
806
+ addScorePopup2(x * TILE_WIDTH + TILE_WIDTH / 2, y * TILE_HEIGHT + 1, `+${finalCol[y]}`, TILE_COLORS[finalCol[y]]);
807
807
  }
808
808
  grid[y][x] = finalCol[y];
809
809
  }
@@ -812,13 +812,13 @@ function run2048Game(terminal) {
812
812
  if (anyMoved) {
813
813
  score += totalMergeScore;
814
814
  if (totalMergeScore > 0) {
815
- triggerShake(4, 1);
815
+ triggerShake2(4, 1);
816
816
  }
817
817
  spawnTile();
818
818
  if (!won && !continuedAfterWin && has2048()) {
819
819
  won = true;
820
820
  if (score > highScore) highScore = score;
821
- triggerShake(8, 2);
821
+ triggerShake2(8, 2);
822
822
  for (let y = 0; y < GRID_SIZE; y++) {
823
823
  for (let x = 0; x < GRID_SIZE; x++) {
824
824
  if (grid[y][x] === 2048) {
@@ -1320,11 +1320,11 @@ function runAsteroidsGame(terminal) {
1320
1320
  if (value >= max) return value - max;
1321
1321
  return value;
1322
1322
  }
1323
- function triggerShake(frames, intensity) {
1323
+ function triggerShake2(frames, intensity) {
1324
1324
  shakeFrames = frames;
1325
1325
  shakeIntensity = intensity;
1326
1326
  }
1327
- function spawnParticles2(x, y, count, color) {
1327
+ function spawnParticles3(x, y, count, color) {
1328
1328
  const chars = ["*", "+", ".", "\xB7"];
1329
1329
  for (let i = 0; i < count; i++) {
1330
1330
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
@@ -1356,8 +1356,8 @@ function runAsteroidsGame(terminal) {
1356
1356
  function hitShip() {
1357
1357
  if (invincibilityFrames > 0) return;
1358
1358
  lives--;
1359
- spawnParticles2(ship.x, ship.y, 15, "\x1B[91m");
1360
- triggerShake(10, 3);
1359
+ spawnParticles3(ship.x, ship.y, 15, "\x1B[91m");
1360
+ triggerShake2(10, 3);
1361
1361
  if (lives <= 0) {
1362
1362
  gameOver = true;
1363
1363
  if (score > highScore) highScore = score;
@@ -1370,7 +1370,7 @@ function runAsteroidsGame(terminal) {
1370
1370
  }
1371
1371
  }
1372
1372
  function splitAsteroid(asteroid) {
1373
- spawnParticles2(asteroid.x, asteroid.y, 8, "\x1B[93m");
1373
+ spawnParticles3(asteroid.x, asteroid.y, 8, "\x1B[93m");
1374
1374
  score += ASTEROID_SCORES[asteroid.size];
1375
1375
  if (asteroid.size === "large") {
1376
1376
  for (let i = 0; i < 2; i++) {
@@ -1438,7 +1438,7 @@ function runAsteroidsGame(terminal) {
1438
1438
  bullets.splice(j, 1);
1439
1439
  splitAsteroid(ast);
1440
1440
  asteroids.splice(i, 1);
1441
- triggerShake(4, 1);
1441
+ triggerShake2(4, 1);
1442
1442
  break;
1443
1443
  }
1444
1444
  }
@@ -1721,17 +1721,17 @@ function runBreakoutGame(terminal) {
1721
1721
  ];
1722
1722
  const brickColors = ["\x1B[1;91m", "\x1B[1;93m", "\x1B[1;92m", "\x1B[1;96m", "\x1B[1;95m"];
1723
1723
  const brickPoints = [50, 40, 30, 20, 10];
1724
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
1724
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
1725
1725
  for (let i = 0; i < count; i++) {
1726
1726
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
1727
1727
  const speed = 0.2 + Math.random() * 0.4;
1728
1728
  particles.push({ x, y, char: chars[Math.floor(Math.random() * chars.length)], color, vx: Math.cos(angle) * speed, vy: Math.sin(angle) * speed * 0.5, life: 10 + Math.floor(Math.random() * 8) });
1729
1729
  }
1730
1730
  }
1731
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
1731
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
1732
1732
  scorePopups.push({ x, y, text, frames: 18, color });
1733
1733
  }
1734
- function triggerShake(frames, intensity) {
1734
+ function triggerShake2(frames, intensity) {
1735
1735
  shakeFrames = frames;
1736
1736
  shakeIntensity = intensity;
1737
1737
  }
@@ -1788,7 +1788,7 @@ function runBreakoutGame(terminal) {
1788
1788
  powerUps.push({ x, y, type: types[Math.floor(Math.random() * types.length)], vy: 0.15 });
1789
1789
  }
1790
1790
  function applyPowerUp(type) {
1791
- triggerShake(8, 2);
1791
+ triggerShake2(8, 2);
1792
1792
  borderFlash = 15;
1793
1793
  switch (type) {
1794
1794
  case "multiball": {
@@ -1801,18 +1801,18 @@ function runBreakoutGame(terminal) {
1801
1801
  balls.push({ x: src.x, y: src.y, vx: Math.cos(angle) * speed, vy: -Math.abs(Math.sin(angle) * speed), active: true });
1802
1802
  }
1803
1803
  }
1804
- addScorePopup(paddleX, GAME_HEIGHT - 5, "MULTI-BALL!", "\x1B[1;96m");
1804
+ addScorePopup2(paddleX, GAME_HEIGHT - 5, "MULTI-BALL!", "\x1B[1;96m");
1805
1805
  break;
1806
1806
  }
1807
1807
  case "wide":
1808
1808
  paddleWidth = PADDLE_WIDTH_WIDE;
1809
1809
  paddleWidthTimer = 600;
1810
- addScorePopup(paddleX, GAME_HEIGHT - 5, "WIDE PADDLE!", "\x1B[1;92m");
1810
+ addScorePopup2(paddleX, GAME_HEIGHT - 5, "WIDE PADDLE!", "\x1B[1;92m");
1811
1811
  break;
1812
1812
  case "laser":
1813
1813
  laserActive = true;
1814
1814
  laserTimer = 400;
1815
- addScorePopup(paddleX, GAME_HEIGHT - 5, "LASER!", "\x1B[1;91m");
1815
+ addScorePopup2(paddleX, GAME_HEIGHT - 5, "LASER!", "\x1B[1;91m");
1816
1816
  break;
1817
1817
  case "slow":
1818
1818
  slowActive = true;
@@ -1821,11 +1821,11 @@ function runBreakoutGame(terminal) {
1821
1821
  b.vx *= 0.6;
1822
1822
  b.vy *= 0.6;
1823
1823
  }
1824
- addScorePopup(paddleX, GAME_HEIGHT - 5, "SLOW-MO!", "\x1B[1;93m");
1824
+ addScorePopup2(paddleX, GAME_HEIGHT - 5, "SLOW-MO!", "\x1B[1;93m");
1825
1825
  break;
1826
1826
  case "extra":
1827
1827
  lives++;
1828
- addScorePopup(paddleX, GAME_HEIGHT - 5, "+1 LIFE!", "\x1B[1;95m");
1828
+ addScorePopup2(paddleX, GAME_HEIGHT - 5, "+1 LIFE!", "\x1B[1;95m");
1829
1829
  break;
1830
1830
  }
1831
1831
  }
@@ -1901,8 +1901,8 @@ function runBreakoutGame(terminal) {
1901
1901
  brick.alive = false;
1902
1902
  const pts = brickPoints[brick.type] || 10;
1903
1903
  score += pts;
1904
- spawnParticles2(brick.x + brick.width / 2, brick.y, 6, brickColors[brick.type]);
1905
- addScorePopup(brick.x + 1, brick.y - 1, `+${pts}`, brickColors[brick.type]);
1904
+ spawnParticles3(brick.x + brick.width / 2, brick.y, 6, brickColors[brick.type]);
1905
+ addScorePopup2(brick.x + 1, brick.y - 1, `+${pts}`, brickColors[brick.type]);
1906
1906
  spawnPowerUp(brick.x + brick.width / 2, brick.y);
1907
1907
  }
1908
1908
  lasers.splice(i, 1);
@@ -1923,17 +1923,17 @@ function runBreakoutGame(terminal) {
1923
1923
  if (ball.x <= 0) {
1924
1924
  ball.x = 0;
1925
1925
  ball.vx = Math.abs(ball.vx);
1926
- spawnParticles2(0, ball.y, 3, themeColor, ["\xB7", "\u2022"]);
1926
+ spawnParticles3(0, ball.y, 3, themeColor, ["\xB7", "\u2022"]);
1927
1927
  }
1928
1928
  if (ball.x >= GAME_WIDTH - 1) {
1929
1929
  ball.x = GAME_WIDTH - 1;
1930
1930
  ball.vx = -Math.abs(ball.vx);
1931
- spawnParticles2(GAME_WIDTH - 1, ball.y, 3, themeColor, ["\xB7", "\u2022"]);
1931
+ spawnParticles3(GAME_WIDTH - 1, ball.y, 3, themeColor, ["\xB7", "\u2022"]);
1932
1932
  }
1933
1933
  if (ball.y <= 0) {
1934
1934
  ball.y = 0;
1935
1935
  ball.vy = Math.abs(ball.vy);
1936
- spawnParticles2(ball.x, 0, 3, themeColor, ["\xB7", "\u2022"]);
1936
+ spawnParticles3(ball.x, 0, 3, themeColor, ["\xB7", "\u2022"]);
1937
1937
  }
1938
1938
  const pL = paddleX - paddleWidth / 2, pR = paddleX + paddleWidth / 2, pY = GAME_HEIGHT - 2;
1939
1939
  if (ball.vy > 0 && ball.y >= pY - 0.5 && ball.y <= pY + 0.5 && ball.x >= pL - 0.5 && ball.x <= pR + 0.5) {
@@ -1944,7 +1944,7 @@ function runBreakoutGame(terminal) {
1944
1944
  ball.vx = Math.sin(angle) * newSpeed;
1945
1945
  ball.vy = -Math.cos(angle) * newSpeed;
1946
1946
  ball.y = pY - 1;
1947
- spawnParticles2(ball.x, pY, 4, themeColor, ["\u2726", "\u25CF"]);
1947
+ spawnParticles3(ball.x, pY, 4, themeColor, ["\u2726", "\u25CF"]);
1948
1948
  }
1949
1949
  for (const brick of bricks) {
1950
1950
  if (!brick.alive) continue;
@@ -1964,12 +1964,12 @@ function runBreakoutGame(terminal) {
1964
1964
  const totalPts = basePts + comboBonus;
1965
1965
  score += totalPts;
1966
1966
  const intensity = Math.min(comboCount, 8);
1967
- triggerShake(3 + intensity, 1 + Math.floor(intensity / 3));
1968
- spawnParticles2(brick.x + brick.width / 2, brick.y, 6 + intensity, brickColors[brick.type]);
1969
- addScorePopup(brick.x + 1, brick.y - 1, comboCount > 1 ? `+${totalPts}!` : `+${totalPts}`, brickColors[brick.type]);
1967
+ triggerShake2(3 + intensity, 1 + Math.floor(intensity / 3));
1968
+ spawnParticles3(brick.x + brick.width / 2, brick.y, 6 + intensity, brickColors[brick.type]);
1969
+ addScorePopup2(brick.x + 1, brick.y - 1, comboCount > 1 ? `+${totalPts}!` : `+${totalPts}`, brickColors[brick.type]);
1970
1970
  spawnPowerUp(brick.x + brick.width / 2, brick.y);
1971
1971
  } else {
1972
- spawnParticles2(ball.x, ball.y, 3, "\x1B[2m" + brickColors[brick.type], ["\xB7", "x"]);
1972
+ spawnParticles3(ball.x, ball.y, 3, "\x1B[2m" + brickColors[brick.type], ["\xB7", "x"]);
1973
1973
  }
1974
1974
  break;
1975
1975
  }
@@ -1982,12 +1982,12 @@ function runBreakoutGame(terminal) {
1982
1982
  if (lives <= 0) {
1983
1983
  gameOver = true;
1984
1984
  if (score > highScore) highScore = score;
1985
- triggerShake(20, 4);
1986
- spawnParticles2(paddleX, GAME_HEIGHT - 2, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593"]);
1985
+ triggerShake2(20, 4);
1986
+ spawnParticles3(paddleX, GAME_HEIGHT - 2, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593"]);
1987
1987
  } else {
1988
1988
  ballAttached = true;
1989
1989
  balls = [{ x: paddleX, y: GAME_HEIGHT - 3, vx: 0, vy: 0, active: true }];
1990
- triggerShake(10, 2);
1990
+ triggerShake2(10, 2);
1991
1991
  }
1992
1992
  }
1993
1993
  const aliveBricks = bricks.filter((b) => b.alive);
@@ -1996,8 +1996,8 @@ function runBreakoutGame(terminal) {
1996
1996
  gameOver = true;
1997
1997
  level++;
1998
1998
  if (score > highScore) highScore = score;
1999
- triggerShake(12, 2);
2000
- for (let i = 0; i < 5; i++) setTimeout(() => spawnParticles2(Math.random() * GAME_WIDTH, Math.random() * GAME_HEIGHT / 2, 15, brickColors[Math.floor(Math.random() * brickColors.length)], ["\u2605", "\u2726", "\u25C6", "\u25CF", "\u2727"]), i * 100);
1999
+ triggerShake2(12, 2);
2000
+ for (let i = 0; i < 5; i++) setTimeout(() => spawnParticles3(Math.random() * GAME_WIDTH, Math.random() * GAME_HEIGHT / 2, 15, brickColors[Math.floor(Math.random() * brickColors.length)], ["\u2605", "\u2726", "\u25C6", "\u25CF", "\u2727"]), i * 100);
2001
2001
  }
2002
2002
  }
2003
2003
  function render() {
@@ -4005,7 +4005,7 @@ function runCrackGame(terminal) {
4005
4005
  lng: (Math.random() * 360 - 180).toFixed(6)
4006
4006
  };
4007
4007
  }
4008
- function spawnParticles2(x, y, count, color, chars = ["\u2588", "\u2593", "\u2592", "\u2591", "\u25CF", "\u25C6", "\u2726"]) {
4008
+ function spawnParticles3(x, y, count, color, chars = ["\u2588", "\u2593", "\u2592", "\u2591", "\u25CF", "\u25C6", "\u2726"]) {
4009
4009
  for (let i = 0; i < count; i++) {
4010
4010
  particles.push({
4011
4011
  x,
@@ -4018,7 +4018,7 @@ function runCrackGame(terminal) {
4018
4018
  });
4019
4019
  }
4020
4020
  }
4021
- function addScorePopup(x, y, text, color) {
4021
+ function addScorePopup2(x, y, text, color) {
4022
4022
  scorePopups.push({ x, y, text, frames: 30, color });
4023
4023
  }
4024
4024
  const controller = {
@@ -4108,17 +4108,17 @@ function runCrackGame(terminal) {
4108
4108
  layerBreachFlash = 20;
4109
4109
  screenShake = 8;
4110
4110
  shakeIntensity = 3;
4111
- spawnParticles2(effectX, effectY, 25, "\x1B[1;32m", ["\u2588", "\u2593", "\u2726", "\u2605", "\u25C6", "\u25CF"]);
4112
- spawnParticles2(effectX - 5, effectY, 15, "\x1B[1;92m", ["\u2591", "\u2592", "\u2593"]);
4113
- spawnParticles2(effectX + 5, effectY, 15, "\x1B[1;92m", ["\u2591", "\u2592", "\u2593"]);
4111
+ spawnParticles3(effectX, effectY, 25, "\x1B[1;32m", ["\u2588", "\u2593", "\u2726", "\u2605", "\u25C6", "\u25CF"]);
4112
+ spawnParticles3(effectX - 5, effectY, 15, "\x1B[1;92m", ["\u2591", "\u2592", "\u2593"]);
4113
+ spawnParticles3(effectX + 5, effectY, 15, "\x1B[1;92m", ["\u2591", "\u2592", "\u2593"]);
4114
4114
  if (currentLayer >= totalLayers) {
4115
4115
  won = true;
4116
4116
  gameOver = true;
4117
- addScorePopup(effectX, effectY - 2, "\u25C6 SYSTEM COMPROMISED \u25C6", "\x1B[1;32m");
4117
+ addScorePopup2(effectX, effectY - 2, "\u25C6 SYSTEM COMPROMISED \u25C6", "\x1B[1;32m");
4118
4118
  for (let i = 0; i < 5; i++) {
4119
4119
  setTimeout(() => {
4120
4120
  if (running) {
4121
- spawnParticles2(
4121
+ spawnParticles3(
4122
4122
  Math.floor(cols / 4) + Math.random() * cols / 2,
4123
4123
  5 + Math.random() * 10,
4124
4124
  10,
@@ -4129,7 +4129,7 @@ function runCrackGame(terminal) {
4129
4129
  }, i * 100);
4130
4130
  }
4131
4131
  } else {
4132
- addScorePopup(effectX, effectY - 2, `LAYER ${currentLayer} BREACHED!`, "\x1B[1;32m");
4132
+ addScorePopup2(effectX, effectY - 2, `LAYER ${currentLayer} BREACHED!`, "\x1B[1;32m");
4133
4133
  currentLayer++;
4134
4134
  setTimeout(() => {
4135
4135
  if (running && !gameOver) initLayer();
@@ -4144,21 +4144,21 @@ function runCrackGame(terminal) {
4144
4144
  gameOver = true;
4145
4145
  gameOverFrame = 0;
4146
4146
  generateFakeCoords();
4147
- spawnParticles2(effectX, effectY, 20, "\x1B[1;31m", ["\u2588", "\u2593", "\u2592", "!", "@", "#"]);
4148
- addScorePopup(effectX, effectY - 2, "\u25C6 TRACED \u25C6", "\x1B[1;31m");
4147
+ spawnParticles3(effectX, effectY, 20, "\x1B[1;31m", ["\u2588", "\u2593", "\u2592", "!", "@", "#"]);
4148
+ addScorePopup2(effectX, effectY - 2, "\u25C6 TRACED \u25C6", "\x1B[1;31m");
4149
4149
  } else if (hasPartial) {
4150
4150
  addLog("partial");
4151
4151
  correctFlash = 4 + correctCount * 2;
4152
4152
  screenShake = Math.min(correctCount + 1, 4);
4153
4153
  shakeIntensity = 1;
4154
4154
  if (correctCount > 0) {
4155
- spawnParticles2(effectX, effectY, 3 + correctCount * 2, "\x1B[1;32m", ["\u2588", "\u25CF", "\u25C6"]);
4156
- addScorePopup(effectX, effectY - 1, `${correctCount} EXACT!`, "\x1B[1;32m");
4155
+ spawnParticles3(effectX, effectY, 3 + correctCount * 2, "\x1B[1;32m", ["\u2588", "\u25CF", "\u25C6"]);
4156
+ addScorePopup2(effectX, effectY - 1, `${correctCount} EXACT!`, "\x1B[1;32m");
4157
4157
  }
4158
4158
  if (presentCount > 0) {
4159
- spawnParticles2(effectX + 3, effectY, 2 + presentCount, "\x1B[1;33m", ["\u2591", "\u2592", "\u25CF"]);
4159
+ spawnParticles3(effectX + 3, effectY, 2 + presentCount, "\x1B[1;33m", ["\u2591", "\u2592", "\u25CF"]);
4160
4160
  if (correctCount === 0) {
4161
- addScorePopup(effectX, effectY - 1, `${presentCount} CLOSE`, "\x1B[1;33m");
4161
+ addScorePopup2(effectX, effectY - 1, `${presentCount} CLOSE`, "\x1B[1;33m");
4162
4162
  }
4163
4163
  }
4164
4164
  } else {
@@ -4166,8 +4166,8 @@ function runCrackGame(terminal) {
4166
4166
  wrongFlash = 6;
4167
4167
  screenShake = 3;
4168
4168
  shakeIntensity = 2;
4169
- spawnParticles2(effectX, effectY, 5, "\x1B[31m", ["\u2591", "\u2592", "\xD7", "\xB7"]);
4170
- addScorePopup(effectX, effectY - 1, "DENIED", "\x1B[2;31m");
4169
+ spawnParticles3(effectX, effectY, 5, "\x1B[31m", ["\u2591", "\u2592", "\xD7", "\xB7"]);
4170
+ addScorePopup2(effectX, effectY - 1, "DENIED", "\x1B[2;31m");
4171
4171
  }
4172
4172
  currentGuess = "";
4173
4173
  }
@@ -4441,7 +4441,7 @@ function runCrackGame(terminal) {
4441
4441
  borderFlash = 3;
4442
4442
  wrongFlash = 3;
4443
4443
  if (timeLeft % 2 === 0) {
4444
- spawnParticles2(Math.random() * cols, 2, 3, "\x1B[1;31m", ["!", "\u2593", "\u2591"]);
4444
+ spawnParticles3(Math.random() * cols, 2, 3, "\x1B[1;31m", ["!", "\u2593", "\u2591"]);
4445
4445
  }
4446
4446
  }
4447
4447
  if (timeLeft <= 0) {
@@ -4455,8 +4455,8 @@ function runCrackGame(terminal) {
4455
4455
  borderFlash = 25;
4456
4456
  const centerX = Math.floor(cols / 2);
4457
4457
  const centerY = Math.floor(rows / 2);
4458
- spawnParticles2(centerX, centerY, 30, "\x1B[1;31m", ["\u2588", "\u2593", "\u2592", "!", "@", "#", "\xD7"]);
4459
- addScorePopup(centerX, centerY - 3, "\u25C6\u25C6 TRACE COMPLETE \u25C6\u25C6", "\x1B[1;31m");
4458
+ spawnParticles3(centerX, centerY, 30, "\x1B[1;31m", ["\u2588", "\u2593", "\u2592", "!", "@", "#", "\xD7"]);
4459
+ addScorePopup2(centerX, centerY - 3, "\u25C6\u25C6 TRACE COMPLETE \u25C6\u25C6", "\x1B[1;31m");
4460
4460
  }
4461
4461
  }
4462
4462
  if (gameOver) {
@@ -4603,7 +4603,7 @@ function runCrackGame(terminal) {
4603
4603
  currentGuess += key.toUpperCase();
4604
4604
  const inputX = 4 + currentGuess.length * 2;
4605
4605
  const inputY = 12 + guesses.length;
4606
- spawnParticles2(inputX, inputY, 1, themeColor, ["\xB7", "\u25CF"]);
4606
+ spawnParticles3(inputX, inputY, 1, themeColor, ["\xB7", "\u25CF"]);
4607
4607
  if (currentGuess.length === 5) {
4608
4608
  screenShake = 2;
4609
4609
  shakeIntensity = 1;
@@ -4691,7 +4691,7 @@ function runFroggerGame(terminal) {
4691
4691
  "\u2588\u2580\u2580 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2580 \u2588\u2580\u2580 \u2588\u2580\u2588",
4692
4692
  "\u2588\u2580 \u2588\u2580\u2584 \u2588\u2584\u2588 \u2588\u2584\u2588 \u2588\u2584\u2588 \u2588\u2588\u2584 \u2588\u2580\u2584"
4693
4693
  ];
4694
- function spawnParticles2(x, y, count, color, chars) {
4694
+ function spawnParticles3(x, y, count, color, chars) {
4695
4695
  for (let i = 0; i < count; i++) {
4696
4696
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
4697
4697
  const speed = 0.1 + Math.random() * 0.15;
@@ -4708,13 +4708,13 @@ function runFroggerGame(terminal) {
4708
4708
  }
4709
4709
  }
4710
4710
  function spawnSplash2(x, y) {
4711
- spawnParticles2(x, y, 8, "\x1B[96m", ["~", "\u223C", "\u2248", "\u25CB"]);
4711
+ spawnParticles3(x, y, 8, "\x1B[96m", ["~", "\u223C", "\u2248", "\u25CB"]);
4712
4712
  }
4713
4713
  function spawnCrash(x, y) {
4714
- spawnParticles2(x, y, 10, "\x1B[91m", ["*", "x", "\xD7", "#"]);
4714
+ spawnParticles3(x, y, 10, "\x1B[91m", ["*", "x", "\xD7", "#"]);
4715
4715
  }
4716
4716
  function spawnSuccess(x, y) {
4717
- spawnParticles2(x, y, 12, "\x1B[92m", ["*", "+", "\u2666", "\u25C7"]);
4717
+ spawnParticles3(x, y, 12, "\x1B[92m", ["*", "+", "\u2666", "\u25C7"]);
4718
4718
  }
4719
4719
  function calculateLilyPads() {
4720
4720
  lilyPadX = [];
@@ -5520,7 +5520,7 @@ function runHangmanGame(terminal) {
5520
5520
  winAnimation = 0;
5521
5521
  loseAnimation = 0;
5522
5522
  }
5523
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
5523
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
5524
5524
  for (let i = 0; i < count; i++) {
5525
5525
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
5526
5526
  const speed = 0.3 + Math.random() * 0.5;
@@ -5535,7 +5535,7 @@ function runHangmanGame(terminal) {
5535
5535
  });
5536
5536
  }
5537
5537
  }
5538
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
5538
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
5539
5539
  scorePopups.push({ x, y, text, frames: 22, color });
5540
5540
  }
5541
5541
  function getDisplayWord() {
@@ -5562,12 +5562,12 @@ function runHangmanGame(terminal) {
5562
5562
  screenShake = 2 + Math.floor(effectIntensity / 2);
5563
5563
  const centerX = Math.floor(terminal.cols / 2);
5564
5564
  const wordY = gameTop + 9;
5565
- spawnParticles2(centerX, wordY, 4 + effectIntensity, "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
5565
+ spawnParticles3(centerX, wordY, 4 + effectIntensity, "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
5566
5566
  const popupText = letterCount > 1 ? `+${points}!` : `+${points}`;
5567
5567
  const popupColor = correctStreak >= 3 ? "\x1B[1;93m" : "\x1B[1;92m";
5568
- addScorePopup(centerX - 2, wordY - 2, popupText, popupColor);
5568
+ addScorePopup2(centerX - 2, wordY - 2, popupText, popupColor);
5569
5569
  if (correctStreak >= 3) {
5570
- addScorePopup(centerX - 4, wordY - 4, `${correctStreak}x STREAK!`, "\x1B[1;96m");
5570
+ addScorePopup2(centerX - 4, wordY - 4, `${correctStreak}x STREAK!`, "\x1B[1;96m");
5571
5571
  }
5572
5572
  if (checkWin()) {
5573
5573
  won = true;
@@ -5578,9 +5578,9 @@ function runHangmanGame(terminal) {
5578
5578
  score += bonus;
5579
5579
  winAnimation = 30;
5580
5580
  screenShake = 8;
5581
- spawnParticles2(centerX, wordY, 15, "\x1B[1;93m", ["\u2605", "\u2726", "\u2666", "\u25C6", "\u25CF"]);
5581
+ spawnParticles3(centerX, wordY, 15, "\x1B[1;93m", ["\u2605", "\u2726", "\u2666", "\u25C6", "\u25CF"]);
5582
5582
  if (bonus > 0) {
5583
- addScorePopup(centerX - 3, wordY + 2, `+${bonus} BONUS!`, "\x1B[1;93m");
5583
+ addScorePopup2(centerX - 3, wordY + 2, `+${bonus} BONUS!`, "\x1B[1;93m");
5584
5584
  }
5585
5585
  }
5586
5586
  } else {
@@ -5590,14 +5590,14 @@ function runHangmanGame(terminal) {
5590
5590
  screenShake = 4 + wrongGuesses;
5591
5591
  const hangmanX = gameLeft + 6;
5592
5592
  const hangmanY = gameTop + 4;
5593
- spawnParticles2(hangmanX, hangmanY, 5, "\x1B[1;91m", ["\u2717", "\xD7", "\xB7", "\u25CB"]);
5593
+ spawnParticles3(hangmanX, hangmanY, 5, "\x1B[1;91m", ["\u2717", "\xD7", "\xB7", "\u25CB"]);
5594
5594
  if (wrongGuesses >= MAX_WRONG) {
5595
5595
  gameOver = true;
5596
5596
  streak = 0;
5597
5597
  loseAnimation = 40;
5598
5598
  screenShake = 20;
5599
- spawnParticles2(hangmanX, hangmanY, 12, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2592", "\u2591"]);
5600
- addScorePopup(hangmanX - 3, hangmanY - 2, "FAILED!", "\x1B[1;91m");
5599
+ spawnParticles3(hangmanX, hangmanY, 12, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2592", "\u2591"]);
5600
+ addScorePopup2(hangmanX - 3, hangmanY - 2, "FAILED!", "\x1B[1;91m");
5601
5601
  }
5602
5602
  }
5603
5603
  }
@@ -5953,7 +5953,7 @@ function runMinesweeperGame(terminal) {
5953
5953
  "\u2588\u2584 \u2584\u2588 \u2588 \u2588\u2584 \u2588 \u2588\u2580\u2580 \u2588\u2580\u2580 \u2588 \u2588 \u2588 \u2588\u2580\u2580 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588",
5954
5954
  "\u2588 \u2580 \u2588 \u2588 \u2588 \u2580\u2588 \u2588\u2588\u2584 \u2584\u2584\u2588 \u2580\u2584\u2580\u2584\u2580 \u2588\u2588\u2584 \u2588\u2588\u2584 \u2588\u2580\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584"
5955
5955
  ];
5956
- function spawnParticles2(x, y, count, color, chars = ["*", "+", ".", "o"]) {
5956
+ function spawnParticles3(x, y, count, color, chars = ["*", "+", ".", "o"]) {
5957
5957
  for (let i = 0; i < count; i++) {
5958
5958
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
5959
5959
  const speed = 0.2 + Math.random() * 0.4;
@@ -5985,10 +5985,10 @@ function runMinesweeperGame(terminal) {
5985
5985
  });
5986
5986
  }
5987
5987
  }
5988
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
5988
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
5989
5989
  scorePopups.push({ x, y, text, frames: 20, color });
5990
5990
  }
5991
- function triggerShake(frames, intensity) {
5991
+ function triggerShake2(frames, intensity) {
5992
5992
  shakeFrames = frames;
5993
5993
  shakeIntensity = intensity;
5994
5994
  }
@@ -6075,14 +6075,14 @@ function runMinesweeperGame(terminal) {
6075
6075
  if (cell.isMine) {
6076
6076
  gameOver = true;
6077
6077
  won = false;
6078
- triggerShake(25, 4);
6078
+ triggerShake2(25, 4);
6079
6079
  spawnExplosion(x * 2 + 1, y);
6080
- addScorePopup(x * 2, y - 1, "MALWARE!", "\x1B[1;91m");
6080
+ addScorePopup2(x * 2, y - 1, "MALWARE!", "\x1B[1;91m");
6081
6081
  revealAllMines();
6082
6082
  return;
6083
6083
  }
6084
6084
  if (!isChain) {
6085
- spawnParticles2(x * 2 + 1, y, 4, themeColor, [".", "*", "+"]);
6085
+ spawnParticles3(x * 2 + 1, y, 4, themeColor, [".", "*", "+"]);
6086
6086
  }
6087
6087
  if (cell.adjacentMines === 0) {
6088
6088
  for (let dy = -1; dy <= 1; dy++) {
@@ -6105,7 +6105,7 @@ function runMinesweeperGame(terminal) {
6105
6105
  cell.isFlagged = !cell.isFlagged;
6106
6106
  if (cell.isFlagged) {
6107
6107
  flagsPlaced++;
6108
- spawnParticles2(x * 2 + 1, y, 3, "\x1B[1;93m", ["!", "^", "*"]);
6108
+ spawnParticles3(x * 2 + 1, y, 3, "\x1B[1;93m", ["!", "^", "*"]);
6109
6109
  } else {
6110
6110
  flagsPlaced--;
6111
6111
  }
@@ -6124,13 +6124,13 @@ function runMinesweeperGame(terminal) {
6124
6124
  if (cellsRevealed >= totalNonMines) {
6125
6125
  gameOver = true;
6126
6126
  won = true;
6127
- triggerShake(10, 2);
6127
+ triggerShake2(10, 2);
6128
6128
  for (let i = 0; i < 5; i++) {
6129
6129
  const x = Math.floor(Math.random() * difficulty.width);
6130
6130
  const y = Math.floor(Math.random() * difficulty.height);
6131
- spawnParticles2(x * 2 + 1, y, 6, "\x1B[1;92m", ["*", "+", "#"]);
6131
+ spawnParticles3(x * 2 + 1, y, 6, "\x1B[1;92m", ["*", "+", "#"]);
6132
6132
  }
6133
- addScorePopup(difficulty.width, difficulty.height / 2, "GRID SECURE!", "\x1B[1;92m");
6133
+ addScorePopup2(difficulty.width, difficulty.height / 2, "GRID SECURE!", "\x1B[1;92m");
6134
6134
  }
6135
6135
  }
6136
6136
  function update() {
@@ -6600,7 +6600,7 @@ function runPongGame(terminal) {
6600
6600
  rallyCount = 0;
6601
6601
  wallHitFlash = 0;
6602
6602
  }
6603
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
6603
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
6604
6604
  for (let i = 0; i < count; i++) {
6605
6605
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
6606
6606
  const speed = 0.2 + Math.random() * 0.4;
@@ -6615,7 +6615,7 @@ function runPongGame(terminal) {
6615
6615
  });
6616
6616
  }
6617
6617
  }
6618
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
6618
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
6619
6619
  scorePopups.push({ x, y, text, frames: 20, color });
6620
6620
  }
6621
6621
  function createGoalExplosion(side) {
@@ -6868,7 +6868,7 @@ function runPongGame(terminal) {
6868
6868
  ballVY = -ballVY;
6869
6869
  ballY = Math.max(0, Math.min(GAME_HEIGHT - 1, ballY));
6870
6870
  wallHitFlash = 6;
6871
- spawnParticles2(ballX, ballY <= 0 ? 1 : GAME_HEIGHT - 2, 4, "\x1B[1;93m", ["\xB7", "\u2022", "\u25CB"]);
6871
+ spawnParticles3(ballX, ballY <= 0 ? 1 : GAME_HEIGHT - 2, 4, "\x1B[1;93m", ["\xB7", "\u2022", "\u25CB"]);
6872
6872
  }
6873
6873
  if (ballX <= PADDLE_OFFSET + 1 && ballX >= PADDLE_OFFSET) {
6874
6874
  if (ballY >= playerY && ballY <= playerY + PADDLE_HEIGHT) {
@@ -6883,9 +6883,9 @@ function runPongGame(terminal) {
6883
6883
  const effectIntensity = Math.min(rallyCount, 10);
6884
6884
  paddleHitFlash = 8;
6885
6885
  if (rallyCount >= 5) screenShake = Math.floor(effectIntensity / 4);
6886
- spawnParticles2(PADDLE_OFFSET + 2, ballY, 4 + Math.floor(effectIntensity / 2), themeColor, ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6886
+ spawnParticles3(PADDLE_OFFSET + 2, ballY, 4 + Math.floor(effectIntensity / 2), themeColor, ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6887
6887
  if (rallyCount >= 3 && rallyCount % 3 === 0) {
6888
- addScorePopup(PADDLE_OFFSET + 4, ballY - 1, `${rallyCount}!`, "\x1B[1;96m");
6888
+ addScorePopup2(PADDLE_OFFSET + 4, ballY - 1, `${rallyCount}!`, "\x1B[1;96m");
6889
6889
  }
6890
6890
  }
6891
6891
  }
@@ -6902,9 +6902,9 @@ function runPongGame(terminal) {
6902
6902
  const effectIntensity = Math.min(rallyCount, 10);
6903
6903
  paddleHitFlash = 8;
6904
6904
  if (rallyCount >= 5) screenShake = Math.floor(effectIntensity / 4);
6905
- spawnParticles2(GAME_WIDTH - PADDLE_OFFSET - 2, ballY, 4 + Math.floor(effectIntensity / 2), "\x1B[1;91m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6905
+ spawnParticles3(GAME_WIDTH - PADDLE_OFFSET - 2, ballY, 4 + Math.floor(effectIntensity / 2), "\x1B[1;91m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6906
6906
  if (rallyCount >= 3 && rallyCount % 3 === 0) {
6907
- addScorePopup(GAME_WIDTH - PADDLE_OFFSET - 6, ballY - 1, `${rallyCount}!`, "\x1B[1;91m");
6907
+ addScorePopup2(GAME_WIDTH - PADDLE_OFFSET - 6, ballY - 1, `${rallyCount}!`, "\x1B[1;91m");
6908
6908
  }
6909
6909
  }
6910
6910
  }
@@ -6916,13 +6916,13 @@ function runPongGame(terminal) {
6916
6916
  screenShake = 8;
6917
6917
  goalFlashSide = "left";
6918
6918
  createGoalExplosion("left");
6919
- addScorePopup(GAME_WIDTH / 2 - 3, GAME_HEIGHT / 2 - 2, "CPU +1", "\x1B[1;91m");
6920
- spawnParticles2(2, ballY, 10, "\x1B[1;91m", ["\u2717", "\xD7", "\u2592", "\u2591"]);
6919
+ addScorePopup2(GAME_WIDTH / 2 - 3, GAME_HEIGHT / 2 - 2, "CPU +1", "\x1B[1;91m");
6920
+ spawnParticles3(2, ballY, 10, "\x1B[1;91m", ["\u2717", "\xD7", "\u2592", "\u2591"]);
6921
6921
  if (aiScore >= WIN_SCORE) {
6922
6922
  gameOver = true;
6923
6923
  scoreFreeze = 0;
6924
6924
  screenShake = 15;
6925
- spawnParticles2(GAME_WIDTH / 2, GAME_HEIGHT / 2, 20, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593"]);
6925
+ spawnParticles3(GAME_WIDTH / 2, GAME_HEIGHT / 2, 20, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593"]);
6926
6926
  } else {
6927
6927
  resetBall(true);
6928
6928
  }
@@ -6935,13 +6935,13 @@ function runPongGame(terminal) {
6935
6935
  goalFlashSide = "right";
6936
6936
  createGoalExplosion("right");
6937
6937
  const rallyBonus = rallyCount >= 5 ? ` (${rallyCount} rally!)` : "";
6938
- addScorePopup(GAME_WIDTH / 2 - 2, GAME_HEIGHT / 2 - 2, `+1${rallyBonus}`, "\x1B[1;92m");
6939
- spawnParticles2(GAME_WIDTH - 3, ballY, 10, "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6938
+ addScorePopup2(GAME_WIDTH / 2 - 2, GAME_HEIGHT / 2 - 2, `+1${rallyBonus}`, "\x1B[1;92m");
6939
+ spawnParticles3(GAME_WIDTH - 3, ballY, 10, "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
6940
6940
  if (playerScore >= WIN_SCORE) {
6941
6941
  gameOver = true;
6942
6942
  scoreFreeze = 0;
6943
6943
  screenShake = 15;
6944
- spawnParticles2(GAME_WIDTH / 2, GAME_HEIGHT / 2, 20, "\x1B[1;93m", ["\u2605", "\u2726", "\u2666", "\u25C6"]);
6944
+ spawnParticles3(GAME_WIDTH / 2, GAME_HEIGHT / 2, 20, "\x1B[1;93m", ["\u2605", "\u2726", "\u2666", "\u25C6"]);
6945
6945
  } else {
6946
6946
  resetBall(false);
6947
6947
  }
@@ -7174,7 +7174,7 @@ function runRunnerGame(terminal) {
7174
7174
  const factor = Math.pow(t, 0.6);
7175
7175
  return Math.floor(ROAD_WIDTH_TOP + (ROAD_WIDTH_BOTTOM - ROAD_WIDTH_TOP) * factor);
7176
7176
  }
7177
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
7177
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
7178
7178
  for (let i = 0; i < count; i++) {
7179
7179
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
7180
7180
  const speed2 = 0.3 + Math.random() * 0.5;
@@ -7189,7 +7189,7 @@ function runRunnerGame(terminal) {
7189
7189
  });
7190
7190
  }
7191
7191
  }
7192
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
7192
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
7193
7193
  scorePopups.push({ x, y, text, frames: 20, color });
7194
7194
  }
7195
7195
  function initGame() {
@@ -7312,8 +7312,8 @@ function runRunnerGame(terminal) {
7312
7312
  const roadLeft = centerX - Math.floor(roadWidth / 2);
7313
7313
  const coinX = roadLeft + Math.floor((coin.lane + 0.5) / LANES * roadWidth);
7314
7314
  const coinY = gameTop + TRACK_HEIGHT - Math.floor(coin.y);
7315
- spawnParticles2(coinX - gameLeft, coinY - gameTop, 6, "\x1B[1;93m", ["\u25C6", "\u2605", "\u25CF", "\u2666"]);
7316
- addScorePopup(coinX, coinY, `+${points}`, "\x1B[1;93m");
7315
+ spawnParticles3(coinX - gameLeft, coinY - gameTop, 6, "\x1B[1;93m", ["\u25C6", "\u2605", "\u25CF", "\u2666"]);
7316
+ addScorePopup2(coinX, coinY, `+${points}`, "\x1B[1;93m");
7317
7317
  } else {
7318
7318
  newCoins.push(coin);
7319
7319
  }
@@ -7703,7 +7703,7 @@ function runRunnerGame(terminal) {
7703
7703
  const roadWidth = getRoadWidth(TRACK_HEIGHT - 1);
7704
7704
  const roadLeft = centerX - Math.floor(roadWidth / 2);
7705
7705
  const playerCenter = roadLeft + Math.floor((playerLane + 0.5) / LANES * roadWidth);
7706
- spawnParticles2(playerCenter - gameLeft, TRACK_HEIGHT - 2, 4, "\x1B[96m", ["\u2726", "\u2605", "\u25C7"]);
7706
+ spawnParticles3(playerCenter - gameLeft, TRACK_HEIGHT - 2, 4, "\x1B[96m", ["\u2726", "\u2605", "\u25C7"]);
7707
7707
  }
7708
7708
  collectCoins();
7709
7709
  if (checkCollision()) {
@@ -7716,7 +7716,7 @@ function runRunnerGame(terminal) {
7716
7716
  const roadWidth = getRoadWidth(TRACK_HEIGHT - 1);
7717
7717
  const roadLeft = centerX - Math.floor(roadWidth / 2);
7718
7718
  const playerCenter = roadLeft + Math.floor((playerLane + 0.5) / LANES * roadWidth);
7719
- spawnParticles2(playerCenter - gameLeft, TRACK_HEIGHT - 1, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593", "\u2591"]);
7719
+ spawnParticles3(playerCenter - gameLeft, TRACK_HEIGHT - 1, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593", "\u2591"]);
7720
7720
  return;
7721
7721
  }
7722
7722
  distance += 0.18 * speed;
@@ -7950,7 +7950,7 @@ function runSimonGame(terminal) {
7950
7950
  "\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588 \u2588\u2580\u2580\u2588 \u2580\u2588\u2580\u2588 \u2588\u2580\u2588 \u2588\u2580\u2588",
7951
7951
  "\u2588\u2580\u2588 \u2588 \u2588\u2580\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584 \u2584\u2584\u2588 \u2588 \u2588\u2588\u2584 \u2588 \u2588 \u2588 \u2588\u2580\u2584 \u2588 \u2588"
7952
7952
  ];
7953
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
7953
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
7954
7954
  for (let i = 0; i < count; i++) {
7955
7955
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
7956
7956
  const speed = 0.3 + Math.random() * 0.5;
@@ -7985,12 +7985,12 @@ function runSimonGame(terminal) {
7985
7985
  x = centerX - offset * 2;
7986
7986
  break;
7987
7987
  }
7988
- spawnParticles2(x, y, 8, QUADRANT_COLORS[quadrant], ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
7988
+ spawnParticles3(x, y, 8, QUADRANT_COLORS[quadrant], ["\u2726", "\u2605", "\u25C6", "\u25CF"]);
7989
7989
  }
7990
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
7990
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
7991
7991
  scorePopups.push({ x, y, text, frames: 20, color });
7992
7992
  }
7993
- function triggerShake(frames, intensity) {
7993
+ function triggerShake2(frames, intensity) {
7994
7994
  shakeFrames = frames;
7995
7995
  shakeIntensity = intensity;
7996
7996
  }
@@ -8047,12 +8047,12 @@ function runSimonGame(terminal) {
8047
8047
  score += currentRound * 10;
8048
8048
  const centerX = GAME_WIDTH / 2;
8049
8049
  const centerY = GAME_HEIGHT / 2;
8050
- addScorePopup(centerX - 2, centerY - 2, `+${currentRound * 10}`, "\x1B[1;92m");
8051
- spawnParticles2(centerX, centerY, 20, "\x1B[1;92m", ["\u2726", "\u2605", "\u2606", "\u25C8"]);
8050
+ addScorePopup2(centerX - 2, centerY - 2, `+${currentRound * 10}`, "\x1B[1;92m");
8051
+ spawnParticles3(centerX, centerY, 20, "\x1B[1;92m", ["\u2726", "\u2605", "\u2606", "\u25C8"]);
8052
8052
  statusMessage = "PATTERN ACCEPTED";
8053
8053
  statusColor = "\x1B[1;92m";
8054
8054
  statusBlink = true;
8055
- triggerShake(6, 1);
8055
+ triggerShake2(6, 1);
8056
8056
  }
8057
8057
  } else {
8058
8058
  phase = "failure";
@@ -8064,10 +8064,10 @@ function runSimonGame(terminal) {
8064
8064
  statusMessage = "ACCESS DENIED";
8065
8065
  statusColor = "\x1B[1;91m";
8066
8066
  statusBlink = true;
8067
- triggerShake(15, 3);
8067
+ triggerShake2(15, 3);
8068
8068
  const centerX = GAME_WIDTH / 2;
8069
8069
  const centerY = GAME_HEIGHT / 2;
8070
- spawnParticles2(centerX, centerY, 15, "\x1B[1;91m", ["\u2717", "\u2716", "\xD7", "\u2573"]);
8070
+ spawnParticles3(centerX, centerY, 15, "\x1B[1;91m", ["\u2717", "\u2716", "\xD7", "\u2573"]);
8071
8071
  }
8072
8072
  }
8073
8073
  function update() {
@@ -8545,7 +8545,7 @@ function runSnakeGame(terminal) {
8545
8545
  attempts++;
8546
8546
  } while (snake.some((s) => s.x === food.x && s.y === food.y) && attempts < 100);
8547
8547
  }
8548
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
8548
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
8549
8549
  for (let i = 0; i < count; i++) {
8550
8550
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
8551
8551
  const speed = 0.3 + Math.random() * 0.4;
@@ -8561,7 +8561,7 @@ function runSnakeGame(terminal) {
8561
8561
  });
8562
8562
  }
8563
8563
  }
8564
- function updateParticles2() {
8564
+ function updateParticles3() {
8565
8565
  for (let i = particles.length - 1; i >= 0; i--) {
8566
8566
  const p = particles[i];
8567
8567
  p.x += p.vx;
@@ -8586,7 +8586,7 @@ function runSnakeGame(terminal) {
8586
8586
  scorePopup.y -= 0.3;
8587
8587
  if (scorePopup.frames <= 0) scorePopup = null;
8588
8588
  }
8589
- updateParticles2();
8589
+ updateParticles3();
8590
8590
  let renderGameLeft = gameLeft;
8591
8591
  let renderGameTop = gameTop;
8592
8592
  if (shakeFrames > 0) {
@@ -8737,7 +8737,7 @@ function runSnakeGame(terminal) {
8737
8737
  shakeFrames = 20;
8738
8738
  shakeIntensity = 3;
8739
8739
  deathFlashFrames = 30;
8740
- spawnParticles2(snake[0].x, snake[0].y, 12, "\x1B[1;31m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
8740
+ spawnParticles3(snake[0].x, snake[0].y, 12, "\x1B[1;31m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
8741
8741
  comboCount = 0;
8742
8742
  return;
8743
8743
  }
@@ -8747,7 +8747,7 @@ function runSnakeGame(terminal) {
8747
8747
  shakeFrames = 20;
8748
8748
  shakeIntensity = 3;
8749
8749
  deathFlashFrames = 30;
8750
- spawnParticles2(snake[0].x, snake[0].y, 12, "\x1B[1;31m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
8750
+ spawnParticles3(snake[0].x, snake[0].y, 12, "\x1B[1;31m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
8751
8751
  comboCount = 0;
8752
8752
  return;
8753
8753
  }
@@ -8775,7 +8775,7 @@ function runSnakeGame(terminal) {
8775
8775
  frames: 20
8776
8776
  };
8777
8777
  const particleCount = 4 + Math.min(comboCount * 2, 10);
8778
- spawnParticles2(food.x, food.y, particleCount, "\x1B[1;33m", ["\u2726", "\u2605", "\u25C6", "\u2666"]);
8778
+ spawnParticles3(food.x, food.y, particleCount, "\x1B[1;33m", ["\u2726", "\u2605", "\u25C6", "\u2666"]);
8779
8779
  spawnFood();
8780
8780
  } else {
8781
8781
  snake.pop();
@@ -9000,7 +9000,7 @@ function runSpaceInvadersGame(terminal) {
9000
9000
  ["|=|", "|#|"]
9001
9001
  // Type 2 - top rows
9002
9002
  ];
9003
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
9003
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
9004
9004
  for (let i = 0; i < count; i++) {
9005
9005
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
9006
9006
  const speed = 0.2 + Math.random() * 0.4;
@@ -9015,7 +9015,7 @@ function runSpaceInvadersGame(terminal) {
9015
9015
  });
9016
9016
  }
9017
9017
  }
9018
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
9018
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
9019
9019
  scorePopups.push({ x, y, text, frames: 18, color });
9020
9020
  }
9021
9021
  function initGame() {
@@ -9340,10 +9340,10 @@ function runSpaceInvadersGame(terminal) {
9340
9340
  shakeFrames = 3 + Math.min(killStreakCount, 5);
9341
9341
  shakeIntensity = 1 + Math.floor(killStreakCount / 3);
9342
9342
  const invaderColors = ["\x1B[1;92m", "\x1B[1;93m", "\x1B[1;95m"];
9343
- spawnParticles2(invader.x + 1, invader.y, 6 + Math.min(killStreakCount, 6), invaderColors[invader.type], ["\u2726", "\u2605", "\u25C6", "\xD7"]);
9343
+ spawnParticles3(invader.x + 1, invader.y, 6 + Math.min(killStreakCount, 6), invaderColors[invader.type], ["\u2726", "\u2605", "\u25C6", "\xD7"]);
9344
9344
  const popupText = killStreakCount >= 3 ? `+${totalScore}!` : `+${totalScore}`;
9345
9345
  const popupColor = killStreakCount >= 3 ? "\x1B[1;91m" : "\x1B[1;93m";
9346
- addScorePopup(invader.x + 1, invader.y - 1, popupText, popupColor);
9346
+ addScorePopup2(invader.x + 1, invader.y - 1, popupText, popupColor);
9347
9347
  }
9348
9348
  }
9349
9349
  }
@@ -9371,13 +9371,13 @@ function runSpaceInvadersGame(terminal) {
9371
9371
  shakeIntensity = 3;
9372
9372
  hitFlashFrames = 15;
9373
9373
  killStreakCount = 0;
9374
- spawnParticles2(Math.floor(playerX), playerY, 10, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
9374
+ spawnParticles3(Math.floor(playerX), playerY, 10, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2592"]);
9375
9375
  if (lives <= 0) {
9376
9376
  gameOver = true;
9377
9377
  gameOverFlashFrames = 30;
9378
9378
  shakeFrames = 20;
9379
9379
  shakeIntensity = 4;
9380
- spawnParticles2(Math.floor(playerX), playerY, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593", "\u2591"]);
9380
+ spawnParticles3(Math.floor(playerX), playerY, 15, "\x1B[1;91m", ["\u2717", "\u2620", "\xD7", "\u2593", "\u2591"]);
9381
9381
  if (score > highScore) highScore = score;
9382
9382
  }
9383
9383
  }
@@ -10356,7 +10356,7 @@ function runTowerGame(terminal) {
10356
10356
  "\u2580\u2588\u2580 \u2588\u2580\u2588 \u2588 \u2588 \u2588 \u2588\u2580\u2580 \u2588\u2580\u2588",
10357
10357
  " \u2588 \u2588\u2584\u2588 \u2580\u2584\u2580\u2584\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584"
10358
10358
  ];
10359
- function spawnParticles2(x, y, count, color) {
10359
+ function spawnParticles3(x, y, count, color) {
10360
10360
  const chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"];
10361
10361
  for (let i = 0; i < count; i++) {
10362
10362
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
@@ -10383,10 +10383,10 @@ function runTowerGame(terminal) {
10383
10383
  vy: 0.1
10384
10384
  });
10385
10385
  }
10386
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
10386
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
10387
10387
  scorePopups.push({ x, y, text, frames: 24, color });
10388
10388
  }
10389
- function triggerShake(frames, intensity) {
10389
+ function triggerShake2(frames, intensity) {
10390
10390
  shakeFrames = frames;
10391
10391
  shakeIntensity = intensity;
10392
10392
  }
@@ -10446,8 +10446,8 @@ function runTowerGame(terminal) {
10446
10446
  gameOver = true;
10447
10447
  if (score > highScore) highScore = score;
10448
10448
  if (perfectCombo > maxCombo) maxCombo = perfectCombo;
10449
- spawnParticles2(dropX + dropWidth / 2, landY, 15, "\x1B[91m");
10450
- triggerShake(10, 3);
10449
+ spawnParticles3(dropX + dropWidth / 2, landY, 15, "\x1B[91m");
10450
+ triggerShake2(10, 3);
10451
10451
  return;
10452
10452
  }
10453
10453
  const isPerfect = Math.abs(dropLeft - topLeft) < PERFECT_THRESHOLD && Math.abs(dropRight - topRight) < PERFECT_THRESHOLD;
@@ -10457,34 +10457,34 @@ function runTowerGame(terminal) {
10457
10457
  const comboBonus = Math.min(perfectCombo * 5, 50);
10458
10458
  points += 10 + comboBonus;
10459
10459
  flashFrames = 6;
10460
- spawnParticles2(dropX + dropWidth / 2, landY, 12, "\x1B[93m");
10460
+ spawnParticles3(dropX + dropWidth / 2, landY, 12, "\x1B[93m");
10461
10461
  const popupText = perfectCombo > 1 ? `PERFECT! x${perfectCombo}` : "PERFECT!";
10462
- addScorePopup(
10462
+ addScorePopup2(
10463
10463
  Math.floor(GAME_WIDTH / 2) - Math.floor(popupText.length / 2),
10464
10464
  Math.floor(GAME_HEIGHT / 2),
10465
10465
  popupText,
10466
10466
  perfectCombo > 3 ? "\x1B[1;91m" : "\x1B[1;93m"
10467
10467
  );
10468
- triggerShake(4, 1);
10468
+ triggerShake2(4, 1);
10469
10469
  } else {
10470
10470
  if (perfectCombo > maxCombo) maxCombo = perfectCombo;
10471
10471
  perfectCombo = 0;
10472
10472
  if (dropLeft < topLeft) {
10473
10473
  const overhangWidth = topLeft - dropLeft;
10474
10474
  spawnFallingPiece(dropLeft, landY, overhangWidth, dropColor);
10475
- spawnParticles2(dropLeft + overhangWidth / 2, landY, 5, dropColor);
10475
+ spawnParticles3(dropLeft + overhangWidth / 2, landY, 5, dropColor);
10476
10476
  }
10477
10477
  if (dropRight > topRight) {
10478
10478
  const overhangWidth = dropRight - topRight;
10479
10479
  spawnFallingPiece(topRight, landY, overhangWidth, dropColor);
10480
- spawnParticles2(topRight + overhangWidth / 2, landY, 5, dropColor);
10480
+ spawnParticles3(topRight + overhangWidth / 2, landY, 5, dropColor);
10481
10481
  }
10482
- triggerShake(3, 1);
10482
+ triggerShake2(3, 1);
10483
10483
  }
10484
10484
  score += points;
10485
10485
  height++;
10486
10486
  if (points > 10) {
10487
- addScorePopup(overlapLeft + overlapWidth / 2, landY - 1, `+${points}`, "\x1B[92m");
10487
+ addScorePopup2(overlapLeft + overlapWidth / 2, landY - 1, `+${points}`, "\x1B[92m");
10488
10488
  }
10489
10489
  tower.push({
10490
10490
  x: overlapLeft,
@@ -10850,7 +10850,7 @@ function runTronGame(terminal) {
10850
10850
  "\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2580\u2588\u2580 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588\u2584 \u2588",
10851
10851
  "\u2588\u2580\u2588 \u2588 \u2588\u2580\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584 \u2588 \u2588\u2580\u2584 \u2588\u2584\u2588 \u2588 \u2580\u2588"
10852
10852
  ];
10853
- function spawnParticles2(x, y, count, color, chars = ["\u2591", "\u2592", "\u2593", "\u2588", "\u25C6"]) {
10853
+ function spawnParticles3(x, y, count, color, chars = ["\u2591", "\u2592", "\u2593", "\u2588", "\u25C6"]) {
10854
10854
  for (let i = 0; i < count; i++) {
10855
10855
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
10856
10856
  const speed = 0.3 + Math.random() * 0.5;
@@ -10866,10 +10866,10 @@ function runTronGame(terminal) {
10866
10866
  }
10867
10867
  }
10868
10868
  function spawnExplosion(x, y, color) {
10869
- spawnParticles2(x, y, 20, color, ["\u2717", "\xD7", "\u2591", "\u2592", "\u2593", "\u2588"]);
10870
- triggerShake(12, 3);
10869
+ spawnParticles3(x, y, 20, color, ["\u2717", "\xD7", "\u2591", "\u2592", "\u2593", "\u2588"]);
10870
+ triggerShake2(12, 3);
10871
10871
  }
10872
- function triggerShake(frames, intensity) {
10872
+ function triggerShake2(frames, intensity) {
10873
10873
  shakeFrames = frames;
10874
10874
  shakeIntensity = intensity;
10875
10875
  }
@@ -11084,7 +11084,7 @@ function runTronGame(terminal) {
11084
11084
  arenaMinY += SHRINK_AMOUNT;
11085
11085
  arenaMaxY -= SHRINK_AMOUNT;
11086
11086
  }
11087
- triggerShake(4, 1);
11087
+ triggerShake2(4, 1);
11088
11088
  }
11089
11089
  }
11090
11090
  }
@@ -11719,7 +11719,7 @@ function runTypingTest(terminal) {
11719
11719
  borderFlash = 0;
11720
11720
  errorFlash = 0;
11721
11721
  }
11722
- function spawnParticles2(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
11722
+ function spawnParticles3(x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
11723
11723
  for (let i = 0; i < count; i++) {
11724
11724
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
11725
11725
  const speed = 0.2 + Math.random() * 0.3;
@@ -11734,7 +11734,7 @@ function runTypingTest(terminal) {
11734
11734
  });
11735
11735
  }
11736
11736
  }
11737
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
11737
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
11738
11738
  scorePopups.push({ x, y, text, frames: 18, color });
11739
11739
  }
11740
11740
  function calculateStats() {
@@ -11773,16 +11773,16 @@ function runTypingTest(terminal) {
11773
11773
  correctStreak++;
11774
11774
  if (correctStreak >= 5 && correctStreak % 5 === 0) {
11775
11775
  borderFlash = 6;
11776
- spawnParticles2(cursorX, textY, 4 + Math.floor(correctStreak / 5), "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6"]);
11777
- addScorePopup(cursorX, textY - 1, `${correctStreak}!`, "\x1B[1;96m");
11776
+ spawnParticles3(cursorX, textY, 4 + Math.floor(correctStreak / 5), "\x1B[1;92m", ["\u2726", "\u2605", "\u25C6"]);
11777
+ addScorePopup2(cursorX, textY - 1, `${correctStreak}!`, "\x1B[1;96m");
11778
11778
  } else if (correctStreak >= 10) {
11779
11779
  if (Math.random() < 0.3) {
11780
- spawnParticles2(cursorX, textY, 2, "\x1B[1;92m", ["\xB7", "\u2022", "\u25CB"]);
11780
+ spawnParticles3(cursorX, textY, 2, "\x1B[1;92m", ["\xB7", "\u2022", "\u25CB"]);
11781
11781
  }
11782
11782
  }
11783
11783
  if (char === " " || typedText.length === currentText.length) {
11784
11784
  wordsCompleted++;
11785
- spawnParticles2(cursorX - 2, textY, 3, "\x1B[1;96m", ["\u2726", "\xB7", "\u25CB"]);
11785
+ spawnParticles3(cursorX - 2, textY, 3, "\x1B[1;96m", ["\u2726", "\xB7", "\u25CB"]);
11786
11786
  }
11787
11787
  if (typedText.length >= currentText.length - 20) {
11788
11788
  if (mode === "words") {
@@ -11798,7 +11798,7 @@ function runTypingTest(terminal) {
11798
11798
  correctStreak = 0;
11799
11799
  errorFlash = 6;
11800
11800
  screenShake = 3;
11801
- spawnParticles2(cursorX, textY, 3, "\x1B[1;91m", ["\u2717", "\xD7", "\xB7"]);
11801
+ spawnParticles3(cursorX, textY, 3, "\x1B[1;91m", ["\u2717", "\xD7", "\xB7"]);
11802
11802
  }
11803
11803
  }
11804
11804
  function handleBackspace() {
@@ -12542,7 +12542,7 @@ function runWordleGame(terminal) {
12542
12542
  "\u2588 \u2588 \u2588\u2584\u2588 \u2588\u2580\u2588 \u2588\u2580\u2580 \u2588\u2580\u2588 \u2588 \u2588 \u2588 \u2588\u2580\u2588 \u2588\u2580\u2588 \u2588\u2580\u2584 \u2588 \u2588\u2580\u2580",
12543
12543
  "\u2588\u2580\u2588 \u2588 \u2588\u2580\u2580 \u2588\u2588\u2584 \u2588\u2580\u2584 \u2580\u2584\u2580\u2584\u2580 \u2588\u2584\u2588 \u2588\u2580\u2584 \u2588\u2584\u2580 \u2588\u2584\u2584 \u2588\u2588\u2584"
12544
12544
  ];
12545
- function spawnParticles2(x, y, count, color, chars = ["*", "+", "o", "."]) {
12545
+ function spawnParticles3(x, y, count, color, chars = ["*", "+", "o", "."]) {
12546
12546
  for (let i = 0; i < count; i++) {
12547
12547
  const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
12548
12548
  const speed = 0.3 + Math.random() * 0.5;
@@ -12557,7 +12557,7 @@ function runWordleGame(terminal) {
12557
12557
  });
12558
12558
  }
12559
12559
  }
12560
- function spawnFirework2(x, y) {
12560
+ function spawnFirework3(x, y) {
12561
12561
  const colors = ["\x1B[1;92m", "\x1B[1;93m", "\x1B[1;96m", "\x1B[1;95m"];
12562
12562
  const color = colors[Math.floor(Math.random() * colors.length)];
12563
12563
  const chars = ["*", "+", "o", ".", "`", "'"];
@@ -12575,10 +12575,10 @@ function runWordleGame(terminal) {
12575
12575
  });
12576
12576
  }
12577
12577
  }
12578
- function addScorePopup(x, y, text, color = "\x1B[1;33m") {
12578
+ function addScorePopup2(x, y, text, color = "\x1B[1;33m") {
12579
12579
  scorePopups.push({ x, y, text, frames: 25, color });
12580
12580
  }
12581
- function triggerShake(frames, intensity) {
12581
+ function triggerShake2(frames, intensity) {
12582
12582
  shakeFrames = frames;
12583
12583
  shakeIntensity = intensity;
12584
12584
  }
@@ -12636,8 +12636,8 @@ function runWordleGame(terminal) {
12636
12636
  }
12637
12637
  function submitGuess() {
12638
12638
  if (currentGuess.length !== WORD_LENGTH) {
12639
- triggerShake(4, 2);
12640
- addScorePopup(Math.floor(cols / 2), 8, "NOT ENOUGH LETTERS", "\x1B[1;91m");
12639
+ triggerShake2(4, 2);
12640
+ addScorePopup2(Math.floor(cols / 2), 8, "NOT ENOUGH LETTERS", "\x1B[1;91m");
12641
12641
  return;
12642
12642
  }
12643
12643
  const result = checkGuess(currentGuess);
@@ -12662,15 +12662,15 @@ function runWordleGame(terminal) {
12662
12662
  }
12663
12663
  stats.guessDistribution[guesses.length - 1]++;
12664
12664
  borderFlash = 20;
12665
- triggerShake(10, 3);
12665
+ triggerShake2(10, 3);
12666
12666
  const messages = ["CIPHER BREACHED!", "CODE CRACKED!", "DECRYPTED!", "BRILLIANT!"];
12667
- addScorePopup(centerX, guessY - 2, messages[Math.floor(Math.random() * messages.length)], "\x1B[1;92m");
12667
+ addScorePopup2(centerX, guessY - 2, messages[Math.floor(Math.random() * messages.length)], "\x1B[1;92m");
12668
12668
  setTimeout(() => {
12669
12669
  if (running) {
12670
12670
  for (let i = 0; i < 5; i++) {
12671
12671
  setTimeout(() => {
12672
12672
  if (running && won) {
12673
- spawnFirework2(
12673
+ spawnFirework3(
12674
12674
  10 + Math.random() * (cols - 20),
12675
12675
  5 + Math.random() * 10
12676
12676
  );
@@ -12683,15 +12683,15 @@ function runWordleGame(terminal) {
12683
12683
  gameOver = true;
12684
12684
  stats.gamesPlayed++;
12685
12685
  stats.currentStreak = 0;
12686
- triggerShake(8, 4);
12686
+ triggerShake2(8, 4);
12687
12687
  borderFlash = 15;
12688
- addScorePopup(centerX, guessY - 2, "DECRYPTION FAILED", "\x1B[1;91m");
12689
- spawnParticles2(centerX, guessY, 10, "\x1B[1;91m", ["X", "x", ".", "*"]);
12688
+ addScorePopup2(centerX, guessY - 2, "DECRYPTION FAILED", "\x1B[1;91m");
12689
+ spawnParticles3(centerX, guessY, 10, "\x1B[1;91m", ["X", "x", ".", "*"]);
12690
12690
  } else {
12691
12691
  if (correctCount > 0 || presentCount > 0) {
12692
12692
  const hint = correctCount > 0 ? `${correctCount} EXACT` : `${presentCount} CLOSE`;
12693
- addScorePopup(centerX, guessY - 1, hint, correctCount > 0 ? "\x1B[1;92m" : "\x1B[1;93m");
12694
- spawnParticles2(centerX, guessY, 3 + correctCount * 2, correctCount > 0 ? "\x1B[1;92m" : "\x1B[1;93m");
12693
+ addScorePopup2(centerX, guessY - 1, hint, correctCount > 0 ? "\x1B[1;92m" : "\x1B[1;93m");
12694
+ spawnParticles3(centerX, guessY, 3 + correctCount * 2, correctCount > 0 ? "\x1B[1;92m" : "\x1B[1;93m");
12695
12695
  }
12696
12696
  }
12697
12697
  currentGuess = "";
@@ -13058,7 +13058,7 @@ function runWordleGame(terminal) {
13058
13058
  submitGuess();
13059
13059
  } else if (key.length === 1 && /[a-zA-Z]/.test(key) && currentGuess.length < WORD_LENGTH) {
13060
13060
  currentGuess += key.toUpperCase();
13061
- spawnParticles2(
13061
+ spawnParticles3(
13062
13062
  Math.floor(cols / 2) - 8 + currentGuess.length * 4,
13063
13063
  4 + guesses.length * 2,
13064
13064
  2,
@@ -13660,6 +13660,140 @@ function runRebootEffect(terminal) {
13660
13660
  };
13661
13661
  }
13662
13662
 
13663
+ // src/games/shared/effects.ts
13664
+ var MAX_PARTICLES2 = 100;
13665
+ var PARTICLE_CHARS = {
13666
+ explosion: ["\u2717", "\xD7", "\xB7", "\u25CB", "\u2592", "\u2591"],
13667
+ success: ["\u2726", "\u2605", "\u25C6", "\u25CF", "\u2666"],
13668
+ fire: ["\u2593", "\u2592", "\u2591", "\u25CF", "\u25C6"],
13669
+ death: ["\u2717", "\u2620", "\xD7", "\u2593", "\u2591"],
13670
+ sparkle: ["\u2726", "\u2727", "\u2605"],
13671
+ firework: ["\u2605", "\u2726", "\u25C6", "\u25CF", "\u2736", "\u2734", "\u25C7", "\u2666", "\u2022", "\u25CB"]
13672
+ };
13673
+ var FIREWORK_COLORS = [
13674
+ "\x1B[1;93m",
13675
+ "\x1B[1;92m",
13676
+ "\x1B[1;96m",
13677
+ "\x1B[1;95m",
13678
+ "\x1B[1;91m",
13679
+ "\x1B[1;97m"
13680
+ ];
13681
+ function spawnParticles2(particles, x, y, count, color, chars = ["\u2726", "\u2605", "\u25C6", "\u25CF"]) {
13682
+ if (particles.length >= MAX_PARTICLES2) return;
13683
+ const actualCount = Math.min(count, MAX_PARTICLES2 - particles.length);
13684
+ for (let i = 0; i < actualCount; i++) {
13685
+ const angle = Math.PI * 2 * i / count + Math.random() * 0.5;
13686
+ const speed = 0.2 + Math.random() * 0.3;
13687
+ particles.push({
13688
+ x,
13689
+ y,
13690
+ char: chars[Math.floor(Math.random() * chars.length)],
13691
+ color,
13692
+ vx: Math.cos(angle) * speed,
13693
+ vy: Math.sin(angle) * speed * 0.5,
13694
+ life: 10 + Math.floor(Math.random() * 8)
13695
+ });
13696
+ }
13697
+ }
13698
+ function spawnFirework2(particles, x, y, intensity = 1) {
13699
+ const chars = PARTICLE_CHARS.firework;
13700
+ for (let i = 0; i < 12 * intensity; i++) {
13701
+ const angle = Math.PI * 2 * i / (12 * intensity);
13702
+ const speed = 0.4 + Math.random() * 0.4;
13703
+ const color = FIREWORK_COLORS[Math.floor(Math.random() * FIREWORK_COLORS.length)];
13704
+ const char = chars[Math.floor(Math.random() * chars.length)];
13705
+ particles.push({
13706
+ x,
13707
+ y,
13708
+ char,
13709
+ color,
13710
+ vx: Math.cos(angle) * speed,
13711
+ vy: Math.sin(angle) * speed - 0.2,
13712
+ life: 20 + Math.floor(Math.random() * 15)
13713
+ });
13714
+ }
13715
+ for (let i = 0; i < 8 * intensity; i++) {
13716
+ const angle = Math.PI * 2 * i / (8 * intensity) + Math.random() * 0.3;
13717
+ const dist = 1.5 + Math.random();
13718
+ particles.push({
13719
+ x: x + Math.cos(angle) * dist,
13720
+ y: y + Math.sin(angle) * dist,
13721
+ char: "\u2727",
13722
+ color: "\x1B[1;97m",
13723
+ vx: Math.cos(angle) * 0.15,
13724
+ vy: Math.sin(angle) * 0.15 - 0.1,
13725
+ life: 15 + Math.floor(Math.random() * 10)
13726
+ });
13727
+ }
13728
+ }
13729
+ function spawnSparkleTrail2(particles, x, y, count = 6, color = "\x1B[1;93m") {
13730
+ for (let i = 0; i < count; i++) {
13731
+ particles.push({
13732
+ x: x + (Math.random() - 0.5) * 4,
13733
+ y,
13734
+ char: PARTICLE_CHARS.sparkle[Math.floor(Math.random() * PARTICLE_CHARS.sparkle.length)],
13735
+ color,
13736
+ vx: (Math.random() - 0.5) * 0.2,
13737
+ vy: -0.3 - Math.random() * 0.3,
13738
+ life: 25 + Math.floor(Math.random() * 15)
13739
+ });
13740
+ }
13741
+ }
13742
+ function updateParticles2(particles, gravityMult = 1) {
13743
+ for (let i = particles.length - 1; i >= 0; i--) {
13744
+ const p = particles[i];
13745
+ p.x += p.vx;
13746
+ p.y += p.vy;
13747
+ p.vy += 0.02 * gravityMult;
13748
+ p.life--;
13749
+ if (p.life <= 0) particles.splice(i, 1);
13750
+ }
13751
+ }
13752
+ function addScorePopup(popups, x, y, text, color = "\x1B[1;33m") {
13753
+ popups.push({ x, y, text, frames: 18, color });
13754
+ }
13755
+ function updatePopups2(popups) {
13756
+ for (let i = popups.length - 1; i >= 0; i--) {
13757
+ const popup = popups[i];
13758
+ popup.y -= 0.25;
13759
+ popup.frames--;
13760
+ if (popup.frames <= 0) popups.splice(i, 1);
13761
+ }
13762
+ }
13763
+ function createShakeState() {
13764
+ return { frames: 0, intensity: 0 };
13765
+ }
13766
+ function triggerShake(state, frames, intensity) {
13767
+ state.frames = frames;
13768
+ state.intensity = intensity;
13769
+ }
13770
+ function applyShake(state) {
13771
+ if (state.frames > 0) {
13772
+ state.frames--;
13773
+ return {
13774
+ offsetX: Math.floor((Math.random() - 0.5) * state.intensity * 2),
13775
+ offsetY: Math.floor((Math.random() - 0.5) * state.intensity)
13776
+ };
13777
+ }
13778
+ return { offsetX: 0, offsetY: 0 };
13779
+ }
13780
+ function createFlashState() {
13781
+ return { frames: 0 };
13782
+ }
13783
+ function triggerFlash(state, frames) {
13784
+ state.frames = frames;
13785
+ }
13786
+ function updateFlash(state) {
13787
+ if (state.frames > 0) {
13788
+ state.frames--;
13789
+ return true;
13790
+ }
13791
+ return false;
13792
+ }
13793
+ function isFlashVisible(state) {
13794
+ return state.frames > 0 && state.frames % 4 < 2;
13795
+ }
13796
+
13663
13797
  // src/games/index.ts
13664
13798
  var games = [
13665
13799
  // Ordered for first-time discovery enjoyment (most accessible first).
@@ -13693,14 +13827,21 @@ function runGame(id, terminal) {
13693
13827
  return game?.run(terminal);
13694
13828
  }
13695
13829
  export {
13830
+ FIREWORK_COLORS,
13696
13831
  GAME_EVENTS,
13832
+ MAX_PARTICLES2 as MAX_PARTICLES,
13697
13833
  MODE_SELECT_ITEMS,
13834
+ PARTICLE_CHARS,
13698
13835
  PAUSE_MENU_ITEMS,
13836
+ addScorePopup,
13837
+ applyShake,
13699
13838
  checkShortcut,
13839
+ createFlashState,
13700
13840
  createGameOverMenuItems,
13701
13841
  createMenuState,
13702
13842
  createModeSelectMenuItems,
13703
13843
  createPauseMenuItems,
13844
+ createShakeState,
13704
13845
  dispatchGameQuit,
13705
13846
  dispatchGameSwitch,
13706
13847
  dispatchGamesMenu,
@@ -13719,6 +13860,7 @@ export {
13719
13860
  getVerticalAnchor,
13720
13861
  handleMatrixKeypress,
13721
13862
  handleMenuInput,
13863
+ isFlashVisible,
13722
13864
  isInAlternateBuffer,
13723
13865
  isLightTheme2 as isLightTheme,
13724
13866
  isMatrixWaitingForKey,
@@ -13759,6 +13901,14 @@ export {
13759
13901
  runWordleGame,
13760
13902
  setTheme,
13761
13903
  showGamesMenu,
13762
- startMatrixRain
13904
+ spawnFirework2 as spawnFirework,
13905
+ spawnParticles2 as spawnParticles,
13906
+ spawnSparkleTrail2 as spawnSparkleTrail,
13907
+ startMatrixRain,
13908
+ triggerFlash,
13909
+ triggerShake,
13910
+ updateFlash,
13911
+ updateParticles2 as updateParticles,
13912
+ updatePopups2 as updatePopups
13763
13913
  };
13764
13914
  //# sourceMappingURL=index.js.map