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

823 lines
23 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 (
"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)
}