This commit is contained in:
syuilo 2018-03-11 07:07:17 +09:00
parent f838b482bf
commit 98f09ad16c
3 changed files with 253 additions and 55 deletions

View file

@ -33,6 +33,8 @@ export interface IGame {
can_put_everywhere: boolean; can_put_everywhere: boolean;
looped_board: boolean; looped_board: boolean;
}; };
form1: any;
form2: any;
} }
/** /**

View file

@ -31,6 +31,21 @@ export default function(request: websocket.request, connection: websocket.connec
updateSettings(msg.settings); updateSettings(msg.settings);
break; break;
case 'init-form':
if (msg.body == null) return;
initForm(msg.body);
break;
case 'update-form':
if (msg.id == null || msg.value === undefined) return;
updateForm(msg.id, msg.value);
break;
case 'message':
if (msg.body == null) return;
message(msg.body);
break;
case 'set': case 'set':
if (msg.pos == null) return; if (msg.pos == null) return;
set(msg.pos); set(msg.pos);
@ -55,6 +70,67 @@ export default function(request: websocket.request, connection: websocket.connec
publishOthelloGameStream(gameId, 'update-settings', settings); publishOthelloGameStream(gameId, 'update-settings', settings);
} }
async function initForm(form) {
const game = await Game.findOne({ _id: gameId });
if (game.is_started) return;
if (!game.user1_id.equals(user._id) && !game.user2_id.equals(user._id)) return;
const set = game.user1_id.equals(user._id) ? {
form1: form
} : {
form2: form
};
await Game.update({ _id: gameId }, {
$set: set
});
publishOthelloGameStream(gameId, 'init-form', {
user_id: user._id,
form
});
}
async function updateForm(id, value) {
const game = await Game.findOne({ _id: gameId });
if (game.is_started) return;
if (!game.user1_id.equals(user._id) && !game.user2_id.equals(user._id)) return;
const form = game.user1_id.equals(user._id) ? game.form2 : game.form1;
const item = form.find(i => i.id == id);
if (item == null) return;
item.value = value;
const set = game.user1_id.equals(user._id) ? {
form2: form
} : {
form1: form
};
await Game.update({ _id: gameId }, {
$set: set
});
publishOthelloGameStream(gameId, 'update-form', {
user_id: user._id,
id,
value
});
}
async function message(message) {
message.id = Math.random();
publishOthelloGameStream(gameId, 'message', {
user_id: user._id,
message
});
}
async function accept(accept: boolean) { async function accept(accept: boolean) {
const game = await Game.findOne({ _id: gameId }); const game = await Game.findOne({ _id: gameId });

View file

@ -2,37 +2,77 @@
<div class="root"> <div class="root">
<header><b>{{ game.user1.name }}</b> vs <b>{{ game.user2.name }}</b></header> <header><b>{{ game.user1.name }}</b> vs <b>{{ game.user2.name }}</b></header>
<p>ゲームの設定</p> <div>
<p>ゲームの設定</p>
<el-select class="map" v-model="mapName" placeholder="マップを選択" @change="onMapChange"> <el-card class="map">
<el-option label="ランダム" :value="null"/> <div slot="header">
<el-option-group v-for="c in mapCategories" :key="c" :label="c"> <el-select :class="$style.mapSelect" v-model="mapName" placeholder="マップを選択" @change="onMapChange">
<el-option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name"> <el-option label="ランダム" :value="null"/>
<span style="float: left">{{ m.name }}</span> <el-option-group v-for="c in mapCategories" :key="c" :label="c">
<span style="float: right; color: #8492a6; font-size: 13px" v-if="m.author">(by <i>{{ m.author }}</i>)</span> <el-option v-for="m in maps" v-if="m.category == c" :key="m.name" :label="m.name" :value="m.name">
</el-option> <span style="float: left">{{ m.name }}</span>
</el-option-group> <span style="float: right; color: #8492a6; font-size: 13px" v-if="m.author">(by <i>{{ m.author }}</i>)</span>
</el-select> </el-option>
</el-option-group>
</el-select>
</div>
<div :class="$style.board" v-if="game.settings.map != null" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
<div v-for="(x, i) in game.settings.map.join('')"
:class="{ none: x == ' ' }"
@click="onPixelClick(i, x)"
>
<template v-if="x == 'b'">%fa:circle%</template>
<template v-if="x == 'w'">%fa:circle R%</template>
</div>
</div>
</el-card>
<div class="board" v-if="game.settings.map != null" :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }"> <el-card class="bw">
<div v-for="(x, i) in game.settings.map.join('')" <div slot="header">
:class="{ none: x == ' ' }" <span>先手/後手</span>
@click="onPixelClick(i, x)" </div>
>
<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="updateSettings" text="石の少ない方が勝ち(ロセオ)"/>
<mk-switch v-model="game.settings.looped_board" @change="updateSettings" text="ループマップ"/>
<mk-switch v-model="game.settings.can_put_everywhere" @change="updateSettings" text="どこでも置けるモード"/>
<div>
<el-radio v-model="game.settings.bw" label="random" @change="updateSettings">ランダム</el-radio> <el-radio v-model="game.settings.bw" label="random" @change="updateSettings">ランダム</el-radio>
<el-radio v-model="game.settings.bw" :label="1" @change="updateSettings">{{ game.user1.name }}が黒</el-radio> <el-radio v-model="game.settings.bw" :label="1" @change="updateSettings">{{ game.user1.name }}が黒</el-radio>
<el-radio v-model="game.settings.bw" :label="2" @change="updateSettings">{{ game.user2.name }}が黒</el-radio> <el-radio v-model="game.settings.bw" :label="2" @change="updateSettings">{{ game.user2.name }}が黒</el-radio>
</div> </el-card>
<el-card class="rules">
<div slot="header">
<span>ルール</span>
</div>
<mk-switch v-model="game.settings.is_llotheo" @change="updateSettings" text="石の少ない方が勝ち(ロセオ)"/>
<mk-switch v-model="game.settings.looped_board" @change="updateSettings" text="ループマップ"/>
<mk-switch v-model="game.settings.can_put_everywhere" @change="updateSettings" text="どこでも置けるモード"/>
</el-card>
<el-card class="bot-form" v-if="form">
<div slot="header">
<span>Botの設定</span>
</div>
<el-alert v-for="message in messages"
:title="message.text"
:type="message.type"
:key="message.id"
/>
<template v-for="item in form">
<mk-switch v-if="item.type == 'button'" v-model="item.value" :key="item.id" :text="item.label" @change="onChangeForm($event, item)">{{ item.desc || '' }}</mk-switch>
<el-card v-if="item.type == 'radio'" :key="item.id">
<div slot="header">
<span>{{ item.label }}</span>
</div>
<el-radio v-for="(r, i) in item.items" :key="item.id + ':' + i" v-model="item.value" :label="r.value" @change="onChangeForm($event, item)">{{ r.label }}</el-radio>
</el-card>
<el-card v-if="item.type == 'textbox'" :key="item.id">
<div slot="header">
<span>{{ item.label }}</span>
</div>
<el-input v-model="item.value" @change="onChangeForm($event, item)"/>
</el-card>
</template>
</el-card>
</div> </div>
<footer> <footer>
@ -64,7 +104,9 @@ export default Vue.extend({
o: null, o: null,
isLlotheo: false, isLlotheo: false,
mapName: maps.eighteight.name, mapName: maps.eighteight.name,
maps: maps maps: maps,
form: null,
messages: []
}; };
}, },
@ -88,11 +130,56 @@ export default Vue.extend({
created() { created() {
this.connection.on('change-accepts', this.onChangeAccepts); this.connection.on('change-accepts', this.onChangeAccepts);
this.connection.on('update-settings', this.onUpdateSettings); this.connection.on('update-settings', this.onUpdateSettings);
this.connection.on('init-form', this.onInitForm);
this.connection.on('message', this.onMessage);
if (this.game.user1_id != (this as any).os.i.id && this.game.settings.form1) this.form = this.game.settings.form1;
if (this.game.user2_id != (this as any).os.i.id && this.game.settings.form2) this.form = this.game.settings.form2;
// for debugging
if ((this as any).os.i.username == 'test1') {
setTimeout(() => {
this.connection.send({
type: 'init-form',
body: [{
id: 'button1',
type: 'button',
label: 'Enable hoge',
value: false
}, {
id: 'radio1',
type: 'radio',
label: '強さ',
value: 2,
items: [{
label: '弱',
value: 1
}, {
label: '中',
value: 2
}, {
label: '強',
value: 3
}]
}]
});
this.connection.send({
type: 'message',
body: {
text: 'Hey',
type: 'info'
}
});
}, 2000);
}
}, },
beforeDestroy() { beforeDestroy() {
this.connection.off('change-accepts', this.onChangeAccepts); this.connection.off('change-accepts', this.onChangeAccepts);
this.connection.off('update-settings', this.onUpdateSettings); this.connection.off('update-settings', this.onUpdateSettings);
this.connection.off('init-form', this.onInitForm);
this.connection.off('message', this.onMessage);
}, },
methods: { methods: {
@ -135,6 +222,24 @@ export default Vue.extend({
} }
}, },
onInitForm(x) {
if (x.user_id == (this as any).os.i.id) return;
this.form = x.form;
},
onMessage(x) {
if (x.user_id == (this as any).os.i.id) return;
this.messages.unshift(x.message);
},
onChangeForm(v, item) {
this.connection.send({
type: 'update-form',
id: item.id,
value: v
});
},
onMapChange(v) { onMapChange(v) {
if (v == null) { if (v == null) {
this.game.settings.map = null; this.game.settings.map = null;
@ -168,40 +273,21 @@ export default Vue.extend({
.root .root
text-align center text-align center
background #f9f9f9
> header > header
padding 8px padding 8px
border-bottom dashed 1px #c4cdd4 border-bottom dashed 1px #c4cdd4
> .map > div
width 300px padding 0 16px
> .board > .map
display grid > .bw
grid-gap 4px > .rules
width 300px > .bot-form
height 300px max-width 400px
margin 16px auto margin 0 auto 16px auto
> div
background transparent
border solid 2px #ddd
border-radius 6px
overflow hidden
cursor pointer
*
pointer-events none
user-select none
width 100%
height 100%
&.none
border-color transparent
> .rules
max-width 300px
margin 0 auto 32px auto
> footer > footer
position sticky position sticky
@ -213,3 +299,37 @@ export default Vue.extend({
> .status > .status
margin 0 0 16px 0 margin 0 0 16px 0
</style> </style>
<style lang="stylus" module>
.mapSelect
width 100%
.board
display grid
grid-gap 4px
width 300px
height 300px
margin 0 auto
> div
background transparent
border solid 2px #ddd
border-radius 6px
overflow hidden
cursor pointer
*
pointer-events none
user-select none
width 100%
height 100%
&.none
border-color transparent
</style>
<style lang="stylus">
.el-alert__content
position initial !important
</style>