717 lines
20 KiB
Go
717 lines
20 KiB
Go
![]() |
package room
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"samba/pkg/log"
|
|||
|
"samba/proto"
|
|||
|
"samba/server/cacheta/poker"
|
|||
|
. "samba/server/cacheta/service"
|
|||
|
"samba/server/game/baseroom"
|
|||
|
. "samba/server/game/player"
|
|||
|
"samba/stub"
|
|||
|
"samba/util/model"
|
|||
|
"samba/util/state"
|
|||
|
"samba/util/util"
|
|||
|
"sort"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
func (r *GameRoom) loadRobot(user *Player, seat *GameSeat) {
|
|||
|
if user.IsRobot() {
|
|||
|
init := false
|
|||
|
for _, s := range r.Seats {
|
|||
|
if !s.Empty() && s.Player().IsRobot() && s.Player().Robot != nil {
|
|||
|
r.SetRobotTemper(seat.No(), s.Player().Robot.Cnf())
|
|||
|
init = true
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
if !init {
|
|||
|
if cnf := r.RandRobotCnf(); cnf != stub.RteUnknown {
|
|||
|
//cnf = r.RobotCnfByTemper(stub.RteAplomb)
|
|||
|
r.SetRobotTemper(seat.No(), cnf)
|
|||
|
} else {
|
|||
|
log.Error(r.SeatLog(seat, "rand robot error. cnf is nil"))
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) makePlayerToProto(seat *GameSeat, hidePoker bool) *proto.CachetaPlayer {
|
|||
|
user := seat.Player()
|
|||
|
protoPlayer := &proto.CachetaPlayer{
|
|||
|
UserId: user.UID,
|
|||
|
Mnick: user.MNick,
|
|||
|
HeadUrl: user.HeadURL,
|
|||
|
IconId: user.IconId,
|
|||
|
AvatarFrame: user.AvatarFrame,
|
|||
|
Sex: user.Sex,
|
|||
|
TakeCoin: user.TakeCoin,
|
|||
|
ClubTakeCoin: user.TakeClubCoin,
|
|||
|
Poker: proto.CachetaHandPoker{},
|
|||
|
Seat: seat.No(),
|
|||
|
State: util.Tie(seat.FakeLeave(), 0, 1),
|
|||
|
Point: seat.Point,
|
|||
|
}
|
|||
|
if r.ClubId() == 0 {
|
|||
|
protoPlayer.Coin = r.getCoin(user.UID)
|
|||
|
} else {
|
|||
|
protoPlayer.ClubCoin = r.getCoin(user.UID)
|
|||
|
}
|
|||
|
if !hidePoker {
|
|||
|
protoPlayer.Poker = seat.handPoker
|
|||
|
} else {
|
|||
|
for i := 0; i < len(seat.pokers); i++ {
|
|||
|
protoPlayer.Poker.Pokers = append(protoPlayer.Poker.Pokers, 0)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return protoPlayer
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) makeProtoRoomInfo() *proto.NtfCachetaRoomInfo {
|
|||
|
protoRoom := &proto.NtfCachetaRoomInfo{
|
|||
|
RoomId: r.Id(),
|
|||
|
RoomType: r.Type(),
|
|||
|
ClubId: r.ClubId(),
|
|||
|
Blind: r.RoomCnf.Blind,
|
|||
|
CuttingPoker: 0,
|
|||
|
WildPoker: nil,
|
|||
|
PickPokerNum: len(r.pickPokers),
|
|||
|
OutPokers: nil,
|
|||
|
State: int(r.gameStep),
|
|||
|
GameCount: r.smallGameCount,
|
|||
|
}
|
|||
|
if r.pokers.CutPoker() != nil {
|
|||
|
protoRoom.CuttingPoker = r.pokers.CutPoker().ToInt()
|
|||
|
}
|
|||
|
for _, pk := range r.pokers.WildPokers() {
|
|||
|
protoRoom.WildPoker = append(protoRoom.WildPoker, pk.ToInt())
|
|||
|
}
|
|||
|
for _, pk := range r.outPokers {
|
|||
|
protoRoom.OutPokers = append(protoRoom.OutPokers, pk.ToInt())
|
|||
|
}
|
|||
|
return protoRoom
|
|||
|
}
|
|||
|
|
|||
|
// 重进入
|
|||
|
func (r *GameRoom) checkEnterRoom(user *Player) (code proto.ErrorCode, seat *GameSeat, isReentry bool) {
|
|||
|
code = proto.RoomFull
|
|||
|
for _, st := range r.Seats {
|
|||
|
if !st.Empty() && st.Player().UID == user.UID {
|
|||
|
seat = st
|
|||
|
isReentry = true
|
|||
|
code = proto.Ok
|
|||
|
st.SetFakeLeave(false)
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
if !isReentry {
|
|||
|
if r.Status() > state.RsWait {
|
|||
|
return proto.GameStarted, nil, false
|
|||
|
}
|
|||
|
for _, st := range r.Seats {
|
|||
|
if st.Empty() {
|
|||
|
seat = st
|
|||
|
code = proto.Ok
|
|||
|
st.SetPlayer(user)
|
|||
|
st.SetFakeLeave(false)
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return code, seat, isReentry
|
|||
|
}
|
|||
|
|
|||
|
// 进入房间
|
|||
|
func (r *GameRoom) onEnterRoom(msg map[string]interface{}) {
|
|||
|
_, _, uid, _ := ParseMsg(msg)
|
|||
|
seat := r.FindSeat(uid)
|
|||
|
if seat != nil {
|
|||
|
r.OnEnterRoom(seat.Player())
|
|||
|
} else {
|
|||
|
if player, err := NewPlayer(uid); err == nil {
|
|||
|
r.OnEnterRoom(player)
|
|||
|
} else {
|
|||
|
log.Error(r.Log(err.Error()))
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 加入房间
|
|||
|
func (r *GameRoom) OnEnterRoom(user *Player) {
|
|||
|
code, seat, isReentry := r.checkEnterRoom(user)
|
|||
|
if code != proto.Ok {
|
|||
|
r.SendMsg(user, proto.RspEnterRoomId, &proto.RspEnterRoom{Code: code}, false)
|
|||
|
return
|
|||
|
}
|
|||
|
if !isReentry {
|
|||
|
if user.IsRobot() {
|
|||
|
r.loadRobot(user, seat)
|
|||
|
}
|
|||
|
r.addResource(seat.Player(), r.RoomCnf.TakeCoin, model.ResTakeCoins, model.ReasonEnter)
|
|||
|
log.Info(r.SeatLog(seat, "进入房间,携带金币:%v", r.getTakeCoin(seat.Player())))
|
|||
|
}
|
|||
|
|
|||
|
// 向玩家发送消息
|
|||
|
rsp := &proto.RspEnterRoom{
|
|||
|
Code: proto.Ok,
|
|||
|
RoomId: r.Id(),
|
|||
|
RoomType: r.Type(),
|
|||
|
PlayType: r.PlayType(),
|
|||
|
ClubId: r.ClubId(),
|
|||
|
GameInfo: r.MakeClubPlayGameInfo(),
|
|||
|
IsReentry: util.Tie(isReentry, 1, 0),
|
|||
|
}
|
|||
|
r.SendMsg(user, proto.RspEnterRoomId, rsp, false)
|
|||
|
|
|||
|
r.notifyRoomInfo(user)
|
|||
|
r.notifyPlayerInfo(user, isReentry)
|
|||
|
|
|||
|
// 首个真人进房间,开启等待定时器
|
|||
|
if r.RealPlayerNum() == 1 {
|
|||
|
r.StartWaitTimer()
|
|||
|
}
|
|||
|
|
|||
|
// 发送游戏开始消息
|
|||
|
if !isReentry && r.SeatPlayerNum() == r.RoomCnf.MinPlayers {
|
|||
|
ntf := &proto.NtfGameStart{Time: time.Now().Unix()}
|
|||
|
r.Broadcast(proto.NtfStartCachetaGameId, ntf, false)
|
|||
|
r.SetStatus(state.RsReadyStart)
|
|||
|
r.gameStep = GsReadyGame
|
|||
|
r.NewTimer(baseroom.TtGameReadyStart, time.Duration(stub.GGlobal.TrucoGameStartTime)*time.Second)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// kickOut:true,踢掉玩家,小局结束,分数不足时强制踢掉
|
|||
|
func (r *GameRoom) leaveRoom(seat *GameSeat, kickOut bool) {
|
|||
|
if r.gameStep == GsWaitGame || kickOut {
|
|||
|
r.addResource(seat.Player(), -seat.Player().TakeCoin, model.ResTakeCoins, model.ReasonLeave)
|
|||
|
log.Info(r.SeatLog(seat, "归还携带金币:%v", -seat.Player().TakeCoin))
|
|||
|
model.SetUserPlayingRoom(seat.Player().UID, r.Id(), false)
|
|||
|
// 回收机器人
|
|||
|
if seat.Player().IsRobot() {
|
|||
|
log.Debug(r.SeatLog(seat, "清理并归还机器人"))
|
|||
|
seat.Player().CleanRobot()
|
|||
|
seat.Player().Robot = nil
|
|||
|
RobotMgr.Push(seat.Player())
|
|||
|
} else {
|
|||
|
playingNum, err := model.DelRoomTypePlayerNum(r.RoomCnf, seat.Player().UID)
|
|||
|
if err == nil && r.RoomCnf.Mode != int(stub.RmClub) {
|
|||
|
// 金币场 广播虚假在玩人数
|
|||
|
BroadcastMsg(proto.NtfPlayingNumId, &proto.NtfPlayingNum{RoomType: r.RoomCnf.Id, PlayingNum: model.CalculateFakeCount(playingNum)})
|
|||
|
}
|
|||
|
}
|
|||
|
r.TriggerClubMemberPlaying(seat.Player().UID, false)
|
|||
|
r.Broadcast(proto.NtfLeaveRoomId, &proto.NtfLeaveRoom{CurrentSeat: seat.No(), RoomId: r.Id()}, false, seat.Player())
|
|||
|
seat.SetPlayer(nil)
|
|||
|
r.TryReleaseRoom()
|
|||
|
} else {
|
|||
|
seat.SetFakeLeave(true)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 离开房间
|
|||
|
func (r *GameRoom) onLeaveRoom(msg map[string]interface{}) {
|
|||
|
_, _, uid, _ := ParseMsg(msg)
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
log.Debug(r.SeatLog(seat, "玩家申请离开房间, room status:%v", r.gameStep))
|
|||
|
// 向玩家发送消息
|
|||
|
rsp := &proto.NtfLeaveRoom{
|
|||
|
CurrentSeat: seat.No(),
|
|||
|
RoomId: r.Id(),
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), proto.RspLeaveRoomId, &proto.RspLeaveRoom{Code: proto.Ok, RoomId: r.Id(), CurrentSeat: seat.No()}, false)
|
|||
|
if r.gameStep == GsWaitGame {
|
|||
|
r.Broadcast(proto.NtfLeaveRoomId, rsp, false, seat.Player())
|
|||
|
} else {
|
|||
|
seat.SetFakeLeave(true)
|
|||
|
r.notifyFakeLeave(seat)
|
|||
|
}
|
|||
|
r.leaveRoom(seat, false)
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) checkPickPoker(seat *GameSeat) proto.ErrorCode {
|
|||
|
if r.gameStep != GsPlayerAct {
|
|||
|
log.Error(r.SeatLog(seat, "不是玩家行动期. game step:%v", r.gameStep))
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
if seat.No() != r.current {
|
|||
|
log.Error(r.SeatLog(seat, "is not current:%v", r.current))
|
|||
|
return proto.NotCurrent
|
|||
|
}
|
|||
|
if seat.ActType != SatPickPokerOrWin {
|
|||
|
log.Error(r.SeatLog(seat, "is not out poker act type:%v", seat.ActType))
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
return proto.Ok
|
|||
|
}
|
|||
|
|
|||
|
// 玩家摸牌
|
|||
|
func (r *GameRoom) onPickPoker(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqCachetaPickPoker](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(err.Error())
|
|||
|
return
|
|||
|
}
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
if code := r.checkPickPoker(seat); code != proto.Ok {
|
|||
|
r.SendMsg(seat.Player(), proto.RspCachetaPickPokerId, proto.RspCachetaPickPoker{Code: code}, false)
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
r.CancelTimer(baseroom.TtPlayerAct)
|
|||
|
seat.ActType = SatOutPoker
|
|||
|
var pickPk *poker.Poker
|
|||
|
rsp := &proto.RspCachetaPickPoker{Code: proto.Ok, IsDrop: req.IsDrop}
|
|||
|
rsp.DropPoker = r.outPokers[len(r.outPokers)-1].ToInt()
|
|||
|
// 摸牌区摸牌
|
|||
|
if req.IsDrop == 0 {
|
|||
|
if len(r.pickPokers) == 0 {
|
|||
|
r.pickPokers = r.pickPokers[0:0]
|
|||
|
for i := len(r.outPokers) - 1; i >= 0; i-- {
|
|||
|
r.pickPokers = append(r.pickPokers, r.outPokers[i])
|
|||
|
}
|
|||
|
r.outPokers = r.outPokers[0:0]
|
|||
|
rsp.IsTurnOver = 1
|
|||
|
}
|
|||
|
pickPk = r.pickPokers[len(r.pickPokers)-1]
|
|||
|
r.pickPokers = r.pickPokers[:len(r.pickPokers)-1]
|
|||
|
|
|||
|
} else {
|
|||
|
if len(r.outPokers) == 0 {
|
|||
|
log.Error(r.SeatLog(seat, "弃牌区为空,异常"))
|
|||
|
return
|
|||
|
}
|
|||
|
pickPk = r.outPokers[len(r.outPokers)-1]
|
|||
|
r.outPokers = r.outPokers[:len(r.outPokers)-1]
|
|||
|
if len(r.outPokers) != 0 {
|
|||
|
rsp.DropPoker = r.outPokers[len(r.outPokers)-1].ToInt()
|
|||
|
} else {
|
|||
|
rsp.DropPoker = 0
|
|||
|
}
|
|||
|
}
|
|||
|
seat.pokers = append(seat.pokers, pickPk)
|
|||
|
seat.handPoker.Pokers = append(seat.handPoker.Pokers, pickPk.ToInt())
|
|||
|
rsp.Poker = pickPk.ToInt()
|
|||
|
|
|||
|
r.SendMsg(seat.Player(), proto.RspCachetaPickPokerId, rsp, false)
|
|||
|
|
|||
|
log.Debug(r.SeatLog(seat, "申请摸牌:%v,玩家手牌:%v", pickPk.ToString(), poker.PokersToString(seat.pokers)))
|
|||
|
|
|||
|
ntf := &proto.NtfCachetaPickPoker{CurrentSeat: seat.No(), IsTurnOver: 0, IsDrop: rsp.IsDrop, DropPoker: rsp.DropPoker}
|
|||
|
if len(r.pickPokers) == 0 {
|
|||
|
for i := len(r.outPokers) - 1; i >= 0; i-- {
|
|||
|
r.pickPokers = append(r.pickPokers, r.outPokers[i])
|
|||
|
}
|
|||
|
for _, pk := range r.pickPokers {
|
|||
|
ntf.PickPokers = append(ntf.PickPokers, pk.ToInt())
|
|||
|
}
|
|||
|
r.outPokers = nil
|
|||
|
ntf.IsTurnOver = 1
|
|||
|
}
|
|||
|
ntf.PickPokerNum = len(r.pickPokers)
|
|||
|
r.Broadcast(proto.NtfCachetaPickPokerId, ntf, true)
|
|||
|
r.PlayerAct(seat.Player())
|
|||
|
|
|||
|
// 牌局回顾
|
|||
|
ntf.Poker = pickPk.ToInt()
|
|||
|
r.record.AddAction(proto.NtfCachetaPickPokerId, ntf)
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) checkOutPoker(seat *GameSeat, req *proto.ReqCachetaOutPoker) (proto.ErrorCode, *poker.Poker) {
|
|||
|
if r.gameStep != GsPlayerAct {
|
|||
|
log.Error(r.SeatLog(seat, "不是玩家行动期. game step:%v", r.gameStep))
|
|||
|
return proto.BadAction, nil
|
|||
|
}
|
|||
|
if seat.No() != r.current {
|
|||
|
log.Error(r.SeatLog(seat, "is not current:%v", r.current))
|
|||
|
return proto.NotCurrent, nil
|
|||
|
}
|
|||
|
if seat.ActType != SatOutPoker {
|
|||
|
log.Error(r.SeatLog(seat, "is not out poker act type:%v", seat.ActType))
|
|||
|
return proto.BadAction, nil
|
|||
|
}
|
|||
|
var pk *poker.Poker
|
|||
|
var ok bool
|
|||
|
if ok, pk = seat.Remove(req.Poker); !ok {
|
|||
|
log.Error(r.SeatLog(seat, "has not poker:%v 手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers)))
|
|||
|
return proto.BadParam, nil
|
|||
|
}
|
|||
|
seat.ActType = SatNone
|
|||
|
return proto.Ok, pk
|
|||
|
}
|
|||
|
|
|||
|
// 出完牌后监测是否听牌
|
|||
|
func (r *GameRoom) checkTingHu(seat *GameSeat) proto.ErrorCode {
|
|||
|
if seat.TingHu {
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
var tmpPokers []*poker.Poker
|
|||
|
tmpPokers = append(tmpPokers, seat.pokers...)
|
|||
|
tmpPokers = append(tmpPokers, r.pokers.WildPoker())
|
|||
|
groups, high := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), tmpPokers)
|
|||
|
log.Debug(r.SeatLog(seat, "检查听牌:%s", poker.DebugGroupsHigh(groups, high, nil)))
|
|||
|
// 添加一张万能牌后,散牌数为0或1即可听牌
|
|||
|
return util.Tie[proto.ErrorCode](len(high) < 2, proto.Ok, proto.BadAction)
|
|||
|
}
|
|||
|
|
|||
|
// 玩家出牌
|
|||
|
func (r *GameRoom) onOutPoker(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqCachetaOutPoker](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(r.Log(err.Error()))
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
//log.Debug(r.SeatLog(seat, "申请出牌:%v,玩家手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers)))
|
|||
|
code, pk := r.checkOutPoker(seat, req)
|
|||
|
if code != proto.Ok {
|
|||
|
rsp := &proto.RspCachetaOutPoker{Code: code, Poker: req.Poker}
|
|||
|
r.SendMsg(seat.Player(), proto.RspCachetaOutPokerId, rsp, false)
|
|||
|
return
|
|||
|
}
|
|||
|
r.outPokers = append(r.outPokers, pk)
|
|||
|
ntf := &proto.NtfCachetaOutPoker{CurrentSeat: seat.No(), Poker: req.Poker}
|
|||
|
r.Broadcast(proto.NtfCachetaOutPokerId, ntf, false)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.NtfPlayerOutPokerId, ntf)
|
|||
|
|
|||
|
if r.checkTingHu(seat) == proto.Ok {
|
|||
|
log.Debug(r.SeatLog(seat, "听牌"))
|
|||
|
seat.TingHu = true
|
|||
|
ntf1 := &proto.NtfCachetaTingHu{Seat: seat.No()}
|
|||
|
r.Broadcast(proto.NtfCachetaTingHuId, ntf1, false)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.NtfCachetaTingHuId, ntf1)
|
|||
|
}
|
|||
|
|
|||
|
log.Debug(r.SeatLog(seat, "出牌:%v 玩家手牌:%v", poker.NewPoker(req.Poker).ToString(), poker.PokersToString(seat.pokers)))
|
|||
|
//log.Debug(r.Log("cancel timer:TtPlayerAct"))
|
|||
|
r.CancelTimer(baseroom.TtPlayerAct)
|
|||
|
|
|||
|
//r.NotifyCutLine()
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) checkTidyPoker(seat *GameSeat, req *proto.ReqCachetaTidyPoker) proto.ErrorCode {
|
|||
|
if r.gameStep >= GsGameSettle {
|
|||
|
log.Error(r.SeatLog(seat, "game is over"))
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
|
|||
|
var clientPks []int
|
|||
|
for _, g := range req.HandPoker.Groups {
|
|||
|
for _, p := range g.Pokers {
|
|||
|
clientPks = append(clientPks, p)
|
|||
|
}
|
|||
|
}
|
|||
|
clientPks = append(clientPks, req.HandPoker.Pokers...)
|
|||
|
|
|||
|
var serverPks []int
|
|||
|
for _, p := range seat.pokers {
|
|||
|
serverPks = append(serverPks, p.ToInt())
|
|||
|
}
|
|||
|
sort.Ints(clientPks)
|
|||
|
sort.Ints(serverPks)
|
|||
|
if len(clientPks) != len(serverPks) {
|
|||
|
return proto.BadParam
|
|||
|
}
|
|||
|
for i, p := range clientPks {
|
|||
|
if p != serverPks[i] {
|
|||
|
return proto.BadParam
|
|||
|
}
|
|||
|
}
|
|||
|
return proto.Ok
|
|||
|
}
|
|||
|
|
|||
|
// 客户端主动调整手牌
|
|||
|
func (r *GameRoom) onTidyPoker(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqCachetaTidyPoker](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(r.Log(err.Error()))
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
rsp := proto.RspCachetaTidyPoker{}
|
|||
|
rsp.Code = r.checkTidyPoker(seat, req)
|
|||
|
if rsp.Code != proto.Ok {
|
|||
|
rsp.HandPoker = seat.handPoker
|
|||
|
} else {
|
|||
|
rsp.HandPoker = req.HandPoker
|
|||
|
seat.handPoker = req.HandPoker
|
|||
|
}
|
|||
|
log.Debug(r.SeatLog(seat, "调整后手牌:%v", seat.HandPokerString()))
|
|||
|
r.SendMsg(seat.Player(), proto.RspCachetaTidyPokerId, rsp, false)
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) checkHu(seat *GameSeat, req *proto.ReqCachetaHu) (proto.ErrorCode, int) {
|
|||
|
// 不是当前玩家
|
|||
|
if seat.No() != r.current {
|
|||
|
// 也没有插队
|
|||
|
if r.gameStep != GsCutLine || !seat.TingHu {
|
|||
|
log.Error(r.SeatLog(seat, "is not current:%v", r.current))
|
|||
|
return proto.NotCurrent, 0
|
|||
|
}
|
|||
|
if r.gameStep == GsCutLine && seat.CutLineNum < 1 {
|
|||
|
log.Error(r.SeatLog(seat, "插队次数已用完"))
|
|||
|
return proto.CutLineZero, 0
|
|||
|
}
|
|||
|
}
|
|||
|
var pokers []*poker.Poker
|
|||
|
pokers = append(pokers, seat.pokers...)
|
|||
|
if req.DropPoker > 0 {
|
|||
|
pokers = append(pokers, poker.NewPoker(req.DropPoker))
|
|||
|
}
|
|||
|
gs, highPokers := poker.FindBestGroupsAndRemains(r.pokers.WildPoker(), pokers)
|
|||
|
log.Debug(r.SeatLog(seat, "检查胡牌:%s", poker.DebugGroupsHigh(gs, highPokers, nil)))
|
|||
|
if len(highPokers) == 0 || len(highPokers) == 1 {
|
|||
|
return proto.Ok, len(highPokers)
|
|||
|
}
|
|||
|
return proto.BadAction, 0
|
|||
|
}
|
|||
|
|
|||
|
// 玩家胡牌
|
|||
|
func (r *GameRoom) onHu(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqCachetaHu](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(r.Log(err.Error()))
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
log.Debug(r.SeatLog(seat, "申请胡牌,玩家手牌:%v", poker.PokersToString(seat.pokers)))
|
|||
|
code, highNum := r.checkHu(seat, req)
|
|||
|
if r.gameStep == GsCutLine {
|
|||
|
seat.CutLineNum = 0
|
|||
|
}
|
|||
|
ntf := &proto.NtfCachetaHu{Code: code, Seat: seat.No()}
|
|||
|
r.Broadcast(proto.NtfCachetaHuId, ntf, true)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.NtfCachetaHuId, ntf)
|
|||
|
|
|||
|
log.Debug(r.SeatLog(seat, "申请胡牌:%v", code))
|
|||
|
if code == proto.Ok {
|
|||
|
r.GameOver(seat, highNum)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 玩家断线重连
|
|||
|
func (r *GameRoom) onReconnect(msg map[string]interface{}) {
|
|||
|
_, _, uid, _ := ParseMsg(msg)
|
|||
|
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
seat.SetFakeLeave(false)
|
|||
|
r.notifyFakeLeave(seat)
|
|||
|
//log.Debug(r.SeatLog(seat, "seat:%v 重连", seat.No()))
|
|||
|
|
|||
|
rsp := &proto.RspCachetaReconnect{
|
|||
|
Code: proto.Ok,
|
|||
|
RoomInfo: r.makeProtoRoomInfo(),
|
|||
|
Players: nil,
|
|||
|
Dealer: r.dealer,
|
|||
|
}
|
|||
|
for _, st := range r.Seats {
|
|||
|
if st.Empty() {
|
|||
|
continue
|
|||
|
}
|
|||
|
rsp.Players = append(rsp.Players, r.makePlayerToProto(st, st.Player().UID != seat.Player().UID))
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), proto.RspCachetaReconnectId, rsp, false)
|
|||
|
if lastMsg, ok := r.LastMsg[uid]; ok {
|
|||
|
ilm := lastMsg.Msg
|
|||
|
if lm, ok1 := lastMsg.Msg.(*proto.NtfPlayerAct); ok1 && lastMsg.Tm.Unix() != 0 {
|
|||
|
lm.Countdown -= int(time.Now().Unix() - lastMsg.Tm.Unix())
|
|||
|
lm.Countdown = util.Tie(lm.Countdown < 0, 0, lm.Countdown)
|
|||
|
} else if lm, ok1 := lastMsg.Msg.(*proto.NtfPlayerRspRaise); ok1 && lastMsg.Tm.Unix() != 0 {
|
|||
|
lm.Countdown -= int(time.Now().Unix() - lastMsg.Tm.Unix())
|
|||
|
lm.Countdown = util.Tie(lm.Countdown < 0, 0, lm.Countdown)
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), lastMsg.MsgId, ilm, false)
|
|||
|
log.Debug(r.SeatLog(seat, "重连补发消息:%v", lastMsg.MsgId))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 服务器维护中
|
|||
|
func (r *GameRoom) onMaintain(msg map[string]interface{}) {
|
|||
|
_, _, _, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.NtfMaintain](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(err.Error())
|
|||
|
return
|
|||
|
}
|
|||
|
r.CancelAllTimer()
|
|||
|
r.gameStep = GsWaitGame
|
|||
|
r.Broadcast(proto.NtfMaintainId, req, false)
|
|||
|
|
|||
|
for _, st := range r.Seats {
|
|||
|
if !st.Empty() {
|
|||
|
msgL := util.MakeMessage(proto.ReqLeaveRoomId, &proto.ReqLeaveRoom{RoomId: r.Id()}, st.Player().UID, r.Id())
|
|||
|
r.OnMessage(proto.ReqLeaveRoomId, msgL)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// gm解散房间
|
|||
|
func (r *GameRoom) onDisbandRoom(_ map[string]interface{}) {
|
|||
|
//_, _, _, data := ParseMsg(msg)
|
|||
|
//req, err := util.MapToStructT[proto.NtfMaintain](data)
|
|||
|
//if err != nil {
|
|||
|
// log.Error(err.Error())
|
|||
|
// return
|
|||
|
//}
|
|||
|
ntf := &proto.NtfMaintain{Code: proto.DisbandRoom}
|
|||
|
r.CancelAllTimer()
|
|||
|
r.gameStep = GsWaitGame
|
|||
|
r.Broadcast(proto.NtfMaintainId, ntf, false)
|
|||
|
|
|||
|
for _, st := range r.Seats {
|
|||
|
if !st.Empty() {
|
|||
|
msgL := util.MakeMessage(proto.ReqLeaveRoomId, &proto.ReqLeaveRoom{RoomId: r.Id()}, st.Player().UID, r.Id())
|
|||
|
r.OnMessage(proto.ReqLeaveRoomId, msgL)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (r *GameRoom) checkEmote(user *Player, req *proto.ReqEmote, rsp *proto.NtfEmote) {
|
|||
|
rsp.Code = proto.Ok
|
|||
|
rsp.EmoteId = req.EmoteId
|
|||
|
rsp.DestSeat = req.DestSeat
|
|||
|
|
|||
|
it, ok := stub.GPayEmote[req.EmoteId]
|
|||
|
if !ok {
|
|||
|
// 免费表情
|
|||
|
return
|
|||
|
}
|
|||
|
if it.Value < 1 {
|
|||
|
rsp.Code = proto.Ok
|
|||
|
return
|
|||
|
}
|
|||
|
resType, err := model.IntToRes(it.Type)
|
|||
|
if err != nil {
|
|||
|
log.Error(err.Error())
|
|||
|
rsp.Code = proto.BadParam
|
|||
|
return
|
|||
|
}
|
|||
|
rsp.ResType = it.Type
|
|||
|
rsp.CostValue = it.Value
|
|||
|
if resType == model.ResCoins {
|
|||
|
op := model.NewUserResourceOp()
|
|||
|
coins, err := op.Get(user.UID, model.ResCoins)
|
|||
|
takeCoins, err1 := op.GetTakeCoin(user.UID)
|
|||
|
if err1 != nil || err != nil {
|
|||
|
rsp.Code = proto.Internal
|
|||
|
log.Error(fmt.Sprintf("err:%v err1:%v", err, err1))
|
|||
|
return
|
|||
|
}
|
|||
|
if coins-takeCoins < it.Value {
|
|||
|
rsp.Code = proto.NotEnough
|
|||
|
log.Debug(fmt.Sprintf("coins:%v takeCoins:%v emote cost:%v not enough", coins, takeCoins, it.Value))
|
|||
|
return
|
|||
|
}
|
|||
|
r.addResource(user, -it.Value, model.ResCoins, model.ReasonEmote)
|
|||
|
coins -= it.Value
|
|||
|
rsp.ResVal = coins
|
|||
|
} else {
|
|||
|
op := model.NewUserResourceOp()
|
|||
|
value, err := op.Get(user.UID, resType)
|
|||
|
if err != nil {
|
|||
|
rsp.Code = proto.Internal
|
|||
|
log.Error(fmt.Sprintf("err:%v", err))
|
|||
|
return
|
|||
|
}
|
|||
|
if value < it.Value {
|
|||
|
rsp.Code = proto.NotEnough
|
|||
|
log.Debug(fmt.Sprintf("resType:%v resValue:%v emote cost:%v not enough", resType, value, it.Value))
|
|||
|
return
|
|||
|
}
|
|||
|
r.addResource(user, -it.Value, resType, model.ReasonEmote)
|
|||
|
value -= it.Value
|
|||
|
rsp.ResVal = value
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// 玩家使用表情
|
|||
|
func (r *GameRoom) onEmote(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
req, err := util.MapToStructT[proto.ReqEmote](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(err.Error())
|
|||
|
return
|
|||
|
}
|
|||
|
rsp := &proto.NtfEmote{Seat: seat.No()}
|
|||
|
r.checkEmote(seat.Player(), req, rsp)
|
|||
|
log.Debug(r.SeatLog(seat, "第%d小局, 使用表情:%v", r.smallGameCount, Emote(req.EmoteId)))
|
|||
|
if rsp.Code == proto.Ok {
|
|||
|
r.Broadcast(proto.NtfEmoteId, rsp, false)
|
|||
|
} else {
|
|||
|
r.SendMsg(seat.Player(), proto.NtfEmoteId, rsp, false)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 玩家准备
|
|||
|
func (r *GameRoom) onReady(msg map[string]interface{}) {
|
|||
|
_, _, uid, _ := ParseMsg(msg)
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
log.Error(r.Log("player:%d seat is nil", uid))
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
log.Debug(r.SeatLog(seat, "收到玩家准备"))
|
|||
|
seat.SetReady(true)
|
|||
|
allReady := true
|
|||
|
for _, st := range r.Seats {
|
|||
|
if !st.Ready() {
|
|||
|
allReady = false
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
if allReady {
|
|||
|
r.CancelTimer(baseroom.TtGameReadyStart)
|
|||
|
if r.SeatPlayerNum() == r.RoomCnf.MinPlayers && r.Status() != state.RsGaming {
|
|||
|
r.ReInit()
|
|||
|
r.GameStart()
|
|||
|
}
|
|||
|
}
|
|||
|
// 游戏中,重连玩家发送准备后,设置为假离开后返回
|
|||
|
if r.Status() == state.RsGaming {
|
|||
|
r.SetFakeLeave(uid, false)
|
|||
|
}
|
|||
|
}
|