892 lines
25 KiB
Go
892 lines
25 KiB
Go
![]() |
package room
|
|||
|
|
|||
|
import (
|
|||
|
"fmt"
|
|||
|
"samba/pkg/log"
|
|||
|
"samba/proto"
|
|||
|
"samba/server/game/baseroom"
|
|||
|
. "samba/server/game/player"
|
|||
|
"samba/server/truco/poker"
|
|||
|
. "samba/server/truco/service"
|
|||
|
"samba/stub"
|
|||
|
"samba/util/model"
|
|||
|
"samba/util/state"
|
|||
|
"samba/util/util"
|
|||
|
"time"
|
|||
|
)
|
|||
|
|
|||
|
func (r *TrucoRoom) loadRobot(user *Player, seat *TrucoSeat) {
|
|||
|
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 *TrucoRoom) makePlayerToProto(seat *TrucoSeat, hidePoker bool) *proto.Player {
|
|||
|
user := seat.Player()
|
|||
|
protoPlayer := &proto.Player{
|
|||
|
UserId: user.UID,
|
|||
|
Mnick: user.MNick,
|
|||
|
HeadUrl: user.HeadURL,
|
|||
|
IconId: user.IconId,
|
|||
|
AvatarFrame: user.AvatarFrame,
|
|||
|
Sex: user.Sex,
|
|||
|
Coin: 0,
|
|||
|
TakeCoin: user.TakeCoin,
|
|||
|
ClubTakeCoin: user.TakeClubCoin,
|
|||
|
Poker: nil,
|
|||
|
Seat: seat.No(),
|
|||
|
IsRed: int(r.teamColor(seat.No())),
|
|||
|
State: util.Tie(seat.FakeLeave(), 0, 1),
|
|||
|
}
|
|||
|
|
|||
|
for _, pk := range seat.Pokers {
|
|||
|
protoPlayer.Poker = append(protoPlayer.Poker, util.Tie[int](!hidePoker, pk.ToInt(), 0))
|
|||
|
}
|
|||
|
if seat.OutPoker != nil {
|
|||
|
protoPlayer.OutPoker = seat.OutPoker.ToInt()
|
|||
|
}
|
|||
|
if r.ClubId() != 0 {
|
|||
|
protoPlayer.ClubCoin = r.GetCoin(user.UID)
|
|||
|
} else {
|
|||
|
protoPlayer.Coin = r.GetCoin(user.UID)
|
|||
|
}
|
|||
|
return protoPlayer
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) makeProtoRoomInfo() *proto.NtfRoomInfo {
|
|||
|
protoRoom := &proto.NtfRoomInfo{
|
|||
|
RoomId: r.Id(),
|
|||
|
RoomType: r.Type(),
|
|||
|
ClubId: r.ClubId(),
|
|||
|
Blind: r.RoomCnf.Blind,
|
|||
|
CuttingPoker: 0,
|
|||
|
Point: r.point,
|
|||
|
RedScore: r.points[TcRed],
|
|||
|
GreenScore: r.points[TcGreen],
|
|||
|
RoundSettle: nil,
|
|||
|
State: int(r.gameStep),
|
|||
|
GameCount: r.smallGameCount,
|
|||
|
Star: r.star,
|
|||
|
}
|
|||
|
if r.pokers.GhostPoker() != nil {
|
|||
|
protoRoom.CuttingPoker = r.pokers.GhostPoker().ToInt()
|
|||
|
}
|
|||
|
if r.gameStep != GsGameSettle && r.gameStep != GsWaitGame {
|
|||
|
for _, rs := range r.light {
|
|||
|
protoRoom.RoundSettle = append(protoRoom.RoundSettle, int(rs))
|
|||
|
}
|
|||
|
}
|
|||
|
return protoRoom
|
|||
|
}
|
|||
|
|
|||
|
// 重进入
|
|||
|
func (r *TrucoRoom) checkEnterRoom(user *Player) (code proto.ErrorCode, seat *TrucoSeat, isReentry bool) {
|
|||
|
if r.ClubId() > 0 {
|
|||
|
if userClubInfo, _ := model.NewUserClubInfoOp().Load(user.UID, r.ClubId()); userClubInfo == nil {
|
|||
|
code = proto.NotInClub
|
|||
|
return
|
|||
|
}
|
|||
|
}
|
|||
|
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 {
|
|||
|
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 *TrucoRoom) onReentryRoom(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
|
|||
|
}
|
|||
|
r.OnEnterRoom(seat.Player())
|
|||
|
}
|
|||
|
|
|||
|
// 加入房间
|
|||
|
func (r *TrucoRoom) 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.MinCoin, model.ResTakeCoins, model.ReasonEnter)
|
|||
|
log.Info(r.SeatLog(seat, "进入房间,携带金币:%v", r.GetTakeCoin(seat.Player())))
|
|||
|
model.SetUserPlayingRoom(seat.Player().UID, r.Id(), true)
|
|||
|
r.TriggerClubMemberPlaying(seat.Player().UID, true)
|
|||
|
} else {
|
|||
|
log.Info(r.SeatLog(seat, "重连进入房间"))
|
|||
|
}
|
|||
|
|
|||
|
// 向玩家发送消息
|
|||
|
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 !isReentry && r.SeatPlayerNum() == r.RoomCnf.MinPlayers {
|
|||
|
ntf := &proto.NtfGameStart{Time: time.Now().Unix()}
|
|||
|
r.Broadcast(proto.NtfStartGameId, ntf, false)
|
|||
|
r.SetStatus(state.RsReadyStart)
|
|||
|
r.gameStep = GsReadyGame
|
|||
|
r.NewTimer(baseroom.TtGameReadyStart, time.Duration(stub.GGlobal.TrucoGameStartTime)*time.Second)
|
|||
|
}
|
|||
|
if isReentry {
|
|||
|
rMsg := util.MakeMessage(proto.ReqReconnectId, &proto.ReqReconnect{RoomId: r.Id()}, user.UID, r.Id())
|
|||
|
r.OnMessage(proto.ReqReconnectId, rMsg)
|
|||
|
}
|
|||
|
|
|||
|
r.UpdateClubWaitingRoom()
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) leaveRoom(seat *TrucoSeat) {
|
|||
|
//log.Debug(r.Log("games tep:%v room status:%v", GsWaitGame, state.RsWait))
|
|||
|
if r.gameStep == GsWaitGame || r.Status() == state.RsWait {
|
|||
|
r.addResource(seat.Player(), -r.GetTakeCoin(seat.Player()), model.ResTakeCoins, model.ReasonLeave)
|
|||
|
log.Info(r.SeatLog(seat, "归还携带金币:%v", -r.GetTakeCoin(seat.Player())))
|
|||
|
model.SetUserPlayingRoom(seat.Player().UID, r.Id(), false)
|
|||
|
// 回收机器人
|
|||
|
if seat.Player().IsRobot() {
|
|||
|
log.Debug(r.SeatLog(seat, "清理并归还机器人"))
|
|||
|
seat.Player().CleanRobot()
|
|||
|
seat.Player().Robot = nil
|
|||
|
if r.ClubId() > 0 {
|
|||
|
ClubRobotMgr.Push(seat.Player(), r.ClubId())
|
|||
|
} else {
|
|||
|
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)
|
|||
|
seat.SetPlayer(nil)
|
|||
|
r.UpdateClubPlayingRoomInfo()
|
|||
|
r.UpdateClubWaitingRoom()
|
|||
|
} else {
|
|||
|
seat.SetFakeLeave(true)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 离开房间
|
|||
|
func (r *TrucoRoom) 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.Status() == state.RsWait {
|
|||
|
r.Broadcast(proto.NtfLeaveRoomId, rsp, false, seat.Player())
|
|||
|
} else {
|
|||
|
seat.SetFakeLeave(true)
|
|||
|
r.notifyFakeLeave(seat)
|
|||
|
}
|
|||
|
r.leaveRoom(seat)
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) checkOutPoker(seat *TrucoSeat, req *proto.ReqPlayerOutPoker) (proto.ErrorCode, *poker.Poker) {
|
|||
|
if !seat.CanAct() {
|
|||
|
log.Error(r.SeatLog(seat, "不能行动"))
|
|||
|
return proto.BadAction, nil
|
|||
|
}
|
|||
|
if r.gameStep >= GsGameSettle {
|
|||
|
log.Error(r.SeatLog(seat, "game is over"))
|
|||
|
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 !r.canOutPoker() {
|
|||
|
log.Error(r.SeatLog(seat, "out card error.because room status is call status"))
|
|||
|
return proto.BadAction, nil
|
|||
|
}
|
|||
|
pokerPos := -1
|
|||
|
for pos, p := range seat.Pokers {
|
|||
|
if p.ToInt() == req.Poker {
|
|||
|
pokerPos = pos
|
|||
|
}
|
|||
|
}
|
|||
|
if pokerPos == -1 {
|
|||
|
log.Error(r.SeatLog(seat, "has not poker:%v %v", req.Poker, poker.NewPoker(req.Poker).ToString()))
|
|||
|
return proto.BadParam, nil
|
|||
|
}
|
|||
|
pk := seat.Pokers[pokerPos]
|
|||
|
seat.Pokers = append(seat.Pokers[:pokerPos], seat.Pokers[pokerPos+1:]...)
|
|||
|
return proto.Ok, pk
|
|||
|
}
|
|||
|
|
|||
|
// 玩家出牌
|
|||
|
func (r *TrucoRoom) onOutPoker(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqPlayerOutPoker](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, outPoker := r.checkOutPoker(seat, req)
|
|||
|
if code != proto.Ok {
|
|||
|
rsp := &proto.RspPlayerOutPoker{Code: code, Poker: nil}
|
|||
|
for _, p := range seat.Pokers {
|
|||
|
rsp.Poker = append(rsp.Poker, p.ToInt())
|
|||
|
}
|
|||
|
if seat.OutPoker != nil {
|
|||
|
rsp.OutPoker = seat.OutPoker.ToInt()
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), proto.RspPlayerOutPokerId, rsp, false)
|
|||
|
return
|
|||
|
} else {
|
|||
|
r.Broadcast(proto.NtfPlayerOutPokerId, &proto.NtfPlayerOutPoker{CurrentSeat: seat.No(), Poker: req.Poker}, false)
|
|||
|
}
|
|||
|
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.NtfPlayerOutPokerId, &proto.NtfPlayerOutPoker{CurrentSeat: seat.No(), Poker: req.Poker})
|
|||
|
if r.pokers.IsBigPoker(outPoker, stub.GGlobalAI.TrucoBiggerPoker) {
|
|||
|
r.gameLog.Teams[r.teamColor(seat.No())].BigPoker++
|
|||
|
}
|
|||
|
|
|||
|
seat.SetCanAct(false)
|
|||
|
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)
|
|||
|
seat.OutPoker = outPoker
|
|||
|
|
|||
|
if r.isRoundOver() {
|
|||
|
if r.roundSettle() {
|
|||
|
// 对局结束,会清理部分数据
|
|||
|
r.GameOver(nil)
|
|||
|
}
|
|||
|
} else {
|
|||
|
r.NextSeat()
|
|||
|
r.PlayerAct(r.CurrentPlayer())
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) checkSetDarkPoker(seat *TrucoSeat, req *proto.ReqSetDarkPoker) (proto.ErrorCode, *poker.Poker) {
|
|||
|
if r.gameStep >= GsGameSettle {
|
|||
|
log.Error(r.SeatLog(seat, "game is over"))
|
|||
|
return proto.BadAction, nil
|
|||
|
}
|
|||
|
pokerPos := -1
|
|||
|
for pos, p := range seat.Pokers {
|
|||
|
if p.ToInt() == req.Poker {
|
|||
|
pokerPos = pos
|
|||
|
}
|
|||
|
}
|
|||
|
if pokerPos == -1 {
|
|||
|
log.Error(r.SeatLog(seat, "has not poker:%v %v", req.Poker, poker.NewPoker(req.Poker).ToString()))
|
|||
|
return proto.BadParam, nil
|
|||
|
}
|
|||
|
// 第一回合不能设置暗排
|
|||
|
if r.round == 0 {
|
|||
|
log.Error(r.SeatLog(seat, "第一回合不能设置暗排"))
|
|||
|
return proto.NotSetDarkPoker, nil
|
|||
|
}
|
|||
|
pk := seat.Pokers[pokerPos]
|
|||
|
return proto.Ok, pk
|
|||
|
}
|
|||
|
|
|||
|
// 设置暗牌或恢复明牌
|
|||
|
func (r *TrucoRoom) onSetDarkPoker(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqSetDarkPoker](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
|
|||
|
}
|
|||
|
code, outPoker := r.checkSetDarkPoker(seat, req)
|
|||
|
if code != proto.Ok {
|
|||
|
r.SendMsg(seat.Player(), proto.RspSetDarkPokerId, &proto.RspPlayerOutPoker{Code: code}, false)
|
|||
|
return
|
|||
|
}
|
|||
|
outPoker.IsDark = !outPoker.IsDark
|
|||
|
rsp := &proto.RspSetDarkPoker{Code: code, Poker: outPoker.ToInt()}
|
|||
|
r.Broadcast(proto.RspSetDarkPokerId, rsp, false)
|
|||
|
log.Debug(r.SeatLog(seat, "设置暗牌或恢复明牌:%v,玩家手牌:%v", poker.NewPoker(req.Poker).ToString(), outPoker.ToString()))
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.RspSetDarkPokerId, rsp)
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) checkPlayerAct(seat *TrucoSeat, req *proto.ReqPlayerAct) proto.ErrorCode {
|
|||
|
if !seat.CanAct() {
|
|||
|
log.Error(r.SeatLog(seat, "不能行动"))
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
if r.gameStep != GsPlayerAct && r.gameStep != GsDecidingAct {
|
|||
|
log.Error(r.SeatLog(seat, "gameStep:%v can not act", r.gameStep))
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
// 玩家本轮操作中已操作过,不能重复操作
|
|||
|
if lastAct := r.lastTeamAct(TcUnknown); lastAct != nil {
|
|||
|
if _, ok := lastAct.actType[seat.No()]; ok {
|
|||
|
return proto.BadAction
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 投降,直接判输,进入本局结算
|
|||
|
if req.Raise == AtGiveUp.ToInt(r.PlayType()) {
|
|||
|
return proto.Ok
|
|||
|
}
|
|||
|
colorPos := r.teamColor(seat.No())
|
|||
|
switch req.Raise {
|
|||
|
case AtTruco3.ToInt(r.PlayType()), AtP6.ToInt(r.PlayType()), AtP9.ToInt(r.PlayType()), AtP12.ToInt(r.PlayType()):
|
|||
|
_, code := r.canRaise(colorPos, req.IsTruco, true)
|
|||
|
return code
|
|||
|
case AtAgree.ToInt(r.PlayType()):
|
|||
|
return r.canAgree(colorPos)
|
|||
|
default:
|
|||
|
log.Error(r.SeatLog(seat, "req.sendRaise:%v is error", req.Raise))
|
|||
|
return proto.BadParam
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 队友是否都已操作
|
|||
|
func (r *TrucoRoom) isTeamAllOperated(teamColor TeamColor) bool {
|
|||
|
teamAct := r.lastTeamAct(teamColor)
|
|||
|
operated := true
|
|||
|
for _, st := range r.Seats {
|
|||
|
if r.teamColor(st.No()) == teamColor {
|
|||
|
if _, ok := teamAct.actType[st.No()]; !ok {
|
|||
|
operated = false
|
|||
|
break
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
return operated
|
|||
|
}
|
|||
|
|
|||
|
// 玩家放弃,1v1则直接结算,2v2则看队友是否也放弃
|
|||
|
func (r *TrucoRoom) actGiveUp(seat *TrucoSeat) {
|
|||
|
teamColor := r.teamColor(seat.No())
|
|||
|
teamAct := r.lastTeamAct(teamColor)
|
|||
|
operated := true
|
|||
|
if r.IsMVM() {
|
|||
|
operated = r.isTeamAllOperated(teamColor)
|
|||
|
}
|
|||
|
if teamAct.maxAct == AtGiveUp && operated {
|
|||
|
r.GameOver(seat)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 玩家行动回合,申请加分或弃牌或同意
|
|||
|
func (r *TrucoRoom) onPlayerAct(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqPlayerAct](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.RspPlayerAct{Point: r.point}
|
|||
|
if rsp.Code = r.checkPlayerAct(seat, req); rsp.Code != proto.Ok {
|
|||
|
r.SendMsg(seat.Player(), proto.RspPlayerActId, rsp, false)
|
|||
|
return
|
|||
|
}
|
|||
|
color := r.teamColor(seat.No())
|
|||
|
seat.SetCanAct(false)
|
|||
|
log.Debug(r.SeatLog(seat, "玩家申请操作:%v,操作回合:%v", IntToActType(req.Raise), r.round))
|
|||
|
// 最近的一个操作是同意或者没有操作,本操作为放弃,则算主动放弃
|
|||
|
nextLastAct := r.lastTeamAct(TcUnknown)
|
|||
|
// 更新玩家选择
|
|||
|
r.updateTeamAct(color, IntToActType(req.Raise), seat.No())
|
|||
|
|
|||
|
timerType := r.getActTimeType()
|
|||
|
if timerType != baseroom.TtUnknown {
|
|||
|
r.CancelTimer(timerType)
|
|||
|
}
|
|||
|
// 更新操作后的最近一个行动,与nextLastAct不一定是同一个act
|
|||
|
lastAct := r.lastTeamAct(TcUnknown)
|
|||
|
rsp.NotOptSeat = -1
|
|||
|
if r.IsMVM() && len(lastAct.actType) < r.RoomCnf.MinPlayers/2 {
|
|||
|
ntfTeammateAct := &proto.NtfTeammateAct{CurrentSeat: seat.No(), Act: req.Raise}
|
|||
|
// 广播给队友更新我的选择
|
|||
|
r.BroadcastToColor(proto.NtfTeammateActId, ntfTeammateAct, color, false)
|
|||
|
}
|
|||
|
rsp.Point = r.point
|
|||
|
rsp.IsTruco = util.Tie(len(r.actTypes) == 1, 1, 0)
|
|||
|
rsp.CurrentSeat = seat.No()
|
|||
|
rsp.Raise = lastAct.maxAct.ToInt(r.PlayType())
|
|||
|
rsp.Star = r.star
|
|||
|
if rsp.Raise == AtGiveUp.ToInt(r.PlayType()) && (nextLastAct == nil || nextLastAct.maxAct == AtAgree) {
|
|||
|
rsp.IsTruco = 2
|
|||
|
}
|
|||
|
|
|||
|
// 本队所有人都已操作完
|
|||
|
if len(lastAct.actType) == r.RoomCnf.MinPlayers/2 {
|
|||
|
if r.gameStep == GsDecidingAct {
|
|||
|
r.gameStep = GsPlayerAct
|
|||
|
}
|
|||
|
if lastAct.maxAct == AtGiveUp {
|
|||
|
// 广播给所有人
|
|||
|
r.Broadcast(proto.RspPlayerActId, rsp, false)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.RspPlayerActId, rsp)
|
|||
|
|
|||
|
r.GameOver(seat)
|
|||
|
} else if lastAct.maxAct == AtTruco3 || lastAct.maxAct == AtP6 || lastAct.maxAct == AtP9 || lastAct.maxAct == AtP12 {
|
|||
|
r.lastTrucoColor = color
|
|||
|
// 金币场 玩家接受truco,或者接受并加到P6,P9,P12等情况倍数加1
|
|||
|
if r.ClubId() == 0 && len(r.actTypes) > 1 {
|
|||
|
r.star++
|
|||
|
r.gameLog.Star = r.star
|
|||
|
rsp.Star = r.star
|
|||
|
}
|
|||
|
actColor := r.rivalColor2(color)
|
|||
|
for _, st := range r.Seats {
|
|||
|
if r.teamColor(st.No()) == actColor {
|
|||
|
st.SetCanAct(true)
|
|||
|
log.Debug(r.SeatLog(st, "玩家可以操作"))
|
|||
|
}
|
|||
|
}
|
|||
|
// 广播给所有人
|
|||
|
r.Broadcast(proto.RspPlayerActId, rsp, false)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.RspPlayerActId, rsp)
|
|||
|
|
|||
|
r.notifyPlayerRspRaise()
|
|||
|
r.NewTimer(baseroom.TtPlayerRspRaise, time.Duration(r.RoomCnf.ActTime+stub.GGlobal.TrucoActExTime)*time.Second, int(r.rivalColor2(color)))
|
|||
|
} else if lastAct.maxAct == AtAgree {
|
|||
|
// 金币场 玩家接受truco,倍数加1
|
|||
|
if r.ClubId() == 0 {
|
|||
|
r.star++
|
|||
|
r.gameLog.Star = r.star
|
|||
|
rsp.Star = r.star
|
|||
|
}
|
|||
|
// 广播给所有人
|
|||
|
r.Broadcast(proto.RspPlayerActId, rsp, false)
|
|||
|
// 牌局回顾
|
|||
|
r.record.AddAction(proto.RspPlayerActId, rsp)
|
|||
|
// 通知原玩家出牌
|
|||
|
r.PlayerAct(r.CurrentPlayer())
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 游戏结束玩家申请展示手牌
|
|||
|
func (r *TrucoRoom) onShowPoker(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, "申请展示手牌"))
|
|||
|
|
|||
|
if r.gameStep != GsGameSettle || len(seat.Pokers) == 0 {
|
|||
|
r.SendMsg(seat.Player(), proto.RspShowPokerId, &proto.RspShowPoker{Code: proto.BadAction}, false)
|
|||
|
return
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), proto.RspShowPokerId, &proto.RspShowPoker{Code: proto.Ok}, false)
|
|||
|
|
|||
|
ntf := &proto.NtfShowPoker{CurrentSeat: seat.No()}
|
|||
|
for _, pk := range seat.Pokers {
|
|||
|
pk.IsDark = false
|
|||
|
ntf.Poker = append(ntf.Poker, pk.ToInt())
|
|||
|
}
|
|||
|
r.Broadcast(proto.NtfShowPokerId, ntf, false)
|
|||
|
r.gameLog.Teams[r.teamColor(seat.No())].Show++
|
|||
|
}
|
|||
|
|
|||
|
// 玩家断线重连
|
|||
|
func (r *TrucoRoom) 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()))
|
|||
|
|
|||
|
// 重连后取消托管状态
|
|||
|
r.setPlayerHosting(seat.Player(), false)
|
|||
|
rsp := &proto.RspReconnect{
|
|||
|
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.RspReconnectId, 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)
|
|||
|
if teamAct := r.lastTeamAct(TeamColor(lm.TeamColor)); teamAct != nil {
|
|||
|
for _, act := range teamAct.actType {
|
|||
|
lm.Ally = append(lm.Ally, act.ToInt(r.PlayType()))
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
r.SendMsg(seat.Player(), lastMsg.MsgId, ilm, false)
|
|||
|
log.Debug(r.SeatLog(seat, "重连补发消息:%v", lastMsg.MsgId))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 服务器维护中
|
|||
|
func (r *TrucoRoom) onMaintain(msg map[string]interface{}) {
|
|||
|
r.onDisbandRoom(msg)
|
|||
|
}
|
|||
|
|
|||
|
// gm解散房间
|
|||
|
func (r *TrucoRoom) 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.disband = state.RdtGm
|
|||
|
r.Broadcast(proto.NtfMaintainId, ntf, false)
|
|||
|
|
|||
|
// 游戏结束
|
|||
|
r.endTime = time.Now()
|
|||
|
if r.record != nil {
|
|||
|
log.Debug("r.record is not nil")
|
|||
|
r.record.SetDisbandType(r)
|
|||
|
r.record.AddAction(proto.NtfMaintainId, ntf)
|
|||
|
r.SaveRecord(nil)
|
|||
|
}
|
|||
|
r.ReleaseRoom()
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) 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 *TrucoRoom) 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)))
|
|||
|
isTeamEmote := Emote(req.EmoteId).IsSecret()
|
|||
|
|
|||
|
r.gameLog.TrucoPlayers[seat.No()].EmoteNum++
|
|||
|
|
|||
|
if isTeamEmote {
|
|||
|
r.BroadcastToColor(proto.NtfEmoteId, rsp, r.teamColor(seat.No()), false)
|
|||
|
} else {
|
|||
|
if rsp.Code == proto.Ok {
|
|||
|
r.Broadcast(proto.NtfEmoteId, rsp, false)
|
|||
|
} else {
|
|||
|
r.SendMsg(seat.Player(), proto.NtfEmoteId, rsp, false)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 玩家准备
|
|||
|
func (r *TrucoRoom) 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)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// 返回错误码,是否解散,解散发起人
|
|||
|
func (r *TrucoRoom) checkUserDisbandRoom(uid int64, req *proto.ReqUserDisbandRoom) (proto.ErrorCode, bool, int64) {
|
|||
|
if r.ClubId() < 1 {
|
|||
|
return proto.BadParam, false, 0
|
|||
|
}
|
|||
|
if r.Status() == state.RsWait {
|
|||
|
return proto.NotDisbandRoom, false, 0
|
|||
|
}
|
|||
|
// 申请解散
|
|||
|
if req.Type == 0 {
|
|||
|
if req.Agree == int(state.DtStart) {
|
|||
|
r.disbandInfo = make(map[int64]int)
|
|||
|
r.disbandTime = time.Now().Unix()
|
|||
|
r.NewTimer(baseroom.TtDelDisbandRoomInfo, time.Second*(baseroom.DisbandRoomCd+2))
|
|||
|
}
|
|||
|
if r.disbandInfo == nil {
|
|||
|
return proto.BadParam, false, 0
|
|||
|
}
|
|||
|
seat := r.GetSeat(uid)
|
|||
|
if seat == nil {
|
|||
|
return proto.BadParam, false, 0
|
|||
|
}
|
|||
|
r.disbandInfo[uid] = req.Agree
|
|||
|
|
|||
|
initiator := int64(0)
|
|||
|
agree := 0
|
|||
|
for k, v := range r.disbandInfo {
|
|||
|
if v > 0 {
|
|||
|
agree++
|
|||
|
}
|
|||
|
if v == 2 {
|
|||
|
initiator = k
|
|||
|
}
|
|||
|
}
|
|||
|
if agree == r.SeatPlayerNum() {
|
|||
|
return proto.Ok, true, initiator
|
|||
|
}
|
|||
|
return proto.Ok, false, initiator
|
|||
|
}
|
|||
|
// 管理员强制解散
|
|||
|
clubUser, err := model.NewUserClubInfoOp().Load(uid, r.ClubId())
|
|||
|
if err != nil {
|
|||
|
return proto.NotClubMgr, false, 0
|
|||
|
}
|
|||
|
if clubUser.Admin == 1 {
|
|||
|
return proto.Ok, true, 0
|
|||
|
}
|
|||
|
if clubUser.Admin != 2 {
|
|||
|
return proto.NotClubMgr, false, 0
|
|||
|
}
|
|||
|
perms := model.NewClubPermissionsOp().Load(r.ClubId())
|
|||
|
for _, pi := range perms {
|
|||
|
if pi == 1005 {
|
|||
|
return proto.Ok, true, 0
|
|||
|
}
|
|||
|
}
|
|||
|
return proto.NotClubMgr, false, 0
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) onUserDisbandRoom(msg map[string]interface{}) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqUserDisbandRoom](data)
|
|||
|
if err != nil {
|
|||
|
log.Error(err.Error())
|
|||
|
return
|
|||
|
}
|
|||
|
code, ok, initiator := r.checkUserDisbandRoom(uid, req)
|
|||
|
rsp := proto.RspUserDisbandRoom{Code: code, RoomId: r.Id(), Type: req.Type}
|
|||
|
SendMsgToGate(uid, proto.RspUserDisbandRoomId, rsp)
|
|||
|
log.Debug(r.Log("user:%v 解散房间.reqType:%v 同意:%v", uid, req.Type, req.Agree))
|
|||
|
ntf := &proto.NtfUserDisbandRoom{
|
|||
|
RoomId: r.Id(),
|
|||
|
CountDown: int(baseroom.DisbandRoomCd - (time.Now().Unix() - r.disbandTime)),
|
|||
|
CountDownEx: 2,
|
|||
|
Type: req.Type,
|
|||
|
Agree: nil,
|
|||
|
Against: nil,
|
|||
|
Initiator: initiator,
|
|||
|
}
|
|||
|
if code == proto.Ok {
|
|||
|
log.Debug(r.Log("解散时间:%v now:%v begin:%v 差值:%v", baseroom.DisbandRoomCd, time.Now().Unix(), r.disbandTime, time.Now().Unix()-r.disbandTime))
|
|||
|
if ntf.CountDown < 0 || ntf.CountDown > 10 {
|
|||
|
ntf.CountDown = 0
|
|||
|
}
|
|||
|
for user, agree := range r.disbandInfo {
|
|||
|
if agree == 0 {
|
|||
|
ntf.Against = append(ntf.Against, user)
|
|||
|
} else {
|
|||
|
ntf.Agree = append(ntf.Agree, user)
|
|||
|
}
|
|||
|
}
|
|||
|
r.Broadcast(proto.NtfUserDisbandRoomId, ntf, true)
|
|||
|
}
|
|||
|
// 管理员解散房间,此时可能房间未开始,直接解散
|
|||
|
if ok {
|
|||
|
r.CancelAllTimer()
|
|||
|
r.gameStep = GsWaitGame
|
|||
|
// 游戏结束
|
|||
|
r.endTime = time.Now()
|
|||
|
r.disband = util.Tie(req.Type == 0, state.RdtMember, state.RdtMgr)
|
|||
|
if r.record != nil {
|
|||
|
r.record.SetDisbandType(r)
|
|||
|
r.record.AddAction(proto.NtfUserDisbandRoomId, ntf)
|
|||
|
r.SaveRecord(nil)
|
|||
|
}
|
|||
|
r.ReleaseRoom()
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (r *TrucoRoom) onHosting(msg map[string]any) {
|
|||
|
_, _, uid, data := ParseMsg(msg)
|
|||
|
req, err := util.MapToStructT[proto.ReqHosting](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
|
|||
|
}
|
|||
|
r.setPlayerHosting(seat.Player(), req.Enable)
|
|||
|
}
|