samba/server/truco/room/trucoRobotBase.go

332 lines
8.5 KiB
Go
Raw Normal View History

2025-06-04 09:51:39 +08:00
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张花色牌有时根据策划需求会设置为84张花色以及花色以下的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)
})
}