diff --git a/locales/index.d.ts b/locales/index.d.ts index 885b316634..ed9e9f9bb9 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -9803,7 +9803,7 @@ export interface Locale extends ILocale { */ "kokushi": string; /** - * 国士無双13面待ち + * 国士無双十三面待 */ "kokushi-13": string; /** @@ -9811,7 +9811,7 @@ export interface Locale extends ILocale { */ "suanko": string; /** - * 四暗刻単騎待ち + * 四暗刻単騎待 */ "suanko-tanki": string; /** diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 8935bc9d7a..87e7c7566c 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -2610,9 +2610,9 @@ _mahjong: "ryampeko": "ニ盃口" "chinitsu": "清一色" "kokushi": "国士無双" - "kokushi-13": "国士無双13面待ち" + "kokushi-13": "国士無双十三面待" "suanko": "四暗刻" - "suanko-tanki": "四暗刻単騎待ち" + "suanko-tanki": "四暗刻単騎待" "daisangen": "大三元" "tsuiso": "字一色" "shosushi": "小四喜" diff --git a/packages/backend/src/core/MahjongService.ts b/packages/backend/src/core/MahjongService.ts index 9a6422340e..f72e4731d6 100644 --- a/packages/backend/src/core/MahjongService.ts +++ b/packages/backend/src/core/MahjongService.ts @@ -321,11 +321,11 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit { this.waitForTurn(room, res.turn, engine); break; case 'ponned': - this.globalEventService.publishMahjongRoomStream(room.id, 'ponned', { caller: res.caller, callee: res.callee, tile: res.tile }); + this.globalEventService.publishMahjongRoomStream(room.id, 'ponned', { caller: res.caller, callee: res.callee, tiles: res.tiles }); this.waitForTurn(room, res.turn, engine); break; case 'kanned': - this.globalEventService.publishMahjongRoomStream(room.id, 'kanned', { caller: res.caller, callee: res.callee, tile: res.tile, rinsyan: res.rinsyan }); + this.globalEventService.publishMahjongRoomStream(room.id, 'kanned', { caller: res.caller, callee: res.callee, tiles: res.tiles, rinsyan: res.rinsyan }); this.waitForTurn(room, res.turn, engine); break; case 'ronned': @@ -501,8 +501,10 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit { await this.clearTurnWaitingTimer(room.id); const res = engine.commit_ankan(myHouse, tile); + room.gameState = engine.state; + await this.saveRoom(room); - this.globalEventService.publishMahjongRoomStream(room.id, 'ankanned', { }); + this.globalEventService.publishMahjongRoomStream(room.id, 'ankanned', { house: myHouse, tiles: res.tiles, rinsyan: res.rinsyan }); this.waitForTurn(room, myHouse, engine); } @@ -519,8 +521,10 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit { await this.clearTurnWaitingTimer(room.id); const res = engine.commit_kakan(myHouse, tile); + room.gameState = engine.state; + await this.saveRoom(room); - this.globalEventService.publishMahjongRoomStream(room.id, 'kakanned', { }); + this.globalEventService.publishMahjongRoomStream(room.id, 'kakanned', { house: myHouse, tiles: res.tiles, rinsyan: res.rinsyan, from: res.from }); } @bindThis @@ -535,6 +539,8 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit { await this.clearTurnWaitingTimer(room.id); const res = engine.commit_tsumoHora(myHouse); + room.gameState = engine.state; + await this.saveRoom(room); this.globalEventService.publishMahjongRoomStream(room.id, 'tsumoHora', { house: myHouse, handTiles: res.handTiles, tsumoTile: res.tsumoTile }); } diff --git a/packages/frontend/src/pages/mahjong/room.game.vue b/packages/frontend/src/pages/mahjong/room.game.vue index e1ab9bcff7..c1f344831a 100644 --- a/packages/frontend/src/pages/mahjong/room.game.vue +++ b/packages/frontend/src/pages/mahjong/room.game.vue @@ -520,7 +520,7 @@ function onStreamPonned(log) { // return; //} - engine.value.commit_pon(log.caller, log.callee); + engine.value.commit_pon(log.caller, log.callee, log.tiles); triggerRef(engine); ponSerifHouses[log.caller] = true; @@ -531,6 +531,42 @@ function onStreamPonned(log) { myTurnTimerRmain.value = room.value.timeLimitForEachTurn; } +function onStreamKanned(log) { + console.log('onStreamKanned', log); + + engine.value.commit_kan(log.caller, log.callee, log.tiles, log.rinsyan); + triggerRef(engine); + + kanSerifHouses[log.caller] = true; + window.setTimeout(() => { + kanSerifHouses[log.caller] = false; + }, 2000); +} + +function onStreamKakanned(log) { + console.log('onStreamKakanned', log); + + engine.value.commit_kakan(log.house, log.tiles, log.rinsyan); + triggerRef(engine); + + kanSerifHouses[log.caller] = true; + window.setTimeout(() => { + kanSerifHouses[log.caller] = false; + }, 2000); +} + +function onStreamAnkanned(log) { + console.log('onStreamAnkanned', log); + + engine.value.commit_ankan(log.house, log.tiles, log.rinsyan); + triggerRef(engine); + + kanSerifHouses[log.caller] = true; + window.setTimeout(() => { + kanSerifHouses[log.caller] = false; + }, 2000); +} + function onStreamRonned(log) { console.log('onStreamRonned', log); @@ -579,6 +615,9 @@ onMounted(() => { props.connection.on('tsumo', onStreamTsumo); props.connection.on('dahaiAndTsumo', onStreamDahaiAndTsumo); props.connection.on('ponned', onStreamPonned); + props.connection.on('kanned', onStreamKanned); + props.connection.on('kakanned', onStreamKakanned); + props.connection.on('ankanned', onStreamAnkanned); props.connection.on('ronned', onStreamRonned); props.connection.on('tsumoHora', onStreamTsumoHora); } @@ -590,6 +629,9 @@ onActivated(() => { props.connection.on('tsumo', onStreamTsumo); props.connection.on('dahaiAndTsumo', onStreamDahaiAndTsumo); props.connection.on('ponned', onStreamPonned); + props.connection.on('kanned', onStreamKanned); + props.connection.on('kakanned', onStreamKakanned); + props.connection.on('ankanned', onStreamAnkanned); props.connection.on('ronned', onStreamRonned); props.connection.on('tsumoHora', onStreamTsumoHora); } @@ -601,6 +643,9 @@ onDeactivated(() => { props.connection.off('tsumo', onStreamTsumo); props.connection.off('dahaiAndTsumo', onStreamDahaiAndTsumo); props.connection.off('ponned', onStreamPonned); + props.connection.off('kanned', onStreamKanned); + props.connection.off('kakanned', onStreamKakanned); + props.connection.off('ankanned', onStreamAnkanned); props.connection.off('ronned', onStreamRonned); props.connection.off('tsumoHora', onStreamTsumoHora); } @@ -612,6 +657,9 @@ onUnmounted(() => { props.connection.off('tsumo', onStreamTsumo); props.connection.off('dahaiAndTsumo', onStreamDahaiAndTsumo); props.connection.off('ponned', onStreamPonned); + props.connection.off('kanned', onStreamKanned); + props.connection.off('kakanned', onStreamKakanned); + props.connection.off('ankanned', onStreamAnkanned); props.connection.off('ronned', onStreamRonned); props.connection.off('tsumoHora', onStreamTsumoHora); } diff --git a/packages/misskey-mahjong/src/engine.master.ts b/packages/misskey-mahjong/src/engine.master.ts index d41f1ffbc4..8a9cb1993b 100644 --- a/packages/misskey-mahjong/src/engine.master.ts +++ b/packages/misskey-mahjong/src/engine.master.ts @@ -405,7 +405,8 @@ export class MasterGameEngine { const pon = this.state.huros[house].find(h => h.type === 'pon' && $type(h.tiles[0]) === $type(tile)); if (pon == null) throw new Error('No such pon'); this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(tile), 1); - this.state.huros[house].push({ type: 'minkan', tiles: [...pon.tiles, tile], from: pon.from }); + const tiles = [tile, ...pon.tiles]; + this.state.huros[house].push({ type: 'minkan', tiles: tiles, from: pon.from }); this.state.activatedDorasCount++; @@ -413,6 +414,8 @@ export class MasterGameEngine { return { rinsyan, + tiles, + from: pon.from, }; } @@ -429,7 +432,8 @@ export class MasterGameEngine { this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t2), 1); this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t3), 1); this.state.handTiles[house].splice(this.state.handTiles[house].indexOf(t4), 1); - this.state.huros[house].push({ type: 'ankan', tiles: [t1, t2, t3, t4] }); + const tiles = [t1, t2, t3, t4]; + this.state.huros[house].push({ type: 'ankan', tiles: tiles }); this.state.activatedDorasCount++; @@ -437,6 +441,7 @@ export class MasterGameEngine { return { rinsyan, + tiles, }; } @@ -502,19 +507,20 @@ export class MasterGameEngine { } else if (kan != null && answers.kan) { // 大明槓 + const tile = this.state.hoTiles[kan.callee].pop()!; const t1 = this.state.handTiles[kan.caller].filter(t => $type(t) === $type(tile)).at(0); if (t1 == null) throw new Error('No such tile'); const t2 = this.state.handTiles[kan.caller].filter(t => $type(t) === $type(tile)).at(1); if (t2 == null) throw new Error('No such tile'); const t3 = this.state.handTiles[kan.caller].filter(t => $type(t) === $type(tile)).at(2); if (t3 == null) throw new Error('No such tile'); - const tile = this.state.hoTiles[kan.callee].pop()!; this.state.handTiles[kan.caller].splice(this.state.handTiles[kan.caller].indexOf(t1), 1); this.state.handTiles[kan.caller].splice(this.state.handTiles[kan.caller].indexOf(t2), 1); this.state.handTiles[kan.caller].splice(this.state.handTiles[kan.caller].indexOf(t3), 1); - this.state.huros[kan.caller].push({ type: 'minkan', tiles: [tile, t1, t2, t3], from: kan.callee }); + const tiles = [tile, t1, t2, t3]; + this.state.huros[kan.caller].push({ type: 'minkan', tiles: tiles, from: kan.callee }); this.state.activatedDorasCount++; @@ -526,21 +532,22 @@ export class MasterGameEngine { type: 'kanned' as const, caller: kan.caller, callee: kan.callee, - tile, + tiles: tiles, rinsyan, turn: this.state.turn, }; } else if (pon != null && answers.pon) { + const tile = this.state.hoTiles[pon.callee].pop()!; const t1 = this.state.handTiles[pon.caller].filter(t => $type(t) === $type(tile)).at(0); if (t1 == null) throw new Error('No such tile'); const t2 = this.state.handTiles[pon.caller].filter(t => $type(t) === $type(tile)).at(1); if (t2 == null) throw new Error('No such tile'); - const tile = this.state.hoTiles[pon.callee].pop()!; this.state.handTiles[pon.caller].splice(this.state.handTiles[pon.caller].indexOf(t1), 1); this.state.handTiles[pon.caller].splice(this.state.handTiles[pon.caller].indexOf(t2), 1); - this.state.huros[pon.caller].push({ type: 'pon', tiles: [tile, t1, t2], from: pon.callee }); + const tiles = [tile, t1, t2]; + this.state.huros[pon.caller].push({ type: 'pon', tiles: tiles, from: pon.callee }); this.state.turn = pon.caller; @@ -548,7 +555,7 @@ export class MasterGameEngine { type: 'ponned' as const, caller: pon.caller, callee: pon.callee, - tile, + tiles: tiles, turn: this.state.turn, }; } else if (cii != null && answers.cii) { diff --git a/packages/misskey-mahjong/src/engine.player.ts b/packages/misskey-mahjong/src/engine.player.ts index 91ec136424..01b48c2bf3 100644 --- a/packages/misskey-mahjong/src/engine.player.ts +++ b/packages/misskey-mahjong/src/engine.player.ts @@ -162,11 +162,6 @@ export class PlayerGameEngine { } } - public commit_kakan(house: House, tile: TileId) { - console.log('commit_kakan', this.state.turn, house, tile); - if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError(); - } - public commit_tsumoHora(house: House, handTiles: TileId[], tsumoTile: TileId): KyokuResult { console.log('commit_tsumoHora', this.state.turn, house); @@ -270,6 +265,38 @@ export class PlayerGameEngine { this.state.turn = caller; } + /** + * 大明槓します + * @param caller 大明槓した人 + * @param callee 牌を捨てた人 + */ + public commit_kan(caller: House, callee: House, tiles: TileId[], rinsyan: TileId) { + this.state.canKan = null; + + this.state.hoTiles[callee].pop(); + if (caller === this.myHouse) { + if (this.myHandTiles.includes(tiles[0])) this.myHandTiles.splice(this.myHandTiles.indexOf(tiles[0]), 1); + if (this.myHandTiles.includes(tiles[1])) this.myHandTiles.splice(this.myHandTiles.indexOf(tiles[1]), 1); + if (this.myHandTiles.includes(tiles[2])) this.myHandTiles.splice(this.myHandTiles.indexOf(tiles[2]), 1); + if (this.myHandTiles.includes(tiles[3])) this.myHandTiles.splice(this.myHandTiles.indexOf(tiles[3]), 1); + } else { + this.state.handTiles[caller].unshift(); + this.state.handTiles[caller].unshift(); + this.state.handTiles[caller].unshift(); + } + this.state.huros[caller].push({ type: 'minkan', tiles: tiles, from: callee }); + + this.state.turn = caller; + } + + public commit_kakan(house: House, tiles: TileId[], rinsyan: TileId) { + console.log('commit_kakan', this.state.turn, house, tiles); + } + + public commit_ankan(house: House, tiles: TileId[], rinsyan: TileId) { + console.log('commit_kakan', this.state.turn, house, tiles); + } + public commit_nop() { this.state.canRon = null; this.state.canPon = null;