diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts
index 629ef1b01d..dcf643a7a5 100644
--- a/packages/backend/src/core/GlobalEventService.ts
+++ b/packages/backend/src/core/GlobalEventService.ts
@@ -226,6 +226,8 @@ export interface MahjongRoomEventTypes {
target: Mahjong.Common.House;
tile: Mahjong.Common.Tile;
};
+ endKyoku: {
+ };
}
//#endregion
diff --git a/packages/backend/src/core/MahjongService.ts b/packages/backend/src/core/MahjongService.ts
index 9e962dc22b..ddc2d2cc56 100644
--- a/packages/backend/src/core/MahjongService.ts
+++ b/packages/backend/src/core/MahjongService.ts
@@ -300,7 +300,7 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
this.waitForTurn(room, userId, engine);
} else if (res.type === 'kanned') {
// TODO
- } else if (res.type === 'ronned') {
+ } else if (res.type === 'endKyoku') {
// TODO
}
}
@@ -415,7 +415,7 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
this.answer(room, engine, currentAnswers);
return;
}
- }, 2000);
+ }, 1000);
this.globalEventService.publishMahjongRoomStream(room.id, 'dahai', { house: house, tile });
} else {
@@ -445,6 +445,38 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
await this.dahai(room, engine, myHouse, tile);
}
+ @bindThis
+ public async commit_kakan(roomId: MiMahjongGame['id'], user: MiUser) {
+ const room = await this.getRoom(roomId);
+ if (room == null) return;
+ if (room.gameState == null) return;
+
+ const engine = new Mahjong.MasterGameEngine(room.gameState);
+ const myHouse = user.id === room.user1Id ? engine.state.user1House : user.id === room.user2Id ? engine.state.user2House : user.id === room.user3Id ? engine.state.user3House : engine.state.user4House;
+
+ await this.clearTurnWaitingTimer(room.id);
+
+ const res = engine.commit_kakan(myHouse);
+
+ this.globalEventService.publishMahjongRoomStream(room.id, 'kakanned', { });
+ }
+
+ @bindThis
+ public async commit_hora(roomId: MiMahjongGame['id'], user: MiUser) {
+ const room = await this.getRoom(roomId);
+ if (room == null) return;
+ if (room.gameState == null) return;
+
+ const engine = new Mahjong.MasterGameEngine(room.gameState);
+ const myHouse = user.id === room.user1Id ? engine.state.user1House : user.id === room.user2Id ? engine.state.user2House : user.id === room.user3Id ? engine.state.user3House : engine.state.user4House;
+
+ await this.clearTurnWaitingTimer(room.id);
+
+ const res = engine.commit_hora(myHouse);
+
+ this.globalEventService.publishMahjongRoomStream(room.id, 'horad', { });
+ }
+
@bindThis
public async commit_ron(roomId: MiMahjongGame['id'], user: MiUser) {
const room = await this.getRoom(roomId);
@@ -504,7 +536,7 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
}
/**
- * プレイヤーの行動を待つ(打牌もしくはツモ和了)
+ * プレイヤーの行動(打牌、加槓、ツモ和了)を待つ
* 制限時間が過ぎたらツモ切り
* NOTE: 時間切れチェックが行われたときにタイミングによっては次のwaitingが始まっている場合があることを考慮し、Setに一意のIDを格納する構造としている
* @param room
@@ -536,7 +568,7 @@ export class MahjongService implements OnApplicationShutdown, OnModuleInit {
}
/**
- * プレイヤーが打牌またはツモ和了したら呼ぶ
+ * プレイヤーが行動(打牌、加槓、ツモ和了)したら呼ぶ
* @param roomId
*/
@bindThis
diff --git a/packages/backend/src/server/api/stream/channels/mahjong-room.ts b/packages/backend/src/server/api/stream/channels/mahjong-room.ts
index e7a0c81abd..51f953443a 100644
--- a/packages/backend/src/server/api/stream/channels/mahjong-room.ts
+++ b/packages/backend/src/server/api/stream/channels/mahjong-room.ts
@@ -39,6 +39,7 @@ class MahjongRoomChannel extends Channel {
case 'updateSettings': this.updateSettings(body.key, body.value); break;
case 'addAi': this.addAi(); break;
case 'dahai': this.dahai(body.tile, body.riichi); break;
+ case 'hora': this.hora(); break;
case 'ron': this.ron(); break;
case 'pon': this.pon(); break;
case 'nop': this.nop(); break;
@@ -74,6 +75,13 @@ class MahjongRoomChannel extends Channel {
this.mahjongService.commit_dahai(this.roomId!, this.user, tile, riichi);
}
+ @bindThis
+ private async hora() {
+ if (this.user == null) return;
+
+ this.mahjongService.commit_hora(this.roomId!, this.user);
+ }
+
@bindThis
private async ron() {
if (this.user == null) return;
diff --git a/packages/frontend/src/pages/mahjong/room.game.vue b/packages/frontend/src/pages/mahjong/room.game.vue
index 9105992e2d..68e9542d74 100644
--- a/packages/frontend/src/pages/mahjong/room.game.vue
+++ b/packages/frontend/src/pages/mahjong/room.game.vue
@@ -79,7 +79,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Ron
Pon
Skip
- Tsumo
+ Tsumo
Riichi
@@ -227,6 +227,26 @@ function riichi() {
});
}
+function kakan() {
+ if (!isMyTurn.value) return;
+
+ engine.value.commit_kakan(engine.value.myHouse);
+ triggerRef(engine);
+
+ props.connection!.send('kakan', {
+ });
+}
+
+function hora() {
+ if (!isMyTurn.value) return;
+
+ engine.value.commit_hora(engine.value.myHouse);
+ triggerRef(engine);
+
+ props.connection!.send('hora', {
+ });
+}
+
function ron() {
engine.value.commit_ron(engine.value.state.canRonSource, engine.value.myHouse);
triggerRef(engine);
@@ -236,9 +256,6 @@ function ron() {
}
function pon() {
- engine.value.commit_pon(engine.value.state.canPonSource, engine.value.myHouse);
- triggerRef(engine);
-
props.connection!.send('pon', {
});
}
@@ -336,8 +353,6 @@ function onStreamPonned(log) {
// return;
//}
- if (log.target === engine.value.myHouse) return;
-
engine.value.commit_pon(log.source, log.target);
triggerRef(engine);
diff --git a/packages/misskey-mahjong/src/engine.master.ts b/packages/misskey-mahjong/src/engine.master.ts
index b581ba9191..539b1d25d9 100644
--- a/packages/misskey-mahjong/src/engine.master.ts
+++ b/packages/misskey-mahjong/src/engine.master.ts
@@ -13,6 +13,10 @@ export type MasterState = {
user2House: House;
user3House: House;
user4House: House;
+
+ round: 'e' | 's' | 'w' | 'n';
+ kyoku: number;
+
tiles: Tile[];
/**
@@ -122,6 +126,8 @@ export class MasterGameEngine {
user2House: 's',
user3House: 'w',
user4House: 'n',
+ round: 'e',
+ kyoku: 1,
tiles,
handTiles: {
e: eHandTiles,
@@ -198,6 +204,12 @@ export class MasterGameEngine {
}
}
+ private endKyoku() {
+ const newState = MasterGameEngine.createInitialState();
+ newState.kyoku = this.state.kyoku + 1;
+ newState.points = this.state.points;
+ }
+
public commit_dahai(house: House, tile: Tile, riichi = false) {
if (this.state.turn !== house) throw new Error('Not your turn');
@@ -311,6 +323,21 @@ export class MasterGameEngine {
};
}
+ public commit_kakan(house: House) {
+ }
+
+ /**
+ * ツモ和了
+ * @param house
+ */
+ public commit_hora(house: House) {
+ if (this.state.turn !== house) throw new Error('Not your turn');
+
+ const yakus = Utils.getYakus(this.state.handTiles[house], null);
+
+ this.endKyoku();
+ }
+
public commit_resolveCallAndRonInterruption(answers: {
pon: boolean;
cii: boolean;
@@ -328,7 +355,10 @@ export class MasterGameEngine {
if (this.state.ronAsking != null && answers.ron.length > 0) {
// TODO
- return;
+ this.endKyoku();
+ return {
+ type: 'endKyoku',
+ };
}
if (this.state.kanAsking != null && answers.kan) {
@@ -401,6 +431,8 @@ export class MasterGameEngine {
user2House: this.state.user2House,
user3House: this.state.user3House,
user4House: this.state.user4House,
+ round: this.state.round,
+ kyoku: this.state.kyoku,
tilesCount: this.state.tiles.length,
handTiles: {
e: house === 'e' ? this.state.handTiles.e : this.state.handTiles.e.map(() => null),
diff --git a/packages/misskey-mahjong/src/engine.player.ts b/packages/misskey-mahjong/src/engine.player.ts
index f1c9a6cb3a..3d2881048f 100644
--- a/packages/misskey-mahjong/src/engine.player.ts
+++ b/packages/misskey-mahjong/src/engine.player.ts
@@ -12,6 +12,10 @@ export type PlayerState = {
user2House: House;
user3House: House;
user4House: House;
+
+ round: 'e' | 's' | 'w' | 'n';
+ kyoku: number;
+
tilesCount: number;
/**
@@ -130,6 +134,16 @@ export class PlayerGameEngine {
}
}
+ public commit_kakan(house: House, tile: Tile) {
+ console.log('commit_kakan', this.state.turn, house, tile);
+ if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError();
+ }
+
+ public commit_hora(house: House) {
+ console.log('commit_hora', this.state.turn, house);
+ if (this.state.turn !== house) throw new PlayerGameEngine.InvalidOperationError();
+ }
+
/**
* ロンします
* @param source 牌を捨てた人