brick-engine-js 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (225) hide show
  1. package/dist/docs/GAME_DEVELOPER_GUIDE.html +1 -1
  2. package/dist/docs/brick-engine-guide.html +1 -1
  3. package/dist/docs/getting-started.html +1 -1
  4. package/dist/docs/jsdoc_standard.html +1 -1
  5. package/dist/docs/publishing.html +1 -1
  6. package/dist/docs/reference/interfaces/modules/Session.html +1 -1
  7. package/dist/docs/reference/modules/GameMenu.html +1 -1
  8. package/dist/docs/reference/modules/GameSession.html +1 -1
  9. package/dist/docs/reference/modules/InitialStateSnapshot.html +1 -1
  10. package/dist/docs/reference/modules/SessionModal.html +1 -1
  11. package/dist/docs/testing_best_practices.html +1 -1
  12. package/package.json +7 -1
  13. package/.env.local.example +0 -2
  14. package/.github/workflows/publish.yml +0 -73
  15. package/.prettierignore +0 -2
  16. package/.prettierrc.json +0 -8
  17. package/eslint.config.mjs +0 -29
  18. package/public/CNAME +0 -1
  19. package/public/docs/GAME_DEVELOPER_GUIDE.html +0 -727
  20. package/public/docs/brick-engine-guide.html +0 -610
  21. package/public/docs/diagrams/lifecycle.mmd +0 -19
  22. package/public/docs/documentation_style_guide.html +0 -994
  23. package/public/docs/getting-started.html +0 -648
  24. package/public/docs/images/lifecycle.svg +0 -1
  25. package/public/docs/index.html +0 -593
  26. package/public/docs/jsdoc_standard.html +0 -656
  27. package/public/docs/publishing.html +0 -573
  28. package/public/docs/reference/enums/Color.html +0 -533
  29. package/public/docs/reference/enums/ControlEventType.html +0 -505
  30. package/public/docs/reference/enums/ControlKey.html +0 -529
  31. package/public/docs/reference/enums/FontAlignment.html +0 -545
  32. package/public/docs/reference/enums/FontSize.html +0 -517
  33. package/public/docs/reference/enums/Sound.html +0 -558
  34. package/public/docs/reference/enums/StateProperty.html +0 -525
  35. package/public/docs/reference/helpers/CellHelper.html +0 -520
  36. package/public/docs/reference/helpers/ControlInputHandlerHelper.html +0 -569
  37. package/public/docs/reference/helpers/CoordinateHelper.html +0 -703
  38. package/public/docs/reference/helpers/RelativeValuesHelper.html +0 -560
  39. package/public/docs/reference/interfaces/Debuggable.html +0 -501
  40. package/public/docs/reference/interfaces/GameModules.html +0 -544
  41. package/public/docs/reference/interfaces/Initializable.html +0 -495
  42. package/public/docs/reference/interfaces/RendererInitializable.html +0 -517
  43. package/public/docs/reference/interfaces/StateSyncable.html +0 -542
  44. package/public/docs/reference/interfaces/modules/Control.html +0 -648
  45. package/public/docs/reference/interfaces/modules/Grid.html +0 -1256
  46. package/public/docs/reference/interfaces/modules/Renderer.html +0 -522
  47. package/public/docs/reference/interfaces/modules/RendererComposite.html +0 -577
  48. package/public/docs/reference/interfaces/modules/Score.html +0 -669
  49. package/public/docs/reference/interfaces/modules/Session.html +0 -585
  50. package/public/docs/reference/interfaces/modules/State.html +0 -897
  51. package/public/docs/reference/interfaces/modules/Text.html +0 -668
  52. package/public/docs/reference/interfaces/modules/Time.html +0 -684
  53. package/public/docs/reference/modules/Debugger.html +0 -579
  54. package/public/docs/reference/modules/DisplayRenderer.html +0 -557
  55. package/public/docs/reference/modules/Game.html +0 -909
  56. package/public/docs/reference/modules/GameControl.html +0 -716
  57. package/public/docs/reference/modules/GameGrid.html +0 -1910
  58. package/public/docs/reference/modules/GameHudGrid.html +0 -508
  59. package/public/docs/reference/modules/GameMenu.html +0 -538
  60. package/public/docs/reference/modules/GameRenderer.html +0 -589
  61. package/public/docs/reference/modules/GameScore.html +0 -664
  62. package/public/docs/reference/modules/GameSession.html +0 -533
  63. package/public/docs/reference/modules/GameSound.html +0 -636
  64. package/public/docs/reference/modules/GameState.html +0 -922
  65. package/public/docs/reference/modules/GameText.html +0 -701
  66. package/public/docs/reference/modules/GameTime.html +0 -696
  67. package/public/docs/reference/modules/HudRenderer.html +0 -568
  68. package/public/docs/reference/modules/InitialStateSnapshot.html +0 -557
  69. package/public/docs/reference/modules/SessionModal.html +0 -520
  70. package/public/docs/reference/types/Axis.html +0 -505
  71. package/public/docs/reference/types/Cell.html +0 -514
  72. package/public/docs/reference/types/ControlCallback.html +0 -488
  73. package/public/docs/reference/types/Coordinate.html +0 -510
  74. package/public/docs/reference/types/GameEntry.html +0 -514
  75. package/public/docs/reference/types/GameEvent.html +0 -514
  76. package/public/docs/reference/types/Piece.html +0 -506
  77. package/public/docs/reference/types/RendererMetrics.html +0 -514
  78. package/public/docs/reference/types/Vector.html +0 -509
  79. package/public/docs/testing_best_practices.html +0 -770
  80. package/public/favicon.ico +0 -0
  81. package/public/fonts/digital-7.monoitalic.ttf +0 -0
  82. package/public/images/cell.svg +0 -32
  83. package/public/images/close.png +0 -0
  84. package/public/images/games.png +0 -0
  85. package/public/images/github.png +0 -0
  86. package/public/images/letter-a.png +0 -0
  87. package/public/images/letter-d.png +0 -0
  88. package/public/images/letter-j.png +0 -0
  89. package/public/images/letter-s.png +0 -0
  90. package/public/images/letter-w.png +0 -0
  91. package/public/images/meta-image.png +0 -0
  92. package/public/images/number-1.png +0 -0
  93. package/public/images/number-2.png +0 -0
  94. package/public/images/number-3.png +0 -0
  95. package/public/images/number-4.png +0 -0
  96. package/public/images/number-5.png +0 -0
  97. package/public/images/number-6.png +0 -0
  98. package/public/images/splash.gif +0 -0
  99. package/public/index.html +0 -15
  100. package/public/sounds/sound_00.wav +0 -0
  101. package/public/sounds/sound_01.wav +0 -0
  102. package/public/sounds/sound_02.wav +0 -0
  103. package/public/sounds/sound_03.wav +0 -0
  104. package/public/sounds/sound_04.wav +0 -0
  105. package/public/sounds/sound_05.wav +0 -0
  106. package/public/sounds/sound_06.wav +0 -0
  107. package/public/sounds/sound_07.wav +0 -0
  108. package/public/sounds/sound_08.wav +0 -0
  109. package/public/sounds/sound_09.wav +0 -0
  110. package/public/sounds/sound_10.wav +0 -0
  111. package/public/sounds/sound_11.wav +0 -0
  112. package/public/sounds/sound_12.wav +0 -0
  113. package/public/sounds/sound_13.wav +0 -0
  114. package/public/sounds/sound_14.wav +0 -0
  115. package/public/sounds/sound_15.wav +0 -0
  116. package/public/style/body.css +0 -86
  117. package/public/style/buttons.css +0 -233
  118. package/public/style/debugger.css +0 -117
  119. package/public/style/sessionModal.css +0 -155
  120. package/public/style/sourceCodeAndCommands.css +0 -74
  121. package/public/style/splash.css +0 -13
  122. package/public/style/theme.css +0 -137
  123. package/scripts/generate-diagrams.sh +0 -20
  124. package/scripts/generate-docs.js +0 -111
  125. package/src/client-game.d.ts +0 -1
  126. package/src/config/configs.test.ts +0 -20
  127. package/src/config/configs.ts +0 -197
  128. package/src/config/env.test.ts +0 -59
  129. package/src/config/env.ts +0 -7
  130. package/src/config/styles.ts +0 -5
  131. package/src/core/Game.test.ts +0 -167
  132. package/src/core/Game.ts +0 -307
  133. package/src/core/InitialStateSnapshot.test.ts +0 -51
  134. package/src/core/InitialStateSnapshot.ts +0 -46
  135. package/src/core/helpers/CellHelper.test.ts +0 -33
  136. package/src/core/helpers/CellHelper.ts +0 -21
  137. package/src/core/helpers/ControlInputHandlerHelper.test.ts +0 -116
  138. package/src/core/helpers/ControlInputHandlerHelper.ts +0 -68
  139. package/src/core/helpers/CoordinateHelper.test.ts +0 -113
  140. package/src/core/helpers/CoordinateHelper.ts +0 -82
  141. package/src/core/helpers/InterfaceIdentifierHelper.test.ts +0 -122
  142. package/src/core/helpers/InterfaceIdentifierHelper.ts +0 -43
  143. package/src/core/helpers/RelativeValuesHelper.test.ts +0 -47
  144. package/src/core/helpers/RelativeValuesHelper.ts +0 -29
  145. package/src/core/module/control/GameControl.test.ts +0 -82
  146. package/src/core/module/control/GameControl.ts +0 -142
  147. package/src/core/module/control/GameControlKeyBinding.test.ts +0 -59
  148. package/src/core/module/control/GameControlKeyBinding.ts +0 -92
  149. package/src/core/module/grid/GameGrid.test.ts +0 -83
  150. package/src/core/module/grid/GameGrid.ts +0 -610
  151. package/src/core/module/grid/GameHudGrid.test.ts +0 -22
  152. package/src/core/module/grid/GameHudGrid.ts +0 -40
  153. package/src/core/module/grid/engines/GridAnalysisEngine.test.ts +0 -157
  154. package/src/core/module/grid/engines/GridAnalysisEngine.ts +0 -124
  155. package/src/core/module/grid/engines/GridLineEngine.test.ts +0 -132
  156. package/src/core/module/grid/engines/GridLineEngine.ts +0 -165
  157. package/src/core/module/grid/engines/GridMovementEngine.test.ts +0 -125
  158. package/src/core/module/grid/engines/GridMovementEngine.ts +0 -113
  159. package/src/core/module/grid/engines/GridRegionEngine.test.ts +0 -136
  160. package/src/core/module/grid/engines/GridRegionEngine.ts +0 -52
  161. package/src/core/module/grid/engines/GridTransformEngine.test.ts +0 -98
  162. package/src/core/module/grid/engines/GridTransformEngine.ts +0 -70
  163. package/src/core/module/renderer/DisplayRenderer.test.ts +0 -86
  164. package/src/core/module/renderer/DisplayRenderer.ts +0 -152
  165. package/src/core/module/renderer/GameRenderer.test.ts +0 -103
  166. package/src/core/module/renderer/GameRenderer.ts +0 -144
  167. package/src/core/module/renderer/HudRenderer.test.ts +0 -108
  168. package/src/core/module/renderer/HudRenderer.ts +0 -203
  169. package/src/core/module/score/GameScore.test.ts +0 -71
  170. package/src/core/module/score/GameScore.ts +0 -188
  171. package/src/core/module/session/GameSession.test.ts +0 -176
  172. package/src/core/module/session/GameSession.ts +0 -103
  173. package/src/core/module/sound/GameSound.test.ts +0 -117
  174. package/src/core/module/sound/GameSound.ts +0 -229
  175. package/src/core/module/state/GameState.test.ts +0 -101
  176. package/src/core/module/state/GameState.ts +0 -339
  177. package/src/core/module/text/GameText.test.ts +0 -87
  178. package/src/core/module/text/GameText.ts +0 -150
  179. package/src/core/module/time/GameTime.test.ts +0 -86
  180. package/src/core/module/time/GameTime.ts +0 -144
  181. package/src/core/types/Interfaces.ts +0 -59
  182. package/src/core/types/Types.ts +0 -124
  183. package/src/core/types/enums.ts +0 -113
  184. package/src/core/types/modules.ts +0 -841
  185. package/src/index.test.ts +0 -15
  186. package/src/index.ts +0 -9
  187. package/src/main.test.ts +0 -137
  188. package/src/main.ts +0 -77
  189. package/src/menu/GameMenu.test.ts +0 -157
  190. package/src/menu/GameMenu.ts +0 -124
  191. package/src/menu/GameMenuSingleton.test.ts +0 -26
  192. package/src/menu/GameMenuSingleton.ts +0 -13
  193. package/src/menu/GameRepository.test.ts +0 -46
  194. package/src/menu/GameRepository.ts +0 -47
  195. package/src/menu/manager/GameManager.test.ts +0 -68
  196. package/src/menu/manager/GameManager.ts +0 -50
  197. package/src/types/global.d.ts +0 -8
  198. package/src/types/interfaces.ts +0 -5
  199. package/src/view/Debugger.test.ts +0 -152
  200. package/src/view/Debugger.ts +0 -124
  201. package/src/view/GameView.test.ts +0 -95
  202. package/src/view/GameView.ts +0 -244
  203. package/src/view/SessionModal.test.ts +0 -141
  204. package/src/view/SessionModal.ts +0 -73
  205. package/src/view/components/layout/ButtonLayout.test.ts +0 -28
  206. package/src/view/components/layout/ButtonLayout.ts +0 -63
  207. package/src/view/components/layout/ContainerLayout.test.ts +0 -48
  208. package/src/view/components/layout/ContainerLayout.ts +0 -50
  209. package/src/view/components/layout/FrameLayout.test.ts +0 -24
  210. package/src/view/components/layout/FrameLayout.ts +0 -25
  211. package/src/view/components/ui/BigButton.test.ts +0 -28
  212. package/src/view/components/ui/BigButton.ts +0 -31
  213. package/src/view/components/ui/Button.test.ts +0 -30
  214. package/src/view/components/ui/Button.ts +0 -30
  215. package/src/view/components/ui/Canvas.test.ts +0 -32
  216. package/src/view/components/ui/Canvas.ts +0 -34
  217. package/src/view/components/ui/SmallButton.test.ts +0 -48
  218. package/src/view/components/ui/SmallButton.ts +0 -32
  219. package/src/view/theme/applyColors.test.ts +0 -47
  220. package/src/view/theme/applyColors.ts +0 -38
  221. package/src/view/theme/dimensions.test.ts +0 -34
  222. package/src/view/theme/dimensions.ts +0 -53
  223. package/tsconfig.json +0 -16
  224. package/vitest.config.ts +0 -14
  225. package/webpack.config.js +0 -133
package/src/index.test.ts DELETED
@@ -1,15 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import * as BrickEngine from './index';
3
-
4
- describe('index (Library Exports)', () => {
5
- it('should export the core modules and configurations', () => {
6
- // [ASSERT]
7
- expect(BrickEngine.Game).toBeDefined();
8
- expect(BrickEngine.GameView).toBeDefined();
9
- expect(BrickEngine.configs).toBeDefined();
10
-
11
- // Exported enums/types check
12
- expect(BrickEngine.Color).toBeDefined();
13
- expect(BrickEngine.FontSize).toBeDefined();
14
- });
15
- });
package/src/index.ts DELETED
@@ -1,9 +0,0 @@
1
- // Library Exports Only
2
- export * from './core/types/enums';
3
- export * from './core/types/Interfaces';
4
- export * from './core/types/modules';
5
- export * from './core/types/Types';
6
- export * from './types/interfaces';
7
- export { default as Game } from './core/Game';
8
- export { default as GameView } from './view/GameView';
9
- export { default as configs } from './config/configs';
package/src/main.test.ts DELETED
@@ -1,137 +0,0 @@
1
- /** @vitest-environment jsdom */
2
- import { describe, it, expect, vi, beforeEach, Mock } from 'vitest';
3
- import p5 from 'p5';
4
- import Game from './core/Game';
5
-
6
- // Mocking everything before importing main.ts logic
7
- const createMockGame = () => ({
8
- setup: vi.fn(),
9
- setSwitchHandler: vi.fn(),
10
- propagateSwitchHandler: vi.fn(),
11
- draw: vi.fn(),
12
- modules: {
13
- state: { turnOn: vi.fn(), turnOff: vi.fn(), isOn: vi.fn() },
14
- control: { subscribe: vi.fn() },
15
- time: { update: vi.fn() },
16
- },
17
- view: {
18
- unbindControls: vi.fn(),
19
- bindControls: vi.fn(),
20
- updateDebuggerGameModules: vi.fn(),
21
- setupDebugger: vi.fn(),
22
- },
23
- });
24
-
25
- const mockGame = createMockGame();
26
-
27
- vi.mock('p5', () => {
28
- return {
29
- default: vi.fn().mockImplementation(function (sketch: (p: unknown) => void) {
30
- const p = {
31
- loop: vi.fn(),
32
- noLoop: vi.fn(),
33
- setup: vi.fn(),
34
- draw: vi.fn(),
35
- };
36
- sketch(p);
37
- return p;
38
- }),
39
- };
40
- });
41
-
42
- vi.mock('./view/GameView', () => {
43
- return {
44
- default: vi.fn().mockImplementation(function () {
45
- return {
46
- build: vi.fn().mockReturnValue({ canvas: {}, canvasHeight: 100, canvasWidth: 100 }),
47
- bindControls: vi.fn(),
48
- unbindControls: vi.fn(),
49
- setupDebugger: vi.fn(),
50
- updateDebuggerGameModules: vi.fn(),
51
- };
52
- }),
53
- };
54
- });
55
-
56
- vi.mock('./menu/GameMenu', () => {
57
- return {
58
- default: vi.fn().mockImplementation(function () {
59
- return mockGame;
60
- }),
61
- };
62
- });
63
-
64
- vi.mock('./core/Debugger', () => ({
65
- default: vi.fn().mockImplementation(function () {
66
- return { setup: vi.fn(), update: vi.fn(), destroy: vi.fn() };
67
- }),
68
- }));
69
-
70
- vi.mock('./config/env', () => ({
71
- isClientMode: vi.fn(),
72
- isServerMode: vi.fn(),
73
- }));
74
-
75
- vi.mock('@client-game', () => ({
76
- default: vi.fn().mockImplementation(function () {
77
- return mockGame;
78
- }),
79
- }));
80
-
81
- vi.mock('./menu/GameMenuSingleton', () => ({
82
- default: {
83
- setInstance: vi.fn(),
84
- getInstance: vi.fn().mockReturnValue(mockGame),
85
- },
86
- }));
87
-
88
- vi.mock('./config/styles', () => ({}));
89
-
90
- describe('main.ts', () => {
91
- beforeEach(() => {
92
- vi.clearAllMocks();
93
- vi.resetModules();
94
- });
95
-
96
- it('should initialize the p5 instance and GameMenu in server mode', async () => {
97
- const { isClientMode, isServerMode } = await import('./config/env');
98
- const GameMenu = (await import('./menu/GameMenu')).default;
99
-
100
- (isClientMode as unknown as Mock).mockReturnValue(false);
101
- (isServerMode as unknown as Mock).mockReturnValue(true);
102
-
103
- const main = await import('./main');
104
-
105
- expect(p5).toHaveBeenCalled();
106
- expect(GameMenu).toHaveBeenCalled();
107
- expect(main.p5Instance).toBeDefined();
108
- });
109
-
110
- it('should initialize ClientGame in client mode', async () => {
111
- const { isClientMode, isServerMode } = await import('./config/env');
112
- const ClientGame = (await import('@client-game')).default;
113
-
114
- (isClientMode as unknown as Mock).mockReturnValue(true);
115
- (isServerMode as unknown as Mock).mockReturnValue(false);
116
-
117
- await import('./main');
118
-
119
- expect(ClientGame).toHaveBeenCalled();
120
- });
121
-
122
- it('should handle game switching', async () => {
123
- const { isClientMode, isServerMode } = await import('./config/env');
124
- (isClientMode as unknown as Mock).mockReturnValue(false);
125
- (isServerMode as unknown as Mock).mockReturnValue(true);
126
-
127
- await import('./main');
128
-
129
- const switchHandler = (mockGame.setSwitchHandler as unknown as Mock).mock.calls[0][0];
130
- const nextGame = createMockGame();
131
-
132
- switchHandler(nextGame as unknown as Game);
133
-
134
- expect(nextGame.setup).toHaveBeenCalled();
135
- expect(nextGame.modules.state.turnOn).toHaveBeenCalled();
136
- });
137
- });
package/src/main.ts DELETED
@@ -1,77 +0,0 @@
1
- import p5 from 'p5';
2
- import Game from './core/Game';
3
- import GameView from './view/GameView';
4
- import GameMenu from './menu/GameMenu';
5
-
6
- import './config/styles';
7
-
8
- import { isClientMode, isServerMode } from './config/env';
9
- import ClientGame from '@client-game';
10
- import { ControlEventType, ControlKey } from './core/types/enums';
11
- import GameMenuSingleton from './menu/GameMenuSingleton';
12
-
13
- export const p5Instance = new p5((p: p5) => {
14
- const view = new GameView(p, document.body);
15
- let activeGame: Game;
16
-
17
- if (isClientMode()) {
18
- // In client mode, we instantiate the game provided via alias
19
- activeGame = new ClientGame(p, view);
20
- activeGame.gameId = 'client-game';
21
- } else if (isServerMode()) {
22
- // In server mode, we instantiate the game menu
23
- activeGame = new GameMenu(p, view);
24
- activeGame.gameId = 'game-menu';
25
- GameMenuSingleton.setInstance(activeGame as GameMenu);
26
- } else {
27
- throw new Error('Invalid APP_MODE');
28
- }
29
-
30
- // Register the switch handler
31
- activeGame.setSwitchHandler((newGame: Game) => {
32
- try {
33
- // Unbind the previous game controls
34
- activeGame.view.unbindControls();
35
-
36
- // Propagate the switch handler to the new game
37
- newGame.propagateSwitchHandler(activeGame);
38
-
39
- // Set the new game
40
- activeGame = newGame;
41
- activeGame.setup();
42
-
43
- // Update debugger
44
- activeGame.view.updateDebuggerGameModules(activeGame.modules);
45
-
46
- // Bind the new game controls
47
- activeGame.view.bindControls(activeGame.modules.control);
48
- activeGame.modules.state.turnOn();
49
-
50
- // Setup exit and power buttons
51
- if (isServerMode() && newGame !== GameMenuSingleton.getInstance()) {
52
- newGame.modules.control.subscribe(ControlKey.EXIT, ControlEventType.PRESSED, () => {
53
- newGame.switchGame(GameMenuSingleton.getInstance());
54
- });
55
-
56
- newGame.modules.control.subscribe(ControlKey.POWER, ControlEventType.PRESSED, () => {
57
- newGame.switchGame(GameMenuSingleton.getInstance());
58
- activeGame.modules.state.turnOff();
59
- });
60
- }
61
- } catch (error) {
62
- console.error('Error switching game:', error);
63
- }
64
- p.loop();
65
- });
66
-
67
- p.setup = () => {
68
- activeGame.setup();
69
- activeGame.view.setupDebugger(activeGame.modules);
70
- activeGame.view.setupSessionModal();
71
- };
72
-
73
- p.draw = () => {
74
- activeGame.draw();
75
- activeGame.view.updateDebugger();
76
- };
77
- }, document.body);
@@ -1,157 +0,0 @@
1
- /** @vitest-environment jsdom */
2
- import { describe, it, expect, vi, beforeEach } from 'vitest';
3
- import GameMenu from './GameMenu';
4
- import GameView from '../view/GameView';
5
- import p5 from 'p5';
6
- import { ControlEventType, ControlKey } from '../core/types/enums';
7
-
8
- // Mock GameRepository to avoid external dependencies
9
- vi.mock('./GameRepository', () => {
10
- return {
11
- default: vi.fn().mockImplementation(function () {
12
- return {
13
- games: [
14
- { id: '1', name: 'Game 1', url: 'url1' },
15
- { id: '2', name: 'Game 2', url: 'url2' },
16
- ],
17
- };
18
- }),
19
- };
20
- });
21
-
22
- // Mock GameManager
23
- vi.mock('./manager/GameManager', () => {
24
- return {
25
- default: vi.fn().mockImplementation(function () {
26
- return {
27
- handleGameSwitch: vi.fn(),
28
- };
29
- }),
30
- };
31
- });
32
-
33
- describe('GameMenu', () => {
34
- let menu: GameMenu;
35
- let mockP5: Record<string, unknown>;
36
- let mockView: Record<string, unknown>;
37
-
38
- beforeEach(() => {
39
- vi.stubGlobal('localStorage', {
40
- getItem: vi.fn().mockReturnValue(null),
41
- setItem: vi.fn(),
42
- clear: vi.fn(),
43
- });
44
-
45
- vi.stubGlobal(
46
- 'AudioContext',
47
- vi.fn().mockImplementation(function () {
48
- return {
49
- createGain: vi.fn().mockReturnValue({
50
- connect: vi.fn(),
51
- gain: { setValueAtTime: vi.fn() },
52
- }),
53
- createBufferSource: vi.fn().mockReturnValue({
54
- connect: vi.fn(),
55
- start: vi.fn(),
56
- buffer: null,
57
- }),
58
- destination: {},
59
- decodeAudioData: vi.fn().mockResolvedValue({}),
60
- resume: vi.fn().mockResolvedValue({}),
61
- };
62
- }),
63
- );
64
-
65
- vi.stubGlobal(
66
- 'fetch',
67
- vi.fn().mockResolvedValue({
68
- arrayBuffer: vi.fn().mockResolvedValue(new ArrayBuffer(0)),
69
- }),
70
- );
71
-
72
- mockP5 = {
73
- deltaTime: 16,
74
- noLoop: vi.fn(),
75
- push: vi.fn(),
76
- pop: vi.fn(),
77
- translate: vi.fn(),
78
- strokeWeight: vi.fn(),
79
- stroke: vi.fn(),
80
- noFill: vi.fn(),
81
- rect: vi.fn(),
82
- fill: vi.fn(),
83
- text: vi.fn(),
84
- textFont: vi.fn(),
85
- textSize: vi.fn(),
86
- textAlign: vi.fn(),
87
- image: vi.fn(),
88
- createGraphics: vi.fn().mockReturnValue({
89
- background: vi.fn(),
90
- strokeWeight: vi.fn(),
91
- noFill: vi.fn(),
92
- stroke: vi.fn(),
93
- rect: vi.fn(),
94
- }),
95
- };
96
-
97
- mockView = {
98
- build: vi.fn(),
99
- bindControls: vi.fn(),
100
- showSessionModal: vi.fn(),
101
- };
102
-
103
- menu = new GameMenu(mockP5 as unknown as p5, mockView as unknown as GameView);
104
- menu.setup();
105
- });
106
-
107
- it('should cycle through games on LEFT/RIGHT arrow press', () => {
108
- const { control, state } = menu.modules;
109
-
110
- // Ensure game is ON and STARTED/PLAYING
111
- state.turnOn();
112
- state.startGame();
113
-
114
- expect(state.isPlaying()).toBe(true);
115
-
116
- // Initial selection should be 0 (Game 1)
117
- expect((menu as unknown as Record<string, unknown>)['_gameSelectionPointer']).toBe(0);
118
-
119
- // Press RIGHT
120
- control.notify(ControlKey.RIGHT, ControlEventType.PRESSED);
121
- expect((menu as unknown as Record<string, unknown>)['_gameSelectionPointer']).toBe(1);
122
-
123
- // Press RIGHT again (should cycle)
124
- control.notify(ControlKey.RIGHT, ControlEventType.PRESSED);
125
- expect((menu as unknown as Record<string, unknown>)['_gameSelectionPointer']).toBe(0);
126
-
127
- // Press LEFT (should cycle back)
128
- control.notify(ControlKey.LEFT, ControlEventType.PRESSED);
129
- expect((menu as unknown as Record<string, unknown>)['_gameSelectionPointer']).toBe(1);
130
- });
131
-
132
- it('should handle game switch on ACTION press', () => {
133
- const { control, state } = menu.modules;
134
- state.turnOn();
135
- state.startGame();
136
-
137
- expect(state.isStarted()).toBe(true);
138
-
139
- const managerSpy = (menu as unknown as { _gameManager: { handleGameSwitch: import('vitest').Mock } })._gameManager.handleGameSwitch;
140
-
141
- control.notify(ControlKey.ACTION, ControlEventType.PRESSED);
142
-
143
- expect(managerSpy).toHaveBeenCalledWith(expect.objectContaining({ name: 'Game 1' }), menu);
144
- });
145
-
146
- it('should play theme when turned on', () => {
147
- const { state, sound } = menu.modules;
148
-
149
- // Mock play to resolve
150
- const soundSpy = vi.spyOn(sound, 'play').mockResolvedValue(undefined as unknown as void);
151
-
152
- state.turnOn();
153
-
154
- expect(state.isOn()).toBe(true);
155
- expect(soundSpy).toHaveBeenCalled();
156
- });
157
- });
@@ -1,124 +0,0 @@
1
- import p5 from 'p5';
2
- import GameView from '../view/GameView';
3
- import Game from '../core/Game';
4
- import { ControlEventType, ControlKey, FontAlign, FontSize, FontVerticalAlign, Sound, StateProperty } from '../core/types/enums';
5
- import GameRepository from './GameRepository';
6
- import GameManager from './manager/GameManager';
7
-
8
- export default class GameMenu extends Game {
9
- private _gameSelectionPointer = 0;
10
- private _isLoading = false;
11
-
12
- private _gameRepository = new GameRepository();
13
- private _gameManager = new GameManager();
14
-
15
- constructor(p: p5, view: GameView) {
16
- super(p, view);
17
- }
18
-
19
- setupGame() {
20
- const { state, control, sound } = this.modules;
21
-
22
- control.subscribe(ControlKey.ACTION, ControlEventType.PRESSED, () => {
23
- if (this._isLoading) return;
24
-
25
- if (state.isStarted()) {
26
- const selectedGame = this._gameRepository.games[this._gameSelectionPointer];
27
- this._gameManager.handleGameSwitch(selectedGame, this);
28
- }
29
- });
30
-
31
- control.subscribe(ControlKey.LEFT, ControlEventType.PRESSED, () => {
32
- if (state.isPlaying()) {
33
- sound.play(Sound.ACTION_1);
34
- if (this._gameSelectionPointer === 0) {
35
- this._gameSelectionPointer = this._gameRepository.games.length - 1;
36
- } else {
37
- this._gameSelectionPointer--;
38
- }
39
- }
40
- });
41
-
42
- control.subscribe(ControlKey.RIGHT, ControlEventType.PRESSED, () => {
43
- if (state.isPlaying()) {
44
- sound.play(Sound.ACTION_1);
45
- if (this._gameSelectionPointer === this._gameRepository.games.length - 1) {
46
- this._gameSelectionPointer = 0;
47
- } else {
48
- this._gameSelectionPointer++;
49
- }
50
- }
51
- });
52
-
53
- state.subscribe(StateProperty.ON, on => {
54
- if (on) {
55
- sound.play(Sound.START_THEME);
56
- }
57
- });
58
- }
59
-
60
- update() {}
61
-
62
- render() {
63
- const { text } = this.modules;
64
-
65
- const { p } = this;
66
- p.push();
67
-
68
- text.setTextSize(FontSize.LARGE);
69
- text.setActiveText();
70
- text.setTextAlign(FontAlign.CENTER, FontVerticalAlign.BOTTOM);
71
-
72
- text.textOnDisplay('Menu', { x: 0.5, y: 0.15 });
73
-
74
- text.setTextSize(FontSize.SMALL);
75
-
76
- text.textOnDisplay('Choose a game and', { x: 0.5, y: 0.25 });
77
- text.textOnDisplay('Press action to play', { x: 0.5, y: 0.32 });
78
-
79
- text.setTextAlign(FontAlign.RIGHT, FontVerticalAlign.BOTTOM);
80
- text.textOnDisplay('<', { x: 0.1, y: 0.54 });
81
-
82
- text.setTextAlign(FontAlign.LEFT, FontVerticalAlign.BOTTOM);
83
- text.textOnDisplay('>', { x: 0.9, y: 0.54 });
84
-
85
- text.setTextSize(FontSize.MEDIUM);
86
- text.setTextAlign(FontAlign.CENTER, FontVerticalAlign.BOTTOM);
87
- text.textOnDisplay(this._gameRepository.games[this._gameSelectionPointer].name, { x: 0.5, y: 0.55 });
88
-
89
- text.setTextSize(FontSize.EXTRA_SMALL);
90
- text.setTextAlign(FontAlign.LEFT, FontVerticalAlign.BOTTOM);
91
-
92
- text.textOnDisplay('Left: Previous option', { x: 0.05, y: 0.78 });
93
- text.textOnDisplay('Right: Next option', { x: 0.05, y: 0.84 });
94
- text.textOnDisplay('Action: Select', { x: 0.05, y: 0.9 });
95
-
96
- p.pop();
97
- }
98
-
99
- drawTitleScreen() {
100
- const { text } = this.modules;
101
-
102
- this.p.push();
103
-
104
- text.setTextSize(FontSize.LARGE);
105
- text.setActiveText();
106
- text.setTextAlign(FontAlign.CENTER, FontVerticalAlign.TOP);
107
-
108
- text.textOnDisplay('Menu', { x: 0.5, y: 0.15 });
109
-
110
- text.setTextSize(FontSize.SMALL);
111
-
112
- text.textOnDisplay('Wellcome to your', { x: 0.5, y: 0.25 });
113
- text.textOnDisplay('favorite brick game', { x: 0.5, y: 0.32 });
114
- text.textOnDisplay('simulator!', { x: 0.5, y: 0.39 });
115
- text.textOnDisplay('Press start', { x: 0.5, y: 0.66 });
116
- text.textOnDisplay('to continue.', { x: 0.5, y: 0.72 });
117
-
118
- this.p.pop();
119
- }
120
-
121
- drawGameOverScreen() {
122
- // Menu doesn't have a game over screen
123
- }
124
- }
@@ -1,26 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import GameMenuSingleton from './GameMenuSingleton';
3
- import GameMenu from './GameMenu';
4
-
5
- describe('GameMenuSingleton', () => {
6
- it('should store and retrieve a GameMenu instance', () => {
7
- const mockInstance = { name: 'MockGameMenu' } as unknown as GameMenu;
8
-
9
- GameMenuSingleton.setInstance(mockInstance);
10
- const retrieved = GameMenuSingleton.getInstance();
11
-
12
- expect(retrieved).toBe(mockInstance);
13
- expect((retrieved as unknown as { name: string }).name).toBe('MockGameMenu');
14
- });
15
-
16
- it('should overwrite the previous instance when setInstance is called again', () => {
17
- const mockInstance1 = { id: 1 } as unknown as GameMenu;
18
- const mockInstance2 = { id: 2 } as unknown as GameMenu;
19
-
20
- GameMenuSingleton.setInstance(mockInstance1);
21
- expect(GameMenuSingleton.getInstance()).toBe(mockInstance1);
22
-
23
- GameMenuSingleton.setInstance(mockInstance2);
24
- expect(GameMenuSingleton.getInstance()).toBe(mockInstance2);
25
- });
26
- });
@@ -1,13 +0,0 @@
1
- import GameMenu from './GameMenu';
2
-
3
- export default class GameMenuSingleton {
4
- private static _instance: GameMenu;
5
-
6
- static setInstance(instance: GameMenu) {
7
- GameMenuSingleton._instance = instance;
8
- }
9
-
10
- static getInstance(): GameMenu {
11
- return GameMenuSingleton._instance;
12
- }
13
- }
@@ -1,46 +0,0 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
- import GameRepository from './GameRepository';
3
-
4
- describe('GameRepository', () => {
5
- beforeEach(() => {
6
- vi.stubGlobal(
7
- 'fetch',
8
- vi.fn().mockResolvedValue({
9
- ok: true,
10
- json: vi.fn().mockResolvedValue({
11
- games: [{ id: 'test-game', name: 'Test Game', url: 'http://localhost:8080/game.bundle.js' }],
12
- }),
13
- }),
14
- );
15
- });
16
-
17
- afterEach(() => {
18
- vi.unstubAllGlobals();
19
- });
20
-
21
- it('should start with a loading state', () => {
22
- const repo = new GameRepository();
23
- expect(repo.games).toBeDefined();
24
- expect(repo.games.length).toBeGreaterThan(0);
25
- expect(repo.games[0].name).toBe('Loading...');
26
- expect(repo.games[0].id).toBe('loading');
27
- });
28
-
29
- it('should load games asynchronously', async () => {
30
- const repo = new GameRepository();
31
- // Wait for the microtask queue to process the fetch
32
- await new Promise(resolve => setTimeout(resolve, 0));
33
-
34
- expect(repo.games[0].id).toBe('test-game');
35
- expect(repo.games[0].name).toBe('Test Game');
36
- expect(repo.games[0].url).toBe('http://localhost:8080/game.bundle.js');
37
- });
38
-
39
- it('should return a frozen list of games', () => {
40
- const repo = new GameRepository();
41
- const games = repo.games;
42
-
43
- // This should throw in strict mode, or at least not change if we tried to mutate it
44
- expect(Object.isFrozen(games)).toBe(true);
45
- });
46
- });
@@ -1,47 +0,0 @@
1
- import { GameEntry } from '../types/interfaces';
2
-
3
- export default class GameRepository {
4
- private _games: GameEntry[] = [{ id: 'loading', name: 'Loading...', url: '' }];
5
-
6
- constructor() {
7
- this.fetchGames();
8
- }
9
-
10
- private async fetchGames(): Promise<void> {
11
- try {
12
- // Read from injected env variables, or fallback to local
13
- const supabaseUrl = process.env.SUPABASE_URL || 'http://127.0.0.1:54321';
14
- const anonKey = process.env.SUPABASE_ANON_KEY || '';
15
-
16
- const headers: HeadersInit = {};
17
- if (anonKey) {
18
- headers['Authorization'] = `Bearer ${anonKey}`;
19
- }
20
-
21
- const response = await fetch(`${supabaseUrl}/functions/v1/list`, {
22
- headers,
23
- });
24
-
25
- if (!response.ok) {
26
- throw new Error(`Error fetching games: ${response.statusText}`);
27
- }
28
- const data = await response.json();
29
- if (data && data.games && data.games.length > 0) {
30
- this._games = data.games.map((game: GameEntry) => ({
31
- id: game.id,
32
- name: game.name,
33
- url: game.url,
34
- }));
35
- } else {
36
- this._games = [{ id: 'empty', name: 'Not found', url: '' }];
37
- }
38
- } catch (error) {
39
- console.error(error);
40
- this._games = [{ id: 'error', name: 'Error', url: '' }];
41
- }
42
- }
43
-
44
- get games(): readonly GameEntry[] {
45
- return Object.freeze(this._games);
46
- }
47
- }