@@ -70,7 +70,7 @@ let isExhausted = false |
| 70 | 70 | let fliesMunchedLastNight = 0 |
| 71 | 71 | let birds = [] |
| 72 | 72 | let staminaRegenCooldown = 0 |
| 73 | | -let staminaBonus = 0; |
| 73 | +let staminaBonus = 0 |
| 74 | 74 | |
| 75 | 75 | // PHASE 4B: Wind System |
| 76 | 76 | let windActive = false |
@@ -477,99 +477,99 @@ function setup () { |
| 477 | 477 | let numObstacles = Math.floor((width * height) / 60000) // More obstacles |
| 478 | 478 | numObstacles = constrain(numObstacles, 15, 25) |
| 479 | 479 | |
| 480 | | -// Create ant balloons |
| 481 | | -let numBalloons = Math.floor(random(15, 21)) |
| 482 | | -for (let i = 0; i < numBalloons; i++) { |
| 483 | | - let attempts = 0 |
| 484 | | - let placed = false |
| 485 | | - |
| 486 | | - while (!placed && attempts < 30) { |
| 487 | | - // FIX: True random distribution with better spread |
| 488 | | - let x, y |
| 489 | | - |
| 490 | | - // Use different strategies for better distribution |
| 491 | | - let strategy = random() |
| 492 | | - |
| 493 | | - if (strategy < 0.3) { |
| 494 | | - // 30% - Truly random across upper area |
| 495 | | - x = random(80, width - 80) |
| 496 | | - y = random(60, height * 0.5) |
| 497 | | - } else if (strategy < 0.6) { |
| 498 | | - // 30% - Radial distribution from center |
| 499 | | - let angle = random(TWO_PI) |
| 500 | | - let radius = random(100, min(width, height) * 0.35) |
| 501 | | - x = width / 2 + cos(angle) * radius |
| 502 | | - y = height * 0.35 + sin(angle) * radius * 0.7 // Elliptical, flatter |
| 503 | | - x = constrain(x, 80, width - 80) |
| 504 | | - y = constrain(y, 60, height * 0.6) |
| 505 | | - } else if (strategy < 0.8) { |
| 506 | | - // 20% - Edge preference for variety |
| 507 | | - if (random() < 0.5) { |
| 508 | | - x = random() < 0.5 ? random(80, 150) : random(width - 150, width - 80) |
| 480 | + // Create ant balloons |
| 481 | + let numBalloons = Math.floor(random(15, 21)) |
| 482 | + for (let i = 0; i < numBalloons; i++) { |
| 483 | + let attempts = 0 |
| 484 | + let placed = false |
| 485 | + |
| 486 | + while (!placed && attempts < 30) { |
| 487 | + // FIX: True random distribution with better spread |
| 488 | + let x, y |
| 489 | + |
| 490 | + // Use different strategies for better distribution |
| 491 | + let strategy = random() |
| 492 | + |
| 493 | + if (strategy < 0.3) { |
| 494 | + // 30% - Truly random across upper area |
| 495 | + x = random(80, width - 80) |
| 509 | 496 | y = random(60, height * 0.5) |
| 497 | + } else if (strategy < 0.6) { |
| 498 | + // 30% - Radial distribution from center |
| 499 | + let angle = random(TWO_PI) |
| 500 | + let radius = random(100, min(width, height) * 0.35) |
| 501 | + x = width / 2 + cos(angle) * radius |
| 502 | + y = height * 0.35 + sin(angle) * radius * 0.7 // Elliptical, flatter |
| 503 | + x = constrain(x, 80, width - 80) |
| 504 | + y = constrain(y, 60, height * 0.6) |
| 505 | + } else if (strategy < 0.8) { |
| 506 | + // 20% - Edge preference for variety |
| 507 | + if (random() < 0.5) { |
| 508 | + x = random() < 0.5 ? random(80, 150) : random(width - 150, width - 80) |
| 509 | + y = random(60, height * 0.5) |
| 510 | + } else { |
| 511 | + x = random(80, width - 80) |
| 512 | + y = random(60, 120) |
| 513 | + } |
| 510 | 514 | } else { |
| 511 | | - x = random(80, width - 80) |
| 512 | | - y = random(60, 120) |
| 513 | | - } |
| 514 | | - } else { |
| 515 | | - // 20% - Poisson disk sampling attempt (avoid clusters) |
| 516 | | - let bestX = random(80, width - 80) |
| 517 | | - let bestY = random(60, height * 0.6) |
| 518 | | - let bestMinDist = 0 |
| 519 | | - |
| 520 | | - // Try a few positions and pick the one furthest from existing balloons |
| 521 | | - for (let j = 0; j < 5; j++) { |
| 522 | | - let testX = random(80, width - 80) |
| 523 | | - let testY = random(60, height * 0.6) |
| 524 | | - let minDist = Infinity |
| 525 | | - |
| 526 | | - for (let obstacle of obstacles) { |
| 527 | | - if (obstacle.type === 'balloon') { |
| 528 | | - let d = dist(testX, testY, obstacle.x, obstacle.y) |
| 529 | | - minDist = min(minDist, d) |
| 515 | + // 20% - Poisson disk sampling attempt (avoid clusters) |
| 516 | + let bestX = random(80, width - 80) |
| 517 | + let bestY = random(60, height * 0.6) |
| 518 | + let bestMinDist = 0 |
| 519 | + |
| 520 | + // Try a few positions and pick the one furthest from existing balloons |
| 521 | + for (let j = 0; j < 5; j++) { |
| 522 | + let testX = random(80, width - 80) |
| 523 | + let testY = random(60, height * 0.6) |
| 524 | + let minDist = Infinity |
| 525 | + |
| 526 | + for (let obstacle of obstacles) { |
| 527 | + if (obstacle.type === 'balloon') { |
| 528 | + let d = dist(testX, testY, obstacle.x, obstacle.y) |
| 529 | + minDist = min(minDist, d) |
| 530 | + } |
| 531 | + } |
| 532 | + |
| 533 | + if (minDist > bestMinDist) { |
| 534 | + bestMinDist = minDist |
| 535 | + bestX = testX |
| 536 | + bestY = testY |
| 530 | 537 | } |
| 531 | 538 | } |
| 532 | | - |
| 533 | | - if (minDist > bestMinDist) { |
| 534 | | - bestMinDist = minDist |
| 535 | | - bestX = testX |
| 536 | | - bestY = testY |
| 537 | | - } |
| 539 | + |
| 540 | + x = bestX |
| 541 | + y = bestY |
| 538 | 542 | } |
| 539 | | - |
| 540 | | - x = bestX |
| 541 | | - y = bestY |
| 542 | | - } |
| 543 | 543 | |
| 544 | | - let radius = random(35, 50) // Varied sizes for visual interest |
| 544 | + let radius = random(35, 50) // Varied sizes for visual interest |
| 545 | 545 | |
| 546 | | - let valid = true |
| 547 | | - // Check distance from other obstacles |
| 548 | | - for (let obstacle of obstacles) { |
| 549 | | - if ( |
| 550 | | - dist(x, y, obstacle.x, obstacle.y) < |
| 551 | | - radius + obstacle.radius + 40 |
| 552 | | - ) { |
| 553 | | - valid = false |
| 554 | | - break |
| 546 | + let valid = true |
| 547 | + // Check distance from other obstacles |
| 548 | + for (let obstacle of obstacles) { |
| 549 | + if ( |
| 550 | + dist(x, y, obstacle.x, obstacle.y) < |
| 551 | + radius + obstacle.radius + 40 |
| 552 | + ) { |
| 553 | + valid = false |
| 554 | + break |
| 555 | + } |
| 555 | 556 | } |
| 556 | | - } |
| 557 | 557 | |
| 558 | | - // Check distance from home branch |
| 559 | | - if (valid && window.homeBranch) { |
| 560 | | - let branchY = window.homeBranch.y |
| 561 | | - if (Math.abs(y - branchY) < radius + 40) { |
| 562 | | - valid = false |
| 558 | + // Check distance from home branch |
| 559 | + if (valid && window.homeBranch) { |
| 560 | + let branchY = window.homeBranch.y |
| 561 | + if (Math.abs(y - branchY) < radius + 40) { |
| 562 | + valid = false |
| 563 | + } |
| 563 | 564 | } |
| 564 | | - } |
| 565 | 565 | |
| 566 | | - if (valid) { |
| 567 | | - obstacles.push(new Obstacle(x, y, radius, 'balloon')) |
| 568 | | - placed = true |
| 566 | + if (valid) { |
| 567 | + obstacles.push(new Obstacle(x, y, radius, 'balloon')) |
| 568 | + placed = true |
| 569 | + } |
| 570 | + attempts++ |
| 569 | 571 | } |
| 570 | | - attempts++ |
| 571 | 572 | } |
| 572 | | -} |
| 573 | 573 | |
| 574 | 574 | // Create beetles |
| 575 | 575 | let numBeetles = Math.floor(random(9, 15)) |
@@ -1411,20 +1411,39 @@ function openStatsPanel () { |
| 1411 | 1411 | // Show panel |
| 1412 | 1412 | document.getElementById('stats-panel').style.display = 'block' |
| 1413 | 1413 | |
| 1414 | | - // Add close button listener |
| 1415 | | - document.getElementById('close-stats-btn').onclick = () => { |
| 1414 | + // FIX: Add both click AND touch listeners |
| 1415 | + let closeBtn = document.getElementById('close-stats-btn') |
| 1416 | + |
| 1417 | + // Remove any existing listeners |
| 1418 | + closeBtn.replaceWith(closeBtn.cloneNode(true)) |
| 1419 | + closeBtn = document.getElementById('close-stats-btn') |
| 1420 | + |
| 1421 | + closeBtn.addEventListener('click', function () { |
| 1416 | 1422 | document.getElementById('stats-panel').style.display = 'none' |
| 1423 | + if (gamePhase === 'DAY') { |
| 1424 | + gamePhase = 'DAY_TO_DUSK' |
| 1425 | + phaseTimer = 0 |
| 1426 | + } |
| 1427 | + }) |
| 1417 | 1428 | |
| 1418 | | - // IMMEDIATELY transition to dusk after closing stats |
| 1429 | + closeBtn.addEventListener('touchend', function (e) { |
| 1430 | + e.preventDefault() |
| 1431 | + document.getElementById('stats-panel').style.display = 'none' |
| 1419 | 1432 | if (gamePhase === 'DAY') { |
| 1420 | 1433 | gamePhase = 'DAY_TO_DUSK' |
| 1421 | 1434 | phaseTimer = 0 |
| 1422 | 1435 | } |
| 1423 | | - } |
| 1436 | + }) |
| 1424 | 1437 | } |
| 1425 | 1438 | |
| 1426 | 1439 | // Make selectSkin global |
| 1427 | | -window.selectSkin = function (skinId) { |
| 1440 | +window.selectSkin = function (skinId, event) { |
| 1441 | + // Prevent touch issues |
| 1442 | + if (event) { |
| 1443 | + event.preventDefault() |
| 1444 | + event.stopPropagation() |
| 1445 | + } |
| 1446 | + |
| 1428 | 1447 | if (unlockedSkins[skinId]) { |
| 1429 | 1448 | currentSkin = skinId |
| 1430 | 1449 | saveGame() |
@@ -1653,7 +1672,7 @@ function saveGame () { |
| 1653 | 1672 | nightsSurvived: nightsSurvived, |
| 1654 | 1673 | currentNight: currentNight, |
| 1655 | 1674 | playerPoints: playerPoints, |
| 1656 | | - spentPoints: spentPoints, |
| 1675 | + spentPoints: spentPoints |
| 1657 | 1676 | } |
| 1658 | 1677 | |
| 1659 | 1678 | localStorage.setItem('cobGameSave', JSON.stringify(saveData)) |
@@ -1792,23 +1811,32 @@ function spawnDawnBirds () { |
| 1792 | 1811 | // ============================================ |
| 1793 | 1812 | |
| 1794 | 1813 | function openUpgradeShop () { |
| 1795 | | - if (currentNight <= 1) return // No shop on first night |
| 1814 | + if (currentNight <= 1) return |
| 1796 | 1815 | |
| 1797 | 1816 | shopOpen = true |
| 1798 | 1817 | noLoop() // Pause the game |
| 1799 | 1818 | |
| 1800 | | - // Calculate points from flies caught this session |
| 1801 | | - // playerPoints = totalFliesCaught |
| 1802 | | - |
| 1803 | 1819 | // Update shop UI |
| 1804 | 1820 | document.getElementById('upgrade-shop').style.display = 'block' |
| 1805 | | - document.getElementById('available-points').textContent = playerPoints - spentPoints |
| 1821 | + document.getElementById('available-points').textContent = |
| 1822 | + playerPoints - spentPoints |
| 1806 | 1823 | |
| 1807 | 1824 | // Populate upgrade lists |
| 1808 | 1825 | updateShopDisplay() |
| 1809 | 1826 | |
| 1810 | | - // Add continue button listener |
| 1811 | | - document.getElementById('continue-btn').onclick = closeUpgradeShop |
| 1827 | + // FIX: Add both click AND touch listeners for mobile |
| 1828 | + let continueBtn = document.getElementById('continue-btn') |
| 1829 | + |
| 1830 | + // Remove any existing listeners to prevent duplicates |
| 1831 | + continueBtn.replaceWith(continueBtn.cloneNode(true)) |
| 1832 | + continueBtn = document.getElementById('continue-btn') |
| 1833 | + |
| 1834 | + // Add both click and touch support |
| 1835 | + continueBtn.addEventListener('click', closeUpgradeShop) |
| 1836 | + continueBtn.addEventListener('touchend', function (e) { |
| 1837 | + e.preventDefault() // Prevent ghost clicks |
| 1838 | + closeUpgradeShop() |
| 1839 | + }) |
| 1812 | 1840 | } |
| 1813 | 1841 | |
| 1814 | 1842 | function closeUpgradeShop () { |
@@ -1862,7 +1890,7 @@ function updateShopDisplay () { |
| 1862 | 1890 | }/${upgrade.maxLevel}) |
| 1863 | 1891 | <br><small>${upgrade.description}</small> |
| 1864 | 1892 | </div> |
| 1865 | | - <button onclick="buyUpgrade('${key}')" ${buttonDisabled} |
| 1893 | + <button ontouchend="buyUpgrade('${key}')" onclick="buyUpgrade('${key}')" ${buttonDisabled} |
| 1866 | 1894 | style="padding: 5px 15px; background: ${ |
| 1867 | 1895 | canAfford && !maxed ? '#4CAF50' : '#666' |
| 1868 | 1896 | }; |
@@ -1907,7 +1935,7 @@ function updateShopDisplay () { |
| 1907 | 1935 | }/${upgrade.maxLevel}) |
| 1908 | 1936 | <br><small>${upgrade.description}</small> |
| 1909 | 1937 | </div> |
| 1910 | | - <button onclick="buyUpgrade('${key}')" ${buttonDisabled} |
| 1938 | + <button ontouchend="buyUpgrade('${key}')" onclick="buyUpgrade('${key}')" ${buttonDisabled} |
| 1911 | 1939 | style="padding: 5px 15px; background: ${ |
| 1912 | 1940 | canAfford && !maxed ? '#FF69B4' : '#666' |
| 1913 | 1941 | }; |
@@ -1934,6 +1962,12 @@ function updateShopDisplay () { |
| 1934 | 1962 | |
| 1935 | 1963 | // Make buyUpgrade global so onclick can access it |
| 1936 | 1964 | window.buyUpgrade = function (upgradeKey) { |
| 1965 | + // Prevent any touch/click propagation issues |
| 1966 | + if (event) { |
| 1967 | + event.preventDefault() |
| 1968 | + event.stopPropagation() |
| 1969 | + } |
| 1970 | + |
| 1937 | 1971 | let upgrade = upgrades[upgradeKey] |
| 1938 | 1972 | if (!upgrade) return |
| 1939 | 1973 | |
@@ -1949,16 +1983,17 @@ window.buyUpgrade = function (upgradeKey) { |
| 1949 | 1983 | } |
| 1950 | 1984 | |
| 1951 | 1985 | // Check if can afford and not maxed |
| 1952 | | - let availablePoints = playerPoints - spentPoints // Calculate available points |
| 1986 | + let availablePoints = playerPoints - spentPoints // Calculate available points |
| 1953 | 1987 | if (availablePoints >= upgrade.cost && upgrade.level < upgrade.maxLevel) { |
| 1954 | | - spentPoints += upgrade.cost // Track spent points |
| 1988 | + spentPoints += upgrade.cost // Track spent points |
| 1955 | 1989 | upgrade.level++ |
| 1956 | 1990 | |
| 1957 | 1991 | // Apply upgrade effects immediately |
| 1958 | 1992 | applyUpgradeEffects() |
| 1959 | 1993 | |
| 1960 | 1994 | // Update display with available points |
| 1961 | | - document.getElementById('available-points').textContent = playerPoints - spentPoints |
| 1995 | + document.getElementById('available-points').textContent = |
| 1996 | + playerPoints - spentPoints |
| 1962 | 1997 | updateShopDisplay() |
| 1963 | 1998 | |
| 1964 | 1999 | // Show notification |
@@ -2709,39 +2744,48 @@ function updateUI () { |
| 2709 | 2744 | `<br><small ${staminaColor}>Dawn Stamina: ${potentialStamina}</small>` |
| 2710 | 2745 | |
| 2711 | 2746 | if (gamePhase === 'NIGHT') { |
| 2712 | | - let timeLeft = Math.ceil((NIGHT_DURATION - phaseTimer) / 60); |
| 2713 | | - |
| 2714 | | - // Calculate current munch percentage |
| 2715 | | - let totalFliesInNight = fliesSpawnedThisNight + flies.length; |
| 2716 | | - let currentMunchPercent = totalFliesInNight > 0 ? |
| 2717 | | - Math.floor((fliesMunched / totalFliesInNight) * 100) : 0; |
| 2718 | | - |
| 2719 | | - // Calculate predicted dawn stamina |
| 2720 | | - let predictedStamina; |
| 2721 | | - if (currentMunchPercent >= 50) { |
| 2722 | | - predictedStamina = 100; |
| 2723 | | - } else { |
| 2724 | | - predictedStamina = Math.floor(20 + (currentMunchPercent * 2) * 0.8); |
| 2725 | | - } |
| 2726 | | - |
| 2727 | | - timerText = `${timeLeft}s • ${flies.length} flies`; |
| 2728 | | - |
| 2729 | | - // Show special fly counts if any |
| 2730 | | - let goldenCount = flies.filter(f => f.type === 'golden').length; |
| 2731 | | - let mothCount = flies.filter(f => f.type === 'moth').length; |
| 2732 | | - let queenCount = flies.filter(f => f.type === 'queen').length; |
| 2733 | | - |
| 2734 | | - if (goldenCount > 0 || mothCount > 0 || queenCount > 0) { |
| 2735 | | - let specialCounts = []; |
| 2736 | | - if (queenCount > 0) specialCounts.push(`${queenCount}👑`); |
| 2737 | | - if (goldenCount > 0) specialCounts.push(`${goldenCount}✨`); |
| 2738 | | - if (mothCount > 0) specialCounts.push(`${mothCount}🦋`); |
| 2739 | | - timerText += ` (${specialCounts.join(' ')})`; |
| 2740 | | - } |
| 2741 | | - // Show munch progress |
| 2742 | | - document.getElementById('timer').innerHTML = timerText + |
| 2743 | | - `<br><small style="color: ${predictedStamina < 40 ? '#ff4444' : predictedStamina < 70 ? '#ffaa44' : '#44ff44'}">` + |
| 2744 | | - `Munched: ${currentMunchPercent}% → ${predictedStamina} dawn stamina</small>`; |
| 2747 | + let timeLeft = Math.ceil((NIGHT_DURATION - phaseTimer) / 60) |
| 2748 | + |
| 2749 | + // Calculate current munch percentage |
| 2750 | + let totalFliesInNight = fliesSpawnedThisNight + flies.length |
| 2751 | + let currentMunchPercent = |
| 2752 | + totalFliesInNight > 0 |
| 2753 | + ? Math.floor((fliesMunched / totalFliesInNight) * 100) |
| 2754 | + : 0 |
| 2755 | + |
| 2756 | + // Calculate predicted dawn stamina |
| 2757 | + let predictedStamina |
| 2758 | + if (currentMunchPercent >= 50) { |
| 2759 | + predictedStamina = 100 |
| 2760 | + } else { |
| 2761 | + predictedStamina = Math.floor(20 + currentMunchPercent * 2 * 0.8) |
| 2762 | + } |
| 2763 | + |
| 2764 | + timerText = `${timeLeft}s • ${flies.length} flies` |
| 2765 | + |
| 2766 | + // Show special fly counts if any |
| 2767 | + let goldenCount = flies.filter(f => f.type === 'golden').length |
| 2768 | + let mothCount = flies.filter(f => f.type === 'moth').length |
| 2769 | + let queenCount = flies.filter(f => f.type === 'queen').length |
| 2770 | + |
| 2771 | + if (goldenCount > 0 || mothCount > 0 || queenCount > 0) { |
| 2772 | + let specialCounts = [] |
| 2773 | + if (queenCount > 0) specialCounts.push(`${queenCount}👑`) |
| 2774 | + if (goldenCount > 0) specialCounts.push(`${goldenCount}✨`) |
| 2775 | + if (mothCount > 0) specialCounts.push(`${mothCount}🦋`) |
| 2776 | + timerText += ` (${specialCounts.join(' ')})` |
| 2777 | + } |
| 2778 | + // Show munch progress |
| 2779 | + document.getElementById('timer').innerHTML = |
| 2780 | + timerText + |
| 2781 | + `<br><small style="color: ${ |
| 2782 | + predictedStamina < 40 |
| 2783 | + ? '#ff4444' |
| 2784 | + : predictedStamina < 70 |
| 2785 | + ? '#ffaa44' |
| 2786 | + : '#44ff44' |
| 2787 | + }">` + |
| 2788 | + `Munched: ${currentMunchPercent}% → ${predictedStamina} dawn stamina</small>` |
| 2745 | 2789 | } else if (gamePhase === 'DAWN') { |
| 2746 | 2790 | let timeLeft = Math.ceil((DAWN_DURATION - phaseTimer) / 60) |
| 2747 | 2791 | // PHASE 4: Show birds and exhaustion status |
@@ -2786,49 +2830,56 @@ function updateUI () { |
| 2786 | 2830 | } |
| 2787 | 2831 | |
| 2788 | 2832 | // PHASE 4: Update meter based on phase |
| 2789 | | -if (gamePhase === 'DAWN') { |
| 2790 | | - // Show stamina instead of silk during dawn |
| 2791 | | - document.getElementById('web-meter-label').textContent = 'STAMINA'; |
| 2792 | | - |
| 2793 | | - // FIX: Always show percentage out of 100, not out of variable max |
| 2794 | | - let staminaPercent = (jumpStamina / 100) * 100; // Always out of 100 |
| 2795 | | - document.getElementById('web-meter-fill').style.width = staminaPercent + '%'; |
| 2796 | | - |
| 2797 | | - // Color based on stamina level |
| 2798 | | - if (jumpStamina < 20) { |
| 2799 | | - // Exhausted - red flash |
| 2800 | | - let flash = sin(frameCount * 0.3) * 0.5 + 0.5; |
| 2801 | | - document.getElementById('web-meter-fill').style.background = |
| 2802 | | - `linear-gradient(90deg, rgb(255, ${50 + flash * 50}, ${50 + flash * 50}), rgb(200, ${30 + flash * 30}, ${30 + flash * 30}))`; |
| 2803 | | - } else if (jumpStamina < 40) { |
| 2804 | | - // Very tired - orange-red |
| 2805 | | - document.getElementById('web-meter-fill').style.background = 'linear-gradient(90deg, #FF6B35, #FF4444)'; |
| 2806 | | - } else if (jumpStamina < 60) { |
| 2807 | | - // Tired - orange |
| 2808 | | - document.getElementById('web-meter-fill').style.background = 'linear-gradient(90deg, #FFA500, #FF8C00)'; |
| 2809 | | - } else if (jumpStamina < 80) { |
| 2810 | | - // OK - yellow-orange |
| 2811 | | - document.getElementById('web-meter-fill').style.background = 'linear-gradient(90deg, #FFD700, #FFA500)'; |
| 2833 | + if (gamePhase === 'DAWN') { |
| 2834 | + // Show stamina instead of silk during dawn |
| 2835 | + document.getElementById('web-meter-label').textContent = 'STAMINA' |
| 2836 | + |
| 2837 | + // FIX: Always show percentage out of 100, not out of variable max |
| 2838 | + let staminaPercent = (jumpStamina / 100) * 100 // Always out of 100 |
| 2839 | + document.getElementById('web-meter-fill').style.width = staminaPercent + '%' |
| 2840 | + |
| 2841 | + // Color based on stamina level |
| 2842 | + if (jumpStamina < 20) { |
| 2843 | + // Exhausted - red flash |
| 2844 | + let flash = sin(frameCount * 0.3) * 0.5 + 0.5 |
| 2845 | + document.getElementById( |
| 2846 | + 'web-meter-fill' |
| 2847 | + ).style.background = `linear-gradient(90deg, rgb(255, ${ |
| 2848 | + 50 + flash * 50 |
| 2849 | + }, ${50 + flash * 50}), rgb(200, ${30 + flash * 30}, ${30 + flash * 30}))` |
| 2850 | + } else if (jumpStamina < 40) { |
| 2851 | + // Very tired - orange-red |
| 2852 | + document.getElementById('web-meter-fill').style.background = |
| 2853 | + 'linear-gradient(90deg, #FF6B35, #FF4444)' |
| 2854 | + } else if (jumpStamina < 60) { |
| 2855 | + // Tired - orange |
| 2856 | + document.getElementById('web-meter-fill').style.background = |
| 2857 | + 'linear-gradient(90deg, #FFA500, #FF8C00)' |
| 2858 | + } else if (jumpStamina < 80) { |
| 2859 | + // OK - yellow-orange |
| 2860 | + document.getElementById('web-meter-fill').style.background = |
| 2861 | + 'linear-gradient(90deg, #FFD700, #FFA500)' |
| 2862 | + } else { |
| 2863 | + // Good stamina - green-yellow |
| 2864 | + document.getElementById('web-meter-fill').style.background = |
| 2865 | + 'linear-gradient(90deg, #90EE90, #FFD700)' |
| 2866 | + } |
| 2867 | + |
| 2868 | + // Show critical warning overlay |
| 2869 | + if (jumpStamina <= 0 && !gameOver) { |
| 2870 | + push() |
| 2871 | + fill(255, 0, 0, 50 + sin(frameCount * 0.3) * 50) |
| 2872 | + rect(0, 0, width, height) |
| 2873 | + |
| 2874 | + textAlign(CENTER) |
| 2875 | + textSize(32) |
| 2876 | + fill(255, 50, 50) |
| 2877 | + stroke(0) |
| 2878 | + strokeWeight(3) |
| 2879 | + text('NO STAMINA - AVOID BIRDS!', width / 2, height / 2) |
| 2880 | + pop() |
| 2881 | + } |
| 2812 | 2882 | } else { |
| 2813 | | - // Good stamina - green-yellow |
| 2814 | | - document.getElementById('web-meter-fill').style.background = 'linear-gradient(90deg, #90EE90, #FFD700)'; |
| 2815 | | - } |
| 2816 | | - |
| 2817 | | - // Show critical warning overlay |
| 2818 | | - if (jumpStamina <= 0 && !gameOver) { |
| 2819 | | - push(); |
| 2820 | | - fill(255, 0, 0, 50 + sin(frameCount * 0.3) * 50); |
| 2821 | | - rect(0, 0, width, height); |
| 2822 | | - |
| 2823 | | - textAlign(CENTER); |
| 2824 | | - textSize(32); |
| 2825 | | - fill(255, 50, 50); |
| 2826 | | - stroke(0); |
| 2827 | | - strokeWeight(3); |
| 2828 | | - text('NO STAMINA - AVOID BIRDS!', width / 2, height / 2); |
| 2829 | | - pop(); |
| 2830 | | - } |
| 2831 | | -} else { |
| 2832 | 2883 | // Normal silk meter |
| 2833 | 2884 | document.getElementById('web-meter-label').textContent = 'SILK' |
| 2834 | 2885 | let meterPercent = (webSilk / maxWebSilk) * 100 |
@@ -2918,6 +2969,13 @@ function showGameOverScreen () { |
| 2918 | 2969 | ` |
| 2919 | 2970 | |
| 2920 | 2971 | document.body.insertAdjacentHTML('beforeend', gameOverHTML) |
| 2972 | + // FIX: Add touch support to restart button |
| 2973 | + let restartBtn = document.getElementById('restart-btn') |
| 2974 | + restartBtn.addEventListener('click', restartGame) |
| 2975 | + restartBtn.addEventListener('touchend', function (e) { |
| 2976 | + e.preventDefault() |
| 2977 | + restartGame() |
| 2978 | + }) |
| 2921 | 2979 | } |
| 2922 | 2980 | |
| 2923 | 2981 | // Add restart game function: |
@@ -3125,6 +3183,14 @@ function recycleNearbyWeb () { |
| 3125 | 3183 | } |
| 3126 | 3184 | |
| 3127 | 3185 | function touchStarted () { |
| 3186 | + // FIX: Don't process game touches when modals are open |
| 3187 | + if ( |
| 3188 | + shopOpen || |
| 3189 | + document.getElementById('stats-panel').style.display === 'block' |
| 3190 | + ) { |
| 3191 | + return false |
| 3192 | + } |
| 3193 | + |
| 3128 | 3194 | if (touches.length > 0) { |
| 3129 | 3195 | touchStartTime = millis() |
| 3130 | 3196 | touchStartX = touches[0].x |
@@ -3188,6 +3254,12 @@ function touchStarted () { |
| 3188 | 3254 | } |
| 3189 | 3255 | |
| 3190 | 3256 | function touchMoved () { |
| 3257 | + if ( |
| 3258 | + shopOpen || |
| 3259 | + document.getElementById('stats-panel').style.display === 'block' |
| 3260 | + ) { |
| 3261 | + return false |
| 3262 | + } |
| 3191 | 3263 | // Update web deployment target while holding |
| 3192 | 3264 | if ( |
| 3193 | 3265 | touchHolding && |
@@ -3206,6 +3278,12 @@ function touchMoved () { |
| 3206 | 3278 | } |
| 3207 | 3279 | |
| 3208 | 3280 | function touchEnded () { |
| 3281 | + if ( |
| 3282 | + shopOpen || |
| 3283 | + document.getElementById('stats-panel').style.display === 'block' |
| 3284 | + ) { |
| 3285 | + return false |
| 3286 | + } |
| 3209 | 3287 | touchHolding = false |
| 3210 | 3288 | touchProcessing = false |
| 3211 | 3289 | |