enhance(reversi): tweak reversi
This commit is contained in:
parent
094c6e32ff
commit
3bf3ba450c
5 changed files with 97 additions and 25 deletions
BIN
packages/frontend/assets/reversi/put.mp3
Normal file
BIN
packages/frontend/assets/reversi/put.mp3
Normal file
Binary file not shown.
BIN
packages/frontend/assets/reversi/stone_b.png
Normal file
BIN
packages/frontend/assets/reversi/stone_b.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
BIN
packages/frontend/assets/reversi/stone_w.png
Normal file
BIN
packages/frontend/assets/reversi/stone_w.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
|
@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<template>
|
||||
<MkSpacer :contentMax="600">
|
||||
<div :class="$style.root" class="_gaps">
|
||||
<header><b><MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA></b>({{ i18n.ts._reversi.black }}) vs <b><MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA></b>({{ i18n.ts._reversi.white }})</header>
|
||||
<div style="display: flex; align-items: center; justify-content: center; gap: 10px;">
|
||||
<span>({{ i18n.ts._reversi.black }})</span>
|
||||
<MkAvatar style="width: 32px; height: 32px;" :user="blackUser" :showIndicator="true"/>
|
||||
<span> vs </span>
|
||||
<MkAvatar style="width: 32px; height: 32px;" :user="whiteUser" :showIndicator="true"/>
|
||||
<span>({{ i18n.ts._reversi.white }})</span>
|
||||
</div>
|
||||
|
||||
<div style="overflow: clip; line-height: 28px;">
|
||||
<div v-if="!iAmPlayer && !game.isEnded && turnUser" class="turn">
|
||||
|
@ -49,8 +55,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
}]"
|
||||
@click="putStone(i)"
|
||||
>
|
||||
<img v-if="stone === true" style="pointer-events: none; user-select: none; display: block; width: 100%; height: 100%;" :src="blackUser.avatarUrl">
|
||||
<img v-if="stone === false" style="pointer-events: none; user-select: none; display: block; width: 100%; height: 100%;" :src="whiteUser.avatarUrl">
|
||||
<template v-if="useAvatarAsStone">
|
||||
<img v-if="stone === true" :class="$style.boardCellStone" :src="blackUser.avatarUrl"/>
|
||||
<img v-if="stone === false" :class="$style.boardCellStone" :src="whiteUser.avatarUrl"/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<img v-if="stone === true" :class="$style.boardCellStone" src="/client-assets/reversi/stone_b.png"/>
|
||||
<img v-if="stone === false" :class="$style.boardCellStone" src="/client-assets/reversi/stone_w.png"/>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="showBoardLabels" :class="$style.labelsY">
|
||||
|
@ -62,10 +74,41 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="status"><b>{{ i18n.tsx._reversi.turnCount({ count: logPos }) }}</b> {{ i18n.ts._reversi.black }}:{{ engine.blackCount }} {{ i18n.ts._reversi.white }}:{{ engine.whiteCount }} {{ i18n.ts._reversi.total }}:{{ engine.blackCount + engine.whiteCount }}</div>
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<div>
|
||||
<b>{{ i18n.tsx._reversi.turnCount({ count: logPos }) }}</b> {{ i18n.ts._reversi.black }}:{{ engine.blackCount }} {{ i18n.ts._reversi.white }}:{{ engine.whiteCount }} {{ i18n.ts._reversi.total }}:{{ engine.blackCount + engine.whiteCount }}
|
||||
</div>
|
||||
<div>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="margin-right: 8px;">({{ i18n.ts._reversi.black }})</span>
|
||||
<MkAvatar style="width: 32px; height: 32px; margin-right: 8px;" :user="blackUser" :showIndicator="true"/>
|
||||
<MkA :to="userPage(blackUser)"><MkUserName :user="blackUser"/></MkA>
|
||||
</div>
|
||||
<div> vs </div>
|
||||
<div style="display: flex; align-items: center;">
|
||||
<span style="margin-right: 8px;">({{ i18n.ts._reversi.white }})</span>
|
||||
<MkAvatar style="width: 32px; height: 32px; margin-right: 8px;" :user="whiteUser" :showIndicator="true"/>
|
||||
<MkA :to="userPage(whiteUser)"><MkUserName :user="whiteUser"/></MkA>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p v-if="game.isLlotheo">{{ i18n.ts._reversi.isLlotheo }}</p>
|
||||
<p v-if="game.loopedBoard">{{ i18n.ts._reversi.loopedMap }}</p>
|
||||
<p v-if="game.canPutEverywhere">{{ i18n.ts._reversi.canPutEverywhere }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="!game.isEnded && iAmPlayer" class="_buttonsCenter">
|
||||
<MkButton danger @click="surrender">{{ i18n.ts._reversi.surrender }}</MkButton>
|
||||
<MkFolder>
|
||||
<template #label>{{ i18n.ts.options }}</template>
|
||||
<div class="_gaps_s" style="text-align: left;">
|
||||
<MkSwitch v-model="showBoardLabels">Show labels</MkSwitch>
|
||||
<MkSwitch v-model="useAvatarAsStone">useAvatarAsStone</MkSwitch>
|
||||
</div>
|
||||
</MkFolder>
|
||||
|
||||
<div class="_buttonsCenter">
|
||||
<MkButton v-if="!game.isEnded && iAmPlayer" danger @click="surrender">{{ i18n.ts._reversi.surrender }}</MkButton>
|
||||
<MkButton @click="share">{{ i18n.ts.share }}</MkButton>
|
||||
</div>
|
||||
|
||||
<div v-if="game.isEnded" class="_panel _gaps_s" style="padding: 16px;">
|
||||
|
@ -79,12 +122,6 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<MkButton style="margin: auto;" :disabled="autoplaying" @click="autoplay()"><i class="ti ti-player-play"></i></MkButton>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p v-if="game.isLlotheo">{{ i18n.ts._reversi.isLlotheo }}</p>
|
||||
<p v-if="game.loopedBoard">{{ i18n.ts._reversi.loopedMap }}</p>
|
||||
<p v-if="game.canPutEverywhere">{{ i18n.ts._reversi.canPutEverywhere }}</p>
|
||||
</div>
|
||||
|
||||
<MkA v-if="game.isEnded" :to="`/reversi`">
|
||||
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/>
|
||||
</MkA>
|
||||
|
@ -98,12 +135,16 @@ import * as CRC32 from 'crc-32';
|
|||
import * as Misskey from 'misskey-js';
|
||||
import * as Reversi from 'misskey-reversi';
|
||||
import MkButton from '@/components/MkButton.vue';
|
||||
import MkFolder from '@/components/MkFolder.vue';
|
||||
import MkSwitch from '@/components/MkSwitch.vue';
|
||||
import { deepClone } from '@/scripts/clone.js';
|
||||
import { useInterval } from '@/scripts/use-interval.js';
|
||||
import { signinRequired } from '@/account.js';
|
||||
import { i18n } from '@/i18n.js';
|
||||
import { misskeyApi } from '@/scripts/misskey-api.js';
|
||||
import { userPage } from '@/filters/user.js';
|
||||
import * as sound from '@/scripts/sound.js';
|
||||
import * as os from '@/os.js';
|
||||
|
||||
const $i = signinRequired();
|
||||
|
||||
|
@ -112,7 +153,8 @@ const props = defineProps<{
|
|||
connection: Misskey.ChannelConnection;
|
||||
}>();
|
||||
|
||||
const showBoardLabels = true;
|
||||
const showBoardLabels = ref<boolean>(false);
|
||||
const useAvatarAsStone = ref<boolean>(true);
|
||||
const autoplaying = ref<boolean>(false);
|
||||
const game = ref<Misskey.entities.ReversiGameDetailed>(deepClone(props.game));
|
||||
const logPos = ref<number>(game.value.logs.length);
|
||||
|
@ -206,8 +248,10 @@ function putStone(pos) {
|
|||
|
||||
triggerRef(engine);
|
||||
|
||||
// サウンドを再生する
|
||||
//sound.play(myColor.value ? 'reversiPutBlack' : 'reversiPutWhite');
|
||||
sound.playUrl('/client-assets/reversi/put.mp3', {
|
||||
volume: 1,
|
||||
playbackRate: 1,
|
||||
});
|
||||
|
||||
const id = Math.random().toString(36).slice(2);
|
||||
props.connection.send('putStone', {
|
||||
|
@ -232,8 +276,13 @@ function onStreamLog(log: Reversi.Serializer.Log & { id: string | null }) {
|
|||
case 'put': {
|
||||
engine.value.putStone(log.pos);
|
||||
triggerRef(engine);
|
||||
|
||||
sound.playUrl('/client-assets/reversi/put.mp3', {
|
||||
volume: 1,
|
||||
playbackRate: 1,
|
||||
});
|
||||
|
||||
checkEnd();
|
||||
//sound.play(x.color ? 'reversiPutBlack' : 'reversiPutWhite');
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -281,7 +330,13 @@ function onStreamRescue(_game) {
|
|||
checkEnd();
|
||||
}
|
||||
|
||||
function surrender() {
|
||||
async function surrender() {
|
||||
const { canceled } = await os.confirm({
|
||||
type: 'warning',
|
||||
text: i18n.ts.areYouSure,
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
misskeyApi('reversi/surrender', {
|
||||
gameId: game.value.id,
|
||||
});
|
||||
|
@ -317,6 +372,13 @@ function autoplay() {
|
|||
}, 1000);
|
||||
}
|
||||
|
||||
function share() {
|
||||
os.post({
|
||||
initialText: `#MisskeyReversi ${location.href}`,
|
||||
instant: true,
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
props.connection.on('log', onStreamLog);
|
||||
props.connection.on('rescue', onStreamRescue);
|
||||
|
@ -341,7 +403,7 @@ $gap: 4px;
|
|||
}
|
||||
|
||||
.board {
|
||||
width: calc(100% - 16px);
|
||||
width: 100%;
|
||||
max-width: 500px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
@ -437,4 +499,12 @@ $gap: 4px;
|
|||
border-color: transparent !important;
|
||||
}
|
||||
}
|
||||
|
||||
.boardCellStone {
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -10,10 +10,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
||||
</div>
|
||||
|
||||
<div class="_panel" style="padding: 16px;">
|
||||
<div class="_buttonsCenter">
|
||||
<MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton>
|
||||
<MkButton primary gradate rounded @click="matchUser">{{ i18n.ts.invite }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<MkFolder v-if="invitations.length > 0" :defaultOpen="true">
|
||||
<template #label>{{ i18n.ts.invitations }}</template>
|
||||
|
@ -28,12 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<MkFolder v-if="$i" :defaultOpen="true">
|
||||
<template #label>{{ i18n.ts._reversi.myGames }}</template>
|
||||
<MkPagination :pagination="myGamesPagination">
|
||||
<MkPagination :pagination="myGamesPagination" :disableAutoLoad="true">
|
||||
<template #default="{ items }">
|
||||
<div :class="$style.gamePreviews">
|
||||
<MkA v-for="g in items" :key="g.id" v-panel :class="$style.gamePreview" tabindex="-1" :to="`/reversi/g/${g.id}`">
|
||||
<div :class="$style.gamePreviewPlayers">
|
||||
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
|
||||
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/> vs <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
|
||||
</div>
|
||||
<div :class="$style.gamePreviewFooter">
|
||||
<span :style="!g.isEnded ? 'color: var(--accent);' : ''">{{ g.isEnded ? i18n.ts._reversi.ended : i18n.ts._reversi.playing }}</span>
|
||||
|
@ -47,12 +49,12 @@ SPDX-License-Identifier: AGPL-3.0-only
|
|||
|
||||
<MkFolder :defaultOpen="true">
|
||||
<template #label>{{ i18n.ts._reversi.allGames }}</template>
|
||||
<MkPagination :pagination="gamesPagination">
|
||||
<MkPagination :pagination="gamesPagination" :disableAutoLoad="true">
|
||||
<template #default="{ items }">
|
||||
<div :class="$style.gamePreviews">
|
||||
<MkA v-for="g in items" :key="g.id" v-panel :class="$style.gamePreview" tabindex="-1" :to="`/reversi/g/${g.id}`">
|
||||
<div :class="$style.gamePreviewPlayers">
|
||||
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/><b><MkUserName :user="g.user1"/></b> vs <b><MkUserName :user="g.user2"/></b><MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
|
||||
<MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user1"/> vs <MkAvatar :class="$style.gamePreviewPlayersAvatar" :user="g.user2"/>
|
||||
</div>
|
||||
<div :class="$style.gamePreviewFooter">
|
||||
<span :style="!g.isEnded ? 'color: var(--accent);' : ''">{{ g.isEnded ? i18n.ts._reversi.ended : i18n.ts._reversi.playing }}</span>
|
||||
|
|
Loading…
Reference in a new issue