package room import ( "fmt" "math/rand" "samba/pkg/log" "samba/proto" "samba/server/game/baseroom" "samba/server/truco/poker" . "samba/server/truco/service" "samba/stub" "samba/util/model" "samba/util/util" "sort" "time" ) const RobotOnMsgTime = 2 const RobotOnMsgRandTime = 5 type BaseRobot struct { *model.UserInfo cnf stub.RobotTemper seat *TrucoSeat room *TrucoRoom emotePokerSeat map[int]struct{} timerTypes map[baseroom.TimerType]uint32 // timer类型对应的TimerId timerSecretEmote map[uint32]struct{} // 暗号表情定时器Id } func NewBaseRobot(cnf stub.RobotTemper, userInfo *model.UserInfo, seat *TrucoSeat, room *TrucoRoom) *BaseRobot { return &BaseRobot{UserInfo: userInfo, seat: seat, cnf: cnf, room: room, emotePokerSeat: map[int]struct{}{}, timerTypes: map[baseroom.TimerType]uint32{}, timerSecretEmote: map[uint32]struct{}{}, } } func (r *BaseRobot) Cnf() stub.RobotTemper { return r.cnf } func (r *BaseRobot) Clean() { for _, tid := range r.timerTypes { TrucoService.CancelTimer(tid) } for tid := range r.timerSecretEmote { TrucoService.CancelTimer(tid) } clear(r.timerTypes) clear(r.timerSecretEmote) clear(r.emotePokerSeat) } func (r *BaseRobot) GameClean() { for _, tid := range r.timerTypes { TrucoService.CancelTimer(tid) } for tid := range r.timerSecretEmote { TrucoService.CancelTimer(tid) } clear(r.timerTypes) clear(r.timerSecretEmote) clear(r.emotePokerSeat) } // NewTimer 新建定时器 func (r *BaseRobot) NewTimer(typ baseroom.TimerType, dur time.Duration, f func()) { if _, ok := r.timerTypes[typ]; ok { // TtEmote是可能被重复设置的,但只有第一次设置的生效,不算error if typ != baseroom.TtEmote { log.Error(fmt.Sprintf("timer type:%s is exist.can not new timer", typ.String())) } return } r.timerTypes[typ] = TrucoService.NewTimer(dur, func() { delete(r.timerTypes, typ) f() }, true, fmt.Sprintf("start type:%s timer", typ.String())) } // NewSecretEmoteTimer 新建暗号表情定时器 func (r *BaseRobot) NewSecretEmoteTimer(dur time.Duration, f func()) { r.timerSecretEmote[TrucoService.NewTimer(dur, f, false)] = struct{}{} } func (r *BaseRobot) getRound() int { return r.room.round % 3 } // 查询是本轮第几号位出牌(1v1:0-1 2v2:0-3 3v3:0-5) func (r *BaseRobot) positionInOutPoker(room *TrucoRoom) int { pos := 0 for i := room.roundStartPos; i < len(room.Seats)+room.roundStartPos; i++ { if seat := room.Seats[i%len(room.Seats)]; !seat.Empty() && seat.Player().UID == r.UID { return pos } pos++ } log.Error(room.SeatLog(room.GetSeat(r.UID), "round pos error")) return -1 } // 手牌比队友大 func (r *BaseRobot) handlePokerBiggerThenTeammate(room *TrucoRoom) bool { if !room.IsMVM() { return true } pk, _ := poker.FindMaxPoker(room.pokers, r.seat.Pokers) teammates := room.Teammate(r.seat.No()) var teammatePokers []*poker.Poker for _, st := range teammates { teammatePokers = append(teammatePokers, st.Pokers...) } pkT, _ := poker.FindMaxPoker(room.pokers, teammatePokers) return room.pokers.Cmp(pk, pkT) == poker.CppBig } // 打出最大手牌 func (r *BaseRobot) outMaxPoker(room *TrucoRoom) *poker.Poker { pk, _ := poker.FindMaxPoker(room.pokers, r.seat.Pokers) return pk } // 打出比场上更大手牌 func (r *BaseRobot) outBiggerPoker(room *TrucoRoom) *poker.Poker { deskPokers := r.getDeskPokers(room) pk, _ := poker.FindBiggerPoker(room.pokers, r.seat.Pokers, deskPokers...) return pk } // 打出最小手牌 func (r *BaseRobot) outMinPoker(room *TrucoRoom) *poker.Poker { pk, _ := poker.FindMinPoker(room.pokers, r.seat.Pokers) return pk } // 获取桌上牌 func (r *BaseRobot) getDeskPokers(room *TrucoRoom) []*poker.Poker { var deskPokers []*poker.Poker for i := room.roundStartPos; i < len(room.Seats)+room.roundStartPos; i++ { if seat := room.Seats[i%len(room.Seats)]; seat.OutPoker != nil { deskPokers = append(deskPokers, seat.OutPoker) } } return deskPokers } // 手牌是否大于场上牌 func (r *BaseRobot) canBigger(room *TrucoRoom) bool { deskPokers := r.getDeskPokers(room) pk, _ := poker.FindBiggerPoker(room.pokers, r.seat.Pokers, deskPokers...) return pk != nil } // 队友打出牌大于对手,并且判断是否为前8张(大牌) func (r *BaseRobot) teammateBigger(room *TrucoRoom) (bigger, big bool) { if !room.IsMVM() { return } teammates := room.Teammate(r.seat.No()) var teammateOutPokers []*poker.Poker for _, st := range teammates { if st.OutPoker != nil { teammateOutPokers = append(teammateOutPokers, st.OutPoker) } } // 有可能队友没出手,pkT为nil pkT, _ := poker.FindMaxPoker(room.pokers, teammateOutPokers) deskPokers := r.getDeskPokers(room) pk, _ := poker.FindMaxPoker(room.pokers, deskPokers) if pk == pkT { bigger = true } if pkT != nil { big = room.pokers.IsBigPoker(pkT, stub.GGlobalAI.TrucoBiggerPoker) } return } // 队伍中大牌数量 rank默认为4张花色牌,有时根据策划需求会设置为8,4张花色以及花色以下的4张牌 func (r *BaseRobot) bigPokerNumForTeam(room *TrucoRoom, rank int) int { colorNum := 0 for _, pk := range r.seat.Pokers { colorNum += util.Tie(room.isBigPoker(pk), 1, 0) } if room.IsMVM() { teammates := room.Teammate(r.seat.No()) for _, st := range teammates { for _, pk := range st.Pokers { colorNum += util.Tie(room.pokers.IsBigPoker(pk, rank), 1, 0) } } } return colorNum } // 第几轮输赢情况 func (r *BaseRobot) roundWinLose(room *TrucoRoom, round int) poker.CmpPokerPoint { color := room.teamColor(r.seat.No()) if int(room.light[round].Color()) == int(color) { return poker.CppBig } else if room.light[round].Color() == GlNormalYellow { return poker.CppEqual } else { return poker.CppSmall } } func (r *BaseRobot) canGiveUp(msg *proto.NtfPlayerAct) bool { return msg.CanGiveUp != 0 } func (r *BaseRobot) canRaise(msg *proto.NtfPlayerAct) bool { return msg.CanCall != 0 } func (r *BaseRobot) canRspRaise(msg *proto.NtfPlayerRspRaise) bool { return msg.CanRaise != 0 } func (r *BaseRobot) sendRaise(room *TrucoRoom, actType ActType, isTruco bool) { req := &proto.ReqPlayerAct{IsTruco: isTruco, Raise: actType.ToInt(r.room.PlayType())} msg := util.MakeMessage(proto.ReqPlayerActId, req, r.UID, room.Id()) room.OnMessage(proto.ReqPlayerActId, msg) } func (r *BaseRobot) sendReady(room *TrucoRoom) { msg := util.MakeMessage(proto.ReqReadyGameId, &proto.ReqReadyGame{}, r.UID, room.Id()) room.OnMessage(proto.ReqReadyGameId, msg) } func (r *BaseRobot) emoteByPoker(card *poker.Poker) Emote { // 有A-3直接发 switch card.Point { case poker.PointA: return EA case poker.Point2: return E2 case poker.Point3: return E3 } // 鬼牌(1-4)发花色 if r.room.pokers.IsBigPoker(card, 4) { switch card.Color { case poker.Spade: return ESpade case poker.Heart: return EHeart case poker.Club: return EClub case poker.Diamond: return EDiamond } } // 大牌(4-8)发大牌表情 if r.room.pokers.IsBigPoker(card, 8) { return EBigger } // 其余为小牌表情 return ESmaller } func (r *BaseRobot) sortPokers() { sort.Slice(r.seat.Pokers, func(i, j int) bool { return r.room.pokers.Cmp(r.seat.Pokers[i], r.seat.Pokers[j]) != poker.CppSmall }) } func (r *BaseRobot) sendSecretEmote(room *TrucoRoom, msg *proto.NtfDealPokers) { if !room.IsMVM() { return } exist := false for pos, pk := range r.seat.Pokers { e := r.emoteByPoker(pk) key := util.Tie( e == ESmaller || e == EBigger, util.Tie(e == ESmaller, int(ESmaller), int(EBigger)), pos, ) if _, ok := r.emotePokerSeat[key]; !ok { req := &proto.ReqEmote{EmoteId: int(e)} msgE := util.MakeMessage(proto.ReqEmoteId, req, r.UID, room.Id()) room.OnMessage(proto.ReqEmoteId, msgE) r.emotePokerSeat[key] = struct{}{} exist = true break } } if exist { r.NewSecretEmoteTimer(time.Second*time.Duration(rand.Int()%2+RobotOnMsgTime), func() { r.sendSecretEmote(r.room, msg) }) } } func (r *BaseRobot) onEmote(req *proto.NtfEmote) { if req.Code != proto.Ok || req.Seat == r.seat.No() { return } if Emote(req.EmoteId).IsSecret() { // 暗号表情不管 return } if seat := r.room.Seats[req.Seat]; seat == nil || seat.Player().IsRobot() { // 机器人发的表情不管 return } if !util.RandHappened(stub.GGlobalAI.TrucoReplyEmote) { return } e := RandNormalEmote() r.NewTimer(baseroom.TtEmote, util.RandSecond(1, 2), func() { msg := util.MakeMessage(proto.ReqEmoteId, &proto.ReqEmote{EmoteId: int(e)}, r.UID, r.room.Id()) r.room.OnMessage(proto.ReqEmoteId, msg) }) }