Merge pull request #1886 from pidajo/master

Pong Clock
pull/1929/head
Gordon Williams 2022-06-07 08:55:20 +01:00 committed by GitHub
commit 473e916878
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 444 additions and 0 deletions

1
apps/pongclock/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First release

15
apps/pongclock/README.md Normal file
View File

@ -0,0 +1,15 @@
# Pong Clock
A clock which is playing Pong while showing the current time as score
* Settings
* Show or hide widgets (auto detecting the used widgets areas)
* Use inverted or standard theme colors for the play area
* Optionally pause while locked (saving battery)
* Loosely based on [https://codepen.io/Rabrennie/pen/WxNEoe](https://codepen.io/Rabrennie/pen/WxNEoe)
![](screenshot.png)
![](screenshot_settings.png)
![](screenshot_invers_full.png)
## Creator
[@pidajo](https://github.com/pidajo)

310
apps/pongclock/app.js Normal file
View File

@ -0,0 +1,310 @@
class Ball {
constructor(collision) {
this.collision = collision;
this.w = 4;
this.h = this.w;
this.y = height / 2 - this.h / 2;
this.x = width / 2 - this.w / 2;
this.oldX = this.x;
this.oldY = this.y;
this.velX = 6;
this.velY = 3.5 + Math.random();
}
reset() {
this.y = height / 2 - this.h / 2;
this.x = width / 2 - this.w / 2;
this.velX = 6;
this.velY = 3.5 + Math.random();
}
checkCollision(that, isLeft) {
let test = false;
if (isLeft) {
test = this.x <= that.w + this.w && this.y > that.y && this.y < that.y + that.h;
} else {
test = this.x >= that.x + this.w && this.y > that.y && this.y < that.y + that.h;
}
if (test) {
this.velX = -this.velX;
this.velY = (3.5 + 2 * Math.random()) * this.velY / Math.abs(this.velY);
if (isLeft) {
right.follow = this;
left.follow = null;
} else {
left.follow = this;
right.follow = null;
}
}
}
move() {
if (this.velX > 0) {
this.checkCollision(right, false);
} else {
this.checkCollision(left, true);
}
this.x += this.velX;
this.y += this.velY;
if (this.y <= this.h) {
this.y = this.h;
this.velY = -this.velY;
}
if (this.y >= height - this.h) {
this.y = height - this.h;
this.velY = -this.velY;
}
if (this.x >= width) {
left.scored();
restart();
} else if (this.x < 0) {
right.scored();
restart();
}
}
}
class Paddle {
constructor(side) {
this.side = side;
this.w = 4; //15;
this.h = 30; //80;
this.y = height / 2 - this.h / 2;
this.follow = null;
this.target = height / 2 - this.h / 2;
this.score = 99;
this.hasLost = false;
}
reset() {
this.follow = null;
this.hasLost = false;
this.target = height / 2 - this.h / 2;
this.y = height / 2 - this.h / 2;
this.move();
}
scored() {
let d = new Date();
let value = 0;
if (this.side == "left") {
value = d.getHours();
} else {
value = d.getMinutes();
}
if (this.score < value) {
this.score++;
} else {
this.score = value;
}
}
move() {
if (this.follow && !this.hasLost) {
var dy = this.follow.y - this.y - this.h / 2;
this.y += dy / 2;
} else {
this.y += (this.target - this.y) / 10;
}
if (this.y < 0) {
this.y = 0;
}
if (this.y > height - this.h) {
this.y = height - this.h;
}
}
}
var updateTimeout = null;
function update() {
var d = new Date();
var lastStep = Date.now();
left.move();
right.move();
if (d.getHours() != left.score) {
right.follow = null;
right.hasLost = true;
}
if (d.getMinutes() != right.score) {
left.follow = null;
left.hasLost = true;
}
ball.move();
redraw();
var nextStep = 40 - (Date.now() - lastStep);
//console.log(nextStep);
updateTimeout = setTimeout(update, nextStep > 0 ? nextStep : 0);
return lastStep;
}
function redraw() {
let fontHeight = width / 3.6;
let fontTop = top + height / 11;
let topHeight = top + height;
g.reset();
if (settings.isInvers) {
g.setColor(g.theme.bg);
g.setBgColor(g.theme.fg);
}
g.clearRect(0, top + left.oldY, left.w, top + left.oldY + left.h);
g.clearRect(width - right.w, top + right.oldY, width, top + right.oldY + right.h);
//g.clearRect(width / 2 - fontHeight * 1.4, fontTop, width / 2 + fontHeight * 1.4, fontTop + fontHeight);
g.clearRect(ball.oldX - ball.w, top + ball.oldY - ball.h, ball.oldX + ball.w, top + ball.oldY + ball.h);
g.setFontVector(fontHeight);
/**/
g.setFontAlign(1, -1);
g.drawString(("0" + left.score).substr(-2), 5 * width / 11, fontTop, true);
g.setFontAlign(-1, -1);
g.drawString(("0" + right.score).substr(-2), 6 * width / 11, fontTop, true);
/**/
g.drawLine(width / 2, top, width / 2, topHeight);
g.fillRect(0, top + left.y, left.w, top + left.y + left.h);
left.oldY = left.y;
g.fillRect(width - right.w, top + right.y, width, top + right.y + right.h);
right.oldY = right.y;
g.fillCircle(ball.x, top + ball.y, ball.w);
ball.oldX = ball.x;
ball.oldY = ball.y;
}
function restart() {
g.reset();
if (settings.isInvers) {
g.setColor(g.theme.bg);
g.setBgColor(g.theme.fg);
}
g.clearRect(0, top, width, top + height);
ball.reset();
left.reset();
right.reset();
right.follow = ball;
left.move();
right.move();
if (settings.withWidgets) {
Bangle.drawWidgets();
}
}
function stop() {
if (updateTimeout) {
clearTimeout(updateTimeout);
}
updateTimeout = null;
if (pauseTimeout) {
clearTimeout(pauseTimeout);
}
pauseTimeout = null;
}
var pauseTimeout = null;
function pause() {
stop();
left.scored();
right.scored();
redraw();
pauseTimeout = setTimeout(pause, Date.now() % 60000);
}
//load settings
const SETTINGS_FILE = "pongclock.json";
var settings = Object.assign({
// default values
withWidgets: true,
isInvers: false,
playLocked: true,
}, require('Storage').readJSON(SETTINGS_FILE, true) || {});
require('Storage').writeJSON(SETTINGS_FILE, settings);
//make clock
Bangle.setUI("clock");
//setup play area
var height = g.getHeight(),
width = g.getWidth();
var top = 0;
g.reset();
g.clearRect(0, top, width, height);
if (settings.withWidgets) {
Bangle.loadWidgets();
Bangle.drawWidgets();
//console.log(WIDGETS);
if (global.WIDGETS) {
let bottom = 0;
for (var i in WIDGETS) {
var w = WIDGETS[i];
if (w.area) {
if (w.area.indexOf("t") >= 0) {
top = Bangle.appRect.y;
}
if (w.area.indexOf("b") >= 0) {
bottom = height - Bangle.appRect.y2;
}
}
}
height -= top + bottom;
}
}
if (settings.isInvers) {
g.setColor(g.theme.bg);
g.setBgColor(g.theme.fg);
}
g.clearRect(0, top, width, top + height);
//setup game
var left = new Paddle("left");
var right = new Paddle("right");
var ball = new Ball(true);
left.x = 20;
right.x = width - 20;
left.scored();
right.scored();
Bangle.on("lock", (on) => {
//console.log(on);
if (!settings.playLocked) {
if (on) {
pause();
} else {
stop();
update();
}
}
});
//start clock
restart();
if (!settings.playLocked && Bangle.isLocked()) {
pause();
} else {
update();
}
/*
//local testing
require("Storage").write("pongclock.info",{
"id":"pongclock",
"name":"Pong Clock",
"type":"clock",
"src":"pongclock.app.js",
"icon":"pongclock.img"
});
*/

View File

@ -0,0 +1,20 @@
{ "id": "pongclock",
"name": "Pong Clock",
"shortName":"Pong Clock",
"icon": "pongclock.png",
"version":"0.01",
"description": "A Pong playing clock",
"tags": "",
"allow_emulator":true,
"supports": ["BANGLEJS", "BANGLEJS2"],
"readme":"README.md",
"screenshots" : [ { "url":"screenshot.png" }, { "url":"screenshot_settings.png" }, { "url":"screenshot_invers_full.png" } ],
"storage": [
{"name":"pongclock.app.js","url":"app.js"},
{"name":"pongclock.img","url":"pongclock-icon.js","evaluate":true},
{"name":"pongclock.settings.js","url":"settings.js"}
],
"data": [
{"name":"pongclock.json"}
]
}

View File

@ -0,0 +1 @@
atob("MDCEBAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAIiAAAAAAAAAAEAAAAAAAABP/EAE//yAAAAAAAD8wAAAT//IAAAAAAv//EA////IAAAAAAP8gAAH///8gAAAAAvP/EB/xA/MAIiAAAf8QAAL/ED8wAAAAAxD/EC8wAf8ALyAAAv8AAAPyAC/wAAAAAAD/ED/wAf8ALyAAD/MAAA/zAC8wAAAAAAD/EAAAAv8ALyAAH/EAAAAAAD8wAAAAAAD/EAAAA/IALyAAL/AAAAAAAP8gAAAAAAD/EAAAH/EALyAAPzD/IAAAAv8AAAAAAAD/EAAAPzAALyAA/yD/IAAAD/IAAAAAAAD/EAAB/xAALyAB/xD/IAAAL/AAAAAAAAD/EAAP8wAALyAC/wD/IAAB/yAAAAAAAAD/EAAv8QAAIiAP8wD/IAAD/wAAAAAAAAD/EAD/MAAAAAAf/zP/MwAf8gAAAA//AAD/EAL/AAAAAAAf////8wA/8AAAAA//AAD/EB/zAAAAIiACIiL/MgL/IAAAAA//AAD/EC////8ALyAAAAD/IAP////wAA//AAD/EC////8ALyAAAAD/IAP////wAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAALyAAAAAAAAAAAAAAAA//AAAAAAAAAAAAIiAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAAAD/8A//AAAAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAIiAAAAAAAAAAAAD/8AAAAAAAASEAAAAALyAAAAAAAAAAAAD/8AAAAAAAP/8wAAAALyAAAAAAAAAAAAD/8AAAAAAB///xAAAALyAAAAAAAAAAAAD/8AAAAAAC///yAAAALyAAAAAAAAAAAAD/8AAAAAAB///xAAAALyAAAAAAAAAAAAD/8AAAAAAAP/8wAAAALyAAAAAAAAAAAAD/8AAAAAAAASEAAAAALyAAAAAAAAAAAAD/8AAAAAAAAAAAAAAALyAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAIiAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALyAAAAAAAAAAAAAAAA==")

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 755 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 480 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,44 @@
(function(back) {
var FILE = "pongclock.json";
// Load settings
var settings = Object.assign({
// default values
withWidgets: true,
isInvers: false,
playLocked: true,
}, require('Storage').readJSON(FILE, true) || {});
function writeSettings() {
require('Storage').writeJSON(FILE, settings);
}
// Show the menu
E.showMenu({
"" : { "title" : "Pong Clock" },
"< Back" : () => back(),
'Widgets?': {
value: !!settings.withWidgets, // !! converts undefined to false
format: v => v?"Show":"Hide",
onchange: v => {
settings.withWidgets = v;
writeSettings();
}
},
'Inverted?': {
value: !!settings.isInvers, // !! converts undefined to false
format: v => v?"Yes":"No",
onchange: v => {
settings.isInvers = v;
writeSettings();
}
},
'On Lock?': {
value: !!settings.playLocked, // !! converts undefined to false
format: v => v?"Play":"Pause",
onchange: v => {
settings.playLocked = v;
writeSettings();
}
}
});
})/*(load)/**/

1
apps/widday/ChangeLog Normal file
View File

@ -0,0 +1 @@
0.01: First release

9
apps/widday/README.md Normal file
View File

@ -0,0 +1,9 @@
# Day Widget
Just shows the day of the current date, to save space in the widget area. The month and year should be known because they don't change that often. Just the number in maximum size for readability.
![](screenshot.png)
## Creator
[@pidajo](https://github.com/pidajo)

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

@ -0,0 +1 @@
atob("MDCEAiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIiIBEREREREREREREREREREREREREREQIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////zMz////8zMzMzMzP////xIiIf///////czM////PMzMzMzMzP///xIiIf/////zzMzM////PMzMzMzMzP///xIiIf////PczMzM////PMzMzMzMzP///xIiIf////3MzMzM////8zMzMz3MzP///xIiIf////3MM8zM//////////zMw////xIiIf////0/88zM/////////zzMz////xIiIf//////88zM/////////8zM3////xIiIf//////88zM////////88zM/////xIiIf//////88zM/////////MzN/////xIiIf//////88zM////////PMzD/////xIiIf//////88zM////////3Mzf/////xIiIf//////88zM////////zMw//////xIiIf//////88zM///////9zMz//////xIiIf//////88zM///////8zMP//////xIiIf//////88zM//////88zM///////xIiIf//////88zM///////MzN///////xIiIf////8zM8zM//////PMzD///////xIiIf////3MzMzMzM3///3Mzf///////xIiIf////3MzMzMzM3//zzMw////////xIiIf////PMzMzMzM3//9zMz////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIf///////////////////////////xIiIBEREREREREREREREREREREREREREQIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIg==")

15
apps/widday/metadata.json Normal file
View File

@ -0,0 +1,15 @@
{ "id": "widday",
"name": "Day Widget",
"shortName":"My Timer",
"icon": "widget.png",
"type": "widget",
"version":"0.01",
"description": "Just the day of the current date as widget",
"readme": "README.md",
"tags": "widget,say,date",
"supports": ["BANGLEJS", "BANGLEJS2"],
"screenshots" : [ { "url":"screenshot.png" } ],
"storage": [
{"name":"widday.wid.js","url":"widget.js"}
]
}

BIN
apps/widday/screenshot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 938 B

27
apps/widday/widget.js Normal file
View File

@ -0,0 +1,27 @@
(() => {
var width = 32; // width of the widget
function draw() {
var date = new Date();
g.reset(); // reset the graphics context to defaults (color/font/etc)
g.setFontAlign(0,1); // center fonts
//g.drawRect(this.x, this.y, this.x+width-1, this.y+23); // check the bounds!
var text = date.getDate();
g.setFont("Vector", 24);
g.drawString(text, this.x+width/2+1, this.y + 28);
//g.setColor(0, 0, 1);
//g.drawRect(this.x, this.y, this.x+width-2, this.y+1);
}
setInterval(function() {
WIDGETS["widday"].draw(WIDGETS["widdateday"]);
}, 10*60000); // update every 10 minutes
// add your widget
WIDGETS["widday"]={
area:"bl", // tl (top left), tr (top right), bl (bottom left), br (bottom right)
width: width, // how wide is the widget? You can change this and call Bangle.drawWidgets() to re-layout
draw:draw // called to draw the widget
};
})()

BIN
apps/widday/widget.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 778 B