mirror of
https://github.com/misskey-dev/misskey.git
synced 2024-12-25 00:49:17 +09:00
enhance(reversi): tweak reversi
This commit is contained in:
parent
094c6e32ff
commit
3bf3ba450c
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>
|
<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>
|
||||||
|
@ -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;"/>
|
<img src="/client-assets/reversi/logo.png" style="display: block; max-width: 100%; max-height: 200px; margin: auto;"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="_panel" style="padding: 16px;">
|
||||||
<div class="_buttonsCenter">
|
<div class="_buttonsCenter">
|
||||||
<MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton>
|
<MkButton primary gradate rounded @click="matchAny">{{ i18n.ts._reversi.freeMatch }}</MkButton>
|
||||||
<MkButton primary gradate rounded @click="matchUser">{{ i18n.ts.invite }}</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">
|
||||||
<template #label>{{ i18n.ts.invitations }}</template>
|
<template #label>{{ i18n.ts.invitations }}</template>
|
||||||
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user