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