From 3fdc4e805c9859facd97ccaed58158f319183901 Mon Sep 17 00:00:00 2001 From: Evert Prants Date: Thu, 20 Oct 2022 18:01:56 +0300 Subject: [PATCH] fixes, features --- src/components/GameField.vue | 139 ++++++++++++++++++++------------ src/components/GameOver.vue | 3 +- src/components/ScoreDisplay.vue | 20 ++++- src/style.scss | 18 ++++- 4 files changed, 123 insertions(+), 57 deletions(-) diff --git a/src/components/GameField.vue b/src/components/GameField.vue index 5c9d38b..2ee9f28 100644 --- a/src/components/GameField.vue +++ b/src/components/GameField.vue @@ -12,8 +12,14 @@ - - {{ + + {{ victory ? 'You won!' : 'Game over.' }} @@ -38,9 +44,12 @@ const radius = ref(32); const diameter = computed(() => radius.value * 2); const colors = ref(5); const angle = ref(90); +const streak = ref(0); +const maxStreak = ref(0); const shotsLeft = ref(4); const offset = ref(0); const score = ref(0); +const hiscore = ref(0); const fitment = computed(() => Math.floor(width / diameter.value)); const gameOver = ref(false); @@ -69,23 +78,36 @@ function createRow(column: number, generate = false) { generate ? field.value.push(...entries) : field.value.unshift(...entries); } -function addRow() { - field.value.forEach((item) => { - item.y += diameter.value - 8; - }); - offset.value += 1; - createRow(0); - +function checkHeightLoss() { const lowestPart = field.value.reduce( (last, current) => (last < current.y ? current.y : last), 0 ); + if (lowestPart > height - diameter.value * 2) { gameOver.value = true; - alert('Game over!'); } } +function saveHiscore(score: number) { + const currentHigh = Number(localStorage.getItem('highscore')); + if ((currentHigh && currentHigh < score) || !currentHigh) { + localStorage.setItem('highscore', score.toString()); + hiscore.value = score; + } +} + +function addRow() { + field.value.forEach((item) => { + item.y += diameter.value - 8; + }); + + offset.value += 1; + + createRow(0); + checkHeightLoss(); +} + function generate() { for (let column = 0; column < 5; column++) { createRow(column, true); @@ -97,9 +119,12 @@ function reset() { field.value = []; offset.value = 0; generate(); + saveHiscore(score.value); score.value = 0; gameOver.value = false; victory.value = false; + streak.value = 0; + maxStreak.value = 0; determineNext(); } @@ -220,6 +245,30 @@ function determineImmediateNeighbors(item: BubbleType) { return neighbors; } +function accountForAllCeilingAttached() { + let accountedFor: BubbleType[] = []; + + const startingPoint = field.value.filter( + (item) => item.y === 0 && !item.exiting + ); + const accountNeighbors = (item: BubbleType) => { + if (!accountedFor.includes(item)) { + accountedFor.push(item); + const neighbors = determineImmediateNeighbors(item); + if (neighbors.length) { + for (const neighbor of neighbors) { + if (accountedFor.includes(neighbor)) continue; + accountNeighbors(neighbor); + } + } + } + }; + for (const item of startingPoint) { + accountNeighbors(item); + } + return accountedFor; +} + function checkForPops(item: BubbleType, friendlies: BubbleType[] = []) { const neighbors = determineImmediateNeighbors(item); const same = neighbors @@ -237,33 +286,6 @@ function checkForPops(item: BubbleType, friendlies: BubbleType[] = []) { return friendlies; } -function findIsolatedGroups(groups: BubbleType[][] = []) { - for (const item of field.value) { - if (groups.some((group) => group.includes(item))) { - continue; - } - - const neighbors = determineImmediateNeighbors(item); - if (neighbors.length) { - let setgroup; - for (const neighbor of neighbors) { - if (groups.some((group) => group.includes(neighbor))) { - setgroup = groups.find((item) => item.includes(neighbor)); - break; - } - } - if (setgroup) { - setgroup.push(item); - } else { - groups.push([item, ...neighbors]); - } - } else { - groups.push([item]); - } - } - return groups; -} - function animateRemovals() { const toRemove = field.value.filter((item) => item.exiting); if (!toRemove.length) return; @@ -285,24 +307,20 @@ function animateRemovals() { } function determineIsolated() { - const groups = findIsolatedGroups([]); - let failedGroups = groups.filter( - (group) => !group.some((item) => item.y === 0) - ); + const accountedFor = accountForAllCeilingAttached(); - if (failedGroups.length) { - let fallCount = 0; - field.value - .filter((item) => failedGroups.some((group) => group.includes(item))) - .forEach((i) => { - i.exiting = true; - fallCount++; - }); + let fallCount = 0; + field.value + .filter((item) => !accountedFor.includes(item)) + .forEach((i) => { + i.exiting = true; + fallCount++; + }); - score.value += fallCount * 10; - } + score.value += fallCount * 10; - animateRemovals(); + if (fallCount > 0) animateRemovals(); + return fallCount; } function stopDead() { @@ -320,9 +338,15 @@ function stopDead() { if (chain.length >= 3) { score.value += chain.length; field.value = field.value.filter((item) => !chain.includes(item)); - determineIsolated(); + const dropped = determineIsolated(); + streak.value += chain.length + dropped; + + if (streak.value > maxStreak.value) { + maxStreak.value = streak.value; + } } else { shotsLeft.value -= 1; + streak.value = 0; } if (shotsLeft.value < 1) { @@ -335,7 +359,10 @@ function stopDead() { if (!field.value.filter((x) => !x.exiting).length) { victory.value = true; gameOver.value = true; + return; } + + checkHeightLoss(); } let steps = 0; @@ -402,6 +429,12 @@ function normalize(vector: { x: number; y: number }) { onMounted(() => { generate(); determineNext(); + + const score = Number(localStorage.getItem('highscore')); + if (score) { + hiscore.value = score; + } + function determineVectors(e: MouseEvent) { const x = e.clientX - fieldRef.value!.offsetLeft; const y = e.clientY - fieldRef.value!.offsetTop; diff --git a/src/components/GameOver.vue b/src/components/GameOver.vue index 143a863..6e42cb8 100644 --- a/src/components/GameOver.vue +++ b/src/components/GameOver.vue @@ -2,10 +2,11 @@

Score: {{ score }}

+

Best streak: {{ streak }}

Click to restart
diff --git a/src/components/ScoreDisplay.vue b/src/components/ScoreDisplay.vue index c565e8d..edea4ac 100644 --- a/src/components/ScoreDisplay.vue +++ b/src/components/ScoreDisplay.vue @@ -1,7 +1,23 @@ diff --git a/src/style.scss b/src/style.scss index bd26ef0..75ddd7d 100644 --- a/src/style.scss +++ b/src/style.scss @@ -51,8 +51,15 @@ body { } &-score { + display: flex; + width: 100%; + justify-content: space-between; position: absolute; - bottom: -1.5rem; + bottom: -3rem; + & > span { + display: flex; + flex-direction: column; + } } &-pointer { @@ -88,6 +95,15 @@ body { font-size: 2rem; pointer-events: none; z-index: 4; + + p:first-of-type { + margin-bottom: 0; + font-size: 2.5rem; + } + + small { + color: rgb(9, 255, 0); + } } &-object {