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