From 708c9bda1c63d83ea392dc1a5e8acfd9924742df Mon Sep 17 00:00:00 2001 From: Renan LE CARO Date: Wed, 19 Mar 2025 14:06:49 +0100 Subject: [PATCH] Build 29039826 --- Readme.md | 12 +- app/build.gradle.kts | 4 +- app/src/main/assets/index.html | 3761 +++++++++++++++++++++++++++++++- dist/PWA/sw-b71.js | 33 +- dist/PWA/sw-b71.js.map | 2 +- dist/index.html | 3761 +++++++++++++++++++++++++++++++- src/PWA/sw-b71.js | 2 +- src/data/version.json | 2 +- src/game.ts | 2 - src/gameStateMutators.ts | 47 +- src/types.d.ts | 1 - 11 files changed, 7593 insertions(+), 34 deletions(-) diff --git a/Readme.md b/Readme.md index f5c5f52..c6dc4ea 100644 --- a/Readme.md +++ b/Readme.md @@ -23,8 +23,10 @@ There's also an easy mode for kids (slower ball). # Next -- check which color you get if picking a color related perk -- sturdy bricks map of remaining hits +- different visual effects on ball to represent which perks it's imbued with (pierce, sapper…). remove visual while it's not affected (can't pierce/sap anymore until touching the puck). +- check which ball color you get right after picking a color related perk +- sturdy bricks: map of remaining hits +- looks like offline PWA mode does not work # bugs @@ -67,11 +69,10 @@ There's also an easy mode for kids (slower ball). # graphics -- apply global curve / brightness to canvas when things blow, or just always to make neon effect better + - lights shadows with background gradient light map ? - webgl rendering - shinier coins by applying glow to them -- different visual effects on ball to represent which perks it's imbued with (pierce, sapper…). remove visual while it's not affected (can't pierce/sap anymore until touching the puck). - experiment with showing the combo somewhere else, maybe top center, maybe instead of score. - the white outline on bricks associated with picky eater kinda works but i feel it's more distracting than anything. maybe try something different ? put a cross on matching coloured bricks, or the contrary, grey out other bricks. @@ -160,8 +161,7 @@ There's also an easy mode for kids (slower ball). - [colin] perk: roulette - gagne instantanément 2 perks aléatoires - let coins go out of bounds left and right, where they'll get lost, but +1 combo per brick - more combo if no coin catch -- combo climbs every time a ball bounces on puck (but bounce is random?) -- +- combo climbs every time a ball bounces on puck (but bounce is random?) # extra levels diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 90a2aae..9481027 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "me.lecaro.breakout" minSdk = 21 targetSdk = 34 - versionCode = 29038489 - versionName = "29038489" + versionCode = 29039826 + versionName = "29039826" 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 0e7e7b6..2e794e0 100644 --- a/app/src/main/assets/index.html +++ b/app/src/main/assets/index.html @@ -1 +1,3760 @@ -Breakout 71 \ No newline at end of file + + + + + + + Breakout 71 + + + + + + + + + + + + + diff --git a/dist/PWA/sw-b71.js b/dist/PWA/sw-b71.js index c6e5413..e62fff8 100644 --- a/dist/PWA/sw-b71.js +++ b/dist/PWA/sw-b71.js @@ -1,2 +1,33 @@ -function e(e,t,n,r,a,i,c){try{var o=e[i](c),u=o.value}catch(e){n(e);return}o.done?t(u):Promise.resolve(u).then(r,a)}function t(t){return function(){var n=this,r=arguments;return new Promise(function(a,i){var c=t.apply(n,r);function o(t){e(c,a,i,o,u,"next",t)}function u(t){e(c,a,i,o,u,"throw",t)}o(void 0)})}}function n(e,t){var n,r,a,i,c={label:0,sent:function(){if(1&a[0])throw a[1];return a[1]},trys:[],ops:[]};return i={next:o(0),throw:o(1),return:o(2)},"function"==typeof Symbol&&(i[Symbol.iterator]=function(){return this}),i;function o(i){return function(o){return function(i){if(n)throw TypeError("Generator is already executing.");for(;c;)try{if(n=1,r&&(a=2&i[0]?r.return:i[0]?r.throw||((a=r.return)&&a.call(r),0):r.next)&&!(a=a.call(r,i[1])).done)return a;switch(r=0,a&&(i=[2&i[0],a.value]),i[0]){case 0:case 1:a=i;break;case 4:return c.label++,{value:i[1],done:!1};case 5:c.label++,r=i[1],i=[0];continue;case 7:i=c.ops.pop(),c.trys.pop();continue;default:if(!(a=(a=c.trys).length>0&&a[a.length-1])&&(6===i[0]||2===i[0])){c=0;continue}if(3===i[0]&&(!a||i[1]>a[0]&&i[1]{ + event.waitUntil((async ()=>{ + const cache = await caches.open(CACHE_NAME); + cache.addAll(APP_STATIC_RESOURCES); + })()); +}); +// delete old caches on activate +self.addEventListener("activate", (event)=>{ + event.waitUntil((async ()=>{ + const names = await caches.keys(); + await Promise.all(names.map((name)=>{ + if (name !== CACHE_NAME) return caches.delete(name); + })); + await clients.claim(); + })()); +}); +self.addEventListener("fetch", (event)=>{ + if (event.request.mode === "navigate" && event.request.url.endsWith("/index.html?isPWA=true")) { + event.respondWith(caches.match("/")); + return; + } +}); + //# sourceMappingURL=sw-b71.js.map diff --git a/dist/PWA/sw-b71.js.map b/dist/PWA/sw-b71.js.map index aee76cf..2667b32 100644 --- a/dist/PWA/sw-b71.js.map +++ b/dist/PWA/sw-b71.js.map @@ -1 +1 @@ -{"mappings":"A,S,E,C,C,C,C,C,C,C,C,C,C,C,C,C,E,G,C,I,E,C,C,E,C,G,E,E,K,A,C,M,E,C,E,G,M,C,E,I,C,E,G,Q,O,C,G,I,C,E,E,C,S,E,C,E,O,W,I,E,I,C,E,U,O,I,Q,S,C,C,C,E,I,E,E,K,C,E,G,S,E,C,E,E,E,E,E,E,E,O,E,C,S,E,C,E,E,E,E,E,E,E,Q,E,C,E,K,E,E,C,C,S,E,C,C,C,E,I,E,E,E,E,E,C,M,E,K,W,G,A,E,C,C,E,C,M,C,C,E,C,O,C,C,E,A,E,K,E,C,I,E,A,E,O,E,C,K,E,G,M,E,G,O,E,E,E,A,Y,O,Q,C,C,C,O,Q,C,C,W,O,I,A,C,E,E,S,E,C,E,O,S,C,E,O,A,S,C,E,G,E,M,A,U,mC,K,G,G,C,G,E,E,G,C,E,A,E,C,C,E,C,E,M,C,C,C,E,C,E,K,E,C,A,C,E,E,M,A,G,E,I,C,G,C,E,E,I,A,G,C,A,C,E,E,I,C,E,C,C,E,C,E,I,C,O,E,O,E,E,A,G,C,E,C,A,E,C,C,E,C,E,K,C,A,E,C,C,E,E,K,E,K,E,E,E,K,M,E,O,E,K,G,C,M,C,C,E,C,K,C,C,C,M,E,E,K,G,E,C,C,E,C,E,C,E,C,Q,M,E,E,E,G,C,G,G,E,I,C,G,G,Q,S,G,C,C,E,A,C,E,E,I,A,E,M,C,G,C,C,E,M,C,E,A,G,C,A,I,C,C,E,E,A,I,C,C,E,A,E,C,E,E,Q,C,G,A,I,C,C,E,E,C,C,G,C,C,E,C,C,C,E,E,C,C,E,C,C,C,E,A,E,C,E,K,C,C,C,E,C,K,C,G,A,I,C,C,E,E,E,K,C,C,C,E,C,C,E,K,C,C,C,E,C,E,E,K,C,G,G,E,K,C,C,C,E,C,C,E,K,C,C,C,E,C,E,G,C,I,C,G,K,C,C,C,E,E,E,G,C,G,G,E,I,C,G,G,Q,C,E,E,I,C,E,E,C,M,E,C,E,C,E,E,C,E,C,Q,C,E,E,C,C,G,A,E,C,C,E,C,M,C,C,E,C,M,C,M,C,C,E,C,C,C,E,C,K,E,K,C,C,C,E,C,E,E,C,C,C,CCIA,IAAM,EAAc,eAAsB,MAAA,CAH1B,YAMV,EAAuB,CAAC,IAAI,CAGlC,KAAK,gBAAgB,CAAC,UAAW,SAAC,CAAlC,EACE,EAAM,SAAS,CACb,EAAC,W,O,E,I,C,S,C,E,O,E,K,E,K,EACe,MAAA,C,EAAM,OAAO,IAAI,CAAC,G,A,M,E,OAChC,AADc,EAAR,IAAA,GACA,MAAM,CAAC,G,C,E,A,C,EACf,KAEJ,GAGA,KAAK,gBAAgB,CAAC,WAAY,SAAC,CAAnC,EACE,EAAM,SAAS,CACb,EAAC,W,O,E,I,C,S,C,E,O,E,K,E,K,EACe,MAAA,C,EAAM,OAAO,IAAI,G,A,M,EAC/B,MAAA,C,EAAM,QAAQ,GAAG,CACf,AAFY,EAAR,IAAA,GAEE,GAAG,CAAC,SAAC,CADP,EAEF,GAAI,IAAS,EACX,OAAO,OAAO,MAAM,CAAC,EAEzB,I,A,M,EAEF,OAPA,EAAA,IAAA,GAOA,C,EAAM,QAAQ,KAAK,G,A,M,E,OAAnB,EAAA,IAAA,G,C,E,A,C,EACF,KAEJ,GAEA,KAAK,gBAAgB,CAAC,QAAS,SAAC,CAAhC,EACE,GACE,AAAuB,aAAvB,EAAM,OAAO,CAAC,IAAI,EAClB,EAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,0BAC3B,CACA,EAAM,WAAW,CAAC,OAAO,KAAK,CAAC,MAC/B,MACF,CACF","sources":["","src/PWA/sw-b71.js"],"sourcesContent":["// The version of the cache.\nfunction asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {\n try {\n var info = gen[key](arg);\n var value = info.value;\n } catch (error) {\n reject(error);\n return;\n }\n if (info.done) {\n resolve(value);\n } else {\n Promise.resolve(value).then(_next, _throw);\n }\n}\nfunction _async_to_generator(fn) {\n return function() {\n var self1 = this, args = arguments;\n return new Promise(function(resolve, reject) {\n var gen = fn.apply(self1, args);\n function _next(value) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"next\", value);\n }\n function _throw(err) {\n asyncGeneratorStep(gen, resolve, reject, _next, _throw, \"throw\", err);\n }\n _next(undefined);\n });\n };\n}\nfunction _ts_generator(thisArg, body) {\n var f, y, t, g, _ = {\n label: 0,\n sent: function() {\n if (t[0] & 1) throw t[1];\n return t[1];\n },\n trys: [],\n ops: []\n };\n return g = {\n next: verb(0),\n \"throw\": verb(1),\n \"return\": verb(2)\n }, typeof Symbol === \"function\" && (g[Symbol.iterator] = function() {\n return this;\n }), g;\n function verb(n) {\n return function(v) {\n return step([\n n,\n v\n ]);\n };\n }\n function step(op) {\n if (f) throw new TypeError(\"Generator is already executing.\");\n while(_)try {\n if (f = 1, y && (t = op[0] & 2 ? y[\"return\"] : op[0] ? y[\"throw\"] || ((t = y[\"return\"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;\n if (y = 0, t) op = [\n op[0] & 2,\n t.value\n ];\n switch(op[0]){\n case 0:\n case 1:\n t = op;\n break;\n case 4:\n _.label++;\n return {\n value: op[1],\n done: false\n };\n case 5:\n _.label++;\n y = op[1];\n op = [\n 0\n ];\n continue;\n case 7:\n op = _.ops.pop();\n _.trys.pop();\n continue;\n default:\n if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) {\n _ = 0;\n continue;\n }\n if (op[0] === 3 && (!t || op[1] > t[0] && op[1] < t[3])) {\n _.label = op[1];\n break;\n }\n if (op[0] === 6 && _.label < t[1]) {\n _.label = t[1];\n t = op;\n break;\n }\n if (t && _.label < t[2]) {\n _.label = t[2];\n _.ops.push(op);\n break;\n }\n if (t[2]) _.ops.pop();\n _.trys.pop();\n continue;\n }\n op = body.call(thisArg, _);\n } catch (e) {\n op = [\n 6,\n e\n ];\n y = 0;\n } finally{\n f = t = 0;\n }\n if (op[0] & 5) throw op[1];\n return {\n value: op[0] ? op[1] : void 0,\n done: true\n };\n }\n}\nvar VERSION = \"29038489\";\n// The name of the cache\nvar CACHE_NAME = \"breakout-71-\".concat(VERSION);\n// The static resources that the app needs to function.\nvar APP_STATIC_RESOURCES = [\n \"/\"\n];\n// On install, cache the static resources\nself.addEventListener(\"install\", function(event) {\n event.waitUntil(_async_to_generator(function() {\n var cache;\n return _ts_generator(this, function(_state) {\n switch(_state.label){\n case 0:\n return [\n 4,\n caches.open(CACHE_NAME)\n ];\n case 1:\n cache = _state.sent();\n cache.addAll(APP_STATIC_RESOURCES);\n return [\n 2\n ];\n }\n });\n })());\n});\n// delete old caches on activate\nself.addEventListener(\"activate\", function(event) {\n event.waitUntil(_async_to_generator(function() {\n var names;\n return _ts_generator(this, function(_state) {\n switch(_state.label){\n case 0:\n return [\n 4,\n caches.keys()\n ];\n case 1:\n names = _state.sent();\n return [\n 4,\n Promise.all(names.map(function(name) {\n if (name !== CACHE_NAME) return caches[\"delete\"](name);\n }))\n ];\n case 2:\n _state.sent();\n return [\n 4,\n clients.claim()\n ];\n case 3:\n _state.sent();\n return [\n 2\n ];\n }\n });\n })());\n});\nself.addEventListener(\"fetch\", function(event) {\n if (event.request.mode === \"navigate\" && event.request.url.endsWith(\"/index.html?isPWA=true\")) {\n event.respondWith(caches.match(\"/\"));\n return;\n }\n});\n\n//# sourceMappingURL=sw-b71.js.map\n","// The version of the cache.\nconst VERSION = \"29038489\";\n\n// The name of the cache\nconst CACHE_NAME = `breakout-71-${VERSION}`;\n\n// The static resources that the app needs to function.\nconst APP_STATIC_RESOURCES = [\"/\"];\n\n// On install, cache the static resources\nself.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const cache = await caches.open(CACHE_NAME);\n cache.addAll(APP_STATIC_RESOURCES);\n })(),\n );\n});\n\n// delete old caches on activate\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const names = await caches.keys();\n await Promise.all(\n names.map((name) => {\n if (name !== CACHE_NAME) {\n return caches.delete(name);\n }\n }),\n );\n await clients.claim();\n })(),\n );\n});\n\nself.addEventListener(\"fetch\", (event) => {\n if (\n event.request.mode === \"navigate\" &&\n event.request.url.endsWith(\"/index.html?isPWA=true\")\n ) {\n event.respondWith(caches.match(\"/\"));\n return;\n }\n});\n"],"names":["asyncGeneratorStep","gen","resolve","reject","_next","_throw","key","arg","info","value","error","done","Promise","then","_async_to_generator","fn","self1","args","arguments","apply","err","undefined","_ts_generator","thisArg","body","f","y","t","g","_","label","sent","trys","ops","next","verb","Symbol","iterator","n","v","step","op","TypeError","call","pop","length","push","e","CACHE_NAME","concat","APP_STATIC_RESOURCES","self","addEventListener","event","waitUntil","_state","caches","open","cache","addAll","keys","all","names","map","name","clients","claim","request","mode","url","endsWith","respondWith","match"],"version":3,"file":"sw-b71.js.map"} \ No newline at end of file +{"mappings":"AAAA,4BAA4B;AAC5B,MAAM,UAAU;AAEhB,wBAAwB;AACxB,MAAM,aAAa,CAAC,YAAY,EAAE,SAAS;AAE3C,uDAAuD;AACvD,MAAM,uBAAuB;IAAC;CAAI;AAElC,yCAAyC;AACzC,KAAK,gBAAgB,CAAC,WAAW,CAAC;IAChC,MAAM,SAAS,CACb,AAAC,CAAA;QACC,MAAM,QAAQ,MAAM,OAAO,IAAI,CAAC;QAChC,MAAM,MAAM,CAAC;IACf,CAAA;AAEJ;AAEA,gCAAgC;AAChC,KAAK,gBAAgB,CAAC,YAAY,CAAC;IACjC,MAAM,SAAS,CACb,AAAC,CAAA;QACC,MAAM,QAAQ,MAAM,OAAO,IAAI;QAC/B,MAAM,QAAQ,GAAG,CACf,MAAM,GAAG,CAAC,CAAC;YACT,IAAI,SAAS,YACX,OAAO,OAAO,MAAM,CAAC;QAEzB;QAEF,MAAM,QAAQ,KAAK;IACrB,CAAA;AAEJ;AAEA,KAAK,gBAAgB,CAAC,SAAS,CAAC;IAC9B,IACE,MAAM,OAAO,CAAC,IAAI,KAAK,cACvB,MAAM,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,2BAC3B;QACA,MAAM,WAAW,CAAC,OAAO,KAAK,CAAC;QAC/B;IACF;AACF","sources":["src/PWA/sw-b71.js"],"sourcesContent":["// The version of the cache.\nconst VERSION = \"29039826\";\n\n// The name of the cache\nconst CACHE_NAME = `breakout-71-${VERSION}`;\n\n// The static resources that the app needs to function.\nconst APP_STATIC_RESOURCES = [\"/\"];\n\n// On install, cache the static resources\nself.addEventListener(\"install\", (event) => {\n event.waitUntil(\n (async () => {\n const cache = await caches.open(CACHE_NAME);\n cache.addAll(APP_STATIC_RESOURCES);\n })(),\n );\n});\n\n// delete old caches on activate\nself.addEventListener(\"activate\", (event) => {\n event.waitUntil(\n (async () => {\n const names = await caches.keys();\n await Promise.all(\n names.map((name) => {\n if (name !== CACHE_NAME) {\n return caches.delete(name);\n }\n }),\n );\n await clients.claim();\n })(),\n );\n});\n\nself.addEventListener(\"fetch\", (event) => {\n if (\n event.request.mode === \"navigate\" &&\n event.request.url.endsWith(\"/index.html?isPWA=true\")\n ) {\n event.respondWith(caches.match(\"/\"));\n return;\n }\n});\n"],"names":[],"version":3,"file":"sw-b71.js.map","sourceRoot":"/__parcel_source_root/"} \ No newline at end of file diff --git a/dist/index.html b/dist/index.html index 0e7e7b6..2e794e0 100644 --- a/dist/index.html +++ b/dist/index.html @@ -1 +1,3760 @@ -Breakout 71 \ No newline at end of file + + + + + + + Breakout 71 + + + + + + + + + + + + + diff --git a/src/PWA/sw-b71.js b/src/PWA/sw-b71.js index 2f44cf5..3beeea2 100644 --- a/src/PWA/sw-b71.js +++ b/src/PWA/sw-b71.js @@ -1,5 +1,5 @@ // The version of the cache. -const VERSION = "29038489"; +const VERSION = "29039826"; // The name of the cache const CACHE_NAME = `breakout-71-${VERSION}`; diff --git a/src/data/version.json b/src/data/version.json index fe47c5f..3575b04 100644 --- a/src/data/version.json +++ b/src/data/version.json @@ -1 +1 @@ -"29038489" +"29039826" diff --git a/src/game.ts b/src/game.ts index 9523834..e332cc3 100644 --- a/src/game.ts +++ b/src/game.ts @@ -273,8 +273,6 @@ export async function openUpgradesPicker(gameState: GameState) { gameState.runStatistics.upgrades_picked++; } - resetCombo(gameState, undefined, undefined); - resetBalls(gameState); } gameCanvas.addEventListener("mouseup", (e) => { diff --git a/src/gameStateMutators.ts b/src/gameStateMutators.ts index 3325c4e..bdc28ec 100644 --- a/src/gameStateMutators.ts +++ b/src/gameStateMutators.ts @@ -82,7 +82,6 @@ export function resetBalls(gameState: GameState) { sx: 0, sy: 0, - sparks: 0, piercedSinceBounce: 0, hitSinceBounce: 0, hitItem: [], @@ -166,9 +165,7 @@ export function resetCombo( ) { const prev = gameState.combo; gameState.combo = baseCombo(gameState); - if (!gameState.levelTime) { - gameState.combo += gameState.perks.hot_start * 15; - } + if (prev > gameState.combo && gameState.perks.soft_reset) { gameState.combo += Math.floor( ((prev - gameState.combo) * (gameState.perks.soft_reset * 10)) / 100, @@ -467,14 +464,13 @@ export function addToScore(gameState: GameState, coin: Coin) { gameState.runStatistics.score += coin.points; } -export function setLevel(gameState: GameState, l: number) { +export async function setLevel(gameState: GameState, l: number) { stopRecording(); pause(false); if (l > 0) { - openUpgradesPicker(gameState); + await openUpgradesPicker(gameState); } gameState.currentLevel = l; - gameState.levelTime = 0; gameState.levelWallBounces = 0; gameState.autoCleanUses = 0; @@ -484,7 +480,9 @@ export function setLevel(gameState: GameState, l: number) { gameState.levelMisses = 0; gameState.runStatistics.levelsPlayed++; - resetCombo(gameState, undefined, undefined); + // Reset combo silently + gameState.combo = baseCombo(gameState) + gameState.perks.hot_start * 15; + resetBalls(gameState); const lvl = currentLevelInfo(gameState); @@ -1161,21 +1159,36 @@ export function ballTick(gameState: GameState, ball: Ball, delta: number) { } if (!isOptionOn("basic")) { - ball.sparks += (delta * (gameState.combo - 1)) / 30; - if (ball.sparks > 1) { + const remainingPierce = + gameState.perks.pierce * 3 - ball.piercedSinceBounce; + const remainingSapper = ball.sapperUses < gameState.perks.sapper; + const extraCombo = gameState.combo - 1; + if ( + (extraCombo && Math.random() > 0.1 / (1 + extraCombo)) || + (remainingSapper && Math.random() > 0.1 / (1 + remainingSapper)) || + (extraCombo && Math.random() > 0.1 / (1 + extraCombo)) + ) { + const color = remainingSapper + ? Math.random() > 0.5 + ? "orange" + : "red" + : gameState.ballsColor; + makeParticle( gameState, ball.x, ball.y, - (Math.random() - 0.5) * gameState.baseSpeed, - (Math.random() - 0.5) * gameState.baseSpeed, - gameState.ballsColor, - false, + gameState.perks.pierce_color || remainingPierce + ? -ball.vx + ((Math.random() - 0.5) * gameState.baseSpeed) / 3 + : (Math.random() - 0.5) * gameState.baseSpeed, + gameState.perks.pierce_color || remainingPierce + ? -ball.vy + ((Math.random() - 0.5) * gameState.baseSpeed) / 3 + : (Math.random() - 0.5) * gameState.baseSpeed, + color, + true, gameState.coinSize / 2, - 100 * ball.sparks, + 100, ); - - ball.sparks = 0; } } } diff --git a/src/types.d.ts b/src/types.d.ts index 5bdb231..c0acf3f 100644 --- a/src/types.d.ts +++ b/src/types.d.ts @@ -94,7 +94,6 @@ export type Ball = { previousVY: number; sx: number; sy: number; - sparks: number; piercedSinceBounce: number; hitSinceBounce: number; hitItem: { index: number; color: string }[];