258 lines
6.6 KiB
TypeScript
258 lines
6.6 KiB
TypeScript
import { Client, Room } from 'colyseus.js'
|
|
import { error, robotLog as log, robotLog as debug } from '../common/Debug'
|
|
import { GameStateConst } from '../constants/GameStateConst'
|
|
import { Player } from '../rooms/schema/Player'
|
|
import assistantUtil from '../utils/assistant.util'
|
|
import { delay, wait } from '../decorators/cfg'
|
|
import { RoomOptions } from '../cfg/RoomOptions'
|
|
import { RULE_CANEAT } from '../constants/GameRuleConst'
|
|
|
|
export class Robot {
|
|
host: string
|
|
roomId: string
|
|
room: Room
|
|
sessionId: string
|
|
client: Client
|
|
myTurn: boolean = false
|
|
player: Player
|
|
|
|
constructor(host: string, roomId: string) {
|
|
this.host = host
|
|
this.roomId = roomId
|
|
this.client = new Client(host)
|
|
}
|
|
|
|
async connect() {
|
|
try {
|
|
this.room = await this.client.joinById(this.roomId, {
|
|
rank: 1,
|
|
accountid: 'robot'
|
|
})
|
|
this.addListeners()
|
|
this.sessionId = this.room.sessionId
|
|
this.setReady()
|
|
return this.room.sessionId
|
|
} catch (err) {
|
|
error(`error join room ${ this.host }, ${ this.roomId }`)
|
|
}
|
|
}
|
|
|
|
async reConnect(sessionId: string) {
|
|
try {
|
|
this.room = await this.client.reconnect(this.roomId, sessionId)
|
|
this.addListeners()
|
|
this.sessionId = this.room.sessionId
|
|
return this.room.sessionId
|
|
} catch (err) {
|
|
error(`error reConnect room ${ this.host }, ${ this.roomId }, sessionId: ${ sessionId }`)
|
|
}
|
|
}
|
|
|
|
addListeners() {
|
|
let self = this
|
|
this.room.onMessage('*', (type, data) => {
|
|
switch (type) {
|
|
case 'draw_card_s2c':
|
|
// if (data.player == self.sessionId) {
|
|
// self.cards = self.cards.concat(data.cards);
|
|
// }
|
|
break
|
|
case 'player_ready_s2c':
|
|
break
|
|
case 'eat_card_s2c':
|
|
if (data.errcode == 0 && data.player == self.sessionId) {
|
|
debug(`eat_success: ${ self.sessionId }`)
|
|
self.selectPet()
|
|
}
|
|
break
|
|
}
|
|
})
|
|
|
|
this.room.onLeave(function () {
|
|
debug('LEFT ROOM', arguments)
|
|
self.room.removeAllListeners()
|
|
self.room.leave()
|
|
})
|
|
/**
|
|
* 监听房间状态的变更, state含有当前房间游戏的所有公共信息, 任何值的更改都会触发该方法
|
|
* */
|
|
this.room.onStateChange(function (state) {
|
|
self.player = state.players.get(self.sessionId)
|
|
// self.players = state.players;
|
|
})
|
|
// 也可以监听state下某个特定值的变更, 比如下面是监听 当前轮的clientid
|
|
this.room.state.listen('playerTurn', (currentValue: string) => {
|
|
let pid = currentValue.split(':')[0]
|
|
self.myTurn = pid === this.sessionId
|
|
if (self.myTurn) {
|
|
self.discard()
|
|
}
|
|
})
|
|
this.room.state.listen('subTurn', (currentValue: string, previousValue: string) => {
|
|
// self.mySubTurn = currentValue === self.sessionId;
|
|
// if (self.mySubTurn) {
|
|
// setTimeout(self.giveup.bind(self), self.delay);
|
|
// }
|
|
})
|
|
// 监听游戏状态的改变
|
|
this.room.state.listen('gameState', (currentValue: number, previousValue: number) => {
|
|
switch (currentValue) {
|
|
case GameStateConst.DETERMINE_TURN:
|
|
// console.log('比大小阶段');
|
|
//TODO: 随机分牌比大小阶段, 需要
|
|
break
|
|
case GameStateConst.CHANGE_HERO:
|
|
// console.log('英雄选择阶段');
|
|
self.selectHero()
|
|
break
|
|
case GameStateConst.STATE_CHANGE_CARD:
|
|
// console.log('开局换卡阶段');
|
|
self.changeCard()
|
|
break
|
|
case GameStateConst.STATE_BEGIN_EAT:
|
|
if (!self.myTurn) {
|
|
self.eatOrGiveUp()
|
|
}
|
|
break
|
|
case GameStateConst.STATE_ROUND_RESULT:
|
|
console.log('结算轮')
|
|
break
|
|
}
|
|
})
|
|
}
|
|
|
|
private reply(messageType: string, message: any) {
|
|
this.room.send(messageType, message)
|
|
}
|
|
|
|
|
|
// >>>>>>>>>>>>>>>>>> begin
|
|
/**
|
|
* 开局选择英雄
|
|
* @private
|
|
*/
|
|
@wait('pickHeroTime')
|
|
private async selectHero() {
|
|
let data = assistantUtil.randomHero()
|
|
this.reply('select_hero_c2s', data)
|
|
}
|
|
|
|
/**
|
|
* 开局准备
|
|
* @private
|
|
*/
|
|
@delay(2)
|
|
private async setReady() {
|
|
this.reply('play_ready_c2s', '')
|
|
}
|
|
|
|
/**
|
|
* 开局换牌
|
|
* @private
|
|
*/
|
|
@wait('cardChangeTime')
|
|
private async changeCard() {
|
|
let cardIds: number[] = []
|
|
this.reply('change_card_c2s', {
|
|
cards: cardIds
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 出牌
|
|
* @private
|
|
*/
|
|
@wait('maxDiscardTime')
|
|
private async discard() {
|
|
let targetCard
|
|
let canEat = !!this.room.state.rules.get(RULE_CANEAT)
|
|
if (this.room.state.cards.size == 1 && canEat) {
|
|
targetCard = [...this.room.state.cards.values()][0]
|
|
}
|
|
let self = this
|
|
let cardArr = [...self.player.cards.values()]
|
|
let cards = assistantUtil.checkDiscard(cardArr, targetCard)
|
|
if (!cards || cards.length == 0) {
|
|
return
|
|
}
|
|
let cardIds: number[] = []
|
|
let hasEatCard = false
|
|
for (let card of cards) {
|
|
if (!targetCard || (targetCard && card.id !== targetCard.id)) {
|
|
cardIds.push(card.id)
|
|
}
|
|
if (targetCard && card.id == targetCard.id) {
|
|
hasEatCard = true
|
|
}
|
|
}
|
|
log(`discard: ${ self.sessionId } ${ cardIds }`)
|
|
let repData: any = {
|
|
cards: cardIds
|
|
}
|
|
if (hasEatCard) {
|
|
repData.target = targetCard.id
|
|
}
|
|
self.reply('discard_card_c2s', repData)
|
|
}
|
|
|
|
/**
|
|
* 吃牌或者放弃
|
|
* @private
|
|
*/
|
|
@wait('maxEatTime')
|
|
private async eatOrGiveUp() {
|
|
let targetCard = [...this.room.state.cards.values()][0]
|
|
let cardArr = [...this.player.cards.values()]
|
|
let tmpCards = assistantUtil.checkDiscard(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(`eta_card: ${ this.sessionId } ${ cardIds } -> ${ target }`)
|
|
this.reply('eat_card_c2s', {
|
|
cards: cardIds,
|
|
target
|
|
})
|
|
}
|
|
|
|
/**
|
|
* 放弃吃牌
|
|
* @private
|
|
*/
|
|
private giveup() {
|
|
this.reply('give_up_eat_c2s', {})
|
|
}
|
|
|
|
|
|
/**
|
|
* 选择一个法术或者一个随从
|
|
* @private
|
|
*/
|
|
@wait('playerActTime')
|
|
private async selectPet() {
|
|
let data = await assistantUtil.selectPet(this.player, this.room.state)
|
|
if (data) {
|
|
this.reply('select_pet_c2s', data)
|
|
}
|
|
}
|
|
|
|
|
|
}
|