2020-04-06 05:41:15 +00:00
/ * *
* BangleJS MARIO CLOCK
2020-04-06 15:11:05 +00:00
*
2020-04-06 05:41:15 +00:00
* + 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
* /
2020-03-26 12:45:53 +00:00
2020-04-03 08:16:22 +00:00
const locale = require ( "locale" ) ;
2020-03-30 21:52:41 +00:00
const storage = require ( 'Storage' ) ;
2020-04-03 08:18:05 +00:00
const settings = ( storage . readJSON ( 'setting.json' , 1 ) || { } ) ;
2020-04-03 08:16:22 +00:00
const timeout = settings . timeout || 10 ;
const is12Hour = settings [ "12hour" ] || false ;
2020-03-26 12:45:53 +00:00
// Screen dimensions
let W , H ;
let intervalRef , displayTimeoutRef = null ;
// Colours
const LIGHTEST = "#effedd" ;
const LIGHT = "#add795" ;
const DARK = "#588d77" ;
const DARKEST = "#122d3e" ;
2020-04-06 06:42:54 +00:00
const NIGHT = "#001818" ;
2020-03-26 12:45:53 +00:00
2020-04-06 06:42:54 +00:00
// Character names
const TOAD = "toad" ;
const MARIO = "mario" ;
const characterSprite = {
2020-03-26 12:45:53 +00:00
frameIdx : 0 ,
x : 35 ,
y : 55 ,
jumpCounter : 0 ,
2020-04-04 10:03:34 +00:00
jumpIncrement : Math . PI / 6 ,
2020-04-06 06:42:54 +00:00
isJumping : false ,
character : MARIO ,
2020-03-26 12:45:53 +00:00
} ;
2020-04-04 10:03:34 +00:00
const coinSprite = {
frameIdx : 0 ,
x : 34 ,
y : 18 ,
isAnimating : false ,
yDefault : 18 ,
2020-03-26 12:45:53 +00:00
} ;
2020-04-04 10:03:34 +00:00
const pyramidSprite = {
x : 90 ,
height : 34 ,
2020-03-26 12:45:53 +00:00
} ;
const ONE _SECOND = 1000 ;
let timer = 0 ;
let backgroundArr = [ ] ;
2020-04-06 06:42:54 +00:00
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 ;
}
2020-03-26 12:45:53 +00:00
function incrementTimer ( ) {
if ( timer > 1000 ) {
timer = 0 ;
}
else {
timer += 50 ;
}
}
function drawBackground ( ) {
2020-04-04 10:03:34 +00:00
// Clear screen
2020-04-06 15:11:05 +00:00
const bgColor = ( nightMode ) ? NIGHT : LIGHTEST ;
g . setColor ( bgColor ) ;
2020-03-26 12:45:53 +00:00
g . fillRect ( 0 , 10 , W , H ) ;
2020-04-06 15:11:05 +00:00
// set cloud colors and draw clouds
const cloudColor = ( nightMode ) ? DARK : LIGHT ;
g . setColor ( cloudColor ) ;
2020-04-04 10:03:34 +00:00
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 ) ;
2020-04-06 06:42:54 +00:00
// Date bar
g . setColor ( DARKEST ) ;
g . fillRect ( 0 , 0 , W , 9 ) ;
2020-03-26 12:45:53 +00:00
}
2020-04-04 10:03:34 +00:00
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
2020-04-06 15:11:05 +00:00
const color = ( nightMode ) ? DARK : LIGHT ;
g . setColor ( color ) ;
2020-04-04 10:03:34 +00:00
g . fillPoly ( pPol ) ;
pyramidSprite . x -= 1 ;
// Reset and randomize pyramid if off-screen
if ( pyramidSprite . x < - 100 ) {
pyramidSprite . x = 90 ;
2020-04-06 15:11:05 +00:00
pyramidSprite . height = genRanNum ( 25 , 60 ) ;
2020-04-04 10:03:34 +00:00
}
}
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 ) ;
}
2020-04-06 05:41:15 +00:00
function generateTreeSprite ( ) {
return {
x : 90 ,
2020-04-06 15:11:05 +00:00
y : genRanNum ( 30 , 60 )
2020-04-06 05:41:15 +00:00
} ;
}
2020-03-26 12:45:53 +00:00
2020-04-06 05:41:15 +00:00
function drawTrees ( ) {
2020-03-26 12:45:53 +00:00
// remove first sprite if offscreen
let firstBackgroundSprite = backgroundArr [ 0 ] ;
if ( firstBackgroundSprite ) {
2020-04-04 10:03:34 +00:00
if ( firstBackgroundSprite . x < - 15 ) backgroundArr . splice ( 0 , 1 ) ;
2020-03-26 12:45:53 +00:00
}
// set background sprite if array empty
2020-04-04 10:03:34 +00:00
let lastBackgroundSprite = backgroundArr [ backgroundArr . length - 1 ] ;
2020-03-26 12:45:53 +00:00
if ( ! lastBackgroundSprite ) {
2020-04-06 05:41:15 +00:00
const newSprite = generateTreeSprite ( ) ;
2020-03-26 12:45:53 +00:00
lastBackgroundSprite = newSprite ;
backgroundArr . push ( lastBackgroundSprite ) ;
}
// add random sprites
2020-04-04 10:03:34 +00:00
if ( backgroundArr . length < 2 && lastBackgroundSprite . x < ( 16 * 7 ) ) {
const randIdx = Math . floor ( Math . random ( ) * 25 ) ;
if ( randIdx < 2 ) {
2020-04-06 05:41:15 +00:00
const newSprite = generateTreeSprite ( ) ;
2020-03-26 12:45:53 +00:00
backgroundArr . push ( newSprite ) ;
}
}
for ( x = 0 ; x < backgroundArr . length ; x ++ ) {
let scenerySprite = backgroundArr [ x ] ;
2020-04-04 10:03:34 +00:00
scenerySprite . x -= 5 ;
drawTreesFrame ( scenerySprite . x , scenerySprite . y ) ;
}
}
2020-03-26 12:45:53 +00:00
2020-04-04 10:03:34 +00:00
function drawCoinFrame ( x , y ) {
const cImg = require ( "heatshrink" ) . decompress ( atob ( "hkPxH+AAcHAAQIEBIXWAAQNEBIWHAAdcBgQLBA4IODBYQKEBAQMDBelcBaJUBM4QRBNYx1EBQILDR4QHBBISdIBIoA==" ) ) ; // Coin image
g . drawImage ( cImg , x , y ) ;
}
2020-03-26 12:45:53 +00:00
2020-04-04 10:03:34 +00:00
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 ;
2020-03-26 12:45:53 +00:00
}
2020-04-04 10:03:34 +00:00
drawCoinFrame ( coinSprite . x , coinSprite . y ) ;
2020-03-26 12:45:53 +00:00
}
2020-04-04 10:03:34 +00:00
function drawMarioFrame ( idx , x , y ) {
switch ( idx ) {
case 0 :
2020-04-06 05:41:15 +00:00
const mFr1 = require ( "heatshrink" ) . decompress ( atob ( "h8UxH+AAkHAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGHQpMDw52FSg2HAAIoDAgIOMB5AAFGQTtKeBLuNcQwOJFwgJFA=" ) ) ; // Mario Frame 1
2020-04-04 10:03:34 +00:00
g . drawImage ( mFr1 , x , y ) ;
break ;
case 1 :
2020-04-06 05:41:15 +00:00
const mFr2 = require ( "heatshrink" ) . decompress ( atob ( "h8UxH+AAkHAAYKFBolcAAIPIBgYPDBpgfGFIY7EA4YcEBIPWAAYdDC4gLDAII5ECoYOFDogODFgoJCBwYZCAQYOFBAhAFFwZKGHQpMDw+HCQYEBSowOBBQIdCCgTOIFgiVHFwYCBUhA9FBwz8HAo73GACQA=" ) ) ; // Mario frame 2
2020-04-04 10:03:34 +00:00
g . drawImage ( mFr2 , x , y ) ;
break ;
default :
}
}
2020-03-26 12:45:53 +00:00
2020-04-06 06:42:54 +00:00
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 ) {
2020-03-26 12:45:53 +00:00
// calculate jumping
2020-04-04 10:03:34 +00:00
const seconds = date . getSeconds ( ) ,
milliseconds = date . getMilliseconds ( ) ;
2020-03-26 12:45:53 +00:00
2020-04-06 06:42:54 +00:00
if ( seconds == 59 && milliseconds > 800 && ! characterSprite . isJumping ) {
characterSprite . isJumping = true ;
2020-03-26 12:45:53 +00:00
}
2020-04-06 06:42:54 +00:00
if ( characterSprite . isJumping ) {
characterSprite . y = ( Math . sin ( characterSprite . jumpCounter ) * - 12 ) + 50 /* Character Y base value */ ;
characterSprite . jumpCounter += characterSprite . jumpIncrement ;
2020-03-26 12:45:53 +00:00
2020-04-06 06:42:54 +00:00
if ( parseInt ( characterSprite . jumpCounter ) === 2 && ! coinSprite . isAnimating ) {
2020-04-04 10:03:34 +00:00
coinSprite . isAnimating = true ;
}
2020-04-06 06:42:54 +00:00
if ( characterSprite . jumpCounter . toFixed ( 1 ) >= 4 ) {
characterSprite . jumpCounter = 0 ;
characterSprite . isJumping = false ;
2020-03-26 12:45:53 +00:00
}
}
// calculate animation timing
2020-04-04 10:03:34 +00:00
if ( timer % 50 === 0 ) {
2020-03-26 12:45:53 +00:00
// shift to next frame
2020-04-06 06:42:54 +00:00
characterSprite . frameIdx ^= 1 ;
2020-03-26 12:45:53 +00:00
}
2020-04-06 06:42:54 +00:00
switch ( characterSprite . character ) {
2020-04-06 15:11:05 +00:00
case ( TOAD ) :
2020-04-06 06:42:54 +00:00
drawToadFrame ( characterSprite . frameIdx , characterSprite . x , characterSprite . y ) ;
break ;
2020-04-06 15:11:05 +00:00
case ( MARIO ) :
2020-04-06 06:42:54 +00:00
default :
drawMarioFrame ( characterSprite . frameIdx , characterSprite . x , characterSprite . y ) ;
}
2020-03-26 12:45:53 +00:00
}
2020-04-04 10:03:34 +00:00
function drawBrickFrame ( x , y ) {
const brk = require ( "heatshrink" ) . decompress ( atob ( "ikQxH+/0HACASB6wAQCoPWw4AOrgT/Cf4T/Cb1cAB8H/wVBAB/+A" ) ) ;
g . drawImage ( brk , x , y ) ;
2020-03-26 12:45:53 +00:00
}
2020-04-04 10:03:34 +00:00
function drawTime ( date ) {
2020-03-26 12:45:53 +00:00
// draw hour brick
2020-04-04 10:03:34 +00:00
drawBrickFrame ( 20 , 25 ) ;
2020-03-26 12:45:53 +00:00
// draw minute brick
2020-04-04 10:03:34 +00:00
drawBrickFrame ( 42 , 25 ) ;
2020-03-26 12:45:53 +00:00
2020-04-04 10:03:34 +00:00
const h = date . getHours ( ) ;
2020-04-03 08:16:22 +00:00
const hours = ( "0" + ( ( is12Hour && h > 12 ) ? h - 12 : h ) ) . substr ( - 2 ) ;
2020-04-04 10:03:34 +00:00
const mins = ( "0" + date . getMinutes ( ) ) . substr ( - 2 ) ;
2020-03-26 12:45:53 +00:00
g . setFont ( "6x8" ) ;
g . setColor ( DARKEST ) ;
g . drawString ( hours , 25 , 29 ) ;
g . drawString ( mins , 47 , 29 ) ;
}
2020-04-04 10:03:34 +00:00
function drawDate ( date ) {
2020-03-26 12:45:53 +00:00
g . setFont ( "6x8" ) ;
g . setColor ( LIGHTEST ) ;
2020-04-04 10:03:34 +00:00
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 ) ;
2020-03-26 12:45:53 +00:00
}
function redraw ( ) {
2020-04-04 10:03:34 +00:00
const date = new Date ( ) ;
2020-03-26 12:45:53 +00:00
// Update timers
incrementTimer ( ) ;
// Draw frame
2020-04-04 10:03:34 +00:00
drawBackground ( ) ;
drawFloor ( ) ;
drawPyramid ( ) ;
drawTrees ( ) ;
drawTime ( date ) ;
drawDate ( date ) ;
2020-04-06 06:42:54 +00:00
drawCharacter ( date ) ;
2020-04-04 10:03:34 +00:00
drawCoin ( ) ;
2020-03-26 12:45:53 +00:00
// 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 ( ) ;
2020-03-30 21:52:41 +00:00
} , ONE _SECOND * timeout ) ;
2020-03-26 12:45:53 +00:00
}
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 ( ( ) => {
2020-04-06 06:42:54 +00:00
if ( intervalRef && ! characterSprite . isJumping ) characterSprite . isJumping = true ;
2020-03-26 12:45:53 +00:00
resetDisplayTimeout ( ) ;
} , BTN1 , { repeat : true } ) ;
setWatch ( ( ) => {
Bangle . setLCDMode ( ) ;
Bangle . showLauncher ( ) ;
} , BTN2 , { repeat : false , edge : "falling" } ) ;
Bangle . on ( 'lcdPower' , ( on ) => {
if ( on ) {
startTimers ( ) ;
} else {
clearTimers ( ) ;
}
} ) ;
2020-04-06 06:42:54 +00:00
Bangle . on ( 'faceUp' , ( up ) => {
2020-03-26 12:45:53 +00:00
if ( up && ! Bangle . isLCDOn ( ) ) {
clearTimers ( ) ;
Bangle . setLCDPower ( true ) ;
}
} ) ;
2020-04-03 08:16:22 +00:00
2020-04-06 06:42:54 +00:00
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 ( ) ;
}
} ) ;
2020-04-03 08:16:22 +00:00
startTimers ( ) ;
2020-03-26 12:45:53 +00:00
}
// Initialise!
2020-04-04 10:03:34 +00:00
init ( ) ;