PageRenderTime 53ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/UltimaXNA/Entity/MobileAnimation.cs

http://ultimaxna.googlecode.com/
C# | 600 lines | 515 code | 47 blank | 38 comment | 90 complexity | a22ae5381a98e3aaafdadd9b4cd3b8b8 MD5 | raw file
  1. /***************************************************************************
  2. * MobileAnimation.cs
  3. * Part of UltimaXNA: http://code.google.com/p/ultimaxna
  4. *
  5. * This program is free software; you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation; either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. ***************************************************************************/
  11. #region usings
  12. using System;
  13. using System.Collections.Generic;
  14. using System.Linq;
  15. using System.Text;
  16. using Microsoft.Xna.Framework;
  17. using UltimaXNA.UltimaData;
  18. #endregion
  19. namespace UltimaXNA.Entity
  20. {
  21. public class MobileAnimation
  22. {
  23. private Mobile Parent = null;
  24. private MobileAction _action;
  25. private bool _actionCanBeInteruptedByStand = false;
  26. private int _actionIndex;
  27. public int ActionIndex
  28. {
  29. get { return _actionIndex; }
  30. }
  31. public bool IsAnimating
  32. {
  33. get
  34. {
  35. if ((!_actionCanBeInteruptedByStand) &&
  36. (_action == MobileAction.None ||
  37. _action == MobileAction.Stand ||
  38. _action == MobileAction.Walk ||
  39. _action == MobileAction.Run))
  40. return false;
  41. return true;
  42. }
  43. }
  44. private float _animationFrame = 0f;
  45. public float AnimationFrame
  46. {
  47. get
  48. {
  49. if (_animationFrame >= 1f)
  50. return 0.999f;
  51. else
  52. return _animationFrame;
  53. }
  54. }
  55. private BodyTypes _bodyType
  56. {
  57. get { return AnimationsXNA.BodyType(Parent.BodyID); }
  58. }
  59. // We use these variables to 'hold' the last frame of an animation before
  60. // switching to Stand Action.
  61. private bool _holdAnimation = false;
  62. private int _holdAnimationTime = 0;
  63. private int HoldAnimationMS
  64. {
  65. get
  66. {
  67. if (Parent is PlayerMobile)
  68. return 100;
  69. else
  70. return 250;
  71. }
  72. }
  73. public MobileAnimation(Mobile parent)
  74. {
  75. Parent = parent;
  76. }
  77. public void Update(GameTime gameTime)
  78. {
  79. // create a local copy of ms since last update.
  80. int msSinceLastUpdate = gameTime.ElapsedGameTime.Milliseconds;
  81. // If we are holding the current animation, then we should wait until our hold time is over
  82. // before switching to the queued Stand animation.
  83. if (_holdAnimation)
  84. {
  85. _holdAnimationTime -= msSinceLastUpdate;
  86. if (_holdAnimationTime >= 0)
  87. {
  88. // we are still holding. Do not update the current Animation frame.
  89. return;
  90. }
  91. else
  92. {
  93. // hold time is over, continue to Stand animation.
  94. unholdAnimation();
  95. _action = MobileAction.Stand;
  96. _actionIndex = getActionIndex(MobileAction.Stand);
  97. _animationFrame = 0f;
  98. _FrameCount = 1;
  99. _FrameDelay = 0;
  100. }
  101. }
  102. if (_action != MobileAction.None)
  103. {
  104. // advance the animation one step, based on gametime passed.
  105. float animationStep = (float)((_FrameCount * (_FrameDelay + 1)) * 10);
  106. float timeStep = ((float)gameTime.ElapsedGameTime.TotalMilliseconds / animationStep) / _FrameCount;
  107. float msPerFrame = (float)((1000 * (_FrameDelay + 1)) / (float)_FrameCount);
  108. // Mounted movement is 2x normal frame rate
  109. if (Parent.IsMounted && ((_action == MobileAction.Walk) || (_action == MobileAction.Run)))
  110. msPerFrame /= 2;
  111. float frameAdvance = (float)(gameTime.ElapsedGameTime.TotalMilliseconds / msPerFrame) / _FrameCount;
  112. if (msPerFrame < 0)
  113. return;
  114. _animationFrame += frameAdvance;
  115. // When animations reach their last frame, if we are queueing to stand, then
  116. // hold the animation on the last frame.
  117. if (_animationFrame >= 1f)
  118. {
  119. if (_repeatCount > 0)
  120. {
  121. _animationFrame %= 1f;
  122. _repeatCount--;
  123. }
  124. else
  125. {
  126. // any requested actions are ended.
  127. _actionCanBeInteruptedByStand = false;
  128. // Hold the last frame of the current action if animation is not Stand.
  129. if (_action == MobileAction.Stand)
  130. {
  131. _animationFrame = 0;
  132. }
  133. else
  134. {
  135. // for most animations, hold the last frame. For Move animations, cycle through.
  136. if (_action == MobileAction.Run || _action == MobileAction.Walk)
  137. _animationFrame %= 1f;
  138. else
  139. _animationFrame -= frameAdvance;
  140. holdAnimation();
  141. }
  142. }
  143. }
  144. }
  145. }
  146. public void UpdateAnimation()
  147. {
  148. animate(_action, _actionIndex, 0, false, false, 0, false);
  149. }
  150. public void Animate(MobileAction action)
  151. {
  152. int actionIndex = getActionIndex(action);
  153. animate(action, actionIndex, 0, false, false, 0, false);
  154. }
  155. public void Animate(int requestedIndex, int frameCount, int repeatCount, bool reverse, bool repeat, int delay)
  156. {
  157. // note that frameCount is NOT used. Not sure if this counts as a bug.
  158. MobileAction action = getActionFromIndex(requestedIndex);
  159. int actionIndex = getActionIndex(action, requestedIndex);
  160. animate(action, actionIndex, repeatCount, reverse, repeat, delay, true);
  161. }
  162. private int _FrameCount, _FrameDelay, _repeatCount;
  163. private void animate(MobileAction action, int actionIndex, int repeatCount, bool reverse, bool repeat, int delay, bool isRequestedAction)
  164. {
  165. if (_action == action)
  166. {
  167. if (_holdAnimation)
  168. {
  169. unholdAnimation();
  170. }
  171. }
  172. if (isRequestedAction)
  173. _actionCanBeInteruptedByStand = true;
  174. if ((_action != action) || (_actionIndex != actionIndex))
  175. {
  176. // If we are switching from any action to a stand action, then hold the last frame of the
  177. // current animation for a moment. Only Stand actions are held; thus when any hold ends,
  178. // then we know we were holding for a Stand action.
  179. if (!(_action == MobileAction.None) && (action == MobileAction.Stand && _action != MobileAction.Stand))
  180. {
  181. if (_action != MobileAction.None)
  182. holdAnimation();
  183. }
  184. else
  185. {
  186. _action = action;
  187. unholdAnimation();
  188. _actionIndex = actionIndex;
  189. _animationFrame = 0f;
  190. _FrameCount = UltimaData.AnimationsXNA.GetAnimationFrameCount(
  191. Parent.BodyID, actionIndex, (int)Parent.Facing, Parent.Hue);
  192. _FrameDelay = delay;
  193. if (repeat == false)
  194. _repeatCount = 0;
  195. else
  196. _repeatCount = repeatCount;
  197. }
  198. }
  199. }
  200. private void holdAnimation()
  201. {
  202. if (!_holdAnimation)
  203. {
  204. _holdAnimation = true;
  205. _holdAnimationTime = HoldAnimationMS;
  206. }
  207. }
  208. private void unholdAnimation()
  209. {
  210. _holdAnimation = false;
  211. }
  212. private int getActionIndex(MobileAction action)
  213. {
  214. return getActionIndex(action, -1);
  215. }
  216. private int getActionIndex(MobileAction action, int index)
  217. {
  218. if (_bodyType == BodyTypes.Humanoid)
  219. {
  220. switch (action)
  221. {
  222. case MobileAction.None:
  223. return getActionIndex(MobileAction.Stand, index);
  224. case MobileAction.Walk:
  225. if (Parent.IsMounted)
  226. return (int)ActionIndexHumanoid.Mounted_RideSlow;
  227. else
  228. if (Parent.IsWarMode)
  229. return (int)ActionIndexHumanoid.Walk_Warmode;
  230. else
  231. {
  232. // Also check if is_armed.
  233. return (int)ActionIndexHumanoid.Walk;
  234. }
  235. case MobileAction.Run:
  236. if (Parent.IsMounted)
  237. return (int)ActionIndexHumanoid.Mounted_RideFast;
  238. else
  239. return (int)ActionIndexHumanoid.Run;
  240. case MobileAction.Stand:
  241. if (Parent.IsMounted)
  242. return (int)ActionIndexHumanoid.Mounted_Stand;
  243. else
  244. if (Parent.IsWarMode)
  245. {
  246. // Also check if weapon type is 2h. Can be 1H or 2H
  247. return (int)ActionIndexHumanoid.Stand_Warmode1H;
  248. }
  249. else
  250. return (int)ActionIndexHumanoid.Stand;
  251. case MobileAction.Death:
  252. // randomly select die forwards or backwards.
  253. if (Utility.RandomValue(0, 1) == 0)
  254. return (int)ActionIndexHumanoid.Die_Backwards;
  255. else
  256. return (int)ActionIndexHumanoid.Die_Forwards;
  257. case MobileAction.Attack:
  258. if (Parent.IsMounted)
  259. {
  260. // check weapon type. Can be 1H, Bow, or XBow
  261. return (int)ActionIndexHumanoid.Mounted_Attack_1H;
  262. }
  263. else
  264. {
  265. // check weapon type. Can be 1H, 2H across, 2H down, 2H jab, bow, xbow, or unarmed.
  266. return (int)ActionIndexHumanoid.Attack_1H;
  267. }
  268. case MobileAction.Cast_Directed:
  269. if (Parent.IsMounted)
  270. return getActionIndex(MobileAction.Stand, index);
  271. else
  272. return (int)ActionIndexHumanoid.Cast_Directed;
  273. case MobileAction.Cast_Area:
  274. if (Parent.IsMounted)
  275. return getActionIndex(MobileAction.Stand, index);
  276. else
  277. return (int)ActionIndexHumanoid.Cast_Area;
  278. case MobileAction.GetHit:
  279. if (Parent.IsMounted)
  280. return getActionIndex(MobileAction.Stand, index);
  281. else
  282. return (int)ActionIndexHumanoid.Hit;
  283. case MobileAction.Block:
  284. if (Parent.IsMounted)
  285. return getActionIndex(MobileAction.Stand, index);
  286. else
  287. return (int)ActionIndexHumanoid.Block_WithShield;
  288. case MobileAction.Emote_Fidget_1:
  289. if (Parent.IsMounted)
  290. return getActionIndex(MobileAction.Stand, index);
  291. else
  292. return (int)ActionIndexHumanoid.Fidget_1;
  293. case MobileAction.Emote_Fidget_2:
  294. if (Parent.IsMounted)
  295. return getActionIndex(MobileAction.Stand, index);
  296. else
  297. return (int)ActionIndexHumanoid.Fidget_2;
  298. case MobileAction.Emote_Bow:
  299. if (Parent.IsMounted)
  300. return getActionIndex(MobileAction.Stand, index);
  301. else
  302. return (int)ActionIndexHumanoid.Emote_Bow;
  303. case MobileAction.Emote_Salute:
  304. if (Parent.IsMounted)
  305. return getActionIndex(MobileAction.Stand, index);
  306. else
  307. return (int)ActionIndexHumanoid.Emote_Salute;
  308. case MobileAction.Emote_Eat:
  309. if (Parent.IsMounted)
  310. return getActionIndex(MobileAction.Stand, index);
  311. else
  312. return (int)ActionIndexHumanoid.Emote_Eat;
  313. default:
  314. return (int)-1;
  315. }
  316. }
  317. else if (_bodyType == BodyTypes.LowDetail)
  318. {
  319. switch (action)
  320. {
  321. case MobileAction.None:
  322. return getActionIndex(MobileAction.Stand, index);
  323. case MobileAction.Walk:
  324. return (int)ActionIndexAnimal.Walk;
  325. case MobileAction.Run:
  326. return (int)ActionIndexAnimal.Run;
  327. case MobileAction.Stand:
  328. return (int)ActionIndexAnimal.Stand;
  329. case MobileAction.Death:
  330. // randomly select die forwards or backwards.
  331. if (Utility.RandomValue(0, 1) == 0)
  332. return (int)ActionIndexAnimal.Die_Backwards;
  333. else
  334. return (int)ActionIndexAnimal.Die_Forwards;
  335. case MobileAction.MonsterAction:
  336. return index;
  337. default:
  338. return (int)-1;
  339. }
  340. }
  341. else if (_bodyType == BodyTypes.HighDetail)
  342. {
  343. switch (action)
  344. {
  345. case MobileAction.None:
  346. return getActionIndex(MobileAction.Stand, index);
  347. case MobileAction.Walk:
  348. return (int)ActionIndexMonster.Walk;
  349. case MobileAction.Run:
  350. return (int)ActionIndexMonster.Run;
  351. case MobileAction.Stand:
  352. return (int)ActionIndexMonster.Stand;
  353. case MobileAction.Death:
  354. // randomly select die forwards or backwards.
  355. if (Utility.RandomValue(0, 1) == 0)
  356. return (int)ActionIndexMonster.Die_Backwards;
  357. else
  358. return (int)ActionIndexMonster.Die_Forwards;
  359. case MobileAction.MonsterAction:
  360. return index;
  361. default:
  362. return (int)-1;
  363. }
  364. }
  365. return -1;
  366. }
  367. private MobileAction getActionFromIndex(int index)
  368. {
  369. if (_bodyType == BodyTypes.Humanoid)
  370. {
  371. switch ((ActionIndexHumanoid)index)
  372. {
  373. case ActionIndexHumanoid.Walk:
  374. case ActionIndexHumanoid.Walk_Armed:
  375. case ActionIndexHumanoid.Walk_Warmode:
  376. case ActionIndexHumanoid.Mounted_RideSlow:
  377. return MobileAction.Walk;
  378. case ActionIndexHumanoid.Mounted_RideFast:
  379. case ActionIndexHumanoid.Run:
  380. case ActionIndexHumanoid.Run_Armed:
  381. return MobileAction.Run;
  382. case ActionIndexHumanoid.Stand:
  383. case ActionIndexHumanoid.Stand_Warmode1H:
  384. case ActionIndexHumanoid.Stand_Warmode2H:
  385. case ActionIndexHumanoid.Mounted_Stand:
  386. return MobileAction.Stand;
  387. case ActionIndexHumanoid.Fidget_1:
  388. return MobileAction.Emote_Fidget_1;
  389. case ActionIndexHumanoid.Fidget_2:
  390. return MobileAction.Emote_Fidget_2;
  391. case ActionIndexHumanoid.Attack_1H:
  392. case ActionIndexHumanoid.Attack_Unarmed1:
  393. case ActionIndexHumanoid.Attack_Unarmed2:
  394. case ActionIndexHumanoid.Attack_2H_Down:
  395. case ActionIndexHumanoid.Attack_2H_Across:
  396. case ActionIndexHumanoid.Attack_2H_Jab:
  397. case ActionIndexHumanoid.Attack_Bow:
  398. case ActionIndexHumanoid.Attack_BowX:
  399. case ActionIndexHumanoid.Mounted_Attack_1H:
  400. case ActionIndexHumanoid.Mounted_Attack_Bow:
  401. case ActionIndexHumanoid.Mounted_Attack_BowX:
  402. case ActionIndexHumanoid.Attack_Unarmed3:
  403. return MobileAction.Attack;
  404. case ActionIndexHumanoid.Cast_Directed:
  405. return MobileAction.Cast_Directed;
  406. case ActionIndexHumanoid.Cast_Area:
  407. return MobileAction.Cast_Area;
  408. case ActionIndexHumanoid.Hit:
  409. return MobileAction.GetHit;
  410. case ActionIndexHumanoid.Die_Backwards:
  411. case ActionIndexHumanoid.Die_Forwards:
  412. return MobileAction.Death;
  413. case ActionIndexHumanoid.Mounted_SlapHorse: // not coded or used?
  414. return MobileAction.Stand;
  415. case ActionIndexHumanoid.Block_WithShield:
  416. return MobileAction.Block;
  417. case ActionIndexHumanoid.Emote_Bow:
  418. return MobileAction.Emote_Bow;
  419. case ActionIndexHumanoid.Emote_Salute:
  420. return MobileAction.Emote_Salute;
  421. case ActionIndexHumanoid.Emote_Eat:
  422. return MobileAction.Emote_Eat;
  423. }
  424. // special case animations. When casting a spell, the server will send animation indexes over 200,
  425. // which all seem to correspond to Cast_Directed. Example indexes are:
  426. // 200, 201, 203, 206, 209, 212, 215, 218, 221, 227, 230, 239, 245, 260, 266 and doubtless others.
  427. if (index >= 200)
  428. return MobileAction.Cast_Directed;
  429. Diagnostics.Logger.Warn("Unknown action index {0}", index);
  430. return MobileAction.None;
  431. }
  432. else if (_bodyType == BodyTypes.LowDetail)
  433. {
  434. switch ((ActionIndexAnimal)index)
  435. {
  436. case ActionIndexAnimal.Stand:
  437. return MobileAction.Stand;
  438. case ActionIndexAnimal.Walk:
  439. return MobileAction.Walk;
  440. case ActionIndexAnimal.Run:
  441. return MobileAction.Run;
  442. default:
  443. return MobileAction.MonsterAction;
  444. }
  445. }
  446. else if (_bodyType == BodyTypes.HighDetail)
  447. {
  448. switch ((ActionIndexMonster)index)
  449. {
  450. case ActionIndexMonster.Stand:
  451. return MobileAction.Stand;
  452. case ActionIndexMonster.Walk:
  453. return MobileAction.Walk;
  454. case ActionIndexMonster.Run:
  455. return MobileAction.Run;
  456. default:
  457. return MobileAction.MonsterAction;
  458. }
  459. }
  460. return MobileAction.None;
  461. }
  462. }
  463. public enum MobileAction
  464. {
  465. None,
  466. Walk,
  467. Run,
  468. Stand,
  469. Death,
  470. Attack,
  471. Cast_Directed,
  472. Cast_Area,
  473. GetHit,
  474. Block,
  475. Emote_Fidget_1,
  476. Emote_Fidget_2,
  477. Emote_Bow,
  478. Emote_Salute,
  479. Emote_Eat,
  480. MonsterAction
  481. }
  482. enum ActionIndexMonster
  483. {
  484. Walk = 0x00,
  485. Stand = 0x01,
  486. Die_Backwards = 0x02,
  487. Die_Forwards = 0x03,
  488. Attack1 = 0x04,
  489. Attack2 = 0x05,
  490. Attack3 = 0x06,
  491. Stumble = 0x07,
  492. MonsterMisc = 0x08,
  493. GetHit2 = 0x09,
  494. GetHit3 = 0x0A,
  495. Emote_Fidget_1 = 0x0B,
  496. Emote_Fidget_2 = 0x0C,
  497. Run = 0x13,
  498. }
  499. enum ActionIndexAnimal
  500. {
  501. Walk = 0x00,
  502. Run = 0x01,
  503. Stand = 0x02,
  504. Graze = 0x03,
  505. Unknown1 = 0x04,
  506. Attack1 = 0x05,
  507. Attack2 = 0x06,
  508. Attack3 = 0x07,
  509. Die_Backwards = 0x08,
  510. Fidget1 = 0x09,
  511. Fidget2 = 0x0A,
  512. LieDown = 0x0B,
  513. Die_Forwards = 0x0C,
  514. }
  515. enum ActionIndexHumanoid
  516. {
  517. Walk = 0x00,
  518. Walk_Armed = 0x01,
  519. Run = 0x02,
  520. Run_Armed = 0x03,
  521. Stand = 0x04,
  522. Fidget_1 = 0x05,
  523. Fidget_2 = 0x06,
  524. Stand_Warmode1H = 0x07,
  525. Stand_Warmode2H = 0x08,
  526. Attack_1H = 0x09,
  527. Attack_Unarmed1 = 0x0A,
  528. Attack_Unarmed2 = 0x0B,
  529. Attack_2H_Down = 0x0C,
  530. Attack_2H_Across = 0x0D,
  531. Attack_2H_Jab = 0x0E,
  532. Walk_Warmode = 0x0F,
  533. Cast_Directed = 0x10,
  534. Cast_Area = 0x11,
  535. Attack_Bow = 0x12,
  536. Attack_BowX = 0x13,
  537. Hit = 0x14,
  538. Die_Backwards = 0x15,
  539. Die_Forwards = 0x16,
  540. Mounted_RideSlow = 0x17,
  541. Mounted_RideFast = 0x18,
  542. Mounted_Stand = 0x19,
  543. Mounted_Attack_1H = 0x1A,
  544. Mounted_Attack_Bow = 0x1B,
  545. Mounted_Attack_BowX = 0x1C,
  546. Mounted_SlapHorse = 0x1D,
  547. Block_WithShield = 0x1E,
  548. Attack_Unarmed3 = 0x1F,
  549. Emote_Bow = 0x20,
  550. Emote_Salute = 0x21,
  551. Emote_Eat = 0x22
  552. }
  553. }