SongBrowserMasterViewController.cs 20 KB

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