package room import ( "errors" "github.com/google/uuid" "math/rand" "samba/pkg/log" "samba/pkg/servername" "samba/proto" "samba/server/cacheta/poker" . "samba/server/cacheta/service" . "samba/server/game/baseroom" . "samba/server/game/player" "samba/stub" "samba/util/event" "samba/util/model" "samba/util/routingKey" "samba/util/state" "samba/util/util" "time" ) const ( WaitRealPlayerTimeout = 30 // 30秒等待真人 RandRobotTimeout = 10 // 10秒内随机机器人 ) func NewPlayer(uid int64) (*Player, error) { p := &Player{UserInfo: &model.UserInfo{UID: uid}} return p, p.Load() } type GameRoom struct { *BaseRoom[*GameSeat] *event.Events pokers poker.IPokerGenerator pickPokers []*poker.Poker // 摸牌区 outPokers []*poker.Poker // 弃牌区 dealer int // 庄家座位 current int // 当前行动人座位 roundStartPos int // 本轮中最开始的位置,用于比牌 round int // 当前轮 gameStep GameStep // 游戏状态 gameNo string // 本局游戏唯一id startTime time.Time endTime time.Time record *TrucoGameRecord // 战绩玩家查询 gameLog *TrucoGameLog // 游戏操作汇总给后台 smallGameCount int // 记录第几小局 waitSecond int // 记录第一个入座玩家等待了几秒,用于分配机器人 } func NewGameRoom(id, roomType, clubId int) (IRoom, proto.ErrorCode) { baseRoom, code := NewBaseRoom[*GameSeat](id, roomType, clubId) if code != proto.Ok { return nil, code } room := &GameRoom{ BaseRoom: baseRoom, Events: event.NewEvents(), pokers: poker.NewCachetaPokers(), dealer: 0, current: 0, round: 0, gameStep: GsReadyGame, gameNo: uuid.NewString(), //record: NewTrucoGameRecord(), smallGameCount: 0, } if room.pokers == nil { log.Error(room.Log("create room fail.play type:%v", baseRoom.RoomCnf.PlayType)) return nil, proto.Internal } room.SetTruthRoom(room) room.SetTimerHandler(room) if room.RoomCnf.CathetaInitPoint == 0 { room.RoomCnf.CathetaInitPoint = 5 } for i := 0; i < room.RoomCnf.MinPlayers; i++ { room.Seats = append(room.Seats, &GameSeat{ BaseSeat: NewBaseSeat(i), pokers: make([]*poker.Poker, 0), //ActType: AtUnknown, Point: room.RoomCnf.CathetaInitPoint, }) } room.dealer = int(rand.Int31n(int32(len(room.Seats)))) room.current = room.dealer room.roundStartPos = room.current if err := CathetaService.QueueBind(QueueName(), routingKey.RoomKey(room.Id()), util.Direct(servername.Cacheta)); err != nil { log.Error(err.Error()) } return room, code } // 获取金币或者俱乐部币 func (r *GameRoom) getCoin(uid int64) (coins int64) { op := model.NewUserResourceOp() coins, _ = op.Get(uid, util.Tie[string](r.ClubId() == 0, model.ResCoins, model.ResClubUserScore)) return coins } // 获取金币或者俱乐部币 func (r *GameRoom) getTakeCoin(player *Player) (coins int64) { return util.Tie(r.ClubId() == 0, player.TakeCoin, player.TakeClubCoin) } func (r *GameRoom) addTakeCoin(player *Player, add int64, reason string) { // holdTakeCoin在多开时,是多个房间的总携带,不是本房间的携带 if r.ClubId() == 0 { if holdTakeCoin, ok := model.NewUserResourceOp().AddTakeCoin(player.UID, add); ok { player.TakeCoin += add res := &proto.ReqAddResource{ UserId: player.UID, ResValue: add, ResType: model.ResTakeCoins, Reason: reason, RoomId: r.Id(), RoomType: r.Type(), GameNo: r.gameNo, Desc: "", IsNotify: true, } model.NewResourceRecord(res, holdTakeCoin).Flush(CathetaService) } } else { log.Error(r.Log("club room:%v has not take_club_coin.room_type:%v play_type:%v club:%v ", r.Id(), r.Type(), r.PlayType(), r.ClubId())) //if holdTakeCoin, ok := model.NewUserResourceOp().AddClubTakeCoin(player.UID, add); ok { // player.TakeCoin += add // res := &proto.ReqAddResource{ // UserId: player.UID, // ResValue: add, // ResType: model.ResTakeClubCoins, // Reason: reason, // RoomId: r.Id(), // RoomId: r.Type(), // GetGameNo: r.gameNo, // Desc: "", // } // model.NewResourceRecord(res, holdTakeCoin).Flush(CathetaService) //} } } func (r *GameRoom) addResource(player *Player, add int64, resType, reason string) { if resType == model.ResTakeCoins { r.addTakeCoin(player, add, reason) } else { resType = util.Tie[string](r.ClubId() == 0, model.ResCoins, model.ResClubUserScore) router := routingKey.DbKey(player.UID) SendMsgToDb(router, player.UID, proto.ReqAddResourceId, &proto.ReqAddResource{ UserId: player.UID, ResValue: add, ResType: resType, Reason: reason, RoomId: r.Id(), RoomType: r.Type(), GameNo: r.gameNo, Desc: "", IsNotify: true, }) } } // 大局之前初始化 func (r *GameRoom) ReInit() { RoomMgr.Add(r) r.startTime = time.Now() r.record = NewTrucoGameRecord() var err error var playingNum int64 for _, seat := range r.Seats { if !seat.Empty() && !seat.Player().IsRobot() { num, err1 := model.AddRoomTypePlayerNum(r.RoomCnf, seat.Player().UID) if err1 != nil { err = errors.Join(err1) } else { playingNum = num } seat.Point = r.RoomCnf.CathetaInitPoint } } if err == nil && r.RoomCnf.Mode != int(stub.RmClub) { // 金币场 广播虚假在玩人数 BroadcastMsg(proto.NtfPlayingNumId, &proto.NtfPlayingNum{RoomType: r.RoomCnf.Id, PlayingNum: model.CalculateFakeCount(playingNum)}) } r.record.AddPlayers(r) r.gameLog = NewTrucoGameLog(r) } func (r *GameRoom) OnMessage(msgId string, msg map[string]interface{}) { switch msgId { case proto.ReqReadyGameId: r.onReady(msg) case proto.ReqEmoteId: r.onEmote(msg) case proto.ReqLeaveRoomId: r.onLeaveRoom(msg) case proto.ReqEnterRoomId: r.onEnterRoom(msg) case proto.ReqCachetaTidyPokerId: r.onTidyPoker(msg) case proto.ReqCachetaPickPokerId: r.onPickPoker(msg) case proto.ReqCachetaOutPokerId: r.onOutPoker(msg) case proto.ReqCachetaHuId: r.onHu(msg) case proto.ReqCachetaReconnectId, proto.ReqReconnectId: r.onReconnect(msg) case proto.NtfMaintainId: r.onMaintain(msg) case proto.ReqDisbandRoomId: r.onDisbandRoom(msg) } } func (r *GameRoom) FindSeat(uid int64) *GameSeat { for _, seat := range r.Seats { if !seat.Empty() && seat.Player().UID == uid { return seat } } return nil } func (r *GameRoom) GetSeat(uid int64) *GameSeat { st := r.FindSeat(uid) if st != nil { return st } log.Error(r.Log("player:%d not in any seat", uid)) return nil } func (r *GameRoom) GetSeatNo(uid int64) int { for _, seat := range r.Seats { if !seat.Empty() && seat.Player().UID == uid { return seat.No() } } log.Error(r.Log("player:%d is not in any seat", uid)) return -1 } func (r *GameRoom) groupHighToHandPoker(groups []*poker.GroupPokers, high []*poker.Poker) proto.CachetaHandPoker { var handPoker proto.CachetaHandPoker for _, group := range groups { g := proto.GroupPoker{} for _, pk := range group.Pokers { g.Pokers = append(g.Pokers, pk.ToInt()) } handPoker.Groups = append(handPoker.Groups, &g) } for _, h := range high { handPoker.Pokers = append(handPoker.Pokers, h.ToInt()) } return handPoker } // 发牌 func (r *GameRoom) testStraight() []*poker.Poker { var pks []*poker.Poker pks = append(pks, poker.NewPokerEx(poker.Club, poker.PointA, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point2, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point3, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point4, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point5, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point6, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point7, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Club, poker.Point8, poker.Ext1)) pks = append(pks, poker.NewPokerEx(poker.Diamond, poker.Point9, poker.Ext1)) return pks } // 发牌 func (r *GameRoom) dealPoker() { r.gameStep = GsDealPoker r.pokers.Shuffle() begin := 0 for _, seat := range r.Seats { if seat.Empty() { continue } seat.pokers = append(seat.pokers, r.pokers.Pokers()[begin:begin+PlayerPokersNum]...) // 调整手牌 groups, high := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), seat.pokers) seat.handPoker = r.groupHighToHandPoker(groups, high) log.Debug(r.SeatLog(seat, "玩家手牌:%v", poker.PokersToString(seat.pokers))) begin = begin + PlayerPokersNum } r.outPokers = append(r.outPokers, r.pokers.Pokers()[begin]) begin += 1 r.pickPokers = r.pokers.Pokers()[begin:] log.Debug(r.Log("所有牌数量:%v, 摸牌区数据:%v, begin:%v", len(r.pokers.Pokers()), len(r.pickPokers), begin)) } func (r *GameRoom) playerPokersToPorto(seatNo int) []*proto.CachetaHandPoker { var handPokers []*proto.CachetaHandPoker for _, seat := range r.Seats { if seat.Empty() { handPokers = append(handPokers, &proto.CachetaHandPoker{Pokers: make([]int, 0)}) continue } if seat.No() == seatNo { handPokers = append(handPokers, &seat.handPoker) } else { handPokers = append(handPokers, &proto.CachetaHandPoker{Pokers: make([]int, PlayerPokersNum)}) } } return handPokers } // 开始游戏 func (r *GameRoom) GameStart() { r.BaseRoom.GameStart() RoomMgr.CreateClubRoom(r.ClubId(), r.Type(), NewGameRoom) r.smallGameStart() } // 小局游戏开始 func (r *GameRoom) smallGameStart() { r.smallGameCount++ log.Debug(r.Log("第%d小局,开始发牌", r.smallGameCount)) r.record.NewGame() r.SetStatus(state.RsGaming) exist := false for _, st := range r.Seats { st.ReInit() if st.Empty() { continue } // 清理分数为0的玩家信息 if st.Point < 1 { r.leaveRoom(st, true) } else { exist = true r.TriggerClubMemberPlaying(st.Player().UID, true) } } if !exist { log.Error(r.Log("房间所有位置都是空,无法开始")) return } r.current = r.dealer r.NextSeat() r.dealer = r.current r.roundStartPos = r.current r.round = 0 r.dealPoker() r.notifyDealPoker() r.NewTimer(TtDealPoker, time.Duration(stub.GGlobal.TrucoDealPokerTime)*time.Second, r.CurrentPlayer()) } // 广播发牌 func (r *GameRoom) notifyDealPoker() { // 向玩家发送消息 rsp := &proto.NtfDealCachetaPokers{ Time: 0, CuttingPoker: 0, WildPoker: nil, Poker: nil, Dealer: r.dealer, DropPoker: r.outPokers[0].ToInt(), PickPokerNum: len(r.pickPokers), GameCount: r.smallGameCount, } if r.pokers.CutPoker() != nil { rsp.CuttingPoker = r.pokers.CutPoker().ToInt() } for _, pk := range r.pokers.WildPokers() { rsp.WildPoker = append(rsp.WildPoker, pk.ToInt()) } for _, seat := range r.Seats { if !seat.Empty() { rsp.Seat = seat.No() rsp.Poker = r.playerPokersToPorto(seat.No()) r.SendMsg(seat.Player(), proto.NtfDealCachetaPokersId, rsp, false) } } log.Debug(r.SeatLog(r.Seats[r.dealer], "庄家:%v", r.dealer)) rsp.Seat = 0 rsp.Poker = nil for _, seat := range r.Seats { if !seat.Empty() { rsp.Poker = append(rsp.Poker, &seat.handPoker) } } // 牌局回顾 r.record.AddAction(proto.NtfDealCachetaPokersId, rsp) } func (r *GameRoom) CurrentPlayer() *Player { return r.Seats[r.current].Player() } func (r *GameRoom) CurrentSeat() *GameSeat { return r.Seats[r.current] } func (r *GameRoom) NextSeat() { for i := r.current + 1; i < r.current+len(r.Seats); i++ { seatNo := i % len(r.Seats) if seatNo == r.current { break } if !r.Seats[seatNo].Empty() { r.current = seatNo r.Seats[r.current].ActType = SatPickPokerOrWin break } } } //func (r *GameRoom) CutLineSeat() *GameSeat { // for i := r.current + 1; i < r.current+len(r.Seats); i++ { // seatNo := i % len(r.Seats) // if seatNo == r.current { // break // } // if !r.Seats[seatNo].Empty() && r.Seats[seatNo].TingHu { // return r.Seats[seatNo] // } // } // return nil //} func (r *GameRoom) AutoPlayerPickPoker() { if r.Seats[r.current].Player().IsRobot() { log.Error(r.SeatLog(r.Seats[r.current], "robot can not trigger timer auto out poker")) } req := proto.ReqCachetaPickPoker{} log.Debug(r.SeatLog(r.Seats[r.current], "自动摸牌")) msg := util.MakeMessage(proto.ReqCachetaPickPokerId, req, r.CurrentPlayer().UID, r.Id()) r.OnMessage(proto.ReqCachetaPickPokerId, msg) } func (r *GameRoom) copyHandPoker(hp proto.CachetaHandPoker) proto.CachetaHandPoker { var handPoker proto.CachetaHandPoker for _, g := range hp.Groups { var ng proto.GroupPoker ng.Pokers = append(ng.Pokers, g.Pokers...) handPoker.Groups = append(handPoker.Groups, &ng) } handPoker.Pokers = append(handPoker.Pokers, hp.Pokers...) return handPoker } func (r *GameRoom) AutoPlayerOutPoker() { if r.Seats[r.current].Player().IsRobot() { log.Error(r.SeatLog(r.Seats[r.current], "robot can not trigger timer auto out poker")) } seat := r.Seats[r.current] // 有牌则出牌 if len(seat.pokers) > 0 { outPoker := 0 if len(seat.handPoker.Pokers) != 0 { pk := seat.handPoker.Pokers[len(seat.handPoker.Pokers)-1] for _, p := range seat.pokers { if pk == p.ToInt() { outPoker = pk break } } } if outPoker == 0 { for i := len(seat.handPoker.Groups) - 1; i >= 0; i-- { if len(seat.handPoker.Groups[i].Pokers) != 0 { pks := seat.handPoker.Groups[i].Pokers pk := pks[len(pks)-1] for _, p := range seat.pokers { if pk == p.ToInt() { outPoker = pk break } } if outPoker != 0 { break } } } } if outPoker == 0 { outPoker = seat.pokers[len(seat.pokers)-1].ToInt() } req := proto.ReqCachetaOutPoker{Poker: outPoker} log.Debug(r.SeatLog(r.Seats[r.current], "自动出牌:%v", poker.NewPoker(req.Poker).ToString())) msg := util.MakeMessage(proto.ReqCachetaOutPokerId, req, r.CurrentPlayer().UID, r.Id()) r.OnMessage(proto.ReqCachetaOutPokerId, msg) return } log.Error(r.SeatLog(r.Seats[r.current], "poker was empty")) } func (r *GameRoom) OnTimer(timerType TimerType, args ...interface{}) { r.CancelTimer(timerType) switch timerType { case TtSecond: r.onWaitTimer() case TtGameReadyStart: if r.SeatPlayerNum() >= r.RoomCnf.MinPlayers && r.Status() != state.RsGaming { r.ReInit() r.GameStart() } case TtDealPoker: r.PlayerAct(r.CurrentPlayer()) case TtPlayerAct: user := args[0].(*Player) if r.CurrentPlayer() == user { seat := r.CurrentSeat() if seat.ActType == SatPickPokerOrWin { r.AutoPlayerPickPoker() } else { r.AutoPlayerOutPoker() } } else { log.Error(r.SeatLog(r.Seats[r.current], " is not user:%v", user.UID)) } case TtPlayerCutLine: r.NextSeat() r.PlayerAct(r.CurrentPlayer()) case TtNextGame: r.smallGameStart() } } // PlayerAct 通知玩家行动 func (r *GameRoom) PlayerAct(user *Player) { seat := r.GetSeat(user.UID) if seat.No() != r.current { log.Error(r.SeatLog(r.GetSeat(user.UID), "is not current:%v", r.current)) return } r.gameStep = GsPlayerAct r.notifyPlayerAct(seat) r.NewTimer(TtPlayerAct, time.Duration(r.RoomCnf.ActTime+stub.GGlobal.TrucoActExTime)*time.Second, user) } // 通知玩家行动 func (r *GameRoom) notifyPlayerAct(seat *GameSeat) { // 向玩家发送消息 rsp := &proto.NtfPlayerCachetaAct{ RoomId: r.Id(), Seat: r.current, Countdown: r.RoomCnf.ActTime, CountdownEx: stub.GGlobal.TrucoActExTime, Type: int(seat.ActType), CanWin: 0, } _, highPokers := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), seat.pokers) if len(highPokers) == 0 || (len(seat.pokers) == poker.HandlerPokerCount+1 && len(highPokers) == 1) { rsp.CanWin = 1 } log.Debug(r.SeatLog(seat, "玩家行动:%s", seat.ActType.String())) r.Broadcast(proto.NtfPlayerCachetaActId, rsp, true) // 牌局回顾 r.record.AddAction(proto.NtfPlayerCachetaActId, rsp) } // 结算 func (r *GameRoom) settlementBySeat(winSeat *GameSeat, highNum int) bool { point := util.Tie(highNum == 0, 2, 1) //winnerAddPoint := 0 // 通杀 bigWinner := 0 // 为1时表明全场只有庄家分数大于0 for _, st := range r.Seats { if st.Empty() { continue } if st.No() != winSeat.No() { st.Point = util.Tie(st.Point-point > 0, st.Point-point, 0) //winnerAddPoint += point //winSeat.Point += point } if st.Point > 0 { bigWinner++ } } // 结算消息 ntf := &proto.NtfCachetaGameSettle{ SeatSettle: nil, NextGame: util.Tie(bigWinner == 1, 0, 1), } for _, st := range r.Seats { if st.Empty() { continue } seatSettle := &proto.SeatSettle{Seat: st.No(), SumPoint: st.Point, HandPoker: st.HandPoker()} if st.No() != winSeat.No() { seatSettle.AddPoint = -point // 没分数,个人大结算 if st.Point < 1 { seatSettle.AddScore = -r.RoomCnf.Blind seatSettle.SumScore = 0 } } else { //seatSettle.AddPoint = winnerAddPoint // 最终胜利者,大结算 if bigWinner == 1 { seatSettle.AddScore += int64(r.RoomCnf.MinPlayers-1) * r.RoomCnf.Blind } } // 净胜分结算 if seatSettle.AddScore != 0 { if seatSettle.AddScore > 0 { tip := seatSettle.AddScore * int64(r.RoomCnf.Rate) / int64(100) r.gameLog.Player(st.No()).Tip = tip seatSettle.AddScore -= tip } log.Info(r.SeatLog(st, "赢取金币:%v", seatSettle.AddScore)) // 机器人输赢汇入总池 if st.Player().IsRobot() { model.AddRobotResourcePool(r.Type(), seatSettle.AddScore, util.Tie[string](r.ClubId() <= 0, model.ResCoins, model.ResClubUserScore)) } r.addResource(st.Player(), -r.RoomCnf.Blind, model.ResTakeCoins, model.ReasonGame) if seatSettle.AddScore != 0 { r.addResource(st.Player(), seatSettle.AddScore, model.ResCoins, model.ReasonGame) } } ntf.SeatSettle = append(ntf.SeatSettle, seatSettle) } r.Broadcast(proto.NtfCachetaGameSettleId, ntf, false) r.record.AddAction(proto.NtfCachetaGameSettleId, ntf) return bigWinner == 1 } // GameOver 本局游戏结束,会清理部分数据 func (r *GameRoom) GameOver(seat *GameSeat, highNum int) { r.gameStep = GsGameSettle //log.Debug(r.Log("cancel timer:all timer")) r.CancelAllTimer() clear(r.LastMsg) bigSettle := r.settlementBySeat(seat, highNum) if !bigSettle { // 开启下一局 r.NewTimer(TtNextGame, time.Duration(stub.GGlobal.TrucoNextGameTime)*time.Second) return } // 游戏结束 r.endTime = time.Now() // 牌局回顾 for _, st := range r.Seats { if !st.Empty() { r.record.AddPlayerRecord(r, st.Player().UID, r.getTakeCoin(st.Player()), r.getTakeCoin(st.Player()), r.ClubId()) } } r.record.Flush() r.gameLog.Flush(r) r.gameStep = GsWaitGame // 清理玩家信息 for _, st := range r.Seats { if !st.Empty() { r.leaveRoom(st, true) } } } // 离开房间 func (r *GameRoom) SetFakeLeave(uid int64, fake bool) { seat := r.GetSeat(uid) if seat == nil { log.Error(r.Log("player:%d seat is nil", uid)) return } if fake { log.Debug(r.SeatLog(seat, "玩家假离开, room status:%v", r.gameStep)) seat.SetFakeLeave(true) } else { log.Debug(r.SeatLog(seat, "玩家假离开后返回, room status:%v", r.gameStep)) seat.SetFakeLeave(false) } r.notifyFakeLeave(seat) } // 推送房间信息给玩家 func (r *GameRoom) notifyFakeLeave(seat *GameSeat) { ntf := &proto.NtfFakeLeave{ RoomId: r.Id(), CurrentSeat: seat.No(), FakeLeave: util.Tie(seat.FakeLeave(), 0, 1), } r.Broadcast(proto.NtfFakeLeaveId, ntf, false) } // 推送房间信息给玩家 func (r *GameRoom) notifyRoomInfo(user *Player) { ntf := r.makeProtoRoomInfo() r.SendMsg(user, proto.NtfCachetaRoomInfoId, ntf, false) } // 推送玩家信息给房间里的玩家 func (r *GameRoom) notifyMeToOthers(user *Player) { ntf := proto.NtfCachetaPlayerInfo{Players: make([]*proto.CachetaPlayer, 0)} ntf.Players = append(ntf.Players, r.makePlayerToProto(r.GetSeat(user.UID), true)) for _, seat := range r.Seats { if !seat.Empty() && seat.Player().UID != user.UID { r.SendMsg(seat.Player(), proto.NtfCachetaPlayerInfoId, ntf, false) } } } // 推送玩家信息给房间里的玩家 func (r *GameRoom) notifyOthersToMe(user *Player) { ntf := proto.NtfCachetaPlayerInfo{Players: make([]*proto.CachetaPlayer, 0)} for _, seat := range r.Seats { if !seat.Empty() { ntf.Players = append(ntf.Players, r.makePlayerToProto(seat, true)) } } r.SendMsg(user, proto.NtfCachetaPlayerInfoId, ntf, false) } // 推送玩家信息给房间里的玩家 func (r *GameRoom) notifyPlayerInfo(user *Player, isReentry bool) { if !isReentry { r.notifyMeToOthers(user) } r.notifyOthersToMe(user) } func (r *GameRoom) SetGmPokers(uid int64, pokers [][]int, ghostPoker int) bool { seatNo := 0 for _, seat := range r.Seats { if !seat.Empty() && seat.Player().UID == uid { seatNo = seat.No() break } } log.Debug(r.SeatLog(r.Seats[seatNo], "玩家:%v seat:%v 配牌:%+v 鬼牌:%v", uid, seatNo, pokers, ghostPoker)) r.pokers.SetGmPoker(pokers, ghostPoker, seatNo) r.pokers.Pokers() return true } func (r *GameRoom) SetRobotTemper(seatNo int, cnf stub.RobotTemper) { seat := r.Seats[seatNo] //user := seat.Player() if !seat.Empty() && seat.Player().IsRobot() { //switch cnf { //case stub.RteAplomb: // user.Robot = NewRobotAplomb(cnf, user.UserInfo, seat, r) //case stub.RteNormal: // user.Robot = NewRobotNormal(cnf, user.UserInfo, seat, r) //case stub.RteRadical: // user.Robot = NewRobotRadical(cnf, user.UserInfo, seat, r) //case stub.RteRotten: // user.Robot = NewRobotRotten(cnf, user.UserInfo, seat, r) //case stub.RteEvaluation: // user.Robot = NewRobotEvaluation(cnf, user.UserInfo, seat, r) //default: // log.Error(r.SeatLog(seat, "初始化机器人失败:%v", cnf.String())) //} log.Debug(r.SeatLog(seat, "初始化%v机器人", cnf.String())) } } func (r *GameRoom) StopWaitTimer() { r.CancelTimer(TtSecond) r.waitSecond = 0 } func (r *GameRoom) StartWaitTimer() { r.NewTimer(TtSecond, time.Second) } func (r *GameRoom) onWaitTimer() { r.waitSecond++ // 达到最少人数或者房间不是等待状态,结束等待定时器 if r.SeatPlayerNum() >= r.RoomCnf.MinPlayers || r.Status() != state.RsWait { r.StopWaitTimer() return } // 所有真人都离开,移除机器人 if r.RealPlayerNum() == 0 { for _, st := range r.Seats { if !st.Empty() { r.leaveRoom(st, false) } } return } /* 2.玩家点击匹配后,优先匹配真人: 1)低于30S(可修改)若匹配到最大人数则立即开始游戏 2)达到30S时若已经匹配到真人,无论匹配到几个,都立即开始游戏 3)超过30S依旧未匹配到真人,则匹配1个(可修改)AI,并继续匹配10S(可修改),10S内若匹配到真人则立即开始游戏,若没有匹配到真人达到10S时也立即开始游戏 */ if r.RobotPlayerNum() == 0 && r.waitSecond > WaitRealPlayerTimeout && r.waitSecond > WaitRealPlayerTimeout+rand.Intn(RandRobotTimeout) { if robot := RobotMgr.Pop(r.RoomCnf); robot != nil { r.OnEnterRoom(robot) } } r.NewTimer(TtSecond, time.Second) }