select.ts 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. import * as level from '../level';
  2. import * as util from '../util';
  3. import {
  4. GameContext,
  5. Screen,
  6. } from './common';
  7. import {
  8. TypingScreen
  9. } from './typing';
  10. export class SelectScreen implements Screen {
  11. readonly name: string = 'select';
  12. folderInfo: HTMLElement;
  13. songInfo: HTMLElement;
  14. songList: HTMLElement;
  15. currentFolderIndex: number;
  16. folderController: FolderSelectController;
  17. listControllers: SongListController[];
  18. init: boolean;
  19. get levelSets() {
  20. return this.context.config!.levelSets;
  21. }
  22. get currentLevelSet() {
  23. return this.levelSets[this.currentFolderIndex];
  24. }
  25. get activeListController() {
  26. return this.listControllers[this.currentFolderIndex];
  27. }
  28. constructor(private context: GameContext) {
  29. let container = context.container;
  30. this.folderInfo = util.getElement(container, '#folder-info');
  31. this.currentFolderIndex = 0;
  32. this.songInfo = util.getElement(container, '#song-info');
  33. this.songList = util.getElement(container, '#song-list');
  34. this.listControllers = [];
  35. this.levelSets.forEach(levelSet => {
  36. let controller = new SongListController(
  37. this.context,
  38. levelSet.levels,
  39. (index) => this.selectSong(index),
  40. (index) => this.chooseSong(index)
  41. );
  42. this.listControllers.push(controller);
  43. });
  44. this.init = true;
  45. this.folderController = new FolderSelectController(
  46. this.folderInfo,
  47. this.levelSets,
  48. (index) => this.selectLevelSet(index)
  49. );
  50. this.init = false;
  51. }
  52. enter(): void {
  53. this.context.bgManager.setBackground(this.context.config!.background);
  54. this.folderController.listeners.attach();
  55. }
  56. handleInput(key: string): void {
  57. this.activeListController.handleInput(key);
  58. this.folderController.handleInput(key);
  59. }
  60. selectSong(index: number): void {
  61. const { selectSound } = this.context.assets!;
  62. if (!this.init && selectSound !== null) {
  63. selectSound.play();
  64. }
  65. let level = this.currentLevelSet.levels[index];
  66. this.songInfo.querySelector('.genre')!.textContent = level.genre;
  67. this.songInfo.querySelector('.creator')!.textContent = level.creator;
  68. this.songInfo.querySelector('.title')!.textContent = level.name;
  69. const linkContainer = this.songInfo.querySelector('.link')!;
  70. linkContainer.innerHTML = '';
  71. if (level.songLink) {
  72. const link = document.createElement('a');
  73. link.href = level.songLink;
  74. link.textContent = "More info";
  75. linkContainer.appendChild(link);
  76. }
  77. }
  78. chooseSong(index: number): void {
  79. const { decideSound } = this.context.assets!;
  80. if (decideSound !== null) {
  81. decideSound.play();
  82. }
  83. let level = this.currentLevelSet.levels[index];
  84. let gameScreen = new TypingScreen(this.context, level, this);
  85. this.context.switchScreen(gameScreen);
  86. }
  87. selectLevelSet(index: number): void {
  88. this.currentFolderIndex = index;
  89. util.clearChildren(this.songList);
  90. this.songList.appendChild(this.activeListController.element);
  91. this.selectSong(this.activeListController.currentIndex);
  92. }
  93. exit(): void {
  94. this.folderController.listeners.detach();
  95. }
  96. transitionExit(): void {}
  97. }
  98. class FolderSelectController {
  99. labelElement: HTMLElement;
  100. levelSets: level.LevelSet[];
  101. currentIndex: number;
  102. onFolderChange: (index: number) => void;
  103. listeners: util.ListenersManager;
  104. constructor(element: HTMLElement, levelSets: level.LevelSet[], onFolderChange: (index: number) => void) {
  105. this.labelElement = util.getElement(element, '.label');
  106. this.levelSets = levelSets;
  107. this.currentIndex = 0;
  108. this.onFolderChange = onFolderChange;
  109. this.listeners = new util.ListenersManager();
  110. this.listeners.add(
  111. element.querySelector('.left')!,
  112. 'click',
  113. () => this.scroll(-1)
  114. );
  115. this.listeners.add(
  116. element.querySelector('.right')!,
  117. 'click',
  118. () => this.scroll(1)
  119. );
  120. this.scroll(0);
  121. }
  122. handleInput(key: string): void {
  123. if (key === 'ArrowLeft' || key === 'h') {
  124. this.scroll(-1);
  125. } else if (key === 'ArrowRight' || key === 'l') {
  126. this.scroll(1);
  127. }
  128. }
  129. scroll(offset: number): void {
  130. this.currentIndex += offset;
  131. while (this.currentIndex < 0) {
  132. this.currentIndex += this.levelSets.length;
  133. }
  134. this.currentIndex %= this.levelSets.length;
  135. this.labelElement.textContent = this.levelSets[this.currentIndex].name;
  136. this.onFolderChange(this.currentIndex);
  137. }
  138. }
  139. class SongListController {
  140. element: HTMLElement;
  141. levels: level.Level[];
  142. currentIndex: number;
  143. onSongChange: (index: number) => void;
  144. onSongChoose: (index: number) => void;
  145. constructor(
  146. context: GameContext,
  147. levels: level.Level[],
  148. onSongChange: (index: number) => void,
  149. onSongChoose: (index: number) => void
  150. ) {
  151. this.element = document.createElement('div');
  152. this.levels = levels;
  153. this.currentIndex = 0;
  154. this.onSongChange = onSongChange;
  155. this.onSongChoose = onSongChoose;
  156. this.element.className = 'song-list';
  157. this.element.style.marginTop = '12.5em';
  158. this.levels.forEach((level, index) => {
  159. let element = context.loadTemplate('song-item');
  160. element.querySelector('.creator')!.textContent = level.creator;
  161. element.querySelector('.title')!.textContent = level.name;
  162. element.querySelector('.difficulty')!.textContent = level.difficulty;
  163. element.querySelector('.song-item')!.addEventListener('click', (event) => this.click(index));
  164. this.element.appendChild(element);
  165. });
  166. this.element.children[0].classList.add('selected');
  167. }
  168. handleInput(key: string): void {
  169. if (key === 'ArrowUp' || key === 'k') {
  170. this.scroll(-1);
  171. } else if (key === 'ArrowDown' || key === 'j') {
  172. this.scroll(1);
  173. } else if (key === 'PageUp') {
  174. this.scroll(-5);
  175. } else if (key === 'PageDown') {
  176. this.scroll(5);
  177. } else if (key === ' ' || key === 'Enter') {
  178. this.choose();
  179. }
  180. }
  181. scroll(offset: number) {
  182. let target = this.currentIndex + offset;
  183. target = Math.max(0, Math.min(this.levels.length - 1, target));
  184. this.select(target);
  185. }
  186. click(index: number) {
  187. if (this.currentIndex === index) {
  188. this.choose();
  189. } else {
  190. this.select(index);
  191. }
  192. }
  193. select(index: number) {
  194. if (this.currentIndex === index) return;
  195. let offset = 12.5 - index * 2.5;
  196. this.element.style.marginTop = offset+'em';
  197. let nextElement = this.element.children[index] as HTMLElement;
  198. let currElement = this.element.children[this.currentIndex] as HTMLElement;
  199. currElement.classList.remove('selected');
  200. nextElement.classList.add('selected');
  201. this.currentIndex = index;
  202. this.onSongChange(index);
  203. }
  204. choose() {
  205. this.onSongChoose(this.currentIndex);
  206. }
  207. }