增加辅助机器人

This commit is contained in:
zhl 2020-12-16 14:18:29 +08:00
parent b169103835
commit 4c9e74b571
19 changed files with 397 additions and 396 deletions

View File

@ -1,2 +1,2 @@
pm2 start npm --name "card" -- run "dev:jc"
pm2 start npm --name "robot" -- run "dev:robot"
pm2 start npm --name "card" --log-date-format "YYYY-MM-DD HH:MM:ss" -- run "dev:jc"
pm2 start npm --name "robot" --log-date-format "YYYY-MM-DD HH:MM:ss" -- run "dev:robot"

View File

@ -7,3 +7,5 @@ export const error = debug('jc:error');
export const msgLog = debug('jc:msg');
export const robotLog = debug('jc:robot');
export const assistLog = debug('jc:assist');

19
src/global.d.ts vendored
View File

@ -8,6 +8,7 @@ import {RemovePetMsg} from "./message/RemovePetMsg";
import {Dispatcher} from "@colyseus/command";
import {Delayed} from "@gamestdio/timer/lib/Delayed";
import {Player} from "./rooms/schema/Player";
import {RobotClient} from "./robot/RobotClient";
export {};
@ -34,6 +35,11 @@ declare module "colyseus" {
* @param player id或者玩家的对象
*/
getClient(player: string | Player): Client;
/**
* room的client数量
*/
clientCount(): number;
// >>>>>>>>> Begin of extend send message <<<<<<<<<<<<<
/**
* 广
@ -154,6 +160,8 @@ declare module "colyseus" {
*/
bMsgQueue(datas: IMsg[], options?: any): void;
send(client: Client, type: string, data?: any): void;
// >>>>>>>>> End of extend send message <<<<<<<<<<<<<
@ -241,6 +249,17 @@ declare module "colyseus" {
*/
addRobot(playerId?: string):void;
/**
*
* @param sessionId
*/
addAssistClient(sessionId: string): void;
/**
* session的辅助机器人
* @param sessionId
*/
getAssistClient(sessionId: string): RobotClient;
}
}

View File

@ -9,6 +9,8 @@ import {Player} from "../rooms/schema/Player";
import {EffectCardCfg} from "../cfg/parsers/EffectCardCfg";
import {SkillTargetType} from "../rooms/logic/skill/SkillConst";
import CfgMan from "../rooms/logic/CfgMan";
import gameUtil from "../utils/game.util";
import assistantUtil from "../utils/assistant.util";
export class Robot {
host: string;
@ -37,6 +39,17 @@ export class Robot {
}
}
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) => {
@ -117,129 +130,6 @@ export class Robot {
this.room.send(messageType, message);
}
private checkTriple(cardArr: Card[], card?: Card): Card[] {
if (card) cardArr.push(card);
let pointMap: Map<number, Card[]> = new Map();
let cardIdSet: Set<number> = new Set();
for (let c of cardArr) {
if (c.type !== 1) {
continue;
}
if (pointMap.has(c.number)) {
let arr = pointMap.get(c.number);
arr.push(c);
pointMap.set(c.number, arr);
} else {
pointMap.set(c.number, [c]);
}
cardIdSet.add(c.number);
}
let fetched = false;
let result:Card[] = [];
// 优先出对子
for (let [point, arr] of pointMap) {
if (card) {
if (point == card.number && arr.length >= 3) {
fetched = true;
result = arr;
break;
}
} else {
if (arr.length >= 3) {
fetched = true;
result = arr;
break;
}
}
}
if (fetched) {
return result;
}
let cardIds = [...cardIdSet];
cardIds.sort((a, b) => a - b);
let tmp = [];
for (let i = 0, length = cardIds.length; i < length; i++) {
let cur = cardIds[i];
i == 0 && tmp.push(cur);
if (i > 0) {
if (cur != tmp[i - 1] + 1) {
tmp.length = 0;
}
tmp.push(cur);
}
if (card) {
if (tmp.indexOf(card.number) >= 0 && tmp.length >=3 ){
break;
}
} else {
if (tmp.length >= 3) {
break;
}
}
}
if (tmp.length >= 3) {
let subTmp = [];
for (let i = tmp[0] - 1; i > 0 ; i -- ) {
if (cardIdSet.has(i)) {
subTmp.push(i);
} else {
break;
}
}
for (let i = tmp[tmp.length]; i < cardIdSet.size; i ++) {
if (cardIdSet.has(i)) {
subTmp.push(i);
} else {
break;
}
}
tmp = tmp.concat(subTmp);
for (let point of tmp) {
if (card && point === card.number) {
result.push(card);
} else {
result.push(pointMap.get(point)[0]);
}
}
return result;
} else {
return arrUtil.randomGet(cardArr, 1);
}
}
/**
*
* @private
*/
private getEnemyPlayer(): Player {
let enemys = [];
for (let [,player] of this.room.state.players) {
if (player.team !== this.player.team) {
enemys.push(player);
}
}
return arrUtil.randomOne(enemys);
}
/**
*
* @param player
* @private
*/
private getRandomPet(player: Player): number {
let pets = [];
for (let [, pet] of player.pets) {
if (pet.ap > 0 && pet.state == 1)
pets.push(pet);
}
let result;
if (pets.length > 0) {
result = arrUtil.randomOne(pets);
}
return result ? result.pos : -1;
}
// >>>>>>>>>>>>>>>>>> begin
/**
@ -247,13 +137,9 @@ export class Robot {
* @private
*/
private async selectHero() {
let heroMap: Map<number, HeroCfg> = global.$cfg.get(BaseConst.HERO);
let heroArr = [...heroMap.values()];
let hero = arrUtil.randomGet(heroArr, 1);
await this.delay(2);
this.reply('select_hero_c2s', {
heroId: hero[0].id
});
let data = assistantUtil.randomHero();
this.reply('select_hero_c2s', data);
}
/**
@ -284,7 +170,7 @@ export class Robot {
await this.delay(3);
let self = this;
let cardArr = [...self.player.cards.values()];
let cards = self.checkTriple(cardArr);
let cards = assistantUtil.checkTriple(cardArr);
if (!cards) {
return;
}
@ -302,7 +188,7 @@ export class Robot {
await this.delay(2);
let targetCard = [...this.room.state.cards.values()][0];
let cardArr = [...this.player.cards.values()];
let tmpCards = this.checkTriple(cardArr, targetCard);
let tmpCards = assistantUtil.checkTriple(cardArr, targetCard);
let next = this.giveup.bind(this);
if (tmpCards.length > 1) {
let cardIds: number[] = [];
@ -345,66 +231,8 @@ export class Robot {
* @private
*/
private async selectPet() {
await this.delay(5, 0.2);
let cards = [...this.room.state.cards.values()];
let result;
let effectMap: Map<number, EffectCardCfg> = global.$cfg.get(BaseConst.EFFECTCARD);
for (let card of cards) {
let effect = effectMap.get(card.effect);
if (effect.type_id == 1) {
result = card;
break;
}
}
if (!result) {
result = arrUtil.randomGet(cards, 1)[0];
}
let targetType: SkillTargetType = CfgMan.getTargetByCard(result.id);
let targetPlayer;
let targetPos;
switch (targetType) {
case SkillTargetType.ENEMY_PLAYER:
targetPlayer = this.getEnemyPlayer();
break;
case SkillTargetType.ENEMY_PET:
for (let [,player] of this.room.state.players) {
if (player.team !== this.player.team) {
let pos = this.getRandomPet(player);
if (pos > - 1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.FRIEND_PET:
for (let [,player] of this.room.state.players) {
if (player.team == this.player.team) {
let pos = this.getRandomPet(player);
if (pos > - 1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.SELF_PET:
let pos = this.getRandomPet(this.player);
if (pos > - 1) {
targetPlayer = this.player;
targetPos = pos;
break;
}
break;
}
this.reply('select_pet_c2s', {
card: result.id,
player: targetPlayer?.id,
pos: targetPos,
effCards: []
})
let data = await assistantUtil.selectPet(this.player, this.room.state);
this.reply('select_pet_c2s', data);
}

View File

@ -1,19 +1,12 @@
import {Client, Room} from "colyseus";
import {ClientState, ISendOptions} from "colyseus/lib/transport/Transport";
import {EventEmitter} from 'events';
import {robotLog as log} from '../common/Debug';
import {assistLog as log} from '../common/Debug';
import {CardGameState} from "../rooms/schema/CardGameState";
import Clock from "@gamestdio/timer";
import {GameStateConst} from "../constants/GameStateConst";
import {Card} from "../rooms/schema/Card";
import {BaseConst} from "../constants/BaseConst";
import arrUtil from "../utils/array.util";
import {HeroCfg} from "../cfg/parsers/HeroCfg";
import {EffectCardCfg} from "../cfg/parsers/EffectCardCfg";
import CfgMan from '../rooms/logic/CfgMan';
import {SkillTargetType} from "../rooms/logic/skill/SkillConst";
import {Player} from "../rooms/schema/Player";
import gameUtil from "../utils/game.util";
import assistantUtil from "../utils/assistant.util";
export class RobotClient implements Client {
id: string;
@ -23,26 +16,19 @@ export class RobotClient implements Client {
selfPlayer: Player;
state: ClientState;
svrstate: CardGameState;
room: Room;
clock: Clock;
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, clock: Clock, onMessageHandlers: {[id: string]: (client: Client, message: any) => void}) {
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.clock = clock;
this.ref = new EventEmitter();
this.onMessageHandlers = onMessageHandlers;
let self = this;
let time = Math.random() * 2500 | 0;
this.clock.setTimeout(function (){
self.reply('play_ready_c2s', '');
}, time);
this.addListeners();
log(`new robot with session: ${sessionId}`);
@ -74,13 +60,15 @@ export class RobotClient implements Client {
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.cards = self.svrstate.players.get(self.sessionId).cards;
self.changeCard();
break;
case GameStateConst.STATE_BEGIN_EAT:
@ -94,8 +82,11 @@ export class RobotClient implements Client {
}
private gameTurnUpdate(currentValue: string, previousValue: string) {
let self = this;
log(`server turn change: ${currentValue}, pre value: ${previousValue}`);
self.myTurn = currentValue === self.sessionId;
if (!this.active) {
return;
}
log(`server turn change: ${currentValue}, pre value: ${previousValue}`);
if (self.myTurn) {
self.discard();
}
@ -104,6 +95,9 @@ export class RobotClient implements Client {
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':
@ -127,80 +121,16 @@ export class RobotClient implements Client {
(this.onMessageHandlers['*'] as any)(this, messageType, message);
}
}
private checkTriple(cardArr: Card[], card?: Card): Card[] {
if (card) cardArr.push(card);
let cards = cardArr.filter(o => o.type == 1);
cards.sort((a, b) => a.number - b.number);
let result = [];
for (let i = 0, length = cards.length; i < length; i ++) {
let cur = cards[i];
i == 0 && result.push(cur);
if (i > 0) {
let isSame = false;
if (result.length > 1) {
if (result[result.length - 1].number == result[result.length - 2].number) {
isSame = true;
}
}
if ((result[result.length - 1].number == cur.number) && (isSame || result.length == 1)) {
result.push(cur);
} else if ((result[result.length - 1].number + 1 == cur.number) && (!isSame || result.length == 1)) {
result.push(cur);
} else {
result.length = 0;
result.push(cur);
}
}
}
if (result.length < 3) {
return [cards[0]];
} else {
return result;
}
}
/**
*
* @private
*/
private getEnemyPlayer(): Player {
let enemys = [];
for (let [,player] of this.svrstate.players) {
if (player.team !== this.selfPlayer.team) {
enemys.push(player);
}
}
return arrUtil.randomOne(enemys);
}
/**
*
* @param player
* @private
*/
private getRandomPet(player: Player): number {
let pets = [];
for (let [, pet] of player.pets) {
if (pet.ap > 0 && pet.state == 1)
pets.push(pet);
}
let result;
if (pets.length > 0) {
result = arrUtil.randomOne(pets);
}
return result ? result.pos : -1;
}
// >>>>>>>>>>>>>>>>>> begin
/**
*
* @private
*/
private discard() {
private async discard() {
await assistantUtil.delay(3);
let self = this;
let next = function () {
let cardArr = [...self.cards.values()];
// cardArr.sort((a, b) => a.number - b.number);
let cards = self.checkTriple(cardArr);
let cardArr = [...self.selfPlayer.cards.values()];
let cards = assistantUtil.checkTriple(cardArr);
if (!cards) {
return;
}
@ -209,17 +139,16 @@ export class RobotClient implements Client {
cards: cardIds
});
}
this.clock.setTimeout(next, 1500);
}
/**
*
* @private
*/
private eatOrGiveUp() {
private async eatOrGiveUp() {
await assistantUtil.delay(2);
let targetCard = [...this.svrstate.cards.values()][0];
let cardArr = [...this.cards.values()];
let tmpCards = this.checkTriple(cardArr, targetCard);
let cardArr = [...this.selfPlayer.cards.values()];
let tmpCards = assistantUtil.checkTriple(cardArr, targetCard);
let next = this.giveup.bind(this);
if (tmpCards.length > 1) {
let cardIds: number[] = [];
@ -230,8 +159,7 @@ export class RobotClient implements Client {
}
next = this.eatCard.bind(this, cardIds, targetCard.id);
}
this.clock.setTimeout(next, 1500);
next.apply(this);
}
/**
@ -256,17 +184,23 @@ export class RobotClient implements Client {
this.reply('give_up_eat_c2s', {});
}
/**
*
* @private
*/
private async setReady() {
await assistantUtil.delay(2.5);
this.reply('play_ready_c2s', '');
}
/**
*
* @private
*/
private selectHero() {
let heroMap: Map<number, HeroCfg> = global.$cfg.get(BaseConst.HERO);
let heroArr = [...heroMap.values()];
let hero = arrUtil.randomGet(heroArr, 1);
this.reply('select_hero_c2s', {
heroId: hero[0].id
});
private async selectHero() {
await assistantUtil.delay(2);
let data = assistantUtil.randomHero();
this.reply('select_hero_c2s', data);
}
/**
@ -286,66 +220,9 @@ export class RobotClient implements Client {
*
* @private
*/
private selectPet() {
let cards = [...this.svrstate.cards.values()];
let result;
let effectMap: Map<number, EffectCardCfg> = global.$cfg.get(BaseConst.EFFECTCARD);
for (let card of cards) {
let effect = effectMap.get(card.effect);
if (effect.type_id == 1) {
result = card;
break;
}
}
if (!result) {
result = arrUtil.randomGet(cards, 1)[0];
}
let targetType: SkillTargetType = CfgMan.getTargetByCard(result.id);
let targetPlayer;
let targetPos;
switch (targetType) {
case SkillTargetType.ENEMY_PLAYER:
targetPlayer = this.getEnemyPlayer();
break;
case SkillTargetType.ENEMY_PET:
for (let [,player] of this.svrstate.players) {
if (player.team !== this.selfPlayer.team) {
let pos = this.getRandomPet(player);
if (pos > - 1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.FRIEND_PET:
for (let [,player] of this.svrstate.players) {
if (player.team == this.selfPlayer.team) {
let pos = this.getRandomPet(player);
if (pos > - 1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.SELF_PET:
let pos = this.getRandomPet(this.selfPlayer);
if (pos > - 1) {
targetPlayer = this.selfPlayer;
targetPos = pos;
break;
}
break;
}
this.reply('select_pet_c2s', {
card: result.id,
player: targetPlayer?.id,
pos: targetPos,
effCards: []
})
private async selectPet() {
let data = await assistantUtil.selectPet(this.selfPlayer, this.svrstate);
this.reply('select_pet_c2s', data)
}
}

View File

@ -6,4 +6,9 @@ export class RobotManage {
let robot = new Robot(host, room);
let sessionId = await robot.connect();
}
async reConnect({host, room, sessionId}: { host: string, room: string, sessionId: string }) {
let robot = new Robot(host, room);
await robot.reConnect(sessionId);
}
}

View File

@ -7,13 +7,20 @@ const router = express.Router();
router.get('/create', async (req, res, next) => {
let query = req.query;
let {host, room } = query;
let {host, room, sessionId } = query;
host = host as string;
room = room as string;
debug(`receive create robot msg: ${host}, ${room}`);
let manage = singleton(RobotManage);
try {
if (sessionId) {
sessionId = sessionId as string;
debug(`receive reconnect msg: ${host}, ${room}, ${sessionId}`);
await manage.reConnect({host, room, sessionId})
} else {
debug(`receive create robot msg: ${host}, ${room}`);
await manage.addOne({host, room});
}
res.json({errcode: 0});
} catch (err) {
res.json({errcode: 1})

View File

@ -27,6 +27,9 @@ export class GeneralRoom extends Room {
battleMan = new BattleHandler();
// 用于游戏过程中各种计时器, 使用该计时器的前提是, 只针对当前操作玩家
gameClock: Map<string, Delayed> = new Map();
assistMap: Map<String, RobotClient> = new Map();
async onAuth (client:Client, options: any, request: IncomingMessage) {
console.log(options);
// TODO: 验证用户信息
@ -102,19 +105,24 @@ export class GeneralRoom extends Room {
async onLeave (client: Client, consented: boolean) {
if (this.state.gameState === GameStateConst.STATE_GAME_OVER || this.state.gameState === GameStateConst.STATE_WAIT_JOIN) {
this.state.players.delete(client.sessionId);
this.assistMap.delete(client.sessionId);
this.bUserLeft(client.sessionId);
} else {
this.state.players.get(client.sessionId).state = PlayerStateConst.PLAYER_OFFLINE;
let assistClient = this.getAssistClient(client.sessionId);
assistClient.active = true;
try {
if (consented) {
throw new Error("consented leave");
}
} else {
await this.allowReconnection(client, 60);
debugRoom(`${client.sessionId} 重连`);
assistClient.active = false;
this.state.players.get(client.sessionId).state = PlayerStateConst.PLAYER_NORMAL;
}
} catch (e) {
debugRoom(`player realy level :${client.sessionId}`);
this.state.players.delete(client.sessionId);
debugRoom(`player realy level :${client.sessionId}, try add robot`);
// this.state.players.delete(client.sessionId);
}
}
}
@ -125,17 +133,26 @@ export class GeneralRoom extends Room {
getClient(player: string | Player): Client {
let result: Client;
let sessionId;
if (typeof player == 'string') {
sessionId = player;
result = this.clients.find(client => client.sessionId == player );
} else {
sessionId = player.id;
result = this.clients.find(client => client.sessionId == player.id );
}
if (!result) {
error(`无法获取id为: ${typeof player == 'string' ? player : player.id} 的客户端`)
error(`无法获取id为: ${typeof player == 'string' ? player : player.id} 的客户端`);
result = this.getAssistClient(sessionId);
debugRoom(`启用辅助机器人, 当前机器人状态: ${(result as RobotClient).active}`)
}
return result;
}
clientCount(): number {
return this.clients.length;
}
/**
* room.gameClock设定任务
* gameClock任何时候只有一个可执行的任务
@ -213,7 +230,9 @@ export class GeneralRoom extends Room {
let data = {
host: 'ws://127.0.0.1:2567',
room: this.roomId,
sessionId: playerId
}
axios.get('http://127.0.0.1:2500/robot/create', {
params: data
}).then((res) => {
@ -247,4 +266,13 @@ export class GeneralRoom extends Room {
// }
// this._events.emit('join', client);
}
addAssistClient(sessionId: string) {
let client = new RobotClient(sessionId, this.state, this['onMessageHandlers']);
this.assistMap.set(sessionId, client);
}
getAssistClient(sessionId: string): RobotClient {
return this.assistMap.get(sessionId);
}
}

View File

@ -215,6 +215,17 @@ Object.defineProperties(Room.prototype, {
value: function (client: Client, data?: any) {
client && client.send("change_card_s2c", data);
}
},
send: {
value: function (client: Client, type: string, data?: any) {
if (client) {
client.send(type, data);
} else {
let assistClient = this.getAssistClient(client.sessionId);
assistClient && assistClient.send(type, data);
}
}
}
});

View File

@ -21,8 +21,8 @@ export class BeginGameCommand extends Command<CardGameState, {}> {
let cardAll = card0.concat(card1);
arrUtil.randomSort(cardAll);
this.state.cardQueue = cardAll;
for (let client of this.room.clients) {
this.room.addCard(client.sessionId, singleton(GameEnv).initCardNum, 0);
for (let [id] of this.state.players) {
this.room.addCard(id, singleton(GameEnv).initCardNum, 0);
}
this.state.updateGameState(GameStateConst.STATE_CHANGE_CARD);
// 超时后结束换卡, 进入下一轮

View File

@ -24,15 +24,15 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
execute({ client, cards , dtype} = this.payload) {
const player = this.state.players.get(client.sessionId);
if (!player) {
client && client.send('discard_card_s2c', {errcode: 1, errmsg: 'player不存在'});
this.room.send(client,'discard_card_s2c', {errcode: 1, errmsg: 'player不存在'});
return;
}
if (!gameUtil.checkCardsExists(player.cards, cards)) {
client && client.send('discard_card_s2c', {errcode: 2, errmsg: '要出的牌在手牌中不存在'});
this.room.send(client,'discard_card_s2c', {errcode: 2, errmsg: '要出的牌在手牌中不存在'});
return;
}
if (this.state.currentTurn != client.sessionId) {
client && client.send('discard_card_s2c', {errcode: 3, errmsg: '不是当前轮'});
this.room.send(client,'discard_card_s2c', {errcode: 3, errmsg: '不是当前轮'});
return;
}
let tmpCards = [];
@ -40,7 +40,7 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
tmpCards.push(player.cards.get(id + ''));
}
if (!gameUtil.checkDiscard(tmpCards)) {
client && client.send('discard_card_s2c', {errcode: 4, errmsg: '出牌不符合规则'});
this.room.send(client,'discard_card_s2c', {errcode: 4, errmsg: '出牌不符合规则'});
return;
}
for (let [key, val] of this.state.cards) {
@ -65,7 +65,7 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
* ,
*
*/
client && client.send('discard_card_s2c', {errcode: 0, cards: cards, type: dtype})
this.room.send(client,'discard_card_s2c', {errcode: 0, cards: cards, type: dtype})
if (cards.length === 1) {
let cardArr: Card[] = [...this.state.cards.values()];
this.room.battleMan.onCardDiscarded(player, cardArr[0])
@ -79,7 +79,7 @@ export class DiscardCommand extends Command<CardGameState, { client: Client, car
this.room.battleMan.onCardLinkOver(player, cardArr);
this.state.updateGameState(GameStateConst.STATE_PICK_PET);
let self = this;
client && client.send('eat_card_s2c', {player: player.id, errcode: 0, errmsg: ''});
this.room.send(client,'eat_card_s2c', {player: player.id, errcode: 0, errmsg: ''});
let time = singleton(GameEnv).playerActTime * 1000 + player.extraTime;
// 开启选随从计时, 计时结束后结束当前轮
let timeOverSelectPet = function () {

View File

@ -12,19 +12,19 @@ export class EatCardCommand extends Command<CardGameState, { client: Client, car
execute({ client, cards, target } = this.payload) {
const player = this.state.players.get(client.sessionId);
if (!player) {
client && client.send('eat_card_s2c', {errcode: 1, errmsg: 'player不存在或者'});
this.room.send(client,'eat_card_s2c', {errcode: 1, errmsg: 'player不存在或者'});
return;
}
if (!gameUtil.checkCardsExists(player.cards, cards)) {
client && client.send('eat_card_s2c', {errcode: 2, errmsg: '要出的牌在手牌中不存在'});
this.room.send(client,'eat_card_s2c', {errcode: 2, errmsg: '要出的牌在手牌中不存在'});
return;
}
if (this.state.currentTurn == client.sessionId) {
client && client.send('eat_card_s2c', {errcode: 3, errmsg: '不能吃自己的牌'});
this.room.send(client,'eat_card_s2c', {errcode: 3, errmsg: '不能吃自己的牌'});
return;
}
if (!this.state.cards.has(target + '')) {
client && client.send('eat_card_s2c', {errcode: 4, errmsg: '找不到要吃的牌'});
this.room.send(client,'eat_card_s2c', {errcode: 4, errmsg: '找不到要吃的牌'});
return;
}
let tmpCards = [];
@ -33,16 +33,16 @@ export class EatCardCommand extends Command<CardGameState, { client: Client, car
}
tmpCards.push(this.state.cards.get(target + ''));
if (!gameUtil.checkDiscard(tmpCards)) {
client && client.send('discard_card_s2c', {errcode: 5, errmsg: '不符合吃牌规则'});
this.room.send(client,'discard_card_s2c', {errcode: 5, errmsg: '不符合吃牌规则'});
return;
}
if (this.state.gameState !== GameStateConst.STATE_BEGIN_EAT) {
client && client.send('discard_card_s2c', {errcode: 7, errmsg: '不是吃牌轮'});
this.room.send(client,'discard_card_s2c', {errcode: 7, errmsg: '不是吃牌轮'});
return;
}
//将吃牌数据临时保存
if (this.state.tmpActionMap.has(client.sessionId)) {
client && client.send('discard_card_s2c', {errcode: 8, errmsg: '不可更改操作'});
this.room.send(client,'discard_card_s2c', {errcode: 8, errmsg: '不可更改操作'});
return ;
}
this.state.tmpActionMap.set(client.sessionId, cards);

View File

@ -101,12 +101,12 @@ export class EatConfirmCommand extends Command<CardGameState, { timeUp: boolean
// 成功后广播吃牌成功消息
let client = this.room.getClient(player.id);
this.room.broadcast('eat_card_s2c', {player: player.id, errcode: 0, errmsg: ''}, {except: client});
client && client.send('eat_card_s2c', {player: player.id, errcode: 0, errmsg: ''});
this.room.send(client,'eat_card_s2c', {player: player.id, errcode: 0, errmsg: ''});
// 向其他玩家发送吃卡失败的消息
for (let [key, val] of tmpActionMap) {
if (typeof val != 'number' && key !== player.id) {
let client = this.room.getClient(key);
client && client.send('eat_card_s2c', {errcode: 9, errmsg: '吃卡失败'});
this.room.send(client,'eat_card_s2c', {errcode: 9, errmsg: '吃卡失败'});
}
}
} else if (!player && timeUp) {

View File

@ -50,7 +50,7 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
str += '更新英雄血量: herohp:玩家index|血量 例: herohp:0|500 \n';
str += '生成几张特定效果的卡: addcard:玩家index|效果卡id|数量 例: addcard:0|20011|1\n';
str += '将某一队设为赢家: setwin:队伍index 例: setwin:0\n';
client && client.send('notice_msg', str);
this.room.send(client,'notice_msg', str);
}
/**
@ -60,7 +60,7 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
*/
addRobot(msg:string) {
let count = msg ? parseInt(msg) : 1;
let count2 = this.room.maxClients - this.room.clients.length;
let count2 = this.room.maxClients - this.room.clientCount();
for (let i = 0; i< Math.min(count, count2); i++) {
this.room.addRobot();
}
@ -84,7 +84,7 @@ export class GMCommand extends Command<CardGameState, {client: Client, message:
card.effect = effectId;
}
let client = this.room.getClient(player.id);
client && client.send('sync_card', {}, {afterNextPatch: true});
this.room.send(client,'sync_card', {}, {afterNextPatch: true});
}
/**

View File

@ -82,7 +82,7 @@ export class GameResultCommand extends Command<CardGameState, {}> {
await self.room.setPrivate(false);
//开启匹配定时, 长时间没匹配到人的话, 添加机器人
let timeOutWaitingPlayer = function () {
let count = self.room.maxClients - self.room.clients.length;
let count = self.room.maxClients - self.room.clientCount();
if (count > 0) {
for (let i = 0; i < count; i++) {
self.room.addRobot();

View File

@ -17,11 +17,12 @@ export class OnJoinCommand extends Command<CardGameState, {
// 实际的team会在PlayReadyCommand中设置
let player = new Player(client.sessionId, 0, team);
this.state.players.set(client.sessionId, player);
this.room.addAssistClient(client.sessionId);
let self = this;
if (this.room.clients.length == 1) {
if (this.room.clientCount() == 1) {
// 正常的匹配逻辑进入的第一个玩家, 开启定时, 超过设定时间人没齐的话, 添加机器人
let timeOutWaitingPlayer = function () {
let count = self.room.maxClients - self.room.clients.length;
let count = self.room.maxClients - self.room.clientCount();
if (count > 0) {
for (let i = 0; i < count; i++) {
self.room.addRobot();
@ -30,7 +31,7 @@ export class OnJoinCommand extends Command<CardGameState, {
}
let time = singleton(GameEnv).waitingPlayerTime * 1000;
self.room.beginSchedule(time, timeOutWaitingPlayer, 'waiting_player');
} else if (this.room.clients.length > 1 && this.room.clients.length < this.room.maxClients) {
} else if (this.room.clientCount() > 1 && this.room.clientCount() < this.room.maxClients) {
let moreTime = singleton(GameEnv).waitingPlayerOnePlus * 1000;
self.room.addScheduleTime(moreTime, 'play_join', 'waiting_player')
}

View File

@ -12,7 +12,7 @@ export class PrepareCommand extends Command<CardGameState, {}> {
this.state.updateGameState(GameStateConst.DETERMINE_TURN);
let time = 3000;
//TODO:: 每人发2张牌, 并比较大小, 确定先手
for (let client of this.room.clients) {
for (let client of this.state.players) {
// this.room.broadcast();
}

222
src/utils/assistant.util.ts Normal file
View File

@ -0,0 +1,222 @@
import {Card} from "../rooms/schema/Card";
import arrUtil from "./array.util";
import {EffectCardCfg} from "../cfg/parsers/EffectCardCfg";
import {BaseConst} from "../constants/BaseConst";
import {SkillTargetType} from "../rooms/logic/skill/SkillConst";
import CfgMan from "../rooms/logic/CfgMan";
import {CardGameState} from "../rooms/schema/CardGameState";
import {Player} from "../rooms/schema/Player";
import {HeroCfg} from "../cfg/parsers/HeroCfg";
let assistantUtil = {
delay(max: number, min?: number) {
min = min || 0;
let milliseconds = (Math.random() * (max - min) + min) * 1000 | 0;
return new Promise(resolve => setTimeout(resolve, milliseconds));
},
/**
*
* @param cardArr
* @param card
*/
checkTriple(cardArr: Card[], card?: Card): Card[] {
if (card) cardArr.push(card);
let pointMap: Map<number, Card[]> = new Map();
let cardIdSet: Set<number> = new Set();
for (let c of cardArr) {
if (c.type !== 1) {
continue;
}
if (pointMap.has(c.number)) {
let arr = pointMap.get(c.number);
arr.push(c);
pointMap.set(c.number, arr);
} else {
pointMap.set(c.number, [c]);
}
cardIdSet.add(c.number);
}
let fetched = false;
let result: Card[] = [];
// 优先出对子
for (let [point, arr] of pointMap) {
if (card) {
if (point == card.number && arr.length >= 3) {
fetched = true;
result = arr;
break;
}
} else {
if (arr.length >= 3) {
fetched = true;
result = arr;
break;
}
}
}
if (fetched) {
return result;
}
let cardIds = [...cardIdSet];
cardIds.sort((a, b) => a - b);
let tmp = [];
for (let i = 0, length = cardIds.length; i < length; i++) {
let cur = cardIds[i];
i == 0 && tmp.push(cur);
if (i > 0) {
if (cur != tmp[i - 1] + 1) {
tmp.length = 0;
}
tmp.push(cur);
}
if (card) {
if (tmp.indexOf(card.number) >= 0 && tmp.length >= 3) {
break;
}
} else {
if (tmp.length >= 3) {
break;
}
}
}
if (tmp.length >= 3) {
let subTmp = [];
for (let i = tmp[0] - 1; i > 0; i--) {
if (cardIdSet.has(i)) {
subTmp.push(i);
} else {
break;
}
}
for (let i = tmp[tmp.length]; i < cardIdSet.size; i++) {
if (cardIdSet.has(i)) {
subTmp.push(i);
} else {
break;
}
}
tmp = tmp.concat(subTmp);
for (let point of tmp) {
if (card && point === card.number) {
result.push(card);
} else {
result.push(pointMap.get(point)[0]);
}
}
return result;
} else {
return arrUtil.randomGet(cardArr, 1);
}
},
/**
*
* @private
*/
getEnemyPlayer(dstPlayer: Player, state: CardGameState): Player {
let enemys = [];
for (let [, player] of state.players) {
if (player.team !== dstPlayer.team) {
enemys.push(player);
}
}
return arrUtil.randomOne(enemys);
},
/**
*
* @param player
* @private
*/
getRandomPet(player: Player): number {
let pets = [];
for (let [, pet] of player.pets) {
if (pet.ap > 0 && pet.state == 1)
pets.push(pet);
}
let result;
if (pets.length > 0) {
result = arrUtil.randomOne(pets);
}
return result ? result.pos : -1;
},
/**
*
* @private
*/
async selectPet(dstPlayer: Player, state: CardGameState) {
await this.delay(5, 0.2);
let cards = [...state.cards.values()];
let result;
let effectMap: Map<number, EffectCardCfg> = global.$cfg.get(BaseConst.EFFECTCARD);
for (let card of cards) {
let effect = effectMap.get(card.effect);
if (effect.type_id == 1) {
result = card;
break;
}
}
if (!result) {
result = arrUtil.randomGet(cards, 1)[0];
}
let targetType: SkillTargetType = CfgMan.getTargetByCard(result.effect);
let targetPlayer;
let targetPos;
switch (targetType) {
case SkillTargetType.ENEMY_PLAYER:
targetPlayer = this.getEnemyPlayer(dstPlayer, state);
break;
case SkillTargetType.ENEMY_PET:
for (let [, player] of state.players) {
if (player.team !== dstPlayer.team) {
let pos = this.getRandomPet(player);
if (pos > -1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.FRIEND_PET:
for (let [, player] of state.players) {
if (player.team == dstPlayer.team) {
let pos = this.getRandomPet(player);
if (pos > -1) {
targetPlayer = player;
targetPos = pos;
break;
}
}
}
break;
case SkillTargetType.SELF_PET:
let pos = this.getRandomPet(dstPlayer);
if (pos > -1) {
targetPlayer = dstPlayer;
targetPos = pos;
break;
}
break;
}
//TODO: 增加效果卡
let effCards: number[] = [];
return {
card: result.id,
player: targetPlayer?.id,
pos: targetPos,
effCards
}
},
randomHero() {
let heroMap: Map<number, HeroCfg> = global.$cfg.get(BaseConst.HERO);
let heroArr = [...heroMap.values()];
let hero = arrUtil.randomGet(heroArr, 1);
return {heroId: hero[0].id}
}
}
export default assistantUtil;

View File

@ -255,7 +255,8 @@ let gameUtil = {
result += pet.ap;
}
return result;
}
},
}
export default gameUtil;