1
0
Fork 0
BangleApps/apps/marioclock/marioclock-app.js

405 lines
10 KiB
JavaScript
Raw Normal View History

/**
* BangleJS MARIO CLOCK
*
* + Original Author: Paul Cockrell https://github.com/paulcockrell
* + Created: April 2020
* + Based on Espruino Mario Clock V3 https://github.com/paulcockrell/espruino-mario-clock
* + Online Image convertor: https://www.espruino.com/Image+Converter, Use transparency + compression + 8bit Web + export as Image String
* + Images must be drawn as PNGs with transparent backgrounds
*/
const locale = require("locale");
const storage = require('Storage');
2020-04-03 08:18:05 +00:00
const settings = (storage.readJSON('setting.json', 1) || {});
const timeout = settings.timeout || 10;
const is12Hour = settings["12hour"] || false;
// Screen dimensions
let W, H;
let intervalRef, displayTimeoutRef = null;
// Colours
const LIGHTEST = "#effedd";
const LIGHT = "#add795";
const DARK = "#588d77";
const DARKEST = "#122d3e";
const NIGHT = "#001818";
// Character names
const TOAD = "toad";
const MARIO = "mario";
const characterSprite = {
frameIdx: 0,
x: 35,
y: 55,
jumpCounter: 0,
jumpIncrement: Math.PI / 6,
isJumping: false,
character: MARIO,
};
const coinSprite = {
frameIdx: 0,
x: 34,
y: 18,
isAnimating: false,
yDefault: 18,
};
const pyramidSprite = {
x: 90,
height: 34,
};
const ONE_SECOND = 1000;
let timer = 0;
let backgroundArr = [];
let nightMode = false;
function genRanNum(min, max) {
return Math.floor(Math.random() * (max - min + 1) + min);
}
function switchCharacter() {
const curChar = characterSprite.character;
let newChar;
if (curChar === MARIO) {
newChar = TOAD;
} else {
newChar = MARIO;
}
characterSprite.character = newChar;
}
function toggleNightMode() {
nightMode = !nightMode;
}
function incrementTimer() {
if (timer > 1000) {
timer = 0;
}
else {
timer += 50;
}
}
function drawBackground() {
// Clear screen
if (nightMode) {
g.setColor(NIGHT);
} else {
g.setColor(LIGHTEST);
}
g.fillRect(0, 10, W, H);
// set cloud colors
if (nightMode) {
g.setColor(DARKEST);
} else {
g.setColor(LIGHT);
}
// draw clouds
g.fillRect(0, 10, g.getWidth(), 15);
g.fillRect(0, 17, g.getWidth(), 17);
g.fillRect(0, 19, g.getWidth(), 19);
g.fillRect(0, 21, g.getWidth(), 21);
// Date bar
g.setColor(DARKEST);
g.fillRect(0, 0, W, 9);
}
function drawFloor() {
const fImg = require("heatshrink").decompress(atob("ikDxH+rgATCoIBQAQYDP")); // Floor image
for (let x = 0; x < 4; x++) {
g.drawImage(fImg, x * 20, g.getHeight() - 5);
}
}
function drawPyramid() {
const pPol = [pyramidSprite.x + 10, H - 6, pyramidSprite.x + 50, pyramidSprite.height, pyramidSprite.x + 90, H - 6]; // Pyramid poly
g.setColor(LIGHT);
g.fillPoly(pPol);
pyramidSprite.x -= 1;
// Reset and randomize pyramid if off-screen
if (pyramidSprite.x < - 100) {
pyramidSprite.x = 90;
pyramidSprite.height = Math.floor(Math.random() * (60 /* max */ - 25 /* min */ + 1) + 25 /* min */);
}
}
function drawTreesFrame(x, y) {
const tImg = require("heatshrink").decompress(atob("h8GxH+AAMHAAIFCAxADEBYgDCAQYAFCwobOAZAEFBxo=")); // Tree image
g.drawImage(tImg, x, y);
g.setColor(DARKEST);
g.drawLine(x + 6 /* Match stalk to palm tree */, y + 6 /* Match stalk to palm tree */, x + 6, H - 6);
}
function generateTreeSprite() {
return {
x: 90,
y: Math.floor(Math.random() * (60 /* max */ - 30 /* min */ + 1) + 30 /* min */)
};
}
function drawTrees() {
// remove first sprite if offscreen
let firstBackgroundSprite = backgroundArr[0];
if (firstBackgroundSprite) {
if (firstBackgroundSprite.x < -15) backgroundArr.splice(0, 1);
}
// set background sprite if array empty
let lastBackgroundSprite = backgroundArr[backgroundArr.length - 1];
if (!lastBackgroundSprite) {
const newSprite = generateTreeSprite();
lastBackgroundSprite = newSprite;
backgroundArr.push(lastBackgroundSprite);
}
// add random sprites
if (backgroundArr.length < 2 && lastBackgroundSprite.x < (16 * 7)) {
const randIdx = Math.floor(Math.random() * 25);
if (randIdx < 2) {
const newSprite = generateTreeSprite();
backgroundArr.push(newSprite);
}
}
for (x = 0; x < backgroundArr.length; x++) {
let scenerySprite = backgroundArr[x];
scenerySprite.x -= 5;
drawTreesFrame(scenerySprite.x, scenerySprite.y);
}
}
function drawCoinFrame(x, y) {
const cImg = require("heatshrink").decompress(atob("hkPxH+AAcHAAQIEBIXWAAQNEBIWHAAdcBgQLBA4IODBYQKEBAQMDBelcBaJUBM4QRBNYx1EBQILDR4QHBBISdIBIoA==")); // Coin image
g.drawImage(cImg, x, y);
}
function drawCoin() {
if (!coinSprite.isAnimating) return;
coinSprite.y -= 8;
if (coinSprite.y < (0 - 15 /*Coin sprite height*/)) {
coinSprite.isAnimating = false;
coinSprite.y = coinSprite.yDefault;
return;
}
drawCoinFrame(coinSprite.x, coinSprite.y);
}
function drawMarioFrame(idx, x, y) {
switch(idx) {
case 0:
const mFr1 = require("heatshrink").decompress(atob("h8UxH+AAkHAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGHQpMDw52FSg2HAAIoDAgIOMB5AAFGQTtKeBLuNcQwOJFwgJFA=")); // Mario Frame 1
g.drawImage(mFr1, x, y);
break;
case 1:
const mFr2 = require("heatshrink").decompress(atob("h8UxH+AAkHAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGHQpMDw+HCQYEBSowOBBQIdCCgTOIFgiVHFwYCBUhA9FBwz8HAo73GACQA=")); // Mario frame 2
g.drawImage(mFr2, x, y);
break;
default:
}
}
function drawToadFrame(idx, x, y) {
switch(idx) {
case 0:
const tFr1 = require("heatshrink").decompress(atob("iEUxH+ACkHAAoNJrnWAAQRGg/WrgACB4QEBCAYOBB44QFB4QICAg4QBBAQbDEgwPCHpAGCGAQ9KAYQPKCYg/EJAoADAwaKFw4BEP4YQCBIIABB468EB4QADYIoQGDwQOGBYYrCCAwbFFwgQEM4gAEeA4OIH4ghFAAYLD")); // Toad Frame 1
g.drawImage(tFr1, x, y);
break;
case 1:
const tFr2 = require("heatshrink").decompress(atob("iEUxH+ACkHAAoNJrnWAAQRGg/WrgACB4QEBCAYOBB44QFB4QICAg4QBBAQbDEgwPCHpAGCGAQ9KAYQPKCYg/EJAoADAwaKFw4BEP4YQCBIIABB468EB4QADYIoQGDwQOGBYQrDb4wcGFxYLDMoYgHRYgwKABAMBA")); // Mario frame 2
g.drawImage(tFr2, x, y);
break;
default:
}
}
function drawCharacter(date, character) {
// calculate jumping
const seconds = date.getSeconds(),
milliseconds = date.getMilliseconds();
if (seconds == 59 && milliseconds > 800 && !characterSprite.isJumping) {
characterSprite.isJumping = true;
}
if (characterSprite.isJumping) {
characterSprite.y = (Math.sin(characterSprite.jumpCounter) * -12) + 50 /* Character Y base value */;
characterSprite.jumpCounter += characterSprite.jumpIncrement;
if (parseInt(characterSprite.jumpCounter) === 2 && !coinSprite.isAnimating) {
coinSprite.isAnimating = true;
}
if (characterSprite.jumpCounter.toFixed(1) >= 4) {
characterSprite.jumpCounter = 0;
characterSprite.isJumping = false;
}
}
// calculate animation timing
if (timer % 50 === 0) {
// shift to next frame
characterSprite.frameIdx ^= 1;
}
switch(characterSprite.character) {
case("toad"):
drawToadFrame(characterSprite.frameIdx, characterSprite.x, characterSprite.y);
break;
case("mario"):
default:
drawMarioFrame(characterSprite.frameIdx, characterSprite.x, characterSprite.y);
}
}
function drawBrickFrame(x, y) {
const brk = require("heatshrink").decompress(atob("ikQxH+/0HACASB6wAQCoPWw4AOrgT/Cf4T/Cb1cAB8H/wVBAB/+A"));
g.drawImage(brk, x, y);
}
function drawTime(date) {
// draw hour brick
drawBrickFrame(20, 25);
// draw minute brick
drawBrickFrame(42, 25);
const h = date.getHours();
const hours = ("0" + ((is12Hour && h > 12) ? h - 12 : h)).substr(-2);
const mins = ("0" + date.getMinutes()).substr(-2);
g.setFont("6x8");
g.setColor(DARKEST);
g.drawString(hours, 25, 29);
g.drawString(mins, 47, 29);
}
function drawDate(date) {
g.setFont("6x8");
g.setColor(LIGHTEST);
let dateStr = locale.date(date, true);
dateStr = dateStr.replace(date.getFullYear(), "").trim().replace(/\/$/i,"");
dateStr = locale.dow(date, true) + " " + dateStr;
2020-04-04 10:05:51 +00:00
g.drawString(dateStr, (W - g.stringWidth(dateStr))/2, 1);
}
function redraw() {
const date = new Date();
// Update timers
incrementTimer();
// Draw frame
drawBackground();
drawFloor();
drawPyramid();
drawTrees();
drawTime(date);
drawDate(date);
drawCharacter(date);
drawCoin();
// Render new frame
g.flip();
}
function clearTimers(){
if(intervalRef) {
clearInterval(intervalRef);
intervalRef = null;
}
if(displayTimeoutRef) {
clearInterval(displayTimeoutRef);
displayTimeoutRef = null;
}
}
function resetDisplayTimeout() {
if (displayTimeoutRef) clearInterval(displayTimeoutRef);
displayTimeoutRef = setInterval(() => {
if (Bangle.isLCDOn()) Bangle.setLCDPower(false);
clearTimers();
}, ONE_SECOND * timeout);
}
function startTimers(){
if(intervalRef) clearTimers();
intervalRef = setInterval(redraw, 50);
resetDisplayTimeout();
redraw();
}
// Main
function init() {
clearInterval();
// Initialise display
Bangle.setLCDMode("80x80");
// Store screen dimensions
W = g.getWidth();
H = g.getHeight();
// Get Mario to jump!
setWatch(() => {
if (intervalRef && !characterSprite.isJumping) characterSprite.isJumping = true;
resetDisplayTimeout();
}, BTN1, {repeat:true});
setWatch(() => {
Bangle.setLCDMode();
Bangle.showLauncher();
}, BTN2, {repeat:false,edge:"falling"});
Bangle.on('lcdPower', (on) => {
if (on) {
startTimers();
} else {
clearTimers();
}
});
Bangle.on('faceUp', (up) => {
if (up && !Bangle.isLCDOn()) {
clearTimers();
Bangle.setLCDPower(true);
}
});
Bangle.on('swipe', (sDir) => {
resetDisplayTimeout();
switch(sDir) {
// Swipe right (1) - change character (on a loop)
case(1):
switchCharacter();
break;
// Swipe left (-1) - change day/night mode (on a loop)
case(-1):
default:
toggleNightMode();
}
});
startTimers();
}
// Initialise!
init();