diff options
| author | syuilo <syuilotan@yahoo.co.jp> | 2018-03-08 17:57:57 +0900 |
|---|---|---|
| committer | syuilo <syuilotan@yahoo.co.jp> | 2018-03-08 17:57:57 +0900 |
| commit | 155012846d8f142515390adb2754b45cdfbb74ef (patch) | |
| tree | 13b237f9464a7df918fee7f35ba6871d08c1ed1c /src/web/app/common | |
| parent | Fix bug (diff) | |
| download | misskey-155012846d8f142515390adb2754b45cdfbb74ef.tar.gz misskey-155012846d8f142515390adb2754b45cdfbb74ef.tar.bz2 misskey-155012846d8f142515390adb2754b45cdfbb74ef.zip | |
#1200
Diffstat (limited to 'src/web/app/common')
| -rw-r--r-- | src/web/app/common/views/components/othello.game.vue | 164 | ||||
| -rw-r--r-- | src/web/app/common/views/components/othello.gameroom.vue | 42 | ||||
| -rw-r--r-- | src/web/app/common/views/components/othello.room.vue | 152 | ||||
| -rw-r--r-- | src/web/app/common/views/components/othello.vue | 18 |
4 files changed, 286 insertions, 90 deletions
diff --git a/src/web/app/common/views/components/othello.game.vue b/src/web/app/common/views/components/othello.game.vue index 1cb2400f7e..2ef6b645ce 100644 --- a/src/web/app/common/views/components/othello.game.vue +++ b/src/web/app/common/views/components/othello.game.vue @@ -1,23 +1,27 @@ <template> <div class="root"> - <header><b>{{ game.black_user.name }}</b>(黒) vs <b>{{ game.white_user.name }}</b>(白)</header> - <p class="turn" v-if="!iAmPlayer && !isEnded">{{ turn.name }}のターンです<mk-ellipsis/></p> - <p class="turn" v-if="logPos != logs.length">{{ turn.name }}のターン</p> - <p class="turn" v-if="iAmPlayer && !isEnded">{{ isMyTurn ? 'あなたのターンです' : '相手のターンです' }}<mk-ellipsis v-if="!isMyTurn"/></p> - <p class="result" v-if="isEnded && logPos == logs.length"> - <template v-if="winner"><b>{{ winner.name }}</b>の勝ち</template> + <header><b>{{ blackUser.name }}</b>(黒) vs <b>{{ whiteUser.name }}</b>(白)</header> + + <p class="turn" v-if="!iAmPlayer && !game.is_ended">{{ turnUser.name }}のターンです<mk-ellipsis/></p> + <p class="turn" v-if="logPos != logs.length">{{ turnUser.name }}のターン</p> + <p class="turn" v-if="iAmPlayer && !game.is_ended">{{ isMyTurn ? 'あなたのターンです' : '相手のターンです' }}<mk-ellipsis v-if="!isMyTurn"/></p> + <p class="result" v-if="game.is_ended && logPos == logs.length"> + <template v-if="game.winner"><b>{{ game.winner.name }}</b>の勝ち{{ game.settings.is_llotheo ? ' (ロセオ)' : '' }}</template> <template v-else>引き分け</template> </p> - <div class="board"> + + <div class="board" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.size }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map.size }, 1fr)` }"> <div v-for="(stone, i) in o.board" - :class="{ empty: stone == null, myTurn: isMyTurn, can: o.canReverse(turn.id == game.black_user.id ? 'black' : 'white', i), prev: o.prevPos == i }" + :class="{ empty: stone == null, none: o.map.data[i] == ' ', myTurn: isMyTurn, can: turnUser ? o.canPut(turnUser.id == blackUser.id ? 'black' : 'white', i) : null, prev: o.prevPos == i }" @click="set(i)" > - <img v-if="stone == 'black'" :src="`${game.black_user.avatar_url}?thumbnail&size=64`" alt=""> - <img v-if="stone == 'white'" :src="`${game.white_user.avatar_url}?thumbnail&size=64`" alt=""> + <img v-if="stone == 'black'" :src="`${blackUser.avatar_url}?thumbnail&size=128`" alt=""> + <img v-if="stone == 'white'" :src="`${whiteUser.avatar_url}?thumbnail&size=128`" alt=""> </div> </div> + <p>黒:{{ o.blackCount }} 白:{{ o.whiteCount }} 合計:{{ o.blackCount + o.whiteCount }}</p> + <div class="graph"> <div v-for="n in 61 - o.stats.length"> </div> @@ -26,7 +30,8 @@ <div :style="{ height: `${ Math.floor(data.w * 100) }%` }"></div> </div> </div> - <div class="player" v-if="isEnded"> + + <div class="player" v-if="game.is_ended"> <el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:fast-backward%</el-button> <el-button type="primary" @click="logPos--" :disabled="logPos == 0">%fa:backward%</el-button> <span>{{ logPos }} / {{ logs.length }}</span> @@ -38,58 +43,63 @@ <script lang="ts"> import Vue from 'vue'; -import { OthelloGameStream } from '../../scripts/streaming/othello-game'; -import Othello from '../../../../../common/othello'; +import Othello, { Color } from '../../../../../common/othello/core'; export default Vue.extend({ - props: ['game'], + props: ['game', 'connection'], data() { return { - o: new Othello(), + o: null as Othello, logs: [], - logPos: 0, - turn: null, - isMyTurn: null, - isEnded: false, - winner: null, - connection: null + logPos: 0 }; }, computed: { iAmPlayer(): boolean { - return this.game.black_user_id == (this as any).os.i.id || this.game.white_user_id == (this as any).os.i.id; + return this.game.user1_id == (this as any).os.i.id || this.game.user2_id == (this as any).os.i.id; }, - myColor(): string { - return this.game.black_user_id == (this as any).os.i.id ? 'black' : 'white'; + myColor(): Color { + if (!this.iAmPlayer) return null; + if (this.game.user1_id == (this as any).os.i.id && this.game.black == 1) return 'black'; + if (this.game.user2_id == (this as any).os.i.id && this.game.black == 2) return 'black'; + return 'white'; }, - opColor(): string { + opColor(): Color { + if (!this.iAmPlayer) return null; return this.myColor == 'black' ? 'white' : 'black'; + }, + blackUser(): any { + return this.game.black == 1 ? this.game.user1 : this.game.user2; + }, + whiteUser(): any { + return this.game.black == 1 ? this.game.user2 : this.game.user1; + }, + turnUser(): any { + if (this.o.turn == 'black') { + return this.game.black == 1 ? this.game.user1 : this.game.user2; + } else if (this.o.turn == 'white') { + return this.game.black == 1 ? this.game.user2 : this.game.user1; + } else { + return null; + } + }, + isMyTurn(): boolean { + if (this.turnUser == null) return null; + return this.turnUser.id == (this as any).os.i.id; } }, watch: { logPos(v) { - if (!this.isEnded) return; - this.o = new Othello(); - this.turn = this.game.black_user; + if (!this.game.is_ended) return; + this.o = new Othello(this.game.settings.map, { + isLlotheo: this.game.settings.is_llotheo + }); this.logs.forEach((log, i) => { if (i < v) { - this.o.set(log.color, log.pos); - - if (log.color == 'black' && this.o.getPattern('white').length > 0) { - this.turn = this.game.white_user; - } - if (log.color == 'black' && this.o.getPattern('white').length == 0) { - this.turn = this.game.black_user; - } - if (log.color == 'white' && this.o.getPattern('black').length > 0) { - this.turn = this.game.black_user; - } - if (log.color == 'white' && this.o.getPattern('black').length == 0) { - this.turn = this.game.white_user; - } + this.o.put(log.color, log.pos); } }); this.$forceUpdate(); @@ -97,74 +107,65 @@ export default Vue.extend({ }, created() { + this.o = new Othello(this.game.settings.map, { + isLlotheo: this.game.settings.is_llotheo + }); + this.game.logs.forEach(log => { - this.o.set(log.color, log.pos); + this.o.put(log.color, log.pos); }); this.logs = this.game.logs; this.logPos = this.logs.length; - this.turn = this.game.turn_user_id == this.game.black_user_id ? this.game.black_user : this.game.white_user; - this.isMyTurn = this.game.turn_user_id == (this as any).os.i.id; - this.isEnded = this.game.is_ended; - this.winner = this.game.winner; }, mounted() { - this.connection = new OthelloGameStream((this as any).os.i, this.game); this.connection.on('set', this.onSet); }, beforeDestroy() { this.connection.off('set', this.onSet); - this.connection.close(); }, methods: { set(pos) { if (!this.isMyTurn) return; - if (!this.o.canReverse(this.myColor, pos)) return; - this.o.set(this.myColor, pos); - if (this.o.getPattern(this.opColor).length > 0) { - this.isMyTurn = !this.isMyTurn; - this.turn = this.myColor == 'black' ? this.game.white_user : this.game.black_user; - } else if (this.o.getPattern(this.myColor).length == 0) { - this.isEnded = true; - this.winner = this.o.blackCount == this.o.whiteCount ? null : this.o.blackCount > this.o.whiteCount ? this.game.black_user : this.game.white_user; - } + if (!this.o.canPut(this.myColor, pos)) return; + + this.o.put(this.myColor, pos); + this.connection.send({ type: 'set', pos }); + + this.checkEnd(); + this.$forceUpdate(); }, onSet(x) { this.logs.push(x); this.logPos++; - this.o.set(x.color, x.pos); - - if (this.o.getPattern('black').length == 0 && this.o.getPattern('white').length == 0) { - this.isEnded = true; - this.winner = this.o.blackCount == this.o.whiteCount ? null : this.o.blackCount > this.o.whiteCount ? this.game.black_user : this.game.white_user; - } else { - if (this.iAmPlayer && this.o.getPattern(this.myColor).length > 0) { - this.isMyTurn = true; - } + this.o.put(x.color, x.pos); + this.checkEnd(); + this.$forceUpdate(); + }, - if (x.color == 'black' && this.o.getPattern('white').length > 0) { - this.turn = this.game.white_user; - } - if (x.color == 'black' && this.o.getPattern('white').length == 0) { - this.turn = this.game.black_user; - } - if (x.color == 'white' && this.o.getPattern('black').length > 0) { - this.turn = this.game.black_user; - } - if (x.color == 'white' && this.o.getPattern('black').length == 0) { - this.turn = this.game.white_user; + checkEnd() { + this.game.is_ended = this.o.isEnded; + if (this.game.is_ended) { + if (this.o.winner == 'black') { + this.game.winner_id = this.game.black == 1 ? this.game.user1_id : this.game.user2_id; + this.game.winner = this.game.black == 1 ? this.game.user1 : this.game.user2; + } else if (this.o.winner == 'white') { + this.game.winner_id = this.game.black == 1 ? this.game.user2_id : this.game.user1_id; + this.game.winner = this.game.black == 1 ? this.game.user2 : this.game.user1; + } else { + this.game.winner_id = null; + this.game.winner = null; } } - this.$forceUpdate(); } } }); @@ -182,8 +183,6 @@ export default Vue.extend({ > .board display grid - grid-template-rows repeat(8, 1fr) - grid-template-columns repeat(8, 1fr) grid-gap 4px width 300px height 300px @@ -221,6 +220,9 @@ export default Vue.extend({ &.prev box-shadow 0 0 0 4px rgba($theme-color, 0.7) + &.none + border-color transparent !important + > img display block width 100% diff --git a/src/web/app/common/views/components/othello.gameroom.vue b/src/web/app/common/views/components/othello.gameroom.vue new file mode 100644 index 0000000000..9f4037515a --- /dev/null +++ b/src/web/app/common/views/components/othello.gameroom.vue @@ -0,0 +1,42 @@ +<template> +<div> + <x-room v-if="!g.is_started" :game="g" :connection="connection"/> + <x-game v-else :game="g" :connection="connection"/> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import XGame from './othello.game.vue'; +import XRoom from './othello.room.vue'; +import { OthelloGameStream } from '../../scripts/streaming/othello-game'; + +export default Vue.extend({ + components: { + XGame, + XRoom + }, + props: ['game'], + data() { + return { + connection: null, + g: null + }; + }, + created() { + this.g = this.game; + this.connection = new OthelloGameStream((this as any).os.i, this.game); + this.connection.on('started', this.onStarted); + }, + beforeDestroy() { + this.connection.off('started', this.onStarted); + this.connection.close(); + }, + methods: { + onStarted(game) { + Object.assign(this.g, game); + this.$forceUpdate(); + } + } +}); +</script> diff --git a/src/web/app/common/views/components/othello.room.vue b/src/web/app/common/views/components/othello.room.vue new file mode 100644 index 0000000000..b41d97ec61 --- /dev/null +++ b/src/web/app/common/views/components/othello.room.vue @@ -0,0 +1,152 @@ +<template> +<div class="root"> + <header><b>{{ game.user1.name }}</b> vs <b>{{ game.user2.name }}</b></header> + + <p>ゲームの設定</p> + + <el-select v-model="mapName" placeholder="マップを選択" @change="onMapChange"> + <el-option v-for="m in maps" :key="m.name" :label="m.name" :value="m.name"/> + </el-select> + + <div class="board" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.size }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map.size }, 1fr)` }"> + <div v-for="(x, i) in game.settings.map.data" + :class="{ none: x == ' ' }" + > + <template v-if="x == 'b'">%fa:circle%</template> + <template v-if="x == 'w'">%fa:circle R%</template> + </div> + </div> + + <div class="rules"> + <mk-switch v-model="game.settings.is_llotheo" @change="onIsLlotheoChange" text="石の少ない方が勝ち(ロセオ)"/> + </div> + + <div class="actions"> + <el-button @click="exit">キャンセル</el-button> + <el-button type="primary" @click="accept" v-if="!isAccepted">準備完了</el-button> + <el-button type="primary" @click="cancel" v-if="isAccepted">準備続行</el-button> + </div> +</div> +</template> + +<script lang="ts"> +import Vue from 'vue'; +import * as maps from '../../../../../common/othello/maps'; + +export default Vue.extend({ + props: ['game', 'connection'], + + data() { + return { + o: null, + isLlotheo: false, + mapName: maps.eighteight.name, + maps: maps + }; + }, + + computed: { + isAccepted(): boolean { + if (this.game.user1_id == (this as any).os.i.id && this.game.user1_accepted) return true; + if (this.game.user2_id == (this as any).os.i.id && this.game.user2_accepted) return true; + return false; + } + }, + + created() { + this.connection.on('change-accepts', this.onChangeAccepts); + this.connection.on('update-settings', this.onUpdateSettings); + }, + + beforeDestroy() { + this.connection.off('change-accepts', this.onChangeAccepts); + this.connection.off('update-settings', this.onUpdateSettings); + }, + + methods: { + exit() { + + }, + + accept() { + this.connection.send({ + type: 'accept' + }); + }, + + cancel() { + this.connection.send({ + type: 'cancel-accept' + }); + }, + + onChangeAccepts(accepts) { + this.game.user1_accepted = accepts.user1; + this.game.user2_accepted = accepts.user2; + this.$forceUpdate(); + }, + + onUpdateSettings(settings) { + this.game.settings = settings; + this.mapName = this.game.settings.map.name; + }, + + onMapChange(v) { + this.game.settings.map = Object.entries(maps).find(x => x[1].name == v)[1]; + this.connection.send({ + type: 'update-settings', + settings: this.game.settings + }); + this.$forceUpdate(); + }, + + onIsLlotheoChange(v) { + this.connection.send({ + type: 'update-settings', + settings: this.game.settings + }); + this.$forceUpdate(); + } + } +}); +</script> + +<style lang="stylus" scoped> +@import '~const.styl' + +.root + text-align center + + > header + padding 8px + border-bottom dashed 1px #c4cdd4 + + > .board + display grid + grid-gap 4px + width 300px + height 300px + margin 16px auto + + > div + background transparent + border solid 2px #eee + border-radius 6px + overflow hidden + + * + pointer-events none + user-select none + width 100% + height 100% + + &.none + border-color transparent + + > .rules + max-width 300px + margin 0 auto + + > .actions + margin-bottom 16px +</style> diff --git a/src/web/app/common/views/components/othello.vue b/src/web/app/common/views/components/othello.vue index 326a8d80cc..31858fca19 100644 --- a/src/web/app/common/views/components/othello.vue +++ b/src/web/app/common/views/components/othello.vue @@ -1,7 +1,7 @@ <template> <div class="mk-othello"> <div v-if="game"> - <x-game :game="game"/> + <x-gameroom :game="game"/> </div> <div class="matching" v-else-if="matching"> <h1><b>{{ matching.name }}</b>を待っています<mk-ellipsis/></h1> @@ -40,18 +40,18 @@ <section v-if="myGames.length > 0"> <h2>自分の対局</h2> <div class="game" v-for="g in myGames" tabindex="-1" @click="game = g"> - <img :src="`${g.black_user.avatar_url}?thumbnail&size=32`" alt=""> - <img :src="`${g.white_user.avatar_url}?thumbnail&size=32`" alt=""> - <span><b>{{ g.black_user.name }}</b> vs <b>{{ g.white_user.name }}</b></span> + <img :src="`${g.user1.avatar_url}?thumbnail&size=32`" alt=""> + <img :src="`${g.user2.avatar_url}?thumbnail&size=32`" alt=""> + <span><b>{{ g.user1.name }}</b> vs <b>{{ g.user2.name }}</b></span> <span class="state">{{ g.is_ended ? '終了' : '進行中' }}</span> </div> </section> <section v-if="games.length > 0"> <h2>みんなの対局</h2> <div class="game" v-for="g in games" tabindex="-1" @click="game = g"> - <img :src="`${g.black_user.avatar_url}?thumbnail&size=32`" alt=""> - <img :src="`${g.white_user.avatar_url}?thumbnail&size=32`" alt=""> - <span><b>{{ g.black_user.name }}</b> vs <b>{{ g.white_user.name }}</b></span> + <img :src="`${g.user1.avatar_url}?thumbnail&size=32`" alt=""> + <img :src="`${g.user2.avatar_url}?thumbnail&size=32`" alt=""> + <span><b>{{ g.user1.name }}</b> vs <b>{{ g.user2.name }}</b></span> <span class="state">{{ g.is_ended ? '終了' : '進行中' }}</span> </div> </section> @@ -61,11 +61,11 @@ <script lang="ts"> import Vue from 'vue'; -import XGame from './othello.game.vue'; +import XGameroom from './othello.gameroom.vue'; export default Vue.extend({ components: { - XGame + XGameroom }, data() { return { |