From d2cfce2a0e3979d2c20029686f7ad342df4bd7a8 Mon Sep 17 00:00:00 2001 From: Renan LE CARO Date: Sat, 15 Feb 2025 19:21:00 +0100 Subject: [PATCH] Initial commit I cleared the project history to start fresh on the public version --- .gitignore | 16 + Readme.md | 84 + app/.gitignore | 1 + app/build.gradle.kts | 83 + app/proguard-rules.pro | 21 + app/src/main/AndroidManifest.xml | 26 + app/src/main/assets/game.js | 2200 ++++++ app/src/main/assets/icon.svg | 146 + app/src/main/assets/index.html | 21 + app/src/main/assets/levels.js | 6458 +++++++++++++++++ app/src/main/assets/privacy.html | 39 + app/src/main/assets/style.css | 227 + .../java/me/lecaro/breakout/MainActivity.kt | 33 + app/src/main/res/drawable/icon.xml | 195 + app/src/main/res/values/strings.xml | 3 + app/src/main/res/xml/backup_rules.xml | 13 + .../main/res/xml/data_extraction_rules.xml | 19 + build.gradle.kts | 5 + cover.png | Bin 0 -> 44618 bytes deploy.sh | 19 + editclient.css | 29 + editclient.js | 274 + editserver.js | 50 + gradle.properties | 23 + gradle/libs.versions.toml | 31 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 + gradlew.bat | 89 + icon.png | Bin 0 -> 12547 bytes package-lock.json | 1079 +++ package.json | 16 + patterns.html | 163 + settings.gradle.kts | 24 + 34 files changed, 11578 insertions(+) create mode 100644 .gitignore create mode 100644 Readme.md create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/assets/game.js create mode 100644 app/src/main/assets/icon.svg create mode 100644 app/src/main/assets/index.html create mode 100644 app/src/main/assets/levels.js create mode 100644 app/src/main/assets/privacy.html create mode 100644 app/src/main/assets/style.css create mode 100644 app/src/main/java/me/lecaro/breakout/MainActivity.kt create mode 100644 app/src/main/res/drawable/icon.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 build.gradle.kts create mode 100644 cover.png create mode 100755 deploy.sh create mode 100644 editclient.css create mode 100644 editclient.js create mode 100644 editserver.js create mode 100644 gradle.properties create mode 100644 gradle/libs.versions.toml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 icon.png create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 patterns.html create mode 100644 settings.gradle.kts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..96519fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties +node_modules \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..1805ac1 --- /dev/null +++ b/Readme.md @@ -0,0 +1,84 @@ +# Breakout 71 + +A simple, single player, challenging arcade breakout game. +The goal is to break all the bricks of 7 levels, which catching as many coins as you can. +You have only one life, if you lose your ball you'll go back to the start +At the end of each level, you get to select an upgrade. + +[Play now](https://breakout.lecaro.me/) - +[Google Play](https://play.google.com/store/apps/details?id=me.lecaro.breakout) - +[itch.io](https://renanlecaro.itch.io/breakout71) - +[GitLab](https://gitlab.com/lecarore/breakout71) + +## TODO + +- show total score on end screen (score added to total) +- show stats on end screen compared to other runs +- handle back bouton in menu +- more levels : famous simple games, letters, fruits, animals +- perk : elastic between balls +- perk : wrap left / right +- perk : twice as many coins after a wall bounce, twice as little otherwise +- perk : fusion reactor (gather coins in one spot to triple their value) +- perk : missing makes you loose all score of level, but otherwise multiplier goes up after each breaking +- perk : n/10 of the broken bricks respawn when the ball comes back +- perk : bricks take twice as many hits but drop 50% more coins +- perk : wind (puck positions adds force to coins and balls) + +## maybe + +- Make a small mp4 of game which can be shown on gameover and shared. https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder +- perk : soft reset, cut combo in half instead of zero +- perk : missile goes when you catch coin +- perk : missile goes when you break a brick +- when game resumes near bottom, be unvulnerable for .5s ? , once per level +- accelerometer controls coins and balls +- mouvement relatif du puck +- balls should collide with each other +- randomize coins gravity a bit, to make fall more appealing +- apply global curve / brightness to canvas when things blow, or just always to make neon effect better +- perk: bricks attract coins +- perk : puck bounce +1 combo, hit nothing resets +- manifest for PWA (android and apple) +- publish on fdroid +- nerf the hot start a bit +- brick parts fly around with trailing effect ? +- trailing white lines behind ball +- some 3d ish effect ? +- shrink brick at beaking time ? +- perk : multiple hits on the same brick (show remaining resistance as number) +- particle effect around ball when loosing some combo (looks bad) +- Make bricks shadow the light ? using a "fill path" in screen mode, with a gradient background...would get very laggy, maybe just for the ball +- keyboard support +- perk : bricks attract ball +- perk : replay last level (remove score, restores lives if any, and rebuild ) +- perk: breaking bricks stains neighbours +- perk: extra kick after bouncing on puck +- perk: transparent coins +- perk: coins of different colors repulse +- 2x coins when ball goes downward ? +- engine: Offline mode web for iphone +- engine: webgl rendering (not with sdf though, that's super slow) + + + +## Credits + +I pulled many background patterns from https://pattern.monster/ +They are displayed in [patterns.html](patterns.html) for easy inclusion. +Some of the sound generating code was written by ChatGPT, and heavily +adapted to my usage over time. Some of the pixel art is taken from google +image search. I hope to replace it by my own over time. + +[Heart](https://www.youtube.com/watch?v=gdWiTfzXb1g) +[Sonic](https://www.deviantart.com/graystripe2000/art/Pixel-art-16x16-Sonic-936384096) +[Finn](https://at.pinterest.com/pin/finn-the-human-pixel-art--140806230775275/) +[Mushroom](https://pixelartmaker.com/art/cce4295a92035ea) + +## APK version + +The web app is around 50kb, compressed down to 10kb with gzip +I wanted an APK to start in fullscreen and be able to list it on fdroid and the play store. + +I stated with an empty view and went to work trimming it down, with the help of that tutorial +https://github.com/fractalwrench/ApkGolf/blob/master/blog/BLOG_POST.md diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..455304b --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,83 @@ +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter +import java.time.ZoneId + +plugins { + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsKotlinAndroid) +} + +android { + namespace = "me.lecaro.breakout" + compileSdk = 34 + + defaultConfig { + applicationId = "me.lecaro.breakout" + minSdk = 21 + targetSdk = 34 +// versionCode = 7 +// versionName = "7.0" + + // Get the current Unix timestamp in seconds + versionCode = (System.currentTimeMillis() / 1000/60).toInt() + // Get the current date as a string + versionName = ZonedDateTime.now(ZoneId.of("CET")) + .format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")) + + + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + vectorDrawables { + useSupportLibrary = true + } + } + + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + signingConfig = signingConfigs.getByName("debug") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { +// compose = true + } + composeOptions { + kotlinCompilerExtensionVersion = "1.5.1" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } +} + +//dependencies { +// +// implementation(libs.androidx.core.ktx) +// implementation(libs.androidx.lifecycle.runtime.ktx) +// implementation(libs.androidx.activity.compose) +// implementation(platform(libs.androidx.compose.bom)) +// implementation(libs.androidx.ui) +// implementation(libs.androidx.ui.graphics) +// implementation(libs.androidx.ui.tooling.preview) +// implementation(libs.androidx.material3) +// testImplementation(libs.junit) +// androidTestImplementation(libs.androidx.junit) +// androidTestImplementation(libs.androidx.espresso.core) +// androidTestImplementation(platform(libs.androidx.compose.bom)) +// androidTestImplementation(libs.androidx.ui.test.junit4) +// debugImplementation(libs.androidx.ui.tooling) +// debugImplementation(libs.androidx.ui.test.manifest) +//} \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..2700098 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/assets/game.js b/app/src/main/assets/game.js new file mode 100644 index 0000000..19c69c3 --- /dev/null +++ b/app/src/main/assets/game.js @@ -0,0 +1,2200 @@ +const MAX_COINS = 400; +const canvas = document.getElementById("game"); +let ctx = canvas.getContext("2d", {alpha: false}); + +let ballSize = 20; +const coinSize = Math.round(ballSize * 0.8); +const puckHeight = ballSize; + +if (allLevels.find(l => l.focus)) { + allLevels = allLevels.filter(l => l.focus) +} +allLevels=allLevels.filter(l=>!l.draft) + + +let runLevels = [] + +let currentLevel = 0; + +const bombSVG = document.createElement('img') +bombSVG.src = 'data:image/svg+xml;base64,' + btoa(` + +`); + + +// Whatever +let puckWidth = 200; +const perks = {}; + +let baseSpeed = 12; // applied to x and y +let combo = 1; + +function baseCombo() { + return 1 + perks.base_combo * 3; +} + +function resetCombo(x, y) { + const prev = combo; + combo = baseCombo(); + if (!levelTime) { + combo += perks.hot_start * 15; + } + if (prev > combo && perks.soft_reset) { + combo += Math.floor((prev - combo) / (1 + perks.soft_reset)) + } + const lost = Math.max(0, prev - combo); + if (lost) { + incrementRunStatistics('combo_resets', 1) + for (let i = 0; i < lost && i < 8; i++) { + setTimeout(() => sounds.comboDecrease(), i * 100); + } + if (typeof x !== "undefined" && typeof y !== "undefined") { + flashes.push({ + type: "text", + text: "-" + lost, + time: levelTime, + color: "red", + x: x, + y: y, + duration: 150, + size: puckHeight, + }); + } + } +} + +function decreaseCombo(by, x, y) { + const prev = combo; + combo = Math.max(baseCombo(), combo - by); + const lost = Math.max(0, prev - combo); + + if (lost) { + sounds.comboDecrease(); + if (typeof x !== "undefined" && typeof y !== "undefined") { + flashes.push({ + type: "text", + text: "-" + lost, + time: levelTime, + color: "red", + x: x, + y: y, + duration: 300, + size: puckHeight, + }); + } + } +} + +let gridSize = 12; + +let running = false, puck = 400; + +let offsetX, gameZoneWidth, gameZoneHeight, brickWidth, needsRender = true; + +const background = document.createElement("img"); +const backgroundCanvas = document.createElement("canvas"); +background.addEventListener("load", () => { + needsRender = true +}) + +const fitSize = () => { + const {width, height} = canvas.getBoundingClientRect(); + canvas.width = width; + canvas.height = height; + backgroundCanvas.width = width; + backgroundCanvas.height = height; + + + gameZoneHeight = isSettingOn("mobile-mode") ? (height * 80) / 100 : height; + const baseWidth = Math.round(Math.min(canvas.width, gameZoneHeight * 0.73)); + brickWidth = Math.floor(baseWidth / gridSize / 2) * 2; + gameZoneWidth = brickWidth * gridSize; + offsetX = Math.floor((canvas.width - gameZoneWidth) / 2); + backgroundCanvas.title = 'resized' + // Ensure puck stays within bounds + setMousePos(puck); + coins = []; + flashes = []; + running = false; + needsRender = true; + putBallsAtPuck(); +}; +window.addEventListener("resize", fitSize); + +function recomputeTargetBaseSpeed() { + baseSpeed = gameZoneWidth / 12 / 10 + currentLevel / 3 + levelTime / (30 * 1000) - perks.slow_down * 2; +} + + +function brickCenterX(index) { + return offsetX + ((index % gridSize) + 0.5) * brickWidth; +} + +function brickCenterY(index) { + return (Math.floor(index / gridSize) + 0.5) * brickWidth; +} + +function getRowCol(index) { + return { + col: index % gridSize, row: Math.floor(index / gridSize), + }; +} + +function getRowColIndex(row, col) { + if (row < 0 || col < 0 || row >= gridSize || col >= gridSize) return -1; + return row * gridSize + col; +} + + +function spawnExplosion(count, x, y, color, duration = 150, size = coinSize) { + if (!!isSettingOn("basic")) return; + for (let i = 0; i < count; i++) { + flashes.push({ + type: "particle", + duration, + time: levelTime, + size, + color, + x: x + ((Math.random() - 0.5) * brickWidth) / 2, + y: y + ((Math.random() - 0.5) * brickWidth) / 2, + vx: (Math.random() - 0.5) * 30, + vy: (Math.random() - 0.5) * 30, + }); + } +} + + +let score = 0; +let scoreStory = []; + +let lastexplosion = 0; +let highScore = parseFloat(localStorage.getItem("breakout-3-hs") || "0"); + +let lastPlayedCoinGrab = 0 + +function addToScore(coin) { + coin.destroyed = true + score += coin.points; + addToTotalScore(coin.points) + if (score > highScore) { + highScore = score; + localStorage.setItem("breakout-3-hs", score); + } + if (!isSettingOn('basic')) { + flashes.push({ + type: "particle", + duration: 100 + Math.random() * 50, + time: levelTime, + size: coinSize / 2, + color: coin.color, + x: coin.previousx, + y: coin.previousy, + vx: (canvas.width - coin.x) / 100, + vy: -coin.y / 100, + ethereal: true, + }) + } + + if (Date.now() - lastPlayedCoinGrab > 16) { + lastPlayedCoinGrab = Date.now() + sounds.coinCatch(coin.x) + } + incrementRunStatistics('caught_coins', coin.points) + +} + +let balls = []; + +function resetBalls() { + const count = 1 + (perks?.multiball || 0); + const perBall = puckWidth / (count + 1); + balls = []; + for (let i = 0; i < count; i++) { + const x = puck - puckWidth / 2 + perBall * (i + 1); + balls.push({ + x, + previousx: x, + y: gameZoneHeight - 1.5 * ballSize, + previousy: gameZoneHeight - 1.5 * ballSize, + vx: Math.random() > 0.5 ? baseSpeed : -baseSpeed, + vy: -baseSpeed, + sx: 0, + sy: 0, + color: currentLevelInfo()?.black_puck ? '#000' : "#FFF", + hitSinceBounce: 0, + piercedSinceBounce: 0, + sparks: 0, + }); + } +} + +function putBallsAtPuck() { + const count = balls.length; + const perBall = puckWidth / (count + 1); + balls.forEach((ball, i) => { + const x = puck - puckWidth / 2 + perBall * (i + 1); + Object.assign(ball, { + x, + previousx: x, + y: gameZoneHeight - 1.5 * ballSize, + previousy: gameZoneHeight - 1.5 * ballSize, + vx: Math.random() > 0.5 ? baseSpeed : -baseSpeed, + vy: -baseSpeed, + sx: 0, + sy: 0, + }); + }); +} + +resetBalls(); +// Default, recomputed at each level load +let bricks = []; +let flashes = []; +let coins = []; +let levelStartScore = 0; +let levelMisses = 0; +let levelSpawnedCoins = 0; + +function getLevelStats() { + const catchRate = (score - levelStartScore) / (levelSpawnedCoins || 1); + let stats = ` + you caught ${score - levelStartScore} coins out of ${levelSpawnedCoins} in ${Math.round(levelTime / 1000)} seconds. + `; + stats += levelMisses ? `You missed ${levelMisses} times. ` : ""; + let text = [stats]; + let repeats = 1; + let choices = 3; + + if (levelTime < 30 * 1000) { + repeats++; + choices++; + text.push("speed bonus: +1 upgrade and choice"); + } else if (levelTime < 60 * 1000) { + choices++; + text.push("speed bonus: +1 choice"); + } + if (catchRate === 1) { + repeats++; + choices++; + text.push("coins bonus: +1 upgrade and choice"); + } else if (catchRate > 0.9) { + choices++; + text.push("coins bonus: +1 choice."); + } + if (levelMisses === 0) { + repeats++; + choices++; + text.push("accuracy bonus: +1 upgrade and choice"); + } else if (levelMisses <= 3) { + choices++; + text.push("accuracy bonus:+1 choice"); + } + + return { + stats, text: text.map(t => '

' + t + '

').join('\n'), repeats, choices, + }; +} + +async function openUpgradesPicker() { + let {text, repeats, choices} = getLevelStats(); + scoreStory.push(`Finished level ${currentLevel + 1} (${currentLevelInfo().name}): ${text}`,); + + while (repeats--) { + const actions = pickRandomUpgrades(choices); + if (!actions.length) break + let textAfterButtons; + if (actions.length < choices) { + textAfterButtons = `

You are running out of upgrades, more will be unlocked when you catch lots of coins.

` + } + const cb = await asyncAlert({ + title: "Pick an upgrade " + (repeats ? "(" + (repeats + 1) + ")" : ""), actions, text, allowClose: false, + textAfterButtons + }); + cb(); + } + resetCombo(); + resetBalls(); +} + +function setLevel(l) { + running = false; + needsRender = true + if (l > 0) { + openUpgradesPicker().then(); + } + currentLevel = l; + + levelTime = 0; + lastTickDown = levelTime; + levelStartScore = score; + levelSpawnedCoins = 0; + levelMisses = 0; + + resetCombo(); + recomputeTargetBaseSpeed(); + resetBalls(); + + const lvl = currentLevelInfo(); + if (lvl.size !== gridSize) { + gridSize = lvl.size; + fitSize(); + } + incrementRunStatistics('lvl_size_' + lvl.size, 1) + incrementRunStatistics('lvl_name_' + lvl.name, 1) + coins = []; + bricks = [...lvl.bricks]; + flashes = []; + + background.src = 'data:image/svg+xml;base64,' + btoa(lvl.svg) + +} + +function currentLevelInfo() { + return runLevels[currentLevel % runLevels.length]; +} + +function reset_perks() { + + for (let u of upgrades) { + perks[u.id] = 0; + } + + const giftable = getPossibleUpgrades().filter(u => u.giftable) + if (!giftable.length) { + debugger + } + + const randomGift = isSettingOn('easy') ? 'slow_down' : giftable[Math.floor(Math.random() * giftable.length)].id; + perks[randomGift] = 1; + return randomGift +} + +const upgrades = [{ + minimumTotalScore: 3000, + id: 'multiball', + giftableAfterTotalScore: 20000, + name: "+1 ball", + max: 3, + help: `Start each level with one more balls.`, +}, { + minimumTotalScore: 5000, + id: 'pierce', + giftableAfterTotalScore: 15000, + name: "Ball pierces bricks", + max: 3, + help: `Pierce through 3 blocks after bouncing on the puck.`, +}, { + minimumTotalScore: 500, + id: 'telekinesis', + giftableAfterTotalScore: 900, + name: "Puck controls ball", + max: 2, + help: `Control the ball's trajectory with the puck.`, +}, { + minimumTotalScore: 0, + extra_levels_minimum_total_score: 250, + id: 'extra_life', + name: "+1 life", + max: 3, + help: `Allows you to survive dropping the ball once.`, +}, { + minimumTotalScore: 20000, + id: 'sapper', + giftableAfterTotalScore: 32000, + name: "Bricks become bombs", + max: 1, + help: `Broken blocks are replaced by bombs.`, +}, { + minimumTotalScore: 100000, + id: 'soft_reset', + name: "Soft reset", + max: 2, + help: `Only loose half your combo when it resets.`, +}, + + { + minimumTotalScore: 30000, + id: 'bigger_explosions', + name: "Bigger explosions", + max: 1, + help: `All bombs have larger area of effect.`, + }, + + { + minimumTotalScore: 2000, + id: 'coin_magnet', + name: "Puck attracts coins", + max: 3, + help: `Coins falling are drawn toward the puck.`, + }, + + { + minimumTotalScore: 7000, + id: 'metamorphosis', + name: "Coins stain bricks", + color_blind_exclude: true, + max: 1, + help: `Coins color the bricks they touch.`, + }, + + { + minimumTotalScore: 6000, + id: 'picky_eater', + giftableAfterTotalScore: 9000, + name: "Single color streak", + color_blind_exclude: true, + max: 1, + help: `Hit bricks of the same color for more coins.`, + }, + + { + minimumTotalScore: 80000, + id: 'pierce_color', + name: "Color pierce", + color_blind_exclude: true, + max: 1, + help: `Colored ball pierces bricks of the same color.`, + }, + + { + minimumTotalScore: 0, + id: 'streak_shots', + giftableAfterTotalScore: 1500, + name: "Single puck hit streak", + max: 1, + help: `Break many bricks at once for more coins.`, + }, + + { + minimumTotalScore: 10000, + id: 'hot_start', + giftableAfterTotalScore: 24000, + name: "Hot start", + max: 3, + help: `Clear the level quickly for more coins.`, + }, + + { + minimumTotalScore: 200, + id: 'sides_are_lava', + giftableAfterTotalScore: 500, + name: "Shoot straight", + max: 1, + help: `Avoid the sides for more coins.`, + }, { + minimumTotalScore: 600, + id: 'top_is_lava', + giftableAfterTotalScore: 1200, + name: "Sky is the limit", + max: 1, + help: `Avoid the top for more coins.`, + }, + + { + minimumTotalScore: 8000, + id: 'catch_all_coins', + giftableAfterTotalScore: 16000, + name: "Compound interest", + max: 3, + help: `Catch all coins with your puck for even more coins.`, + }, { + minimumTotalScore: 0, + extra_levels_minimum_total_score: 6250, + id: 'viscosity', + name: "Slower coins fall", + max: 3, + help: `Coins quickly decelerate and fall more slowly.`, + }, + + { + minimumTotalScore: 0, + extra_levels_minimum_total_score: 750, + id: 'base_combo', + giftableAfterTotalScore: 0, + name: "+3 base combo", + max: 3, + help: `Your combo starts 3 points higher.`, + }, + + { + minimumTotalScore: 0, + extra_levels_minimum_total_score: 25, + id: 'slow_down', + name: "Slower ball", + max: 2, + help: `Slows down the ball.`, + }, { + minimumTotalScore: 65000, + id: 'extra_levels', + name: "+1 level", + max: 3, + help: `Play one more level before game over.`, + }, { + minimumTotalScore: 2500, + id: 'skip_last', + name: "Last brick breaks", + max: 3, + help: `The last brick will self-destruct.`, + }, { + minimumTotalScore: 3600, id: 'smaller_puck', name: "Smaller puck", max: 2, help: `Gives you more control.`, + }, { + minimumTotalScore: 0, + extra_levels_minimum_total_score: 0, + id: 'bigger_puck', + name: "Bigger puck", + max: 2, + help: `Catches more coins.`, + }] + +function computeUpgradeCurrentMaxLevel(u, ts) { + let max = 0 + const setMax = (v) => max = Math.max(max, v) + if (u.max && ts >= u.minimumTotalScore) { + setMax(1) + } + if (u.max > 1) { + if (u.minimumTotalScore) { + setMax(Math.min(u.max, Math.floor(ts / u.minimumTotalScore))) + } else if (u.extra_levels_minimum_total_score) { + setMax(Math.min(u.max, Math.floor(ts / u.extra_levels_minimum_total_score) + 1)) + } + } + + return max +} + +function getPossibleUpgrades() { + const ts = getTotalScore() + return upgrades + .filter(u => !(isSettingOn('color_blind') && u.color_blind_exclude)) + .map(u => ({ + ...u, max: computeUpgradeCurrentMaxLevel(u, ts), giftable: ts >= (u.giftableAfterTotalScore ?? Infinity) + })).filter(u => u.max > 0) +} + +function levelTotalScoreCondition(l, li) { + return li < 8 ? 0 : Math.round(Math.pow(10, 1 + (li + l.size) / 30) * (li)) * 10 +} + +function shuffleLevels(nameToAvoid = null) { + const ts = getTotalScore() + runLevels = allLevels + .filter((l, li) => ts >= levelTotalScoreCondition(l, li)) + .filter(l => l.name !== nameToAvoid || allLevels.length === 1) + .sort(() => Math.random() - 0.5) + .slice(0, 7 + 3) + .sort((a, b) => a.bricks.filter(i => i).length - b.bricks.filter(i => i).length) +} + +function getUpgraderUnlockPoints() { + let list = [] + + upgrades + .filter(u => !(isSettingOn('color_blind') && u.color_blind_exclude)) + .forEach(u => { + if (u.minimumTotalScore) { + list.push({ + threshold: u.minimumTotalScore, + title: 'Unlock: ' + u.name, + help: 'This new perks will be added to the choices offered to you.' + }) + } + if (u.max > 1) { + for (var l = 1; l < u.max; l++) list.push({ + threshold: l * (u.minimumTotalScore || u.extra_levels_minimum_total_score || 0), + title: 'Upgrade: ' + u.name, + help: 'You will be able to take this perk ' + (l + 1) + ' times for greater effect.' + }) + } + if (u.giftableAfterTotalScore) { + list.push({ + threshold: u.giftableAfterTotalScore, + title: 'Start: ' + u.name, + help: u.name + ' will be added to the list of possible starting perks.' + }) + } + + }) + + allLevels.forEach((l, li) => { + list.push({ + threshold: levelTotalScoreCondition(l, li), + title: 'Level: ' + l.name, + help: l.name + ' will be added to the list of possible levels.' + }) + }) + + return list.filter(o => o.threshold).sort((a, b) => a.threshold - b.threshold) +} + + +function pickRandomUpgrades(count) { + + + let list = getPossibleUpgrades() + .sort(() => Math.random() - 0.5) + .filter(u => perks[u.id] < u.max) + .slice(0, count) + .sort((a, b) => a.id > b.id ? 1 : -1) + .map(u => { + + incrementRunStatistics('offered_upgrade.' + u.id, 1) + return { + key: u.id, text: u.name, value: () => { + perks[u.id]++; + incrementRunStatistics('picked_upgrade.' + u.id, 1) + scoreStory.push("Picked upgrade : " + u.name); + }, help: u.help, max: u.max, + + checked: perks[u.id], + + } + }) + + + return list; +} + + +function restart() { + console.log("restart") + // When restarting, we want to avoid restarting with the same level we're on, so we exclude from the next + // run's level list + shuffleLevels(levelTime || score ? currentLevelInfo().name : null); + resetRunStatistics() + score = 0; + scoreStory = []; + const randomGift = reset_perks(); + + incrementRunStatistics('starting_upgrade.' + randomGift, 1) + + setLevel(0); + scoreStory.push(`You started playing with the upgrade "${upgrades.find(u => u.id === randomGift)?.name}" on the level "${runLevels[0].name}". `,); +} + +function setMousePos(x) { + + needsRender = true; + puck = x; + + if (offsetX > ballSize) { + // We have borders visible, enforce them + if (puck < offsetX + puckWidth / 2) { + puck = offsetX + puckWidth / 2; + } + if (puck > offsetX + gameZoneWidth - puckWidth / 2) { + puck = offsetX + gameZoneWidth - puckWidth / 2; + } + } else { + // Let puck touch the border of the screen + if (puck < puckWidth / 2) { + puck = puckWidth / 2; + } + if (puck > offsetX * 2 + gameZoneWidth - puckWidth / 2) { + puck = offsetX * 2 + gameZoneWidth - puckWidth / 2; + } + } + if (!running && !levelTime) { + putBallsAtPuck(); + } +} + +canvas.addEventListener("mouseup", (e) => { + if (e.button !== 0) return; + running = !running; +}); + +canvas.addEventListener("mousemove", (e) => { + setMousePos(e.x); +}); + +canvas.addEventListener("touchstart", (e) => { + e.preventDefault(); + if (!e.touches?.length) return; + setMousePos(e.touches[0].pageX); + running = true; +}); +canvas.addEventListener("touchend", (e) => { + e.preventDefault(); + running = false; +}); +canvas.addEventListener("touchcancel", (e) => { + e.preventDefault(); + running = false; + needsRender = true +}); +canvas.addEventListener("touchmove", (e) => { + if (!e.touches?.length) return; + setMousePos(e.touches[0].pageX); +}); + +let lastTick = performance.now(); + +function brickIndex(x, y) { + return getRowColIndex(Math.floor(y / brickWidth), Math.floor((x - offsetX) / brickWidth),); +} + +function hasBrick(index) { + if (bricks[index]) return index; +} + +function hitsSomething(x, y, radius) { + return (hasBrick(brickIndex(x - radius, y - radius)) ?? hasBrick(brickIndex(x + radius, y - radius)) ?? hasBrick(brickIndex(x + radius, y + radius)) ?? hasBrick(brickIndex(x - radius, y + radius))); +} + +function shouldPierceByColor(ballOrCoin, vhit, hhit, chit) { + if (!perks.pierce_color) return false + // if (ballOrCoin.color === 'white') return true + if (typeof vhit !== 'undefined' && bricks[vhit] !== ballOrCoin.color) { + return false + } + if (typeof hhit !== 'undefined' && bricks[hhit] !== ballOrCoin.color) { + return false + } + if (typeof chit !== 'undefined' && bricks[chit] !== ballOrCoin.color) { + return false + } + return true +} + +function brickHitCheck(ballOrCoin, radius, isBall) { + // Make ball/coin bonce, and return bricks that were hit + const {x, y, previousx, previousy, hitSinceBounce} = ballOrCoin; + + const vhit = hitsSomething(previousx, y, radius); + const hhit = hitsSomething(x, previousy, radius); + const chit = (typeof vhit == "undefined" && typeof hhit == "undefined" && hitsSomething(x, y, radius)) || undefined; + + + let pierce = isBall && ballOrCoin.piercedSinceBounce < perks.pierce * 3; + if (pierce && (typeof vhit !== "undefined" || typeof hhit !== "undefined" || typeof chit !== "undefined")) { + ballOrCoin.piercedSinceBounce++ + } + if (isBall && shouldPierceByColor(ballOrCoin, vhit, hhit, chit)) { + pierce = true + } + + + if (typeof vhit !== "undefined" || typeof chit !== "undefined") { + if (!pierce) { + ballOrCoin.y = ballOrCoin.previousy; + ballOrCoin.vy *= -1; + } + + if (!isBall) { + // Roll on corners + const leftHit = bricks[brickIndex(x - radius, y + radius)]; + const rightHit = bricks[brickIndex(x + radius, y + radius)]; + + if (leftHit && !rightHit) { + ballOrCoin.vx += 1; + } + if (!leftHit && rightHit) { + ballOrCoin.vx -= 1; + } + } + } + if (typeof hhit !== "undefined" || typeof chit !== "undefined") { + if (!pierce) { + ballOrCoin.x = ballOrCoin.previousx; + ballOrCoin.vx *= -1; + } + } + + return vhit ?? hhit ?? chit; +} + +function bordersHitCheck(coin, radius, delta) { + if (coin.destroyed) return; + coin.previousx = coin.x; + coin.previousy = coin.y; + coin.x += coin.vx * delta; + coin.y += coin.vy * delta; + coin.sx ||= 0; + coin.sy ||= 0; + coin.sx += coin.previousx - coin.x; + coin.sy += coin.previousy - coin.y; + coin.sx *= 0.9; + coin.sy *= 0.9; + + let vhit = 0, hhit = 0; + + + if (coin.x < (offsetX > ballSize ? offsetX : 0) + radius) { + coin.x = offsetX + radius; + coin.vx *= -1; + hhit = 1; + } + if (coin.y < radius) { + coin.y = radius; + coin.vy *= -1; + vhit = 1; + } + if (coin.x > canvas.width - (offsetX > ballSize ? offsetX : 0) - radius) { + coin.x = canvas.width - offsetX - radius; + coin.vx *= -1; + hhit = 1; + } + + return hhit + vhit * 2; +} + +let lastTickDown = 0; + +function tick() { + + recomputeTargetBaseSpeed(); + const currentTick = performance.now(); + + puckWidth = (gameZoneWidth / 12) * (3 - perks.smaller_puck + perks.bigger_puck); + + if (running) { + + levelTime += currentTick - lastTick; + // How many time to compute + let delta = Math.min(4, (currentTick - lastTick) / (1000 / 60)); + delta *= running ? 1 : 0 + + + coins = coins.filter((coin) => !coin.destroyed); + balls = balls.filter((ball) => !ball.destroyed); + + const remainingBricks = bricks.filter((b) => b && b !== "black").length; + + if (levelTime > lastTickDown + 1000 && perks.hot_start) { + lastTickDown = levelTime; + decreaseCombo(perks.hot_start, puck, gameZoneHeight - 2 * puckHeight); + } + + if (remainingBricks < perks.skip_last) { + bricks.forEach((type, index) => { + if (type) { + explodeBrick(index, balls[0], true); + } + }); + } + if (!remainingBricks && !coins.length) { + incrementRunStatistics('level_time', levelTime) + + if (currentLevel + 1 < max_levels()) { + setLevel(currentLevel + 1); + } else { + gameOver("Run finished with " + score + " points", "You cleared all levels for this run."); + } + } else if (running || levelTime) { + let playedCoinBounce = false; + const coinRadius = Math.round(coinSize / 2); + + coins.forEach((coin) => { + if (coin.destroyed) return; + if (perks.coin_magnet) { + coin.vx += ((delta * (puck - coin.x)) / (100 + Math.pow(coin.y - gameZoneHeight, 2) + Math.pow(coin.x - puck, 2))) * perks.coin_magnet * 100; + } + + const ratio = 1 - (perks.viscosity * 0.03 + 0.005) * delta; + + coin.vy *= ratio; + coin.vx *= ratio; + + // Gravity + coin.vy += delta * coin.weight * 0.8; + + const speed = Math.abs(coin.sx) + Math.abs(coin.sx); + const hitBorder = bordersHitCheck(coin, coinRadius, delta); + + if (coin.y > gameZoneHeight - coinRadius - puckHeight && coin.y < gameZoneHeight + puckHeight + coin.vy && Math.abs(coin.x - puck) < coinRadius + puckWidth / 2 + // a bit of margin to be nice + puckHeight) { + addToScore(coin); + + } else if (coin.y > canvas.height + coinRadius) { + coin.destroyed = true; + if (perks.catch_all_coins) { + decreaseCombo(coin.points * perks.catch_all_coins, coin.x, canvas.height - coinRadius); + } + } + + const hitBrick = brickHitCheck(coin, coinRadius, false); + + if (perks.metamorphosis && typeof hitBrick !== "undefined") { + if (bricks[hitBrick] && coin.color !== bricks[hitBrick] && bricks[hitBrick] !== "black" && !coin.coloredABrick) { + bricks[hitBrick] = coin.color; + coin.coloredABrick = true + } + } + if (typeof hitBrick !== "undefined" || hitBorder) { + coin.vx *= 0.8; + coin.vy *= 0.8; + + if (speed > 20 && !playedCoinBounce) { + playedCoinBounce = true; + sounds.coinBounce(coin.x, 0.2); + } + + if (Math.abs(coin.vy) < 3) { + coin.vy = 0; + } + } + }); + + balls.forEach((ball) => ballTick(ball, delta)); + + flashes.forEach((flash) => { + if (flash.type === "particle") { + flash.x += flash.vx * delta; + flash.y += flash.vy * delta; + if (!flash.ethereal) { + flash.vy += 0.5; + if (hasBrick(brickIndex(flash.x, flash.y))) { + flash.destroyed = true; + } + } + } + }); + } + } + + render(); + + requestAnimationFrame(tick); + lastTick = currentTick; +} + +function isTelekinesisActive(ball) { + return perks.telekinesis && !ball.hitSinceBounce && ball.vy < 0; +} + +function ballTick(ball, delta) { + ball.previousvx = ball.vx; + ball.previousvy = ball.vy; + + if (isTelekinesisActive(ball)) { + ball.vx += ((puck - ball.x) / 1000) * delta * perks.telekinesis; + } else if (ball.vx * ball.vx + ball.vy * ball.vy < baseSpeed * baseSpeed * 2) { + ball.vx *= 1.01; + ball.vy *= 1.01; + } else { + ball.vx *= 0.99; + if (Math.abs(ball.vy) > 0.5 * baseSpeed) { + ball.vy *= 0.99; + } + } + + const borderHitCode = bordersHitCheck(ball, ballSize / 2, delta); + if (borderHitCode) { + if (perks.sides_are_lava && borderHitCode % 2) { + resetCombo(ball.x, ball.y); + } + if (perks.top_is_lava && borderHitCode >= 2) { + resetCombo(ball.x, ball.y + ballSize); + } + sounds.wallBeep(ball.x); + ball.bouncesList?.push({x: ball.previousx, y: ball.previousy}) + } + + // Puck collision + const ylimit = gameZoneHeight - puckHeight - ballSize / 2; + if (ball.y > ylimit && Math.abs(ball.x - puck) < ballSize / 2 + puckWidth / 2 && ball.vy > 0) { + const speed = Math.sqrt(ball.vx * ball.vx + ball.vy * ball.vy); + const angle = Math.atan2(-puckWidth / 2, ball.x - puck); + ball.vx = speed * Math.cos(angle); + ball.vy = speed * Math.sin(angle); + + sounds.wallBeep(ball.x); + if (perks.streak_shots) { + resetCombo(ball.x, ball.y); + } + if (!ball.hitSinceBounce) { + incrementRunStatistics('miss') + levelMisses++; + flashes.push({ + type: "text", + text: 'miss', + time: levelTime, + color: ball.color, + x: ball.x, + y: ball.y - ballSize, + duration: 450, + size: puckHeight, + }) + if (ball.bouncesList?.length) { + ball.bouncesList.push({ + x: ball.previousx, + y: ball.previousy + }) + for(si=0; si< ball.bouncesList.length-1;si++){ + // segement + const start= ball.bouncesList[si] + const end= ball.bouncesList[si+1] + const distance= Math.sqrt(Math.pow(start.x-end.x,2)+ Math.pow(start.y-end.y,2)) + const parts = distance/30 + for(var i = 0; i gameZoneHeight + ballSize / 2 && running) { + ball.destroyed = true; + if (!balls.find((b) => !b.destroyed)) { + if (perks.extra_life) { + perks.extra_life--; + resetBalls(); + sounds.revive(); + running = false; + coins = []; + flashes.push({ + type: "ball", + duration: 500, + time: levelTime, + size: brickWidth * 2, + color: "white", + x: ball.x, + y: ball.y, + }); + } else { + gameOver("Game Over", "You dropped the ball after catching " + score + " coins. "); + } + } + } + const hitBrick = brickHitCheck(ball, ballSize / 2, true); + if (typeof hitBrick !== "undefined") { + const wasABomb = bricks[hitBrick] === "black"; + explodeBrick(hitBrick, ball, false); + + if (perks.sapper && !wasABomb) { + bricks[hitBrick] = "black"; + } + } + + if (!isSettingOn("basic")) { + ball.sparks += (delta * (combo - 1)) / 30; + if (ball.sparks > 1) { + flashes.push({ + type: "particle", + duration: 100 * ball.sparks, + time: levelTime, + size: coinSize / 2, + color: ball.color, + x: ball.x, + y: ball.y, + vx: (Math.random() - 0.5) * baseSpeed, + vy: (Math.random() - 0.5) * baseSpeed, + }); + ball.sparks = 0; + } + } +} + + +let runStatistics = {}; + +function resetRunStatistics() { + runStatistics = { + started: Date.now(), + ended: null, + width: window.innerWidth, + height: window.innerHeight, + easy: isSettingOn('easy'), + color_blind: isSettingOn('color_blind'), + } +} + + +function incrementRunStatistics(key, amount = 1) { + runStatistics[key + '_total'] = (runStatistics[key + '_total'] || 0) + amount + runStatistics[key + '_lvl_' + currentLevel] = (runStatistics[key + '_lvl_' + currentLevel] || 0) + amount +} + +function getTotalScore() { + try { + return JSON.parse(localStorage.getItem('breakout_71_total_score') || '0') + } catch (e) { + return 0 + } +} + +function addToTotalScore(points) { + try { + localStorage.setItem('breakout_71_total_score', JSON.stringify(getTotalScore() + points)) + } catch (e) { + } +} + +function gameOver(title, intro) { + if (!running) return; + running = false; + needsRender = true + + runStatistics.ended = Date.now() + + const {stats} = getLevelStats(); + + scoreStory.push(`During level ${currentLevel + 1} ${stats}`); + if (balls.find((b) => !b.destroyed)) { + scoreStory.push(`You cleared the last level and won. `); + } else { + scoreStory.push(`You dropped the ball and finished your run early. `); + } + + try { + // Stores only last 100 runs + const runsHistory = JSON.parse(localStorage.getItem('breakout_71_history') || '[]').slice(0, 99).concat([runStatistics]) + + // Generate some histogram + + localStorage.setItem('breakout_71_history', '
' + JSON.stringify(runsHistory, null, 2) + '
') + } catch { + } + + let animationDelay = -300 + const getDelay = () => { + animationDelay += 800 + return 'animation-delay:' + animationDelay + 'ms;' + } + // unlocks + let unlocksInfo = '' + const endTs = getTotalScore() + const startTs = endTs - score + const list = getUpgraderUnlockPoints() + list.filter(u => u.threshold > startTs && u.threshold < endTs).forEach(u => { + unlocksInfo += ` +

+ ${u.title} + +

+` + }) + + const previousUnlockAt = list.findLast(u => u.threshold <= endTs)?.threshold || 0 + const nextUnlock = list.find(u => u.threshold > endTs) + + if (nextUnlock) { + const total = nextUnlock?.threshold - previousUnlockAt + const done = endTs - previousUnlockAt + intro += `Score ${nextUnlock.threshold - endTs} more points to reach the next unlock.` + + unlocksInfo += ` +

+ ${nextUnlock.title} + +

+ +` + list.slice(list.indexOf(nextUnlock) + 1).slice(0, 3).forEach(u => { + unlocksInfo += ` +

+ ${u.title} +

+` + }) + } + + + asyncAlert({ + allowClose: true, title, text: ` +

${intro}

+ ${unlocksInfo} + `, textAfterButtons: ` + + ${scoreStory.map((t) => "

" + t + "

").join("")} + ` + }).then(() => restart()); +} + +function explodeBrick(index, ball, isExplosion) { + const color = bricks[index]; + if (color === 'black') { + delete bricks[index]; + const x = brickCenterX(index), y = brickCenterY(index); + + incrementRunStatistics('explosion', 1) + sounds.explode(ball.x); + const {col, row} = getRowCol(index); + const size = 1 + perks.bigger_explosions; + // Break bricks around + for (let dx = -size; dx <= size; dx++) { + for (let dy = -size; dy <= size; dy++) { + const i = getRowColIndex(row + dy, col + dx); + if (bricks[i] && i !== -1) { + explodeBrick(i, ball, true) + } + } + } + // Blow nearby coins + coins.forEach((c) => { + const dx = c.x - x; + const dy = c.y - y; + const d2 = Math.max(brickWidth, Math.abs(dx) + Math.abs(dy)); + c.vx += (dx / d2) * 10 * size / c.weight; + c.vy += (dy / d2) * 10 * size / c.weight; + }); + lastexplosion = Date.now(); + + flashes.push({ + type: "ball", duration: 150, time: levelTime, size: brickWidth * 2, color: "white", x, y, + }); + spawnExplosion(7 * (1 + perks.bigger_explosions), x, y, "white", 150, coinSize,); + ball.hitSinceBounce++; + } else if (color) { + // Flashing is take care of by the tick loop + const x = brickCenterX(index), y = brickCenterY(index); + + bricks[index] = ""; + + levelSpawnedCoins += combo; + + incrementRunStatistics('spawned_coins', combo) + + coins = coins.filter((c) => !c.destroyed); + for (let i = 0; i < combo; i++) { + // Avoids saturating the canvas with coins + if (coins.length > MAX_COINS * (isSettingOn("basic") ? 0.5 : 1)) { + // Just pick a random one + coins[Math.floor(Math.random() * coins.length)].points++; + continue; + } + + const coord = { + x: x + (Math.random() - 0.5) * (brickWidth - coinSize), + y: y + (Math.random() - 0.5) * (brickWidth - coinSize), + }; + + coins.push({ + points: 1, + color, ...coord, + previousx: coord.x, + previousy: coord.y, + vx: ball.vx * (0.5 + Math.random()), + vy: ball.vy * (0.5 + Math.random()), + sx: 0, + sy: 0, + weight: 0.8 + Math.random() * 0.2 + }); + } + + combo += perks.streak_shots + perks.catch_all_coins + perks.sides_are_lava + perks.top_is_lava + perks.picky_eater; + + if (!isExplosion) { + // color change + if ((perks.picky_eater || perks.pierce_color) && color !== ball.color) { + // reset streak + if (perks.picky_eater) resetCombo(ball.x, ball.y); + ball.color = color; + } else { + sounds.comboIncreaseMaybe(ball.x, 1); + } + } + ball.hitSinceBounce++; + + flashes.push({ + type: "ball", duration: 40, time: levelTime, size: brickWidth, color: color, x, y, + }); + spawnExplosion(5 + combo, x, y, color, 100, coinSize / 2); + } +} + +function max_levels() { + return 7 + perks.extra_levels; +} + +function render() { + if (running) needsRender = true + if (!needsRender) { + return + } + needsRender = false; + + const level = currentLevelInfo(); + const {width, height} = canvas; + if (!width || !height) return; + + let scoreInfo = ""; + for (let i = 0; i < perks.extra_life; i++) { + scoreInfo += "🖤 "; + } + + scoreInfo += score.toString(); + scoreDisplay.innerText = scoreInfo; + + + if (!isSettingOn("basic") && !level.color && level.svg && !level.black_puck) { + + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = 0.7 + ctx.fillStyle = "#000"; + ctx.fillRect(0, 0, width, height); + + ctx.globalCompositeOperation = "multiply"; + ctx.globalAlpha = 0.3; + const gradient = ctx.createLinearGradient(offsetX, gameZoneHeight - puckHeight, offsetX, height - puckHeight * 3,); + gradient.addColorStop(0, "black"); + gradient.addColorStop(1, "transparent"); + ctx.fillStyle = gradient; + ctx.fillRect(offsetX, gameZoneHeight - puckHeight * 3, gameZoneWidth, puckHeight * 4,); + + ctx.globalCompositeOperation = "screen"; + ctx.globalAlpha = 0.6; + coins.forEach((coin) => { + if (!coin.destroyed) drawFuzzyBall(ctx, coin.color, coinSize * 2, coin.x, coin.y); + }); + balls.forEach((ball) => { + drawFuzzyBall(ctx, ball.color, ballSize * 2, ball.x, ball.y); + }); + ctx.globalAlpha = 0.5; + bricks.forEach((color, index) => { + if (!color) return; + const x = brickCenterX(index), y = brickCenterY(index); + drawFuzzyBall(ctx, color == 'black' ? '#666' : color, brickWidth, x, y); + }); + ctx.globalAlpha = 1; + flashes.forEach((flash) => { + const {x, y, time, color, size, type, duration} = flash; + const elapsed = levelTime - time; + ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2); + if (type === "ball" || type === "particle") { + drawFuzzyBall(ctx, color, size, x, y); + } + }); + + ctx.globalAlpha = 0.9; + ctx.globalCompositeOperation = "multiply"; + if (level.svg && background.complete) { + if (backgroundCanvas.title !== level.name) { + backgroundCanvas.title = level.name + backgroundCanvas.width = canvas.width + backgroundCanvas.height = canvas.height + const bgctx = backgroundCanvas.getContext("2d") + bgctx.fillStyle = level.color + bgctx.fillRect(0, 0, canvas.width, canvas.height) + bgctx.fillStyle = ctx.createPattern(background, "repeat"); + bgctx.fillRect(0, 0, width, height); + console.log("redrew context") + } + ctx.drawImage(backgroundCanvas, 0, 0) + + + } + } else { + + ctx.globalCompositeOperation = "source-over"; + ctx.globalAlpha = 1 + ctx.fillStyle = level.color || "#000"; + ctx.fillRect(0, 0, width, height); + + flashes.forEach((flash) => { + const {x, y, time, color, size, type, duration} = flash; + const elapsed = levelTime - time; + ctx.globalAlpha = Math.min(1, 2 - (elapsed / duration) * 2); + if (type === "particle") { + drawBall(ctx, color, size, x, y); + } + }); + } + + if (combo > baseCombo()) { + ctx.globalCompositeOperation = "screen"; + ctx.globalAlpha = (2 + combo - baseCombo()) / 50; + + if (perks.top_is_lava) { + drawRedGradientSquare(ctx, offsetX, 0, gameZoneWidth, ballSize, 0, 0, 0, ballSize,); + } + if (perks.sides_are_lava) { + drawRedGradientSquare(ctx, offsetX, 0, ballSize, gameZoneHeight, 0, 0, ballSize, 0,); + drawRedGradientSquare(ctx, offsetX + gameZoneWidth - ballSize, 0, ballSize, gameZoneHeight, ballSize, 0, 0, 0,); + } + if (perks.catch_all_coins) { + drawRedGradientSquare(ctx, offsetX, gameZoneHeight - ballSize, gameZoneWidth, ballSize, 0, ballSize, 0, 0,); + } + if (perks.streak_shots) { + drawRedGradientSquare(ctx, puck - puckWidth / 2, gameZoneHeight - puckHeight - ballSize, puckWidth, ballSize, 0, ballSize, 0, 0,); + } + if (perks.picky_eater) { + let okColors = new Set(balls.map((b) => b.color)); + + bricks.forEach((type, index) => { + if (!type || type === "black" || okColors.has(type)) return; + const x = brickCenterX(index), y = brickCenterY(index); + drawFuzzyBall(ctx, "red", brickWidth, x, y); + }); + } + ctx.globalAlpha = 1; + } + + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + const lastExplosionDelay = Date.now() - lastexplosion + 5; + const shaked = lastExplosionDelay < 200; + if (shaked) { + ctx.translate((Math.sin(Date.now()) * 50) / lastExplosionDelay, (Math.sin(Date.now() + 36) * 50) / lastExplosionDelay,); + } + + ctx.globalCompositeOperation = "source-over"; + renderAllBricks(ctx); + + ctx.globalCompositeOperation = "screen"; + flashes = flashes.filter((f) => levelTime - f.time < f.duration && !f.destroyed,); + + flashes.forEach((flash) => { + const {x, y, time, color, size, type, text, duration, points} = flash; + const elapsed = levelTime - time; + ctx.globalAlpha = Math.max(0, Math.min(1, 2 - (elapsed / duration) * 2)); + if (type === "text") { + ctx.globalCompositeOperation = "source-over"; + drawText(ctx, text, color, size, {x, y: y - elapsed / 10}); + } else if (type === "particle") { + ctx.globalCompositeOperation = "screen"; + drawBall(ctx, color, size, x, y); + drawFuzzyBall(ctx, color, size, x, y); + } + }); + + // Coins + ctx.globalAlpha = 1; + ctx.globalCompositeOperation = "source-over"; + coins.forEach((coin) => { + if (!coin.destroyed) drawCoin(ctx, coin.color, coinSize, coin, level.color || 'black'); + }); + + + // Black shadow around balls + if (coins.length > 10 && !isSettingOn('basic')) { + ctx.globalAlpha = Math.min(0.8, (coins.length - 10) / 50); + balls.forEach((ball) => { + drawBall(ctx, level.color || "#000", ballSize * 6, ball.x, ball.y); + }); + } + + + ctx.globalAlpha = 1 + ctx.globalCompositeOperation = "source-over"; + const puckColor = level.black_puck ? '#000' : '#FFF' + balls.forEach((ball) => { + drawBall(ctx, ball.color, ballSize, ball.x, ball.y); + // effect + if (isTelekinesisActive(ball)) { + ctx.strokeStyle = puckColor; + ctx.beginPath(); + ctx.bezierCurveTo(puck, gameZoneHeight, puck, ball.y, ball.x, ball.y); + ctx.stroke(); + } + }); + // The puck + + ctx.globalAlpha = 1 + ctx.globalCompositeOperation = "source-over"; + drawPuck(ctx, puckColor, puckWidth, puckHeight) + + + if (combo > 1) { + ctx.globalCompositeOperation = "destination-out"; + drawText(ctx, "x " + combo, "white", puckHeight, { + x: puck, y: gameZoneHeight - puckHeight / 2, + }); + } + // Borders + ctx.fillStyle = puckColor; + ctx.globalCompositeOperation = "source-over"; + if (offsetX > ballSize) { + ctx.fillRect(offsetX, 0, 1, height); + ctx.fillRect(width - offsetX - 1, 0, 1, height); + } + if (isSettingOn("mobile-mode")) { + ctx.fillRect(offsetX, gameZoneHeight, gameZoneWidth, 1); + if (!running) { + drawText(ctx, "Keep pressing here to play", puckColor, puckHeight, { + x: canvas.width / 2, y: gameZoneHeight + (canvas.height - gameZoneHeight) / 2, + }); + } + } + + if (shaked) { + ctx.resetTransform(); + } +} + +let cachedBricksRender = document.createElement("canvas"); +let cachedBricksRenderKey = null; + +function renderAllBricks(destinationCtx) { + ctx.globalAlpha = 1; + + const level = currentLevelInfo(); + + const newKey = gameZoneWidth + "_" + bricks.join("_") + bombSVG.complete; + if (newKey !== cachedBricksRenderKey) { + cachedBricksRenderKey = newKey; + + cachedBricksRender.width = gameZoneWidth; + cachedBricksRender.height = gameZoneWidth + 1; + const ctx = cachedBricksRender.getContext("2d"); + ctx.clearRect(0, 0, gameZoneWidth, gameZoneWidth); + ctx.resetTransform(); + ctx.translate(-offsetX, 0); + // Bricks + bricks.forEach((color, index) => { + const x = brickCenterX(index), y = brickCenterY(index); + + if (!color) return; + drawBrick(ctx, color, x, y, level.squared || false); + if (color === 'black') { + ctx.globalCompositeOperation = "source-over"; + drawIMG(ctx, bombSVG, brickWidth, x, y); + } + }); + } + + destinationCtx.drawImage(cachedBricksRender, offsetX, 0); +} + +let cachedGraphics = {}; + +function drawPuck(ctx, color, puckWidth, puckHeight) { + + const key = "puck" + color + "_" + puckWidth + '_' + puckHeight; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = puckWidth; + can.height = puckHeight * 2; + const canctx = can.getContext("2d"); + canctx.fillStyle = color; + + + canctx.beginPath(); + canctx.moveTo(0, puckHeight * 2) + canctx.lineTo(0, puckHeight * 1.25) + canctx.bezierCurveTo(0, puckHeight * .75, puckWidth, puckHeight * .75, puckWidth, puckHeight * 1.25) + canctx.lineTo(puckWidth, puckHeight * 2) + canctx.fill(); + cachedGraphics[key] = can; + } + + ctx.drawImage(cachedGraphics[key], Math.round(puck - puckWidth / 2), gameZoneHeight - puckHeight * 2,); + + +} + +function drawBall(ctx, color, width, x, y) { + const key = "ball" + color + "_" + width; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + const size = Math.round(width); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d"); + canctx.beginPath(); + canctx.arc(size / 2, size / 2, Math.round(size / 2), 0, 2 * Math.PI); + canctx.fillStyle = color; + canctx.fill(); + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], Math.round(x - width / 2), Math.round(y - width / 2),); +} + +function drawCoin(ctx, color, width, ball, bg) { + const key = "coin with halo" + "_" + color + "_" + width + '_' + bg; + + const size = width * 3; + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d"); + + // coin + canctx.beginPath(); + canctx.arc(size / 2, size / 2, width / 2, 0, 2 * Math.PI); + canctx.fillStyle = color; + canctx.fill(); + + canctx.strokeStyle = bg; + canctx.stroke(); + + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], Math.round(ball.x - size / 2), Math.round(ball.y - size / 2),); +} + +function drawFuzzyBall(ctx, color, width, x, y) { + const key = "fuzzy-circle" + color + "_" + width; + + const size = Math.round(width * 3); + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d"); + const gradient = canctx.createRadialGradient(size / 2, size / 2, 0, size / 2, size / 2, size / 2,); + gradient.addColorStop(0, color); + gradient.addColorStop(1, "transparent"); + canctx.fillStyle = gradient; + canctx.fillRect(0, 0, size, size); + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2),); +} + +function drawBrick(ctx, color, x, y, squared) { + const tlx = Math.ceil(x - brickWidth / 2); + const tly = Math.ceil(y - brickWidth / 2); + const brx = Math.ceil(x + brickWidth / 2) - 1; + const bry = Math.ceil(y + brickWidth / 2) - 1; + + const width = brx - tlx, height = bry - tly; + const key = "brick" + color + "_" + width + "_" + height + '_' + squared + // "_" + + // isSettingOn("rounded-bricks"); + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = width; + can.height = height; + const canctx = can.getContext("2d"); + + + if (squared) { + + canctx.fillStyle = color; + canctx.fillRect(0, 0, width, height); + } else { + + const bord = Math.floor(brickWidth / 6); + canctx.strokeStyle = color; + canctx.lineJoin = "round"; + canctx.lineWidth = bord * 1.5; + canctx.strokeRect(bord, bord, width - bord * 2, height - bord * 2); + + canctx.fillStyle = color; + canctx.fillRect(bord, bord, width - bord * 2, height - bord * 2); + } + + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], tlx, tly, width, height); + // It's not easy to have a 1px gap between bricks without antialiasing +} + +function drawRedGradientSquare(ctx, x, y, width, height, redX, redY, blackX, blackY, color = "red",) { + const key = "gradient" + width + "_" + height + "_" + redX + "_" + redY + "_" + blackX + "_" + blackY + "_" + color; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = width; + can.height = height; + const canctx = can.getContext("2d"); + + const gradient = canctx.createLinearGradient(redX, redY, blackX, blackY); + gradient.addColorStop(0, color); + gradient.addColorStop(1, "black"); + canctx.fillStyle = gradient; + canctx.fillRect(0, 0, width, height); + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], x, y, width, height); +} + + +function drawIMG(ctx, img, size, x, y) { + const key = "svg" + img + "_" + size + '_' + img.complete; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = size; + can.height = size; + + const canctx = can.getContext("2d"); + + const ratio = size / Math.max(img.width, img.height); + const w = img.width * ratio; + const h = img.height * ratio; + canctx.drawImage(img, (size - w) / 2, (size - h) / 2, w, h); + + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], Math.round(x - size / 2), Math.round(y - size / 2),); +} + +function drawText(ctx, text, color, fontSize, {x, y}) { + const key = "text" + text + "_" + color + "_" + fontSize; + + if (!cachedGraphics[key]) { + const can = document.createElement("canvas"); + can.width = fontSize * text.length; + can.height = fontSize; + const canctx = can.getContext("2d"); + canctx.fillStyle = color; + canctx.textAlign = "center"; + canctx.textBaseline = "middle"; + canctx.font = fontSize + "px monospace"; + + canctx.fillText(text, can.width / 2, can.height / 2, can.width); + + cachedGraphics[key] = can; + } + ctx.drawImage(cachedGraphics[key], Math.round(x - cachedGraphics[key].width / 2), Math.round(y - cachedGraphics[key].height / 2),); +} + +function pixelsToPan(pan) { + return (pan - offsetX) / gameZoneWidth; +} + +let lastComboPlayed = NaN, shepard = 6; + +function playShepard(delta, pan, volume) { + const shepardMax = 11, factor = 1.05945594920268, baseNote = 392; + shepard += delta; + if (shepard > shepardMax) shepard = 0; + if (shepard < 0) shepard = shepardMax; + + const play = (note) => { + const freq = baseNote * Math.pow(factor, note); + const diff = Math.abs(note - shepardMax * 0.5); + const maxDistanceToIdeal = 1.5 * shepardMax; + const vol = Math.max(0, volume * (1 - diff / maxDistanceToIdeal)); + createSingleBounceSound(freq, pan, vol); + return freq.toFixed(2) + " at " + Math.floor(vol * 100) + "% diff " + diff; + }; + + play(1 + shepardMax + shepard); + play(shepard); + play(-1 - shepardMax + shepard); +} + +const sounds = { + wallBeep: (pan) => { + if (!isSettingOn("sound")) return; + createSingleBounceSound(800, pixelsToPan(pan)); + }, + + comboIncreaseMaybe: (x, volume) => { + if (!isSettingOn("sound")) return; + let delta = 0; + if (!isNaN(lastComboPlayed)) { + if (lastComboPlayed < combo) delta = 1; + if (lastComboPlayed > combo) delta = -1; + } + playShepard(delta, pixelsToPan(x), volume); + lastComboPlayed = combo; + }, + + comboDecrease() { + if (!isSettingOn("sound")) return; + playShepard(-1, 0.5, 0.5); + }, coinBounce: (pan, volume) => { + if (!isSettingOn("sound")) return; + createSingleBounceSound(1200, pixelsToPan(pan), volume); + }, explode: (pan) => { + if (!isSettingOn("sound")) return; + createExplosionSound(pixelsToPan(pan)); + }, revive: () => { + if (!isSettingOn("sound")) return; + createRevivalSound(500); + }, coinCatch(pan) { + if (!isSettingOn("sound")) return; + createSingleBounceSound(440, pixelsToPan(pan), .8) + } +}; + +// How to play the code on the leftconst context = new window.AudioContext(); +let audioContext, delayNode; + +function getAudioContext() { + if (!audioContext) { + audioContext = new (window.AudioContext || window.webkitAudioContext)(); + } + return audioContext; +} + +function createSingleBounceSound(baseFreq = 800, pan = 0.5, volume = 1, duration = 0.1,) { + const context = getAudioContext(); + // Frequency for the metal "ping" + const baseFrequency = baseFreq; // Hz + + // Create an oscillator for the impact sound + const oscillator = context.createOscillator(); + oscillator.type = "sine"; + oscillator.frequency.setValueAtTime(baseFrequency, context.currentTime); + + // Create a gain node to control the volume + const gainNode = context.createGain(); + oscillator.connect(gainNode); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); + gainNode.connect(panner); + panner.connect(context.destination); + + // Set up the gain envelope to simulate the impact and quick decay + gainNode.gain.setValueAtTime(0.8 * volume, context.currentTime); // Initial impact + gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + duration,); // Quick decay + + // Start the oscillator + oscillator.start(context.currentTime); + + // Stop the oscillator after the decay + oscillator.stop(context.currentTime + duration); +} + +function createRevivalSound(baseFreq = 440) { + const context = getAudioContext(); + + // Create multiple oscillators for a richer sound + const oscillators = [context.createOscillator(), context.createOscillator(), context.createOscillator(),]; + + // Set the type and frequency for each oscillator + oscillators.forEach((osc, index) => { + osc.type = "sine"; + osc.frequency.setValueAtTime(baseFreq + index * 2, context.currentTime); // Slight detuning + }); + + // Create a gain node to control the volume + const gainNode = context.createGain(); + + // Connect all oscillators to the gain node + oscillators.forEach((osc) => osc.connect(gainNode)); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(0, context.currentTime); // Center panning + gainNode.connect(panner); + panner.connect(context.destination); + + // Set up the gain envelope to simulate a smooth attack and decay + gainNode.gain.setValueAtTime(0, context.currentTime); // Start at zero + gainNode.gain.linearRampToValueAtTime(0.8, context.currentTime + 0.5); // Ramp up to full volume + gainNode.gain.exponentialRampToValueAtTime(0.001, context.currentTime + 2); // Slow decay + + // Start all oscillators + oscillators.forEach((osc) => osc.start(context.currentTime)); + + // Stop all oscillators after the decay + oscillators.forEach((osc) => osc.stop(context.currentTime + 2)); +} + +let noiseBuffer; + +function createExplosionSound(pan = 0.5) { + const context = getAudioContext(); + // Create an audio buffer + if (!noiseBuffer) { + const bufferSize = context.sampleRate * 2; // 2 seconds + noiseBuffer = context.createBuffer(1, bufferSize, context.sampleRate); + const output = noiseBuffer.getChannelData(0); + + // Fill the buffer with random noise + for (let i = 0; i < bufferSize; i++) { + output[i] = Math.random() * 2 - 1; + } + } + + // Create a noise source + const noiseSource = context.createBufferSource(); + noiseSource.buffer = noiseBuffer; + + // Create a gain node to control the volume + const gainNode = context.createGain(); + noiseSource.connect(gainNode); + + // Create a filter to shape the explosion sound + const filter = context.createBiquadFilter(); + filter.type = "lowpass"; + filter.frequency.setValueAtTime(1000, context.currentTime); // Set the initial frequency + gainNode.connect(filter); + + // Create a stereo panner node for left-right panning + const panner = context.createStereoPanner(); + panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime); // pan 0 to 1 maps to -1 to 1 + + // Connect filter to panner and then to the destination (speakers) + filter.connect(panner); + panner.connect(context.destination); + + // Ramp down the gain to simulate the explosion's fade-out + gainNode.gain.setValueAtTime(1, context.currentTime); + gainNode.gain.exponentialRampToValueAtTime(0.01, context.currentTime + 1); + + // Lower the filter frequency over time to create the "explosive" effect + filter.frequency.exponentialRampToValueAtTime(60, context.currentTime + 1); + + // Start the noise source + noiseSource.start(context.currentTime); + + // Stop the noise source after the sound has played + noiseSource.stop(context.currentTime + 1); +} + +let levelTime = 0; + +setInterval(() => { + document.body.className = (running ? " running " : " paused ") + (currentLevelInfo().black_puck ? ' black_puck ' : ' '); +}, 100); + +window.addEventListener("visibilitychange", () => { + if (document.hidden) { + running = false; + needsRender = true + } +}); + +const scoreDisplay = document.getElementById("score"); + + +function asyncAlert({ + title, + text, + actions = [{text: "OK", value: "ok", help: ""}], + allowClose = true, + textAfterButtons = '' + }) { + return new Promise((resolve) => { + const popupWrap = document.createElement("div"); + document.body.appendChild(popupWrap); + popupWrap.className = "popup"; + + function closeWithResult(value) { + resolve(value); + // Doing this async lets the menu scroll persist if it's shown a second time + setTimeout(() => { + document.body.removeChild(popupWrap); + }); + } + + if (allowClose) { + const closeButton = document.createElement("button"); + closeButton.title = "close" + closeButton.className = "close-modale" + closeButton.addEventListener('click', (e) => { + e.preventDefault() + closeWithResult(null) + }) + popupWrap.appendChild(closeButton) + } + + const popup = document.createElement("div"); + + if (title) { + const p = document.createElement("h2"); + p.innerHTML = title; + popup.appendChild(p); + } + + if (text) { + const p = document.createElement("div"); + p.innerHTML = text; + popup.appendChild(p); + } + + actions.filter(i => i).forEach(({text, value, help, checked = 0, max = 0, disabled}) => { + const button = document.createElement("button"); + let checkMark = '' + if (max) { + checkMark += '' + for (let i = 0; i < max; i++) { + checkMark += ''; + } + checkMark += '' + } + button.innerHTML = `${checkMark} +
+ ${text} + ${help || ''} +
`; + + + if (disabled) { + button.setAttribute("disabled", "disabled"); + } else { + button.addEventListener("click", (e) => { + e.preventDefault(); + closeWithResult(value) + }); + } + popup.appendChild(button); + }); + + if (textAfterButtons) { + const p = document.createElement("div"); + p.className = 'textAfterButtons' + p.innerHTML = textAfterButtons; + popup.appendChild(p); + } + + + popupWrap.appendChild(popup); + }); +} + +// Settings +let cachedSettings = {}; + +function isSettingOn(key) { + if (typeof cachedSettings[key] == "undefined") { + try { + cachedSettings[key] = JSON.parse(localStorage.getItem("breakout-settings-enable-" + key),); + } catch (e) { + console.warn(e); + } + } + return cachedSettings[key] ?? options[key]?.default ?? false; +} + +function toggleSetting(key) { + cachedSettings[key] = !isSettingOn(key); + try { + const lskey = "breakout-settings-enable-" + key; + localStorage.setItem(lskey, JSON.stringify(cachedSettings[key])); + } catch (e) { + console.warn(e); + } + if (options[key].afterChange) options[key].afterChange(); +} + +scoreDisplay.addEventListener("click", async (e) => { + e.preventDefault(); + const cb = await asyncAlert({ + title: `You scored ${score} points so far`, text: ` +

You are playing level ${currentLevel + 1} out of ${max_levels()}.

+ ${scoreStory.map((t) => "

" + t + "

").join("")} +

You high score is ${highScore}.

+ `, allowClose: true, actions: [{ + text: "New run", help: "Start a brand new run.", value: () => { + restart(); + return true; + }, + }], + }); + if (cb) { + await cb() + } +}); + +document.getElementById("menu").addEventListener("click", (e) => { + e.preventDefault(); + openSettingsPanel(); +}); + +const options = { + sound: { + default: true, name: `Game sounds`, help: `Can slow down some phones.`, + }, "mobile-mode": { + default: window.innerHeight > window.innerWidth, + name: `Mobile mode`, + help: `Leaves space for your thumb.`, + afterChange() { + fitSize(); + }, + }, + basic: { + default: false, name: `Fast mode`, help: `Simpler graphics for older devices.`, + }, + "easy": { + default: false, name: `Easy mode`, help: `Slower ball as starting perk.`, restart: true, + }, "color_blind": { + default: false, name: `Color blind mode`, help: `Removes mechanics about colors.`, restart: true, + }, +}; + +async function openSettingsPanel() { + running = false; + needsRender = true + + const optionsList = []; + for (const key in options) { + optionsList.push({ + checked: isSettingOn(key) ? 1 : 0, max: 1, text: options[key].name, help: options[key].help, value: () => { + toggleSetting(key) + if (options[key].restart) { + restart() + } else { + openSettingsPanel(); + } + }, + }); + } + + const cb = await asyncAlert({ + title: "Breakout 71", text: ` + `, allowClose: true, actions: [ + ...optionsList, + + (window.screenTop || window.screenY) && { + text: "Fullscreen", + help: "Might not work on some machines", + value() { + const docel = document.documentElement + if (docel.requestFullscreen) { + docel.requestFullscreen(); + } else if (docel.webkitRequestFullscreen) { + docel.webkitRequestFullscreen(); + } + } + }, + { + text: 'Reset Game', + help: "Erase high score and statistics", + async value() { + if (await asyncAlert({ + title: 'Reset', + actions: [ + { + text: 'Yes', + value: true + }, + { + text: 'No', + value: false + } + ], + allowClose: true, + })) { + localStorage.clear() + window.location.reload() + } + + } + + } + ], + textAfterButtons: ` +

Made in France by Renan LE CARO
+ privacy policy - + Google Play - + itch.io + +

+ ` + }) + if (cb) { + cb() + } +} + +fitSize() +restart() +tick(); \ No newline at end of file diff --git a/app/src/main/assets/icon.svg b/app/src/main/assets/icon.svg new file mode 100644 index 0000000..f411508 --- /dev/null +++ b/app/src/main/assets/icon.svg @@ -0,0 +1,146 @@ + + + + diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html new file mode 100644 index 0000000..c1b6dfa --- /dev/null +++ b/app/src/main/assets/index.html @@ -0,0 +1,21 @@ + + + + + + + Breakout 𝟕𝟏 + + + + + + + + + + + diff --git a/app/src/main/assets/levels.js b/app/src/main/assets/levels.js new file mode 100644 index 0000000..f4c46c9 --- /dev/null +++ b/app/src/main/assets/levels.js @@ -0,0 +1,6458 @@ +let allLevels=[ + { + "name": "71 mini", + "size": 5, + "bricks": [ + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#e32119", + "", + "", + "#ffd300", + "#e32119", + "#e32119", + "", + "", + "#ffd300", + "", + "#e32119", + "", + "", + "", + "#e32119", + "#e32119", + "#e32119" + ], + "svg": "", + "color": "" + }, + { + "name": "Butterfly ?", + "bricks": [ + "#59EEA3", + "", + null, + "black", + "black", + null, + null, + "#59EEA3", + "#59EEA3", + "#59EEA3", + null, + "#F29E4A", + "#F0F04C", + null, + "#59EEA3", + "#59EEA3", + "#A1F051", + "#59EEA3", + "#59EEA3", + "#F0F04C", + "#F29E4A", + "#59EEA3", + "#59EEA3", + "#A1F051", + "#A1F051", + "black", + "#59EEA3", + "#F29E4A", + "#F0F04C", + "#59EEA3", + "black", + "#A1F051", + "#A1F051", + "black", + "#53EE53", + "#F0F04C", + "#F29E4A", + "#53EE53", + "black", + "#A1F051", + "#A1F051", + "#53EE53", + "#53EE53", + "#F29E4A", + "#F0F04C", + "#53EE53", + "#53EE53", + "#53EE53", + "#53EE53", + "#53EE53", + "", + "#F0F04C", + "#F29E4A", + "", + "#53EE53", + "#53EE53", + "#53EE53", + null, + null, + null, + "", + "", + "", + "#53EE53" + ], + "size": 8, + "svg": "", + "focus": false + }, + { + "name": "Castle", + "size": 7, + "bricks": [ + "#E67070", + "", + "#E67070", + "", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "black", + "black", + "black", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "black", + "black", + "black", + "#E67070", + "#E67070", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#5DA3EA", + "#6262EA", + "#5DA3EA", + "#6262EA", + "#5DA3EA" + ], + "svg": "", + "focus": false + }, + { + "name": "Yin yang", + "size": 8, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + "", + "white", + "white", + "white", + "white", + "white", + "#333", + "", + "", + "white", + "black", + "white", + "white", + "#333", + "#333", + "", + "", + "white", + "black", + "white", + "#333", + "#333", + "#333", + "", + "", + "white", + "white", + "white", + "#333", + "white", + "#333", + "", + "", + "white", + "white", + "#333", + "#333", + "white", + "#333", + "", + "", + "white", + "#333", + "#333", + "#333", + "#333", + "#333", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Creeper", + "size": 10, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#59EEA3", + "#59EEA3", + "#A1F051", + "#A1F051", + "#59EEA3", + "#59EEA3", + "#A1F051", + "#A1F051", + "", + "", + "#59EEA3", + "#A1F051", + "#59EEA3", + "#59EEA3", + "#A1F051", + "#59EEA3", + "#A1F051", + "#59EEA3", + "", + "", + "#A1F051", + "black", + "black", + "#59EEA3", + "#59EEA3", + "black", + "black", + "#59EEA3", + "", + "", + "#59EEA3", + "black", + "black", + "#A1F051", + "#59EEA3", + "black", + "black", + "#59EEA3", + "", + "", + "#A1F051", + "#59EEA3", + "#59EEA3", + "black", + "black", + "#A1F051", + "#A1F051", + "#59EEA3", + "", + "", + "#59EEA3", + "#59EEA3", + "black", + "black", + "black", + "black", + "#59EEA3", + "#A1F051", + "", + "", + "#A1F051", + "#A1F051", + "black", + "black", + "black", + "black", + "#59EEA3", + "#A1F051", + "", + "", + "#59EEA3", + "#A1F051", + "black", + "#59EEA3", + "#59EEA3", + "black", + "#A1F051", + "#59EEA3", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false + }, + { + "name": "Blocky Stairs", + "size": 8, + "bricks": [ + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + null, + null, + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + null, + null, + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + "#A664E8", + "#A664E8", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + "#A664E8", + "#A664E8", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + "#E869E8", + "#E869E8", + "#A664E8", + "#A664E8", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#E869E8", + "#E869E8", + "#A664E8", + "#A664E8", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA" + ], + "svg": "", + "focus": false + }, + { + "name": "Dots", + "size": 9, + "bricks": [ + "#6262EA", + null, + "#5DA3EA", + null, + "#5BECEC", + null, + "#59EEA3", + null, + "#53EE53", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#6262EA", + null, + "#5DA3EA", + null, + "#5BECEC", + null, + "#59EEA3", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#A664E8", + null, + "#6262EA", + null, + "#5DA3EA", + null, + "#5BECEC", + null, + "#59EEA3", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#A664E8", + null, + "#6262EA", + null, + "#5DA3EA", + null, + "#5BECEC", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#E869E8", + null, + "#A664E8", + null, + "#6262EA", + null, + "#5DA3EA", + null, + "#5BECEC" + ], + "svg": "", + "focus": false + }, + { + "name": "Lines", + "size": 9, + "bricks": [ + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Heart", + "size": 15, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + null, + "", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + null, + "", + "#ab0c0c", + "#F44848", + "white", + "white", + "#F44848", + "#F44848", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + null, + "", + "#ab0c0c", + "#F44848", + "white", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + null, + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + null, + "", + "#ab0c0c", + "#F44848", + "white", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + null, + "", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + null, + "", + "", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + "", + null, + "", + "", + "", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "#ab0c0c", + "#F44848", + "#F44848", + "#F44848", + "#ab0c0c", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#F44848", + "#ab0c0c", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Swiss", + "size": 7, + "bricks": [ + "", + null, + null, + null, + null, + null, + null, + null, + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + null, + null, + "#ab0c0c", + "#ab0c0c", + "white", + "#ab0c0c", + "#ab0c0c", + null, + null, + "#ab0c0c", + "white", + "white", + "white", + "#ab0c0c", + null, + null, + "#ab0c0c", + "#ab0c0c", + "white", + "#ab0c0c", + "#ab0c0c", + null, + null, + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c" + ], + "svg": "", + "color": "", + "focus": false, + "squared": true + }, + { + "name": "Germany", + "size": 6, + "bricks": [ + "", + null, + null, + null, + null, + null, + null, + "#231f20", + "#231f20", + "#231f20", + "#231f20", + null, + null, + "#e32119", + "#e32119", + "#e32119", + "#e32119", + null, + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "squared": false, + "color": "#ffffff", + "focus": false, + "black_puck": true + }, + { + "name": "France", + "size": 8, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + null, + "", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#e32119", + "#e32119", + null, + "", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#e32119", + "#e32119", + null, + "", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#e32119", + "#e32119", + null, + "", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#e32119", + "#e32119", + null, + "", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#e32119", + "#e32119", + null, + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Smiley", + "size": 8, + "bricks": [ + "", + null, + null, + null, + null, + null, + null, + null, + null, + "#ffd300", + "#ffd300", + null, + null, + "#ffd300", + "#ffd300", + null, + null, + "#ffd300", + "black", + null, + null, + "black", + "#ffd300", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#ffd300", + null, + null, + null, + null, + "#ffd300", + null, + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + null, + null, + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "focus": false + }, + { + "name": "Labirynth", + "size": 11, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#F44848", + "", + "#F44848", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#F44848", + "", + "", + null, + "", + "", + "", + "", + "", + "#5DA3EA", + "", + "", + "", + "#F44848", + null, + "", + "#F44848", + "#5DA3EA", + "#5DA3EA", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + null, + "", + "", + "", + "#5DA3EA", + "", + "", + "", + "", + "", + "#F44848", + null, + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#F44848", + "", + "#F44848", + "", + "", + null, + "", + "#5DA3EA", + "", + "", + "", + "#5DA3EA", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#F44848", + "", + "#5DA3EA", + "", + "", + "", + "", + "#5DA3EA", + "#F44848", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "", + "", + "", + null, + null, + null, + null, + "#F44848", + "", + "#F44848" + ], + "svg": "", + "focus": false + }, + { + "name": "Temple", + "size": 11, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "white", + "white", + "white", + "", + "", + "", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "", + "", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "", + "", + "", + "#A664E8", + "", + "#A664E8", + "", + "#A664E8", + "", + "#A664E8", + "", + "", + "", + "", + "#E869E8", + "", + "#E869E8", + "", + "#E869E8", + "", + "#E869E8", + "", + "", + "", + "", + "#E66BA8", + "", + "#E66BA8", + "", + "#E66BA8", + "", + "#E66BA8", + "", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Pacman", + "size": 12, + "bricks": [ + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + null, + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "black", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "#F44848", + "", + "#F44848", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + null, + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + null, + null, + null, + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Ship", + "size": 11, + "bricks": [ + "", + "", + "", + "", + "#E67070", + "white", + "white", + "", + "", + "", + null, + "", + "", + "", + "", + "#E67070", + "white", + "white", + "white", + "", + "", + null, + "", + "", + "", + "", + "#E67070", + "white", + "white", + "white", + "", + "", + null, + "", + "", + "", + "", + "#E67070", + "", + "", + "", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "", + "#F29E4A", + "black", + "#F29E4A", + "black", + "#F29E4A", + "black", + "#F29E4A", + "black", + "#F29E4A", + "#F29E4A", + "", + "", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + null, + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#333", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#333", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#333", + "#333", + "#6262EA", + "#6262EA", + "#6262EA", + "#333", + "#333", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA" + ], + "svg": "", + "focus": false + }, + { + "name": "We come in peace", + "size": 13, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "#5BECEC", + "", + "", + "", + "", + "", + "#5BECEC", + "", + "", + null, + "", + "", + "", + "", + "#5BECEC", + "", + "", + "", + "#5BECEC", + "", + "", + "", + null, + "", + "", + "", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "", + "", + null, + "", + "", + "#5BECEC", + "#5BECEC", + "black", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "black", + "#5BECEC", + "#5BECEC", + "", + null, + "", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + null, + "", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + null, + "", + "#5BECEC", + "", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "", + "#5BECEC", + null, + "", + "#5BECEC", + "", + "#5BECEC", + "", + "", + "", + "", + "", + "#5BECEC", + "", + "#5BECEC", + null, + "", + "", + "", + "", + "#5BECEC", + "#5BECEC", + "", + "#5BECEC", + "#5BECEC", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Space mushroom", + "size": 10, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "white", + "white", + "", + "", + "", + null, + "", + "", + "", + "white", + "white", + "white", + "white", + "", + "", + null, + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "", + null, + "", + "white", + "white", + "black", + "white", + "white", + "black", + "white", + "white", + null, + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + null, + "", + "", + "", + "white", + "", + "", + "white", + "", + "", + null, + "", + "", + "white", + "", + "white", + "white", + "", + "white", + "", + null, + "", + "white", + "", + "white", + "", + "", + "white", + "", + "white" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Sonic", + "size": 16, + "bricks": [ + "", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "#231f20", + "#231f20", + "", + null, + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#231f20", + "#231f20", + "#5DA3EA", + "#231f20", + "", + null, + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#231f20", + "#F29E4A", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#231f20", + "", + null, + "", + "", + "#231f20", + "#5DA3EA", + "#231f20", + "#F29E4A", + "#F29E4A", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#231f20", + "#231f20", + null, + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#231f20", + "#F29E4A", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + null, + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "white", + "#5DA3EA", + "white", + "white", + null, + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "#A1F051", + "#5DA3EA", + "white", + "#A1F051", + null, + "", + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "#A1F051", + "#5DA3EA", + "white", + "#231f20", + null, + "", + "", + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "#A1F051", + "white", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "white", + "white", + "white", + "#F29E4A", + "#F29E4A", + "#231f20", + null, + "", + "#231f20", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#231f20", + "", + null, + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#F29E4A", + "#231f20", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "#57e389", + "focus": false + }, + { + "name": "Small heart", + "size": 15, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "#ab0c0c", + "#e32119", + "white", + "white", + "#e32119", + "#ab0c0c", + "", + "#ab0c0c", + "white", + "white", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "#ab0c0c", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#e32119", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Eye", + "size": 9, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + "", + "", + "#231f20", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "#231f20", + "white", + "#6262EA", + "#6262EA", + "#6262EA", + "white", + "#231f20", + "", + "#231f20", + "white", + "white", + "#6262EA", + "black", + "#6262EA", + "white", + "white", + "#231f20", + "", + "#231f20", + "white", + "#6262EA", + "#6262EA", + "#6262EA", + "white", + "#231f20", + "", + "", + "", + "#231f20", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "#99c1f1", + "focus": false + }, + { + "name": "enderman", + "size": 10, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "#231f20", + "#333", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#333", + "#231f20", + "", + "", + "#231f20", + "#333", + "#333", + "#231f20", + "#231f20", + "#333", + "#333", + "#231f20", + "", + "", + "#333", + "#231f20", + "#333", + "#333", + "#333", + "#333", + "#231f20", + "#333", + "", + "", + "#231f20", + "#231f20", + "#333", + "#333", + "#333", + "#333", + "#231f20", + "#231f20", + "", + "", + "#E66BA8", + "#E869E8", + "#E66BA8", + "#333", + "#333", + "#E66BA8", + "#E869E8", + "#E66BA8", + "", + "", + "#333", + "#231f20", + "#231f20", + "#333", + "#333", + "#231f20", + "#231f20", + "#333", + "", + "", + "#231f20", + "#333", + "#333", + "#231f20", + "#231f20", + "#333", + "#333", + "#231f20", + "", + "", + "#333", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#333", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "#9141ac", + "focus": false, + "squared": true + }, + { + "name": "Mushroom", + "size": 16, + "bricks": [ + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "#231f20", + "#231f20", + "", + "", + "", + "", + "", + "#231f20", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "#231f20", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "#231f20", + "", + "", + "#231f20", + "white", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "white", + "white", + "white", + "#231f20", + "", + "#231f20", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#231f20", + "#231f20", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "white", + "white", + "#e32119", + "#231f20", + "#231f20", + "white", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "white", + "white", + "white", + "white", + "#231f20", + "#231f20", + "white", + "white", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "#231f20", + "#231f20", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "#e32119", + "#231f20", + "#231f20", + "white", + "#e32119", + "#e32119", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#e32119", + "#e32119", + "#e32119", + "#231f20", + "", + "#231f20", + "#231f20", + "#231f20", + "white", + "white", + "#231f20", + "white", + "white", + "#231f20", + "white", + "white", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + "#231f20", + "white", + "white", + "white", + "#231f20", + "white", + "white", + "#231f20", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "", + "#231f20", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "", + "", + "#231f20", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#231f20", + "", + "", + "", + "", + "", + "", + "", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "#231f20", + "", + "", + "", + "" + ], + "svg": "", + "color": "#62a0ea", + "focus": false, + "black_puck": false + }, + { + "name": "tulip", + "size": 11, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "", + "", + "#ab0c0c", + "", + "#ab0c0c", + "", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Gold chain", + "size": 7, + "bricks": [ + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "black", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "black", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "black", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Marion", + "size": 9, + "bricks": [ + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + "", + "", + "#ab0c0c", + "#ab0c0c", + "", + "#e32119", + "#ab0c0c", + "", + "", + "", + "#ab0c0c", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "#ab0c0c", + "", + "#ab0c0c", + "#e32119", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "#e32119", + "#ab0c0c", + "#e32119", + "#e32119", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "", + "#e32119", + "", + "#e32119", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "", + "", + "", + "#e32119", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "", + "", + "", + "#e32119", + "#e32119", + null, + "", + "#e32119", + "#e32119", + "", + "", + "", + "#e32119", + "#e32119", + null, + "#ab0c0c", + "#e32119", + "#e32119", + null, + null, + null, + "#e32119", + "#e32119", + "#ab0c0c" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Renan", + "size": 9, + "bricks": [ + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "pairs", + "size": 8, + "bricks": [ + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + "#5DA3EA", + null, + null, + null, + null, + null, + null, + null, + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA", + null, + "#6262EA" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "cups", + "size": 11, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + null, + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "", + "#e32119", + "black", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "cactus", + "size": 10, + "bricks": [ + null, + "", + "", + "", + "#A1F051", + "", + "", + "", + "", + "", + "", + "", + "#A1F051", + "", + "#A1F051", + "#618227", + "", + "", + "", + "", + "", + "", + "#A1F051", + "", + "#A1F051", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "", + "#A1F051", + "", + "", + "", + "", + "", + "", + "#A1F051", + "#618227", + "#A1F051", + "#618227", + "", + "", + "", + "", + "", + "", + "#A1F051", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "#A1F051", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "sun", + "size": 11, + "bricks": [ + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + null, + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + null, + "#ffd300", + "#ffd300", + "#ffd300", + "white", + "white", + "#ffd300", + "white", + "white", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "white", + "white", + "white", + "white", + "white", + "#ffd300", + "#ffd300", + null, + "", + "#ffd300", + "#ffd300", + "#ffd300", + "white", + "white", + "white", + "#ffd300", + "#ffd300", + "#ffd300", + null, + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + null, + null, + null, + null, + null, + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "focus": false, + "color": "#62a0ea", + "squared": true + }, + { + "name": "Mountain", + "size": 9, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "", + "", + "", + "", + "", + "white", + "", + "", + "", + "", + "", + "", + "", + "white", + "white", + "white", + "", + "", + "", + "", + "", + "", + "#A1F051", + "#A1F051", + "white", + "white", + "", + "", + "white", + "", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "", + "#618227", + "#618227", + "#618227", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#A1F051", + "#A1F051", + "#A1F051", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Dollar", + "size": 17, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#618227", + "", + "#618227", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "#f6d32d", + "focus": false + }, + { + "name": "Waves", + "size": 8, + "bricks": [ + "", + "", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "", + "", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "", + "", + "", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5BECEC", + "#5BECEC", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5BECEC", + "#5BECEC", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC", + "#5BECEC" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Box", + "size": 8, + "bricks": [ + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "#ffd300", + "#ffd300", + "", + "#6262EA", + "", + "", + "#6262EA", + "", + "#ffd300", + "#ffd300", + "", + "#6262EA", + "", + "", + "#6262EA", + "", + "#ffd300", + "#ffd300", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300" + ], + "svg": "", + "color": "", + "focus": false, + "squared": false + }, + { + "name": "Rose", + "size": 9, + "bricks": [ + "", + "", + "#F44848", + "#F44848", + "", + "", + "", + "", + null, + "", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "", + "", + "", + null, + "", + "#e32119", + "#F44848", + "#F44848", + "#ab0c0c", + "", + "", + "", + null, + "", + "#e32119", + "#e32119", + "#ab0c0c", + "#ab0c0c", + "", + "", + "", + null, + "", + "", + "#e32119", + "#ab0c0c", + "", + "#A1F051", + "", + "", + null, + "", + "", + "", + "#618227", + "", + "#A1F051", + "#618227", + "", + null, + "", + "", + "", + "#618227", + "#618227", + "", + "#618227", + "", + null, + "", + "", + "", + "", + "#618227", + "#618227", + "", + "", + null, + null, + null, + null, + null, + null, + "#618227" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Time", + "size": 9, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "white", + "#ffd300", + "white", + "", + "", + "", + "", + "", + "white", + "#ffd300", + "#ffd300", + "#ffd300", + "white", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "", + "focus": false, + "squared": false + }, + { + "name": "Watermelon", + "size": 8, + "bricks": [ + "", + "", + "", + "", + "", + "#F44848", + "#618227", + "", + "", + "", + "", + "", + "#F44848", + "#F44848", + "black", + "#618227", + "", + "", + "", + "#F44848", + "black", + "#F44848", + "#F44848", + "#618227", + "", + "", + "#F44848", + "#F44848", + "#F44848", + "#F44848", + "#A1F051", + "#618227", + "", + "#F44848", + "#F44848", + "black", + "#F44848", + "#F44848", + "#618227", + "", + "#F44848", + "black", + "#F44848", + "#F44848", + "#A1F051", + "#A1F051", + "#618227", + "", + "#618227", + "#618227", + "#A1F051", + "#A1F051", + "#618227", + "#618227", + "", + "", + "", + "#618227", + "#618227", + "#618227", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Worms", + "size": 13, + "bricks": [ + null, + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "", + "white", + "white", + "#E67070", + "white", + "white", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "white", + "black", + "#E67070", + "black", + "white", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "white", + "black", + "#E67070", + "black", + "white", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "white", + "white", + "#E67070", + "white", + "white", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "white", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "", + "", + "white", + "#E67070", + "#E67070", + "white", + "#E67070", + "", + "", + "#E67070", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "#E67070" + ], + "svg": "\n \n \n \n \n\n", + "color": "", + "focus": false, + "squared": false + }, + { + "name": "sunrise", + "size": 8, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "#e32119", + "#ffd300", + "#ffd300", + "#e32119", + "", + "", + "", + "#e32119", + "#ffd300", + "white", + "white", + "#ffd300", + "#e32119", + "", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5BECEC", + "#5BECEC", + "#5DA3EA", + "#6262EA", + "#6262EA", + "", + "#6262EA", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "", + "", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "crosses", + "size": 13, + "bricks": [ + "#6262EA", + "", + "", + "", + "#6262EA", + "", + "", + "", + "#6262EA", + "", + "", + "", + "#6262EA", + "", + "", + "#A664E8", + "", + "", + "", + "#A664E8", + "", + "", + "", + "#A664E8", + "", + "", + "", + "#A664E8", + "#A664E8", + "#A664E8", + "", + "#A664E8", + "#A664E8", + "#A664E8", + "", + "#A664E8", + "#A664E8", + "#A664E8", + "", + "", + "", + "#A664E8", + "", + "", + "", + "#A664E8", + "", + "", + "", + "#A664E8", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "#E869E8", + "#E869E8", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8", + "", + "", + "", + "#E869E8" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "Negative space", + "size": 9, + "bricks": [ + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "#6262EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false + }, + { + "name": "UK", + "size": 11, + "bricks": [ + "#6262EA", + "#e32119", + "#6262EA", + "#6262EA", + "white", + "#e32119", + "white", + "#6262EA", + "#6262EA", + "#e32119", + "#6262EA", + "#6262EA", + "#6262EA", + "#e32119", + "#6262EA", + "white", + "#e32119", + "white", + "#6262EA", + "#e32119", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#e32119", + "white", + "#e32119", + "white", + "#e32119", + "#6262EA", + "#6262EA", + "#6262EA", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "#6262EA", + "#6262EA", + "#6262EA", + "#e32119", + "white", + "#e32119", + "white", + "#e32119", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#6262EA", + "#e32119", + "#6262EA", + "white", + "#e32119", + "white", + "#6262EA", + "#e32119", + "#6262EA", + "#6262EA", + "#6262EA", + "#e32119", + "#6262EA", + "#6262EA", + "white", + "#e32119", + "white", + "#6262EA", + "#6262EA", + "#e32119", + "#6262EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Greece", + "size": 11, + "bricks": [ + "#5DA3EA", + "#5DA3EA", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "white", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "squared": true, + "color": "" + }, + { + "name": "Russia", + "size": 8, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "white", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "squared": true, + "color": "" + }, + { + "name": "Ukraine", + "size": 8, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "" + }, + { + "name": "Poland", + "size": 7, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "white", + "white", + "white", + "white", + "white", + "", + "", + "white", + "white", + "white", + "white", + "white", + "", + "", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "", + "squared": true + }, + { + "name": "yellow 71", + "size": 9, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "71 on white", + "size": 6, + "bricks": [ + "white", + "white", + "white", + "white", + "white", + "white", + "#e32119", + "#e32119", + "#e32119", + "white", + "white", + "#e32119", + "white", + "white", + "#e32119", + "white", + "#e32119", + "#e32119", + "white", + "#e32119", + "white", + "white", + "white", + "#e32119", + "white", + "#e32119", + "white", + "white", + "white", + "#e32119", + "white", + "white", + "white", + "white", + "white", + "white", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false + }, + { + "name": "blue 71", + "size": 8, + "bricks": [ + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "", + "#6262EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "#5DA3EA", + "", + "#6262EA", + "#6262EA", + null, + null, + "", + "#5DA3EA", + "#5DA3EA", + "#6262EA", + "#6262EA", + "#6262EA", + null, + null, + "#5DA3EA", + "#5DA3EA", + "", + null, + "#6262EA", + "#6262EA", + null, + "", + "#5DA3EA", + "#5DA3EA", + "", + null, + "#6262EA", + "#6262EA", + null, + "#5DA3EA", + "#5DA3EA", + "", + null, + null, + "#6262EA", + "#6262EA", + "", + "#5DA3EA", + "#5DA3EA", + "", + null, + null, + "#6262EA", + "#6262EA", + "", + "#5DA3EA", + "#5DA3EA", + null, + null, + null, + "#6262EA", + "#6262EA" + ], + "svg": "", + "color": "", + "focus": false + }, + { + "name": "seventy one", + "size": 21, + "bricks": [ + "#e32119", + "#e32119", + "", + "#ffd300", + "#ffd300", + "", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "", + "", + "#ffd300", + "", + "#ffd300", + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "", + "#e32119", + null, + "#e32119", + "#e32119", + "", + "#ffd300", + "#ffd300", + "", + "#e32119", + "#e32119", + "", + "#ffd300", + "#ffd300", + "", + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "", + "#e32119", + null, + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "", + "", + "#ffd300", + "", + "#ffd300", + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "", + "#e32119", + null, + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "#e32119", + null, + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#e32119", + "", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "#ffd300", + "", + "", + "#ffd300", + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "#ffd300", + "#e32119", + "", + "", + "#ffd300", + "", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#ffd300", + "#ffd300", + null, + "#e32119", + "", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "", + "#ffd300", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false + }, + { + "name": "B71", + "size": 10, + "bricks": [ + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#e32119", + "#e32119", + "#e32119", + "#ffd300", + "#ffd300", + "#ffd300", + "#ffd300", + "", + "#e32119", + "", + "#e32119", + "", + "", + "#e32119", + "", + "", + "#ffd300", + "#e32119", + "#e32119", + "", + "#e32119", + "", + "", + "#e32119", + "", + "", + "#ffd300", + "", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "#ffd300", + "", + "", + "#e32119", + "", + "#e32119", + "", + "", + "#e32119", + "", + "#ffd300", + "", + "", + "#e32119", + "", + "#e32119", + "", + "", + "#e32119", + "#ffd300", + "", + "", + "", + "#e32119", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "#ffd300", + "", + "", + "#e32119", + "#e32119", + "#e32119", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "" + }, + { + "name": "Pig", + "size": 9, + "bricks": [ + null, + "", + "", + "", + "", + "", + "", + "", + "", + "", + "#E66BA8", + "#E66BA8", + "", + "", + "", + "#E66BA8", + "#E66BA8", + "", + "", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "", + "", + "white", + "white", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "white", + "white", + "", + "", + "white", + "black", + "#E66BA8", + "#E66BA8", + "#E66BA8", + "black", + "white", + "", + "", + "#E66BA8", + "#E66BA8", + "#E67070", + "#E67070", + "#E67070", + "#E66BA8", + "#E66BA8", + "", + "", + "#E66BA8", + "#E67070", + "black", + "#E67070", + "black", + "#E67070", + "#E66BA8", + "", + "", + "#E66BA8", + "#E66BA8", + "#E67070", + "#E67070", + "#E67070", + "#E66BA8", + "#E66BA8", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false + }, + { + "name": "Big pig", + "size": 15, + "bricks": [ + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + null, + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "#E67070", + "white", + "black", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "black", + "white", + "#E67070", + "", + "", + "", + "#E67070", + "#E67070", + "black", + "black", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "black", + "black", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "black", + "#E67070", + "black", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "", + "", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "", + "", + "#A1F051", + "#A1F051", + "#A1F051", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#E67070", + "#A1F051", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#E67070", + "#A1F051", + "#E67070", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#E67070", + "#E67070", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#A1F051", + "#E67070", + "#E67070", + "#A1F051", + "#A1F051", + "#A1F051", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "", + "" + ], + "svg": "", + "focus": false, + "color": "", + "squared": true + } +] \ No newline at end of file diff --git a/app/src/main/assets/privacy.html b/app/src/main/assets/privacy.html new file mode 100644 index 0000000..6f1a94f --- /dev/null +++ b/app/src/main/assets/privacy.html @@ -0,0 +1,39 @@ + + + + + + + Breakout privacy policy + + + + + +

Privacy policy

+

+ Breakout 71 is published + by Renan LE CARO, a French citizen and programmer. You can contact me at + this adress : breakout71@lecaro.me +

+

+ If you access breakout.lecaro.me though a web browser, your IP address + will be logged on my server to prevent abuses. + My server is hosted by Hetzner + Online GmbH in germany. +

+

+ If you install the app through google play, no information will + be collected at all by me. +

+ + diff --git a/app/src/main/assets/style.css b/app/src/main/assets/style.css new file mode 100644 index 0000000..f7353b8 --- /dev/null +++ b/app/src/main/assets/style.css @@ -0,0 +1,227 @@ +* { + font-family: + Courier New, + Courier, + Lucida Sans Typewriter, + Lucida Typewriter, + monospace; + box-sizing: border-box; +} + +body { + margin: 0; + padding: 0; + overflow: hidden; + width: 100vw; + height: 100vh; + color: white; + background-size: 120px 120px; + background-color: var(--background1); + --background1: #030c23; + --background2: #03112a; +} + +#game { + position: absolute; + top: 0; + left: 0; + height: 100vh; + width: 100vw; +} +#score, +#menu { + position: absolute; + top: 0; + z-index: 1; + padding: 10px; + appearance: none; + background: none; + border: none; + font: inherit; + color: white; + min-width: 40px; + min-height: 40px; + line-height: 20px; +} +body.black_puck #score, +body.black_puck #menu { + color:black; +} +#score:hover, +#score:focus, +#menu:hover, +#menu:focus { + background: rgba(0,0,0,0.3); + cursor: pointer; +} + +#score { + right: 0; +} +#menu { + left: 0; +} +@media screen and (orientation: portrait) { + #menu > span { + display: none; + } +} + +.popup { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.95); + z-index: 10; + display: flex; + overflow: auto; +} + +.popup > div { + margin: auto; + padding: 20px; + /*border: 1px solid white;*/ + transform-origin: center; + display: flex; + flex-direction: column; + align-items: stretch; + width: 90%; + max-width: 450px; +} + +.popup > div > * { + padding: 0; + margin: 0; +} + +.popup > div > h2, +.popup > div > p { + margin-bottom: 20px; +} +.popup > div > button { + font:inherit; + background: rgba(0, 0, 0, 0.8); + color: white; + padding: 10px; + cursor: pointer; + border: 1px solid white; + text-align: left; + display: flex; + gap: 10px; + margin-top: -1px; +} + +.popup > div > button:not([disabled]):hover, +.popup > div > button:not([disabled]):focus { + border-color: #f1d33b; + position: relative; + z-index: 1; +} + + +.popup button.close-modale { + color:white; + position: absolute; + top:0; + right:0; + width: 60px; + height: 60px; + background:none; + border: none; + cursor: pointer; + background: rgba(0,0,0,0.2); + overflow: hidden; +} +.popup button.close-modale:before { + content: "+"; + position: absolute; + transform: translate(-50%, -50%) rotate(45deg) ; + font-size: 80px; + display: inline-block; +} +.popup button.close-modale:hover { + font-weight: bold; +} + +.popup > div > button[disabled] { + /*border: 1px solid #666;*/ + opacity: 0.5; +} + +.popup > div > button > div { + flex-grow: 1; +} +.popup > div > button > div > em { + display: block; + opacity: 0.8; +} + +.popup > div > button > span.checks { + width: 40px; + height: 40px; + display: inline-flex; + gap:5px; + flex-grow: 0; + flex-shrink: 0; +} +.popup > div > button > span.checks>span { + flex-basis: 10px; + flex-grow: 1; + flex-shrink: 1; + /*border: 1px solid white;*/ + background: white; + opacity: 0.1; + border-radius: 4px; + align-self: stretch; +} +.popup > div > button > span.checks>span.checked { + opacity: 1; +} + +.popup .textAfterButtons{ + color: rgba(255, 255, 255, 0.58); +} + +.popup a[href]{ + color:inherit; +} +.popup a[href]:hover, +.popup a[href]:focus +{ + color:white; +} + +/*Unlocks progress bar*/ +.progress { + display: block; + padding: 5px 10px; + background: #1c1c2f; + color:#fff; + box-shadow: inset 3px 3px 5px rgba(0,0,0,0.5); + border-radius: 5px; + text-align: center; + position: relative; + overflow: hidden; +} +.progress >.progress_bar_part{ + display: block; + background: #4049ca; + box-shadow: inset 3px 3px 5px rgba(0,0,0,0.5); + left: 0; + position: absolute; + right: 0; + top: 0; + bottom: 0; + transform-origin: top left; + animation: grow 1s both ease-out; + z-index: 1; +} +.progress> span { + display: block; + position: relative; + z-index: 2; +} +@keyframes grow { + 0%{ + transform: scale(0,1); + } +} \ No newline at end of file diff --git a/app/src/main/java/me/lecaro/breakout/MainActivity.kt b/app/src/main/java/me/lecaro/breakout/MainActivity.kt new file mode 100644 index 0000000..d67ba02 --- /dev/null +++ b/app/src/main/java/me/lecaro/breakout/MainActivity.kt @@ -0,0 +1,33 @@ +package me.lecaro.breakout +import android.os.Bundle +import android.util.Log +import android.view.Window +import android.view.WindowManager +import android.webkit.ConsoleMessage +import android.webkit.WebChromeClient +import android.webkit.WebView +class MainActivity : android.app.Activity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + requestWindowFeature(Window.FEATURE_NO_TITLE); + window.setFlags( + WindowManager.LayoutParams.FLAG_FULLSCREEN, + WindowManager.LayoutParams.FLAG_FULLSCREEN + ); +// WebView.setWebContentsDebuggingEnabled(true) + val webView = WebView(this) + webView.settings.javaScriptEnabled = true + webView.settings.domStorageEnabled = true + webView.loadUrl("file:///android_asset/index.html") + webView.webChromeClient = object : WebChromeClient() { + override fun onConsoleMessage(consoleMessage: ConsoleMessage): Boolean { + Log.d( + "WebView", "${consoleMessage.message()} -- From line " + + "${consoleMessage.lineNumber()} of ${consoleMessage.sourceId()}" + ) + return true + } + } + setContentView(webView) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/icon.xml b/app/src/main/res/drawable/icon.xml new file mode 100644 index 0000000..fb58588 --- /dev/null +++ b/app/src/main/res/drawable/icon.xml @@ -0,0 +1,195 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..7123164 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + Breakout + \ No newline at end of file diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml new file mode 100644 index 0000000..fa0f996 --- /dev/null +++ b/app/src/main/res/xml/backup_rules.xml @@ -0,0 +1,13 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml new file mode 100644 index 0000000..9ee9997 --- /dev/null +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -0,0 +1,19 @@ + + + + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..a0985ef --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,5 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +plugins { + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.jetbrainsKotlinAndroid) apply false +} \ No newline at end of file diff --git a/cover.png b/cover.png new file mode 100644 index 0000000000000000000000000000000000000000..0c098b486c7783680450524a71097c1a5c8144b3 GIT binary patch literal 44618 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 XxVCbqLwTwh0v{6tOZ^5t5A1&dUki~s literal 0 HcmV?d00001 diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..78ca82d --- /dev/null +++ b/deploy.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +#Randomly shuffle the version ids +# Generate a random number between 1000 and 9999 +random_number=$(shuf -i 1000-9999 -n 1) + +# Use sed to replace the pattern with the random number +sed -i "s/\?v=[0-9]*/\?v=$random_number/g" ./app/src/main/assets/index.html + + +DOMAIN="breakout.lecaro.me" +PUBLIC_CONTENT="./app/src/main/assets/" +ssh staging "mkdir -p /opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN" +rsync -avz --delete --delete-excluded --exclude="*.sh" --exclude="node_modules" --exclude="android" --exclude=".*" $PUBLIC_CONTENT staging:/opt/mup-nginx-proxy/config/html/static_sites/$DOMAIN + + +# generate zip for itch +rm -f breakout.zip +zip -j breakout.zip app/src/main/assets/* \ No newline at end of file diff --git a/editclient.css b/editclient.css new file mode 100644 index 0000000..2d8069e --- /dev/null +++ b/editclient.css @@ -0,0 +1,29 @@ + +body { + background: black; + color: white; +} + +#palette button.active { + transform: scale(1.2); +} + +.level-bricks-preview { + position: relative; +} + +#palette { + position: fixed; + top: 0; + right: 0; + width: 80px; + bottom: 0; + overflow: auto; +} + +#levels { + display: flex; + gap: 40px; + flex-wrap: wrap; + margin-right: 80px; +} \ No newline at end of file diff --git a/editclient.js b/editclient.js new file mode 100644 index 0000000..f6d961c --- /dev/null +++ b/editclient.js @@ -0,0 +1,274 @@ +let currentColor = '' + +const colorsList = [ + 'white', + 'black', + '', + '#F44848', + '#ab0c0c', + '#F29E4A', + '#F0F04C', + '#A1F051', + '#53EE53', + '#59EEA3', + '#5BECEC', + '#5DA3EA', + '#6262EA', + '#A664E8', + '#E869E8', + '#E66BA8', + '#E67070', + "#333", + '#231f20', + '#e32119', + '#ffd300', + '#e1c8b4', + '#618227' +] +const palette = document.getElementById('palette'); + +colorsList.forEach(color => { + const btn = document.createElement('button') + Object.assign(btn.style, { + background: color, + display: 'inline-block', + width: '40px', + height: '40px', + border: '1px solid black' + }) + if (color === currentColor) { + btn.className = 'active' + } + palette.appendChild(btn) + btn.addEventListener('click', (e) => { + currentColor = color + e.preventDefault() + document.querySelector('#palette button.active')?.classList.remove('active'); + btn.classList.add('active') + }) +}) + +function renderAllLevels() { + allLevels.forEach((level, levelIndex) => { + addLevelEditorToList(level, levelIndex) + + }) +} + +function addLevelEditorToList(level, levelIndex) { + const {name, bricks, size, svg,color} = level + let div = document.createElement('div') + + + div.innerHTML = ` +
+ + + + + + + + + + + + + + +
+ +
+
+ `; + document.getElementById('levels').appendChild(div) + + renderLevelBricks(levelIndex) + updateLevelBackground(levelIndex) + +} + +function updateLevelBackground(levelIndex){ + const div=document.getElementById("bricks-of-"+levelIndex) + const {svg, color}= allLevels[levelIndex] + Object.assign(div.style, svg ? + {backgroundImage:`url('data:image/svg+xml,${encodeURIComponent(svg)}')`, backgroundColor:'transparent'} : + {backgroundImage:'none', backgroundColor:color||'#111'} + ) +} + +function renderLevelBricks(levelIndex) { + const {size, bricks} = allLevels[levelIndex] + + const buttons = [] + for (let x = 0; x < size; x++) { + for (let y = 0; y < size; y++) { + const index = y * size + x + buttons.push(``) + } + } + const div = document.getElementById("bricks-of-" + levelIndex) + div.innerHTML = buttons.join('') + Object.assign(div.style, { + width: size * 40 + 'px', + height: size * 40 + 'px' + }) +} + + + +document.getElementById('levels').addEventListener('change', e => { + const levelIndexStr = e.target.getAttribute('data-level') + if ( levelIndexStr) { + const levelIndex = parseInt(levelIndexStr) + const level = allLevels[levelIndex] + if( e.target.getAttribute('type') === 'color'){ + + level.color = e.target.value + level.svg = '' + updateLevelBackground(levelIndex) + }else if( e.target.getAttribute('type') === 'checkbox' && e.target.hasAttribute('data-field')){ + level[e.target.getAttribute('data-field')] = !!e.target.checked + } + + save() + } + +}) +document.getElementById('levels').addEventListener('click', e => { + const resize = e.target.getAttribute('data-offset-level-size') + const moveX = e.target.getAttribute('data-offset-x') + const moveY = e.target.getAttribute('data-offset-y') + const levelIndexStr = e.target.getAttribute('data-level') + if (!levelIndexStr) return + if (e.target.tagName!=='BUTTON') return + + + const levelIndex = parseInt(levelIndexStr) + const level = allLevels[levelIndex] + const {bricks, size} = level; + + if (resize) { + const newSize = size + parseInt(resize) + const newBricks = [] + for (let x = 0; x < Math.min(size, newSize); x++) { + for (let y = 0; y < Math.min(size, newSize); y++) { + newBricks[y * newSize + x] = bricks[y * size + x] || '' + } + } + + level.size = newSize; + level.bricks = newBricks; + } else if (moveX && moveY) { + const dx = parseInt(moveX), dy = parseInt(moveY) + const moved = [] + for (let x = 0; x < size; x++) { + for (let y = 0; y < size; y++) { + moved[(y + dy) * size + (x + dx)] = bricks[y * size + x] || '' + } + } + + level.bricks = moved; + } else if (e.target.getAttribute('data-rename')) { + const newName = prompt('Name ? ', level.name) + if (newName) { + level.name = newName + e.target.textContent = newName + } + }else if (e.target.getAttribute('data-delete')) { + + if (confirm('Delete level')) { + allLevels=allLevels.filter((l,i)=>i!==levelIndex) + save().then(()=>window.location.reload()) + } + }else if(e.target.getAttribute('data-set-bg-svg')){ + const newBg = prompt('New svg code',level.svg||'') + if(newBg){ + level.svg=newBg + level.color = '' + } + + updateLevelBackground(levelIndex) + } + renderLevelBricks(levelIndex) + save() + + +}, true) + +let applying = undefined + +function colorPixel(e) { + if (typeof applying === 'undefined') return + const index = e.target.getAttribute('data-set-color-of') + const level = e.target.getAttribute('data-level') + if (index && level) { + const levelIndex = parseInt(level) + e.target.style.background = applying || 'transparent' + allLevels[levelIndex].bricks[index] = applying + } +} + +document.getElementById('levels').addEventListener('mousedown', e => { + const index = e.target.getAttribute('data-set-color-of') + const level = e.target.getAttribute('data-level') + if (index && level) { + const before = allLevels[parseInt(level)].bricks[parseInt(index)] || '' + applying = before === currentColor ? '' : currentColor + colorPixel(e) + } +}) + +document.getElementById('levels').addEventListener('mouseenter', e => { + if (typeof applying !== undefined) { + + colorPixel(e) + } +}, true) + +document.addEventListener('mouseup', e => { + applying = undefined + save() +}) + + +document.getElementById('new-level').addEventListener('click', e => { + + const name = prompt("Name ? ") + if (!name) return; + + allLevels.push({ + name, + size: 8, + bricks: ['white'], + svg: '', + }) + const levelIndex = allLevels.length - 1 + addLevelEditorToList(allLevels[levelIndex], levelIndex) + save() +}, true) + +renderAllLevels() + + +function save() { + return fetch('/', { + method: 'POST', headers: { + 'Content-Type': 'text/plain' + }, + body: 'let allLevels=' + JSON.stringify(allLevels, null, 2) + }) +} diff --git a/editserver.js b/editserver.js new file mode 100644 index 0000000..d9ff80b --- /dev/null +++ b/editserver.js @@ -0,0 +1,50 @@ +const express = require('express') +const bodyParser = require('body-parser'); +const fs = require('fs') +const app = express() +const port = 4400 + +const srcPath = 'app/src/main/assets/levels.js' +app.use(bodyParser.text({ + type: 'text/plain', + limit:'1MB' +})); + + +app.get('/', (req, res) => { + res.end(` + + + + Level editor + + + + +
+
+ + +
+ + + + + + `) +}) +app.post('/', (req, res) => { + console.log(req.body) + if(req.body?.trim()) { + fs.writeFileSync(srcPath, req.body) + } + res.end('OK') +}) + +app.listen(port, () => { + console.log(`Example app listening on port http://localhost:${port}`) +}) diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..20e2a01 --- /dev/null +++ b/gradle.properties @@ -0,0 +1,23 @@ +# Project-wide Gradle settings. +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. For more details, visit +# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects +# org.gradle.parallel=true +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Kotlin code style for this project: "official" or "obsolete": +kotlin.code.style=official +# Enables namespacing of each library's R class so that its R class includes only the +# resources declared in the library itself and none from the library's dependencies, +# thereby reducing the size of the R class for that library +android.nonTransitiveRClass=true \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..a641cb5 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,31 @@ +[versions] +agp = "8.3.1" +kotlin = "1.9.0" +coreKtx = "1.13.1" +junit = "4.13.2" +junitVersion = "1.1.5" +espressoCore = "3.5.1" +lifecycleRuntimeKtx = "2.6.1" +activityCompose = "1.7.0" +composeBom = "2023.08.00" + +[libraries] +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } +androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } +androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } +androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } +androidx-ui = { group = "androidx.compose.ui", name = "ui" } +androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } +androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" } +androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" } +androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" } +androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } +androidx-material3 = { group = "androidx.compose.material3", name = "material3" } + +[plugins] +androidApplication = { id = "com.android.application", version.ref = "agp" } +jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..e708b1c023ec8b20f512888fe07c5bd3ff77bb8f GIT binary patch literal 59203 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

K7UpUK$>l@ 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 E2O(~oYXATM literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..b080ee6 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Sun Jan 05 13:08:47 CET 2025 +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..4f906e0 --- /dev/null +++ b/gradlew @@ -0,0 +1,185 @@ +#!/usr/bin/env sh + +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin or MSYS, switch paths to Windows format before running java +if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=`expr $i + 1` + done + case $i in + 0) set -- ;; + 1) set -- "$args0" ;; + 2) set -- "$args0" "$args1" ;; + 3) set -- "$args0" "$args1" "$args2" ;; + 4) set -- "$args0" "$args1" "$args2" "$args3" ;; + 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=`save "$@"` + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..ac1b06f --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/icon.png b/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..a3a77ee16ef3bdee2089c853ccfed74f7bef8f6e GIT binary patch literal 12547 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 0.6" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==" + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/body-parser": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz", + "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==", + "dependencies": { + "bytes": "3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "on-finished": "2.4.1", + "qs": "6.13.0", + "raw-body": "2.5.2", + "type-is": "~1.6.18", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz", + "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.21.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz", + "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "1.20.3", + "content-disposition": "0.5.4", + "content-type": "~1.0.4", + "cookie": "0.7.1", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "1.3.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.12", + "proxy-addr": "~2.0.7", + "qs": "6.13.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "0.19.0", + "serve-static": "1.16.2", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz", + "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "2.4.1", + "parseurl": "~1.3.3", + "statuses": "2.0.1", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "function-bind": "^1.1.2", + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ignore-by-default": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz", + "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/nodemon": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.9.tgz", + "integrity": "sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==", + "dependencies": { + "chokidar": "^3.5.2", + "debug": "^4", + "ignore-by-default": "^1.0.1", + "minimatch": "^3.1.2", + "pstree.remy": "^1.1.8", + "semver": "^7.5.3", + "simple-update-notifier": "^2.0.0", + "supports-color": "^5.5.0", + "touch": "^3.1.0", + "undefsafe": "^2.0.5" + }, + "bin": { + "nodemon": "bin/nodemon.js" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/nodemon" + } + }, + "node_modules/nodemon/node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/nodemon/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.3.tgz", + "integrity": "sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz", + "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pstree.remy": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz", + "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==" + }, + "node_modules/qs": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", + "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "dependencies": { + "side-channel": "^1.0.6" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz", + "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "2.0.0", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "2.4.1", + "range-parser": "~1.2.1", + "statuses": "2.0.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/serve-static": { + "version": "1.16.2", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz", + "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.19.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/touch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.1.tgz", + "integrity": "sha512-r0eojU4bI8MnHr8c5bNo7lJDdI2qXlWWJk6a9EAFG7vbhTjElYhBVS3/miuE0uOuoLdb8Mc/rVfsmm6eo5o9GA==", + "bin": { + "nodetouch": "bin/nodetouch.js" + } + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/undefsafe": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz", + "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c9a817b --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "name": "breakout.lecaro.me", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "start": "nodemon editserver.js --watch editserver.js " + }, + "author": "", + "license": "ISC", + "dependencies": { + "body-parser": "^1.20.3", + "express": "^4.21.2", + "nodemon": "^3.1.9" + } +} diff --git a/patterns.html b/patterns.html new file mode 100644 index 0000000..7f7dfb3 --- /dev/null +++ b/patterns.html @@ -0,0 +1,163 @@ + + + + + Patterns preview + + + + + + + \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..73efb8a --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,24 @@ +pluginManagement { + repositories { + google { + content { + includeGroupByRegex("com\\.android.*") + includeGroupByRegex("com\\.google.*") + includeGroupByRegex("androidx.*") + } + } + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} + +rootProject.name = "Breakout" +include(":app") + \ No newline at end of file