Merge pull request #2313 from berkenbu/master

Add lightning mode to F9 lander app, new app "Tetris"
pull/2297/head^2
Gordon Williams 2022-11-28 10:23:49 +00:00 committed by GitHub
commit 62826cbb27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 264 additions and 5 deletions

View File

@ -1 +1,2 @@
0.01: New App!
0.02: Add lightning

View File

@ -46,6 +46,9 @@ var booster = { x : g.getWidth()/4 + Math.random()*g.getWidth()/2,
var exploded = false;
var nExplosions = 0;
var landed = false;
var lightning = 0;
var settings = require("Storage").readJSON('f9settings.json', 1) || {};
const gravity = 4;
const dt = 0.1;
@ -61,18 +64,40 @@ function flameImageGen (throttle) {
function drawFalcon(x, y, throttle, angle) {
g.setColor(1, 1, 1).drawImage(falcon9, x, y, {rotate:angle});
if (throttle>0) {
if (throttle>0 || lightning>0) {
var flameImg = flameImageGen(throttle);
var r = falcon9.height/2 + flameImg.height/2-1;
var xoffs = -Math.sin(angle)*r;
var yoffs = Math.cos(angle)*r;
if (Math.random()>0.7) g.setColor(1, 0.5, 0);
else g.setColor(1, 1, 0);
g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle});
if (throttle>0) g.drawImage(flameImg, x+xoffs, y+yoffs, {rotate:angle});
if (lightning>1 && lightning<30) {
for (var i=0; i<6; ++i) {
var r = Math.random()*6;
var x = Math.random()*5 - xoffs;
var y = Math.random()*5 - yoffs;
g.setColor(1, Math.random()*0.5+0.5, 0).fillCircle(booster.x+x, booster.y+y, r);
}
}
}
}
function drawLightning() {
var c = {x:cloudOffs+50, y:30};
var dx = c.x-booster.x;
var dy = c.y-booster.y;
var m1 = {x:booster.x+0.6*dx+Math.random()*20, y:booster.y+0.6*dy+Math.random()*10};
var m2 = {x:booster.x+0.4*dx+Math.random()*20, y:booster.y+0.4*dy+Math.random()*10};
g.setColor(1, 1, 1).drawLine(c.x, c.y, m1.x, m1.y).drawLine(m1.x, m1.y, m2.x, m2.y).drawLine(m2.x, m2.y, booster.x, booster.y);
}
function drawBG() {
if (lightning==1) {
g.setBgColor(1, 1, 1).clear();
Bangle.buzz(200);
return;
}
g.setBgColor(0.2, 0.2, 1).clear();
g.setColor(0, 0, 1).fillRect(0, g.getHeight()-oceanHeight, g.getWidth()-1, g.getHeight()-1);
g.setColor(0.5, 0.5, 1).fillCircle(cloudOffs+34, 30, 15).fillCircle(cloudOffs+60, 35, 20).fillCircle(cloudOffs+75, 20, 10);
@ -88,6 +113,7 @@ function renderScreen(input) {
drawBG();
showFuel();
drawFalcon(booster.x, booster.y, Math.floor(input.throttle*12), input.angle);
if (lightning>1 && lightning<6) drawLightning();
}
function getInputs() {
@ -97,6 +123,7 @@ function getInputs() {
if (t > 1) t = 1;
if (t < 0) t = 0;
if (booster.fuel<=0) t = 0;
if (lightning>0 && lightning<20) t = 0;
return {throttle: t, angle: a};
}
@ -121,7 +148,6 @@ function gameStep() {
else {
var input = getInputs();
if (booster.y >= targetY) {
// console.log(booster.x + " " + booster.y + " " + booster.vy + " " + droneX + " " + input.angle);
if (Math.abs(booster.x-droneX-droneShip.width/2)<droneShip.width/2 && Math.abs(input.angle)<Math.PI/8 && booster.vy<maxV) {
renderScreen({angle:0, throttle:0});
epilogue("You landed!");
@ -129,6 +155,8 @@ function gameStep() {
else exploded = true;
}
else {
if (lightning) ++lightning;
if (settings.lightning && (lightning==0||lightning>40) && Math.random()>0.98) lightning = 1;
booster.x += booster.vx*dt;
booster.y += booster.vy*dt;
booster.vy += gravity*dt;

View File

@ -1,7 +1,7 @@
{ "id": "f9lander",
"name": "Falcon9 Lander",
"shortName":"F9lander",
"version":"0.01",
"version":"0.02",
"description": "Land a rocket booster",
"icon": "f9lander.png",
"screenshots" : [ { "url":"f9lander_screenshot1.png" }, { "url":"f9lander_screenshot2.png" }, { "url":"f9lander_screenshot3.png" }],
@ -10,6 +10,7 @@
"supports" : ["BANGLEJS", "BANGLEJS2"],
"storage": [
{"name":"f9lander.app.js","url":"app.js"},
{"name":"f9lander.img","url":"app-icon.js","evaluate":true}
{"name":"f9lander.img","url":"app-icon.js","evaluate":true},
{"name":"f9lander.settings.js", "url":"settings.js"}
]
}

36
apps/f9lander/settings.js Normal file
View File

@ -0,0 +1,36 @@
// This file should contain exactly one function, which shows the app's settings
/**
* @param {function} back Use back() to return to settings menu
*/
const boolFormat = v => v ? /*LANG*/"On" : /*LANG*/"Off";
(function(back) {
const SETTINGS_FILE = 'f9settings.json'
// initialize with default settings...
let settings = {
'lightning': false,
}
// ...and overwrite them with any saved values
// This way saved values are preserved if a new version adds more settings
const storage = require('Storage')
const saved = storage.readJSON(SETTINGS_FILE, 1) || {}
for (const key in saved) {
settings[key] = saved[key];
}
// creates a function to safe a specific setting, e.g. save('color')(1)
function save(key) {
return function (value) {
settings[key] = value;
storage.write(SETTINGS_FILE, settings);
}
}
const menu = {
'': { 'title': 'OpenWind' },
'< Back': back,
'Lightning': {
value: settings.lightning,
format: boolFormat,
onchange: save('lightning'),
}
}
E.showMenu(menu);
})

8
apps/tetris/README.md Normal file
View File

@ -0,0 +1,8 @@
# Tetris
Bangle version of the classic game of Tetris.
## Controls
Tapping the screen rotates the pieces once, swiping left, right or down moves the
piece in that direction, if possible.

1
apps/tetris/app-icon.js Normal file
View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+If4A/AH4A/AH4A/ABe5AA0jABwvYAIovBgABEFAQHFL7IuEL4QuFA45fcF4YuNL7i/FFwoHHL7QvFFxpfaF4wAOF/4nHF5+0AAy3SXYoHGW4QBDF4MAAIgvRFwwHHdAbqDFIQuDL6ouJL4ovDFwpfUAAoHFL4a/FFwhfTFxZfDF4ouFL6QANFopfDF/4vNjwAGF8ABFF4MAAIgvBX4IBDX4YBDL6TyFFIIuEL4QuEL4QuEL6ovDFwpfFF4YuFL6i/FFwhfEX4ouEL6YvFFwpfDF4ouFL6QvGAAwtFL4Yv/AAonHAB4vHG563CAIbuDA5i/CAIb2DA4hfJEwoHPFApZEGwpfLFyJfFFxJfMAAoHNFAa5GX54uTL4YuLL5QAVFowAIF+4A/AH4A/AH4A/AHY"))

14
apps/tetris/metadata.json Normal file
View File

@ -0,0 +1,14 @@
{ "id": "tetris",
"name": "Tetris",
"shortName":"Tetris",
"version":"0.01",
"description": "Tetris",
"icon": "tetris.png",
"readme": "README.md",
"tags": "games",
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"tetris.app.js","url":"tetris.app.js"},
{"name":"tetris.img","url":"app-icon.js","evaluate":true}
]
}

170
apps/tetris/tetris.app.js Normal file
View File

@ -0,0 +1,170 @@
const block = Graphics.createImage(`
########
# # # ##
## # ###
# # ####
## #####
# ######
########
########
`);
const tcols = [ {r:0, g:0, b:1}, {r:0, g:1, b:0}, {r:0, g:1, b:1}, {r:1, g:0, b:0}, {r:1, g:0, b:1}, {r:1, g:1, b:0}, {r:1, g:0.5, b:0.5} ];
const tiles = [
[[0, 0, 0, 0],
[0, 0, 0, 0],
[1, 1, 1, 1],
[0, 0, 0, 0]],
[[0, 0, 0],
[0, 1, 0],
[1, 1, 1]],
[[0, 0, 0],
[1, 0, 0],
[1, 1, 1]],
[[0, 0, 0],
[0, 0, 1],
[1, 1, 1]],
[[0, 0, 0],
[1, 1, 0],
[0, 1, 1]],
[[0, 0, 0],
[0, 1, 1],
[1, 1, 0]],
[[1, 1],
[1, 1]]
];
const ox = 176/2 - 5*8;
const oy = 8;
var pf = Array(23).fill().map(()=>Array(12).fill(0)); // field is really 10x20, but adding a border for collision checks
pf[20].fill(1);
pf[21].fill(1);
pf[22].fill(1);
pf.forEach((x,i) => { pf[i][0] = 1; pf[i][11] = 1; });
function rotateTile(t, r) {
var nt = JSON.parse(JSON.stringify(t));
if (t.length==2) return nt;
var s = t.length;
for (m=0; m<r; ++m) {
tl = JSON.parse(JSON.stringify(nt));
for (i=0; i<s; ++i)
for (j=0; j<s; ++j)
nt[i][j] = tl[s-1-j][i];
}
return nt;
}
function drawBoundingBox() {
g.setBgColor(0, 0, 0).clear().setColor(1, 1, 1);
g.theme.bg = 0;
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) {
if (qClear) g.setColor(0);
else g.setColor(tcols[n].r, tcols[n].g, tcols[n].b);
for (i=0; i<tile.length; ++i)
for (j=0; j<tile.length; ++j)
if (tile[j][i]>0)
if (qClear) g.fillRect(x+8*i, y+8*j, x+8*(i+1)-1, y+8*(j+1)-1);
else g.drawImage(block, x+8*i, y+8*j);
}
function showNext(n, r) {
var nt = rotateTile(tiles[n], r);
g.setColor(0).fillRect(176-33, 40, 176-33+33, 82);
drawTile(nt, ntn, 176-33, 40);
}
var time = Date.now();
var px=4, py=0;
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;
function redrawPF(ly) {
for (y=0; y<=ly; ++y)
for (x=1; x<11; ++x) {
c = pf[y][x];
if (c>0) g.setColor(tcols[c-1].r, tcols[c-1].g, tcols[c-1].b).drawImage(block, ox+(x-1)*8, oy+y*8);
else g.setColor(0, 0, 0).fillRect(ox+(x-1)*8, oy+y*8, ox+x*8-1, oy+(y+1)*8-1);
}
}
function insertAndCheck() {
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
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);
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);
}
}
// spawn new tile
px = 4; py = 0;
ctn = ntn;
ntn = Math.floor(Math.random()*7);
ct = rotateTile(tiles[ctn], ntr);
ntr = Math.floor(Math.random()*4);
showNext(ntn, ntr);
}
function moveOk(t, dx, dy) {
var ok = true;
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 gameStep() {
if (Date.now()-time > dropInterval) { // drop one step
time = Date.now();
if (moveOk(ct, 0, 1)) {
drawTile(ct, ctn, ox+px*8, oy+py*8, true);
py++;
}
else { // reached the bottom
insertAndCheck(ct, ctn, px, py);
}
drawTile(ct, ctn, ox+px*8, oy+py*8, false);
}
}
Bangle.setUI();
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.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);
}
});
drawBoundingBox();
g.setColor(1, 1, 1).setFontAlign(0, 1, 0).setFont("6x15", 1).drawString("Lines", 22, 30).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);
var gi = setInterval(gameStep, 20);

BIN
apps/tetris/tetris.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 B