From 1a1b79d41bbf657f244c16f55d47982153bdfe6c Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Sun, 21 Jan 2024 19:24:31 -0500 Subject: [PATCH 1/3] tetris: major overhaul Added score, levels, bugfixes and misc, inspired by NES tetris --- apps/tetris/ChangeLog | 1 + apps/tetris/metadata.json | 2 +- apps/tetris/tetris.app.js | 255 +++++++++++++++++++++++--------------- 3 files changed, 154 insertions(+), 104 deletions(-) diff --git a/apps/tetris/ChangeLog b/apps/tetris/ChangeLog index 86661f1b6..158cccc5b 100644 --- a/apps/tetris/ChangeLog +++ b/apps/tetris/ChangeLog @@ -2,3 +2,4 @@ 0.02: Better controls, implement game over. 0.03: Implement mode and level selection screens. 0.04: Bring back old controls as "swipe" in menu, exit with button press +0.10: Major overhaul: added score, levels, bugfixes and misc, inspired by NES tetris diff --git a/apps/tetris/metadata.json b/apps/tetris/metadata.json index ba73918d5..8b73ffb43 100644 --- a/apps/tetris/metadata.json +++ b/apps/tetris/metadata.json @@ -1,7 +1,7 @@ { "id": "tetris", "name": "Tetris", "shortName":"Tetris", - "version":"0.04", + "version":"0.10", "description": "Tetris", "icon": "tetris.png", "readme": "README.md", diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index e4295732b..baad84f77 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -41,7 +41,7 @@ const oy = 8; 2 .. accelerometer. 12 lines record. 3 .. altimeter */ -var control = 0, level = 0; +var control = 0, level = 0, lines = 0, score = 0; var alt_start = -9999; /* For altimeter control */ /* 0 .. menu 1 .. game @@ -77,7 +77,9 @@ function drawBoundingBox() { for (i=0; i<4; ++i) g.drawRect(ox-i-1, oy-i-1, ox+10*8+i, oy+20*8+i); } -function drawTile (tile, n, x, y, qClear) { +function drawTile(tile, n, x, y, qClear) { + if (state != 1) // stops tile from being drawn on the game over screen + return; if (qClear) g.setColor(0); else g.setColor(tcols[n].r, tcols[n].g, tcols[n].b); for (i=0; i0) pf[py+y][px+x+1] = ctn+1; // check for full lines + let clearCount = 0; for (y=19; y>0; y--) { var qFull = true; for (x=1; x<11; ++x) qFull &= pf[y][x]>0; if (qFull) { - nlines++; - dropInterval -= 5; - Bangle.buzz(30); + clearCount++; for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); redrawPF(y); - g.setColor(0).fillRect(5, 30, 41, 80).setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); } } + if (clearCount) { + lines += clearCount; + let effectiveLevel = Math.max(level, 1); + if (clearCount == 1) { // single + score += 100 * effectiveLevel; + Bangle.buzz(80, 0.5); + } + else if (clearCount == 2) { // double + score += 300 * effectiveLevel; + Bangle.buzz(80); + } + else if (clearCount == 3) { // triple + score += 500 * effectiveLevel; + Bangle.buzz(150); + } + else if (clearCount >= 4) { // tetris + score += 800 * effectiveLevel; + Bangle.buzz(300); + } + if (lines != 0 && lines % 10 == 0) { + level++; + calculateSpeed(); + } + redrawStats(); + } // spawn new tile px = 4; py = 0; ctn = ntn; @@ -149,18 +204,33 @@ function insertAndCheck() { function moveOk(t, dx, dy) { var ok = true; - for (y=0; y 0) ok = false; return ok; } +function pauseGame() { + print("Paused"); + state = 3; +} + +function resumeGame() { + print("Resumed"); + state = 1; +} + function gameStep() { if (state != 1) return; if (Date.now()-time > dropInterval) { // drop one step time = Date.now(); - if (moveOk(ct, 0, 1)) { + if (level >= 15 && moveOk(ct, 0, 2)) { + // at level 15, pieces drop twile as quickly + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + py += 2; + } + else if (moveOk(ct, 0, 1)) { drawTile(ct, ctn, ox+px*8, oy+py*8, true); py++; } @@ -181,16 +251,18 @@ function rotate() { } function move(x, y) { - if (moveOk(ct, x, y)) { + r = moveOk(ct, x, y); + if (r) { drawTile(ct, ctn, ox+px*8, oy+py*8, true); px += x; py += y; drawTile(ct, ctn, ox+px*8, oy+py*8, false); } + return r; } function linear(x) { - print("Linear: ", x); + //print("Linear: ", x); let now = px / 10; if (x < now-0.06) move(-1, 0); @@ -200,36 +272,16 @@ function linear(x) { function newGame() { E.showMenu(); - Bangle.setUI({mode : "custom", btn: () => load()}); - if (control == 4) { // Swipe - Bangle.on("touch", (e) => { - t = rotateTile(ct, 3); - if (moveOk(t, 0, 0)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); - ct = t; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); - } + Bangle.setUI(); + if (control == 2) { + Bangle.on("accel", (e) => { + if (state != 1) return; + if (control != 2) return; + print(e.x); + linear((0.2-e.x) * 2.5); }); - - Bangle.on("swipe", (x,y) => { - if (y<0) y = 0; - if (moveOk(ct, x, y)) { - drawTile(ct, ctn, ox+px*8, oy+py*8, true); - px += x; - py += y; - drawTile(ct, ctn, ox+px*8, oy+py*8, false); - } - }); - } else { // control != 4 - if (control == 2) { // Tilt - Bangle.on("accel", (e) => { - if (state != 1) return; - if (control != 2) return; - print(e.x); - linear((0.2-e.x) * 2.5); - }); - } - if (control == 3) { // Move + } + if (control == 3) { Bangle.setBarometerPower(true); Bangle.on("pressure", (e) => { if (state != 1) return; @@ -238,86 +290,83 @@ function newGame() { if (alt_start == -9999) alt_start = a; a = a - alt_start; - print(e.altitude, a); + //print(e.altitude, a); linear(a); }); - } - Bangle.on("drag", (e) => { - let h = 176/2; - if (state == 2) { - if (e.b) - selectGame(); - return; - } - if (!e.b) - return; - if (state == 0) return; - if (e.y < h) { - if (e.x < h) - rotate(); - else { - let i = 0; - for (i=0; i<10; i++) { - move(0, 1); - g.flip(); - } - } - } else { - if (control == 1) - linear((e.x - 20) / 156); - if (control != 0) - return; - if (e.x < h) - move(-1, 0); - else - move(1, 0); - } - }); } + Bangle.on("drag", (e) => { + let h = 176/2; + if (state == 2) { + if (e.b) + selectGame(); + return; + } + if (!e.b) + return; + if (state == 0) return; + if (e.y < h) { + if (e.x < h) + rotate(); + else { + while (move(0, 1)) { + score++; + g.flip(); + } + redrawStats(true); + } + } else { + if (control == 1) + linear((e.x - 20) / 156); + if (control != 0) + return; + if (e.x < h) + move(-1, 0); + else + move(1, 0); + } + }); + setWatch(() => { + if (state == 1) + pauseGame(); + else if (state == 3) + resumeGame(); + }, BTN1, {repeat: true}); initGame(); drawGame(); state = 1; - var step = 450 - 50*level; - if (control == 3) - step = step*2; - dropInterval = step; - var gi = setInterval(gameStep, 50); + calculateSpeed(); + var gi = setInterval(gameStep, 20); } function drawGame() { drawBoundingBox(); g.setColor(1, 1, 1).setFontAlign(0, 1, 0) - .setFont("6x15", 1).drawString("Lines", 22, 30) + .setFont("6x15", 1).drawString("Score", 22, 30) + .drawString("Level", 22, 80) + .drawString("Lines", 22, 130) .drawString("Next", 176-22, 30); showNext(ntn, ntr); - g.setColor(0).fillRect(5, 30, 41, 80) - .setColor(1, 1, 1).drawString(nlines.toString(), 22, 50); -} - -function selectLevel() { - print("Level selection menu"); - - var menu = {}; - menu["< Back"] = () => {selectGame();}; - menu[/*LANG*/"Level 1"] = () => { level = 0; selectGame(); }; - menu[/*LANG*/"Level 2"] = () => { level = 1; selectGame(); }; - menu[/*LANG*/"Level 3"] = () => { level = 2; selectGame(); }; - E.showMenu(menu); + redrawStats(); } function selectGame() { state = 0; print("Game selection menu"); - //for (let i = 0; i < 100000; i++) ; - + var menu = {}; - menu[/*LANG*/"Normal"] = () => { control = 0; newGame(); }; - menu[/*LANG*/"Drag"] = () => { control = 1; newGame(); }; - menu[/*LANG*/"Tilt"] = () => { control = 2; newGame(); }; - menu[/*LANG*/"Move"] = () => { control = 3; newGame(); }; - menu[/*LANG*/"Swipe"] = () => { control = 4; newGame(); }; - menu[/*LANG*/"Level"] = () => { selectLevel(); }; + menu["Normal"] = () => { control = 0; newGame(); }; + menu["Drag"] = () => { control = 1; newGame(); }; + menu["Tilt"] = () => { control = 2; newGame(); }; + menu["Pressure"] = () => { control = 3; newGame(); }; + level = 1; + menu["Level"] = { + value : 1, + min : 0, + max : 10, + wrap : true, + onchange : (l) => { level = l; } + }; E.showMenu(menu); } From 60bab26d838a2b45d15c734741bd00ab26e7d2b9 Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Mon, 22 Jan 2024 10:52:51 -0500 Subject: [PATCH 2/3] tetris: bug fixes, add back swipe controls --- apps/tetris/tetris.app.js | 132 ++++++++++++++++++++++---------------- 1 file changed, 77 insertions(+), 55 deletions(-) diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index baad84f77..1917dade8 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -115,7 +115,7 @@ function calculateSpeed() { step = 20; // usually limited by the hardware // levels 15+ are programmed to go faster by skipping lines } - print(`level ${level}: drop interval ${step}ms`) + print(`level ${level}: drop interval ${step}ms`); if (control == 3) step = step*2; dropInterval = step; @@ -272,59 +272,80 @@ function linear(x) { function newGame() { E.showMenu(); - Bangle.setUI(); - if (control == 2) { - Bangle.on("accel", (e) => { - if (state != 1) return; - if (control != 2) return; - print(e.x); - linear((0.2-e.x) * 2.5); - }); - } - if (control == 3) { - Bangle.setBarometerPower(true); - Bangle.on("pressure", (e) => { - if (state != 1) return; - if (control != 3) return; - let a = e.altitude; - if (alt_start == -9999) - alt_start = a; - a = a - alt_start; - //print(e.altitude, a); - linear(a); - }); - } - Bangle.on("drag", (e) => { - let h = 176/2; - if (state == 2) { - if (e.b) - selectGame(); - return; - } - if (!e.b) - return; - if (state == 0) return; - if (e.y < h) { - if (e.x < h) - rotate(); - else { - while (move(0, 1)) { - score++; - g.flip(); + Bangle.setUI({mode : "custom", btn: () => load()}); + if (control == 4) { // Swipe + Bangle.on("touch", (e) => { + t = rotateTile(ct, 3); + if (moveOk(t, 0, 0)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + ct = t; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); } - redrawStats(true); + }); + + Bangle.on("swipe", (x,y) => { + if (y<0) y = 0; + if (moveOk(ct, x, y)) { + drawTile(ct, ctn, ox+px*8, oy+py*8, true); + px += x; + py += y; + drawTile(ct, ctn, ox+px*8, oy+py*8, false); + } + }); + } else { // control != 4 + if (control == 2) { // Tilt + Bangle.on("accel", (e) => { + if (state != 1) return; + if (control != 2) return; + print(e.x); + linear((0.2-e.x) * 2.5); + }); } - } else { - if (control == 1) - linear((e.x - 20) / 156); - if (control != 0) - return; - if (e.x < h) - move(-1, 0); - else - move(1, 0); - } - }); + if (control == 3) { // Move + Bangle.setBarometerPower(true); + Bangle.on("pressure", (e) => { + if (state != 1) return; + if (control != 3) return; + let a = e.altitude; + if (alt_start == -9999) + alt_start = a; + a = a - alt_start; + //print(e.altitude, a); + linear(a); + }); + } + Bangle.on("drag", (e) => { + let h = 176/2; + if (state == 2) { + if (e.b) + selectGame(); + return; + } + if (!e.b) + return; + if (state == 0) return; + if (e.y < h) { + if (e.x < h) + rotate(); + else { + while (move(0, 1)) { + score++; + g.flip(); + } + redrawStats(true); + } + } else { + if (control == 1) + linear((e.x - 20) / 156); + if (control != 0) + return; + if (e.x < h) + move(-1, 0); + else + move(1, 0); + } + }); + } setWatch(() => { if (state == 1) pauseGame(); @@ -333,9 +354,9 @@ function newGame() { }, BTN1, {repeat: true}); initGame(); - drawGame(); - state = 1; calculateSpeed(); + state = 1; + drawGame(); var gi = setInterval(gameStep, 20); } @@ -346,8 +367,8 @@ function drawGame() { .drawString("Level", 22, 80) .drawString("Lines", 22, 130) .drawString("Next", 176-22, 30); - showNext(ntn, ntr); redrawStats(); + showNext(ntn, ntr); } function selectGame() { @@ -359,6 +380,7 @@ function selectGame() { menu["Drag"] = () => { control = 1; newGame(); }; menu["Tilt"] = () => { control = 2; newGame(); }; menu["Pressure"] = () => { control = 3; newGame(); }; + menu["Swipe"] = () => { control = 4; newGame(); }; level = 1; menu["Level"] = { value : 1, From a08e4dbd4f29199788b392834b516eb5311d55c7 Mon Sep 17 00:00:00 2001 From: Anonymous941 Date: Mon, 22 Jan 2024 13:36:45 -0500 Subject: [PATCH 3/3] tetris: add clear effect and more bug fixes --- apps/tetris/tetris.app.js | 52 +++++++++++++++++++++++++++++++-------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/apps/tetris/tetris.app.js b/apps/tetris/tetris.app.js index 1917dade8..d90627bde 100644 --- a/apps/tetris/tetris.app.js +++ b/apps/tetris/tetris.app.js @@ -134,7 +134,8 @@ function gameOver() { state = 0; g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22) .drawString("Game Over", 176/2, 76); - E.showAlert("Game Over").then(selectGame, print); + // this cannot allow changing game controls because it would set up duplicate events + E.showAlert("Game Over").then(startGame, print); lines = 0; score = 0; } @@ -151,19 +152,26 @@ function redrawStats(onlyScore) { } function insertAndCheck() { - for (y=0; y0) pf[py+y][px+x+1] = ctn+1; - // check for full lines let clearCount = 0; - for (y=19; y>0; y--) { + let linesToClear = []; + let yReal = 19; // the y for display purposes + // check for full lines + for (let y=19; y>0; y--) { var qFull = true; - for (x=1; x<11; ++x) qFull &= pf[y][x]>0; + for (let x=1; x<11; ++x) qFull &= pf[y][x]>0; if (qFull) { clearCount++; + linesToClear.push([y, yReal]); + print(`linesToClear.push(${y})`); + // clear the line, but do not display it yet for (ny=y; ny>0; ny--) pf[ny] = JSON.parse(JSON.stringify(pf[ny-1])); - redrawPF(y); + y++; } + yReal--; } if (clearCount) { lines += clearCount; @@ -178,11 +186,32 @@ function insertAndCheck() { } else if (clearCount == 3) { // triple score += 500 * effectiveLevel; - Bangle.buzz(150); + Bangle.buzz(200); } else if (clearCount >= 4) { // tetris score += 800 * effectiveLevel; - Bangle.buzz(300); + Bangle.buzz(500); + } + // the score will not be shown yet because redrawStats was not called + + // clear effect + let timer = getTime(); + g.setColor(0, 0, 0); + while (true) { + var rectLength = (getTime()-timer)/0.05 + 1; + if (rectLength > 6) + break; + var x1 = 6 - rectLength; + var x2 = 4 + rectLength; + for (let line of linesToClear) { + let y = line[1]; + g.fillRect(ox+x1*8, oy+y*8, ox+x2*8-1, oy+(y+1)*8-1); + } + g.flip(); + } + // display the cleared lines + for (let line of linesToClear) { + redrawPF(line[0]); } if (lines != 0 && lines % 10 == 0) { level++; @@ -301,7 +330,7 @@ function newGame() { linear((0.2-e.x) * 2.5); }); } - if (control == 3) { // Move + if (control == 3) { // Pressure Bangle.setBarometerPower(true); Bangle.on("pressure", (e) => { if (state != 1) return; @@ -352,7 +381,10 @@ function newGame() { else if (state == 3) resumeGame(); }, BTN1, {repeat: true}); + startGame(); +} +function startGame() { initGame(); calculateSpeed(); state = 1;