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 0) msgTimer--; + if (msgTimer === 0) statusMsg = ""; + + var format = KEY_DB[currentKeyIdx]; + + // --- BEMENET KEZELÉS --- + if (mode === MODE_MENU) { + if (keyboard.getPrevPress(true)) mode = MODE_EDIT; + if (keyboard.getNextPress(true)) { + menuCursor++; + if (menuCursor >= menuItems.length) menuCursor = 0; + } + + if (keyboard.getSelPress(true)) { + if (menuCursor === 0) { // SAVE + try { + var saveArr = code.slice(0, format.pin_num); + saveArr.push(currentKeyIdx); + var csvData = saveArr.join(","); + storage.write(CODE_FILE, csvData, "write"); + var scadContent = generateSCAD(code, format); + storage.write(SCAD_FILE, scadContent, "write"); + statusMsg = "Saved!"; + } catch(e) { statusMsg = "Err"; } + msgTimer = 30; mode = MODE_EDIT; + } + else if (menuCursor === 1) { // LOAD + try { + var raw = storage.read(CODE_FILE); + if (raw) { + var str = String(raw).trim(); + var parts = str.split(","); + if (parts.length > 1) { + var savedIdx = parseInt(parts[parts.length-1]); + if (!isNaN(savedIdx) && savedIdx < KEY_DB.length) { + currentKeyIdx = savedIdx; + format = KEY_DB[currentKeyIdx]; + initCodeForFormat(currentKeyIdx); + parts.pop(); + } + } + for(var i=0; i= KEY_DB.length) currentKeyIdx = 0; + initCodeForFormat(currentKeyIdx); + + // Rittal Auto-Set + if (KEY_DB[currentKeyIdx].manufacturer === "Rittal") { + code[0] = 3; code[1] = 5; code[2] = 2; code[3] = 4; + statusMsg = "Auto: Rittal 3524"; + } else { + statusMsg = "Type: " + KEY_DB[currentKeyIdx].format_name; + } + msgTimer = 30; + } + else if (menuCursor === 3) { break; } + } + } + else { + // *** EDIT *** + if (keyboard.getPrevPress(true)) { mode = MODE_MENU; menuCursor = 0; } + if (keyboard.getSelPress(true)) { + cursor++; + if (cursor >= format.pin_num) cursor = 0; + } + if (keyboard.getNextPress(true)) { + code[cursor]++; + if (code[cursor] > format.max_depth_ind) code[cursor] = format.min_depth_ind; + } + } + + // --- RAJZOLÁS --- + sprite.fill(bg_color); + sprite.setTextColor(fg_color, bg_color); + + sprite.setTextSize(1); + sprite.drawText(format.manufacturer + " " + format.format_name, 2, 2); + + // --- SKÁLÁZÁS --- + var shoulderX = 0; + var spineY = dh - 20; // Gerinc alul + + var maxDisplayWidth = dw; + var keyTotalLenInch = format.last_pin_inch + format.elbow_inch; + var ppi = maxDisplayWidth / (keyTotalLenInch + 0.1); + + var bladeHeightPx = format.uncut_depth_inch * ppi; + var topY = spineY - bladeHeightPx; + + // Gerinc és Váll + var keyEndPx = keyTotalLenInch * ppi; + sprite.drawLine(shoulderX, spineY, Math.round(keyEndPx), spineY, fg_color); + sprite.drawLine(shoulderX, spineY, shoulderX, Math.round(topY), fg_color); + + var lastY = topY; + + // --- MARÁS SZIMULÁCIÓ --- + for (var x = 0; x <= keyEndPx; x++) { + var maxY = topY; + + for (var i = 0; i < format.pin_num; i++) { + var pinCenterInch = format.first_pin_inch + (i * format.pin_increment_inch); + var pinCenterPx = pinCenterInch * ppi; + + var depthInd = code[i] - format.min_depth_ind; + var cutDepthInch = depthInd * format.depth_step_inch; + var cutDepthPx = cutDepthInch * ppi; + var cutBottomY = topY + cutDepthPx; + + var halfFlatW = (format.pin_width_inch * ppi) / 2; + var dist = Math.abs(x - pinCenterPx); + + var thisY = topY; + + if (dist <= halfFlatW) { + thisY = cutBottomY; + } else { + var rise = dist - halfFlatW; + thisY = cutBottomY - rise; + } + + if (thisY > maxY) maxY = thisY; + } + + if (maxY > spineY) maxY = spineY; + + if (x > 0) { + sprite.drawLine(x - 1, Math.round(lastY), x, Math.round(maxY), fg_color); + } + lastY = maxY; + } + + // Tip lezárása + sprite.drawLine(Math.round(keyEndPx), Math.round(lastY), Math.round(keyEndPx), spineY, fg_color); + + // Számok kiírása (JAVÍTVA: Soha nem megy ki a képből) + sprite.setTextSize(2); + for(var i=0; i + +# M5 stick KEY Copier tool +I wrote some code in Bruce JavaScript. The idea came from the code running on Flipper Zero! + +### The application allows you to copy and create keys using the screen! +You can save and restore the saved key! + +# important !!! +# The application is optimized for the M5 stick display! diff --git a/examples/key copier/keycopier.js b/examples/key copier/keycopier.js new file mode 100644 index 0000000..4bba7b7 --- /dev/null +++ b/examples/key copier/keycopier.js @@ -0,0 +1,417 @@ +var display = require('display'); +var keyboard = require('keyboard'); +var storage = require('storage'); + +// --- OpenSCAD KÓD GENERÁLÓ FÜGGVÉNY (KWIKSET KW1 5-PIN IMPERIAL) --- +function generateSCADScript(keyCodes) { + + var PIN_COUNT = 5; + var shoulder = 0.247; + var pin_spacing = 0.150; + var depth_inc = 0.023; + + var convertedBits = []; + for (var i = 0; i < PIN_COUNT; i++) { + var bitValue = Math.max(0, keyCodes[i] - 1); + convertedBits.push(bitValue); + } + + var SCAD = ""; + + SCAD += "// Generated for 3D Printing by KeyCopier\n"; + SCAD += "// Key code: kw1(" + JSON.stringify(convertedBits) + ")\n"; + SCAD += "// NOTE: Using Kwikset KW1 5-PIN IMPERIAL standards.\n"; + SCAD += "\n"; + + SCAD += "function mm(i) = i*25.4;\n"; + + // A modulok definíciója + SCAD += "module rounded(size, r) {\n"; + SCAD += " union() {\n"; + SCAD += " translate([r, 0, 0]) cube([size[0]-2*r, size[1], size[2]]);\n"; + SCAD += " translate([0, r, 0]) cube([size[0], size[1]-2*r, size[2]]);\n"; + SCAD += " translate([r, r, 0]) cylinder(h=size[2], r=r);\n"; + SCAD += " translate([size[0]-r, r, 0]) cylinder(h=size[2], r=r);\n"; + SCAD += " translate([r, size[1]-r, 0]) cylinder(h=size[2], r=r);\n"; + SCAD += " translate([size[0]-r, size[1]-r, 0]) cylinder(h=size[2], r=r);\n"; + SCAD += " }\n"; + SCAD += "}\n"; + + SCAD += "module bit() {\n"; + SCAD += " w = mm(1/4);\n"; + SCAD += " difference() {\n"; + SCAD += " translate([-w/2, 0, 0]) cube([w, mm(1), w]);\n"; + SCAD += " translate([-mm(5/128), 0, 0]) rotate([0, 0, 135]) cube([w, w, w]);\n"; + SCAD += " translate([mm(5/128), 0, 0]) rotate([0, 0, -45]) cube([w, w, w]);\n"; + SCAD += " }\n"; + SCAD += "}\n"; + + SCAD += "module kw1(bits) {\n"; + SCAD += " thickness = mm(0.080);\n"; + SCAD += " length = mm(1.500);\n"; + SCAD += " width = mm(.337);\n"; + SCAD += " shoulder = mm(" + shoulder.toFixed(3) + ");\n"; + SCAD += " pin_spacing = mm(" + pin_spacing.toFixed(3) + ");\n"; + SCAD += " depth_inc = mm(" + depth_inc.toFixed(3) + ");\n"; + SCAD += " fudge = 0.5;\n"; + SCAD += " h_l = mm(1); h_w = mm(1); h_d = mm(1/16);\n"; + + SCAD += " difference() {\n"; + SCAD += " // blade and key handle\n"; + SCAD += " union() {\n"; + SCAD += " translate([-h_l, -h_w/2 + width/2, 0]) rounded([h_l, h_w, thickness], mm(1/4));\n"; + SCAD += " // cut a little off the tip to avoid going too long\n"; + SCAD += " cube([length - mm(1/64), width, thickness]);\n"; + SCAD += " }\n"; + SCAD += " \n"; + SCAD += " // chamfer the tip\n"; + SCAD += " translate([length, mm(1/8), 0]) {\n"; + SCAD += " rotate([0, 0, 45]) cube([10, 10, thickness]);\n"; + SCAD += " rotate([0, 0, 225]) cube([10, 10, thickness]);\n"; + SCAD += " }\n"; + SCAD += " \n"; + SCAD += " // put in a hole for keychain use\n"; + SCAD += " translate([-h_l + mm(3/16), width/2, 0]) cylinder(h=thickness, r=mm(1/8));\n"; + SCAD += " \n"; + SCAD += " // cut the channels in the key. designed more for printability than accuracy\n"; + SCAD += " union() {\n"; + SCAD += " translate([-h_d, mm(.105), mm(.025)]) rotate([225, 0, 0]) cube([length + h_d, width, width]);\n"; + SCAD += " translate([-h_d, mm(.105), mm(.05)]) rotate([260, 0, 0]) cube([length + h_d, thickness/2, mm(1/32)]);\n"; + SCAD += " translate([-h_d, mm(.105), 0]) cube([length + h_d, mm(7/128), mm(.05)]);\n"; + SCAD += " translate([-h_d, mm(.105) + mm(7/128), mm(.05)]) rotate([225, 0, 0]) cube([length + h_d, mm(3/64), thickness]);\n"; + SCAD += " }\n"; + SCAD += " \n"; + SCAD += " translate([-h_d, width - mm(9/64), mm(.043)]) {\n"; + SCAD += " cube([length + h_d, width - (width - mm(10/64)), thickness]);\n"; + SCAD += " rotate([50, 0, 0]) cube([length + h_d, width, thickness]);\n"; + SCAD += " }\n"; + SCAD += " \n"; + SCAD += " union() {\n"; + SCAD += " translate([-h_d, mm(0.015), mm(.03)]) cube([length + h_d, mm(15/256), thickness]);\n"; + SCAD += " 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)]);\n"; + SCAD += " }\n"; + SCAD += " \n"; + SCAD += " // Do the actual bitting\n"; + SCAD += " for (b = [0:4]) {\n"; + SCAD += " translate([shoulder + fudge + b*pin_spacing, width - mm(.008) - bits[b]*depth_inc - fudge, 0]) bit();\n"; + SCAD += " }\n"; + SCAD += " }\n"; + SCAD += "}\n"; + + SCAD += "kw1(" + JSON.stringify(convertedBits) + ");\n"; + + return SCAD; +} +// --- FŐ PROGRAM --- +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]; // 6 hely, de csak 5-öt használunk + var maxDepth = 7; + + // --- MENTÉS HELYE --- + var CODE_FILE_PATH = "/KeyCopier/key_copier.code"; + var SCAD_FILE_PATH = "/KeyCopier/key_model.scad"; + + var showMenu = false; + var menuCursor = 0; + var menuItems = ["Save", "Load", "Exit"]; + + var statusMsg = ""; + var msgTimer = 0; + + keyboard.setLongPress(true); + + // --- GRAFIKAI PARAMÉTEREK (5 PIN VIZUALIZÁCIÓ) --- + // Skálázáshoz használt valós értékek (mm) + var PIN_COUNT_REAL = 6; + var DEPTH_INC_REAL = 0.58; + var PIN_SPACING_REAL = 3.81; + var FIRST_PIN_START_X = 6.27; + + // Teljes hossz számítása a skálázáshoz + var VISUAL_TOTAL_REAL_MM = FIRST_PIN_START_X + (PIN_COUNT_REAL - 1) * PIN_SPACING_REAL + PIN_SPACING_REAL; + + var TANGENT = 0.84; + var PIN_HALF_WIDTH = 3; + var VISUAL_SCALE_FACTOR = 8; + var DEPTH_STEP = Math.round(DEPTH_INC_REAL * VISUAL_SCALE_FACTOR); + + var TOP_Y = 55; + var keySpineY = TOP_Y + 77; // Vastagság: 77 pixel + var numY = 25; + var availableWidth = dw - 20; + var scaleRatio = availableWidth / VISUAL_TOTAL_REAL_MM; + + // A Tip rajzolásához szükséges fix pozíciók + var X_GERINC_END_PX = 220; + var X_TIP_END_PX = 240; + var Y_TIP_OFFSET = 35; + var tipStartY = keySpineY - Y_TIP_OFFSET; + + while (true) { + + if (msgTimer > 0) msgTimer--; + if (msgTimer === 0) statusMsg = ""; + + // --- BEMENET --- + + if (showMenu) { + 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 *** + try { + var csvData = code.slice(0, 5).join(","); + var csvSuccess = storage.write(CODE_FILE_PATH, csvData, "write"); + + var scadData = generateSCADScript(code); + var scadSuccess = storage.write(SCAD_FILE_PATH, scadData, "write"); + + if (csvSuccess && scadSuccess) statusMsg = "Saved & SCAD!"; + else statusMsg = "Save Error"; + } catch (e) { statusMsg = "Sys Err"; } + msgTimer = 30; + showMenu = false; + } + else if (menuCursor === 1) { + // *** LOAD (JAVÍTVA) *** + var raw = undefined; + var loaded = false; + try { raw = storage.read(CODE_FILE_PATH); } catch (e) { statusMsg = "IO Err"; } + + if (raw !== undefined && raw !== null) { + try { + var str = String(raw).trim(); + var lines = str.split("\n"); + if (lines.length > 0) str = lines[lines.length-1]; + 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); + } + + // JAVÍTÁS: For ciklus használata spread operátor helyett + if (tempCode.length >= 5) { + var startIdx = tempCode.length - 5; + for (var i = 0; i < 5; i++) { + code[i] = tempCode[startIdx + i]; + } + 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; } + } + + } else { + // *** SZERKESZTŐ (5 PIN) *** + if (keyboard.getPrevPress(true)) { + showMenu = true; + menuCursor = 0; + } + if (keyboard.getSelPress(true)) { + cursor++; + if (cursor > 4) cursor = 0; // Csak 5 pin (0-4) + } + 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); + + // Számértékek (5 db) + sprite.setTextSize(2); + for(var i=0; i<5; i++) { + var pinRealX = FIRST_PIN_START_X + (i * PIN_SPACING_REAL); + var pinCenterX = 10 + Math.round(pinRealX * scaleRatio); + + var textStr = code[i].toString(); + + if (i === cursor && !showMenu) { + textStr = "[" + textStr + "]"; + sprite.drawText(textStr, pinCenterX - 14, numY); + } else { + sprite.drawText(textStr, pinCenterX - 5, numY); + } + } + + // --- KERET RAJZOLÁSA --- + + // 1. Vízszintes alsó gerinc 220 px-ig + sprite.drawLine(0, keySpineY, X_GERINC_END_PX, keySpineY, fg_color); + // 2. Függőleges nyak + sprite.drawLine(0, keySpineY, 0, TOP_Y, fg_color); + + // 3. FERDE KULCS VÉG (TIP) - Jobbra, felfelé + sprite.drawLine(X_GERINC_END_PX, keySpineY, X_TIP_END_PX, tipStartY, fg_color); + + // 4. A kulcs tetejének lezárása a Tip felett + sprite.drawLine(X_GERINC_END_PX, TOP_Y, X_TIP_END_PX, TOP_Y, fg_color); + + // --- KULCS PROFIL (5 PIN) --- + var startDrawX = 10; // X eltolás a rajzoláshoz + + for(var j=0; j<5; j++) { + var pinRealX = FIRST_PIN_START_X + (j * PIN_SPACING_REAL); + var cx = startDrawX + Math.round(pinRealX * scaleRatio); + + var currentDepthPx = (code[j] - 1) * DEPTH_STEP; + var bottomY = TOP_Y + currentDepthPx; + + // Pin alja + sprite.drawLine(cx - PIN_HALF_WIDTH, bottomY, cx + PIN_HALF_WIDTH, bottomY, fg_color); + + // Bal oldali kapcsolat (Előző pinhez vagy elejéhez) + var prevDepthPx = 0; + var prevCx = 0; + if (j > 0) { + prevDepthPx = (code[j-1] - 1) * DEPTH_STEP; + var prevPinRealX = FIRST_PIN_START_X + (j-1) * PIN_SPACING_REAL; + prevCx = startDrawX + Math.round(prevPinRealX * scaleRatio); + } + + var myLeftWidth = PIN_HALF_WIDTH + (currentDepthPx * TANGENT); + var prevRightWidth = PIN_HALF_WIDTH + (prevDepthPx * TANGENT); + var distToPrev = (j === 0) ? 999 : (cx - prevCx); + + if (j === 0) { + // Első pin eleje + 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 { + // Csatlakozás az előző pinhez + 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 peakY = bottomY - ((peakDistX - PIN_HALF_WIDTH) / TANGENT); + 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 prevSlopeTopX = prevCx + prevRightWidth; + sprite.drawLine(prevSlopeTopX, TOP_Y, slopeTopX, TOP_Y, fg_color); + } + } + + // Jobb oldali kapcsolat (Következő pinhez VAGY Tiphez) + var myRightWidth = PIN_HALF_WIDTH + (currentDepthPx * TANGENT); + + if (j === 4) { + // ** UTOLSÓ PIN (5.) LEZÁRÁSA ** + // Ez volt a vizuális hiba forrása. Most fixen a gerinc végéig húzzuk. + var slopeEndX = cx + myRightWidth; + + // Felfelé húzzuk a vonalat a kulcs tetejéig (TOP_Y) + sprite.drawLine(cx + PIN_HALF_WIDTH, bottomY, slopeEndX, TOP_Y, fg_color); + + // Onnan pedig vízszintesen a Tip kezdetéig (X_GERINC_END_PX) + // (Ha a slopeEndX túlmegy, akkor nem rajzolunk visszafelé) + if (slopeEndX < X_GERINC_END_PX) { + sprite.drawLine(slopeEndX, TOP_Y, X_GERINC_END_PX, TOP_Y, fg_color); + } + + } else { + // Normál köztes pin kapcsolat (j < 4) + var nextDepthPx = (code[j+1] - 1) * DEPTH_STEP; + var nextPinRealX = FIRST_PIN_START_X + (j+1) * PIN_SPACING_REAL; + var nextCx = startDrawX + Math.round(nextPinRealX * scaleRatio); + + var nextLeftWidth = PIN_HALF_WIDTH + (nextDepthPx * TANGENT); + var distToNext = nextCx - cx; + + 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 peakY = bottomY - ((myPeakDistX - PIN_HALF_WIDTH) / TANGENT); + 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/resistor-calulator/resistor.js b/examples/resistor-calulator/resistor.js new file mode 100644 index 0000000..6b7fd1f --- /dev/null +++ b/examples/resistor-calulator/resistor.js @@ -0,0 +1,282 @@ +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 (R, G, B formátum 0-255) --- + var C_BLACK = display.color(0, 0, 0); + var C_BROWN = display.color(165, 42, 42); + var C_RED = display.color(255, 0, 0); + var C_ORANGE = display.color(255, 165, 0); + var C_YELLOW = display.color(255, 255, 0); + var C_GREEN = display.color(0, 255, 0); + var C_BLUE = display.color(0, 0, 255); + var C_VIOLET = display.color(255, 0, 255); + var C_GRAY = display.color(128, 128, 128); + var C_WHITE = display.color(255, 255, 255); + + var C_GOLD = display.color(255, 215, 0); + var C_SILVER = display.color(192, 192, 192); + + var C_BG = C_BLACK; + var C_TXT = C_WHITE; + + // Sáv színek (0-9) + var bandColors = [ + C_BLACK, C_BROWN, C_RED, C_ORANGE, C_YELLOW, + C_GREEN, C_BLUE, C_VIOLET, C_GRAY, C_WHITE + ]; + + var txtColors = [ + C_WHITE, C_WHITE, C_WHITE, C_BLACK, C_BLACK, + C_BLACK, C_WHITE, C_WHITE, C_WHITE, C_BLACK + ]; + + var multColors = bandColors.concat([C_GOLD, C_SILVER]); + + var multLabels = [ + "x1", "x10", "x100", "x1K", "x10K", + "x100K", "x1M", "x10M", "x100M", "x1G", + "x0.1", "x0.01" + ]; + + var tolColors = [C_BROWN, C_RED, C_GOLD, C_SILVER]; + var tolValues = ["1%", "2%", "5%", "10%"]; + + // --- Adatok --- + var band1 = 1; + var band2 = 0; + var mult = 2; + var tol = 2; + + var focus = 0; + var FILE_PATH = "/data/resistor.txt"; + + // Betöltés + try { + if (storage.exists(FILE_PATH)) { + var raw = storage.read(FILE_PATH); + if (raw) { + var parts = String(raw).trim().split(","); + if (parts.length === 4) { + band1 = parseInt(parts[0]); + band2 = parseInt(parts[1]); + mult = parseInt(parts[2]); + tol = parseInt(parts[3]); + } + } + } + } catch(e) {} + + keyboard.setLongPress(true); + + // --- FÜGGVÉNYEK --- + + function formatResistor(val) { + if (val < 1) return val.toFixed(2); + if (val < 10) return val.toFixed(1); + if (val >= 1000000000) return (val / 1000000000).toFixed(2) + "G"; + if (val >= 1000000) return (val / 1000000).toFixed(2) + "M"; + if (val >= 1000) return (val / 1000).toFixed(2) + "K"; + return val.toFixed(0); + } + + function calculate() { + var base = (band1 * 10) + band2; + var factor = 1; + if (mult < 10) factor = Math.pow(10, mult); + else if (mult === 10) factor = 0.1; + else if (mult === 11) factor = 0.01; + return base * factor; + } + + function saveState() { + try { + var data = band1 + "," + band2 + "," + mult + "," + tol; + storage.write(FILE_PATH, data, "write"); + } catch(e) {} + } + + while (true) { + + // --- GOMBKEZELÉS --- + var btnEsc = keyboard.getPrevPress(true); + var btnNext = keyboard.getNextPress(true); + var btnSel = keyboard.getSelPress(true); + + if (btnEsc && btnNext) { + return; + } + + if (btnEsc && !btnNext) { + focus++; + if (focus > 3) focus = 0; + } + + if (btnSel || (btnNext && !btnEsc)) { + if (focus === 0) { + band1++; if (band1 > 9) band1 = 0; + } + else if (focus === 1) { + band2++; if (band2 > 9) band2 = 0; + } + else if (focus === 2) { + mult++; if (mult > 11) mult = 0; + } + else if (focus === 3) { + tol++; if (tol > 3) tol = 0; + } + saveState(); + } + + // --- RAJZOLÁS --- + sprite.fill(C_BG); + sprite.setTextSize(1); + + // ========================================== + // 1. BAL OLDALI OSZLOPOK (Mindig látszik a keret) + // ========================================== + var colH = 13; + var startY = 3; + var col1X = 2; + var col2X = 24; + var colW = 20; + + for (var i = 0; i <= 9; i++) { + var y = startY + (i * colH); + var c = bandColors[i]; + var numCol = txtColors[i]; + + // -- 1. OSZLOP -- + if (band1 === i) { + // Ha ez az oszlop aktív, akkor FEHÉR, ha nem, akkor SZÜRKE a keret + var boxColor = (focus === 0) ? C_WHITE : C_GRAY; + sprite.drawRect(col1X - 2, y, colW + 4, colH, boxColor); + } + + sprite.drawFillRect(col1X, y + 1, colW, colH - 2, c); + sprite.setTextColor(numCol, c); + sprite.drawText(i.toString(), col1X + 7, y + 2); + + // -- 2. OSZLOP -- + if (band2 === i) { + var boxColor = (focus === 1) ? C_WHITE : C_GRAY; + sprite.drawRect(col2X - 2, y, colW + 4, colH, boxColor); + } + + sprite.drawFillRect(col2X, y + 1, colW, colH - 2, c); + sprite.setTextColor(numCol, c); + sprite.drawText(i.toString(), col2X + 7, y + 2); + } + + // ========================================== + // 2. SZORZÓ OSZLOP (Mindig látszik a keret) + // ========================================== + var multBoxX = 225; + var multTxtRightX = 222; + var multH = 11; + + for (var m = 0; m <= 11; m++) { + var my = startY + (m * multH); + var mc = multColors[m]; + var txt = multLabels[m]; + + // Aktív az, amit éppen szerkesztünk (focus===2), de a keret mindig kirajzolódik + var isSelected = (mult === m); + var isFocused = (focus === 2); + + // Szöveg szín + // Ha épp ezen a soron vagyunk (fókusz), akkor PIROS, amúgy alap + var txtColor = (isSelected && isFocused) ? C_RED : C_TXT; + + sprite.setTextColor(txtColor, C_BG); + sprite.drawText(txt, multTxtRightX - (txt.length * 6), my + 1); + + if (isSelected) { + var boxColor = isFocused ? C_WHITE : C_GRAY; + sprite.drawRect(multBoxX - 1, my, 14, multH, boxColor); + } + + sprite.drawFillRect(multBoxX, my + 1, 12, multH - 2, mc); + } + + // ========================================== + // 3. ELLENÁLLÁS TEST + // ========================================== + var centerX = 135; + var resY = 40; + var bodyColor = display.color(222, 184, 135); + + sprite.drawLine(centerX - 55, resY + 10, centerX + 55, resY + 10, C_SILVER); + sprite.drawFillRect(centerX - 40, resY, 80, 20, bodyColor); + + sprite.drawFillRect(centerX - 30, resY, 10, 20, bandColors[band1]); + sprite.drawFillRect(centerX - 10, resY, 10, 20, bandColors[band2]); + sprite.drawFillRect(centerX + 10, resY, 10, 20, multColors[mult]); + sprite.drawFillRect(centerX + 30, resY, 10, 20, tolColors[tol]); + + // ========================================== + // 4. EREDMÉNY DOBOZ + // ========================================== + var ohmVal = calculate(); + var valStr = formatResistor(ohmVal); + + var boxX = 61; + var boxW = 130; + var boxCenterX = boxX + (boxW / 2); + + sprite.drawFillRect(boxX, 75, boxW, 30, C_BLUE); + sprite.setTextSize(2); + sprite.setTextColor(C_WHITE, C_BLUE); + + var valTxtW = valStr.length * 12; + sprite.drawText(valStr, boxCenterX - (valTxtW/2) - 10, 83); + sprite.setTextSize(1); + sprite.drawText("ohm", boxCenterX + (valTxtW/2), 90); + + // ========================================== + // 5. TOLERANCIA (Mindig látszik a keret) + // ========================================== + var tolY = 118; + + var tolItemW = 19; + var tolStart = 85; + var tolH = 15; + var tolSelH = 19; + + for (var t = 0; t < 4; t++) { + var tx = tolStart + (t * (tolItemW + 5)); + var tc = tolColors[t]; + var tVal = tolValues[t]; + + // Keret kirajzolása, ha ez a kiválasztott érték + if (tol === t) { + var boxColor = (focus === 3) ? C_WHITE : C_GRAY; + sprite.drawRect(tx - 2, tolY - 2, tolItemW + 4, tolSelH, boxColor); + } + + sprite.drawFillRect(tx, tolY, tolItemW, tolH, tc); + + var tTxtCol = C_WHITE; + if (t === 2 || t === 3) tTxtCol = C_BLACK; + + sprite.setTextColor(tTxtCol, tc); + sprite.drawText(tVal, tx + 1, tolY + 3); + } + + // Fejléc + sprite.setTextColor(C_WHITE, C_BG); + sprite.drawText("R-Calc", centerX - 20, 5); + + sprite.pushSprite(); + try { delay(50); } catch(e) {} + } +} + +main();