import {Client} from "colyseus"; import {ClientState, ISendOptions} from "colyseus/lib/transport/Transport"; import {EventEmitter} from 'events'; import {assistLog as log} from '../common/Debug'; import {CardGameState} from "../rooms/schema/CardGameState"; import {GameStateConst} from "../constants/GameStateConst"; import {Card} from "../rooms/schema/Card"; import {Player} from "../rooms/schema/Player"; import assistantUtil from "../utils/assistant.util"; import {wait} from "../decorators/cfg"; /** * 服务端辅助机器人 * 每个player加入房间后, 生成一个辅助机器人, 同时将active置为false * 玩家掉线后, 将对应的机器人active置为true, 机器人开始接手 * 玩家重连后, 将对应的机器人active置为false, 机器人停止工作, 玩家接手 * TODO: 根据客户端指令来关闭开启辅助机器人 */ export class RobotClient implements Client { id: string; readyState: number; ref: EventEmitter; sessionId: string; selfPlayer: Player; state: ClientState; svrstate: CardGameState; myTurn: boolean = false; cards: Map; onMessageHandlers: {[id: string]: (client: Client, message: any) => void} = {}; listenerState: any; listenerTurn: any; active: boolean = false; constructor(sessionId: string, state: CardGameState, onMessageHandlers: {[id: string]: (client: Client, message: any) => void}) { this.sessionId = sessionId; this.svrstate = state; this.selfPlayer = this.svrstate.players.get(sessionId); this.ref = new EventEmitter(); this.onMessageHandlers = onMessageHandlers; this.addListeners(); log(`new robot with session: ${sessionId}`); } addListeners() { this.listenerState = this.svrstate.listen('gameState', this.gameSateUpdate.bind(this)); this.listenerTurn = this.svrstate.listen('currentTurn', this.gameTurnUpdate.bind(this)); } close(code?: number, data?: string): void { } enqueueRaw(data: ArrayLike, options?: ISendOptions): void { // log('enqueueRaw:' + data.length); } error(code: number, message?: string): void { } leave(code?: number, data?: string): void { this.listenerState && this.listenerState(); this.listenerTurn && this.listenerTurn(); } raw(data: ArrayLike, options?: ISendOptions): void { } private gameSateUpdate(currentValue: number, previousValue: number) { let self = this; if (!this.active) { return; } log(`server game state change: ${currentValue}, pre value: ${previousValue}`); switch (currentValue) { case GameStateConst.CHANGE_HERO: self.selectHero(); break; case GameStateConst.STATE_CHANGE_CARD: self.changeCard(); break; case GameStateConst.STATE_BEGIN_EAT: if (!self.myTurn) { self.eatOrGiveUp(); } break; case GameStateConst.STATE_ROUND_RESULT: break; } } private gameTurnUpdate(currentValue: string, previousValue: string) { let self = this; self.myTurn = currentValue === self.sessionId; if (!this.active) { return; } log(`server turn change: ${currentValue}, pre value: ${previousValue}`); if (self.myTurn) { self.discard(); } } public send(messageOrType: any, messageOrOptions?: any | ISendOptions, options?: ISendOptions) { // log(`receive server msg: ${messageOrType}, ${messageOrOptions}`); let self = this; if (!this.active) { return; } let data = messageOrOptions; switch (messageOrType) { case 'draw_card_s2c': break; case 'player_ready_s2c': break; case 'steal_card_s2c': break; case 'eat_card_s2c': if (data.errcode == 0 && data.player == self.sessionId) { self.selectPet(); } break; } } private reply(messageType: string, message: any) { if (this.onMessageHandlers[messageType]) { this.onMessageHandlers[messageType](this, message); } else if (this.onMessageHandlers['*']) { (this.onMessageHandlers['*'] as any)(this, messageType, message); } } // >>>>>>>>>>>>>>>>>> begin /** * 出牌 * @private */ @wait('maxDiscardTime') private async discard() { let self = this; let cardArr = [...self.selfPlayer.cards.values()]; let cards = assistantUtil.checkTriple(cardArr); if (!cards) { return; } let cardIds = cards.map(o => o.id); log(`discard: ${self.sessionId} ${cardIds}`); self.reply('discard_card_c2s', { cards: cardIds }); } /** * 吃牌或者放弃 * @private */ @wait('maxEatTime') private async eatOrGiveUp() { let targetCard = [...this.svrstate.cards.values()][0]; let cardArr = [...this.selfPlayer.cards.values()]; let tmpCards = assistantUtil.checkTriple(cardArr, targetCard); let next = this.giveup.bind(this); if (tmpCards.length > 1 && targetCard.type === 1) { let cardIds: number[] = []; for (let card of tmpCards) { if (card.id !== targetCard.id) { cardIds.push(card.id); } } next = this.eatCard.bind(this, cardIds, targetCard.id); } next.apply(this); } /** * 吃牌 * @param cardIds * @param target * @private */ private eatCard(cardIds: number[], target: number) { log(`${this.sessionId} 吃牌 ${cardIds} -> ${target}`); this.reply('eat_card_c2s', { cards: cardIds, target }); } /** * 放弃吃牌 * @private */ private giveup () { this.reply('give_up_eat_c2s', {}); } /** * 开局装备 * @private */ private async setReady() { this.reply('play_ready_c2s', ''); } /** * 开局选择英雄 * @private */ @wait('pickHeroTime') private async selectHero() { let data = assistantUtil.randomHero(); this.reply('select_hero_c2s', data); } /** * 开局换牌 * @private */ @wait('cardChangeTime') private changeCard() { let cardIds: number[] = []; this.reply('change_card_c2s', { cards: cardIds }); } /** * 选择一个法术或者一个随从 * @private */ @wait('playerActTime') private async selectPet() { let data = await assistantUtil.selectPet(this.selfPlayer, this.svrstate); if (data) { this.reply('select_pet_c2s', data); } } }