HGHuangHuangComponentSystem.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace ET.Server
  5. {
  6. [FriendOf(typeof (HGHuangHuangComponent))]
  7. public static class HGHuangHuangComponentSystem
  8. {
  9. [ObjectSystem]
  10. public class HGHuangHuangComponentAwakeSystem: AwakeSystem<HGHuangHuangComponent>
  11. {
  12. protected override void Awake(HGHuangHuangComponent self)
  13. {
  14. Room room = self.GetParent<Room>();
  15. if (room == null)
  16. {
  17. Log.Error($"黄冈晃晃主逻辑组件获取不到主实体...");
  18. return;
  19. }
  20. Log.Info($"创建黄冈晃晃主逻辑组件...");
  21. self.Flag = false;
  22. self.Time = 0;
  23. self.State = 0;
  24. self.Players = new Player[room.MaxNum];
  25. self.CurrentRound = 0;
  26. self.ZhuangPos = 0;
  27. self.Rand = new[] { 0, 0 };
  28. self.GangType = -1;
  29. self.AdmitDefeatList = new List<long>();
  30. self.CanHuIds = new List<Player>();
  31. self.CanPgIds = new List<Player>();
  32. self.OperableList = new List<Player>();
  33. self.ClickHuIds = new List<Player>();
  34. self.CanPgIds = new List<Player>();
  35. self.OperableList = new List<Player>();
  36. self.ClickHuIds = new List<Player>();
  37. self.CardList = new List<int>();
  38. self.UpdateTime = 0;
  39. // 初始化牌库
  40. for (int i = 0; i < 4; i++)
  41. {
  42. foreach (int value in HGHuangHuangConst.Values)
  43. {
  44. self.CardList.Add(value);
  45. }
  46. RandomGenerator.Shuffle(self.CardList);
  47. }
  48. }
  49. }
  50. [ObjectSystem]
  51. public class HGHuangHuangComponentDestroySystem: DestroySystem<HGHuangHuangComponent>
  52. {
  53. protected override void Destroy(HGHuangHuangComponent self)
  54. {
  55. Log.Info($"销毁黄冈晃晃主逻辑组件...");
  56. }
  57. }
  58. [ObjectSystem]
  59. [FriendOf(typeof(Room))]
  60. public class HGHuangHuangComponentUpdateSystem : UpdateSystem<HGHuangHuangComponent>
  61. {
  62. protected override void Update(HGHuangHuangComponent self)
  63. {
  64. long timeNow = TimeHelper.ClientNow();
  65. // 每秒执行一次
  66. if (timeNow - self.UpdateTime <= 1000)
  67. {
  68. return;
  69. }
  70. self.UpdateTime = timeNow;
  71. Room room = self.GetParent<Room>();
  72. if (room == null)
  73. {
  74. Log.Error($"黄冈晃晃主逻辑组件获取不到主实体...");
  75. return;
  76. }
  77. switch (self.State)
  78. {
  79. case 0:
  80. // 等待状态
  81. Log.Debug($"检测: 黄冈晃晃-房间号:{room.RoomId}, 房间人数:{room.Players.Count}, 状态:检测是否可开局...");
  82. if (self.CheckReadyStart(room))
  83. {
  84. self.State = 1;
  85. self.Flag = false;
  86. self.Time = 0;
  87. }
  88. // 120秒未开始直接解散
  89. if (self.Time >= 120)
  90. {
  91. room.Dispose();
  92. }
  93. break;
  94. case 1:
  95. // 开局
  96. if (!self.Flag)
  97. {
  98. if (self.Time >= 3)
  99. {
  100. self.Start(room);
  101. self.Flag = true;
  102. self.Time = 0;
  103. Log.Debug($"检测: 黄冈晃晃-房间号:{room.RoomId}, 房间人数:{room.Players.Count}, 状态:开局...VS动画...");
  104. }
  105. }
  106. else
  107. {
  108. if (self.Time >= 4)
  109. {
  110. self.State = 2;
  111. self.Flag = false;
  112. self.Time = 0;
  113. }
  114. }
  115. break;
  116. case 2:
  117. // 进行中
  118. if (!self.Flag)
  119. {
  120. self.Flag = true;
  121. self.Time = 0;
  122. // 摸牌
  123. self.DrawCard(room);
  124. }
  125. else
  126. {
  127. // 强制操作
  128. if (self.Time >= 15)
  129. {
  130. if (self.OperableList != null && self.OperableList.Count > 0)
  131. {
  132. // 过
  133. }
  134. if (self.DrawCardPlayer.Id == self.CurrentPlayer.Id)
  135. {
  136. // 出牌
  137. self.DisCard(room, self.CurrentPlayer, 0, true);
  138. }
  139. }
  140. }
  141. break;
  142. case 3:
  143. // 已结束
  144. break;
  145. }
  146. self.Time++;
  147. }
  148. }
  149. /// <summary>
  150. /// 检测是否可开始 3秒倒计时开始
  151. /// </summary>
  152. /// <param name="self"></param>
  153. /// <param name="room"></param>
  154. /// <returns></returns>
  155. private static bool CheckReadyStart(this HGHuangHuangComponent self, Room room)
  156. {
  157. if (!room.IsStart() || self.State != 0)
  158. {
  159. return false;
  160. }
  161. // 通知客户端3秒倒计时
  162. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  163. {
  164. MessageHelper.SendToClient(player, new G2C_ReadyStartPush(){ReadyStartTime = 3});
  165. }
  166. return true;
  167. }
  168. /// <summary>
  169. /// 发牌
  170. /// </summary>
  171. /// <param name="self"></param>
  172. /// <param name="room"></param>
  173. private static void SendCard(this HGHuangHuangComponent self, Room room)
  174. {
  175. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  176. {
  177. for (int i = 0; i < 13; i++)
  178. {
  179. int card = self.CardList[0];
  180. player.RemainCards = CardHelper.Add(player.RemainCards, card);
  181. self.CardList.RemoveAt(0);
  182. }
  183. }
  184. }
  185. /// <summary>
  186. /// 游戏开局
  187. /// </summary>
  188. /// <param name="self"></param>
  189. /// <param name="room"></param>
  190. private static void Start(this HGHuangHuangComponent self, Room room)
  191. {
  192. self.CurrentRound += 1;
  193. // 摇骰子
  194. int rand1 = RandomGenerator.RandomNumber(1, 7);
  195. int rand2 = RandomGenerator.RandomNumber(1, 7);
  196. self.Rand = new[] { rand1, rand2 };
  197. // 定庄
  198. // self.ZhuangPos = (rand1 + rand2) % 4;
  199. self.ZhuangPos = (rand1 + rand2) % room.MaxNum;
  200. // 设置当前操作玩家
  201. self.CurrentPlayer = self.Players[self.ZhuangPos];
  202. // 设置当前摸牌玩家
  203. self.DrawCardPlayer = self.Players[self.ZhuangPos];
  204. // 发牌
  205. self.SendCard(room);
  206. // 广播
  207. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  208. {
  209. player.State = 2;
  210. MessageHelper.SendToClient(player, new G2C_StartPush(){info = ProtoHelper.RoomToProto(room, player, null)});
  211. Log.Info($"游戏开局, 房间id={room.RoomId}, 玩家id={player.Id}, 玩家={player.Name}, 手牌信息={player.RemainCards}");
  212. }
  213. }
  214. /// <summary>
  215. /// 摸牌
  216. /// </summary>
  217. /// <param name="self"></param>
  218. /// <param name="room"></param>
  219. private static void DrawCard(this HGHuangHuangComponent self, Room room)
  220. {
  221. // 2个以上玩家认输或牌库没牌了直接结束
  222. if (self.AdmitDefeatList.Count > 2 || self.CardList.Count <= 0)
  223. {
  224. self.GameOver(room);
  225. return;
  226. }
  227. Player drawCardPlayer = self.DrawCardPlayer;
  228. if (drawCardPlayer == null)
  229. {
  230. Log.Error($"房间id={room.RoomId}, 摸牌玩家为空...");
  231. return;
  232. }
  233. // 摸牌
  234. int card = self.CardList[0];
  235. drawCardPlayer.RemainCards = CardHelper.Add(drawCardPlayer.RemainCards, card);
  236. self.CardList.RemoveAt(0);
  237. // 当前摸的牌
  238. self.DrawCard = card;
  239. // 吃,碰,杠,胡,过
  240. bool hasAct = false;
  241. // 检测摸牌人动作, 校验摸牌是否胡
  242. Struct.HuRes huRes = HGHuangHuangHelper.CheckHu(drawCardPlayer.RemainCards);
  243. if (huRes.Type != HGHuangHuangConst.HU_DEFAULT)
  244. {
  245. hasAct = true;
  246. drawCardPlayer.Act[3] = 1;
  247. self.CanHuIds.Add(drawCardPlayer);
  248. if (!self.OperableList.Contains(drawCardPlayer))
  249. {
  250. self.OperableList.Add(drawCardPlayer);
  251. }
  252. }
  253. // todo 玩家听牌状态不允许杠
  254. // 检测摸牌人动作, 校验摸牌是否杠
  255. List<Struct.Kezi> gang = HGHuangHuangHelper.IsDrawGang(drawCardPlayer);
  256. if (gang is { Count: > 0 })
  257. {
  258. hasAct = true;
  259. drawCardPlayer.Act[2] = 1;
  260. if (!self.OperableList.Contains(drawCardPlayer))
  261. {
  262. self.OperableList.Add(drawCardPlayer);
  263. }
  264. if (!self.CanPgIds.Contains(drawCardPlayer))
  265. {
  266. self.CanPgIds.Add(drawCardPlayer);
  267. }
  268. }
  269. // 过牌
  270. drawCardPlayer.Act[4] = hasAct? 1 : 0;
  271. if (drawCardPlayer.HuCards.Any(_card => _card > 0 && _card == self.DrawCard))
  272. {
  273. drawCardPlayer.Act[4] = 0;
  274. }
  275. // 推送摸牌广播
  276. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  277. {
  278. MessageHelper.SendToClient(player, new G2C_DrawCardPush(){info = ProtoHelper.RoomToProto(room, player, drawCardPlayer)});
  279. Log.Info($"摸牌... 玩家ID:{player.Id}, 位置:{player.Pos}, 摸牌玩家:{player.Id == drawCardPlayer.Id}, 手牌大小:{player.RemainCards.Length}, 手牌信息:{string.Join(", ", player.RemainCards)}, 摸的牌:{card}");
  280. }
  281. }
  282. /// <summary>
  283. /// 出牌
  284. /// </summary>
  285. /// <param name="self"></param>
  286. /// <param name="room"></param>
  287. /// <param name="player"></param>
  288. /// <param name="card">出的牌(0.当前摸牌玩家自动出牌 -1.非当前摸牌玩家自动出牌 >0.玩家手动出牌)</param>
  289. /// <param name="flag">是否自动出牌</param>
  290. public static async void DisCard(this HGHuangHuangComponent self, Room room, Player player, int card, bool flag)
  291. {
  292. if (player == null)
  293. {
  294. Log.Error($"出牌错误,player is null.");
  295. return;
  296. }
  297. using (await CoroutineLockComponent.Instance.Wait(CoroutineLockType.DisCard, player.Id))
  298. {
  299. if (self.CurrentPlayer.Id == player.Id && self.DrawCardPlayer.Id == player.Id)
  300. {
  301. Log.Info($"出牌... 玩家ID:{player.Id}, 位置:{player.Pos}, 手牌大小:{player.RemainCards.Length}, 手牌信息: {string.Join(", ", player.RemainCards)}");
  302. // 自动出牌
  303. if (flag)
  304. {
  305. if (self.PengPlayer != null && self.PengPlayer.Id == player.Id)
  306. {
  307. self.PengPlayer = null;
  308. int[] tmpCard = new int[player.RemainCards.Length];
  309. Array.Copy(player.RemainCards, 0, tmpCard, 0, player.RemainCards.Length);
  310. Array.Sort(tmpCard);
  311. card = tmpCard[^1];
  312. }
  313. else
  314. {
  315. card = player.RemainCards[^1];
  316. }
  317. // todo 真人托管状态
  318. }
  319. if (!player.RemainCards.Contains(card))
  320. {
  321. Log.Error($"出牌错误,玩家的牌不在手中...");
  322. return;
  323. }
  324. // 玩家出牌
  325. player.RemainCards = CardHelper.Remove(player.RemainCards, card);
  326. player.DisCards = CardHelper.Add(player.DisCards, card);
  327. player.DiscardCount += 1;
  328. self.DisCard = card;
  329. self.DisCardPlayer = player;
  330. self.PengPlayer = null;
  331. // 给出牌人广播
  332. MessageHelper.SendToClient(player, new G2C_DisCardPush(){info = ProtoHelper.RoomToProto(room, player, player)});
  333. self.CanHuIds.Clear();
  334. self.ClickHuIds.Clear();
  335. self.OperableList.Clear();
  336. self.CanPgIds.Clear();
  337. self.CleanUserAct();
  338. // 检测其它三家的动作
  339. int[] nextPos = room.GetSorcPos(player.Pos);
  340. foreach (int index in nextPos)
  341. {
  342. Player otherPlayer = self.Players[index];
  343. if (otherPlayer != null)
  344. {
  345. // 吃,碰,杠,胡,过
  346. int[] act = new int[5];
  347. List<Struct.Kezi> gangs = new List<Struct.Kezi>();
  348. List<Struct.Kezi> pengs = new List<Struct.Kezi>();
  349. List<Struct.Kezi> chis = new List<Struct.Kezi>();
  350. // todo 玩家听牌状态不允许有动作
  351. // 出牌是否杠
  352. gangs = HGHuangHuangHelper.IsDiscardGang(otherPlayer, card, player.Id);
  353. // 出牌是否碰
  354. pengs = HGHuangHuangHelper.IsDiscardPeng(otherPlayer, card, player.Id);
  355. // 出牌是否吃
  356. if (otherPlayer.Pos == nextPos[0])
  357. {
  358. chis = HGHuangHuangHelper.IsDiscardChi(otherPlayer, card, player.Id);
  359. }
  360. // 游戏中才会动作
  361. if (otherPlayer.State == 2)
  362. {
  363. bool hasAct = false;
  364. int[] tmpCards = new int[otherPlayer.RemainCards.Length];
  365. Array.Copy(otherPlayer.RemainCards, 0, tmpCards, 0, otherPlayer.RemainCards.Length);
  366. tmpCards = CardHelper.Add(tmpCards, card);
  367. // 校验胡牌
  368. Struct.HuRes huType = HGHuangHuangHelper.CheckHu(tmpCards);
  369. if (huType.Type != HGHuangHuangConst.HU_DEFAULT)
  370. {
  371. hasAct = true;
  372. act[3] = 1;
  373. // 放入可胡玩家列表
  374. if (!self.CanHuIds.Contains(otherPlayer))
  375. {
  376. self.CanHuIds.Add(otherPlayer);
  377. }
  378. // 加入可操作玩家list
  379. if (!self.OperableList.Contains(otherPlayer))
  380. {
  381. self.OperableList.Add(otherPlayer);
  382. }
  383. }
  384. // 是否杠
  385. if (gangs.Count > 0)
  386. {
  387. hasAct = true;
  388. act[2] = 1;
  389. }
  390. // 是否碰
  391. if (pengs.Count > 0)
  392. {
  393. hasAct = true;
  394. act[1] = 1;
  395. }
  396. // 是否吃
  397. if (chis.Count > 0)
  398. {
  399. hasAct = true;
  400. act[0] = 1;
  401. }
  402. // 是否过
  403. if (hasAct) {
  404. act[4] = 1;
  405. }
  406. if (act[2] == 1 || act[1] == 1) {
  407. // 加入可操作玩家list
  408. if (!self.OperableList.Contains(otherPlayer)) {
  409. self.OperableList.Add(otherPlayer);
  410. }
  411. // 加入可碰杠玩家集合
  412. if (!self.CanPgIds.Contains(otherPlayer)) {
  413. self.CanPgIds.Add(otherPlayer);
  414. }
  415. }
  416. // 设置玩家动作
  417. otherPlayer.Act = act;
  418. }
  419. // 广播其它三家
  420. MessageHelper.SendToClient(otherPlayer, new G2C_DisCardPush(){info = ProtoHelper.RoomToProto(room, otherPlayer, player)});
  421. }
  422. }
  423. if (self.CanHuIds.Count <= 0)
  424. {
  425. self.GangPlayer = null;
  426. }
  427. // 判断是否下家摸牌, 指定下家摸牌
  428. if (self.OperableList.Count == 0)
  429. {
  430. Player nextPlayer = null;
  431. foreach (int index in nextPos)
  432. {
  433. Player tempPlayer = self.Players[index];
  434. if (tempPlayer is { State: 2 } && tempPlayer.Id != player.Id)
  435. {
  436. nextPlayer = tempPlayer;
  437. break;
  438. }
  439. }
  440. // 有三家认输,就会有null
  441. if (nextPlayer == null)
  442. {
  443. self.GameOver(room);
  444. }
  445. else
  446. {
  447. // 设置摸牌玩家
  448. self.DrawCardPlayer = nextPlayer;
  449. // 设置操作玩家
  450. self.CurrentPlayer = nextPlayer;
  451. // 摸牌
  452. self.Flag = false;
  453. }
  454. }
  455. else
  456. {
  457. if (self.CanHuIds.Count > 0)
  458. {
  459. self.CurrentPlayer = self.Players[self.CanHuIds.First().Pos];
  460. } else if (self.CanPgIds.Count > 0)
  461. {
  462. self.CurrentPlayer = self.Players[self.CanPgIds.First().Pos];
  463. }
  464. else
  465. {
  466. self.CurrentPlayer = self.Players[self.OperableList.First().Pos];
  467. }
  468. }
  469. }
  470. }
  471. }
  472. /// <summary>
  473. /// 清空玩家可操作动作
  474. /// </summary>
  475. /// <param name="self"></param>
  476. private static void CleanUserAct(this HGHuangHuangComponent self) {
  477. foreach (Player player in self.Players)
  478. {
  479. if (player != null)
  480. {
  481. player.Act = new int[5];
  482. }
  483. }
  484. }
  485. /// <summary>
  486. /// 结束
  487. /// </summary>
  488. /// <param name="self"></param>
  489. /// <param name="room"></param>
  490. private static void GameOver(this HGHuangHuangComponent self, Room room)
  491. {
  492. }
  493. }
  494. }