Merge pull request #2990 from PastaRhythm/master

Bangle Blobs Clock
pull/2991/head
Gordon Williams 2023-08-29 12:07:31 +01:00 committed by GitHub
commit 480c0f7eac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 820 additions and 0 deletions

1
apps/bblobface/ChangeLog Normal file
View File

@ -0,0 +1 @@
1.00: Initial release of Bangle Blobs Clock!

35
apps/bblobface/README.md Normal file
View File

@ -0,0 +1,35 @@
# Bangle Blobs Clock
What if every time you checked the time, you could play a turn of a turn-based puzzle game?
You check the time dozens, maybe hundreds of times per day, and Bangle Blobs Clock wants to add a splash of fun to each of these moments!
Bangle Blobs Clock is a fully featured watch face with a turn-based puzzle game right next to the clock.
![](screenshot1.png)
![](screenshot2.png)
## Clock Features
- Hour and minute
- Seconds (only while the screen is unlocked to save power)
- Month, day, and day of week
- Battery percentage. Blue while charging, red when low, green otherwise.
- Respects your 24-hour/12-hour time setting in Locale
- Press the pause button to access your Widgets
- Supports Fast Loading
## The Game
This is a turn-based puzzle game based on Puyo Puyo, an addictive puzzle game franchise by SEGA.
Blobs arrive in pairs that you can move, rotate, and place. When at least four Blobs of the same color touch, they pop, causing Blobs above them to fall.
If this causes another pop, it's called a chain! Build a massive chain reaction of popping Blobs!
- Drag left and right to move the pair
- Tap the left or right half of the screen to rotate the pair
- Swipe down to place the pair
## More Info
If you're confused about the functionality of the clock or want a better explanation of how to play the game, I wrote up a user manual here: https://docs.google.com/document/d/1watPzChawBu4iM0lXypreejs3wvf2_8C-x5V2MWJQBc/edit?usp=sharing
## Special Thanks
I'm Pasta Rhythm, computer scientist and aspiring game developer. I would like to say thank you to the people who inspired me while I was making this app:
- [nxdefiant, who made a Tetris game.](https://github.com/espruino/BangleApps/tree/master/apps/tetris) Bangle Blobs is my first Bangle app and my first time using JavaScript, so this was a daunting project. This Tetris game served as a great example that helped me get started.
- [gfwilliams for Anton Clock](https://github.com/espruino/BangleApps/tree/master/apps/antonclk) and [Targor for Kanagawa Clock.](https://github.com/espruino/BangleApps/tree/master/apps/kanagsec) These were good examples for how to make a watch face for the Bangle.js 2.
- Thanks to Gordon Williams and to everyone who contributes to Espruino and the Bangle.js 2 projects!
- SEGA, owners of the Puyo Puyo franchise that Bangle Blobs is based on. Please check out official Puyo Puyo games!
- Compile, the original creators of Puyo Puyo. The company went bankrupt long ago, but the people who worked for them continue to make games.

View File

@ -0,0 +1 @@
require("heatshrink").decompress(atob("mEwxH+HGm56+5BQ4JBAJItXAAoMMCJQAPJ5pfhJApPQL65HHKIbTU2nXAAu0I5xQNBo4tC2gAFGIxHIL5oNGEoItGGIgwDL6oMGFxgwFL6oVFFxwwEL7YuPGARfVBYwvUL6YLGL84THL84KHL7YHCL6AeBFx+0JggAGLx4wQFwa3DAIwvHNJQwMFwhgIEQ7ILGAYxHBAQWJADUeFAIAEjwtnjwAFGMglBFowxEGA/XgrgICJouMGA4aBAIgvMB4ouOGAouGMZgNGFx4wCPQ5hMN44vTK44wLNo5fUcRwuHL67iOHAxfhFxYJBBooeBFx8ecRY4KBowwOFxDgHM5BtHGBguZfhIkBGI4ICFyILFAIxBHAAoOGXIgLHBowBGFo0FAAoxHFxhfPAoQAJCIguNGxRtGABYpDQB72LFxwwEcCJfJFx4wCL7gvTADYv/F/4APYoQuOaoYwpFz4wOF0IwDGI4ICF0IxFAAgtFA="))

768
apps/bblobface/app.js Normal file
View File

@ -0,0 +1,768 @@
{
// ~~ Variables for clock ~~
let clockDrawTimeout;
let twelveHourTime = require('Storage').readJSON('setting.json', 1)['12hour'];
let updateSeconds = !Bangle.isLocked();
let batteryLevel = E.getBattery();
// ~~ Variables for game logic ~~
const NUM_COLORS = 6;
const NUISANCE_COLOR = 7;
let grid = [
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0])
];
let hiddenRow = new Uint8Array([0, 0, 0, 0, 0, 0]);
let nextQueue = [{pivot: 1, leaf: 1}, {pivot: 1, leaf: 1}];
let currentPair = {pivot: 0, leaf: 0};
let dropCoordinates = {pivotX: 2, pivotY: 11, leafX: 2, leafY: 10};
let pairX = 2;
let pairOrientation = 0; //0 is up, 1 is right, 2 is down, 3 is left
let slotsToCheck = [];
let selectedColors;
let lastChain = 0;
let gameLost = false;
let gamePaused = false;
let midChain = false;
/*
Sets up a new game.
Must be called once before the first round.
*/
let restartGame = function() {
grid = [
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0])
];
hiddenRow = new Uint8Array([0, 0, 0, 0, 0, 0]);
currentPair = {pivot: 0, leaf: 0};
pairX = 2;
pairOrientation = 0; //0 is up, 1 is right, 2 is down, 3 is left
slotsToCheck = [];
gameLost = false;
lastChain = 0;
//Set up random colors
selectedColors = new Uint8Array([1, 2, 3, 4, 5, 6]);
for (let i = NUM_COLORS - 1; i > 0; i--) {
let swap = selectedColors[i];
let swapIndex = Math.floor(Math.random() * (i + 1));
selectedColors[i] = selectedColors[swapIndex];
selectedColors[swapIndex] = swap;
}
//Create the first two pairs (Always in the first three colors)
nextQueue[0].pivot = selectedColors[Math.floor(Math.random() * 3)];
nextQueue[0].leaf = selectedColors[Math.floor(Math.random() * 3)];
nextQueue[1].pivot = selectedColors[Math.floor(Math.random() * 3)];
nextQueue[1].leaf = selectedColors[Math.floor(Math.random() * 3)];
};
/*
Readies the next pair and generates a new one for the queue.
*/
let newPair = function() {
currentPair.pivot = nextQueue[0].pivot;
currentPair.leaf = nextQueue[0].leaf;
nextQueue[0].pivot = nextQueue[1].pivot;
nextQueue[0].leaf = nextQueue[1].leaf;
nextQueue[1].pivot = selectedColors[Math.floor(Math.random() * 4)];
nextQueue[1].leaf = selectedColors[Math.floor(Math.random() * 4)];
pairX = 2;
pairOrientation = 0;
calcDropCoordinates();
};
/*
Calculates the coordinates at which the current pair will be placed when quick dropped.
*/
let calcDropCoordinates = function() {
dropCoordinates.pivotX = pairX;
//Find Y coordinate of pivot
dropCoordinates.pivotY = -2;
for (let i = 11; i >= 0; i--) {
if (grid[i][pairX] == 0) {
dropCoordinates.pivotY = i;
break;
}
}
if (dropCoordinates.pivotY == -2 && hiddenRow[pairX] == 0)
dropCoordinates.pivotY = -1;
//Find coordinates of leaf
if (pairOrientation == 1) {
dropCoordinates.leafX = pairX + 1;
dropCoordinates.leafY = -2;
for (let i = 11; i >= 0; i--) {
if (grid[i][pairX + 1] == 0) {
dropCoordinates.leafY = i;
break;
}
}
if (dropCoordinates.leafY == -2 && hiddenRow[pairX + 1] == 0)
dropCoordinates.leafY = -1;
} else if (pairOrientation == 3) {
dropCoordinates.leafX = pairX - 1;
dropCoordinates.leafY = -2;
for (let i = 11; i >= 0; i--) {
if (grid[i][pairX - 1] == 0) {
dropCoordinates.leafY = i;
break;
}
}
if (dropCoordinates.leafY == -2 && hiddenRow[pairX - 1] == 0)
dropCoordinates.leafY = -1;
} else if (pairOrientation == 2) {
dropCoordinates.leafX = pairX;
dropCoordinates.leafY = dropCoordinates.pivotY;
dropCoordinates.pivotY--;
} else {
dropCoordinates.leafX = pairX;
dropCoordinates.leafY = dropCoordinates.pivotY - 1;
}
};
/*
Moves the current pair a certain number of slots.
*/
let movePair = function(dx) {
pairX += dx;
if (dx < 0) {
if (pairX < (pairOrientation == 3 ? 1 : 0))
pairX = (pairOrientation == 3 ? 1 : 0);
}
if (dx > 0) {
if (pairX > (pairOrientation == 1 ? 4 : 5))
pairX = (pairOrientation == 1 ? 4 : 5);
}
calcDropCoordinates();
};
/*
Rotates the pair in the given direction around the pivot.
*/
let rotatePair = function(clockwise) {
pairOrientation += (clockwise ? 1 : -1);
if (pairOrientation > 3)
pairOrientation = 0;
if (pairOrientation < 0)
pairOrientation = 3;
if (pairOrientation == 1 && pairX == 5)
pairX = 4;
if (pairOrientation == 3 && pairX == 0)
pairX = 1;
calcDropCoordinates();
};
/*
Places the current pair at the drop coordinates.
*/
let quickDrop = function() {
if (dropCoordinates.pivotY == -1) {
hiddenRow[dropCoordinates.pivotX] = currentPair.pivot;
} else if (dropCoordinates.pivotY > -1) {
grid[dropCoordinates.pivotY][dropCoordinates.pivotX] = currentPair.pivot;
}
if (dropCoordinates.leafY == -1) {
hiddenRow[dropCoordinates.leafX] = currentPair.leaf;
} else if (dropCoordinates.leafY > -1) {
grid[dropCoordinates.leafY][dropCoordinates.leafX] = currentPair.leaf;
}
currentPair.pivot = 0;
currentPair.leaf = 0;
};
/*
Makes all blobs fall to the lowest available slot.
All blobs that fall will be added to slotsToCheck.
*/
let settleBlobs = function() {
for (let x = 0; x < 6; x++) {
let lowestOpen = 11;
for (let y = 11; y >= 0; y--) {
if (grid[y][x] != 0) {
if (y != lowestOpen) {
grid[lowestOpen][x] = grid[y][x];
grid[y][x] = 0;
addSlotToCheck(x, lowestOpen);
}
lowestOpen--;
}
}
if (lowestOpen >= 0 && hiddenRow[x] != 0) {
grid[lowestOpen][x] = hiddenRow[x];
hiddenRow[x] = 0;
addSlotToCheck(x, lowestOpen);
}
}
};
/*
Adds a slot to slotsToCheck. This slot will be checked for a pop
next time popAll is called.
*/
let addSlotToCheck = function(x, y) {
slotsToCheck.push({x: x, y: y});
};
/*
Checks for a pop at every slot in slotsToCheck.
Pops at all locations.
*/
let popAll = function() {
let result = {pops: 0};
while(slotsToCheck.length > 0) {
let coord = slotsToCheck.pop();
if (grid[coord.y][coord.x] != 0 && grid[coord.y][coord.x] != NUISANCE_COLOR) {
if (checkSlotForPop(coord.x, coord.y))
result.pops += 1;
}
}
return result;
};
/*
Checks a specific slot for a pop.
If there are four or more adjacent blobs of the same color, they are removed.
*/
let checkSlotForPop = function(x, y) {
let toDelete = [
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0]),
new Uint8Array([0, 0, 0, 0, 0, 0])
];
let blobsInClump = 0;
let color = grid[y][x];
let toCheck = [{x: x, y: y}];
//Count every blob in this clump
while (toCheck.length > 0) {
let coord = toCheck.pop();
if (grid[coord.y][coord.x] == color && toDelete[coord.y][coord.x] == 0) {
blobsInClump++;
toDelete[coord.y][coord.x] = 1;
if (coord.x > 0) toCheck.push({x: coord.x - 1, y: coord.y});
if (coord.x < 5) toCheck.push({x: coord.x + 1, y: coord.y});
if (coord.y > 0) toCheck.push({x: coord.x, y: coord.y - 1});
if (coord.y < 11) toCheck.push({x: coord.x, y: coord.y + 1});
}
if (grid[coord.y][coord.x] == NUISANCE_COLOR && toDelete[coord.y][coord.x] == 0)
toDelete[coord.y][coord.x] = 1; //For erasing garbage
}
//If there are at least four blobs in this clump, remove them from the grid and draw a pop.
if (blobsInClump >= 4) {
for (let y = 0; y < 12; y++) {
for (let x = 0; x < 6; x++) {
if (toDelete[y][x] == 1) {
grid[y][x] = 0;
//Clear the blob out of the slot
g.setBgColor(0, 0, 0);
g.clearRect((x*18)+34, (y*14)+7, (x*18)+52, (y*14)+21);
//Draw the pop
let colorInfo = getColor(color);
g.setColor(colorInfo.r, colorInfo.g, colorInfo.b);
if (color < NUISANCE_COLOR) {
//A fancy pop for popped colors!
g.drawEllipse((x*18)+36, (y*14)+7, (x*18)+50, (y*14)+21);
g.drawEllipse((x*18)+27, (y*14)-2, (x*18)+59, (y*14)+30);
} else if (color == NUISANCE_COLOR) {
//Nuisance Blobs are simply crossed out.
//TODO: Nuisance Blobs are currently unusued, but also untested. Test before use.
g.drawLine((x*18)+34, (y*14)+7, (x*18)+52, (y*14)+21);
}
}
}
}
return true;
}
return false;
};
// Variables for graphics
let oldGhost = {pivotX: 0, pivotY: 0, leafX: 0, leafY: 0};
/*
Draws the time on the side.
*/
let drawTime = function(scheduleNext) {
//Change this to alter the y-coordinate of the top edge.
let dy = 25;
g.setBgColor(0, 0, 0);
g.clearRect(2, dy, 30, dy + 121);
//Draw the time
let d = new Date();
let h = d.getHours(), m = d.getMinutes();
if (twelveHourTime) {
let mer = 'A';
if (h >= 12) mer = 'P';
if (h >= 13) h -= 12;
if (h == 0) h = 12;
g.setColor(1, 1, 1);
g.setFont("Vector", 12);
g.drawString(mer, 23, dy + 63);
}
let hs = h.toString().padStart(2, 0);
let ms = m.toString().padStart(2, 0);
g.setFont("Vector", 24);
g.setColor(1, 0.2, 1);
g.drawString(hs, 3, dy + 21);
g.setColor(0.5, 0.5, 1);
g.drawString(ms, 3, dy + 42);
//Draw seconds
let s = d.getSeconds();
if (updateSeconds) {
let ss = s.toString().padStart(2, 0);
g.setFont("Vector", 12);
g.setColor(0.2, 1, 0.2);
g.drawString(ss, 3, dy + 63);
}
//Draw the date
let dayString = d.getDate().toString();
let dayNames = ["SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"];
let dayName = dayNames[d.getDay()];
let monthNames = ["JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JLY", "AUG", "SEP", "OCT", "NOV", "DEC"];
let monthName = monthNames[d.getMonth()];
g.setColor(1, 1, 1);
g.setFont("Vector", 12);
g.drawString(monthName, 3, dy + 84);
g.drawString(dayString, 3, dy + 97);
g.setColor(0.5, 0.5, 0.5);
g.drawString(dayName, 3, dy + 110);
//Draw battery
if (s == 0) batteryLevel = E.getBattery();
if (Bangle.isCharging()) {
g.setColor(0, 0, 1);
} else if (batteryLevel <= 15) {
g.setColor(1, 0, 0);
} else {
g.setColor(0, 1, 0);
}
g.drawString(batteryLevel + "%", 3, dy + 1);
//Schedule the next draw if requested.
if (!scheduleNext) return;
if (clockDrawTimeout) clearTimeout(clockDrawTimeout);
let interval = updateSeconds ? 1000 : 60000;
clockDrawTimeout = setTimeout(function() {
clockDrawTimeout = undefined;
drawTime(true);
}, interval - (Date.now() % interval));
};
/*
Returns a tuple in the format {r, g, b} with the color
of the blob with the given ID.
This saves memory compared to having the colors stored in an array.
*/
let getColor = function(color) {
if (color == 1)
return {r: 1, g: 0, b: 0};
if (color == 2)
return {r: 0, g: 1, b: 0};
if (color == 3)
return {r: 0, g: 0, b: 1};
if (color == 4)
return {r: 1, g: 1, b: 0};
if (color == 5)
return {r: 1, g: 0, b: 1};
if (color == 6)
return {r: 0, g: 1, b: 1};
if (color == 7)
return {r: 0.5, g: 0.5, b: 0.5};
return {r: 1, g: 1, b: 1};
};
/*
Clears the screen and draws the background.
*/
let drawBackground = function() {
//Background
g.setBgColor(0.5, 0.2, 0.1);
g.clear();
g.setBgColor(0, 0, 0);
g.clearRect(33, 0, 142, 176);
g.setBgColor(0.5, 0.5, 0.5);
g.clearRect(33, 4, 142, 6);
//Reset button
g.setBgColor(0.5, 0.5, 0.5);
g.setColor(0, 0, 0);
g.clearRect(143, 150, 175, 175);
g.setFont("Vector", 30);
g.drawString("R", 152, 150);
//Pause button
g.clearRect(0, 150, 32, 175);
g.fillRect(9, 154, 13, 171);
g.fillRect(18, 154, 22, 171);
};
/*
Draws a box under the next queue that displays
the current value of lastChain.
*/
let drawChainCount = function() {
g.setBgColor(0, 0, 0);
g.setColor(1, 0.2, 0.2);
g.setFont("Vector", 23);
g.clearRect(145, 42, 173, 64);
if (lastChain > 0) {
if (lastChain < 10) g.drawString(lastChain, 154, 44);
if (lastChain >= 10) g.drawString(lastChain, 147, 44);
}
};
/*
Draws the blob at the given slot.
*/
let drawBlobAtSlot = function(x, y) {
//If this blob is in the hidden row, clear it out and stop.
if (y < 0) {
g.setBgColor(0, 0, 0);
g.clearRect((x*18)+34, 0, (x*18)+52, 3);
return;
}
//First, clear what was in that slot.
g.setBgColor(0, 0, 0);
g.clearRect((x*18)+34, (y*14)+7, (x*18)+52, (y*14)+21);
let color = grid[y][x];
if (color != 0) {
let myColor = getColor(color);
g.setColor(myColor.r, myColor.g, myColor.b);
g.fillEllipse((x*18)+34, (y*14)+7, (x*18)+52, (y*14)+21);
g.setColor(1, 1, 1);
g.drawEllipse((x*18)+34, (y*14)+7, (x*18)+52, (y*14)+21);
}
};
/*
Draws the ghost piece.
clearOld: if the previous location of the ghost piece should be cleared.
*/
let drawGhostPiece = function(clearOld) {
if (clearOld) {
g.setColor(0, 0, 0);
g.fillRect((oldGhost.pivotX*18)+38, (oldGhost.pivotY*14)+8, (oldGhost.pivotX*18)+47, (oldGhost.pivotY*14)+17);
g.fillRect((oldGhost.leafX*18)+38, (oldGhost.leafY*14)+8, (oldGhost.leafX*18)+47, (oldGhost.leafY*14)+17);
}
let pivotX = dropCoordinates.pivotX;
let pivotY = dropCoordinates.pivotY;
let leafX = dropCoordinates.leafX;
let leafY = dropCoordinates.leafY;
let pivotColor = getColor(currentPair.pivot);
let leafColor = getColor(currentPair.leaf);
g.setColor(pivotColor.r, pivotColor.g, pivotColor.b);
g.fillRect((pivotX*18)+40, (pivotY*14)+10, (pivotX*18)+45, (pivotY*14)+15);
g.setColor(1, 1, 1);
g.drawRect((pivotX*18)+38, (pivotY*14)+8, (pivotX*18)+47, (pivotY*14)+17);
g.setColor(leafColor.r, leafColor.g, leafColor.b);
g.fillRect((leafX*18)+40, (leafY*14)+10, (leafX*18)+45, (leafY*14)+15);
oldGhost = {pivotX: pivotX, pivotY: pivotY, leafX: leafX, leafY: leafY};
};
/*
Draws the next queue.
*/
let drawNextQueue = function() {
g.setBgColor(0, 0, 0);
g.clearRect(145, 4, 173, 28);
let p1 = nextQueue[0].pivot;
let l1 = nextQueue[0].leaf;
let p2 = nextQueue[1].pivot;
let l2 = nextQueue[1].leaf;
let p1C = getColor(p1);
let l1C = getColor(l1);
let p2C = getColor(p2);
let l2C = getColor(l2);
g.setColor(p1C.r, p1C.g, p1C.b);
g.fillEllipse(146, 17, 157, 28);
g.setColor(l1C.r, l1C.g, l1C.b);
g.fillEllipse(146, 5, 157, 16);
g.setColor(p2C.r, p2C.g, p2C.b);
g.fillEllipse(162, 17, 173, 28);
g.setColor(l2C.r, l2C.g, l2C.b);
g.fillEllipse(162, 5, 173, 16);
g.setColor(1, 1, 1);
g.drawLine(159, 4, 159, 28);
g.drawEllipse(146, 17, 157, 28);
g.drawEllipse(146, 5, 157, 16);
g.drawEllipse(162, 17, 173, 28);
g.drawEllipse(162, 5, 173, 16);
};
/*
Redraws the screen, except for the ghost piece.
*/
let redrawBoard = function() {
drawBackground();
drawNextQueue();
drawChainCount();
drawTime(false);
for (let y = 0; y < 12; y++) {
for (let x = 0; x < 6; x++) {
drawBlobAtSlot(x, y);
}
}
};
/*
Toggles the pause screen.
*/
let togglePause = function() {
gamePaused = !gamePaused;
if (gamePaused) {
g.setBgColor(0.5, 0.2, 0.1);
g.clear();
drawTime(false);
g.setBgColor(0, 0, 0);
g.setColor(1, 1, 1);
g.clearRect(48, 66, 157, 110);
g.setFont("Vector", 20);
g.drawString("Tap here\nto unpause", 50, 68);
require("widget_utils").show();
Bangle.drawWidgets();
} else {
require("widget_utils").hide();
redrawBoard();
drawGhostPiece(false);
//Display the loss text if the game is lost.
if (gameLost) {
g.setBgColor(0, 0, 0);
g.setColor(1, 1, 1);
g.clearRect(33, 73, 142, 103);
g.setFont("Vector", 20);
g.drawString("You Lose", 43, 80);
}
}
};
// ~~ Events ~~
let dragAmnt = 0;
let onTouch = (z, e) => {
if (midChain) return;
if (gamePaused) {
if (e.x >= 40 && e.y >= 58 && e.x <= 165 && e.y <= 118) {
g.setBgColor(1, 1, 1);
g.clearRect(48, 66, 157, 110);
g.flip();
togglePause();
}
} else {
//Tap reset button
if (e.x >= 143 && e.y >= 150) {
restartGame();
newPair();
redrawBoard();
drawGhostPiece(false);
g.flip();
return;
}
//Tap pause button
if (e.x <= 32 && e.y >= 150) {
togglePause();
return;
}
//While playing, rotate pieces.
if (!gameLost && !gamePaused) {
if (e.x < 88) {
rotatePair(false);
drawGhostPiece(true);
} else {
rotatePair(true);
drawGhostPiece(true);
}
}
}
};
Bangle.on("touch", onTouch);
let onDrag = (e) => {
if (gameLost || gamePaused || midChain) return;
//Do nothing if the user is dragging down so that they don't accidentally move while dropping
if (e.dy >= 5) {
return;
}
dragAmnt += e.dx;
if (e.b == 0) {
dragAmnt = 0;
}
if (dragAmnt >= 20) {
movePair(Math.floor(dragAmnt / 20));
drawGhostPiece(true);
dragAmnt = dragAmnt % 20;
}
if (dragAmnt <= -20) {
movePair(Math.ceil(dragAmnt / 20));
drawGhostPiece(true);
dragAmnt = dragAmnt % 20;
}
};
Bangle.on("drag", onDrag);
let onSwipe = (x, y) => {
if (gameLost || gamePaused || midChain) return;
if (y > 0) {
let pivotX = dropCoordinates.pivotX;
let pivotY = dropCoordinates.pivotY;
let leafX = dropCoordinates.leafX;
let leafY = dropCoordinates.leafY;
if (pivotY < -1 && leafY < -1) return;
quickDrop();
drawBlobAtSlot(pivotX, pivotY);
drawBlobAtSlot(leafX, leafY);
g.flip();
//Check for pops
if (pivotY >= 0) addSlotToCheck(pivotX, pivotY);
if (leafY >= 0) addSlotToCheck(leafX, leafY);
midChain = true;
let currentChain = 0;
while (popAll().pops > 0) {
currentChain++;
lastChain = currentChain;
drawChainCount();
g.flip();
settleBlobs();
redrawBoard();
g.flip();
}
newPair();
drawNextQueue();
drawGhostPiece(false);
//If the top slot of the third column is taken, lose the game.
if (grid[0][2] != 0) {
gameLost = true;
g.setBgColor(0, 0, 0);
g.setColor(1, 1, 1);
g.clearRect(33, 73, 142, 103);
g.setFont("Vector", 20);
g.drawString("You Lose", 43, 80);
}
midChain = false;
}
};
Bangle.on("swipe", onSwipe);
let onLock = on => {
updateSeconds = !on;
drawTime(true);
};
Bangle.on('lock', onLock);
let onCharging = charging => {
drawTime(false);
};
Bangle.on('charging', onCharging);
Bangle.setUI({mode:"clock", remove:function() {
//Remove listeners
Bangle.removeListener("touch", onTouch);
Bangle.removeListener("drag", onDrag);
Bangle.removeListener("swipe", onSwipe);
Bangle.removeListener('lock', onLock);
Bangle.removeListener('charging', onCharging);
if (clockDrawTimeout) clearTimeout(clockDrawTimeout);
require("widget_utils").show();
}});
g.reset();
Bangle.loadWidgets();
require("widget_utils").hide();
drawBackground();
drawTime(true);
restartGame();
newPair();
drawGhostPiece(false);
drawNextQueue();
drawChainCount();
}

BIN
apps/bblobface/app.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 691 B

View File

@ -0,0 +1,15 @@
{ "id": "bblobface",
"name": "Bangle Blobs Clock",
"shortName":"BBClock",
"icon": "app.png",
"version": "1.00",
"description": "A fully featured watch face with a playable game on the side.",
"readme":"README.md",
"type": "clock",
"tags": "clock, game",
"supports" : ["BANGLEJS2"],
"storage": [
{"name":"bblobface.app.js","url":"app.js"},
{"name":"bblobface.img","url":"app-icon.js","evaluate":true}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB