brick-engine-cli 1.0.13 → 1.0.15
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.
|
@@ -53,6 +53,15 @@ async function publishCommand(options) {
|
|
|
53
53
|
type: "input",
|
|
54
54
|
name: "gameName",
|
|
55
55
|
message: "Enter your game name:",
|
|
56
|
+
validate: (input) => {
|
|
57
|
+
if (!input || input.trim().length === 0) {
|
|
58
|
+
return "Game name is required.";
|
|
59
|
+
}
|
|
60
|
+
if (input.length > 10) {
|
|
61
|
+
return "Game name cannot exceed 10 characters.";
|
|
62
|
+
}
|
|
63
|
+
return true;
|
|
64
|
+
},
|
|
56
65
|
});
|
|
57
66
|
prompts.push({
|
|
58
67
|
type: "input",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "brick-engine-cli",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.15",
|
|
4
4
|
"description": "CLI to scaffold Brick Game projects",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@types/inquirer": "^8.2.12",
|
|
16
|
-
"brick-engine-js": "^1.0.
|
|
16
|
+
"brick-engine-js": "^1.0.40",
|
|
17
17
|
"chalk": "^4.1.2",
|
|
18
18
|
"commander": "^11.1.0",
|
|
19
19
|
"dotenv": "^17.3.1",
|
|
@@ -66,6 +66,15 @@ export async function publishCommand(options: { url?: string; key?: string }) {
|
|
|
66
66
|
type: "input",
|
|
67
67
|
name: "gameName",
|
|
68
68
|
message: "Enter your game name:",
|
|
69
|
+
validate: (input: string) => {
|
|
70
|
+
if (!input || input.trim().length === 0) {
|
|
71
|
+
return "Game name is required.";
|
|
72
|
+
}
|
|
73
|
+
if (input.length > 10) {
|
|
74
|
+
return "Game name cannot exceed 10 characters.";
|
|
75
|
+
}
|
|
76
|
+
return true;
|
|
77
|
+
},
|
|
69
78
|
});
|
|
70
79
|
|
|
71
80
|
prompts.push({
|
package/templates/src/index.ts
CHANGED
|
@@ -8,9 +8,18 @@ import {
|
|
|
8
8
|
FontVerticalAlign,
|
|
9
9
|
Color,
|
|
10
10
|
Cell,
|
|
11
|
+
ScoreProperty,
|
|
11
12
|
} from 'brick-engine-js';
|
|
12
13
|
|
|
14
|
+
/**
|
|
15
|
+
* This is the main class of your game.
|
|
16
|
+
* It extends 'Game', which provides the lifecycle and access to the engine's modules.
|
|
17
|
+
*/
|
|
13
18
|
export default class MyGame extends Game {
|
|
19
|
+
/**
|
|
20
|
+
* Initial player state.
|
|
21
|
+
* 'Cell' represents a single square on the grid.
|
|
22
|
+
*/
|
|
14
23
|
private initialPlayerCell: Cell = {
|
|
15
24
|
value: 1,
|
|
16
25
|
color: Color.CYAN,
|
|
@@ -19,38 +28,47 @@ export default class MyGame extends Game {
|
|
|
19
28
|
y: 17,
|
|
20
29
|
},
|
|
21
30
|
};
|
|
22
|
-
private initialTickInterval = 200;
|
|
23
31
|
|
|
24
|
-
|
|
32
|
+
// Initial difficulty settings
|
|
33
|
+
private initialSpawnEnemyInterval = 500;
|
|
34
|
+
private spawnEnemyInterval = 500;
|
|
25
35
|
|
|
36
|
+
private initialMoveEnemiesDownInterval = 100;
|
|
37
|
+
private moveEnemiesDownInterval = 100;
|
|
38
|
+
|
|
39
|
+
// Difficulty increase rates as level goes up
|
|
40
|
+
private increaseMoveEnemiesDownIntervalRate = 5;
|
|
41
|
+
private increaseSpawnEnemyIntervalRate = 25;
|
|
42
|
+
|
|
43
|
+
// Current game state
|
|
44
|
+
private player: Cell = this.initialPlayerCell;
|
|
26
45
|
private enemies: Cell[] = [];
|
|
27
46
|
private enemyColor = Color.RED;
|
|
28
47
|
private enemyValue = 2;
|
|
29
48
|
|
|
30
|
-
private spawnRate = 5; // Ticks between spawns
|
|
31
|
-
|
|
32
49
|
/**
|
|
33
|
-
*
|
|
50
|
+
* setupGame() is called once when the engine is initialized.
|
|
51
|
+
* Use this to configure controls, data persistence, and initial events.
|
|
34
52
|
*/
|
|
35
53
|
setupGame(): void {
|
|
36
|
-
|
|
54
|
+
// Destructure only the modules we need
|
|
55
|
+
const { control, session, grid, score, sound } = this.modules;
|
|
37
56
|
|
|
38
57
|
// Reset game local state when starting/restarting.
|
|
39
58
|
this.player = this.initialPlayerCell;
|
|
40
|
-
time.setTickInterval(this.initialTickInterval);
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
/**
|
|
61
|
+
* The 'session' module allows saving and loading game state.
|
|
62
|
+
* This is useful so the player doesn't lose progress on page refresh.
|
|
63
|
+
*/
|
|
43
64
|
session.register({
|
|
44
|
-
// Unique identifier for
|
|
45
|
-
serialId: 'game-data',
|
|
46
|
-
// Recieves from LocalStorage and restore the game state
|
|
65
|
+
serialId: 'game-data', // Unique identifier for this data set
|
|
47
66
|
deserialize: (data: string) => {
|
|
48
67
|
if (!data) return;
|
|
49
68
|
const { player, enemies } = JSON.parse(data);
|
|
50
69
|
this.player = player;
|
|
51
70
|
this.enemies = enemies;
|
|
52
71
|
},
|
|
53
|
-
// Prepare data to be saved to LocalStorage
|
|
54
72
|
serialize: () => {
|
|
55
73
|
return JSON.stringify({
|
|
56
74
|
player: this.player,
|
|
@@ -59,7 +77,12 @@ export default class MyGame extends Game {
|
|
|
59
77
|
},
|
|
60
78
|
});
|
|
61
79
|
|
|
62
|
-
|
|
80
|
+
/**
|
|
81
|
+
* Control Configuration.
|
|
82
|
+
* Subscribe functions to be executed when specific keys are pressed or held.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
// Move Left (single press)
|
|
63
86
|
control.subscribeForPlayingScreen(
|
|
64
87
|
ControlKey.LEFT,
|
|
65
88
|
ControlEventType.PRESSED,
|
|
@@ -68,7 +91,7 @@ export default class MyGame extends Game {
|
|
|
68
91
|
},
|
|
69
92
|
);
|
|
70
93
|
|
|
71
|
-
// Move Right
|
|
94
|
+
// Move Right (single press)
|
|
72
95
|
control.subscribeForPlayingScreen(
|
|
73
96
|
ControlKey.RIGHT,
|
|
74
97
|
ControlEventType.PRESSED,
|
|
@@ -76,21 +99,91 @@ export default class MyGame extends Game {
|
|
|
76
99
|
this.player = grid.moveCellRight(this.player);
|
|
77
100
|
},
|
|
78
101
|
);
|
|
102
|
+
|
|
103
|
+
// Move Left (held down)
|
|
104
|
+
control.subscribeForPlayingScreen(
|
|
105
|
+
ControlKey.LEFT,
|
|
106
|
+
ControlEventType.HELD,
|
|
107
|
+
() => {
|
|
108
|
+
this.player = grid.moveCellLeft(this.player);
|
|
109
|
+
},
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// Move Right (held down)
|
|
113
|
+
control.subscribeForPlayingScreen(
|
|
114
|
+
ControlKey.RIGHT,
|
|
115
|
+
ControlEventType.HELD,
|
|
116
|
+
() => {
|
|
117
|
+
this.player = grid.moveCellRight(this.player);
|
|
118
|
+
},
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Listen for score changes to increase level and difficulty.
|
|
123
|
+
*/
|
|
124
|
+
score.subscribe(ScoreProperty.SCORE, () => {
|
|
125
|
+
// Increase speed every 50 points
|
|
126
|
+
if (
|
|
127
|
+
score.score > 0 &&
|
|
128
|
+
score.score % 50 === 0 &&
|
|
129
|
+
score.level < score.maxLevel
|
|
130
|
+
) {
|
|
131
|
+
// Adjust time intervals to make the game faster
|
|
132
|
+
this.moveEnemiesDownInterval =
|
|
133
|
+
this.initialMoveEnemiesDownInterval -
|
|
134
|
+
score.level * this.increaseMoveEnemiesDownIntervalRate;
|
|
135
|
+
|
|
136
|
+
this.spawnEnemyInterval =
|
|
137
|
+
this.initialSpawnEnemyInterval -
|
|
138
|
+
score.level * this.increaseSpawnEnemyIntervalRate;
|
|
139
|
+
|
|
140
|
+
// Increase level in the score module
|
|
141
|
+
score.increaseLevel(1);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
// Play different sounds as the level increases
|
|
146
|
+
score.subscribe(ScoreProperty.LEVEL, () => {
|
|
147
|
+
if (score.level < 6) {
|
|
148
|
+
sound.play(Sound.SCORE_2);
|
|
149
|
+
} else {
|
|
150
|
+
sound.play(Sound.SCORE_3);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
79
153
|
}
|
|
80
154
|
|
|
81
155
|
/**
|
|
82
|
-
*
|
|
156
|
+
* update() is the main game logic loop.
|
|
157
|
+
* It runs every frame to process movement, collisions, and render to the grid.
|
|
83
158
|
*/
|
|
84
159
|
update(): void {
|
|
85
160
|
const { grid, state, score, sound, time } = this.modules;
|
|
86
161
|
|
|
162
|
+
/**
|
|
163
|
+
* The 'time' module handles timing deterministically.
|
|
164
|
+
* time.every(ms, callback) runs the function every millisecond interval.
|
|
165
|
+
*/
|
|
166
|
+
|
|
87
167
|
// 1. Move enemies down
|
|
88
|
-
this.
|
|
89
|
-
.
|
|
90
|
-
|
|
168
|
+
time.every(this.moveEnemiesDownInterval, () => {
|
|
169
|
+
this.enemies = this.enemies
|
|
170
|
+
.filter(enemy => enemy.coordinate.y !== grid.bottomRow) // Remove if they reach the bottom
|
|
171
|
+
.map(enemy => grid.moveCellDown(enemy)); // Move them down
|
|
172
|
+
|
|
173
|
+
// 2. Check collisions between enemies and the player (or other occupied areas)
|
|
174
|
+
const collision = this.enemies.find(enemy =>
|
|
175
|
+
grid.isAreaOccupied([enemy.coordinate]),
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
if (collision) {
|
|
179
|
+
sound.play(Sound.EXPLOSION);
|
|
180
|
+
state.triggerGameOver(); // End the game
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
});
|
|
91
184
|
|
|
92
|
-
//
|
|
93
|
-
|
|
185
|
+
// 3. Spawn new enemies at random positions at the top
|
|
186
|
+
time.every(this.spawnEnemyInterval, () => {
|
|
94
187
|
const spawnX = Math.floor(Math.random() * grid.width);
|
|
95
188
|
|
|
96
189
|
const newEnemy = {
|
|
@@ -103,61 +196,34 @@ export default class MyGame extends Game {
|
|
|
103
196
|
};
|
|
104
197
|
|
|
105
198
|
this.enemies.push(newEnemy);
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// 3. Check collisions
|
|
109
|
-
const collision = this.enemies.find(enemy =>
|
|
110
|
-
grid.isAreaOccupied([enemy.coordinate]),
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
if (collision) {
|
|
114
|
-
sound.play(Sound.EXPLOSION);
|
|
115
|
-
state.triggerGameOver();
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
199
|
+
});
|
|
118
200
|
|
|
119
|
-
|
|
201
|
+
/**
|
|
202
|
+
* Grid Rendering.
|
|
203
|
+
* Clear the grid first, then 'stamp' objects onto it.
|
|
204
|
+
*/
|
|
120
205
|
grid.resetGrid();
|
|
121
206
|
|
|
122
207
|
// Draw Player
|
|
123
|
-
// Cell is a class that represents a single cell in the grid
|
|
124
208
|
grid.stampCell(this.player);
|
|
125
209
|
|
|
126
|
-
// Draw
|
|
127
|
-
//
|
|
210
|
+
// Draw all enemies
|
|
211
|
+
// grid.stampPiece accepts a collection of cells
|
|
128
212
|
grid.stampPiece(this.enemies);
|
|
129
213
|
|
|
130
|
-
//
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
if (
|
|
135
|
-
score.score > 0 &&
|
|
136
|
-
score.score % 50 === 0 &&
|
|
137
|
-
score.level < score.maxLevel
|
|
138
|
-
) {
|
|
139
|
-
// Set tick interval to 200ms minus 15ms per level
|
|
140
|
-
time.setTickInterval(this.initialTickInterval - 15 * score.level);
|
|
141
|
-
|
|
142
|
-
// Increase level
|
|
143
|
-
score.increaseLevel(1);
|
|
144
|
-
|
|
145
|
-
// Play sound based on level
|
|
146
|
-
if (score.level < 6) {
|
|
147
|
-
sound.play(Sound.SCORE_2);
|
|
148
|
-
} else {
|
|
149
|
-
sound.play(Sound.SCORE_3);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
214
|
+
// 4. Increase score automatically over time
|
|
215
|
+
time.every(this.moveEnemiesDownInterval, () => {
|
|
216
|
+
score.increaseScore(1);
|
|
217
|
+
});
|
|
152
218
|
}
|
|
153
219
|
|
|
154
220
|
/**
|
|
155
|
-
*
|
|
221
|
+
* render() is used for visual-only elements that don't affect collision (e.g., HUD).
|
|
156
222
|
*/
|
|
157
223
|
render(): void {}
|
|
158
224
|
|
|
159
225
|
/**
|
|
160
|
-
*
|
|
226
|
+
* Draw the title screen interface.
|
|
161
227
|
*/
|
|
162
228
|
drawTitleScreen(): void {
|
|
163
229
|
const { text } = this.modules;
|
|
@@ -175,7 +241,7 @@ export default class MyGame extends Game {
|
|
|
175
241
|
}
|
|
176
242
|
|
|
177
243
|
/**
|
|
178
|
-
* Game Over
|
|
244
|
+
* Draw the Game Over screen interface.
|
|
179
245
|
*/
|
|
180
246
|
drawGameOverScreen(): void {
|
|
181
247
|
const { text, score } = this.modules;
|