diff --git a/index.html b/index.html index 30c7be64d..2d53b20ea 100644 --- a/index.html +++ b/index.html @@ -74,6 +74,29 @@
+
+
+
Welcome to Hextris
+ +
+
+
+
+
Select Level
+
+ +
+
+ +
+
+
LEVEL COMPLETE!
+
+
+ + +
+
@@ -84,6 +107,12 @@
HIGH SCORE
10292
+ + + +
GAME OVER
diff --git a/js/checking.js b/js/checking.js index 800013709..c2ea05930 100644 --- a/js/checking.js +++ b/js/checking.js @@ -81,4 +81,6 @@ function consolidateBlocks(hex,side,index){ hex.texts.push(new Text(hex.x,hex.y,"+ "+adder.toString(),"bold Q ",deletedBlocks[0].color,fadeUpAndOut)); hex.lastColorScored = deletedBlocks[0].color; score += adder; + window.progressCounter += adder; // accumulate for bar fill (200 per cycle) + updateProgressBar(); } diff --git a/js/initialization.js b/js/initialization.js index a22adf86f..669010e4e 100644 --- a/js/initialization.js +++ b/js/initialization.js @@ -120,7 +120,13 @@ function initialize(a) { highscores = []; } } + // level progress saved in localStorage, default only level 1 unlocked + window.currentLevel = 1; + window.maxUnlockedLevel = parseInt(localStorage.getItem('maxUnlockedLevel')) || 1; window.blocks = []; + // progress for bar (stays full green after 200 until '1' pressed; resets every 200) + window.progressCounter = 0; + window.barFull = false; window.MainHex; window.gdx = 0; window.gdy = 0; @@ -133,7 +139,7 @@ function initialize(a) { window.importedHistory = undefined; window.startTime = undefined; window.gameState; - setStartScreen(); + showWelcome(); if (a != 1) { window.canRestart = 1; window.onblur = function(e) { @@ -141,7 +147,7 @@ function initialize(a) { pause(); } }; - $('#startBtn').off(); + $('#startBtn').off().hide(); if (settings.platform == 'mobile') { $('#startBtn').on('touchstart', startBtnHandler); } else { @@ -176,6 +182,16 @@ function initialize(a) { document.addEventListener("backbutton", handlePause, false); document.addEventListener("menubutton", handlePause, false); //menu button on android + $('#welcomeStartBtn').off().on('click', function() { + showLevels(); + }); + $('#backToWelcomeBtn').off().on('click', function() { + showWelcome(); + }); + // complete screen buttons + $('#nextLevelBtn').off().on('click', nextLevel); + $('#replayLevelBtn').off().on('click', replayLevel); + setTimeout(function() { if (settings.platform == "mobile") { try { @@ -290,3 +306,178 @@ function handleClickBefore(e) { return; } } + +function getLevelTarget(level) { + return 250 * (level + 1); +} + +function saveLevelProgress() { + localStorage.setItem('maxUnlockedLevel', maxUnlockedLevel); +} + +function renderLevelsList() { + var list = $('#levelsList'); + list.empty(); + for(var i = 1; i <= 100; i++) { + var item = $('
' + i + '
'); + if (i > maxUnlockedLevel) { + item.addClass('locked'); + } else { + item.on('click', (function(lvl) { + return function() { + startLevel(lvl); + }; + })(i)); + } + list.append(item); + } + list.scrollTop(0); +} + +function showWelcome() { + hideAllScreens(); + $('#welcomeScreen').fadeIn(150, "linear"); + $('#startBtn').hide(); + init(); + gameState = 0; + requestAnimFrame(animLoop); +} + +function showLevels() { + hideAllScreens(); + $('#levelsScreen').fadeIn(150, "linear"); + renderLevelsList(); + gameState = 0; + requestAnimFrame(animLoop); +} + +function hideAllScreens() { + $('#welcomeScreen').fadeOut(150); + $('#levelsScreen').fadeOut(150); + $('#helpScreen').fadeOut(150); + $('#gameoverscreen').fadeOut(150); + $('#levelCompleteScreen').fadeOut(150); + $('#overlay').fadeOut(150); + $('#buttonCont').fadeOut(150); + $('.helpText').fadeOut(150); + updateLevelDisplay(false); +} + +function startLevel(level) { + currentLevel = level; + hideAllScreens(); + setTimeout(function() { + if (settings.platform == "mobile") { + try { + document.body.removeEventListener('touchstart', handleTapBefore, false); + } catch (e) {} + try { + document.body.removeEventListener('touchstart', handleTap, false); + } catch (e) {} + document.body.addEventListener('touchstart', handleTap, false); + } else { + try { + document.body.removeEventListener('mousedown', handleClickBefore, false); + } catch (e) {} + try { + document.body.removeEventListener('mousedown', handleClick, false); + } catch (e) {} + document.body.addEventListener('mousedown', handleClick, false); + } + }, 5); + if (!canRestart) return false; + // always full init for fresh level start (after complete or menu) + init(1); + checkVisualElements(0); + updateLevelDisplay(true); +} + +function completeLevelIfQualified() { + var target = getLevelTarget(currentLevel); + if (score >= target) { + if (currentLevel + 1 > maxUnlockedLevel) { + maxUnlockedLevel = currentLevel + 1; + saveLevelProgress(); + } + showLevelComplete(); + } +} + +function showLevelComplete() { + // stop game, clear save, dim canvas, show complete banner + gameState = 0; + clearSaveState(); + importing = 0; + hideAllScreens(); + var c = document.getElementById("canvas"); + c.className = "blur"; + $('#pauseBtn').hide(); + $('#restartBtn').hide(); + $('#levelCompleteScreen').fadeIn(150, "linear"); + $('#completeLevelNum').text('LEVEL ' + currentLevel); + $('#completeScore').text('SCORE: ' + score); + // handlers set once in init +} + +function updateLevelDisplay(show) { + if (show) { + $('#currentLevelNum').text(currentLevel); + $('#levelDisplay').show(); + $('#progressContainer').show(); + window.barFull = false; + window.progressCounter = 0; + window.currentProgress = 0; + updateProgressBar(); + } else { + $('#levelDisplay').hide(); + $('#progressContainer').hide(); + } +} + +function nextLevel() { + var next = currentLevel + 1; + if (next > 100) next = 100; + startLevel(next); +} + +function replayLevel() { + startLevel(currentLevel); +} + +function updateProgressBar() { + // progress fills/stays green after 200 until '1'; resets after clear + var mod = window.progressCounter % 200; + if (mod === 0 && window.progressCounter > 0) { + window.barFull = true; + } else if (!window.barFull) { + window.currentProgress = mod; // temp for calc + } + if (window.barFull) { + window.currentProgress = 200; // force stay full + } else { + window.currentProgress = mod; + } + // if full, stay 100% until cleared + var percent = (window.currentProgress / 200) * 100; + $('#progressBar').css('width', percent + '%'); +} + +function clearBlocksForSpace() { + // clear all falling/settled blocks to make space (called on '1' when bar full) + blocks = []; + for (var i = 0; i < MainHex.blocks.length; i++) { + MainHex.blocks[i] = []; + } + // bomb effect: boom text at center + if (MainHex) { + MainHex.texts.push(new Text(trueCanvas.width / 2, trueCanvas.height / 2 - 50, "💣 BOOM!", "bold 40px", "#e74c3c", fadeUpAndOut)); + } + window.barFull = false; + window.progressCounter = 0; + window.currentProgress = 0; + updateProgressBar(); + // slight delay for visual + setTimeout(function() { + if (gameState == 1) updateProgressBar(); + }, 100); +} diff --git a/js/input.js b/js/input.js index 190f66f1c..66dcc8ab1 100644 --- a/js/input.js +++ b/js/input.js @@ -103,8 +103,9 @@ function addKeyListeners() { init(1); } if (gameState == 2) { - init(); + // after gameover, route to levels (follows full menu flow with progress etc) $("#gameoverscreen").fadeOut(); + showLevels(); } if (gameState===0) { resumeGame(); @@ -112,6 +113,16 @@ function addKeyListeners() { } }); + // '1' clears blocks when progress bar full (every 200 score) + keypress.register_combo({ + keys: "1", + on_keydown: function() { + if (gameState == 1 && window.currentProgress >= 200) { + clearBlocksForSpace(); + } + } + }); + $("#pauseBtn").on('touchstart mousedown', function() { if (gameState != 1 && gameState != -1) { return; @@ -152,33 +163,37 @@ function addKeyListeners() { if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { $("#restart").on('touchstart', function() { - init(); - canRestart = false; + // after gameover restart, route back to levels menu (follows welcome/levels/progress flow) $("#gameoverscreen").fadeOut(); + showLevels(); + canRestart = false; }); } else { $("#restart").on('mousedown', function() { - init(); - canRestart = false; + // after gameover restart, route back to levels menu (follows welcome/levels/progress flow) $("#gameoverscreen").fadeOut(); + showLevels(); + canRestart = false; }); } if(/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)) { $("#restartBtn").on('touchstart', function() { - init(1); - canRestart = false; + // after gameover restart, route back to levels menu (follows welcome/levels/progress flow) $("#gameoverscreen").fadeOut(); + showLevels(); + canRestart = false; }); } else { $("#restartBtn").on('mousedown', function() { - init(1); - canRestart = false; + // after gameover restart, route back to levels menu (follows welcome/levels/progress flow) $("#gameoverscreen").fadeOut(); + showLevels(); + canRestart = false; }); diff --git a/js/main.js b/js/main.js index 5d8aae365..477eea746 100644 --- a/js/main.js +++ b/js/main.js @@ -71,6 +71,7 @@ function resumeGame() { }, 7000); checkVisualElements(0); + updateLevelDisplay(true); } function checkVisualElements(arg) { @@ -211,20 +212,8 @@ function exportHistory() { } function setStartScreen() { - $('#startBtn').show(); - init(); - if (isStateSaved()) { - importing = 0; - } else { - importing = 1; - } - - $('#pauseBtn').hide(); - $('#restartBtn').hide(); - $('#startBtn').show(); - - gameState = 0; - requestAnimFrame(animLoop); + // now shows welcome page instead of original start button + showWelcome(); } var spd = 1; @@ -247,11 +236,16 @@ function animLoop() { else{ MainHex.delay--; } + // check after update if target reached; stop and show complete (before possible overflow) + if (score >= getLevelTarget(currentLevel)) { + completeLevelIfQualified(); + } } lastTime = now; - if (checkGameOver() && !importing) { + // skip gameover check if level completed this frame + if (gameState === 1 && checkGameOver() && !importing) { var saveState = localStorage.getItem("saveState") || "{}"; saveState = JSONfn.parse(saveState); gameState = 2; diff --git a/js/view.js b/js/view.js index 91a772f08..755e869e6 100644 --- a/js/view.js +++ b/js/view.js @@ -18,6 +18,10 @@ function renderText(x, y, fontSize, color, text, font) { } function drawScoreboard() { + // skip texts if any menu/complete screen visible + if ($('#welcomeScreen').is(':visible') || $('#levelsScreen').is(':visible') || $('#levelCompleteScreen').is(':visible')) { + return; + } if (scoreOpacity < 1) { scoreOpacity += 0.01; textOpacity += 0.01; @@ -159,6 +163,7 @@ function gameOverDisplay() { $("#socialShare").fadeIn(); $("#restart").fadeIn(); set_score_pos(); + updateLevelDisplay(false); } function updateHighScores (){ diff --git a/style/style.css b/style/style.css index b57f23e85..a276d0197 100644 --- a/style/style.css +++ b/style/style.css @@ -431,6 +431,160 @@ body { -ms-user-select:none; user-select:none; } + +.screen { + height:100%; + width:100%; + z-index:3001; + position:fixed; + top:0; + margin:0; + font-size:15px; + text-align:center; + display:none; + color:#232323; + background-color:rgba(236,240,241,1); +} + +.welcomeContainer, .levelsContainer { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + width: 80%; + max-width: 400px; +} + +#welcomeTitle { + font-size: 8vmin; + color: #2ecc71; + margin-bottom: 40px; + font-weight: bold; +} + +.menuBtn { + background-color: #2c3e50; + color: #ecf0f1; + padding: 15px 30px; + font-size: 5vmin; + border-radius: 10px; + cursor: pointer; + margin: 10px auto; + width: 70%; + max-width: 300px; +} + +#levelsHeader { + font-size: 6vmin; + color: #2c3e50; + margin-bottom: 20px; + font-weight: bold; +} + +#levelsList { + height: 50vh; + overflow-y: auto; + background-color: #ecf0f1; + border: 2px solid #bdc3c7; + border-radius: 10px; + padding: 10px; + margin-bottom: 20px; + display: flex; + flex-wrap: wrap; + justify-content: space-around; + align-content: flex-start; +} + +.level-item { + width: 30%; + margin: 5px; + padding: 15px 10px; + background-color: #2ecc71; + color: white; + border-radius: 8px; + cursor: pointer; + font-size: 4vmin; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.level-item.locked { + background-color: #95a5a6; + cursor: default; +} + +.level-item.locked::after { + content: '🔒'; + position: absolute; + font-size: 2vmin; + top: 5px; + right: 5px; +} + +.level-item.completed { + background-color: #2ecc71; +} + +#backToWelcomeBtn { + background-color: #2c3e50; + color: #ecf0f1; + padding: 15px 30px; + font-size: 5vmin; + border-radius: 10px; + cursor: pointer; + margin: 10px auto; + width: 70%; + max-width: 300px; +} + +.completeContainer { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + width: 80%; + max-width: 400px; + text-align: center; +} + +#completeTitle { + font-size: 7vmin; + color: #2ecc71; + margin-bottom: 20px; +} + +#completeLevelNum { + font-size: 6vmin; + color: #2c3e50; + margin-bottom: 10px; +} + +#completeScore { + font-size: 5vmin; + color: #2c3e50; + margin-bottom: 30px; +} + +#nextLevelBtn, #replayLevelBtn { + background-color: #2c3e50; + color: #ecf0f1; + padding: 15px 30px; + font-size: 5vmin; + border-radius: 10px; + cursor: pointer; + margin: 10px auto; + width: 70%; + max-width: 300px; +} + #afterhr { margin:0; padding:0;