SongBrowserMasterViewController.cs 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. using UnityEngine;
  2. using System.Linq;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Security.Cryptography;
  6. using UnityEngine.Events;
  7. using UnityEngine.SceneManagement;
  8. using UnityEngine.UI;
  9. using System.Text;
  10. using HMUI;
  11. using System.Text.RegularExpressions;
  12. using System.IO;
  13. using System.Threading;
  14. namespace SongBrowserPlugin
  15. {
  16. public class SongSortButton
  17. {
  18. public SongSortMode SortMode;
  19. public Button Button;
  20. }
  21. public class SongBrowserMasterViewController : MonoBehaviour
  22. {
  23. // Which Scene index to run on
  24. public const int MenuIndex = 1;
  25. private Logger _log = new Logger("SongBrowserMasterViewController");
  26. // Private UI fields
  27. private SongSelectionMasterViewController _songSelectionMasterView;
  28. private SongDetailViewController _songDetailViewController;
  29. private SongListViewController _songListViewController;
  30. private MainMenuViewController _mainMenuViewController;
  31. private Dictionary<String, Sprite> _icons;
  32. private Button _buttonInstance;
  33. private List<SongSortButton> _sortButtonGroup;
  34. private Button _addFavoriteButton;
  35. private String _addFavoriteButtonText = null;
  36. // Model
  37. private SongBrowserModel _model;
  38. /// <summary>
  39. /// Unity OnLoad
  40. /// </summary>
  41. public static void OnLoad()
  42. {
  43. if (Instance != null) return;
  44. new GameObject("Song Browser").AddComponent<SongBrowserMasterViewController>();
  45. }
  46. public static SongBrowserMasterViewController Instance;
  47. /// <summary>
  48. /// Builds the UI for this plugin.
  49. /// </summary>
  50. private void Awake()
  51. {
  52. _log.Debug("Awake()");
  53. Instance = this;
  54. SceneManager.activeSceneChanged += SceneManagerOnActiveSceneChanged;
  55. SongLoaderPlugin.SongLoader.SongsLoaded.AddListener(OnSongLoaderLoadedSongs);
  56. DontDestroyOnLoad(gameObject);
  57. }
  58. /// <summary>
  59. /// Get a handle to the view controllers we are going to add elements to.
  60. /// </summary>
  61. public void AcquireUIElements()
  62. {
  63. _icons = new Dictionary<String, Sprite>();
  64. foreach (Sprite sprite in Resources.FindObjectsOfTypeAll<Sprite>())
  65. {
  66. if (_icons.ContainsKey(sprite.name))
  67. {
  68. continue;
  69. }
  70. _icons.Add(sprite.name, sprite);
  71. }
  72. try
  73. {
  74. _buttonInstance = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "PlayButton"));
  75. _mainMenuViewController = Resources.FindObjectsOfTypeAll<MainMenuViewController>().First();
  76. _songSelectionMasterView = Resources.FindObjectsOfTypeAll<SongSelectionMasterViewController>().First();
  77. _songDetailViewController = Resources.FindObjectsOfTypeAll<SongDetailViewController>().First();
  78. _songListViewController = Resources.FindObjectsOfTypeAll<SongListViewController>().First();
  79. }
  80. catch (Exception e)
  81. {
  82. _log.Exception("Exception AcquireUIElements(): " + e);
  83. }
  84. }
  85. /// <summary>
  86. /// Builds the SongBrowser UI
  87. /// </summary>
  88. public void CreateUI()
  89. {
  90. _log.Debug("CreateUI");
  91. // _icons.ForEach(i => Console.WriteLine(i.ToString()));
  92. try
  93. {
  94. RectTransform rect = _songSelectionMasterView.transform as RectTransform;
  95. // Create Sorting Songs By-Buttons
  96. _sortButtonGroup = new List<SongSortButton>
  97. {
  98. CreateSortButton(rect, "PlayButton", "Fav", 3, "AllDirectionsIcon", 30f, 77.5f, 15f, 5f, SongSortMode.Favorites),
  99. CreateSortButton(rect, "PlayButton", "Def", 3, "AllDirectionsIcon", 15f, 77.5f, 15f, 5f, SongSortMode.Default),
  100. CreateSortButton(rect, "PlayButton", "Org", 3, "AllDirectionsIcon", 0f, 77.5f, 15f, 5f, SongSortMode.Original),
  101. CreateSortButton(rect, "PlayButton", "New", 3, "AllDirectionsIcon", -15f, 77.5f, 15f, 5f, SongSortMode.Newest)
  102. };
  103. // Creaate Add to Favorites Button
  104. RectTransform transform = _songDetailViewController.transform as RectTransform;
  105. _addFavoriteButton = UIBuilder.CreateUIButton(transform, "QuitButton", _buttonInstance);
  106. (_addFavoriteButton.transform as RectTransform).anchoredPosition = new Vector2(45f, 5f);
  107. (_addFavoriteButton.transform as RectTransform).sizeDelta = new Vector2(10f, 10f);
  108. if (_addFavoriteButtonText == null)
  109. {
  110. LevelStaticData level = getSelectedSong();
  111. RefreshAddFavoriteButton(level);
  112. }
  113. UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
  114. //UIBuilder.SetButtonIcon(ref _addFavoriteButton, _icons["AllDirectionsIcon"]);
  115. UIBuilder.SetButtonTextSize(ref _addFavoriteButton, 3);
  116. UIBuilder.SetButtonIconEnabled(ref _addFavoriteButton, false);
  117. _addFavoriteButton.onClick.RemoveAllListeners();
  118. _addFavoriteButton.onClick.AddListener(delegate () {
  119. ToggleSongInFavorites();
  120. });
  121. RefreshUI();
  122. }
  123. catch (Exception e)
  124. {
  125. _log.Exception("Exception CreateUI: " + e.Message);
  126. }
  127. }
  128. /// <summary>
  129. /// Generic create sort button.
  130. /// </summary>
  131. /// <param name="rect"></param>
  132. /// <param name="templateButtonName"></param>
  133. /// <param name="buttonText"></param>
  134. /// <param name="iconName"></param>
  135. /// <param name="x"></param>
  136. /// <param name="y"></param>
  137. /// <param name="w"></param>
  138. /// <param name="h"></param>
  139. /// <param name="action"></param>
  140. private SongSortButton CreateSortButton(RectTransform rect, string templateButtonName, string buttonText, float fontSize, string iconName, float x, float y, float w, float h, SongSortMode sortMode)
  141. {
  142. SongSortButton sortButton = new SongSortButton();
  143. Button newButton = UIBuilder.CreateUIButton(rect, templateButtonName, _buttonInstance);
  144. newButton.interactable = true;
  145. (newButton.transform as RectTransform).anchoredPosition = new Vector2(x, y);
  146. (newButton.transform as RectTransform).sizeDelta = new Vector2(w, h);
  147. UIBuilder.SetButtonText(ref newButton, buttonText);
  148. //UIBuilder.SetButtonIconEnabled(ref _originalButton, false);
  149. UIBuilder.SetButtonIcon(ref newButton, _icons[iconName]);
  150. UIBuilder.SetButtonTextSize(ref newButton, fontSize);
  151. newButton.onClick.RemoveAllListeners();
  152. newButton.onClick.AddListener(delegate () {
  153. _log.Debug("Sort button - {0} - pressed.", sortMode.ToString());
  154. _model.Settings.sortMode = sortMode;
  155. _model.Settings.Save();
  156. UpdateSongList();
  157. });
  158. sortButton.Button = newButton;
  159. sortButton.SortMode = sortMode;
  160. return sortButton;
  161. }
  162. /// <summary>
  163. /// Bind to some UI events.
  164. /// </summary>
  165. /// <param name="arg0"></param>
  166. /// <param name="scene"></param>
  167. private void SceneManagerOnActiveSceneChanged(Scene arg0, Scene scene)
  168. {
  169. //_log.Debug("scene.buildIndex==" + scene.buildIndex);
  170. try
  171. {
  172. if (scene.buildIndex == SongBrowserMasterViewController.MenuIndex)
  173. {
  174. _log.Debug("SceneManagerOnActiveSceneChanged - Setting Up UI");
  175. AcquireUIElements();
  176. if (_model == null)
  177. {
  178. _model = new SongBrowserModel();
  179. }
  180. _model.Init(new DataAccess.BeatSaberSongList() /*_songSelectionMasterView, _songListViewController*/);
  181. CreateUI();
  182. _songListViewController.didSelectSongEvent += OnDidSelectSongEvent;
  183. }
  184. }
  185. catch (Exception e)
  186. {
  187. _log.Exception("Exception during scene change: " + e);
  188. }
  189. }
  190. /// <summary>
  191. /// Song Loader has loaded the songs, lets sort them.
  192. /// </summary>
  193. private void OnSongLoaderLoadedSongs()
  194. {
  195. _log.Debug("OnSongLoaderLoadedSongs");
  196. //RefreshAddFavoriteButton(sortedSongList[0]);
  197. // Call into SongLoaderPlugin to get all the song info.
  198. try
  199. {
  200. UpdateSongList();
  201. }
  202. catch (Exception e)
  203. {
  204. _log.Exception("Exception trying to update song list: {0}", e);
  205. }
  206. }
  207. /// <summary>
  208. /// Adjust UI based on song selected.
  209. /// Various ways of detecting if a song is not properly selected. Seems most hit the first one.
  210. /// </summary>
  211. /// <param name="songListViewController"></param>
  212. private void OnDidSelectSongEvent(SongListViewController songListViewController)
  213. {
  214. LevelStaticData level = getSelectedSong();
  215. if (level == null)
  216. {
  217. _log.Debug("No song selected?");
  218. return;
  219. }
  220. if (_model.Settings == null)
  221. {
  222. _log.Debug("Settings not instantiated yet?");
  223. return;
  224. }
  225. RefreshAddFavoriteButton(level);
  226. }
  227. /// <summary>
  228. /// Return LevelStaticData or null.
  229. /// </summary>
  230. private LevelStaticData getSelectedSong()
  231. {
  232. // song list not even visible
  233. if (!_songSelectionMasterView.isActiveAndEnabled)
  234. {
  235. return null;
  236. }
  237. int selectedIndex = _songSelectionMasterView.GetSelectedSongIndex();
  238. if (selectedIndex < 0)
  239. {
  240. return null;
  241. }
  242. LevelStaticData level = _songSelectionMasterView.GetLevelStaticDataForSelectedSong();
  243. return level;
  244. }
  245. /// <summary>
  246. /// Add/Remove song from favorites depending on if it already exists.
  247. /// </summary>
  248. private void ToggleSongInFavorites()
  249. {
  250. LevelStaticData songInfo = _songSelectionMasterView.GetLevelStaticDataForSelectedSong();
  251. if (_model.Settings.favorites.Contains(songInfo.levelId))
  252. {
  253. _log.Info("Remove {0} from favorites", songInfo.name);
  254. _model.Settings.favorites.Remove(songInfo.levelId);
  255. _addFavoriteButtonText = "+1";
  256. }
  257. else
  258. {
  259. _log.Info("Add {0} to favorites", songInfo.name);
  260. _model.Settings.favorites.Add(songInfo.levelId);
  261. _addFavoriteButtonText = "-1";
  262. }
  263. UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
  264. _model.Settings.Save();
  265. }
  266. /// <summary>
  267. /// Helper to quickly refresh add to favorites button
  268. /// </summary>
  269. /// <param name="levelId"></param>
  270. private void RefreshAddFavoriteButton(LevelStaticData level)
  271. {
  272. var levelId = _songListViewController.levelId;
  273. if (levelId == null)
  274. {
  275. if (level != null)
  276. {
  277. levelId = level.levelId;
  278. }
  279. }
  280. //if (level != null) _log.Debug(level.songName);
  281. //if (level != null)
  282. // _log.Debug("Level.id=" + level.levelId);
  283. //_log.Debug("_songListViewController.levelId=" + _songListViewController.levelId);
  284. if (levelId == null)
  285. {
  286. _addFavoriteButtonText = "0";
  287. return;
  288. }
  289. if (levelId == null)
  290. {
  291. levelId = level.levelId;
  292. }
  293. if (_model.Settings.favorites.Contains(levelId))
  294. {
  295. _addFavoriteButtonText = "-1";
  296. }
  297. else
  298. {
  299. _addFavoriteButtonText = "+1";
  300. }
  301. UIBuilder.SetButtonText(ref _addFavoriteButton, _addFavoriteButtonText);
  302. }
  303. /// <summary>
  304. /// Adjust the UI colors.
  305. /// </summary>
  306. public void RefreshUI()
  307. {
  308. // So far all we need to refresh is the sort buttons.
  309. foreach (SongSortButton sortButton in _sortButtonGroup)
  310. {
  311. UIBuilder.SetButtonBorder(ref sortButton.Button, Color.black);
  312. if (sortButton.SortMode == _model.Settings.sortMode)
  313. {
  314. UIBuilder.SetButtonBorder(ref sortButton.Button, Color.red);
  315. }
  316. }
  317. }
  318. /// <summary>
  319. /// Try to refresh the song list. Broken for now.
  320. /// </summary>
  321. public void RefreshSongList(List<LevelStaticData> songList)
  322. {
  323. _log.Debug("Attempting to refresh the song list view.");
  324. try
  325. {
  326. // Check a couple of possible situations that we can't refresh
  327. if (!_songSelectionMasterView.isActiveAndEnabled)
  328. {
  329. _log.Debug("No song list to refresh.");
  330. return;
  331. }
  332. SongListTableView songTableView = _songListViewController.GetComponentInChildren<SongListTableView>();
  333. if (songTableView == null)
  334. {
  335. return;
  336. }
  337. TableView tableView = ReflectionUtil.GetPrivateField<TableView>(songTableView, "_tableView");
  338. if (tableView == null)
  339. {
  340. return;
  341. }
  342. // Convert to Array once in-case this is costly.
  343. LevelStaticData[] songListArray = songList.ToArray();
  344. // Refresh the master view
  345. bool useLocalLeaderboards = ReflectionUtil.GetPrivateField<bool>(_songSelectionMasterView, "_useLocalLeaderboards");
  346. bool showDismissButton = true;
  347. bool showPlayerStats = ReflectionUtil.GetPrivateField<bool>(_songSelectionMasterView, "_showPlayerStats");
  348. GameplayMode gameplayMode = ReflectionUtil.GetPrivateField<GameplayMode>(_songSelectionMasterView, "_gameplayMode");
  349. _songSelectionMasterView.Init(
  350. _songSelectionMasterView.levelId,
  351. _songSelectionMasterView.difficulty,
  352. songListArray,
  353. useLocalLeaderboards, showDismissButton, showPlayerStats, gameplayMode
  354. );
  355. // Refresh the table views
  356. ReflectionUtil.SetPrivateField(songTableView, "_levels", songListArray);
  357. tableView.ReloadData();
  358. // Clear Force selection of index 0 so we don't end up in a weird state.
  359. songTableView.ClearSelection();
  360. _songListViewController.SelectSong(0);
  361. _songSelectionMasterView.HandleSongListDidSelectSong(_songListViewController);
  362. RefreshUI();
  363. RefreshAddFavoriteButton(songList[0]);
  364. }
  365. catch (Exception e)
  366. {
  367. _log.Exception("Exception refreshing song list: {0}", e.Message);
  368. }
  369. }
  370. /// <summary>
  371. /// Helper for updating the model (which updates the song list)
  372. /// </summary>
  373. public void UpdateSongList()
  374. {
  375. _model.UpdateSongLists(true);
  376. RefreshSongList(_model.SortedSongList);
  377. }
  378. /// <summary>
  379. /// Map some key presses directly to UI interactions to make testing easier.
  380. /// </summary>
  381. private void Update()
  382. {
  383. // cycle sort modes
  384. if (Input.GetKeyDown(KeyCode.T))
  385. {
  386. if (_model.Settings.sortMode == SongSortMode.Favorites)
  387. _model.Settings.sortMode = SongSortMode.Newest;
  388. else if (_model.Settings.sortMode == SongSortMode.Newest)
  389. _model.Settings.sortMode = SongSortMode.Original;
  390. else if (_model.Settings.sortMode == SongSortMode.Original)
  391. _model.Settings.sortMode = SongSortMode.Default;
  392. else if (_model.Settings.sortMode == SongSortMode.Default)
  393. _model.Settings.sortMode = SongSortMode.Favorites;
  394. UpdateSongList();
  395. }
  396. // z,x,c,v can be used to get into a song, b will hit continue button after song ends
  397. if (Input.GetKeyDown(KeyCode.Z))
  398. {
  399. Button _buttonInstance = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "SoloButton"));
  400. _buttonInstance.onClick.Invoke();
  401. }
  402. if (Input.GetKeyDown(KeyCode.X))
  403. {
  404. Button _buttonInstance = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "FreePlayButton"));
  405. _buttonInstance.onClick.Invoke();
  406. }
  407. if (Input.GetKeyDown(KeyCode.C))
  408. {
  409. _songListViewController.SelectSong(0);
  410. _songSelectionMasterView.HandleSongListDidSelectSong(_songListViewController);
  411. DifficultyViewController _difficultyViewController = Resources.FindObjectsOfTypeAll<DifficultyViewController>().First();
  412. _difficultyViewController.SelectDifficulty(LevelStaticData.Difficulty.Hard);
  413. _songSelectionMasterView.HandleDifficultyViewControllerDidSelectDifficulty(_difficultyViewController);
  414. }
  415. if (Input.GetKeyDown(KeyCode.V))
  416. {
  417. _songSelectionMasterView.HandleSongDetailViewControllerDidPressPlayButton(_songDetailViewController);
  418. }
  419. if (Input.GetKeyDown(KeyCode.B))
  420. {
  421. Button _buttonInstance = Resources.FindObjectsOfTypeAll<Button>().First(x => (x.name == "ContinueButton"));
  422. _buttonInstance.onClick.Invoke();
  423. }
  424. // change song index
  425. if (Input.GetKeyDown(KeyCode.N))
  426. {
  427. int newIndex = _songSelectionMasterView.GetSelectedSongIndex() - 1;
  428. _songListViewController.SelectSong(newIndex);
  429. _songSelectionMasterView.HandleSongListDidSelectSong(_songListViewController);
  430. SongListTableView songTableView = Resources.FindObjectsOfTypeAll<SongListTableView>().First();
  431. _songListViewController.HandleSongListTableViewDidSelectRow(songTableView, newIndex);
  432. }
  433. if (Input.GetKeyDown(KeyCode.M))
  434. {
  435. int newIndex = _songSelectionMasterView.GetSelectedSongIndex() + 1;
  436. _songListViewController.SelectSong(newIndex);
  437. _songSelectionMasterView.HandleSongListDidSelectSong(_songListViewController);
  438. SongListTableView songTableView = Resources.FindObjectsOfTypeAll<SongListTableView>().First();
  439. _songListViewController.HandleSongListTableViewDidSelectRow(songTableView, newIndex);
  440. }
  441. // add to favorites
  442. if (Input.GetKeyDown(KeyCode.F))
  443. {
  444. ToggleSongInFavorites();
  445. }
  446. }
  447. }
  448. }