Implement surrender of reversi
This commit is contained in:
		
							parent
							
								
									244d567b3a
								
							
						
					
					
						commit
						be9f6ad294
					
				
					 4 changed files with 98 additions and 2 deletions
				
			
		| 
						 | 
					@ -182,6 +182,10 @@ common/views/components/games/reversi/reversi.vue:
 | 
				
			||||||
    waiting-for: "{}を待っています"
 | 
					    waiting-for: "{}を待っています"
 | 
				
			||||||
    cancel: "キャンセル"
 | 
					    cancel: "キャンセル"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					common/views/components/games/reversi/reversi.game.vue:
 | 
				
			||||||
 | 
					  surrender: "投了"
 | 
				
			||||||
 | 
					  surrendered: "投了により"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
common/views/components/games/reversi/reversi.index.vue:
 | 
					common/views/components/games/reversi/reversi.index.vue:
 | 
				
			||||||
  title: "Misskey Reversi"
 | 
					  title: "Misskey Reversi"
 | 
				
			||||||
  sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
 | 
					  sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
<template>
 | 
					<template>
 | 
				
			||||||
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
 | 
					<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
 | 
				
			||||||
	<header><b>{{ blackUser | userName }}</b>(%i18n:common.reversi.black%) vs <b>{{ whiteUser | userName }}</b>(%i18n:common.reversi.white%)</header>
 | 
						<header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>(%i18n:common.reversi.black%) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>(%i18n:common.reversi.white%)</header>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div style="overflow: hidden">
 | 
						<div style="overflow: hidden">
 | 
				
			||||||
		<p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}<mk-ellipsis/></p>
 | 
							<p class="turn" v-if="!iAmPlayer && !game.isEnded">{{ '%i18n:common.reversi.turn-of%'.replace('{}', $options.filters.userName(turnUser)) }}<mk-ellipsis/></p>
 | 
				
			||||||
| 
						 | 
					@ -8,7 +8,10 @@
 | 
				
			||||||
		<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
 | 
							<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">%i18n:common.reversi.opponent-turn%<mk-ellipsis/></p>
 | 
				
			||||||
		<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
 | 
							<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">%i18n:common.reversi.my-turn%</p>
 | 
				
			||||||
		<p class="result" v-if="game.isEnded && logPos == logs.length">
 | 
							<p class="result" v-if="game.isEnded && logPos == logs.length">
 | 
				
			||||||
			<template v-if="game.winner">{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}{{ game.settings.isLlotheo ? ' (ロセオ)' : '' }}</template>
 | 
								<template v-if="game.winner">
 | 
				
			||||||
 | 
									<span>{{ '%i18n:common.reversi.won%'.replace('{}', $options.filters.userName(game.winner)) }}</span>
 | 
				
			||||||
 | 
									<span v-if="game.surrendered != null"> (%i18n:@surrendered%)</span>
 | 
				
			||||||
 | 
								</template>
 | 
				
			||||||
			<template v-else>%i18n:common.reversi.drawn%</template>
 | 
								<template v-else>%i18n:common.reversi.drawn%</template>
 | 
				
			||||||
		</p>
 | 
							</p>
 | 
				
			||||||
	</div>
 | 
						</div>
 | 
				
			||||||
| 
						 | 
					@ -41,6 +44,10 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<p class="status"><b>{{ '%i18n:common.reversi.this-turn%'.split('{}')[0] }}{{ logPos }}{{ '%i18n:common.reversi.this-turn%'.split('{}')[1] }}</b> %i18n:common.reversi.black%:{{ o.blackCount }} %i18n:common.reversi.white%:{{ o.whiteCount }} %i18n:common.reversi.total%:{{ o.blackCount + o.whiteCount }}</p>
 | 
						<p class="status"><b>{{ '%i18n:common.reversi.this-turn%'.split('{}')[0] }}{{ logPos }}{{ '%i18n:common.reversi.this-turn%'.split('{}')[1] }}</b> %i18n:common.reversi.black%:{{ o.blackCount }} %i18n:common.reversi.white%:{{ o.whiteCount }} %i18n:common.reversi.total%:{{ o.blackCount + o.whiteCount }}</p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						<div class="actions" v-if="!game.isEnded && iAmPlayer">
 | 
				
			||||||
 | 
							<form-button @click="surrender">%i18n:@surrender%</form-button>
 | 
				
			||||||
 | 
						</div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	<div class="player" v-if="game.isEnded">
 | 
						<div class="player" v-if="game.isEnded">
 | 
				
			||||||
		<el-button-group>
 | 
							<el-button-group>
 | 
				
			||||||
			<el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</el-button>
 | 
								<el-button type="primary" @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</el-button>
 | 
				
			||||||
| 
						 | 
					@ -79,22 +86,27 @@ export default Vue.extend({
 | 
				
			||||||
			if (!this.$store.getters.isSignedIn) return false;
 | 
								if (!this.$store.getters.isSignedIn) return false;
 | 
				
			||||||
			return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id;
 | 
								return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		myColor(): Color {
 | 
							myColor(): Color {
 | 
				
			||||||
			if (!this.iAmPlayer) return null;
 | 
								if (!this.iAmPlayer) return null;
 | 
				
			||||||
			if (this.game.user1Id == this.$store.state.i.id && this.game.black == 1) return true;
 | 
								if (this.game.user1Id == this.$store.state.i.id && this.game.black == 1) return true;
 | 
				
			||||||
			if (this.game.user2Id == this.$store.state.i.id && this.game.black == 2) return true;
 | 
								if (this.game.user2Id == this.$store.state.i.id && this.game.black == 2) return true;
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		opColor(): Color {
 | 
							opColor(): Color {
 | 
				
			||||||
			if (!this.iAmPlayer) return null;
 | 
								if (!this.iAmPlayer) return null;
 | 
				
			||||||
			return this.myColor === true ? false : true;
 | 
								return this.myColor === true ? false : true;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		blackUser(): any {
 | 
							blackUser(): any {
 | 
				
			||||||
			return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
								return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		whiteUser(): any {
 | 
							whiteUser(): any {
 | 
				
			||||||
			return this.game.black == 1 ? this.game.user2 : this.game.user1;
 | 
								return this.game.black == 1 ? this.game.user2 : this.game.user1;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		turnUser(): any {
 | 
							turnUser(): any {
 | 
				
			||||||
			if (this.o.turn === true) {
 | 
								if (this.o.turn === true) {
 | 
				
			||||||
				return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
									return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
				
			||||||
| 
						 | 
					@ -104,11 +116,13 @@ export default Vue.extend({
 | 
				
			||||||
				return null;
 | 
									return null;
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		isMyTurn(): boolean {
 | 
							isMyTurn(): boolean {
 | 
				
			||||||
			if (!this.iAmPlayer) return false;
 | 
								if (!this.iAmPlayer) return false;
 | 
				
			||||||
			if (this.turnUser == null) return false;
 | 
								if (this.turnUser == null) return false;
 | 
				
			||||||
			return this.turnUser.id == this.$store.state.i.id;
 | 
								return this.turnUser.id == this.$store.state.i.id;
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		cellsStyle(): any {
 | 
							cellsStyle(): any {
 | 
				
			||||||
			return {
 | 
								return {
 | 
				
			||||||
				'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`,
 | 
									'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`,
 | 
				
			||||||
| 
						 | 
					@ -165,11 +179,13 @@ export default Vue.extend({
 | 
				
			||||||
	mounted() {
 | 
						mounted() {
 | 
				
			||||||
		this.connection.on('set', this.onSet);
 | 
							this.connection.on('set', this.onSet);
 | 
				
			||||||
		this.connection.on('rescue', this.onRescue);
 | 
							this.connection.on('rescue', this.onRescue);
 | 
				
			||||||
 | 
							this.connection.on('ended', this.onEnded);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	beforeDestroy() {
 | 
						beforeDestroy() {
 | 
				
			||||||
		this.connection.off('set', this.onSet);
 | 
							this.connection.off('set', this.onSet);
 | 
				
			||||||
		this.connection.off('rescue', this.onRescue);
 | 
							this.connection.off('rescue', this.onRescue);
 | 
				
			||||||
 | 
							this.connection.off('ended', this.onEnded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		clearInterval(this.pollingClock);
 | 
							clearInterval(this.pollingClock);
 | 
				
			||||||
	},
 | 
						},
 | 
				
			||||||
| 
						 | 
					@ -215,6 +231,10 @@ export default Vue.extend({
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		},
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							onEnded(x) {
 | 
				
			||||||
 | 
								this.game = x.game;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		checkEnd() {
 | 
							checkEnd() {
 | 
				
			||||||
			this.game.isEnded = this.o.isEnded;
 | 
								this.game.isEnded = this.o.isEnded;
 | 
				
			||||||
			if (this.game.isEnded) {
 | 
								if (this.game.isEnded) {
 | 
				
			||||||
| 
						 | 
					@ -250,6 +270,12 @@ export default Vue.extend({
 | 
				
			||||||
 | 
					
 | 
				
			||||||
			this.checkEnd();
 | 
								this.checkEnd();
 | 
				
			||||||
			this.$forceUpdate();
 | 
								this.$forceUpdate();
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							surrender() {
 | 
				
			||||||
 | 
								(this as any).api('games/reversi/games/surrender', {
 | 
				
			||||||
 | 
									gameId: this.game.id
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
| 
						 | 
					@ -265,6 +291,9 @@ root(isDark)
 | 
				
			||||||
		padding 8px
 | 
							padding 8px
 | 
				
			||||||
		border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
 | 
							border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							a
 | 
				
			||||||
 | 
								color inherit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .board
 | 
						> .board
 | 
				
			||||||
		width calc(100% - 16px)
 | 
							width calc(100% - 16px)
 | 
				
			||||||
		max-width 500px
 | 
							max-width 500px
 | 
				
			||||||
| 
						 | 
					@ -381,6 +410,9 @@ root(isDark)
 | 
				
			||||||
		margin 0
 | 
							margin 0
 | 
				
			||||||
		padding 16px 0
 | 
							padding 16px 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						> .actions
 | 
				
			||||||
 | 
							padding-bottom 16px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	> .player
 | 
						> .player
 | 
				
			||||||
		padding-bottom 32px
 | 
							padding-bottom 32px
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ export interface IReversiGame {
 | 
				
			||||||
	isStarted: boolean;
 | 
						isStarted: boolean;
 | 
				
			||||||
	isEnded: boolean;
 | 
						isEnded: boolean;
 | 
				
			||||||
	winnerId: mongo.ObjectID;
 | 
						winnerId: mongo.ObjectID;
 | 
				
			||||||
 | 
						surrendered: mongo.ObjectID;
 | 
				
			||||||
	logs: Array<{
 | 
						logs: Array<{
 | 
				
			||||||
		at: Date;
 | 
							at: Date;
 | 
				
			||||||
		color: boolean;
 | 
							color: boolean;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										59
									
								
								src/server/api/endpoints/games/reversi/games/surrender.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/server/api/endpoints/games/reversi/games/surrender.ts
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,59 @@
 | 
				
			||||||
 | 
					import $ from 'cafy'; import ID from '../../../../../../misc/cafy-id';
 | 
				
			||||||
 | 
					import ReversiGame, { pack } from '../../../../../../models/games/reversi/game';
 | 
				
			||||||
 | 
					import { ILocalUser } from '../../../../../../models/user';
 | 
				
			||||||
 | 
					import getParams from '../../../../get-params';
 | 
				
			||||||
 | 
					import { publishReversiGameStream } from '../../../../../../stream';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const meta = {
 | 
				
			||||||
 | 
						desc: {
 | 
				
			||||||
 | 
							ja: '指定したリバーシの対局で投了します。'
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						requireCredential: true,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params: {
 | 
				
			||||||
 | 
							gameId: $.type(ID).optional.note({
 | 
				
			||||||
 | 
								desc: {
 | 
				
			||||||
 | 
									ja: '投了したい対局'
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default (params: any, user: ILocalUser) => new Promise(async (res, rej) => {
 | 
				
			||||||
 | 
						const [ps, psErr] = getParams(meta, params);
 | 
				
			||||||
 | 
						if (psErr) return rej(psErr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const game = await ReversiGame.findOne({ _id: ps.gameId });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (game == null) {
 | 
				
			||||||
 | 
							return rej('game not found');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (game.isEnded) {
 | 
				
			||||||
 | 
							return rej('this game is already ended');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!game.user1Id.equals(user._id) && !game.user2Id.equals(user._id)) {
 | 
				
			||||||
 | 
							return rej('access denied');
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const winnerId = game.user1Id.equals(user._id) ? game.user2Id : game.user1Id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						await ReversiGame.update({
 | 
				
			||||||
 | 
							_id: game._id
 | 
				
			||||||
 | 
						}, {
 | 
				
			||||||
 | 
							$set: {
 | 
				
			||||||
 | 
								surrendered: user._id,
 | 
				
			||||||
 | 
								isEnded: true,
 | 
				
			||||||
 | 
								winnerId: winnerId
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						publishReversiGameStream(game._id, 'ended', {
 | 
				
			||||||
 | 
							winnerId: winnerId,
 | 
				
			||||||
 | 
							game: await pack(game._id, user)
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						res();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue