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.

Character.cs 14 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. using Preparation.GameData;
  2. using Preparation.Interface;
  3. using Preparation.Utility;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Threading;
  7. namespace GameClass.GameObj
  8. {
  9. public partial class Character : GameObj, ICharacter // 负责人LHR摆烂终了
  10. {
  11. private readonly object beAttackedLock = new();
  12. #region 角色的基本属性及方法,包括与道具的交互方法
  13. /*
  14. //THUAI5子弹
  15. /// <summary>
  16. /// 装弹冷却
  17. /// </summary>
  18. protected int cd;
  19. public int CD
  20. {
  21. get => cd;
  22. private
  23. set {
  24. lock (gameObjLock)
  25. {
  26. cd = value;
  27. Debugger.Output(this, string.Format("'s CD has been set to: {0}.", value));
  28. }
  29. }
  30. }
  31. public int OrgCD { get; protected set; }
  32. protected int maxBulletNum;
  33. public int MaxBulletNum => maxBulletNum; // 人物最大子弹数
  34. protected int bulletNum;
  35. public int BulletNum => bulletNum; // 目前持有的子弹数
  36. */
  37. public int MaxHp { get; protected set; } // 最大血量
  38. protected int hp;
  39. public int HP
  40. {
  41. get => hp;
  42. set {
  43. lock (gameObjLock)
  44. hp = value <= MaxHp ? value : MaxHp;
  45. }
  46. }
  47. private int deathCount = 0;
  48. public int DeathCount => deathCount; // 玩家的死亡次数
  49. private int score = 0;
  50. public int Score
  51. {
  52. get => score;
  53. }
  54. // public double AttackRange => BulletFactory.BulletAttackRange(this.BulletOfPlayer);
  55. private double vampire = 0; // 回血率:0-1之间
  56. public double Vampire
  57. {
  58. get => vampire;
  59. set {
  60. if (value > 1)
  61. lock (gameObjLock)
  62. vampire = 1;
  63. else if (value < 0)
  64. lock (gameObjLock)
  65. vampire = 0;
  66. else
  67. lock (gameObjLock)
  68. vampire = value;
  69. }
  70. }
  71. private double OriVampire { get; }
  72. /*
  73. public readonly BulletType OriBulletOfPlayer;
  74. private BulletType bulletOfPlayer;
  75. public BulletType BulletOfPlayer
  76. {
  77. get => bulletOfPlayer;
  78. set {
  79. lock (gameObjLock)
  80. bulletOfPlayer = value;
  81. }
  82. }
  83. */
  84. private Prop? propInventory;
  85. public Prop? PropInventory // 持有的道具
  86. {
  87. get => propInventory;
  88. set {
  89. lock (gameObjLock)
  90. {
  91. propInventory = value;
  92. Debugger.Output(this, " prop becomes " + (PropInventory == null ? "null" : PropInventory.ToString()));
  93. }
  94. }
  95. }
  96. /// <summary>
  97. /// 使用物品栏中的道具
  98. /// </summary>
  99. /// <returns>被使用的道具</returns>
  100. public Prop? UseProp()
  101. {
  102. lock (gameObjLock)
  103. {
  104. var oldProp = PropInventory;
  105. PropInventory = null;
  106. return oldProp;
  107. }
  108. }
  109. /// <summary>
  110. /// 是否正在更换道具(包括捡起与抛出)
  111. /// </summary>
  112. private bool isModifyingProp = false;
  113. public bool IsModifyingProp
  114. {
  115. get => isModifyingProp;
  116. set {
  117. lock (gameObjLock)
  118. {
  119. isModifyingProp = value;
  120. }
  121. }
  122. }
  123. /// <summary>
  124. /// 是否在隐身
  125. /// </summary>
  126. private bool isInvisible = false;
  127. public bool IsInvisible
  128. {
  129. get => isInvisible;
  130. set {
  131. lock (gameObjLock)
  132. {
  133. isInvisible = value;
  134. }
  135. }
  136. }
  137. /*
  138. /// <summary>
  139. /// 进行一次远程攻击
  140. /// </summary>
  141. /// <param name="posOffset">子弹初始位置偏差值</param>
  142. /// <returns>攻击操作发出的子弹</returns>
  143. public Bullet? RemoteAttack(XY posOffset)
  144. {
  145. if (TrySubBulletNum())
  146. return ProduceOneBullet(this.Position + posOffset);
  147. else
  148. return null;
  149. }
  150. protected Bullet? ProduceOneBullet(XY initPos)
  151. {
  152. var newBullet = BulletFactory.GetBullet(this);
  153. newBullet?.SetPosition(initPos);
  154. return newBullet;
  155. }
  156. /// <summary>
  157. /// 尝试将子弹数量减1
  158. /// </summary>
  159. /// <returns>减操作是否成功</returns>
  160. private bool TrySubBulletNum()
  161. {
  162. lock (gameObjLock)
  163. {
  164. if (bulletNum > 0)
  165. {
  166. --bulletNum;
  167. return true;
  168. }
  169. return false;
  170. }
  171. }
  172. /// <summary>
  173. /// 尝试将子弹数量加1
  174. /// </summary>
  175. /// <returns>加操作是否成功</returns>
  176. public bool TryAddBulletNum()
  177. {
  178. lock (gameObjLock)
  179. {
  180. if (bulletNum < maxBulletNum)
  181. {
  182. ++bulletNum;
  183. return true;
  184. }
  185. return false;
  186. }
  187. }
  188. */
  189. /// <summary>
  190. /// 尝试加血
  191. /// </summary>
  192. /// <param name="add">欲加量</param>
  193. /// <returns>加操作是否成功</returns>
  194. public bool TryAddHp(int add)
  195. {
  196. if (hp < MaxHp)
  197. {
  198. lock (gameObjLock)
  199. hp = MaxHp > hp + add ? hp + add : MaxHp;
  200. Debugger.Output(this, " hp has added to: " + hp.ToString());
  201. return true;
  202. }
  203. return false;
  204. }
  205. /// <summary>
  206. /// 尝试减血
  207. /// </summary>
  208. /// <param name="sub">减血量</param>
  209. /// <returns>减操作是否成功</returns>
  210. public bool TrySubHp(int sub)
  211. {
  212. if (hp > 0)
  213. {
  214. lock (gameObjLock)
  215. hp = 0 >= hp - sub ? 0 : hp - sub;
  216. Debugger.Output(this, " hp has subed to: " + hp.ToString());
  217. return true;
  218. }
  219. return false;
  220. }
  221. /// <summary>
  222. /// 增加死亡次数
  223. /// </summary>
  224. /// <returns>当前死亡次数</returns>
  225. private int AddDeathCount()
  226. {
  227. lock (gameObjLock)
  228. {
  229. ++deathCount;
  230. return deathCount;
  231. }
  232. }
  233. /// <summary>
  234. /// 加分
  235. /// </summary>
  236. /// <param name="add">增加量</param>
  237. public void AddScore(int add)
  238. {
  239. lock (gameObjLock)
  240. {
  241. score += add;
  242. Debugger.Output(this, " 's score has been added to: " + score.ToString());
  243. }
  244. }
  245. /// <summary>
  246. /// 减分
  247. /// </summary>
  248. /// <param name="sub">减少量</param>
  249. public void SubScore(int sub)
  250. {
  251. lock (gameObjLock)
  252. {
  253. score -= sub;
  254. Debugger.Output(this, " 's score has been subed to: " + score.ToString());
  255. }
  256. }
  257. /*
  258. /// <summary>
  259. /// 遭受攻击
  260. /// </summary>
  261. /// <param name="subHP"></param>
  262. /// <param name="hasSpear"></param>
  263. /// <param name="attacker">伤害来源</param>
  264. /// <returns>人物在受到攻击后死了吗</returns>
  265. public bool BeAttack(Bullet bullet)
  266. {
  267. lock (beAttackedLock)
  268. {
  269. if (hp <= 0)
  270. return false; // 原来已经死了
  271. if (bullet.Parent.TeamID != this.TeamID)
  272. {
  273. if (HasShield)
  274. {
  275. if (bullet.HasSpear)
  276. _ = TrySubHp(bullet.AP);
  277. else
  278. return false;
  279. }
  280. else
  281. {
  282. TrySubHp(bullet.AP);
  283. }
  284. #if DEBUG
  285. Console.WriteLine($"PlayerID:{ID} is being shot! Now his hp is {hp}.");
  286. #endif
  287. if (hp <= 0)
  288. TryActivatingLIFE(); // 如果有复活甲
  289. }
  290. return hp <= 0;
  291. }
  292. }
  293. /// <summary>
  294. /// 攻击被反弹,反弹伤害不会再被反弹
  295. /// </summary>
  296. /// <param name="subHP"></param>
  297. /// <param name="hasSpear"></param>
  298. /// <param name="bouncer">反弹伤害者</param>
  299. /// <returns>是否因反弹伤害而死</returns>
  300. private bool BeBounced(int subHP, bool hasSpear, Character? bouncer)
  301. {
  302. lock (beAttackedLock)
  303. {
  304. if (hp <= 0)
  305. return false;
  306. if (!(bouncer?.TeamID == this.TeamID))
  307. {
  308. if (hasSpear || !HasShield)
  309. _ = TrySubHp(subHP);
  310. if (hp <= 0)
  311. TryActivatingLIFE();
  312. }
  313. return hp <= 0;
  314. }
  315. }
  316. */
  317. /// <summary>
  318. /// 角色所属队伍ID
  319. /// </summary>
  320. private long teamID = long.MaxValue;
  321. public long TeamID
  322. {
  323. get => teamID;
  324. set {
  325. lock (gameObjLock)
  326. {
  327. teamID = value;
  328. Debugger.Output(this, " joins in the team: " + value.ToString());
  329. }
  330. }
  331. }
  332. private long playerID = long.MaxValue;
  333. public long PlayerID
  334. {
  335. get => playerID;
  336. set {
  337. lock (gameObjLock)
  338. {
  339. playerID = value;
  340. }
  341. }
  342. }
  343. /// <summary>
  344. /// 角色携带的信息
  345. /// </summary>
  346. private string message = "THUAI5";
  347. public string Message
  348. {
  349. get => message;
  350. set {
  351. lock (gameObjLock)
  352. {
  353. message = value;
  354. }
  355. }
  356. }
  357. #endregion
  358. #region 角色拥有的buff相关属性、方法
  359. public void AddMoveSpeed(int buffTime, double add = 2.0) => buffManeger.AddMoveSpeed(add, buffTime, newVal =>
  360. { MoveSpeed = newVal < GameData.characterMaxSpeed ? newVal : GameData.characterMaxSpeed; },
  361. OrgMoveSpeed);
  362. public bool HasFasterSpeed => buffManeger.HasFasterSpeed;
  363. public void AddShield(int shieldTime) => buffManeger.AddShield(shieldTime);
  364. public bool HasShield => buffManeger.HasShield;
  365. public void AddLIFE(int LIFETime) => buffManeger.AddLIFE(LIFETime);
  366. public bool HasLIFE => buffManeger.HasLIFE;
  367. public void AddSpear(int spearTime) => buffManeger.AddSpear(spearTime);
  368. public bool HasSpear => buffManeger.HasSpear;
  369. private Array buffTypeArray = Enum.GetValues(typeof(BuffType));
  370. public Dictionary<BuffType, bool> Buff
  371. {
  372. get {
  373. Dictionary<BuffType, bool> buff = new Dictionary<BuffType, bool>();
  374. foreach (BuffType type in buffTypeArray)
  375. {
  376. if (type != BuffType.Null)
  377. buff.Add(type, GetBuffStatus(type));
  378. }
  379. return buff;
  380. }
  381. }
  382. private bool GetBuffStatus(BuffType type)
  383. {
  384. switch (type)
  385. {
  386. case BuffType.Spear:
  387. return this.HasSpear;
  388. case BuffType.AddSpeed:
  389. return this.HasFasterSpeed;
  390. case BuffType.Shield:
  391. return this.HasShield;
  392. case BuffType.AddLIFE:
  393. return this.HasLIFE;
  394. default:
  395. return false;
  396. }
  397. }
  398. private void TryActivatingLIFE()
  399. {
  400. if (buffManeger.TryActivatingLIFE())
  401. {
  402. hp = MaxHp;
  403. }
  404. }
  405. #endregion
  406. public override void Reset() // 要加锁吗?
  407. {
  408. _ = AddDeathCount();
  409. base.Reset();
  410. this.MoveSpeed = OrgMoveSpeed;
  411. HP = MaxHp;
  412. PropInventory = null;
  413. // BulletOfPlayer = OriBulletOfPlayer;
  414. // lock (gameObjLock)
  415. // bulletNum = maxBulletNum;
  416. buffManeger.ClearAll();
  417. IsInvisible = false;
  418. this.Vampire = this.OriVampire;
  419. }
  420. public override bool IsRigid => true;
  421. public override ShapeType Shape => ShapeType.Circle;
  422. protected override bool IgnoreCollideExecutor(IGameObj targetObj)
  423. {
  424. if (targetObj.Type == GameObjType.BirthPoint)
  425. {
  426. if (object.ReferenceEquals(((BirthPoint)targetObj).Parent, this)) // 自己的出生点可以忽略碰撞
  427. {
  428. return true;
  429. }
  430. }
  431. else if (targetObj.Type == GameObjType.Prop) // 自己队的地雷忽略碰撞
  432. {
  433. return true;
  434. }
  435. return false;
  436. }
  437. }
  438. }