github.com/lianghucheng/zrddz@v0.0.0-20200923083010-c71f680932e2/src/game/internal/landlord_room.go (about)

     1  package internal
     2  
     3  import (
     4  	"common"
     5  	"conf"
     6  	"fmt"
     7  	"game/poker"
     8  	"msg"
     9  	"sort"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/name5566/leaf/log"
    14  	"github.com/name5566/leaf/timer"
    15  	"gopkg.in/mgo.v2/bson"
    16  )
    17  
    18  // 玩家状态
    19  const (
    20  	_                       = iota
    21  	landlordReady           // 1 准备
    22  	landlordWaiting         // 2 等待
    23  	landlordActionBid       // 3 前端显示叫地主动作
    24  	landlordActionGrab      // 4 前端显示抢地主动作
    25  	landlordActionDouble    // 5 前端显示加倍动作
    26  	landlordActionShowCards // 6 前端显示明牌动作
    27  	landlordActionDiscard   // 7 前端显示出牌动作
    28  )
    29  
    30  // 倒计时
    31  /*
    32  const (
    33  	cd_landlordBid            = 5  // 5 秒
    34  	cd_landlordGrab           = 3  // 3 秒
    35  	cd_landlordDouble         = 3  // 3 秒
    36  	cd_landlordShowCards      = 3  // 3 秒
    37  	cd_landlordDiscard        = 20 // 20 秒
    38  	cd_landlordDiscardNothing = 3  // 3 秒
    39  )
    40  */
    41  type LandlordRoom struct {
    42  	room
    43  	rule              *poker.LandlordRule
    44  	userIDPlayerDatas map[int]*LandlordPlayerData // Key: userID
    45  	cards             []int                       // 洗好的牌
    46  	lastThree         []int                       // 最后三张
    47  	discards          []int                       // 玩家出的牌
    48  	rests             []int                       // 剩余的牌
    49  
    50  	dealerUserID    int   // 庄家 userID(庄家第一个叫地主)
    51  	bidUserID       int   // 叫地主的玩家 userID(只有一个)
    52  	grabUserIDs     []int // 抢地主的玩家 userID
    53  	landlordUserID  int   // 地主 userID
    54  	peasantUserIDs  []int // 农民 userID
    55  	discarderUserID int   // 最近一次出牌的人 userID
    56  
    57  	finisherUserID int  // 上一局出完牌的人 userID(做下一局庄家)
    58  	spring         bool // 春天
    59  
    60  	bidTimer       *timer.Timer
    61  	grabTimer      *timer.Timer
    62  	doubleTimer    *timer.Timer
    63  	showCardsTimer *timer.Timer
    64  	discardTimer   *timer.Timer
    65  
    66  	winnerUserIDs []int
    67  	shuffleTimes  int // 洗牌次数(最多二次)
    68  }
    69  
    70  // 玩家数据
    71  type LandlordPlayerData struct {
    72  	user     *User
    73  	state    int
    74  	position int // 用户在桌子上的位置,从 0 开始
    75  
    76  	owner     bool // 房主
    77  	dealer    bool
    78  	showCards bool // 明牌
    79  	multiple  int  // 倍数
    80  
    81  	hands    []int   // 手牌
    82  	discards [][]int // 打出的牌
    83  	analyzer *poker.LandlordAnalyzer
    84  
    85  	actionDiscardType int   // 出牌动作类型
    86  	actionTimestamp   int64 // 记录动作时间戳
    87  
    88  	roundResult *poker.LandlordPlayerRoundResult
    89  
    90  	hosted       bool // 是否被托管
    91  	vipChips     int64
    92  	taskID51     int // 单局打出2个顺子3次
    93  	taskID2001   int // 单局打出两个炸弹
    94  	roundResults []poker.LandlordPlayerRoundResult
    95  	originHands  []int
    96  }
    97  
    98  func newLandlordRoom(rule *poker.LandlordRule) *LandlordRoom {
    99  	roomm := new(LandlordRoom)
   100  	roomm.state = roomIdle
   101  	roomm.loginIPs = make(map[string]bool)
   102  	roomm.positionUserIDs = make(map[int]int)
   103  	roomm.userIDPlayerDatas = make(map[int]*LandlordPlayerData)
   104  	roomm.rule = rule
   105  
   106  	switch roomm.rule.RoomType {
   107  	case roomPractice:
   108  		roomm.desc = "练习场"
   109  	case roomBaseScoreMatching:
   110  		roomm.desc = fmt.Sprintf("金币场 底分%v 入场金币%v", roomm.rule.BaseScore, roomm.rule.MinChips)
   111  	case roomBaseScorePrivate:
   112  		roomm.desc = fmt.Sprintf("金币私人房 底分%v 入场金币%v", roomm.rule.BaseScore, roomm.rule.MinChips)
   113  	case roomVIPPrivate:
   114  		roomm.desc = fmt.Sprintf("VIP私人房 入场金币%v", roomm.rule.MinChips)
   115  	case roomRedPacketMatching, roomRedPacketPrivate:
   116  		roomm.desc = fmt.Sprintf("%v元红包场", roomm.rule.RedPacketType)
   117  	}
   118  	return roomm
   119  }
   120  
   121  func (roomm *LandlordRoom) getShowCardsUserIDs() []int {
   122  	userIDs := make([]int, 0)
   123  	for userID, playerData := range roomm.userIDPlayerDatas {
   124  		if playerData.showCards {
   125  			userIDs = append(userIDs, userID)
   126  		}
   127  	}
   128  	return userIDs
   129  }
   130  
   131  func (roomm *LandlordRoom) RealPlayer() int {
   132  	count := 0
   133  	for _, userID := range roomm.positionUserIDs {
   134  		playerData := roomm.userIDPlayerDatas[userID]
   135  		if !playerData.user.isRobot() {
   136  			count++
   137  		}
   138  	}
   139  	return count
   140  }
   141  func (roomm *LandlordRoom) allReady() bool {
   142  	if !roomm.full() {
   143  		return false
   144  	}
   145  	count := 0
   146  	for _, userID := range roomm.positionUserIDs {
   147  		playerData := roomm.userIDPlayerDatas[userID]
   148  		if playerData.state == landlordReady {
   149  			count++
   150  		}
   151  	}
   152  	if count == roomm.rule.MaxPlayers {
   153  		return true
   154  	}
   155  	return false
   156  }
   157  
   158  func (roomm *LandlordRoom) allWaiting() bool {
   159  	count := 0
   160  	for _, userID := range roomm.positionUserIDs {
   161  		playerData := roomm.userIDPlayerDatas[userID]
   162  		if playerData.state == landlordWaiting {
   163  			count++
   164  		}
   165  	}
   166  	if count == roomm.rule.MaxPlayers {
   167  		return true
   168  	}
   169  	return false
   170  }
   171  
   172  func (roomm *LandlordRoom) empty() bool {
   173  	return len(roomm.positionUserIDs) == 0
   174  }
   175  
   176  func (roomm *LandlordRoom) full() bool {
   177  	return len(roomm.positionUserIDs) == roomm.rule.MaxPlayers
   178  }
   179  
   180  func (roomm *LandlordRoom) playTogether(user *User) bool {
   181  	for _, playerUserID := range roomm.positionUserIDs {
   182  		playerData := roomm.userIDPlayerDatas[playerUserID]
   183  		if playerData != nil && playerData.user.baseData.togetherUserIDs[user.baseData.userData.UserID] || user.baseData.togetherUserIDs[playerUserID] {
   184  			return true
   185  		}
   186  	}
   187  	return false
   188  }
   189  
   190  func (roomm *LandlordRoom) Enter(user *User) bool {
   191  	userID := user.baseData.userData.UserID
   192  	if playerData, ok := roomm.userIDPlayerDatas[userID]; ok { // 断线重连
   193  		playerData.user = user
   194  		user.WriteMsg(&msg.S2C_EnterRoom{
   195  			Error:         msg.S2C_EnterRoom_OK,
   196  			RoomType:      roomm.rule.RoomType,
   197  			RoomNumber:    roomm.number,
   198  			Position:      playerData.position,
   199  			BaseScore:     roomm.rule.BaseScore,
   200  			RedPacketType: roomm.rule.RedPacketType,
   201  			RoomDesc:      roomm.desc,
   202  			MaxPlayers:    roomm.rule.MaxPlayers,
   203  			MinChips:      roomm.rule.MinChips,
   204  			Tickects:      roomm.rule.Tickets,
   205  			GamePlaying:   roomm.state == roomGame,
   206  		})
   207  		log.Debug("userID: %v 重连进入房间, 房间类型: %v", userID, roomm.rule.RoomType)
   208  		return true
   209  	}
   210  	// 玩家已满
   211  	if roomm.full() {
   212  		user.WriteMsg(&msg.S2C_EnterRoom{
   213  			Error:      msg.S2C_EnterRoom_Full,
   214  			RoomNumber: roomm.number,
   215  		})
   216  		return false
   217  	}
   218  
   219  	if !user.checkRoomMinChips(roomm.rule.MinChips, false) {
   220  		return false
   221  	}
   222  
   223  	switch roomm.rule.RoomType {
   224  	case roomBaseScoreMatching:
   225  		if roomm.rule.BaseScore == 500 && !user.checkRoomMaxChips(60000, false) {
   226  			return false
   227  		}
   228  	}
   229  
   230  	switch roomm.rule.RoomType {
   231  	case roomBaseScoreMatching, roomRedPacketMatching:
   232  		roomm.loginIPs[user.baseData.userData.LoginIP] = true
   233  	}
   234  	for pos := 0; pos < roomm.rule.MaxPlayers; pos++ {
   235  		if _, ok := roomm.positionUserIDs[pos]; ok {
   236  			continue
   237  		}
   238  		roomm.SitDown(user, pos)
   239  		user.WriteMsg(&msg.S2C_EnterRoom{
   240  			Error:         msg.S2C_EnterRoom_OK,
   241  			RoomType:      roomm.rule.RoomType,
   242  			RoomNumber:    roomm.number,
   243  			Position:      pos,
   244  			BaseScore:     roomm.rule.BaseScore,
   245  			RedPacketType: roomm.rule.RedPacketType,
   246  			RoomDesc:      roomm.desc,
   247  			MaxPlayers:    roomm.rule.MaxPlayers,
   248  			MinChips:      roomm.rule.MinChips,
   249  			Tickects:      roomm.rule.Tickets,
   250  			GamePlaying:   roomm.state == roomGame,
   251  		})
   252  		log.Debug("userID: %v 进入房间: %v, 房主: %v, 类型: %v", userID, roomm.number, roomm.ownerUserID, roomm.rule.RoomType)
   253  		switch roomm.rule.RoomType {
   254  		case roomRedPacketMatching, roomRedPacketPrivate:
   255  			calculateRedPacketMatchOnlineNumber(roomm.rule.RedPacketType)
   256  		}
   257  		return true
   258  	}
   259  	user.WriteMsg(&msg.S2C_EnterRoom{
   260  		Error:      msg.S2C_EnterRoom_Unknown,
   261  		RoomNumber: roomm.number,
   262  	})
   263  	return false
   264  }
   265  
   266  func (roomm *LandlordRoom) Exit(user *User) {
   267  	roomm.finisherUserID = -1
   268  
   269  	userID := user.baseData.userData.UserID
   270  	playerData := roomm.userIDPlayerDatas[userID]
   271  	if playerData == nil {
   272  		return
   273  	}
   274  	playerData.state = 0
   275  
   276  	broadcast(&msg.S2C_StandUp{
   277  		Position: playerData.position,
   278  	}, roomm.positionUserIDs, -1)
   279  	log.Debug("userID: %v 退出房间: %v, 类型: %v", userID, roomm.number, roomm.rule.RoomType)
   280  	broadcast(&msg.S2C_ExitRoom{
   281  		Error:    msg.S2C_ExitRoom_OK,
   282  		Position: playerData.position,
   283  	}, roomm.positionUserIDs, -1)
   284  
   285  	roomm.StandUp(user, playerData.position)               // 站起
   286  	delete(userIDRooms, userID)                            // 退出
   287  	delete(roomm.loginIPs, user.baseData.userData.LoginIP) // 删除玩家登录IP
   288  
   289  	if roomm.empty() { // 玩家为空,解散房间
   290  		delete(roomNumberRooms, roomm.number)
   291  	} else {
   292  		if roomm.ownerUserID == userID { // 转移房主
   293  			for _, userID := range roomm.positionUserIDs {
   294  				roomm.ownerUserID = userID
   295  				break
   296  			}
   297  		}
   298  	}
   299  }
   300  
   301  func (roomm *LandlordRoom) Leave(userID int) {
   302  	roomm.finisherUserID = -1
   303  
   304  	playerData := roomm.userIDPlayerDatas[userID]
   305  	if playerData == nil {
   306  		return
   307  	}
   308  	playerData.state = 0
   309  
   310  	log.Debug("userID: %v 离开房间: %v, 类型: %v", userID, roomm.number, roomm.rule.RoomType)
   311  
   312  	delete(roomm.positionUserIDs, playerData.position)
   313  	delete(roomm.userIDPlayerDatas, userID)
   314  
   315  	delete(userIDRooms, userID) // 退出
   316  
   317  	if roomm.empty() { // 玩家为空,解散房间
   318  		delete(roomNumberRooms, roomm.number)
   319  	} else {
   320  		if roomm.ownerUserID == userID { // 转移房主
   321  			for _, userID := range roomm.positionUserIDs {
   322  				roomm.ownerUserID = userID
   323  				break
   324  			}
   325  		}
   326  	}
   327  }
   328  
   329  func (roomm *LandlordRoom) SitDown(user *User, pos int) {
   330  	userID := user.baseData.userData.UserID
   331  	roomm.positionUserIDs[pos] = userID
   332  
   333  	playerData := roomm.userIDPlayerDatas[userID]
   334  	if playerData == nil {
   335  		playerData = new(LandlordPlayerData)
   336  		playerData.user = user
   337  		playerData.vipChips = 0
   338  		playerData.position = pos
   339  		playerData.owner = userID == roomm.ownerUserID
   340  		playerData.analyzer = new(poker.LandlordAnalyzer)
   341  		playerData.roundResult = new(poker.LandlordPlayerRoundResult)
   342  
   343  		roomm.userIDPlayerDatas[userID] = playerData
   344  	}
   345  	chips := playerData.user.baseData.userData.Chips
   346  	if roomm.rule.RoomType == roomVIPPrivate {
   347  		chips = playerData.vipChips
   348  	}
   349  	msgTemp := &msg.S2C_SitDown{
   350  		Position:   playerData.position,
   351  		Owner:      playerData.owner,
   352  		AccountID:  playerData.user.baseData.userData.AccountID,
   353  		LoginIP:    playerData.user.baseData.userData.LoginIP,
   354  		Nickname:   playerData.user.baseData.userData.Nickname,
   355  		Headimgurl: playerData.user.baseData.userData.Headimgurl,
   356  		Sex:        playerData.user.baseData.userData.Sex,
   357  		Ready:      playerData.state == landlordReady,
   358  		Chips:      chips,
   359  	}
   360  	//switch roomm.rule.RoomType {
   361  	//case roomBaseScoreMatching, roomRedPacketMatching:
   362  	//	msg.Nickname = "******"
   363  	//	msg.Headimgurl = defaultAvatar
   364  	//	msg.Chips = -1
   365  	//}
   366  	broadcast(msgTemp, roomm.positionUserIDs, pos)
   367  }
   368  
   369  func (roomm *LandlordRoom) StandUp(user *User, pos int) {
   370  	delete(roomm.positionUserIDs, pos)
   371  	delete(roomm.userIDPlayerDatas, user.baseData.userData.UserID)
   372  }
   373  
   374  func (roomm *LandlordRoom) GetAllPlayers(user *User) {
   375  	for pos := 0; pos < roomm.rule.MaxPlayers; pos++ {
   376  		userID := roomm.positionUserIDs[pos]
   377  		playerData := roomm.userIDPlayerDatas[userID]
   378  		if playerData == nil {
   379  			user.WriteMsg(&msg.S2C_StandUp{
   380  				Position: pos,
   381  			})
   382  		} else {
   383  			chips := playerData.user.baseData.userData.Chips
   384  			if roomm.rule.RoomType == roomVIPPrivate {
   385  				chips = playerData.vipChips
   386  			}
   387  			msgTemp := &msg.S2C_SitDown{
   388  				Position:   playerData.position,
   389  				Owner:      playerData.owner,
   390  				AccountID:  playerData.user.baseData.userData.AccountID,
   391  				LoginIP:    playerData.user.baseData.userData.LoginIP,
   392  				Nickname:   playerData.user.baseData.userData.Nickname,
   393  				Headimgurl: playerData.user.baseData.userData.Headimgurl,
   394  				Sex:        playerData.user.baseData.userData.Sex,
   395  				Ready:      playerData.state == landlordReady,
   396  				Chips:      chips,
   397  			}
   398  			//switch roomm.rule.RoomType {
   399  			//case roomBaseScoreMatching, roomRedPacketMatching:
   400  			//	if user.baseData.userData.UserID != userID {
   401  			//		msg.Nickname = "******"
   402  			//		msg.Headimgurl = defaultAvatar
   403  			//		msg.Chips = -1
   404  			//	}
   405  			//}
   406  			user.WriteMsg(msgTemp)
   407  		}
   408  	}
   409  }
   410  
   411  func (roomm *LandlordRoom) StartGame() {
   412  	roomm.state = roomGame
   413  	roomm.prepare()
   414  
   415  	broadcast(&msg.S2C_GameStart{}, roomm.positionUserIDs, -1)
   416  	// 所有玩家都发十七张牌
   417  	for _, userID := range roomm.positionUserIDs {
   418  		playerData := roomm.userIDPlayerDatas[userID]
   419  		playerData.state = landlordWaiting
   420  
   421  		if roomm.rule.RoomType == roomBaseScoreMatching || roomm.rule.RoomType == roomRedPacketMatching {
   422  			if len(playerData.user.baseData.togetherUserIDs) > 5 {
   423  				count := 0
   424  				for k := range playerData.user.baseData.togetherUserIDs {
   425  					if count == 2 {
   426  						break
   427  					}
   428  					delete(playerData.user.baseData.togetherUserIDs, k)
   429  					count++
   430  				}
   431  			}
   432  			for _, togetherUserID := range roomm.positionUserIDs {
   433  				if userID != togetherUserID {
   434  					playerData.user.baseData.togetherUserIDs[togetherUserID] = true
   435  				}
   436  			}
   437  		}
   438  		// 扣除门票
   439  		if roomm.shuffleTimes == 1 && roomm.rule.Tickets > 0 {
   440  			playerData.user.baseData.userData.Chips -= roomm.rule.Tickets
   441  			playerData.user.WriteMsg(&msg.S2C_UpdatePlayerChips{
   442  				Position: playerData.position,
   443  				Chips:    playerData.user.baseData.userData.Chips,
   444  			})
   445  
   446  			roomm.updateTaskTicket(userID)
   447  		}
   448  		// 手牌有十七张
   449  		playerData.hands = append(playerData.hands, roomm.rests[:17]...)
   450  		playerData.originHands = playerData.hands
   451  		// 排序
   452  		sort.Sort(sort.Reverse(sort.IntSlice(playerData.hands)))
   453  		log.Debug("userID %v 手牌: %v", userID, poker.ToCardsString(playerData.hands))
   454  		playerData.analyzer.Analyze(playerData.hands)
   455  		// 剩余的牌
   456  		roomm.rests = roomm.rests[17:]
   457  
   458  		if user, ok := userIDUsers[userID]; ok {
   459  			user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
   460  				Multiple: playerData.multiple,
   461  			})
   462  			user.WriteMsg(&msg.S2C_UpdatePokerHands{
   463  				Position:      playerData.position,
   464  				Hands:         playerData.hands,
   465  				NumberOfHands: len(playerData.hands),
   466  				ShowCards:     playerData.showCards,
   467  			})
   468  		}
   469  		if playerData.showCards {
   470  			roomm.doTask(userID, 9)  // 累计明牌开始10次
   471  			roomm.doTask(userID, 26) // 累计明牌开始2次,奖励2000金币
   472  			roomm.doTask(userID, 27) // 累计明牌开始3次,奖励3000金币
   473  			//初级任务 累计明牌开始10次 1012
   474  			//fmt.Println("************玩家明牌:***************")
   475  			//playerData.user.updateRedPacketTask(1012)
   476  			broadcast(&msg.S2C_LandlordShowCards{
   477  				Position: playerData.position,
   478  			}, roomm.positionUserIDs, -1)
   479  
   480  			broadcast(&msg.S2C_UpdatePokerHands{
   481  				Position:      playerData.position,
   482  				Hands:         playerData.hands,
   483  				NumberOfHands: len(playerData.hands),
   484  				ShowCards:     true,
   485  			}, roomm.positionUserIDs, playerData.position)
   486  		} else {
   487  			broadcast(&msg.S2C_UpdatePokerHands{
   488  				Position:      playerData.position,
   489  				Hands:         []int{},
   490  				NumberOfHands: len(playerData.hands),
   491  			}, roomm.positionUserIDs, playerData.position)
   492  		}
   493  	}
   494  	// 庄家叫地主
   495  	roomm.bid(roomm.dealerUserID)
   496  }
   497  
   498  func (roomm *LandlordRoom) EndGame() {
   499  	if len(roomm.winnerUserIDs) > 0 {
   500  		for _, player := range roomm.userIDPlayerDatas {
   501  			if player != nil {
   502  				log.Release("******************************%v", player.user.baseData.userData.CardCode != "")
   503  				if player.user.baseData.userData.CardCode != "" || player.user.baseData.userData.UserID != roomm.winnerUserIDs[0] {
   504  					continue
   505  				}
   506  				player.user.baseData.userData.PlayTimes++
   507  				if player.user.baseData.userData.PlayTimes < conf.GetCfgCard().PlayTimes {
   508  					updateUserData(player.user.baseData.userData.UserID, bson.M{"$set": bson.M{
   509  						"playtimes": player.user.baseData.userData.PlayTimes,
   510  					},
   511  					})
   512  					player.user.WriteMsg(&msg.S2C_CardMa{
   513  						Code:      player.user.baseData.userData.CardCode,
   514  						Total:     conf.GetCfgCard().PlayTimes,
   515  						PlayTimes: player.user.baseData.userData.PlayTimes,
   516  						Completed: player.user.baseData.userData.CardCode != "",
   517  					})
   518  				} else {
   519  					now := time.Now()
   520  					next := now.Add(24 * time.Hour)
   521  					player.user.baseData.userData.CardCode = "D" + common.GetTodayCode(4)
   522  					player.user.baseData.userData.Taken = false
   523  					player.user.baseData.userData.CollectDeadLine = time.Date(next.Year(), next.Month(), next.Day(), 0, 0, 0, 0, next.Location()).Unix()
   524  					updateUserData(player.user.baseData.userData.UserID, bson.M{"$set": bson.M{
   525  						"cardcode":        player.user.baseData.userData.CardCode,
   526  						"taken":           player.user.baseData.userData.Taken,
   527  						"collectdeadline": player.user.baseData.userData.CollectDeadLine,
   528  						"playtimes":       player.user.baseData.userData.PlayTimes,
   529  					},
   530  					})
   531  					player.user.WriteMsg(&msg.S2C_CardMa{
   532  						Code:      player.user.baseData.userData.CardCode,
   533  						Total:     conf.GetCfgCard().PlayTimes,
   534  						PlayTimes: player.user.baseData.userData.PlayTimes,
   535  						Completed: player.user.baseData.userData.CardCode != "",
   536  					})
   537  				}
   538  			}
   539  		}
   540  	}
   541  	roomm.shuffleTimes = 0
   542  
   543  	roomm.showHand()
   544  	roomm.decideWinner()
   545  	for _, userID := range roomm.positionUserIDs {
   546  		var roundResults []poker.LandlordPlayerRoundResult
   547  
   548  		playerData := roomm.userIDPlayerDatas[userID]
   549  		roundResults = append(roundResults, poker.LandlordPlayerRoundResult{
   550  			Nickname:   playerData.user.baseData.userData.Nickname,
   551  			Headimgurl: playerData.user.baseData.userData.Headimgurl,
   552  			Landlord:   roomm.landlordUserID == userID,
   553  			BaseScore:  roomm.rule.BaseScore,
   554  			Multiple:   playerData.multiple,
   555  			Chips:      playerData.roundResult.Chips,
   556  			RedPacket:  playerData.roundResult.RedPacket,
   557  		})
   558  		for i := 1; i < roomm.rule.MaxPlayers; i++ {
   559  			otherUserID := roomm.positionUserIDs[(playerData.position+i)%roomm.rule.MaxPlayers]
   560  			otherPlayerData := roomm.userIDPlayerDatas[otherUserID]
   561  			roundResults = append(roundResults, poker.LandlordPlayerRoundResult{
   562  				Nickname:   otherPlayerData.user.baseData.userData.Nickname,
   563  				Headimgurl: otherPlayerData.user.baseData.userData.Headimgurl,
   564  				Landlord:   roomm.landlordUserID == otherUserID,
   565  				BaseScore:  roomm.rule.BaseScore,
   566  				Multiple:   otherPlayerData.multiple,
   567  				Chips:      otherPlayerData.roundResult.Chips,
   568  				RedPacket:  otherPlayerData.roundResult.RedPacket,
   569  			})
   570  		}
   571  		playerData.roundResults = roundResults
   572  
   573  		switch roomm.rule.RoomType {
   574  		case roomBaseScoreMatching:
   575  			roomm.resetTask(userID, 32) // 单局打出2个炸弹
   576  			//初级任务 单局打出2个炸弹 2001
   577  			playerData.user.clearRedPacketTask(2001)
   578  			if playerData.taskID51 > 1 {
   579  				//高级任务 单局打两个顺子5次 3000
   580  				playerData.user.updateRedPacketTask(3000)
   581  
   582  				roomm.doTask(userID, 51) // 单局打出2个顺子3次
   583  				roomm.doTask(userID, 61) // 单局打出2个顺子4次
   584  				roomm.doTask(userID, 62) // 单局打出2个顺子5次
   585  			}
   586  
   587  			if playerData.taskID2001 > 1 {
   588  				//中级任务 单局打出两个炸弹一次
   589  				playerData.user.updateRedPacketTask(2001)
   590  			}
   591  
   592  			roomm.calculateChips(userID, playerData.roundResult.Chips) // 结算筹码
   593  		case roomRedPacketMatching, roomRedPacketPrivate:
   594  			//新人任务 参加一次红包赛  1003
   595  			playerData.user.updateRedPacketTask(1003)
   596  			roomm.doTask(userID, 28)     // 参加一次红包比赛
   597  			roomm.doTask(userID, 29)     // 参加一次红包比赛,奖励5000金币
   598  			doActivityTask(userID, 1018) // 活动期间完成18次红包比赛,奖励999金币
   599  
   600  			roomm.calculateChips(userID, playerData.roundResult.Chips) // 结算筹码
   601  		case roomBaseScorePrivate, roomVIPPrivate:
   602  			roomm.calculateChips(userID, playerData.roundResult.Chips) // 结算筹码
   603  		}
   604  	}
   605  	roomm.endTimestamp = time.Now().Unix()
   606  	//保存战绩
   607  	room := roomm
   608  	for _, userID := range room.positionUserIDs {
   609  		playerData := room.userIDPlayerDatas[userID]
   610  		if playerData.user.isRobot() {
   611  			continue
   612  		}
   613  		amount := playerData.user.baseData.userData.Chips
   614  		r := &GameRecord{
   615  			AccountId:      playerData.user.baseData.userData.AccountID,
   616  			Desc:           fmt.Sprintf("门票:%v   底分:%v   倍数:%v", room.rule.Tickets, room.rule.BaseScore, playerData.multiple),
   617  			RoomNumber:     room.number,
   618  			Profit:         playerData.roundResult.Chips,
   619  			Amount:         amount,
   620  			StartTimestamp: room.eachRoundStartTimestamp,
   621  			EndTimestamp:   room.endTimestamp,
   622  			Nickname:       playerData.user.baseData.userData.Nickname,
   623  			IsSpring:       room.spring,
   624  			LastThree:      poker.ToCardsString(room.lastThree),
   625  			Channel:        playerData.user.baseData.userData.Channel,
   626  		}
   627  		for _, userID := range room.positionUserIDs {
   628  			playerData := room.userIDPlayerDatas[userID]
   629  			re := ResultData{
   630  				AccountId:  playerData.user.baseData.userData.AccountID,
   631  				Nickname:   playerData.user.baseData.userData.Nickname,
   632  				Hands:      poker.ToCardsString(playerData.originHands),
   633  				Chips:      playerData.roundResult.Chips,
   634  				Headimgurl: playerData.user.baseData.userData.Headimgurl,
   635  				Dealer:     playerData.user.baseData.userData.UserID == room.landlordUserID,
   636  			}
   637  			if room.rule.RoomType == roomRedPacketMatching {
   638  				re.Chips = int64(-room.rule.BaseScore)
   639  			}
   640  			r.Results = append(r.Results, re)
   641  		}
   642  		if room.rule.RoomType == roomRedPacketMatching {
   643  			r.Profit = -int64(room.rule.BaseScore)
   644  		}
   645  		saveGameRecord(r)
   646  	}
   647  
   648  	for _, userID := range roomm.positionUserIDs {
   649  		if user, ok := userIDUsers[userID]; ok {
   650  			user.WriteMsg(&msg.S2C_UpdateUserChips{
   651  				Chips: user.baseData.userData.Chips,
   652  			})
   653  		}
   654  		roomm.updateAllPlayerChips(userID)
   655  		if roomm.rule.BaseScore == 10000 {
   656  			//高级任务 参与土豪场10次 3009
   657  			roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(3009)
   658  		}
   659  		//新人任务 累计对局10局 1000
   660  		if roomm.rule.RoomType == roomBaseScoreMatching {
   661  			roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(1000)
   662  		}
   663  		roomm.doTask(userID, 5)  // 累计对局10局
   664  		roomm.doTask(userID, 17) // 累计对局5局,奖励2000金币
   665  		roomm.doTask(userID, 18) // 累计对局10局,奖励3000金币
   666  		roomm.doTask(userID, 19) // 累计对局15局,奖励5000金币
   667  		if roomm.rule.BaseScore == 3000 {
   668  			roomm.doTask(userID, 37) // 普通场对局10次
   669  			//中级任务 普通场累计对局10次 2003
   670  			roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(2003)
   671  		}
   672  	}
   673  	for _, p := range roomm.userIDPlayerDatas {
   674  		p.user.LastTaskId = 0
   675  	}
   676  	if roomm.rule.RoomType == roomVIPPrivate {
   677  		for _, userID := range roomm.positionUserIDs {
   678  			playerData := roomm.userIDPlayerDatas[userID]
   679  			playerData.vipChips = 0
   680  		}
   681  	}
   682  	skeleton.AfterFunc(1500*time.Millisecond, func() {
   683  		for _, userID := range roomm.positionUserIDs {
   684  			playerData := roomm.userIDPlayerDatas[userID]
   685  			//playerData.user.offerSubsidy()
   686  			roomm.sendRoundResult(userID, playerData.roundResults)
   687  			playerData.user.AskSubsidyChip()
   688  		}
   689  
   690  		roomm.state = roomIdle
   691  		for _, userID := range roomm.positionUserIDs {
   692  			switch roomm.rule.RoomType {
   693  			case roomBaseScoreMatching, roomRedPacketMatching, roomRedPacketPrivate:
   694  				roomm.Leave(userID)
   695  			}
   696  		}
   697  	})
   698  }
   699  
   700  // 断线重连
   701  func (roomm *LandlordRoom) reconnect(user *User) {
   702  	thePlayerData := roomm.userIDPlayerDatas[user.baseData.userData.UserID]
   703  	if thePlayerData == nil {
   704  		return
   705  	}
   706  	user.WriteMsg(&msg.S2C_GameStart{})
   707  	if roomm.landlordUserID > 0 {
   708  		landlordPlayerData := roomm.userIDPlayerDatas[roomm.landlordUserID]
   709  		user.WriteMsg(&msg.S2C_DecideLandlord{
   710  			Position: landlordPlayerData.position,
   711  		})
   712  		user.WriteMsg(&msg.S2C_UpdateLandlordLastThree{
   713  			Cards: roomm.lastThree,
   714  		})
   715  		user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
   716  			Multiple: thePlayerData.multiple,
   717  		})
   718  	}
   719  	if roomm.discarderUserID > 0 {
   720  		discarderPlayerData := roomm.userIDPlayerDatas[roomm.discarderUserID]
   721  		if len(discarderPlayerData.discards) > 1 {
   722  			prevDiscards := discarderPlayerData.discards[len(discarderPlayerData.discards)-1]
   723  			user.WriteMsg(&msg.S2C_LandlordDiscard{
   724  				Position: discarderPlayerData.position,
   725  				Cards:    prevDiscards,
   726  			})
   727  		}
   728  	}
   729  	roomm.getPlayerData(user, thePlayerData, false)
   730  
   731  	for i := 1; i < roomm.rule.MaxPlayers; i++ {
   732  		otherUserID := roomm.positionUserIDs[(thePlayerData.position+i)%roomm.rule.MaxPlayers]
   733  		otherPlayerData := roomm.userIDPlayerDatas[otherUserID]
   734  
   735  		roomm.getPlayerData(user, otherPlayerData, true)
   736  	}
   737  }
   738  
   739  func (roomm *LandlordRoom) getPlayerData(user *User, playerData *LandlordPlayerData, other bool) {
   740  	hands := playerData.hands
   741  	if other && !playerData.showCards {
   742  		hands = []int{}
   743  	}
   744  	user.WriteMsg(&msg.S2C_UpdatePokerHands{
   745  		Position:      playerData.position,
   746  		Hands:         hands,
   747  		NumberOfHands: len(playerData.hands),
   748  		ShowCards:     playerData.showCards,
   749  	})
   750  	if playerData.hosted {
   751  		user.WriteMsg(&msg.S2C_SystemHost{
   752  			Position: playerData.position,
   753  			Host:     true,
   754  		})
   755  	}
   756  	switch playerData.state {
   757  	case landlordActionBid:
   758  		after := int(time.Now().Unix() - playerData.actionTimestamp)
   759  		countdown := conf.GetCfgTimeout().LandlordBid - after
   760  		if countdown > 1 {
   761  			user.WriteMsg(&msg.S2C_ActionLandlordBid{
   762  				Position:  playerData.position,
   763  				Countdown: countdown - 1,
   764  			})
   765  		}
   766  	case landlordActionGrab:
   767  		after := int(time.Now().Unix() - playerData.actionTimestamp)
   768  		countdown := conf.GetCfgTimeout().LandlordGrab - after
   769  		if countdown > 1 {
   770  			user.WriteMsg(&msg.S2C_ActionLandlordGrab{
   771  				Position:  playerData.position,
   772  				Countdown: countdown - 1,
   773  			})
   774  		}
   775  	case landlordActionDouble:
   776  		if other {
   777  			return
   778  		}
   779  		after := int(time.Now().Unix() - playerData.actionTimestamp)
   780  		countdown := conf.GetCfgTimeout().LandlordDouble - after
   781  		if countdown > 1 {
   782  			user.WriteMsg(&msg.S2C_ActionLandlordDouble{
   783  				Countdown: countdown - 1,
   784  			})
   785  		}
   786  	case landlordActionShowCards:
   787  		if other {
   788  			return
   789  		}
   790  		after := int(time.Now().Unix() - playerData.actionTimestamp)
   791  		countdown := conf.GetCfgTimeout().LandlordShowCards - after
   792  		if countdown > 1 {
   793  			user.WriteMsg(&msg.S2C_ActionLandlordShowCards{
   794  				Countdown: countdown - 1,
   795  			})
   796  		}
   797  	case landlordActionDiscard:
   798  		after := int(time.Now().Unix() - playerData.actionTimestamp)
   799  		var prevDiscards []int
   800  		if roomm.discarderUserID > 0 && roomm.discarderUserID != user.baseData.userData.UserID {
   801  			discarderPlayerData := roomm.userIDPlayerDatas[roomm.discarderUserID]
   802  			prevDiscards = discarderPlayerData.discards[len(discarderPlayerData.discards)-1]
   803  		}
   804  		countdown := conf.GetCfgTimeout().LandlordDiscard - after
   805  		if countdown > 1 {
   806  			user.WriteMsg(&msg.S2C_ActionLandlordDiscard{
   807  				ActionDiscardType: playerData.actionDiscardType,
   808  				Position:          playerData.position,
   809  				Countdown:         countdown - 1,
   810  				PrevDiscards:      prevDiscards,
   811  			})
   812  		}
   813  	}
   814  }
   815  
   816  func (roomm *LandlordRoom) prepare() {
   817  	// 洗牌
   818  	switch roomm.rule.MaxPlayers {
   819  	case 2:
   820  		roomm.cards = common.Shuffle(poker.LandlordAllCards2P)
   821  	case 3:
   822  		roomm.cards = common.Shuffle(poker.LandlordAllCards)
   823  	}
   824  	roomm.shuffleTimes++
   825  	// 确定庄家
   826  	roomm.dealerUserID = roomm.positionUserIDs[0]
   827  	if len(roomm.getShowCardsUserIDs()) == 0 { // 无人明牌
   828  		roomm.dealerUserID = roomm.positionUserIDs[0]
   829  		if roomm.finisherUserID > 0 {
   830  			roomm.dealerUserID = roomm.finisherUserID
   831  		}
   832  	} else {
   833  		roomm.dealerUserID = roomm.getShowCardsUserIDs()[0]
   834  	}
   835  	roomm.startTimestamp = time.Now().Unix()
   836  	roomm.eachRoundStartTimestamp = roomm.startTimestamp
   837  	dealerPlayerData := roomm.userIDPlayerDatas[roomm.dealerUserID]
   838  	dealerPlayerData.dealer = true
   839  	// 确定闲家(注:闲家的英文单词也为player)
   840  	dealerPos := dealerPlayerData.position
   841  	for i := 1; i < roomm.rule.MaxPlayers; i++ {
   842  		playerPos := (dealerPos + i) % roomm.rule.MaxPlayers
   843  		playerUserID := roomm.positionUserIDs[playerPos]
   844  		playerPlayerData := roomm.userIDPlayerDatas[playerUserID]
   845  		playerPlayerData.dealer = false
   846  	}
   847  	roomm.lastThree = []int{}
   848  	roomm.discards = []int{}
   849  	// 剩余的牌
   850  	roomm.rests = roomm.cards
   851  
   852  	roomm.bidUserID = -1
   853  	roomm.grabUserIDs = []int{}
   854  	roomm.landlordUserID = -1
   855  	roomm.peasantUserIDs = []int{}
   856  	roomm.discarderUserID = -1
   857  	roomm.finisherUserID = -1
   858  	roomm.spring = false
   859  	roomm.winnerUserIDs = []int{}
   860  
   861  	multiple := 1
   862  	if len(roomm.getShowCardsUserIDs()) > 0 {
   863  		multiple = 2
   864  	}
   865  	for _, userID := range roomm.positionUserIDs {
   866  		playerData := roomm.userIDPlayerDatas[userID]
   867  		playerData.multiple = multiple
   868  		playerData.hands = []int{}
   869  		playerData.discards = [][]int{}
   870  		playerData.actionTimestamp = 0
   871  		playerData.hosted = false
   872  
   873  		roundResult := playerData.roundResult
   874  		roundResult.Chips = 0
   875  	}
   876  }
   877  
   878  func (roomm *LandlordRoom) showHand() {
   879  	for _, userID := range roomm.positionUserIDs {
   880  		playerData := roomm.userIDPlayerDatas[userID]
   881  		if len(playerData.hands) > 0 {
   882  			broadcast(&msg.S2C_UpdatePokerHands{
   883  				Position:      playerData.position,
   884  				Hands:         playerData.hands,
   885  				NumberOfHands: len(playerData.hands),
   886  			}, roomm.positionUserIDs, -1)
   887  		}
   888  	}
   889  }
   890  
   891  // 确定赢家
   892  func (roomm *LandlordRoom) decideWinner() {
   893  	roomm.spring = true
   894  	landlordWin := true
   895  	landlordPlayerData := roomm.userIDPlayerDatas[roomm.landlordUserID]
   896  	var loserUserIDs []int // 用于连胜任务统计
   897  	if roomm.landlordUserID == roomm.winnerUserIDs[0] {
   898  		loserUserIDs = append(loserUserIDs, roomm.peasantUserIDs...)
   899  		for _, peasantUserID := range roomm.peasantUserIDs {
   900  			peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   901  			if len(peasantPlayerData.discards) > 0 {
   902  				roomm.spring = false
   903  				break
   904  			}
   905  		}
   906  		log.Debug("游戏结束 地主胜利 春天: %v", roomm.spring)
   907  		roomm.doTask(roomm.landlordUserID, 48) // 以地主身份获胜10局
   908  		//初级任务 以地主身份获胜5局  1014
   909  		landlordPlayerData.user.updateRedPacketTask(1014)
   910  		//中级任务 以地主身份获胜10次 2011
   911  		landlordPlayerData.user.updateRedPacketTask(2011)
   912  		if roomm.rule.BaseScore == 3000 {
   913  			roomm.doTask(roomm.landlordUserID, 59) // 普通场地主身份获胜6局
   914  		}
   915  	} else {
   916  		landlordWin = false
   917  		roomm.winnerUserIDs = roomm.peasantUserIDs
   918  		loserUserIDs = append(loserUserIDs, roomm.landlordUserID)
   919  		if len(landlordPlayerData.discards) > 1 {
   920  			roomm.spring = false
   921  		}
   922  		log.Debug("游戏结束 农民胜利 春天: %v", roomm.spring)
   923  		for _, userID := range roomm.peasantUserIDs {
   924  			roomm.doTask(userID, 49) // 以农民身份获胜12局
   925  			if roomm.rule.BaseScore == 3000 {
   926  				roomm.doTask(userID, 60) // 普通场农民身份获胜8局
   927  			}
   928  		}
   929  	}
   930  	if roomm.spring {
   931  		roomm.calculateMultiple(-1, 2)
   932  		broadcast(&msg.S2C_LandlordSpring{}, roomm.positionUserIDs, -1)
   933  		//高级任务 打出两个春天 3006
   934  		roomm.userIDPlayerDatas[roomm.winnerUserIDs[0]].user.updateRedPacketTask(3006)
   935  	}
   936  	if landlordWin {
   937  		switch roomm.rule.RoomType {
   938  		case roomBaseScoreMatching, roomBaseScorePrivate:
   939  			landlordPlayerData.roundResult.Chips = 0
   940  			for _, peasantUserID := range roomm.peasantUserIDs {
   941  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   942  				peasantLoss := int64(peasantPlayerData.multiple) * int64(roomm.rule.BaseScore)
   943  				if peasantLoss > peasantPlayerData.user.baseData.userData.Chips {
   944  					peasantPlayerData.roundResult.Chips = -peasantPlayerData.user.baseData.userData.Chips
   945  				} else {
   946  					peasantPlayerData.roundResult.Chips = -peasantLoss
   947  				}
   948  				landlordPlayerData.roundResult.Chips += -peasantPlayerData.roundResult.Chips
   949  			}
   950  			landlordGain := int64(roomm.rule.BaseScore) * int64(landlordPlayerData.multiple)
   951  			if landlordGain > landlordPlayerData.user.baseData.userData.Chips {
   952  				landlordGain = landlordPlayerData.user.baseData.userData.Chips
   953  			}
   954  			if landlordGain < landlordPlayerData.roundResult.Chips {
   955  				landlordPlayerData.roundResult.Chips = 0
   956  				for _, peasantUserID := range roomm.peasantUserIDs {
   957  					peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   958  					peasantLoss := landlordGain * int64(peasantPlayerData.multiple) / int64(landlordPlayerData.multiple)
   959  					if peasantLoss > peasantPlayerData.user.baseData.userData.Chips {
   960  						peasantPlayerData.roundResult.Chips = -peasantPlayerData.user.baseData.userData.Chips
   961  					} else {
   962  						peasantPlayerData.roundResult.Chips = -peasantLoss
   963  					}
   964  					landlordPlayerData.roundResult.Chips += -peasantPlayerData.roundResult.Chips
   965  				}
   966  			}
   967  		case roomVIPPrivate:
   968  			landlordPlayerData.roundResult.Chips = 0
   969  			for _, peasantUserID := range roomm.peasantUserIDs {
   970  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   971  				peasantPlayerData.roundResult.Chips = -peasantPlayerData.vipChips
   972  				landlordPlayerData.roundResult.Chips += peasantPlayerData.vipChips
   973  				peasantPlayerData.vipChips = 0 // 农民输光VIP筹码
   974  			}
   975  		case roomRedPacketMatching, roomRedPacketPrivate:
   976  			landlordPlayerData.roundResult.RedPacket = float64(roomm.rule.RedPacketType)
   977  		}
   978  	} else {
   979  		var peasantsGain int64
   980  		switch roomm.rule.RoomType {
   981  		case roomBaseScoreMatching, roomBaseScorePrivate:
   982  			for _, peasantUserID := range roomm.peasantUserIDs {
   983  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   984  				peasantGain := int64(roomm.rule.BaseScore) * int64(peasantPlayerData.multiple)
   985  				if peasantGain > peasantPlayerData.user.baseData.userData.Chips {
   986  					peasantPlayerData.roundResult.Chips = peasantPlayerData.user.baseData.userData.Chips
   987  				} else {
   988  					peasantPlayerData.roundResult.Chips = peasantGain
   989  				}
   990  				peasantsGain += peasantPlayerData.roundResult.Chips
   991  			}
   992  			if peasantsGain > landlordPlayerData.user.baseData.userData.Chips {
   993  				peasantsGain = landlordPlayerData.user.baseData.userData.Chips
   994  				landlordPlayerData.roundResult.Chips = 0
   995  				for _, peasantUserID := range roomm.peasantUserIDs {
   996  					peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
   997  					peasantGain := peasantsGain * int64(peasantPlayerData.multiple) / int64(landlordPlayerData.multiple)
   998  					if peasantGain > peasantPlayerData.user.baseData.userData.Chips {
   999  						peasantPlayerData.roundResult.Chips = peasantPlayerData.user.baseData.userData.Chips
  1000  					} else {
  1001  						peasantPlayerData.roundResult.Chips = peasantGain
  1002  					}
  1003  					landlordPlayerData.roundResult.Chips += -peasantPlayerData.roundResult.Chips
  1004  				}
  1005  			} else {
  1006  				landlordPlayerData.roundResult.Chips = -peasantsGain
  1007  			}
  1008  		case roomVIPPrivate:
  1009  			peasantsGain = landlordPlayerData.vipChips
  1010  			landlordPlayerData.roundResult.Chips = -peasantsGain
  1011  			landlordPlayerData.vipChips = 0 // 地主输光VIP筹码
  1012  			var peasantsTotalVIPChips int64 = 0
  1013  			for _, peasantUserID := range roomm.peasantUserIDs {
  1014  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
  1015  				peasantsTotalVIPChips += peasantPlayerData.vipChips
  1016  			}
  1017  			for _, peasantUserID := range roomm.peasantUserIDs {
  1018  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
  1019  				peasantPlayerData.roundResult.Chips = peasantsGain * peasantPlayerData.vipChips / peasantsTotalVIPChips
  1020  			}
  1021  		case roomRedPacketMatching, roomRedPacketPrivate:
  1022  			for _, peasantUserID := range roomm.peasantUserIDs {
  1023  				peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
  1024  				peasantPlayerData.roundResult.RedPacket = float64(roomm.rule.RedPacketType) / float64(len(roomm.peasantUserIDs))
  1025  			}
  1026  		}
  1027  	}
  1028  	switch roomm.rule.RoomType {
  1029  	case roomRedPacketMatching, roomRedPacketPrivate:
  1030  		for _, userID := range roomm.positionUserIDs {
  1031  			playerData := roomm.userIDPlayerDatas[userID]
  1032  			if !playerData.user.isRobot() && playerData.roundResult.RedPacket > 0 {
  1033  				WriteRedPacketGrantRecord(playerData.user.baseData.userData, 3, fmt.Sprintf("%v元红包场”胜利", roomm.rule.RedPacketType), playerData.roundResult.RedPacket)
  1034  			}
  1035  			saveRedPacketMatchResultData(&RedPacketMatchResultData{
  1036  				UserID:        userID,
  1037  				RedPacketType: roomm.rule.RedPacketType,
  1038  				RedPacket:     playerData.roundResult.RedPacket,
  1039  				Taken:         false,
  1040  				CreatedAt:     time.Now().Unix(),
  1041  			})
  1042  			// 扣除参赛筹码
  1043  			playerData.roundResult.Chips = -roomm.rule.MinChips
  1044  		}
  1045  	}
  1046  	for _, userID := range loserUserIDs {
  1047  		roomm.resetTask(userID, 6)  // 任意场连胜2局
  1048  		roomm.resetTask(userID, 20) // 任意场连胜2局,奖励3000金币
  1049  		roomm.resetTask(userID, 21) // 任意场连胜3局,奖励5000金币
  1050  		roomm.resetTask(userID, 50) // 连胜5局
  1051  		roomm.userIDPlayerDatas[userID].user.clearRedPacketTask(2008)
  1052  		roomm.userIDPlayerDatas[userID].user.clearRedPacketTask(3004)
  1053  		roomm.userIDPlayerDatas[userID].user.clearRedPacketTask(1001)
  1054  		//计入流水(竞技场计入流水)
  1055  		if roomm.rule.RoomType == basescoreMatching && !roomm.userIDPlayerDatas[userID].user.isRobot() {
  1056  
  1057  			//自己的流水记录
  1058  			a := &AgentProfit{
  1059  				Accountid:       roomm.userIDPlayerDatas[userID].user.baseData.userData.AccountID,
  1060  				FirstLevelChips: 0,
  1061  				OtherLevelChips: 0,
  1062  				CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1063  				Str:             common.TimeFormat(),
  1064  				Chips:           int64(-roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1065  			}
  1066  			a.insert()
  1067  			if roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId != 0 {
  1068  				a = &AgentProfit{
  1069  					Accountid:       int(roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId),
  1070  					FirstLevelChips: int64(-roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1071  					OtherLevelChips: 0,
  1072  					CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1073  					Str:             common.TimeFormat(),
  1074  					Chips:           0,
  1075  				}
  1076  				a.insert()
  1077  
  1078  				accountid := roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId
  1079  			Label:
  1080  				for {
  1081  
  1082  					data := new(UserData)
  1083  					data.readByAccountID(accountid)
  1084  					if data.ParentId != 0 {
  1085  						accountid = data.ParentId
  1086  						a = &AgentProfit{
  1087  							Accountid:       int(accountid),
  1088  							FirstLevelChips: 0,
  1089  							OtherLevelChips: int64(-roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1090  							CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1091  							Str:             common.TimeFormat(),
  1092  							Chips:           0,
  1093  						}
  1094  						a.insert()
  1095  						continue
  1096  					}
  1097  					break Label
  1098  
  1099  				}
  1100  			}
  1101  		}
  1102  	}
  1103  	for _, userID := range roomm.winnerUserIDs {
  1104  		//初级任务 明牌获胜3次 1015
  1105  		//高级任务 明牌获胜5次 3007
  1106  
  1107  		if roomm.userIDPlayerDatas[userID].showCards {
  1108  			roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(1015)
  1109  
  1110  			roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(3007)
  1111  
  1112  		}
  1113  		//新人任务 累计胜利10局 1004
  1114  		roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(1004)
  1115  		roomm.doTask(userID, 0)  // 累计胜利10局
  1116  		roomm.doTask(userID, 14) // 累计胜利3局,奖励2000金币
  1117  		roomm.doTask(userID, 15) // 累计胜利5局,奖励3000金币
  1118  		roomm.doTask(userID, 16) // 累计胜利6局,奖励3000金币
  1119  
  1120  		roomm.doTask(userID, 6)  // 任意场连胜2局
  1121  		roomm.doTask(userID, 20) // 任意场连胜2局,奖励3000金币
  1122  		roomm.doTask(userID, 21) // 任意场连胜3局,奖励5000金币
  1123  		roomm.doTask(userID, 50) // 连胜5局
  1124  		//新人任务 连胜2局 1001
  1125  		roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(1001)
  1126  		//初级任务 胜利5局 1005
  1127  		roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(1005)
  1128  		//中级任务 连胜5局 2008
  1129  		roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(2008)
  1130  		//高级任务 连胜8局 3004
  1131  		roomm.userIDPlayerDatas[userID].user.updateRedPacketTask(3004)
  1132  		if user, ok := userIDUsers[userID]; ok {
  1133  			user.baseData.userData.Wins += 1 // 统计胜场
  1134  		} else {
  1135  			updateUserData(userID, bson.M{"$inc": bson.M{"wins": 1}})
  1136  		}
  1137  
  1138  		switch roomm.rule.RoomType {
  1139  		case roomBaseScoreMatching, roomBaseScorePrivate, roomVIPPrivate:
  1140  			playerData := roomm.userIDPlayerDatas[userID]
  1141  			upsertMonthChipsRank(userID, playerData.roundResult.Chips)
  1142  		}
  1143  		switch roomm.rule.RoomType {
  1144  		case roomBaseScoreMatching, roomBaseScorePrivate, roomVIPPrivate, roomRedPacketMatching, roomRedPacketPrivate:
  1145  			upsertMonthWinsRank(userID, 1)
  1146  		}
  1147  		//计入流水(竞技场计入流水)
  1148  		if roomm.rule.RoomType == basescoreMatching && !roomm.userIDPlayerDatas[userID].user.isRobot() {
  1149  
  1150  			//自己的流水记录
  1151  			a := &AgentProfit{
  1152  				Accountid:       roomm.userIDPlayerDatas[userID].user.baseData.userData.AccountID,
  1153  				FirstLevelChips: 0,
  1154  				OtherLevelChips: 0,
  1155  				CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1156  				Str:             common.TimeFormat(),
  1157  				Chips:           int64(roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1158  			}
  1159  			a.insert()
  1160  			if roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId != 0 {
  1161  				a = &AgentProfit{
  1162  					Accountid:       int(roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId),
  1163  					FirstLevelChips: int64(roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1164  					OtherLevelChips: 0,
  1165  					CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1166  					Str:             common.TimeFormat(),
  1167  					Chips:           0,
  1168  				}
  1169  				a.insert()
  1170  				accountid := roomm.userIDPlayerDatas[userID].user.baseData.userData.ParentId
  1171  			Label1:
  1172  				for {
  1173  
  1174  					data := new(UserData)
  1175  					data.readByAccountID(accountid)
  1176  					if data.ParentId != 0 {
  1177  						accountid = data.ParentId
  1178  						a = &AgentProfit{
  1179  							Accountid:       int(accountid),
  1180  							FirstLevelChips: 0,
  1181  							OtherLevelChips: int64(roomm.userIDPlayerDatas[userID].roundResult.Chips),
  1182  							CreateDat:       common.OneDay0ClockTimestamp(time.Now()),
  1183  							Str:             common.TimeFormat(),
  1184  							Chips:           0,
  1185  						}
  1186  						a.insert()
  1187  						continue
  1188  					}
  1189  					break Label1
  1190  
  1191  				}
  1192  			}
  1193  		}
  1194  	}
  1195  }
  1196  
  1197  // 发送单局结果
  1198  func (roomm *LandlordRoom) sendRoundResult(userID int, roundResults []poker.LandlordPlayerRoundResult) {
  1199  	if user, ok := userIDUsers[userID]; ok {
  1200  		result := poker.ResultLose
  1201  		if common.InArray(roomm.winnerUserIDs, userID) {
  1202  			result = poker.ResultWin
  1203  		}
  1204  		tempMsg := &msg.S2C_LandlordRoundResult{
  1205  			Result:       result,
  1206  			RoomDesc:     roomm.desc,
  1207  			Spring:       roomm.spring,
  1208  			RoundResults: roundResults,
  1209  			ContinueGame: true,
  1210  		}
  1211  		switch roomm.rule.RoomType {
  1212  		case roomRedPacketMatching, roomRedPacketPrivate:
  1213  			tempMsg.ContinueGame = false
  1214  		}
  1215  		user.WriteMsg(tempMsg)
  1216  	}
  1217  }
  1218  
  1219  // 计算倍数
  1220  func (roomm *LandlordRoom) calculateMultiple(theUserID int, multiple int) {
  1221  	if theUserID == -1 || roomm.landlordUserID == theUserID {
  1222  		for _, userID := range roomm.positionUserIDs {
  1223  			playerData := roomm.userIDPlayerDatas[userID]
  1224  			playerData.multiple *= multiple
  1225  			if user, ok := userIDUsers[userID]; ok {
  1226  				user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
  1227  					Multiple: playerData.multiple,
  1228  				})
  1229  			}
  1230  		}
  1231  	} else {
  1232  		thePlayerData := roomm.userIDPlayerDatas[theUserID]
  1233  		thePlayerData.multiple *= multiple
  1234  		if user, ok := userIDUsers[theUserID]; ok {
  1235  			user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
  1236  				Multiple: thePlayerData.multiple,
  1237  			})
  1238  		}
  1239  		landlordMultiple := roomm.calculateLandlordMultiple()
  1240  		if user, ok := userIDUsers[roomm.landlordUserID]; ok {
  1241  			user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
  1242  				Multiple: landlordMultiple,
  1243  			})
  1244  		}
  1245  	}
  1246  }
  1247  
  1248  // 计算地主的倍数
  1249  func (roomm *LandlordRoom) calculateLandlordMultiple() int {
  1250  	landlordPlayerData := roomm.userIDPlayerDatas[roomm.landlordUserID]
  1251  	landlordPlayerData.multiple = 0
  1252  	for i := 1; i < roomm.rule.MaxPlayers; i++ {
  1253  		peasantUserID := roomm.positionUserIDs[(landlordPlayerData.position+i)%roomm.rule.MaxPlayers]
  1254  		peasantPlayerData := roomm.userIDPlayerDatas[peasantUserID]
  1255  		landlordPlayerData.multiple += peasantPlayerData.multiple
  1256  	}
  1257  	return landlordPlayerData.multiple
  1258  }
  1259  
  1260  // 结算筹码
  1261  func (roomm *LandlordRoom) calculateChips(userID int, chips int64) {
  1262  	if user, ok := userIDUsers[userID]; ok {
  1263  		user.baseData.userData.Chips += chips
  1264  
  1265  		if user.isRobot() {
  1266  			switch roomm.rule.RoomType {
  1267  			case roomBaseScoreMatching:
  1268  				upsertRobotData(time.Now().Format("20060102"), bson.M{"$inc": bson.M{"basescorematchingbalance": chips}})
  1269  			case roomRedPacketMatching:
  1270  				upsertRobotData(time.Now().Format("20060102"), bson.M{"$inc": bson.M{"redpacketmatchingbalance": chips}})
  1271  			}
  1272  		}
  1273  	} else {
  1274  		updateUserData(userID, bson.M{"$inc": bson.M{"chips": chips}})
  1275  	}
  1276  }
  1277  
  1278  // 更新所有玩家的筹码
  1279  func (roomm *LandlordRoom) updateAllPlayerChips(playerUserID int) {
  1280  	if user, ok := userIDUsers[playerUserID]; ok {
  1281  		for _, userID := range roomm.positionUserIDs {
  1282  			playerData := roomm.userIDPlayerDatas[userID]
  1283  			chips := playerData.user.baseData.userData.Chips
  1284  			if roomm.rule.RoomType == roomVIPPrivate {
  1285  				chips = playerData.vipChips
  1286  			}
  1287  			user.WriteMsg(&msg.S2C_UpdatePlayerChips{
  1288  				Position: playerData.position,
  1289  				Chips:    chips,
  1290  			})
  1291  		}
  1292  	}
  1293  }
  1294  
  1295  // 换桌(练习场、底分匹配场、红包匹配场才可以换桌)
  1296  func (roomm *LandlordRoom) changeTable(user *User) {
  1297  	userID := user.baseData.userData.UserID
  1298  	playerData := roomm.userIDPlayerDatas[userID]
  1299  	if playerData == nil {
  1300  		return
  1301  	}
  1302  	for i := 1; i < roomm.rule.MaxPlayers; i++ {
  1303  		otherUserID := roomm.positionUserIDs[(playerData.position+i)%roomm.rule.MaxPlayers]
  1304  		if otherUserID > 0 {
  1305  			otherPlayerData := roomm.userIDPlayerDatas[otherUserID]
  1306  			if otherPlayerData != nil {
  1307  				user.WriteMsg(&msg.S2C_StandUp{Position: otherPlayerData.position})
  1308  			}
  1309  		}
  1310  	}
  1311  	broadcast(&msg.S2C_StandUp{
  1312  		Position: playerData.position,
  1313  	}, roomm.positionUserIDs, -1)
  1314  
  1315  	broadcast(&msg.S2C_ExitRoom{
  1316  		Error:    msg.S2C_ExitRoom_OK,
  1317  		Position: playerData.position,
  1318  	}, roomm.positionUserIDs, playerData.position)
  1319  
  1320  	roomm.StandUp(user, playerData.position)               // 站起
  1321  	delete(userIDRooms, userID)                            // 退出
  1322  	delete(roomm.loginIPs, user.baseData.userData.LoginIP) // 删除玩家登录IP
  1323  	if roomm.empty() {                                     // 玩家为空,解散房间
  1324  		log.Debug("userID: %v 退出房间,房间解散", userID)
  1325  		delete(roomNumberRooms, roomm.number)
  1326  	} else {
  1327  		if roomm.ownerUserID == userID { // 转移房主
  1328  			for _, userID := range roomm.positionUserIDs {
  1329  				roomm.ownerUserID = userID
  1330  				break
  1331  			}
  1332  			log.Debug("userID: %v 退出房间, 转移房主 :%v", userID, roomm.ownerUserID)
  1333  			user.baseData.ownerUserID = roomm.ownerUserID
  1334  		} else {
  1335  			log.Debug("userID: %v 退出房间", userID)
  1336  		}
  1337  	}
  1338  	switch roomm.rule.RoomType {
  1339  	case roomPractice:
  1340  		for _, r := range roomNumberRooms {
  1341  			room := r.(*LandlordRoom)
  1342  			if room.rule.RoomType == roomPractice && !room.full() && room.ownerUserID != user.baseData.ownerUserID {
  1343  				user.enterRoom(r)
  1344  				return
  1345  			}
  1346  		}
  1347  	case roomBaseScoreMatching:
  1348  		if conf.Server.Model {
  1349  			for _, r := range roomNumberRooms {
  1350  				room := r.(*LandlordRoom)
  1351  				if !room.loginIPs[user.baseData.userData.LoginIP] && room.rule.RoomType == roomBaseScoreMatching && room.rule.BaseScore == roomm.rule.BaseScore && !room.full() && room.ownerUserID != user.baseData.ownerUserID {
  1352  					if !room.playTogether(user) {
  1353  						user.enterRoom(r)
  1354  						return
  1355  					}
  1356  				}
  1357  			}
  1358  			for _, r := range roomNumberRooms {
  1359  				room := r.(*LandlordRoom)
  1360  				if room.rule.RoomType == roomBaseScoreMatching && room.rule.BaseScore == roomm.rule.BaseScore && !room.full() && room.ownerUserID != user.baseData.ownerUserID {
  1361  
  1362  					user.enterRoom(r)
  1363  					return
  1364  
  1365  				}
  1366  			}
  1367  		}
  1368  	case roomRedPacketMatching:
  1369  		// 一元红包场换房
  1370  		if roomm.rule.RedPacketType == 1 {
  1371  			if time.Now().Hour() < conf.GetOneRedpacketInfo().Start || time.Now().Hour() > conf.GetOneRedpacketInfo().End {
  1372  				user.WriteMsg(&msg.S2C_EnterRoom{Error: msg.S2C_EnterRoom_NotRightNow})
  1373  				return
  1374  			}
  1375  		}
  1376  		if roomm.rule.RedPacketType == 10 {
  1377  			if time.Now().Hour() < conf.GetTenRedpacketInfo().Start || time.Now().Hour() > conf.GetTenRedpacketInfo().End {
  1378  				user.WriteMsg(&msg.S2C_EnterRoom{Error: msg.S2C_EnterRoom_NotRightNow})
  1379  				return
  1380  			}
  1381  		}
  1382  		if !conf.Server.Model {
  1383  			for _, r := range roomNumberRooms {
  1384  				room := r.(*LandlordRoom)
  1385  				if !room.loginIPs[user.baseData.userData.LoginIP] && room.rule.RoomType == roomRedPacketMatching && room.rule.RedPacketType == roomm.rule.RedPacketType && !room.full() && room.ownerUserID != user.baseData.ownerUserID {
  1386  					if !room.playTogether(user) {
  1387  						user.enterRoom(r)
  1388  						return
  1389  					}
  1390  				}
  1391  			}
  1392  		}
  1393  		for _, r := range roomNumberRooms {
  1394  			room := r.(*LandlordRoom)
  1395  			if room.rule.RoomType == roomRedPacketMatching && room.rule.RedPacketType == roomm.rule.RedPacketType && !room.full() && room.ownerUserID != user.baseData.ownerUserID {
  1396  
  1397  				user.enterRoom(r)
  1398  				return
  1399  
  1400  			}
  1401  		}
  1402  	}
  1403  	user.createLandlordRoom(roomm.rule)
  1404  }
  1405  
  1406  func (room *LandlordRoom) updateTaskTicket(userID int) {
  1407  	playerData := room.userIDPlayerDatas[userID]
  1408  	for _, task := range playerData.user.baseData.taskIDTaskDatas {
  1409  		if TaskList[task.TaskID].Type == taskRedPacket && task.Progress < TaskList[task.TaskID].Total {
  1410  			upsertTaskTicket(bson.M{"userid": userID, "finish": false, "taskid": task.TaskID},
  1411  				bson.M{"$inc": bson.M{"ticket_" + strconv.Itoa(int(room.rule.Tickets)): 1}})
  1412  		}
  1413  	}
  1414  }