samba/server/truco/room/trucoRobotBase.go
2025-06-04 09:51:39 +08:00

332 lines
8.5 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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)
})
}