From a2cec5475d83c9384783c06fa22e8fffb162a9fd Mon Sep 17 00:00:00 2001 From: zhl Date: Fri, 15 Jan 2021 10:25:26 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=8C=B9=E9=85=8D=E6=B5=81?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global.d.ts | 2 + src/index.ts | 18 +- src/rooms/GeneralRoom.ts | 11 +- src/rooms/RankedLobbyRoom.ts | 393 ++++++++++++++++++++++++++ src/rooms/commands/NextTurnCommand.ts | 72 ++--- src/rooms/commands/OnJoinCommand.ts | 87 +++--- src/rooms/schema/Player.ts | 198 +++++++------ 7 files changed, 608 insertions(+), 173 deletions(-) create mode 100644 src/rooms/RankedLobbyRoom.ts diff --git a/src/global.d.ts b/src/global.d.ts index fc5afeb..118ee07 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -30,6 +30,8 @@ declare module "colyseus" { battleMan: BattleHandler; dispatcher: Dispatcher; mainClock: Delayed; + robotCount: number; + match: boolean; /** * 根据sessionId获取client * @param player 玩家id或者玩家的对象 diff --git a/src/index.ts b/src/index.ts index 2082573..8b6d90d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,15 +1,14 @@ import http from "http"; import express from "express"; import cors from "cors"; -import {RedisPresence, Server} from "colyseus"; -import { monitor } from "@colyseus/monitor"; +import {Server} from "colyseus"; +import {monitor} from "@colyseus/monitor"; import rateLimit from "express-rate-limit"; // import socialRoutes from "@colyseus/social/express" - -import { GeneralRoom } from "./rooms/GeneralRoom"; -import {MongooseDriver} from "colyseus/lib/matchmaker/drivers/MongooseDriver"; +import {GeneralRoom} from "./rooms/GeneralRoom"; import {initData} from "./common/GConfig"; import {Config} from "./cfg/Config"; +import {RankedLobbyRoom} from "./rooms/RankedLobbyRoom"; require('./rooms/MSender'); require('./rooms/RoomExtMethod'); @@ -34,7 +33,9 @@ const gameServer = new Server({ // register your room handlers gameServer.define('general_room', GeneralRoom); - +gameServer + .define('match_room', RankedLobbyRoom) + .filterBy(['numClientsToMatch']); /** * Register @colyseus/social routes * @@ -61,5 +62,6 @@ gameServer.onShutdown(function () { console.log("master process is being shut down!"); //TODO:: 保存所有数据至db, 重启时恢复 }); -gameServer.listen(port).then(()=>{}); -console.log(`Listening on ws://localhost:${ port }`) +gameServer.listen(port).then(() => { +}); +console.log(`Listening on ws://localhost:${port}`) diff --git a/src/rooms/GeneralRoom.ts b/src/rooms/GeneralRoom.ts index 0a41f7d..f7f30d6 100644 --- a/src/rooms/GeneralRoom.ts +++ b/src/rooms/GeneralRoom.ts @@ -30,6 +30,8 @@ export class GeneralRoom extends Room { gameClock: Map = new Map(); assistMap: Map = new Map(); + match = false; + robotCount = 0; async onAuth (client:Client, options: any, request: IncomingMessage) { debugRoom(options); @@ -40,8 +42,14 @@ export class GeneralRoom extends Room { } onCreate (options: any) { let cs = new CardGameState(); - this.setMetadata({rank: 2}); this.setState(cs); + this.setPrivate(true); + if (options.count) { + this.robotCount = Math.min(Math.max(0, options.count), this.maxClients - 1); + } + if (options.match) { + this.match = options.match; + } this.battleMan.init(cs, this); this.clock.start(); this.state.gameState = GameStateConst.STATE_WAIT_JOIN; @@ -105,6 +113,7 @@ export class GeneralRoom extends Room { let data = { client: client, accountId: options.accountid, + seat: options.seat }; this.dispatcher.dispatch(new OnJoinCommand(), data); } diff --git a/src/rooms/RankedLobbyRoom.ts b/src/rooms/RankedLobbyRoom.ts new file mode 100644 index 0000000..6d16279 --- /dev/null +++ b/src/rooms/RankedLobbyRoom.ts @@ -0,0 +1,393 @@ +import {Client, generateId, matchMaker, Room} from "colyseus"; + +interface MatchmakingGroup { + averageRank: number; + clients: ClientStat[], + priority?: boolean; + + ready?: boolean; + confirmed?: number; + count: number; + // cancelConfirmationTimeout?: Delayed; +} + +interface ClientStat { + clients: Map; + waitingTime: number; + options?: any; + groupTag?: string; + group?: MatchmakingGroup; + rank: number; + confirmed?: boolean; +} + +export class RankedLobbyRoom extends Room { + /** + * If `allowUnmatchedGroups` is true, players inside an unmatched group (that + * did not reached `numClientsToMatch`, and `maxWaitingTime` has been + * reached) will be matched together. Your room should fill the remaining + * spots with "bots" on this case. + */ + allowUnmatchedGroups: boolean = true; + + /** + * Evaluate groups for each client at interval + */ + evaluateGroupsInterval = 1000; + + /** + * Groups of players per iteration + */ + groups: MatchmakingGroup[] = []; + + /** + * name of the room to create + */ + roomToCreate = "general_room"; + + /** + * 最大匹配时间 + */ + maxWaitingTime = 15 * 1000; + /** + * 超过该时间, 组队玩家可匹配单排玩家 + * @type {number} + */ + groupAddOneTime = 10 * 1000; + + /** + * after this time, try to fit this client with a not-so-compatible group + */ + maxWaitingTimeForPriority?: number = 10 * 1000; + + /** + * number of players on each match + */ + numClientsToMatch = 4; + + // /** + // * after a group is ready, clients have this amount of milliseconds to confirm + // * connection to the created room + // */ + // cancelConfirmationAfter = 5000; + maxDiffRatio = 1.2; + /** + * rank and group cache per-player + */ + stats: ClientStat[] = []; + + onCreate(options: any) { + if (options.maxWaitingTime) { + this.maxWaitingTime = options.maxWaitingTime; + } + + if (options.numClientsToMatch) { + this.numClientsToMatch = options.numClientsToMatch; + } + + this.onMessage("bye", (client: Client, message: any) => { + const stat = this.stats.find(stat => stat.clients.has(client.sessionId)); + + if (stat && stat.group && typeof (stat.group.confirmed) === "number") { + stat.confirmed = true; + stat.group.confirmed++; + stat.clients.delete(client.sessionId); + client.leave(); + } + }) + + /** + * Redistribute clients into groups at every interval + */ + this.setSimulationInterval(() => this.redistributeGroups(), this.evaluateGroupsInterval); + } + + onJoin(client: Client, options: any) { + /** + * 如果请求带有group, 那么查找当前队列中group相同的记录 + * 如果没有, 就插一条记录 + * 如果找到的记录, clients已经查过2条, 则也插入一条记录 + * 如果找到的记录, clients是1, 则把当前client添加到该记录中, 同时更新rank + */ + if (options.group) { + let length = this.stats.length; + let groupData; + for (let i = 0; i < length; i++) { + if (this.stats[i].groupTag == options.group) { + groupData = this.stats[i]; + break + } + } + if (!groupData) { + let clientMap = new Map(); + clientMap.set(client.sessionId, {client, options}); + groupData = { + clients: clientMap, + rank: options.rank, + groupTag: options.group, + waitingTime: 0, + options + } + this.stats.push(groupData); + } else { + if (groupData.clients.size >= 2) { + let clientMap = new Map(); + options.group = generateId(); + clientMap.set(client.sessionId, {client, options}); + this.stats.push({ + clients: clientMap, + rank: options.rank, + waitingTime: 0, + options + }); + } else { + groupData.clients.set(client.sessionId, {client, options}); + groupData.rank = (groupData.rank + options.rank) / 2 + } + } + client.send("clients", groupData.clients.size); + } else { + let clientMap = new Map(); + clientMap.set(client.sessionId, {client, options}); + this.stats.push({ + clients: clientMap, + rank: options.rank, + waitingTime: 0, + options + }); + } + + + client.send("clients", 1); + } + + createGroup() { + let group: MatchmakingGroup = {clients: [], averageRank: 0, count: 0}; + this.groups.push(group); + return group; + } + + redistributeGroups() { + // re-set all groups + this.groups = []; + + const stats = this.stats.sort((a, b) => a.rank - b.rank); + + let currentGroup: MatchmakingGroup = this.createGroup(); + let totalRank = 0; + // 先过滤一边组队的情况 + for (let i = 0, l = stats.length; i < l; i++) { + if (stats[i].clients.size == 1) { + continue; + } + const stat = stats[i]; + stat.waitingTime += this.clock.deltaTime; + if (stat.group && stat.group.ready) { + continue; + } + if (currentGroup.averageRank > 0) { + const diff = Math.abs(stat.rank - currentGroup.averageRank); + const diffRatio = (diff / currentGroup.averageRank); + + /** + * figure out how to identify the diff ratio that makes sense + */ + if (diffRatio > this.maxDiffRatio) { + currentGroup = this.createGroup(); + totalRank = 0; + } + } + + stat.group = currentGroup; + currentGroup.clients.push(stat); + currentGroup.count += stat.clients.size; + + totalRank += stat.rank; + currentGroup.averageRank = totalRank / currentGroup.clients.length; + if (currentGroup.count === this.numClientsToMatch) { + currentGroup.ready = true; + currentGroup = this.createGroup(); + totalRank = 0; + } + } + totalRank = 0; + currentGroup = this.createGroup(); + for (let i = 0, l = stats.length; i < l; i++) { + + const stat = stats[i]; + + if (stats[i].clients.size == 1) { + stat.waitingTime += this.clock.deltaTime; + } else { + if (stat.waitingTime < this.groupAddOneTime) { + continue; + } + } + /** + * do not attempt to re-assign groups for clients inside "ready" groups + */ + if (stat.group && stat.group.ready) { + continue; + } + + /** + * Force this client to join a group, even if rank is incompatible + */ + // if ( + // this.maxWaitingTimeForPriority !== undefined && + // stat.waitingTime >= this.maxWaitingTimeForPriority + // ) { + // currentGroup.priority = true; + // } + + if ( + currentGroup.averageRank > 0 && + !currentGroup.priority + ) { + const diff = Math.abs(stat.rank - currentGroup.averageRank); + const diffRatio = (diff / currentGroup.averageRank); + + /** + * figure out how to identify the diff ratio that makes sense + */ + if (diffRatio > this.maxDiffRatio) { + currentGroup = this.createGroup(); + totalRank = 0; + } + } + + stat.group = currentGroup; + currentGroup.clients.push(stat); + currentGroup.count += stat.clients.size; + + totalRank += stat.rank; + currentGroup.averageRank = totalRank / currentGroup.count; + + if ( + (currentGroup.count === this.numClientsToMatch) || + + /** + * Match long-waiting clients with bots + * FIXME: peers of this group may be entered short ago + */ + (stat.waitingTime >= this.maxWaitingTime && this.allowUnmatchedGroups) + ) { + currentGroup.ready = true; + currentGroup = this.createGroup(); + totalRank = 0; + } + } + + this.checkGroupsReady(); + } + + generateSeat(index: number): number { + switch (index) { + case 0: + return 0; + case 1: + return 4; + case 2: + return 1; + case 3: + return 2; + } + } + async checkGroupsReady() { + await Promise.all( + this.groups + .map(async (group) => { + if (group.ready) { + group.confirmed = 0; + + /** + * Create room instance in the server. + */ + const room = await matchMaker.createRoom(this.roomToCreate, {rank: true, count: this.numClientsToMatch - group.count}); + // TODO: 预处理数据, 确定座次 + let hasGroup = false; + for (let client of group.clients) { + if (client.clients.size > 1) { + hasGroup = true; + break; + } + } + let seat = 0; + if (hasGroup) { + for (let client of group.clients) { + if (client.clients.size > 1) { + for (let [,sub] of client.clients) { + sub.seat = this.generateSeat(seat ++); + } + } + } + for (let client of group.clients) { + if (client.clients.size == 1) { + for (let [,sub] of client.clients) { + sub.seat = this.generateSeat(seat ++); + } + } + } + } else { + group.clients.sort((a, b) => a.rank - b.rank); + for (let client of group.clients) { + for (let [,sub] of client.clients) { + sub.seat = seat ++; + } + } + } + + + await Promise.all(group.clients.map(async (client) => { + const matchData = await matchMaker.reserveSeatFor(room, client.options); + + /** + * Send room data for new WebSocket connection! + */ + for (let [,data] of client.clients) { + let options: any = {seat: data.seat, rank: data.options.rank}; + Object.assign(options, matchData); + data.client.send("match_success", options); + } + })); + + // /** + // * Cancel & re-enqueue clients if some of them couldn't confirm connection. + // */ + // group.cancelConfirmationTimeout = this.clock.setTimeout(() => { + // group.clients.forEach(stat => { + // this.send(stat.client, 0); + // stat.group = undefined; + // stat.waitingTime = 0; + // }); + // }, this.cancelConfirmationAfter); + + } else { + /** + * Notify all clients within the group on how many players are in the queue + */ + group.clients.forEach(client => { + for (let [,data] of client.clients) { + data.client.send("clients", group.count); + } + }); + } + }) + ); + } + + onLeave(client: Client, consented: boolean) { + const stat = this.stats.find(stat => stat.clients.has(client.sessionId)); + if (stat.clients.size > 1) { + stat.clients.delete(client.sessionId); + let data = [...stat.clients.values()][0]; + stat.rank = data.options.rank; + } else { + this.stats.remove(stat); + } + } + + onDispose() { + } + +} diff --git a/src/rooms/commands/NextTurnCommand.ts b/src/rooms/commands/NextTurnCommand.ts index b4fe1c9..83f0ccf 100644 --- a/src/rooms/commands/NextTurnCommand.ts +++ b/src/rooms/commands/NextTurnCommand.ts @@ -12,42 +12,42 @@ import {GameEnv} from "../../cfg/GameEnv"; */ export class NextTurnCommand extends Command { - async execute(){ - this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW); - const players = [...this.state.players.values()]; - players.sort((a, b) => a.idx - b.idx); - const sessionIds = players.map(p => p.id); - if (!this.state.currentTurn) { - this.state.round = 0; - } - // 如果上一轮是最后一个玩家, 则round + 1; - if (this.state.currentTurn - && sessionIds.indexOf(this.state.currentTurn) == (sessionIds.length - 1)) { - this.state.round += 1; - // 所有玩家根据配置, 增加5点灵活值 - if (this.state.round > 0) { - let moreRoundTime = new GameEnv().roundExtTime * 1000; - let maxTime = new GameEnv().maxExtTime * 1000; - for (let [, p] of this.state.players) { - p.extraTime = Math.min(p.extraTime + moreRoundTime, maxTime); - } - } - } - this.state.updateGameTurn((this.state.currentTurn) - ? sessionIds[(sessionIds.indexOf(this.state.currentTurn) + 1) % sessionIds.length] - : sessionIds[0]); - let player = this.state.players.get(this.state.currentTurn); - player.cardQueue.clear(); - if (!player) { - error('未找到玩家'); - } - let time = this.room.battleMan.onPlayerRoundStart(player); - await this.delay(time); - if (player.state == PlayerStateConst.PLAYER_DEAD) { - return [new TurnEndCommand()]; - } else { - return [new DrawCommand()] - } + async execute() { + this.state.updateGameState(GameStateConst.STATE_BEGIN_DRAW); + const players = [...this.state.players.values()]; + players.sort((a, b) => a.idx - b.idx); + const sessionIds = players.map(p => p.id); + if (!this.state.currentTurn) { + this.state.round = 0; } + // 如果上一轮是最后一个玩家, 则round + 1; + if (this.state.currentTurn + && sessionIds.indexOf(this.state.currentTurn) == (sessionIds.length - 1)) { + this.state.round += 1; + // 所有玩家根据配置, 增加5点灵活值 + if (this.state.round > 0) { + let moreRoundTime = new GameEnv().roundExtTime * 1000; + let maxTime = new GameEnv().maxExtTime * 1000; + for (let [, p] of this.state.players) { + p.extraTime = Math.min(p.extraTime + moreRoundTime, maxTime); + } + } + } + this.state.updateGameTurn((this.state.currentTurn) + ? sessionIds[(sessionIds.indexOf(this.state.currentTurn) + 1) % sessionIds.length] + : sessionIds[0]); + let player = this.state.players.get(this.state.currentTurn); + player.cardQueue.clear(); + if (!player) { + error('未找到玩家'); + } + let time = this.room.battleMan.onPlayerRoundStart(player); + await this.delay(time); + if (player.state == PlayerStateConst.PLAYER_DEAD) { + return [new TurnEndCommand()]; + } else { + return [new DrawCommand()] + } + } } diff --git a/src/rooms/commands/OnJoinCommand.ts b/src/rooms/commands/OnJoinCommand.ts index aee1a82..f983af5 100644 --- a/src/rooms/commands/OnJoinCommand.ts +++ b/src/rooms/commands/OnJoinCommand.ts @@ -9,43 +9,60 @@ import {GameEnv} from "../../cfg/GameEnv"; * 玩家成功加入房间 */ export class OnJoinCommand extends Command { - execute({client, accountId} = this.payload) { - let count = this.state.players.size; - let team = (count == 1 || count == 2)? 1 : 0; - let player = new Player(client.sessionId, count, team); - if (accountId && accountId == 'robot') { - accountId = `robot_${client.sessionId}`; - } else if (!accountId) { - accountId = `player_${client.sessionId}`; + execute({client, accountId, seat} = this.payload) { + let count = this.state.players.size; + if (count >= this.room.maxClients) { + return; + } + if (accountId && accountId == 'robot') { + accountId = `robot_${client.sessionId}`; + } else if (!accountId) { + accountId = `player_${client.sessionId}`; + } + let team = 0; + let idx = count; + if (seat != undefined) { + idx = +seat; + } else { + team = (count == 1 || count == 2) ? 1 : 0; + } + let player = new Player(client.sessionId, idx, team); + player.accountId = accountId; + this.state.players.set(client.sessionId, player); + this.room.addAssistClient(client.sessionId); + let self = this; + if (this.room.clientCount() == 1) { + // 正常的匹配逻辑进入的第一个玩家, 开启定时, 超过设定时间人没齐的话, 添加机器人 + let timeOutWaitingPlayer = function () { + let count = self.room.maxClients - self.room.clientCount(); + if (count > 0) { + for (let i = 0; i < count; i++) { + self.room.addRobot(); + } } - player.accountId = accountId; - this.state.players.set(client.sessionId, player); - this.room.addAssistClient(client.sessionId); - let self = this; - if (this.room.clientCount() == 1) { - // 正常的匹配逻辑进入的第一个玩家, 开启定时, 超过设定时间人没齐的话, 添加机器人 - let timeOutWaitingPlayer = function () { - let count = self.room.maxClients - self.room.clientCount(); - if (count > 0) { - for (let i = 0; i < count; i++) { - self.room.addRobot(); - } - } - } - let time = new GameEnv().waitingPlayerTime * 1000; - self.room.beginSchedule(time, timeOutWaitingPlayer, 'waiting_player'); - } else if (this.room.clientCount() > 1 && this.room.clientCount() < this.room.maxClients) { - let moreTime = new GameEnv().waitingPlayerOnePlus * 1000; - self.room.addScheduleTime(moreTime, 'play_join', 'waiting_player') - } - if (this.state.players.size >= this.room.maxClients) { - this.room.lock().then(() => {}); - this.state.updateGameState(GameStateConst.STATE_WAIT_PREPARE); - } - this.room.bUserJoin(`${client.sessionId}`, {except: client}); + } + let time = new GameEnv().waitingPlayerTime * 1000; + self.room.beginSchedule(time, timeOutWaitingPlayer, 'waiting_player'); + } else if (this.room.clientCount() > 1 && this.room.clientCount() < this.room.maxClients) { + let moreTime = new GameEnv().waitingPlayerOnePlus * 1000; + self.room.addScheduleTime(moreTime, 'play_join', 'waiting_player') + } + if (this.state.players.size >= this.room.maxClients) { + this.room.lock().then(() => { + }); + this.state.updateGameState(GameStateConst.STATE_WAIT_PREPARE); + } else if (this.state.players.size < this.room.maxClients + && this.state.players.size >= this.room.maxClients - this.room.robotCount) { + for (let i = 0; i < this.room.robotCount; i++) { + self.room.addRobot(); + } } + this.room.bUserJoin(`${client.sessionId}`, {except: client}); + } + } diff --git a/src/rooms/schema/Player.ts b/src/rooms/schema/Player.ts index d26b9d1..f143598 100644 --- a/src/rooms/schema/Player.ts +++ b/src/rooms/schema/Player.ts @@ -1,4 +1,11 @@ -import {ArraySchema, filter, MapSchema, Schema, SetSchema, type} from "@colyseus/schema"; +import { + ArraySchema, + filter, + MapSchema, + Schema, + SetSchema, + type +} from "@colyseus/schema"; import {Pet} from "./Pet"; import {Card} from "./Card"; import {GameEnv} from "../../cfg/GameEnv"; @@ -7,107 +14,112 @@ import {StateTypeEnum} from "../enums/StateTypeEnum"; export class Player extends Schema { - @type("string") - id: string; + @type("string") + id: string; - accountId: string; + accountId: string; + /** + * 用于组队匹配时候队伍的标记 + * @type {string} + */ + group?: string; - @type("number") - heroId: number; - /** - * 手牌 - */ - @filter(function(this: Player, client, value, root) { - return (client.sessionId == this.id); - }) - @type({ map: Card }) - cards = new MapSchema(); + @type("number") + heroId: number; + /** + * 手牌 + */ + @filter(function (this: Player, client, value, root) { + return (client.sessionId == this.id); + }) + @type({map: Card}) + cards = new MapSchema(); - @type({ set: "string" }) - cardSet = new SetSchema(); + @type({set: "string"}) + cardSet = new SetSchema(); - /** - * 玩家出的牌 - */ - @type([Card]) - cardQueue = new ArraySchema(); - /** - * 当前hp - */ - @type("number") - hp: number; + /** + * 玩家出的牌 + */ + @type([Card]) + cardQueue = new ArraySchema(); + /** + * 当前hp + */ + @type("number") + hp: number; - /** - * 状态 - * 0: 正常状态 - * 1: 已准备好 - * 2: 死亡 - * 3: 已换完牌 - * 4: 玩家已选择英雄 - * 9: 掉线 - */ - @type("number") - state: number; - /** - * 随从 - */ - @type({ map: Pet }) - pets = new MapSchema(); + /** + * 状态 + * 0: 正常状态 + * 1: 已准备好 + * 2: 死亡 + * 3: 已换完牌 + * 4: 玩家已选择英雄 + * 9: 掉线 + */ + @type("number") + state: number; + /** + * 随从 + */ + @type({map: Pet}) + pets = new MapSchema(); - /** - * 队伍 - */ - @type("number") - team: number; + /** + * 队伍 + */ + @type("number") + team: number; - @type("number") - idx: number; - /** - * 玩家灵活时限, 客户端每次显示倒计时的时候, 需读取该时间 - */ - @type("number") - extraTime: number; - /** - * 当前游戏总抽卡数量 - */ - countTotal: number; - /** - * 当前累计抽卡 - */ - countPresent: number; - /** - * 记录当前局内统计数字 - */ - statData: Map = new Map(); - /** - * 用于记录pet值, 用于统计伤害 - */ - petData: Map = new Map(); + @type("number") + idx: number; + /** + * 玩家灵活时限, 客户端每次显示倒计时的时候, 需读取该时间 + */ + @type("number") + extraTime: number; + /** + * 当前游戏总抽卡数量 + */ + countTotal: number; + /** + * 当前累计抽卡 + */ + countPresent: number; + /** + * 记录当前局内统计数字 + */ + statData: Map = new Map(); + /** + * 用于记录pet值, 用于统计伤害 + */ + petData: Map = new Map(); - /** - * 英雄绑定的卡组, 选好英雄后, 从默认配置或玩家卡组(待实现)中获取 - * key = 效果卡id - * val = weight - */ - @type({ map: "number" }) - unitCfgs = new MapSchema(); + /** + * 英雄绑定的卡组, 选好英雄后, 从默认配置或玩家卡组(待实现)中获取 + * key = 效果卡id + * val = weight + */ + @type({map: "number"}) + unitCfgs = new MapSchema(); - constructor(id: string, idx: number, team: number) { - super(); - this.id = id; - this.state = PlayerStateConst.PLAYER_NORMAL; - this.hp = 0; - this.idx = idx; - this.heroId = 0; - this.team = team; - this.countTotal = 0; - this.countPresent = 0; - for (let i = 0; i < new GameEnv().maxPlayerPetCount + 1; i++) { - let pet = new Pet(i); - pet.state = 0; - pet.isHero = i === 0; - this.pets.set(i+'', pet); + constructor(id: string, idx: number, team: number) { + super(); + this.id = id; + this.state = PlayerStateConst.PLAYER_NORMAL; + this.hp = 0; + this.idx = idx; + this.heroId = 0; + this.team = team; + this.countTotal = 0; + this.countPresent = 0; + for (let i = 0; i < new GameEnv().maxPlayerPetCount + 1; i++) { + let pet = new Pet(i); + pet.state = 0; + pet.isHero = i === 0; + this.pets.set(i + '', pet); - } } + } }