create-lagom-game 0.0.1-beta.1

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Peter Mandile
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ import path from "path";
3
+ import fs from "fs-extra";
4
+ import { fileURLToPath } from "url";
5
+ import prompts from "prompts";
6
+
7
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
8
+
9
+ async function main() {
10
+ const { projectName } = await prompts({
11
+ type: "text",
12
+ name: "projectName",
13
+ message: "Project name",
14
+ initial: "my-lagom-game"
15
+ });
16
+
17
+ const target = path.resolve(process.cwd(), projectName);
18
+ const template = path.resolve(__dirname, "../template");
19
+
20
+ await fs.copy(template, target);
21
+
22
+ console.log(`\nCreated ${projectName}`);
23
+ console.log(`\nNext steps:`);
24
+ console.log(` cd ${projectName}`);
25
+ console.log(` pnpm install`);
26
+ console.log(` pnpm dev`);
27
+ }
28
+
29
+ main();
package/package.json ADDED
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "create-lagom-game",
3
+ "version": "0.0.1-beta.1",
4
+ "bin": {
5
+ "create-lagom-game": "bin/create-lagom-game.js"
6
+ },
7
+ "files": [
8
+ "bin",
9
+ "template"
10
+ ],
11
+ "dependencies": {
12
+ "prompts": "^2.4.2",
13
+ "fs-extra": "^11.3.3"
14
+ },
15
+ "devDependencies": {
16
+ "typescript": "^5.6.2",
17
+ "vite": "7.1.11"
18
+ }
19
+ }
@@ -0,0 +1,13 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/vite.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Lagom</title>
8
+ </head>
9
+ <body>
10
+ <div id="app"></div>
11
+ <script type="module" src="/src/main.ts"></script>
12
+ </body>
13
+ </html>
@@ -0,0 +1,19 @@
1
+ {
2
+ "name": "lagom-game-template",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "vite",
7
+ "build": "tsc && vite build",
8
+ "preview": "vite preview",
9
+ "deploy": "tsc && vite build && butler push dist USERNAME/GAMENAME:html5"
10
+ },
11
+ "dependencies": {
12
+ "lagom-engine": "workspace:^"
13
+ },
14
+ "devDependencies": {
15
+ "typescript": "^5.6.2",
16
+ "vite": "7.1.11"
17
+ },
18
+ "packageManager": "pnpm@10.15.1"
19
+ }
@@ -0,0 +1,84 @@
1
+ import { ActionOnPress, AudioAtlas, Entity, FrameTriggerSystem, Game, Log, LogLevel, Scene, TextDisp, TimerSystem } from "lagom-engine";
2
+ import { SoundManager } from "./util/SoundManager";
3
+
4
+ class TitleScene extends Scene {
5
+ onAdded() {
6
+ super.onAdded();
7
+
8
+ this.addGUIEntity(new SoundManager());
9
+ this.addGlobalSystem(new TimerSystem());
10
+ this.addGlobalSystem(new FrameTriggerSystem());
11
+
12
+ this.addGUIEntity(new Entity("title")).addComponent(
13
+ new TextDisp(100, 10, "GAME NAME", {
14
+ fontFamily: "retro",
15
+ fill: 0xffffff,
16
+ }),
17
+ );
18
+
19
+ this.addSystem(
20
+ new ActionOnPress(() => {
21
+ this.game.setScene(new MainScene(this.game));
22
+ }),
23
+ );
24
+ }
25
+ }
26
+
27
+ class MainScene extends Scene {
28
+ onAdded() {
29
+ super.onAdded();
30
+
31
+ this.addGUIEntity(new SoundManager());
32
+ this.addGlobalSystem(new TimerSystem());
33
+ this.addGlobalSystem(new FrameTriggerSystem());
34
+
35
+ this.addGUIEntity(new Entity("main scene")).addComponent(
36
+ new TextDisp(100, 10, "MAIN SCENE", {
37
+ fontFamily: "pixeloid",
38
+ fill: 0xffffff,
39
+ }),
40
+ );
41
+ }
42
+ }
43
+
44
+ export class GameTemplate extends Game {
45
+ startScene = () => new TitleScene(this);
46
+ resourceLoad = async () => {
47
+ await this.resourceLoader.autoLoad();
48
+
49
+ this.resourceLoader.getSound("flip").play();
50
+ console.log("loaded all resources");
51
+ // await this.resourceLoader.addResource("mute_button", muteButtonSpr, {
52
+ // tileHeight: 16,
53
+ // tileWidth: 16,
54
+ // });
55
+ };
56
+ static GAME_WIDTH = 512;
57
+ static GAME_HEIGHT = 512;
58
+
59
+ static muted = false;
60
+ static musicPlaying = false;
61
+ static audioAtlas: AudioAtlas = new AudioAtlas();
62
+
63
+ constructor() {
64
+ super({
65
+ width: GameTemplate.GAME_WIDTH,
66
+ height: GameTemplate.GAME_HEIGHT,
67
+ resolution: 1,
68
+ backgroundColor: 0x200140,
69
+ });
70
+
71
+ // Set the global log level
72
+ Log.logLevel = LogLevel.INFO;
73
+
74
+ // Load an empty scene while we async load the resources for the main one
75
+ // this.setScene(new Scene(this));
76
+
77
+ // TODO sound loader and font loader need to go in the actual loader
78
+ // Import sounds and set their properties
79
+ // const music = GameTemplate.audioAtlas.load("music", "src/assets/ADD_ME")
80
+ // .loop(true)
81
+ // .volume(0.3);
82
+
83
+ }
84
+ }
@@ -0,0 +1,132 @@
1
+ import {
2
+ CircleSatCollider,
3
+ CollisionMatrix,
4
+ Component,
5
+ Entity,
6
+ Game,
7
+ Key,
8
+ Log,
9
+ LogLevel,
10
+ newSystem,
11
+ RectSatCollider,
12
+ RenderCircle,
13
+ RenderRect,
14
+ SatCollisionSystem,
15
+ Scene,
16
+ types,
17
+ } from "lagom-engine";
18
+
19
+ enum Layers {
20
+ Guy,
21
+ Wall,
22
+ WallCheck,
23
+ }
24
+
25
+ class MoveMe extends Component {
26
+ constructor(public vel: number = 0) {
27
+ super();
28
+ }
29
+ }
30
+
31
+ class Gravity extends Component {}
32
+
33
+ class Grounded extends Component {
34
+ constructor(public onGround: boolean = false) {
35
+ super();
36
+ }
37
+ }
38
+
39
+ class MainScene extends Scene {
40
+ onAdded() {
41
+ super.onAdded();
42
+
43
+ const matrix = new CollisionMatrix();
44
+ matrix.addCollision(Layers.Guy, Layers.Wall);
45
+ matrix.addCollision(Layers.Wall, Layers.WallCheck);
46
+ this.addGlobalSystem(new SatCollisionSystem(matrix));
47
+
48
+ const e2 = this.addEntity(new Entity("", 105, 100));
49
+ e2.addComponent(new CircleSatCollider({ layer: Layers.Guy, radius: 10 })).onTrigger.register((caller, data) => {
50
+ if (data.other.layer == Layers.Wall) {
51
+ // console.log("hit")
52
+ caller.parent.transform.x -= data.result.overlapV.x;
53
+ caller.parent.transform.y -= data.result.overlapV.y;
54
+ }
55
+ });
56
+ const grounded = e2.addComponent(new Grounded());
57
+ const groundChild = e2.addChild(new Entity("groundCheck", 0, 12));
58
+ const groundCheck = groundChild.addComponent(
59
+ new CircleSatCollider({
60
+ layer: Layers.WallCheck,
61
+ radius: 12,
62
+ }),
63
+ );
64
+ groundCheck.onTriggerEnter.register((caller, data) => {
65
+ console.log("grounded");
66
+ if (data.other.layer == Layers.Wall) {
67
+ grounded.onGround = true;
68
+ }
69
+ });
70
+
71
+ groundCheck.onTriggerExit.register((caller, data) => {
72
+ console.log("no grounded");
73
+ if (data.other.layer == Layers.Wall) {
74
+ grounded.onGround = false;
75
+ }
76
+ });
77
+ groundChild.addComponent(new RenderCircle({ radius: 2 }, 0x00ffff, undefined));
78
+
79
+ e2.addComponent(new RenderCircle({ radius: 10 }));
80
+
81
+ e2.addComponent(new MoveMe());
82
+ e2.addComponent(new Gravity());
83
+
84
+ const floor = this.addEntity(new Entity("floor", 0, 150));
85
+ floor.addComponent(new RectSatCollider({ layer: Layers.Wall, height: 10, width: 200 }));
86
+ floor.addComponent(new RenderRect({ width: 200, height: 10 }));
87
+
88
+ this.addFnSystem(
89
+ newSystem([MoveMe], (d, e, moveme) => {
90
+ if (this.game.keyboard.isKeyDown(Key.KeyA)) {
91
+ e.transform.position.x -= d * 0.1;
92
+ }
93
+ if (this.game.keyboard.isKeyDown(Key.KeyD)) {
94
+ e.transform.position.x += d * 0.1;
95
+ }
96
+ if (this.game.keyboard.isKeyPressed(Key.KeyW) && grounded.onGround) {
97
+ moveme.vel = -50;
98
+ }
99
+ }),
100
+ );
101
+
102
+ this.addFixedFnSystem(
103
+ newSystem(types(MoveMe, Gravity), (delta, entity, moveme, gravity) => {
104
+ moveme.vel += delta * 0.2;
105
+ if (moveme.vel > 20) {
106
+ moveme.vel = 20;
107
+ }
108
+ entity.transform.y += moveme.vel * delta * 0.01;
109
+ }),
110
+ );
111
+
112
+ // this.addFnSystem(newSystem([MoveMe, MoveMeToo], (d, e) => {}))
113
+ }
114
+ }
115
+
116
+ export class Platformer extends Game {
117
+ resourceLoad = async () => {};
118
+ startScene = () => new MainScene(this);
119
+ static GAME_WIDTH = 512;
120
+ static GAME_HEIGHT = 512;
121
+
122
+ constructor() {
123
+ super({
124
+ width: Platformer.GAME_WIDTH,
125
+ height: Platformer.GAME_HEIGHT,
126
+ resolution: 1,
127
+ backgroundColor: 0x200140,
128
+ });
129
+
130
+ Log.logLevel = LogLevel.INFO;
131
+ }
132
+ }
@@ -0,0 +1,258 @@
1
+ // import {
2
+ // CollisionMatrix,
3
+ // CollisionSystem,
4
+ // Component,
5
+ // CType,
6
+ // Diagnostics,
7
+ // DiscreteCollisionSystem,
8
+ // Entity,
9
+ // Game,
10
+ // Key,
11
+ // Observable,
12
+ // RectCollider,
13
+ // RenderRect,
14
+ // Scene,
15
+ // System,
16
+ // TextDisp
17
+ // } from "lagom-engine";
18
+ //
19
+ // enum Layers {
20
+ // leftpaddle,
21
+ // ball,
22
+ // rightpaddle
23
+ // }
24
+ //
25
+ // enum PaddleSide {
26
+ // left,
27
+ // right
28
+ // }
29
+ //
30
+ // export class Pong extends Game {
31
+ // constructor() {
32
+ // super({width: 800, height: 600, resolution: 1, backgroundColor: 0x000000});
33
+ // this.setScene(new MainScene(this));
34
+ // }
35
+ // }
36
+ //
37
+ // class MainScene extends Scene {
38
+ // onAdded() {
39
+ // super.onAdded();
40
+ //
41
+ // const collisionMatrix = new CollisionMatrix();
42
+ // collisionMatrix.addCollision(Layers.leftpaddle, Layers.ball);
43
+ // collisionMatrix.addCollision(Layers.rightpaddle, Layers.ball);
44
+ //
45
+ // this.addGlobalSystem(new DiscreteCollisionSystem(collisionMatrix));
46
+ //
47
+ // this.addEntity(new Paddle(30, 300, PaddleSide.left));
48
+ // this.addEntity(new Paddle(740, 300, PaddleSide.right));
49
+ // this.addEntity(new Ball(400, 200));
50
+ // const scoreboard = new Scoreboard(400, 50);
51
+ // this.addEntity(scoreboard);
52
+ // this.addEntity(new Diagnostics("red"));
53
+ //
54
+ // this.addSystem(new PlayerMover());
55
+ // this.addSystem(new BallMover());
56
+ // this.addSystem(new ScoreSystem(scoreboard.score));
57
+ // }
58
+ // }
59
+ //
60
+ // class Paddle extends Entity {
61
+ // private static width = 30;
62
+ // private static height = 80;
63
+ //
64
+ // constructor(x: number, y: number, private side: PaddleSide) {
65
+ // super("paddle", x, y);
66
+ // }
67
+ //
68
+ // onAdded() {
69
+ // super.onAdded();
70
+ //
71
+ // if (this.side === PaddleSide.left) {
72
+ // this.addComponent(new PlayerControlled(Key.KeyW, Key.KeyS));
73
+ // } else {
74
+ // this.addComponent(new PlayerControlled(Key.ArrowUp, Key.ArrowDown));
75
+ // }
76
+ //
77
+ // this.addComponent(new RenderRect(0, 0, Paddle.width, Paddle.height, 0xffffff, 0xffffff));
78
+ //
79
+ // this.addComponent(
80
+ // new RectCollider(<CollisionSystem<any>>this.getScene().getGlobalSystem<CollisionSystem<any>>(CollisionSystem),
81
+ // {
82
+ // layer: Layers.leftpaddle,
83
+ // height: Paddle.height, width: Paddle.width
84
+ // }));
85
+ // }
86
+ // }
87
+ //
88
+ // class PlayerControlled extends Component {
89
+ // constructor(public upKey: Key, public downKey: Key) {
90
+ // super();
91
+ // }
92
+ // }
93
+ //
94
+ // class PlayerMover extends System<[PlayerControlled]> {
95
+ // types: [CType<PlayerControlled>] = [PlayerControlled];
96
+ //
97
+ // private readonly moveSpeed = 40;
98
+ //
99
+ // runOnEntities(delta: number, entity: Entity, playerControlled: PlayerControlled): void {
100
+ //
101
+ // if (this.scene.game.keyboard.isKeyDown(playerControlled.upKey) && entity.transform.position.y > 0) {
102
+ // entity.transform.position.y += this.moveSpeed * -1 * (delta / 100);
103
+ // }
104
+ // if (this.scene.game.keyboard.isKeyDown(playerControlled.downKey)
105
+ // && entity.transform.position.y + entity.transform.height < entity.getScene().getGame().renderer.height) {
106
+ // entity.transform.position.y += this.moveSpeed * (delta / 100);
107
+ // }
108
+ // }
109
+ // }
110
+ //
111
+ //
112
+ // class BallMovement extends Component {
113
+ // xSpeed: number;
114
+ // ySpeed: number;
115
+ //
116
+ // constructor() {
117
+ // super();
118
+ // this.xSpeed = -30;
119
+ // this.ySpeed = 30;
120
+ // }
121
+ // }
122
+ //
123
+ //
124
+ // class BallMover extends System<[BallMovement]> {
125
+ // types: [CType<BallMovement>] = [BallMovement];
126
+ //
127
+ // topBounce: number;
128
+ // bottomBounce: number;
129
+ //
130
+ // constructor() {
131
+ // super();
132
+ // this.topBounce = 10;
133
+ // this.bottomBounce = 600 - 10;
134
+ // }
135
+ //
136
+ // runOnEntities(delta: number, entity: Entity, ball: BallMovement): void {
137
+ //
138
+ // const bodyY = entity.transform.y;
139
+ // if (bodyY > this.bottomBounce || bodyY < this.topBounce) {
140
+ // ball.ySpeed *= -1;
141
+ // }
142
+ // entity.transform.x += ball.xSpeed * (delta / 100);
143
+ // entity.transform.y += ball.ySpeed * (delta / 100);
144
+ // }
145
+ // }
146
+ //
147
+ // class Ball extends Entity {
148
+ // constructor(x: number, y: number) {
149
+ // super("ball", x, y);
150
+ // }
151
+ //
152
+ // onAdded(): void {
153
+ // super.onAdded();
154
+ //
155
+ // const rect = new RenderRect(0, 0, 10, 10, 0xffffff, 0xffffff);
156
+ // this.addComponent(rect);
157
+ // this.addComponent(new BallMovement());
158
+ //
159
+ // const collider = this.addComponent(
160
+ // new RectCollider(<CollisionSystem<any>>this.getScene().getGlobalSystem<CollisionSystem<any>>(CollisionSystem),
161
+ // {
162
+ // xOff: 0, yOff: 0, layer: Layers.ball, rotation: 0,
163
+ // height: 10, width: 10
164
+ // }));
165
+ //
166
+ // collider.onTriggerEnter.register(() => {
167
+ // const movement = this.getComponent<BallMovement>(BallMovement);
168
+ // if (movement !== null) {
169
+ // movement.xSpeed *= -1;
170
+ // }
171
+ // });
172
+ // }
173
+ // }
174
+ //
175
+ // class Scoreboard extends Entity {
176
+ // score: Score;
177
+ //
178
+ // constructor(x: number, y: number) {
179
+ // super("scoreboard", x, y);
180
+ // this.score = new Score();
181
+ // }
182
+ //
183
+ // onAdded() {
184
+ // super.onAdded();
185
+ //
186
+ // const p1Label = new TextDisp(-30, 0, this.score.player1Score.toString(), {fill: 0x777777});
187
+ // this.addComponent(p1Label);
188
+ // this.score.onP1Score.register((_, num) => {
189
+ // p1Label.pixiObj.text = num.toString();
190
+ // });
191
+ //
192
+ // const p2Label = new TextDisp(30, 0, this.score.player2Score.toString(), {fill: 0x777777});
193
+ // this.addComponent(p2Label);
194
+ // this.score.onP2Score.register((_, num) => {
195
+ // p2Label.pixiObj.text = num.toString();
196
+ // });
197
+ // }
198
+ // }
199
+ //
200
+ // class Score extends Component {
201
+ // private _player1Score: number;
202
+ // private _player2Score: number;
203
+ //
204
+ // constructor() {
205
+ // super();
206
+ // this._player1Score = 0;
207
+ // this._player2Score = 0;
208
+ // }
209
+ //
210
+ // player1Scored(): void {
211
+ // this._player1Score++;
212
+ // this.onP1Score.trigger(this, this._player1Score);
213
+ // }
214
+ //
215
+ // get player1Score(): number {
216
+ // return this._player1Score;
217
+ // }
218
+ //
219
+ // player2Scored(): void {
220
+ // this._player2Score++;
221
+ // this.onP2Score.trigger(this, this._player2Score);
222
+ // }
223
+ //
224
+ // get player2Score(): number {
225
+ // return this._player2Score;
226
+ // }
227
+ //
228
+ // readonly onP1Score: Observable<Score, number> = new Observable();
229
+ // readonly onP2Score: Observable<Score, number> = new Observable();
230
+ //
231
+ // onRemoved(): void {
232
+ // super.onRemoved();
233
+ //
234
+ // this.onP1Score.releaseAll();
235
+ // this.onP2Score.releaseAll();
236
+ // }
237
+ // }
238
+ //
239
+ // class ScoreSystem extends System<[BallMovement]> {
240
+ // types: [CType<BallMovement>] = [BallMovement];
241
+ //
242
+ // constructor(private score: Score) {
243
+ // super();
244
+ // }
245
+ //
246
+ // runOnEntities(delta: number, entity: Entity, args_0: BallMovement): void {
247
+ // if (entity.transform.x < 0) {
248
+ // this.score.player2Scored();
249
+ // entity.destroy();
250
+ // this.getScene().addEntity(new Ball(400, 200));
251
+ // }
252
+ // if (entity.transform.x > 800) {
253
+ // this.score.player1Scored();
254
+ // entity.destroy();
255
+ // this.getScene().addEntity(new Ball(400, 200));
256
+ // }
257
+ // }
258
+ // }
@@ -0,0 +1,359 @@
1
+ {
2
+ "id": "1",
3
+ "name": "Untitled",
4
+ "description": "No description",
5
+ "tileSize": 12,
6
+ "spriteSheets": {
7
+ "600d29ce-27cb-48ed-b91d-13f8c07e6c2a": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAJAAAAAwCAYAAAD+WvNWAAAAAXNSR0IArs4c6QAAAt1JREFUeJztncFt3DAQRb+C+OoefHEBPtiNpIzAgCsxYLiMNGIfXIAv7iHXHJRDwICrHXFJzgxFUf8BCwjaNYcSPz+H4i49AZjRH5NTufG1esXwItS9q3pP8BVQuNiaGNY3aq0OXTWIwLLezev76+5uBoAfHx9nsb85xp1WjnOxFHaqrNk4lhWu9Xp4fTQp+7tFIQKSYGrcbl4pq7SMks9t7UhF9+jP7/uzz19dv5tdQ3CfcLx0IQ8HSlW+tRPV/O1WjlQcVxKPJbF4gnDic4C9gHIE0kpE2pvbSkhN4jy8Ps5rw5Z0Xsp7vHOgEmF4i8g6f/Jo4KZO9/bzRbznqVxIEgxw6kJWAqoRhJeIvBrFqsE3TdolwayJKyZ2pFhYFtP4VkluTsyWDVN63VZ1+x+3JoHWCMhjCLPI9q2cqHWvznWSJo5zdf0+5cy+lmLJEU8KjYAsp7taEW35HGdNIO7CCaIpnbaXimY581oyV7y8qKnL0V7NSQmodhz3XqsqKX9L97Fg6weXKjyXMsgB0PR078XOHlxIsxhcUv5uoQMRFb0IqIdpeQoPp9i9+wD5AmIDE5EeHCglxFFFOow4exAQ2TE5PeGSC2h6U67D5MYwX29yiDGM+wB0IKJkSwGV9ORRcqGh3AegAxEll3qEdY5SWm5NnFY5Smmc4dwHoAMRJVsISOMQe82FhnQfIC0gJrnkIq0dyEJoexPr0KJkDkRUWP+6wfMJbkmsmngeT9SHdh+ADkSUtBKQR97Sey40vPsAsoA4zSbZtHAg758B9YJ2P6RdwhyIqFj2lBbfp2mN5/eZDo/XDmVkAL6ebsXOd/P8ebI7RwwdiAD4J55YKPH5cHzz/DnRgcgZa84DnLoPcJpEW85oep0dlbx3aCT3kaADEZGc/AeggMgKuQ4UhjAuNZAq+CCRiHw93c6pZDpwpO1LvLenGQ5JQMuhzfufrYQYveC9w9rh4BBGVPwF3wE0x+ESvQkAAAAASUVORK5CYII="
8
+ },
9
+ "layers": [
10
+ {
11
+ "id": "b2cce924-1569-4bd3-87b4-628d278a97d9",
12
+ "name": "Layer_0",
13
+ "description": "",
14
+ "tiles": [
15
+ {
16
+ "id": "0",
17
+ "x": 216,
18
+ "y": -72,
19
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
20
+ "scaleX": 1
21
+ },
22
+ {
23
+ "id": "0",
24
+ "x": 216,
25
+ "y": -60,
26
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
27
+ "scaleX": 1
28
+ },
29
+ {
30
+ "id": "0",
31
+ "x": 216,
32
+ "y": -48,
33
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
34
+ "scaleX": 1
35
+ },
36
+ {
37
+ "id": "0",
38
+ "x": 216,
39
+ "y": -36,
40
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
41
+ "scaleX": 1
42
+ },
43
+ {
44
+ "id": "0",
45
+ "x": 216,
46
+ "y": -24,
47
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
48
+ "scaleX": 1
49
+ },
50
+ {
51
+ "id": "47",
52
+ "x": 216,
53
+ "y": 0,
54
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
55
+ "scaleX": 1
56
+ },
57
+ {
58
+ "id": "0",
59
+ "x": 312,
60
+ "y": -48,
61
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
62
+ "scaleX": 1
63
+ },
64
+ {
65
+ "id": "0",
66
+ "x": 312,
67
+ "y": -24,
68
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
69
+ "scaleX": 1
70
+ },
71
+ {
72
+ "id": "14",
73
+ "x": 264,
74
+ "y": -36,
75
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
76
+ "scaleX": 1
77
+ },
78
+ {
79
+ "id": "15",
80
+ "x": 276,
81
+ "y": -36,
82
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
83
+ "scaleX": 1
84
+ },
85
+ {
86
+ "id": "26",
87
+ "x": 264,
88
+ "y": -24,
89
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
90
+ "scaleX": 1
91
+ },
92
+ {
93
+ "id": "27",
94
+ "x": 276,
95
+ "y": -24,
96
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
97
+ "scaleX": 1
98
+ },
99
+ {
100
+ "id": "16",
101
+ "x": 216,
102
+ "y": 24,
103
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
104
+ "scaleX": 1
105
+ },
106
+ {
107
+ "id": "17",
108
+ "x": 228,
109
+ "y": 24,
110
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
111
+ "scaleX": 1
112
+ },
113
+ {
114
+ "id": "28",
115
+ "x": 216,
116
+ "y": 36,
117
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
118
+ "scaleX": 1
119
+ },
120
+ {
121
+ "id": "29",
122
+ "x": 228,
123
+ "y": 36,
124
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
125
+ "scaleX": 1
126
+ },
127
+ {
128
+ "id": "40",
129
+ "x": 216,
130
+ "y": 48,
131
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
132
+ "scaleX": 1
133
+ },
134
+ {
135
+ "id": "41",
136
+ "x": 228,
137
+ "y": 48,
138
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
139
+ "scaleX": 1
140
+ },
141
+ {
142
+ "id": "16",
143
+ "x": 300,
144
+ "y": 24,
145
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
146
+ "scaleX": 1
147
+ },
148
+ {
149
+ "id": "17",
150
+ "x": 312,
151
+ "y": 24,
152
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
153
+ "scaleX": 1
154
+ },
155
+ {
156
+ "id": "28",
157
+ "x": 300,
158
+ "y": 36,
159
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
160
+ "scaleX": 1
161
+ },
162
+ {
163
+ "id": "29",
164
+ "x": 312,
165
+ "y": 36,
166
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
167
+ "scaleX": 1
168
+ },
169
+ {
170
+ "id": "40",
171
+ "x": 300,
172
+ "y": 48,
173
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
174
+ "scaleX": 1
175
+ },
176
+ {
177
+ "id": "41",
178
+ "x": 312,
179
+ "y": 48,
180
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
181
+ "scaleX": 1
182
+ },
183
+ {
184
+ "id": "16",
185
+ "x": 264,
186
+ "y": 72,
187
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
188
+ "scaleX": 1
189
+ },
190
+ {
191
+ "id": "17",
192
+ "x": 276,
193
+ "y": 72,
194
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
195
+ "scaleX": 1
196
+ },
197
+ {
198
+ "id": "28",
199
+ "x": 264,
200
+ "y": 84,
201
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
202
+ "scaleX": 1
203
+ },
204
+ {
205
+ "id": "29",
206
+ "x": 276,
207
+ "y": 84,
208
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
209
+ "scaleX": 1
210
+ },
211
+ {
212
+ "id": "40",
213
+ "x": 264,
214
+ "y": 96,
215
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
216
+ "scaleX": 1
217
+ },
218
+ {
219
+ "id": "41",
220
+ "x": 276,
221
+ "y": 96,
222
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
223
+ "scaleX": 1
224
+ },
225
+ {
226
+ "id": "9",
227
+ "x": 240,
228
+ "y": -60,
229
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
230
+ "scaleX": 1
231
+ },
232
+ {
233
+ "id": "9",
234
+ "x": 252,
235
+ "y": -36,
236
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
237
+ "scaleX": 1
238
+ },
239
+ {
240
+ "id": "9",
241
+ "x": 264,
242
+ "y": 48,
243
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
244
+ "scaleX": 1
245
+ },
246
+ {
247
+ "id": "9",
248
+ "x": 240,
249
+ "y": 72,
250
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
251
+ "scaleX": 1
252
+ },
253
+ {
254
+ "id": "9",
255
+ "x": 300,
256
+ "y": 72,
257
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
258
+ "scaleX": 1
259
+ },
260
+ {
261
+ "id": "11",
262
+ "x": 276,
263
+ "y": 24,
264
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
265
+ "scaleX": 1
266
+ },
267
+ {
268
+ "id": "11",
269
+ "x": 276,
270
+ "y": 36,
271
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
272
+ "scaleX": 1
273
+ },
274
+ {
275
+ "id": "11",
276
+ "x": 264,
277
+ "y": 36,
278
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
279
+ "scaleX": 1
280
+ },
281
+ {
282
+ "id": "11",
283
+ "x": 264,
284
+ "y": 24,
285
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
286
+ "scaleX": 1
287
+ },
288
+ {
289
+ "id": "10",
290
+ "x": 300,
291
+ "y": -48,
292
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
293
+ "scaleX": 1
294
+ },
295
+ {
296
+ "id": "10",
297
+ "x": 288,
298
+ "y": -48,
299
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
300
+ "scaleX": 1
301
+ },
302
+ {
303
+ "id": "10",
304
+ "x": 288,
305
+ "y": -36,
306
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
307
+ "scaleX": 1
308
+ },
309
+ {
310
+ "id": "10",
311
+ "x": 288,
312
+ "y": -24,
313
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
314
+ "scaleX": 1
315
+ },
316
+ {
317
+ "id": "10",
318
+ "x": 300,
319
+ "y": -24,
320
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
321
+ "scaleX": 1
322
+ },
323
+ {
324
+ "id": "10",
325
+ "x": 300,
326
+ "y": -36,
327
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
328
+ "scaleX": 1
329
+ },
330
+ {
331
+ "id": "10",
332
+ "x": 312,
333
+ "y": -36,
334
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
335
+ "scaleX": 1
336
+ },
337
+ {
338
+ "id": "47",
339
+ "x": 216,
340
+ "y": 120,
341
+ "spriteSheetId": "600d29ce-27cb-48ed-b91d-13f8c07e6c2a",
342
+ "scaleX": 1
343
+ }
344
+ ],
345
+ "collider": false,
346
+ "isAutoTile": false,
347
+ "rules": []
348
+ }
349
+ ],
350
+ "settings": {
351
+ "showGrid": true,
352
+ "GBStudioMode": false,
353
+ "backgroundColor": "#FFFFFF"
354
+ },
355
+ "exports": {
356
+ "spritesheet": "",
357
+ "tiles": []
358
+ }
359
+ }
Binary file
Binary file
Binary file
@@ -0,0 +1,4 @@
1
+ body {
2
+ margin: 0;
3
+ padding: 0;
4
+ }
@@ -0,0 +1,17 @@
1
+ import "./main.css";
2
+ import { Platformer } from "./Platformer";
3
+ import { GameTemplate } from "./GameTemplate";
4
+
5
+ document.querySelector<HTMLDivElement>("#app")!.innerHTML = `
6
+ <div id="main" style="align-items: center; justify-content: center; height: 100%; display: flex">
7
+ </div>
8
+ <!-- <canvas id="detect-render" width="768" height="768""></canvas>-->
9
+ `;
10
+ const main = document.querySelector<HTMLDivElement>("#main")!;
11
+ const game = new Platformer();
12
+ // const game = new GameTemplate();
13
+
14
+ game.start().then(() => {
15
+ main.appendChild(game.application.canvas);
16
+ game.application.canvas.focus();
17
+ });
@@ -0,0 +1,102 @@
1
+ import { AnimatedSpriteController, Button, Component, CType, Entity, Key, System, Timer } from "lagom-engine";
2
+
3
+ import { GameTemplate } from "../GameTemplate";
4
+
5
+ class MuteComp extends Component {}
6
+
7
+ class MuteListener extends System<[AnimatedSpriteController, MuteComp]> {
8
+ types: [CType<AnimatedSpriteController>, CType<MuteComp>] = [AnimatedSpriteController, MuteComp];
9
+
10
+ runOnEntities(delta: number, e: Entity, spr: AnimatedSpriteController, args_1: MuteComp): void {
11
+ if (this.scene.game.mouse.isButtonPressed(Button.LEFT)) {
12
+ // TODO this is a bit cheeky, how can I do it now?
13
+ // const pos = e.scene.game.canvas.plugins.interaction.mouse.global;
14
+ // if (pos.x >= GameTemplate.GAME_WIDTH - 24 && pos.x <= GameTemplate.GAME_WIDTH - 8 && pos.y >= GameTemplate.GAME_HEIGHT - 24 && pos.y <= GameTemplate.GAME_HEIGHT - 8) {
15
+ // (e.scene.getEntityWithName("audio") as SoundManager).toggleMute();
16
+ // spr.setAnimation(Number(GameTemplate.muted));
17
+ // }
18
+ } else if (this.scene.game.keyboard.isKeyPressed(Key.KeyM)) {
19
+ (e.scene.getEntityWithName("audio") as SoundManager).toggleMute();
20
+ spr.setAnimation(Number(GameTemplate.muted));
21
+ }
22
+ }
23
+ }
24
+
25
+ export class SoundManager extends Entity {
26
+ constructor() {
27
+ super("audio", GameTemplate.GAME_WIDTH - 16 - 8, GameTemplate.GAME_HEIGHT - 24, 0);
28
+ this.startMusic();
29
+ }
30
+
31
+ onAdded(): void {
32
+ super.onAdded();
33
+
34
+ this.addComponent(new MuteComp());
35
+ const spr = this.addComponent(
36
+ new AnimatedSpriteController(Number(GameTemplate.muted), [
37
+ {
38
+ id: 0,
39
+ textures: [this.scene.game.getResource("mute_button").tileAt(0, 0)],
40
+ },
41
+ {
42
+ id: 1,
43
+ textures: [this.scene.game.getResource("mute_button").tileAt(1, 0)],
44
+ },
45
+ ]),
46
+ );
47
+
48
+ this.addComponent(new Timer(50, spr, false)).onTrigger.register((caller, data) => {
49
+ data.setAnimation(Number(GameTemplate.muted));
50
+ });
51
+
52
+ this.scene.addSystem(new MuteListener());
53
+ }
54
+
55
+ toggleMute() {
56
+ GameTemplate.muted = !GameTemplate.muted;
57
+
58
+ if (GameTemplate.muted) {
59
+ this.stopAllSounds();
60
+ } else {
61
+ this.startMusic();
62
+ }
63
+ }
64
+
65
+ startMusic() {
66
+ if (!GameTemplate.muted && !GameTemplate.musicPlaying) {
67
+ GameTemplate.audioAtlas.play("music");
68
+ GameTemplate.musicPlaying = true;
69
+ }
70
+ }
71
+
72
+ stopAllSounds(music = true) {
73
+ if (music) {
74
+ GameTemplate.audioAtlas.sounds.forEach((v: any, k: string) => v.stop());
75
+ GameTemplate.musicPlaying = false;
76
+ } else {
77
+ GameTemplate.audioAtlas.sounds.forEach((v: any, k: string) => {
78
+ if (k !== "music") v.stop();
79
+ });
80
+ }
81
+ }
82
+
83
+ onRemoved(): void {
84
+ super.onRemoved();
85
+ this.stopAllSounds(false);
86
+ }
87
+
88
+ playSound(name: string, restart = false) {
89
+ if (!GameTemplate.muted) {
90
+ if (GameTemplate.audioAtlas.sounds.get(name)?.isPlaying && !restart) return;
91
+ GameTemplate.audioAtlas.play(name);
92
+ }
93
+ }
94
+
95
+ stopSound(name: string) {
96
+ GameTemplate.audioAtlas.sounds.forEach((value, key) => {
97
+ if (key === name) {
98
+ value.stop();
99
+ }
100
+ });
101
+ }
102
+ }
@@ -0,0 +1 @@
1
+ /// <reference types="vite/client" />
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "Node",
6
+ "esModuleInterop": true,
7
+ "skipLibCheck": true,
8
+
9
+ /* Linting */
10
+ "strict": true
11
+ },
12
+ "include": [
13
+ "template"
14
+ ]
15
+ }
@@ -0,0 +1,6 @@
1
+ // vite.config.js
2
+ import { defineConfig } from "vite";
3
+
4
+ export default defineConfig({
5
+ base: "",
6
+ });