enhance(reversi): tweak reversi

This commit is contained in:
syuilo 2024-01-20 14:51:40 +09:00
parent 094c6e32ff
commit 3bf3ba450c
5 changed files with 97 additions and 25 deletions

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -6,7 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<MkSpacer :contentMax="600"> <MkSpacer :contentMax="600">
<div :class="$style.root" class="_gaps"> <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 style="overflow: clip; line-height: 28px;">
<div v-if="!iAmPlayer && !game.isEnded && turnUser" class="turn"> <div v-if="!iAmPlayer && !game.isEnded && turnUser" class="turn">
@ -49,8 +55,14 @@ SPDX-License-Identifier: AGPL-3.0-only
}]" }]"
@click="putStone(i)" @click="putStone(i)"
> >
<img v-if="stone === true" style="pointer-events: none; user-select: none; display: block; width: 100%; height: 100%;" :src="blackUser.avatarUrl"> <template v-if="useAvatarAsStone">
<img v-if="stone === false" style="pointer-events: none; user-select: none; display: block; width: 100%; height: 100%;" :src="whiteUser.avatarUrl"> <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> </div>
<div v-if="showBoardLabels" :class="$style.labelsY"> <div v-if="showBoardLabels" :class="$style.labelsY">
@ -62,10 +74,41 @@ SPDX-License-Identifier: AGPL-3.0-only
</div> </div>
</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"> <MkFolder>
<MkButton danger @click="surrender">{{ i18n.ts._reversi.surrender }}</MkButton> <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>
<div v-if="game.isEnded" class="_panel _gaps_s" style="padding: 16px;"> <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> <MkButton style="margin: auto;" :disabled="autoplaying" @click="autoplay()"><i class="ti ti-player-play"></i></MkButton>
</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>
<MkA v-if="game.isEnded" :to="`/reversi`"> <MkA v-if="game.isEnded" :to="`/reversi`">
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/> <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; width: 200px; margin: auto;"/>
</MkA> </MkA>
@ -98,12 +135,16 @@ import * as CRC32 from 'crc-32';
import * as Misskey from 'misskey-js'; import * as Misskey from 'misskey-js';
import * as Reversi from 'misskey-reversi'; import * as Reversi from 'misskey-reversi';
import MkButton from '@/components/MkButton.vue'; 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 { deepClone } from '@/scripts/clone.js';
import { useInterval } from '@/scripts/use-interval.js'; import { useInterval } from '@/scripts/use-interval.js';
import { signinRequired } from '@/account.js'; import { signinRequired } from '@/account.js';
import { i18n } from '@/i18n.js'; import { i18n } from '@/i18n.js';
import { misskeyApi } from '@/scripts/misskey-api.js'; import { misskeyApi } from '@/scripts/misskey-api.js';
import { userPage } from '@/filters/user.js'; import { userPage } from '@/filters/user.js';
import * as sound from '@/scripts/sound.js';
import * as os from '@/os.js';
const $i = signinRequired(); const $i = signinRequired();
@ -112,7 +153,8 @@ const props = defineProps<{
connection: Misskey.ChannelConnection; connection: Misskey.ChannelConnection;
}>(); }>();
const showBoardLabels = true; const showBoardLabels = ref<boolean>(false);
const useAvatarAsStone = ref<boolean>(true);
const autoplaying = ref<boolean>(false); const autoplaying = ref<boolean>(false);
const game = ref<Misskey.entities.ReversiGameDetailed>(deepClone(props.game)); const game = ref<Misskey.entities.ReversiGameDetailed>(deepClone(props.game));
const logPos = ref<number>(game.value.logs.length); const logPos = ref<number>(game.value.logs.length);
@ -206,8 +248,10 @@ function putStone(pos) {
triggerRef(engine); triggerRef(engine);
// sound.playUrl('/client-assets/reversi/put.mp3', {
//sound.play(myColor.value ? 'reversiPutBlack' : 'reversiPutWhite'); volume: 1,
playbackRate: 1,
});
const id = Math.random().toString(36).slice(2); const id = Math.random().toString(36).slice(2);
props.connection.send('putStone', { props.connection.send('putStone', {
@ -232,8 +276,13 @@ function onStreamLog(log: Reversi.Serializer.Log & { id: string | null }) {
case 'put': { case 'put': {
engine.value.putStone(log.pos); engine.value.putStone(log.pos);
triggerRef(engine); triggerRef(engine);
sound.playUrl('/client-assets/reversi/put.mp3', {
volume: 1,
playbackRate: 1,
});
checkEnd(); checkEnd();
//sound.play(x.color ? 'reversiPutBlack' : 'reversiPutWhite');
break; break;
} }
@ -281,7 +330,13 @@ function onStreamRescue(_game) {
checkEnd(); checkEnd();
} }
function surrender() { async function surrender() {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts.areYouSure,
});
if (canceled) return;
misskeyApi('reversi/surrender', { misskeyApi('reversi/surrender', {
gameId: game.value.id, gameId: game.value.id,
}); });
@ -317,6 +372,13 @@ function autoplay() {
}, 1000); }, 1000);
} }
function share() {
os.post({
initialText: `#MisskeyReversi ${location.href}`,
instant: true,
});
}
onMounted(() => { onMounted(() => {
props.connection.on('log', onStreamLog); props.connection.on('log', onStreamLog);
props.connection.on('rescue', onStreamRescue); props.connection.on('rescue', onStreamRescue);
@ -341,7 +403,7 @@ $gap: 4px;
} }
.board { .board {
width: calc(100% - 16px); width: 100%;
max-width: 500px; max-width: 500px;
margin: 0 auto; margin: 0 auto;
} }
@ -437,4 +499,12 @@ $gap: 4px;
border-color: transparent !important; border-color: transparent !important;
} }
} }
.boardCellStone {
pointer-events: none;
user-select: none;
display: block;
width: 100%;
height: 100%;
}
</style> </style>

View File

@ -10,9 +10,11 @@ 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;"/> <img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
</div> </div>
<div class="_buttonsCenter"> <div class="_panel" style="padding: 16px;">
<MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton> <div class="_buttonsCenter">
<MkButton primary gradate rounded @click="matchUser">{{ i18n.ts.invite }}</MkButton> <MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton>
<MkButton primary gradate rounded @click="matchUser">{{ i18n.ts.invite }}</MkButton>
</div>
</div> </div>
<MkFolder v-if="invitations.length > 0" :defaultOpen="true"> <MkFolder v-if="invitations.length > 0" :defaultOpen="true">
@ -28,12 +30,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkFolder v-if="$i" :defaultOpen="true"> <MkFolder v-if="$i" :defaultOpen="true">
<template #label>{{ i18n.ts._reversi.myGames }}</template> <template #label>{{ i18n.ts._reversi.myGames }}</template>
<MkPagination :pagination="myGamesPagination"> <MkPagination :pagination="myGamesPagination" :disableAutoLoad="true">
<template #default="{ items }"> <template #default="{ items }">
<div :class="$style.gamePreviews"> <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}`"> <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"> <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>
<div :class="$style.gamePreviewFooter"> <div :class="$style.gamePreviewFooter">
<span :style="!g.isEnded ? 'color: var(--accent);' : ''">{{ g.isEnded ? i18n.ts._reversi.ended : i18n.ts._reversi.playing }}</span> <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"> <MkFolder :defaultOpen="true">
<template #label>{{ i18n.ts._reversi.allGames }}</template> <template #label>{{ i18n.ts._reversi.allGames }}</template>
<MkPagination :pagination="gamesPagination"> <MkPagination :pagination="gamesPagination" :disableAutoLoad="true">
<template #default="{ items }"> <template #default="{ items }">
<div :class="$style.gamePreviews"> <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}`"> <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"> <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>
<div :class="$style.gamePreviewFooter"> <div :class="$style.gamePreviewFooter">
<span :style="!g.isEnded ? 'color: var(--accent);' : ''">{{ g.isEnded ? i18n.ts._reversi.ended : i18n.ts._reversi.playing }}</span> <span :style="!g.isEnded ? 'color: var(--accent);' : ''">{{ g.isEnded ? i18n.ts._reversi.ended : i18n.ts._reversi.playing }}</span>