239 lines
6.9 KiB
TypeScript
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);
|
|
}
|
|
}
|
|
|
|
}
|