diff --git a/bruce-emulator/Keycopier/keycopier.js b/bruce-emulator/Keycopier/keycopier.js
new file mode 100644
index 0000000..ef16227
--- /dev/null
+++ b/bruce-emulator/Keycopier/keycopier.js
@@ -0,0 +1,292 @@
+var display = require('display');
+var keyboard = require('keyboard');
+var storage = require('storage');
+
+function main() {
+
+ // --- Képernyő ---
+ var dw = display.width();
+ var dh = display.height();
+ var sprite = display.createSprite(dw, dh);
+
+ // --- Színek ---
+ var bg_color = display.color(255, 130, 0);
+ var fg_color = display.color(0, 0, 0);
+
+ // --- Adatok ---
+ var cursor = 0;
+ var code = [1, 1, 1, 1, 1, 1];
+ var maxDepth = 7;
+
+ // --- MENTÉS HELYE ---
+ var FILE_PATH = "/KeyCopier/key_copier.code";
+
+ var showMenu = false;
+ var menuCursor = 0;
+ var menuItems = ["Save", "Load", "Exit"];
+
+ var statusMsg = "";
+ var msgTimer = 0;
+
+ keyboard.setLongPress(true);
+
+ // --- GRAFIKA ---
+ var TANGENT = 0.84;
+ var PIN_HALF_WIDTH = 3;
+ var DEPTH_STEP = 4;
+ var numY = 25;
+ var TOP_Y = 55;
+ var keySpineY = 95;
+
+ while (true) {
+
+ if (msgTimer > 0) msgTimer--;
+ if (msgTimer === 0) statusMsg = "";
+
+ // --- BEMENET ---
+
+ if (showMenu) {
+ // *** MENÜ ***
+ if (keyboard.getPrevPress(true)) showMenu = false;
+
+ if (keyboard.getNextPress(true)) {
+ menuCursor++;
+ if (menuCursor >= menuItems.length) menuCursor = 0;
+ }
+
+ if (keyboard.getSelPress(true)) {
+ if (menuCursor === 0) {
+ // **********************************
+ // *** SAVE (FELÜLÍRÁS "write" MÓDDAL) ***
+ // **********************************
+ try {
+ // CSV formátum
+ var csvData = code.join(",");
+
+ // JAVÍTÁS: Itt adjuk át a "write" paramétert!
+ // Ez biztosítja, hogy felülírja a fájlt, ne hozzáfűzzön.
+ var success = storage.write(FILE_PATH, csvData, "write");
+
+ if (success) statusMsg = "Saved!";
+ else statusMsg = "Write: False";
+
+ } catch (e) {
+ statusMsg = "Sys Err";
+ }
+ msgTimer = 30;
+ showMenu = false;
+ }
+ else if (menuCursor === 1) {
+ // **********************************
+ // *** LOAD (BETÖLTÉS) ***
+ // **********************************
+ var raw = undefined;
+ var loaded = false;
+
+ try {
+ // Közvetlen olvasás
+ raw = storage.read(FILE_PATH);
+ } catch (e) {
+ statusMsg = "IO Err";
+ }
+
+ if (raw !== undefined && raw !== null) {
+ try {
+ var str = String(raw).trim();
+ // Ha esetleg maradtak benne sortörések, csak az első sort vesszük
+ // Ez extra védelem, ha régebben appendelt fájlt olvasunk be
+ var lines = str.split("\n");
+ if (lines.length > 0) str = lines[lines.length-1]; // Az utolsó sort vesszük (a legfrissebbet)
+
+ var parts = str.split(",");
+
+ var tempCode = [];
+ for (var k = 0; k < parts.length; k++) {
+ var val = parseInt(parts[k].trim());
+ if (!isNaN(val)) tempCode.push(val);
+ }
+
+ // Mivel lehet, hogy a fájlban több sor van (a régi hibás mentések miatt),
+ // csak az utolsó 6 számot vesszük figyelembe, ha több lenne.
+ if (tempCode.length >= 6) {
+ // Az utolsó 6 elemet töltjük be
+ var startIdx = tempCode.length - 6;
+ code = tempCode.slice(startIdx, startIdx + 6);
+ statusMsg = "Loaded!";
+ loaded = true;
+ } else {
+ statusMsg = "Bad Len";
+ }
+ } catch (e) {
+ statusMsg = "Parse Err";
+ }
+
+ if (!loaded && statusMsg === "") statusMsg = "Bad Data";
+
+ } else {
+ if (statusMsg === "") statusMsg = "No File";
+ }
+
+ msgTimer = 30;
+ showMenu = false;
+ }
+ else if (menuCursor === 2) {
+ break; // Exit
+ }
+ }
+
+ } else {
+ // *** SZERKESZTŐ ***
+ if (keyboard.getPrevPress(true)) {
+ showMenu = true;
+ menuCursor = 0;
+ }
+ if (keyboard.getSelPress(true)) {
+ cursor++;
+ if (cursor > 5) cursor = 0;
+ }
+ if (keyboard.getNextPress(true)) {
+ code[cursor]++;
+ if (code[cursor] > maxDepth) code[cursor] = 1;
+ }
+ }
+
+ // --- RAJZOLÁS ---
+ sprite.fill(bg_color);
+ sprite.setTextColor(fg_color, bg_color);
+
+ sprite.setTextSize(2);
+ sprite.drawText("Key Copier", 5, 2);
+
+ var availableWidth = dw - 20;
+ var pinSpacing = Math.floor(availableWidth / 6);
+ var startX = 10;
+
+ sprite.setTextSize(2);
+ for(var i=0; i<6; i++) {
+ var pinCenterX = startX + (i * pinSpacing) + (pinSpacing/2);
+ var textStr = code[i].toString();
+
+ if (i === cursor && !showMenu) {
+ textStr = "[" + textStr + "]";
+ sprite.drawText(textStr, pinCenterX - 14, numY);
+ } else {
+ sprite.drawText(textStr, pinCenterX - 5, numY);
+ }
+ }
+
+ var keyEnd = dw - 5;
+ sprite.drawLine(0, keySpineY, keyEnd, keySpineY, fg_color);
+ sprite.drawLine(0, keySpineY, 0, TOP_Y, fg_color);
+ sprite.drawLine(keyEnd, keySpineY, keyEnd, TOP_Y + 10, fg_color);
+
+ for(var j=0; j<6; j++) {
+ var cx = startX + (j * pinSpacing) + (pinSpacing/2);
+ var currentDepthPx = (code[j] - 1) * DEPTH_STEP;
+ var bottomY = TOP_Y + currentDepthPx;
+
+ sprite.drawLine(cx - PIN_HALF_WIDTH, bottomY, cx + PIN_HALF_WIDTH, bottomY, fg_color);
+
+ var prevDepthPx = 0;
+ if (j > 0) prevDepthPx = (code[j-1] - 1) * DEPTH_STEP;
+ var myLeftWidth = PIN_HALF_WIDTH + (currentDepthPx * TANGENT);
+ var prevRightWidth = PIN_HALF_WIDTH + (prevDepthPx * TANGENT);
+ var distToPrev = (j === 0) ? 999 : pinSpacing;
+
+ if (j === 0) {
+ var slopeEndX = cx - myLeftWidth;
+ if (slopeEndX < 0) slopeEndX = 0;
+ sprite.drawLine(cx - PIN_HALF_WIDTH, bottomY, slopeEndX, TOP_Y, fg_color);
+ if (slopeEndX > 0) sprite.drawLine(0, TOP_Y, slopeEndX, TOP_Y, fg_color);
+ } else {
+ if ((myLeftWidth + prevRightWidth) > distToPrev) {
+ var ratio = currentDepthPx / (currentDepthPx + prevDepthPx);
+ if (currentDepthPx + prevDepthPx === 0) ratio = 0.5;
+ var peakDistX = ratio * distToPrev;
+ var peakX = cx - peakDistX;
+ var rise = (peakDistX - PIN_HALF_WIDTH) / TANGENT;
+ var peakY = bottomY - rise;
+ sprite.drawLine(cx - PIN_HALF_WIDTH, bottomY, peakX, peakY, fg_color);
+ } else {
+ var slopeTopX = cx - myLeftWidth;
+ sprite.drawLine(cx - PIN_HALF_WIDTH, bottomY, slopeTopX, TOP_Y, fg_color);
+ var prevCx = cx - pinSpacing;
+ var prevSlopeTopX = prevCx + prevRightWidth;
+ sprite.drawLine(prevSlopeTopX, TOP_Y, slopeTopX, TOP_Y, fg_color);
+ }
+ }
+
+ var nextDepthPx = 0;
+ if (j < 5) nextDepthPx = (code[j+1] - 1) * DEPTH_STEP;
+ var myRightWidth = PIN_HALF_WIDTH + (currentDepthPx * TANGENT);
+ var nextLeftWidth = PIN_HALF_WIDTH + (nextDepthPx * TANGENT);
+ var distToNext = (j === 5) ? 999 : pinSpacing;
+
+ if (j === 5) {
+ var slopeEndX = cx + myRightWidth;
+ sprite.drawLine(cx + PIN_HALF_WIDTH, bottomY, slopeEndX, TOP_Y, fg_color);
+ sprite.drawLine(slopeEndX, TOP_Y, keyEnd, TOP_Y, fg_color);
+ } else {
+ if ((myRightWidth + nextLeftWidth) > distToNext) {
+ var ratio = currentDepthPx / (currentDepthPx + nextDepthPx);
+ if (currentDepthPx + nextDepthPx === 0) ratio = 0.5;
+ var peakDistX = ratio * distToNext;
+ var myPeakDistX = distToNext - peakDistX;
+ var peakX = cx + myPeakDistX;
+ var rise = (myPeakDistX - PIN_HALF_WIDTH) / TANGENT;
+ var peakY = bottomY - rise;
+ sprite.drawLine(cx + PIN_HALF_WIDTH, bottomY, peakX, peakY, fg_color);
+ } else {
+ var slopeTopX = cx + myRightWidth;
+ sprite.drawLine(cx + PIN_HALF_WIDTH, bottomY, slopeTopX, TOP_Y, fg_color);
+ }
+ }
+ }
+
+ // --- Menü ---
+ if (showMenu) {
+ var menuW = 80;
+ var menuH = 55;
+ var menuX = (dw - menuW) / 2;
+ var menuY = (dh - menuH) / 2;
+
+ sprite.drawFillRect(menuX, menuY, menuW, menuH, fg_color);
+
+ sprite.drawLine(menuX, menuY, menuX + menuW, menuY, bg_color);
+ sprite.drawLine(menuX, menuY + menuH, menuX + menuW, menuY + menuH, bg_color);
+ sprite.drawLine(menuX, menuY, menuX, menuY + menuH, bg_color);
+ sprite.drawLine(menuX + menuW, menuY, menuX + menuW, menuY + menuH, bg_color);
+
+ sprite.setTextSize(1);
+ sprite.setTextColor(bg_color, fg_color);
+
+ for (var m = 0; m < menuItems.length; m++) {
+ var itemY = menuY + 12 + (m * 12);
+ var itemText = menuItems[m];
+ if (m === menuCursor) itemText = "> " + itemText + " <";
+ sprite.drawText(itemText, menuX + 15, itemY);
+ }
+ }
+
+ // --- Üzenet ---
+ if (statusMsg !== "") {
+ var msgH = 20;
+ sprite.drawFillRect(0, dh - msgH, dw, msgH, fg_color);
+ sprite.drawLine(0, dh - msgH, dw, dh - msgH, bg_color);
+ sprite.setTextColor(bg_color, fg_color);
+ sprite.setTextSize(1);
+ sprite.drawText(statusMsg, (dw/2) - (statusMsg.length*3), dh - 15);
+ }
+ else if (!showMenu) {
+ sprite.setTextSize(1);
+ sprite.drawText("Back: Menu | Next: Cut", 5, dh - 12);
+ }
+
+ sprite.pushSprite();
+ delay(50);
+ }
+
+ keyboard.setLongPress(false);
+}
+
+main();
diff --git a/examples/M5Stick JS bruce/M5stickcplus2_arcade-games.js b/examples/M5Stick JS bruce/M5stickcplus2_arcade-games.js
new file mode 100644
index 0000000..9aeac8f
--- /dev/null
+++ b/examples/M5Stick JS bruce/M5stickcplus2_arcade-games.js
@@ -0,0 +1,2340 @@
+var display = require('display');
+var keyboardApi = require('keyboard');
+
+var fillScreen = display.fill;
+var drawRect = display.drawRect;
+var drawFillRect = display.drawFillRect;
+var drawString = display.drawString;
+var setTextColor = display.setTextColor;
+var setTextSize = display.setTextSize;
+
+var getPrevPress = keyboardApi.getPrevPress;
+var getSelPress = keyboardApi.getSelPress;
+var getNextPress = keyboardApi.getNextPress;
+
+var WIDTH = 240
+var HEIGHT = 135;
+var BLACK = 0;
+var WHITE = 16777215;
+var GREEN = 65280;
+var BLUE = 255;
+var YELLOW = 16776960;
+var CYAN = 65535;
+var MAGENTA = 16711935;
+var ORANGE = 16753920;
+var PURPLE = 8388736;
+var DARKBLUE = 128;
+var DARKRED = 8388608;
+var LIGHTGREEN = 8453888;
+var LIGHTBLUE = 10025880;
+var GRAY = 8421504;
+var GOLD = 16766720;
+var BROWN = 10824234;
+var RED = 255;
+var STATE_MAIN_MENU = 0;
+var STATE_BREAKOUT = 1;
+var STATE_SNAKE = 2;
+var STATE_SPACE_SHOOTER = 3;
+var STATE_SLOTS = 4;
+var STATE_FLAPPY_BIRD = 5;
+var STATE_BLACKJACK = 6;
+var STATE_PAUSED = 10;
+var STATE_GAME_OVER = 11;
+var STATE_LEVEL_UP = 12;
+var STATE_EXIT_CONFIRM = 13;
+var gameState = STATE_MAIN_MENU;
+var prevGameState = -1;
+var menuSelection = 0;
+var pauseMenuSelection = 0;
+var exitConfirmSelection = 0;
+var staticDrawn = false;
+var mainMenuStaticDrawn = false;
+var pauseStaticDrawn = false;
+var frameCounter = 0;
+var menuLastSelState = false;
+var mainMenuScroll = 0;
+var MENU_VISIBLE_ITEMS = 5;
+var menuOptions = ["BREAKOUT", "SNAKE", "SPACE SHOOTER", "SLOTS", "FLAPPY BIRD", "BLACKJACK", "QUIT"];
+function drawMainMenu() {
+ if (!mainMenuStaticDrawn || menuSelection !== menuLastSelection) {
+ fillScreen(BLACK);
+ setTextSize(3);
+ setTextColor(YELLOW);
+ drawString("ARCADE GAMES", 15, 15);
+ drawFillRect(10, 40, WIDTH - 20, HEIGHT - 50, BLACK);
+ drawRect(9, 39, WIDTH - 18, HEIGHT - 48, WHITE);
+ setTextSize(1);
+ setTextColor(WHITE);
+ if (menuSelection >= mainMenuScroll + MENU_VISIBLE_ITEMS) {
+ mainMenuScroll = menuSelection - MENU_VISIBLE_ITEMS + 1;
+ } else if (menuSelection < mainMenuScroll) {
+ mainMenuScroll = menuSelection;
+ }
+ var startY = 50;
+ var itemHeight = 15;
+ for (var i = 0; i < menuOptions.length; i++) {
+ if (i < mainMenuScroll || i >= mainMenuScroll + MENU_VISIBLE_ITEMS) continue;
+ var displayIdx = i - mainMenuScroll;
+ if (i === menuSelection) {
+ setTextColor(GREEN);
+ drawFillRect(55, startY + displayIdx * itemHeight - 2, 130, 15, GRAY);
+ drawString("> " + menuOptions[i], 60, startY + 4 + displayIdx * itemHeight);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + menuOptions[i], 60, startY + 4 + displayIdx * itemHeight);
+ }
+ }
+ if (mainMenuScroll > 0) {
+ setTextColor(YELLOW);
+ drawString("▲", WIDTH / 2 - 5, startY - 8);
+ }
+ if (mainMenuScroll + MENU_VISIBLE_ITEMS < menuOptions.length) {
+ setTextColor(YELLOW);
+ drawString("▼", WIDTH / 2 - 5, startY + MENU_VISIBLE_ITEMS * itemHeight + 8);
+ }
+
+ setTextColor(CYAN);
+ drawString("Developed by msi", WIDTH - 95, HEIGHT - 10);
+
+ mainMenuStaticDrawn = true;
+ menuLastSelection = menuSelection;
+ }
+}
+function drawPauseMenu() {
+ if (!pauseStaticDrawn || pauseMenuSelection !== menuLastSelection) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(WHITE);
+ drawString("PAUSED", 80, 40);
+ var options = ["CONTINUE", "MAIN MENU", "QUIT"];
+ setTextSize(1);
+ for (var i = 0; i < options.length; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(70, 60 + i * 15, 100, 12, GRAY);
+ drawString("> " + options[i], 75, 65 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 75, 65 + i * 15);
+ }
+ }
+ setTextColor(CYAN);
+ drawString("Developed by msi", WIDTH - 95, HEIGHT - 10);
+ pauseStaticDrawn = true;
+ menuLastSelection = pauseMenuSelection;
+ }
+}
+function drawGameOverMenu() {
+ if (!staticDrawn) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(YELLOW);
+ drawString("GAME OVER", 65, 20);
+ setTextColor(WHITE);
+ setTextSize(1);
+ if (prevGameState === STATE_FLAPPY_BIRD) drawString("Score: " + flappyScore, 90, 47);
+ else if (prevGameState === STATE_SNAKE) drawString("Score: " + snakeScore, 90, 47);
+ else if (prevGameState === STATE_SPACE_SHOOTER) drawString("Score: " + spaceScore, 90, 47);
+ else if (prevGameState === STATE_BREAKOUT) drawString("Score: " + breakoutScore, 90, 47);
+ var options = ["RESTART", "MAIN MENU", "QUIT"];
+ for (var i = 0; i < options.length; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(70, 60 + i * 15, 100, 12, GRAY);
+ drawString("> " + options[i], 75, 65 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 75, 65 + i * 15);
+ }
+ }
+
+ setTextColor(CYAN);
+ drawString("Developed by msi", WIDTH - 95, HEIGHT - 10);
+
+ staticDrawn = true;
+ }
+}
+function drawLevelUpMenu() {
+ if (!staticDrawn) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(GREEN);
+ drawString("LEVEL UP", 75, 40);
+ var options = ["CONTINUE", "MAIN MENU", "QUIT"];
+ setTextSize(1);
+ for (var i = 0; i < options.length; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(70, 60 + i * 15, 100, 12, GRAY);
+ drawString("> " + options[i], 75, 65 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 75, 65 + i * 15);
+ }
+ }
+
+ setTextColor(CYAN);
+ drawString("Developed by msi", WIDTH - 95, HEIGHT - 10);
+
+ staticDrawn = true;
+ }
+}
+function drawExitConfirm() {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(YELLOW);
+ drawString("EXIT?", 90, 40);
+ setTextSize(1);
+ var options = ["YES", "NO"];
+ for (var i = 0; i < options.length; i++) {
+ if (i === exitConfirmSelection) {
+ setTextColor(GREEN);
+ var highlightWidth = 40;
+ var highlightHeight = 15;
+ drawFillRect(70 + i * 60, 80, highlightWidth, highlightHeight, GRAY);
+ drawString("> " + options[i], 75 + i * 60, 85);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 75 + i * 60, 85);
+ }
+ }
+
+ setTextColor(CYAN);
+ drawString("Developed by msi", WIDTH - 95, HEIGHT - 10);
+
+ staticDrawn = true;
+}
+var PADDLE_WIDTH = 40;
+var PADDLE_HEIGHT = 6;
+var BALL_SIZE = 5;
+var BRICK_WIDTH = 20;
+var BRICK_HEIGHT = 10;
+var BRICK_MARGIN = 2;
+var BRICK_ROWS = 5;
+var BRICK_COLS = 10;
+var BRICK_COLORS = [YELLOW, ORANGE, YELLOW, GREEN, BLUE, PURPLE];
+var BREAKOUT_STATE_START = 0;
+var BREAKOUT_STATE_PLAYING = 1;
+var BREAKOUT_STATE_GAME_OVER = 2;
+var BREAKOUT_STATE_WIN = 3;
+var BREAKOUT_STATE_NEXT_LEVEL = 4;
+var breakoutState = BREAKOUT_STATE_START;
+var paddle = { x: WIDTH / 2 - PADDLE_WIDTH / 2, y: HEIGHT - 15, width: PADDLE_WIDTH, height: PADDLE_HEIGHT, speed: 12, color: WHITE, lastX: WIDTH / 2 - PADDLE_WIDTH / 2, lastY: HEIGHT - 15 };
+var ball = { x: WIDTH / 2, y: HEIGHT - 20 - BALL_SIZE, size: BALL_SIZE, speedX: 0, speedY: 0, color: WHITE, stuck: true, lastX: WIDTH / 2, lastY: HEIGHT - 20 - BALL_SIZE };
+var bricks = [];
+var breakoutScore = 0;
+var breakoutLives = 3;
+var breakoutLevel = 1;
+var breakoutStaticDrawn = false;
+var breakoutLastStaticDrawnState = -1;
+var breakoutSelPressCount = 0;
+var breakoutSelPressWindowStart = -1;
+var breakoutSelPressWindow = 400;
+var breakoutSelPressThreshold = 4;
+var breakoutLastSelState = false;
+var breakoutIsPaused = false;
+var breakoutPauseCooldown = 0;
+var lastScoreDrawn = -1;
+var lastLivesDrawn = -1;
+function resetBreakout() {
+ paddle.x = WIDTH / 2 - PADDLE_WIDTH / 2;
+ ball.x = paddle.x + paddle.width / 2;
+ ball.y = paddle.y - ball.size;
+ ball.stuck = true;
+ resetBall();
+ if (breakoutState === BREAKOUT_STATE_START || breakoutState === BREAKOUT_STATE_GAME_OVER) {
+ breakoutScore = 0;
+ breakoutLives = 3;
+ breakoutLevel = 1;
+ }
+ createBricks();
+ breakoutState = BREAKOUT_STATE_PLAYING;
+ breakoutPauseCooldown = 0;
+ breakoutStaticDrawn = false;
+ breakoutLastStaticDrawnState = -1;
+}
+function resetBall() {
+ ball.speedX = (Math.random() * 2 - 1) * 2;
+ ball.speedY = -3;
+}
+function createBricks() {
+ bricks = [];
+ for (var i = 0; i < BRICK_ROWS; i++) {
+ for (var j = 0; j < BRICK_COLS; j++) {
+ var colorIndex = i % BRICK_COLORS.length;
+ var strength = 1;
+ if (Math.random() < 0.1 * breakoutLevel) strength = 2;
+ bricks.push({ x: j * (BRICK_WIDTH + BRICK_MARGIN) + 10, y: i * (BRICK_HEIGHT + BRICK_MARGIN) + 20, width: BRICK_WIDTH, height: BRICK_HEIGHT, color: BRICK_COLORS[colorIndex], strength: strength, hit: false, changed: true });
+ }
+ }
+}
+function breakoutNextLevel() {
+ breakoutLevel++;
+ paddle.width = Math.max(PADDLE_WIDTH - (breakoutLevel - 1) * 3, 20);
+ resetBall();
+ ball.stuck = true;
+ breakoutState = BREAKOUT_STATE_NEXT_LEVEL;
+ breakoutStaticDrawn = false;
+ breakoutLastStaticDrawnState = -1;
+ tone(700, 200);
+ tone(900, 200);
+}
+function drawBreakout() {
+ switch (breakoutState) {
+ case BREAKOUT_STATE_START: drawBreakoutStartScreen(); break;
+ case BREAKOUT_STATE_PLAYING:
+ if (!breakoutIsPaused) {
+ drawBreakoutPlayScreen();
+ }
+ break;
+ case BREAKOUT_STATE_GAME_OVER: drawBreakoutGameOverScreen(); break;
+ case BREAKOUT_STATE_WIN: drawBreakoutWinScreen(); break;
+ case BREAKOUT_STATE_NEXT_LEVEL: drawBreakoutNextLevelScreen(); break;
+ }
+}
+function drawBreakoutStartScreen() {
+ if (!breakoutStaticDrawn || breakoutState !== breakoutLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(YELLOW);
+ drawString("BREAKOUT", WIDTH / 2 - 48, 10);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("PREV: Go Left", WIDTH / 2 - 39, 45);
+ drawString("NEXT: Go Right", WIDTH / 2 - 42, 61);
+ drawString("M5 (Press): Release Ball", WIDTH / 2 - 69, 77);
+ drawString("M5 (Hold): Pause", WIDTH / 2 - 48, 93);
+ setTextColor(GREEN);
+ drawString("Press M5 to Begin", WIDTH / 2 - 51, 125);
+ breakoutStaticDrawn = true;
+ breakoutLastStaticDrawnState = breakoutState;
+ }
+}
+function drawBreakoutPlayScreen() {
+ if (!breakoutStaticDrawn || breakoutState !== breakoutLastStaticDrawnState ||
+ breakoutScore !== lastScoreDrawn || breakoutLives !== lastLivesDrawn) {
+ if (breakoutScore !== lastScoreDrawn || breakoutLives !== lastLivesDrawn) {
+ drawFillRect(0, 0, WIDTH, 20, BLACK);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Score: " + breakoutScore, 10, 5);
+ drawString("Level: " + breakoutLevel, 100, 5);
+ drawString("Lives:", 180, 5);
+ for (var i = 0; i < breakoutLives; i++) drawFillRect(215 + i * 8, 5, 5, 5, GREEN);
+ lastScoreDrawn = breakoutScore;
+ lastLivesDrawn = breakoutLives;
+ } else {
+ fillScreen(BLACK);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Score: " + breakoutScore, 10, 5);
+ drawString("Level: " + breakoutLevel, 100, 5);
+ drawString("Lives:", 180, 5);
+ for (var i = 0; i < breakoutLives; i++) drawFillRect(215 + i * 8, 5, 5, 5, GREEN);
+ for (var i = 0; i < bricks.length; i++) {
+ if (!bricks[i].hit) {
+ drawFillRect(bricks[i].x, bricks[i].y, bricks[i].width, bricks[i].height, bricks[i].color);
+ if (bricks[i].strength > 1) drawRect(bricks[i].x + 2, bricks[i].y + 2, bricks[i].width - 4, bricks[i].height - 4, WHITE);
+ }
+ }
+ breakoutStaticDrawn = true;
+ }
+ breakoutLastStaticDrawnState = breakoutState;
+ }
+ for (var i = 0; i < bricks.length; i++) {
+ if (bricks[i].hit && bricks[i].changed) {
+ drawFillRect(bricks[i].x, bricks[i].y, BRICK_WIDTH, BRICK_HEIGHT, BLACK);
+ bricks[i].changed = false;
+ }
+ }
+ drawFillRect(paddle.lastX, paddle.lastY, paddle.width, paddle.height, BLACK);
+ drawFillRect(paddle.x, paddle.y, paddle.width, paddle.height, paddle.color);
+ paddle.lastX = paddle.x;
+ paddle.lastY = paddle.y;
+
+ drawFillRect(ball.lastX - ball.size / 2, ball.lastY - ball.size / 2, ball.size, ball.size, BLACK);
+ if (ball.y - ball.size / 2 < 20) ball.y = 20 + ball.size / 2;
+ drawFillRect(ball.x - ball.size / 2, ball.y - ball.size / 2, ball.size, ball.size, ball.color);
+ ball.lastX = ball.x;
+ ball.lastY = ball.y;
+}
+function drawBreakoutGameOverScreen() {
+ if (!breakoutStaticDrawn || breakoutState !== breakoutLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(WHITE);
+ drawString("GAME OVER", WIDTH / 2 - 54, 40);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Score: " + breakoutScore, WIDTH / 2 - 24, 80);
+ setTextColor(YELLOW);
+ drawString("M5 to Play Again", 72, 100);
+ drawString("PREV to Menu", 84, 115);
+ breakoutStaticDrawn = true;
+ breakoutLastStaticDrawnState = breakoutState;
+ tone(400, 300);
+ tone(300, 300);
+ }
+}
+function drawBreakoutWinScreen() {
+ if (!breakoutStaticDrawn || breakoutState !== breakoutLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(GREEN);
+ drawString("YOU WIN!", WIDTH / 2 - 48, 40);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Score: " + breakoutScore, WIDTH / 2 - 24, 80);
+ setTextColor(YELLOW);
+ drawString("M5 to Play Again", WIDTH / 2 - 66, 100);
+ drawString("PREV to Menu", WIDTH / 2 - 30, 115);
+ breakoutStaticDrawn = true;
+ breakoutLastStaticDrawnState = breakoutState;
+ tone(800, 200);
+ tone(1000, 200);
+ }
+}
+function drawBreakoutNextLevelScreen() {
+ if (!breakoutStaticDrawn || breakoutState !== breakoutLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(GREEN);
+ drawString("LEVEL UP!", WIDTH / 2 - 48, 40);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Score: " + breakoutScore, WIDTH / 2 - 24, 80);
+ setTextColor(YELLOW);
+ drawString("M5 to Continue", WIDTH / 2 - 66, 100);
+ breakoutStaticDrawn = true;
+ breakoutLastStaticDrawnState = breakoutState;
+ }
+}
+function updateBreakout() {
+ if (breakoutState !== BREAKOUT_STATE_PLAYING || breakoutIsPaused) return;
+ if (getPrevPress()) paddle.x -= paddle.speed;
+ else if (getNextPress()) paddle.x += paddle.speed;
+ if (paddle.x < 0) paddle.x = 0;
+ if (paddle.x + paddle.width > WIDTH) paddle.x = WIDTH - paddle.width;
+ if (ball.stuck) {
+ ball.x = paddle.x + paddle.width / 2;
+ ball.y = paddle.y - ball.size;
+ return;
+ }
+ ball.x += ball.speedX;
+ ball.y += ball.speedY;
+ if (ball.x - ball.size / 2 < 0 || ball.x + ball.size / 2 > WIDTH) {
+ ball.speedX = -ball.speedX;
+ tone(500, 100);
+ }
+ if (ball.y - ball.size / 2 < 0) {
+ ball.speedY = -ball.speedY;
+ tone(500, 100);
+ }
+ if (ball.y - ball.size / 2 < 20) {
+ ball.y = 20 + ball.size / 2;
+ ball.speedY = Math.abs(ball.speedY);
+ tone(500, 100);
+ }
+ if (ball.y + ball.size / 2 > HEIGHT) {
+ breakoutLives--;
+ tone(200, 300);
+ if (breakoutLives <= 0) breakoutState = BREAKOUT_STATE_GAME_OVER;
+ else ball.stuck = true;
+ return;
+ }
+ if (ball.y + ball.size / 2 > paddle.y - 2 && ball.y - ball.size / 2 < paddle.y + paddle.height && ball.x + ball.size / 2 > paddle.x && ball.x - ball.size / 2 < paddle.x + paddle.width) {
+ var hitPos = (ball.x - (paddle.x + paddle.width / 2)) / (paddle.width / 2);
+ ball.speedX = hitPos * 4;
+ ball.speedY = -Math.abs(ball.speedY) * 1.05;
+ tone(600, 150);
+ }
+ for (var i = 0; i < bricks.length; i++) {
+ if (!bricks[i].hit && ball.x + ball.size / 2 > bricks[i].x && ball.x - ball.size / 2 < bricks[i].x + bricks[i].width && ball.y + ball.size / 2 > bricks[i].y && ball.y - ball.size / 2 < bricks[i].y + bricks[i].height) {
+ bricks[i].strength--;
+ bricks[i].changed = true;
+ if (bricks[i].strength <= 0) {
+ bricks[i].hit = true;
+ breakoutScore += 10 * breakoutLevel;
+ tone(700, 100);
+ } else {
+ breakoutScore += 5;
+ tone(650, 100);
+ }
+ var overlapLeft = ball.x + ball.size / 2 - bricks[i].x;
+ var overlapRight = bricks[i].x + bricks[i].width - (ball.x - ball.size / 2);
+ var overlapTop = ball.y + ball.size / 2 - bricks[i].y;
+ var overlapBottom = bricks[i].y + bricks[i].height - (ball.y - ball.size / 2);
+ var minOverlap = Math.min(overlapLeft, overlapRight, overlapTop, overlapBottom);
+ if (minOverlap === overlapLeft || minOverlap === overlapRight) ball.speedX = -ball.speedX;
+ else ball.speedY = -ball.speedY;
+ break;
+ }
+ }
+ var remainingBricks = 0;
+ for (var i = 0; i < bricks.length; i++) if (!bricks[i].hit) remainingBricks++;
+ if (remainingBricks === 0) {
+ if (breakoutLevel < 5) breakoutNextLevel();
+ else breakoutState = BREAKOUT_STATE_WIN;
+ }
+}
+var GRID_SIZE = 10;
+var HUD_HEIGHT = 20;
+var COLS = Math.floor(WIDTH / GRID_SIZE);
+var ROWS = Math.floor((HEIGHT - HUD_HEIGHT) / GRID_SIZE);
+var cApple = 10079232;
+var SNAKE_STATE_MENU = 0;
+var SNAKE_STATE_GAME = 1;
+var SNAKE_STATE_PAUSED = 2;
+var SNAKE_STATE_GAME_OVER = 3;
+var snakeState = SNAKE_STATE_MENU;
+var snake = [];
+var direction = 0;
+var nextDirection = 0;
+var food = { x: 8, y: 4 };
+var snakeScore = 0;
+var snakeHighScore = 0;
+var snakeFrameCounter = 0;
+var snakeSpeed = 10;
+var snakeSpeedIncrease = true;
+var snakeStaticDrawn = false;
+var snakeLastStaticDrawnState = -1;
+var snakeLastSelState = false;
+var snakeTotalDelay = 300;
+var snakeDelayTime = snakeTotalDelay;
+var snakeCanMove = true;
+var snakeTime = now();
+var snakePrevTime = now();
+var snakeErasePos = null;
+var snakeLastScore = -1;
+var snakeLastHighScore = -1;
+var snakeForceHudRedraw = false;
+function resetSnake() {
+ snake = [
+ { x: 4, y: 4 },
+ { x: 3, y: 4 },
+ { x: 2, y: 4 },
+ { x: 1, y: 4 }
+ ];
+ direction = 3;
+ nextDirection = 3;
+ snakeScore = 0;
+ snakeFrameCounter = 0;
+ placeFood();
+ snakeState = SNAKE_STATE_GAME;
+ snakeStaticDrawn = false;
+ snakeLastStaticDrawnState = -1;
+ snakeDelayTime = snakeTotalDelay;
+ snakeErasePos = null;
+ snakeLastScore = -1;
+ snakeLastHighScore = -1;
+ snakeForceHudRedraw = true;
+}
+function placeFood() {
+ var validPos = false;
+ var maxAttempts = 100;
+ var attempts = 0;
+
+
+ var minX = 1;
+ var maxX = COLS - 2;
+ var minY = 1;
+ var maxY = ROWS - 2;
+
+ while (!validPos && attempts < maxAttempts) {
+ attempts++;
+ var newX = Math.floor(Math.random() * (maxX - minX)) + minX;
+ var newY = Math.floor(Math.random() * (maxY - minY)) + minY;
+
+ validPos = true;
+
+
+ for (var i = 0; i < snake.length; i++) {
+ if (snake[i].x === newX && snake[i].y === newY) {
+ validPos = false;
+ break;
+ }
+ }
+
+ if (validPos) {
+ food.x = newX;
+ food.y = newY;
+ break;
+ }
+ }
+
+
+ if (!validPos) {
+ food.x = 10;
+ food.y = 10;
+ }
+}
+function drawSnake() {
+ switch (snakeState) {
+ case SNAKE_STATE_MENU:
+ drawSnakeMenu();
+ break;
+ case SNAKE_STATE_GAME:
+ if (!snakeStaticDrawn || snakeState !== snakeLastStaticDrawnState) {
+ fillScreen(BLACK);
+ snakeStaticDrawn = true;
+ snakeLastStaticDrawnState = snakeState;
+ snakeForceHudRedraw = true;
+ }
+
+ if (snakeErasePos) {
+ drawFillRect(snakeErasePos.x * GRID_SIZE, snakeErasePos.y * GRID_SIZE + HUD_HEIGHT, GRID_SIZE, GRID_SIZE, BLACK);
+ snakeErasePos = null;
+ }
+
+ if (snake.length > 0) {
+ drawFillRect(snake[0].x * GRID_SIZE, snake[0].y * GRID_SIZE + HUD_HEIGHT, GRID_SIZE, GRID_SIZE, YELLOW);
+ for (var i = 1; i < snake.length; i++) {
+ drawFillRect(snake[i].x * GRID_SIZE, snake[i].y * GRID_SIZE + HUD_HEIGHT, GRID_SIZE, GRID_SIZE, GREEN);
+ }
+ }
+
+ drawFillRect(food.x * GRID_SIZE, food.y * GRID_SIZE + HUD_HEIGHT, GRID_SIZE, GRID_SIZE, cApple);
+
+ if (snakeScore !== snakeLastScore || snakeHighScore !== snakeLastHighScore || snakeForceHudRedraw) {
+ drawSnakeScore();
+ snakeLastScore = snakeScore;
+ snakeLastHighScore = snakeHighScore;
+ snakeForceHudRedraw = false;
+ }
+ break;
+ case SNAKE_STATE_PAUSED:
+ break;
+ case SNAKE_STATE_GAME_OVER:
+ break;
+ }
+}
+function drawSnakeMenu() {
+ if (!snakeStaticDrawn || snakeState !== snakeLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(3);
+ setTextColor(GREEN);
+ drawString("SNAKE", 75, 10);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("PRESS M5 TO START", 66, 55);
+ drawString("PREV: TURN LEFT", 75, 80);
+ drawString("NEXT: TURN RIGHT", 72, 95);
+ drawString("M5: PAUSE", 90, 110);
+ snakeStaticDrawn = true;
+ snakeLastStaticDrawnState = snakeState;
+ }
+}
+function drawSnakeScore() {
+ drawFillRect(0, 0, WIDTH, HUD_HEIGHT, BLACK);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("SCORE: " + snakeScore, 5, 5);
+ drawString("HIGH SCORE: " + snakeHighScore, 150, 5);
+}
+function updateSnakeDelayTime() {
+ var timePassed = snakeTime - snakePrevTime;
+ if (timePassed == 0) {
+ snakeTime = now();
+ return;
+ }
+ snakePrevTime = snakeTime;
+ snakeTime = now();
+ snakeDelayTime -= timePassed;
+}
+function updateSnake() {
+ if (snakeState !== SNAKE_STATE_GAME || snake.length === 0) return;
+ updateSnakeDelayTime();
+ if (snakeDelayTime < 0) {
+ snakeCanMove = true;
+
+ var newHeadX = snake[0].x;
+ var newHeadY = snake[0].y;
+
+ switch (direction) {
+ case 0: newHeadY -= 1; break;
+ case 1: newHeadY += 1; break;
+ case 2: newHeadX -= 1; break;
+ case 3: newHeadX += 1; break;
+ }
+
+ if (newHeadX < 0 || newHeadX >= COLS || newHeadY < 0 || newHeadY >= ROWS) {
+ snakeGameOver();
+ return;
+ }
+
+ for (var i = 0; i < snake.length; i++) {
+ if (newHeadX === snake[i].x && newHeadY === snake[i].y) {
+ snakeGameOver();
+ return;
+ }
+ }
+
+ snake.unshift({ x: newHeadX, y: newHeadY });
+
+ if (newHeadX === food.x && newHeadY === food.y) {
+ snakeScore += 10;
+ if (snakeScore > snakeHighScore) snakeHighScore = snakeScore;
+
+ placeFood();
+
+ tone(600, 150);
+ } else {
+ var tail = snake.pop();
+ snakeErasePos = { x: tail.x, y: tail.y };
+ }
+
+ direction = nextDirection;
+ snakeDelayTime = snakeTotalDelay;
+ }
+}
+function snakeGameOver() {
+ snakeState = SNAKE_STATE_GAME_OVER;
+ gameState = STATE_GAME_OVER;
+ prevGameState = STATE_SNAKE;
+ staticDrawn = false;
+ snakeStaticDrawn = false;
+ if (snakeScore > snakeHighScore) snakeHighScore = snakeScore;
+}
+var PLAYER_SIZE = 16;
+var ENEMY_SIZE = 14;
+var BULLET_SIZE = 5;
+var POWERUP_SIZE = 10;
+var EXPLOSION_MAX_SIZE = 22;
+var SPACE_STATE_MENU = 0;
+var SPACE_STATE_GAME = 1;
+var SPACE_STATE_GAME_OVER = 2;
+var SPACE_STATE_LEVEL_UP = 3;
+var spaceState = SPACE_STATE_MENU;
+var player = { x: WIDTH / 2, y: HEIGHT - 25, width: PLAYER_SIZE, height: PLAYER_SIZE, speed: 10, lives: 3, weaponLevel: 1, weaponTime: 0, invincible: false, invincibleTime: 0, lastX: WIDTH / 2, lastY: HEIGHT - 25 };
+var bullets = [];
+var enemies = [];
+var enemyBullets = [];
+var explosions = [];
+var stars = [];
+var powerups = [];
+var spaceScore = 0;
+var spaceHighScore = 0;
+var spaceLevel = 1;
+var spaceFrameCounter = 0;
+var enemySpawnRate = 50;
+var enemyShootRate = 100;
+var bossActive = false;
+var boss = null;
+var killCount = 0;
+var levelUpThreshold = 20;
+var fireRate = 6;
+var lastFireTime = 0;
+var spaceLastSelState = false;
+var spaceIsPaused = false;
+var spaceStaticDrawn = false;
+var spaceLastStaticDrawnState = -1;
+function resetSpaceShooter() {
+ player.x = WIDTH / 2;
+ player.y = HEIGHT - 25;
+ player.lastX = player.x;
+ player.lastY = player.y;
+ player.lives = 3;
+ player.weaponLevel = 1;
+ player.weaponTime = 0;
+ player.invincible = false;
+ player.invincibleTime = 0;
+ bullets = [];
+ enemies = [];
+ enemyBullets = [];
+ explosions = [];
+ powerups = [];
+ boss = null;
+ spaceScore = 0;
+ spaceLevel = 1;
+ spaceFrameCounter = 0;
+ enemySpawnRate = 50;
+ enemyShootRate = 100;
+ bossActive = false;
+ killCount = 0;
+ lastFireTime = 0;
+ spaceLastSelState = false;
+ spaceIsPaused = false;
+ createStars();
+ spaceState = SPACE_STATE_GAME;
+ spaceStaticDrawn = false;
+ spaceLastStaticDrawnState = -1;
+}
+function createStars() {
+ stars = [];
+ for (var i = 0; i < 60; i++) stars.push({ x: Math.random() * WIDTH, y: Math.random() * HEIGHT, size: Math.random() * 2.5 + 1, speed: Math.random() * 0.8 + 0.3, lastX: 0, lastY: 0 });
+}
+function updateStars() {
+ for (var i = 0; i < stars.length; i++) {
+ var star = stars[i];
+ star.y += star.speed * 0.1;
+ if (star.y > HEIGHT) star.y -= HEIGHT;
+ }
+}
+function drawStars() {
+ for (var i = 0; i < stars.length; i++) {
+ var star = stars[i];
+ drawFillRect(star.lastX - 1, star.lastY - 1, star.size + 2, star.size + 2, BLACK);
+ var brightness = (spaceFrameCounter + i * 10) % 100;
+ var color = brightness < 50 ? WHITE : CYAN;
+ if (i % 7 === 0) color = YELLOW;
+ if (i % 11 === 0) color = MAGENTA;
+ drawFillRect(star.x, star.y, star.size, star.size, color);
+ star.lastX = star.x;
+ star.lastY = star.y;
+ }
+}
+function drawSpaceShooter() {
+ switch (spaceState) {
+ case SPACE_STATE_MENU: drawSpaceMenu(); break;
+ case SPACE_STATE_GAME:
+ if (!spaceIsPaused) drawSpaceGameplay();
+ break;
+ case SPACE_STATE_GAME_OVER: break;
+ case SPACE_STATE_LEVEL_UP: break;
+ }
+}
+function drawSpaceMenu() {
+ if (!spaceStaticDrawn || spaceState !== spaceLastStaticDrawnState) {
+ fillScreen(BLACK);
+ setTextSize(3);
+ setTextColor(CYAN);
+ drawString("SPACE", 75, 15);
+ setTextColor(ORANGE);
+ drawString("SHOOTER", 60, 40);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("PREV: GO LEFT", 10, 100);
+ drawString("NEXT: GO RIGHT", 140, 100);
+ drawString("M5: PAUSE", 105, 120);
+ if (spaceFrameCounter % 30 < 20) {
+ setTextColor(WHITE);
+ drawString("PRESS M5 TO START", 70, 75);
+ }
+ spaceStaticDrawn = true;
+ spaceLastStaticDrawnState = spaceState;
+ }
+}
+function drawSpaceGameplay() {
+ if (!spaceStaticDrawn || spaceState !== spaceLastStaticDrawnState) {
+ fillScreen(BLACK);
+ spaceStaticDrawn = true;
+ spaceLastStaticDrawnState = spaceState;
+ }
+ if (spaceFrameCounter % 10 === 0) {
+ updateStars();
+ drawStars();
+ }
+ drawEnemies();
+ drawBullets();
+ drawEnemyBullets();
+ drawExplosions();
+ drawPowerups();
+ if (player.x !== player.lastX || player.y !== player.lastY) {
+ drawFillRect(player.lastX - player.width/2 - 6, player.lastY - 5, player.width + 12, player.height + 15, BLACK);
+ player.lastX = player.x;
+ player.lastY = player.y;
+ }
+ drawPlayer(player.x, player.y, player.weaponLevel);
+ drawSpaceHUD();
+}
+function drawPlayer(x, y, weaponLevel) {
+ drawFillRect(x - player.width/2, y, player.width, player.height, BLUE);
+ drawFillRect(x - player.width/2 + 3, y + 3, player.width - 6, player.height - 8, CYAN);
+ drawFillRect(x - 4, y - 5, 8, 5, WHITE);
+ if (spaceFrameCounter % 6 < 3) {
+ drawFillRect(x - 6, y + player.height, 12, 4, YELLOW);
+ drawFillRect(x - 4, y + player.height + 4, 8, 3, YELLOW);
+ } else {
+ drawFillRect(x - 5, y + player.height, 10, 5, YELLOW);
+ drawFillRect(x - 3, y + player.height + 3, 6, 2, YELLOW);
+ }
+ if (weaponLevel > 1) {
+ drawFillRect(x - player.width/2 - 4, y + 3, 4, 4, YELLOW);
+ drawFillRect(x + player.width/2, y + 3, 4, 4, YELLOW);
+ drawFillRect(x - player.width/2 - 2, y + 5, 2, 6, YELLOW);
+ drawFillRect(x + player.width/2 + 1, y + 5, 2, 6, YELLOW);
+ }
+ if (weaponLevel > 2) {
+ drawFillRect(x - player.width/2 - 4, y + 10, 4, 4, YELLOW);
+ drawFillRect(x + player.width/2, y + 10, 4, 4, YELLOW);
+ }
+ if (player.invincible && spaceFrameCounter % 6 < 3) drawRect(x - player.width/2 - 2, y - 2, player.width + 4, player.height + 4, WHITE);
+}
+function drawEnemies() {
+ for (var i = 0; i < enemies.length; i++) {
+ var enemy = enemies[i];
+ if (enemy && enemy.active) {
+ drawFillRect(enemy.lastX - 5, enemy.lastY - 5, enemy.width + 10, enemy.height + 10, BLACK);
+ drawFillRect(enemy.x, enemy.y, enemy.width, enemy.height, enemy.type.color);
+ enemy.lastX = enemy.x;
+ enemy.lastY = enemy.y;
+ }
+ }
+ if (bossActive && boss) {
+ drawFillRect(boss.lastX - 5, boss.lastY - 5, boss.width + 10, boss.height + 10, BLACK);
+ drawFillRect(boss.x, boss.y, boss.width, boss.height, MAGENTA);
+ boss.lastX = boss.x;
+ boss.lastY = boss.y;
+ }
+}
+function drawBullets() {
+ for (var i = 0; i < bullets.length; i++) {
+ var bullet = bullets[i];
+ if (bullet && bullet.active) {
+ if (!bullet.lastX) bullet.lastX = bullet.x;
+ if (!bullet.lastY) bullet.lastY = bullet.y;
+ if (bullet.x !== bullet.lastX || bullet.y !== bullet.lastY) {
+ drawFillRect(bullet.lastX - bullet.width / 2, bullet.lastY, bullet.width, bullet.height + 2, BLACK);
+ drawFillRect(bullet.x - bullet.width / 2, bullet.y, bullet.width, bullet.height, GREEN);
+ bullet.lastX = bullet.x;
+ bullet.lastY = bullet.y;
+ }
+ }
+ }
+}
+function drawEnemyBullets() {
+ for (var i = 0; i < enemyBullets.length; i++) {
+ var bullet = enemyBullets[i];
+ if (bullet && bullet.active) {
+ if (!bullet.lastX) bullet.lastX = bullet.x;
+ if (!bullet.lastY) bullet.lastY = bullet.y;
+ if (bullet.x !== bullet.lastX || bullet.y !== bullet.lastY) {
+ drawFillRect(bullet.lastX - bullet.width / 2, bullet.lastY, bullet.width, bullet.height, BLACK);
+ drawFillRect(bullet.x - bullet.width / 2, bullet.y, bullet.width, bullet.height, YELLOW);
+ bullet.lastX = bullet.x;
+ bullet.lastY = bullet.y;
+ }
+ }
+ }
+}
+function drawExplosions() {
+ for (var i = 0; i < explosions.length; i++) {
+ var explosion = explosions[i];
+ if (explosion && explosion.active) drawFillRect(explosion.x - explosion.size / 2, explosion.y - explosion.size / 2, explosion.size, explosion.size, YELLOW);
+ }
+}
+function drawPowerups() {
+ for (var i = 0; i < powerups.length; i++) {
+ var powerup = powerups[i];
+ if (powerup && powerup.active) {
+ if (!powerup.lastX) powerup.lastX = powerup.x;
+ if (!powerup.lastY) powerup.lastY = powerup.y;
+ if (powerup.x !== powerup.lastX || powerup.y !== powerup.lastY) {
+ drawFillRect(powerup.lastX - powerup.width / 2 - 2, powerup.lastY - 2, powerup.width + 4, powerup.height + 4, BLACK);
+ drawFillRect(powerup.x - powerup.width / 2, powerup.y, powerup.width, powerup.height, powerup.type.color);
+ powerup.lastX = powerup.x;
+ powerup.lastY = powerup.y;
+ }
+ }
+ }
+}
+function drawSpaceHUD() {
+ drawFillRect(0, 0, WIDTH, 10, BLACK);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("SCORE: " + spaceScore, 5, 5);
+ drawString("LEVEL: " + spaceLevel, 100, 5);
+ drawString("LIVES: " + player.lives, 180, 5);
+}
+function updateSpaceShooter() {
+ if (spaceState !== SPACE_STATE_GAME || spaceIsPaused) return;
+ spaceFrameCounter++;
+ if (spaceFrameCounter - lastFireTime >= fireRate) {
+ fireBullet();
+ lastFireTime = spaceFrameCounter;
+ }
+ updateBullets();
+ updateEnemies();
+ updateEnemyBullets();
+ updateExplosions();
+ updatePowerups();
+ checkCollisions();
+ checkLevelProgress();
+}
+function updateBullets() {
+ for (var i = 0; i < bullets.length; i++) {
+ if (bullets[i] && bullets[i].active) {
+ bullets[i].y -= bullets[i].speed;
+ if (bullets[i].y + bullets[i].height < 0) bullets[i].active = false;
+ }
+ }
+}
+function updateEnemies() {
+ if (spaceFrameCounter % enemySpawnRate === 0 && !bossActive) spawnEnemy();
+ for (var i = 0; i < enemies.length; i++) {
+ if (enemies[i] && enemies[i].active) {
+ enemies[i].y += enemies[i].type.speed;
+ if (enemies[i].y > HEIGHT) {
+ drawFillRect(enemies[i].lastX - 5, enemies[i].lastY - 5, enemies[i].width + 10, enemies[i].height + 10, BLACK);
+ enemies[i].active = false;
+ }
+ if (enemies[i].type.shootRate > 0 && spaceFrameCounter % enemies[i].type.shootRate === 0) spawnEnemyBullet(enemies[i].x + enemies[i].width / 2, enemies[i].y + enemies[i].height);
+ }
+ }
+ if (bossActive && boss) {
+ boss.y += boss.speed;
+ if (boss.y + boss.height > HEIGHT) {
+ boss.y = HEIGHT - boss.height;
+ boss.speed = -1.5;
+ } else if (boss.y < 0) {
+ boss.y = 0;
+ boss.speed = 1.5;
+ }
+ if (spaceFrameCounter % 50 === 0) spawnEnemyBullet(boss.x + boss.width / 2, boss.y + boss.height);
+ }
+}
+function updateEnemyBullets() {
+ for (var i = 0; i < enemyBullets.length; i++) {
+ if (enemyBullets[i] && enemyBullets[i].active) {
+ enemyBullets[i].y += enemyBullets[i].speed;
+ if (enemyBullets[i].y > HEIGHT) enemyBullets[i].active = false;
+ }
+ }
+}
+function updateExplosions() {
+ for (var i = 0; i < explosions.length; i++) {
+ if (explosions[i] && explosions[i].active && explosions[i].life > 0) {
+ explosions[i].life--;
+ if (explosions[i].life <= 0) {
+ explosions[i].active = false;
+ drawFillRect(explosions[i].x - explosions[i].size / 2, explosions[i].y - explosions[i].size / 2, explosions[i].size, explosions[i].size, BLACK);
+ }
+ }
+ }
+}
+function updatePowerups() {
+ for (var i = 0; i < powerups.length; i++) {
+ if (powerups[i] && powerups[i].active) {
+ powerups[i].y += 1;
+ if (powerups[i].y > HEIGHT) powerups[i].active = false;
+ }
+ }
+}
+var enemyTypes = [
+ { color: BLUE, detailColor: PURPLE, health: 1, speed: 1.2, points: 10, shootRate: 0, shape: "triangle" },
+ { color: LIGHTGREEN, detailColor: DARKBLUE, health: 2, speed: 1.8, points: 20, shootRate: 100, shape: "square" },
+ { color: ORANGE, detailColor: MAGENTA, health: 1, speed: 2.5, points: 15, shootRate: 0, shape: "diamond" },
+ { color: BLUE, detailColor: YELLOW, health: 3, speed: 0.9, points: 30, shootRate: 70, shape: "circle" }
+];
+var powerupTypes = [
+ { type: "health", color: WHITE },
+ { type: "weapon", color: YELLOW }
+];
+function spawnEnemy() {
+ var type = enemyTypes[Math.floor(Math.random() * enemyTypes.length)];
+ enemies.push({ x: Math.random() * (WIDTH - ENEMY_SIZE), y: 0, width: ENEMY_SIZE, height: ENEMY_SIZE, type: type, active: true, lastX: 0, lastY: 0 });
+}
+function spawnEnemyBullet(x, y) {
+ enemyBullets.push({ x: x, y: y, width: BULLET_SIZE, height: BULLET_SIZE, speed: 3, active: true, lastX: 0, lastY: 0 });
+}
+function spawnBoss() {
+ var bossWidth = Math.min(28 + (spaceLevel - 1) * 4, 56);
+ var bossHeight = Math.min(28 + (spaceLevel - 1) * 4, 56);
+ boss = { x: WIDTH / 2 - bossWidth / 2, y: -bossHeight, width: bossWidth, height: bossHeight, health: 25 * spaceLevel, active: true, speed: 1.5, lastX: WIDTH / 2 - bossWidth / 2, lastY: -bossHeight };
+ bossActive = true;
+}
+function spawnPowerup(x, y) {
+ if (powerups.length < 5 && Math.random() < 0.1) {
+ var type = powerupTypes[Math.floor(Math.random() * powerupTypes.length)];
+ powerups.push({ x: x, y: y, width: POWERUP_SIZE, height: POWERUP_SIZE, type: type, active: true, lastX: x, lastY: y });
+ }
+}
+function fireBullet() {
+ bullets.push({ x: player.x, y: player.y - player.height / 2, width: BULLET_SIZE, height: BULLET_SIZE, speed: 5, active: true, lastX: player.x, lastY: player.y - player.height / 2 });
+}
+function checkCollisions() {
+ for (var i = 0; i < bullets.length; i++) {
+ if (!bullets[i] || !bullets[i].active) continue;
+ var bullet = bullets[i];
+ for (var j = 0; j < enemies.length; j++) {
+ if (!enemies[j] || !enemies[j].active) continue;
+ var enemy = enemies[j];
+ if (checkCollision(bullet.x - bullet.width/2, bullet.y - bullet.height/2, bullet.width, bullet.height, enemy.x, enemy.y, enemy.width, enemy.height)) {
+ bullet.active = false;
+ drawFillRect(bullet.lastX - bullet.width / 2, bullet.lastY, bullet.width, bullet.height + 2, BLACK);
+ enemy.active = false;
+ drawFillRect(enemy.lastX - 5, enemy.lastY - 5, enemy.width + 10, enemy.height + 10, BLACK);
+ spaceScore += enemy.type.points;
+ killCount++;
+ createExplosion(enemy.x + enemy.width/2, enemy.y + enemy.height/2);
+ spawnPowerup(enemy.x, enemy.y);
+ }
+ }
+ if (bossActive && boss && checkCollision(bullet.x - bullet.width/2, bullet.y - bullet.height/2, bullet.width, bullet.height, boss.x, boss.y, boss.width, boss.height)) {
+ bullet.active = false;
+ drawFillRect(bullet.lastX - bullet.width / 2, bullet.lastY, bullet.width, bullet.height + 2, BLACK);
+ boss.health--;
+ if (boss.health <= 0) {
+ bossActive = false;
+ spaceScore += 500;
+ killCount += 5;
+ createExplosion(boss.x + boss.width/2, boss.y + boss.height/2);
+ spaceLevelUp();
+ }
+ }
+ }
+ if (!player.invincible) {
+ for (var j = 0; j < enemies.length; j++) {
+ if (!enemies[j] || !enemies[j].active) continue;
+ var enemy = enemies[j];
+ if (checkCollision(player.x - player.width/2, player.y, player.width, player.height, enemy.x, enemy.y, enemy.width, enemy.height)) {
+ enemy.active = false;
+ drawFillRect(enemy.lastX - 5, enemy.lastY - 5, enemy.width + 10, enemy.height + 10, BLACK);
+ player.lives--;
+ createExplosion(player.x, player.y + player.height, true);
+ player.invincible = true;
+ player.invincibleTime = 60;
+ tone(500, 200);
+ if (player.lives <= 0) {
+ spaceState = SPACE_STATE_GAME_OVER;
+ gameState = STATE_GAME_OVER;
+ prevGameState = STATE_SPACE_SHOOTER;
+ staticDrawn = false;
+ if (spaceScore > spaceHighScore) spaceHighScore = spaceScore;
+ spaceStaticDrawn = false;
+ }
+ break;
+ }
+ }
+ for (var j = 0; j < enemyBullets.length; j++) {
+ if (!enemyBullets[j] || !enemyBullets[j].active) continue;
+ var bullet = enemyBullets[j];
+ if (checkCollision(player.x - player.width/2, player.y, player.width, player.height, bullet.x - bullet.width/2, bullet.y, bullet.width, bullet.height)) {
+ bullet.active = false;
+ player.lives--;
+ createExplosion(player.x, player.y + player.height, true);
+ player.invincible = true;
+ player.invincibleTime = 60;
+ tone(500, 200);
+ if (player.lives <= 0) {
+ spaceState = SPACE_STATE_GAME_OVER;
+ gameState = STATE_GAME_OVER;
+ prevGameState = STATE_SPACE_SHOOTER;
+ staticDrawn = false;
+ if (spaceScore > spaceHighScore) spaceHighScore = spaceScore;
+ spaceStaticDrawn = false;
+ }
+ break;
+ }
+ }
+ if (bossActive && boss && checkCollision(player.x - player.width/2, player.y, player.width, player.height, boss.x, boss.y, boss.width, boss.height)) {
+ player.lives--;
+ createExplosion(player.x, player.y + player.height, true);
+ player.invincible = true;
+ player.invincibleTime = 60;
+ tone(500, 200);
+ if (player.lives <= 0) {
+ spaceState = SPACE_STATE_GAME_OVER;
+ gameState = STATE_GAME_OVER;
+ prevGameState = STATE_SPACE_SHOOTER;
+ staticDrawn = false;
+ if (spaceScore > spaceHighScore) spaceHighScore = spaceScore;
+ spaceStaticDrawn = false;
+ }
+ }
+ }
+ for (var i = 0; i < powerups.length; i++) {
+ if (!powerups[i] || !powerups[i].active) continue;
+ var powerup = powerups[i];
+ if (checkCollision(player.x - player.width/2, player.y, player.width, player.height, powerup.x - powerup.width/2, powerup.y, powerup.width, powerup.height)) {
+ powerup.active = false;
+ drawFillRect(powerup.lastX - powerup.width / 2 - 2, powerup.lastY - 2, powerup.width + 4, powerup.height + 4, BLACK);
+ if (powerup.type.type === "health" && player.lives < 3) player.lives++;
+ else if (powerup.type.type === "weapon" && player.weaponLevel < 3) {
+ player.weaponLevel++;
+ player.weaponTime = 300;
+ }
+ }
+ }
+ if (player.invincible) {
+ player.invincibleTime--;
+ if (player.invincibleTime <= 0) player.invincible = false;
+ }
+ if (player.weaponTime > 0) {
+ player.weaponTime--;
+ if (player.weaponTime <= 0) player.weaponLevel = 1;
+ }
+}
+function checkLevelProgress() {
+ if (!bossActive && killCount >= levelUpThreshold) spawnBoss();
+}
+function spaceLevelUp() {
+ spaceLevel++;
+ killCount = 0;
+ spaceScore += spaceLevel * 100;
+ enemies = [];
+ enemyBullets = [];
+ enemySpawnRate = Math.max(20, 50 - spaceLevel * 5);
+ enemyShootRate = Math.max(40, 100 - spaceLevel * 10);
+ spaceState = SPACE_STATE_LEVEL_UP;
+}
+function createExplosion(x, y, isPlayerExplosion) {
+ if (explosions.length < 10) {
+ var explosion = { x: x, y: y, size: EXPLOSION_MAX_SIZE, active: true, life: 10 };
+ if (isPlayerExplosion) explosion.size = 16;
+ explosions.push(explosion);
+ tone(600, 150);
+ }
+}
+function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2) {
+ return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
+}
+var SLOT_STATE_MENU = 0;
+var SLOT_STATE_SPIN = 1;
+var SLOT_STATE_GAME_OVER = 2;
+var SLOT_STATE_PAUSED = 3;
+var slotState = SLOT_STATE_MENU;
+var slotMoney = 300;
+var slotBetOptions = [1, 2, 3, 5, 10, 20];
+var slotBetIndex = 0;
+var slotReels = [0, 0, 0];
+var slotSymbols = ["7", "BAR", "Bell", "Chy", "Lem"];
+var slotWeights = [15, 15, 20, 25, 25];
+var slotStaticDrawn = false;
+var slotLastSelState = false;
+var slotMessage = "";
+var slotMessageTimer = 0;
+function resetSlots() {
+ slotMoney = 500;
+ slotBetIndex = 0;
+ slotReels = [0, 0, 0];
+ slotState = SLOT_STATE_SPIN;
+ slotStaticDrawn = false;
+ slotMessage = "";
+ slotMessageTimer = 0;
+}
+function drawSlots() {
+ if (!slotStaticDrawn) {
+ fillScreen(BLACK);
+ setTextSize(1);
+ if (slotState === SLOT_STATE_MENU) {
+ setTextSize(2);
+ setTextColor(YELLOW);
+ drawString("SLOTS", 90, 20);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("M5: Bet", 100, 50);
+ drawString("NEXT: Bet Change", 95, 70);
+ drawString("Press M5 to Start", 80, 90);
+ } else if (slotState === SLOT_STATE_SPIN) {
+ setTextColor(WHITE);
+ drawString("Money: " + slotMoney, 10, 10);
+ drawString("PREV: Menu", 10, 30);
+ drawString("Bet: " + slotBetOptions[slotBetIndex], 100, 10);
+ setTextSize(2);
+ setTextColor(CYAN);
+ var reel1 = slotSymbols[slotReels[0]].length === 1 ? " " + slotSymbols[slotReels[0]] + " " : slotSymbols[slotReels[0]];
+ var reel2 = slotSymbols[slotReels[1]].length === 1 ? " | " + slotSymbols[slotReels[1]] + " |" : " |" + slotSymbols[slotReels[1]] + "|";
+ var reel3 = slotSymbols[slotReels[2]].length === 1 ? " " + slotSymbols[slotReels[2]] + " " : slotSymbols[slotReels[2]];
+ var reelWidth = 45;
+ var totalReelWidth = reelWidth * 3 + 30;
+ var startX = (WIDTH - totalReelWidth) / 2 - 20;
+ drawString(String.fromCharCode(91), startX, 60);
+ drawString(reel1, startX + 10, 60);
+ drawString(reel2, startX + 55, 60);
+ drawString(reel3, startX + 135, 60);
+ drawString(String.fromCharCode(93), startX + 180, 60);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("M5: Spin", 10, 110);
+ drawString("NEXT: Bet Change", 145, 110);
+ if (slotMessageTimer > 0 && slotMessage !== "") {
+ setTextColor(YELLOW);
+ setTextSize(2);
+ var centerX = 120 - 20;
+ if (slotMessage === "JACKPOT!") drawString(slotMessage, centerX - 40, 110);
+ else if (slotMessage === "WIN!") drawString(slotMessage, centerX - 20, 110);
+ else if (slotMessage === "Pair!") drawString(slotMessage, centerX - 20, 110);
+ else if (slotMessage === "So Close!") drawString(slotMessage, centerX - 40, 110);
+ slotMessageTimer--;
+ if (slotMessageTimer <= 0) slotMessage = "";
+ }
+ } else if (slotState === SLOT_STATE_GAME_OVER) {
+ setTextSize(2);
+ setTextColor(MAGENTA);
+ drawString("BUSTED!", 80, 50);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("M5 to Retry", 70, 90);
+ drawString("PREV to Menu", 70, 110);
+ } else if (slotState === SLOT_STATE_PAUSED) {
+ drawSlotsPausedMenu();
+ }
+ slotStaticDrawn = true;
+ }
+}
+function drawSlotsPausedMenu() {
+ if (!slotStaticDrawn || pauseMenuSelection !== menuLastSelection) {
+ fillScreen(BLACK);
+ var frameX = (WIDTH - 100) / 2;
+ var frameY = (HEIGHT - 60) / 2;
+ drawRect(frameX, frameY, 100, 60, WHITE);
+ var optionYStart = frameY + 10;
+ var options = ["Resume", "Main Menu", "Exit"];
+ for (var i = 0; i < 3; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(frameX + 5, optionYStart + i * 15, 90, 12, GRAY);
+ drawString("> " + options[i], frameX + 10, optionYStart + 5 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], frameX + 10, optionYStart + 5 + i * 15);
+ }
+ }
+ slotStaticDrawn = true;
+ menuLastSelection = pauseMenuSelection;
+ }
+}
+function getWeightedRandom(weights) {
+ var totalWeight = weights.reduce(function(sum, w) { return sum + w; }, 0);
+ var roll = Math.random() * totalWeight;
+ var cumulative = 0;
+ for (var i = 0; i < weights.length; i++) {
+ cumulative += weights[i];
+ if (roll < cumulative) return i;
+ }
+ return weights.length - 1;
+}
+function updateSlots(selPressed) {
+ if (slotState !== SLOT_STATE_SPIN) return;
+ var bet = slotBetOptions[slotBetIndex];
+ if (slotMoney < bet) slotBetIndex = Math.max(0, slotBetIndex - 1);
+ if (selPressed) {
+ slotMoney -= bet;
+ slotReels = [
+ getWeightedRandom(slotWeights),
+ getWeightedRandom(slotWeights),
+ getWeightedRandom(slotWeights)
+ ];
+ slotMessage = "";
+ if (slotReels[0] === 0 && slotReels[1] === 0 && slotReels[2] === 0) {
+ slotMoney += bet * 40;
+ slotMessage = "JACKPOT!";
+ slotMessageTimer = 30;
+ tone(1000, 500);
+ } else if (slotReels[0] === slotReels[1] && slotReels[1] === slotReels[2]) {
+ var multiplier;
+ switch (slotReels[0]) {
+ case 0: multiplier = 40; break;
+ case 1: multiplier = 20; break;
+ case 2: multiplier = 15; break;
+ case 3: multiplier = 10; break;
+ case 4: multiplier = 5; break;
+ }
+ slotMoney += bet * multiplier;
+ slotMessage = "WIN!";
+ slotMessageTimer = 20;
+ tone(800, 300);
+ } else if (slotReels[0] === slotReels[1] || slotReels[1] === slotReels[2]) {
+ slotMoney += bet * 1;
+ slotMessage = "Pair!";
+ slotMessageTimer = 15;
+ tone(600, 200);
+ }
+ if (slotMoney <= 0) slotState = SLOT_STATE_GAME_OVER;
+ slotStaticDrawn = false;
+ }
+}
+var BIRD_WIDTH = 16;
+var BIRD_HEIGHT = 12;
+var PIPE_WIDTH = 30;
+var PIPE_GAP = 60;
+var PIPE_SPEED = 1.5;
+var GRAVITY = 0.18;
+var FLAP_POWER = 3.2;
+var GROUND_HEIGHT = 15;
+var bird = { x: 60, y: HEIGHT / 2, width: BIRD_WIDTH, height: BIRD_HEIGHT, velocity: 0, lastY: 0 };
+var pipes = [];
+var flappyScore = 0;
+var flappyHighScore = 0;
+var pipeSpawnTimer = 0;
+var groundOffset = 0;
+var FLAPPY_STATE_MENU = 0;
+var FLAPPY_STATE_GAME = 1;
+var FLAPPY_STATE_PAUSED = 2;
+var flappyState = FLAPPY_STATE_MENU;
+var suppressGameOverSound = false;
+var flappyStaticDrawn = false;
+var flappyLastSelState = false;
+function resetFlappyBird() {
+ bird.y = HEIGHT / 2;
+ bird.velocity = 0;
+ bird.lastY = bird.y;
+ pipes = [];
+ flappyScore = 0;
+ pipeSpawnTimer = 0;
+ groundOffset = 0;
+ clouds = [];
+ initializeClouds();
+ spawnPipe();
+ flappyState = FLAPPY_STATE_MENU;
+ flappyStaticDrawn = false;
+ staticDrawn = false;
+}
+function spawnPipe() {
+ var gapY = Math.random() * (HEIGHT - PIPE_GAP - GROUND_HEIGHT - 40) + 20;
+ pipes.push({ x: WIDTH, y: gapY, width: PIPE_WIDTH, height: PIPE_GAP, passed: false });
+}
+function drawFlappyMenu() {
+ if (!flappyStaticDrawn) {
+ fillScreen(BLACK);
+ setTextSize(3);
+ setTextColor(YELLOW);
+ drawString("FLAPPY BIRD", 25, 30);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("PREV/NEXT: Pause", 72, 70);
+ drawString("M5: Flap", 96, 85);
+ setTextColor(GREEN);
+ drawString("Press M5 to Start", 72, 120);
+ flappyStaticDrawn = true;
+ }
+}
+var clouds = [];
+
+function initializeClouds() {
+ clouds = [];
+ for (var i = 0; i < 5; i++) {
+ clouds.push({
+ x: Math.random() * WIDTH,
+ y: Math.random() * 40 + 5,
+ width: Math.random() * 30 + 20,
+ height: Math.random() * 10 + 10,
+ speed: Math.random() * 0.3 + 0.1,
+ lastX: 0
+ });
+ }
+}
+
+function updateClouds() {
+ for (var i = 0; i < clouds.length; i++) {
+ var cloud = clouds[i];
+ cloud.x -= cloud.speed;
+ if (cloud.x + cloud.width < 0) {
+ cloud.x = WIDTH;
+ cloud.y = Math.random() * 40 + 5;
+ cloud.width = Math.random() * 30 + 20;
+ cloud.height = Math.random() * 10 + 10;
+ }
+ }
+}
+
+function drawClouds() {
+ for (var i = 0; i < clouds.length; i++) {
+ var cloud = clouds[i];
+ if (cloud.lastX > 0) {
+ drawFillRect(cloud.lastX, cloud.y, cloud.width, cloud.height, BLACK);
+ }
+ drawFillRect(cloud.x, cloud.y, cloud.width, cloud.height, GRAY);
+ cloud.lastX = cloud.x;
+ }
+}
+
+function drawFlappyBird() {
+ switch (flappyState) {
+ case FLAPPY_STATE_MENU:
+ drawFlappyMenu();
+ break;
+ case FLAPPY_STATE_GAME:
+ if (!flappyStaticDrawn) {
+ fillScreen(BLACK);
+ drawFillRect(0, HEIGHT - GROUND_HEIGHT, WIDTH, GROUND_HEIGHT, BROWN);
+ drawFillRect(0, HEIGHT - GROUND_HEIGHT, WIDTH, 3, GREEN);
+ if (clouds.length === 0) {
+ initializeClouds();
+ }
+ flappyStaticDrawn = true;
+ }
+
+ updateClouds();
+ drawClouds();
+
+ for (var i = 0; i < pipes.length; i++) {
+ var clearWidth = pipes[i].width + PIPE_SPEED + 2;
+ drawFillRect(pipes[i].x, 0, clearWidth, pipes[i].y, BLACK);
+ drawFillRect(pipes[i].x, pipes[i].y + pipes[i].height, clearWidth, HEIGHT - pipes[i].y - pipes[i].height - GROUND_HEIGHT, BLACK);
+ }
+
+ drawFillRect(WIDTH / 2 - 15, 20, 30, 24, BLACK);
+ setTextSize(2);
+ setTextColor(WHITE);
+ drawString("" + flappyScore, WIDTH / 2 - 5, 30);
+
+ for (var i = 0; i < pipes.length; i++) {
+ drawFillRect(pipes[i].x, 0, pipes[i].width, pipes[i].y, GREEN);
+ drawFillRect(pipes[i].x, pipes[i].y + pipes[i].height, pipes[i].width, HEIGHT - pipes[i].y - pipes[i].height - GROUND_HEIGHT, GREEN);
+ }
+
+ drawFillRect(0, HEIGHT - GROUND_HEIGHT + 5, WIDTH, 2, YELLOW);
+ drawFillRect(bird.x - bird.width / 2, bird.lastY - bird.height / 2, bird.width, bird.height, BLACK);
+ drawFillRect(bird.x - bird.width / 2, bird.y - bird.height / 2, bird.width, bird.height, YELLOW);
+ break;
+ case FLAPPY_STATE_PAUSED:
+ break;
+ }
+}
+function updateFlappyBird() {
+ if (flappyState !== FLAPPY_STATE_GAME) return;
+ bird.lastY = bird.y;
+ bird.velocity += GRAVITY;
+ bird.y += bird.velocity;
+ if (bird.y < 0) {
+ bird.y = 0;
+ bird.velocity = 0;
+ }
+ if (bird.y > HEIGHT - GROUND_HEIGHT - BIRD_HEIGHT / 2) {
+ prevGameState = STATE_FLAPPY_BIRD;
+ gameState = STATE_GAME_OVER;
+ if (!suppressGameOverSound) {
+ tone(400, 300); tone(300, 300);
+ }
+ suppressGameOverSound = false;
+ flappyState = FLAPPY_STATE_MENU;
+ staticDrawn = false;
+ if (flappyScore > flappyHighScore) flappyHighScore = flappyScore;
+ return;
+ }
+ pipeSpawnTimer++;
+ if (pipeSpawnTimer >= 100) {
+ spawnPipe();
+ pipeSpawnTimer = 0;
+ }
+ for (var i = 0; i < pipes.length; i++) {
+ pipes[i].x -= PIPE_SPEED;
+ if (pipes[i].x + pipes[i].width < 0) {
+ pipes.splice(i, 1);
+ i--;
+ continue;
+ }
+ if (!pipes[i].passed && pipes[i].x + pipes[i].width < bird.x - BIRD_WIDTH / 2) {
+ pipes[i].passed = true;
+ flappyScore++;
+ tone(800, 100);
+ }
+ if (checkCollision(bird.x - BIRD_WIDTH / 2, bird.y - BIRD_HEIGHT / 2, BIRD_WIDTH, BIRD_HEIGHT,
+ pipes[i].x, 0, pipes[i].width, pipes[i].y) ||
+ checkCollision(bird.x - BIRD_WIDTH / 2, bird.y - BIRD_HEIGHT / 2, BIRD_WIDTH, BIRD_HEIGHT,
+ pipes[i].x, pipes[i].y + pipes[i].height, pipes[i].width, HEIGHT - pipes[i].y - pipes[i].height - GROUND_HEIGHT)) {
+ prevGameState = STATE_FLAPPY_BIRD;
+ gameState = STATE_GAME_OVER;
+ if (!suppressGameOverSound) {
+ tone(400, 300);
+ }
+ suppressGameOverSound = false;
+ flappyState = FLAPPY_STATE_MENU;
+ staticDrawn = false;
+ if (flappyScore > flappyHighScore) flappyHighScore = flappyScore;
+ return;
+ }
+ }
+ groundOffset = (groundOffset + 1) % 20;
+}
+var blackjack = {
+ playerMoney: 1000,
+ currentBet: 0,
+ deck: [],
+ playerHand: [],
+ dealerHand: [],
+ playerBust: false,
+ dealerBust: false,
+ playerBlackjack: false,
+ dealerBlackjack: false,
+ state: 0,
+ resultMessage: ""
+};
+var BET_OPTIONS = [10, 25, 50, 100];
+var selectedBetIndex = 0;
+var lastBet = 10;
+var SUITS = ['H', 'D', 'S', 'C'];
+var VALUES = ['A', '2', '3', '4', '5', '6', '7', '8', '9', '10', 'J', 'Q', 'K'];
+function resetBlackjack() {
+ blackjack.playerMoney = 1000;
+ blackjack.currentBet = 0;
+ blackjack.deck = [];
+ blackjack.playerHand = [];
+ blackjack.dealerHand = [];
+ blackjack.playerBust = false;
+ blackjack.dealerBust = false;
+ blackjack.playerBlackjack = false;
+ blackjack.state = 1;
+ blackjack.resultMessage = "";
+ selectedBetIndex = 0;
+ staticDrawn = false;
+ drawBlackjack.firstDraw = true;
+ drawBlackjack.lastMoney = -1;
+ drawBlackjack.lastBetIndex = -1;
+ drawBlackjack.lastPauseSelection = -1;
+ drawBlackjack.lastState = -1;
+ if (typeof blackjack.prevState === 'undefined') {
+ blackjack.prevState = -1;
+ }
+}
+function createBlackjackDeck() {
+ blackjack.deck = [];
+ for (var i = 0; i < SUITS.length; i++) {
+ for (var j = 0; j < VALUES.length; j++) {
+ blackjack.deck.push({ suit: SUITS[i], value: VALUES[j] });
+ }
+ }
+ shuffleBlackjackDeck();
+}
+function shuffleBlackjackDeck() {
+ for (var i = blackjack.deck.length - 1; i > 0; i--) {
+ var j = Math.floor(Math.random() * (i + 1));
+ var temp = blackjack.deck[i];
+ blackjack.deck[i] = blackjack.deck[j];
+ blackjack.deck[j] = temp;
+ }
+}
+function drawBlackjackCard() {
+ if (typeof blackjack.deck === 'undefined' || blackjack.deck.length === 0) {
+ createBlackjackDeck();
+ }
+ return blackjack.deck.pop();
+}
+function calculateHandValue(hand) {
+ if (!hand || hand.length === 0) return 0;
+ var value = 0;
+ var aces = 0;
+ for (var i = 0; i < hand.length; i++) {
+ var card = hand[i];
+ if (card.value === 'A') {
+ aces++;
+ value += 11;
+ } else if (card.value === 'J' || card.value === 'Q' || card.value === 'K') {
+ value += 10;
+ } else {
+ value += parseInt(card.value);
+ }
+ }
+ while (value > 21 && aces > 0) {
+ value -= 10;
+ aces--;
+ }
+ return value;
+}
+function startBlackjackGame(betAmount) {
+ if (typeof blackjack.deck === 'undefined' || blackjack.deck.length === 0) {
+ createBlackjackDeck();
+ }
+ if (blackjack.playerMoney < betAmount) return false;
+ blackjack.currentBet = betAmount;
+ blackjack.playerMoney -= betAmount;
+ lastBet = betAmount;
+ blackjack.state = 2;
+ blackjack.playerHand = [drawBlackjackCard(), drawBlackjackCard()];
+ blackjack.dealerHand = [drawBlackjackCard(), drawBlackjackCard()];
+ blackjack.playerBlackjack = calculateHandValue(blackjack.playerHand) === 21;
+ blackjack.dealerBlackjack = calculateHandValue(blackjack.dealerHand) === 21;
+ if (blackjack.playerBlackjack || blackjack.dealerBlackjack) {
+ dealerTurn();
+ }
+ staticDrawn = false;
+ return true;
+}
+function hit() {
+ if (blackjack.state !== 2) return;
+ var card = drawBlackjackCard();
+ if (!card) {
+ createBlackjackDeck();
+ card = drawBlackjackCard();
+ }
+ if (card) {
+ blackjack.playerHand.push(card);
+ var handValue = calculateHandValue(blackjack.playerHand);
+ if (handValue > 21) {
+ blackjack.playerBust = true;
+ dealerTurn();
+ } else if (handValue === 21) {
+ dealerTurn();
+ }
+ staticDrawn = false;
+ }
+}
+function stand() {
+ if (blackjack.state !== 2) return;
+ if (!blackjack.dealerHand || blackjack.dealerHand.length === 0) {
+ blackjack.dealerHand = [];
+ }
+ dealerTurn();
+ staticDrawn = false;
+}
+function dealerTurn() {
+ if (blackjack.dealerBlackjack) {
+ determineWinner();
+ return;
+ }
+ if (!blackjack.playerBust && !blackjack.playerBlackjack) {
+ if (!blackjack.dealerHand) {
+ blackjack.dealerHand = [];
+ }
+ var dealerValue = calculateHandValue(blackjack.dealerHand);
+ while (dealerValue < 17) {
+ var card = drawBlackjackCard();
+ if (!card) {
+ createBlackjackDeck();
+ card = drawBlackjackCard();
+ }
+ if (card) {
+ blackjack.dealerHand.push(card);
+ dealerValue = calculateHandValue(blackjack.dealerHand);
+ staticDrawn = false;
+ delay(500);
+ } else {
+ break;
+ }
+ }
+ if (dealerValue > 21) blackjack.dealerBust = true;
+ }
+ determineWinner();
+}
+function determineWinner() {
+ blackjack.state = 3;
+ var playerValue = calculateHandValue(blackjack.playerHand);
+ var dealerValue = calculateHandValue(blackjack.dealerHand || []);
+
+ if (blackjack.playerBlackjack && blackjack.dealerBlackjack) {
+ blackjack.playerMoney += blackjack.currentBet;
+ blackjack.resultMessage = "Push (Blackjack vs. Blackjack)";
+ } else if (blackjack.playerBlackjack) {
+ blackjack.playerMoney += Math.floor(blackjack.currentBet * 2.5);
+ blackjack.resultMessage = "Blackjack! You Win!";
+ tone(800, 300);
+ } else if (blackjack.dealerBlackjack) {
+ blackjack.resultMessage = "Dealer Blackjack! You Lose!";
+ tone(500, 200);
+ } else if (blackjack.playerBust) {
+ blackjack.resultMessage = "Bust! You Lose!";
+ tone(500, 200);
+ } else if (blackjack.dealerBust || playerValue > dealerValue) {
+ blackjack.playerMoney += blackjack.currentBet * 2;
+ blackjack.resultMessage = "You Win!";
+ tone(800, 300);
+ } else if (playerValue === dealerValue) {
+ blackjack.playerMoney += blackjack.currentBet;
+ blackjack.resultMessage = "Push!";
+ } else {
+ blackjack.resultMessage = "You Lose!";
+ tone(500, 200);
+ }
+
+ if (!blackjack.resultMessage.includes(" ")) {
+ blackjack.resultMessage = blackjack.resultMessage + " Game";
+ }
+
+ staticDrawn = false;
+}
+function drawBlackjackPauseMenu() {
+ fillScreen(BLACK);
+ setTextSize(2);
+ setTextColor(GOLD);
+ drawString("PAUSED", 70, 20);
+ drawRect(90, 40, 60, 60, GRAY);
+ setTextSize(1);
+ var options = ["Resume", "Main Menu", "Quit"];
+ for (var i = 0; i < options.length; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(95, 48 + i * 15, 50, 12, GRAY);
+ drawString("> " + options[i], 100, 55 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 100, 55 + i * 15);
+ }
+ }
+}
+function drawBlackjack() {
+ if (!staticDrawn || blackjack.state !== drawBlackjack.lastState ||
+ (blackjack.state === 2 && blackjack.playerHand.length !== drawBlackjack.lastPlayerHandLength)) {
+ fillScreen(BLACK);
+ staticDrawn = true;
+
+ if (blackjack.state === 0) {
+ setTextSize(2);
+ setTextColor(GOLD);
+ drawString("BLACKJACK", 70, 20);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Money: " + blackjack.playerMoney, 70, 50);
+ drawString("M5: Play", 70, 70);
+ } else if (blackjack.state === 1) {
+ setTextSize(2);
+ setTextColor(GOLD);
+ drawString("PLACE YOUR BET", 50, 20);
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Money: " + blackjack.playerMoney, 10, 50);
+ for (var i = 0; i < BET_OPTIONS.length; i++) {
+ if (i === selectedBetIndex) {
+ setTextColor(YELLOW);
+ drawString("> " + BET_OPTIONS[i], 100, 50 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + BET_OPTIONS[i], 100, 50 + i * 15);
+ }
+ }
+ setTextColor(WHITE);
+ drawString("PREV/NEXT: Change", 10, 115);
+ drawString("M5: Confirm", 150, 115);
+ } else if (blackjack.state === 2) {
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Money: " + blackjack.playerMoney, 5, 10);
+ drawString("Bet: " + blackjack.currentBet, 190, 10);
+ drawString("Dealer's Hand:", 5, 25);
+ for (var i = 0; i < blackjack.dealerHand.length; i++) {
+ if (i === 1 && !blackjack.playerBust && blackjack.dealerHand.length === 2 &&
+ calculateHandValue(blackjack.playerHand) !== 21) {
+ drawFillRect(5 + i * 30, 40, 20, 30, GRAY);
+ } else if (blackjack.dealerHand[i]) {
+ drawFillRect(5 + i * 30, 40, 20, 30, WHITE);
+ setTextColor(blackjack.dealerHand[i].suit === 'H' ||
+ blackjack.dealerHand[i].suit === 'D' ? GRAY : BLACK);
+ drawString(blackjack.dealerHand[i].value + blackjack.dealerHand[i].suit, 7 + i * 30, 55);
+ }
+ }
+ setTextColor(YELLOW);
+ drawString("Your Hand: " + calculateHandValue(blackjack.playerHand), 5, 75);
+ setTextColor(WHITE);
+ for (var i = 0; i < blackjack.playerHand.length; i++) {
+ if (blackjack.playerHand[i]) {
+ drawFillRect(5 + i * 30, 90, 20, 30, WHITE);
+ setTextColor(blackjack.playerHand[i].suit === 'H' ||
+ blackjack.playerHand[i].suit === 'D' ? GRAY : BLACK);
+ drawString(blackjack.playerHand[i].value + blackjack.playerHand[i].suit, 7 + i * 30, 105);
+ }
+ }
+ if (!blackjack.playerBust && !blackjack.playerBlackjack) {
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("PREV: Menu", 15, 125);
+ drawString("M5: Stand", 95, 125);
+ drawString("NEXT: Hit", 170, 125);
+ }
+ } else if (blackjack.state === 3) {
+ setTextSize(1);
+ setTextColor(WHITE);
+ drawString("Money: " + blackjack.playerMoney, 5, 10);
+ drawString("Bet: " + blackjack.currentBet, 190, 10);
+ drawString("Dealer's Hand: " + calculateHandValue(blackjack.dealerHand), 5, 25);
+ for (var i = 0; i < blackjack.dealerHand.length; i++) {
+ if (blackjack.dealerHand[i]) {
+ drawFillRect(5 + i * 30, 40, 20, 30, WHITE);
+ setTextColor(blackjack.dealerHand[i].suit === 'H' ||
+ blackjack.dealerHand[i].suit === 'D' ? GRAY : BLACK);
+ drawString(blackjack.dealerHand[i].value + blackjack.dealerHand[i].suit, 7 + i * 30, 55);
+ }
+ }
+ setTextColor(YELLOW);
+ drawString("Your Hand: " + calculateHandValue(blackjack.playerHand), 5, 75);
+ setTextColor(WHITE);
+ for (var i = 0; i < blackjack.playerHand.length; i++) {
+ if (blackjack.playerHand[i]) {
+ drawFillRect(5 + i * 30, 90, 20, 30, WHITE);
+ setTextColor(blackjack.playerHand[i].suit === 'H' ||
+ blackjack.playerHand[i].suit === 'D' ? GRAY : BLACK);
+ drawString(blackjack.playerHand[i].value + blackjack.playerHand[i].suit, 7 + i * 30, 105);
+ }
+ }
+
+ setTextSize(2);
+ setTextColor(blackjack.resultMessage.includes("Win") ? GRAY :
+ blackjack.resultMessage.includes("Lose") ? YELLOW : WHITE);
+
+ try {
+ var lines = blackjack.resultMessage.split(" ");
+ if (lines.length >= 2) {
+ drawString(lines[0], 180 - (lines[0].length * 10) / 2, 60);
+ drawString(lines[1], 180 - (lines[1].length * 10) / 2, 80);
+ } else {
+ drawString(blackjack.resultMessage, 180 - (blackjack.resultMessage.length * 10) / 2, 70);
+ }
+ } catch (e) {
+ drawString("Game Over", 70, 70);
+ }
+
+ setTextSize(1);
+ setTextColor(WHITE);
+ var nextHandText = "M5: Next Hand";
+ var nextHandX = 120 - (nextHandText.length * 5) / 2;
+ drawString(nextHandText, nextHandX, 125);
+ } else if (blackjack.state === 4) {
+ fillScreen(BLACK);
+ drawRect(60, 30, 120, 70, GRAY);
+ setTextSize(1);
+ var options = ["Resume", "Main Menu", "Quit"];
+ for (var i = 0; i < options.length; i++) {
+ if (i === pauseMenuSelection) {
+ setTextColor(YELLOW);
+ drawFillRect(95, 47 + i * 15, 50, 10, GRAY);
+ drawString("> " + options[i], 100, 50 + i * 15);
+ } else {
+ setTextColor(WHITE);
+ drawString(" " + options[i], 100, 50 + i * 15);
+ }
+ }
+ }
+ drawBlackjack.lastState = blackjack.state;
+ drawBlackjack.lastPlayerHandLength = blackjack.playerHand.length;
+ }
+}
+drawBlackjack.lastState = -1;
+drawBlackjack.lastPlayerHandLength = 0;
+
+function updateBlackjack() {
+ if (gameState !== STATE_BLACKJACK) return;
+ if (blackjack.state === 2 && (blackjack.playerBust || blackjack.playerBlackjack || blackjack.dealerBlackjack)) {
+ dealerTurn();
+ }
+}
+function handleInput() {
+ var currentSelState = getSelPress();
+ var prevPressed = getPrevPress();
+ var nextPressed = getNextPress();
+ var shouldExit = false;
+ switch (gameState) {
+ case STATE_MAIN_MENU:
+ if (prevPressed) {
+ menuSelection = (menuSelection - 1 + menuOptions.length) % menuOptions.length;
+ mainMenuStaticDrawn = false;
+ }
+ if (nextPressed) {
+ menuSelection = (menuSelection + 1) % menuOptions.length;
+ mainMenuStaticDrawn = false;
+ }
+ if (currentSelState && !menuLastSelState) {
+ if (menuSelection === 0) gameState = STATE_BREAKOUT;
+ else if (menuSelection === 1) gameState = STATE_SNAKE;
+ else if (menuSelection === 2) gameState = STATE_SPACE_SHOOTER;
+ else if (menuSelection === 3) { resetSlots(); gameState = STATE_SLOTS; }
+ else if (menuSelection === 4) { resetFlappyBird(); gameState = STATE_FLAPPY_BIRD; }
+ else if (menuSelection === 5) { resetBlackjack(); gameState = STATE_BLACKJACK; }
+ else if (menuSelection === 6) { gameState = STATE_EXIT_CONFIRM; exitConfirmSelection = 1; }
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ }
+ menuLastSelState = currentSelState;
+ break;
+ case STATE_BREAKOUT:
+ if (currentSelState && !breakoutLastSelState) {
+ breakoutSelPressCount++;
+ if (breakoutSelPressCount === 1) breakoutSelPressWindowStart = Date.now();
+ if (Date.now() - breakoutSelPressWindowStart <= breakoutSelPressWindow) {
+ if (breakoutSelPressCount >= breakoutSelPressThreshold) {
+ breakoutIsPaused = true;
+ gameState = STATE_PAUSED;
+ prevGameState = STATE_BREAKOUT;
+ breakoutSelPressCount = 0;
+ breakoutSelPressWindowStart = -1;
+ breakoutPauseCooldown = 30;
+ pauseStaticDrawn = false;
+ }
+ } else {
+ breakoutSelPressCount = 1;
+ breakoutSelPressWindowStart = Date.now();
+ }
+ if (breakoutState === BREAKOUT_STATE_START) {
+ resetBreakout();
+ } else if (breakoutState === BREAKOUT_STATE_PLAYING && ball.stuck) {
+ ball.stuck = false;
+ resetBall();
+ } else if (breakoutState === BREAKOUT_STATE_GAME_OVER || breakoutState === BREAKOUT_STATE_WIN) {
+ resetBreakout();
+ } else if (breakoutState === BREAKOUT_STATE_NEXT_LEVEL) {
+ resetBreakout();
+ }
+ } else if (!currentSelState && breakoutLastSelState) {
+ breakoutLastSelState = false;
+ }
+ if (prevPressed && (breakoutState === BREAKOUT_STATE_GAME_OVER || breakoutState === BREAKOUT_STATE_WIN)) {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ }
+ if (breakoutState === BREAKOUT_STATE_PLAYING && !breakoutIsPaused) {
+ if (prevPressed) paddle.x -= paddle.speed;
+ if (nextPressed) paddle.x += paddle.speed;
+ if (paddle.x < 0) paddle.x = 0;
+ if (paddle.x + paddle.width > WIDTH) paddle.x = WIDTH - paddle.width;
+ }
+ breakoutLastSelState = currentSelState;
+ break;
+ case STATE_SNAKE:
+ if (currentSelState && !snakeLastSelState) {
+ if (snakeState === SNAKE_STATE_MENU) resetSnake();
+ else if (snakeState === SNAKE_STATE_GAME) {
+ snakeState = SNAKE_STATE_PAUSED;
+ gameState = STATE_PAUSED;
+ prevGameState = STATE_SNAKE;
+ pauseStaticDrawn = false;
+ }
+ }
+ if (prevPressed && snakeState === SNAKE_STATE_GAME) {
+ switch (direction) {
+ case 0: nextDirection = 2; break;
+ case 1: nextDirection = 3; break;
+ case 2: nextDirection = 1; break;
+ case 3: nextDirection = 0; break;
+ }
+ }
+ if (nextPressed && snakeState === SNAKE_STATE_GAME) {
+ switch (direction) {
+ case 0: nextDirection = 3; break;
+ case 1: nextDirection = 2; break;
+ case 2: nextDirection = 0; break;
+ case 3: nextDirection = 1; break;
+ }
+ }
+ snakeLastSelState = currentSelState;
+ break;
+ case STATE_SPACE_SHOOTER:
+ if (currentSelState && !spaceLastSelState) {
+ if (spaceState === SPACE_STATE_MENU) resetSpaceShooter();
+ else if (spaceState === SPACE_STATE_GAME) {
+ spaceIsPaused = true;
+ gameState = STATE_PAUSED;
+ prevGameState = STATE_SPACE_SHOOTER;
+ pauseStaticDrawn = false;
+ }
+ }
+ if (prevPressed) player.x -= player.speed;
+ if (nextPressed) player.x += player.speed;
+ if (player.x < player.width / 2) player.x = player.width / 2;
+ if (player.x > WIDTH - player.width / 2) player.x = WIDTH - player.width / 2;
+ spaceLastSelState = currentSelState;
+ break;
+ case STATE_SLOTS:
+ if (currentSelState && !slotLastSelState) {
+ if (slotState === SLOT_STATE_MENU) resetSlots();
+ else if (slotState === SLOT_STATE_SPIN) updateSlots(true);
+ else if (slotState === SLOT_STATE_GAME_OVER) resetSlots();
+ else if (slotState === SLOT_STATE_PAUSED) {
+ if (pauseMenuSelection === 0) {
+ slotState = SLOT_STATE_SPIN;
+ slotStaticDrawn = false;
+ } else if (pauseMenuSelection === 1) {
+ gameState = STATE_MAIN_MENU;
+ resetSlots();
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ } else if (pauseMenuSelection === 2) {
+ gameState = STATE_EXIT_CONFIRM;
+ prevGameState = STATE_SLOTS;
+ exitConfirmSelection = 1;
+ staticDrawn = false;
+ }
+ }
+ }
+ if (slotState === SLOT_STATE_SPIN) {
+ if (prevPressed) {
+ slotState = SLOT_STATE_PAUSED;
+ pauseMenuSelection = 0;
+ slotStaticDrawn = false;
+ }
+ if (nextPressed) {
+ slotBetIndex = (slotBetIndex + 1) % slotBetOptions.length;
+ slotStaticDrawn = false;
+ }
+ } else if (slotState === SLOT_STATE_PAUSED) {
+ if (prevPressed) {
+ pauseMenuSelection = (pauseMenuSelection - 1 + 3) % 3;
+ slotStaticDrawn = false;
+ }
+ if (nextPressed) {
+ pauseMenuSelection = (pauseMenuSelection + 1) % 3;
+ slotStaticDrawn = false;
+ }
+ } else if (slotState === SLOT_STATE_GAME_OVER) {
+ if (prevPressed) {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ slotLastSelState = false;
+ }
+ }
+ slotLastSelState = currentSelState;
+ break;
+ case STATE_FLAPPY_BIRD:
+ if (currentSelState && !flappyLastSelState) {
+ if (flappyState === FLAPPY_STATE_MENU) {
+ flappyState = FLAPPY_STATE_GAME;
+ flappyStaticDrawn = false;
+ } else if (flappyState === FLAPPY_STATE_GAME) {
+ bird.velocity = -FLAP_POWER;
+ tone(600, 150);
+ }
+ }
+ if (prevPressed && flappyState === FLAPPY_STATE_GAME) {
+ flappyState = FLAPPY_STATE_PAUSED;
+ gameState = STATE_PAUSED;
+ prevGameState = STATE_FLAPPY_BIRD;
+ pauseStaticDrawn = false;
+ }
+ if (nextPressed && flappyState === FLAPPY_STATE_GAME) {
+ flappyState = FLAPPY_STATE_PAUSED;
+ gameState = STATE_PAUSED;
+ prevGameState = STATE_FLAPPY_BIRD;
+ pauseStaticDrawn = false;
+ }
+ flappyLastSelState = currentSelState;
+ break;
+ case STATE_BLACKJACK:
+ var betMenuSelection = selectedBetIndex;
+ if (typeof blackjack.prevState === 'undefined') {
+ blackjack.prevState = -1;
+ }
+ if (currentSelState && !menuLastSelState) {
+ if (blackjack.state === 1) {
+ startBlackjackGame(BET_OPTIONS[betMenuSelection]);
+ } else if (blackjack.state === 2) {
+ stand();
+ } else if (blackjack.state === 3) {
+ if (blackjack.playerMoney <= 0) {
+ prevGameState = STATE_BLACKJACK;
+ gameState = STATE_GAME_OVER;
+ staticDrawn = false;
+ } else {
+ blackjack.dealerHand = [];
+ blackjack.playerHand = [];
+ blackjack.currentBet = 0;
+ blackjack.playerBust = false;
+ blackjack.dealerBust = false;
+ blackjack.playerBlackjack = false;
+ blackjack.state = 1;
+ betMenuSelection = 0;
+ staticDrawn = false;
+ }
+ } else if (blackjack.state === 4) {
+ if (pauseMenuSelection === 0) {
+ blackjack.state = blackjack.prevState;
+ staticDrawn = false;
+ } else if (pauseMenuSelection === 1) {
+ gameState = STATE_MAIN_MENU;
+ resetBlackjack();
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ } else if (pauseMenuSelection === 2) {
+ gameState = STATE_EXIT_CONFIRM;
+ prevGameState = STATE_BLACKJACK;
+ exitConfirmSelection = 1;
+ staticDrawn = false;
+ }
+ }
+ }
+ if (nextPressed && blackjack.state === 1) {
+ betMenuSelection = (betMenuSelection + 1) % BET_OPTIONS.length;
+ staticDrawn = false;
+ }
+ if (prevPressed && blackjack.state === 1) {
+ betMenuSelection = (betMenuSelection - 1 + BET_OPTIONS.length) % BET_OPTIONS.length;
+ staticDrawn = false;
+ }
+ if (prevPressed && blackjack.state === 2) {
+ blackjack.prevState = blackjack.state;
+ blackjack.state = 4;
+ pauseMenuSelection = 0;
+ staticDrawn = false;
+ }
+ if (blackjack.state === 4) {
+ if (prevPressed) {
+ pauseMenuSelection = (pauseMenuSelection - 1 + 3) % 3;
+ staticDrawn = false;
+ }
+ if (nextPressed) {
+ pauseMenuSelection = (pauseMenuSelection + 1) % 3;
+ staticDrawn = false;
+ }
+ }
+ if (nextPressed && blackjack.state === 2 && !blackjack.playerBust && !blackjack.playerBlackjack) {
+ hit();
+ }
+ selectedBetIndex = betMenuSelection;
+ menuLastSelState = currentSelState;
+ break;
+ case STATE_PAUSED:
+ if (prevPressed) {
+ pauseMenuSelection = (pauseMenuSelection - 1 + 3) % 3;
+ pauseStaticDrawn = false;
+ }
+ if (nextPressed) {
+ pauseMenuSelection = (pauseMenuSelection + 1) % 3;
+ pauseStaticDrawn = false;
+ }
+ if (currentSelState && !menuLastSelState && (prevGameState !== STATE_BREAKOUT || breakoutPauseCooldown <= 0)) {
+ if (pauseMenuSelection === 0) {
+ gameState = prevGameState;
+ if (prevGameState === STATE_BREAKOUT) {
+ breakoutIsPaused = false;
+ breakoutStaticDrawn = false;
+ breakoutLastStaticDrawnState = -1;
+ }
+ if (prevGameState === STATE_SPACE_SHOOTER) {
+ spaceIsPaused = false;
+ spaceStaticDrawn = false;
+ spaceLastStaticDrawnState = -1;
+ }
+ if (prevGameState === STATE_SNAKE) {
+ snakeState = SNAKE_STATE_GAME;
+ snakeStaticDrawn = false;
+ snakeLastStaticDrawnState = -1;
+ }
+ if (prevGameState === STATE_FLAPPY_BIRD) {
+ flappyState = FLAPPY_STATE_GAME;
+ flappyStaticDrawn = false;
+ }
+ staticDrawn = false;
+ pauseStaticDrawn = false;
+ } else if (pauseMenuSelection === 1) {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ flappyLastSelState = false;
+ snakeState = SNAKE_STATE_MENU;
+ if (prevGameState === STATE_SPACE_SHOOTER) resetSpaceShooter();
+ if (prevGameState === STATE_FLAPPY_BIRD) flappyState = FLAPPY_STATE_MENU;
+ prevGameState = -1;
+ } else if (pauseMenuSelection === 2) {
+ gameState = STATE_EXIT_CONFIRM;
+ exitConfirmSelection = 1;
+ staticDrawn = false;
+ }
+ }
+ menuLastSelState = currentSelState;
+ if (prevGameState === STATE_BREAKOUT && breakoutPauseCooldown > 0) breakoutPauseCooldown--;
+ break;
+ case STATE_GAME_OVER:
+ if (prevPressed) {
+ pauseMenuSelection = (pauseMenuSelection - 1 + 3) % 3;
+ staticDrawn = false;
+ }
+ if (nextPressed) {
+ pauseMenuSelection = (pauseMenuSelection + 1) % 3;
+ staticDrawn = false;
+ }
+ if (currentSelState && !menuLastSelState) {
+ if (pauseMenuSelection === 0) {
+ if (prevGameState === STATE_FLAPPY_BIRD) resetFlappyBird();
+ else if (prevGameState === STATE_SNAKE) resetSnake();
+ else if (prevGameState === STATE_SPACE_SHOOTER) resetSpaceShooter();
+ else if (prevGameState === STATE_BREAKOUT) resetBreakout();
+ else if (prevGameState === STATE_BLACKJACK) resetBlackjack();
+ gameState = prevGameState;
+ staticDrawn = false;
+ } else if (pauseMenuSelection === 1) {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ snakeState = SNAKE_STATE_MENU;
+ if (prevGameState === STATE_SPACE_SHOOTER) resetSpaceShooter();
+ prevGameState = -1;
+ } else if (pauseMenuSelection === 2) {
+ gameState = STATE_EXIT_CONFIRM;
+ exitConfirmSelection = 1;
+ staticDrawn = false;
+ }
+ }
+ menuLastSelState = currentSelState;
+ if (gameState === STATE_MAIN_MENU) menuLastSelState = false;
+ break;
+ case STATE_LEVEL_UP:
+ if (prevPressed) {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ }
+ if (currentSelState && !menuLastSelState) {
+ if (prevGameState === STATE_SPACE_SHOOTER) {
+ spaceState = SPACE_STATE_GAME;
+ gameState = prevGameState;
+ spaceStaticDrawn = false;
+ } else if (prevGameState === STATE_BREAKOUT) {
+ resetBreakout();
+ gameState = prevGameState;
+ breakoutStaticDrawn = false;
+ }
+ }
+ menuLastSelState = currentSelState;
+ break;
+ case STATE_EXIT_CONFIRM:
+ if (prevPressed) {
+ exitConfirmSelection = (exitConfirmSelection - 1 + 2) % 2;
+ staticDrawn = false;
+ }
+ if (nextPressed) {
+ exitConfirmSelection = (exitConfirmSelection + 1) % 2;
+ staticDrawn = false;
+ }
+ if (currentSelState && !menuLastSelState) {
+ if (exitConfirmSelection === 0) {
+ shouldExit = true;
+ } else {
+ gameState = STATE_MAIN_MENU;
+ staticDrawn = false;
+ mainMenuStaticDrawn = false;
+ menuLastSelState = false;
+ breakoutLastSelState = false;
+ snakeLastSelState = false;
+ spaceLastSelState = false;
+ slotLastSelState = false;
+ flappyLastSelState = false;
+ }
+ }
+ menuLastSelState = currentSelState;
+ break;
+ }
+ return shouldExit;
+}
+function main() {
+ gameState = STATE_MAIN_MENU;
+ createStars();
+ while (true) {
+ var startTime = Date.now();
+ if (handleInput()) break;
+ switch (gameState) {
+ case STATE_MAIN_MENU: drawMainMenu(); break;
+ case STATE_BREAKOUT: updateBreakout(); drawBreakout(); break;
+ case STATE_SNAKE: updateSnake(); drawSnake(); break;
+ case STATE_SPACE_SHOOTER: updateSpaceShooter(); drawSpaceShooter(); break;
+ case STATE_SLOTS: updateSlots(false); drawSlots(); break;
+ case STATE_FLAPPY_BIRD: updateFlappyBird(); drawFlappyBird(); break;
+ case STATE_BLACKJACK: updateBlackjack(); drawBlackjack(); break;
+ case STATE_PAUSED: drawPauseMenu(); break;
+ case STATE_GAME_OVER: drawGameOverMenu(); break;
+ case STATE_LEVEL_UP: drawLevelUpMenu(); break;
+ case STATE_EXIT_CONFIRM: drawExitConfirm(); break;
+ }
+ frameCounter++;
+ var frameTime = Date.now() - startTime;
+ delay(Math.max(1, 33 - frameTime));
+ }
+}
+main()
diff --git a/examples/M5Stick JS bruce/M5stickcplus2_calculator.js b/examples/M5Stick JS bruce/M5stickcplus2_calculator.js
new file mode 100644
index 0000000..8eaf8f9
--- /dev/null
+++ b/examples/M5Stick JS bruce/M5stickcplus2_calculator.js
@@ -0,0 +1,398 @@
+var displayApi = require('display');
+var keyboardApi = require('keyboard');
+
+var width = displayApi.width;
+var height = displayApi.height;
+var color = displayApi.color;
+var drawFillRect = displayApi.drawFillRect;
+var drawRect = displayApi.drawRect;
+var drawString = displayApi.drawString;
+var setTextColor = displayApi.setTextColor;
+var setTextSize = displayApi.setTextSize;
+
+var getPrevPress = keyboardApi.getPrevPress;
+var getNextPress = keyboardApi.getNextPress;
+var getSelPress = keyboardApi.getSelPress;
+
+var screenWidth = width();
+var screenHeight = height();
+
+var COLOR_BLACK = color(0, 0, 0);
+var COLOR_WHITE = color(255, 255, 255);
+var COLOR_GRAY = color(128, 128, 128);
+var COLOR_BLUE = color(0, 100, 200);
+var COLOR_GREEN = color(0, 150, 0);
+var COLOR_RED = color(200, 0, 0);
+var COLOR_ORANGE = color(255, 165, 0);
+
+var display = "0";
+var currentNumber = "";
+var previousNumber = "";
+var operator = "";
+var waitingForNewNumber = false;
+
+var keyboard = [
+ ["C", "DEL", "+/-", "*"],
+ ["7", "8", "9", "/"],
+ ["4", "5", "6", "-"],
+ ["1", "2", "3", "+"],
+ ["0", ".", "=", "EXIT"]
+];
+
+var selectedRow = 0;
+var selectedCol = 0;
+
+var KEY_WIDTH = 30;
+var KEY_HEIGHT = 16;
+var KEY_SPACING = 3;
+var KEYBOARD_START_Y = 45;
+var KEYBOARD_START_X = 10;
+
+var redraw = true;
+var shouldExit = false;
+
+function exit() {
+ shouldExit = true;
+}
+
+function setupScreen() {
+ drawFillRect(0, 0, screenWidth, screenHeight, COLOR_BLACK);
+ drawTitle();
+ drawDisplay();
+ drawKeyboard();
+}
+
+function drawTitle() {
+ setTextColor(COLOR_WHITE);
+ setTextSize(1);
+ drawString("Calculator", 55, 5);
+}
+
+function drawDisplay() {
+ var displayWidth = screenWidth - 20;
+ var displayX = 10;
+ drawFillRect(displayX, 18, displayWidth, 25, COLOR_WHITE);
+ drawRect(displayX, 18, displayWidth, 25, COLOR_GRAY);
+
+ setTextColor(COLOR_BLACK);
+ setTextSize(1);
+
+ var displayText = display;
+ if (displayText.length > 18) {
+ displayText = displayText.substring(displayText.length - 18);
+ }
+
+ var textWidth = displayText.length * 6;
+ var textX = displayX + displayWidth - textWidth - 5;
+ if (textX < displayX + 5) textX = displayX + 5;
+
+ drawString(displayText, textX, 28);
+}
+
+function drawKeyboard() {
+ for (var row = 0; row < keyboard.length; row++) {
+ for (var col = 0; col < keyboard[row].length; col++) {
+ var key = keyboard[row][col];
+ if (key === "") continue;
+
+ var x = KEYBOARD_START_X + col * (KEY_WIDTH + KEY_SPACING);
+ var y = KEYBOARD_START_Y + row * (KEY_HEIGHT + KEY_SPACING);
+
+ var keyColor = COLOR_WHITE;
+ var textColor = COLOR_BLACK;
+
+ if (row === selectedRow && col === selectedCol) {
+ keyColor = COLOR_BLUE;
+ textColor = COLOR_WHITE;
+ }
+
+ if (key === "C") {
+ keyColor = (row === selectedRow && col === selectedCol) ? COLOR_RED : color(255, 200, 200);
+ } else if (key === "=") {
+ keyColor = (row === selectedRow && col === selectedCol) ? COLOR_GREEN : color(200, 255, 200);
+ } else if (key === "EXIT") {
+ keyColor = (row === selectedRow && col === selectedCol) ? COLOR_RED : color(255, 150, 150);
+ } else if ("+-*/".indexOf(key) >= 0 || key === "+/-") {
+ keyColor = (row === selectedRow && col === selectedCol) ? COLOR_ORANGE : color(255, 230, 200);
+ }
+
+ drawFillRect(x, y, KEY_WIDTH, KEY_HEIGHT, keyColor);
+ drawRect(x, y, KEY_WIDTH, KEY_HEIGHT, COLOR_GRAY);
+
+ setTextColor(textColor);
+ setTextSize(1);
+
+ var textX = x + KEY_WIDTH / 2 - 3;
+ var textY = y + KEY_HEIGHT / 2 - 3;
+
+ if (key === "EXIT") {
+ textX = x + 3;
+ textY = y + KEY_HEIGHT / 2 - 3;
+ }
+
+ drawString(key, textX, textY);
+ }
+ }
+}
+
+function moveSelection(direction) {
+ if (direction === "up") {
+ if (selectedRow > 0) {
+ selectedRow--;
+ } else {
+ selectedRow = keyboard.length - 1;
+ }
+ } else if (direction === "down") {
+ if (selectedRow < keyboard.length - 1) {
+ selectedRow++;
+ } else {
+ selectedRow = 0;
+ }
+ } else if (direction === "left") {
+ if (selectedCol > 0) {
+ selectedCol--;
+ } else {
+ selectedCol = keyboard[selectedRow].length - 1;
+ }
+ } else if (direction === "right") {
+ if (selectedCol < keyboard[selectedRow].length - 1) {
+ selectedCol++;
+ } else {
+ selectedCol = 0;
+ }
+ }
+
+ while (keyboard[selectedRow][selectedCol] === "") {
+ if (direction === "right" || direction === "down") {
+ selectedCol++;
+ if (selectedCol >= keyboard[selectedRow].length) {
+ selectedCol = 0;
+ selectedRow++;
+ if (selectedRow >= keyboard.length) {
+ selectedRow = 0;
+ }
+ }
+ } else {
+ selectedCol--;
+ if (selectedCol < 0) {
+ selectedRow--;
+ if (selectedRow < 0) {
+ selectedRow = keyboard.length - 1;
+ }
+ selectedCol = keyboard[selectedRow].length - 1;
+ }
+ }
+ }
+}
+
+function pressKey() {
+ var key = keyboard[selectedRow][selectedCol];
+ if (key === "") return;
+
+ if (key === "C") {
+ clear();
+ } else if (key === "DEL") {
+ backspace();
+ } else if (key === "=") {
+ calculate();
+ } else if (key === "+/-") {
+ toggleSign();
+ } else if (key === "EXIT") {
+ exit();
+ } else if ("+-*/".indexOf(key) >= 0) {
+ setOperator(key);
+ } else if (key === ".") {
+ addDecimal();
+ } else {
+ addNumber(key);
+ }
+
+ redraw = true;
+}
+
+function toggleSign() {
+ if (currentNumber !== "" && currentNumber !== "0") {
+ if (currentNumber.indexOf("-") === 0) {
+ currentNumber = currentNumber.substring(1);
+ } else {
+ currentNumber = "-" + currentNumber;
+ }
+ display = currentNumber;
+ }
+}
+
+function addNumber(num) {
+ if (waitingForNewNumber) {
+ currentNumber = num;
+ waitingForNewNumber = false;
+ } else {
+ if (currentNumber === "0") {
+ currentNumber = num;
+ } else {
+ currentNumber += num;
+ }
+ }
+ display = currentNumber;
+}
+
+function addDecimal() {
+ if (waitingForNewNumber) {
+ currentNumber = "0.";
+ waitingForNewNumber = false;
+ } else {
+ if (currentNumber.indexOf(".") === -1) {
+ currentNumber += ".";
+ }
+ }
+ display = currentNumber;
+}
+
+function setOperator(op) {
+ if (operator !== "" && !waitingForNewNumber) {
+ calculate();
+ }
+
+ previousNumber = currentNumber;
+ operator = op;
+ waitingForNewNumber = true;
+ display = currentNumber;
+}
+
+function calculate() {
+ if (operator === "" || waitingForNewNumber) return;
+
+ var prev = parseFloat(previousNumber);
+ var curr = parseFloat(currentNumber);
+ var result = 0;
+
+ try {
+ switch (operator) {
+ case "+":
+ result = prev + curr;
+ break;
+ case "-":
+ result = prev - curr;
+ break;
+ case "*":
+ result = prev * curr;
+ break;
+ case "/":
+ if (curr === 0) {
+ display = "Error: Div by 0";
+ clear();
+ return;
+ }
+ result = prev / curr;
+ break;
+ }
+
+ if (result.toString().length > 12) {
+ if (Math.abs(result) >= 1000000 || Math.abs(result) < 0.001) {
+ result = result.toExponential(3);
+ } else {
+ result = result.toPrecision(6);
+ }
+ }
+
+ currentNumber = result.toString();
+ display = currentNumber;
+ operator = "";
+ waitingForNewNumber = true;
+
+ } catch (e) {
+ display = "Error";
+ clear();
+ }
+}
+
+function clear() {
+ display = "0";
+ currentNumber = "";
+ previousNumber = "";
+ operator = "";
+ waitingForNewNumber = false;
+}
+
+function backspace() {
+ if (currentNumber.length > 1) {
+ currentNumber = currentNumber.slice(0, -1);
+ display = currentNumber;
+ } else {
+ currentNumber = "0";
+ display = "0";
+ }
+}
+
+function updateDisplay() {
+ var displayWidth = screenWidth - 20;
+ var displayX = 10;
+ drawFillRect(displayX, 18, displayWidth, 25, COLOR_WHITE);
+ drawRect(displayX, 18, displayWidth, 25, COLOR_GRAY);
+
+ setTextColor(COLOR_BLACK);
+ setTextSize(1);
+
+ var displayText = display;
+ if (displayText.length > 18) {
+ displayText = displayText.substring(displayText.length - 18);
+ }
+
+ var textWidth = displayText.length * 6;
+ var textX = displayX + displayWidth - textWidth - 5;
+ if (textX < displayX + 5) textX = displayX + 5;
+
+ drawString(displayText, textX, 28);
+
+ drawKeyboard();
+}
+
+function showHelp() {
+ drawFillRect(0, 0, screenWidth, screenHeight, COLOR_BLACK);
+ setTextColor(COLOR_WHITE);
+ setTextSize(1);
+
+ drawString("Bruce Calculator", 40, 10);
+ drawString("Developed by MSI", 40, 25);
+
+ drawString("Prev: Down", 50, 50);
+ drawString("Next: Right", 48, 65);
+ drawString("Sel: Press", 48, 80);
+
+ drawString("Press any key to continue", 25, 120);
+
+ while (true) {
+ if (getPrevPress() || getNextPress() || getSelPress()) {
+ while (getPrevPress() || getNextPress() || getSelPress()) {
+ delay(50);
+ }
+ break;
+ }
+ delay(50);
+ }
+
+ setupScreen();
+}
+
+setTextSize(1);
+showHelp();
+
+while (!shouldExit) {
+ if (getPrevPress()) {
+ moveSelection("down");
+ redraw = true;
+ }
+ if (getNextPress()) {
+ moveSelection("right");
+ redraw = true;
+ }
+ if (getSelPress()) {
+ pressKey();
+ }
+
+ if (redraw) {
+ updateDisplay();
+ redraw = false;
+ }
+
+ delay(50);
+}
diff --git a/examples/M5Stick JS bruce/browser_bruce_M5.js b/examples/M5Stick JS bruce/browser_bruce_M5.js
new file mode 100644
index 0000000..4f05508
--- /dev/null
+++ b/examples/M5Stick JS bruce/browser_bruce_M5.js
@@ -0,0 +1,185 @@
+var dialog = require('dialog');
+var wifi = require('wifi');
+var display = require('display');
+var keyboard = require('keyboard');
+
+var tftWidth = display.width();
+var tftHeight = display.height();
+
+var request = { body: '' };
+
+function drawWindow(title) {
+ display.fill(0);
+ var boxColor = 0xFFFF;
+ if (typeof BRUCE_PRICOLOR !== 'undefined') {
+ boxColor = BRUCE_PRICOLOR;
+ }
+ display.drawRoundRect(5, 5, tftWidth - 10, tftHeight - 10, 5, boxColor);
+
+ display.setTextSize(2);
+ display.setTextAlign('center', 'top');
+
+ var safeTitle = title;
+ if (safeTitle.length > 15) safeTitle = safeTitle.substring(0, 15) + "..";
+ display.drawText(safeTitle, tftWidth / 2, 5);
+
+ display.setTextAlign('left', 'top');
+ display.setTextSize(1);
+ display.drawText('Loading...', 20, 35);
+}
+
+var textViewer = dialog.createTextViewer(request.body, {
+ fontSize: 1,
+ startX: 10,
+ startY: 25,
+ width: tftWidth - 20,
+ height: tftHeight - 35,
+ indentWrappedLines: true,
+});
+
+var history = [];
+
+// Egyszerű HTML tag eltávolító (hogy ne krix-kraxot láss)
+function stripHtml(html) {
+ var tmp = html;
+ // Cseréljük a
és
jeleket új sorra
+ tmp = tmp.replace(/
/gi, "\n");
+ tmp = tmp.replace(/<\/p>/gi, "\n\n");
+ // Minden más <...> taget törlünk
+ tmp = tmp.replace(/<[^>]*>/g, "");
+ // Üres sorok ritkítása
+ tmp = tmp.replace(/\n\s*\n/g, "\n");
+ return tmp;
+}
+
+function goToPage(url) {
+ var title = url;
+ if (title.indexOf('://') > -1) {
+ title = title.substring(title.indexOf('://') + 3);
+ }
+
+ drawWindow(title);
+ textViewer.clear();
+ textViewer.setText("Connecting direct...\n" + url);
+ textViewer.draw();
+
+ try {
+ // KÖZVETLEN LEKÉRÉS (Nincs W3C közvetítő)
+ console.log("Direct fetch: " + url);
+
+ var response = wifi.httpFetch(url, {
+ method: 'GET',
+ headers: { "User-Agent": "curl/7.64.1" } // A 'curl' álcázás segít a wttr.in-nek
+ });
+
+ if (response && response.body) {
+ // Ha ez az időjárás oldal, nem kell tisztítani
+ if (url.indexOf("wttr.in") !== -1) {
+ request = { body: response.body };
+ } else {
+ // Ha sima weboldal, leszedjük a HTML tageket
+ var cleanText = stripHtml(response.body);
+ request = { body: cleanText };
+ }
+ } else {
+ request = { body: 'Error: Empty response.' };
+ }
+
+ } catch (e) {
+ var err = String(e);
+ request = { body: 'Direct Error: ' + err + '\n(Try http instead of https)' };
+ }
+
+ history.push(url);
+ textViewer.setText(request.body);
+ textViewer.draw();
+}
+
+var websites = [
+ 'http://wttr.in/Budapest?format=3', // 1. EZT PRÓBÁLD! (Tiszta szöveg)
+ 'http://wttr.in/Budapest?0', // 2. Részletes időjárás
+ 'http://example.com', // 3. Egyszerű teszt oldal
+ 'http://info.cern.ch', // 4. Az első weboldal (nagyon egyszerű)
+];
+
+function selectWebsite() {
+ var websitesChoice = {};
+ for (var index = 0; index < websites.length; index++) {
+ // Rövidítjük a nevet a menüben
+ var label = websites[index];
+ if (label.indexOf("wttr.in") !== -1) label = "Idojaras (BP)";
+ if (label.indexOf("example") !== -1) label = "Example.com";
+ if (label.indexOf("cern") !== -1) label = "CERN Info";
+
+ websitesChoice[label] = websites[index];
+ }
+ websitesChoice['Cancel'] = 'Cancel';
+ websitesChoice['Quit'] = 'Quit';
+ return dialog.choice(websitesChoice);
+}
+
+// ... (A selectSection és main függvények ugyanazok maradtak, csak a main hívja a selectWebsite-ot)
+
+function selectSection() {
+ var websitesSections = {};
+ var getMaxLines = textViewer.getMaxLines();
+ var limit = getMaxLines > 50 ? 50 : getMaxLines;
+
+ for (var index = 0; index < limit; index++) {
+ var lineText = textViewer.getLine(index);
+ if (lineText && lineText.length > 0 && lineText[0] !== ' ') {
+ var key = lineText;
+ if (key.length > 20) key = key.substring(0, 20) + "..";
+ websitesSections[key] = String(index);
+ }
+ }
+ websitesSections['Cancel'] = 'Cancel';
+ var choice = dialog.choice(websitesSections);
+ return choice === 'Cancel' ? -1 : parseInt(choice, 10);
+}
+
+function main() {
+ var url = selectWebsite();
+ if (url === 'Quit' || url === 'Cancel') return;
+
+ goToPage(url);
+
+ var redraw = true;
+
+ while (true) {
+ if (keyboard.getSelPress()) {
+ // Egyszerűsített menü
+ var choice = dialog.choice(['Select Website', 'Quit', 'Cancel']);
+
+ if (choice === 'Quit') break;
+ if (choice === 'Cancel') redraw = true;
+ if (choice === 'Select Website') {
+ var newUrl = selectWebsite();
+ if (newUrl !== 'Cancel' && newUrl !== 'Quit') {
+ goToPage(newUrl);
+ } else if (newUrl === 'Quit') break;
+ else redraw = true;
+ }
+ }
+
+ if (keyboard.getPrevPress()) {
+ textViewer.scrollUp();
+ textViewer.draw();
+ redraw = false;
+ }
+ if (keyboard.getNextPress()) {
+ textViewer.scrollDown();
+ textViewer.draw();
+ redraw = false;
+ }
+
+ if (redraw) {
+ textViewer.draw();
+ redraw = false;
+ }
+
+ delay(100);
+ }
+}
+
+main();
diff --git a/examples/M5Stick JS bruce/dino_game_M5.js b/examples/M5Stick JS bruce/dino_game_M5.js
new file mode 100644
index 0000000..b92cc7d
--- /dev/null
+++ b/examples/M5Stick JS bruce/dino_game_M5.js
@@ -0,0 +1,508 @@
+var display = require('display');
+var keyboard = require('keyboard');
+var audio = require('audio');
+
+// I am putting all code in function to optimise, if variables are outside
+// functions they are put in global namespace, and it's slower to get
+// But if you store too much in one function you can cause stackoverflow
+function main() {
+ // This is XBM format, you can convert images to this format using:
+ // https://www.online-utility.org/image/convert/to/XBM
+ // dinoRun: 40x43
+ var dinoRunWidth = 40;
+ var dinoRunHeight = 43;
+ var dinoRunSprite = [
+ new Uint8Array([
+ 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00,
+ 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc,
+ 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00,
+ 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0,
+ 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00,
+ 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00,
+ 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f,
+ 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f,
+ 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff,
+ 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff,
+ 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03,
+ 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0,
+ 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f,
+ 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0x3c, 0x3c, 0x00, 0x00,
+ 0x00, 0x0c, 0x3c, 0x00, 0x00, 0x00, 0x0c, 0x30, 0x00, 0x00, 0x00, 0x3c,
+ 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x00, 0x30, 0x00,
+ 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00,
+ ]),
+ new Uint8Array([
+ 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00, 0xc0, 0xff, 0x3f, 0x00, 0x00,
+ 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xfc, 0xff, 0x00, 0x00, 0xf0, 0xfc,
+ 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00,
+ 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0,
+ 0xff, 0xff, 0x00, 0x00, 0xf0, 0xff, 0xff, 0x00, 0x00, 0xf0, 0x3f, 0x00,
+ 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x0f, 0x00, 0x00,
+ 0xf0, 0xff, 0x0f, 0x03, 0x00, 0xfc, 0x0f, 0x00, 0x03, 0x00, 0xfc, 0x0f,
+ 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x03, 0x80, 0xff, 0x0f, 0x00, 0x0f,
+ 0xf0, 0xff, 0xff, 0x00, 0x0f, 0xf0, 0xff, 0xff, 0x00, 0x3f, 0xfc, 0xff,
+ 0xcf, 0x00, 0x3f, 0xfc, 0xff, 0xcf, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0xff, 0xff,
+ 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x0f, 0x00, 0xfc, 0xff, 0xff, 0x03,
+ 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xf0, 0xff, 0xff, 0x03, 0x00, 0xc0,
+ 0xff, 0xff, 0x00, 0x00, 0xc0, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0x3f,
+ 0x00, 0x00, 0x00, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xfc, 0x30, 0x00, 0x00,
+ 0x00, 0xfc, 0x30, 0x00, 0x00, 0x00, 0x3c, 0x30, 0x00, 0x00, 0x00, 0x3c,
+ 0xf0, 0x00, 0x00, 0x00, 0x0c, 0xf0, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00,
+ 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00, 0x00, 0x3c, 0x00, 0x00, 0x00,
+ ]),
+ ];
+ // dino_duck: 55x26
+ var dinoDuckSprite = [
+ new Uint8Array([
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0,
+ 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff,
+ 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff,
+ 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff,
+ 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0xf0, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0x7e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x78, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00,
+ 0x00, 0x00,
+ ]),
+ new Uint8Array([
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0xe0,
+ 0xff, 0x1f, 0x3f, 0xc0, 0xff, 0x7f, 0xe0, 0xff, 0x1f, 0x3f, 0xc0, 0xff,
+ 0x7f, 0xf8, 0xff, 0x7f, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xff,
+ 0xff, 0xff, 0xff, 0x7f, 0xfe, 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x7f, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0x7f, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x7f, 0xc0, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
+ 0x00, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
+ 0x1f, 0x00, 0x00, 0xfc, 0xff, 0xff, 0xe1, 0xff, 0x07, 0x00, 0xfc, 0xff,
+ 0xff, 0xe1, 0xff, 0x07, 0x00, 0xf0, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
+ 0xf0, 0xff, 0x60, 0x00, 0x00, 0x00, 0x00, 0xf8, 0xe1, 0x63, 0x00, 0x00,
+ 0x00, 0x00, 0xf8, 0xe1, 0xe3, 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0xe0,
+ 0x01, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ ]),
+ ];
+ // ground: 623x12
+ var groundSprite = new Uint8Array([
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1c, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x00,
+ 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00,
+ 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x60,
+ 0xe0, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+ 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x60, 0x00,
+ 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x3c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x04, 0x70,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x0c, 0x00, 0x1c,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x60, 0x00, 0x00, 0x1e,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x02, 0x38,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
+ 0x03, 0x00, 0x00, 0x00, 0x00, 0x80, 0x07, 0x60, 0x00, 0x00, 0x00, 0x80,
+ 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ var bird1Sprite = new Uint8Array([
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00,
+ 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x3f, 0x00, 0x00, 0x00, 0x00,
+ 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00, 0xfc, 0x3f, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00,
+ 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00,
+ 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03,
+ 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0xff, 0xff, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0x03, 0x00,
+ 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
+ ]);
+ var bird2Sprite = new Uint8Array([
+ 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x03, 0x00, 0x00, 0x00,
+ 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0x00, 0x00, 0x00,
+ 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x3f, 0x00, 0x00, 0x00,
+ 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xc0, 0x0f, 0xff, 0x00, 0x00, 0x00,
+ 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00, 0xf0, 0x3f, 0xff, 0x03, 0x00, 0x00,
+ 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00, 0xfc, 0x3f, 0xff, 0x0f, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00, 0xff, 0xff, 0xff, 0x0f, 0x00, 0x00,
+ 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x3f, 0x00, 0x00,
+ 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03, 0x00, 0xc0, 0xff, 0xff, 0xff, 0x03,
+ 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x0f, 0x00,
+ 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00, 0x00, 0x00, 0xfc, 0xff, 0xff, 0x00,
+ 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00, 0x00, 0x00, 0xf0, 0xff, 0x03, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ ]);
+ var cactusSmallSprite = new Uint8Array([
+ 0xc0, 0x01, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x23, 0xe0, 0x73,
+ 0xe0, 0x73, 0xe0, 0x73, 0xe2, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73,
+ 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x73, 0xe7, 0x7f, 0xe7, 0x3f, 0xe7, 0x1f,
+ 0xe7, 0x03, 0xff, 0x03, 0xfe, 0x03, 0xfc, 0x03, 0xe0, 0x03, 0xe0, 0x03,
+ 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03,
+ 0xe0, 0x03, 0xe0, 0x03, 0xe0, 0x03,
+ ]);
+ var cactusBigSprite = new Uint8Array([
+ 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x0e, 0x7f, 0x00, 0x1f, 0x7f, 0x00,
+ 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x00, 0x1f, 0x7f, 0x38,
+ 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c,
+ 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c, 0x1f, 0x7f, 0x7c,
+ 0x1f, 0x7f, 0x7c, 0xff, 0x7f, 0x7c, 0xfe, 0x7f, 0x7c, 0xfc, 0x7f, 0x7c,
+ 0xf8, 0x7f, 0x7c, 0xf0, 0x7f, 0x7c, 0x00, 0x7f, 0x7c, 0x00, 0xff, 0x3f,
+ 0x00, 0xff, 0x1f, 0x00, 0xff, 0x0f, 0x00, 0xff, 0x07, 0x00, 0x7f, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x00,
+ 0x00, 0x7f, 0x00, 0x00, 0x7f, 0x04, 0xd0, 0x7f, 0x00,
+ ]);
+ // cloud: 46x13
+ var cloudSprite = new Uint8Array([
+ 0x00, 0x00, 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0xf0, 0x73, 0x00, 0x00,
+ 0x00, 0x00, 0x18, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x0e, 0x80, 0x00, 0x00,
+ 0x00, 0x00, 0x02, 0x80, 0x07, 0x00, 0x00, 0x00, 0x02, 0x80, 0xfc, 0x00,
+ 0x00, 0xc0, 0x03, 0x40, 0x80, 0x00, 0x00, 0x60, 0x00, 0x00, 0x80, 0x07,
+ 0xc0, 0x3f, 0x00, 0x00, 0x00, 0x04, 0x60, 0x00, 0x00, 0x00, 0x00, 0x1c,
+ 0x20, 0x00, 0x00, 0x00, 0x00, 0x10, 0x36, 0x02, 0x00, 0x00, 0x00, 0x20,
+ 0x03, 0xfc, 0xff, 0xff, 0xff, 0x3f,
+ ]);
+ var obstacles = [
+ {
+ isAnimated: true,
+ spawnsY: [88, 62, 45],
+ width: 42,
+ height: 36,
+ sprites: [bird1Sprite, bird2Sprite],
+ },
+ {
+ isAnimated: false,
+ spawnsY: [92],
+ width: 15,
+ height: 33,
+ sprites: [cactusSmallSprite],
+ },
+ {
+ isAnimated: false,
+ spawnsY: [78],
+ width: 23,
+ height: 47,
+ sprites: [cactusBigSprite],
+ },
+ ];
+ var black = display.color(0, 0, 0);
+ var white = display.color(255, 255, 255);
+ var grey = display.color(100, 100, 100);
+ var gravity = 700;
+
+ // Dino properties
+ var dinoY = 82; // Initial position
+ var dinoVelocity = 0;
+ var dinoJumpStrength = -350;
+ var dinoIsJumping = false;
+ var dinoIsDucking = false;
+
+ // Obstacle properties
+ var obstacleX = 300; // Start off-screen
+ var obstacle = obstacles[1];
+ var obstacleY = obstacle.spawnsY[0];
+
+ // Ground properties
+ var groundX = 0; // Track ground position
+ var groundSpeed = 4; // Speed of ground movement
+ var sprite = display.createSprite();
+
+ var deltaTime = 0;
+ var nowTime = now();
+ var oldTime = nowTime;
+ var startTime = nowTime;
+
+ var playPointSound = 0;
+
+ var dayInterval = 700;
+ var dayTransitionRange = 10;
+ var baseColorValue = 0;
+ var baseColorInverted = Math.abs(baseColorValue - 255);
+
+ var displayWidth = display.width();
+ var displayHeight = display.height();
+
+ var clouds = [
+ { x: random(displayWidth, displayWidth + 100), y: random(0, 50) },
+ { x: random(displayWidth + 200, displayWidth + 300), y: random(0, 50) },
+ { x: random(displayWidth + 400, displayWidth + 500), y: random(0, 50) }
+ ];
+ var foreground = black;
+ var background = white;
+
+ var score = 0;
+
+ sprite.fill(background);
+ sprite.setTextColor(foreground);
+ sprite.setTextSize(2);
+ sprite.setTextAlign(2);
+ keyboard.setLongPress(true);
+ while (true) {
+ if (keyboard.getPrevPress(true)) {
+ break; // Exits the game when a prev button is pressed.
+ }
+
+ var selPressed = keyboard.getSelPress(true);
+ if (selPressed && !dinoIsJumping && !dinoIsDucking) {
+ dinoVelocity = dinoJumpStrength; // Start the jump
+ dinoIsJumping = true;
+ audio.tone(494, 40, true);
+ }
+
+ var nextPressed = keyboard.getNextPress(true) || keyboard.getEscPress(true);
+ if (nextPressed && !dinoIsJumping) {
+ dinoIsDucking = true;
+ }
+
+ if (!nextPressed && dinoIsDucking) {
+ dinoIsDucking = false;
+ }
+
+ nowTime = now();
+ deltaTime = (nowTime - oldTime) / 1000;
+ oldTime = nowTime;
+
+ score = Math.floor((nowTime - startTime) / 100);
+
+ if (playPointSound === 0 && score % 100 < 10 && score > 50) {
+ playPointSound = 1;
+ audio.tone(784, 80, true);
+ } else if (playPointSound === 1) {
+ audio.tone(784, 220, true);
+ playPointSound = 2;
+ } else if (score % 100 > 50 && playPointSound === 2) {
+ playPointSound = 0;
+ }
+
+ // Apply gravity
+ dinoVelocity += gravity * deltaTime;
+ dinoY += dinoVelocity * deltaTime;
+
+ // Prevent dino from falling below ground
+ if (dinoY > 82) {
+ dinoY = 82;
+ dinoIsJumping = false; // Allow jumping again
+ dinoVelocity = 0;
+ }
+
+ groundSpeed = 4 + Math.floor(score / 200);
+ // Move the ground
+ groundX -= groundSpeed;
+ if (groundX <= -383) {
+ groundX = 0; // Reset position for seamless looping
+ }
+
+ // Move the obstacle
+ obstacleX -= groundSpeed;
+ // Reset obstacle
+ if (obstacleX < -obstacle.width) {
+ obstacleX = displayWidth + random(0, 100); // Random respawn
+ obstacle = obstacles[random(0, 3)];
+ obstacleY = obstacle.spawnsY[random(0, obstacle.spawnsY.length)];
+ }
+
+ // Move clouds
+ for (var i = 0; i < 2; i++) {
+ clouds[i].x -= groundSpeed - 2;
+ if (clouds[i].x < -46) {
+ clouds[i].x = displayWidth + 50;
+ clouds[i].y = random(0, 50);
+ }
+ }
+
+ // Day/night cycle
+ var modScore = score % (dayInterval * 2);
+ baseColorValue = 0;
+ baseColorInverted = 255;
+ if (modScore % dayInterval >= dayInterval - dayTransitionRange) {
+ baseColorValue = Math.round(
+ 255 *
+ (((modScore % dayInterval) - (dayInterval - dayTransitionRange)) /
+ dayTransitionRange)
+ );
+ baseColorInverted = Math.abs(baseColorValue - 255);
+ }
+ if (modScore < dayInterval) {
+ foreground = display.color(
+ baseColorValue,
+ baseColorValue,
+ baseColorValue
+ );
+ background = display.color(
+ baseColorInverted,
+ baseColorInverted,
+ baseColorInverted
+ );
+ } else {
+ foreground = display.color(
+ baseColorInverted,
+ baseColorInverted,
+ baseColorInverted
+ );
+ background = display.color(
+ baseColorValue,
+ baseColorValue,
+ baseColorValue
+ );
+ }
+
+ // Draw the scene
+ sprite.fill(background);
+ for (var i = 0; i < 2; i++) {
+ sprite.drawXBitmap(clouds[i].x, clouds[i].y, cloudSprite, 46, 13, grey);
+ }
+ sprite.drawXBitmap(groundX, 118, groundSprite, 623, 12, foreground);
+ if ((displayWidth > 240) && ((623 + groundX) < displayWidth)) {
+ sprite.drawXBitmap(623 + groundX, 118, groundSprite, 623, 12, foreground);
+ }
+ sprite.drawXBitmap(
+ obstacleX,
+ obstacleY,
+ obstacle.sprites[
+ obstacle.isAnimated ? Math.floor((nowTime % 500) / 250) : 0
+ ],
+ obstacle.width,
+ obstacle.height,
+ foreground
+ );
+ if (dinoIsDucking) {
+ sprite.drawXBitmap(
+ 10,
+ dinoY + 17,
+ dinoDuckSprite[Math.floor((nowTime % 200) / 100)],
+ 55,
+ 26,
+ foreground
+ );
+ } else {
+ sprite.drawXBitmap(
+ 10,
+ dinoY,
+ dinoRunSprite[Math.floor((nowTime % 200) / 100)],
+ dinoRunWidth,
+ dinoRunHeight,
+ foreground
+ );
+ }
+ sprite.setTextColor(foreground);
+ sprite.drawText(score, 235, 5);
+ sprite.pushSprite();
+
+ // Collision detection
+ if (
+ 20 < obstacleX + obstacle.width &&
+ 40 > obstacleX &&
+ dinoY + dinoRunHeight - 20 /* dinoBottom */ > obstacleY
+ ) {
+ // Game over
+ if (obstacleY < 78) {
+ if (obstacleY < 50) {
+ if (!dinoIsJumping) continue;
+ } else {
+ if (dinoIsDucking) continue;
+ }
+ }
+
+ audio.tone(60, 100); // 50
+ delay(20);
+ audio.tone(60, 180); // 90
+ display.setTextColor(foreground);
+ display.setTextSize(2);
+ display.setTextAlign(0);
+ display.drawText('GAME OVER', 70, 40);
+ delay(500);
+ while (!keyboard.getAnyPress()) {
+ delay(10);
+ }
+
+ obstacleX = displayWidth + 50;
+ delay(500);
+ startTime = now();
+ }
+ }
+ keyboard.setLongPress(false);
+}
+main();
diff --git a/examples/M5Stick JS bruce/gifs/half_patrick.gif b/examples/M5Stick JS bruce/gifs/half_patrick.gif
new file mode 100644
index 0000000..c3bac8b
Binary files /dev/null and b/examples/M5Stick JS bruce/gifs/half_patrick.gif differ
diff --git a/examples/M5Stick JS bruce/gifs/half_rainbow.gif b/examples/M5Stick JS bruce/gifs/half_rainbow.gif
new file mode 100644
index 0000000..5d69129
Binary files /dev/null and b/examples/M5Stick JS bruce/gifs/half_rainbow.gif differ
diff --git a/examples/M5Stick JS bruce/gifs/half_rick.gif b/examples/M5Stick JS bruce/gifs/half_rick.gif
new file mode 100644
index 0000000..83c405b
Binary files /dev/null and b/examples/M5Stick JS bruce/gifs/half_rick.gif differ
diff --git a/examples/M5Stick JS bruce/gifs/half_spongebob.gif b/examples/M5Stick JS bruce/gifs/half_spongebob.gif
new file mode 100644
index 0000000..35f7dd1
Binary files /dev/null and b/examples/M5Stick JS bruce/gifs/half_spongebob.gif differ
diff --git a/examples/M5Stick JS bruce/gifs_M5.js b/examples/M5Stick JS bruce/gifs_M5.js
new file mode 100644
index 0000000..107e965
--- /dev/null
+++ b/examples/M5Stick JS bruce/gifs_M5.js
@@ -0,0 +1,83 @@
+var display = require('display');
+var serialApi = require('serial');
+var keyboard = require('keyboard');
+
+var println = display.println;
+var gifOpen = display.gifOpen;
+var serialPrintln = serialApi.println;
+
+var tftWidth = display.width();
+
+function now() {
+ return new Date().getTime();
+}
+
+function main() {
+ display.fill(0);
+ println("Loading 3 GIFs...");
+
+ var sectionWidth = Math.floor(tftWidth / 3);
+
+ // 'var'-t használunk 'const' helyett, hogy módosítható legyen (biztonságosabb)
+ var gif1 = gifOpen('littlefs', '/gifs/spongebob.gif');
+ var gif2 = gifOpen('littlefs', '/gifs/patrick.gif');
+ var gif3 = gifOpen('littlefs', '/gifs/rick.gif');
+
+ if (!gif1 && !gif2 && !gif3) {
+ display.fill(0);
+ println("Error: No GIFs found!");
+ delay(3000);
+ throw new Error("Cannot load gifs.");
+ }
+
+ var elapsed;
+ var frames = 0;
+ var fps = 0;
+ var startTime = now();
+
+ display.fill(0);
+
+ while(true) {
+ // --- LEJÁTSZÁS (try-catch, hogy futás közben se fagyjon le) ---
+ try {
+ if (gif1) gif1.playFrame(0, 0);
+ if (gif2) gif2.playFrame(sectionWidth, 0);
+ if (gif3) gif3.playFrame(sectionWidth * 2, 0);
+ } catch(err) {
+ // Ha hiba van lejátszás közben, figyelmen kívül hagyjuk
+ }
+
+ frames++;
+ if (frames % 10 === 0) {
+ elapsed = (now() - startTime) / 1000;
+ if (elapsed > 0) fps = frames / elapsed;
+ }
+
+ // --- KILÉPÉS ---
+ // Csak az oldalsó gomb (Next) vagy ESC (Prev)
+ if (keyboard.getNextPress() || keyboard.getPrevPress()) {
+
+ display.fill(0);
+ println("Closing...");
+
+ // *** A HIBAJAVÍTÁS LÉNYEGE: ***
+ // Megpróbáljuk szépen bezárni őket, de ha hiba van, elnyomjuk azt.
+ try {
+ if (gif1) gif1.close();
+ } catch (e) { /* Hiba elnyomása */ }
+
+ try {
+ if (gif2) gif2.close();
+ } catch (e) { /* Hiba elnyomása */ }
+
+ try {
+ if (gif3) gif3.close();
+ } catch (e) { /* Hiba elnyomása */ }
+
+ delay(100); // Pici várakozás
+ break;
+ }
+ }
+}
+
+main();
\ No newline at end of file
diff --git a/examples/M5Stick JS bruce/highway_racer_M5.js b/examples/M5Stick JS bruce/highway_racer_M5.js
new file mode 100644
index 0000000..fe2d9b3
--- /dev/null
+++ b/examples/M5Stick JS bruce/highway_racer_M5.js
@@ -0,0 +1,253 @@
+var display = require('display');
+var keyboard = require('keyboard');
+
+// --- API Hivatkozások ---
+var width = display.width;
+var height = display.height;
+var drawFillRect = display.drawFillRect;
+var drawString = display.drawString;
+var setTextSize = display.setTextSize;
+
+// --- Kijelző Méretek ---
+var screenWidth = width();
+var screenHeight = height();
+
+// --- Konfiguráció ---
+var LANE_COUNT = 4;
+var LANE_WIDTH = Math.floor(screenWidth / LANE_COUNT);
+var CAR_WIDTH = Math.floor(LANE_WIDTH * 0.5);
+var CAR_HEIGHT = Math.floor(LANE_WIDTH * 0.8);
+
+// A JÁTÉKOS Y POZÍCIÓJA (FIX)
+var PLAYER_Y = screenHeight - CAR_HEIGHT - 10;
+
+// Színek
+var COLOR_PLAYER = 0x0066FF;
+var COLOR_ENEMY1 = 0xFF0000;
+var COLOR_ENEMY2 = 0x00FF00;
+var COLOR_ENEMY3 = 0xFFFF00;
+var COLOR_ROAD = 0x202020;
+var COLOR_LINE = 0xFFFFFF;
+
+// --- Játék Állapot ---
+var playerLane = 1;
+var playerX = 0;
+var playerTargetX = 0;
+var playerScore = 0;
+var gameSpeed = 4;
+
+var cars = [];
+var carSpawnTimer = 0;
+var roadLines = [];
+
+var gameState = "START"; // START, PLAY, GAMEOVER
+var menuSelection = 0;
+
+// --- Inicializálás ---
+function initGame() {
+ playerLane = 1;
+ updatePlayerPosTarget();
+ playerX = playerTargetX;
+ playerScore = 0;
+ cars = [];
+ carSpawnTimer = 0;
+ gameSpeed = 4;
+ gameState = "PLAY";
+
+ roadLines = [];
+ var numLines = Math.floor(screenHeight / 30) + 2;
+ for (var i = 0; i < numLines; i++) {
+ roadLines.push({ y: i * 30 });
+ }
+}
+
+function updatePlayerPosTarget() {
+ playerTargetX = (playerLane * LANE_WIDTH) + (LANE_WIDTH / 2);
+}
+
+// --- Logika ---
+
+function updatePhysics() {
+ // Játékos mozgás
+ if (playerX !== playerTargetX) {
+ var diff = playerTargetX - playerX;
+ var step = Math.ceil(Math.abs(diff) / 3);
+ if (Math.abs(diff) <= step) {
+ playerX = playerTargetX;
+ } else {
+ playerX += diff > 0 ? step : -step;
+ }
+ }
+
+ // Út mozgatása
+ for (var i = 0; i < roadLines.length; i++) {
+ roadLines[i].y += gameSpeed;
+ if (roadLines[i].y > screenHeight) {
+ roadLines[i].y = -30;
+ }
+ }
+
+ // Ellenségek spawnolása
+ carSpawnTimer++;
+ var spawnThreshold = Math.max(15, 50 - (gameSpeed * 2));
+
+ if (carSpawnTimer > spawnThreshold) {
+ if (Math.random() > 0.3) {
+ spawnCar();
+ carSpawnTimer = 0;
+ }
+ }
+
+ // Autók mozgatása és ütközés
+ for (var i = cars.length - 1; i >= 0; i--) {
+ var car = cars[i];
+ car.y += (car.speed + gameSpeed * 0.8);
+
+ // Törlés ha kiment a képernyőről
+ if (car.y > screenHeight) {
+ cars.splice(i, 1);
+ playerScore++;
+ // Sebesség növelés
+ if (playerScore % 5 === 0 && gameSpeed < 12) {
+ gameSpeed += 0.5;
+ }
+ continue;
+ }
+
+ // Ütközésvizsgálat
+ var dx = Math.abs(playerX - car.x);
+
+ // ITT VOLT A HIBA: playerY helyett PLAYER_Y kell!
+ var dy = Math.abs(PLAYER_Y - car.y);
+
+ // Ha ütközés van
+ if (dx < (CAR_WIDTH * 0.8) && dy < (CAR_HEIGHT * 0.8)) {
+ gameState = "GAMEOVER";
+ }
+ }
+}
+
+function spawnCar() {
+ var lane = Math.floor(Math.random() * LANE_COUNT);
+
+ for (var i = 0; i < cars.length; i++) {
+ if (cars[i].lane === lane && cars[i].y < 60) return;
+ }
+
+ var type = Math.floor(Math.random() * 3);
+ var cColor = type === 0 ? COLOR_ENEMY1 : type === 1 ? COLOR_ENEMY2 : COLOR_ENEMY3;
+
+ cars.push({
+ x: (lane * LANE_WIDTH) + (LANE_WIDTH / 2),
+ y: -40,
+ lane: lane,
+ speed: 2 + Math.random() * 2,
+ color: cColor
+ });
+}
+
+// --- Rajzolás ---
+
+function drawCar(x, y, color, isPlayer) {
+ var w = CAR_WIDTH;
+ var h = CAR_HEIGHT;
+ var left = Math.floor(x - w / 2);
+ var top = Math.floor(y - h / 2);
+
+ drawFillRect(left, top, w, h, color);
+ drawFillRect(left + 2, top + (h/4), w - 4, h/2, 0x000000);
+
+ var lightColor = isPlayer ? 0xFF0000 : 0xFFFF00;
+ var lightY = isPlayer ? top + h - 2 : top;
+
+ drawFillRect(left + 1, lightY, 3, 2, lightColor);
+ drawFillRect(left + w - 4, lightY, 3, 2, lightColor);
+}
+
+function drawGame() {
+ drawFillRect(0, 0, screenWidth, screenHeight, COLOR_ROAD);
+
+ for (var i = 1; i < LANE_COUNT; i++) {
+ var lx = i * LANE_WIDTH;
+ for (var j = 0; j < roadLines.length; j++) {
+ drawFillRect(lx - 1, roadLines[j].y, 2, 15, COLOR_LINE);
+ }
+ }
+
+ for (var i = 0; i < cars.length; i++) {
+ drawCar(cars[i].x, cars[i].y, cars[i].color, false);
+ }
+
+ // Játékos kirajzolása (PLAYER_Y változóval)
+ drawCar(playerX, PLAYER_Y, COLOR_PLAYER, true);
+
+ setTextSize(1);
+ drawString("Score: " + playerScore, 2, 2);
+}
+
+function drawMenu(title, subtitle) {
+ drawFillRect(0, 0, screenWidth, screenHeight, 0x000000);
+ setTextSize(2);
+ drawString(title, (screenWidth/2) - (title.length*6), 30);
+ setTextSize(1);
+ drawString(subtitle, (screenWidth/2) - (subtitle.length*3), 60);
+ drawString("[A] Start [B] Lane", 10, screenHeight - 20);
+}
+
+function drawGameOver() {
+ var boxW = screenWidth - 40;
+ var boxH = 80;
+ var boxX = 20;
+ var boxY = (screenHeight / 2) - 40;
+
+ drawFillRect(boxX, boxY, boxW, boxH, 0x330000);
+ drawFillRect(boxX+2, boxY+2, boxW-4, boxH-4, 0xFF0000);
+
+ setTextSize(2);
+ drawString("CRASH!", boxX + 25, boxY + 20);
+ setTextSize(1);
+ drawString("Score: " + playerScore, boxX + 35, boxY + 45);
+ drawString("Press [A] to Retry", boxX + 15, boxY + 65);
+}
+
+// --- Fő Loop ---
+
+drawMenu("RACER", "M5Stick Edition");
+
+while (true) {
+ var btnA = keyboard.getSelPress(); // Nagy gomb
+ var btnB = keyboard.getNextPress(); // Oldalsó gomb
+ var btnEsc = keyboard.getEscPress(); // CardKB ESC
+
+ if (btnEsc) break;
+
+ if (gameState === "START") {
+ if (btnA) initGame();
+ }
+
+ else if (gameState === "GAMEOVER") {
+ if (btnA) initGame();
+ }
+
+ else if (gameState === "PLAY") {
+ // BALRA: A gomb vagy Prev
+ if ((btnA || keyboard.getPrevPress()) && playerLane > 0) {
+ playerLane--;
+ updatePlayerPosTarget();
+ }
+
+ // JOBBRA: B gomb vagy Next
+ if ((btnB || keyboard.getNextPress()) && playerLane < LANE_COUNT - 1) {
+ playerLane++;
+ updatePlayerPosTarget();
+ }
+
+ updatePhysics();
+ drawGame();
+ }
+
+ delay(33);
+}
+
+display.fill(0);
+drawString("Bye!", 60, 60);
\ No newline at end of file
diff --git a/examples/M5Stick JS bruce/space_shooter_M5.js b/examples/M5Stick JS bruce/space_shooter_M5.js
new file mode 100644
index 0000000..d386fc5
--- /dev/null
+++ b/examples/M5Stick JS bruce/space_shooter_M5.js
@@ -0,0 +1,315 @@
+var display = require('display');
+var keyboard = require('keyboard');
+
+// --- API Hivatkozások ---
+var width = display.width;
+var height = display.height;
+var fillScreen = display.fill;
+var drawFillRect = display.drawFillRect;
+var drawString = display.drawString;
+var setTextSize = display.setTextSize;
+
+// --- Kijelző Méretek ---
+var screenWidth = width();
+var screenHeight = height();
+
+// --- Játék Beállítások ---
+var PLAYER_WIDTH = 12;
+var PLAYER_HEIGHT = 8;
+var PLAYER_SPEED = 8;
+var RELOAD_SPEED = 4;
+
+// Színek
+var COLOR_PLAYER = 0x00FF00;
+var COLOR_BULLET = 0xFFFF00;
+var COLOR_ENEMY = 0xFF0000;
+var COLOR_ENEMY_BULLET = 0xFF6600;
+var COLOR_BOSS = 0xFF00FF;
+var COLOR_BG = 0x000000;
+var COLOR_STAR = 0xAAAAAA;
+
+// --- Változók ---
+var playerX = screenWidth / 2;
+var playerY = screenHeight - 20;
+var playerLives = 3;
+var playerScore = 0;
+var playerReloadTimer = 0;
+
+var bullets = [];
+var enemies = [];
+var enemyBullets = [];
+var bosses = [];
+var stars = [];
+
+var level = 1;
+var enemySpawnTimer = 0;
+var bossSpawnTimer = 0;
+
+var gameRunning = true;
+var gameOver = false;
+var gameState = "START";
+
+// --- CSILLAGOK ---
+function initStars() {
+ stars = [];
+ for (var i = 0; i < 15; i++) {
+ stars.push({
+ x: Math.floor(Math.random() * screenWidth),
+ y: Math.floor(Math.random() * screenHeight),
+ speed: 1 + Math.random() * 2
+ });
+ }
+}
+
+function updateAndDrawStars() {
+ for (var i = 0; i < stars.length; i++) {
+ drawFillRect(Math.floor(stars[i].x), Math.floor(stars[i].y), 1, 1, COLOR_BG);
+ stars[i].y += stars[i].speed;
+ if (stars[i].y > screenHeight) {
+ stars[i].y = 0;
+ stars[i].x = Math.random() * screenWidth;
+ }
+ drawFillRect(Math.floor(stars[i].x), Math.floor(stars[i].y), 1, 1, COLOR_STAR);
+ }
+}
+
+// --- LÖVEDÉKEK ---
+function createBullet(x, y, isPlayer) {
+ if (isPlayer) {
+ bullets.push({x: x, y: y, speed: 6, width: 2, height: 6});
+ } else {
+ enemyBullets.push({x: x, y: y, speed: 3, width: 2, height: 4});
+ }
+}
+
+// --- ELLENSÉGEK ---
+function createEnemy(type) {
+ var enemy = {
+ x: Math.random() * (screenWidth - 16),
+ y: -10,
+ width: 12, height: 8,
+ speed: 1 + level * 0.2,
+ health: 1, type: type, shootTimer: 0
+ };
+ if (type === 2) {
+ enemy.width = 16; enemy.height = 12;
+ enemy.health = 3; enemy.speed = 0.5 + level * 0.1;
+ }
+ enemies.push(enemy);
+}
+
+function createBoss() {
+ var boss = {
+ x: screenWidth / 2 - 20, y: -40,
+ width: 40, height: 25, speed: 1,
+ health: 20 + (level * 5), maxHealth: 20 + (level * 5),
+ type: 1, shootTimer: 0, moveDir: 1
+ };
+ bosses.push(boss);
+}
+
+// --- LOGIKA ---
+
+function updatePlayer(btnLeft, btnRight, btnFire) {
+ // Balra (Prev)
+ if (btnLeft && playerX > 0) {
+ playerX -= PLAYER_SPEED;
+ }
+
+ // Jobbra (Next)
+ if (btnRight && playerX < screenWidth - PLAYER_WIDTH) {
+ playerX += PLAYER_SPEED;
+ }
+
+ // Tűz (Sel)
+ if (playerReloadTimer > 0) playerReloadTimer--;
+
+ if (btnFire && playerReloadTimer === 0) {
+ createBullet(playerX + PLAYER_WIDTH / 2 - 1, playerY - 2, true);
+ playerReloadTimer = RELOAD_SPEED;
+ }
+}
+
+function updateGameObjects() {
+ // Lövedékek
+ for (var i = bullets.length - 1; i >= 0; i--) {
+ bullets[i].y -= bullets[i].speed;
+ if (bullets[i].y < -10) { bullets.splice(i, 1); continue; }
+
+ for (var j = enemies.length - 1; j >= 0; j--) {
+ var e = enemies[j];
+ if (bullets[i] && e &&
+ bullets[i].x < e.x + e.width && bullets[i].x + bullets[i].width > e.x &&
+ bullets[i].y < e.y + e.height && bullets[i].y + bullets[i].height > e.y) {
+ bullets.splice(i, 1);
+ e.health--;
+ if (e.health <= 0) { enemies.splice(j, 1); playerScore += 10; }
+ break;
+ }
+ }
+ if (bullets[i]) {
+ for (var b = 0; b < bosses.length; b++) {
+ var boss = bosses[b];
+ if (bullets[i].x < boss.x + boss.width && bullets[i].x + bullets[i].width > boss.x &&
+ bullets[i].y < boss.y + boss.height && bullets[i].y + bullets[i].height > boss.y) {
+ bullets.splice(i, 1);
+ boss.health--;
+ if (boss.health <= 0) {
+ bosses.splice(b, 1); playerScore += 500; level++; playerLives++;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ // Ellenségek
+ enemySpawnTimer++;
+ if (bosses.length === 0 && enemySpawnTimer > Math.max(20, 60 - level * 5)) {
+ enemySpawnTimer = 0;
+ createEnemy(Math.random() > 0.8 ? 2 : 1);
+ }
+ bossSpawnTimer++;
+ if (bosses.length === 0 && playerScore > level * 500 && bossSpawnTimer > 200) {
+ createBoss(); bossSpawnTimer = 0;
+ }
+
+ for (var i = enemies.length - 1; i >= 0; i--) {
+ var e = enemies[i];
+ e.y += e.speed;
+ e.shootTimer++;
+ if (e.shootTimer > 80 - level) {
+ e.shootTimer = 0;
+ if (Math.random() > 0.5) createBullet(e.x + e.width/2, e.y + e.height, false);
+ }
+ if (e.y > screenHeight) { enemies.splice(i, 1); continue; }
+ if (e.x < playerX + PLAYER_WIDTH && e.x + e.width > playerX &&
+ e.y < playerY + PLAYER_HEIGHT && e.y + e.height > playerY) {
+ playerLives--; enemies.splice(i, 1);
+ if (playerLives <= 0) gameState = "GAMEOVER";
+ }
+ }
+
+ // Boss
+ for (var i = 0; i < bosses.length; i++) {
+ var boss = bosses[i];
+ if (boss.y < 20) boss.y++;
+ boss.x += boss.speed * boss.moveDir;
+ if (boss.x <= 0 || boss.x >= screenWidth - boss.width) boss.moveDir *= -1;
+ boss.shootTimer++;
+ if (boss.shootTimer > 20) {
+ createBullet(boss.x + boss.width/2, boss.y + boss.height, false);
+ if (level > 2) {
+ createBullet(boss.x, boss.y + boss.height, false);
+ createBullet(boss.x + boss.width, boss.y + boss.height, false);
+ }
+ boss.shootTimer = 0;
+ }
+ }
+
+ // Ellenség lövedék
+ for (var i = enemyBullets.length - 1; i >= 0; i--) {
+ var eb = enemyBullets[i];
+ eb.y += eb.speed;
+ if (eb.y > screenHeight) { enemyBullets.splice(i, 1); continue; }
+ if (eb.x < playerX + PLAYER_WIDTH && eb.x + eb.width > playerX &&
+ eb.y < playerY + PLAYER_HEIGHT && eb.y + eb.height > playerY) {
+ enemyBullets.splice(i, 1); playerLives--;
+ if (playerLives <= 0) gameState = "GAMEOVER";
+ }
+ }
+}
+
+// --- RAJZOLÁS ---
+function drawGame() {
+ fillScreen(COLOR_BG);
+ updateAndDrawStars();
+
+ drawFillRect(Math.floor(playerX), Math.floor(playerY), PLAYER_WIDTH, PLAYER_HEIGHT, COLOR_PLAYER);
+ drawFillRect(Math.floor(playerX + PLAYER_WIDTH/2 - 2), Math.floor(playerY - 2), 4, 2, COLOR_PLAYER);
+
+ for (var i = 0; i < bullets.length; i++) drawFillRect(Math.floor(bullets[i].x), Math.floor(bullets[i].y), bullets[i].width, bullets[i].height, COLOR_BULLET);
+ for (var i = 0; i < enemyBullets.length; i++) drawFillRect(Math.floor(enemyBullets[i].x), Math.floor(enemyBullets[i].y), enemyBullets[i].width, enemyBullets[i].height, COLOR_ENEMY_BULLET);
+
+ for (var i = 0; i < enemies.length; i++) {
+ var e = enemies[i];
+ drawFillRect(Math.floor(e.x), Math.floor(e.y), e.width, e.height, COLOR_ENEMY);
+ drawFillRect(Math.floor(e.x + 2), Math.floor(e.y + 2), 2, 2, 0xFFFFFF);
+ drawFillRect(Math.floor(e.x + e.width - 4), Math.floor(e.y + 2), 2, 2, 0xFFFFFF);
+ }
+ for (var i = 0; i < bosses.length; i++) {
+ var b = bosses[i];
+ drawFillRect(Math.floor(b.x), Math.floor(b.y), b.width, b.height, COLOR_BOSS);
+ var hpBar = (b.health / b.maxHealth) * b.width;
+ drawFillRect(Math.floor(b.x), Math.floor(b.y - 4), Math.floor(hpBar), 2, 0x00FF00);
+ }
+
+ setTextSize(1);
+ drawString("Score: " + playerScore, 2, 2);
+ drawString("HP: " + playerLives, screenWidth - 40, 2);
+}
+
+function drawMenu(title, sub) {
+ fillScreen(0x000000);
+ setTextSize(2); drawString(title, 20, 30);
+ setTextSize(1); drawString(sub, 20, 60);
+ drawString("[Prev] Left [Next] Right", 5, screenHeight - 20);
+ drawString("[M5] Fire [Prev+Next] Exit", 5, screenHeight - 10);
+}
+
+function drawGameOver() {
+ fillScreen(0x000000);
+ drawFillRect(20, 30, screenWidth - 40, 80, 0x330000);
+ setTextSize(2); drawString("GAME OVER", 35, 45);
+ setTextSize(1); drawString("Score: " + playerScore, 50, 70);
+ drawString("Press [M5] to Retry", 30, 90);
+}
+
+// --- FŐ CIKLUS ---
+
+// FONTOS: Kikapcsoljuk a hosszú nyomást, hogy ne zavarjon
+keyboard.setLongPress(false);
+
+initStars();
+drawMenu("SPACE", "SHOOTER");
+
+while (true) {
+ // Először eltároljuk a gombok állapotát
+ var btnFire = keyboard.getSelPress(); // M5 Nagy gomb
+ var btnLeft = keyboard.getPrevPress(); // Felső (Prev)
+ var btnRight = keyboard.getNextPress(); // Alsó (Next)
+
+ // KILÉPÉS: Ha mindkettőt egyszerre nyomod (Prev + Next)
+ // Megjegyzés: Egyszerre kell lenyomni őket!
+ if (btnLeft && btnRight) {
+ break;
+ }
+
+ if (gameState === "START") {
+ if (btnFire) {
+ gameState = "PLAY";
+ playerX = screenWidth / 2;
+ playerLives = 3;
+ playerScore = 0;
+ level = 1;
+ enemies = []; bullets = []; bosses = []; enemyBullets = [];
+ }
+ }
+ else if (gameState === "GAMEOVER") {
+ if (btnFire) gameState = "START";
+ }
+ else if (gameState === "PLAY") {
+ // Átadjuk a gombok állapotát a játékos frissítésnek
+ updatePlayer(btnLeft, btnRight, btnFire);
+ updateGameObjects();
+ drawGame();
+ }
+
+ if (gameState === "START") drawMenu("SPACE", "SHOOTER");
+ if (gameState === "GAMEOVER") drawGameOver();
+
+ delay(30);
+}
+
+fillScreen(0);
+drawString("Bye!", 60, 60);
\ No newline at end of file
diff --git a/examples/M5Stick JS bruce/tamagochi_M5.js b/examples/M5Stick JS bruce/tamagochi_M5.js
new file mode 100644
index 0000000..7382831
--- /dev/null
+++ b/examples/M5Stick JS bruce/tamagochi_M5.js
@@ -0,0 +1,354 @@
+// created by hxd57. V2
+// repository: https://github.com/HawkstoNGriM/m5-bruce-tamagochi-lite
+var dbStore = {fs: "sd", path: "/pet.json"}; // fs can be "sd" or "littlefs"
+var storage = require("storage");
+var display = require('display');
+var keyboardApi = require('keyboard');
+var dialog = require('dialog');
+var serialApi = require('serial');
+
+var width = display.width;
+var height = display.height;
+var color = display.color;
+var fillScreen = display.fill;
+var drawString = display.drawString;
+var setTextColor = display.setTextColor;
+var setTextSize = display.setTextSize;
+
+var keyboardPrompt = keyboardApi.keyboard;
+var getNextPress = keyboardApi.getNextPress;
+
+var dialogMessage = dialog.message;
+var dialogError = dialog.error;
+var dialogChoice = dialog.choice;
+
+var serialCmd = serialApi.cmd;
+var pastelColors = {
+ "Peach": color(255, 223, 186),
+ "Mint": color(186, 255, 201),
+ "Pink": color(255, 186, 255),
+ "Blue": color(186, 225, 255),
+ "Yellow": color(255, 255, 186),
+ "White": color(255, 255, 255),
+ "Lavender": color(230, 230, 250),
+ "Coral": color(255, 127, 80),
+ "Aqua": color(127, 255, 212),
+ "Beige": color(245, 245, 220)
+};
+var currentBgColor = pastelColors["Peach"];
+
+var faceColors = {
+ "Black": color(0, 0, 0),
+ "White": color(255, 255, 255),
+ "Red": color(255, 0, 0),
+ "Blue": color(0, 0, 255),
+ "Green": color(0, 255, 0),
+ "Purple": color(128, 0, 128)
+};
+var faceColor = faceColors["Black"]; // Default face color
+
+function Pet(name, type, hunger, cleanliness, happiness, timeLastFed, timeLastPet, timeLastCleaned) {
+ this.name = name || "gotchi"; // Changed default name to "gotchi"
+ this.type = type || "cat";
+ this.hunger = Math.min(100, Math.max(0, hunger !== undefined ? hunger : 0)); // Ensure hunger is between 0% and 100%
+ this.cleanliness = Math.min(100, Math.max(0, cleanliness !== undefined ? cleanliness : 100)); // Ensure cleanliness is between 0% and 100%
+ this.happiness = Math.min(100, Math.max(0, happiness !== undefined ? happiness : 50)); // Ensure happiness is between 0% and 100%
+ var time = now();
+ this.timeLastFed = timeLastFed || time;
+ this.timeLastPet = timeLastPet || time;
+ this.timeLastCleaned = timeLastCleaned || time; // Track cleaning time
+}
+
+Pet.prototype = {
+ feed: function() {
+ this.hunger = Math.max(0, this.hunger - 10); // Reduce hunger by 10%, but not below 0%
+ this.timeLastFed = now();
+ this.happiness = Math.min(100, this.happiness + 20); // Increase happiness by 20%, but not above 100%
+ display.fill(currentBgColor);
+ dialogMessage(this.name + " has been fed!");
+ },
+ clean: function() {
+ this.cleanliness = 100; // Fully clean
+ this.timeLastCleaned = now();
+ display.fill(currentBgColor);
+ dialogMessage(this.name + " is now clean!");
+ },
+ pet: function() {
+ this.happiness = Math.min(100, this.happiness + 10); // Reduced happiness gain
+ this.timeLastPet = now();
+ display.fill(currentBgColor);
+ dialogMessage(this.name + " loves your petting!");
+ },
+ updateHunger: function() {
+ var time = now();
+ var elapsed = time - this.timeLastFed;
+ this.hunger = Math.min(100, Math.max(0, this.hunger + Math.floor(elapsed / 7200000) * 10)); // Ensure hunger is between 0% and 100%
+ },
+ updateHappiness: function() {
+ var time = now();
+ var elapsed = time - this.timeLastPet;
+ this.happiness = Math.max(0, this.happiness - Math.floor(elapsed / 3600000) * 5); // Ensure happiness is between 0% and 100%
+ },
+ updateCleanliness: function() {
+ var time = now();
+ var elapsed = time - this.timeLastCleaned;
+ var hours = elapsed / 3600000;
+ this.cleanliness = Math.max(0, 100 - Math.floor(hours * 5)); // Ensure cleanliness is between 0% and 100%
+ },
+ save: function() {
+ var petData = {
+ name: this.name,
+ type: this.type,
+ hunger: this.hunger,
+ cleanliness: this.cleanliness,
+ happiness: this.happiness,
+ timeLastFed: this.timeLastFed,
+ timeLastPet: this.timeLastPet,
+ timeLastCleaned: this.timeLastCleaned // Save cleaning time
+ };
+ var jsonString = JSON.stringify(petData) + "\n";
+ storage.write(dbStore, jsonString);
+ }
+};
+
+
+function loadPet() {
+ var data = null;
+ try {
+ data = storage.read(dbStore);
+ } catch (e) {
+ return null;
+ }
+
+ if (data) {
+ try {
+ var lines = data.split("\n");
+ // Filter out empty lines
+ var nonEmptyLines = [];
+ for (var i = 0; i < lines.length; i++) {
+ if (lines[i].trim() !== "") nonEmptyLines.push(lines[i]);
+ }
+ var lastLine = nonEmptyLines[nonEmptyLines.length - 1];
+ if (lastLine) {
+ var obj = JSON.parse(lastLine);
+ // Ensure timeLastFed and timeLastPet are numbers
+ obj.timeLastFed = Number(obj.timeLastFed);
+ obj.timeLastPet = Number(obj.timeLastPet);
+ // Handle old saves without timeLastCleaned
+ if (obj.timeLastCleaned === undefined) {
+ var hoursAgo = (100 - obj.cleanliness) / 5;
+ obj.timeLastCleaned = now() - hoursAgo * 3600000;
+ } else {
+ obj.timeLastCleaned = Number(obj.timeLastCleaned);
+ }
+ return new Pet(obj.name, obj.type, obj.hunger, obj.cleanliness,
+ obj.happiness, obj.timeLastFed, obj.timeLastPet, obj.timeLastCleaned);
+ }
+ } catch (e) {
+ display.fill(currentBgColor);
+ dialogError("Failed to load pet data: " + e.message);
+ return null;
+ }
+ }
+ return null; // No pet file found
+}
+
+function drawPet(pet) {
+ var screenWidth = width();
+ var screenHeight = height();
+ var textYSpacing = 14;
+
+ var faces = {
+ cat: [" >_< ", "=^_^=", " ^-^ "],
+ dog: [" T_T ", " o_o ", " ^_^ "],
+ bird: [" x_x ", " -_- ", " ^v^ "]
+ };
+
+ fillScreen(currentBgColor);
+ setTextColor(faceColor);
+ setTextSize(2);
+
+ // Include cleanliness in state calculation
+ var stateIndex = (pet.hunger >= 70 || pet.happiness <= 30 || pet.cleanliness <= 30) ? 0 :
+ (pet.hunger >= 30 || pet.happiness <= 50 || pet.cleanliness <= 50) ? 1 : 2;
+
+ var face = faces[pet.type][stateIndex];
+ var faceWidth = face.length * 12;
+ var faceX = Math.floor((screenWidth - faceWidth) / 2);
+ var faceY = Math.floor(screenHeight * 0.3);
+
+ var time = now();
+ if (time % 4000 < 200) {
+ face = face.replace(/[^\s]/g, "-");
+ }
+ if (stateIndex === 2 && time % 1000 < 500) {
+ faceX += Math.sin(time / 200) * (screenWidth * 0.1);
+ }
+
+ drawString(face, faceX, faceY);
+ drawString(face, faceX + 1, faceY);
+
+ var happyText = "Happy: " + pet.happiness + "%";
+ var happyWidth = happyText.length * 12;
+ var happyX = Math.floor((screenWidth - happyWidth) / 2);
+ drawString(happyText, happyX, 10);
+
+ var statusY = screenHeight - 60;
+ drawString("Hngr: " + pet.hunger + "%", 10, statusY);
+ drawString("Clean: " + pet.cleanliness + "%", 10, statusY + textYSpacing);
+
+ var fedHrs = Math.floor((time - pet.timeLastFed) / 3600000);
+ var petHrs = Math.floor((time - pet.timeLastPet) / 3600000);
+ drawString("Fed:" + fedHrs + "h Pet:" + petHrs + "h", 10, screenHeight - 30);
+}
+
+
+
+function showHeartAnimation() {
+ var screenWidth = width();
+ var screenHeight = height();
+ var heartText = "<3-<3";
+ var heartWidth = heartText.length * 12;
+ var heartX = Math.floor((screenWidth - heartWidth) / 2);
+ var heartY = Math.floor(screenHeight * 0.5);
+
+ // Use the current background color
+ fillScreen(currentBgColor);
+ setTextColor(faceColor);
+ setTextSize(1);
+
+ // Draw the heart
+ drawString(heartText, heartX, heartY);
+
+ delay(1000);
+
+ var heartWidth2 = 7 * 12;
+ var heartX = Math.floor((screenWidth - heartWidth2) / 2);
+ var heartY = Math.floor(screenHeight * 0.5);
+ fillScreen(currentBgColor);
+ setTextColor(faceColor);
+ setTextSize(1);
+ drawString(heartText, heartX, heartY);
+
+ delay(1000);
+
+ var heartWidth2 = 3 * 12;
+ var heartX = Math.floor((screenWidth - heartWidth2) / 2);
+ var heartY = Math.floor(screenHeight * 0.5);
+ fillScreen(currentBgColor);
+ setTextColor(color(255, 0, 0));
+ setTextSize(1);
+ drawString(heartText, heartX, heartY);
+
+ delay(1000);
+
+ // Restore the original background color
+ fillScreen(currentBgColor);
+}
+
+var pet = loadPet();
+
+if (!pet) {
+ fillScreen(currentBgColor);
+ setTextColor(faceColor);
+ setTextSize(2);
+ var name = keyboardPrompt("", 12, "Pet's name?") || "gotchi"; // Default to "gotchi"
+ var type = dialogChoice({["Cat"]:"cat", ["Dog"]:"dog", ["Bird"]:"bird"}) || "cat";
+ pet = new Pet(name, type);
+ pet.save();
+}
+
+var updateTime=0;
+while (true) {
+ if(now()-updateTime>500){
+ pet.updateHunger();
+ pet.updateHappiness();
+ pet.updateCleanliness();
+ drawPet(pet);
+ updateTime = now();
+ }
+
+ if (getNextPress()) {
+ var choice = dialogChoice({
+ ["Pet"]: "pet",
+ ["Feed"]: "feed",
+ ["Clean"]: "clean",
+ ["Heart"]: "heart",
+ ["Settings"]: "settings",
+ ["New Pet"]: "newpet",
+ ["Exit"]: "exit"
+ }) || "";
+
+ if (choice === "exit") {
+ pet.save(); // Save before exiting
+ break;
+ }
+
+ if (choice === "settings") {
+ var setting = dialogChoice({
+ ["Change BG Color"]: "bgcolor",
+ ["Face Color"]: "facecolor",
+ ["Back"]: "back"
+ });
+
+ if (setting === "bgcolor") {
+ var colorChoice = dialogChoice([
+ "Peach",
+ "Mint",
+ "Pink",
+ "Blue",
+ "Yellow",
+ "White",
+ "Lavender",,
+ "Coral",
+ "Aqua",
+ "Beige"
+ ]);
+ if (colorChoice && pastelColors[colorChoice]) {
+ currentBgColor = pastelColors[colorChoice];
+ }
+ }
+ else if (setting === "facecolor") {
+ var fc = dialogChoice({
+ ["Black"]: "black",
+ ["White"]: "white",
+ ["Red"]: "red",
+ ["Blue"]: "blue",
+ ["Green"]: "green",
+ ["Purple"]: "purple"
+ });
+ faceColor = faceColors[fc];
+ }
+ }
+ else if (choice === "newpet") {
+ var confirm = dialogChoice({
+ ["Yes (will delete old pet)"]: "yes",
+ ["No (cancel)"]: "no"
+ });
+
+ if (confirm === "yes") {
+ serialCmd("storage remove JS-scripts/bruce_0.sub");
+ serialCmd("storage remove pet.json");
+
+ // Create a new pet
+ fillScreen(currentBgColor);
+ setTextColor(faceColor);
+ setTextSize(2);
+ var name = keyboardPrompt("", 12, "Pet's name?") || "gotchi"; // Default to "gotchi"
+ var type = dialogChoice({["Cat"]:"cat", ["Dog"]:"dog", ["Bird"]:"bird"}) || "cat";
+ pet = new Pet(name, type);
+ pet.save();
+ }
+ }
+ else if (choice === "heart") {
+ showHeartAnimation();
+ }
+ else if (choice && pet[choice]) {
+ pet[choice]();
+ delay(1000);
+ }
+ pet.save(); // Save after performing an action
+ }
+
+ delay(10);
+}
diff --git a/examples/key copier SCAD maker/AR4.scad b/examples/key copier SCAD maker/AR4.scad
new file mode 100644
index 0000000..3644c66
--- /dev/null
+++ b/examples/key copier SCAD maker/AR4.scad
@@ -0,0 +1,26 @@
+// Generated by KeyCopier for Arrow AR4
+function mm(i) = i*25.4;
+
+module rounded(size, r) { union() { translate([r, 0, 0]) cube([size[0]-2*r, size[1], size[2]]); translate([0, r, 0]) cube([size[0], size[1]-2*r, size[2]]); translate([r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); } }
+module bit() { w = mm(1/4); difference() { translate([-w/2, 0, 0]) cube([w, mm(1), w]); translate([-mm(5/128), 0, 0]) rotate([0, 0, 135]) cube([w, w, w]); translate([mm(5/128), 0, 0]) rotate([0, 0, -45]) cube([w, w, w]); } }
+module key(bits) {
+ thickness = mm(0.080);
+ length = mm(1.140);
+ width = mm(0.312);
+ shoulder = mm(0.265);
+ pin_spacing = mm(0.155);
+ depth_inc = mm(0.014);
+ fudge = 0.5;
+ h_l = mm(1); h_w = mm(1); h_d = mm(1/16);
+ difference() {
+ union() { translate([-h_l, -h_w/2 + width/2, 0]) rounded([h_l, h_w, thickness], mm(1/4)); cube([length - mm(1/64), width, thickness]); }
+ translate([length, mm(1/8), 0]) { rotate([0, 0, 45]) cube([10, 10, thickness]); rotate([0, 0, 225]) cube([10, 10, thickness]); }
+ translate([-h_l + mm(3/16), width/2, 0]) cylinder(h=thickness, r=mm(1/8), $fn=16);
+ union() { translate([-h_d, mm(.105), mm(.025)]) rotate([225, 0, 0]) cube([length + h_d, width, width]); translate([-h_d, mm(.105), mm(.05)]) rotate([260, 0, 0]) cube([length + h_d, thickness/2, mm(1/32)]); translate([-h_d, mm(.105), 0]) cube([length + h_d, mm(7/128), mm(.05)]); translate([-h_d, mm(.105) + mm(7/128), mm(.05)]) rotate([225, 0, 0]) cube([length + h_d, mm(3/64), thickness]); }
+ for (b = [0:5]) {
+ translate([shoulder + fudge + b*pin_spacing, width - mm(.008) - (bits[b] - 0)*depth_inc - fudge, 0]) bit();
+ }
+ }
+}
+
+key([3,4,5,1,4,0]);
diff --git a/examples/key copier SCAD maker/Rittal 3524 E.scad b/examples/key copier SCAD maker/Rittal 3524 E.scad
new file mode 100644
index 0000000..5bc6a27
--- /dev/null
+++ b/examples/key copier SCAD maker/Rittal 3524 E.scad
@@ -0,0 +1,28 @@
+// Generated by KeyCopier for Rittal 3524 E
+function mm(i) = i*25.4;
+
+module rounded(size, r) { union() { translate([r, 0, 0]) cube([size[0]-2*r, size[1], size[2]]); translate([0, r, 0]) cube([size[0], size[1]-2*r, size[2]]); translate([r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); } }
+module bit() { w = mm(1/4); difference() { translate([-w/2, 0, 0]) cube([w, mm(1), w]); translate([-mm(5/128), 0, 0]) rotate([0, 0, 135]) cube([w, w, w]); translate([mm(5/128), 0, 0]) rotate([0, 0, -45]) cube([w, w, w]); } }
+module key(bits) {
+ thickness = mm(0.080);
+ length = mm(0.750);
+ width = mm(0.331);
+ shoulder = mm(0.138);
+ pin_spacing = mm(0.138);
+ depth_inc = mm(0.031);
+ fudge = 0.5;
+ h_l = mm(1); h_w = mm(1); h_d = mm(1/16);
+ difference() {
+ union() { translate([-h_l, -h_w/2 + width/2, 0]) rounded([h_l, h_w, thickness], mm(1/4)); cube([length - mm(1/64), width, thickness]); }
+ translate([length, mm(1/8), 0]) { rotate([0, 0, 45]) cube([10, 10, thickness]); rotate([0, 0, 225]) cube([10, 10, thickness]); }
+ translate([-h_l + mm(3/16), width/2, 0]) cylinder(h=thickness, r=mm(1/8), $fn=16);
+ union() { translate([-h_d, mm(.105), mm(.025)]) rotate([225, 0, 0]) cube([length + h_d, width, width]); translate([-h_d, mm(.105), mm(.05)]) rotate([260, 0, 0]) cube([length + h_d, thickness/2, mm(1/32)]); translate([-h_d, mm(.105), 0]) cube([length + h_d, mm(7/128), mm(.05)]); translate([-h_d, mm(.105) + mm(7/128), mm(.05)]) rotate([225, 0, 0]) cube([length + h_d, mm(3/64), thickness]); }
+ translate([-h_d, width - mm(9/64), mm(.043)]) { cube([length + h_d, width - (width - mm(10/64)), thickness]); rotate([50, 0, 0]) cube([length + h_d, width, thickness]); }
+ union() { translate([-h_d, mm(0.015), mm(.03)]) cube([length + h_d, mm(15/256), thickness]); translate([-h_d, mm(0.015) + mm(13/256), thickness - mm(1/64)]) rotate([45, 0, 0]) cube([length + h_d, mm(1/16), mm(1/16)]); }
+ for (b = [0:3]) {
+ translate([shoulder + fudge + b*pin_spacing, width - mm(.008) - (bits[b] - 1)*depth_inc - fudge, 0]) bit();
+ }
+ }
+}
+
+key([3,5,2,4]);
diff --git a/examples/key copier SCAD maker/elezt 751.stl b/examples/key copier SCAD maker/elezt 751.stl
new file mode 100644
index 0000000..e7857ed
Binary files /dev/null and b/examples/key copier SCAD maker/elezt 751.stl differ
diff --git a/examples/key copier SCAD maker/elzet.scad b/examples/key copier SCAD maker/elzet.scad
new file mode 100644
index 0000000..e4b7bec
--- /dev/null
+++ b/examples/key copier SCAD maker/elzet.scad
@@ -0,0 +1,28 @@
+// Generated by KeyCopier for Elzett 751 (Euro)
+function mm(i) = i*25.4;
+
+module rounded(size, r) { union() { translate([r, 0, 0]) cube([size[0]-2*r, size[1], size[2]]); translate([0, r, 0]) cube([size[0], size[1]-2*r, size[2]]); translate([r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); translate([size[0]-r, size[1]-r, 0]) cylinder(h=size[2], r=r, $fn=16); } }
+module bit() { w = mm(1/4); difference() { translate([-w/2, 0, 0]) cube([w, mm(1), w]); translate([-mm(5/128), 0, 0]) rotate([0, 0, 135]) cube([w, w, w]); translate([mm(5/128), 0, 0]) rotate([0, 0, -45]) cube([w, w, w]); } }
+module key(bits) {
+ thickness = mm(0.080);
+ length = mm(0.925);
+ width = mm(0.338);
+ shoulder = mm(0.177);
+ pin_spacing = mm(0.158);
+ depth_inc = mm(0.020);
+ fudge = 0.5;
+ h_l = mm(1); h_w = mm(1); h_d = mm(1/16);
+ difference() {
+ union() { translate([-h_l, -h_w/2 + width/2, 0]) rounded([h_l, h_w, thickness], mm(1/4)); cube([length - mm(1/64), width, thickness]); }
+ translate([length, mm(1/8), 0]) { rotate([0, 0, 45]) cube([10, 10, thickness]); rotate([0, 0, 225]) cube([10, 10, thickness]); }
+ translate([-h_l + mm(3/16), width/2, 0]) cylinder(h=thickness, r=mm(1/8), $fn=16);
+ union() { translate([-h_d, mm(.105), mm(.025)]) rotate([225, 0, 0]) cube([length + h_d, width, width]); translate([-h_d, mm(.105), mm(.05)]) rotate([260, 0, 0]) cube([length + h_d, thickness/2, mm(1/32)]); translate([-h_d, mm(.105), 0]) cube([length + h_d, mm(7/128), mm(.05)]); translate([-h_d, mm(.105) + mm(7/128), mm(.05)]) rotate([225, 0, 0]) cube([length + h_d, mm(3/64), thickness]); }
+ translate([-h_d, width - mm(9/64), mm(.043)]) { cube([length + h_d, width - (width - mm(10/64)), thickness]); rotate([50, 0, 0]) cube([length + h_d, width, thickness]); }
+ union() { translate([-h_d, mm(0.015), mm(.03)]) cube([length + h_d, mm(15/256), thickness]); translate([-h_d, mm(0.015) + mm(13/256), thickness - mm(1/64)]) rotate([45, 0, 0]) cube([length + h_d, mm(1/16), mm(1/16)]); }
+ for (b = [0:4]) {
+ translate([shoulder + fudge + b*pin_spacing, width - mm(.008) - (bits[b] - 1)*depth_inc - fudge, 0]) bit();
+ }
+ }
+}
+
+key([5,6,2,3,2]);
diff --git a/examples/key copier SCAD maker/keycopier_scad.js b/examples/key copier SCAD maker/keycopier_scad.js
new file mode 100644
index 0000000..ba1f218
--- /dev/null
+++ b/examples/key copier SCAD maker/keycopier_scad.js
@@ -0,0 +1,295 @@
+var display = require('display');
+var keyboard = require('keyboard');
+var storage = require('storage');
+
+// ===================================================================================
+// 1. TELJES KULCS ADATBÁZIS (Bővítve: Elzett & JMA/Universal & Rittal)
+// ===================================================================================
+var KEY_DB = [
+ { manufacturer: "Elzett", format_name: "751 (Euro)", pin_num: 5, first_pin_inch: 0.177, last_pin_inch: 0.807, pin_increment_inch: 0.1575, pin_width_inch: 0.039, elbow_inch: 0.118, drill_angle: 90, uncut_depth_inch: 0.338, deepest_depth_inch: 0.197, depth_step_inch: 0.0197, min_depth_ind: 1, max_depth_ind: 6 },
+ { manufacturer: "Universal", format_name: "JMA / Euro", pin_num: 5, first_pin_inch: 0.157, last_pin_inch: 0.787, pin_increment_inch: 0.1575, pin_width_inch: 0.039, elbow_inch: 0.118, drill_angle: 90, uncut_depth_inch: 0.335, deepest_depth_inch: 0.197, depth_step_inch: 0.020, min_depth_ind: 1, max_depth_ind: 6 },
+ { manufacturer: "Rittal", format_name: "3524 E", pin_num: 4, first_pin_inch: 0.138, last_pin_inch: 0.650, pin_increment_inch: 0.138, pin_width_inch: 0.060, elbow_inch: 0.100, drill_angle: 90, uncut_depth_inch: 0.331, deepest_depth_inch: 0.177, depth_step_inch: 0.031, min_depth_ind: 1, max_depth_ind: 5 },
+ { manufacturer: "Kwikset", format_name: "KW1", first_pin_inch: 0.247, last_pin_inch: 0.847, pin_increment_inch: 0.15, pin_num: 5, pin_width_inch: 0.084, elbow_inch: 0.15, drill_angle: 90, uncut_depth_inch: 0.329, deepest_depth_inch: 0.191, depth_step_inch: 0.023, min_depth_ind: 1, max_depth_ind: 7 },
+ { manufacturer: "Schlage", format_name: "SC4", first_pin_inch: 0.231, last_pin_inch: 1.012, pin_increment_inch: 0.1562, pin_num: 6, pin_width_inch: 0.031, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.335, deepest_depth_inch: 0.2, depth_step_inch: 0.015, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "Yale", format_name: "Y11", first_pin_inch: 0.124, last_pin_inch: 0.502, pin_increment_inch: 0.095, pin_num: 5, pin_width_inch: 0.039, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.246, deepest_depth_inch: 0.167, depth_step_inch: 0.020, min_depth_ind: 1, max_depth_ind: 5 },
+ { manufacturer: "Yale", format_name: "Y2", first_pin_inch: 0.200, last_pin_inch: 1.025, pin_increment_inch: 0.165, pin_num: 6, pin_width_inch: 0.054, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.320, deepest_depth_inch: 0.149, depth_step_inch: 0.019, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "Master Lock", format_name: "M1", first_pin_inch: 0.185, last_pin_inch: 0.689, pin_increment_inch: 0.126, pin_num: 5, pin_width_inch: 0.039, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.276, deepest_depth_inch: 0.171, depth_step_inch: 0.015, min_depth_ind: 0, max_depth_ind: 7 },
+ { manufacturer: "Arrow", format_name: "AR4", first_pin_inch: 0.265, last_pin_inch: 1.040, pin_increment_inch: 0.155, pin_num: 6, pin_width_inch: 0.060, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.312, deepest_depth_inch: 0.186, depth_step_inch: 0.014, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "American", format_name: "AM7", first_pin_inch: 0.157, last_pin_inch: 0.781, pin_increment_inch: 0.125, pin_num: 6, pin_width_inch: 0.039, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.283, deepest_depth_inch: 0.173, depth_step_inch: 0.016, min_depth_ind: 1, max_depth_ind: 8 },
+ { manufacturer: "Sargent", format_name: "S22", first_pin_inch: 0.216, last_pin_inch: 0.996, pin_increment_inch: 0.156, pin_num: 6, pin_width_inch: 0.063, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.328, deepest_depth_inch: 0.148, depth_step_inch: 0.020, min_depth_ind: 1, max_depth_ind: 10 },
+ { manufacturer: "Corbin", format_name: "CO88", first_pin_inch: 0.250, last_pin_inch: 1.030, pin_increment_inch: 0.156, pin_num: 6, pin_width_inch: 0.047, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.343, deepest_depth_inch: 0.217, depth_step_inch: 0.014, min_depth_ind: 1, max_depth_ind: 10 },
+ { manufacturer: "Lockwood", format_name: "LW4", first_pin_inch: 0.245, last_pin_inch: 0.870, pin_increment_inch: 0.1562, pin_num: 5, pin_width_inch: 0.031, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.344, deepest_depth_inch: 0.203, depth_step_inch: 0.014, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "Lockwood", format_name: "LW5", first_pin_inch: 0.245, last_pin_inch: 1.0262, pin_increment_inch: 0.1562, pin_num: 6, pin_width_inch: 0.031, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.344, deepest_depth_inch: 0.203, depth_step_inch: 0.014, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "National", format_name: "NA12", first_pin_inch: 0.150, last_pin_inch: 0.710, pin_increment_inch: 0.140, pin_num: 5, pin_width_inch: 0.039, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.270, deepest_depth_inch: 0.157, depth_step_inch: 0.013, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "National", format_name: "NA25", first_pin_inch: 0.250, last_pin_inch: 0.874, pin_increment_inch: 0.156, pin_num: 5, pin_width_inch: 0.039, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.304, deepest_depth_inch: 0.191, depth_step_inch: 0.012, min_depth_ind: 0, max_depth_ind: 9 },
+ { manufacturer: "Russwin", format_name: "RU45", first_pin_inch: 0.250, last_pin_inch: 1.030, pin_increment_inch: 0.156, pin_num: 6, pin_width_inch: 0.053, elbow_inch: 0.1, drill_angle: 90, uncut_depth_inch: 0.343, deepest_depth_inch: 0.203, depth_step_inch: 0.028, min_depth_ind: 1, max_depth_ind: 6 },
+ { manufacturer: "Weiser", format_name: "WR3", first_pin_inch: 0.237, last_pin_inch: 0.861, pin_increment_inch: 0.156, pin_num: 5, pin_width_inch: 0.090, elbow_inch: 0.150, drill_angle: 90, uncut_depth_inch: 0.315, deepest_depth_inch: 0.153, depth_step_inch: 0.018, min_depth_ind: 0, max_depth_ind: 10 }
+];
+
+// ==========================================
+// 2. SCAD GENERÁLÁS (TEMPLATE ALAPÚ)
+// ==========================================
+function generateSCAD(keyCodes, format) {
+ var bits = [];
+ for(var i=0; i