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

     1  package internal
     2  
     3  import (
     4  	"common"
     5  	"conf"
     6  	"game/poker"
     7  	"math/rand"
     8  	"msg"
     9  	"sort"
    10  	"time"
    11  
    12  	"github.com/name5566/leaf/log"
    13  )
    14  
    15  // 叫地主
    16  func (room *LandlordRoom) bid(userID int) {
    17  	playerData := room.userIDPlayerDatas[userID]
    18  	playerData.state = landlordActionBid
    19  
    20  	broadcast(&msg.S2C_ActionLandlordBid{
    21  		Position:  playerData.position,
    22  		Countdown: conf.GetCfgTimeout().LandlordBid,
    23  	}, room.positionUserIDs, -1)
    24  
    25  	playerData.actionTimestamp = time.Now().Unix()
    26  	log.Debug("等待 userID %v 叫地主", userID)
    27  	room.bidTimer = skeleton.AfterFunc((time.Duration(conf.GetCfgTimeout().LandlordBid+2))*time.Second, func() {
    28  		log.Debug("userID %v 自动不叫", userID)
    29  		room.doBid(userID, false)
    30  	})
    31  }
    32  
    33  // 抢地主
    34  func (room *LandlordRoom) grab(userID int) {
    35  	playerData := room.userIDPlayerDatas[userID]
    36  	playerData.state = landlordActionGrab
    37  
    38  	broadcast(&msg.S2C_ActionLandlordGrab{
    39  		Position:  playerData.position,
    40  		Countdown: conf.GetCfgTimeout().LandlordGrab,
    41  	}, room.positionUserIDs, -1)
    42  
    43  	playerData.actionTimestamp = time.Now().Unix()
    44  	log.Debug("等待 userID %v 抢地主", userID)
    45  	room.grabTimer = skeleton.AfterFunc(time.Duration(conf.GetCfgTimeout().LandlordGrab+2)*time.Second, func() {
    46  		log.Debug("userID %v 自动不抢", userID)
    47  		room.doGrab(userID, false)
    48  	})
    49  }
    50  
    51  // 确定地主
    52  func (room *LandlordRoom) decideLandlord(userID int) {
    53  	broadcast(&msg.S2C_ClearAction{}, room.positionUserIDs, -1)
    54  
    55  	room.doTask(userID, 7)  // 累计当地主10次
    56  	room.doTask(userID, 54) // 当地主15次
    57  
    58  	room.landlordUserID = userID
    59  	playerData := room.userIDPlayerDatas[room.landlordUserID]
    60  	//新人任务 累计当地主5次 1002
    61  	if room.rule.RoomType == roomBaseScoreMatching {
    62  		playerData.user.updateRedPacketTask(1002)
    63  		//初级任务 当地主10次 (1010)
    64  		playerData.user.updateRedPacketTask(1010)
    65  	}
    66  	if room.rule.BaseScore == 3000 {
    67  		//中级任务 普通场当地主8次 2006
    68  		playerData.user.updateRedPacketTask(2006)
    69  		room.doTask(userID, 44) // 普通场当地主8次
    70  	}
    71  	//高级任务 当地主20次 3001
    72  	playerData.user.updateRedPacketTask(3001)
    73  	for i := 1; i < room.rule.MaxPlayers; i++ {
    74  		peasantUserID := room.positionUserIDs[(playerData.position+i)%room.rule.MaxPlayers]
    75  		room.peasantUserIDs = append(room.peasantUserIDs, peasantUserID)
    76  	}
    77  	room.calculateLandlordMultiple()
    78  
    79  	broadcast(&msg.S2C_DecideLandlord{
    80  		Position: playerData.position,
    81  	}, room.positionUserIDs, -1)
    82  	// 最后三张
    83  	room.lastThree = room.rests[:3]
    84  	room.rests = []int{}
    85  	sort.Sort(sort.Reverse(sort.IntSlice(room.lastThree)))
    86  	log.Debug("三张: %v", poker.ToCardsString(room.lastThree))
    87  
    88  	broadcast(&msg.S2C_UpdateLandlordLastThree{
    89  		Cards: room.lastThree,
    90  	}, room.positionUserIDs, -1)
    91  
    92  	playerData.hands = append(playerData.hands, room.lastThree...)
    93  	playerData.taskID51 = 0 // 单局打出2个顺子3次计数初始化
    94  	playerData.taskID2001 = 0
    95  	sort.Sort(sort.Reverse(sort.IntSlice(playerData.hands)))
    96  
    97  	if user, ok := userIDUsers[userID]; ok {
    98  		user.WriteMsg(&msg.S2C_UpdatePokerHands{
    99  			Position:      playerData.position,
   100  			Hands:         playerData.hands,
   101  			NumberOfHands: len(playerData.hands),
   102  			ShowCards:     playerData.showCards,
   103  		})
   104  		user.WriteMsg(&msg.S2C_UpdateLandlordMultiple{
   105  			Multiple: playerData.multiple,
   106  		})
   107  	}
   108  	if playerData.showCards {
   109  		broadcast(&msg.S2C_UpdatePokerHands{
   110  			Position:      playerData.position,
   111  			Hands:         playerData.hands,
   112  			NumberOfHands: len(playerData.hands),
   113  			ShowCards:     true,
   114  		}, room.positionUserIDs, playerData.position)
   115  	} else {
   116  		broadcast(&msg.S2C_UpdatePokerHands{
   117  			Position:      playerData.position,
   118  			Hands:         []int{},
   119  			NumberOfHands: len(playerData.hands),
   120  		}, room.positionUserIDs, playerData.position)
   121  	}
   122  	switch room.rule.RoomType {
   123  	case roomPractice:
   124  		skeleton.AfterFunc(1*time.Second, func() {
   125  			room.showCards()
   126  		})
   127  	case roomBaseScoreMatching, roomBaseScorePrivate:
   128  		skeleton.AfterFunc(1*time.Second, func() {
   129  			room.double()
   130  		})
   131  	case roomVIPPrivate, roomRedPacketMatching, roomRedPacketPrivate:
   132  		broadcast(&msg.S2C_ClearAction{}, room.positionUserIDs, -1)
   133  		room.discard(room.landlordUserID, poker.ActionLandlordDiscardMust)
   134  	}
   135  }
   136  
   137  // 加倍
   138  func (room *LandlordRoom) double() {
   139  	actionTimestamp := time.Now().Unix()
   140  	for _, userID := range room.positionUserIDs {
   141  		playerData := room.userIDPlayerDatas[userID]
   142  		playerData.state = landlordActionDouble
   143  		playerData.actionTimestamp = actionTimestamp
   144  
   145  		if user, ok := userIDUsers[userID]; ok {
   146  			user.WriteMsg(&msg.S2C_ActionLandlordDouble{
   147  				Countdown: conf.GetCfgTimeout().LandlordDouble,
   148  			})
   149  		}
   150  	}
   151  	log.Debug("等待所有人加倍")
   152  	room.doubleTimer = skeleton.AfterFunc(time.Duration(conf.GetCfgTimeout().LandlordDouble+2)*time.Second, func() {
   153  		for _, userID := range room.positionUserIDs {
   154  			playerData := room.userIDPlayerDatas[userID]
   155  			if playerData.state == landlordActionDouble {
   156  				log.Debug("userID %v 自动不加倍", userID)
   157  				room.doDouble(userID, false)
   158  			}
   159  		}
   160  	})
   161  }
   162  
   163  // 明牌
   164  func (room *LandlordRoom) showCards() {
   165  	broadcast(&msg.S2C_ClearAction{}, room.positionUserIDs, -1)
   166  
   167  	actionTimestamp := time.Now().Unix()
   168  	switch room.rule.RoomType {
   169  	case roomBaseScoreMatching:
   170  		landlordPlayData := room.userIDPlayerDatas[room.landlordUserID]
   171  		if landlordPlayData.showCards {
   172  			room.discard(room.landlordUserID, poker.ActionLandlordDiscardMust)
   173  			return
   174  		}
   175  		landlordPlayData.state = landlordActionShowCards
   176  		landlordPlayData.actionTimestamp = actionTimestamp
   177  		landlordPlayData.user.WriteMsg(&msg.S2C_ActionLandlordShowCards{
   178  			Countdown: conf.GetCfgTimeout().LandlordShowCards,
   179  		})
   180  
   181  		log.Debug("等待地主明牌")
   182  		room.showCardsTimer = skeleton.AfterFunc(time.Duration(conf.GetCfgTimeout().LandlordShowCards+2)*time.Second, func() {
   183  			if landlordPlayData.state == landlordActionShowCards {
   184  				log.Debug("地主: %v 自动不明牌", room.landlordUserID)
   185  				room.doShowCards(room.landlordUserID, false)
   186  			}
   187  		})
   188  	default:
   189  		if len(room.getShowCardsUserIDs()) == room.rule.MaxPlayers {
   190  			room.discard(room.landlordUserID, poker.ActionLandlordDiscardMust)
   191  			return
   192  		}
   193  		for _, userID := range room.positionUserIDs {
   194  			playerData := room.userIDPlayerDatas[userID]
   195  			if !playerData.showCards {
   196  				playerData.state = landlordActionShowCards
   197  				playerData.actionTimestamp = actionTimestamp
   198  				playerData.user.WriteMsg(&msg.S2C_ActionLandlordShowCards{
   199  					Countdown: conf.GetCfgTimeout().LandlordShowCards,
   200  				})
   201  			}
   202  		}
   203  		log.Debug("等待其他人明牌")
   204  		room.showCardsTimer = skeleton.AfterFunc(time.Duration(conf.GetCfgTimeout().LandlordShowCards+2)*time.Second, func() {
   205  			for _, userID := range room.positionUserIDs {
   206  				playerData := room.userIDPlayerDatas[userID]
   207  				if playerData.state == landlordActionShowCards {
   208  					log.Debug("userID %v 自动不明牌", userID)
   209  					room.doShowCards(userID, false)
   210  				}
   211  			}
   212  		})
   213  	}
   214  }
   215  
   216  // 出牌
   217  func (room *LandlordRoom) discard(userID int, actionDiscardType int) {
   218  	playerData := room.userIDPlayerDatas[userID]
   219  	playerData.state = landlordActionDiscard
   220  	playerData.actionDiscardType = actionDiscardType
   221  
   222  	broadcast(&msg.S2C_ActionLandlordDiscard{
   223  		ActionDiscardType: poker.ActionLandlordDiscardAlternative,
   224  		Position:          playerData.position,
   225  		Countdown:         conf.GetCfgTimeout().LandlordDiscard,
   226  	}, room.positionUserIDs, playerData.position)
   227  
   228  	prevDiscards := []int{}
   229  	countdown := conf.GetCfgTimeout().LandlordDiscard
   230  	hint := make([][]int, 0)
   231  	switch playerData.actionDiscardType {
   232  	case poker.ActionLandlordDiscardNothing:
   233  		if playerData.hosted {
   234  			goto HOST
   235  		}
   236  		countdown = conf.GetCfgTimeout().LandlordDiscardNothing
   237  	case poker.ActionLandlordDiscardAlternative:
   238  		discarderPlayerData := room.userIDPlayerDatas[room.discarderUserID]
   239  		prevDiscards = discarderPlayerData.discards[len(discarderPlayerData.discards)-1]
   240  		if poker.CompareLandlordDiscard(playerData.hands, prevDiscards) {
   241  			goto DISCARD_HANDS
   242  		}
   243  		if playerData.hosted {
   244  			goto HOST
   245  		}
   246  		hint = poker.GetDiscardHint(prevDiscards, playerData.hands)
   247  		log.Debug("提示出牌: %v", poker.ToMeldsString(hint))
   248  	case poker.ActionLandlordDiscardMust:
   249  		if len(playerData.hands) > 1 && poker.GetLandlordCardsType(playerData.hands[:2]) == poker.KingBomb && poker.GetLandlordCardsType(playerData.hands[2:]) > poker.Error {
   250  			goto DISCARD_KINGBOMB
   251  		}
   252  		handsType := poker.GetLandlordCardsType(playerData.hands)
   253  		if handsType != poker.Error && handsType != poker.FourDualsolo && handsType != poker.FourDualpair {
   254  			goto DISCARD_HANDS
   255  		}
   256  		if playerData.hosted {
   257  			goto HOST
   258  		}
   259  	}
   260  	if user, ok := userIDUsers[userID]; ok {
   261  		user.WriteMsg(&msg.S2C_ActionLandlordDiscard{
   262  			ActionDiscardType: playerData.actionDiscardType,
   263  			Position:          playerData.position,
   264  			Countdown:         countdown,
   265  			PrevDiscards:      prevDiscards,
   266  			Hint:              hint,
   267  		})
   268  	}
   269  	playerData.actionTimestamp = time.Now().Unix()
   270  	log.Debug("等待 userID %v 出牌 动作: %v", userID, playerData.actionDiscardType)
   271  	room.discardTimer = skeleton.AfterFunc(time.Duration(countdown+2)*time.Second, func() {
   272  		switch playerData.actionDiscardType {
   273  		case poker.ActionLandlordDiscardNothing:
   274  			log.Debug("userID %v 自动不出", userID)
   275  			room.doDiscard(userID, []int{})
   276  		default:
   277  			room.doSystemHost(userID, true)
   278  		}
   279  	})
   280  	return
   281  HOST: // 托管出牌
   282  	skeleton.AfterFunc(1500*time.Millisecond, func() {
   283  		room.doHostDiscard(userID)
   284  	})
   285  	return
   286  DISCARD_HANDS: // 自动出手牌
   287  	skeleton.AfterFunc(1500*time.Millisecond, func() {
   288  		log.Debug("userID %v 自动出: %v", userID, poker.ToCardsString(playerData.hands))
   289  		room.doDiscard(userID, playerData.hands)
   290  	})
   291  	return
   292  DISCARD_KINGBOMB: // 自动出王炸
   293  	skeleton.AfterFunc(1500*time.Millisecond, func() {
   294  		log.Debug("userID %v 自动出王炸", userID)
   295  		room.doDiscard(userID, []int{53, 52})
   296  	})
   297  }
   298  
   299  func (room *LandlordRoom) doPrepare(userID int, showCards bool) {
   300  	playerData := room.userIDPlayerDatas[userID]
   301  	if room.rule.RoomType == roomVIPPrivate && playerData.vipChips == 0 {
   302  		if user, ok := userIDUsers[userID]; ok {
   303  			user.WriteMsg(&msg.S2C_SetVIPRoomChips{
   304  				Error:    msg.S2C_SetVIPChips_ChipsUnset,
   305  				Position: playerData.position,
   306  			})
   307  		}
   308  		return
   309  	}
   310  
   311  	playerData.state = landlordReady
   312  	playerData.showCards = showCards
   313  
   314  	broadcast(&msg.S2C_Prepare{
   315  		Position: playerData.position,
   316  		Ready:    true,
   317  	}, room.positionUserIDs, -1)
   318  
   319  	if room.allReady() {
   320  		room.state = roomGame
   321  		skeleton.AfterFunc(1*time.Second, func() {
   322  			room.StartGame()
   323  		})
   324  	}
   325  }
   326  
   327  func (room *LandlordRoom) doBid(userID int, bid bool) {
   328  	playerData := room.userIDPlayerDatas[userID]
   329  	if playerData.state != landlordActionBid {
   330  		return
   331  	}
   332  	room.bidTimer.Stop()
   333  	playerData.state = landlordWaiting
   334  
   335  	broadcast(&msg.S2C_LandlordBid{
   336  		Position: playerData.position,
   337  		Bid:      bid,
   338  	}, room.positionUserIDs, -1)
   339  
   340  	dealerPlayerData := room.userIDPlayerDatas[room.dealerUserID]
   341  	nextUserID := room.positionUserIDs[(playerData.position+1)%room.rule.MaxPlayers]
   342  	lastPos := (dealerPlayerData.position + room.rule.MaxPlayers - 1) % room.rule.MaxPlayers
   343  	if bid {
   344  		room.bidUserID = userID
   345  		if room.rule.BaseScore == 3000 {
   346  			//中级任务 普通场叫地主6次 2007
   347  			playerData.user.updateRedPacketTask(2007)
   348  			room.doTask(userID, 45) // 普通场叫地主6次
   349  		}
   350  		if playerData.position == lastPos {
   351  			skeleton.AfterFunc(1*time.Second, func() {
   352  				room.decideLandlord(userID)
   353  			})
   354  		} else {
   355  			room.grab(nextUserID)
   356  		}
   357  	} else {
   358  		if playerData.position == lastPos {
   359  			if room.shuffleTimes == 2 {
   360  				skeleton.AfterFunc(1*time.Second, func() {
   361  					landlordUserID := room.positionUserIDs[rand.Intn(room.rule.MaxPlayers)]
   362  					room.decideLandlord(landlordUserID)
   363  				})
   364  				return
   365  			}
   366  			if len(room.getShowCardsUserIDs()) == 0 {
   367  				skeleton.AfterFunc(1*time.Second, func() {
   368  					room.StartGame()
   369  				})
   370  			} else {
   371  				skeleton.AfterFunc(1*time.Second, func() {
   372  					room.decideLandlord(room.getShowCardsUserIDs()[0])
   373  				})
   374  			}
   375  		} else {
   376  			room.bid(nextUserID)
   377  		}
   378  	}
   379  }
   380  
   381  func (room *LandlordRoom) doGrab(userID int, grab bool) {
   382  	playerData := room.userIDPlayerDatas[userID]
   383  	if playerData.state != landlordActionGrab {
   384  		return
   385  	}
   386  	room.grabTimer.Stop()
   387  	playerData.state = landlordWaiting
   388  
   389  	broadcast(&msg.S2C_LandlordGrab{
   390  		Position: playerData.position,
   391  		Grab:     grab,
   392  		Again:    userID == room.bidUserID,
   393  	}, room.positionUserIDs, -1)
   394  
   395  	dealerPlayerData := room.userIDPlayerDatas[room.dealerUserID]
   396  	nextUserID := room.positionUserIDs[(playerData.position+1)%room.rule.MaxPlayers]
   397  	lastPos := (dealerPlayerData.position + room.rule.MaxPlayers - 1) % room.rule.MaxPlayers
   398  	if grab {
   399  		room.doTask(userID, 8) // 累计抢地主10次
   400  		//初级任务 抢地主10次 1011
   401  		playerData.user.updateRedPacketTask(1011)
   402  		room.calculateMultiple(-1, 2)
   403  		room.grabUserIDs = append(room.grabUserIDs, userID)
   404  		if userID == room.bidUserID {
   405  			skeleton.AfterFunc(1*time.Second, func() {
   406  				room.decideLandlord(userID)
   407  			})
   408  		} else {
   409  			if playerData.position == lastPos {
   410  				room.grab(room.bidUserID)
   411  			} else {
   412  				room.grab(nextUserID)
   413  			}
   414  		}
   415  	} else {
   416  		numberOfGrab := len(room.grabUserIDs)
   417  		if userID == room.bidUserID {
   418  			skeleton.AfterFunc(1*time.Second, func() {
   419  				room.decideLandlord(room.grabUserIDs[numberOfGrab-1])
   420  			})
   421  		} else {
   422  			if playerData.position == lastPos {
   423  				if numberOfGrab == 0 {
   424  					skeleton.AfterFunc(1*time.Second, func() {
   425  						room.decideLandlord(room.bidUserID)
   426  					})
   427  				} else {
   428  					room.grab(room.bidUserID)
   429  				}
   430  			} else {
   431  				room.grab(nextUserID)
   432  			}
   433  		}
   434  	}
   435  }
   436  
   437  func (room *LandlordRoom) doDouble(userID int, double bool) {
   438  	playerData := room.userIDPlayerDatas[userID]
   439  	if playerData.state != landlordActionDouble {
   440  		return
   441  	}
   442  	playerData.state = landlordWaiting
   443  
   444  	broadcast(&msg.S2C_LandlordDouble{
   445  		Position: playerData.position,
   446  		Double:   double,
   447  	}, room.positionUserIDs, -1)
   448  
   449  	if double {
   450  		room.doTask(userID, 10) // 累计加倍底分10次
   451  		//初级任务 菜鸟场加倍底分4次 1016
   452  		if room.rule.BaseScore == 500 {
   453  			playerData.user.updateRedPacketTask(1016)
   454  		}
   455  		if room.rule.BaseScore == 3000 {
   456  			//中级任务 普通场加倍底分4次 2005
   457  			playerData.user.updateRedPacketTask(2005)
   458  			//高级任务 普通场加倍底分8次 3008
   459  			playerData.user.updateRedPacketTask(3008)
   460  			room.doTask(userID, 43) // 普通场加倍8次
   461  		}
   462  
   463  		room.calculateMultiple(userID, 2)
   464  	}
   465  	if room.allWaiting() {
   466  		room.doubleTimer.Stop()
   467  		skeleton.AfterFunc(1*time.Second, func() {
   468  			room.showCards()
   469  		})
   470  	}
   471  }
   472  
   473  func (room *LandlordRoom) doShowCards(userID int, showCards bool) {
   474  	playerData := room.userIDPlayerDatas[userID]
   475  	if playerData.state != landlordActionShowCards {
   476  		return
   477  	}
   478  	playerData.state = landlordWaiting
   479  	playerData.showCards = showCards
   480  
   481  	if playerData.showCards {
   482  		room.calculateMultiple(userID, 2)
   483  
   484  		broadcast(&msg.S2C_LandlordShowCards{
   485  			Position: playerData.position,
   486  		}, room.positionUserIDs, -1)
   487  
   488  		broadcast(&msg.S2C_UpdatePokerHands{
   489  			Position:      playerData.position,
   490  			Hands:         playerData.hands,
   491  			NumberOfHands: len(playerData.hands),
   492  			ShowCards:     true,
   493  		}, room.positionUserIDs, playerData.position)
   494  		//初级任务 累计明牌开始10次 1012
   495  		playerData.user.updateRedPacketTask(1012)
   496  		//高级任务  普通场明牌开始10次 3005
   497  		if room.rule.BaseScore == 3000 {
   498  			playerData.user.updateRedPacketTask(3005)
   499  		}
   500  	}
   501  	if room.allWaiting() {
   502  		room.showCardsTimer.Stop()
   503  		broadcast(&msg.S2C_ClearAction{}, room.positionUserIDs, -1)
   504  		room.discard(room.landlordUserID, poker.ActionLandlordDiscardMust)
   505  	}
   506  }
   507  
   508  // cards 长度为零时表示不出
   509  func (room *LandlordRoom) doDiscard(userID int, cards []int) {
   510  	playerData := room.userIDPlayerDatas[userID]
   511  	if playerData.state != landlordActionDiscard {
   512  		return
   513  	}
   514  	cards = poker.ReSortLandlordCards(cards)
   515  	cardsLen := len(cards)
   516  	cardsType := poker.GetLandlordCardsType(cards)
   517  	contain := common.Contain(playerData.hands, cards)
   518  
   519  	var prevDiscards []int
   520  	if room.discarderUserID > 0 && room.discarderUserID != userID {
   521  		discarderPlayerData := room.userIDPlayerDatas[room.discarderUserID]
   522  		prevDiscards = discarderPlayerData.discards[len(discarderPlayerData.discards)-1]
   523  	}
   524  	if cardsLen == 0 && playerData.actionDiscardType == poker.ActionLandlordDiscardMust ||
   525  		cardsLen > 0 && playerData.actionDiscardType == poker.ActionLandlordDiscardNothing ||
   526  		cardsLen > 0 && !contain || cardsLen > 0 && cardsType == poker.Error ||
   527  		cardsLen > 0 && playerData.actionDiscardType == poker.ActionLandlordDiscardAlternative && !poker.CompareLandlordDiscard(cards, prevDiscards) {
   528  		if user, ok := userIDUsers[userID]; ok {
   529  			after := int(time.Now().Unix() - playerData.actionTimestamp)
   530  			countdown := conf.GetCfgTimeout().LandlordDiscard - after
   531  			if countdown > 1 {
   532  				user.WriteMsg(&msg.S2C_ActionLandlordDiscard{
   533  					ActionDiscardType: playerData.actionDiscardType,
   534  					Position:          playerData.position,
   535  					Countdown:         countdown - 1,
   536  					PrevDiscards:      prevDiscards,
   537  				})
   538  			}
   539  		}
   540  		return
   541  	}
   542  	if room.discardTimer != nil {
   543  		room.discardTimer.Stop()
   544  		room.discardTimer = nil
   545  	}
   546  	playerData.state = landlordWaiting
   547  
   548  	broadcast(&msg.S2C_LandlordDiscard{
   549  		Position: playerData.position,
   550  		Cards:    cards,
   551  	}, room.positionUserIDs, -1)
   552  
   553  	nextUserID := room.positionUserIDs[(playerData.position+1)%room.rule.MaxPlayers]
   554  	if cardsLen == 0 {
   555  		log.Debug("userID %v 不出", userID)
   556  		if room.discarderUserID == nextUserID {
   557  			room.discard(nextUserID, poker.ActionLandlordDiscardMust)
   558  		} else {
   559  			nextUserPlayerData := room.userIDPlayerDatas[nextUserID]
   560  			if poker.CompareLandlordHands(prevDiscards, nextUserPlayerData.hands) {
   561  				room.discard(nextUserID, poker.ActionLandlordDiscardNothing)
   562  			} else {
   563  				room.discard(nextUserID, poker.ActionLandlordDiscardAlternative)
   564  			}
   565  		}
   566  		return
   567  	}
   568  	switch cardsType {
   569  	case poker.AirplaneChain, poker.TrioSoloAirplane, poker.TrioPairChain:
   570  		room.doTask(userID, 1)  // 累计打出3个飞机
   571  		room.doTask(userID, 23) // 累计打出3个飞机,奖励3000金币
   572  		//初级任务 累计打出3个飞机 1006
   573  		playerData.user.updateRedPacketTask(1006)
   574  		if room.rule.BaseScore == 3000 {
   575  			room.doTask(userID, 38) // 普通场打出1个飞机
   576  			room.doTask(userID, 55) // 普通场打出2个飞机
   577  		}
   578  	case poker.PairSisters:
   579  		room.doTask(userID, 2)  // 累计打出5个连对
   580  		room.doTask(userID, 33) // 累计打出6个连对
   581  		//初级任务 累计打出5个连对 1007
   582  		playerData.user.updateRedPacketTask(1007)
   583  		//中级任务 累计打出10个连对 2002
   584  		playerData.user.updateRedPacketTask(2002)
   585  		if room.rule.BaseScore == 3000 {
   586  			room.doTask(userID, 39) // 普通场打出2个连对
   587  			room.doTask(userID, 56) // 普通场打出4个连对
   588  		}
   589  	case poker.KingBomb:
   590  		room.doTask(userID, 3) // 累计打出3个炸弹
   591  		//初级任务 累计打出3个炸弹 1008
   592  		playerData.user.updateRedPacketTask(1008)
   593  		//初级任务 累计打出3个王炸 1009
   594  		playerData.user.updateRedPacketTask(1009)
   595  		//中级任务 累计打出5个王炸
   596  		playerData.user.updateRedPacketTask(2000)
   597  		room.doTask(userID, 4)  // 累计打出3个王炸
   598  		room.doTask(userID, 24) // 累计打出2个王炸,奖励3000金币
   599  		room.doTask(userID, 25) // 累计打出3个炸弹,奖励3000金币
   600  
   601  		room.doTask(userID, 30) // 打出4个炸弹
   602  		room.doTask(userID, 31) // 打出4个王炸
   603  		room.doTask(userID, 34) // 打出5个炸弹
   604  		room.doTask(userID, 46) // 打出6个炸弹
   605  		room.doTask(userID, 47) // 打出5个王炸
   606  		//中级任务 单局打出2个炸弹
   607  		//playerData.user.updateRedPacketTask(2001)
   608  		playerData.taskID2001++
   609  		room.doTask(userID, 32) // 单局打出2个炸弹
   610  		if room.rule.BaseScore == 3000 {
   611  			room.doTask(userID, 40) // 普通场打出2个炸弹
   612  			room.doTask(userID, 41) // 普通场打出2个王炸
   613  			room.doTask(userID, 57) // 普通场打出3个炸弹
   614  		}
   615  
   616  		room.calculateMultiple(-1, 2)
   617  	case poker.Bomb:
   618  		//初级任务 累计打出3个炸弹 1008
   619  		playerData.user.updateRedPacketTask(1008)
   620  		//中级任务 单局打出2个炸弹
   621  		//playerData.user.updateRedPacketTask(2001)
   622  		playerData.taskID2001++
   623  		room.doTask(userID, 3)  // 累计打出3个炸弹
   624  		room.doTask(userID, 25) // 累计打出3个炸弹,奖励3000金币
   625  
   626  		room.doTask(userID, 30) // 打出4个炸弹
   627  		room.doTask(userID, 32) // 单局打出2个炸弹
   628  		room.doTask(userID, 34) // 打出5个炸弹
   629  		room.doTask(userID, 46) // 打出6个炸弹
   630  
   631  		if room.rule.BaseScore == 3000 {
   632  			room.doTask(userID, 40) // 普通场打出2个炸弹
   633  			room.doTask(userID, 57) // 普通场打出3个炸弹
   634  		}
   635  		room.calculateMultiple(-1, 2)
   636  	case poker.SoloChain:
   637  		room.doTask(userID, 36) // 打出10个顺子
   638  		playerData.taskID51++   // 单局打出2个顺子3次
   639  
   640  		if room.rule.BaseScore == 3000 {
   641  			//中级任务 普通场打出6个顺子 2004
   642  			playerData.user.updateRedPacketTask(2004)
   643  			room.doTask(userID, 42) // 普通场打出6个顺子
   644  			room.doTask(userID, 58) // 普通场打出10个顺子
   645  		}
   646  	case poker.TrioPair:
   647  		room.doTask(userID, 52) // 打出8次三带二
   648  		//中级任务 打出8次三带二 2009
   649  		playerData.user.updateRedPacketTask(2009)
   650  
   651  		if room.rule.BaseScore == 3000 {
   652  			room.doTask(userID, 63) // 普通场打出8次三带二
   653  			//高级任务 普通场打出8次三带二 3002
   654  			playerData.user.updateRedPacketTask(3002)
   655  		}
   656  	case poker.FourDualsolo:
   657  		//中级任务 打出三次四带二 2010
   658  		playerData.user.updateRedPacketTask(2010)
   659  		room.doTask(userID, 53) // 打出3次四带二
   660  	}
   661  	room.discarderUserID = userID
   662  	room.discards = append(room.discards, cards...)
   663  	playerData.discards = append(playerData.discards, cards)
   664  	playerData.hands = common.Remove(playerData.hands, cards)
   665  	log.Debug("userID %v, 出牌: %v, 剩余: %v", userID, poker.ToCardsString(cards), poker.ToCardsString(playerData.hands))
   666  	if playerData.showCards {
   667  		broadcast(&msg.S2C_UpdatePokerHands{
   668  			Position:      playerData.position,
   669  			Hands:         playerData.hands,
   670  			NumberOfHands: len(playerData.hands),
   671  			ShowCards:     true,
   672  		}, room.positionUserIDs, -1)
   673  	} else {
   674  		if user, ok := userIDUsers[userID]; ok {
   675  			user.WriteMsg(&msg.S2C_UpdatePokerHands{
   676  				Position:      playerData.position,
   677  				Hands:         playerData.hands,
   678  				NumberOfHands: len(playerData.hands),
   679  			})
   680  		}
   681  		broadcast(&msg.S2C_UpdatePokerHands{
   682  			Position:      playerData.position,
   683  			Hands:         []int{},
   684  			NumberOfHands: len(playerData.hands),
   685  		}, room.positionUserIDs, playerData.position)
   686  	}
   687  	if len(playerData.hands) == 0 {
   688  		room.winnerUserIDs = append(room.winnerUserIDs, userID)
   689  		skeleton.AfterFunc(1*time.Second, func() {
   690  			room.EndGame()
   691  		})
   692  		return
   693  	}
   694  	if room.discarderUserID == nextUserID {
   695  		room.discard(nextUserID, poker.ActionLandlordDiscardMust)
   696  	} else {
   697  		nextUserPlayerData := room.userIDPlayerDatas[nextUserID]
   698  		if poker.CompareLandlordHands(cards, nextUserPlayerData.hands) {
   699  			room.discard(nextUserID, poker.ActionLandlordDiscardNothing)
   700  		} else {
   701  			room.discard(nextUserID, poker.ActionLandlordDiscardAlternative)
   702  		}
   703  	}
   704  }
   705  
   706  // 托管出牌
   707  func (room *LandlordRoom) doHostDiscard(userID int) {
   708  	playerData := room.userIDPlayerDatas[userID]
   709  	if playerData.state != landlordActionDiscard {
   710  		return
   711  	}
   712  	switch playerData.actionDiscardType {
   713  	case poker.ActionLandlordDiscardNothing:
   714  		room.doDiscard(userID, []int{})
   715  		return
   716  	case poker.ActionLandlordDiscardAlternative:
   717  		discarderPlayerData := room.userIDPlayerDatas[room.discarderUserID]
   718  		prevDiscards := discarderPlayerData.discards[len(discarderPlayerData.discards)-1]
   719  		hint := poker.GetDiscardHint(prevDiscards, playerData.hands)
   720  		if len(hint) == 0 {
   721  			room.doDiscard(userID, []int{})
   722  			return
   723  		}
   724  		hintType := poker.GetLandlordCardsType(hint[0])
   725  		if len(playerData.discards) == 0 { // 还没有出过牌
   726  			switch hintType {
   727  			case poker.KingBomb, poker.Bomb:
   728  				room.doDiscard(userID, []int{})
   729  			}
   730  		}
   731  		if playerData.user.isRobot() {
   732  			if common.InArray(room.peasantUserIDs, playerData.user.baseData.userData.UserID) &&
   733  				common.InArray(room.peasantUserIDs, discarderPlayerData.user.baseData.userData.UserID) {
   734  				switch hintType {
   735  				case poker.Solo:
   736  					if poker.CardValue(hint[0][0]) < 12 {
   737  						room.doDiscard(userID, hint[0])
   738  						return
   739  					}
   740  				case poker.Pair:
   741  					if poker.CardValue(hint[0][0]) < 11 {
   742  						room.doDiscard(userID, hint[0])
   743  						return
   744  					}
   745  				}
   746  				room.doDiscard(userID, []int{})
   747  				return
   748  			}
   749  		}
   750  		log.Debug("userID %v 托管出牌: %v", userID, poker.ToCardsString(hint[0]))
   751  		room.doDiscard(userID, hint[0])
   752  		return
   753  	case poker.ActionLandlordDiscardMust:
   754  		analyzer := new(poker.LandlordAnalyzer)
   755  		minCards := analyzer.GetMinDiscards(playerData.hands)
   756  		log.Debug("userID %v 托管出牌: %v", userID, poker.ToCardsString(minCards))
   757  		room.doDiscard(userID, minCards)
   758  		return
   759  	}
   760  }
   761  
   762  func (room *LandlordRoom) doSystemHost(userID int, host bool) {
   763  	playerData := room.userIDPlayerDatas[userID]
   764  	if playerData.hosted == host {
   765  		return
   766  	}
   767  	playerData.hosted = host
   768  	// 托管不让别人知道
   769  	playerData.user.WriteMsg(&msg.S2C_SystemHost{
   770  		Position: playerData.position,
   771  		Host:     host,
   772  	})
   773  	//broadcast(&msg.S2C_SystemHost{
   774  	//	Position: playerData.position,
   775  	//	Host:     host,
   776  	//}, room.positionUserIDs, -1)
   777  
   778  	if host {
   779  		room.doHostDiscard(userID)
   780  	}
   781  }
   782  
   783  // 重置任务
   784  func (room *LandlordRoom) resetTask(userID int, taskID int) {
   785  	if room.rule.RoomType == roomBaseScoreMatching {
   786  		if user, ok := userIDUsers[userID]; ok {
   787  			if task, ok := user.baseData.taskIDTaskDatas[taskID]; ok {
   788  				if task.Progress < TaskList[taskID].Total {
   789  					task.Progress = 0
   790  					user.WriteMsg(&msg.S2C_UpdateTaskProgress{
   791  						TaskID:   task.TaskID,
   792  						Progress: task.Progress,
   793  					})
   794  				}
   795  			}
   796  		} else {
   797  			resetTaskProgress(userID, taskID)
   798  		}
   799  	}
   800  }
   801  
   802  // 做任务
   803  func (room *LandlordRoom) doTask(userID int, taskID int) {
   804  	if room.rule.RoomType == roomBaseScoreMatching || taskID == 28 || taskID == 29 {
   805  		if user, ok := userIDUsers[userID]; ok {
   806  			user.doTask(taskID)
   807  		} else {
   808  			addTaskProgress(userID, taskID)
   809  		}
   810  	}
   811  }