You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

ActionManager.cs 23 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492
  1. using System;
  2. using System.Threading;
  3. using GameClass.GameObj;
  4. using GameEngine;
  5. using Preparation.Interface;
  6. using Preparation.Utility;
  7. using Timothy.FrameRateTask;
  8. namespace Gaming
  9. {
  10. public partial class Game
  11. {
  12. private readonly ActionManager actionManager;
  13. private class ActionManager
  14. {
  15. // 人物移动
  16. private void SkillWhenColliding(Character player, IGameObj collisionObj)
  17. {
  18. if (collisionObj.Type == GameObjType.Bullet)
  19. {
  20. if (((Bullet)collisionObj).Parent != player && ((Bullet)collisionObj).TypeOfBullet == BulletType.JumpyDumpty)
  21. {
  22. if (characterManager.BeStunned((Character)player, GameData.timeOfStunnedWhenJumpyDumpty))
  23. player.AddScore(GameData.TrickerScoreStudentBeStunned(GameData.timeOfStunnedWhenJumpyDumpty));
  24. gameMap.Remove((GameObj)collisionObj);
  25. }
  26. }
  27. if (player.FindIActiveSkill(ActiveSkillType.CanBeginToCharge).IsBeingUsed && collisionObj.Type == GameObjType.Character && ((Character)collisionObj).IsGhost())
  28. {
  29. if (characterManager.BeStunned((Character)collisionObj, GameData.timeOfGhostStunnedWhenCharge))
  30. player.AddScore(GameData.StudentScoreTrickerBeStunned(GameData.timeOfGhostStunnedWhenCharge));
  31. characterManager.BeStunned(player, GameData.timeOfStudentStunnedWhenCharge);
  32. }
  33. }
  34. public bool MovePlayer(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
  35. {
  36. if (moveTimeInMilliseconds < 5) return false;
  37. if (!playerToMove.Commandable() || !TryToStop()) return false;
  38. characterManager.SetPlayerState(playerToMove, PlayerStateType.Moving);
  39. moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
  40. return true;
  41. }
  42. public bool MovePlayerWhenStunned(Character playerToMove, int moveTimeInMilliseconds, double moveDirection)
  43. {
  44. if (!playerToMove.Commandable() && playerToMove.PlayerState != PlayerStateType.Stunned) return false;
  45. characterManager.BeStunned(playerToMove, moveTimeInMilliseconds);
  46. moveEngine.MoveObj(playerToMove, moveTimeInMilliseconds, moveDirection);
  47. return true;
  48. }
  49. public bool Stop(Character player)
  50. {
  51. if (player.Commandable() || !TryToStop())
  52. {
  53. characterManager.SetPlayerState(player);
  54. return true;
  55. }
  56. return false;
  57. }
  58. public bool Fix(Student player)// 自动检查有无发电机可修
  59. {
  60. if (player.CharacterType == CharacterType.Teacher || (!player.Commandable()) || player.PlayerState == PlayerStateType.Fixing)
  61. return false;
  62. Generator? generatorForFix = (Generator?)gameMap.OneForInteract(player.Position, GameObjType.Generator);
  63. if (generatorForFix == null || generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator)
  64. return false;
  65. ++generatorForFix.NumOfFixing;
  66. characterManager.SetPlayerState(player, PlayerStateType.Fixing);
  67. new Thread
  68. (
  69. () =>
  70. {
  71. new FrameRateTaskExecutor<int>(
  72. loopCondition: () => gameMap.Timer.IsGaming && player.PlayerState == PlayerStateType.Fixing,
  73. loopToDo: () =>
  74. {
  75. if (generatorForFix.Repair(player.FixSpeed * GameData.frameDuration, player))
  76. gameMap.NumOfRepairedGenerators++;
  77. if (generatorForFix.DegreeOfRepair == GameData.degreeOfFixedGenerator)
  78. characterManager.SetPlayerState(player);
  79. },
  80. timeInterval: GameData.frameDuration,
  81. finallyReturn: () => 0
  82. )
  83. .Start();
  84. --generatorForFix.NumOfFixing;
  85. }
  86. )
  87. { IsBackground = true }.Start();
  88. return true;
  89. }
  90. public bool OpenDoorway(Student player)
  91. {
  92. if (!(player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheDoorway)
  93. return false;
  94. Doorway? doorwayToOpen = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway);
  95. if (doorwayToOpen == null || doorwayToOpen.OpenStartTime > 0 || !doorwayToOpen.PowerSupply)
  96. return false;
  97. characterManager.SetPlayerState(player, PlayerStateType.OpeningTheDoorway, doorwayToOpen);
  98. int startTime = doorwayToOpen.OpenStartTime = gameMap.Timer.nowTime();
  99. new Thread
  100. (
  101. () =>
  102. {
  103. Thread.Sleep(GameData.degreeOfOpenedDoorway - doorwayToOpen.OpenDegree);
  104. if (doorwayToOpen.OpenStartTime == startTime)
  105. {
  106. doorwayToOpen.OpenDegree = GameData.degreeOfOpenedDoorway;
  107. player.SetPlayerStateNaturally();
  108. }
  109. }
  110. )
  111. { IsBackground = true }.Start();
  112. return true;
  113. }
  114. public bool Escape(Student player)
  115. {
  116. if (!(player.Commandable()) || player.CharacterType == CharacterType.Robot || player.CharacterType == CharacterType.Teacher)
  117. return false;
  118. Doorway? doorwayForEscape = (Doorway?)gameMap.OneForInteract(player.Position, GameObjType.Doorway);
  119. if (doorwayForEscape != null && doorwayForEscape.IsOpen())
  120. {
  121. player.AddScore(GameData.StudentScoreEscape);
  122. ++gameMap.NumOfEscapedStudent;
  123. player.RemoveFromGame(PlayerStateType.Escaped);
  124. return true;
  125. }
  126. else
  127. {
  128. EmergencyExit? emergencyExit = (EmergencyExit?)gameMap.OneForInteract(player.Position, GameObjType.EmergencyExit);
  129. if (emergencyExit != null && emergencyExit.IsOpen)
  130. {
  131. player.AddScore(GameData.StudentScoreEscape);
  132. ++gameMap.NumOfEscapedStudent;
  133. player.RemoveFromGame(PlayerStateType.Escaped);
  134. return true;
  135. }
  136. return false;
  137. }
  138. }
  139. public bool Treat(Student player, Student? playerTreated = null)
  140. {
  141. if (playerTreated == null)
  142. {
  143. playerTreated = gameMap.StudentForInteract(player.Position);
  144. if (playerTreated == null) return false;
  145. }
  146. if (player == playerTreated || (!player.Commandable()) || playerTreated.PlayerState == PlayerStateType.Treated ||
  147. (!playerTreated.Commandable()) ||
  148. playerTreated.HP == playerTreated.MaxHp || !GameData.ApproachToInteract(playerTreated.Position, player.Position))
  149. return false;
  150. new Thread
  151. (
  152. () =>
  153. {
  154. characterManager.SetPlayerState(playerTreated, PlayerStateType.Treated);
  155. characterManager.SetPlayerState(player, PlayerStateType.Treating);
  156. new FrameRateTaskExecutor<int>(
  157. loopCondition: () => playerTreated.PlayerState == PlayerStateType.Treated && player.PlayerState == PlayerStateType.Treating && gameMap.Timer.IsGaming,
  158. loopToDo: () =>
  159. {
  160. if (playerTreated.AddDegreeOfTreatment(GameData.frameDuration * player.TreatSpeed, player))
  161. characterManager.SetPlayerState(playerTreated);
  162. },
  163. timeInterval: GameData.frameDuration,
  164. finallyReturn: () => 0
  165. )
  166. .Start();
  167. if (player.PlayerState == PlayerStateType.Treating) characterManager.SetPlayerState(player);
  168. else if (playerTreated.PlayerState == PlayerStateType.Treated) characterManager.SetPlayerState(playerTreated);
  169. }
  170. )
  171. { IsBackground = true }.Start();
  172. return true;
  173. }
  174. public bool Rescue(Student player, Student? playerRescued = null)
  175. {
  176. if (playerRescued == null)
  177. {
  178. playerRescued = gameMap.StudentForInteract(player.Position);
  179. if (playerRescued == null) return false;
  180. }
  181. if ((!player.Commandable()) || playerRescued.PlayerState != PlayerStateType.Addicted || !GameData.ApproachToInteract(playerRescued.Position, player.Position))
  182. return false;
  183. characterManager.SetPlayerState(player, PlayerStateType.Rescuing);
  184. characterManager.SetPlayerState(playerRescued, PlayerStateType.Rescued);
  185. new Thread
  186. (
  187. () =>
  188. {
  189. new FrameRateTaskExecutor<int>(
  190. loopCondition: () => playerRescued.PlayerState == PlayerStateType.Rescued && player.PlayerState == PlayerStateType.Rescuing && gameMap.Timer.IsGaming,
  191. loopToDo: () =>
  192. {
  193. playerRescued.TimeOfRescue += GameData.frameDuration;
  194. },
  195. timeInterval: GameData.frameDuration,
  196. finallyReturn: () => 0,
  197. maxTotalDuration: GameData.basicTimeOfRescue
  198. )
  199. .Start();
  200. if (playerRescued.PlayerState == PlayerStateType.Rescued)
  201. {
  202. if (playerRescued.TimeOfRescue >= GameData.basicTimeOfRescue)
  203. {
  204. characterManager.SetPlayerState(playerRescued);
  205. playerRescued.HP = playerRescued.MaxHp / 2;
  206. player.AddScore(GameData.StudentScoreRescue);
  207. }
  208. else
  209. characterManager.SetPlayerState(playerRescued, PlayerStateType.Addicted);
  210. }
  211. if (player.PlayerState == PlayerStateType.Rescuing) characterManager.SetPlayerState(player);
  212. playerRescued.TimeOfRescue = 0;
  213. }
  214. )
  215. { IsBackground = true }.Start();
  216. return true;
  217. }
  218. public bool OpenChest(Character player)
  219. {
  220. if ((!player.Commandable()) || player.PlayerState == PlayerStateType.OpeningTheChest)
  221. return false;
  222. Chest? chestToOpen = (Chest?)gameMap.OneForInteract(player.Position, GameObjType.Chest);
  223. if (chestToOpen == null || chestToOpen.OpenStartTime > 0)
  224. return false;
  225. characterManager.SetPlayerState(player, PlayerStateType.OpeningTheChest, chestToOpen);
  226. int startTime = gameMap.Timer.nowTime();
  227. chestToOpen.Open(startTime, player);
  228. new Thread
  229. (
  230. () =>
  231. {
  232. Thread.Sleep(GameData.degreeOfOpenedChest / player.SpeedOfOpenChest);
  233. if (chestToOpen.OpenStartTime == startTime)
  234. {
  235. player.SetPlayerStateNaturally();
  236. for (int i = 0; i < GameData.maxNumOfPropInChest; ++i)
  237. {
  238. Prop prop = chestToOpen.PropInChest[i];
  239. chestToOpen.PropInChest[i] = new NullProp();
  240. prop.ReSetPos(player.Position, gameMap.GetPlaceType(player.Position));
  241. gameMap.Add(prop);
  242. }
  243. }
  244. }
  245. )
  246. { IsBackground = true }.Start();
  247. return true;
  248. }
  249. public bool ClimbingThroughWindow(Character player)
  250. {
  251. if (!player.Commandable())
  252. return false;
  253. Window? windowForClimb = (Window?)gameMap.OneForInteractInACross(player.Position, GameObjType.Window);
  254. if (windowForClimb == null || windowForClimb.WhoIsClimbing != null)
  255. return false;
  256. XY windowToPlayer = new(
  257. (Math.Abs(player.Position.x - windowForClimb.Position.x) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.x > windowForClimb.Position.x ? 1 : -1)) : 0,
  258. (Math.Abs(player.Position.y - windowForClimb.Position.y) > GameData.numOfPosGridPerCell / 2) ? (GameData.numOfPosGridPerCell / 2 * (player.Position.y > windowForClimb.Position.y ? 1 : -1)) : 0);
  259. /* Character? characterInWindow = (Character?)gameMap.OneInTheSameCell(windowForClimb.Position - 2 * windowToPlayer, GameObjType.Character);
  260. if (characterInWindow != null)
  261. {
  262. if (player.IsGhost() && !characterInWindow.IsGhost())
  263. characterManager.BeAttacked((Student)(characterInWindow), player.Attack(characterInWindow.Position, PlaceType.Null));
  264. return false;
  265. }*/
  266. //Wall addWall = new Wall(windowForClimb.Position - 2 * windowToPlayer);
  267. // gameMap.Add(addWall);
  268. characterManager.SetPlayerState(player, PlayerStateType.ClimbingThroughWindows);
  269. windowForClimb.WhoIsClimbing = player;
  270. new Thread
  271. (
  272. () =>
  273. {
  274. new FrameRateTaskExecutor<int>(
  275. loopCondition: () => player.PlayerState == PlayerStateType.ClimbingThroughWindows && gameMap.Timer.IsGaming,
  276. loopToDo: () => { },
  277. timeInterval: GameData.frameDuration,
  278. finallyReturn: () => 0,
  279. maxTotalDuration: (int)((windowToPlayer + windowForClimb.Position - player.Position).Length() * 1000 / player.MoveSpeed)
  280. )
  281. .Start();
  282. if (player.PlayerState != PlayerStateType.ClimbingThroughWindows)
  283. {
  284. windowForClimb.WhoIsClimbing = null;
  285. return;
  286. }
  287. player.ReSetPos(windowToPlayer + windowForClimb.Position, PlaceType.Window);
  288. player.MoveSpeed = player.SpeedOfClimbingThroughWindows;
  289. moveEngine.MoveObj(player, (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed), (-1 * windowToPlayer).Angle());
  290. new FrameRateTaskExecutor<int>(
  291. loopCondition: () => player.PlayerState == PlayerStateType.ClimbingThroughWindows && gameMap.Timer.IsGaming,
  292. loopToDo: () =>
  293. {
  294. },
  295. timeInterval: GameData.frameDuration,
  296. finallyReturn: () => 0,
  297. maxTotalDuration: (int)(windowToPlayer.Length() * 3.0 * 1000 / player.MoveSpeed)
  298. )
  299. .Start();
  300. XY PosJumpOff = windowForClimb.Position - 2 * windowToPlayer;
  301. player.ReSetPos(PosJumpOff, gameMap.GetPlaceType(PosJumpOff));
  302. player.MoveSpeed = player.ReCalculateBuff(BuffType.AddSpeed, player.OrgMoveSpeed, GameData.MaxSpeed, GameData.MinSpeed);
  303. windowForClimb.WhoIsClimbing = null;
  304. // gameMap.Remove(addWall);
  305. if (player.PlayerState == PlayerStateType.ClimbingThroughWindows)
  306. {
  307. characterManager.SetPlayerState(player);
  308. }
  309. }
  310. )
  311. { IsBackground = true }.Start();
  312. return true;
  313. }
  314. public bool LockOrOpenDoor(Character player)
  315. {
  316. if (!(player.Commandable()) || player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor)
  317. return false;
  318. Door? doorToLock = (Door?)gameMap.OneForInteract(player.Position, GameObjType.Door);
  319. if (doorToLock == null || doorToLock.OpenOrLockDegree > 0 || gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character) != null)
  320. return false;
  321. bool flag = false;
  322. foreach (Prop prop in player.PropInventory)
  323. {
  324. switch (prop.GetPropType())
  325. {
  326. case PropType.Key3:
  327. if (doorToLock.Place == PlaceType.Door3)
  328. flag = true;
  329. break;
  330. case PropType.Key5:
  331. if (doorToLock.Place == PlaceType.Door5)
  332. flag = true;
  333. break;
  334. case PropType.Key6:
  335. if (doorToLock.Place == PlaceType.Door6)
  336. flag = true;
  337. break;
  338. default:
  339. break;
  340. }
  341. if (flag) break;
  342. }
  343. if (!flag) return false;
  344. characterManager.SetPlayerState(player, PlayerStateType.LockingOrOpeningTheDoor);
  345. new Thread
  346. (
  347. () =>
  348. {
  349. new FrameRateTaskExecutor<int>(
  350. loopCondition: () => flag && player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor && gameMap.Timer.IsGaming && doorToLock.OpenOrLockDegree < GameData.degreeOfLockingOrOpeningTheDoor,
  351. loopToDo: () =>
  352. {
  353. flag = ((gameMap.PartInTheSameCell(doorToLock.Position, GameObjType.Character)) == null);
  354. doorToLock.OpenOrLockDegree += GameData.frameDuration * player.SpeedOfOpeningOrLocking;
  355. },
  356. timeInterval: GameData.frameDuration,
  357. finallyReturn: () => 0
  358. )
  359. .Start();
  360. if (doorToLock.OpenOrLockDegree >= GameData.degreeOfLockingOrOpeningTheDoor)
  361. {
  362. doorToLock.IsOpen = (!doorToLock.IsOpen);
  363. }
  364. if (player.PlayerState == PlayerStateType.LockingOrOpeningTheDoor)
  365. characterManager.SetPlayerState(player);
  366. doorToLock.OpenOrLockDegree = 0;
  367. }
  368. )
  369. { IsBackground = true }.Start();
  370. return true;
  371. }
  372. /*
  373. private void ActivateMine(Character player, Mine mine)
  374. {
  375. gameMap.ObjListLock.EnterWriteLock();
  376. try { gameMap.ObjList.Remove(mine); }
  377. catch { }
  378. finally { gameMap.ObjListLock.ExitWriteLock(); }
  379. switch (mine.GetPropType())
  380. {
  381. case PropType.Dirt:
  382. player.AddMoveSpeed(Constant.dirtMoveSpeedDebuff, Constant.buffPropTime);
  383. break;
  384. case PropType.Attenuator:
  385. player.AddAP(Constant.attenuatorAtkDebuff, Constant.buffPropTime);
  386. break;
  387. case PropType.Divider:
  388. player.ChangeCD(Constant.dividerCdDiscount, Constant.buffPropTime);
  389. break;
  390. }
  391. }
  392. */
  393. private object numLock = new object();
  394. private int lastTime = 0;
  395. private int numStop = 0;
  396. private int NumStop => numStop;
  397. private bool TryToStop()
  398. {
  399. lock (numLock)
  400. {
  401. int time = gameMap.Timer.nowTime();
  402. if (time / GameData.frameDuration > lastTime)
  403. {
  404. lastTime = time / GameData.frameDuration;
  405. numStop = 1;
  406. return true;
  407. }
  408. else
  409. {
  410. if (numStop == GameData.LimitOfStopAndMove)
  411. return false;
  412. else
  413. {
  414. ++numStop;
  415. return true;
  416. }
  417. }
  418. }
  419. }
  420. private readonly Map gameMap;
  421. private readonly CharacterManager characterManager;
  422. public readonly MoveEngine moveEngine;
  423. public ActionManager(Map gameMap, CharacterManager characterManager)
  424. {
  425. this.gameMap = gameMap;
  426. this.moveEngine = new MoveEngine(
  427. gameMap: gameMap,
  428. OnCollision: (obj, collisionObj, moveVec) =>
  429. {
  430. SkillWhenColliding((Character)obj, collisionObj);
  431. Preparation.Utility.Debugger.Output(obj, " end move with " + collisionObj.ToString());
  432. //if (collisionObj is Mine)
  433. //{
  434. // ActivateMine((Character)obj, (Mine)collisionObj);
  435. // return MoveEngine.AfterCollision.ContinueCheck;
  436. //}
  437. return MoveEngine.AfterCollision.MoveMax;
  438. },
  439. EndMove: obj =>
  440. {
  441. // Debugger.Output(obj, " end move at " + obj.Position.ToString() + " At time: " + Environment.TickCount64);
  442. }
  443. );
  444. this.characterManager = characterManager;
  445. }
  446. }
  447. }
  448. }