Skip to content

Commit

Permalink
enhance(drop-and-fusion): some tweaks
Browse files Browse the repository at this point in the history
  • Loading branch information
syuilo committed Jan 13, 2024
1 parent 5503ad9 commit d246f6c
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 120 deletions.
Binary file added packages/frontend/assets/drop-and-fusion/go.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
277 changes: 172 additions & 105 deletions packages/frontend/src/pages/drop-and-fusion.game.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,135 +4,163 @@ SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<div :class="$style.root">
<div v-if="!gameLoaded" :class="$style.loadingScreen">
Loading...
</div>
<!-- ↓に対してTransitionコンポーネントを使うと何故かkeyを指定していてもキャッシュが効かず様々なコンポーネントが都度再評価されてパフォーマンスが低下する -->
<div v-show="gameLoaded" class="_gaps_s">
<div :class="$style.header">
<div :class="[$style.frame, $style.headerTitle]">
<div :class="$style.frameInner">
<b>BUBBLE GAME</b>
<div>- {{ gameMode }} -</div>
</div>
</div>
<div :class="[$style.frame, $style.frameH]">
<div :class="$style.frameInner">
<MkButton inline small @click="hold">HOLD</MkButton>
<img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/>
</div>
<div :class="[$style.frameInner, $style.stock]" style="text-align: center;">
<TransitionGroup
:enterActiveClass="$style.transition_stock_enterActive"
:leaveActiveClass="$style.transition_stock_leaveActive"
:enterFromClass="$style.transition_stock_enterFrom"
:leaveToClass="$style.transition_stock_leaveTo"
:moveClass="$style.transition_stock_move"
>
<img v-for="x in stock" :key="x.id" :src="getTextureImageUrl(x.mono)" style="width: 32px; vertical-align: bottom;"/>
</TransitionGroup>
</div>
<MkSpacer :contentMax="800">
<div :class="$style.root">
<div v-if="!gameLoaded" :class="$style.loadingScreen">
<div>
Loading...
</div>
</div>
<div ref="containerEl" :class="[$style.gameContainer, { [$style.gameOver]: isGameOver && !replaying }]" @contextmenu.stop.prevent @click.stop.prevent="onClick" @touchmove.stop.prevent="onTouchmove" @touchend="onTouchend" @mousemove="onMousemove">
<img v-if="defaultStore.state.darkMode" src="/client-assets/drop-and-fusion/frame-dark.svg" :class="$style.mainFrameImg"/>
<img v-else src="/client-assets/drop-and-fusion/frame-light.svg" :class="$style.mainFrameImg"/>
<canvas ref="canvasEl" :class="$style.canvas"/>
<!-- ↓に対してTransitionコンポーネントを使うと何故かkeyを指定していてもキャッシュが効かず様々なコンポーネントが都度再評価されてパフォーマンスが低下する -->
<div v-show="gameLoaded" class="_gaps_s">
<div v-if="readyGo === 'ready'" :class="$style.readyGo_bg">
</div>
<Transition
:enterActiveClass="$style.transition_combo_enterActive"
:leaveActiveClass="$style.transition_combo_leaveActive"
:enterFromClass="$style.transition_combo_enterFrom"
:leaveToClass="$style.transition_combo_leaveTo"
:moveClass="$style.transition_combo_move"
:enterActiveClass="$style.transition_zoom_enterActive"
:leaveActiveClass="$style.transition_zoom_leaveActive"
:enterFromClass="$style.transition_zoom_enterFrom"
:leaveToClass="$style.transition_zoom_leaveTo"
:moveClass="$style.transition_zoom_move"
mode="default"
>
<div v-show="combo > 1" :class="$style.combo" :style="{ fontSize: `${100 + ((comboPrev - 2) * 15)}%` }">{{ comboPrev }} Chain!</div>
<div v-if="readyGo === 'ready'" :class="$style.readyGo_ready">
<img src="/client-assets/drop-and-fusion/ready.png" :class="$style.readyGo_img"/>
</div>
<div v-else-if="readyGo === 'go'" :class="$style.readyGo_go">
<img src="/client-assets/drop-and-fusion/go.png" :class="$style.readyGo_img"/>
</div>
</Transition>
<div v-if="!isGameOver && !replaying" :class="$style.dropperContainer" :style="{ left: dropperX + 'px' }">
<!--<img v-if="currentPick" src="/client-assets/drop-and-fusion/dropper.png" :class="$style.dropper" :style="{ left: dropperX + 'px' }"/>-->

<div :class="$style.header">
<div :class="[$style.frame, $style.headerTitle]">
<div :class="$style.frameInner">
<b>BUBBLE GAME</b>
<div>- {{ gameMode }} -</div>
</div>
</div>
<div :class="[$style.frame, $style.frameH]">
<div :class="$style.frameInner">
<MkButton inline small @click="hold">HOLD</MkButton>
<img v-if="holdingStock" :src="getTextureImageUrl(holdingStock.mono)" style="width: 32px; margin-left: 8px; vertical-align: bottom;"/>
</div>
<div :class="[$style.frameInner, $style.stock]" style="text-align: center;">
<TransitionGroup
:enterActiveClass="$style.transition_stock_enterActive"
:leaveActiveClass="$style.transition_stock_leaveActive"
:enterFromClass="$style.transition_stock_enterFrom"
:leaveToClass="$style.transition_stock_leaveTo"
:moveClass="$style.transition_stock_move"
>
<img v-for="x in stock" :key="x.id" :src="getTextureImageUrl(x.mono)" style="width: 32px; vertical-align: bottom;"/>
</TransitionGroup>
</div>
</div>
</div>

<div ref="containerEl" :class="[$style.gameContainer, { [$style.gameOver]: isGameOver && !replaying }]" @contextmenu.stop.prevent @click.stop.prevent="onClick" @touchmove.stop.prevent="onTouchmove" @touchend="onTouchend" @mousemove="onMousemove">
<img v-if="defaultStore.state.darkMode" src="/client-assets/drop-and-fusion/frame-dark.svg" :class="$style.mainFrameImg"/>
<img v-else src="/client-assets/drop-and-fusion/frame-light.svg" :class="$style.mainFrameImg"/>
<canvas ref="canvasEl" :class="$style.canvas"/>
<Transition
:enterActiveClass="$style.transition_picked_enterActive"
:leaveActiveClass="$style.transition_picked_leaveActive"
:enterFromClass="$style.transition_picked_enterFrom"
:leaveToClass="$style.transition_picked_leaveTo"
:moveClass="$style.transition_picked_move"
mode="out-in"
:enterActiveClass="$style.transition_combo_enterActive"
:leaveActiveClass="$style.transition_combo_leaveActive"
:enterFromClass="$style.transition_combo_enterFrom"
:leaveToClass="$style.transition_combo_leaveTo"
:moveClass="$style.transition_combo_move"
>
<img v-if="currentPick" :key="currentPick.id" :src="getTextureImageUrl(currentPick.mono)" :class="$style.currentMono" :style="{ marginBottom: -((currentPick?.mono.sizeY * viewScale) / 2) + 'px', left: -((currentPick?.mono.sizeX * viewScale) / 2) + 'px', width: `${currentPick?.mono.sizeX * viewScale}px` }"/>
<div v-show="combo > 1" :class="$style.combo" :style="{ fontSize: `${100 + ((comboPrev - 2) * 15)}%` }">{{ comboPrev }} Chain!</div>
</Transition>
<template v-if="dropReady && currentPick">
<img src="/client-assets/drop-and-fusion/drop-arrow.svg" :class="$style.currentMonoArrow"/>
<div :class="$style.dropGuide"/>
</template>
</div>
<div v-if="isGameOver && !replaying" :class="$style.gameOverLabel">
<div class="_gaps_s">
<img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/>
<div>SCORE: <MkNumber :value="score"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</div>
<div>MAX CHAIN: <MkNumber :value="maxCombo"/></div>
<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b><MkNumber :value="yenTotal ?? score"/>円</b></div>
<div v-if="!isGameOver && !replaying" :class="$style.dropperContainer" :style="{ left: dropperX + 'px' }">
<!--<img v-if="currentPick" src="/client-assets/drop-and-fusion/dropper.png" :class="$style.dropper" :style="{ left: dropperX + 'px' }"/>-->
<Transition
:enterActiveClass="$style.transition_picked_enterActive"
:leaveActiveClass="$style.transition_picked_leaveActive"
:enterFromClass="$style.transition_picked_enterFrom"
:leaveToClass="$style.transition_picked_leaveTo"
:moveClass="$style.transition_picked_move"
mode="out-in"
>
<img v-if="currentPick" :key="currentPick.id" :src="getTextureImageUrl(currentPick.mono)" :class="$style.currentMono" :style="{ marginBottom: -((currentPick?.mono.sizeY * viewScale) / 2) + 'px', left: -((currentPick?.mono.sizeX * viewScale) / 2) + 'px', width: `${currentPick?.mono.sizeX * viewScale}px` }"/>
</Transition>
<template v-if="dropReady && currentPick">
<img src="/client-assets/drop-and-fusion/drop-arrow.svg" :class="$style.currentMonoArrow"/>
<div :class="$style.dropGuide"/>
</template>
</div>
</div>
<div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ti ti-player-play"></i> {{ i18n.ts.replaying }}</span></div>
</div>
<div v-if="replaying" :class="$style.frame">
<div :class="$style.frameInner">
<div style="background: #0004;">
<div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
<div v-if="isGameOver && !replaying" :class="$style.gameOverLabel">
<div class="_gaps_s">
<img src="/client-assets/drop-and-fusion/gameover.png" style="width: 200px; max-width: 100%; display: block; margin: auto; margin-bottom: -5px;"/>
<div>SCORE: <MkNumber :value="score"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</div>
<div>MAX CHAIN: <MkNumber :value="maxCombo"/></div>
<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b><MkNumber :value="yenTotal ?? score"/>円</b></div>
</div>
</div>
<div v-if="replaying" :class="$style.replayIndicator"><span :class="$style.replayIndicatorText"><i class="ti ti-player-play"></i> {{ i18n.ts.replaying }}</span></div>
</div>
<div :class="$style.frameInner">
<div class="_buttonsCenter">
<MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END</MkButton>
<MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton>
<MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ti ti-player-track-next"></i> x16</MkButton>

<div v-if="replaying" :class="$style.frame">
<div :class="$style.frameInner">
<div style="background: #0004;">
<div style="height: 10px; background: var(--accent); will-change: width;" :style="{ width: `${(currentFrame / endedAtFrame) * 100}%` }"></div>
</div>
</div>
</div>
</div>
<div v-if="isGameOver" :class="$style.frame">
<div :class="$style.frameInner">
<div class="_buttonsCenter">
<MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton>
<MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton>
<MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton>
<MkButton rounded @click="exportLog">Copy replay data</MkButton>
<div :class="$style.frameInner">
<div class="_buttonsCenter">
<MkButton @click="endReplay"><i class="ti ti-player-stop"></i> END</MkButton>
<MkButton :primary="replayPlaybackRate === 4" @click="replayPlaybackRate = replayPlaybackRate === 4 ? 1 : 4"><i class="ti ti-player-track-next"></i> x4</MkButton>
<MkButton :primary="replayPlaybackRate === 16" @click="replayPlaybackRate = replayPlaybackRate === 16 ? 1 : 16"><i class="ti ti-player-track-next"></i> x16</MkButton>
</div>
</div>
</div>
</div>
<div style="display: flex;">
<div :class="$style.frame" style="flex: 1; margin-right: 10px;">

<div v-if="isGameOver" :class="$style.frame">
<div :class="$style.frameInner">
<div>SCORE: <b><MkNumber :value="score"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</b></div>
<div>HIGH SCORE: <b v-if="highScore"><MkNumber :value="highScore"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</b><b v-else>-</b></div>
<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b v-if="yenTotal"><MkNumber :value="yenTotal"/>円</b><b v-else>-</b></div>
<div class="_buttonsCenter">
<MkButton primary rounded @click="backToTitle">{{ i18n.ts.backToTitle }}</MkButton>
<MkButton primary rounded @click="replay">{{ i18n.ts.showReplay }}</MkButton>
<MkButton primary rounded @click="share">{{ i18n.ts.share }}</MkButton>
<MkButton rounded @click="exportLog">Copy replay data</MkButton>
</div>
</div>
</div>
<div :class="[$style.frame]" style="margin-left: auto;">
<div :class="$style.frameInner" style="text-align: center;">
<div @click="showConfig = !showConfig"><i class="ti ti-settings"></i></div>

<div style="display: flex;">
<div :class="$style.frame" style="flex: 1; margin-right: 10px;">
<div :class="$style.frameInner">
<div>SCORE: <b><MkNumber :value="score"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</b></div>
<div>HIGH SCORE: <b v-if="highScore"><MkNumber :value="highScore"/>{{ gameMode === 'yen' ? '円' : 'pt' }}</b><b v-else>-</b></div>
<div v-if="gameMode === 'yen'">TOTAL EARNINGS: <b v-if="yenTotal"><MkNumber :value="yenTotal"/>円</b><b v-else>-</b></div>
</div>
</div>
<div :class="[$style.frame]" style="margin-left: auto;">
<div :class="$style.frameInner" style="text-align: center;">
<div @click="showConfig = !showConfig"><i class="ti ti-settings"></i></div>
</div>
</div>
</div>
</div>
<div v-if="showConfig" :class="$style.frame">
<div :class="$style.frameInner">
<div class="_gaps">
<MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)">
<template #label>BGM {{ i18n.ts.volume }}</template>
</MkRange>
<MkRange v-model="sfxVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('sfxVolume', v)">
<template #label>{{ i18n.ts.sfx }} {{ i18n.ts.volume }}</template>
</MkRange>

<div v-if="showConfig" :class="$style.frame">
<div :class="$style.frameInner">
<div class="_gaps">
<MkRange v-model="bgmVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('bgmVolume', v)">
<template #label>BGM {{ i18n.ts.volume }}</template>
</MkRange>
<MkRange v-model="sfxVolume" :min="0" :max="1" :step="0.01" :textConverter="(v) => `${Math.floor(v * 100)}%`" :continuousUpdate="true" @dragEnded="(v) => updateSettings('sfxVolume', v)">
<template #label>{{ i18n.ts.sfx }} {{ i18n.ts.volume }}</template>
</MkRange>
</div>
</div>
</div>
</div>
<div :class="$style.frame">
<div :class="$style.frameInner">
<MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">Surrender</MkButton>
<MkButton v-else full @click="restart">Retry</MkButton>

<div :class="$style.frame">
<div :class="$style.frameInner">
<MkButton v-if="!isGameOver && !replaying" full danger @click="surrender">Surrender</MkButton>
<MkButton v-else full @click="restart">Retry</MkButton>
</div>
</div>
</div>
</div>
</div>
</MkSpacer>
</template>

<script lang="ts" setup>
Expand Down Expand Up @@ -439,6 +467,7 @@ const maxCombo = ref(0);
const dropReady = ref(true);
const isGameOver = ref(false);
const gameLoaded = ref(false);
const readyGo = ref<'ready' | 'go' | null>('ready');
const highScore = ref<number | null>(null);
const yenTotal = ref<number | null>(null);
const showConfig = ref(false);
Expand Down Expand Up @@ -579,6 +608,13 @@ async function start() {
window.requestAnimationFrame(tick);

gameLoaded.value = true;

window.setTimeout(() => {
readyGo.value = 'go';
window.setTimeout(() => {
readyGo.value = null;
}, 1000);
}, 1500);
}

function onClick(ev: MouseEvent) {
Expand Down Expand Up @@ -650,6 +686,7 @@ function reset() {
comboPrev.value = 0;
maxCombo.value = 0;
gameLoaded.value = false;
readyGo.value = false;
}

function dispose() {
Expand Down Expand Up @@ -1128,6 +1165,36 @@ definePageMetadata({
padding: 32px;
}

.readyGo_bg {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
backdrop-filter: blur(4px);
}

.readyGo_ready,
.readyGo_go {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 101;
pointer-events: none;
}

.readyGo_img {
display: block;
width: 250px;
max-width: 100%;
}

.frame {
padding: 7px;
background: #8C4F26;
Expand Down
28 changes: 13 additions & 15 deletions packages/frontend/src/pages/drop-and-fusion.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,16 @@ SPDX-License-Identifier: AGPL-3.0-only
-->

<template>
<MkSpacer :contentMax="800">
<Transition
:enterActiveClass="$style.transition_zoom_enterActive"
:leaveActiveClass="$style.transition_zoom_leaveActive"
:enterFromClass="$style.transition_zoom_enterFrom"
:leaveToClass="$style.transition_zoom_leaveTo"
:moveClass="$style.transition_zoom_move"
mode="out-in"
>
<div v-if="!gameStarted" :class="$style.root">
<Transition
:enterActiveClass="$style.transition_zoom_enterActive"
:leaveActiveClass="$style.transition_zoom_leaveActive"
:enterFromClass="$style.transition_zoom_enterFrom"
:leaveToClass="$style.transition_zoom_leaveTo"
:moveClass="$style.transition_zoom_move"
mode="out-in"
>
<MkSpacer v-if="!gameStarted" :contentMax="800">
<div :class="$style.root">
<div class="_gaps">
<div :class="$style.frame" style="text-align: center;">
<div :class="$style.frameInner">
Expand Down Expand Up @@ -78,11 +78,9 @@ SPDX-License-Identifier: AGPL-3.0-only
</div>
</div>
</div>
<div v-else>
<XGame :gameMode="gameMode" :mute="mute" @end="onGameEnd"/>
</div>
</Transition>
</MkSpacer>
</MkSpacer>
<XGame v-else :gameMode="gameMode" :mute="mute" @end="onGameEnd"/>
</Transition>
</template>

<script lang="ts" setup>
Expand Down

0 comments on commit d246f6c

Please sign in to comment.