card_svr/src/robot/RobotClient.ts

239 lines
6.9 KiB
TypeScript

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<String, Card>;
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<number>, 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<number>, 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);
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);
}
}
}