gamedefs.go 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748
  1. package gamelogic
  2. import (
  3. "encoding/json"
  4. "bet24.com/log"
  5. )
  6. const (
  7. GAMEID = 36
  8. GAME_NAME = "blitzludo"
  9. CHAIR_COUNT = 4
  10. PLANE_COUNT = 4
  11. )
  12. const (
  13. Phase_Free = iota
  14. Phase_Match //匹配结束
  15. Phase_Dice //掷骰子中
  16. Phase_Move //移动飞机中
  17. Phase_End //结束
  18. )
  19. const (
  20. ScoreType_Bet = iota
  21. ScoreType_End
  22. ScoreType_Return
  23. )
  24. const (
  25. TIMER_READY_0 = iota
  26. TIMER_READY_1
  27. TIMER_READY_2
  28. TIMER_READY_3
  29. TIMER_MATCH
  30. TIMER_GAME
  31. TIMER_ROBOT
  32. TIMER_ADD_ROBOT
  33. TIMER_REMOVE_ROBOT
  34. TIMER_QUIT_0
  35. TIMER_QUIT_1
  36. TIMER_QUIT_2
  37. TIMER_QUIT_3
  38. )
  39. const SEC_READY = 15 * 1000 //准备计时
  40. const SEC_MATCH = 4000 //匹配计时 4000 匹配动画时间
  41. const SEC_DELAY = 2000 //动画等待时间2s
  42. const SEC_MOVE_TIME = 300 //移动一格时间
  43. const SEC_NEXT_CHAIR = 500 //换人间隔
  44. const SEC_AUTO = 2000 //托管超时
  45. const SEC_ROBOT_CHAT = 5000
  46. const MAX_STEP = 58 // 终点
  47. const SAFE_START = 53 //安全通道起始坐标
  48. const LOOP_START = 52 //转圈起始位
  49. const LOOP_END = 51 //转圈截止位
  50. const (
  51. Action_Dice = iota // 投掷骰子
  52. Action_Move // 移动飞机
  53. Action_Drop // 2 弃牌
  54. Action_Invalid
  55. )
  56. // 飞机是否安全
  57. func isSafe(pos int) bool {
  58. //还未出发
  59. if pos == 0 {
  60. return true
  61. }
  62. if pos >= SAFE_START {
  63. return true
  64. }
  65. //第九格和第14格为安全区
  66. return (pos-9)%13 == 0 || (pos-14)%13 == 0
  67. }
  68. // 查找优先级高的棋子
  69. func (gs *GameScene) getPriorityPlane(chairId int) (int, int) {
  70. unsafePlane := make(map[int]int)
  71. farthestPlane := -1
  72. farthestPosition := -1
  73. myPlanes := gs.Players[chairId].Planes
  74. for k, v := range myPlanes {
  75. if !v.CanMove {
  76. continue
  77. }
  78. if v.Position > farthestPosition {
  79. farthestPlane = v.Id
  80. farthestPosition = v.Position
  81. }
  82. if isSafe(v.Position) {
  83. continue
  84. }
  85. for k1, v1 := range myPlanes {
  86. if k == k1 || !v1.CanMove {
  87. continue
  88. }
  89. //叠棋
  90. if v.Position == v1.Position && v.Id != v1.Id {
  91. continue
  92. }
  93. unsafePlane[v.Id] = v.Position
  94. }
  95. }
  96. unsafePlaneId := -1
  97. if len(unsafePlane) > 0 {
  98. farthest := -1 //最远的
  99. for unsafeId, unsafePosition := range unsafePlane {
  100. for i := 0; i < CHAIR_COUNT; i++ {
  101. //不跟自己比
  102. if i == chairId || !gs.Players[i].IsValid || gs.Players[i].Dropped {
  103. continue
  104. }
  105. if gs.Players[i].checkChairPosition(chairId, unsafePosition) == 2 {
  106. //有危险
  107. if unsafePosition > farthest {
  108. farthest = unsafePosition
  109. unsafePlaneId = unsafeId
  110. }
  111. }
  112. }
  113. }
  114. }
  115. //存在风险的棋子和最远端的棋子
  116. return unsafePlaneId, farthestPlane
  117. }
  118. // 检查起飞区附近是否有敌人
  119. func (gs *GameScene) checkTookOffArea(chairId int) bool {
  120. //检查全部玩家
  121. for i := 0; i < CHAIR_COUNT; i++ {
  122. if i == chairId || !gs.Players[i].IsValid || gs.Players[i].Dropped {
  123. continue
  124. }
  125. //坐标转换后在1-10之间的都算有棋子
  126. for _, v := range gs.Players[i].Planes {
  127. if v.Position == 0 {
  128. continue
  129. }
  130. if gs.Players[i].convertPlanePosition(v.Position) <= 10 {
  131. //有棋子
  132. return true
  133. }
  134. }
  135. }
  136. return false
  137. }
  138. // 是否相撞
  139. func isCrash(chair1, pos1, chair2, pos2 int) bool {
  140. if pos1 == 0 || pos2 == 0 || isSafe(pos1) || isSafe(pos2) {
  141. return false
  142. }
  143. return (chair1*13+pos1)%LOOP_START == (chair2*13+pos2)%LOOP_START
  144. }
  145. // 是否有效座位
  146. func isValidChair(chairId int) bool {
  147. return chairId >= 0 && chairId < CHAIR_COUNT
  148. }
  149. // 是否有效棋子
  150. func isValidPlane(planeId int) bool {
  151. return planeId >= 0 && planeId < PLANE_COUNT
  152. }
  153. // 是否有效点数
  154. func isValidPoint(point int) bool {
  155. return point >= 1 && point <= 6
  156. }
  157. type userAction struct {
  158. ChairId int
  159. Action int
  160. Number int
  161. PlaneId int
  162. }
  163. func (ua *userAction) dump() {
  164. log.Debug(" Chair[%d],Action[%d],PlaneId[%d],Number[%d]", ua.ChairId, ua.Action, ua.PlaneId, ua.Number)
  165. }
  166. // 弃权
  167. func (p *PlayerInfo) drop() {
  168. p.Dropped = true
  169. }
  170. // 检测位置是否撞机
  171. func (gs *GameScene) checkPositionIsCrashed(chairId, position, planeId int) (total int, tempCrashed *Plane, crashedPlayer *PlayerInfo) {
  172. total = 0
  173. //自己的棋子数量
  174. myPlaneCount := 0
  175. for i := 0; i < CHAIR_COUNT; i++ {
  176. //不能忽略自己的棋子 有可能自己的棋子和对方的棋子形成了安全区
  177. if i == chairId {
  178. planeCount := gs.Players[i].checkSamePositionCount(position, planeId)
  179. myPlaneCount += planeCount
  180. if planeCount != 0 {
  181. break
  182. }
  183. continue
  184. }
  185. //无效的玩家过滤
  186. if !gs.Players[i].IsValid {
  187. continue
  188. }
  189. if gs.Players[i].Dropped {
  190. continue
  191. }
  192. //检查加上步数后是否和该玩家的棋子是否相撞
  193. crashed := gs.Players[i].checkCrashed(chairId, position)
  194. crashedCount := len(crashed)
  195. if crashedCount < 1 {
  196. continue
  197. }
  198. total += crashedCount
  199. //如果当前用户有一个被踩则暂存
  200. if crashedCount == 1 {
  201. tempCrashed = crashed[0]
  202. crashedPlayer = &gs.Players[i]
  203. }
  204. }
  205. if total != 0 && myPlaneCount >= 1 {
  206. total += myPlaneCount
  207. }
  208. return total, tempCrashed, crashedPlayer
  209. }
  210. // 检测前方是否有敌人的棋子 (两人场判断)
  211. func (gs *GameScene) checkFrontHasEnemy(chairId, position, planeId int) bool {
  212. //敌人的座位号
  213. enemyChairId := (chairId + 2) % CHAIR_COUNT
  214. planeCount := gs.Players[chairId].checkSamePositionCount(position, planeId)
  215. if planeCount != 0 {
  216. return false
  217. }
  218. crashed := gs.Players[enemyChairId].checkCrashed(chairId, position)
  219. crashedCount := len(crashed)
  220. return crashedCount == 1
  221. }
  222. // 得到不能攻击的点数
  223. func (gs *GameScene) getUnAttackPoint(chairId int) []int {
  224. //临时标记
  225. points := []int{1, 2, 3, 4, 5, 6}
  226. unAttackPoint := make([]int, 0)
  227. attackPoint := make([]int, 0)
  228. for _, v := range gs.Players[chairId].Planes {
  229. //没有出机场或者已经到终点和快到终点的过滤
  230. if v.Position == 0 || v.Position == MAX_STEP || v.Position >= SAFE_START {
  231. continue
  232. }
  233. for _, p := range points {
  234. //可以攻击 则替换点数
  235. isHasEnemy := gs.checkFrontHasEnemy(chairId, v.Position+p, v.Id)
  236. if isHasEnemy {
  237. attackPoint = append(attackPoint, p)
  238. }
  239. }
  240. }
  241. if len(attackPoint) == 0 {
  242. return unAttackPoint
  243. }
  244. //剔除可以攻击的点数
  245. for _, v := range points {
  246. isHas := false
  247. for _, p := range attackPoint {
  248. if v == p {
  249. isHas = true
  250. break
  251. }
  252. }
  253. if !isHas {
  254. unAttackPoint = append(unAttackPoint, v)
  255. }
  256. }
  257. return unAttackPoint
  258. }
  259. // 检查棋子移动后结果
  260. func (gs *GameScene) checkPlaneMoveResult(chairId, position int) int {
  261. //临时标记
  262. temp := -1
  263. for i := 0; i < CHAIR_COUNT; i++ {
  264. //不跟自己比
  265. if i == chairId {
  266. continue
  267. }
  268. //无效的玩家过滤
  269. if !gs.Players[i].IsValid {
  270. continue
  271. }
  272. if gs.Players[i].Dropped {
  273. continue
  274. }
  275. //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
  276. ret := gs.Players[i].checkChairPosition(chairId, position)
  277. if ret == 2 {
  278. //有危险则不在判断
  279. temp = ret
  280. break
  281. }
  282. if ret > temp {
  283. temp = ret
  284. }
  285. }
  286. return temp
  287. }
  288. // 检查自己是否落后对手超过10步以上了(只考虑最远端的)
  289. func (gs *GameScene) checkIsBehind(chairId int) bool {
  290. myPositionCount := gs.Players[chairId].calculatePosition()
  291. if myPositionCount < 10 {
  292. return false
  293. }
  294. for i := 0; i < CHAIR_COUNT; i++ {
  295. if i == chairId {
  296. continue
  297. }
  298. if !gs.Players[i].IsValid {
  299. continue
  300. }
  301. if gs.Players[i].Dropped {
  302. continue
  303. }
  304. //检查自己的棋子加上步数后 是否超过或者接近对手的棋子
  305. ret := gs.Players[i].calculatePosition()
  306. if ret >= myPositionCount+10 {
  307. return true
  308. }
  309. }
  310. return false
  311. }
  312. // 计算机器人飞机移动权重
  313. func (gs *GameScene) calcRobotPlaneWeight(chairId, number int) int {
  314. // 初始化变量
  315. tempWeight := -1 // 权重
  316. weightPlaneId := -1 // 权重最高的棋子
  317. planeId := -1
  318. farthest := -1 // 最远的
  319. tookOffCount, leaveCount := gs.Players[chairId].checkTookOffCount()
  320. checkTookOffArea := false
  321. if number == 6 && tookOffCount < 4 && leaveCount == tookOffCount {
  322. //摇到6 并且还有没有起飞的 已经起飞的都离开起飞区域了
  323. checkTookOffArea = gs.checkTookOffArea(chairId)
  324. }
  325. // 获取通道是否打开的信息
  326. openChannel := gs.Players[chairId].OpenChannel
  327. // 获取危险的棋子的编号
  328. unsafePlaneId, farthestPlane := gs.getPriorityPlane(chairId)
  329. for _, v := range gs.Players[chairId].Planes {
  330. if !v.CanMove {
  331. continue
  332. }
  333. id := v.Id
  334. position := v.Position
  335. movePosition := position + number
  336. if openChannel && movePosition == MAX_STEP {
  337. planeId = id
  338. break
  339. }
  340. weight := -1 // 权重
  341. relativePos := (movePosition) % 13 //移动后的相对坐标
  342. //计算距离
  343. safeDistance := (relativePos - 9) % 13
  344. if safeDistance <= (relativePos-14)%13 {
  345. safeDistance = (relativePos - 14) % 13
  346. }
  347. if movePosition == 48 {
  348. //最后一个安全区
  349. weight = 7
  350. } else if id == unsafePlaneId || safeDistance == 0 {
  351. weight = 6
  352. }
  353. if position == 0 && (checkTookOffArea || (tookOffCount == 1 && !openChannel)) {
  354. weight = 5
  355. }
  356. if openChannel {
  357. if movePosition >= LOOP_START {
  358. weight = 9
  359. }
  360. } else {
  361. if position == LOOP_END || movePosition > LOOP_END {
  362. weight = 0
  363. }
  364. }
  365. //判断是否可以攻击
  366. if position != 0 && (weight == -1 || weight == 6) {
  367. total, _, _ := gs.checkPositionIsCrashed(chairId, movePosition, id)
  368. if total == 1 {
  369. weight = 8
  370. }
  371. }
  372. if weight == -1 {
  373. weight = 3
  374. if position == 0 {
  375. weight = 2
  376. }
  377. if weight != 2 {
  378. moveResult := gs.checkPlaneMoveResult(chairId, movePosition) //移动后的结果
  379. if moveResult == 1 {
  380. //如果是可以追击则权重上升
  381. weight++
  382. } else if moveResult == 2 {
  383. if number != 6 {
  384. //超过去有危险
  385. weight = 1
  386. } else if gs.Players[chairId].ContinueSixPoint >= 1 {
  387. //摆脱机会不大 哪怕本次摇出6 接下来再次出6无法移动
  388. weight = 2
  389. if (position-9)%13 == 0 || (position-14)%13 == 0 {
  390. //已经在安全区
  391. weight = 1
  392. }
  393. }
  394. } else {
  395. //最远端的
  396. if id == farthestPlane {
  397. weight = 6
  398. }
  399. }
  400. }
  401. }
  402. if weight > tempWeight || (weight == tempWeight && position > farthest) {
  403. farthest = position
  404. weightPlaneId = id
  405. if weight > tempWeight {
  406. tempWeight = weight
  407. }
  408. }
  409. }
  410. //找到优先级更高的棋子时 根据权重选择
  411. if !isValidPlane(planeId) && isValidPlane(weightPlaneId) {
  412. planeId = weightPlaneId
  413. }
  414. return planeId
  415. }
  416. func (gs *GameScene) addAction(chairId, action, number, planeId int, isRobot bool) (bool, string, bool, int) {
  417. gs.initActionResult()
  418. //是否下一个玩家
  419. var isNextChair = false
  420. var stepCount = 0
  421. //判断数据有效性
  422. if chairId != gs.WhoseTurn {
  423. return false, "wrong turn", isNextChair, stepCount
  424. }
  425. if action == gs.Players[chairId].LastAction {
  426. return false, "wrong action", isNextChair, stepCount
  427. }
  428. //移动飞机
  429. if action == Action_Move {
  430. var isReach = false
  431. if !isValidPlane(planeId) {
  432. planeId = gs.tryMovePlane(chairId, isRobot)
  433. }
  434. ok, movePlaneId, isReach, moveStepCount, oldPosition := gs.Players[chairId].canMovePlane(planeId)
  435. if !ok {
  436. return false, "wrong movement", isNextChair, moveStepCount
  437. }
  438. stepCount = moveStepCount
  439. planeId = movePlaneId
  440. //获胜额外获得一次机会
  441. if !isReach {
  442. // 检测撞机
  443. total, tempCrashed, crashedPlayer := gs.checkPositionIsCrashed(chairId, gs.Players[chairId].Planes[planeId].Position, planeId)
  444. //一个区域如果出现2个棋子,无论是否同玩家 则为安全区 否则则攻击
  445. if total == 1 {
  446. gs.ActionResult.CrashedChairId = crashedPlayer.chairId //被撞击座位
  447. gs.ActionResult.CrashedPlaneId = tempCrashed.Id //被撞击棋子ID
  448. tempCrashed.Position = 0
  449. tempCrashed.CanMove = false
  450. //玩家被踩加一
  451. crashedPlayer.Death++
  452. //踩别人的棋子额外获得一次机会
  453. gs.Players[chairId].Kills++
  454. //打开通道
  455. gs.Players[chairId].OpenChannel = true
  456. }
  457. //没有攻击玩家 并且 点数不是6 或者 6的次数没有超过限制 则到下一个玩家操作
  458. if total != 1 && (gs.Players[chairId].Number != 6 || gs.Players[chairId].ContinueSixPoint > 2) {
  459. isNextChair = true
  460. }
  461. }
  462. gs.ActionResult.PlaneId = planeId
  463. gs.ActionResult.OldPosition = oldPosition
  464. gs.ActionResult.Position = gs.Players[chairId].Planes[planeId].Position
  465. } else {
  466. if !isValidPoint(number) {
  467. return false, "wrong point", isNextChair, stepCount
  468. }
  469. //掷骰子
  470. ok := gs.Players[chairId].setRollNumber(number)
  471. gs.ActionResult.Number = number
  472. if ok {
  473. if !gs.Players[chairId].isCanMove() {
  474. isNextChair = true
  475. } else {
  476. gs.LastTurn = gs.WhoseTurn
  477. }
  478. //如果通道打开了则判断
  479. if gs.Players[chairId].OpenChannel {
  480. //如果都不移动则判断是否快到终点
  481. gs.Players[chairId].isWillWin()
  482. }
  483. } else {
  484. //如果点数设置失败(6的次数超过限制) 则直接跳过
  485. isNextChair = true
  486. }
  487. }
  488. gs.Players[chairId].LastAction = action
  489. gs.ActionResult.Action = action
  490. if isNextChair {
  491. //重置6次数 要在此处重置 否则会导致到终点后重新计算
  492. gs.Players[chairId].ContinueSixPoint = 0
  493. }
  494. gs.userActions = append(gs.userActions, userAction{ChairId: chairId, Action: action, Number: number, PlaneId: planeId})
  495. return true, "", isNextChair, stepCount
  496. }
  497. // 尝试移动飞机
  498. func (gs *GameScene) tryMovePlane(chairId int, isRobot bool) int {
  499. var planeId int = -1
  500. //判断是否被封禁
  501. openChannel := gs.Players[chairId].OpenChannel
  502. number := gs.Players[chairId].Number
  503. if isRobot {
  504. //机器人计算权重
  505. planeId = gs.calcRobotPlaneWeight(chairId, number)
  506. return planeId
  507. }
  508. //判断前端操作指令是否合理 托管
  509. if gs.Players[chairId].AutoOut {
  510. farthest := -1 //最远的
  511. tempPlaneId := -1 //临时标记
  512. for _, v := range gs.Players[chairId].Planes {
  513. if !v.CanMove {
  514. continue
  515. }
  516. //先出机场中棋子
  517. if v.Position == 0 {
  518. planeId = v.Id
  519. break
  520. }
  521. //其次走最后是距离
  522. if v.Position > farthest {
  523. if !openChannel && (v.Position == LOOP_END || v.Position+number > LOOP_END) {
  524. tempPlaneId = v.Id
  525. } else {
  526. farthest = v.Position
  527. planeId = v.Id
  528. }
  529. }
  530. }
  531. if !isValidPlane(planeId) {
  532. planeId = tempPlaneId
  533. }
  534. }
  535. return planeId
  536. }
  537. func (gs *GameScene) initActionResult() {
  538. gs.ActionResult = ActionResult{Action: -1, Number: -1, PlaneId: -1, OldPosition: -1, Position: -1, CrashedChairId: -1, CrashedPlaneId: -1}
  539. }
  540. func (gs *GameScene) getValidUserCount() int {
  541. ret := 0
  542. for i := 0; i < CHAIR_COUNT; i++ {
  543. if gs.Players[i].IsValid {
  544. ret++
  545. }
  546. }
  547. return ret
  548. }
  549. func (gs *GameScene) getScore(userId int) int {
  550. for i := 0; i < CHAIR_COUNT; i++ {
  551. if gs.Players[i].userId == userId {
  552. return gs.Players[i].Score
  553. }
  554. }
  555. return 0
  556. }
  557. func (gs *GameScene) getScene(chairId int, player bool) string {
  558. d, _ := json.Marshal(gs)
  559. return string(d)
  560. }
  561. // 找到下一个操作者 顺时针操作
  562. func (gs *GameScene) nextChair() {
  563. gs.LastTurn = gs.WhoseTurn
  564. for i := 1; i < CHAIR_COUNT; i++ {
  565. next := (gs.LastTurn + i) % CHAIR_COUNT
  566. if !gs.Players[next].IsValid {
  567. continue
  568. }
  569. if gs.Players[next].Dropped {
  570. continue
  571. }
  572. gs.Players[next].LastAction = Action_Move
  573. gs.WhoseTurn = next
  574. break
  575. }
  576. }
  577. func (gs *GameScene) addWinner(chairId int) (int, bool) {
  578. leftPlayerCount := 0
  579. totalPlayerCount := 0
  580. leftChair := CHAIR_COUNT
  581. for i := 0; i < CHAIR_COUNT; i++ {
  582. if !gs.Players[i].IsValid {
  583. continue
  584. }
  585. totalPlayerCount++
  586. if chairId == i {
  587. continue
  588. }
  589. if gs.Players[i].Dropped {
  590. gs.Players[i].Place = -1
  591. continue
  592. }
  593. leftPlayerCount++
  594. leftChair = i
  595. }
  596. multiple := totalPlayerCount
  597. //4人游戏第一名拿4份 第二名没有奖励
  598. if totalPlayerCount == 4 {
  599. multiple = 4
  600. }
  601. //大家都弃权了 第一名拿全部池子内金币
  602. if leftPlayerCount == 0 {
  603. gs.Players[chairId].Score = gs.pool
  604. gs.Players[chairId].Place = 1
  605. return gs.Players[chairId].Score, true
  606. }
  607. if gs.pool >= gs.base*multiple {
  608. gs.Players[chairId].Score = gs.base * multiple
  609. gs.Players[chairId].Place = 1
  610. } else if gs.pool <= gs.base && totalPlayerCount > 2 {
  611. //池子还有钱并且是4人游戏 则第二名拿剩下的一份
  612. gs.Players[chairId].Score = gs.pool
  613. gs.Players[chairId].Place = 2
  614. }
  615. gs.pool -= gs.Players[chairId].Score
  616. if gs.pool > 0 && leftPlayerCount == 1 {
  617. gs.Players[leftChair].Score = gs.pool
  618. gs.Players[leftChair].Place = 2
  619. gs.pool = 0
  620. }
  621. return gs.Players[chairId].Score, gs.pool == 0
  622. }
  623. // commands
  624. const (
  625. CMD_ROOMINFO = "CMD_ROOMINFO"
  626. CMD_ACTION = "CMD_ACTION"
  627. CMD_TABLECHAT = "CMD_TABLECHAT"
  628. CMD_CANCLE_AUTO = "CMD_CANCLE_AUTO"
  629. CMD_DROP = "CMD_DROP"
  630. )
  631. type CmdAction struct {
  632. Action int
  633. PlaneId int
  634. }
  635. // 广播Drop
  636. type CmdDrop struct {
  637. ChairId int
  638. }