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: "{}を待っています"
 | 
			
		||||
    cancel: "キャンセル"
 | 
			
		||||
 | 
			
		||||
common/views/components/games/reversi/reversi.game.vue:
 | 
			
		||||
  surrender: "投了"
 | 
			
		||||
  surrendered: "投了により"
 | 
			
		||||
 | 
			
		||||
common/views/components/games/reversi/reversi.index.vue:
 | 
			
		||||
  title: "Misskey Reversi"
 | 
			
		||||
  sub-title: "他のMisskeyユーザーとリバーシで対戦しよう"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
<template>
 | 
			
		||||
<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">
 | 
			
		||||
		<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="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">
 | 
			
		||||
			<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>
 | 
			
		||||
		</p>
 | 
			
		||||
	</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>
 | 
			
		||||
 | 
			
		||||
	<div class="actions" v-if="!game.isEnded && iAmPlayer">
 | 
			
		||||
		<form-button @click="surrender">%i18n:@surrender%</form-button>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
	<div class="player" v-if="game.isEnded">
 | 
			
		||||
		<el-button-group>
 | 
			
		||||
			<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;
 | 
			
		||||
			return this.game.user1Id == this.$store.state.i.id || this.game.user2Id == this.$store.state.i.id;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		myColor(): Color {
 | 
			
		||||
			if (!this.iAmPlayer) return null;
 | 
			
		||||
			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;
 | 
			
		||||
			return false;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		opColor(): Color {
 | 
			
		||||
			if (!this.iAmPlayer) return null;
 | 
			
		||||
			return this.myColor === true ? false : true;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		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 === true) {
 | 
			
		||||
				return this.game.black == 1 ? this.game.user1 : this.game.user2;
 | 
			
		||||
| 
						 | 
				
			
			@ -104,11 +116,13 @@ export default Vue.extend({
 | 
			
		|||
				return null;
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		isMyTurn(): boolean {
 | 
			
		||||
			if (!this.iAmPlayer) return false;
 | 
			
		||||
			if (this.turnUser == null) return false;
 | 
			
		||||
			return this.turnUser.id == this.$store.state.i.id;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		cellsStyle(): any {
 | 
			
		||||
			return {
 | 
			
		||||
				'grid-template-rows': `repeat(${this.game.settings.map.length}, 1fr)`,
 | 
			
		||||
| 
						 | 
				
			
			@ -165,11 +179,13 @@ export default Vue.extend({
 | 
			
		|||
	mounted() {
 | 
			
		||||
		this.connection.on('set', this.onSet);
 | 
			
		||||
		this.connection.on('rescue', this.onRescue);
 | 
			
		||||
		this.connection.on('ended', this.onEnded);
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	beforeDestroy() {
 | 
			
		||||
		this.connection.off('set', this.onSet);
 | 
			
		||||
		this.connection.off('rescue', this.onRescue);
 | 
			
		||||
		this.connection.off('ended', this.onEnded);
 | 
			
		||||
 | 
			
		||||
		clearInterval(this.pollingClock);
 | 
			
		||||
	},
 | 
			
		||||
| 
						 | 
				
			
			@ -215,6 +231,10 @@ export default Vue.extend({
 | 
			
		|||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		onEnded(x) {
 | 
			
		||||
			this.game = x.game;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		checkEnd() {
 | 
			
		||||
			this.game.isEnded = this.o.isEnded;
 | 
			
		||||
			if (this.game.isEnded) {
 | 
			
		||||
| 
						 | 
				
			
			@ -250,6 +270,12 @@ export default Vue.extend({
 | 
			
		|||
 | 
			
		||||
			this.checkEnd();
 | 
			
		||||
			this.$forceUpdate();
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		surrender() {
 | 
			
		||||
			(this as any).api('games/reversi/games/surrender', {
 | 
			
		||||
				gameId: this.game.id
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
| 
						 | 
				
			
			@ -265,6 +291,9 @@ root(isDark)
 | 
			
		|||
		padding 8px
 | 
			
		||||
		border-bottom dashed 1px isDark ? #4c5761 : #c4cdd4
 | 
			
		||||
 | 
			
		||||
		a
 | 
			
		||||
			color inherit
 | 
			
		||||
 | 
			
		||||
	> .board
 | 
			
		||||
		width calc(100% - 16px)
 | 
			
		||||
		max-width 500px
 | 
			
		||||
| 
						 | 
				
			
			@ -381,6 +410,9 @@ root(isDark)
 | 
			
		|||
		margin 0
 | 
			
		||||
		padding 16px 0
 | 
			
		||||
 | 
			
		||||
	> .actions
 | 
			
		||||
		padding-bottom 16px
 | 
			
		||||
 | 
			
		||||
	> .player
 | 
			
		||||
		padding-bottom 32px
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,6 +25,7 @@ export interface IReversiGame {
 | 
			
		|||
	isStarted: boolean;
 | 
			
		||||
	isEnded: boolean;
 | 
			
		||||
	winnerId: mongo.ObjectID;
 | 
			
		||||
	surrendered: mongo.ObjectID;
 | 
			
		||||
	logs: Array<{
 | 
			
		||||
		at: Date;
 | 
			
		||||
		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