diff --git a/Readme.md b/Readme.md
index 3322d53..2869777 100644
--- a/Readme.md
+++ b/Readme.md
@@ -32,7 +32,8 @@ languages, I may add features again.
- measured and improve the performance (test here https://breakout.lecaro.me/?stresstest)
- added a few levels
- autoplay mode (with wake lock and computer play https://breakout.lecaro.me/?autoplay )
-- slower coins fall once they are past the paddle
+- Added particle and sound effect when coin drops below the "waterline" of the puck
+- slower coins fall once they are under the paddle
- in game level editor
- allow loading newer save in outdated app (for rollback)
- game crashes when reaching level 12 (no level info in runLevels)
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index efb6205..c424b46 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -29,8 +29,8 @@ android {
applicationId = "me.lecaro.breakout"
minSdk = 21
targetSdk = 34
- versionCode = 29077593
- versionName = "29077593"
+ versionCode = 29079087
+ versionName = "29079087"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
diff --git a/app/src/main/assets/index.html b/app/src/main/assets/index.html
index 3e43235..f443120 100644
--- a/app/src/main/assets/index.html
+++ b/app/src/main/assets/index.html
@@ -1 +1 @@
-
Breakout 71
\ No newline at end of file
+Breakout 71
\ No newline at end of file
diff --git a/dist/index.html b/dist/index.html
index f52e942..ac01f1f 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -965,16 +965,16 @@ 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 tick() {
- startWork('tick init');
+ startWork("tick init");
const currentTick = performance.now();
const timeDeltaMs = currentTick - gameState.lastTick;
gameState.lastTick = currentTick;
let frames = Math.min(4, timeDeltaMs / (1000 / 60));
if (gameState.keyboardPuckSpeed) (0, _gameStateMutators.setMousePos)(gameState, gameState.puckPosition + gameState.keyboardPuckSpeed);
if (gameState.perks.superhot) frames *= (0, _pureFunctions.clamp)(Math.abs(gameState.puckPosition - gameState.lastPuckPosition) / 5, 0.2 / gameState.perks.superhot, 1);
- startWork('normalizeGameState');
+ startWork("normalizeGameState");
(0, _gameStateMutators.normalizeGameState)(gameState);
- startWork('gameStateTick');
+ startWork("gameStateTick");
if (gameState.running) {
gameState.levelTime += timeDeltaMs * frames;
gameState.runStatistics.runTime += timeDeltaMs * frames;
@@ -984,11 +984,11 @@ function tick() {
gameState.needsRender = false;
(0, _render.render)(gameState);
}
- startWork('recordOneFrame');
+ startWork("recordOneFrame");
if (gameState.running) (0, _recording.recordOneFrame)(gameState);
- startWork('playPendingSounds');
+ startWork("playPendingSounds");
if ((0, _options.isOptionOn)("sound")) (0, _sounds.playPendingSounds)(gameState);
- startWork('idle');
+ startWork("idle");
requestAnimationFrame(tick);
FPSCounter++;
}
@@ -1001,7 +1001,7 @@ setInterval(()=>{
const showStats = window.location.search.includes("stress");
let total = {};
let lastTick = performance.now();
-let doing = '';
+let doing = "";
function startWork(what) {
if (!showStats) return;
const newNow = performance.now();
@@ -1011,7 +1011,7 @@ function startWork(what) {
}
if (showStats) setInterval(()=>{
const totalTime = (0, _gameUtils.sumOfValues)(total);
- console.debug((0, _gameStateMutators.liveCount)(gameState.coins) + ' coins\n' + Object.entries(total).sort((a, b)=>b[1] - a[1]).filter((a)=>a[1] > 1).map((t)=>t[0] + ':' + (t[1] / totalTime * 100).toFixed(2) + '% (' + t[1] + 'ms)').join('\n'));
+ console.debug((0, _gameStateMutators.liveCount)(gameState.coins) + " coins\n" + Object.entries(total).sort((a, b)=>b[1] - a[1]).filter((a)=>a[1] > 1).map((t)=>t[0] + ":" + (t[1] / totalTime * 100).toFixed(2) + "% (" + t[1] + "ms)").join("\n"));
total = {};
}, 2000);
setInterval(()=>{
@@ -1439,7 +1439,7 @@ function restart(params) {
}
if (window.location.search.match(/autoplay|stress/)) {
startComputerControlledGame();
- if (!(0, _options.isOptionOn)('show_fps')) (0, _options.toggleOption)('show_fps');
+ if (!(0, _options.isOptionOn)("show_fps")) (0, _options.toggleOption)("show_fps");
} else restart({});
function startComputerControlledGame() {
const perks = {
@@ -1523,7 +1523,7 @@ module.exports = JSON.parse("{\"_\":\"\",\"B\":\"black\",\"W\":\"#FFFFFF\",\"g\"
module.exports = JSON.parse('[{"name":"71 mini","size":5,"bricks":"bbb____bt__btt__b_t___ttt","color":""},{"name":"Butterfly","bricks":"_________bb_t_t_bbbbb_t_bbbbbbbtbbbb_bbbtbbb____btb____bbbtbbb__bb_t_bb__________","size":9,"color":""},{"name":"Castle","size":7,"bricks":"s_s_s_ssssssssssBBBssssBBBssttbbbttttbbbtttbtbtbt","color":""},{"name":"Eyes","size":9,"bricks":"ttttttt__tWWWWWWW_tWrrWttW_tWWWWWWW_ttttttt_____t______ttttt____ttttt_____t_t","color":"","credit":"My favorite character in https://nuclearthrone.com/"},{"name":"Creeper","size":10,"bricks":"___________ccGGccGG__cGccGcGc__GBBccBBc__cBBGcBBc__GccBBGGc__ccBBBBcG__GGBBBBcG__cGBccBGc___________","credit":"https://en.wikipedia.org/wiki/Creeper_(Minecraft)","color":""},{"name":"Stairs","size":8,"bricks":"tt______tt______bbtt____bbtt____vvbbtt__vvbbtt__ppvvbbttppvvbbtt","color":""},{"name":"Dots","size":9,"bricks":"b_t_a_c_c__________b_t_a_c__________P_b_t_a_c__________P_b_t_a__________P_P_b_t_a","color":""},{"name":"Lines","size":9,"bricks":"aaaaaaaa___________tttttttt_________aaaaaaaa___________tttttttt_________aaaaaaaa","color":""},{"name":"Heart","size":15,"bricks":"__________________RRR___RRR_____RSSSR_RSSSR___RSWWSSRSSSSSR__RSWSSSSSSSSSR__RSSSSSSSSSSSR__RSWSSSSSSSSSR___RSSSSSSSSSR_____RSSSSSSSR_______RSSSSSR_________RSSSR___________RSR_____________R____________________________________","color":"","credit":"https://www.youtube.com/watch?v=gdWiTfzXb1g"},{"name":"Swiss","size":7,"bricks":"________RRRRR__RRWRR__RWWWR__RRWRR__RRRRR________","color":""},{"name":"Germany","size":4,"bricks":"____ggggrrrryyyy","color":"#5da3ea"},{"name":"France","size":6,"bricks":"______ttWWrrttWWrrttWWrrttWWrrttWWrr","color":""},{"name":"Smiley","size":8,"bricks":"_________yy__yy__yy__yy__________________yyyyyy___yyyy__________","color":""},{"name":"Labyrinthe","size":11,"bricks":"_______tttS_Stttt_S________t___S__Stt_ttttt____t_____S__ttt_S_S____t___t_tttt_t_S_t____tSt_t_t_Sttt___t_t_____Sttt_tttttS"},{"name":"Temple","size":11,"bricks":"_______________WWW______WWWWWWW___WWWWWWWWW___b_b_b_b____b_b_b_b____v_v_v_v____P_P_P_P____P_P_P_P____WWWWWWW___WWWWWWWWW_","color":""},{"name":"Pacman","size":12,"bricks":"____yyyy______yyyyyyyy___yyyyByyyyy__yyyyyyyyy__yyyyyyyy____yyyyyy______yyyyyy___S_Syyyyyyyy_____yyyyyyyyy___yyyyyyyyyy___yyyyyyyy______yyyy","color":"","credit":"https://en.wikipedia.org/wiki/Pacman"},{"name":"Ship","size":11,"bricks":"____sWW________sWWW_______sWWW_______s___OOOOOOOOOOOOOO_OBOBOBOBOO__OOOOOOOO_bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb___________"},{"name":"We come in peace","size":13,"bricks":"________________a_____a_______a___a_______aaaaaaa_____aaBaaaBaa___aaaaaaaaaaa__aaaaaaaaaaa__a_aaaaaaa_a__a_a_____a_a_____aa_aa_____________________________","color":"","credit":"https://en.wikipedia.org/wiki/Space_invaders"},{"name":"Space mushroom","size":10,"bricks":"______________WW_______WWWW_____WWWWWW___WWBWWBWW__WWWWWWWW____W__W_____W_WW_W___W_W__W_W","color":"","credit":"https://en.wikipedia.org/wiki/Space_invaders"},{"name":"Wololo","size":9,"bricks":"____WW_OOW___WW__OWW__W___OWWWbbbW_WWW_WbW_WOW__WWb__OW__bbb__O___W_W__O___W_W__O","color":"","credit":"https://aoe.heavengames.com/theacademy/unitsboatsandbuildings/priest/"},{"name":"Small heart","size":15,"bricks":"________________________________RRRR___RRRR___RrWWrR_RWWrrR__RWWrrrRWWrrrR__RrrrrrrrrrrrR__RrrrrrrrrrrrR___RrrrrrrrrrR_____RrrrrrrrR_______RrrrrrR_________RrrrR___________RrR_____________R______________________","color":""},{"name":"Eye","size":9,"bricks":"____________ggg_____gWWWg___gWbbbWg_gWWbBbWWg_gWbbbWg___gWWWg_____ggg____________","color":"#5da3ea"},{"name":"Enderman","size":10,"bricks":"___________gggggggg__gggggggg__gggggggg__gggggggg__vvvggvvv__gggggggg__gggggggg__gggggggg_____________________","color":"#154b07","credit":"https://minecraft.wiki/w/Enderman"},{"name":"Mushroom","size":16,"bricks":"_____________________rrrrWW________WWrrrrWWWW_____WWrrrrrrWWWW____WrrWWWWrrWWW___rrrWWWWWWrrrrr__rrrWWWWWWrrWWr__WrrWWWWWWrWWWW__WWrrWWWWrrWWWW__WWrrrrrrrrrWWr__WrrWWWWWWWWrrr_____WWBWWBWW_______WWWBWWBWWW______WWWWWWWWWW_______WWWWWWWW____________________","color":"","credit":"https://pixelartmaker.com/art/cce4295a92035ea"},{"name":"Tulip","size":11,"bricks":"______________R_R_R______RRRRR______RRRRR______RRRRR_______RRR_________k________k_k_k______k_k_k_______kkk_________k________________","color":""},{"name":"Chain","size":7,"bricks":"yyy____yBy____yyyyy____yBy____yyyyy____yBy____yyy","color":""},{"name":"Marion","size":9,"bricks":"rr_____rr_rr___rr__rrr_rrr__rrrrrrr__rr_r_rr__rr___rr__rr___rr__rr___rr_rrr___rrr","color":""},{"name":"Renan","size":9,"bricks":"yyyyyyy___yyyyyyy__yy___yy__yy___yy__yyyyyy___yy_yy____yy__yy___yy___yy_yyy___yyy","color":""},{"name":"Violet Pairs","size":8,"bricks":"b_b_b_b_b_b_b_b__________t_t_t_t_t_t_t_t________b_b_b_b_b_b_b_b","color":""},{"name":"Red Cups","size":11,"bricks":"___________rBr_rBr_rBrrrr_rrr_rrr___________r_rBr_rBr_rr_rrr_rrr_r___________rBr_rBr_rBrrrr_rrr_rrr__________","color":""},{"name":"Cactus","size":10,"bricks":"____G______rG_Gk______G_Gk______kkkk_r_____kkk_G______GkGk_____rGkk_______Gk________kk________kk_____","color":""},{"name":"Sunny Face","size":11,"bricks":"____yyy______yyyyyyy___yyyyyyyyy__yyyyyyyyy_yyyWWyWWyyyyyyyyyyyyyyyyyyyyyyyyy_yyWWWWWyy__yyyWWWyyy___yyyyyyy______yyy","color":"#5da3ea"},{"name":"Mountain","size":9,"bricks":"_______________W_______WWW______GGWW__W_GGGGG_kkkGGGGG_kkkkGGGGkkkkkGGGGkkkkkkGGG_________","color":""},{"name":"Dollar","size":17,"bricks":"________________________G_G______________G_G____________GGGGGGG_________GGGGGGGGG_______GG__G_G__GG______GG__G_G__GG______GG__G_G___________GGGGGGGG__________GGGGGGGG___________G_G__GG______GG__G_G__GG______GG__G_G__GG_______GGGGGGGGG_________GGGGGGG____________G_G______________G_G________________________","color":""},{"name":"Waves","size":8,"bricks":"___bbb____bbb____bbttbbbbbttbbbbttttaatttttaattttaaaaaaa","color":""},{"name":"Box","size":8,"bricks":"yyyyyyyyy______yy_bbbb_yy_b__b_yy_b__b_yy_bbbb_yy______yyyyyyyyy","color":"","squared":false},{"name":"Rose","size":9,"bricks":"__SS______SSSS_____SSSS_____SSSS______SS_k______k_kk_____kk_k______kk________k","color":""},{"name":"Time","size":9,"bricks":"__________WWWWWWW___WWWWW_____yyy_______y________y_______WyW_____WyyyW___yyyyyyy__________","color":"","squared":false},{"name":"Watermelon","size":8,"bricks":"_____Sk_____SSBk___SBSSk__SSSSSk_SSBSSk_SBSSSSk_kSSSkk___kkk____","color":""},{"name":"Worms","size":13,"bricks":"___sssss_______sssssss______WWsWWsss_____WBsBWsss_____WBsBWsss_____WWsWWsss_____sssssss_______ssssss_____WWWWWWss_______WssWs__s_____ssss__sss___sssssssssss__sssssssss_ss","color":"","squared":false,"credit":"https://en.wikipedia.org/wiki/Worms_(series)"},{"name":"Ocean Sunrise","size":8,"bricks":"SSSSSSSSSSSyySSSSSyyyySSSyyyyyySbttttttbbbttttbbbbbttbbbbbbbbbbb","color":""},{"name":"Crosses","size":13,"bricks":"b___b___b___b__v___v___v___vvv_vvv_vvv___v___v___v__p___p___p___ppp_ppp_ppp_ppp___p___p___p__P___P___P___PPP_PPP_PPP___P___P___P__p___p___p___ppp_ppp_ppp_ppp___p___p___p","color":""},{"name":"Negative space","size":9,"bricks":"tttttttttt_t_t_t_t_________b_b_b_b_bbbbbbbbbb_b_b_b_b___________t_t_t_t_ttttttttt_________"},{"name":"UK","size":11,"bricks":"brbbWrWbbrbbbrbWrWbrbbbbbrWrWrbbbWWWWWrWWWWWrrrrrrrrrrrWWWWWrWWWWWbbbrWrWrbbbbbrbWrWbrbbbrbbWrWbbrb__________","color":""},{"name":"Greece","size":11,"bricks":"ttWttttttttttWttWWWWWWWWWWWttttttttWttWWWWWWttWttttttttWWWWWWWWWWWtttttttttttWWWWWWWWWWWttttttttttt__________","color":""},{"name":"Russia","size":8,"bricks":"________WWWWWWWWWWWWWWWWttttttttttttttttrrrrrrrrrrrrrrrr________________","color":""},{"name":"Ukraine","size":8,"bricks":"________ttttttttttttttttttttttttyyyyyyyyyyyyyyyyyyyyyyyy________","color":""},{"name":"Poland","size":7,"bricks":"________WWWWW__WWWWW__rrrrr__rrrrr_______________","color":""},{"name":"Yellow 71","size":9,"bricks":"_________yyyyy__yyyyyyy_yyy___yy__yy__yyy__yy_yyy___yy_yy____yy_yy____yy__________________","color":""},{"name":"71 on white","size":6,"bricks":"WWWWWWrrrWWrWWrWrrWrWWWrWrWWWrWWWWWW______"},{"name":"Blue 71","size":8,"bricks":"ttttt__bttttt_bb___ttbbb__tt__bb__tt__bb_tt___bb_tt___bb_tt___bb","color":""},{"name":"Seventy one","size":21,"bricks":"rr_yy_rrry_yrrry_yrrrr_ry_yr__y_yr_ry_y_r_rr_yy_rr_yy_r_ry_y_r_r_ry_yr__y_yr_ry_y_r_rr_y_yrrry_yrrryyy_r_yyy__________________y______________r_____yyyrrry_yrrryyyrr_y_y__yrr_y_yrr_y_yr__y_yyyyrrr_y_rrry_yrrryyy____________________yrrryyyrrr_________yy_r_ry_yrr_____________rrry_yrrryyyyyyyyyyyy_____________________________________________________________________________________________________________________________"},{"name":"B71","size":10,"bricks":"__________bbbtttt_b_b__b__tbb_b__b__t_b_bbb__t__b_b__b_t__b_b__bt___b_bbb_t__bbb__________"},{"name":"Pig","size":9,"bricks":"__________PP___PP__PPP_PPP__WWPPPWW__WBPPPBW__PPsssPP__PsBsBsP__PPsssPP___________"},{"name":"Big Pig","size":15,"bricks":"________________sss_______sss__ss__sssss__ss____sssssssss_____sWBsssssBWs___ssBBsssssBBss__ssss_____ssss__sss_sssss_sss__sss_sBsBs_sss__sss_sssss_sss___sss_____sss____sssssssssss__GGGsssssssssGGGGGGsGsssssGsGGGGGGssGGGGGssGGG_______________","color":""},{"name":"Donkey Kong","size":9,"bricks":"OOr__a___OOr__a___ppppppp_O______a________a____pppppppr_a______b_a___O__ppppppp__","color":""},{"name":"Banana","size":12,"bricks":"_________________e__________eee_________eee_________eee_________eeeyy_____yyeeyyyy___yyyyey_yC___yy_yyy___C_____yyyy_________yyyy_________yyyy"},{"name":"Fox","size":8,"bricks":"e______eee_OO_eeeeOOOOeeeOBOOBOeOOOOOOOO_WWBBWW___WWWW_____WW___"},{"name":"Wiki","size":10,"bricks":"_______________________GGGG_____GGkkGG___GkggggkG__GgWWWWgG__GkggggkG___GGkkGG_____GGGG_______________________","color":"#1c71d8"},{"name":"Baby Dog","size":8,"bricks":"_______W__eeeeWWWWeeWeWWWeBWeBeeeeWWWWee_eWBBWe__eWWWWe____WW___"},{"name":"dog 21","size":9,"bricks":"__________O_____O_OOOWWWOOOOOWWWWWOOOOeWWWWOO_eBeWWBW__eBeWWBW___eWBWW_____WRW____________","credit":"https://prohama.com/dog-21-pattern/"},{"name":"icon:extra_life","size":9,"bricks":"___________rr_rr___rrrrrrr_rrrrrrrrrrrrrrrrrr_rrrrrrr___rrrrr_____rrr_______r_____________"},{"name":"icon:streak_shots","size":8,"bricks":"_W_W_W__W_W_W_W_tttttt_WttttttW________W______W______W_____WWWW_"},{"name":"icon:base_combo","size":7,"bricks":"________bbbbb__bybyb__bbbbb__bybyb__bbbbb________"},{"name":"icon:slow_down","size":10,"bricks":"_____________kk_______kkkk_____kkkkkkGG__kkkkkkGBG_kkkkkkGGGGkkkkkkGG__GGGGGG____GG__GG_____________"},{"name":"icon:bigger_puck","size":8,"bricks":"_________tttttt__tttttt______________________W___________WWWWWW_"},{"name":"icon:viscosity","size":8,"bricks":"________tt______bbtt__ttbbbbttbbbtbbtbbbbbtbbtbbbbbybbybbbbbbbbb"},{"name":"icon:left_is_lava","size":8,"bricks":"r_______rtttttt_rtttttt_r_______r_______r____W__r_______r_WWW___"},{"name":"icon:right_is_lava","size":8,"bricks":"_______r_ttttttr_ttttttr_______r_______r_____W_r_______r__WWW__r"},{"name":"icon:telekinesis","size":8,"bricks":"_____PW_____s______P______s_______P_______s_______P_____WWWWW"},{"name":"icon:top_is_lava","size":8,"bricks":"rrrrrrrr_tttttt__tttttt____________________W_______________WWW__"},{"name":"icon:coin_magnet","size":8,"bricks":"__y__y_yy_________y_y_y_y________y_y______________y______WWW____"},{"name":"icon:skip_last","size":5,"bricks":"_ttt_t_t_ttt_ttt_t_t_ttt_"},{"name":"icon:multiball","size":8,"bricks":"_________tttttt__tttttt___________W__W____________________WWW___"},{"name":"icon:smaller_puck","size":8,"bricks":"_________tttttt__tttttt_____________W_____________________WW____"},{"name":"icon:pierce","size":6,"bricks":"ttttttttttWtttt__ttt__ttt__ttt__tttt"},{"name":"icon:picky_eater","size":8,"bricks":"_rrr_______ry_____ryy_____r_y______yyy______________y_____WWWW__"},{"name":"icon:metamorphosis","size":8,"bricks":"aaaaaa__aaaa__________W___________ttaatt__tttttt_________WWW"},{"name":"icon:compound_interest","size":8,"bricks":"_________tttttt__ttt__t______y_____________W__y_________rrWWWrrr"},{"name":"icon:hot_start","size":7,"bricks":"ttttttttttt_tt_____W_____y_y_____y_____y_y_WWW_y_"},{"name":"icon:sapper","size":9,"bricks":"_____WW______W__W_tttWttt_yttgggtt__tgggggt__tgggggt__tgggggt__ttgggtt__ttttttt___________","color":"#000000"},{"name":"icon:bigger_explosions","size":8,"bricks":"__r_______ry_rr___ryry__ryyyW_rr_rrWyyy___yryrr__yrry_rr_rr"},{"name":"icon:extra_levels","size":6,"bricks":"__________b__t_bb_ttt_b__t_bbb____________"},{"name":"icon:pierce_color","size":8,"bricks":"bb___bbbb__b_bbb_____bbb____bbbbb____bbbbb____bbbbb____bbbbb____"},{"name":"icon:soft_reset","size":9,"bricks":"__rr______rrr_yy__rrrr_yyy_rrrr_yyyy_____yyyy_yyyyyyyy_yyyyyyyy__yyyyyy____yyyy__"},{"name":"icon:ball_repulse_ball","size":8,"bricks":"WsP__PsWs______sP______P________________P______Ps______sWsP__PsW"},{"name":"icon:ball_attract_ball","size":8,"bricks":"__P__P____s__s__PsW__WsP________________PsW__WsP__s__s____P__P__"},{"name":"icon:puck_repulse_ball","size":8,"bricks":"__________________W_______s___W___P__s______P____________WWW__"},{"name":"A","size":7,"bricks":"__ttt___ttttt_ttt_ttttt___ttttttttttt___tttt___tt"},{"name":"B","size":9,"bricks":"_bbbbb_____bb_bb____bb_bb____bb_bb____bbbb_____bb_bb____bb_bb____bb_bb___bbbbb____"},{"name":"C","size":8,"bricks":"__rrrr___rrrrrr_rrr___rrrr______rr______rrr___rr_rrrrrr___rrrr"},{"name":"D","size":8,"bricks":"_GGGGG____GG__G___GG__GG__GG__GG__GG__GG__GG__GG__GG__G__GGGGG"},{"name":"e","size":8,"bricks":"__tttt___tttttt_tt____tttt____tttttttttttt_______tt__tt___tttt_"},{"name":"icon:wind","size":9,"bricks":"_ss______s___PPPP_s_________sssssss___________sssssss_s________s___PPPP__ss"},{"name":"icon:sturdy_bricks","size":7,"bricks":"ttbttttbtttbtt____W_____W_W___W___W_______WWW____"},{"name":"icon:respawn","size":9,"bricks":"tttt___ttttt__t__ttta_ttt_______________________________W_________________WWW"},{"name":"Elephant","size":18,"bricks":"_________________________llll_________lll_llllll_lll___lsssllllllllsssl__lsssllllllllsssl__lsssllBllBllsssl__lssllWllllWllssl___ll__llllll__ll_________llll_______________ll______________llll______________ll________________________________________________________________________________________________________________________________________","color":"","credit":"https://prohama.com/elephant-5-pattern/"},{"name":"Orca","size":20,"bricks":"____________________________________________________________________________________________ggggg____ggg_ggg___ggggggg____ggggg___ggggggggg____ggg___ggggWggWWW_____gggggggggggWWWW_____ggggggggggWWWWW_____gggggggggWWWWW_______gggggggWWWWW___________WWggWWW______________ggg_gg______________gg__g__________________________________________________________________________________________________________","color":"#1c71d8","credit":"https://prohama.com/whale-2-pattern/"},{"name":"Shark","size":17,"bricks":"__________________________________________g_______________ggg____________ggggggg_________ggggggggg_______ggggggggggg_____gggggWWWggggg____gBgWWWWWWWgBg___ggWWWWrWrWWWWgg__ggWWWrrrrrWWWgg_ggWWWrrrrrrrWWWggggWWrrrrrrrrrWWgggWWWrWrWrWrWrWWWggWWrrWWWWWWWrrWWggWWWWWWWWWWWWWWWg_________________","color":"#3584e4","credit":"https://prohama.com/shark-2-pattern/"},{"name":"Bird","size":13,"bricks":"_______RRR____R____RSSSR___RR__RSSWWWR__RSR_RSWWBWR__RSSRRSWWWWyy_RSSSRSWWWR___RSSSSSSRR_____RRSSyyyy______RSyyyyy___RRRRSyyyy____RSSSRyyy_____RRRR______________________","color":"","credit":"https://prohama.com/bird-1-size-13x12/"},{"name":"Tux","size":14,"bricks":"_____gggg________gggggggg_____gggggggggg____gggggggggg___gggggggggggg__gggWBggWBggg__gggBBggBBggg__ggggyyyygggg_ggggggyyggggggggggWWWWWWggggg_gWWWWWWWWg_g__WWWWWWWWWW____WWWWWWWWWW____yyy____yyy__","color":"#62a0ea","credit":"https://prohama.com/pingwin-4-pattern/"},{"name":"Armenia","size":6,"bricks":"_______rrrr__bbbb__yyyy_____________","color":""},{"name":"Austria","size":6,"bricks":"_______rrrr__WWWW__rrrr______","color":""},{"name":"Benin","size":8,"bricks":"_________kkyyyy__kkyyyy__kkrrrr__kkrrrr__________________________","color":""},{"name":"Botswana","size":10,"bricks":"___________tttttttt__tttttttt__tttttttt__WWWWWWWW__BBBBBBBB__WWWWWWWW__tttttttt__tttttttt__tttttttt___________","color":""},{"name":"Bulgaria","size":6,"bricks":"_______WWWW__cccc__rrrr_____________","color":""},{"name":"Canada","size":7,"bricks":"________rWWWr__rWrWr__rWWWr______________________","color":""},{"name":"Chad","size":8,"bricks":"_________bbyyRR__bbyyRR__bbyyRR","color":""},{"name":"China","size":6,"bricks":"______RRyRRRRyRyRRRRyRRRRRRRRR______","color":""},{"name":"Colombia","size":7,"bricks":"________yyyyy__yyyyy__bbbbb__RRRRR_______________","color":""},{"name":"Republic of the Congo","size":7,"bricks":"________kkkyy__kkyyr__kyyrr__yyrrr_______________","color":""},{"name":"C\xf4te d\'Ivoire","size":8,"bricks":"_________OOWWGG__OOWWGG__OOWWGG","color":""},{"name":"Denmark","size":8,"bricks":"_________rrWrrr__rrWrrr__WWWWWW__rrWrrr__rrWrrr","color":""},{"name":"El Salvador","size":8,"bricks":"_________bbbbbb__bbbbbb__WWWkWW__WWkWWW__bbbbbb__bbbbbb","color":""},{"name":"Egypt","size":8,"bricks":"_________RRRRRR__RRRRRR__WWWyWW__WWyWWW__gggggg__gggggg","color":"#1c71d8"},{"name":"Estonia","size":8,"bricks":"_________tttttt__tttttt__gggggg__gggggg__WWWWWW__WWWWWW","color":"#26a269"},{"name":"Finland","size":6,"bricks":"_______WtWW__tttt__WtWW_____________","color":""},{"name":"Gabon","size":5,"bricks":"______CCC__yyy__ttt______","color":""},{"name":"Georgia","size":9,"bricks":"__________WrWrWrW__WWWrWWW__rrrrrrr__WWWrWWW__WrWrWrW__________________","color":""},{"name":"Guinea","size":8,"bricks":"_________rryycc__rryycc__rryycc","color":""},{"name":"Indonesia","size":6,"bricks":"_______rrrr__rrrr__WWWW__WWWW_______","color":""},{"name":"icon:one_more_choice","size":7,"bricks":"ttt____tbbb___tbttt__tbtbbb__btbbb___tbbb____bbb_"},{"name":"icon:instant_upgrade","size":5,"bricks":"ttt__tbbb_tbbb_tbbb__bbb_"},{"name":"icon:checkmark_checked","size":6,"bricks":"_ggggbgBBBbbbbBbbggbbbBggBbBBg_gggg_"},{"name":"icon:checkmark_unchecked","size":6,"bricks":"_gggg_gBBBBggBBBBggBBBBggBBBBg_gggg_"},{"name":"icon:concave_puck","size":7,"bricks":"___W_____________W__________W__W__WWW___WWWWWWWWW","color":""},{"name":"icon:helium","size":8,"bricks":"v______vvv____vvv______vv______vv______vv______vv______v__WWWW__","color":""},{"name":"icon:asceticism","size":8,"bricks":"_________y____y____W____y______y_________y____y_________y_WWWW_y","color":""},{"name":"icon:unbounded","size":9,"bricks":"WWWWWWWWWW_r_r_r_WWrtttttrWW_ttttt_WWr_____rWW_______WWr___W_rWW_______WWrWWW__rW","color":""},{"name":"icon:shunt","size":8,"bricks":"_______y______yy______yy__yCCyyy__y__yyy_yy__yyy_yy__yyyyyy__yyy","color":""},{"name":"icon:yoyo","size":8,"bricks":"_rrrrrr_rrrrrrrrrrrrrrrr_WWWWWW_rWrrrrrrrrWrrrrr_rrWrrr_____W___","color":""},{"name":"icon:nbricks","size":6,"bricks":"yy__yyyyy_yyyyyyyyyyyyyyyy_yyyyy__yy","color":""},{"name":"icon:etherealcoins","size":11,"bricks":"_____v_________vvv________ttt________ttt_______vtttv_____vvtttvv____vvtttvv____vvtttvv____v_ryr_v_______r________________","color":""},{"name":"icon:shocks","size":8,"bricks":"_r__r_r_rWWWyy_r_WWW__r_yWWWyry_y_ryyy_rry__WWW___ryWWWryr__WWWy","color":""},{"name":"icon:zen","size":9,"bricks":"___tt______tttt_____BttB_____bbbb____bbbbbb___BbbbbB___tttttt__tttttttt__tttttt__","color":""},{"name":"icon:sacrifice","size":9,"bricks":"__r___r___rrr_rrr_rrWWWWWrrrrWrWrWrrrrWWrWWrr_rrWWWrr___rWrWr_____rrr_______r____","color":""},{"name":"icon:trampoline","size":8,"bricks":"___y_____y____y___bbyb___bttttb_bttytttb_bttttb__tbbbbt__t____t_","color":""},{"name":"icon:ghost_coins","size":7,"bricks":"__yyy___yyyyy_yyOyOyyyyyyyyyyyOOOyyyyyyyyyyy_y_yy","color":""},{"name":"icon:forgiving","size":8,"bricks":"____G______G_G____G___G__G_____GG_____G__G___G____G_G____WWWWW__","color":""},{"name":"icon:ball_attracts_coins","size":8,"bricks":"WWW_____WWW_y___WWW____y__y_y____y____y_____y_____y____y___y_y__","color":""},{"name":"icon:reach","size":8,"bricks":"________ttt__tttttt_Wttt__t__t____r__r___________________WWW____","color":""},{"name":"icon:passive_income","size":8,"bricks":"__________tttt____tttt_______________W___________________rWWWr__","color":""},{"name":"icon:clairvoyant","size":9,"bricks":"__y___y__y__y_y__y_y__t__y____ttt_____tWWWt___tWWgWWt_tttWWWttt__________________","color":""},{"name":"icon:implosions","size":8,"bricks":"y______W__ryW_W__yr_WW____r_WWWy_WWW_rr___WW_rrryW_Wy___W_____y_","color":""},{"name":"icon:corner_shot","size":9,"bricks":"___W____y___W_y______W___y____W_y______W___y____W______W_W_WWW_WW_W_WWWWWW_W_WWWW","color":""},{"name":"icon:premium","size":11,"bricks":"__g____g___g____g____g_g__gbg__g______g______gg_gbg_gg_gbbgbbbgbbggbbgbbbgbbg_gbgbbbgbg___ggggggg____ggggggg_____________","color":""},{"name":"icon:reroll","size":8,"bricks":"__llllll_llBlBlelllllleBWWWWWeeeWBWBWeBeWWWWWeeeWBWBWBe_WWWWWe__","color":""},{"name":"icon:loop","size":7,"bricks":"bbbbbbbtttttt_aaaaa__cccc___CCC____GG_____y______","color":""},{"name":"icon:addiction","size":9,"bricks":"__________________________l__WWWWW_lWWWyylllly_WWWWW_ly_______l__________________","color":""},{"name":"icon:help","size":8,"bricks":"___bb_____bbbb___bb__bb__bb__bb_____bb_____bb______________bb___","color":""},{"name":"Pingwin","size":13,"bricks":"______gggg________ggWWgg_______gWWgWgy______ggWWWg_______ggggg_______gggWWW______gggggWWW___gggggggWWW____ggggggWWW_____ggggWWWW____gggWWWWW______ggWWWW________gWWyyy___","color":"#3584e4","credit":"https://prohama.com/pingwin-2-pattern/"},{"name":"Dog 8","size":17,"bricks":"_____________________________________gg_ggggg_gg____ggWWgWWWWWgWWgg__gWWgWWWWWWWgWWg__gWWgWWWWWWWgWWg__gggWWWWWWWWWggg___gWggWWWWWggWg____gWggWWWWWggWg____gWWWWgggWWWWg_____gWgWWgWWgWg______gWWggsggWWg_______gWgsssgWg_________ggsssgg____________ggg_________________________________________","color":"#62a0ea","credit":"https://prohama.com/dog-8-pattern/"},{"name":"Sunglasses","size":24,"bricks":"____________________________________________________ggggg______ggggg_______gg___g______g___gg_____gg________________gg___gg__________________gg_gggggggggg____gggggggggggggtttttggggggggbbbbbgggggtWWWttttggggbbbbWWWbgg_gtWttttttggggbbbbWbbbg__gtttttttgg__ggbbbbbbbg__gtttttttg____gbbbbbbbg__ggtttttgg____ggbbbbbgg___ggtttgg______ggbbbgg_____ggggg________ggggg___________________________________________________________________________________________________________________________________________________________________________________________________________________________","color":"#26a269","credit":"https://prohama.com/sunglasses-pattern-1/"},{"name":"Balloon","size":21,"bricks":"_____bbbWbbbWbbb_________PWbWPWbWPWbWP_______bWbbbWbbbWbbbWb_____WbbbWbbbWbbbWbbbW___WPWbWPWbWPWbWPWbWPW__bWbbbWbbbWbbbWbbbWb__bbbPbbbPbbbPbbbPbbb__bbPPPbPPPbPPPbPPPbb___PPWPPPWPPPWPPPWPP____PWbWPWbWPWbWPWbWP_____PWPPPWPPPWPPPWP_______PPWPPPWPPPWPP_________WbWPWbWPWbW___________bbbbbbbbb_____________b_____b______________b_____b______________b_____b______________WWWWWWW_______________PPPPP________________PPPPP________________PPPPP________","color":"#240a8b","credit":"https://prohama.com/balloon-1/"},{"name":"Opening","size":14,"bricks":"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbyyyyyyyyyyyybbyB___BB___Bybby__________ybbyyy______yyybbbbyyB__Byybbbbbbbyy__yybbbbbbbbby__ybbbbbyyyyby__ybyyyy___yby__yby______yby__yby______yBy__yBy______yyy__yyy___","color":"#000000"},{"name":"Stripes","size":17,"bricks":"bbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb______ttbbbbbb______tttttbbb______tttttt________tttttt________tttttt______bbtttttt______bbbbbttt______bbbbbb________bbbbbb________bbbbbb________bbbbbb___________bbb______________","color":""},{"name":"icon:starting_perks","size":8,"bricks":"_________b_b_b___________g_g_g_g_________g_g_g_g_________g_g_g_g","color":""},{"name":"icon:download","size":8,"bricks":"___bb______bb______bb______bb______bb____bbbbbb___bbbb__gggbbggg","color":""},{"name":"icon:upload","size":8,"bricks":"gggbbggg__bbbb___bbbbbb____bb______bb______bb______bb______bb___","color":""},{"name":"icon:coins","size":8,"bricks":"__bbbb___bbggbb_bbggggbbbggggggbbggggggbbbggggbb_bbggbb___bbbb__","color":""},{"name":"icon:reset","size":8,"bricks":"bb____bbbbb__bbb_bbbbbb___bbbb____bbbb___bbbbbb_bbb__bbbbb____bb","color":""},{"name":"icon:fountain_toss","size":12,"bricks":"__________________________________________________WWWWWWWW___WttttttttW_WtytttytyttWWtttyttttttWlWtyttttytWl_lWWWWWWWWl___llllllll______________","color":""},{"name":"You are here","size":13,"bricks":"_____rrr_________rrrrr_______rrr_rrr______rr___rr______rr___rr_______rr_rr________rrrrr_________rrr__________rrr_________WWrWW_______WWWrWWW______WWWWWWW_______WWWWW____","color":""},{"name":"Gear","size":13,"bricks":"_________________l_l_l______l_lllll_l_____lllllll____lllll_lllll___lll___lll___lll_____lll___lll___lll___lllll_lllll____lllllll_____l_lllll_l______l_l_l_________________","color":""},{"name":"Play","size":15,"bricks":"_________________rrrrrrrrrrr___rrrrWWrrrrrrr__rrrrWWWrrrrrr__rrrrWWWWrrrrr__rrrrWWWWWrrrr__rrrrWWWWWWrrr__rrrrWWWWWrrrr__rrrrWWWWrrrrr__rrrrWWWrrrrrr__rrrrWWrrrrrrr___rrrrrrrrrrr_______________________________________________","color":""},{"name":"City","size":18,"bricks":"_______yyy___bbbbb________yyy__ybyyb________yyy__ybyyb__tt___yyy_b_ybbbb_tttt______bbbbbbbtttttt_______ybyybbbbbbb_______ybyybbyybyb_____b_ybbbbbyybyb_____bbbbbbbbbbbbb__bb___bbbbbbybyyb_bbbb__ybyybbybyybbbbbbb_ybyybbbbbbbtttttt_ybbbbbyybybtyyyyt_bbbbbbyybybtyyyyt_bbbbbbbbbbbtttttt_byybybybyybtytyyt_byybybybyybtttttt_byybb","color":""},{"name":"Wiggle","size":17,"bricks":"__________________cccccc_ccc_cccc__c____c_c_c_c__c__ccc_cc_c_ccc_cc____c_c__c_____c___ccc_cccc_ccc_cc__c________c_c__c__ccc_ccc_cc_cccc____c_c_c_c________ccc_c_c_ccccccc__c___c_________c__ccc_ccccccccccc____c______________ccc_ccc_ccc_ccc__c___c_c_c_c_c_c__ccccc_ccc_ccc_c__________________","color":""},{"name":"Graph","size":18,"bricks":"_______________________yy________________yyt__yytttt______tt_tttyy___t____yyt____t_____t____yy____tt_____t____t_____t______yy___t_____t______yy__tt_____yytttttt___tt___ttyy_____t___t____t__t_____t___yytttt__t_____t___yy______tt____t____t_______yy___t____ttt_____yyttyy______tyy___t___yy_______yytttt_________________________","color":""},{"name":"Lightbulb","size":14,"bricks":"_______________y__yyyyy___y____yyyyyyy______yyyyyyyyy_____yyyyyyyyy___y_yyyyyyyyy__y__yyyyyyyyy_____yyyyyyyyy_____yyyBBByyy___y__yyByByy___y____yByBy_________lllll______y___lll___y_______lll______","color":""},{"name":"Note","size":16,"bricks":"_____________WWW__________WWWWWW_______WWWWWW__W____WWWWWW_____W____WWW________W____W__________W____W__________W____W__________W____W__________W____W________WWW____W_______WWWW____W_______WWW___WWW____________WWWW____________WWW____________________________","color":""},{"name":"Rocket","size":13,"bricks":"______b___________bbb_________bbBbb________btttb________ttBtt________ttttt________ttBtt________ttttt________ttBtt_______bbtttbb_____bbbyyybbb____bbbyyybbb____bb_ByB_bb__","color":""},{"name":"Abstract","size":16,"bricks":"________________aaaaa_cccc_aaaaaaaaaa_cccc_aaaaa________________aaaa_cccc_aaaaaaaaaa_cccc_aaaaaa________________aaa_cccc_aaaaaaaaaa_cccc_aaaaaaa________________aa_cccc_aaaaaaaaaa_cccc_aaaaaaaa________________a_cccc_aaaaaaaaaa_cccc_aaaaaaaaa________________","color":""},{"name":"Fingerprint","size":15,"bricks":"___SSSSSSSS______S_______SS____S__SSSSS__SS__S__S____SS__S____S__SS__SS_S___S__S_SS__S__S_S__S___SS_SS__SS_S_____S___S__S_S__SS__S__SS_S_S_SS_S__S__S_S_S_S___S_S__S_S_S_S___S_S__S___S_S___S_S__S__S__S___S_S__S__S__S__S___S_S_","color":""},{"name":"Leaf","size":14,"bricks":"____________________________________________________________GGkGG________GGkGGkGG_____GGkGGkGGkkG_kkkkkkkkkkkGGG__GGkGGkGGkkG____GGkGGkGG_______GGkGG_______________________________________________","color":""},{"name":"Abstract 2","size":14,"bricks":"______________yyyy______yyyy______________bbb_bbbbbb_bbbbb___bbbb___bbb__y__bb__y__b______________bbb_bbbbbb_bbbbb___bbbb___bbb__y__bb__y__b______________bbb_bbbbbb_bbbbb___bbbb___bbb__y__bb__y__b","color":""},{"name":"Abstract 3","size":13,"bricks":"______________p_aaa_ppp_a__p___a_p___a__ppp_a_p_aaa_______________aaa_p_a_ppp__a___p_a___p__a_ppp_aaa_p_______________p_aaa_ppp_a__p___a_p___a__ppp_a_p_aaa______________","color":""},{"name":"Abstract 4","size":13,"bricks":"______________y_y_y_y_y_y__y_y_y_y_y_y__y_y_y_y_y_y_______________bbb_bbb_bbb_______________bbb_bbb_bbb_______________y_y_y_y_y_y__y_y_y_y_y_y__y_y_y_y_y_y______________","color":""},{"name":"Abstract 5","size":13,"bricks":"______________ccc_ccc_ccc__c_a_c_c_a_c__caa_aaa_aac_______________cca_aaa_acc__c_a_a_a_a_c__cca_aaa_acc_______________caa_aaa_aac__c_a_c_c_a_c__ccc_ccc_ccc______________","color":""},{"name":"Abstract 6","size":13,"bricks":"_vvvvv_vvvvv__v___v_v___v__v_bbbbbbb_v__v_b_v_v_b_v__v_b_v_v_b_v__v_b_v_v_b_v__v_b_v_v_b_v__v_b_v_v_b_v__v_b_v_v_b_v__v_b_vvv_b_v__v_b_____b_v__vvvvvvvvvvv_bbbb_____bbbb","color":""},{"name":"icon:new_run","size":7,"bricks":"_ggg____gbgg___gbbgg__gbbbg__gbbgg__gbgg___ggg___","color":""},{"name":"icon:unlocks","size":6,"bricks":"_bbbb__b__b__b__b_gggggggggggggggggg","color":""},{"name":"icon:settings","size":9,"bricks":"___g_g____g_ggg_g___ggbgg__gggbbbggg_gbb_bbg_gggbbbggg__ggbgg___g_ggg_g____g_g___","color":""},{"name":"icon:creative","size":7,"bricks":"bbg_bgg_______bbb_bgg_______bgg_bbg_______bbg_bbb","color":""},{"name":"icon:limitless","size":12,"bricks":"_________________________bbb____ttb_bbbbb__tttbbbb_bbbttt_bbbb__bbbt__bbbb_ttbbb__bbttttttbbbbbb_ttt___bbbb_____________________________________","color":""},{"name":"icon:history","size":8,"bricks":"__gggg___ggbggg_gggbgggggggbggggggggbbgggggggggg_gggggg___gggg__","color":""},{"name":"Hemiola","size":11,"bricks":"___gggg_____gggrrgg_____ggrrg_______gggg_____gggyygg_____ggyyg_______gggg_____gggCCgg_____ggCCg_______gggg________gg_____","color":"#240a8b","credit":"Left a wonderful review on the play store."},{"name":"Obigre","size":13,"bricks":"_______________________________________OOOORgRgRgOOOOWOORgRgRgOOOOOWORgRgRgOWOOWOORgRgRgOOWOOWORgRgRgOWOOWOORgRgRgOOOOOOORgRgRgOOO_______________________________________","color":"#62a0ea","credit":"Colin helped a lot with the game design https://colin-crapahute.bearblog.dev/"},{"name":"Noodlemire","size":15,"bricks":"_________________________________ggggggggg_____g_________g___g___________g_g_____________gg_____________gg_____yyy_____ggg__yyyyyyy__ggggtyyyyyyyyytggggtttttttttttgggg_ttttttttt_gg_____ttttt___________________________________","color":"#240a8b","credit":"Early adopter of the game"},{"name":"Bearded axe","size":12,"bricks":"______________WyyyOOy_____WyyyOOy_____Wyy_OO______Wyy_OO______Wyy_OO__________OO__________OO__________OO__________OO__________OO__________OO____","color":"","credit":"Did some nice bug reports"},{"name":"icon:minefield","size":7,"bricks":"W__B__WWWBBBWWB__W__BBBWWWBBW__B__WWWBBBWW_______","color":""},{"name":"icon:side_flip","size":7,"bricks":"________ttttt__rttty__rttty__rttty__ttttt________","color":""},{"name":"icon:side_kick","size":7,"bricks":"________ttttt__ytttr__ytttr__ytttr__ttttt________","color":""},{"name":"Lebanon","size":9,"bricks":"_________rrrrrrrrrWWWWkWWWWWWWkkkWWWWWkkkkkWWWWWWkWWWWrrrrrrrrr__________________","color":""},{"name":"Spain","size":9,"bricks":"_________rrrrrrrrryyyyyyyyyyWrWyyyyyyrWryyyyyyWrWyyyyyyyyyyyyyyrrrrrrrrr_________","color":""},{"name":"Uzbekistan","size":8,"bricks":"tWtttWttWtttWttttWtWtWttWWWWWWWWWWWWWWWWGGGGGGGGGGGGGGGGGGGGGGGG","color":""},{"name":"Pakistan","size":8,"bricks":"________WWkkkkkkWWkkWkWkWWkWkkkkWWkWkkWkWWkkWWkkWWkkkkkk________","color":""},{"name":"Korea","size":10,"bricks":"__________WWWWWWWWWWWgWWWWWWgWWgWrrrrWgWWWWrrbbWWWWWWrrbbWWWWgWbbbbWgWWgWWWWWWgWWWWWWWWWWW__________","color":"#62a0ea"},{"name":"icon:trickledown","size":8,"bricks":"_ytttttt_________y_y_y__tttttt____________y_y_y___tttttt_y______","color":""},{"name":"icon:transparency","size":9,"bricks":"__W_W_W___________W_W_W_W_W_________W_W_W_W_W_________W_W_W_W_W___________W_W_W__","color":""},{"name":"icon:superhot","size":11,"bricks":"____________________________________________W_W_WWW_WWWWWW_W_W__W_W_W_WWW__W_____________________________________________","color":""},{"name":"icon:bricks_attract_coins","size":7,"bricks":"_y__y___tttttyyttttt__ttttt_yttttty_ttttt___y__y_","color":""},{"name":"icon:rainbow","size":6,"bricks":"__rOyC_rOyCa_rOyCarOyCatrOyCatrOyCat","color":""},{"name":"icon:hypnosis","size":8,"bricks":"WrrrrrrrWrWWWWWrWrWrrrWrWrWrWrWrWrWWWrWrWrrrrrWrWWWWWWWrrrrrrrrr","color":""},{"name":"icon:bricks_attract_ball","size":8,"bricks":"llW_____ll_v________p________bll____t_ll___G____lly_____ll_r____","color":""},{"name":"Chile","size":9,"bricks":"_________tttWWWWWWtWtWWWWWWtttWWWWWWrrrrrrrrrrrrrrrrrrrrrrrrrrr__________________","color":""},{"name":"T\xfcrkiye","size":12,"bricks":"____________rrrrrrrrrrrrrrrWWWrrrrrrrrWWrrrrrrrrrWWrrWrWrrrrrWWrrrWrrrrrrWWrrWrWrrrrrrWWrrrrrrrrrrrWWWrrrrrrrrrrrrrrrrrr________________________","color":""},{"name":"icon:editor","size":10,"bricks":"_______ggg______gggg_____ggggg____ggggg____ggggg____ggggg____ggggg____bgggg_____bbgg______bbb_______","color":""},{"color":"","size":11,"bricks":"_____e________WWWWW_____WWWWWWW____WWWWWWW____WWWWWWW__W__lllll__WWWeeeeeeeWWeeeeeWeeeeeeleeWWWeeleeeeWWWWWeeeeleWWlWWele","name":"Taj Mahal","credit":"An approximative reproduction "},{"color":"","size":9,"bricks":"__________SS_t_SS__S_____S____t_t____t_____t____t_t____S_____S__SS_t_SS__________","name":"Abstract 7","credit":""},{"color":"","size":8,"bricks":"PP_vv_PP_P__v__P________vv_PP_vvv__P__v_________PP_vv_PP_P__v__P","name":"Abstract 9","credit":""},{"color":"","size":9,"bricks":"____W_____WWWWWWW__WB_W_BW__W_____W_WWW_B_WWW_W_____W__WB_W_BW__WWWWWWW_____W____","name":"Crosshair","credit":""},{"color":"","size":15,"bricks":"bbbB_ttttt_BbbbbBbb_ttBtt_bbBbb____tt_tt____bbbbb_tt_tt_bbbb_______________ttttt_b_b_tttttt_____b_b_____tt_ttt_b_b_ttt_ttBtBt_bBb_tBtBtttt_t_bbb_t_ttt________________bb_ttttttt_bb__Bb_tB___Bt_bB__Bb_ttt_ttt_bB_bbb_________bbb","name":"Abstract 10","credit":""},{"color":"","size":6,"bricks":"SSSSSSSOOOOSSBOOBSSOOOOSSOOOOS_OSSO_","name":"Face","credit":""},{"color":"","size":11,"bricks":"_____O__________O__________O__________O_________OOO________OOO____k___O_O___kkk_OO_OO_kkkkkOOOOOkkkkkOOO_OOOkkkOOO___OOOk","name":"Eiffel tower","credit":""},{"color":"","size":9,"bricks":"P_t_s_t_PP_t___t_PP_ttttt_PP_______PPPPPPPPPPP_______PP_sssss_PP_s___s_PP_s_t_s_P","name":"Abstract 11","credit":""},{"color":"","size":8,"bricks":"BbBb____bbbb____BbBb____bbbb________tBtB____tttt____tBtB____tttt","name":"Abstract 12","credit":""},{"color":"","size":9,"bricks":"SSSSbSSSSSbbSbSbbSSbbS_SbbSSSSS_SSSSbb_____bbSSSS_SSSSSbbS_SbbSSbbSbSbbSSSSSbSSSS","name":"Abstract 13","credit":""},{"color":"","size":11,"bricks":"aa_tt_aa_ttaa_tt_aa_tt__B__B__B__bb_aa_bb_aabb_aa_bb_aa__B__B__B__aa_bb_tt_bbaa_bb_tt_bb__B__B__B__tt_aa_bb_aatt_aa_bb_aa","name":"Abstract 14","credit":""},{"color":"","size":10,"bricks":"___________Oyyyyyyy__Oyyyyyyy__Oyy__Oyy__Oyy_______Oyyyyyyy_______Oyy__Oyy__Oyy__Oyyyyyyy__Oyyyyyyy_","name":"S","credit":""}]');
},{}],"iyP6E":[function(require,module,exports,__globalThis) {
-module.exports = JSON.parse("\"29077593\"");
+module.exports = JSON.parse("\"29079087\"");
},{}],"1u3Dx":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
@@ -2414,7 +2414,7 @@ parcelHelpers.export(exports, "cycleMaxCoins", ()=>cycleMaxCoins);
let cachedSettings = {};
try {
for(let key in localStorage)try {
- cachedSettings[key] = JSON.parse(localStorage.getItem(key) || 'null');
+ cachedSettings[key] = JSON.parse(localStorage.getItem(key) || "null");
} catch (e) {
console.warn(e);
}
@@ -2508,7 +2508,7 @@ function comboKeepingRate(level) {
}
function hoursSpentPlaying() {
try {
- const timePlayed = (0, _settings.getSettingValue)('breakout_71_total_play_time', 0);
+ const timePlayed = (0, _settings.getSettingValue)("breakout_71_total_play_time", 0);
return Math.floor(timePlayed / 1000 / 60 / 60);
} catch (e) {
return 0;
@@ -2820,33 +2820,6 @@ function createOscillator(context, frequency, type) {
oscillator.frequency.setValueAtTime(frequency, context.currentTime);
return oscillator;
}
-// TODO
-function createWaterDropSound(baseFreq = 500, pan = 0.5, volume = 1, duration = 0.6, type = "sine") {
- const context = getAudioContext();
- if (!context) return;
- const oscillator = createOscillator(context, baseFreq, type);
- const gainNode = context.createGain();
- const panner = context.createStereoPanner();
- // Connect nodes
- oscillator.connect(gainNode);
- gainNode.connect(panner);
- panner.connect(context.destination);
- panner.connect(audioRecordingTrack);
- // Panning
- panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime);
- const now = context.currentTime;
- // Volume envelope: soft plop -> fade out
- gainNode.gain.setValueAtTime(0.0001, now);
- gainNode.gain.exponentialRampToValueAtTime(0.7 * volume, now + duration / 100); // Quick swell
- gainNode.gain.exponentialRampToValueAtTime(0.1, now + duration / 3); // Fade out
- gainNode.gain.exponentialRampToValueAtTime(0.001, now + duration); // Fade out
- // Pitch envelope: slight downward pitch bend to simulate water tension
- oscillator.frequency.setValueAtTime(baseFreq, now);
- oscillator.frequency.exponentialRampToValueAtTime(baseFreq * 0.5, now + duration);
- // Start and stop
- oscillator.start(now);
- oscillator.stop(now + duration);
-}
},{"./options":"d5NoS","@parcel/transformer-js/src/esmodule-helpers.js":"gkKU3"}],"d5NoS":[function(require,module,exports,__globalThis) {
var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
@@ -3887,7 +3860,7 @@ frames = 1) {
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
if (coin.previousY < gameState.gameZoneHeight && coin.y > gameState.gameZoneHeight && coin.vy > 0 && speed > 20) {
schedulGameSound(gameState, "plouf", coin.x, (0, _pureFunctions.clamp)(speed, 20, 100) / 100 * 0.2);
- if (!(0, _options.isOptionOn)('basic')) makeParticle(gameState, coin.x, gameState.gameZoneHeight, -coin.vx / 5, -coin.vy / 5, coin.color, false);
+ if (!(0, _options.isOptionOn)("basic")) makeParticle(gameState, coin.x, gameState.gameZoneHeight, -coin.vx / 5, -coin.vy / 5, coin.color, false);
}
if (coin.y > gameState.gameZoneHeight - coinRadius - gameState.puckHeight && coin.y < gameState.gameZoneHeight + gameState.puckHeight + coin.vy && Math.abs(coin.x - gameState.puckPosition) < coinRadius + gameState.puckWidth / 2 + // a bit of margin to be nice , negative in case it's a negative coin
gameState.puckHeight * (coin.points ? 1 : -1)) {
@@ -4331,7 +4304,7 @@ const haloCanvasCtx = haloCanvas.getContext("2d", {
});
const haloScale = 16;
function render(gameState) {
- (0, _game.startWork)('render:init');
+ (0, _game.startWork)("render:init");
const level = (0, _gameUtils.currentLevelInfo)(gameState);
const hasCombo = gameState.combo > (0, _gameStateMutators.baseCombo)(gameState);
const { width, height } = gameCanvas;
@@ -4342,7 +4315,7 @@ function render(gameState) {
});
else menuLabel.innerText = (0, _i18N.t)("play.menu_label");
const catchRate = gameState.levelSpawnedCoins ? (gameState.levelSpawnedCoins - gameState.levelLostCoins) / gameState.levelSpawnedCoins : 1;
- (0, _game.startWork)('render:scoreDisplay');
+ (0, _game.startWork)("render:scoreDisplay");
scoreDisplay.innerHTML = ((0, _options.isOptionOn)("show_fps") || gameState.computer_controlled ? `
${Math.floor((0, _gameStateMutators.liveCount)(gameState.coins) / (0, _settings.getCurrentMaxCoins)() * 100)} %
@@ -4368,7 +4341,7 @@ function render(gameState) {
scoreDisplay.className = gameState.computer_controlled && "computer_controlled" || gameState.lastScoreIncrease > gameState.levelTime - 500 && "active" || "";
// Clear
if (!(0, _options.isOptionOn)("basic") && level.svg && level.color === "#000000") {
- (0, _game.startWork)('render:halo:clear');
+ (0, _game.startWork)("render:halo:clear");
haloCanvasCtx.globalCompositeOperation = "source-over";
haloCanvasCtx.globalAlpha = 0.99;
haloCanvasCtx.fillStyle = level.color;
@@ -4376,17 +4349,17 @@ function render(gameState) {
const brightness = (0, _options.isOptionOn)("extra_bright") ? 3 : 1;
haloCanvasCtx.globalCompositeOperation = "lighten";
haloCanvasCtx.globalAlpha = 0.1 + 5 / ((0, _gameStateMutators.liveCount)(gameState.coins) + 10);
- (0, _game.startWork)('render:halo:coins');
+ (0, _game.startWork)("render:halo:coins");
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
const color = getCoinRenderColor(gameState, coin);
drawFuzzyBall(haloCanvasCtx, color, gameState.coinSize * 2 * brightness / haloScale, coin.x / haloScale, coin.y / haloScale);
});
- (0, _game.startWork)('render:halo:balls');
+ (0, _game.startWork)("render:halo:balls");
gameState.balls.forEach((ball)=>{
haloCanvasCtx.globalAlpha = 0.3 * (1 - (0, _gameUtils.ballTransparency)(ball, gameState));
drawFuzzyBall(haloCanvasCtx, gameState.ballsColor, gameState.ballSize * 2 * brightness / haloScale, ball.x / haloScale, ball.y / haloScale);
});
- (0, _game.startWork)('render:halo:bricks');
+ (0, _game.startWork)("render:halo:bricks");
haloCanvasCtx.globalAlpha = 0.05;
gameState.bricks.forEach((color, index)=>{
if (!color) return;
@@ -4394,7 +4367,7 @@ function render(gameState) {
drawFuzzyBall(haloCanvasCtx, color == "black" ? "#666666" : color, // Perf could really go down there because of the size of the halo
Math.min(200, gameState.brickWidth * 1.5 * brightness) / haloScale, x / haloScale, y / haloScale);
});
- (0, _game.startWork)('render:halo:particles');
+ (0, _game.startWork)("render:halo:particles");
haloCanvasCtx.globalCompositeOperation = "screen";
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (flash)=>{
const { x, y, time, color, size, duration } = flash;
@@ -4408,7 +4381,7 @@ function render(gameState) {
ctx.imageSmoothingQuality = "high";
ctx.drawImage(haloCanvas, 0, 0, width, height);
ctx.imageSmoothingEnabled = false;
- (0, _game.startWork)('render:halo:pattern');
+ (0, _game.startWork)("render:halo:pattern");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "multiply";
if (level.svg && background.width && background.complete) {
@@ -4448,7 +4421,7 @@ function render(gameState) {
ctx.fillRect(0, 0, width, height);
}
} else {
- (0, _game.startWork)('render:halo-basic');
+ (0, _game.startWork)("render:halo-basic");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = level.color || "#000";
@@ -4460,7 +4433,7 @@ function render(gameState) {
drawBall(ctx, color, size, x, y);
});
}
- (0, _game.startWork)('render:explosionshake');
+ (0, _game.startWork)("render:explosionshake");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
@@ -4469,7 +4442,7 @@ function render(gameState) {
const amplitude = (gameState.perks.bigger_explosions + 1) * 50 / lastExplosionDelay;
ctx.translate(Math.sin(Date.now()) * amplitude, Math.sin(Date.now() + 36) * amplitude);
}
- (0, _game.startWork)('render:coins');
+ (0, _game.startWork)("render:coins");
// Coins
ctx.globalAlpha = 1;
(0, _gameStateMutators.forEachLiveOne)(gameState.coins, (coin)=>{
@@ -4481,7 +4454,7 @@ function render(gameState) {
// (color === "#ffd300" && "#ffd300") ||
hollow && color || gameState.level.color, coin.a);
});
- (0, _game.startWork)('render:ball shade');
+ (0, _game.startWork)("render:ball shade");
// Black shadow around balls
if (!(0, _options.isOptionOn)("basic")) {
ctx.globalCompositeOperation = "source-over";
@@ -4490,10 +4463,10 @@ function render(gameState) {
drawBall(ctx, level.color || "#000", gameState.ballSize * 6, ball.x, ball.y);
});
}
- (0, _game.startWork)('render:bricks');
+ (0, _game.startWork)("render:bricks");
ctx.globalCompositeOperation = "source-over";
renderAllBricks();
- (0, _game.startWork)('render:lights');
+ (0, _game.startWork)("render:lights");
ctx.globalCompositeOperation = "screen";
(0, _gameStateMutators.forEachLiveOne)(gameState.lights, (flash)=>{
const { x, y, time, color, size, duration } = flash;
@@ -4501,7 +4474,7 @@ function render(gameState) {
ctx.globalAlpha = Math.min(1, 2 - elapsed / duration * 2) * 0.5;
drawBrick(gameState, ctx, color, x, y, -1, gameState.perks.clairvoyant >= 2);
});
- (0, _game.startWork)('render:texts');
+ (0, _game.startWork)("render:texts");
ctx.globalCompositeOperation = "screen";
(0, _gameStateMutators.forEachLiveOne)(gameState.texts, (flash)=>{
const { x, y, time, color, size, duration } = flash;
@@ -4510,7 +4483,7 @@ function render(gameState) {
ctx.globalCompositeOperation = "source-over";
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
});
- (0, _game.startWork)('render:particles');
+ (0, _game.startWork)("render:particles");
(0, _gameStateMutators.forEachLiveOne)(gameState.particles, (particle)=>{
const { x, y, time, color, size, duration } = particle;
const elapsed = gameState.levelTime - time;
@@ -4518,14 +4491,14 @@ function render(gameState) {
ctx.globalCompositeOperation = "screen";
drawBall(ctx, color, size, x, y);
});
- (0, _game.startWork)('render:extra_life');
+ (0, _game.startWork)("render:extra_life");
if (gameState.perks.extra_life) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = gameState.puckColor;
for(let i = 0; i < gameState.perks.extra_life; i++)ctx.fillRect(gameState.offsetXRoundedDown, gameState.gameZoneHeight - gameState.puckHeight / 2 + 2 * i, gameState.gameZoneWidthRoundedUp, 1);
}
- (0, _game.startWork)('render:balls');
+ (0, _game.startWork)("render:balls");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
gameState.balls.forEach((ball)=>{
@@ -4553,11 +4526,11 @@ function render(gameState) {
ctx.stroke();
}
});
- (0, _game.startWork)('render:puck');
+ (0, _game.startWork)("render:puck");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
drawPuck(ctx, gameState.puckColor, gameState.puckWidth, gameState.puckHeight, 0, gameState.perks.concave_puck, gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1);
- (0, _game.startWork)('render:combotext');
+ (0, _game.startWork)("render:combotext");
if (gameState.combo > 1) {
ctx.globalCompositeOperation = "source-over";
const comboText = "x " + gameState.combo;
@@ -4569,7 +4542,7 @@ function render(gameState) {
drawText(ctx, comboText, "#000", gameState.puckHeight, left + gameState.coinSize * 1.5, gameState.gameZoneHeight - gameState.puckHeight / 2, true);
} else drawText(ctx, comboTextWidth > gameState.puckWidth ? gameState.combo.toString() : comboText, "#000", comboTextWidth > gameState.puckWidth ? 12 : 20, gameState.puckPosition, gameState.gameZoneHeight - gameState.puckHeight / 2, false);
}
- (0, _game.startWork)('render:Borders');
+ (0, _game.startWork)("render:Borders");
// Borders
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
@@ -4587,7 +4560,7 @@ function render(gameState) {
if (redTop) drawStraightLine(ctx, gameState, "#FF0000", gameState.offsetXRoundedDown, 1, width - gameState.offsetXRoundedDown, 1, 1);
ctx.globalAlpha = 1;
drawStraightLine(ctx, gameState, hasCombo && gameState.perks.compound_interest && "#FF0000" || (0, _options.isOptionOn)("mobile-mode") && "#FFFFFF" || "", gameState.offsetXRoundedDown, gameState.gameZoneHeight, width - gameState.offsetXRoundedDown, gameState.gameZoneHeight, 1);
- (0, _game.startWork)('render:contrast');
+ (0, _game.startWork)("render:contrast");
if (!(0, _options.isOptionOn)("basic") && (0, _options.isOptionOn)("contrast") && level.svg && level.color === "#000000") {
ctx.imageSmoothingEnabled = true;
// haloCanvasCtx.globalCompositeOperation = 'multiply';
@@ -4601,11 +4574,11 @@ function render(gameState) {
ctx.drawImage(haloCanvas, 0, 0, width, height);
ctx.imageSmoothingEnabled = false;
}
- (0, _game.startWork)('render:breakout.lecaro.me?autoplay');
+ (0, _game.startWork)("render:breakout.lecaro.me?autoplay");
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
if ((0, _options.isOptionOn)("mobile-mode") && gameState.computer_controlled) drawText(ctx, "breakout.lecaro.me?autoplay", gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
- (0, _game.startWork)('render:mobile_press_to_play');
+ (0, _game.startWork)("render:mobile_press_to_play");
if ((0, _options.isOptionOn)("mobile-mode") && !gameState.running) drawText(ctx, (0, _i18N.t)("play.mobile_press_to_play"), gameState.puckColor, gameState.puckHeight, gameState.canvasWidth / 2, gameState.gameZoneHeight + (gameState.canvasHeight - gameState.gameZoneHeight) / 2);
// if(isOptionOn('mobile-mode')) {
// ctx.globalCompositeOperation = "source-over";
@@ -4614,9 +4587,9 @@ function render(gameState) {
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
// }
// ctx.globalAlpha=1
- (0, _game.startWork)('render:askForWakeLock');
+ (0, _game.startWork)("render:askForWakeLock");
askForWakeLock(gameState);
- (0, _game.startWork)('render:resetTransform');
+ (0, _game.startWork)("render:resetTransform");
if (shaked) ctx.resetTransform();
}
function drawStraightLine(ctx, gameState, mode, x1, y1, x2, y2, alpha = 1) {
@@ -4921,7 +4894,7 @@ var _asyncAlert = require("./asyncAlert");
var _upgrades = require("./upgrades");
var _levelEditor = require("./levelEditor");
function addToTotalPlayTime(ms) {
- (0, _settings.setSettingValue)('breakout_71_total_play_time', (0, _settings.getSettingValue)('breakout_71_total_play_time', 0) + ms);
+ (0, _settings.setSettingValue)("breakout_71_total_play_time", (0, _settings.getSettingValue)("breakout_71_total_play_time", 0) + ms);
}
function gameOver(title, intro) {
if (!(0, _game.gameState).running) return;
@@ -5590,7 +5563,7 @@ var parcelHelpers = require("@parcel/transformer-js/src/esmodule-helpers.js");
parcelHelpers.defineInteropFlag(exports);
parcelHelpers.export(exports, "toast", ()=>toast);
let div = document.createElement("div");
-div.classList = 'hidden toast';
+div.classList = "hidden toast";
document.body.appendChild(div);
let timeout;
function toast(html) {
@@ -5599,7 +5572,7 @@ function toast(html) {
if (timeout) clearTimeout(timeout);
timeout = setTimeout(()=>{
timeout = undefined;
- div.classList = 'hidden toast';
+ div.classList = "hidden toast";
}, 1500);
}
diff --git a/src/PWA/sw-b71.js b/src/PWA/sw-b71.js
index 175436c..4d8fecb 100644
--- a/src/PWA/sw-b71.js
+++ b/src/PWA/sw-b71.js
@@ -1,5 +1,5 @@
// The version of the cache.
-const VERSION = "29077593";
+const VERSION = "29079087";
// The name of the cache
const CACHE_NAME = `breakout-71-${VERSION}`;
diff --git a/src/data/levels.json b/src/data/levels.json
index eb4748c..48fc058 100644
--- a/src/data/levels.json
+++ b/src/data/levels.json
@@ -1303,4 +1303,4 @@
"name": "S",
"credit": ""
}
-]
\ No newline at end of file
+]
diff --git a/src/data/version.json b/src/data/version.json
index 28d01ef..75ef593 100644
--- a/src/data/version.json
+++ b/src/data/version.json
@@ -1 +1 @@
-"29077593"
+"29079087"
diff --git a/src/game.less b/src/game.less
index 285eb65..21d235c 100644
--- a/src/game.less
+++ b/src/game.less
@@ -521,7 +521,6 @@ h2.histogram-title strong {
}
}
-
.toast {
position: fixed;
left: 0;
@@ -535,18 +534,19 @@ h2.histogram-title strong {
border-radius: 2px;
padding-right: 10px;
pointer-events: none;
- transition: opacity 200ms, transform 200ms;
- &.hidden{
+ transition:
+ opacity 200ms,
+ transform 200ms;
+ &.hidden {
opacity: 0;
transform: translate(-20px, -20px) scale(0.5);
}
- &.visible{
+ &.visible {
opacity: 0.8;
transform: none;
}
}
-
.gridEdit > div > span,
.palette > span {
display: inline-flex;
diff --git a/src/game.ts b/src/game.ts
index 4cd9df3..65dbcc7 100644
--- a/src/game.ts
+++ b/src/game.ts
@@ -27,7 +27,8 @@ import {
max_levels,
pickedUpgradesHTMl,
reasonLevelIsLocked,
- sample, sumOfValues,
+ sample,
+ sumOfValues,
} from "./game_utils";
import "./PWA/sw_loader";
@@ -41,7 +42,8 @@ import {
} from "./settings";
import {
forEachLiveOne,
- gameStateTick, liveCount,
+ gameStateTick,
+ liveCount,
normalizeGameState,
pickRandomUpgrades,
setLevel,
@@ -420,9 +422,8 @@ export function hitsSomething(x: number, y: number, radius: number) {
);
}
-
export function tick() {
-startWork('tick init')
+ startWork("tick init");
const currentTick = performance.now();
const timeDeltaMs = currentTick - gameState.lastTick;
@@ -443,9 +444,9 @@ startWork('tick init')
);
}
-startWork('normalizeGameState')
+ startWork("normalizeGameState");
normalizeGameState(gameState);
-startWork('gameStateTick')
+ startWork("gameStateTick");
if (gameState.running) {
gameState.levelTime += timeDeltaMs * frames;
gameState.runStatistics.runTime += timeDeltaMs * frames;
@@ -456,15 +457,15 @@ startWork('gameStateTick')
gameState.needsRender = false;
render(gameState);
}
-startWork('recordOneFrame')
+ startWork("recordOneFrame");
if (gameState.running) {
recordOneFrame(gameState);
}
-startWork('playPendingSounds')
+ startWork("playPendingSounds");
if (isOptionOn("sound")) {
playPendingSounds(gameState);
}
-startWork('idle')
+ startWork("idle");
requestAnimationFrame(tick);
FPSCounter++;
@@ -477,28 +478,41 @@ setInterval(() => {
FPSCounter = 0;
}, 1000);
-const showStats= window.location.search.includes("stress")
-let total={}
-let lastTick=performance.now();
-let doing= ''
-export function startWork(what){
- if(!showStats) return
- const newNow=performance.now();
- if(doing) {
- total[doing] = (total[doing]||0) + ( newNow-lastTick )
+const showStats = window.location.search.includes("stress");
+let total = {};
+let lastTick = performance.now();
+let doing = "";
+export function startWork(what) {
+ if (!showStats) return;
+ const newNow = performance.now();
+ if (doing) {
+ total[doing] = (total[doing] || 0) + (newNow - lastTick);
}
- lastTick=newNow
- doing=what
+ lastTick = newNow;
+ doing = what;
}
-if(showStats)
-setInterval(()=>{
- const totalTime = sumOfValues(total)
- console.debug(
- liveCount(gameState.coins) +' coins\n'+
- Object.entries(total).sort((a,b)=>b[1]-a[1]).filter(a=>a[1]>1).map(t=>t[0]+':'+(t[1]/totalTime*100).toFixed(2)+'% ('+t[1]+'ms)').join('\n'))
- total={}
-},2000)
-
+if (showStats)
+ setInterval(() => {
+ const totalTime = sumOfValues(total);
+ console.debug(
+ liveCount(gameState.coins) +
+ " coins\n" +
+ Object.entries(total)
+ .sort((a, b) => b[1] - a[1])
+ .filter((a) => a[1] > 1)
+ .map(
+ (t) =>
+ t[0] +
+ ":" +
+ ((t[1] / totalTime) * 100).toFixed(2) +
+ "% (" +
+ t[1] +
+ "ms)",
+ )
+ .join("\n"),
+ );
+ total = {};
+ }, 2000);
setInterval(() => {
monitorLevelsUnlocks(gameState);
@@ -1041,22 +1055,26 @@ export function restart(params: RunParams) {
play();
}
}
- if (window.location.search.match(/autoplay|stress/) ) {
+if (window.location.search.match(/autoplay|stress/)) {
startComputerControlledGame();
- if(!isOptionOn('show_fps'))
- toggleOption('show_fps')
+ if (!isOptionOn("show_fps")) toggleOption("show_fps");
} else {
restart({});
}
export function startComputerControlledGame() {
-
const perks: Partial = { base_combo: 20, pierce: 3 };
- if(window.location.search.includes("stress")){
-
- Object.assign(perks,{base_combo:5000, pierce:20, rainbow:3, sapper:2, etherealcoins:1, bricks_attract_ball:1, respawn:3})
-
- }else{
+ if (window.location.search.includes("stress")) {
+ Object.assign(perks, {
+ base_combo: 5000,
+ pierce: 20,
+ rainbow: 3,
+ sapper: 2,
+ etherealcoins: 1,
+ bricks_attract_ball: 1,
+ respawn: 3,
+ });
+ } else {
for (let i = 0; i < 10; i++) {
const u = sample(upgrades);
diff --git a/src/gameOver.ts b/src/gameOver.ts
index 366faa5..7c0af07 100644
--- a/src/gameOver.ts
+++ b/src/gameOver.ts
@@ -9,7 +9,7 @@ import {
pickedUpgradesHTMl,
reasonLevelIsLocked,
} from "./game_utils";
-import {getSettingValue, getTotalScore, setSettingValue} from "./settings";
+import { getSettingValue, getTotalScore, setSettingValue } from "./settings";
import { stopRecording } from "./recording";
import { asyncAlert } from "./asyncAlert";
import { rawUpgrades } from "./upgrades";
@@ -17,7 +17,10 @@ import { run } from "jest";
import { editRawLevelList } from "./levelEditor";
export function addToTotalPlayTime(ms: number) {
- setSettingValue('breakout_71_total_play_time', getSettingValue('breakout_71_total_play_time',0)+ms)
+ setSettingValue(
+ "breakout_71_total_play_time",
+ getSettingValue("breakout_71_total_play_time", 0) + ms,
+ );
}
export function gameOver(title: string, intro: string) {
diff --git a/src/gameStateMutators.ts b/src/gameStateMutators.ts
index 2942828..9ee24d5 100644
--- a/src/gameStateMutators.ts
+++ b/src/gameStateMutators.ts
@@ -461,11 +461,11 @@ export function explodeBrick(
gameState.runStatistics.coins_spawned += coinsToSpawn;
gameState.runStatistics.bricks_broken++;
- const maxCoins = getCurrentMaxCoins()
+ const maxCoins = getCurrentMaxCoins();
const spawnableCoins =
liveCount(gameState.coins) > getCurrentMaxCoins()
? 1
- : Math.floor((maxCoins - liveCount(gameState.coins)) /2) ;
+ : Math.floor((maxCoins - liveCount(gameState.coins)) / 2);
const pointsPerCoin = Math.max(1, Math.ceil(coinsToSpawn / spawnableCoins));
@@ -1218,10 +1218,28 @@ export function gameStateTick(
const speed = (Math.abs(coin.vx) + Math.abs(coin.vy)) * 10;
const hitBorder = bordersHitCheck(gameState, coin, coin.size / 2, frames);
- if(coin.previousYgameState.gameZoneHeight && coin.vy>0 && speed > 20) {
- schedulGameSound(gameState, "plouf", coin.x, clamp(speed, 20,100)/100*0.2);
- if(!isOptionOn('basic')){
- makeParticle(gameState, coin.x,gameState.gameZoneHeight, -coin.vx/5, -coin.vy/5, coin.color, false )
+ if (
+ coin.previousY < gameState.gameZoneHeight &&
+ coin.y > gameState.gameZoneHeight &&
+ coin.vy > 0 &&
+ speed > 20
+ ) {
+ schedulGameSound(
+ gameState,
+ "plouf",
+ coin.x,
+ (clamp(speed, 20, 100) / 100) * 0.2,
+ );
+ if (!isOptionOn("basic")) {
+ makeParticle(
+ gameState,
+ coin.x,
+ gameState.gameZoneHeight,
+ -coin.vx / 5,
+ -coin.vy / 5,
+ coin.color,
+ false,
+ );
}
}
diff --git a/src/pure_functions.ts b/src/pure_functions.ts
index b143cd3..36d3c8a 100644
--- a/src/pure_functions.ts
+++ b/src/pure_functions.ts
@@ -1,4 +1,4 @@
-import {getSettingValue} from "./settings";
+import { getSettingValue } from "./settings";
export function clamp(value: number, min: number, max: number) {
return Math.max(min, Math.min(value, max));
@@ -10,7 +10,7 @@ export function comboKeepingRate(level: number) {
export function hoursSpentPlaying() {
try {
- const timePlayed = getSettingValue('breakout_71_total_play_time',0)
+ const timePlayed = getSettingValue("breakout_71_total_play_time", 0);
return Math.floor(timePlayed / 1000 / 60 / 60);
} catch (e) {
return 0;
diff --git a/src/recording.ts b/src/recording.ts
index c72b907..f771bcc 100644
--- a/src/recording.ts
+++ b/src/recording.ts
@@ -12,7 +12,6 @@ let mediaRecorder: MediaRecorder | null,
recordCanvasCtx: CanvasRenderingContext2D;
export function recordOneFrame(gameState: GameState) {
-
if (!isOptionOn("record")) {
return;
}
diff --git a/src/render.ts b/src/render.ts
index dd68bd2..44b9c96 100644
--- a/src/render.ts
+++ b/src/render.ts
@@ -13,7 +13,7 @@ import {
} from "./game_utils";
import { Coin, colorString, GameState } from "./types";
import { t } from "./i18n/i18n";
-import {gameState, lastMeasuredFPS, startWork} from "./game";
+import { gameState, lastMeasuredFPS, startWork } from "./game";
import { isOptionOn } from "./options";
import {
catchRateBest,
@@ -25,7 +25,7 @@ import {
wallBouncedBest,
wallBouncedGood,
} from "./pure_functions";
-import {getCurrentMaxCoins} from "./settings";
+import { getCurrentMaxCoins } from "./settings";
export const gameCanvas = document.getElementById("game") as HTMLCanvasElement;
export const ctx = gameCanvas.getContext("2d", {
@@ -52,7 +52,7 @@ const haloCanvasCtx = haloCanvas.getContext("2d", {
export const haloScale = 16;
export function render(gameState: GameState) {
-startWork('render:init')
+ startWork("render:init");
const level = currentLevelInfo(gameState);
const hasCombo = gameState.combo > baseCombo(gameState);
@@ -72,12 +72,12 @@ startWork('render:init')
? (gameState.levelSpawnedCoins - gameState.levelLostCoins) /
gameState.levelSpawnedCoins
: 1;
-startWork('render:scoreDisplay')
+ startWork("render:scoreDisplay");
scoreDisplay.innerHTML =
(isOptionOn("show_fps") || gameState.computer_controlled
? `
- ${Math.floor(liveCount(gameState.coins) / getCurrentMaxCoins() * 100)} %
+ ${Math.floor((liveCount(gameState.coins) / getCurrentMaxCoins()) * 100)} %
/
${lastMeasuredFPS} FPS
@@ -109,8 +109,7 @@ startWork('render:scoreDisplay')
"";
// Clear
if (!isOptionOn("basic") && level.svg && level.color === "#000000") {
-
- startWork('render:halo:clear')
+ startWork("render:halo:clear");
haloCanvasCtx.globalCompositeOperation = "source-over";
haloCanvasCtx.globalAlpha = 0.99;
haloCanvasCtx.fillStyle = level.color;
@@ -120,7 +119,7 @@ startWork('render:scoreDisplay')
haloCanvasCtx.globalCompositeOperation = "lighten";
haloCanvasCtx.globalAlpha =
0.1 + (0.5 * 10) / (liveCount(gameState.coins) + 10);
- startWork('render:halo:coins')
+ startWork("render:halo:coins");
forEachLiveOne(gameState.coins, (coin) => {
const color = getCoinRenderColor(gameState, coin);
drawFuzzyBall(
@@ -132,7 +131,7 @@ startWork('render:scoreDisplay')
);
});
- startWork('render:halo:balls')
+ startWork("render:halo:balls");
gameState.balls.forEach((ball) => {
haloCanvasCtx.globalAlpha = 0.3 * (1 - ballTransparency(ball, gameState));
drawFuzzyBall(
@@ -144,7 +143,7 @@ startWork('render:scoreDisplay')
);
});
- startWork('render:halo:bricks')
+ startWork("render:halo:bricks");
haloCanvasCtx.globalAlpha = 0.05;
gameState.bricks.forEach((color, index) => {
if (!color) return;
@@ -160,7 +159,7 @@ startWork('render:scoreDisplay')
);
});
- startWork('render:halo:particles')
+ startWork("render:halo:particles");
haloCanvasCtx.globalCompositeOperation = "screen";
forEachLiveOne(gameState.particles, (flash) => {
const { x, y, time, color, size, duration } = flash;
@@ -184,7 +183,7 @@ startWork('render:scoreDisplay')
ctx.drawImage(haloCanvas, 0, 0, width, height);
ctx.imageSmoothingEnabled = false;
- startWork('render:halo:pattern')
+ startWork("render:halo:pattern");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "multiply";
if (level.svg && background.width && background.complete) {
@@ -236,8 +235,7 @@ startWork('render:scoreDisplay')
ctx.fillRect(0, 0, width, height);
}
} else {
-
- startWork('render:halo-basic')
+ startWork("render:halo-basic");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
ctx.fillStyle = level.color || "#000";
@@ -250,7 +248,7 @@ startWork('render:scoreDisplay')
});
}
-startWork('render:explosionshake')
+ startWork("render:explosionshake");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
const lastExplosionDelay = Date.now() - gameState.lastExplosion + 5;
@@ -263,7 +261,7 @@ startWork('render:explosionshake')
Math.sin(Date.now() + 36) * amplitude,
);
}
-startWork('render:coins')
+ startWork("render:coins");
// Coins
ctx.globalAlpha = 1;
forEachLiveOne(gameState.coins, (coin) => {
@@ -286,7 +284,7 @@ startWork('render:coins')
coin.a,
);
});
- startWork('render:ball shade')
+ startWork("render:ball shade");
// Black shadow around balls
if (!isOptionOn("basic")) {
ctx.globalCompositeOperation = "source-over";
@@ -304,11 +302,11 @@ startWork('render:coins')
);
});
}
-startWork('render:bricks')
+ startWork("render:bricks");
ctx.globalCompositeOperation = "source-over";
renderAllBricks();
-startWork('render:lights')
+ startWork("render:lights");
ctx.globalCompositeOperation = "screen";
forEachLiveOne(gameState.lights, (flash) => {
const { x, y, time, color, size, duration } = flash;
@@ -325,7 +323,7 @@ startWork('render:lights')
);
});
-startWork('render:texts')
+ startWork("render:texts");
ctx.globalCompositeOperation = "screen";
forEachLiveOne(gameState.texts, (flash) => {
const { x, y, time, color, size, duration } = flash;
@@ -335,7 +333,7 @@ startWork('render:texts')
drawText(ctx, flash.text, color, size, x, y - elapsed / 10);
});
-startWork('render:particles')
+ startWork("render:particles");
forEachLiveOne(gameState.particles, (particle) => {
const { x, y, time, color, size, duration } = particle;
const elapsed = gameState.levelTime - time;
@@ -344,7 +342,7 @@ startWork('render:particles')
drawBall(ctx, color, size, x, y);
});
-startWork('render:extra_life')
+ startWork("render:extra_life");
if (gameState.perks.extra_life) {
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
@@ -359,7 +357,7 @@ startWork('render:extra_life')
}
}
-startWork('render:balls')
+ startWork("render:balls");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
gameState.balls.forEach((ball) => {
@@ -411,7 +409,7 @@ startWork('render:balls')
}
});
- startWork('render:puck')
+ startWork("render:puck");
ctx.globalAlpha = 1;
ctx.globalCompositeOperation = "source-over";
drawPuck(
@@ -424,7 +422,7 @@ startWork('render:balls')
gameState.perks.streak_shots && hasCombo ? getDashOffset(gameState) : -1,
);
- startWork('render:combotext')
+ startWork("render:combotext");
if (gameState.combo > 1) {
ctx.globalCompositeOperation = "source-over";
const comboText = "x " + gameState.combo;
@@ -464,7 +462,7 @@ startWork('render:balls')
);
}
}
- startWork('render:Borders')
+ startWork("render:Borders");
// Borders
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
@@ -548,7 +546,7 @@ startWork('render:balls')
1,
);
- startWork('render:contrast')
+ startWork("render:contrast");
if (
!isOptionOn("basic") &&
isOptionOn("contrast") &&
@@ -569,7 +567,7 @@ startWork('render:balls')
ctx.imageSmoothingEnabled = false;
}
- startWork('render:breakout.lecaro.me?autoplay')
+ startWork("render:breakout.lecaro.me?autoplay");
ctx.globalCompositeOperation = "source-over";
ctx.globalAlpha = 1;
@@ -584,7 +582,7 @@ startWork('render:balls')
(gameState.canvasHeight - gameState.gameZoneHeight) / 2,
);
}
- startWork('render:mobile_press_to_play')
+ startWork("render:mobile_press_to_play");
if (isOptionOn("mobile-mode") && !gameState.running) {
drawText(
ctx,
@@ -604,10 +602,10 @@ startWork('render:balls')
// ctx.fillRect(0,gameState.gameZoneHeight, gameState.canvasWidth, gameState.canvasHeight-gameState.gameZoneHeight)
// }
// ctx.globalAlpha=1
- startWork('render:askForWakeLock')
+ startWork("render:askForWakeLock");
askForWakeLock(gameState);
- startWork('render:resetTransform')
+ startWork("render:resetTransform");
if (shaked) {
ctx.resetTransform();
}
diff --git a/src/settings.ts b/src/settings.ts
index 81ab5ec..901a46d 100644
--- a/src/settings.ts
+++ b/src/settings.ts
@@ -2,15 +2,14 @@
let cachedSettings: { [key: string]: unknown } = {};
- try {
- for(let key in localStorage){
-
- try {
- cachedSettings[key] = JSON.parse(localStorage.getItem(key)||'null') ;
- } catch (e) {
- console.warn(e);
-}
- }
+try {
+ for (let key in localStorage) {
+ try {
+ cachedSettings[key] = JSON.parse(localStorage.getItem(key) || "null");
+ } catch (e) {
+ console.warn(e);
+ }
+ }
} catch (e) {
console.warn(e);
}
@@ -20,24 +19,23 @@ export function getSettingValue(key: string, defaultValue: T) {
}
// We avoid using localstorage synchronously for perf reasons
-let needsSaving= new Set()
+let needsSaving: Set = new Set();
export function setSettingValue(key: string, value: T) {
- if(cachedSettings[key] !==value){
- needsSaving.add(key)
- cachedSettings[key] = value;
+ if (cachedSettings[key] !== value) {
+ needsSaving.add(key);
+ cachedSettings[key] = value;
}
}
-setInterval(()=>{
+setInterval(() => {
try {
- for(let key of needsSaving){
- localStorage.setItem(key, JSON.stringify(cachedSettings[key]));
+ for (let key of needsSaving) {
+ localStorage.setItem(key, JSON.stringify(cachedSettings[key]));
}
- needsSaving.clear()
+ needsSaving.clear();
} catch (e) {
console.warn(e);
}
-}, 500)
-
+}, 500);
export function getTotalScore() {
return getSettingValue("breakout_71_total_score", 0);
@@ -47,7 +45,7 @@ export function getCurrentMaxCoins() {
return Math.pow(2, getSettingValue("max_coins", 2)) * 200;
}
export function getCurrentMaxParticles() {
- return getCurrentMaxCoins()
+ return getCurrentMaxCoins();
}
export function cycleMaxCoins() {
setSettingValue("max_coins", (getSettingValue("max_coins", 2) + 1) % 7);
diff --git a/src/sounds.ts b/src/sounds.ts
index a4d9f2e..f345b73 100644
--- a/src/sounds.ts
+++ b/src/sounds.ts
@@ -33,7 +33,7 @@ export const sounds = {
plouf: (volume: number, pan: number) => {
if (!isOptionOn("sound")) return;
- createSingleBounceSound(500, pan, volume*0.5);
+ createSingleBounceSound(500, pan, volume * 0.5);
// createWaterDropSound(800, pan, volume*0.2, 0.2,'triangle')
},
@@ -277,44 +277,3 @@ function createOscillator(
oscillator.frequency.setValueAtTime(frequency, context.currentTime);
return oscillator;
}
-// TODO
-
-function createWaterDropSound(
- baseFreq = 500,
- pan = 0.5,
- volume = 1,
- duration = 0.6,
- type: OscillatorType = "sine"
-) {
- const context = getAudioContext();
- if (!context) return;
-
- const oscillator = createOscillator(context, baseFreq, type);
- const gainNode = context.createGain();
- const panner = context.createStereoPanner();
-
- // Connect nodes
- oscillator.connect(gainNode);
- gainNode.connect(panner);
- panner.connect(context.destination);
- panner.connect(audioRecordingTrack);
-
- // Panning
- panner.pan.setValueAtTime(pan * 2 - 1, context.currentTime);
-
- const now = context.currentTime;
-
- // Volume envelope: soft plop -> fade out
- gainNode.gain.setValueAtTime(0.0001, now);
- gainNode.gain.exponentialRampToValueAtTime(0.7 * volume, now + duration/100); // Quick swell
- gainNode.gain.exponentialRampToValueAtTime(0.1, now + duration/3); // Fade out
- gainNode.gain.exponentialRampToValueAtTime(0.001, now + duration); // Fade out
-
- // Pitch envelope: slight downward pitch bend to simulate water tension
- oscillator.frequency.setValueAtTime(baseFreq, now);
- oscillator.frequency.exponentialRampToValueAtTime(baseFreq * 0.5, now + duration);
-
- // Start and stop
- oscillator.start(now);
- oscillator.stop(now + duration);
-}
\ No newline at end of file
diff --git a/src/toast.ts b/src/toast.ts
index 6fef3ca..e1efd0d 100644
--- a/src/toast.ts
+++ b/src/toast.ts
@@ -1,17 +1,15 @@
-
-
-let div= document.createElement("div");
-div.classList = 'hidden toast';
- document.body.appendChild(div);
-let timeout: NodeJS.Timeout|undefined;
+let div = document.createElement("div");
+div.classList = "hidden toast";
+document.body.appendChild(div);
+let timeout: NodeJS.Timeout | undefined;
export function toast(html) {
div.classList = "toast visible";
div.innerHTML = html;
- if(timeout) {
- clearTimeout(timeout)
+ if (timeout) {
+ clearTimeout(timeout);
}
- timeout=setTimeout(() => {
- timeout=undefined
- div.classList = 'hidden toast';
+ timeout = setTimeout(() => {
+ timeout = undefined;
+ div.classList = "hidden toast";
}, 1500);
}