mirror of https://github.com/espruino/BangleApps
tetris: major overhaul
Added score, levels, bugfixes and misc, inspired by NES tetrispull/3163/head
parent
daf7e745ec
commit
1a1b79d41b
|
@ -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
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{ "id": "tetris",
|
||||
"name": "Tetris",
|
||||
"shortName":"Tetris",
|
||||
"version":"0.04",
|
||||
"version":"0.10",
|
||||
"description": "Tetris",
|
||||
"icon": "tetris.png",
|
||||
"readme": "README.md",
|
||||
|
|
|
@ -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; i<tile.length; ++i)
|
||||
|
@ -99,8 +101,25 @@ var ctn = Math.floor(Math.random()*7); // current tile number
|
|||
var ntn = Math.floor(Math.random()*7); // next tile number
|
||||
var ntr = Math.floor(Math.random()*4); // next tile rotation
|
||||
var ct = rotateTile(tiles[ctn], Math.floor(Math.random()*4)); // current tile (rotated)
|
||||
var dropInterval = 450;
|
||||
var nlines = 0;
|
||||
var dropInterval;
|
||||
|
||||
function calculateSpeed() {
|
||||
let step = 500;
|
||||
if (level <= 6) // 200-500ms
|
||||
step = step - 50*level;
|
||||
else if (level <= 13) { // 25-175ms
|
||||
step = 200;
|
||||
step = step - 25*(level - 6);
|
||||
}
|
||||
else {
|
||||
step = 20; // usually limited by the hardware
|
||||
// levels 15+ are programmed to go faster by skipping lines
|
||||
}
|
||||
print(`level ${level}: drop interval ${step}ms`)
|
||||
if (control == 3)
|
||||
step = step*2;
|
||||
dropInterval = step;
|
||||
}
|
||||
|
||||
function redrawPF(ly) {
|
||||
for (y=0; y<=ly; ++y)
|
||||
|
@ -112,29 +131,65 @@ function redrawPF(ly) {
|
|||
}
|
||||
|
||||
function gameOver() {
|
||||
state = 0;
|
||||
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("Vector",22)
|
||||
.drawString("Game Over", 176/2, 76);
|
||||
state = 0;
|
||||
E.showAlert("Game Over").then(selectGame, print);
|
||||
lines = 0;
|
||||
score = 0;
|
||||
}
|
||||
|
||||
function redrawStats(onlyScore) {
|
||||
g.setColor(0).fillRect(5, 30, 41, 60)
|
||||
.setColor(1, 1, 1).drawString(score.toString(), 22, 50);
|
||||
if (!onlyScore) {
|
||||
g.setColor(0).fillRect(5, 80, 41, 110)
|
||||
.setColor(1, 1, 1).drawString(level.toString(), 22, 100)
|
||||
.setColor(0).fillRect(5, 130, 41, 160)
|
||||
.setColor(1, 1, 1).drawString(lines.toString(), 22, 150);
|
||||
}
|
||||
}
|
||||
|
||||
function insertAndCheck() {
|
||||
for (y=0; y<ct.length; ++y)
|
||||
for (y=0; y<ct.length; ++y)
|
||||
for (x=0; x<ct[y].length; ++x)
|
||||
if (ct[y][x]>0) 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<t.length; ++y)
|
||||
for (y=0; y<t.length; ++y)
|
||||
for (x=0; x<t[y].length; ++x)
|
||||
if (t[y][x]*pf[py+dy+y][px+dx+x+1] > 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);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue