CondomDispenser.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. using UnityEngine;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. public class CondomDispenser : Collidable
  5. {
  6. #region Unity Singleton
  7. private static CondomDispenser _instance;
  8. public static CondomDispenser Instance
  9. {
  10. get
  11. {
  12. if (_instance == null)
  13. {
  14. _instance = FindObjectOfType(typeof(CondomDispenser)) as CondomDispenser;
  15. if (_instance == null)
  16. {
  17. Debug.LogError("No gameObject with CondomDispenser component exists. Make sure to create a gameObject with CondomDispenser component");
  18. }
  19. }
  20. return _instance;
  21. }
  22. }
  23. protected override void Awake()
  24. {
  25. base.Awake();
  26. if (_instance != null && _instance != this)
  27. {
  28. Debug.LogWarning("CondomDispenser instance already exists on another gameObject. Destroying this gameObject CondomDispenser");
  29. Destroy(gameObject);
  30. return;
  31. }
  32. _instance = this;
  33. DontDestroyOnLoad(gameObject);
  34. Init();
  35. }
  36. #endregion
  37. private static Transform _transform;
  38. public static Transform GetTransform() { return _transform; }
  39. public const int INFINITE_AMMO = -1;
  40. public float moveSpeed = 1f;
  41. public float recoilOffset = 20;
  42. public float recoilTimeOut = 0.04f;
  43. public float recoilTimeIn = 0.2f;
  44. public int maxQueuedTaps = 3;
  45. public Weapon defaultWeapon;
  46. public Weapon[] otherWeapons;
  47. /// <summary>
  48. /// Using List instead of Queue, due to capping the queue, and the last tap is replaced by the latest tap
  49. /// </summary>
  50. private List<Vector2> _queuedTaps = new List<Vector2>();
  51. private Vector3 _originalPosition;
  52. private Weapon _currentWeapon;
  53. private Dictionary<PickupType, Weapon> _weaponsDictionary;
  54. [HideInInspector]
  55. public List<CondomBase>condoms = new List<CondomBase>();
  56. public int ammoLeft = INFINITE_AMMO;
  57. #region FSM decleration
  58. private enum DispenserState
  59. {
  60. NullState,
  61. Idle,
  62. Moving,
  63. Shooting,
  64. CheckingQueue
  65. }
  66. private enum DispenserTransition
  67. {
  68. NullTransition,
  69. Move,
  70. Shoot,
  71. CheckQueue,
  72. NoQueuedTaps,
  73. HasQueuedTaps
  74. }
  75. private FSMSystem<DispenserTransition, DispenserState> fsm;
  76. #endregion
  77. #region Unity Callbacks
  78. protected void Init()
  79. {
  80. _transform = transform;
  81. _originalPosition = transform.localPosition;
  82. _weaponsDictionary = new Dictionary<PickupType, Weapon>();
  83. _weaponsDictionary[defaultWeapon.type] = defaultWeapon;
  84. foreach (Weapon w in otherWeapons)
  85. {
  86. _weaponsDictionary[w.type] = w;
  87. }
  88. MakeFSM();
  89. }
  90. void OnEnable()
  91. {
  92. iTween.Stop(gameObject); //stop any itweens from the previous game, just in case
  93. NotificationCenter.AddListener(OnFire, NotificationType.Fire);
  94. NotificationCenter.AddListener(Reset, NotificationType.GameStart);
  95. NotificationCenter.AddListener(OnPickedUpWeaponUpgrade, NotificationType.PickedUpWeaponUpgrade);
  96. NotificationCenter.AddListener(OnFireIndependently, NotificationType.FireIndependently);
  97. NotificationCenter.AddListener(OnSpawnCondom, NotificationType.SpawnCondom);
  98. }
  99. void OnDisable()
  100. {
  101. NotificationCenter.RemoveListener(OnFire, NotificationType.Fire);
  102. NotificationCenter.RemoveListener(Reset, NotificationType.GameStart);
  103. NotificationCenter.RemoveListener(OnPickedUpWeaponUpgrade, NotificationType.PickedUpWeaponUpgrade);
  104. NotificationCenter.RemoveListener(OnFireIndependently, NotificationType.FireIndependently);
  105. NotificationCenter.RemoveListener(OnSpawnCondom, NotificationType.SpawnCondom);
  106. condoms.Clear();
  107. }
  108. public void CustomFixedUpdate()
  109. {
  110. //AVDebug.Log("CD "+transform.localPosition.x+","+transform.localPosition.y+" at tick "+RecordingManager.Ticks);
  111. fsm.Update();
  112. if (_currentWeapon != null)
  113. {
  114. if (_currentWeapon.isAutomatic)
  115. {
  116. //move with the mouse (or current touch position)
  117. Vector2 pos = UICamera.mainCamera.ScreenToWorldPoint(Input.mousePosition);
  118. Vector3 newPos = transform.position;
  119. newPos.y = pos.y;
  120. transform.position = newPos;
  121. }
  122. _currentWeapon.UpdateWeapon();
  123. if (_currentWeapon.IsEmpty)
  124. {
  125. ResetWeaponToDefault();
  126. }
  127. }
  128. }
  129. public void UpdateCondoms()
  130. {
  131. for (int i = condoms.Count - 1; i >= 0; i--)
  132. {
  133. CondomBase c = condoms[i];
  134. if (c.gameObject.activeInHierarchy)
  135. {
  136. c.BroadcastMessage("CustomFixedUpdate");
  137. } else
  138. {
  139. condoms.RemoveAt(i);
  140. }
  141. }
  142. }
  143. #endregion
  144. #region Notification callbacks
  145. void Reset(Notification note)
  146. {
  147. #if LEVEL_EDITOR
  148. ammoLeft = -1; //TODO should be read from some level editor field
  149. #else
  150. ammoLeft = LevelsManager.Instance.CurrentLevel.Ammo;
  151. #endif
  152. NotificationCenter.Post(NotificationType.UpdateAmmoLeft, ammoLeft);
  153. transform.localPosition = _originalPosition;
  154. _queuedTaps.Clear();
  155. ResetWeaponToDefault();
  156. MakeFSM();
  157. }
  158. void OnFire(Notification note)
  159. {
  160. if (HasAmmo())
  161. {
  162. if (maxQueuedTaps > 0 && // 0 means infinite
  163. _queuedTaps.Count >= maxQueuedTaps)
  164. {
  165. //remove the last tap
  166. _queuedTaps.RemoveAt(_queuedTaps.Count-1);
  167. }
  168. Vector2 tapPos = (Vector2)note.data;
  169. _queuedTaps.Add(tapPos);
  170. if (fsm.CurrentStateID == DispenserState.Idle)
  171. {
  172. fsm.PerformTransition(DispenserTransition.Move);
  173. }
  174. DecrementAmmo();
  175. }
  176. else
  177. {
  178. SoundManager.Play(SoundEvent.emptyDispenser);
  179. #if LEVEL_EDITOR
  180. Debug.LogWarning("No more ammo!");
  181. Debug.Break();
  182. #endif
  183. }
  184. }
  185. void OnPickedUpWeaponUpgrade(Notification note)
  186. {
  187. SetWeapon((PickupType)note.data);
  188. }
  189. /// <summary>
  190. /// This is mainly used when a homing missile is fired automatically
  191. /// When a virus is spawned
  192. /// </summary>
  193. void OnFireIndependently(Notification note)
  194. {
  195. PoolObject condom = (PoolObject)note.data;
  196. condoms.Add(condom.GetComponent<CondomBase>());
  197. Transform condomTransform = condom.transform;
  198. Vector3 originalScale = condomTransform.localScale;
  199. condomTransform.parent = transform.parent;
  200. condomTransform.localScale = originalScale;
  201. condomTransform.localPosition = transform.localPosition;
  202. if (_currentWeapon.IsEmpty)
  203. {
  204. ResetWeaponToDefault();
  205. }
  206. }
  207. #endregion
  208. #region iTween Callbacks
  209. void OniTweenMoveFinished()
  210. {
  211. //AVDebug.Log("CD move finished at tick "+RecordingManager.Ticks);
  212. fsm.PerformTransition(DispenserTransition.Shoot);
  213. }
  214. void OniTweenRecoilPushbackFinished()
  215. {
  216. //AVDebug.Log("CD recoil pushback finished at tick "+RecordingManager.Ticks);
  217. iTween.MoveTo(gameObject, iTween.Hash(
  218. "usefixedupdate", true,
  219. "islocal", true,
  220. "x", _originalPosition.x,
  221. "time", recoilTimeIn,
  222. "oncomplete", "OniTweenRecoilFinished",
  223. "oncompletetarget", gameObject));
  224. }
  225. void OniTweenRecoilFinished()
  226. {
  227. //AVDebug.Log("CD recoil finished at tick "+RecordingManager.Ticks);
  228. fsm.PerformTransition(DispenserTransition.CheckQueue);
  229. }
  230. #endregion
  231. #region FSM
  232. /// <summary>
  233. /// (start)->(Idle)
  234. /// (Idle)-Move>(Moving)
  235. /// (Moving)-Shoot>(Shooting)
  236. /// (Shooting)-CheckQueue>(CheckingQueue)
  237. /// (CheckingQueue)-HasQueuedTaps>(Moving)
  238. /// (CheckingQueue)-NoQueuedTaps>(Idle)
  239. /// http://yuml.me/edit/f6369289
  240. /// </summary>
  241. void MakeFSM()
  242. {
  243. var idleState = new FSMState<DispenserTransition, DispenserState>(DispenserState.Idle);
  244. idleState.AddTransition(DispenserTransition.Move, DispenserState.Moving);
  245. var movingState = new FSMState<DispenserTransition, DispenserState>(DispenserState.Moving);
  246. movingState.AddTransition(DispenserTransition.Shoot, DispenserState.Shooting);
  247. movingState.DoBeforeEntering = DoBeforeEnteringMoving;
  248. var shootingState = new FSMState<DispenserTransition, DispenserState>(DispenserState.Shooting);
  249. shootingState.AddTransition(DispenserTransition.CheckQueue, DispenserState.CheckingQueue);
  250. shootingState.DoBeforeEntering = DoBeforeEnteringShooting;
  251. var checkingQueueState = new FSMState<DispenserTransition, DispenserState>(DispenserState.CheckingQueue);
  252. checkingQueueState.AddTransition(DispenserTransition.HasQueuedTaps, DispenserState.Moving);
  253. checkingQueueState.AddTransition(DispenserTransition.NoQueuedTaps, DispenserState.Idle);
  254. checkingQueueState.DoBeforeEntering = DoBeforeEnteringCheckingQueue;
  255. fsm = new FSMSystem<DispenserTransition, DispenserState>("CondomDispenser FSM");
  256. //fsm.isDebug = true;
  257. fsm.AddState(idleState);
  258. fsm.AddState(movingState);
  259. fsm.AddState(shootingState);
  260. fsm.AddState(checkingQueueState);
  261. }
  262. void DoBeforeEnteringMoving()
  263. {
  264. //dequeue
  265. Vector2 pos = _queuedTaps[0];
  266. pos = UICamera.mainCamera.ScreenToWorldPoint(pos);
  267. _queuedTaps.RemoveAt(0);
  268. if (_currentWeapon.isAutomatic)
  269. {
  270. //don't waste time with animations when the weapon fires automatically
  271. Vector3 newPos = transform.position;
  272. newPos.y = pos.y;
  273. transform.position = newPos;
  274. fsm.PerformTransition(DispenserTransition.Shoot);
  275. } else
  276. {
  277. //AVDebug.Log("CD start moving at tick "+RecordingManager.Ticks);
  278. iTween.MoveTo(gameObject, iTween.Hash(
  279. "usefixedupdate", true,
  280. "y", pos.y,
  281. "speed", moveSpeed,
  282. "oncomplete", "OniTweenMoveFinished",
  283. "oncompletetarget", gameObject));
  284. }
  285. }
  286. void DoBeforeEnteringShooting()
  287. {
  288. if (RecordingManager.Instance.IsRecording)
  289. {
  290. NotificationCenter.Post(NotificationType.SpawnCondom, new Vector2(transform.localPosition.x, transform.localPosition.y));
  291. }
  292. //make recoil animation
  293. iTween.MoveTo(gameObject, iTween.Hash(
  294. "usefixedupdate", true,
  295. "islocal", true,
  296. "x", transform.localPosition.x + recoilOffset,
  297. "time", recoilTimeOut,
  298. "oncomplete", "OniTweenRecoilPushbackFinished",
  299. "oncompletetarget", gameObject));
  300. if (_currentWeapon.IsEmpty)
  301. {
  302. ResetWeaponToDefault();
  303. }
  304. }
  305. void DoBeforeEnteringCheckingQueue()
  306. {
  307. if (_queuedTaps.Count > 0)
  308. {
  309. fsm.PerformTransition(DispenserTransition.HasQueuedTaps);
  310. } else
  311. {
  312. fsm.PerformTransition(DispenserTransition.NoQueuedTaps);
  313. }
  314. }
  315. #endregion
  316. void ResetWeaponToDefault()
  317. {
  318. SetWeapon(PickupType.DefaultWeapon);
  319. NotificationCenter.Post(NotificationType.WeaponReset);
  320. }
  321. void SetWeapon(PickupType pickupType)
  322. {
  323. if (_currentWeapon != null)
  324. {
  325. _currentWeapon.Disarm();
  326. }
  327. _currentWeapon = _weaponsDictionary[pickupType];
  328. _currentWeapon.Arm();
  329. }
  330. void OnSpawnCondom(Notification note)
  331. {
  332. //if playing back this is crucial due to iTween not being particularly replayable for some strange reason
  333. iTween.Stop(gameObject);
  334. Vector3 newPos = (Vector2)note.data;
  335. newPos.z = _originalPosition.z;
  336. transform.localPosition = newPos;
  337. //create condom
  338. PoolObject condom = _currentWeapon.Fire();
  339. condoms.Add(condom.GetComponent<CondomBase>());
  340. Transform condomTransform = condom.transform;
  341. Vector3 originalScale = condomTransform.localScale;
  342. condomTransform.parent = transform.parent;
  343. condomTransform.localScale = originalScale;
  344. condomTransform.localPosition = transform.localPosition;
  345. }
  346. bool HasAmmo()
  347. {
  348. return (ammoLeft == INFINITE_AMMO || ammoLeft > 0);
  349. }
  350. void DecrementAmmo()
  351. {
  352. if (ammoLeft > 0 &&
  353. _currentWeapon.type == PickupType.DefaultWeapon)
  354. {
  355. --ammoLeft;
  356. NotificationCenter.Post(NotificationType.UpdateAmmoLeft, ammoLeft);
  357. }
  358. }
  359. }