tablesink_impl.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. package gamelogic
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "math"
  6. "math/rand"
  7. "time"
  8. coreservice "bet24.com/servers/coreservice/client"
  9. "bet24.com/servers/games/blitzludo/config"
  10. robotmanager "bet24.com/servers/insecureframe/robot"
  11. "bet24.com/servers/user"
  12. )
  13. func (ts *tablesink) checkAndStartGame() {
  14. if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End {
  15. return
  16. }
  17. // 查看是否所有人都ready
  18. readyCount := 0
  19. for i := 0; i < CHAIR_COUNT; i++ {
  20. usr := ts.table.GetUserByChair(i)
  21. if usr == nil {
  22. continue
  23. }
  24. userStatus := usr.GetUserStatus()
  25. // 旁观不判断
  26. if userStatus <= user.UserStatus_Free ||
  27. userStatus == user.UserStatus_Watch {
  28. continue
  29. }
  30. // 如果是开始
  31. if userStatus == user.UserStatus_Play {
  32. ts.table.LogWithTableId("-----checkAndStartGame: chair[%d]: %d, playing kicked", i, usr.GetUserId())
  33. //ts.table.KickUserByChair(i, true)
  34. continue
  35. }
  36. if userStatus != user.UserStatus_Ready {
  37. ts.table.LogWithTableId("-----checkAndStartGame: chair[%d]: %d, status: %d", i, usr.GetUserId(), usr.GetUserStatus())
  38. return
  39. }
  40. readyCount++
  41. }
  42. // ts.table.LogWithTableId("-----checkAndStartGame : %d ", readyCount)
  43. if ts.IsDual() && readyCount == 2 || readyCount == 4 {
  44. ts.startGame()
  45. }
  46. }
  47. func (ts *tablesink) checkIsNeedRobot() {
  48. if !robotmanager.IsRunning() {
  49. return
  50. }
  51. if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End {
  52. return
  53. }
  54. count := 0
  55. for i := 0; i < CHAIR_COUNT; i++ {
  56. usr := ts.table.GetUserByChair(i)
  57. if usr == nil {
  58. continue
  59. }
  60. /*if usr.IsRobot() {
  61. continue
  62. }*/
  63. if usr.GetUserStatus() <= user.UserStatus_Free {
  64. continue
  65. }
  66. count++
  67. }
  68. chairCount := CHAIR_COUNT
  69. if ts.IsDual() {
  70. chairCount = 2
  71. }
  72. if count >= chairCount {
  73. return
  74. }
  75. robotCount := 1
  76. if !ts.IsDual() {
  77. robotCount = rand.Intn(chairCount-count) + 1
  78. }
  79. ts.table.LogWithTableId("-------checkIsNeedRobot. need: %d ------", robotCount)
  80. for i := 0; i < robotCount; i++ {
  81. sec := rand.Intn(robotCount*3*1000) + 1000
  82. time.AfterFunc(time.Duration(sec)*time.Millisecond, func() {
  83. if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End {
  84. return
  85. }
  86. tableID := ts.table.GetTableID()
  87. robotmanager.GetOneRobotEnterTable(tableID, ts.roomInfo.MinGold, ts.roomInfo.MaxGold)
  88. })
  89. }
  90. }
  91. func (ts *tablesink) removeOneRobot() {
  92. if ts.gameScene.Phase != Phase_End {
  93. return
  94. }
  95. for i := 0; i < CHAIR_COUNT; i++ {
  96. usr := ts.table.GetUserByChair(i)
  97. if usr == nil {
  98. continue
  99. }
  100. if !usr.IsRobot() {
  101. continue
  102. }
  103. ts.table.KickUserByChair(i, false)
  104. return
  105. }
  106. }
  107. func (ts *tablesink) checkUserReadyStatus(chair int) {
  108. if ts.gameScene.Phase != Phase_Free && ts.gameScene.Phase != Phase_End {
  109. return
  110. }
  111. usr := ts.table.GetUserByChair(chair)
  112. if usr == nil {
  113. return
  114. }
  115. if !usr.IsRobot() {
  116. ts.table.LogWithTableId("1111111------checkUserReadyStatus: %d %d", chair, usr.GetUserId())
  117. //超时没有准备则踢出
  118. ts.table.KickUserByChair(chair, true)
  119. return
  120. }
  121. //桌上还有没有真人,有就准备,没有就踢走
  122. count := 0
  123. for i := 0; i < CHAIR_COUNT; i++ {
  124. u := ts.table.GetUserByChair(i)
  125. if u == nil {
  126. continue
  127. }
  128. if u.IsRobot() {
  129. continue
  130. }
  131. if u.GetUserStatus() <= user.UserStatus_Free {
  132. continue
  133. }
  134. count++
  135. }
  136. if count >= 1 {
  137. ts.table.LogWithTableId("tablesink.checkUserReadyStatus: %d robot to ready ", usr.GetUserId())
  138. ts.table.SetUserReadyStatus(usr.GetUserIndex(), true)
  139. } else {
  140. ts.table.LogWithTableId("------checkUserReadyStatus: %d ", usr.GetUserId())
  141. //超时没有准备则踢出
  142. ts.table.KickUser(usr.GetUserIndex(), !usr.IsRobot())
  143. // if usr.IsRobot() {
  144. // //ts.table.UserWatch(usr.GetUserIndex())
  145. // ts.table.KickUser(usr.GetUserIndex(), false)
  146. // } else {
  147. // ts.table.UserWatch(usr.GetUserIndex())
  148. // }
  149. }
  150. }
  151. func (ts *tablesink) checkUserQuitStatus(chair int) {
  152. if ts.gameScene.Phase != Phase_End {
  153. return
  154. }
  155. usr := ts.table.GetUserByChair(chair)
  156. if usr == nil {
  157. return
  158. }
  159. if usr.IsRobot() {
  160. ts.table.KillTimer(TIMER_QUIT_0 + chair)
  161. ts.table.KickUser(usr.GetUserIndex(), !usr.IsRobot())
  162. return
  163. }
  164. }
  165. // 匹配阶段超时
  166. func (ts *tablesink) dealMatchTimeOut() {
  167. ts.gameScene.Phase = Phase_Move
  168. ts.LastOpraTime = time.Now()
  169. ts.gameScene.Index++
  170. ts.table.NotifySceneChanged(-1)
  171. ts.resetGameTimer(false, false, 0)
  172. }
  173. // 移动棋子或掷骰子 阶段超时
  174. func (ts *tablesink) dealPlayTimeOut() {
  175. if ts.gameScene.Phase != Phase_Dice && ts.gameScene.Phase != Phase_Move {
  176. return
  177. }
  178. if isValidChair(ts.gameScene.WhoseTurn) {
  179. ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut = true
  180. }
  181. ts.dealGamePlay()
  182. }
  183. // 处理游戏阶段逻辑 包含掷骰子和移动飞机
  184. func (ts *tablesink) dealGamePlay() {
  185. ts.LastOpraTime = time.Now()
  186. chairId := ts.gameScene.WhoseTurn
  187. if !isValidChair(chairId) {
  188. ts.table.LogWithTableId("tablesink.dealGamePlay whoseturn invalid %d", chairId)
  189. return
  190. }
  191. player := ts.gameScene.Players[chairId]
  192. // 掷骰子
  193. if player.LastAction == Action_Move {
  194. ts.doAction(chairId, Action_Dice, 0, nil)
  195. } else {
  196. ts.doAction(chairId, Action_Move, -1, nil)
  197. }
  198. }
  199. func (ts *tablesink) onRobotChatTimer() {
  200. start := rand.Intn(4)
  201. for i := 0; i < CHAIR_COUNT; i++ {
  202. chair := (start + i) % CHAIR_COUNT
  203. usr := ts.table.GetUserByChair(chair)
  204. if usr == nil || !usr.IsRobot() {
  205. continue
  206. }
  207. whoseturn := ts.table.GetUserByChair(ts.gameScene.WhoseTurn)
  208. if whoseturn == nil {
  209. return
  210. }
  211. ts.sendBatChat(usr.GetUserIndex(), usr.GetUserId(), whoseturn.GetUserId())
  212. return
  213. }
  214. }
  215. func (ts *tablesink) sendBatChat(userIndex int32, sender, receiver int) {
  216. if sender == receiver {
  217. return
  218. }
  219. if rand.Intn(100) >= 35 {
  220. return
  221. }
  222. chatIds := []int{0, 4, 10, 11}
  223. chatId := chatIds[rand.Intn(4)]
  224. data := fmt.Sprintf("3/%d/%d/%d", chatId, sender, receiver)
  225. ts.table.LogWithTableId("tablesink.sendBatChat ,%s", data)
  226. ts.recvChatMsg(userIndex, data)
  227. }
  228. // 检查机器人操作
  229. func (ts *tablesink) checkRobotAction(isCanMove, isNextChair bool, moveDuration int) {
  230. chairId := ts.gameScene.WhoseTurn
  231. usr := ts.table.GetUserByChair(chairId)
  232. if usr == nil {
  233. return
  234. }
  235. if !usr.IsRobot() {
  236. return
  237. }
  238. //机器人也要考虑延迟问题 否则会导致前端收消息过快
  239. delay := SEC_DELAY //掷骰子时间 机器人用等待时间
  240. switch ts.gameScene.Phase {
  241. case Phase_Dice:
  242. //如果没有可以移动的则直接进入掷骰子阶段的定时间
  243. if isNextChair && !isCanMove {
  244. //没有移动直接换人 加延迟
  245. delay += SEC_NEXT_CHAIR
  246. } else if isCanMove {
  247. //移动棋子时间
  248. delay -= SEC_NEXT_CHAIR
  249. //机器人要判断是否只有一个棋子可以移动
  250. count := ts.gameScene.Players[chairId].canMovePlaneCountByPosition()
  251. if count != 1 {
  252. //有多个棋子时增加选择时间
  253. delay += rand.Intn(2500)
  254. }
  255. }
  256. case Phase_Move:
  257. //前一个有移动的话 要算上移动的等待时间
  258. if isCanMove {
  259. delay += moveDuration
  260. }
  261. }
  262. time.AfterFunc(time.Duration(delay)*time.Millisecond, func() {
  263. ts.dealGamePlay()
  264. })
  265. }
  266. func (ts *tablesink) startGame() {
  267. // 创建场景
  268. tableId := ts.table.GetTableID()
  269. ts.gameScene.initData(tableId)
  270. ts.gameScene.base = ts.roomInfo.BaseScore
  271. validChairs := []int{}
  272. for i := 0; i < CHAIR_COUNT; i++ {
  273. usr := ts.table.GetUserByChair(i)
  274. if usr == nil {
  275. continue
  276. }
  277. userStatus := usr.GetUserStatus()
  278. if userStatus != user.UserStatus_Ready {
  279. ts.table.LogWithTableId("ts.startGame chair[%d] userStatue = %d", i, userStatus)
  280. return
  281. }
  282. userGold := ts.table.GetUserChipOrGoldByUser(usr)
  283. if userGold < ts.roomInfo.MinGold || (ts.roomInfo.MaxGold > 0 && userGold > ts.roomInfo.MaxGold) {
  284. ts.table.LogWithTableId("tablesink.startGame %d 金币不足,站起", usr.GetUserId())
  285. if usr.IsRobot() {
  286. go ts.table.KickUser(usr.GetUserIndex(), false)
  287. } else {
  288. go ts.table.UserWatch(usr.GetUserIndex())
  289. }
  290. break
  291. }
  292. // 先投注
  293. ok := ts.writeScore(usr.GetUserId(), -ts.roomInfo.BaseScore, 0, 0, ScoreType_Bet, ts.roomInfo.RoomName, usr.IsRobot())
  294. if !ok {
  295. break
  296. }
  297. ts.gameScene.Players[i].initData(i, usr.GetUserId(), userGold)
  298. ts.gameScene.Players[i].IsValid = true
  299. ts.gameScene.Players[i].userId = usr.GetUserId()
  300. ts.gameScene.Players[i].bet = ts.roomInfo.BaseScore
  301. ts.gameScene.Players[i].isRobot = usr.IsRobot()
  302. ts.gameScene.pool += ts.roomInfo.BaseScore
  303. validChairs = append(validChairs, i)
  304. }
  305. readyCount := len(validChairs)
  306. if ts.IsDual() && readyCount != 2 || !ts.IsDual() && readyCount < CHAIR_COUNT {
  307. ts.table.LogWithTableId("游戏开始,用户不足")
  308. ts.gameScene.Phase = Phase_End
  309. // 分数还原
  310. for i := 0; i < CHAIR_COUNT; i++ {
  311. if !ts.gameScene.Players[i].IsValid {
  312. continue
  313. }
  314. if ts.gameScene.Players[i].bet > 0 {
  315. ts.writeScore(ts.gameScene.Players[i].userId, ts.gameScene.Players[i].bet, 0, 0,
  316. ScoreType_Return, ts.roomInfo.RoomName, ts.gameScene.Players[i].isRobot)
  317. }
  318. }
  319. ts.endGame()
  320. ts.gameScene.initData(tableId)
  321. return
  322. }
  323. ts.gameScene.gameInit(validChairs[rand.Intn(len(validChairs))])
  324. ts.sendMatchEnd()
  325. ts.gameScene.dump(false)
  326. ts.table.StartGame()
  327. }
  328. // 发送匹配结束
  329. func (ts *tablesink) sendMatchEnd() {
  330. //发送匹配结束
  331. ts.gameScene.Phase = Phase_Match
  332. ts.table.SetTimer(TIMER_MATCH, SEC_MATCH)
  333. ts.table.KillTimer(TIMER_ROBOT)
  334. }
  335. func (ts *tablesink) endGame() {
  336. ts.table.LogWithTableId("--------endGame-----------")
  337. ts.gameScene.dump(true)
  338. // 先调用框架的EngGame,保证旁观用户状态改变
  339. ts.table.EndGame()
  340. //realUserCount := 0
  341. //robotCount := 0
  342. readyCount := 0
  343. for i := 0; i < CHAIR_COUNT; i++ {
  344. usr := ts.table.GetUserByChair(i)
  345. if usr == nil {
  346. continue
  347. }
  348. if usr.GetUserStatus() == user.UserStatus_Watch {
  349. continue
  350. }
  351. if !usr.IsRobot() {
  352. ts.table.SetTimer(TIMER_READY_0+i, SEC_READY)
  353. readyCount++
  354. } else {
  355. //机器人随机 1-2秒退出房间
  356. ts.table.SetTimer(TIMER_QUIT_0+i, 1000+rand.Intn(1000))
  357. }
  358. }
  359. // if ts.IsDual() && readyCount == 2 || readyCount == 4 {
  360. // //ts.removeOneRobot()
  361. // ts.table.SetTimer(TIMER_REMOVE_ROBOT, 5000+rand.Intn(3000))
  362. // }
  363. }
  364. // 聊天
  365. func (ts *tablesink) recvChatMsg(userIndex int32, data string) {
  366. usr := ts.table.GetPlayer(userIndex)
  367. if usr == nil {
  368. ts.table.LogWithTableId("tablesink.recvChatMsg,usr[%d] not exist", userIndex)
  369. return
  370. }
  371. ts.table.SendGameData(-1, CMD_TABLECHAT, data)
  372. }
  373. // 取消托管消息
  374. func (ts *tablesink) recvCancleAutoMsg(userIndex int32, data string) {
  375. usr := ts.table.GetPlayer(userIndex)
  376. if usr == nil {
  377. ts.table.LogWithTableId("tablesink.recvCancleAutoMsg,usr[%d] not exist", userIndex)
  378. return
  379. }
  380. chairId := usr.GetUserChairId()
  381. if chairId == -1 {
  382. return
  383. }
  384. ts.gameScene.Players[chairId].AutoOut = false
  385. // 重启倒计时
  386. if ts.gameScene.WhoseTurn != chairId {
  387. return
  388. }
  389. ts.table.KillTimer(TIMER_GAME)
  390. switch ts.gameScene.Phase {
  391. case Phase_Dice:
  392. ts.table.SetTimer(TIMER_GAME, ts.GetMoveSec())
  393. return
  394. case Phase_Move:
  395. ts.table.SetTimer(TIMER_GAME, ts.GetDiceSec())
  396. return
  397. }
  398. }
  399. // 操作掷骰子或者移动飞机
  400. func (ts *tablesink) recvAction(userIndex int32, data string) {
  401. if ts.gameScene.Phase != Phase_Dice && ts.gameScene.Phase != Phase_Move {
  402. ts.table.LogWithTableId("tablesink.recvAction,phase is wrong! %d ", ts.gameScene.Phase)
  403. return
  404. }
  405. usr := ts.table.GetPlayer(userIndex)
  406. if usr == nil {
  407. ts.table.LogWithTableId("tablesink.recvAction,usr[%d] not exist", userIndex)
  408. return
  409. }
  410. chairId := usr.GetUserChairId()
  411. if !isValidChair(chairId) {
  412. ts.table.LogWithTableId("tablesink.recvAction invalid chair %d", chairId)
  413. return
  414. }
  415. var action CmdAction
  416. err := json.Unmarshal([]byte(data), &action)
  417. if err != nil {
  418. ts.table.LogWithTableId("tablesink.recvAction,Unmarshal failed[%d] %s", userIndex, data)
  419. return
  420. }
  421. if action.Action == Action_Drop {
  422. ts.dealDrop(chairId)
  423. return
  424. }
  425. ts.doAction(chairId, action.Action, action.PlaneId, usr)
  426. }
  427. // 获得骰子点数
  428. func (ts *tablesink) getDicePoint(chairId int, isRobot bool) int {
  429. number := 0
  430. round := ts.gameScene.Players[chairId].round
  431. hopeRound := ts.gameScene.Players[chairId].hopeRound
  432. hope := ts.gameScene.Players[chairId].hope
  433. if isRobot {
  434. //如果是机器人出点的时候 检查是否有可以攻击的对手
  435. rnd := rand.Intn(100)
  436. points := []int{1, 2, 3, 4, 5, 6}
  437. if rnd <= config.Rooms.GetParam1(ts.roomInfo.RoomID) {
  438. //检查前方是否有非机器人棋子
  439. for _, v := range ts.gameScene.Players[chairId].Planes {
  440. //没有出机场或者已经到终点和快到终点的过滤
  441. if v.Position == 0 || v.Position == MAX_STEP || v.Position >= SAFE_START {
  442. continue
  443. }
  444. for _, p := range points {
  445. //可以攻击优先
  446. total, _, _ := ts.gameScene.checkPositionIsCrashed(chairId, v.Position+p, v.Id)
  447. if total == 1 {
  448. number = 6
  449. if p < 6 {
  450. number = p
  451. }
  452. if number == hope {
  453. ts.gameScene.Players[chairId].isShakeOutHope = true
  454. }
  455. return number
  456. }
  457. }
  458. }
  459. }
  460. }
  461. //每过4轮检查一次期待点数
  462. if hopeRound%4 == 0 {
  463. if !ts.gameScene.Players[chairId].isShakeOutHope {
  464. //如果3轮内没有出过 则必出期待点
  465. number = hope
  466. ts.gameScene.Players[chairId].isShakeOutHope = true
  467. return number
  468. }
  469. ts.gameScene.Players[chairId].isShakeOutHope = false
  470. }
  471. //随机取
  472. number = ts.getRandomNumber(round)
  473. if ts.IsDual() && !isRobot {
  474. //如果是两人场 减少机器人被攻击的可能性
  475. rnd := rand.Intn(100)
  476. if rnd <= config.Rooms.GetParam1(ts.roomInfo.RoomID) {
  477. //可以攻击 则替换点数
  478. unAttackPoint := ts.gameScene.getUnAttackPoint(chairId)
  479. if len(unAttackPoint) > 0 {
  480. number = unAttackPoint[rand.Intn(len(unAttackPoint))]
  481. }
  482. }
  483. } else if ts.IsDual() && isRobot && number < 3 && ts.gameScene.checkIsBehind(chairId) {
  484. //从3,4,5,6中随机取一个
  485. number = rand.Intn(4) + 3
  486. }
  487. if number == hope {
  488. ts.gameScene.Players[chairId].isShakeOutHope = true
  489. }
  490. return number
  491. }
  492. func (ts *tablesink) doAction(chairId, action, planeId int, usr *user.UserInfo) {
  493. isRobot := ts.isRobot(chairId)
  494. number := 0
  495. if action == Action_Dice {
  496. ts.gameScene.Players[chairId].round++
  497. ts.gameScene.Players[chairId].hopeRound++
  498. number = ts.getDicePoint(chairId, isRobot)
  499. //ts.sendWinChat(usr.GetUserIndex(), usr.GetUserId(), 2000+rand.Intn(2000))
  500. }
  501. ok, errMsg, isNextChair, stepCount := ts.gameScene.addAction(chairId, action, number, planeId, isRobot)
  502. if !ok {
  503. if usr != nil {
  504. ts.table.SendGameData(usr.GetUserIndex(), CMD_ACTION, errMsg)
  505. }
  506. return
  507. }
  508. ts.table.KillTimer(TIMER_GAME)
  509. if ts.gameScene.Players[chairId].isWin() {
  510. ts.checkAndEndGame(chairId, stepCount)
  511. return
  512. }
  513. isCanMove := true
  514. moveDuration := 0
  515. if isNextChair {
  516. ts.gameScene.nextChair()
  517. //如果切换玩家并且前一个玩家是移动棋子则需要增加延迟
  518. if action == Action_Dice {
  519. isCanMove = false
  520. }
  521. }
  522. if action == Action_Move {
  523. ts.gameScene.Phase = Phase_Move
  524. moveDuration = stepCount * SEC_MOVE_TIME
  525. if ts.gameScene.ActionResult.CrashedChairId >= 0 && ts.gameScene.ActionResult.CrashedPlaneId >= 0 {
  526. //有攻击则增加时间
  527. moveDuration += SEC_DELAY
  528. }
  529. } else {
  530. ts.gameScene.Phase = Phase_Dice
  531. }
  532. ts.gameScene.Index++
  533. ts.table.NotifySceneChanged(-1)
  534. ts.resetGameTimer(isCanMove, isNextChair, moveDuration)
  535. }
  536. // 重置游戏计时器
  537. func (ts *tablesink) resetGameTimer(isCanMove, isNextChair bool, moveDuration int) {
  538. ts.table.KillTimer(TIMER_GAME)
  539. ts.table.KillTimer(TIMER_ROBOT)
  540. if !isValidChair(ts.gameScene.WhoseTurn) {
  541. return
  542. }
  543. switch ts.gameScene.Phase {
  544. case Phase_Dice:
  545. if ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut {
  546. times := SEC_AUTO
  547. if isNextChair && !isCanMove {
  548. //没有移动直接换人 加延迟
  549. times += SEC_NEXT_CHAIR
  550. } else if isCanMove {
  551. times = SEC_AUTO + SEC_DELAY
  552. }
  553. ts.table.SetTimer(TIMER_GAME, times)
  554. } else {
  555. //如果没有可以移动的则直接进入掷骰子阶段的定时间
  556. times := ts.GetDiceSec()
  557. if isNextChair && !isCanMove {
  558. //没有移动直接换人 加延迟
  559. times += SEC_NEXT_CHAIR
  560. } else if isCanMove {
  561. times = ts.GetMoveSec() + SEC_DELAY
  562. }
  563. ts.table.SetTimer(TIMER_GAME, times)
  564. }
  565. case Phase_Move:
  566. if ts.gameScene.WhoseTurn != -1 && ts.gameScene.Players[ts.gameScene.WhoseTurn].AutoOut {
  567. //移动棋子时间要长一些
  568. times := SEC_AUTO
  569. if isCanMove {
  570. times += moveDuration
  571. }
  572. ts.table.SetTimer(TIMER_GAME, times)
  573. } else {
  574. //前一个有移动的话 要算上移动的等待时间
  575. times := ts.GetDiceSec()
  576. if isCanMove {
  577. times += moveDuration
  578. }
  579. ts.table.SetTimer(TIMER_GAME, times)
  580. }
  581. }
  582. ts.table.SetTimer(TIMER_ROBOT, SEC_ROBOT_CHAT)
  583. ts.LastOpraTime = time.Now()
  584. ts.checkRobotAction(isCanMove, isNextChair, moveDuration)
  585. }
  586. func (ts *tablesink) checkAndEndGame(winner, stepCount int) {
  587. _, ended := ts.gameScene.addWinner(winner)
  588. if ended {
  589. // 结束了,把没写分的都写分
  590. for i := 0; i < CHAIR_COUNT; i++ {
  591. ts.writeChairScore(i)
  592. }
  593. ts.enterEndPhase()
  594. } else {
  595. // 没有结束,先写分
  596. ts.writeChairScore(winner)
  597. ts.table.SetUserEndGame(winner)
  598. ts.gameScene.Players[winner].IsValid = false
  599. ts.gameScene.Index++
  600. ts.gameScene.nextChair()
  601. ts.table.NotifySceneChanged(-1)
  602. //额外增加播放旗子动画
  603. moveDuration := stepCount*SEC_MOVE_TIME + SEC_DELAY*2
  604. ts.resetGameTimer(true, true, moveDuration)
  605. }
  606. }
  607. func (ts *tablesink) writeChairScore(chairId int) {
  608. if !ts.gameScene.Players[chairId].IsValid {
  609. return
  610. }
  611. if ts.gameScene.Players[chairId].writtenScore {
  612. return
  613. }
  614. player := &ts.gameScene.Players[chairId]
  615. if player.Dropped {
  616. return
  617. }
  618. score := ts.gameScene.Players[chairId].Score
  619. if score <= 0 {
  620. ts.gameScene.Players[chairId].Score = -ts.roomInfo.BaseScore
  621. ts.writeScore(player.userId, 0, 0, 1, ScoreType_End, ts.roomInfo.RoomName, ts.isRobot(chairId))
  622. go ts.table.WriteBetRecord(player.userId, ts.roomInfo.BaseScore, 0, 1.0, "lose", "lose", GAME_NAME)
  623. usr := ts.table.GetUserByChair(chairId)
  624. if usr != nil {
  625. go ts.table.RequestADAward(usr.GetUserIndex(), -ts.roomInfo.BaseScore)
  626. }
  627. return
  628. }
  629. tax := (score - ts.roomInfo.BaseScore) * ts.roomInfo.RoomInfoBase.TaxRate / 100
  630. ts.writeScore(player.userId, score-tax, tax, 1, ScoreType_End, ts.roomInfo.RoomName, ts.isRobot(chairId))
  631. go ts.table.WriteBetRecord(player.userId, ts.roomInfo.BaseScore, score-tax, 1.0, "win", fmt.Sprintf("win[%d]", player.Place), GAME_NAME)
  632. player.writtenScore = true
  633. }
  634. func (ts *tablesink) enterEndPhase() {
  635. ts.gameScene.Phase = Phase_End
  636. ts.gameScene.WhoseTurn = -1
  637. ts.gameScene.Index++
  638. ts.table.NotifySceneChanged(-1)
  639. ts.endGame()
  640. }
  641. func (ts *tablesink) writeScore(userId int, amount, tax int, status, scoreType int, sourceName string, isRobot bool) bool {
  642. if !isRobot {
  643. config.Rooms.AddWinAmount(ts.roomInfo.RoomID, -amount)
  644. }
  645. if config.Server.LoseTax > 0 && scoreType == ScoreType_End {
  646. if amount > 0 && tax > 0 {
  647. go coreservice.ModifyJackpot(tax/10, GAMEID, userId, GAME_NAME, config.Server.IsChipRoom > 0)
  648. tax = 0
  649. } else {
  650. // 如果是退出、放弃,则加税
  651. score := ts.gameScene.getScore(userId)
  652. if score < 0 {
  653. tax = int(math.Abs(float64(score))) * ts.roomInfo.RoomInfoBase.TaxRate / 100
  654. }
  655. }
  656. }
  657. ok, _ := ts.table.WriteUserMoney(userId, amount, tax, status, scoreType, sourceName)
  658. return ok
  659. }
  660. func (ts *tablesink) isRobot(chairId int) bool {
  661. usr := ts.table.GetUserByChair(chairId)
  662. if usr == nil {
  663. return false
  664. }
  665. return usr.IsRobot()
  666. }
  667. func (ts *tablesink) dealDrop(chairId int) bool {
  668. if !isValidChair(chairId) {
  669. ts.table.LogWithTableId("tablesink.dealDrop invalid ChairId %d", chairId)
  670. return false
  671. }
  672. if !ts.gameScene.Players[chairId].IsValid {
  673. ts.table.LogWithTableId("tablesink.dealDrop invalid ChairId2 %d", chairId)
  674. return false
  675. }
  676. if ts.gameScene.Players[chairId].Place > 0 {
  677. ts.table.LogWithTableId("tablesink.dealDrop Winning users cannot dropped %d", chairId)
  678. return false
  679. }
  680. if ts.gameScene.Players[chairId].Dropped {
  681. ts.table.LogWithTableId("tablesink.dealDrop already dropped %d", chairId)
  682. return false
  683. }
  684. ts.gameScene.Index++
  685. ts.gameScene.Players[chairId].drop()
  686. ts.gameScene.Players[chairId].LastAction = Action_Drop
  687. // 如果没人了,结算并写分
  688. validPlayerCount := 0
  689. winner := -1 //获胜者
  690. for i := 0; i < CHAIR_COUNT; i++ {
  691. if !ts.gameScene.Players[i].IsValid {
  692. continue
  693. }
  694. if ts.gameScene.Players[i].Dropped {
  695. continue
  696. }
  697. validPlayerCount++
  698. winner = i
  699. }
  700. usr := ts.table.GetUserByChair(chairId)
  701. ts.gameScene.Players[chairId].Score = -ts.roomInfo.BaseScore
  702. if ts.gameScene.Players[chairId].Score+ts.gameScene.Players[chairId].enterGold < 0 {
  703. ts.table.LogWithTableId("tablesink.dealDrop 分数不够扣 %d %d",
  704. ts.gameScene.Players[chairId].Score, ts.gameScene.Players[chairId].enterGold)
  705. ts.gameScene.Players[chairId].Score = -ts.gameScene.Players[chairId].enterGold
  706. }
  707. //逃跑也写分
  708. ts.writeScore(usr.GetUserId(), 0, 0, 1, ScoreType_End, ts.roomInfo.RoomName, ts.isRobot(chairId))
  709. go ts.table.WriteBetRecord(ts.gameScene.Players[chairId].userId, ts.roomInfo.BaseScore, 0, 1.0, "drop", "drop", GAME_NAME)
  710. if usr != nil {
  711. go ts.table.RequestADAward(usr.GetUserIndex(), -ts.roomInfo.BaseScore)
  712. }
  713. ts.table.SetUserEndGame(chairId)
  714. ts.gameScene.Players[chairId].gameInit()
  715. ts.broadcastDrop(chairId) //广播弃权消息滞后避免广播弃权消息时,用户已经退出
  716. if validPlayerCount <= 1 {
  717. ts.gameScene.LastTurn = chairId
  718. ts.checkAndEndGame(winner, 0)
  719. return true
  720. }
  721. // 其他人还能继续
  722. if ts.gameScene.WhoseTurn == chairId {
  723. ts.gameScene.nextChair()
  724. ts.resetGameTimer(true, true, 0)
  725. ts.table.NotifySceneChanged(-1)
  726. }
  727. return true
  728. }
  729. func (ts *tablesink) broadcastDrop(chairId int) {
  730. var cmd CmdDrop
  731. cmd.ChairId = chairId
  732. d, _ := json.Marshal(cmd)
  733. ts.table.SendGameData(-1, CMD_DROP, string(d))
  734. }