332 lines
8.5 KiB
Go
332 lines
8.5 KiB
Go
![]() |
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)
|
|||
|
})
|
|||
|
|
|||
|
}
|