package gamelogic import ( "encoding/json" "fmt" "math/rand" "os" "path/filepath" "sort" "time" "bet24.com/servers/games/baloot/config" robotmanager "bet24.com/servers/insecureframe/robot" badge "bet24.com/servers/micros/badge/proto" cardlibrary "bet24.com/servers/micros/cardlibrary/proto" ladder "bet24.com/servers/micros/ladderservice/proto" task "bet24.com/servers/micros/task/proto" userservices "bet24.com/servers/micros/userservices/proto" waterpool "bet24.com/servers/micros/waterpool/proto" "bet24.com/servers/user" "github.com/google/uuid" excelize "github.com/xuri/excelize/v2" ) func (ts *tablesink) checkAndStartGame() { if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End && ts.gameScene.Phase != Phase_GameEnd { return } // 查看是否所有人都ready readyCount := 0 for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } userStatus := usr.GetUserStatus() // 旁观不判断 if userStatus <= user.UserStatus_Free || userStatus == user.UserStatus_Watch { continue } if usr.GetUserStatus() != user.UserStatus_Ready && userStatus != user.UserStatus_Offline { ts.table.LogWithTableId("-----checkAndStartGame: chair[%d]: %d, status: %d", i, usr.GetUserId(), usr.GetUserStatus()) return } readyCount++ } ts.table.LogWithTableId("-----checkAndStartGame : %d ", readyCount) if readyCount >= CHAIR_COUNT { ts.startGame() } } func (ts *tablesink) checkUserReadyStatus(chair int) { if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End && ts.gameScene.Phase != Phase_GameEnd { ts.table.LogWithTableId("tablesink.checkUserReadyStatus status not right %d", ts.gameScene.Phase) return } usr := ts.table.GetUserByChair(chair) if usr == nil { ts.table.LogWithTableId("tablesink.checkUserReadyStatus user not exist %d", chair) return } if ts.gameScene.Phase == Phase_GameEnd { ts.table.SetUserReadyStatus(usr.GetUserIndex(), true) return } if !usr.IsRobot() { ts.table.LogWithTableId("tablesink.checkUserReadyStatus kicking [%d]: %d ", chair, usr.GetUserId()) ts.table.UserWatch(usr.GetUserIndex()) return } //桌上还有没有真人,有就准备,没有就踢走 count := 0 for i := 0; i < CHAIR_COUNT; i++ { u := ts.table.GetUserByChair(i) if u == nil { continue } if u.IsRobot() { continue } if u.GetUserStatus() <= user.UserStatus_Free { continue } count++ } if count >= 1 || (ts.roomInfo.Test && count >= 0) { ts.table.SetUserReadyStatus(usr.GetUserIndex(), true) } else { ts.table.LogWithTableId("tablesink.checkUserReadyStatus: %d ", usr.GetUserId()) if usr.IsRobot() { //ts.table.UserWatch(usr.GetUserIndex()) ts.table.KickUser(usr.GetUserIndex(), false) } else { ts.table.UserWatch(usr.GetUserIndex()) } } } func (ts *tablesink) onPlayerFirstOutCard() { ts.gameScene.WhoseTurn = getPreviousChair(ts.gameScene.Banker) ts.gameScene.RoundIndex++ ts.gameScene.LastTurn = -1 ts.gameScene.Phase = Phase_Play ts.gameScene.PhaseIndex = 0 ts.allPassCount = 0 // 从初始化模拟场景数据 ts.simulator = newSimulatorScene() ts.simulator.initData(ts.gameScene.TrumpType, ts.gameScene.FinalClub, ts.gameScene.BuyChair, ts.gameScene.BaseTimes, ts.gameScene.IsBO1, ts.gameScene.IsClose) delaySec := 0 for i := 0; i < CHAIR_COUNT; i++ { ts.simulator.fakers[i].handCards = append(ts.simulator.fakers[i].handCards, ts.gameScene.Players[i].HandCards...) ts.simulator.fakers[i].actionProb = ts.gameScene.Players[i].robotActionProb cardList := make([]int, len(ts.gameScene.Players[i].HandCards)) copy(cardList, ts.gameScene.Players[i].HandCards) ts.gameScene.Players[i].ProjectTips = ts.logic.getBestProject(cardList) if len(ts.gameScene.Players[i].ProjectTips) > 0 { if ts.gameScene.Players[i].ProjectTips[0] >= PROJECT_FIFTY && isMostOverKInHandCard(ts.gameScene.Players[i].HandCards, ts.gameScene.TrumpType) { ts.sendRobotChatAction(RobotChatAction_GoodHandCard, ts.gameScene.Players[i].userID, -1, delaySec) delaySec += 2500 } } if isAllBelow10InHandCard(ts.gameScene.Players[i].HandCards) { ts.sendRobotChatAction(RobotChatAction_BadHandCard, ts.gameScene.Players[i].userID, -1, delaySec) delaySec += 2500 } } ts.table.NotifySceneChanged(-1) ts.resetGameTimer() } func (ts *tablesink) isMeetTheStorageConditions() bool { if !ts.gameScene.isBuyChairWin() { return false } buyWinCardScore := 0 playerWinCardScore := 0 for i := 0; i < CHAIR_COUNT; i++ { if isSameTeam(i, ts.gameScene.BuyChair) { buyWinCardScore += ts.gameScene.Players[i].winCardScore } else { playerWinCardScore += ts.gameScene.Players[i].winCardScore } } differ := buyWinCardScore - playerWinCardScore if differ < 30 { return false } percent := 0 winScore := ts.gameScene.Players[ts.gameScene.BuyChair].winCardScore friendWinScore := ts.gameScene.Players[getFriendChair(ts.gameScene.BuyChair)].winCardScore percent = (winScore * 100) / (winScore + friendWinScore) return percent >= 60 } func (ts *tablesink) isSatisfyTheCondition() bool { if ts.gameScene.FinalClub != Suit_Sun { return false } cardList := []int{} for i := 0; i < BUY_PHRSE_HOLD_CARD; i++ { cardList = append(cardList, ts.gameScene.Players[ts.gameScene.BuyChair].bakHandCards[i]) } cardList = append(cardList, ts.gameScene.PublicCard) sortedCards := sortCards(cardList) // if haveFourA(cardList) { // return false // } maxCardNum := 0 if ts.gameScene.FinalClub == Suit_Sun { for i := CardType_Diamond; i <= CardType_Spade; i++ { maxCardNum += getScoreCardNum(sortedCards[i], i, false) } if maxCardNum >= 4 { return true } } if ts.gameScene.FinalClub != Suit_Hokum { return false } if len(sortedCards[ts.gameScene.TrumpType]) < 3 { return false } if len(sortedCards[ts.gameScene.TrumpType]) >= 5 { return true } maxCardNum = 0 trumpBigCardNum := getScoreCardNum(sortedCards[ts.gameScene.TrumpType], ts.gameScene.TrumpType, true) if len(sortedCards[ts.gameScene.TrumpType]) >= 3 && trumpBigCardNum >= 1 { for j := CardType_Diamond; j <= CardType_Spade; j++ { if ts.gameScene.TrumpType == j { continue } maxCardNum += getScoreCardNum(sortedCards[j], j, false) } if maxCardNum >= 1 { return true } } return false } func (ts *tablesink) statisRobotChatCount() { if !ts.roomInfo.Test { return } var f *excelize.File var err error sheetName := "Sheet1" // 先查看名为excelName的表格是否存在 dirName := "./baloot_excel/robotchat" fileName := "baloot" + ts.uniqueId + ".xlsx" _, err1 := os.Stat(fileName) if err1 != nil { // 创建一个新的 Excel f = excelize.NewFile() // 在工作簿中添加一个名为 "Sheet1" 的工作表 f.NewSheet(sheetName) // 设置列宽 f.SetColWidth(sheetName, "A", "A", 5) f.SetColWidth(sheetName, "B", "B", 5) f.SetColWidth(sheetName, "C", "C", 5) f.SetColWidth(sheetName, "D", "D", 200) } else { // 打开一个现有的 Excel 文件 f, err = excelize.OpenFile(fileName) if err != nil { return } } // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) // 获取已有数据的行数 rows, err := f.GetRows(sheetName) if err != nil { return } rowIndex := len(rows) data := [][]string{} for i := 0; i < len(ts.gameScene.statics); i++ { str := []string{ fmt.Sprintf("%d", ts.gameScene.ScoreToWin), fmt.Sprintf("%d", ts.gameScene.statics[i].round), fmt.Sprintf("%d", len(ts.gameScene.statics[i].action)), getRobotChatDesc(ts.gameScene.statics[i].action)} data = append(data, str) } for _, row := range data { for colIndex, col := range row { cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) f.SetCellValue(sheetName, cellName, col) f.SetCellStyle(sheetName, cellName, cellName, style) } rowIndex++ } f.SaveAs(filepath.Join(dirName, fileName)) } func (ts *tablesink) statisticsScoreHistoryToExcel() { if !ts.roomInfo.Test { return } var f *excelize.File var err error sheetName := "Sheet1" // 先查看名为excelName的表格是否存在 dirName := "./baloot_excel/scorehistory" fileName := "baloot" + ts.uniqueId + ".xlsx" _, err1 := os.Stat(fileName) if err1 != nil { // 创建一个新的 Excel f = excelize.NewFile() // 在工作簿中添加一个名为 "Sheet1" 的工作表 f.NewSheet(sheetName) // 设置列宽 f.SetColWidth(sheetName, "A", "A", 5) f.SetColWidth(sheetName, "B", "B", 100) } else { // 打开一个现有的 Excel 文件 f, err = excelize.OpenFile(fileName) if err != nil { return } } // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) // 获取已有数据的行数 rows, err := f.GetRows(sheetName) if err != nil { return } rowIndex := len(rows) data := [][]string{ {fmt.Sprintf("%d", ts.gameScene.GameIndex), getScoreHistoryDesc(ts.gameScene.scoreHistory)}, } for _, row := range data { for colIndex, col := range row { cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) f.SetCellValue(sheetName, cellName, col) f.SetCellStyle(sheetName, cellName, cellName, style) } rowIndex++ } f.SaveAs(filepath.Join(dirName, fileName)) } func (ts *tablesink) genCardLibrary() { var f *excelize.File var err error sheetName := "Sheet1" // 先查看名为excelName的表格是否存在 dirName := "./baloot_excel/cardlibrary" fileName := "baloot" + ts.uniqueId + ".xlsx" _, err1 := os.Stat(fileName) if err1 != nil { // 创建一个新的 Excel f = excelize.NewFile() // 在工作簿中添加一个名为 "Sheet1" 的工作表 f.NewSheet(sheetName) // 设置列宽 f.SetColWidth(sheetName, "A", "AI", 5) } else { // 打开一个现有的 Excel 文件 f, err = excelize.OpenFile(fileName) if err != nil { return } } // 获取已有数据的行数 rows, err := f.GetRows(sheetName) if err != nil { return } rowIndex := len(rows) // 追加数据到现有工作表 data := [][]string{ {fmt.Sprintf("%d", ts.gameScene.FirstActionChair), fmt.Sprintf("%d", ts.gameScene.BuyChair), fmt.Sprintf("%d", ts.logic.cards[0]), fmt.Sprintf("%d", ts.logic.cards[1]), fmt.Sprintf("%d", ts.logic.cards[2]), fmt.Sprintf("%d", ts.logic.cards[3]), fmt.Sprintf("%d", ts.logic.cards[4]), fmt.Sprintf("%d", ts.logic.cards[5]), fmt.Sprintf("%d", ts.logic.cards[6]), fmt.Sprintf("%d", ts.logic.cards[7]), fmt.Sprintf("%d", ts.logic.cards[8]), fmt.Sprintf("%d", ts.logic.cards[9]), fmt.Sprintf("%d", ts.logic.cards[10]), fmt.Sprintf("%d", ts.logic.cards[11]), fmt.Sprintf("%d", ts.logic.cards[12]), fmt.Sprintf("%d", ts.logic.cards[13]), fmt.Sprintf("%d", ts.logic.cards[14]), fmt.Sprintf("%d", ts.logic.cards[15]), fmt.Sprintf("%d", ts.logic.cards[16]), fmt.Sprintf("%d", ts.logic.cards[17]), fmt.Sprintf("%d", ts.logic.cards[18]), fmt.Sprintf("%d", ts.logic.cards[19]), fmt.Sprintf("%d", ts.logic.cards[20]), fmt.Sprintf("%d", ts.logic.cards[21]), fmt.Sprintf("%d", ts.logic.cards[22]), fmt.Sprintf("%d", ts.logic.cards[23]), fmt.Sprintf("%d", ts.logic.cards[24]), fmt.Sprintf("%d", ts.logic.cards[25]), fmt.Sprintf("%d", ts.logic.cards[26]), fmt.Sprintf("%d", ts.logic.cards[27]), fmt.Sprintf("%d", ts.logic.cards[28]), fmt.Sprintf("%d", ts.logic.cards[29]), fmt.Sprintf("%d", ts.logic.cards[30]), fmt.Sprintf("%d", ts.logic.cards[31]), fmt.Sprintf("%d", ts.gameScene.currentRoundDoubling)}, } // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) for _, row := range data { for colIndex, col := range row { cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) f.SetCellValue(sheetName, cellName, col) f.SetCellStyle(sheetName, cellName, cellName, style) } rowIndex++ } // 保存到Excel文件中 f.SaveAs(filepath.Join(dirName, fileName)) } func (ts *tablesink) haveSmallProject() bool { for i := 0; i < CHAIR_COUNT; i++ { if len(ts.gameScene.Players[i].Projects) == 0 { continue } for j := 0; j < len(ts.gameScene.Players[i].Projects); j++ { if ts.gameScene.Players[i].bakProjects[j].Type == PROJECT_FOURHUNDRED { return false } if isBothInclude9J(ts.gameScene.Players[i].bakProjects[j].Cards) { return false } } return true } return false } func (ts *tablesink) statisticsRobotAction() { if !ts.roomInfo.Test { return } var f *excelize.File var err error sheetName := "Sheet1" // 先查看名为excelName的表格是否存在 dirName := "./baloot_excel/robotaction" fileName := "baloot" + ts.uniqueId + ".xlsx" _, err1 := os.Stat(fileName) if err1 != nil { // 创建一个新的 Excel f = excelize.NewFile() // 在工作簿中添加一个名为 "Sheet1" 的工作表 f.NewSheet(sheetName) // 设置列宽 f.SetColWidth(sheetName, "A", "D", 10) } else { // 打开一个现有的 Excel 文件 f, err = excelize.OpenFile(fileName) if err != nil { return } } // 获取已有数据的行数 rows, err := f.GetRows(sheetName) if err != nil { return } rowIndex := len(rows) // 追加数据到现有工作表 data := [][]string{} perStr := []string{} perStr = append(perStr, fmt.Sprintf("%d", ts.gameScene.FinalClub)) for i := 0; i < CHAIR_COUNT; i++ { str := ts.getRobotTypeAction(i) perStr = append(perStr, str) } data = append(data, perStr) // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) for _, row := range data { for colIndex, col := range row { cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) f.SetCellValue(sheetName, cellName, col) f.SetCellStyle(sheetName, cellName, cellName, style) } rowIndex++ } // 保存到Excel文件中 e := f.SaveAs(filepath.Join(dirName, fileName)) if e != nil { ts.importDataToExcel() } } func (ts *tablesink) getRobotTypeAction(chairId int) string { if !ts.gameScene.Players[chairId].isRobot { return fmt.Sprintf("%d", 40) } if ts.gameScene.isBuyChairWin() { if isSameTeam(ts.gameScene.BuyChair, chairId) { return fmt.Sprintf("%d", ts.gameScene.Players[chairId].robotType*10+1) } return fmt.Sprintf("%d", ts.gameScene.Players[chairId].robotType*10) } if isSameTeam(ts.gameScene.BuyChair, chairId) { return fmt.Sprintf("%d", ts.gameScene.Players[chairId].robotType*10) } return fmt.Sprintf("%d", ts.gameScene.Players[chairId].robotType*10+1) } func (ts *tablesink) importDataToExcel() { if !ts.roomInfo.Test || !ts.roomInfo.IsDoublingMode { return } if !ts.isMeetTheStorageConditions() { return } if !ts.isSatisfyTheCondition() { return } if !ts.haveSmallProject() { return } ts.genCardLibrary() var f *excelize.File var err error sheetName := "Sheet1" // 先查看名为excelName的表格是否存在 dirName := "./baloot_excel/cardlibrarydesc" fileName := "baloot" + ts.uniqueId + ".xlsx" _, err1 := os.Stat(fileName) if err1 != nil { // 创建一个新的 Excel f = excelize.NewFile() // 在工作簿中添加一个名为 "Sheet1" 的工作表 f.NewSheet(sheetName) // 设置列宽 f.SetColWidth(sheetName, "A", "A", 72) f.SetColWidth(sheetName, "B", "E", 40) f.SetColWidth(sheetName, "F", "F", 18) f.SetColWidth(sheetName, "G", "G", 15) f.SetColWidth(sheetName, "H", "H", 10) // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) f.SetCellStyle(sheetName, "A1", "H1", style) // 设置工作表的单元格值 f.SetCellValue(sheetName, "A1", "[公共牌], [买牌玩家], [模式], [加倍倍数], [主牌花色], [BO1], [开关], [首出玩家], [买牌阶段]") f.SetCellValue(sheetName, "B1", "0号椅子玩家[全部手牌][剩余三张牌]") f.SetCellValue(sheetName, "C1", "1号椅子玩家[全部手牌][剩余三张牌]") f.SetCellValue(sheetName, "D1", "2号椅子玩家[全部手牌][剩余三张牌]") f.SetCellValue(sheetName, "E1", "3号椅子玩家[全部手牌][剩余三张牌]") f.SetCellValue(sheetName, "F1", "[0号, 1号, 2号, 3号]") f.SetCellValue(sheetName, "G1", "[0号队: 1号队]") f.SetCellValue(sheetName, "H1", "加倍倍数") } else { // 打开一个现有的 Excel 文件 f, err = excelize.OpenFile(fileName) if err != nil { return } } // 获取已有数据的行数 rows, err := f.GetRows(sheetName) if err != nil { return } rowIndex := len(rows) // 追加数据到现有工作表 data := [][]string{ {fmt.Sprintf("[%s], [%d], [%s], [%d], [%d], [%v], [%v], [%d], [%s]", getCardHex(ts.gameScene.PublicCard), ts.gameScene.BuyChair, getSuitDesc(ts.gameScene.FinalClub), ts.gameScene.BaseTimes, ts.gameScene.TrumpType, ts.gameScene.IsBO1, ts.gameScene.IsClose, ts.gameScene.FirstActionChair, getPhaseDesc(ts.gameScene.buyPhase)), getCardsHex(ts.gameScene.Players[0].bakHandCards) + getCardsHex(ts.gameScene.Players[0].LeftSendCards), getCardsHex(ts.gameScene.Players[1].bakHandCards) + getCardsHex(ts.gameScene.Players[1].LeftSendCards), getCardsHex(ts.gameScene.Players[2].bakHandCards) + getCardsHex(ts.gameScene.Players[2].LeftSendCards), getCardsHex(ts.gameScene.Players[3].bakHandCards) + getCardsHex(ts.gameScene.Players[3].LeftSendCards), fmt.Sprintf("[%d, %d, %d, %d]", ts.gameScene.Players[0].winCardScore, ts.gameScene.Players[1].winCardScore, ts.gameScene.Players[2].winCardScore, ts.gameScene.Players[3].winCardScore), fmt.Sprintf("[%d : %d]", ts.gameScene.Players[0].TotalChangeScore, ts.gameScene.Players[1].TotalChangeScore), fmt.Sprintf("%d", ts.gameScene.currentRoundDoubling)}, } bigChair := 0 bigScore := -1 for i := 0; i < CHAIR_COUNT; i++ { if ts.gameScene.Players[i].winCardScore > bigScore { bigChair = i bigScore = ts.gameScene.Players[i].winCardScore } } // 设置对齐方式 // 水平对齐方式 center left right fill(填充) justify(两端对齐) centerContinuous(跨列居中) distributed(分散对齐) // 垂直对齐方式 center top justify distributed style, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, }) style1, _ := f.NewStyle(&excelize.Style{ Alignment: &excelize.Alignment{ Horizontal: "center", Vertical: "center", }, Fill: excelize.Fill{ Type: "pattern", // gradient 渐变色 pattern 填充图案 Pattern: 1, Color: []string{"#FFFF00"}, Shading: 1, // 类型是 gradient 使用 0-5 横向(每种颜色横向分布) 纵向 对角向上 对角向下 有外向内 由内向外 }, }) for _, row := range data { for colIndex, col := range row { cellName, _ := excelize.CoordinatesToCellName(colIndex+1, rowIndex+1) f.SetCellValue(sheetName, cellName, col) if (bigChair + 1) == colIndex { f.SetCellStyle(sheetName, cellName, cellName, style1) } else { f.SetCellStyle(sheetName, cellName, cellName, style) } } rowIndex++ } // 保存到Excel文件中 f.SaveAs(filepath.Join(dirName, fileName)) } func (ts *tablesink) dealGameTimeOut() { if ts.gameScene.Phase == Phase_End { return } switch ts.gameScene.Phase { case Phase_Start: ts.dealGamePlay() return case Phase_GameEnd: isSurrenderFinish := ts.surrenderInfo.isSurrenderEnd() if ts.gameScene.nextGame(isSurrenderFinish) { // 比赛场流程特殊处理 if ts.table.GetOwner() == -1 && config.Server.IsLadderRoom == 0 { ts.gameScene.Phase = Phase_Free ts.table.NotifySceneChanged(-1) ts.table.SetTimer(TIMER_GAME, 3000) } else { ts.gameStart() diff := ts.gameScene.Players[0].TotalScore - ts.gameScene.Players[1].TotalScore win := 0 if diff <= -80 || diff >= 80 { if diff >= 80 { win = 1 } start := rand.Intn(4) for i := 0; i < CHAIR_COUNT; i++ { chairId := (start + i) % CHAIR_COUNT if !isSameTeam(chairId, win) && ts.gameScene.Players[chairId].isRobot { ts.sendRobotChatAction(RobotChatAction_LoseOverEighty, ts.gameScene.Players[chairId].userID, -1, 0) break } } } } } else { if isSurrenderFinish { ts.enterSurrenderEndPhase() } else { ts.enterEndPhase() } } return case Phase_Correct: ts.onTimerCorrectStart() return case Phase_Free: ts.gameStart() return case Phase_ChooseTrump: if !isValidChair(ts.gameScene.WhoseTurn) { ts.table.LogWithTableId("tablesink.dealGameTimeOut invalid whoseturn") return } if !ts.gameScene.Players[ts.gameScene.WhoseTurn].isRobot { ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut = true } cardList := make([]int, len(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards)) copy(cardList, ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards) cardList = append(cardList, ts.gameScene.PublicCard) t := getBestTrumpType(cardList, ts.gameScene.PublicCard) ts.dealChooseTrump(t) return case Phase_Reshuffle: for i := 0; i < CHAIR_COUNT; i++ { if ts.gameScene.Players[i].ReshuffleAction == Reshuffle_None && ts.gameScene.Players[i].CanDoReshuffle { if !ts.gameScene.Players[i].isRobot { ts.gameScene.Players[i].AutoOut = true } ts.dealReshuffle(i, Reshuffle_Pass) } } return case Phase_Double: for i := 0; i < CHAIR_COUNT; i++ { action := ts.gameScene.Players[i].getAutoBuyAction() if action != Action_Buy_Invaild && ts.gameScene.Players[i].canAction() { ts.gameScene.Players[i].AutoOut = true ts.dealDouble(i, action) } } return case Phase_Play: if !isValidChair(ts.gameScene.WhoseTurn) { ts.table.LogWithTableId("tablesink.dealGameTimeOut invalid whoseturn") return } projects := []int{} if len(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards) == 1 { ts.dealOutCard(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards[0], projects) return } if !ts.gameScene.Players[ts.gameScene.WhoseTurn].isRobot { ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut = true } if ts.gameScene.Players[ts.gameScene.WhoseTurn].CanGawah { ts.dealGawah() return } outCard := CARD_COUNT if len(ts.gameScene.Players[ts.gameScene.WhoseTurn].BigCards) > 0 { outCard = ts.gameScene.Players[ts.gameScene.WhoseTurn].BigCards[0] } else { outCard = ts.logic.worstCard(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards, ts.gameScene.RoundType) } ts.dealOutCard(outCard, projects) return case Phase_FirstBuy: fallthrough case Phase_SecondBuy: if ts.gameScene.WhoseTurn != -1 { action := ts.gameScene.Players[ts.gameScene.WhoseTurn].getAutoBuyAction() if action != Action_Buy_Invaild && ts.gameScene.Players[ts.gameScene.WhoseTurn].canAction() { if !ts.gameScene.Players[ts.gameScene.WhoseTurn].isRobot { ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut = true } ts.dealBuy(ts.gameScene.WhoseTurn, action) } } else { for i := 0; i < CHAIR_COUNT; i++ { action := ts.gameScene.Players[i].getAutoBuyAction() if action != Action_Buy_Invaild && ts.gameScene.Players[i].canAction() { if !ts.gameScene.Players[i].isRobot { ts.gameScene.Players[i].AutoOut = true } ts.dealBuy(i, action) } } } return case Phase_CloseOpen: if !ts.gameScene.Players[ts.gameScene.WhoseTurn].isRobot { ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut = true } ts.dealCloseOrOpen(Action_Buy_Open) return } ts.table.LogWithTableId("tablesink.dealGameTimeOut unreachable") } func (ts *tablesink) dealGamePlay() { ts.table.LogWithTableId("------dealGamePlay -----") ts.gameScene.Phase = Phase_FirstBuy ts.gameScene.PhaseIndex = 0 for i := 0; i < CHAIR_COUNT; i++ { u := ts.table.GetUserByChair(i) if u == nil { continue } buyList := []int{} if i == ts.gameScene.WhoseTurn { buyList = append(buyList, Action_Buy_Pass, Action_Buy_Hokum, Action_Buy_Sun) } ts.gameScene.Players[i].setPlayerOperators(buyList) } ts.table.NotifySceneChanged(-1) ts.resetGameTimer() } func (ts *tablesink) startGame() { ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) if ts.canStartGame(true) { delayTime := 1500 // 比赛场特殊处理 if ts.table.GetOwner() == -1 { delayTime = 2000 usr := ts.table.GetUserByChair(0) if usr.GetSetCount() == 1 { delayTime = 5000 } } else if ts.table.GetOwner() > 0 { ts.table.NotifySceneChanged(-1) delayTime = 3000 } ts.table.SetTimer(TIMER_START_GAME, delayTime) } } func (ts *tablesink) canStartGame(needCost bool) bool { count := 0 for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { break } userStatus := usr.GetUserStatus() if userStatus != user.UserStatus_Ready && userStatus != user.UserStatus_Offline { ts.table.LogWithTableId("ts.startGame chair[%d] userStatue = %d", i, userStatus) break } if !needCost { count++ continue } userGold := ts.table.GetUserChipOrGoldByUser(usr) if userGold < ts.roomInfo.MinGold || (ts.roomInfo.MaxGold > 0 && userGold > ts.roomInfo.MaxGold) { ts.table.LogWithTableId("tablesink.startGame %d 金币不足,站起", usr.GetUserId(),userGold,ts.roomInfo.MinGold,ts.roomInfo.MaxGold) if usr.IsRobot() { go ts.table.KickUser(usr.GetUserIndex(), false) } else { go ts.table.UserWatch(usr.GetUserIndex()) } break } // 先投注 ok, _ := ts.writeScore(usr.GetUserId(), -ts.roomInfo.BaseScore, 0, 0, ScoreType_Bet, ts.roomInfo.RoomName, usr.IsRobot()) if !ok { break } ts.gameScene.Players[i].initData(usr.GetUserId(), userGold, 0) ts.gameScene.Players[i].IsValid = true ts.gameScene.Players[i].userID = usr.GetUserId() ts.gameScene.Players[i].bet = ts.roomInfo.BaseScore ts.gameScene.Players[i].isRobot = usr.IsRobot() count++ } if count < CHAIR_COUNT { ts.table.LogWithTableId("游戏开始,用户不足") if !needCost { return false } ts.gameScene.Phase = Phase_End ts.gameScene.PhaseIndex = 0 // 分数还原 for i := 0; i < CHAIR_COUNT; i++ { if !ts.gameScene.Players[i].IsValid { continue } if ts.gameScene.Players[i].bet > 0 { ts.writeScore(ts.gameScene.Players[i].userID, ts.gameScene.Players[i].bet, 0, 0, ScoreType_Return, ts.roomInfo.RoomName, ts.gameScene.Players[i].isRobot) } } if ts.table.IsPrivate() { ts.table.PrivateRoomSetWinners([]int{}) } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) return false } return true } func (ts *tablesink) checkNeedControlPlayer() int { if !ts.roomInfo.IsOpenCardLibrary { return waterpool.PoolControl_Normal } if ts.gameScene.getUserCount() != 1 { return waterpool.PoolControl_Normal } controlType := waterpool.PoolControl_Normal gameType := waterpool.GameType_Normal if ts.roomInfo.IsDoublingMode { gameType = waterpool.GameType_Doubling } if ts.roomInfo.IsCorrectionMode { gameType = waterpool.GameType_Correct } isMatch := false if ts.table.GetOwner() == -1 { isMatch = true } baseScore := ts.roomInfo.BaseScore for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil || usr.IsRobot() { continue } if isMatch { baseScore = usr.GetBaseScore() } userId := usr.GetUserId() poolType, _ := waterpool.GetUserPoolType(userId, GAMEID, gameType, baseScore, usr.GetUserGold(), isMatch) if poolType == waterpool.PoolControl_Normal { continue } ts.gameScene.Players[i].isControl = true ts.gameScene.Players[i].controlType = poolType poolValue := waterpool.GetUserWaterPool(userId) if poolValue < 0 { poolValue = -poolValue } ts.gameScene.Players[i].controlDoubling = poolValue / baseScore / 2 if ts.gameScene.Players[i].controlDoubling > MAX_DOUBLING_LIMIT { ts.gameScene.Players[i].controlDoubling = MAX_DOUBLING_LIMIT } r := 6 + rand.Intn(4) ts.gameScene.Players[i].averageDoubling = ts.gameScene.Players[i].controlDoubling / r if ts.gameScene.ScoreToWin == -1 { ts.gameScene.Players[i].averageDoubling = ts.gameScene.Players[i].controlDoubling } controlType = poolType } return controlType } func (ts *tablesink) checkNeedControlRobot() { if !ts.roomInfo.IsOpenCardLibrary { return } controlRobot := ts.gameScene.getControlRobot() if !isValidChair(controlRobot) { return } controlType := waterpool.GetInventoryType(GAMEID, ts.roomInfo.RoomName) if controlType > waterpool.PoolControl_Normal { ts.gameScene.Players[controlRobot].controlType = controlType ts.gameScene.Players[controlRobot].isControl = true } } func (ts *tablesink) isPlayerEnterChasing(chairId int) bool { if !ts.gameScene.Players[chairId].isControl { return false } if ts.gameScene.Players[chairId].controlType == waterpool.PoolControl_Lose && ts.gameScene.Players[chairId].TotalScore > ts.gameScene.Players[getNextChair(chairId)].TotalScore { return true } if ts.gameScene.Players[chairId].controlType == waterpool.PoolControl_Win && ts.gameScene.Players[chairId].TotalScore < ts.gameScene.Players[getNextChair(chairId)].TotalScore { return true } return false } func (ts *tablesink) isEnterWindUp() bool { for i := 0; i < CHAIR_COUNT; i++ { if ts.gameScene.Players[i].TotalScore >= WINDUP_SCORE { return true } } return false } func (ts *tablesink) getControlChair() (int, int, bool) { controlChair := -1 realControlChair := -1 if !ts.roomInfo.IsOpenCardLibrary { return controlChair, realControlChair, false } for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } if !ts.gameScene.Players[i].isControl { continue } isNeedControl := false if !ts.roomInfo.IsDoublingMode { if ts.gameScene.GameIndex == 0 { isNeedControl = true } else { if ts.isPlayerEnterChasing(i) { isNeedControl = true } if ts.isEnterWindUp() { isNeedControl = true } } } else { isNeedControl = true } if !isNeedControl { continue } controType := ts.gameScene.Players[i].controlType if controType == waterpool.PoolControl_Win { controlChair = i } else { r := rand.Intn(10000) if r%2 == 1 { controlChair = getNextChair(i) } else { controlChair = getPreviousChair(i) } } realControlChair = i } return controlChair, realControlChair, false } func (ts *tablesink) getDoubling(controlChair int) int { if !isValidChair(controlChair) || !ts.roomInfo.IsDoublingMode { return -1 } controlDoubling := ts.gameScene.Players[controlChair].controlDoubling if controlDoubling <= 0 { return controlDoubling } leftDoubling := 0 if ts.gameScene.Players[controlChair].controlType == waterpool.PoolControl_Win { leftDoubling = controlDoubling - ts.gameScene.Players[controlChair].getPlayerCurrentTotalDoublings() } else { leftDoubling = controlDoubling - ts.gameScene.Players[getNextChair(controlChair)].getPlayerCurrentTotalDoublings() } if leftDoubling < ts.gameScene.Players[controlChair].averageDoubling { return 0 } return ts.gameScene.Players[controlChair].averageDoubling } func (ts *tablesink) gameStart() { // 发牌 controlChair, realControlChair, bNewUser := ts.getControlChair() doubling := ts.getDoubling(realControlChair) newChairId := -1 if ts.roomInfo.RoomID == 0 && !ts.table.IsPrivate() { bNewUser = true for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } if !usr.IsRobot() { newChairId = i break } } } if bNewUser { controlChair = newChairId } ts.logic.shuffle(ts.roomInfo.Test, controlChair, ts.gameScene.FirstActionChair, doubling, bNewUser) if bNewUser || ts.roomInfo.IsOpenCardLibrary { for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } userStatus := usr.GetUserStatus() if userStatus <= user.UserStatus_Free || userStatus == user.UserStatus_Watch { continue } ts.gameScene.Players[i].HandCards = ts.logic.getNormalCards() ts.gameScene.Players[i].bakHandCards = append(ts.gameScene.Players[i].bakHandCards, ts.gameScene.Players[i].HandCards...) } } else { var list []weightInfo for i := 0; i < CHAIR_COUNT; i++ { value := cardlibrary.GetUserCardLibraryrWeightValue(ts.gameScene.Players[i].userID) if ts.gameScene.Players[i].isRobot { value = 0 } list = append(list, weightInfo{ chair: i, weight: value, }) } sort.Slice(list, func(i, j int) bool { return list[i].weight > list[j].weight }) for i := 0; i < len(list); i++ { usr := ts.table.GetUserByChair(list[i].chair) if usr == nil { continue } userStatus := usr.GetUserStatus() if userStatus <= user.UserStatus_Free || userStatus == user.UserStatus_Watch { continue } ts.gameScene.Players[list[i].chair].HandCards = ts.logic.getNormalCardsInControl(ts.gameScene.Players[list[i].chair].userID) ts.gameScene.Players[list[i].chair].bakHandCards = append(ts.gameScene.Players[list[i].chair].bakHandCards, ts.gameScene.Players[list[i].chair].HandCards...) } } ts.gameScene.PublicCard = ts.logic.getOneCard() ts.gameScene.Phase = Phase_Start ts.gameScene.PhaseIndex = 0 ts.gameScene.LastTurn = -1 ts.gameScene.Index++ if ts.roomInfo.IsDoublingMode && getCardValue(ts.gameScene.PublicCard) == CardValueJ { ts.table.SetTimer(TIMER_GAME, SEC_START) } else { ts.table.SetTimer(TIMER_GAME, 2000) } ts.table.LogWithTableId("tablesink.gameStart[%d]", ts.table.GetTableID()) if ts.gameScene.GameIndex == 0 { ts.gameScene.dump(true) } else { ts.table.NotifySceneChanged(-1) if ts.roomInfo.IsDoublingMode { if getCardValue(ts.gameScene.PublicCard) == CardValueJ { ts.addDoublingData(Doubling_PublicCard_J) ts.sendShowDoublingMsg([]int{Doubling_PublicCard_J}) } } } } func (ts *tablesink) addDoublingData(doubleType int) { bFound := false valueDouble := []int{} if len(ts.roomInfo.ValueDoubling) != VALUE_DOUBLING_LEN { valueDouble = append(valueDouble, value_doubling...) } else { valueDouble = append(valueDouble, ts.roomInfo.ValueDoubling...) } for i := 0; i < len(ts.gameScene.DoublingDetails); i++ { if doubleType == ts.gameScene.DoublingDetails[i].DoublingType { ts.gameScene.DoublingDetails[i].DoublingTimes *= valueDouble[doubleType] bFound = true break } } if !bFound { ts.gameScene.DoublingDetails = append(ts.gameScene.DoublingDetails, DoublingData{ DoublingType: doubleType, DoublingTimes: valueDouble[doubleType], }) } } func (ts *tablesink) sendShowDoublingMsg(doublingTypes []int) { var data ShowDoubling data.Index = ts.gameScene.Index data.Times = 1 valueDouble := []int{} if len(ts.roomInfo.ValueDoubling) != VALUE_DOUBLING_LEN { valueDouble = append(valueDouble, value_doubling...) } else { valueDouble = append(valueDouble, ts.roomInfo.ValueDoubling...) } for i := 0; i < len(doublingTypes); i++ { data.Times *= valueDouble[doublingTypes[i]] } d, _ := json.Marshal(data) ts.table.SendGameData(-1, CMD_SHOWDOUBLING, string(d)) } func (ts *tablesink) recvChatMsg(userIndex int32, data string) { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvChatMsg,user[%d] not exist", userIndex) return } if ts.gameScene.Phase != Phase_Play { return } ts.table.SendGameData(-1, CMD_TABLECHAT, data) } func (ts *tablesink) recvCancleAutoMsg(userIndex int32, data string) { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvCancleAutoMsg,user[%d] not exist", userIndex) return } chairId := usr.GetUserChairId() if chairId == -1 { return } if !ts.gameScene.Players[chairId].AutoOut { return } ts.gameScene.Players[chairId].AutoOut = false // 重启倒计时 if ts.gameScene.WhoseTurn != chairId { return } if ts.gameScene.Phase == Phase_Play { ts.table.SetTimer(TIMER_GAME, ts.getSecond(ts.gameScene.Phase)) } } func (ts *tablesink) recvAutoMsg(userIndex int32, data string) { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvAutoMsg,user[%d] not exist", userIndex) return } chairId := usr.GetUserChairId() if chairId == -1 { return } ts.gameScene.Players[chairId].AutoOut = true // 重启倒计时 if ts.gameScene.WhoseTurn != chairId { return } if ts.gameScene.Phase == Phase_Play { ts.table.SetTimer(TIMER_GAME, SEC_AUTO) } } func (ts *tablesink) recvSyncData(userIndex int32, data string) bool { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvSyncData,user[%d] not exist", userIndex) return false } chairId := usr.GetUserChairId() if !isValidChair(chairId) { ts.table.LogWithTableId("tablesink.recvSyncData invalid chair %d", chairId) return false } if !ts.gameScene.Players[chairId].IsValid { ts.table.LogWithTableId("tablesink.recvSyncData invalid player [%d]", chairId) return false } if chairId != ts.gameScene.WhoseTurn { ts.table.LogWithTableId("tablesink.recvSyncData not WhoseTurn %d != WhoseTurn(%d)", chairId, ts.gameScene.WhoseTurn) return false } if ts.gameScene.Phase != Phase_Correct { ts.table.LogWithTableId("tablesink.recvSyncData not CorrectPhase %d", ts.gameScene.Phase) return false } ts.table.SendGameData(-1, CMD_SYNCDATA, data) return true } func (ts *tablesink) recvAction(userIndex int32, data string) bool { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvAction,user[%d] not exist", userIndex) return false } chairId := usr.GetUserChairId() if !isValidChair(chairId) { ts.table.LogWithTableId("tablesink.recvAction invalid chair %d", chairId) return false } ts.gameScene.Players[chairId].AutoOut = false if !ts.gameScene.Players[chairId].IsValid { ts.table.LogWithTableId("tablesink.recvAction invalid player [%d]", chairId) return false } var cmd CmdAction e := json.Unmarshal([]byte(data), &cmd) if e != nil { ts.table.LogWithTableId("tablesink.recvAction Unmarshal failed %s", data) return false } // 重洗阶段不用判断WhoseTurn,只需知道自己是否CanDoReshuffle即可 if cmd.Action != Action_CorrectionStart { if chairId != ts.gameScene.WhoseTurn && ts.gameScene.WhoseTurn != -1 && ts.gameScene.Phase != Phase_Reshuffle { ts.table.LogWithTableId("tablesink.recvAction not WhoseTurn %d != WhoseTurn(%d) action[%s] ", chairId, ts.gameScene.WhoseTurn, getActionDesc(cmd.Action)) return false } } ret := false errMsg := "" switch cmd.Action { case Action_Buy: ret, errMsg = ts.dealBuy(chairId, cmd.Param) case Action_ChooseTrump: ret, errMsg = ts.dealChooseTrump(cmd.Param) case Action_OutCard: ret, errMsg = ts.dealOutCard(cmd.Param, cmd.Projects) case Action_CloseOrOpen: ret, errMsg = ts.dealCloseOrOpen(cmd.Param) case Action_Gawah: ret, errMsg = ts.dealGawah() case Action_Double: ret, errMsg = ts.dealDouble(chairId, cmd.Param) case Action_Reshuffle: ret, errMsg = ts.dealReshuffle(chairId, cmd.Param) case Action_CorrectionStart: ret, errMsg = ts.dealCorrectionStart(chairId, cmd.Param) case Action_CorrectionFinish: ret, errMsg = ts.dealCorrectionFinish(cmd.Param, cmd.CorrectCards) case Action_Sawa: ret, errMsg = ts.dealSawa() } if !ret { var cmdFailed CmdActionFailed cmdFailed.CmdAction = cmd cmdFailed.ErrMsg = errMsg d, _ := json.Marshal(cmdFailed) ts.table.SendGameData(userIndex, CMD_ACTION_FAILED, string(d)) } return ret } func (ts *tablesink) recvSurrender(userIndex int32, data string) bool { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvSurrender,user[%d] not exist", userIndex) return false } chairId := usr.GetUserChairId() if !isValidChair(chairId) { ts.table.LogWithTableId("tablesink.recvSurrender invalid chair %d", chairId) return false } if ts.surrenderInfo.isSurrendering { ts.table.LogWithTableId("tablesink.recvSurrender isSurrendering") return false } if ts.gameScene.LeftSurrenderCount <= 0 { ts.table.LogWithTableId("tablesink.recvSurrender max operatorCount:%d", ts.gameScene.LeftSurrenderCount) return false } ts.surrenderInfo.isSurrendering = true ts.gameScene.LeftSurrenderCount-- ts.surrenderInfo.WhoseSurrender = chairId ts.surrenderInfo.SurrenderResult = Surrender_None d := ts.surrenderInfo.getSurrenderInfo() ts.table.SendGameData(userIndex, CMD_SURRENDER_RESULT, d) friendIndex := ts.table.GetUserByChair(getFriendChair(chairId)).GetUserIndex() ts.table.SendGameData(friendIndex, CMD_SURRENDER_RESULT, d) // 玩家设置5s倒计时 if ts.gameScene.Players[getFriendChair(chairId)].isRobot { ts.table.SetTimer(TIMER_ROBOT_SURRENDER, 1000+rand.Intn(2000)) } else { ts.table.SetTimer(TIMER_SURRENDER, 5000) } return true } func (ts *tablesink) recvSurrenderResult(userIndex int32, data string) bool { usr := ts.table.GetPlayer(userIndex) if usr == nil { ts.table.LogWithTableId("tablesink.recvSurrenderResult,user[%d] not exist", userIndex) return false } chairId := usr.GetUserChairId() if !isValidChair(chairId) { ts.table.LogWithTableId("tablesink.recvSurrenderResult invalid chair %d", chairId) return false } if !ts.surrenderInfo.isSurrendering { ts.table.LogWithTableId("tablesink.recvSurrenderResult not isSurrendering") return false } if getFriendChair(chairId) != ts.surrenderInfo.WhoseSurrender { ts.table.LogWithTableId("tablesink.recvSurrenderResult not CorrentChair:%d", chairId) return false } var cmd int e := json.Unmarshal([]byte(data), &cmd) if e != nil { ts.table.LogWithTableId("tablesink.recvSurrenderResult Unmarshal failed %s", data) return false } if cmd != Surrender_Failed && cmd != Surrender_Success { ts.table.LogWithTableId("tablesink.recvSurrenderResult cmd ERROR %d", cmd) return false } ts.surrenderInfo.SurrenderResult = cmd ts.table.KillTimer(TIMER_SURRENDER) d := ts.surrenderInfo.getSurrenderInfo() if cmd == Surrender_Success { ts.gameScene.Players[ts.surrenderInfo.WhoseSurrender].IsSurrender = true ts.table.SendGameData(-1, CMD_SURRENDER_RESULT, d) } else { ts.surrenderInfo.isSurrendering = false friendIndex := ts.table.GetUserByChair(getFriendChair(chairId)).GetUserIndex() ts.table.SendGameData(friendIndex, CMD_SURRENDER_RESULT, d) } return true } func (ts *tablesink) enterSurrenderEndPhase() { // 结算 winners := []int{} surrendChair := ts.surrenderInfo.WhoseSurrender winners = append(winners, getNextChair(surrendChair), getPreviousChair(surrendChair)) totalBet := 0 if ts.gameScene.Players[winners[0]].bet > 0 { totalBet = ts.gameScene.getTotalBet(winners[0]) } // 平分 endScore := totalBet / len(winners) if endScore != 0 { for _, v := range winners { ts.gameScene.Players[v].EndScore = endScore } } // 写分 for i := 0; i < CHAIR_COUNT; i++ { p := &ts.gameScene.Players[i] usr := ts.table.GetUserByChair(i) if !p.IsValid || usr == nil { continue } if isSameTeam(i, surrendChair) { ts.gameScene.Players[i].TotalScore = 0 } else { ts.gameScene.Players[i].TotalScore = SCORE_TO_WIN } realWin := p.EndScore - p.bet if p.bet == 0 { realWin = 0 } tax := 0 if realWin > 0 { tax = realWin * ts.roomInfo.TaxRate / 100 } wScore := p.EndScore - tax if p.bet == 0 { wScore = 0 } ts.writeScore(p.userID, wScore, tax, 0, scoreType_Surrender, ts.roomInfo.RoomName, p.isRobot) if wScore > 0 { userservices.DoRecord(p.userID, userservices.Record_MaxWin, wScore) } if !p.isRobot { roomType := waterpool.RoomType_Normal if ts.roomInfo.IsDoublingMode { roomType = waterpool.RoomType_Doubling } if ts.roomInfo.RoomType == 3 { roomType = waterpool.RoomType_Quick } if config.Server.IsLadderRoom > 0 { roomType = waterpool.RoomType_Ladder } if p.isControl { go waterpool.UpdataUserWaterPool(p.userID, realWin, GAME_NAME, roomType, ts.roomInfo.RoomID) } else { go waterpool.ReduceInventoryValue(GAMEID, ts.roomInfo.RoomName, realWin-tax, roomType) } } // 写记录 if ts.table.IsPrivate() { } else { if p.bet != 0 { go ts.table.WriteBetRecordWithSetcount(p.userID, p.bet, wScore, 1.0, "SurrenderGold", "Surrender", ts.roomInfo.RoomName, 1) } if config.Server.IsLadderRoom > 0 { ts.gameScene.Players[i].LastWinCount = ladder.GetUserConsecutiveWinCount(p.userID, GAMEID) ts.gameScene.Players[i].LadderChangeValue, _ = ladder.AddUserLadderScore(p.userID, GAMEID, ts.roomInfo.RoomName, wScore-p.bet) if ts.gameScene.Players[i].LadderChangeValue > 0 { ts.gameScene.Players[i].LastWinCount++ } d := ladder.GetUserLadderInfo(p.userID, GAMEID) if d != nil { ts.gameScene.Players[i].LadderInfo = *d } else { ts.table.LogWithTableId("User:%d, LadderInfo is nil", p.userID) } } } } ts.gameScene.Phase = Phase_End ts.table.NotifySceneChanged(-1) if ts.table.IsPrivate() { winnerUsers := make([]int, len(winners)) for i := 0; i < len(winners); i++ { winnerUsers[i] = ts.gameScene.Players[winners[i]].userID } ts.table.PrivateRoomSetWinners(winnerUsers) ts.writePrivateBetRecords() } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) } func (ts *tablesink) dealCloseOrOpen(action int) (bool, string) { if ts.gameScene.Phase != Phase_CloseOpen { ts.table.LogWithTableId("tablesink.dealCloseOrOpen incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if action != Action_Buy_Open && action != Action_Buy_Close { ts.table.LogWithTableId("tablesink.dealCloseOrOpen incorrect action %d", action) return false, "Wrong Action" } if !isValidCard(ts.gameScene.WhoseTurn) { ts.table.LogWithTableId("tablesink.dealCloseOrOpen Wrong WhoseTurn:%d", ts.gameScene.WhoseTurn) return false, "Wrong ChairId" } if ts.gameScene.Players[ts.gameScene.WhoseTurn].LastAction == Action_CloseOrOpen { ts.table.LogWithTableId("tablesink.dealCloseOrOpen already do CloseOrOpen") return false, "Wrong Done Action" } if action == Action_Buy_Close { ts.gameScene.IsClose = true ts.logic.setIsFirstOutCardClose(true) } else { ts.gameScene.IsClose = false ts.logic.setIsFirstOutCardClose(false) } // 记录玩家操作 ts.gameScene.Players[ts.gameScene.WhoseTurn].LastAction = Action_CloseOrOpen ts.gameScene.Players[ts.gameScene.WhoseTurn].LastActionData = action ts.gameScene.LastTurn = ts.gameScene.WhoseTurn ts.gameScene.addAction(ts.gameScene.WhoseTurn, Action_CloseOrOpen, action) ts.gameScene.WhoseTurn = -1 ts.gameScene.PhaseIndex++ ts.table.NotifySceneChanged(-1) buyAction := Action_Buy_Double + ts.gameScene.BaseTimes - 1 for i := 0; i < CHAIR_COUNT; i++ { buyList := []int{} if isSameTeam(ts.gameScene.LastTurn, i) { ts.gameScene.Players[i].setPlayerOperators(buyList) continue } buyList = append(buyList, Action_Buy_Pass, buyAction) ts.gameScene.Players[i].setPlayerOperators(buyList) } ts.gameScene.Phase = Phase_Double ts.gameScene.PhaseIndex = 0 ts.table.SetTimer(TIMER_DOUBLE_CHANGE, 600) return true, "" } func (ts *tablesink) dealCorrectionStart(chairId int, mode int) (bool, string) { if ts.gameScene.Phase != Phase_Play { ts.table.LogWithTableId("tablesink.dealCorrectionStart incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if ts.gameScene.isCorrectEnd { return false, "have Corrected" } if !ts.roomInfo.IsCorrectionMode { return false, "not CorrectionMode" } if isValidChair(ts.gameScene.SawaChair) && chairId == ts.gameScene.SawaChair { return false, "SawaChair" } if !ts.gameScene.CanCorrect { return false, "Can Not Operator" } ts.table.KillAllTimer() ts.gameScene.isCorrectEnd = true ts.gameScene.CorrectMode = mode ts.gameScene.Players[chairId].LastAction = Action_CorrectionStart ts.gameScene.LastTurn = chairId ts.gameScene.WhoseTurn = chairId ts.gameScene.addAction(chairId, Action_CorrectionStart, mode) ts.gameScene.PhaseIndex = 0 ts.gameScene.ShowAllCards = true ts.gameScene.Phase = Phase_Correct if !ts.simulator.haveWrongAction(ts.gameScene.CorrectMode, ts.gameScene.SawaChair) || (isSameTeam(ts.gameScene.SawaChair, chairId) && ts.gameScene.CorrectMode == Correct_Sawa) { ts.gameScene.CorrectResult = 0 ts.gameScene.PhaseIndex++ ts.table.NotifySceneChanged(-1) ts.table.SetTimer(TIMER_CORRECTFINISH, SEC_CORRECTFINISH) return true, "" } ts.table.NotifySceneChanged(-1) ts.DumpScene() ts.resetGameTimer() return true, "" } func (ts *tablesink) onTimerCorrectStart() { ts.table.KillAllTimer() ts.gameScene.CorrectResult = 0 ts.gameScene.ShowAllCards = true ts.gameScene.Index++ ts.gameScene.PhaseIndex++ ts.table.NotifySceneChanged(-1) ts.table.SetTimer(TIMER_CORRECTFINISH, SEC_CORRECTFINISH) } func (ts *tablesink) dealCorrectionFinish(correctType int, cards []int) (bool, string) { if ts.gameScene.Phase != Phase_Correct { ts.table.LogWithTableId("tablesink.dealReshuffle incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if !ts.roomInfo.IsCorrectionMode { return false, "not CorrectionMode" } if ts.gameScene.CorrectMode == Correct_Invaild { return false, "Invaild CorrectMode" } if len(cards) != 2 { return false, "Wrong Cards" } if ts.gameScene.PhaseIndex != 0 { return false, "Wrong Phase Index" } ts.table.KillAllTimer() ts.gameScene.Players[ts.gameScene.WhoseTurn].LastAction = Action_CorrectionFinish ts.gameScene.LastTurn = ts.gameScene.WhoseTurn ts.gameScene.addCorrectAction(ts.gameScene.WhoseTurn, Action_CorrectionFinish, correctType, cards) ts.gameScene.PhaseIndex++ ts.gameScene.CorrectCards = append(ts.gameScene.CorrectCards, cards...) if ts.gameScene.CorrectMode == Correct_Sawa { ts.gameScene.CorrectResult = ts.simulator.checkWrongActionInSawa(ts.gameScene.SawaChair, cards) } else { ts.gameScene.CorrectResult = ts.simulator.checkWrongActionInNormal(ts.gameScene.WhoseTurn, correctType, cards) } ts.gameScene.ShowAllCards = true ts.gameScene.CorrectType = correctType ts.table.NotifySceneChanged(-1) ts.table.SetTimer(TIMER_CORRECTFINISH, SEC_CORRECTFINISH) return true, "" } func (ts *tablesink) onTimerCorrectFinish() { winChair := ts.gameScene.WhoseTurn if ts.gameScene.CorrectResult == 0 { winChair = getNextChair(winChair) } for i := 0; i < CHAIR_COUNT; i++ { if ts.gameScene.Players[i].HaveBaloot && !ts.gameScene.Players[i].isIncludeBaloot() && ts.gameScene.CorrectMode == Correct_Normal { ts.gameScene.Players[i].Projects = append(ts.gameScene.Players[i].Projects, SingleProject{ Type: PROJECT_BALOOT, Score: value_project[PROJECT_BALOOT], Cards: []int{genACard(ts.gameScene.TrumpType, CardValueQ), genACard(ts.gameScene.TrumpType, CardValueK)}, }) if ts.roomInfo.IsDoublingMode { ts.addDoublingData(Doubling_Project_Baloot) } } if isSameTeam(i, winChair) { if ts.gameScene.FinalClub == Suit_Hokum { ts.gameScene.Players[i].WinCardScore = HOKUM_TOTAL_SCORE } else { ts.gameScene.Players[i].WinCardScore = SUN_TOTAL_SCORE } } else { ts.gameScene.Players[i].WinCardScore = 0 } } ts.gameScene.Players[winChair].WinCardNumber += ts.gameScene.getLeftCardNumber() for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].clearHandCard() } ts.gameScene.RoundWinner = winChair ts.onTimerNewRound() } func (ts *tablesink) dealReshuffle(chairId int, action int) (bool, string) { if ts.gameScene.Phase != Phase_Reshuffle { ts.table.LogWithTableId("tablesink.dealReshuffle incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } p := &ts.gameScene.Players[chairId] if !p.CanDoReshuffle || p.ReshuffleAction != Reshuffle_None { ts.table.LogWithTableId("tablesink.dealReshuffle Can not do the action %d", action) return false, "Wrong Action" } if ts.gameScene.havePlayerConfirmReshuffle(chairId) { ts.table.LogWithTableId("tablesink.dealReshuffle Can not do the action %d", action) return false, "Wrong Action" } // 记录玩家操作 ts.gameScene.Players[chairId].LastAction = Action_Reshuffle ts.gameScene.Players[chairId].LastActionData = action ts.gameScene.Players[chairId].ReshuffleAction = action ts.gameScene.LastTurn = chairId ts.gameScene.addAction(chairId, Action_Reshuffle, action) ts.gameScene.PhaseIndex++ if action == Reshuffle_Confirm { ts.gameScene.WhoseTurn = -1 ts.gameScene.reshuffleResult = -1 ts.table.NotifySceneChanged(-1) ts.table.SetTimer(TIMER_ALL_PASS, SEC_ALL_PASS*30) return true, "" } if ts.gameScene.isAllCanDoReshufflePlayerPass() { ts.gameScene.Phase = ts.gameScene.reshufflePhase ts.gameScene.PhaseIndex = 0 ts.gameScene.LastTurn = ts.gameScene.BuyChair if ts.gameScene.reshuffleResult == 0 { ts.resetGameTimer() } else { ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) } } ts.table.NotifySceneChanged(-1) return true, "" } func (ts *tablesink) dealDouble(chairId int, buyAction int) (bool, string) { if ts.gameScene.Phase != Phase_Double { ts.table.LogWithTableId("tablesink.dealDouble incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } p := &ts.gameScene.Players[chairId] if !p.canDoBuyAction(buyAction) { ts.table.LogWithTableId("tablesink.dealDouble Can not do the buy action %d", buyAction) return false, "Wrong Action" } // 记录玩家操作 ts.gameScene.Players[chairId].LastAction = Action_Double ts.gameScene.Players[chairId].LastActionData = buyAction ts.gameScene.LastTurn = chairId ts.gameScene.PhaseIndex++ ts.gameScene.addAction(chairId, Action_Double, buyAction) ts.gameScene.initAllPlayerTime() if buyAction != Action_Buy_Pass && ts.roomInfo.IsDoublingMode { ts.addDoublingData(buyAction) ts.sendShowDoublingMsg([]int{buyAction}) } if buyAction == Action_Buy_Double && ts.gameScene.FinalClub != Suit_Hokum { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].setPlayerOperators([]int{}) } ts.gameScene.setBaseTimes(buyAction) ts.gameScene.BuyChair = chairId ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) ts.table.NotifySceneChanged(-1) return true, "" } if ts.gameScene.isAllPlayerPass() { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].setPlayerOperators([]int{}) } ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) ts.table.NotifySceneChanged(-1) return true, "" } ts.gameScene.Players[chairId].setPlayerOperators([]int{}) if buyAction == Action_Buy_Coffee { ts.gameScene.IsClose = false for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].setPlayerOperators([]int{}) } ts.gameScene.BaseTimes = 1 ts.gameScene.BuyChair = chairId ts.gameScene.IsBO1 = true ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) ts.table.NotifySceneChanged(-1) return true, "" } if buyAction != Action_Buy_Pass { ts.gameScene.setBaseTimes(buyAction) ts.gameScene.BuyChair = chairId for i := 0; i < CHAIR_COUNT; i++ { buyList := []int{} ts.gameScene.Players[i].setPlayerOperators(buyList) } ts.table.NotifySceneChanged(-1) if ts.gameScene.BaseTimes%2 != 0 { ts.gameScene.WhoseTurn = -1 ts.gameScene.IsClose = false for i := 0; i < CHAIR_COUNT; i++ { buyList := []int{} if isSameTeam(chairId, i) { ts.gameScene.Players[i].setPlayerOperators(buyList) continue } buyList = append(buyList, Action_Buy_Pass, buyAction+1) ts.gameScene.Players[i].setPlayerOperators(buyList) } } else { ts.gameScene.WhoseTurn = ts.gameScene.BuyChair ts.gameScene.Phase = Phase_CloseOpen ts.gameScene.PhaseIndex = 0 } ts.table.SetTimer(TIMER_DOUBLE_CHANGE, 100) return true, "" } ts.table.NotifySceneChanged(-1) return true, "" } func (ts *tablesink) isFirstBuyEnterSecondBuy(chairId, buyAction int) bool { return (chairId == ts.gameScene.Banker && ts.gameScene.Phase == Phase_FirstBuy && buyAction == Action_Buy_Pass && ts.gameScene.BuyAction <= Action_Buy_Pass) } func (ts *tablesink) dealBuy(chairId int, buyAction int) (bool, string) { if ts.gameScene.Phase != Phase_FirstBuy && ts.gameScene.Phase != Phase_SecondBuy { ts.table.LogWithTableId("tablesink.dealBuy incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } p := &ts.gameScene.Players[chairId] if !p.canDoBuyAction(buyAction) { ts.table.LogWithTableId("tablesink.dealBuy Chair:%d Can not do the buy action %d", chairId, buyAction) return false, "Wrong Action" } if ts.gameScene.WhoseTurn != -1 { if chairId != ts.gameScene.WhoseTurn { ts.table.LogWithTableId("tablesink.dealBuy not the chairId %d", chairId) return false, "Wrong chairId" } } // 记录玩家操作 ts.gameScene.Players[chairId].LastAction = Action_Buy ts.gameScene.Players[chairId].LastActionData = buyAction ts.gameScene.LastTurn = chairId ts.gameScene.initAllPlayerTime() ts.gameScene.Players[chairId].setPlayerOperators([]int{}) ts.gameScene.addAction(chairId, Action_Buy, buyAction) ts.gameScene.buyPhase = ts.gameScene.Phase // 查看是否触发加倍 if ts.roomInfo.IsDoublingMode && !isSameTeam(ts.gameScene.BuyChair, chairId) { if ts.gameScene.BuyAction == Action_Buy_Hokum && buyAction == Action_Buy_Ashkal { ts.addDoublingData(Doubling_Hokum_to_Ashkal) ts.sendShowDoublingMsg([]int{Doubling_Hokum_to_Ashkal}) } if ts.gameScene.BuyAction == Action_Buy_Hokum && buyAction == Action_Buy_Sun { ts.addDoublingData(Doubling_Hokum_to_Sun) ts.sendShowDoublingMsg([]int{Doubling_Hokum_to_Sun}) } if ts.gameScene.BuyAction == Action_Buy_Ashkal && buyAction == Action_Buy_Sun { ts.addDoublingData(Doubling_Ashkal_to_Sun) ts.sendShowDoublingMsg([]int{Doubling_Ashkal_to_Sun}) } if ts.gameScene.BuyAction == Action_Buy_Sun && buyAction == Action_Buy_Sun { ts.addDoublingData(Doubling_Sun_to_Sun) ts.sendShowDoublingMsg([]int{Doubling_Sun_to_Sun}) } } if buyAction == Action_Buy_ConfirmHokum || buyAction == Action_Buy_SwitchSun || ts.gameScene.isBuySunEnterDouble(buyAction, chairId) || ts.gameScene.canReshuffle(buyAction, chairId) || ts.isFirstBuyEnterSecondBuy(chairId, buyAction) { ts.gameScene.PhaseIndex++ ts.table.NotifySceneChanged(-1) } // 判断买牌玩家是否有变化 buyChairIsChange := false if (buyAction > Action_Buy_Pass && buyAction <= Action_Buy_Ashkal) || ts.gameScene.BuyAction <= Action_Buy_Pass { buyChairIsChange = true } // 两轮都不买重洗 if ts.gameScene.WhoseTurn == ts.gameScene.Banker && ts.gameScene.BuyAction <= Action_Buy_Pass && ts.gameScene.Phase == Phase_SecondBuy && buyAction == Action_Buy_Pass { ts.gameScene.WhoseTurn = -1 ts.gameScene.PhaseIndex++ ts.table.NotifySceneChanged(-1) ts.allPassCount++ ts.table.SetTimer(TIMER_ALL_PASS, SEC_ALL_PASS*5) return true, "" } result := 0 prePhase := ts.gameScene.Phase if buyChairIsChange { result = ts.gameScene.resetBuyActionOfChangeBuyChair(buyAction, chairId) if ts.gameScene.isEnterReshuffle(buyAction) { ts.gameScene.reshuffleResult = result ts.resetGameTimer() } else { if result == 0 { ts.resetGameTimer() } else { ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) } } if prePhase != ts.gameScene.Phase { ts.gameScene.initAllPlayerLastAction() } else { ts.gameScene.PhaseIndex++ } // 特殊要求First_Buy切换到Second_Buy阶段,做延迟处理 if prePhase == Phase_FirstBuy && ts.gameScene.Phase == Phase_SecondBuy { ts.table.SetTimer(TIMER_BUY_CHANGE, 100) return true, "" } ts.table.NotifySceneChanged(-1) return true, "" } result = ts.gameScene.resetBuyActionOfNotChangeBuyChair(chairId, buyAction) isConfirmHokum := false if buyAction == Action_Buy_ConfirmHokum && prePhase == Phase_FirstBuy { isConfirmHokum = true data := ShowCardType{Type: ts.gameScene.TrumpType, Chair: chairId} d, _ := json.Marshal(data) ts.table.SendGameData(-1, CMD_SHOW_CARDTYPE, string(d)) for i := 0; i < CHAIR_COUNT; i++ { buyList := []int{} ts.gameScene.Players[i].setPlayerOperators(buyList) } } ts.gameScene.Index++ ts.table.NotifySceneChanged(-1) if result == 1 { if !isConfirmHokum { ts.resetGameTimer() } else { for i := 0; i < CHAIR_COUNT; i++ { buyList := []int{} if isSameTeam(i, ts.gameScene.BuyChair) { ts.gameScene.Players[i].setPlayerOperators(buyList) continue } buyList = append(buyList, Action_Buy_Pass, Action_Buy_Double) ts.gameScene.Players[i].setPlayerOperators(buyList) } ts.table.SetTimer(TIMER_DOUBLE_CHANGE, 2200) } } else if result == 2 { ts.table.SetTimer(TIMER_SEND_LEFT_CARD, SEC_SEND_LEFT_CARD) } if prePhase != ts.gameScene.Phase { ts.gameScene.initAllPlayerLastAction() } return true, "" } func (ts *tablesink) killAutoTimer() { for i := 0; i < CHAIR_COUNT; i++ { ts.table.KillTimer(TIMER_AUTO_ACTION_0 + i) } } func (ts *tablesink) resetGameTimer() { ts.LastOpraTime = time.Now() ts.killAutoTimer() switch ts.gameScene.Phase { case Phase_Start: ts.table.SetTimer(TIMER_GAME, SEC_START) case Phase_GameEnd: if ts.gameScene.ScoreToWin == -1 && !ts.table.IsPrivate() { ts.table.SetTimer(TIMER_GAME, 500) } else { if config.Server.IsLadderRoom == 1 && (ts.gameScene.ScoreToWin+ts.gameScene.GameIndex) < -1 { ts.table.SetTimer(TIMER_GAME, 6000) } else { if config.Server.IsLadderRoom == 1 && (ts.gameScene.ScoreToWin+ts.gameScene.GameIndex) == -1 { ts.table.SetTimer(TIMER_GAME, 500) } else { ts.table.SetTimer(TIMER_GAME, 500) } } } case Phase_Correct: ts.table.SetTimer(TIMER_GAME, 60000) case Phase_Reshuffle: for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil || usr.IsRobot() { continue } if !ts.gameScene.Players[i].AutoOut { continue } if !ts.gameScene.Players[i].canReshuffle() { continue } ts.table.SetTimer(TIMER_AUTO_ACTION_0+i, SEC_AUTO) } ts.table.SetTimer(TIMER_GAME, SEC_RESHUFFLE) default: if ts.gameScene.WhoseTurn == -1 { ts.checkAutoPlayerAction() ts.table.SetTimer(TIMER_GAME, ts.getSecond(ts.gameScene.Phase)) } else { if !isValidChair(ts.gameScene.WhoseTurn) { return } chairId := ts.gameScene.WhoseTurn usr := ts.table.GetUserByChair(chairId) if usr == nil { ts.table.LogWithTableId("resetGameTimer Phase:%d ChairId:%d nnot exist", ts.gameScene.Phase, chairId) ts.gameScene.Phase = Phase_End ts.gameScene.PhaseIndex = 0 // 分数还原 for i := 0; i < CHAIR_COUNT; i++ { if !ts.gameScene.Players[i].IsValid { continue } if ts.gameScene.Players[i].bet > 0 { ts.writeScore(ts.gameScene.Players[i].userID, ts.gameScene.Players[i].bet, 0, 0, ScoreType_Return, ts.roomInfo.RoomName, ts.gameScene.Players[i].isRobot) } } if ts.table.IsPrivate() { ts.table.PrivateRoomSetWinners([]int{}) } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) return } if isValidChair(chairId) { if (ts.gameScene.Players[chairId].AutoOut || usr.GetUserStatus() == user.UserStatus_Offline) && !usr.IsRobot() { ts.table.SetTimer(TIMER_GAME, SEC_AUTO) } else { ts.table.SetTimer(TIMER_GAME, ts.getSecond(ts.gameScene.Phase)) } } else { ts.table.SetTimer(TIMER_GAME, ts.getSecond(ts.gameScene.Phase)) } } } if ts.gameScene.Phase == Phase_Play { for i := 0; i < CHAIR_COUNT; i++ { ts.table.KillTimer(TIMER_ROBOT_SEND_CHAT_0 + i) } ts.table.SetTimer(TIMER_ROBOT_SEND_CHAT_0+ts.gameScene.WhoseTurn, 5000) } ts.checkRobotAction() } func (ts *tablesink) getSecond(phase int) int { ret := 0 switch phase { case Phase_FirstBuy: fallthrough case Phase_SecondBuy: ret = ts.roomInfo.SecBuy case Phase_ChooseTrump: ret = ts.roomInfo.SecChooseTrump case Phase_Double: ret = ts.roomInfo.SecDouble case Phase_CloseOpen: ret = ts.roomInfo.SecCloseOpen case Phase_Reshuffle: ret = SEC_RESHUFFLE case Phase_Play: if isValidChair(ts.gameScene.WhoseTurn) && len(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards) == 1 { ret = 700 } else { ret = ts.roomInfo.SecPlay } } if ret < 100 { ret *= 1000 } if ret == 0 { ret = 20000 } return ret } func (ts *tablesink) checkAutoPlayerAction() { if ts.gameScene.WhoseTurn != -1 { return } for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil || usr.IsRobot() || !ts.gameScene.Players[i].canAction() { continue } if !ts.gameScene.Players[i].AutoOut { continue } ts.table.SetTimer(TIMER_AUTO_ACTION_0+i, SEC_AUTO) } } func (ts *tablesink) onTimerBuyFailed() { if ts.gameScene.Phase == Phase_End { return } if ts.surrenderInfo.isSurrenderEnd() { ts.gameScene.BuyChair = getNextChair(ts.surrenderInfo.WhoseSurrender) ts.gameScene.FinalClub = Suit_Sun ts.gameScene.LastWinChair = ts.gameScene.BuyChair ts.gameScene.Players[ts.gameScene.BuyChair].FinalCalcScore = 26 ts.gameScene.Players[ts.gameScene.BuyChair].TotalScore += 26 ts.gameScene.Players[getFriendChair(ts.gameScene.BuyChair)].FinalCalcScore = 26 ts.gameScene.Players[getFriendChair(ts.gameScene.BuyChair)].TotalScore += 26 ts.enterGameEndPhase(false) return } ts.gameScene.setNextBanker() ts.gameScene.gameInit(getPreviousChair(ts.gameScene.Banker)) ts.gameStart() if ts.gameScene.GameIndex == 0 { ts.table.NotifySceneChanged(-1) if ts.roomInfo.IsDoublingMode { if getCardValue(ts.gameScene.PublicCard) == CardValueJ { ts.addDoublingData(Doubling_PublicCard_J) ts.sendShowDoublingMsg([]int{Doubling_PublicCard_J}) } } } } func (ts *tablesink) sendLeftCard() { ts.logic.setTrumpAndSuit(ts.gameScene.TrumpType, ts.gameScene.FinalClub) getPublicChairId := ts.gameScene.BuyChair ts.gameScene.Phase = Phase_SendLeftCard ts.gameScene.PhaseIndex = 0 if ts.gameScene.FinalClub == Suit_Ashkal { getPublicChairId = (getPublicChairId + 2) % CHAIR_COUNT } count := 0 for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } userStatus := usr.GetUserStatus() if userStatus <= user.UserStatus_Free || userStatus == user.UserStatus_Watch { continue } count++ leftCards := ts.logic.getLeftCards(getPublicChairId == i) ts.gameScene.Players[i].HandCards = append(ts.gameScene.Players[i].HandCards, leftCards...) ts.gameScene.Players[i].LeftSendCards = append(ts.gameScene.Players[i].LeftSendCards, leftCards...) if getPublicChairId == i { ts.gameScene.Players[i].HandCards = append(ts.gameScene.Players[i].HandCards, ts.gameScene.PublicCard) ts.gameScene.Players[i].LeftSendCards = append(ts.gameScene.Players[i].LeftSendCards, ts.gameScene.PublicCard) } if ts.gameScene.FinalClub == Suit_Hokum { ts.gameScene.Players[i].checkBaloot(ts.gameScene.TrumpType) } p := &ts.gameScene.Players[i] sort.Slice(p.HandCards, func(m, n int) bool { return getCardSortValue(p.HandCards[m], ts.gameScene.TrumpType) < getCardSortValue(p.HandCards[n], ts.gameScene.TrumpType) }) } if count < CHAIR_COUNT { ts.table.LogWithTableId("sendLeftCard 用户不足") ts.gameScene.Phase = Phase_End ts.gameScene.PhaseIndex = 0 // 分数还原 for i := 0; i < CHAIR_COUNT; i++ { if !ts.gameScene.Players[i].IsValid { continue } if ts.gameScene.Players[i].bet > 0 { ts.writeScore(ts.gameScene.Players[i].userID, ts.gameScene.Players[i].bet, 0, 0, ScoreType_Return, ts.roomInfo.RoomName, ts.gameScene.Players[i].isRobot) } } if ts.table.IsPrivate() { ts.table.PrivateRoomSetWinners([]int{}) } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) return } ts.gameScene.onAllCardsSent() ts.table.SetTimer(TIMER_START_OUT_CARD, SEC_START_OUT_CARD) ts.table.NotifySceneChanged(-1) } func (ts *tablesink) dealChooseTrump(t int) (bool, string) { if ts.gameScene.Phase != Phase_ChooseTrump { ts.table.LogWithTableId("tablesink.dealChooseTrump incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if t == getCardType(ts.gameScene.PublicCard) || t >= CardType_Invalid { ts.table.LogWithTableId("tablesink.dealChooseTrump incorrect Type %d", t) return false, "Wrong Type" } ts.gameScene.FinalClub = Suit_Hokum ts.gameScene.TrumpType = t data := ShowCardType{Type: t, Chair: ts.gameScene.WhoseTurn} d, _ := json.Marshal(data) ts.table.SendGameData(-1, CMD_SHOW_CARDTYPE, string(d)) chair := ts.gameScene.WhoseTurn ts.gameScene.LastTurn = chair ts.gameScene.Players[chair].LastAction = Action_ChooseTrump ts.gameScene.Players[chair].LastActionData = t ts.gameScene.addAction(chair, Action_ChooseTrump, t) ts.gameScene.PhaseIndex++ ts.gameScene.WhoseTurn = -1 ts.table.NotifySceneChanged(-1) for i := 0; i < CHAIR_COUNT; i++ { p := &ts.gameScene.Players[i] sort.Slice(p.HandCards, func(m, n int) bool { return getCardSortValue(p.HandCards[m], ts.gameScene.TrumpType) < getCardSortValue(p.HandCards[n], ts.gameScene.TrumpType) }) buyList := []int{} if isSameTeam(i, ts.gameScene.BuyChair) { ts.gameScene.Players[i].setPlayerOperators(buyList) continue } buyList = append(buyList, Action_Buy_Pass, Action_Buy_Double) ts.gameScene.Players[i].setPlayerOperators(buyList) } ts.gameScene.Phase = Phase_Double ts.gameScene.PhaseIndex = 0 ts.table.SetTimer(TIMER_DOUBLE_CHANGE, 2200) return true, "" } func (ts *tablesink) dealSawa() (bool, string) { if ts.gameScene.Phase != Phase_Play { ts.table.LogWithTableId("tablesink.dealSawa incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if ts.gameScene.WhoseTurn == -1 { ts.table.LogWithTableId("tablesink.dealSawa not correct WhoseTurn %d", ts.gameScene.WhoseTurn) return false, "Wrong WhoseTurn" } if ts.gameScene.RoundType != CardType_Invalid { ts.table.LogWithTableId("tablesink.dealSawa Not First OutCard") return false, "Not First OutCard" } if isValidChair(ts.gameScene.SawaChair) { ts.table.LogWithTableId("tablesink.dealSawa Already Sawa") return false, "Already Sawa" } for i := 0; i < CHAIR_COUNT; i++ { if ts.gameScene.Players[i].HaveBaloot && !ts.gameScene.Players[i].isIncludeBaloot() { ts.gameScene.Players[i].Projects = append(ts.gameScene.Players[i].Projects, SingleProject{ Type: PROJECT_BALOOT, Score: value_project[PROJECT_BALOOT], Cards: []int{genACard(ts.gameScene.TrumpType, CardValueQ), genACard(ts.gameScene.TrumpType, CardValueK)}, }) d, _ := json.Marshal(i) ts.table.SendGameData(-1, CMD_CALL_BALOOT, string(d)) if ts.roomInfo.IsDoublingMode { ts.addDoublingData(Doubling_Project_Baloot) ts.sendShowDoublingMsg([]int{Doubling_Project_Baloot}) } } } if ts.roomInfo.IsCorrectionMode { ts.gameScene.CanCorrect = true } ts.table.KillAllTimer() p := &ts.gameScene.Players[ts.gameScene.WhoseTurn] ts.gameScene.LastTurn = ts.gameScene.WhoseTurn p.LastAction = Action_Sawa ts.gameScene.addAction(ts.gameScene.WhoseTurn, Action_Sawa, ts.gameScene.WhoseTurn) ts.gameScene.PhaseIndex++ ts.gameScene.ShowAllCards = true ts.gameScene.SawaChair = ts.gameScene.WhoseTurn ts.table.NotifySceneChanged(-1) if ts.roomInfo.IsCorrectionMode { ts.table.SetTimer(TIMER_SAWA, SEC_SAWA) } else { ts.table.SetTimer(TIMER_SAWA, 2200) } return true, "" } func (ts *tablesink) onTimerSawa() { if ts.gameScene.Phase != Phase_Play { ts.table.LogWithTableId("tablesink.dealSawa incorrect phase %d", ts.gameScene.Phase) return } chair := ts.gameScene.WhoseTurn winChair := chair if !ts.roomInfo.IsCorrectionMode { for n := 0; n < CHAIR_COUNT; n++ { for i := 0; i <= len(ts.gameScene.Players[chair].HandCards); i++ { card := ts.gameScene.Players[chair].HandCards[i] if !ts.simulator.isBiggerThanAll(card, getCardType(card), ts.gameScene.Players[n].HandCards) { winChair = n break } } if winChair != chair { if isSameTeam(winChair, chair) { winChair = getNextChair(chair) } break } } } leftScore := 0 getScore := ts.gameScene.Players[winChair].WinCardScore + ts.gameScene.Players[getNextChair(winChair)].WinCardScore if ts.gameScene.FinalClub == Suit_Hokum { leftScore = HOKUM_TOTAL_SCORE - getScore } else { leftScore = SUN_TOTAL_SCORE - getScore } for i := 0; i < CHAIR_COUNT; i++ { if isSameTeam(i, winChair) { ts.gameScene.Players[i].WinCardScore += leftScore } } ts.gameScene.ShowAllCards = true ts.gameScene.CanCorrect = false ts.gameScene.Players[winChair].WinCardNumber += ts.gameScene.getLeftCardNumber() for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].clearHandCard() } ts.gameScene.RoundWinner = winChair ts.onTimerNewRound() } func (ts *tablesink) dealGawah() (bool, string) { if ts.gameScene.Phase != Phase_Play { ts.table.LogWithTableId("tablesink.dealGawah incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if ts.gameScene.WhoseTurn == -1 { ts.table.LogWithTableId("tablesink.dealGawah not correct WhoseTurn %d", ts.gameScene.WhoseTurn) return false, "Wrong WhoseTurn" } chair := ts.gameScene.WhoseTurn p := &ts.gameScene.Players[chair] if !p.CanGawah { ts.table.LogWithTableId("tablesink.dealGawah can not Gawah") return false, "Wrong Action" } ts.gameScene.LastTurn = chair p.LastAction = Action_Gawah p.CanGawah = false ts.gameScene.addAction(chair, Action_Gawah, chair) ts.gameScene.PhaseIndex++ ts.table.KillTimer(TIMER_GAME) leftScore := ts.gameScene.getLeftScore() ts.gameScene.Players[chair].winCardScore += leftScore doublingData := []int{} for i := 0; i < CHAIR_COUNT; i++ { if isSameTeam(i, chair) { ts.gameScene.Players[i].addScore(leftScore) } else { ts.gameScene.Players[i].addScore(0) } if ts.gameScene.Players[i].HaveBaloot && !ts.gameScene.Players[i].isIncludeBaloot() { ts.gameScene.Players[i].Projects = append(ts.gameScene.Players[i].Projects, SingleProject{ Type: PROJECT_BALOOT, Score: value_project[PROJECT_BALOOT], Cards: []int{genACard(ts.gameScene.TrumpType, CardValueQ), genACard(ts.gameScene.TrumpType, CardValueK)}, }) d, _ := json.Marshal(i) ts.table.SendGameData(-1, CMD_CALL_BALOOT, string(d)) if ts.roomInfo.IsDoublingMode { ts.addDoublingData(Doubling_Project_Baloot) doublingData = append(doublingData, Doubling_Project_Baloot) } } } ts.gameScene.ShowAllCards = true winCardNum := ts.gameScene.getLeftCardNumber() preWinCount := ts.gameScene.Players[chair].consecutiveWinCardCount ts.gameScene.Players[chair].consecutiveWinCardCount += winCardNum / CHAIR_COUNT ts.gameScene.Players[getFriendChair(chair)].consecutiveWinCardCount += winCardNum / CHAIR_COUNT ts.gameScene.Players[chair].WinCardNumber += winCardNum if ts.gameScene.Players[chair].consecutiveWinCardCount >= 5 && ts.roomInfo.IsDoublingMode { leftCount := 0 if preWinCount >= 5 { leftCount = ts.gameScene.Players[chair].consecutiveWinCardCount - preWinCount } else { leftCount = ts.gameScene.Players[chair].consecutiveWinCardCount - 4 } winData := []int{} for i := 0; i < leftCount; i++ { ts.addDoublingData(Doubling_ConsecutiveWinOverFour) winData = append(winData, Doubling_ConsecutiveWinOverFour) } doublingData = append(doublingData, winData...) } if len(doublingData) > 0 { ts.sendShowDoublingMsg(doublingData) } ts.table.NotifySceneChanged(-1) for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].clearHandCard() } ts.gameScene.RoundWinner = chair ts.table.SetTimer(TIMER_NEWROUND, 2200) return true, "" } func (ts *tablesink) dealOutCard(card int, projects []int) (bool, string) { if ts.gameScene.Phase != Phase_Play { ts.table.LogWithTableId("tablesink.dealOutCard incorrect phase %d", ts.gameScene.Phase) return false, "Wrong Phase" } if ts.gameScene.WhoseTurn == -1 { ts.table.LogWithTableId("tablesink.dealOutCard incorrect WhoseTurn %d", ts.gameScene.WhoseTurn) return false, "Wrong WhoseTurn" } chair := ts.gameScene.WhoseTurn p := &ts.gameScene.Players[chair] if ts.gameScene.isFirstOutCardInCloseMode() && !ts.roomInfo.IsCorrectionMode { if getCardType(card) == ts.gameScene.TrumpType && !p.IsAllTrumpCard(ts.gameScene.TrumpType) { ts.table.LogWithTableId("tablesink.dealOutCard OutCard Fail") return false, "Wrong Card" } } if isValidChair(ts.gameScene.SawaChair) { return false, "is In Sawa" } // 检查牌是否能出 if !ts.simulator.fakers[chair].isOutBigCards(card) && !ts.roomInfo.IsCorrectionMode { ts.table.LogWithTableId("tablesink.dealOutCard not Out BigCard %d, BigCard%s", card, getCardsHex(p.BigCards)) return false, "Wrong BigCard" } // 检查牌是否能出 if !ts.logic.canOut(card, ts.gameScene.RoundType, p.HandCards) && !ts.roomInfo.IsCorrectionMode { ts.table.LogWithTableId("tablesink.dealOutCard removeCard failed %d", card) return false, "Wrong Card Type" } handCards := make([]int, len(p.HandCards)) copy(handCards, p.HandCards) // 牌在不在? if !p.removeCard(card) { ts.table.LogWithTableId("tablesink.dealOutCard removeCard failed %d", card) return false, "Card Not Exist" } // 纠错模式,记录玩家出牌是否正确 if ts.roomInfo.IsCorrectionMode { flag := -1 correctType := CorrectType_Invaild shouldOutCards := []int{} if ts.gameScene.isFirstOutCardInCloseMode() { if getCardType(card) == ts.gameScene.TrumpType && !p.IsAllTrumpCard(ts.gameScene.TrumpType) { flag = card correctType = CorrectType_Close for n := 0; n < len(handCards); n++ { if getCardType(handCards[n]) != ts.gameScene.TrumpType { shouldOutCards = append(shouldOutCards, handCards[n]) } } } } if !ts.simulator.fakers[chair].isOutBigCards(card) { flag = card for n := 0; n < len(handCards); n++ { if ts.simulator.fakers[chair].isOutBigCards(handCards[n]) { shouldOutCards = append(shouldOutCards, handCards[n]) } } if ts.gameScene.RoundType == ts.gameScene.TrumpType { correctType = CorrectType_BigCardInHokum } else { correctType = CorrectType_TrumpCard } } if !ts.logic.canOut(card, ts.gameScene.RoundType, handCards) { flag = card correctType = CorrectType_SameType for n := 0; n < len(handCards); n++ { if ts.logic.canOut(handCards[n], ts.gameScene.RoundType, handCards) { shouldOutCards = append(shouldOutCards, handCards[n]) } } } ts.simulator.fakers[chair].correctionList = append(ts.simulator.fakers[chair].correctionList, correction{ correctCard: flag, correctType: correctType, suggestCards: shouldOutCards, }) } ts.simulator.fakers[chair].removeCard(card) if ts.gameScene.RoundType != CardType_Invalid && getCardType(card) != ts.gameScene.RoundType { ts.simulator.fakers[chair].updataACardTypeToNotExist(ts.gameScene.RoundType) } if p.CanGawah { p.CanGawah = false } ts.gameScene.LastTurn = chair p.LastAction = Action_OutCard p.LastActionData = card p.IsCallBiggest = false ts.gameScene.addOutCardAction(chair, Action_OutCard, card, projects) doublingData := []int{} if getCardType(card) == ts.gameScene.TrumpType && (getCardValue(card) == CardValueQ || getCardValue(card) == CardValueK) && p.outAllBalootCard(ts.gameScene.TrumpType) { p.Projects = append(p.Projects, SingleProject{ Type: PROJECT_BALOOT, Score: value_project[PROJECT_BALOOT], Cards: []int{genACard(ts.gameScene.TrumpType, CardValueQ), genACard(ts.gameScene.TrumpType, CardValueK)}, }) d, _ := json.Marshal(ts.gameScene.WhoseTurn) ts.table.SendGameData(-1, CMD_CALL_BALOOT, string(d)) if ts.roomInfo.IsDoublingMode { ts.addDoublingData(Doubling_Project_Baloot) doublingData = append(doublingData, Doubling_Project_Baloot) } } if ts.gameScene.RoundIndex == 1 && len(projects) == 4 { ts.gameScene.checkProject(chair, projects) for n := 0; n < len(ts.gameScene.Players[chair].Projects); n++ { //if ts.gameScene.Players[chair].CallProject == PROJECT_FOURHUNDRED { var param badge.Scope param.GameName = GAME_NAME param.RankInfo.Score = ts.gameScene.Players[chair].Projects[n].taskScore userId := ts.gameScene.Players[chair].userID badge.DoAction(userId, badge.Action_Game_CallPoints, 1, param) } } // 加倍模式项目加倍 if ts.roomInfo.IsDoublingMode && ts.gameScene.RoundIndex == 1 && len(p.Projects) > 0 { projectData := []int{} for i := 0; i < len(p.Projects); i++ { ts.addDoublingData(p.Projects[i].Type) projectData = append(projectData, p.Projects[i].Type) } doublingData = append(doublingData, projectData...) } delaySce := 0 if ts.gameScene.RoundIndex == 2 { if len(ts.gameScene.Players[chair].Projects) > 0 { userservices.DoRecord(ts.gameScene.Players[chair].userID, userservices.Record_Project, 1) if ts.gameScene.Players[chair].Projects[0].Type >= PROJECT_FIFTY && ts.gameScene.Players[getFriendChair(chair)].isRobot { delaySce += 3500 ts.sendRobotChatAction(RobotChatAction_FriendGetBigProject, ts.gameScene.Players[getFriendChair(chair)].userID, ts.gameScene.Players[chair].userID, delaySce) delaySce += 2500 } if ts.gameScene.Players[chair].Projects[0].Type >= PROJECT_HUNDRED { start := rand.Intn(4) for i := 0; i < CHAIR_COUNT; i++ { chairId := (start + i) % CHAIR_COUNT if !isSameTeam(chairId, chair) && ts.gameScene.Players[chairId].isRobot { ts.sendRobotChatAction(RobotChatAction_EmenyGetBigProject, ts.gameScene.Players[chairId].userID, -1, delaySce) delaySce += 2500 break } } } } } if ts.gameScene.FinalClub == Suit_Hokum && ts.gameScene.RoundType == CardType_Invalid && getCardType(card) != ts.gameScene.TrumpType && !isBiggestCard(card, ts.gameScene.TrumpType) { if isScoreCard(card, []int{}, ts.gameScene.GameOutCardHistory, ts.gameScene.TrumpType) { p.IsCallBiggest = true } } if ts.gameScene.RoundType == CardType_Invalid { ts.gameScene.RoundType = getCardType(card) ts.simulator.roundType = ts.gameScene.RoundType if ts.roomInfo.IsCorrectionMode && len(ts.gameScene.Players[chair].HandCards) == NORMAL_HOLD_CARD-2 { ts.gameScene.CanCorrect = true } } ts.gameScene.GameOutCardHistory = append(ts.gameScene.GameOutCardHistory, card) if !ts.gameScene.isRoundEnd() { ts.gameScene.nextChair() if ts.gameScene.FinalClub == Suit_Hokum { isFriendCallBiggest := false friendOutCard := ts.gameScene.Players[getFriendChair(ts.gameScene.WhoseTurn)].CurrentCard if isValidCard(friendOutCard) { isFriendCallBiggest = ts.gameScene.Players[getFriendChair(ts.gameScene.WhoseTurn)].IsCallBiggest if isBiggestCard(friendOutCard, ts.gameScene.TrumpType) { isFriendCallBiggest = true } } bigCards := ts.simulator.getCanOutCards(ts.gameScene.WhoseTurn, isFriendCallBiggest) if !ts.roomInfo.IsCorrectionMode { ts.gameScene.Players[ts.gameScene.WhoseTurn].initBigCards(bigCards) } ts.simulator.fakers[ts.gameScene.WhoseTurn].initBigCards(bigCards) } if len(doublingData) > 0 { ts.sendShowDoublingMsg(doublingData) } ts.resetGameTimer() ts.table.NotifySceneChanged(-1) return true, "" } ts.table.KillTimer(TIMER_GAME) for i := 0; i < CHAIR_COUNT; i++ { ts.table.KillTimer(TIMER_ROBOT_SEND_CHAT_0 + i) } // 一圈打完了,需要延迟开新的一圈 outCards := ts.gameScene.getRoundCards() winner := ts.logic.getWinner(outCards, ts.gameScene.RoundType) ts.table.LogWithTableId("end a round outCards = %s roundType = %d,winner = %d", getCardsHex(outCards), ts.gameScene.RoundType, winner) for i := 0; i < CHAIR_COUNT; i++ { if !isSameTeam(i, winner) { ts.gameScene.Players[i].consecutiveWinCardCount = 0 ts.simulator.fakers[i].consecutiveWinCardCount = 0 } else { ts.gameScene.Players[i].consecutiveWinCardCount++ ts.simulator.fakers[i].consecutiveWinCardCount++ } if i == winner { ts.gameScene.Players[i].selfConsecutiveWinCardCount = 0 } else { ts.gameScene.Players[i].selfConsecutiveWinCardCount++ } } if ts.gameScene.Players[winner].consecutiveWinCardCount >= 5 && ts.roomInfo.IsDoublingMode { ts.addDoublingData(Doubling_ConsecutiveWinOverFour) doublingData = append(doublingData, Doubling_ConsecutiveWinOverFour) start := rand.Intn(4) for i := 0; i < CHAIR_COUNT; i++ { chairId := (start + i) % CHAIR_COUNT if !isSameTeam(chairId, winner) && ts.gameScene.Players[chairId].isRobot && ts.gameScene.Players[winner].consecutiveWinCardCount == 5 { ts.sendRobotChatAction(RobotChatAction_LoseOverFive, ts.gameScene.Players[chairId].userID, -1, delaySce) delaySce += 2500 break } } } if ts.gameScene.Players[winner].selfConsecutiveWinCardCount == 5 { if ts.gameScene.Players[winner].isRobot { ts.sendRobotChatAction(RobotChatAction_SelfWinOverFive, ts.gameScene.Players[winner].userID, -1, delaySce) delaySce += 2500 } if ts.gameScene.Players[winner].isRobot && ts.gameScene.Players[getFriendChair(winner)].isRobot { ts.sendRobotChatAction(RobotChatAction_FriendWinOverFive, ts.gameScene.Players[getFriendChair(winner)].userID, -1, delaySce) } } winScore := ts.gameScene.endAOutCardRound(winner) for i := 0; i < CHAIR_COUNT; i++ { if isSameTeam(i, winner) { ts.simulator.fakers[i].addScore(winScore) } } for i := 0; i < CHAIR_COUNT; i++ { ts.simulator.gameOutCardHistory = append(ts.simulator.gameOutCardHistory, ts.simulator.fakers[i].currentCard) ts.simulator.fakers[i].currentCard = CARD_COUNT ts.gameScene.Players[i].initBigCards([]int{}) ts.simulator.fakers[i].initBigCards([]int{}) } if len(doublingData) > 0 { ts.sendShowDoublingMsg(doublingData) } ts.table.NotifySceneChanged(-1) if ts.gameScene.isGameEnd() { ts.table.SetTimer(TIMER_NEWROUND, 1800) } else { ts.table.SetTimer(TIMER_NEWROUND, SEC_NEWROUND) } return true, "" } func (ts *tablesink) onTimerNewRound() { if ts.gameScene.Phase == Phase_End { return } if ts.gameScene.isGameEnd() { if ts.roomInfo.IsCorrectionMode { ts.table.SetTimer(TIMER_CORRECTDELAY, SCE_CORRECT_DELAY) return } // 轮次结束也要清理收牌信息 ts.gameScene.newOutCardRound() // 进入SetEnd阶段 ts.enterGameEndPhase(true) return } // 继续 ts.gameScene.newOutCardRound() if ts.gameScene.RoundIndex == 2 { for i := 0; i < CHAIR_COUNT; i++ { if len(ts.gameScene.Players[i].Projects) == 0 { continue } ts.simulator.fakers[i].projects = append(ts.simulator.fakers[i].projects, ts.gameScene.Players[i].Projects...) } } if !ts.roomInfo.IsCorrectionMode { ts.gameScene.checkCanGawah() } ts.simulator.roundType = ts.gameScene.RoundType ts.resetGameTimer() ts.gameScene.Index++ ts.table.NotifySceneChanged(-1) } func (ts *tablesink) enterGameEndPhase(isCalc bool) { if isCalc { ts.gameScene.gameEnd() } else { ts.gameScene.PhaseIndex = 0 ts.gameScene.Phase = Phase_GameEnd } if !ts.table.IsPrivate() && ts.roomInfo.LevelParam >= 100 { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].LevelValue = 0 if ts.gameScene.Players[i].FinalCalcScore == 0 { continue } value := (10 * ts.roomInfo.LevelParam * ts.gameScene.Players[i].FinalCalcScore) / 100 value = ts.table.AddExperience(ts.gameScene.Players[i].userID, value) ts.gameScene.Players[i].LevelValue = value } } var scores perRoundScores scores.positionScore0 = ts.gameScene.Players[0].FinalCalcScore scores.positionScore1 = ts.gameScene.Players[1].FinalCalcScore ts.gameScene.scoreHistory = append(ts.gameScene.scoreHistory, scores) goal := -1 if scores.positionScore0 == 0 { goal = 1 } else if scores.positionScore1 == 0 { goal = 1 } if goal != -1 { for i := 0; i < CHAIR_COUNT; i++ { if isSameTeam(i, goal) { userservices.DoRecord(ts.gameScene.Players[i].userID, userservices.Record_BalootWinAllScore, 1) } } } ts.statisticsRobotAction() ts.gameScene.statics = append(ts.gameScene.statics, robotChatActionList{ round: ts.gameScene.GameIndex, action: ts.gameScene.robotChatList, }) ts.gameScene.robotChatList = []robotChatAction{} ts.gameScene.robotSendChatCount = 0 ts.resetGameTimer() ts.reportTaskAndUpdateGameScore() //ts.table.NotifySceneChanged(-1) if !ts.gameScene.isGameFinish() && !ts.surrenderInfo.isSurrenderEnd() { ts.table.SendGameData(-1, CMD_SMALL_RESULT, ts.gameScene.getScene(-1, true, false)) } // 为比赛场服务,最后一局EndScore不清零 if (ts.gameScene.ScoreToWin < -1 && (ts.gameScene.GameIndex+ts.gameScene.ScoreToWin) < -1) || (!ts.table.IsPrivate() && ts.gameScene.ScoreToWin == -1) { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].EndScore = 0 } } if ts.gameScene.ScoreToWin < -1 && (ts.gameScene.GameIndex+ts.gameScene.ScoreToWin) < -1 && ts.table.GetOwner() == -1 { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].Doublings = []DoublingDetail{} ts.gameScene.Players[i].TotalScore = 0 } } } func (ts *tablesink) reportTaskAndUpdateGameScore() { for i := 0; i < CHAIR_COUNT; i++ { if !ts.gameScene.Players[i].IsValid { continue } usr := ts.table.GetUserByChair(i) if usr == nil { continue } score := 0 // 比赛场,需要按玩家底分来上报分数,注意上报是本局变化的分数 if ts.table.GetOwner() == -1 { oldScore := usr.GetScore() ts.table.LogWithTableId("reportTaskAndUpdateGameScore,%d,%d,%d", oldScore, usr.GetBaseScore(), usr.GetSetCount()) // 上报完成后,修改自身分数 score = ts.gameScene.getDoublingTotalTimes() * usr.GetBaseScore() if ts.gameScene.Players[i].FinalCalcScore < ts.gameScene.Players[getNextChair(i)].FinalCalcScore { score = -score } else if ts.gameScene.Players[i].FinalCalcScore == ts.gameScene.Players[getNextChair(i)].FinalCalcScore { if !isSameTeam(i, ts.gameScene.BuyChair) { score = -score } } usr.SetScore(oldScore + score) ts.table.UpdateGameScore(usr.GetUserId(), score) ts.gameScene.Players[i].EndScore = score ts.gameScene.Players[i].MatchScore += score } else { if !ts.table.IsPrivate() && ts.gameScene.ScoreToWin == -1 { score = ts.gameScene.getDoublingTotalTimes() * ts.gameScene.Players[i].bet if !ts.gameScene.isPlayerWinInQuickGame(i) { score = -score } ts.gameScene.Players[i].EndScore = score + ts.gameScene.Players[i].bet } ts.table.UpdateGameScore(usr.GetUserId(), ts.gameScene.Players[i].getTotalScore()) } if ts.table.IsPrivate() && config.Server.IsLadderRoom > 0 { p := &ts.gameScene.Players[i] ts.gameScene.Players[i].LastWinCount = ladder.GetUserConsecutiveWinCount(p.userID, GAMEID) ts.gameScene.Players[i].LadderChangeValue, _ = ladder.AddUserLadderScore(p.userID, GAMEID, ts.roomInfo.RoomName, score) if ts.gameScene.Players[i].LadderChangeValue > 0 { ts.gameScene.Players[i].LastWinCount++ } d := ladder.GetUserLadderInfo(p.userID, GAMEID) if d != nil { ts.gameScene.Players[i].LadderInfo = *d } else { ts.table.LogWithTableId("User:%d, LadderInfo is nil", p.userID) } } if ts.gameScene.FinalClub == Suit_Sun { task.DoTaskAction(usr.GetUserId(), TaskAction_Baloot_Sun, 1, task.TaskScope{GameName: GAME_NAME}) } if ts.gameScene.Players[i].TotalChangeScore >= 80 { task.DoTaskAction(usr.GetUserId(), TaskAction_Baloot80, 1, task.TaskScope{GameName: GAME_NAME}) } if ts.gameScene.Players[i].TotalChangeScore >= 100 { task.DoTaskAction(usr.GetUserId(), TaskAction_Baloot100, 1, task.TaskScope{GameName: GAME_NAME}) } if ts.gameScene.Players[i].TotalChangeScore >= 120 { task.DoTaskAction(usr.GetUserId(), TaskAction_Baloot120, 1, task.TaskScope{GameName: GAME_NAME}) } } } func (ts *tablesink) enterEndPhase() { // 结算 winners := ts.gameScene.getWinner() ts.table.LogWithTableId("tablesink.enterEndPhase getWinners:%v", winners) if len(winners) == 0 { ts.table.LogWithTableId("tablesink.enterEndPhase getWinners = 0") if ts.table.IsPrivate() { ts.table.PrivateRoomSetWinners([]int{}) } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) return } totalBet := 0 if ts.gameScene.Players[winners[0]].bet > 0 { totalBet = ts.gameScene.getTotalBet(winners[0]) } // 平分 endScore := totalBet / len(winners) if endScore != 0 { for _, v := range winners { ts.gameScene.Players[v].EndScore = endScore } } // 写分 for i := 0; i < CHAIR_COUNT; i++ { p := &ts.gameScene.Players[i] usr := ts.table.GetUserByChair(i) if !p.IsValid || usr == nil { continue } realWin := p.EndScore - p.bet if p.bet == 0 { realWin = 0 } tax := 0 if realWin > 0 { tax = realWin * ts.roomInfo.TaxRate / 100 } wScore := p.EndScore - tax if p.bet == 0 { wScore = 0 } ts.writeScore(p.userID, wScore, tax, 0, ScoreType_End, ts.roomInfo.RoomName, p.isRobot) if wScore > 0 { userservices.DoRecord(p.userID, userservices.Record_MaxWin, wScore) } if !p.isRobot { roomType := waterpool.RoomType_Normal if ts.roomInfo.IsDoublingMode { roomType = waterpool.RoomType_Doubling } if ts.roomInfo.RoomType == 3 { roomType = waterpool.RoomType_Quick } if config.Server.IsLadderRoom > 0 { roomType = waterpool.RoomType_Ladder } if p.isControl { go waterpool.UpdataUserWaterPool(p.userID, realWin, GAME_NAME, roomType, ts.roomInfo.RoomID) } else { go waterpool.ReduceInventoryValue(GAMEID, ts.roomInfo.RoomName, realWin-tax, roomType) } } // 写记录 if ts.table.IsPrivate() { } else { if p.bet != 0 { go ts.table.WriteBetRecordWithSetcount(p.userID, p.bet, wScore, 1.0, "normal", fmt.Sprintf("totalpoits:[%v]", p.roundScores), ts.roomInfo.RoomName, len(p.roundScores)) } if config.Server.IsLadderRoom > 0 { ts.gameScene.Players[i].LastWinCount = ladder.GetUserConsecutiveWinCount(p.userID, GAMEID) ts.gameScene.Players[i].LadderChangeValue, _ = ladder.AddUserLadderScore(p.userID, GAMEID, ts.roomInfo.RoomName, wScore-p.bet) if ts.gameScene.Players[i].LadderChangeValue > 0 { ts.gameScene.Players[i].LastWinCount++ } d := ladder.GetUserLadderInfo(p.userID, GAMEID) if d != nil { ts.gameScene.Players[i].LadderInfo = *d } else { ts.table.LogWithTableId("User:%d, LadderInfo is nil", p.userID) } } } } if ts.roomInfo.RoomID == 0 && !ts.table.IsPrivate() { for i := 0; i < CHAIR_COUNT; i++ { ts.gameScene.Players[i].LevelValue = 0 if ts.gameScene.Players[i].isRobot { continue } value := ts.table.AddExperience(ts.gameScene.Players[i].userID, 100) ts.gameScene.Players[i].LevelValue = value } } ts.gameScene.Phase = Phase_End ts.table.NotifySceneChanged(-1) if ts.table.IsPrivate() { winnerUsers := make([]int, len(winners)) for i := 0; i < len(winners); i++ { winnerUsers[i] = ts.gameScene.Players[winners[i]].userID } ts.table.PrivateRoomSetWinners(winnerUsers) ts.writePrivateBetRecords() } ts.endGame() ts.uniqueId = uuid.New().String() ts.gameScene.initData(ts.table.GetTableID(), ts.roomInfo.ScoreToWin, ts.roomInfo.MaxSurrenderCount) } func (ts *tablesink) writePrivateBetRecords() { for i := 0; i < CHAIR_COUNT; i++ { p := &ts.gameScene.Players[i] usr := ts.table.GetUserByChair(i) if !p.IsValid || usr == nil { continue } fee, prize := ts.table.PrivateRoomGetFeeAndPrize(p.userID) ts.table.LogWithTableId("writePrivateBetRecords user[%d] fee[%d],prize[%d]", p.userID, fee, prize) ts.table.WriteBetRecordWithSetcount(p.userID, fee, prize, 1.0, ts.roomType, fmt.Sprintf("totalpoits:%v", p.roundScores), ts.roomInfo.RoomName, len(p.roundScores)) } } func (ts *tablesink) endGame() { ts.table.LogWithTableId("--------endGame-----------") ts.gameScene.dump(true) ts.statisticsScoreHistoryToExcel() ts.statisRobotChatCount() ts.gameScene.PhaseIndex = 0 ts.gameScene.GameIndex = 0 ts.gameScene.Index = 0 ts.surrenderInfo.isSurrendering = false ts.table.KillAllTimer() if ts.table.IsPrivate() { ts.table.SetTimer(TIMER_DELAY_END, 1000) // 私人场,不继续下一把 if ts.roomInfo.BaseScore == 0 { ts.gameScene.pointsToEndScore() } return } ts.table.EndGame() //realUserCount := 0 robotCount := 0 for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } // 机器人,随机3-5秒准备 if !usr.IsRobot() { ts.table.SetTimer(TIMER_READY_0+i, SEC_READY) ts.gameScene.Players[i].isEndToStart = true //realUserCount++ } else { //ts.table.SetTimer(TIMER_READY_0+i, 100) robotCount++ } } if robotCount > 0 { ts.table.SetTimer(TIMER_REMOVE_ROBOT, 1000+rand.Intn(1000)) } } func (ts *tablesink) writeScore(userId int, amount, tax int, status, scoreType int, sourceName string, isRobot bool) (bool, int) { return ts.table.WriteUserMoney(userId, amount, tax, status, scoreType, sourceName) } func (ts *tablesink) checkIsNeedRobot() { if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End { fmt.Print("游戏阶段=====ts.gameScene.Phase",ts.gameScene.Phase) return } if ts.roomInfo.RobotCount == 0 { fmt.Print("游戏阶段=====ts.roomInfo.RobotCount",ts.roomInfo.RobotCount) return } if ts.table.IsPrivate() { fmt.Print("游戏阶段=====ts.table.IsPrivate") return } count := 0 playerCount := 0 for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } if !usr.IsRobot() { playerCount++ } if usr.GetUserStatus() <= user.UserStatus_Free { continue } count++ } chairCount := CHAIR_COUNT if count >= chairCount { fmt.Print("游戏阶段=====玩家数大于椅子数=====") return } robotCount := chairCount - count ts.table.LogWithTableId("-------checkIsNeedRobot. need: %d ------", robotCount) if robotCount > 0 && playerCount > 0 { sec := ts.roomInfo.RobotEnterRoomTime*1000 - 500 time.AfterFunc(time.Duration(sec)*time.Millisecond, func() { if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End { fmt.Print("游戏阶段=====ts.gameScene.Phase",ts.gameScene.Phase) return } tableID := ts.table.GetTableID() robotmanager.GetOneRobotEnterTable(tableID, ts.roomInfo.MinGold, ts.roomInfo.MaxGold) }) } } func (ts *tablesink) removeOneRobot() { if ts.gameScene.Phase != Phase_End && ts.gameScene.Phase != Phase_Free { return } for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil { continue } if !usr.IsRobot() { continue } ts.table.KickUserByChair(i, false) //return } } func (ts *tablesink) getRobotActionTime(chairId int, isNeedDiminishing bool) int { if ts.gameScene.Phase == Phase_FirstBuy || ts.gameScene.Phase == Phase_SecondBuy { return 1000 + rand.Intn(2000) } if !isNeedDiminishing { diff := ts.roomInfo.MaxRobotNotFirstOutMS - ts.roomInfo.MinRobotNotFirstOutMS if diff <= 0 { diff = MS_MaxRobotNotFirstOut - MS_MinRobotNotFirstOut } minMs := ts.roomInfo.MinRobotNotFirstOutMS if minMs < MIN_OUT_MS { minMs = MS_MinRobotNotFirstOut } return minMs + rand.Intn(diff) } ms := 0 if ts.gameScene.getRoundOutedPlayerCount() > 0 { diff := ts.roomInfo.MaxRobotNotFirstOutMS - ts.roomInfo.MinRobotNotFirstOutMS if diff <= 0 { diff = MS_MaxRobotNotFirstOut - MS_MinRobotNotFirstOut } minMs := ts.roomInfo.MinRobotNotFirstOutMS if minMs <= MIN_OUT_MS { minMs = MS_MinRobotNotFirstOut } leftCardCount := NORMAL_HOLD_CARD - len(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards) deMs := ts.roomInfo.DiminishingNotFirstOutMS * leftCardCount if deMs <= 0 || (minMs-deMs) < MIN_OUT_MS { deMs = MS_DiminishingNotFirstOut * leftCardCount } ms = minMs + rand.Intn(diff) - deMs } else { diff := ts.roomInfo.MaxRobotFirstOutMS - ts.roomInfo.MinRobotFirstOutMS if diff <= 0 { diff = MS_MaxRobotFirstOut - MS_MinRobotFirstOut } minMs := ts.roomInfo.MinRobotFirstOutMS if minMs < MIN_OUT_MS { minMs = MS_MinRobotFirstOut } leftCardCount := NORMAL_HOLD_CARD - len(ts.gameScene.Players[ts.gameScene.WhoseTurn].HandCards) deMs := ts.roomInfo.DiminishingFirstOutMS * leftCardCount if deMs <= 0 || (minMs-deMs) < MIN_OUT_MS { deMs = MS_DiminishingFirstOut * leftCardCount } ms = minMs + rand.Intn(diff) - deMs } return ms } func (ts *tablesink) checkRobotAction() { if ts.gameScene.Phase == Phase_GameEnd || ts.gameScene.Phase == Phase_Start { return } if ts.gameScene.Phase == Phase_Reshuffle { for i := 0; i < CHAIR_COUNT; i++ { usr := ts.table.GetUserByChair(i) if usr == nil || !usr.IsRobot() { continue } if ts.gameScene.Players[i].canReshuffle() { ms := rand.Intn(500) + 500 ts.table.SetTimer(TIMER_ROBOT_ACTION_0+i, ms) break } } return } if ts.gameScene.WhoseTurn != -1 { usr := ts.table.GetUserByChair(ts.gameScene.WhoseTurn) if usr == nil || !usr.IsRobot() { return } ms := ts.getRobotActionTime(ts.gameScene.WhoseTurn, ts.gameScene.Phase == Phase_Play) ts.table.SetTimer(TIMER_ROBOT_ACTION_0+ts.gameScene.WhoseTurn, ms) } else { start := rand.Intn(CHAIR_COUNT) ms := 0 for i := 0; i < CHAIR_COUNT; i++ { iChair := (i + start) % CHAIR_COUNT usr := ts.table.GetUserByChair(iChair) if usr == nil || !usr.IsRobot() || !ts.gameScene.Players[iChair].canAction() { continue } if ms == 0 { ms = ts.getRobotActionTime(iChair, false) } else { ms += 1000 } ts.table.SetTimer(TIMER_ROBOT_ACTION_0+iChair, ms) } } } func (ts *tablesink) onTimerAutoAction(chairId int) { if ts.gameScene.WhoseTurn != -1 && ts.gameScene.Phase != Phase_Reshuffle { ts.table.LogWithTableId("tablesink.onTimerAutoAction WhoseTurn != -1") return } if !ts.gameScene.Players[chairId].AutoOut { ts.table.LogWithTableId("tablesink.onTimerAutoAction not Auto") return } if ts.gameScene.Phase != Phase_FirstBuy && ts.gameScene.Phase != Phase_SecondBuy && ts.gameScene.Phase != Phase_Double && ts.gameScene.Phase != Phase_Reshuffle { ts.table.LogWithTableId("tablesink.onTimerAutoAction not Correct Phase") return } if ts.gameScene.Phase == Phase_Reshuffle { ts.dealReshuffle(chairId, Reshuffle_Pass) return } action := ts.gameScene.Players[chairId].getAutoBuyAction() if action != Action_Buy_Invaild && ts.gameScene.Players[chairId].canAction() { if ts.gameScene.Phase == Phase_Double { ts.dealDouble(chairId, action) } if ts.gameScene.Phase == Phase_FirstBuy || ts.gameScene.Phase == Phase_SecondBuy { ts.dealBuy(chairId, action) } } } func (ts *tablesink) onTimerRobotAction(chairId int) { if ts.gameScene.WhoseTurn != -1 { if ts.gameScene.WhoseTurn != chairId && ts.gameScene.Phase != Phase_Reshuffle { ts.table.LogWithTableId("tablesink.robotAction whoseturn changed") return } } else { if !ts.gameScene.Players[chairId].canAction() { ts.table.LogWithTableId("tablesink.robotAction canAction changed") return } } if ts.gameScene.Phase == Phase_Play { if len(ts.gameScene.Players[chairId].HandCards) == 0 { ts.table.LogWithTableId("tablesink.robotAction chair:%d HandCard == 0", chairId) return } var projects []int if ts.gameScene.RoundIndex == 1 { cardList := make([]int, len(ts.gameScene.Players[chairId].HandCards)) copy(cardList, ts.gameScene.Players[chairId].HandCards) projects = ts.logic.getBestProject(cardList) } if ts.gameScene.Players[chairId].CanGawah { ts.dealGawah() return } outCard := CARD_COUNT isQuickOrLadder := false if ts.roomInfo.RoomType == 3 || config.Server.IsLadderRoom == 1 { isQuickOrLadder = true } outCard = ts.simulator.getBestOutCard(chairId, isQuickOrLadder) if !isValidCard(outCard) { if len(ts.gameScene.Players[chairId].BigCards) > 0 { outCard = ts.gameScene.Players[chairId].BigCards[0] } else { outCard = ts.logic.worstCard(ts.gameScene.Players[chairId].HandCards, ts.gameScene.RoundType) } } ts.dealOutCard(outCard, projects) } else { action := ts.getAction(chairId) switch ts.gameScene.Phase { case Phase_FirstBuy: fallthrough case Phase_SecondBuy: if ts.roomInfo.RoomID == 0 && !ts.table.IsPrivate() { action = Action_Buy_Pass if ts.allPassCount >= 2 { action = Action_Buy_Sun } } ts.dealBuy(chairId, action) case Phase_ChooseTrump: ts.dealChooseTrump(action) case Phase_CloseOpen: ts.dealCloseOrOpen(action) case Phase_Double: ts.dealDouble(chairId, action) case Phase_Reshuffle: if ts.gameScene.Players[chairId].ReshuffleAction == Reshuffle_None && ts.gameScene.Players[chairId].CanDoReshuffle { ts.dealReshuffle(chairId, Reshuffle_Confirm) } default: ts.table.LogWithTableId("tablesink.robotAction invalid phase %d", ts.gameScene.Phase) } } } func (ts *tablesink) getAction(chairId int) int { buyWinScore := 0 action, optionButtons := ts.getInitActionAndOperationList(chairId) // 不抢自己队友的买牌 if isValidChair(ts.gameScene.BuyChair) && isFriendChair(ts.gameScene.BuyChair, chairId) && (ts.gameScene.Phase == Phase_FirstBuy || ts.gameScene.Phase == Phase_SecondBuy) { return action } // 做模拟场景分析,并获取最大赢分的操作 for i := 0; i < len(optionButtons); i++ { // 过滤掉Action_Buy_Pass的解析 if optionButtons[i] == Action_Buy_Pass && ts.gameScene.Phase != Phase_ChooseTrump && ts.gameScene.Phase != Phase_CloseOpen { continue } trumpType := CardType_Invalid suit := Suit_Hokum baseTimes := 1 isBo1 := false isClose := false score := -1000 switch ts.gameScene.Phase { case Phase_FirstBuy: if optionButtons[i] == Action_Buy_Hokum || optionButtons[i] == Action_Buy_ConfirmHokum { trumpType = getCardType(ts.gameScene.PublicCard) if ts.gameScene.Players[chairId].robotType != RobotType_Cautious && optionButtons[i] == Action_Buy_Hokum { sortedCards := sortCards(ts.gameScene.Players[chairId].HandCards) if len(sortedCards[trumpType]) >= 2 { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { ts.gameScene.Players[chairId].robotActionType = ts.gameScene.Players[chairId].robotType return Action_Buy_Hokum } else { return action } } } if ts.gameScene.Players[chairId].robotType != RobotType_Cautious && optionButtons[i] == Action_Buy_ConfirmHokum && ts.gameScene.Players[chairId].robotActionType != RobotType_Cautious { return Action_Buy_ConfirmHokum } } else if optionButtons[i] == Action_Buy_Ashkal { if ts.gameScene.Players[chairId].robotType != RobotType_Cautious { totalPoint := 0 for j := 0; j < len(ts.gameScene.Players[chairId].HandCards); j++ { totalPoint += getCardPoint(ts.gameScene.Players[chairId].HandCards[j], CardType_Invalid) } if totalPoint >= 25 { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { return Action_Buy_Ashkal } else { return action } } } suit = Suit_Ashkal } else { if ts.gameScene.Players[chairId].robotType != RobotType_Cautious { totalPoint := 0 for j := 0; j < len(ts.gameScene.Players[chairId].HandCards); j++ { totalPoint += getCardPoint(ts.gameScene.Players[chairId].HandCards[j], CardType_Invalid) } if totalPoint >= 22 && getCardPoint(ts.gameScene.PublicCard, CardType_Invalid) >= 3 { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { return optionButtons[i] } else { return action } } } suit = Suit_Sun } case Phase_SecondBuy: if optionButtons[i] == Action_Buy_Hokum || optionButtons[i] == Action_Buy_ConfirmHokum { if ts.gameScene.Players[chairId].robotType != RobotType_Cautious && optionButtons[i] == Action_Buy_Hokum { t := getLAGsBestTrumpType(ts.gameScene.Players[chairId].HandCards, ts.gameScene.PublicCard) if t != CardType_Invalid { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { ts.gameScene.Players[chairId].robotActionType = ts.gameScene.Players[chairId].robotType return Action_Buy_Hokum } else { return action } } } if ts.gameScene.Players[chairId].robotType != RobotType_Cautious && optionButtons[i] == Action_Buy_ConfirmHokum && ts.gameScene.Players[chairId].robotActionType != RobotType_Cautious { return Action_Buy_ConfirmHokum } for n := CardType_Diamond; n <= CardType_Spade; n++ { if n == getCardType(ts.gameScene.PublicCard) { continue } maxScore := ts.getSimulatorSceneBuyWinScore(n, suit, chairId, baseTimes, isBo1, isClose) if maxScore > score { score = maxScore trumpType = n } } } else if optionButtons[i] == Action_Buy_Ashkal { if ts.gameScene.Players[chairId].robotType != RobotType_Cautious { totalPoint := 0 for j := 0; j < len(ts.gameScene.Players[chairId].HandCards); j++ { totalPoint += getCardPoint(ts.gameScene.Players[chairId].HandCards[j], CardType_Invalid) } if totalPoint >= 25 { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { return optionButtons[i] } else { return action } } } suit = Suit_Ashkal } else { if ts.gameScene.Players[chairId].robotType != RobotType_Cautious { totalPoint := 0 for j := 0; j < len(ts.gameScene.Players[chairId].HandCards); j++ { totalPoint += getCardPoint(ts.gameScene.Players[chairId].HandCards[j], CardType_Invalid) } if totalPoint >= 22 && getCardPoint(ts.gameScene.PublicCard, CardType_Invalid) >= 3 { r := rand.Intn(100) if r < ts.gameScene.Players[chairId].robotType*50 { return optionButtons[i] } else { return action } } } suit = Suit_Sun } case Phase_Double: trumpType = ts.gameScene.TrumpType suit = ts.gameScene.FinalClub baseTimes = optionButtons[i] - Action_Buy_Double + 2 if optionButtons[i] == Action_Buy_Coffee { baseTimes = 1 isBo1 = true } case Phase_CloseOpen: isClose = (optionButtons[i] == Action_Buy_Close) trumpType = ts.gameScene.TrumpType case Phase_ChooseTrump: if ts.gameScene.Players[chairId].robotType != RobotType_Cautious && ts.gameScene.Players[chairId].robotActionType != RobotType_Cautious { return getLAGsBestTrumpType(ts.gameScene.Players[chairId].HandCards, ts.gameScene.PublicCard) } trumpType = optionButtons[i] } score = ts.getSimulatorSceneBuyWinScore(trumpType, suit, chairId, baseTimes, isBo1, isClose) if baseTimes >= 2 { score = score / baseTimes } if score >= buyWinScore { buyWinScore = score if ts.gameScene.Phase == Phase_FirstBuy || ts.gameScene.Phase == Phase_SecondBuy { // 这里是避免机器人买牌过于频繁 if buyWinScore >= 10 { action = optionButtons[i] } } else if ts.gameScene.Phase == Phase_Double { if buyWinScore >= 30 { action = optionButtons[i] } } else { action = optionButtons[i] } } } return action } func (ts *tablesink) getInitActionAndOperationList(chairId int) (int, []int) { optionButtons := []int{} action := 0 switch ts.gameScene.Phase { case Phase_FirstBuy: fallthrough case Phase_SecondBuy: action = Action_Buy_Pass optionButtons = ts.gameScene.Players[chairId].CanAction if !ts.gameScene.Players[chairId].canDoBuyAction(Action_Buy_Pass) { action = ts.gameScene.Players[chairId].getAutoBuyAction() } case Phase_Double: optionButtons = ts.gameScene.Players[chairId].CanAction action = Action_Buy_Pass case Phase_CloseOpen: optionButtons = append(optionButtons, Action_Buy_Close, Action_Buy_Open) action = Action_Buy_Open case Phase_ChooseTrump: for i := CardType_Diamond; i <= CardType_Spade; i++ { if i == getCardType(ts.gameScene.PublicCard) { continue } optionButtons = append(optionButtons, i) } cardList := make([]int, len(ts.gameScene.Players[chairId].HandCards)) copy(cardList, ts.gameScene.Players[chairId].HandCards) cardList = append(cardList, ts.gameScene.PublicCard) currentCardIndex := ts.logic.currentCardIndex for j := 0; j < CHAIR_COUNT; j++ { if j == chairId { leftCards := ts.logic.getLeftCards(true) cardList = append(cardList, leftCards...) break } ts.logic.currentCardIndex += SURPLUS_SEND_CARD } ts.logic.currentCardIndex = currentCardIndex action = getBestTrumpType(cardList, ts.gameScene.PublicCard) } return action, optionButtons } func (ts *tablesink) getSimulatorSceneBuyWinScore(trumpType, suit, chairId, baseTimes int, isBo1, isClose bool) int { getPublicCardChairId := chairId if suit == Suit_Ashkal { getPublicCardChairId = getFriendChair(chairId) } ss := newSimulatorScene() ss.initData(trumpType, suit, chairId, baseTimes, isBo1, isClose) currentCardIndex := ts.logic.currentCardIndex for j := 0; j < CHAIR_COUNT; j++ { leftCards := ts.logic.getLeftCards(getPublicCardChairId == j) ss.fakers[j].handCards = append(ss.fakers[j].handCards, ts.gameScene.Players[j].HandCards...) ss.fakers[j].handCards = append(ss.fakers[j].handCards, leftCards...) if getPublicCardChairId == j { ss.fakers[j].handCards = append(ss.fakers[j].handCards, ts.gameScene.PublicCard) } f := &ss.fakers[j] sort.Slice(f.handCards, func(m, n int) bool { return getCardSortValue(f.handCards[m], trumpType) < getCardSortValue(f.handCards[n], trumpType) }) } ts.logic.currentCardIndex = currentCardIndex isQuickOrLadder := false if ts.roomInfo.RoomType == 3 || config.Server.IsLadderRoom == 1 { isQuickOrLadder = true } ss.startSimulatorScene(getPreviousChair(ts.gameScene.Banker), isQuickOrLadder) buyWinScore := ss.buyWinScore if ss.suit != Suit_Hokum { buyWinScore /= 2 } if !ss.isWinScoreBigThanFriend() { buyWinScore = -1 } return buyWinScore } func (ts *tablesink) updateRobotChatAction(action int) { bFound := false for i := 0; i < len(ts.gameScene.robotChatList); i++ { if ts.gameScene.robotChatList[i].chatType == action { bFound = true ts.gameScene.robotChatList[i].count++ } } if !bFound { ts.gameScene.robotChatList = append(ts.gameScene.robotChatList, robotChatAction{count: 1, chatType: action}) } } func (ts *tablesink) sendRobotChatAction(action, sender, receiver, delaySec int) { if ts.gameScene.robotSendChatCount >= ts.gameScene.robotSendChatMaxCount { return } if ts.roomInfo.ScoreToWin == 1 && ts.gameScene.robotSendChatCount >= (ts.gameScene.RoundIndex/4+1)*2 { return } if ts.roomInfo.ScoreToWin != 1 && ts.gameScene.robotSendChatCount >= ts.gameScene.GameIndex+1 { return } if ts.roomInfo.ScoreToWin != 1 { if ts.gameScene.isSendChat { return } ts.gameScene.isSendChat = true } usr := ts.table.GetUserByUserId(sender) if usr == nil { return } if !usr.IsRobot() { return } timer := time.NewTimer(time.Duration(delaySec) * time.Millisecond) go func() { <-timer.C ts.updateRobotChatAction(action) userIndex := usr.GetUserIndex() var data string switch action { case RobotChatAction_MuiltyTimeOut: data = fmt.Sprintf("1/7/%d", sender) case RobotChatAction_LoseOverFive: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/5/%d", sender) } else { data = fmt.Sprintf("2/7/%d", sender) } case RobotChatAction_LoseOverEighty: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/5/%d", sender) } else { data = fmt.Sprintf("2/8/%d", sender) } case RobotChatAction_FriendGetBigProject: r := rand.Intn(90) if r < 30 { chatIds := []int{1, 6, 7} chatId := chatIds[rand.Intn(3)] data = fmt.Sprintf("3/%d/%d/%d", chatId, sender, receiver) } else if r < 60 && r >= 30 { data = fmt.Sprintf("1/9/%d", sender) } else { chatIds := []int{0, 3, 15} chatId := chatIds[rand.Intn(3)] data = fmt.Sprintf("2/%d/%d", chatId, usr.GetUserId()) } case RobotChatAction_SelfWinOverFive: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/8/%d", sender) } else { chatIds := []int{16, 17} chatId := chatIds[rand.Intn(2)] data = fmt.Sprintf("2/%d/%d", chatId, sender) } case RobotChatAction_GoodHandCard: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/9/%d", sender) } else { chatIds := []int{0, 16, 12, 6} chatId := chatIds[rand.Intn(4)] data = fmt.Sprintf("2/%d/%d", chatId, sender) } case RobotChatAction_BadHandCard: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/5/%d", sender) } else { chatIds := []int{1, 5, 8} chatId := chatIds[rand.Intn(3)] data = fmt.Sprintf("2/%d/%d", chatId, sender) } case RobotChatAction_EmenyGetBigProject: r := rand.Intn(100) if r < 50 { data = fmt.Sprintf("1/5/%d", sender) } else { chatIds := []int{8, 14, 2} chatId := chatIds[rand.Intn(3)] data = fmt.Sprintf("2/%d/%d", chatId, sender) } case RobotChatAction_EnterRoom: data = fmt.Sprintf("1/0/%d", sender) case RobotChatAction_Reply: data = fmt.Sprintf("1/1/%d", sender) case RobotChatAction_FriendWinOverFive: data = fmt.Sprintf("1/2/%d", sender) } ts.recvChatMsg(userIndex, data) ts.gameScene.robotSendChatCount++ timer.Stop() }() }