@inglorious/engine 7.0.1 → 8.0.0

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/README.md CHANGED
@@ -1,115 +1,115 @@
1
- # Inglorious Engine
2
-
3
- [![NPM version](https://img.shields.io/npm/v/@inglorious/engine.svg)](https://www.npmjs.com/package/@inglorious/engine)
4
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
-
6
- The core orchestrator for the [Inglorious Engine](https://github.com/IngloriousCoderz/inglorious-engine). This package provides a complete game loop, state management, and rendering pipeline in a single, cohesive unit. It is designed to be highly configurable and extensible, allowing you to build games with a functional, data-oriented approach.
7
-
8
- ---
9
-
10
- ## Core Concepts
11
-
12
- The `@inglorious/engine` package acts as the central hub that brings together all the engine's components. Its main responsibilities are:
13
-
14
- 1. **Orchestrating the Game Loop**: The `Engine` class manages the game loop, which is responsible for continuously updating the state and rendering the game. The loop can be configured to use different timing mechanisms, such as `animationFrame` or a fixed `fps`.
15
-
16
- 2. **State Management**: It leverages the `@inglorious/store` package to manage the game's state. It provides methods to start, stop, and update the state manager, processing a queue of events on each frame.
17
-
18
- 3. **Integrating the Renderer**: The engine is headless by design, but it can be configured with a renderer to display the game. The engine takes a renderer object in its configuration and integrates its systems and logic into the main game loop.
19
-
20
- 4. **Dev Tools Integration**: The engine automatically connects to a browser's dev tools for debugging and time-travel capabilities if `devMode` is enabled in the game's state.
21
-
22
- ---
23
-
24
- ## Installation
25
-
26
- ```bash
27
- npm install @inglorious/engine
28
- ```
29
-
30
- ---
31
-
32
- ## API
33
-
34
- ### `new Engine(...gameConfigs)`
35
-
36
- Creates a new `Engine` instance, given one or more configuration objects.
37
-
38
- **Parameters:**
39
-
40
- - `gameConfig` (object): The game-specific configuration. It is an object with the following properties:
41
- - `loop` (object, optional): Configuration for the game loop.
42
- - `type` (string, optional): The type of loop to use (`animationFrame` or `fixed`). Defaults to `animationFrame`.
43
- - `fps` (number, optional): The target frames per second. Only used with the `fixed` loop type. Defaults to `60`.
44
- - `types` (object, optional): A map of entity types.
45
- - `entities` (object, optional): A map of initial entities.
46
- - `systems` (array, optional): An array of system objects, which define behaviors for the whole state.
47
-
48
- **Returns:**
49
-
50
- - A new `Engine` instance.
51
-
52
- ### `await engine.init()`
53
-
54
- An asynchronous function that initializes the resources required for the game to load.
55
-
56
- ### `engine.start()`
57
-
58
- Starts the game loop, triggering the first `update` and `render` calls.
59
-
60
- ### `engine.stop()`
61
-
62
- Halts the game loop and cleans up any resources. This method also processes a final `stop` event to ensure a clean shutdown.
63
-
64
- ---
65
-
66
- ## Basic Usage
67
-
68
- Here is a complete example showing how to set up and run a game using the engine.
69
-
70
- ```html
71
- <!DOCTYPE html>
72
- <html lang="en">
73
- <body>
74
- <canvas id="canvas" width="800" height="600"></canvas>
75
-
76
- <script type="text/javascript">
77
- window.process = { env: "development" }
78
- </script>
79
-
80
- <script type="importmap">
81
- {
82
- "imports": {
83
- "mutative": "https://unpkg.com/mutative@latest/dist/mutative.esm.mjs",
84
- "@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@latest/src/",
85
- "@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@latest/src/",
86
- "@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@latest/src/",
87
- "@inglorious/renderer-2d/": "https://unpkg.com/@inglorious%2Frenderer-2d@latest/src/",
88
- "game": "/game.js"
89
- }
90
- }
91
- </script>
92
-
93
- <script
94
- type="module"
95
- src="https://unpkg.com/@inglorious%2Fengine@latest/src/main.js"
96
- ></script>
97
- </body>
98
- </html>
99
- ```
100
-
101
- ---
102
-
103
- ## License
104
-
105
- **MIT License - Free and open source**
106
-
107
- Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
108
-
109
- You're free to use, modify, and distribute this software. See [LICENSE](./LICENSE) for details.
110
-
111
- ---
112
-
113
- ## Contributing
114
-
115
- We welcome contributions from the community\! Whether you're fixing a bug, adding a feature, or improving the documentation, your help is appreciated. Please read our [Contributing Guidelines](https://github.com/IngloriousCoderz/inglorious-engine/blob/main/CONTRIBUTING.md) for details on how to get started.
1
+ # Inglorious Engine
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/@inglorious/engine.svg)](https://www.npmjs.com/package/@inglorious/engine)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ The core orchestrator for the [Inglorious Engine](https://github.com/IngloriousCoderz/inglorious-engine). This package provides a complete game loop, state management, and rendering pipeline in a single, cohesive unit. It is designed to be highly configurable and extensible, allowing you to build games with a functional, data-oriented approach.
7
+
8
+ ---
9
+
10
+ ## Core Concepts
11
+
12
+ The `@inglorious/engine` package acts as the central hub that brings together all the engine's components. Its main responsibilities are:
13
+
14
+ 1. **Orchestrating the Game Loop**: The `Engine` class manages the game loop, which is responsible for continuously updating the state and rendering the game. The loop can be configured to use different timing mechanisms, such as `animationFrame` or a fixed `fps`.
15
+
16
+ 2. **State Management**: It leverages the `@inglorious/store` package to manage the game's state. It provides methods to start, stop, and update the state manager, processing a queue of events on each frame.
17
+
18
+ 3. **Integrating the Renderer**: The engine is headless by design, but it can be configured with a renderer to display the game. The engine takes a renderer object in its configuration and integrates its systems and logic into the main game loop.
19
+
20
+ 4. **Dev Tools Integration**: The engine automatically connects to a browser's dev tools for debugging and time-travel capabilities if `devMode` is enabled in the game's state.
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @inglorious/engine
28
+ ```
29
+
30
+ ---
31
+
32
+ ## API
33
+
34
+ ### `new Engine(...gameConfigs)`
35
+
36
+ Creates a new `Engine` instance, given one or more configuration objects.
37
+
38
+ **Parameters:**
39
+
40
+ - `gameConfig` (object): The game-specific configuration. It is an object with the following properties:
41
+ - `loop` (object, optional): Configuration for the game loop.
42
+ - `type` (string, optional): The type of loop to use (`animationFrame` or `fixed`). Defaults to `animationFrame`.
43
+ - `fps` (number, optional): The target frames per second. Only used with the `fixed` loop type. Defaults to `60`.
44
+ - `types` (object, optional): A map of entity types.
45
+ - `entities` (object, optional): A map of initial entities.
46
+ - `systems` (array, optional): An array of system objects, which define behaviors for the whole state.
47
+
48
+ **Returns:**
49
+
50
+ - A new `Engine` instance.
51
+
52
+ ### `await engine.init()`
53
+
54
+ An asynchronous function that initializes the resources required for the game to load.
55
+
56
+ ### `engine.start()`
57
+
58
+ Starts the game loop, triggering the first `update` and `render` calls.
59
+
60
+ ### `engine.stop()`
61
+
62
+ Halts the game loop and cleans up any resources. This method also processes a final `stop` event to ensure a clean shutdown.
63
+
64
+ ---
65
+
66
+ ## Basic Usage
67
+
68
+ Here is a complete example showing how to set up and run a game using the engine.
69
+
70
+ ```html
71
+ <!DOCTYPE html>
72
+ <html lang="en">
73
+ <body>
74
+ <canvas id="canvas" width="800" height="600"></canvas>
75
+
76
+ <script type="text/javascript">
77
+ window.process = { env: "development" }
78
+ </script>
79
+
80
+ <script type="importmap">
81
+ {
82
+ "imports": {
83
+ "mutative": "https://unpkg.com/mutative@latest/dist/mutative.esm.mjs",
84
+ "@inglorious/utils/": "https://unpkg.com/@inglorious%2Futils@latest/src/",
85
+ "@inglorious/store/": "https://unpkg.com/@inglorious%2Fstore@latest/src/",
86
+ "@inglorious/engine/": "https://unpkg.com/@inglorious%2Fengine@latest/src/",
87
+ "@inglorious/renderer-2d/": "https://unpkg.com/@inglorious%2Frenderer-2d@latest/src/",
88
+ "game": "/game.js"
89
+ }
90
+ }
91
+ </script>
92
+
93
+ <script
94
+ type="module"
95
+ src="https://unpkg.com/@inglorious%2Fengine@latest/src/main.js"
96
+ ></script>
97
+ </body>
98
+ </html>
99
+ ```
100
+
101
+ ---
102
+
103
+ ## License
104
+
105
+ **MIT License - Free and open source**
106
+
107
+ Created by [Matteo Antony Mistretta](https://github.com/IngloriousCoderz)
108
+
109
+ You're free to use, modify, and distribute this software. See [LICENSE](./LICENSE) for details.
110
+
111
+ ---
112
+
113
+ ## Contributing
114
+
115
+ We welcome contributions from the community\! Whether you're fixing a bug, adding a feature, or improving the documentation, your help is appreciated. Please read our [Contributing Guidelines](https://github.com/IngloriousCoderz/inglorious-engine/blob/main/CONTRIBUTING.md) for details on how to get started.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@inglorious/engine",
3
- "version": "7.0.1",
3
+ "version": "8.0.0",
4
4
  "description": "A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!",
5
5
  "author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
6
6
  "license": "MIT",
@@ -30,11 +30,11 @@
30
30
  "access": "public"
31
31
  },
32
32
  "dependencies": {
33
- "@inglorious/store": "5.4.1",
33
+ "@inglorious/store": "5.5.0",
34
34
  "@inglorious/utils": "3.6.2"
35
35
  },
36
36
  "peerDependencies": {
37
- "@inglorious/store": "5.4.1",
37
+ "@inglorious/store": "5.5.0",
38
38
  "@inglorious/utils": "3.6.2"
39
39
  },
40
40
  "devDependencies": {
@@ -13,53 +13,52 @@ const Z = 2
13
13
  export function modernAcceleration(params) {
14
14
  params = extend(DEFAULT_PARAMS, params)
15
15
 
16
- return (type) =>
17
- extend(type, {
18
- ...createMovementEventHandlers([
19
- "moveLeft",
20
- "moveRight",
21
- "moveUp",
22
- "moveDown",
23
- "moveLeftRight",
24
- "moveUpDown",
25
- ]),
16
+ return (type) => ({
17
+ ...createMovementEventHandlers([
18
+ "moveLeft",
19
+ "moveRight",
20
+ "moveUp",
21
+ "moveDown",
22
+ "moveLeftRight",
23
+ "moveUpDown",
24
+ ]),
26
25
 
27
- create(entity, entityId, api) {
28
- type.create?.(entity, entityId, api)
26
+ create(entity, entityId, api) {
27
+ type.create?.(entity, entityId, api)
29
28
 
30
- if (entityId !== entity.id) return
29
+ if (entityId !== entity.id) return
31
30
 
32
- entity.maxAcceleration ??= params.maxAcceleration
33
- entity.movement ??= {}
34
- },
31
+ entity.maxAcceleration ??= params.maxAcceleration
32
+ entity.movement ??= {}
33
+ },
35
34
 
36
- update(entity, dt, api) {
37
- type.update?.(entity, dt, api)
35
+ update(entity, dt, api) {
36
+ type.update?.(entity, dt, api)
38
37
 
39
- const { movement, maxAcceleration } = entity
40
- entity.acceleration = zero()
38
+ const { movement, maxAcceleration } = entity
39
+ entity.acceleration = zero()
41
40
 
42
- if (movement.moveLeft) {
43
- entity.acceleration[X] = -maxAcceleration
44
- }
45
- if (movement.moveRight) {
46
- entity.acceleration[X] = maxAcceleration
47
- }
48
- if (movement.moveUp) {
49
- entity.acceleration[Z] = maxAcceleration
50
- }
51
- if (movement.moveDown) {
52
- entity.acceleration[Z] = -maxAcceleration
53
- }
41
+ if (movement.moveLeft) {
42
+ entity.acceleration[X] = -maxAcceleration
43
+ }
44
+ if (movement.moveRight) {
45
+ entity.acceleration[X] = maxAcceleration
46
+ }
47
+ if (movement.moveUp) {
48
+ entity.acceleration[Z] = maxAcceleration
49
+ }
50
+ if (movement.moveDown) {
51
+ entity.acceleration[Z] = -maxAcceleration
52
+ }
54
53
 
55
- if (movement.moveLeftRight) {
56
- entity.acceleration[X] += movement.moveLeftRight * maxAcceleration
57
- }
58
- if (movement.moveUpDown) {
59
- entity.acceleration[Z] += -movement.moveUpDown * maxAcceleration
60
- }
61
- },
62
- })
54
+ if (movement.moveLeftRight) {
55
+ entity.acceleration[X] += movement.moveLeftRight * maxAcceleration
56
+ }
57
+ if (movement.moveUpDown) {
58
+ entity.acceleration[Z] += -movement.moveUpDown * maxAcceleration
59
+ }
60
+ },
61
+ })
63
62
  }
64
63
 
65
64
  export function modernControls(params) {
@@ -21,66 +21,65 @@ export function shooterControls(params) {
21
21
  const DEADZONE = 0.1
22
22
  const NO_MOVEMENT = 0
23
23
 
24
- return (type) =>
25
- extend(type, {
26
- ...createMovementEventHandlers([
27
- "moveLeft",
28
- "moveRight",
29
- "moveUp",
30
- "moveDown",
31
- "strafe",
32
- "move",
33
- "turn",
34
- ]),
24
+ return (type) => ({
25
+ ...createMovementEventHandlers([
26
+ "moveLeft",
27
+ "moveRight",
28
+ "moveUp",
29
+ "moveDown",
30
+ "strafe",
31
+ "move",
32
+ "turn",
33
+ ]),
35
34
 
36
- create(entity, entityId, api) {
37
- type.create?.(entity, entityId, api)
35
+ create(entity, entityId, api) {
36
+ type.create?.(entity, entityId, api)
38
37
 
39
- if (entityId !== entity.id) return
38
+ if (entityId !== entity.id) return
40
39
 
41
- entity.maxSpeed ??= params.maxSpeed
42
- entity.maxAngularSpeed ??= params.maxAngularSpeed
43
- entity.maxAcceleration ??= params.maxAcceleration
44
- entity.movement ??= {}
45
- },
40
+ entity.maxSpeed ??= params.maxSpeed
41
+ entity.maxAngularSpeed ??= params.maxAngularSpeed
42
+ entity.maxAcceleration ??= params.maxAcceleration
43
+ entity.movement ??= {}
44
+ },
46
45
 
47
- update(entity, dt, api) {
48
- const mouse = api.getEntity("mouse")
46
+ update(entity, dt, api) {
47
+ const mouse = api.getEntity("mouse")
49
48
 
50
- const { movement, maxAngularSpeed, maxAcceleration } = entity
51
- entity.acceleration = zero()
49
+ const { movement, maxAngularSpeed, maxAcceleration } = entity
50
+ entity.acceleration = zero()
52
51
 
53
- if (movement.moveLeft) {
54
- entity.acceleration[Z] = -maxAcceleration
55
- }
56
- if (movement.moveRight) {
57
- entity.acceleration[Z] = maxAcceleration
58
- }
59
- if (movement.moveUp) {
60
- entity.acceleration[X] = maxAcceleration
61
- }
62
- if (movement.moveDown) {
63
- entity.acceleration[X] = -maxAcceleration
64
- }
52
+ if (movement.moveLeft) {
53
+ entity.acceleration[Z] = -maxAcceleration
54
+ }
55
+ if (movement.moveRight) {
56
+ entity.acceleration[Z] = maxAcceleration
57
+ }
58
+ if (movement.moveUp) {
59
+ entity.acceleration[X] = maxAcceleration
60
+ }
61
+ if (movement.moveDown) {
62
+ entity.acceleration[X] = -maxAcceleration
63
+ }
65
64
 
66
- if (movement.strafe) {
67
- entity.acceleration[Z] += movement.strafe * maxAcceleration
68
- }
69
- if (movement.move) {
70
- entity.acceleration[X] += -movement.move * maxAcceleration
71
- }
72
- if (movement.turn) {
73
- entity.orientation += -movement.turn * maxAngularSpeed * dt
74
- }
65
+ if (movement.strafe) {
66
+ entity.acceleration[Z] += movement.strafe * maxAcceleration
67
+ }
68
+ if (movement.move) {
69
+ entity.acceleration[X] += -movement.move * maxAcceleration
70
+ }
71
+ if (movement.turn) {
72
+ entity.orientation += -movement.turn * maxAngularSpeed * dt
73
+ }
75
74
 
76
- const isUsingAnalogMovement =
77
- Math.abs(movement.move ?? NO_MOVEMENT) > DEADZONE ||
78
- Math.abs(movement.strafe ?? NO_MOVEMENT) > DEADZONE
75
+ const isUsingAnalogMovement =
76
+ Math.abs(movement.move ?? NO_MOVEMENT) > DEADZONE ||
77
+ Math.abs(movement.strafe ?? NO_MOVEMENT) > DEADZONE
79
78
 
80
- if (!isUsingAnalogMovement) {
81
- merge(entity, face(entity, mouse, dt))
82
- }
83
- merge(entity, tankMove(entity, dt))
84
- },
85
- })
79
+ if (!isUsingAnalogMovement) {
80
+ merge(entity, face(entity, mouse, dt))
81
+ }
82
+ merge(entity, tankMove(entity, dt))
83
+ },
84
+ })
86
85
  }
@@ -15,57 +15,56 @@ const Z = 2
15
15
  export function tankControls(params) {
16
16
  params = extend(DEFAULT_PARAMS, params)
17
17
 
18
- return (type) =>
19
- extend(type, {
20
- ...createMovementEventHandlers([
21
- "turnLeft",
22
- "turnRight",
23
- "moveForward",
24
- "moveBackward",
25
- "strafe",
26
- "move",
27
- "turn",
28
- ]),
18
+ return (type) => ({
19
+ ...createMovementEventHandlers([
20
+ "turnLeft",
21
+ "turnRight",
22
+ "moveForward",
23
+ "moveBackward",
24
+ "strafe",
25
+ "move",
26
+ "turn",
27
+ ]),
29
28
 
30
- create(entity, entityId, api) {
31
- type.create?.(entity, entityId, api)
29
+ create(entity, entityId, api) {
30
+ type.create?.(entity, entityId, api)
32
31
 
33
- if (entityId !== entity.id) return
32
+ if (entityId !== entity.id) return
34
33
 
35
- entity.maxSpeed ??= params.maxSpeed
36
- entity.maxAngularSpeed ??= params.maxAngularSpeed
37
- entity.maxAcceleration ??= params.maxAcceleration
38
- entity.movement ??= {}
39
- },
34
+ entity.maxSpeed ??= params.maxSpeed
35
+ entity.maxAngularSpeed ??= params.maxAngularSpeed
36
+ entity.maxAcceleration ??= params.maxAcceleration
37
+ entity.movement ??= {}
38
+ },
40
39
 
41
- update(entity, dt) {
42
- const { movement, maxAngularSpeed, maxAcceleration } = entity
43
- entity.acceleration = zero()
40
+ update(entity, dt) {
41
+ const { movement, maxAngularSpeed, maxAcceleration } = entity
42
+ entity.acceleration = zero()
44
43
 
45
- if (movement.turnLeft) {
46
- entity.orientation += maxAngularSpeed * dt
47
- }
48
- if (movement.turnRight) {
49
- entity.orientation -= maxAngularSpeed * dt
50
- }
51
- if (movement.moveForward) {
52
- entity.acceleration[X] = maxAcceleration
53
- }
54
- if (movement.moveBackward) {
55
- entity.acceleration[X] = -maxAcceleration
56
- }
44
+ if (movement.turnLeft) {
45
+ entity.orientation += maxAngularSpeed * dt
46
+ }
47
+ if (movement.turnRight) {
48
+ entity.orientation -= maxAngularSpeed * dt
49
+ }
50
+ if (movement.moveForward) {
51
+ entity.acceleration[X] = maxAcceleration
52
+ }
53
+ if (movement.moveBackward) {
54
+ entity.acceleration[X] = -maxAcceleration
55
+ }
57
56
 
58
- if (movement.strafe != null) {
59
- entity.acceleration[Z] += movement.strafe * maxAcceleration
60
- }
61
- if (movement.move) {
62
- entity.acceleration[X] += -movement.move * maxAcceleration
63
- }
64
- if (movement.turn) {
65
- entity.orientation += -movement.turn * maxAngularSpeed * dt
66
- }
57
+ if (movement.strafe != null) {
58
+ entity.acceleration[Z] += movement.strafe * maxAcceleration
59
+ }
60
+ if (movement.move) {
61
+ entity.acceleration[X] += -movement.move * maxAcceleration
62
+ }
63
+ if (movement.turn) {
64
+ entity.orientation += -movement.turn * maxAngularSpeed * dt
65
+ }
67
66
 
68
- merge(entity, tankMove(entity, dt))
69
- },
70
- })
67
+ merge(entity, tankMove(entity, dt))
68
+ },
69
+ })
71
70
  }
@@ -13,53 +13,52 @@ const Z = 2
13
13
  export function modernVelocity(params) {
14
14
  params = extend(DEFAULT_PARAMS, params)
15
15
 
16
- return (type) =>
17
- extend(type, {
18
- ...createMovementEventHandlers([
19
- "moveLeft",
20
- "moveRight",
21
- "moveUp",
22
- "moveDown",
23
- "moveLeftRight",
24
- "moveUpDown",
25
- ]),
16
+ return (type) => ({
17
+ ...createMovementEventHandlers([
18
+ "moveLeft",
19
+ "moveRight",
20
+ "moveUp",
21
+ "moveDown",
22
+ "moveLeftRight",
23
+ "moveUpDown",
24
+ ]),
26
25
 
27
- create(entity, entityId, api) {
28
- type.create?.(entity, entityId, api)
26
+ create(entity, entityId, api) {
27
+ type.create?.(entity, entityId, api)
29
28
 
30
- if (entityId !== entity.id) return
29
+ if (entityId !== entity.id) return
31
30
 
32
- entity.maxSpeed ??= params.maxSpeed
33
- entity.movement ??= {}
34
- },
31
+ entity.maxSpeed ??= params.maxSpeed
32
+ entity.movement ??= {}
33
+ },
35
34
 
36
- update(entity, dt, api) {
37
- type.update?.(entity, dt, api)
35
+ update(entity, dt, api) {
36
+ type.update?.(entity, dt, api)
38
37
 
39
- const { movement, maxSpeed } = entity
40
- entity.velocity = zero()
38
+ const { movement, maxSpeed } = entity
39
+ entity.velocity = zero()
41
40
 
42
- if (movement.moveLeft) {
43
- entity.velocity[X] = -maxSpeed
44
- }
45
- if (movement.moveRight) {
46
- entity.velocity[X] = maxSpeed
47
- }
48
- if (movement.moveUp) {
49
- entity.velocity[Z] = maxSpeed
50
- }
51
- if (movement.moveDown) {
52
- entity.velocity[Z] = -maxSpeed
53
- }
41
+ if (movement.moveLeft) {
42
+ entity.velocity[X] = -maxSpeed
43
+ }
44
+ if (movement.moveRight) {
45
+ entity.velocity[X] = maxSpeed
46
+ }
47
+ if (movement.moveUp) {
48
+ entity.velocity[Z] = maxSpeed
49
+ }
50
+ if (movement.moveDown) {
51
+ entity.velocity[Z] = -maxSpeed
52
+ }
54
53
 
55
- if (movement.moveLeftRight) {
56
- entity.velocity[X] += movement.moveLeftRight * maxSpeed
57
- }
58
- if (movement.moveUpDown) {
59
- entity.velocity[Z] += -movement.moveUpDown * maxSpeed
60
- }
61
- },
62
- })
54
+ if (movement.moveLeftRight) {
55
+ entity.velocity[X] += movement.moveLeftRight * maxSpeed
56
+ }
57
+ if (movement.moveUpDown) {
58
+ entity.velocity[Z] += -movement.moveUpDown * maxSpeed
59
+ }
60
+ },
61
+ })
63
62
  }
64
63
 
65
64
  export function modernControls(params) {
@@ -20,65 +20,64 @@ export function shooterControls(params) {
20
20
  const DEADZONE = 0.1
21
21
  const NO_MOVEMENT = 0
22
22
 
23
- return (type) =>
24
- extend(type, {
25
- ...createMovementEventHandlers([
26
- "moveLeft",
27
- "moveRight",
28
- "moveUp",
29
- "moveDown",
30
- "move",
31
- "strafe",
32
- "turn",
33
- ]),
23
+ return (type) => ({
24
+ ...createMovementEventHandlers([
25
+ "moveLeft",
26
+ "moveRight",
27
+ "moveUp",
28
+ "moveDown",
29
+ "move",
30
+ "strafe",
31
+ "turn",
32
+ ]),
34
33
 
35
- create(entity, entityId, api) {
36
- type.create?.(entity, entityId, api)
34
+ create(entity, entityId, api) {
35
+ type.create?.(entity, entityId, api)
37
36
 
38
- if (entityId !== entity.id) return
37
+ if (entityId !== entity.id) return
39
38
 
40
- entity.maxSpeed ??= params.maxSpeed
41
- entity.maxAngularSpeed ??= params.maxAngularSpeed
42
- entity.movement ??= {}
43
- },
39
+ entity.maxSpeed ??= params.maxSpeed
40
+ entity.maxAngularSpeed ??= params.maxAngularSpeed
41
+ entity.movement ??= {}
42
+ },
44
43
 
45
- update(entity, dt, api) {
46
- const mouse = api.getEntity("mouse")
44
+ update(entity, dt, api) {
45
+ const mouse = api.getEntity("mouse")
47
46
 
48
- const { movement, maxSpeed, maxAngularSpeed } = entity
49
- entity.velocity = zero()
47
+ const { movement, maxSpeed, maxAngularSpeed } = entity
48
+ entity.velocity = zero()
50
49
 
51
- if (movement.moveLeft) {
52
- entity.velocity[Z] = -maxSpeed
53
- }
54
- if (movement.moveRight) {
55
- entity.velocity[Z] = maxSpeed
56
- }
57
- if (movement.moveUp) {
58
- entity.velocity[X] = maxSpeed
59
- }
60
- if (movement.moveDown) {
61
- entity.velocity[X] = -maxSpeed
62
- }
50
+ if (movement.moveLeft) {
51
+ entity.velocity[Z] = -maxSpeed
52
+ }
53
+ if (movement.moveRight) {
54
+ entity.velocity[Z] = maxSpeed
55
+ }
56
+ if (movement.moveUp) {
57
+ entity.velocity[X] = maxSpeed
58
+ }
59
+ if (movement.moveDown) {
60
+ entity.velocity[X] = -maxSpeed
61
+ }
63
62
 
64
- if (movement.strafe) {
65
- entity.velocity[Z] += movement.strafe * maxSpeed
66
- }
67
- if (movement.move) {
68
- entity.velocity[X] += -movement.move * maxSpeed
69
- }
70
- if (movement.turn) {
71
- entity.orientation += -movement.turn * maxAngularSpeed * dt
72
- }
63
+ if (movement.strafe) {
64
+ entity.velocity[Z] += movement.strafe * maxSpeed
65
+ }
66
+ if (movement.move) {
67
+ entity.velocity[X] += -movement.move * maxSpeed
68
+ }
69
+ if (movement.turn) {
70
+ entity.orientation += -movement.turn * maxAngularSpeed * dt
71
+ }
73
72
 
74
- const isUsingAnalogMovement =
75
- Math.abs(movement.move ?? NO_MOVEMENT) > DEADZONE ||
76
- Math.abs(movement.strafe ?? NO_MOVEMENT) > DEADZONE
73
+ const isUsingAnalogMovement =
74
+ Math.abs(movement.move ?? NO_MOVEMENT) > DEADZONE ||
75
+ Math.abs(movement.strafe ?? NO_MOVEMENT) > DEADZONE
77
76
 
78
- if (!isUsingAnalogMovement) {
79
- merge(entity, face(entity, mouse, dt))
80
- }
81
- merge(entity, tankMove(entity, dt))
82
- },
83
- })
77
+ if (!isUsingAnalogMovement) {
78
+ merge(entity, face(entity, mouse, dt))
79
+ }
80
+ merge(entity, tankMove(entity, dt))
81
+ },
82
+ })
84
83
  }
@@ -14,56 +14,55 @@ const Z = 2
14
14
  export function tankControls(params) {
15
15
  params = extend(DEFAULT_PARAMS, params)
16
16
 
17
- return (type) =>
18
- extend(type, {
19
- ...createMovementEventHandlers([
20
- "turnLeft",
21
- "turnRight",
22
- "moveForward",
23
- "moveBackward",
24
- "strafe",
25
- "move",
26
- "turn",
27
- ]),
17
+ return (type) => ({
18
+ ...createMovementEventHandlers([
19
+ "turnLeft",
20
+ "turnRight",
21
+ "moveForward",
22
+ "moveBackward",
23
+ "strafe",
24
+ "move",
25
+ "turn",
26
+ ]),
28
27
 
29
- create(entity, entityId, api) {
30
- type.create?.(entity, entityId, api)
28
+ create(entity, entityId, api) {
29
+ type.create?.(entity, entityId, api)
31
30
 
32
- if (entityId !== entity.id) return
31
+ if (entityId !== entity.id) return
33
32
 
34
- entity.maxSpeed ??= params.maxSpeed
35
- entity.maxAngularSpeed ??= params.maxAngularSpeed
36
- entity.movement ??= {}
37
- },
33
+ entity.maxSpeed ??= params.maxSpeed
34
+ entity.maxAngularSpeed ??= params.maxAngularSpeed
35
+ entity.movement ??= {}
36
+ },
38
37
 
39
- update(entity, dt) {
40
- const { movement, maxSpeed, maxAngularSpeed } = entity
41
- entity.velocity = zero()
38
+ update(entity, dt) {
39
+ const { movement, maxSpeed, maxAngularSpeed } = entity
40
+ entity.velocity = zero()
42
41
 
43
- if (movement.turnLeft) {
44
- entity.orientation += maxAngularSpeed * dt
45
- }
46
- if (movement.turnRight) {
47
- entity.orientation -= maxAngularSpeed * dt
48
- }
49
- if (movement.moveForward) {
50
- entity.velocity[X] = maxSpeed
51
- }
52
- if (movement.moveBackward) {
53
- entity.velocity[X] = -maxSpeed
54
- }
42
+ if (movement.turnLeft) {
43
+ entity.orientation += maxAngularSpeed * dt
44
+ }
45
+ if (movement.turnRight) {
46
+ entity.orientation -= maxAngularSpeed * dt
47
+ }
48
+ if (movement.moveForward) {
49
+ entity.velocity[X] = maxSpeed
50
+ }
51
+ if (movement.moveBackward) {
52
+ entity.velocity[X] = -maxSpeed
53
+ }
55
54
 
56
- if (movement.strafe) {
57
- entity.velocity[Z] += movement.strafe * maxSpeed
58
- }
59
- if (movement.move) {
60
- entity.velocity[X] += -movement.move * maxSpeed
61
- }
62
- if (movement.turn) {
63
- entity.orientation += -movement.turn * maxAngularSpeed * dt
64
- }
55
+ if (movement.strafe) {
56
+ entity.velocity[Z] += movement.strafe * maxSpeed
57
+ }
58
+ if (movement.move) {
59
+ entity.velocity[X] += -movement.move * maxSpeed
60
+ }
61
+ if (movement.turn) {
62
+ entity.orientation += -movement.turn * maxAngularSpeed * dt
63
+ }
65
64
 
66
- merge(entity, tankMove(entity, dt))
67
- },
68
- })
65
+ merge(entity, tankMove(entity, dt))
66
+ },
67
+ })
69
68
  }
@@ -1,29 +1,26 @@
1
- import { extend } from "@inglorious/utils/data-structures/objects.js"
2
-
3
1
  export function collisionGizmos(params) {
4
- return (type) =>
5
- extend(type, {
6
- render(entity, ctx, api) {
7
- type.render(entity, ctx, api)
2
+ return (type) => ({
3
+ render(entity, ctx, api) {
4
+ type.render(entity, ctx, api)
8
5
 
9
- const game = api.getEntity("game")
6
+ const game = api.getEntity("game")
10
7
 
11
- if (!game.debug) {
12
- return
13
- }
8
+ if (!game.debug) {
9
+ return
10
+ }
14
11
 
15
- if (!params?.shapes) {
16
- return
17
- }
12
+ if (!params?.shapes) {
13
+ return
14
+ }
18
15
 
19
- ctx.save()
16
+ ctx.save()
20
17
 
21
- Object.values(entity.collisions).forEach((collision) => {
22
- const render = params.shapes[collision.shape]
23
- render?.({ ...entity, ...collision, color: "#00FF00" }, ctx, api)
24
- })
18
+ Object.values(entity.collisions).forEach((collision) => {
19
+ const render = params.shapes[collision.shape]
20
+ render?.({ ...entity, ...collision, color: "#00FF00" }, ctx, api)
21
+ })
25
22
 
26
- ctx.restore()
27
- },
28
- })
23
+ ctx.restore()
24
+ },
25
+ })
29
26
  }
@@ -1,5 +1,3 @@
1
- import { extend } from "@inglorious/utils/data-structures/objects.js"
2
-
3
1
  const DEFAULT_STATE = "default"
4
2
 
5
3
  export function fsm(states) {
@@ -8,7 +6,7 @@ export function fsm(states) {
8
6
  ]
9
7
 
10
8
  return (type) => {
11
- return extend(type, {
9
+ return {
12
10
  create(entity, entityId, api) {
13
11
  type.create?.(entity, entityId, api)
14
12
 
@@ -30,6 +28,6 @@ export function fsm(states) {
30
28
  }),
31
29
  {},
32
30
  ),
33
- })
31
+ }
34
32
  }
35
33
  }
@@ -8,21 +8,20 @@ const DEFAULT_PARAMS = {
8
8
  export function bouncy(params) {
9
9
  params = extend(DEFAULT_PARAMS, params)
10
10
 
11
- return (type) =>
12
- extend(type, {
13
- create(entity, entityId, api) {
14
- type.create?.(entity, entityId, api)
11
+ return (type) => ({
12
+ create(entity, entityId, api) {
13
+ type.create?.(entity, entityId, api)
15
14
 
16
- if (entityId !== entity.id) return
15
+ if (entityId !== entity.id) return
17
16
 
18
- defaults(entity, params)
19
- },
17
+ defaults(entity, params)
18
+ },
20
19
 
21
- land(entity, entityId) {
22
- if (entity.id === entityId) {
23
- entity.vy = jump(entity) * entity.bounciness
24
- entity.groundObject = undefined
25
- }
26
- },
27
- })
20
+ land(entity, entityId) {
21
+ if (entity.id === entityId) {
22
+ entity.vy = jump(entity) * entity.bounciness
23
+ entity.groundObject = undefined
24
+ }
25
+ },
26
+ })
28
27
  }
@@ -9,30 +9,29 @@ const DEFAULT_PARAMS = {
9
9
  export function clamped(params) {
10
10
  params = extend(DEFAULT_PARAMS, params)
11
11
 
12
- return (type) =>
13
- extend(type, {
14
- create(entity, entityId, api) {
15
- type.create?.(entity, entityId, api)
12
+ return (type) => ({
13
+ create(entity, entityId, api) {
14
+ type.create?.(entity, entityId, api)
16
15
 
17
- if (entityId !== entity.id) return
16
+ if (entityId !== entity.id) return
18
17
 
19
- entity.collisions ??= {}
20
- entity.collisions[params.collisionGroup] ??= {}
21
- entity.collisions[params.collisionGroup].shape ??= "rectangle"
22
- },
18
+ entity.collisions ??= {}
19
+ entity.collisions[params.collisionGroup] ??= {}
20
+ entity.collisions[params.collisionGroup].shape ??= "rectangle"
21
+ },
23
22
 
24
- update(entity, dt, api) {
25
- type.update?.(entity, dt, api)
23
+ update(entity, dt, api) {
24
+ type.update?.(entity, dt, api)
26
25
 
27
- const game = api.getEntity("game")
28
- merge(entity, {
29
- position: clampToBounds(
30
- entity,
31
- game.size,
32
- params.collisionGroup,
33
- params.depthAxis,
34
- ),
35
- })
36
- },
37
- })
26
+ const game = api.getEntity("game")
27
+ merge(entity, {
28
+ position: clampToBounds(
29
+ entity,
30
+ game.size,
31
+ params.collisionGroup,
32
+ params.depthAxis,
33
+ ),
34
+ })
35
+ },
36
+ })
38
37
  }
@@ -21,126 +21,123 @@ const NO_PENETRAION = 0
21
21
  export function jumpable(params) {
22
22
  params = extend(DEFAULT_PARAMS, params)
23
23
 
24
- return (type) =>
25
- extend(type, {
26
- create(entity, entityId, api) {
27
- type.create?.(entity, entityId, api)
24
+ return (type) => ({
25
+ create(entity, entityId, api) {
26
+ type.create?.(entity, entityId, api)
28
27
 
29
- if (entityId !== entity.id) return
28
+ if (entityId !== entity.id) return
30
29
 
31
- defaults(entity, params)
32
- entity.jumpsLeft ??= entity.maxJumps
30
+ defaults(entity, params)
31
+ entity.jumpsLeft ??= entity.maxJumps
33
32
 
34
- entity.velocity ??= zero()
35
- entity.vy ??= 0
36
- },
33
+ entity.velocity ??= zero()
34
+ entity.vy ??= 0
35
+ },
37
36
 
38
- jump(entity, entityId, api) {
39
- type.jump?.(entity, entityId, api)
37
+ jump(entity, entityId, api) {
38
+ type.jump?.(entity, entityId, api)
40
39
 
41
- if (entityId === entity.id && entity.jumpsLeft) {
42
- entity.vy = jump(entity)
43
- entity.groundObject = undefined
44
- entity.jumpsLeft--
45
- }
46
- },
47
-
48
- update(entity, dt, api) {
49
- type.update?.(entity, dt, api)
50
-
51
- const entities = api.getEntities()
52
-
53
- entity.collisions ??= {}
54
- entity.collisions.platform ??= {}
55
- entity.collisions.platform.shape ??= "rectangle"
56
-
57
- let width, height
58
- if (entity.collisions.platform.shape === "circle") {
59
- width = entity.collisions.platform.radius * DOUBLE
60
- height = entity.collisions.platform.radius * DOUBLE
61
- } else {
62
- ;[width, height] = entity.size
40
+ if (entityId === entity.id && entity.jumpsLeft) {
41
+ entity.vy = jump(entity)
42
+ entity.groundObject = undefined
43
+ entity.jumpsLeft--
44
+ }
45
+ },
46
+
47
+ update(entity, dt, api) {
48
+ type.update?.(entity, dt, api)
49
+
50
+ const entities = api.getEntities()
51
+
52
+ entity.collisions ??= {}
53
+ entity.collisions.platform ??= {}
54
+ entity.collisions.platform.shape ??= "rectangle"
55
+
56
+ let width, height
57
+ if (entity.collisions.platform.shape === "circle") {
58
+ width = entity.collisions.platform.radius * DOUBLE
59
+ height = entity.collisions.platform.radius * DOUBLE
60
+ } else {
61
+ ;[width, height] = entity.size
62
+ }
63
+ const [prevX, prevY] = [...entity.position]
64
+
65
+ // 1. HORIZONTAL MOVEMENT & RESOLUTION
66
+ entity.position[X] += entity.velocity[X] * dt
67
+ const entityLeft = entity.position[X] - width / HALF
68
+ const entityRight = entity.position[X] + width / HALF
69
+
70
+ const collisionX = findCollision(entity, entities, "platform")
71
+ if (collisionX) {
72
+ const vx = entity.velocity[X]
73
+
74
+ // Check if moving right and crossing the platform's left edge
75
+ const prevRight = prevX + width / HALF
76
+ const platformLeft = collisionX.position[X] - collisionX.size[X] / HALF
77
+ if (vx > NO_VELOCITY && prevRight <= platformLeft) {
78
+ const penetration = entityRight - platformLeft
79
+ if (penetration > NO_PENETRAION) entity.position[X] -= penetration
80
+ entity.velocity[X] = 0
63
81
  }
64
- const [prevX, prevY] = [...entity.position]
65
-
66
- // 1. HORIZONTAL MOVEMENT & RESOLUTION
67
- entity.position[X] += entity.velocity[X] * dt
68
- const entityLeft = entity.position[X] - width / HALF
69
- const entityRight = entity.position[X] + width / HALF
70
-
71
- const collisionX = findCollision(entity, entities, "platform")
72
- if (collisionX) {
73
- const vx = entity.velocity[X]
74
-
75
- // Check if moving right and crossing the platform's left edge
76
- const prevRight = prevX + width / HALF
77
- const platformLeft =
78
- collisionX.position[X] - collisionX.size[X] / HALF
79
- if (vx > NO_VELOCITY && prevRight <= platformLeft) {
80
- const penetration = entityRight - platformLeft
81
- if (penetration > NO_PENETRAION) entity.position[X] -= penetration
82
- entity.velocity[X] = 0
83
- }
84
82
 
85
- // Check if moving left and crossing the platform's right edge
86
- const prevLeft = prevX - width / HALF
87
- const platformRight =
88
- collisionX.position[X] + collisionX.size[X] / HALF
89
- if (vx < NO_VELOCITY && prevLeft >= platformRight) {
90
- const penetration = platformRight - entityLeft
91
- if (penetration > NO_PENETRAION) entity.position[X] += penetration
92
- entity.velocity[X] = 0
93
- }
83
+ // Check if moving left and crossing the platform's right edge
84
+ const prevLeft = prevX - width / HALF
85
+ const platformRight = collisionX.position[X] + collisionX.size[X] / HALF
86
+ if (vx < NO_VELOCITY && prevLeft >= platformRight) {
87
+ const penetration = platformRight - entityLeft
88
+ if (penetration > NO_PENETRAION) entity.position[X] += penetration
89
+ entity.velocity[X] = 0
94
90
  }
95
-
96
- // 2. VERTICAL MOVEMENT & RESOLUTION
97
- const wasOnGround = entity.groundObject
98
- entity.groundObject = undefined
99
-
100
- const { vy, position: nextGravityPosition } = applyGravity(entity, dt)
101
- entity.vy = vy
102
- entity.position[Y] = nextGravityPosition[Y]
103
-
104
- const collisionY = findCollision(entity, entities, "platform")
105
- if (collisionY) {
106
- const prevBottom = prevY - height / HALF
107
- const platformTop = collisionY.position[Y] + collisionY.size[Y] / HALF
108
-
109
- // Landing on top of a platform (one-way platform logic)
110
- if (entity.vy <= NO_VELOCITY && prevBottom >= platformTop) {
111
- const entityBottom = entity.position[Y] - height / HALF
112
- const penetration = platformTop - entityBottom
113
- if (penetration > NO_PENETRAION) entity.position[Y] += penetration
114
- entity.vy = 0
115
- entity.groundObject = collisionY
116
- entity.jumpsLeft = entity.maxJumps
117
-
118
- // Only notify on the frame we actually land, not every frame we're on the ground.
119
- if (!wasOnGround) {
120
- api.notify("land", {
121
- entityId: entity.id,
122
- targetId: collisionY.id,
123
- })
124
- }
91
+ }
92
+
93
+ // 2. VERTICAL MOVEMENT & RESOLUTION
94
+ const wasOnGround = entity.groundObject
95
+ entity.groundObject = undefined
96
+
97
+ const { vy, position: nextGravityPosition } = applyGravity(entity, dt)
98
+ entity.vy = vy
99
+ entity.position[Y] = nextGravityPosition[Y]
100
+
101
+ const collisionY = findCollision(entity, entities, "platform")
102
+ if (collisionY) {
103
+ const prevBottom = prevY - height / HALF
104
+ const platformTop = collisionY.position[Y] + collisionY.size[Y] / HALF
105
+
106
+ // Landing on top of a platform (one-way platform logic)
107
+ if (entity.vy <= NO_VELOCITY && prevBottom >= platformTop) {
108
+ const entityBottom = entity.position[Y] - height / HALF
109
+ const penetration = platformTop - entityBottom
110
+ if (penetration > NO_PENETRAION) entity.position[Y] += penetration
111
+ entity.vy = 0
112
+ entity.groundObject = collisionY
113
+ entity.jumpsLeft = entity.maxJumps
114
+
115
+ // Only notify on the frame we actually land, not every frame we're on the ground.
116
+ if (!wasOnGround) {
117
+ api.notify("land", {
118
+ entityId: entity.id,
119
+ targetId: collisionY.id,
120
+ })
125
121
  }
126
-
127
- // Hitting head on bottom of a platform
128
- // else if (entity.vy > 0) {
129
- // const prevTop = prevY + height / HALF
130
- // const platformBottom =
131
- // collisionY.position[Y] - collisionY.size[Y] / HALF
132
- // if (prevTop <= platformBottom) {
133
- // const entityTop = entity.position[Y] + height / HALF
134
- // const penetration = entityTop - platformBottom
135
- // if (penetration > 0) entity.position[Y] -= penetration
136
- // entity.vy = 0
137
- // }
138
- // }
139
122
  }
140
123
 
141
- entity.orientation = magnitude(entity.velocity)
142
- ? angle(entity.velocity)
143
- : entity.orientation
144
- },
145
- })
124
+ // Hitting head on bottom of a platform
125
+ // else if (entity.vy > 0) {
126
+ // const prevTop = prevY + height / HALF
127
+ // const platformBottom =
128
+ // collisionY.position[Y] - collisionY.size[Y] / HALF
129
+ // if (prevTop <= platformBottom) {
130
+ // const entityTop = entity.position[Y] + height / HALF
131
+ // const penetration = entityTop - platformBottom
132
+ // if (penetration > 0) entity.position[Y] -= penetration
133
+ // entity.vy = 0
134
+ // }
135
+ // }
136
+ }
137
+
138
+ entity.orientation = magnitude(entity.velocity)
139
+ ? angle(entity.velocity)
140
+ : entity.orientation
141
+ },
142
+ })
146
143
  }