HGHHComponentSystem.cs 49 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Linq;
  5. namespace ET.Server
  6. {
  7. [FriendOf(typeof (HGHHComponent))]
  8. public static class HGHHComponentSystem
  9. {
  10. [ObjectSystem]
  11. public class HGHuangHuangComponentAwakeSystem: AwakeSystem<HGHHComponent>
  12. {
  13. protected override void Awake(HGHHComponent self)
  14. {
  15. Room room = self.GetParent<Room>();
  16. if (room == null)
  17. {
  18. Log.Error($"黄冈晃晃主逻辑组件获取不到主实体...");
  19. return;
  20. }
  21. Log.Info($"创建黄冈晃晃主逻辑组件...");
  22. self.Flag = false;
  23. self.Time = 0;
  24. self.State = 0;
  25. self.Players = new Player[room.MaxNum];
  26. self.CurrentRound = 0;
  27. self.ZhuangPos = 0;
  28. self.Rand = new[] { 0, 0 };
  29. self.GangType = -1;
  30. self.AdmitDefeatList = new List<long>();
  31. self.CanPgIds = new List<long>();
  32. self.GangHuIds = new List<long>();
  33. self.CanHuIds = new List<long>();
  34. self.OperableList = new List<long>();
  35. self.ClickHuIds = new List<long>();
  36. self.HuResult = -1;
  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 HGHHConst.Values)
  43. {
  44. self.CardList.Add(value);
  45. }
  46. RandomGenerator.Shuffle(self.CardList);
  47. }
  48. }
  49. }
  50. [ObjectSystem]
  51. public class HGHHComponentDestroySystem: DestroySystem<HGHHComponent>
  52. {
  53. protected override void Destroy(HGHHComponent self)
  54. {
  55. Log.Info($"销毁黄冈晃晃主逻辑组件...");
  56. }
  57. }
  58. [ObjectSystem]
  59. [FriendOf(typeof(Room))]
  60. public class HGHuangHuangComponentUpdateSystem : UpdateSystem<HGHHComponent>
  61. {
  62. protected override void Update(HGHHComponent 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 >= 30)
  129. // {
  130. // if (self.DrawCardPlayer.Id == self.CurrentPlayer.Id)
  131. // {
  132. // if (self.OperableList.Count > 0)
  133. // {
  134. // self.Guo(room, self.CurrentPlayer);
  135. // }
  136. // self.DisCard(room, self.CurrentPlayer, 0, true);
  137. // }
  138. // else
  139. // {
  140. // if (self.OperableList.Count > 0)
  141. // {
  142. // self.Guo(room, self.CurrentPlayer);
  143. // }
  144. // }
  145. //
  146. // self.Time = 30;
  147. //
  148. // }
  149. // else
  150. // {
  151. // // todo 自动托管逻辑
  152. // if (self.CurrentPlayer != null && self.CurrentPlayer.State != 3 && self.CurrentPlayer.IsAuto)
  153. // {
  154. //
  155. // }
  156. // }
  157. }
  158. break;
  159. case 3:
  160. // 已结束
  161. if (!self.Flag)
  162. {
  163. self.Flag = true;
  164. self.Time = 0;
  165. Log.Debug($"游戏结束: 黄冈晃晃-房间号:{room.RoomId}, 房间人数:{room.Players.Count}");
  166. }
  167. else
  168. {
  169. // todo 超时删除房间
  170. if (self.Time >= 60)
  171. {
  172. room.Dispose();
  173. }
  174. }
  175. break;
  176. }
  177. self.Time++;
  178. }
  179. }
  180. /// <summary>
  181. /// 检测是否可开始 3秒倒计时开始
  182. /// </summary>
  183. /// <param name="self"></param>
  184. /// <param name="room"></param>
  185. /// <returns></returns>
  186. private static bool CheckReadyStart(this HGHHComponent self, Room room)
  187. {
  188. if (!room.IsStart() || self.State != 0)
  189. {
  190. return false;
  191. }
  192. // 通知客户端3秒倒计时
  193. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  194. {
  195. MessageHelper.SendToClient(player, new G2C_HGHHReadyStartPush(){ReadyStartTime = 3});
  196. }
  197. return true;
  198. }
  199. /// <summary>
  200. /// 发牌
  201. /// </summary>
  202. /// <param name="self"></param>
  203. /// <param name="room"></param>
  204. private static void SendCard(this HGHHComponent self, Room room)
  205. {
  206. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  207. {
  208. for (int i = 0; i < 13; i++)
  209. {
  210. int card = self.CardList[0];
  211. player.RemainCards = CardHelper.Add(player.RemainCards, card);
  212. self.CardList.RemoveAt(0);
  213. }
  214. }
  215. }
  216. /// <summary>
  217. /// 游戏开局
  218. /// </summary>
  219. /// <param name="self"></param>
  220. /// <param name="room"></param>
  221. private static void Start(this HGHHComponent self, Room room)
  222. {
  223. self.CurrentRound += 1;
  224. // 摇骰子
  225. int rand1 = RandomGenerator.RandomNumber(1, 7);
  226. int rand2 = RandomGenerator.RandomNumber(1, 7);
  227. self.Rand = new[] { rand1, rand2 };
  228. // 定庄
  229. // self.ZhuangPos = (rand1 + rand2) % 4;
  230. self.ZhuangPos = (rand1 + rand2) % room.MaxNum;
  231. // 设置当前操作玩家
  232. self.CurrentPlayer = self.Players[self.ZhuangPos];
  233. // 设置当前摸牌玩家
  234. self.DrawCardPlayer = self.Players[self.ZhuangPos];
  235. // 发牌
  236. self.SendCard(room);
  237. // 广播
  238. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  239. {
  240. player.State = 2;
  241. MessageHelper.SendToClient(player, new G2C_HGHHStartPush(){info = ProtoHelper.RoomToProto(room, player, self.CurrentPlayer)});
  242. Log.Info($"游戏开局, 房间id={room.RoomId}, 玩家id={player.Id}, 玩家={player.Name}, 手牌信息={string.Join(", ", player.RemainCards)}");
  243. }
  244. }
  245. /// <summary>
  246. /// 摸牌
  247. /// </summary>
  248. /// <param name="self"></param>
  249. /// <param name="room"></param>
  250. private static void DrawCard(this HGHHComponent self, Room room)
  251. {
  252. // 2个以上玩家认输或牌库没牌了直接结束
  253. if (self.AdmitDefeatList.Count > 2 || self.CardList.Count <= 0)
  254. {
  255. self.GameOver(room);
  256. return;
  257. }
  258. Player drawCardPlayer = self.DrawCardPlayer;
  259. if (drawCardPlayer == null)
  260. {
  261. Log.Error($"房间id={room.RoomId}, 摸牌玩家为空...");
  262. return;
  263. }
  264. // 摸牌
  265. int card = self.CardList[0];
  266. drawCardPlayer.RemainCards = CardHelper.Add(drawCardPlayer.RemainCards, card);
  267. self.CardList.RemoveAt(0);
  268. // 当前摸的牌
  269. self.DrawCard = card;
  270. drawCardPlayer.Act = new int[5];
  271. drawCardPlayer.ActInfo.Clear();
  272. // 吃,碰,杠,胡,过
  273. bool hasAct = false;
  274. // 检测摸牌人动作, 校验摸牌是否胡
  275. Struct.HuRes huRes = HGHHHelper.CheckHu(drawCardPlayer.RemainCards);
  276. if (huRes.Type != HGHHConst.HU_DEFAULT)
  277. {
  278. hasAct = true;
  279. drawCardPlayer.Act[3] = 1;
  280. self.CanHuIds.Add(drawCardPlayer.Id);
  281. if (!self.OperableList.Contains(drawCardPlayer.Id))
  282. {
  283. self.OperableList.Add(drawCardPlayer.Id);
  284. }
  285. }
  286. // todo 玩家听牌状态不允许杠
  287. // 检测摸牌人动作, 校验摸牌是否杠
  288. List<Struct.Kezi> gang = HGHHHelper.IsDrawGang(drawCardPlayer);
  289. if (gang is { Count: > 0 })
  290. {
  291. hasAct = true;
  292. drawCardPlayer.Act[2] = 1;
  293. if (!self.OperableList.Contains(drawCardPlayer.Id))
  294. {
  295. self.OperableList.Add(drawCardPlayer.Id);
  296. }
  297. if (!self.CanPgIds.Contains(drawCardPlayer.Id))
  298. {
  299. self.CanPgIds.Add(drawCardPlayer.Id);
  300. }
  301. }
  302. // 过牌
  303. drawCardPlayer.Act[4] = hasAct? 1 : 0;
  304. if (drawCardPlayer.HuCards.Any(_card => _card > 0 && _card == self.DrawCard))
  305. {
  306. drawCardPlayer.Act[4] = 0;
  307. }
  308. // 设置当前操作玩家
  309. self.CurrentPlayer = drawCardPlayer;
  310. // 重置时间和流程标记
  311. self.Time = 30;
  312. self.Flag = true;
  313. Log.Info($"摸牌... 玩家ID:{drawCardPlayer.Id}, 位置:{drawCardPlayer.Pos}, 手牌大小:{drawCardPlayer.RemainCards.Length}, 手牌信息:{string.Join(", ", drawCardPlayer.RemainCards)}, 摸的牌:{card}");
  314. // 推送摸牌广播
  315. foreach (Player player in room.GetAllPlayers().Values.Where(player => player != null))
  316. {
  317. MessageHelper.SendToClient(player, new G2C_HGHHDrawCardPush(){info = ProtoHelper.RoomToProto(room, player, drawCardPlayer)});
  318. }
  319. }
  320. /// <summary>
  321. /// 出牌
  322. /// </summary>
  323. /// <param name="self"></param>
  324. /// <param name="room"></param>
  325. /// <param name="player"></param>
  326. /// <param name="card">出的牌(0.当前摸牌玩家自动出牌 -1.非当前摸牌玩家自动出牌 >0.玩家手动出牌)</param>
  327. /// <param name="flag">是否自动出牌</param>
  328. public static async void DisCard(this HGHHComponent self, Room room, Player player, int card, bool flag)
  329. {
  330. if (player == null)
  331. {
  332. Log.Error($"出牌错误,player is null.");
  333. return;
  334. }
  335. using (await CoroutineLockComponent.Instance.Wait(CoroutineLockType.PlayerOperation, player.Id))
  336. {
  337. if (self.CurrentPlayer.Id == player.Id && self.DrawCardPlayer.Id == player.Id)
  338. {
  339. Log.Info($"出牌... 玩家ID:{player.Id}, 位置:{player.Pos}, 手牌大小:{player.RemainCards.Length}, 手牌信息: {string.Join(", ", player.RemainCards)}");
  340. // 自动出牌
  341. if (flag)
  342. {
  343. if (self.PengPlayer != null && self.PengPlayer.Id == player.Id)
  344. {
  345. self.PengPlayer = null;
  346. int[] tmpCard = new int[player.RemainCards.Length];
  347. Array.Copy(player.RemainCards, 0, tmpCard, 0, player.RemainCards.Length);
  348. Array.Sort(tmpCard);
  349. card = tmpCard[^1];
  350. }
  351. else
  352. {
  353. card = player.RemainCards[^1];
  354. }
  355. // todo 真人托管状态
  356. }
  357. if (!player.RemainCards.Contains(card))
  358. {
  359. Log.Error($"出牌错误,玩家的牌不在手中...");
  360. return;
  361. }
  362. // 玩家出牌
  363. player.RemainCards = CardHelper.Remove(player.RemainCards, card);
  364. player.DisCards = CardHelper.Add(player.DisCards, card);
  365. player.DiscardCount += 1;
  366. self.DisCard = card;
  367. self.DisCardPlayer = player;
  368. self.PengPlayer = null;
  369. // // 给出牌人广播
  370. // MessageHelper.SendToClient(player, new G2C_HGHHDisCardPush(){info = ProtoHelper.RoomToProto(room, player, player)});
  371. self.CanHuIds.Clear();
  372. self.ClickHuIds.Clear();
  373. self.OperableList.Clear();
  374. self.CanPgIds.Clear();
  375. self.CleanUserAct();
  376. // 检测其它三家的动作
  377. int[] nextPos = room.GetSorcPos(player.Pos);
  378. foreach (int index in nextPos)
  379. {
  380. Player otherPlayer = self.Players[index];
  381. if (otherPlayer is not { State: 2 })
  382. {
  383. continue;
  384. }
  385. // 吃,碰,杠,胡,过
  386. int[] act = new int[5];
  387. List<Struct.Kezi> gangs = new List<Struct.Kezi>();
  388. List<Struct.Kezi> pengs = new List<Struct.Kezi>();
  389. List<Struct.Kezi> chis = new List<Struct.Kezi>();
  390. // todo 玩家听牌状态不允许有动作
  391. // 出牌是否杠
  392. gangs = HGHHHelper.IsDiscardGang(otherPlayer, card, player.Id);
  393. // 出牌是否碰
  394. pengs = HGHHHelper.IsDiscardPeng(otherPlayer, card, player.Id);
  395. // 出牌是否吃
  396. if (otherPlayer.Pos == nextPos[0])
  397. {
  398. chis = HGHHHelper.IsDiscardChi(otherPlayer, card, player.Id);
  399. }
  400. bool hasAct = false;
  401. int[] tmpCards = new int[otherPlayer.RemainCards.Length];
  402. Array.Copy(otherPlayer.RemainCards, 0, tmpCards, 0, otherPlayer.RemainCards.Length);
  403. tmpCards = CardHelper.Add(tmpCards, card);
  404. // 校验胡牌
  405. Struct.HuRes huType = HGHHHelper.CheckHu(tmpCards);
  406. if (huType.Type != HGHHConst.HU_DEFAULT)
  407. {
  408. hasAct = true;
  409. act[3] = 1;
  410. // 放入可胡玩家列表
  411. if (!self.CanHuIds.Contains(otherPlayer.Id))
  412. {
  413. self.CanHuIds.Add(otherPlayer.Id);
  414. }
  415. // 加入可操作玩家list
  416. if (!self.OperableList.Contains(otherPlayer.Id))
  417. {
  418. self.OperableList.Add(otherPlayer.Id);
  419. }
  420. }
  421. // 是否杠
  422. if (gangs.Count > 0)
  423. {
  424. hasAct = true;
  425. act[2] = 1;
  426. foreach (Struct.Kezi kezi in gangs.Where(kezi => kezi is { Card: >= 0 }))
  427. {
  428. otherPlayer.ActInfo.Add(new Struct.Kezi(kezi.Type, kezi.Card, kezi.PlayerId));
  429. }
  430. }
  431. // 是否碰
  432. if (pengs.Count > 0)
  433. {
  434. hasAct = true;
  435. act[1] = 1;
  436. foreach (Struct.Kezi kezi in pengs.Where(kezi => kezi is { Card: >= 0 }))
  437. {
  438. otherPlayer.ActInfo.Add(new Struct.Kezi(kezi.Type, kezi.Card, kezi.PlayerId));
  439. }
  440. }
  441. // 是否吃
  442. if (chis.Count > 0)
  443. {
  444. hasAct = true;
  445. act[0] = 1;
  446. foreach (Struct.Kezi kezi in chis.Where(kezi => kezi is { Card: >= 0 }))
  447. {
  448. otherPlayer.ActInfo.Add(new Struct.Kezi(kezi.Type, kezi.Card, kezi.PlayerId));
  449. }
  450. }
  451. // 是否过
  452. if (hasAct) {
  453. act[4] = 1;
  454. }
  455. if (act[2] == 1 || act[1] == 1 || act[0] == 1) {
  456. // 加入可操作玩家list
  457. if (!self.OperableList.Contains(otherPlayer.Id)) {
  458. self.OperableList.Add(otherPlayer.Id);
  459. }
  460. // 加入可碰杠玩家集合
  461. if (!self.CanPgIds.Contains(otherPlayer.Id)) {
  462. self.CanPgIds.Add(otherPlayer.Id);
  463. }
  464. }
  465. // 设置玩家动作
  466. otherPlayer.Act = act;
  467. // // 广播其它三家
  468. // MessageHelper.SendToClient(otherPlayer, new G2C_HGHHDisCardPush(){info = ProtoHelper.RoomToProto(room, otherPlayer, player)});
  469. }
  470. if (self.CanHuIds.Count <= 0)
  471. {
  472. self.GangPlayer = null;
  473. }
  474. // 判断是否下家摸牌, 指定下家摸牌
  475. if (self.OperableList.Count == 0)
  476. {
  477. Player nextPlayer = null;
  478. foreach (int index in nextPos)
  479. {
  480. Player tempPlayer = self.Players[index];
  481. if (tempPlayer is { State: 2 } && tempPlayer.Id != player.Id)
  482. {
  483. nextPlayer = tempPlayer;
  484. break;
  485. }
  486. }
  487. // 有三家认输,就会有null
  488. if (nextPlayer == null)
  489. {
  490. self.GameOver(room);
  491. }
  492. else
  493. {
  494. // 设置摸牌玩家
  495. self.DrawCardPlayer = nextPlayer;
  496. // 设置操作玩家
  497. self.CurrentPlayer = nextPlayer;
  498. // 摸牌
  499. self.Flag = false;
  500. }
  501. }
  502. else
  503. {
  504. long id = 0;
  505. if (self.CanHuIds.Count > 0)
  506. {
  507. id = self.CanHuIds.First();
  508. } else if (self.CanPgIds.Count > 0)
  509. {
  510. id = self.CanPgIds.First();
  511. }
  512. else
  513. {
  514. id = self.OperableList.First();
  515. }
  516. Player tmpPlayer = room.GetPlayer(id);
  517. if (tmpPlayer != null)
  518. {
  519. self.CurrentPlayer = self.Players[tmpPlayer.Pos];
  520. }
  521. }
  522. // 重置时间和流程标记
  523. self.Time = 30;
  524. self.Flag = false;
  525. // 给出牌人广播
  526. MessageHelper.SendToClient(player, new G2C_HGHHDisCardPush(){info = ProtoHelper.RoomToProto(room, player, self.CurrentPlayer)});
  527. // 给其他三家广播
  528. foreach (int index in nextPos)
  529. {
  530. Player otherPlayer = self.Players[index];
  531. if (otherPlayer != null)
  532. {
  533. // 广播其它三家
  534. MessageHelper.SendToClient(otherPlayer, new G2C_HGHHDisCardPush(){info = ProtoHelper.RoomToProto(room, otherPlayer, self.CurrentPlayer)});
  535. }
  536. }
  537. }
  538. }
  539. }
  540. /// <summary>
  541. /// 玩家是否可以操作吃
  542. /// </summary>
  543. /// <param name="self"></param>
  544. /// <param name="player"></param>
  545. /// <returns></returns>
  546. private static bool IsCanChi(this HGHHComponent self, Player player)
  547. {
  548. if (self.ClickHuIds.Count > 0)
  549. {
  550. return false;
  551. }
  552. if (self.CanHuIds.Count > 1)
  553. {
  554. return false;
  555. }
  556. else
  557. {
  558. if (self.CanHuIds.Count == 1)
  559. {
  560. if (self.CanPgIds.Count > 0)
  561. {
  562. if (self.CanPgIds.Any(id => id > 0 && id != player.Id))
  563. {
  564. return false;
  565. }
  566. }
  567. return self.CanHuIds.Contains(player.Id);
  568. }
  569. else
  570. {
  571. if (self.CanPgIds.Count > 0)
  572. {
  573. return self.CanPgIds.Contains(player.Id);
  574. }
  575. else
  576. {
  577. return true;
  578. }
  579. }
  580. }
  581. }
  582. /// <summary>
  583. /// 玩家操作吃
  584. /// </summary>
  585. /// <param name="self"></param>
  586. /// <param name="room"></param>
  587. /// <param name="player"></param>
  588. /// <param name="card"></param>
  589. public static void Chi(this HGHHComponent self, Room room, Player player, int card)
  590. {
  591. if (!self.IsCanChi(player))
  592. {
  593. return;
  594. }
  595. if (self.DisCard >= HGHHConst.DONG_FENG)
  596. {
  597. return;
  598. }
  599. // 删除出牌人打出的牌堆
  600. self.DisCardPlayer.DisCards = CardHelper.Remove(self.DisCardPlayer.DisCards, self.DisCard);
  601. // 吃牌玩家的手牌
  602. int[] temp = CardHelper.Add(player.RemainCards, self.DisCard);
  603. for (int i = 0; i < 3; i++)
  604. {
  605. temp = CardHelper.Remove(temp, card + i);
  606. }
  607. player.RemainCards = temp;
  608. // 刻子
  609. player.KeZi.Add(new Struct.Kezi((int)HGHHConst.KeziType.CHI, card, self.DisCardPlayer.Id));
  610. // 重置摸牌人
  611. self.DrawCardPlayer = player;
  612. self.PengPlayer = player;
  613. self.OperableList.Clear();
  614. // 重置时间和流程标记
  615. self.Time = 30;
  616. self.Flag = true;
  617. Log.Info($"玩家吃牌... 玩家ID:{player.Id}, 位置:{player.Pos}, 手牌大小:{player.RemainCards.Length}, 手牌信息:{string.Join(", ", player.RemainCards)}, 吃的牌:{card}");
  618. // 广播
  619. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  620. {
  621. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  622. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 1});
  623. }
  624. }
  625. /// <summary>
  626. /// 玩家是否可以操作碰
  627. /// </summary>
  628. /// <param name="self"></param>
  629. /// <param name="player"></param>
  630. /// <returns></returns>
  631. private static bool IsCanPengGang(this HGHHComponent self, Player player)
  632. {
  633. if (!self.CanPgIds.Contains(player.Id))
  634. {
  635. return false;
  636. }
  637. if (self.ClickHuIds.Count > 0)
  638. {
  639. return false;
  640. }
  641. return self.CanHuIds.Count switch
  642. {
  643. > 1 => false,
  644. 1 => self.CanHuIds.Contains(player.Id),
  645. _ => true
  646. };
  647. }
  648. /// <summary>
  649. /// 玩家操作碰
  650. /// </summary>
  651. /// <param name="self"></param>
  652. /// <param name="room"></param>
  653. /// <param name="player"></param>
  654. public static void Peng(this HGHHComponent self, Room room, Player player)
  655. {
  656. if (!self.IsCanPengGang(player))
  657. {
  658. return;
  659. }
  660. int num = CardHelper.CountCardNum(player.RemainCards, self.DisCard);
  661. if (num < 2)
  662. {
  663. return;
  664. }
  665. // 删除出牌人打出的牌堆
  666. int[] temp = CardHelper.Remove(self.DisCardPlayer.DisCards, self.DisCard);
  667. self.DisCardPlayer.DisCards = temp;
  668. // 碰牌玩家的手牌
  669. int[] remainCards = player.RemainCards;
  670. for (int i = 0; i < 2; i++)
  671. {
  672. remainCards = CardHelper.Remove(remainCards, self.DisCard);
  673. }
  674. player.RemainCards = remainCards;
  675. // 刻子
  676. player.KeZi.Add(new Struct.Kezi((int)HGHHConst.KeziType.PENG, self.DisCard, self.DisCardPlayer.Id));
  677. // 重置摸牌人
  678. self.DrawCardPlayer = player;
  679. self.PengPlayer = player;
  680. self.OperableList.Clear();
  681. // 重置时间和摸牌标记
  682. self.Time = 30;
  683. self.Flag = true;
  684. Log.Info($"玩家碰牌... 玩家ID:{player.Id}, 位置:{player.Pos}, 手牌大小:{player.RemainCards.Length}, 手牌信息:{string.Join(", ", player.RemainCards)}, 碰的牌:{self.DisCard}");
  685. // 广播
  686. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  687. {
  688. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  689. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 2});
  690. }
  691. }
  692. /// <summary>
  693. /// 摸牌杠(暗杠或者回杠)
  694. /// </summary>
  695. /// <param name="self"></param>
  696. /// <param name="room"></param>
  697. /// <param name="player"></param>
  698. /// <param name="card"></param>
  699. /// <returns></returns>
  700. private static int GangPaiDraw(this HGHHComponent self, Room room, Player player, int card)
  701. {
  702. int type = (int)HGHHConst.KeziType.DEFAULT;
  703. int[] remainCards = player.RemainCards;
  704. int num = CardHelper.CountCardNum(remainCards, card);
  705. if (num == 4)
  706. {
  707. for (int i = 0; i < 4; i++)
  708. {
  709. remainCards = CardHelper.Remove(remainCards, card);
  710. }
  711. player.RemainCards = remainCards;
  712. player.KeZi.Add(new Struct.Kezi((int)HGHHConst.KeziType.AN_GANG, card, player.Id));
  713. self.GangPlayer = player;
  714. self.GangPai = card;
  715. self.GangType = (int)HGHHConst.KeziType.AN_GANG;
  716. type = (int)HGHHConst.KeziType.AN_GANG;
  717. }
  718. else if (num == 1)
  719. {
  720. List<Struct.Kezi> templist = new List<Struct.Kezi>();
  721. if (player.KeZi.Count > 0)
  722. {
  723. templist = player.KeZi;
  724. }
  725. for (int i = 0; i < templist.Count; i++)
  726. {
  727. Struct.Kezi kz = templist[i];
  728. if (kz is not { Type: (int)HGHHConst.KeziType.PENG } || kz.Card != card)
  729. {
  730. continue;
  731. }
  732. player.KeZi.RemoveAt(i);
  733. remainCards = CardHelper.Remove(remainCards, card);
  734. player.KeZi.Add(new Struct.Kezi((int)HGHHConst.KeziType.HUI_GANG, card, player.Id));
  735. }
  736. player.RemainCards = remainCards;
  737. self.GangPlayer = player;
  738. self.GangPai = card;
  739. self.GangType = (int)HGHHConst.KeziType.HUI_GANG;
  740. type = (int)HGHHConst.KeziType.HUI_GANG;
  741. }
  742. return type;
  743. }
  744. /// <summary>
  745. /// 出牌杠(明杠)
  746. /// </summary>
  747. /// <param name="self"></param>
  748. /// <param name="room"></param>
  749. /// <param name="player"></param>
  750. /// <param name="card"></param>
  751. /// <returns></returns>
  752. private static int GangPaiDisCard(this HGHHComponent self, Room room, Player player, int card)
  753. {
  754. const int type = (int)HGHHConst.KeziType.DEFAULT;
  755. if (self.DisCardPlayer == null)
  756. {
  757. return type;
  758. }
  759. self.DisCardPlayer.DisCards = CardHelper.Remove(self.DisCardPlayer.DisCards, self.DisCard);
  760. for (int i = 0; i < 3; i++)
  761. {
  762. player.RemainCards = CardHelper.Remove(player.RemainCards, card);
  763. }
  764. player.KeZi.Add(new Struct.Kezi((int)HGHHConst.KeziType.MING_GANG, card, self.DisCardPlayer.Id));
  765. self.GangPlayer = player;
  766. self.GangPai = card;
  767. self.GangType = (int)HGHHConst.KeziType.MING_GANG;
  768. return (int)HGHHConst.KeziType.MING_GANG;
  769. }
  770. /// <summary>
  771. /// 检测是否有抢杠胡
  772. /// </summary>
  773. /// <param name="self"></param>
  774. /// <param name="room"></param>
  775. /// <param name="player"></param>
  776. /// <param name="card">杠的牌</param>
  777. /// <returns></returns>
  778. private static bool CheckQangGangHu(this HGHHComponent self, Room room, Player player, int card)
  779. {
  780. bool flag = false;
  781. int[] nextPos = room.GetSorcPos(player.Pos);
  782. foreach (int index in nextPos) {
  783. Player p = self.Players[index];
  784. if (p == null)
  785. {
  786. continue;
  787. }
  788. // 玩家手牌
  789. int[] tmpCards = new int[p.RemainCards.Length];
  790. Array.Copy(p.RemainCards, 0, tmpCards, 0, p.RemainCards.Length);
  791. tmpCards = CardHelper.Add(tmpCards, card);
  792. Struct.HuRes result = HGHHHelper.CheckHu(tmpCards);
  793. if (result.Type == HGHHConst.HU_DEFAULT)
  794. {
  795. continue;
  796. }
  797. flag = true;
  798. if (!self.CanHuIds.Contains(p.Id)) {
  799. self.CanHuIds.Add(p.Id);
  800. }
  801. if (!self.OperableList.Contains(p.Id)) {
  802. self.OperableList.Add(p.Id);
  803. }
  804. if (!self.GangHuIds.Contains(p.Id)) {
  805. self.GangHuIds.Add(p.Id);
  806. }
  807. p.Act = new [] { 0, 0, 0, 1, 1 };
  808. }
  809. return flag;
  810. }
  811. /// <summary>
  812. /// 玩家操作杠
  813. /// </summary>
  814. /// <param name="self"></param>
  815. /// <param name="room"></param>
  816. /// <param name="player"></param>
  817. /// <param name="card"></param>
  818. public static void Gang(this HGHHComponent self, Room room, Player player, int card)
  819. {
  820. if (card <= 0 || !self.IsCanPengGang(player))
  821. {
  822. return;
  823. }
  824. int type = (int)HGHHConst.KeziType.DEFAULT;
  825. if (self.DrawCardPlayer.Id == player.Id)
  826. {
  827. type = self.GangPaiDraw(room, player, card);
  828. }
  829. else if (self.DrawCardPlayer.Id != player.Id && self.DisCard == card)
  830. {
  831. type = self.GangPaiDisCard(room, player, card);
  832. }
  833. if (type == (int)HGHHConst.KeziType.DEFAULT)
  834. {
  835. return;
  836. }
  837. self.OperableList.Clear();
  838. // 重置时间和摸牌标记
  839. self.Time = 30;
  840. self.Flag = false;
  841. // bool flag = true;
  842. if (self.GangType == (int)HGHHConst.KeziType.HUI_GANG)
  843. {
  844. if (self.CheckQangGangHu(room, self.GangPlayer, self.GangPai))
  845. {
  846. // 当前操作玩家
  847. Player tmpPlayer = room.GetPlayer(self.CanHuIds.First());
  848. if (tmpPlayer != null)
  849. {
  850. self.CurrentPlayer = self.Players[tmpPlayer.Pos];
  851. }
  852. // flag = false;
  853. }
  854. }
  855. // 广播
  856. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  857. {
  858. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  859. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 3});
  860. }
  861. }
  862. /// <summary>
  863. /// 自摸胡牌(手动/自动)
  864. /// </summary>
  865. /// <param name="self"></param>
  866. /// <param name="room"></param>
  867. /// <param name="player"></param>
  868. private static void ZimoHuPai(this HGHHComponent self, Room room, Player player)
  869. {
  870. if (self.OperableList.Contains(player.Id) && self.CanHuIds.Contains(player.Id))
  871. {
  872. Struct.HuRes res = HGHHHelper.CheckHuType(player.KeZi, player.RemainCards, self.DrawCard);
  873. if (res.Type != HGHHConst.HU_DEFAULT)
  874. {
  875. self.OperableList.Remove(player.Id);
  876. self.CanHuIds.Remove(player.Id);
  877. self.ClickHuIds.Remove(player.Id);
  878. // 将摸的牌从手牌中移除
  879. player.RemainCards = CardHelper.Remove(player.RemainCards, self.DrawCard);
  880. self.HuResult = self.GangPlayer != null && player.Id == self.GangPlayer.Id ? (int)HGHHConst.Result.GANGKAI : (int)HGHHConst.Result.ZIMO;
  881. // 广播
  882. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  883. {
  884. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  885. info.Time = 15;
  886. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 4});
  887. }
  888. // 自摸胡牌结算
  889. self.SettlementHu();
  890. }
  891. self.GameOver(room);
  892. }
  893. else
  894. {
  895. Log.Error($"自摸胡牌出错, playerId={player.Id}");
  896. }
  897. }
  898. /// <summary>
  899. /// 点炮胡牌广播
  900. /// </summary>
  901. /// <param name="self"></param>
  902. /// <param name="room"></param>
  903. /// <param name="player"></param>
  904. /// <param name="huType">牌型</param>
  905. /// <param name="isGangHu">是否抢杠胡</param>
  906. private static void DianPaoHuPaiBroadcast(this HGHHComponent self, Room room, Player player, int huType, bool isGangHu)
  907. {
  908. if (isGangHu)
  909. {
  910. // 移除抢杠胡集合玩家
  911. self.GangHuIds.Remove(player.Id);
  912. // 获取杠玩家
  913. Player gangPlayer = self.GangPlayer;
  914. if (gangPlayer == null)
  915. {
  916. return;
  917. }
  918. foreach (Struct.Kezi kezi in gangPlayer.KeZi.Where(kezi => kezi.Card == self.GangPai && kezi.Type == (int)HGHHConst.KeziType.HUI_GANG))
  919. {
  920. kezi.Type = (int)HGHHConst.KeziType.PENG;
  921. }
  922. self.HuResult = (int)HGHHConst.Result.QIANGGANG;
  923. }
  924. else
  925. {
  926. // 将玩家打出的牌从出牌集合中删除
  927. if (self.DisCardPlayer.DisCards.Contains(self.DisCard))
  928. {
  929. self.DisCardPlayer.DisCards = CardHelper.Remove(self.DisCardPlayer.DisCards, self.DisCard);
  930. }
  931. self.HuResult = (int)HGHHConst.Result.DIANPAO;
  932. }
  933. player.HuCards.Add(self.GangPai);
  934. // 广播
  935. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  936. {
  937. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  938. info.Time = 15;
  939. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 4});
  940. }
  941. }
  942. /// <summary>
  943. /// 点炮胡牌(手动/自动)
  944. /// </summary>
  945. /// <param name="self"></param>
  946. /// <param name="room"></param>
  947. /// <param name="player"></param>
  948. private static void DianPaoHuPai(this HGHHComponent self, Room room, Player player)
  949. {
  950. int card = 0;
  951. bool isGangHU = false;
  952. if (self.GangHuIds.Contains(player.Id))
  953. {
  954. card = self.GangPai;
  955. isGangHU = true;
  956. }
  957. else
  958. {
  959. card = self.DisCard;
  960. }
  961. if (self.CanHuIds.Contains(player.Id))
  962. {
  963. int[] userCards = player.RemainCards;
  964. int[] tmpCards = new int[userCards.Length];
  965. Array.Copy(userCards, 0, tmpCards, 0, userCards.Length);
  966. tmpCards = CardHelper.Add(tmpCards, card);
  967. Struct.HuRes res = HGHHHelper.CheckHuType(player.KeZi, tmpCards, card);
  968. if (res.Type != HGHHConst.HU_DEFAULT)
  969. {
  970. if (!self.ClickHuIds.Contains(player.Id))
  971. {
  972. self.ClickHuIds.Add(player.Id);
  973. }
  974. self.CanPgIds.Clear();
  975. self.OperableList.Remove(player.Id);
  976. self.CanHuIds.Remove(player.Id);
  977. // 广播
  978. self.DianPaoHuPaiBroadcast(room, player, res.Type, isGangHU);
  979. if (self.CanHuIds.Count == 0)
  980. {
  981. // 点炮胡结算
  982. self.SettlementHu();
  983. self.GameOver(room);
  984. }
  985. else
  986. {
  987. long id = self.CanHuIds.First();
  988. Player tmpPlayer = room.GetPlayer(id);
  989. if (tmpPlayer != null)
  990. {
  991. self.CurrentPlayer = tmpPlayer;
  992. }
  993. self.Time = 15;
  994. }
  995. }
  996. }
  997. }
  998. /// <summary>
  999. /// 玩家操作胡
  1000. /// </summary>
  1001. /// <param name="self"></param>
  1002. /// <param name="room"></param>
  1003. /// <param name="player"></param>
  1004. public static void Hu(this HGHHComponent self, Room room, Player player)
  1005. {
  1006. int r = player.RemainCards.Length % 3;
  1007. switch (r)
  1008. {
  1009. case 2:
  1010. self.ZimoHuPai(room, player);
  1011. break;
  1012. case 1:
  1013. self.DianPaoHuPai(room, player);
  1014. break;
  1015. }
  1016. }
  1017. /// <summary>
  1018. /// 玩家操作过
  1019. /// </summary>
  1020. /// <param name="self"></param>
  1021. /// <param name="room"></param>
  1022. /// <param name="player"></param>
  1023. public static void Guo(this HGHHComponent self, Room room, Player player)
  1024. {
  1025. // 玩家必须在可操作列表里
  1026. if (!self.OperableList.Contains(player.Id))
  1027. {
  1028. return;
  1029. }
  1030. // 从碰杠集合中移除
  1031. self.CanPgIds.Remove(player.Id);
  1032. // 从可操作玩家集合中移除
  1033. self.OperableList.Remove(player.Id);
  1034. player.Act = new int[5];
  1035. player.ActInfo.Clear();
  1036. // 不是摸牌人,改time
  1037. if (self.DrawCardPlayer.Id != player.Id)
  1038. {
  1039. if (self.CanHuIds.Contains(player.Id))
  1040. {
  1041. self.CanHuIds.Remove(player.Id);
  1042. }
  1043. if (self.GangHuIds.Count > 0)
  1044. {
  1045. self.GangHuIds.Remove(player.Id);
  1046. if (self.GangHuIds.Count > 0)
  1047. {
  1048. // 设置操作动作玩家
  1049. if (self.CurrentPlayer.Id == player.Id)
  1050. {
  1051. self.CurrentPlayer = room.GetPlayer(self.GangHuIds.First());
  1052. self.Time = 15;
  1053. // 广播过
  1054. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  1055. {
  1056. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  1057. info.Time = 15;
  1058. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 5});
  1059. }
  1060. }
  1061. }
  1062. else
  1063. {
  1064. if (self.ClickHuIds.Count == 0)
  1065. {
  1066. self.DrawCardPlayer = self.GangPlayer;
  1067. self.Flag = false;
  1068. }
  1069. else
  1070. {
  1071. // 没有其他人可以胡,且有人点击胡 结算胡 游戏结束
  1072. self.SettlementHu();
  1073. self.GameOver(room);
  1074. }
  1075. }
  1076. }
  1077. else
  1078. {
  1079. if (self.OperableList.Count == 0)
  1080. {
  1081. if (self.ClickHuIds.Count > 0)
  1082. {
  1083. // 没有其他人有胡 且有人点击胡 结算胡 游戏结束
  1084. self.SettlementHu();
  1085. self.GameOver(room);
  1086. }
  1087. else
  1088. {
  1089. self.DrawCardPlayer = self.GetNextPlayer(self.DisCardPlayer.Pos);
  1090. self.Flag = false;
  1091. }
  1092. }
  1093. else
  1094. {
  1095. if (self.CurrentPlayer.Id == player.Id)
  1096. {
  1097. long opId = 0;
  1098. if (self.CanHuIds.Count > 0)
  1099. {
  1100. self.CurrentPlayer = self.Players[room.GetPlayer(self.CanHuIds.First()).Pos];
  1101. opId = self.CanHuIds.First();
  1102. }
  1103. else if (self.CanPgIds.Count > 0)
  1104. {
  1105. self.CurrentPlayer = self.Players[room.GetPlayer(self.CanPgIds.First()).Pos];
  1106. opId = self.CanPgIds.First();
  1107. }
  1108. else
  1109. {
  1110. self.CurrentPlayer = self.Players[room.GetPlayer(self.OperableList.First()).Pos];
  1111. opId = self.OperableList.First();
  1112. }
  1113. self.Time = 15;
  1114. // 广播过
  1115. foreach (Player p in room.GetAllPlayers().Values.Where(p => p != null))
  1116. {
  1117. RoomInfo info = ProtoHelper.RoomToProto(room, p, player);
  1118. info.Time = 15;
  1119. MessageHelper.SendToClient(p, new G2C_HGHHOperationPush(){info = info, OpType = 5});
  1120. }
  1121. }
  1122. }
  1123. if (self.CanHuIds.Count == 0)
  1124. {
  1125. self.GangPlayer = null;
  1126. }
  1127. }
  1128. }
  1129. else
  1130. {
  1131. if (self.CanHuIds.Contains(player.Id))
  1132. {
  1133. // 从胡牌集合中移除
  1134. self.CanHuIds.Remove(player.Id);
  1135. }
  1136. }
  1137. }
  1138. /// <summary>
  1139. /// 获取下个玩家
  1140. /// </summary>
  1141. /// <param name="self"></param>
  1142. /// <param name="curPos">当前玩家位置</param>
  1143. /// <returns></returns>
  1144. private static Player GetNextPlayer(this HGHHComponent self, int curPos)
  1145. {
  1146. return self.Players[(curPos + 1) % self.GetParent<Room>().MaxNum];
  1147. }
  1148. /// <summary>
  1149. /// 清空玩家可操作动作
  1150. /// </summary>
  1151. /// <param name="self"></param>
  1152. private static void CleanUserAct(this HGHHComponent self) {
  1153. foreach (Player player in self.Players)
  1154. {
  1155. if (player != null)
  1156. {
  1157. player.Act = new int[5];
  1158. }
  1159. }
  1160. }
  1161. /// <summary>
  1162. /// 胡牌结算
  1163. /// </summary>
  1164. /// <param name="self"></param>
  1165. private static void SettlementHu(this HGHHComponent self)
  1166. {
  1167. }
  1168. /// <summary>
  1169. /// 结束
  1170. /// </summary>
  1171. /// <param name="self"></param>
  1172. /// <param name="room"></param>
  1173. private static void GameOver(this HGHHComponent self, Room room)
  1174. {
  1175. self.State = 3;
  1176. self.Time = 60;
  1177. self.Flag = false;
  1178. foreach (Player player in self.Players)
  1179. {
  1180. if (player != null)
  1181. {
  1182. player.State = 3;
  1183. }
  1184. }
  1185. Log.Info($"牌局结束...roomId={room.RoomId}");
  1186. }
  1187. }
  1188. }