brick-engine-js 1.0.1 → 1.0.3

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 (131) 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 +8 -1
  13. package/public/docs/GAME_DEVELOPER_GUIDE.html +1 -1
  14. package/public/docs/brick-engine-guide.html +1 -1
  15. package/public/docs/getting-started.html +1 -1
  16. package/public/docs/jsdoc_standard.html +1 -1
  17. package/public/docs/publishing.html +1 -1
  18. package/public/docs/reference/interfaces/modules/Session.html +1 -1
  19. package/public/docs/reference/modules/GameMenu.html +1 -1
  20. package/public/docs/reference/modules/GameSession.html +1 -1
  21. package/public/docs/reference/modules/InitialStateSnapshot.html +1 -1
  22. package/public/docs/reference/modules/SessionModal.html +1 -1
  23. package/public/docs/testing_best_practices.html +1 -1
  24. package/.env.local.example +0 -2
  25. package/.github/workflows/publish.yml +0 -73
  26. package/.prettierignore +0 -2
  27. package/.prettierrc.json +0 -8
  28. package/eslint.config.mjs +0 -29
  29. package/scripts/generate-diagrams.sh +0 -20
  30. package/scripts/generate-docs.js +0 -111
  31. package/src/client-game.d.ts +0 -1
  32. package/src/config/configs.test.ts +0 -20
  33. package/src/config/configs.ts +0 -197
  34. package/src/config/env.test.ts +0 -59
  35. package/src/config/env.ts +0 -7
  36. package/src/config/styles.ts +0 -5
  37. package/src/core/Game.test.ts +0 -167
  38. package/src/core/Game.ts +0 -307
  39. package/src/core/InitialStateSnapshot.test.ts +0 -51
  40. package/src/core/InitialStateSnapshot.ts +0 -46
  41. package/src/core/helpers/CellHelper.test.ts +0 -33
  42. package/src/core/helpers/CellHelper.ts +0 -21
  43. package/src/core/helpers/ControlInputHandlerHelper.test.ts +0 -116
  44. package/src/core/helpers/ControlInputHandlerHelper.ts +0 -68
  45. package/src/core/helpers/CoordinateHelper.test.ts +0 -113
  46. package/src/core/helpers/CoordinateHelper.ts +0 -82
  47. package/src/core/helpers/InterfaceIdentifierHelper.test.ts +0 -122
  48. package/src/core/helpers/InterfaceIdentifierHelper.ts +0 -43
  49. package/src/core/helpers/RelativeValuesHelper.test.ts +0 -47
  50. package/src/core/helpers/RelativeValuesHelper.ts +0 -29
  51. package/src/core/module/control/GameControl.test.ts +0 -82
  52. package/src/core/module/control/GameControl.ts +0 -142
  53. package/src/core/module/control/GameControlKeyBinding.test.ts +0 -59
  54. package/src/core/module/control/GameControlKeyBinding.ts +0 -92
  55. package/src/core/module/grid/GameGrid.test.ts +0 -83
  56. package/src/core/module/grid/GameGrid.ts +0 -610
  57. package/src/core/module/grid/GameHudGrid.test.ts +0 -22
  58. package/src/core/module/grid/GameHudGrid.ts +0 -40
  59. package/src/core/module/grid/engines/GridAnalysisEngine.test.ts +0 -157
  60. package/src/core/module/grid/engines/GridAnalysisEngine.ts +0 -124
  61. package/src/core/module/grid/engines/GridLineEngine.test.ts +0 -132
  62. package/src/core/module/grid/engines/GridLineEngine.ts +0 -165
  63. package/src/core/module/grid/engines/GridMovementEngine.test.ts +0 -125
  64. package/src/core/module/grid/engines/GridMovementEngine.ts +0 -113
  65. package/src/core/module/grid/engines/GridRegionEngine.test.ts +0 -136
  66. package/src/core/module/grid/engines/GridRegionEngine.ts +0 -52
  67. package/src/core/module/grid/engines/GridTransformEngine.test.ts +0 -98
  68. package/src/core/module/grid/engines/GridTransformEngine.ts +0 -70
  69. package/src/core/module/renderer/DisplayRenderer.test.ts +0 -86
  70. package/src/core/module/renderer/DisplayRenderer.ts +0 -152
  71. package/src/core/module/renderer/GameRenderer.test.ts +0 -103
  72. package/src/core/module/renderer/GameRenderer.ts +0 -144
  73. package/src/core/module/renderer/HudRenderer.test.ts +0 -108
  74. package/src/core/module/renderer/HudRenderer.ts +0 -203
  75. package/src/core/module/score/GameScore.test.ts +0 -71
  76. package/src/core/module/score/GameScore.ts +0 -188
  77. package/src/core/module/session/GameSession.test.ts +0 -176
  78. package/src/core/module/session/GameSession.ts +0 -103
  79. package/src/core/module/sound/GameSound.test.ts +0 -117
  80. package/src/core/module/sound/GameSound.ts +0 -229
  81. package/src/core/module/state/GameState.test.ts +0 -101
  82. package/src/core/module/state/GameState.ts +0 -339
  83. package/src/core/module/text/GameText.test.ts +0 -87
  84. package/src/core/module/text/GameText.ts +0 -150
  85. package/src/core/module/time/GameTime.test.ts +0 -86
  86. package/src/core/module/time/GameTime.ts +0 -144
  87. package/src/core/types/Interfaces.ts +0 -59
  88. package/src/core/types/Types.ts +0 -124
  89. package/src/core/types/enums.ts +0 -113
  90. package/src/core/types/modules.ts +0 -841
  91. package/src/index.test.ts +0 -15
  92. package/src/index.ts +0 -9
  93. package/src/main.test.ts +0 -137
  94. package/src/main.ts +0 -77
  95. package/src/menu/GameMenu.test.ts +0 -157
  96. package/src/menu/GameMenu.ts +0 -124
  97. package/src/menu/GameMenuSingleton.test.ts +0 -26
  98. package/src/menu/GameMenuSingleton.ts +0 -13
  99. package/src/menu/GameRepository.test.ts +0 -46
  100. package/src/menu/GameRepository.ts +0 -47
  101. package/src/menu/manager/GameManager.test.ts +0 -68
  102. package/src/menu/manager/GameManager.ts +0 -50
  103. package/src/types/global.d.ts +0 -8
  104. package/src/types/interfaces.ts +0 -5
  105. package/src/view/Debugger.test.ts +0 -152
  106. package/src/view/Debugger.ts +0 -124
  107. package/src/view/GameView.test.ts +0 -95
  108. package/src/view/GameView.ts +0 -244
  109. package/src/view/SessionModal.test.ts +0 -141
  110. package/src/view/SessionModal.ts +0 -73
  111. package/src/view/components/layout/ButtonLayout.test.ts +0 -28
  112. package/src/view/components/layout/ButtonLayout.ts +0 -63
  113. package/src/view/components/layout/ContainerLayout.test.ts +0 -48
  114. package/src/view/components/layout/ContainerLayout.ts +0 -50
  115. package/src/view/components/layout/FrameLayout.test.ts +0 -24
  116. package/src/view/components/layout/FrameLayout.ts +0 -25
  117. package/src/view/components/ui/BigButton.test.ts +0 -28
  118. package/src/view/components/ui/BigButton.ts +0 -31
  119. package/src/view/components/ui/Button.test.ts +0 -30
  120. package/src/view/components/ui/Button.ts +0 -30
  121. package/src/view/components/ui/Canvas.test.ts +0 -32
  122. package/src/view/components/ui/Canvas.ts +0 -34
  123. package/src/view/components/ui/SmallButton.test.ts +0 -48
  124. package/src/view/components/ui/SmallButton.ts +0 -32
  125. package/src/view/theme/applyColors.test.ts +0 -47
  126. package/src/view/theme/applyColors.ts +0 -38
  127. package/src/view/theme/dimensions.test.ts +0 -34
  128. package/src/view/theme/dimensions.ts +0 -53
  129. package/tsconfig.json +0 -16
  130. package/vitest.config.ts +0 -14
  131. 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
- }