@inglorious/engine 0.2.0 → 0.4.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.
Files changed (169) hide show
  1. package/README.md +76 -75
  2. package/package.json +14 -25
  3. package/src/{engine/ai → ai}/movement/dynamic/align.js +63 -63
  4. package/src/{engine/ai → ai}/movement/dynamic/arrive.js +42 -42
  5. package/src/{engine/ai → ai}/movement/dynamic/evade.js +38 -38
  6. package/src/{engine/ai → ai}/movement/dynamic/face.js +19 -19
  7. package/src/{engine/ai → ai}/movement/dynamic/flee.js +45 -45
  8. package/src/{engine/ai → ai}/movement/dynamic/look-where-youre-going.js +16 -16
  9. package/src/{engine/ai → ai}/movement/dynamic/match-velocity.js +51 -51
  10. package/src/{engine/ai → ai}/movement/dynamic/pursue.js +38 -38
  11. package/src/{engine/ai → ai}/movement/dynamic/seek.js +44 -44
  12. package/src/{engine/ai → ai}/movement/dynamic/wander.js +31 -31
  13. package/src/{engine/ai → ai}/movement/kinematic/align.js +37 -37
  14. package/src/{engine/ai → ai}/movement/kinematic/arrive.js +42 -42
  15. package/src/{engine/ai → ai}/movement/kinematic/face.js +19 -19
  16. package/src/{engine/ai → ai}/movement/kinematic/flee.js +26 -26
  17. package/src/{engine/ai → ai}/movement/kinematic/seek.js +26 -26
  18. package/src/{engine/ai → ai}/movement/kinematic/seek.test.js +42 -42
  19. package/src/{engine/ai → ai}/movement/kinematic/wander-as-seek.js +31 -31
  20. package/src/{engine/ai → ai}/movement/kinematic/wander.js +27 -27
  21. package/src/{engine/animation → animation}/sprite.js +101 -101
  22. package/src/{engine/animation → animation}/ticker.js +38 -38
  23. package/src/{engine/behaviors → behaviors}/camera.js +68 -68
  24. package/src/{engine/behaviors → behaviors}/controls/dynamic/modern.js +76 -76
  25. package/src/{engine/behaviors → behaviors}/controls/dynamic/shooter.js +84 -84
  26. package/src/{engine/behaviors → behaviors}/controls/dynamic/tank.js +69 -69
  27. package/src/{engine/behaviors → behaviors}/controls/event-handlers.js +17 -17
  28. package/src/{engine/behaviors → behaviors}/controls/kinematic/modern.js +76 -76
  29. package/src/{engine/behaviors → behaviors}/controls/kinematic/shooter.js +82 -82
  30. package/src/{engine/behaviors → behaviors}/controls/kinematic/tank.js +67 -67
  31. package/src/behaviors/debug/collision.js +29 -0
  32. package/src/{engine/behaviors → behaviors}/fps.js +29 -29
  33. package/src/{engine/behaviors → behaviors}/fsm.js +33 -33
  34. package/src/{engine/behaviors → behaviors}/fsm.test.js +49 -49
  35. package/src/{engine/behaviors → behaviors}/game.js +15 -15
  36. package/src/{engine/behaviors → behaviors}/input/controls.js +37 -37
  37. package/src/{engine/behaviors → behaviors}/input/gamepad.js +114 -114
  38. package/src/{engine/behaviors → behaviors}/input/input.js +48 -48
  39. package/src/{engine/behaviors → behaviors}/input/keyboard.js +64 -64
  40. package/src/{engine/behaviors → behaviors}/input/mouse.js +91 -91
  41. package/src/{engine/behaviors → behaviors}/physics/bouncy.js +25 -25
  42. package/src/{engine/behaviors → behaviors}/physics/clamped.js +36 -36
  43. package/src/{engine/behaviors → behaviors}/physics/collidable.js +20 -20
  44. package/src/{engine/behaviors → behaviors}/physics/jumpable.js +145 -145
  45. package/src/{engine/behaviors → behaviors}/ui/button.js +17 -17
  46. package/src/{engine/collision → collision}/detection.js +110 -110
  47. package/src/{engine/core → core}/dev-tools.js +135 -135
  48. package/src/{engine/core → core}/engine.js +119 -119
  49. package/src/{engine/core → core}/loop.js +15 -15
  50. package/src/{engine/core → core}/loops/animation-frame.js +25 -25
  51. package/src/{engine/core → core}/loops/elapsed.js +22 -22
  52. package/src/{engine/core → core}/loops/fixed.js +27 -27
  53. package/src/{engine/core → core}/loops/flash.js +13 -13
  54. package/src/{engine/core → core}/loops/lag.js +26 -26
  55. package/src/main.js +10 -10
  56. package/src/{engine/movement → movement}/dynamic/modern.js +21 -21
  57. package/src/{engine/movement → movement}/dynamic/tank.js +43 -43
  58. package/src/{engine/movement → movement}/kinematic/modern.js +16 -16
  59. package/src/{engine/movement → movement}/kinematic/modern.test.js +27 -27
  60. package/src/{engine/movement → movement}/kinematic/tank.js +27 -27
  61. package/src/{engine/physics → physics}/bounds.js +138 -138
  62. package/src/{engine/physics → physics}/position.js +43 -43
  63. package/src/{engine/physics → physics}/position.test.js +80 -80
  64. package/src/{engine/systems → systems}/sprite-animation.js +27 -27
  65. package/src/engine/behaviors/debug/collision.js +0 -35
  66. package/src/engine/core/api.js +0 -34
  67. package/src/engine/core/select.js +0 -26
  68. package/src/engine/core/store.js +0 -178
  69. package/src/engine/core/store.test.js +0 -110
  70. package/src/renderers/canvas/absolute-position.js +0 -18
  71. package/src/renderers/canvas/camera.js +0 -13
  72. package/src/renderers/canvas/canvas-renderer.js +0 -68
  73. package/src/renderers/canvas/character.js +0 -38
  74. package/src/renderers/canvas/form/button.js +0 -25
  75. package/src/renderers/canvas/fps.js +0 -18
  76. package/src/renderers/canvas/image/hitmask.js +0 -51
  77. package/src/renderers/canvas/image/image.js +0 -34
  78. package/src/renderers/canvas/image/sprite.js +0 -49
  79. package/src/renderers/canvas/image/tilemap.js +0 -66
  80. package/src/renderers/canvas/mouse.js +0 -37
  81. package/src/renderers/canvas/rendering-system.js +0 -79
  82. package/src/renderers/canvas/shapes/circle.js +0 -29
  83. package/src/renderers/canvas/shapes/rectangle.js +0 -27
  84. package/src/renderers/react/game/character/character.module.scss +0 -17
  85. package/src/renderers/react/game/character/index.jsx +0 -20
  86. package/src/renderers/react/game/cursor/cursor.module.scss +0 -47
  87. package/src/renderers/react/game/cursor/index.jsx +0 -20
  88. package/src/renderers/react/game/form/fields/field/field.module.scss +0 -5
  89. package/src/renderers/react/game/form/fields/field/index.jsx +0 -56
  90. package/src/renderers/react/game/form/fields/fields.module.scss +0 -48
  91. package/src/renderers/react/game/form/fields/index.jsx +0 -12
  92. package/src/renderers/react/game/form/form.module.scss +0 -18
  93. package/src/renderers/react/game/form/index.jsx +0 -22
  94. package/src/renderers/react/game/fps/index.jsx +0 -16
  95. package/src/renderers/react/game/game.jsx +0 -72
  96. package/src/renderers/react/game/index.jsx +0 -29
  97. package/src/renderers/react/game/platform/index.jsx +0 -30
  98. package/src/renderers/react/game/platform/platform.module.scss +0 -7
  99. package/src/renderers/react/game/scene/index.jsx +0 -27
  100. package/src/renderers/react/game/scene/scene.module.scss +0 -9
  101. package/src/renderers/react/game/sprite/index.jsx +0 -60
  102. package/src/renderers/react/game/sprite/sprite.module.css +0 -3
  103. package/src/renderers/react/game/stats/index.jsx +0 -22
  104. package/src/renderers/react/hocs/with-absolute-position/index.jsx +0 -20
  105. package/src/renderers/react/hocs/with-absolute-position/with-absolute-position.module.scss +0 -5
  106. package/src/renderers/react/index.jsx +0 -9
  107. package/src/utils/algorithms/decision-tree.js +0 -24
  108. package/src/utils/algorithms/decision-tree.test.js +0 -153
  109. package/src/utils/algorithms/path-finding.js +0 -155
  110. package/src/utils/algorithms/path-finding.test.js +0 -151
  111. package/src/utils/algorithms/types.d.ts +0 -28
  112. package/src/utils/data-structures/array.js +0 -83
  113. package/src/utils/data-structures/array.test.js +0 -173
  114. package/src/utils/data-structures/board.js +0 -159
  115. package/src/utils/data-structures/board.test.js +0 -242
  116. package/src/utils/data-structures/boolean.js +0 -9
  117. package/src/utils/data-structures/heap.js +0 -164
  118. package/src/utils/data-structures/heap.test.js +0 -103
  119. package/src/utils/data-structures/object.js +0 -138
  120. package/src/utils/data-structures/object.test.js +0 -218
  121. package/src/utils/data-structures/objects.js +0 -66
  122. package/src/utils/data-structures/objects.test.js +0 -99
  123. package/src/utils/data-structures/tree.js +0 -36
  124. package/src/utils/data-structures/tree.test.js +0 -33
  125. package/src/utils/data-structures/types.d.ts +0 -4
  126. package/src/utils/functions/functions.js +0 -19
  127. package/src/utils/functions/functions.test.js +0 -23
  128. package/src/utils/math/geometry/circle.js +0 -70
  129. package/src/utils/math/geometry/circle.test.js +0 -97
  130. package/src/utils/math/geometry/hitmask.js +0 -70
  131. package/src/utils/math/geometry/hitmask.test.js +0 -155
  132. package/src/utils/math/geometry/line.js +0 -35
  133. package/src/utils/math/geometry/line.test.js +0 -49
  134. package/src/utils/math/geometry/point.js +0 -78
  135. package/src/utils/math/geometry/point.test.js +0 -81
  136. package/src/utils/math/geometry/rectangle.js +0 -76
  137. package/src/utils/math/geometry/rectangle.test.js +0 -42
  138. package/src/utils/math/geometry/segment.js +0 -80
  139. package/src/utils/math/geometry/segment.test.js +0 -183
  140. package/src/utils/math/geometry/triangle.js +0 -15
  141. package/src/utils/math/geometry/triangle.test.js +0 -11
  142. package/src/utils/math/geometry/types.d.ts +0 -23
  143. package/src/utils/math/linear-algebra/2d.js +0 -28
  144. package/src/utils/math/linear-algebra/2d.test.js +0 -17
  145. package/src/utils/math/linear-algebra/quaternion.js +0 -22
  146. package/src/utils/math/linear-algebra/quaternion.test.js +0 -25
  147. package/src/utils/math/linear-algebra/quaternions.js +0 -20
  148. package/src/utils/math/linear-algebra/quaternions.test.js +0 -29
  149. package/src/utils/math/linear-algebra/types.d.ts +0 -4
  150. package/src/utils/math/linear-algebra/vector.js +0 -327
  151. package/src/utils/math/linear-algebra/vector.test.js +0 -265
  152. package/src/utils/math/linear-algebra/vectors.js +0 -122
  153. package/src/utils/math/linear-algebra/vectors.test.js +0 -65
  154. package/src/utils/math/linear-interpolation.js +0 -9
  155. package/src/utils/math/numbers.js +0 -90
  156. package/src/utils/math/numbers.test.js +0 -137
  157. package/src/utils/math/rng.js +0 -44
  158. package/src/utils/math/rng.test.js +0 -39
  159. package/src/utils/math/statistics.js +0 -43
  160. package/src/utils/math/statistics.test.js +0 -47
  161. package/src/utils/math/trigonometry.js +0 -89
  162. package/src/utils/math/trigonometry.test.js +0 -52
  163. package/src/utils/physics/acceleration.js +0 -61
  164. package/src/utils/physics/friction.js +0 -28
  165. package/src/utils/physics/friction.test.js +0 -42
  166. package/src/utils/physics/gravity.js +0 -69
  167. package/src/utils/physics/gravity.test.js +0 -77
  168. package/src/utils/physics/jump.js +0 -31
  169. package/src/utils/physics/velocity.js +0 -36
@@ -1,27 +1,27 @@
1
- import { Sprite } from "@inglorious/engine/animation/sprite.js"
2
-
3
- /**
4
- * Advances sprite animations for entities that have a sprite component
5
- * with an active animation state.
6
- *
7
- * @param {Object} state - The game state, containing all entities.
8
- * @param {Object<string, Object>} state.entities - A map of entity IDs to entity objects.
9
- * @param {number} dt - The delta time since the last update.
10
- * @param {Object} api - The game API.
11
- * @param {Function} api.notify - A function to notify about events.
12
- */
13
- export function spriteAnimationSystem() {
14
- return {
15
- update(state, dt, api) {
16
- for (const id in state.entities) {
17
- const entity = state.entities[id]
18
-
19
- if (!entity.sprite || !entity.sprite.state) {
20
- continue
21
- }
22
-
23
- Sprite.play(entity.sprite.state, { entity, dt, notify: api.notify })
24
- }
25
- },
26
- }
27
- }
1
+ import { Sprite } from "@inglorious/engine/animation/sprite.js"
2
+
3
+ /**
4
+ * Advances sprite animations for entities that have a sprite component
5
+ * with an active animation state.
6
+ *
7
+ * @param {Object} state - The game state, containing all entities.
8
+ * @param {Object<string, Object>} state.entities - A map of entity IDs to entity objects.
9
+ * @param {number} dt - The delta time since the last update.
10
+ * @param {Object} api - The game API.
11
+ * @param {Function} api.notify - A function to notify about events.
12
+ */
13
+ export function spriteAnimationSystem() {
14
+ return {
15
+ update(state, dt, api) {
16
+ for (const id in state.entities) {
17
+ const entity = state.entities[id]
18
+
19
+ if (!entity.sprite || !entity.sprite.state) {
20
+ continue
21
+ }
22
+
23
+ Sprite.play(entity.sprite.state, { entity, dt, notify: api.notify })
24
+ }
25
+ },
26
+ }
27
+ }
@@ -1,35 +0,0 @@
1
- import { renderHitmask } from "@inglorious/renderers/canvas/image/hitmask.js"
2
- import { renderCircle } from "@inglorious/renderers/canvas/shapes/circle.js"
3
- import { renderRectangle } from "@inglorious/renderers/canvas/shapes/rectangle.js"
4
- import { extend } from "@inglorious/utils/data-structures/objects.js"
5
-
6
- const Shape = {
7
- circle: renderCircle,
8
- rectangle: renderRectangle,
9
- platform: renderRectangle,
10
- hitmask: renderHitmask,
11
- }
12
-
13
- export function collisionGizmos() {
14
- return (type) =>
15
- extend(type, {
16
- render(entity, ctx, api) {
17
- type.render(entity, ctx, api)
18
-
19
- const game = api.getEntity("game")
20
-
21
- if (!game.debug) {
22
- return
23
- }
24
-
25
- ctx.save()
26
-
27
- Object.values(entity.collisions).forEach((collision) => {
28
- const render = Shape[collision.shape]
29
- render({ ...entity, ...collision, color: "#00FF00" }, ctx, api)
30
- })
31
-
32
- ctx.restore()
33
- },
34
- })
35
- }
@@ -1,34 +0,0 @@
1
- import { createSelector as _createSelector } from "./select.js"
2
-
3
- export function createApi(store) {
4
- const createSelector = (inputSelectors, resultFunc) => {
5
- const selector = _createSelector(inputSelectors, resultFunc)
6
- return () => selector(store.getState())
7
- }
8
-
9
- const getTypes = () => store.getTypes()
10
-
11
- const getEntities = () => store.getState().entities
12
-
13
- const getEntity = (id) => getEntities()[id]
14
-
15
- const notify = (type, payload) => {
16
- store.notify(type, payload)
17
- }
18
-
19
- const dispatch = (action) => {
20
- store.dispatch(action)
21
- }
22
-
23
- const getType = (id) => store.getOriginalTypes()?.[id]
24
-
25
- return {
26
- createSelector,
27
- getTypes,
28
- getEntities,
29
- getEntity,
30
- getType,
31
- notify,
32
- dispatch,
33
- }
34
- }
@@ -1,26 +0,0 @@
1
- /**
2
- * Creates a memoized selector function.
3
- * NB: this implementation does not support spreading the input selectors for clarity, please just put them in an array.
4
- * @param {Array<Function>} inputSelectors - An array of input selector functions.
5
- * @param {Function} resultFunc - A function that receives the results of the input selectors and returns a computed value.
6
- * @returns {Function} A memoized selector function that, when called, returns the selected state.
7
- */
8
- export function createSelector(inputSelectors, resultFunc) {
9
- let lastInputs = []
10
- let lastResult = null
11
-
12
- return (state) => {
13
- const nextInputs = inputSelectors.map((selector) => selector(state))
14
- const inputsChanged =
15
- lastInputs.length !== nextInputs.length ||
16
- nextInputs.some((input, index) => input !== lastInputs[index])
17
-
18
- if (!inputsChanged) {
19
- return lastResult
20
- }
21
-
22
- lastInputs = nextInputs
23
- lastResult = resultFunc(...nextInputs)
24
- return lastResult
25
- }
26
- }
@@ -1,178 +0,0 @@
1
- import { map } from "@inglorious/utils/data-structures/object.js"
2
- import { extend } from "@inglorious/utils/data-structures/objects.js"
3
- import { pipe } from "@inglorious/utils/functions/functions.js"
4
- import { produce } from "immer"
5
-
6
- /**
7
- * Creates a store to manage state and events.
8
- * @param {Object} config - Configuration options for the store.
9
- * @param {Object} [config.types] - The initial types configuration.
10
- * @param {Object} [config.entities] - The initial entities configuration.
11
- * @returns {Object} The store with methods to interact with state and events.
12
- */
13
- export function createStore({
14
- types: originalTypes,
15
- entities: originalEntities,
16
- systems = [],
17
- }) {
18
- const listeners = new Set()
19
- let incomingEvents = []
20
-
21
- let types = augmentTypes(originalTypes)
22
- let entities = augmentEntities(originalEntities)
23
-
24
- const initialState = { entities }
25
- let state = initialState
26
-
27
- return {
28
- subscribe,
29
- update,
30
- notify,
31
- dispatch, // needed for compatibility with Redux
32
- getTypes,
33
- getOriginalTypes,
34
- getState,
35
- setState,
36
- reset,
37
- }
38
-
39
- /**
40
- * Subscribes a listener to state updates.
41
- * @param {Function} listener - The listener function to call on updates.
42
- * @returns {Function} A function to unsubscribe the listener.
43
- */
44
- function subscribe(listener) {
45
- listeners.add(listener)
46
-
47
- return function unsubscribe() {
48
- listeners.delete(listener)
49
- }
50
- }
51
-
52
- /**
53
- * Updates the state based on elapsed time and processes events.
54
- * @param {number} dt - The delta time since the last update in milliseconds.
55
- * @param {Object} api - The engine's public API.
56
- */
57
- function update(dt, api) {
58
- const processedEvents = []
59
-
60
- state = produce(state, (state) => {
61
- incomingEvents.push({ type: "update", payload: dt })
62
-
63
- while (incomingEvents.length) {
64
- const event = incomingEvents.shift()
65
- processedEvents.push(event)
66
-
67
- if (event.type === "morph") {
68
- const { id, type } = event.payload
69
- originalTypes[id] = type
70
- types = augmentTypes(originalTypes)
71
- }
72
-
73
- if (event.type === "add") {
74
- const { id, ...entity } = event.payload
75
- state.entities[id] = augmentEntity(id, entity)
76
- }
77
-
78
- if (event.type === "remove") {
79
- const id = event.payload
80
- delete state.entities[id]
81
- }
82
-
83
- for (const id in state.entities) {
84
- const entity = state.entities[id]
85
- const type = types[entity.type]
86
- const handle = type[event.type]
87
- handle?.(entity, event.payload, api)
88
- }
89
-
90
- systems.forEach((system) => {
91
- const handle = system[event.type]
92
- handle?.(state, event.payload, api)
93
- })
94
- }
95
- })
96
-
97
- listeners.forEach((onUpdate) => onUpdate())
98
-
99
- return processedEvents
100
- }
101
-
102
- /**
103
- * Notifies the store of a new event.
104
- * @param {string} type - The event object type to notify.
105
- * @param {any} payload - The event object payload to notify.
106
- */
107
- function notify(type, payload) {
108
- dispatch({ type, payload })
109
- }
110
-
111
- /**
112
- * Dispatches an event to be processed in the next update cycle.
113
- * @param {Object} event - The event object.
114
- * @param {string} event.type - The type of the event.
115
- * @param {any} [event.payload] - The payload of the event.
116
- */
117
- function dispatch(event) {
118
- incomingEvents.push(event)
119
- }
120
-
121
- /**
122
- * Retrieves the augmented types configuration.
123
- * This includes composed behaviors and event handlers wrapped for immutability.
124
- * @returns {Object} The augmented types configuration.
125
- */
126
- function getTypes() {
127
- return types
128
- }
129
-
130
- /**
131
- * Retrieves the original, un-augmented types configuration.
132
- * @returns {Object} The original types configuration.
133
- */
134
- function getOriginalTypes() {
135
- return originalTypes
136
- }
137
-
138
- /**
139
- * Retrieves the current state.
140
- * @returns {Object} The current state.
141
- */
142
- function getState() {
143
- return state
144
- }
145
-
146
- function setState(newState) {
147
- state = newState
148
- }
149
-
150
- function reset() {
151
- state = initialState // Reset state to its originally computed value
152
- }
153
- }
154
-
155
- function augmentTypes(types) {
156
- return pipe(applyBehaviors)(types)
157
- }
158
-
159
- function applyBehaviors(types) {
160
- return map(types, (_, type) => {
161
- if (!Array.isArray(type)) {
162
- return type
163
- }
164
-
165
- const behaviors = type.map((fn) =>
166
- typeof fn !== "function" ? (type) => extend(type, fn) : fn,
167
- )
168
- return pipe(...behaviors)({})
169
- })
170
- }
171
-
172
- function augmentEntities(entities) {
173
- return map(entities, augmentEntity)
174
- }
175
-
176
- function augmentEntity(id, entity) {
177
- return { ...entity, id }
178
- }
@@ -1,110 +0,0 @@
1
- import { expect, test } from "vitest"
2
-
3
- import { createStore } from "./store.js"
4
-
5
- test("it should process events by mutating state inside handlers", () => {
6
- const config = {
7
- types: {
8
- kitty: {
9
- feed(entity) {
10
- entity.isFed = true
11
- },
12
- update(entity) {
13
- entity.isMeowing = true
14
- },
15
- },
16
- },
17
- entities: {
18
- kitty1: { type: "kitty" },
19
- },
20
- }
21
- const afterState = {
22
- entities: {
23
- kitty1: {
24
- id: "kitty1",
25
- type: "kitty",
26
- isFed: true,
27
- isMeowing: true,
28
- },
29
- },
30
- }
31
-
32
- const store = createStore(config)
33
- store.notify("feed")
34
- store.update(0, {})
35
-
36
- const state = store.getState()
37
- expect(state).toStrictEqual(afterState)
38
- })
39
-
40
- test("it should send an event from an entity and process it in the same update cycle", () => {
41
- const config = {
42
- types: {
43
- doggo: {
44
- update(entity, dt, api) {
45
- api.notify("bark")
46
- },
47
- },
48
- kitty: {
49
- bark(entity) {
50
- entity.position = "far"
51
- },
52
- },
53
- },
54
- entities: {
55
- doggo1: { type: "doggo" },
56
- kitty1: { type: "kitty", position: "near" },
57
- },
58
- }
59
- const afterState = {
60
- entities: {
61
- doggo1: { id: "doggo1", type: "doggo" },
62
- kitty1: { id: "kitty1", type: "kitty", position: "far" },
63
- },
64
- }
65
-
66
- const store = createStore(config)
67
- const api = { notify: store.notify }
68
- store.update(0, api)
69
-
70
- const state = store.getState()
71
- expect(state).toStrictEqual(afterState)
72
- })
73
-
74
- test("it should add an entity via an 'add' event", () => {
75
- const config = {
76
- types: {
77
- kitty: {},
78
- },
79
- entities: {},
80
- }
81
- const newEntity = { id: "kitty1", type: "kitty" }
82
-
83
- const store = createStore(config)
84
- store.notify("add", newEntity)
85
- store.update(0, {})
86
-
87
- const state = store.getState()
88
- expect(state).toStrictEqual({
89
- entities: {
90
- kitty1: { id: "kitty1", type: "kitty" },
91
- },
92
- })
93
- })
94
-
95
- test("it should remove an entity via a 'remove' event", () => {
96
- const config = {
97
- types: {},
98
- entities: {
99
- kitty1: { type: "kitty" },
100
- },
101
- }
102
- const store = createStore(config)
103
-
104
- store.notify("remove", "kitty1")
105
-
106
- store.update(0, {})
107
-
108
- const state = store.getState()
109
- expect(state.entities.kitty1).toBeUndefined()
110
- })
@@ -1,18 +0,0 @@
1
- import { snap, zero } from "@inglorious/utils/math/linear-algebra/vector.js"
2
-
3
- export function absolutePosition(render) {
4
- return (entity, ctx, { api }) => {
5
- const { position = zero() } = entity
6
- const [x, y, z] = snap(position)
7
-
8
- const game = api.getEntity("game")
9
- const [, , , screenHeight] = game.bounds
10
-
11
- ctx.save()
12
-
13
- ctx.translate(x, screenHeight - y - z)
14
- render(entity, ctx, api)
15
-
16
- ctx.restore()
17
- }
18
- }
@@ -1,13 +0,0 @@
1
- import { renderRectangle } from "./shapes/rectangle.js"
2
-
3
- export function renderCamera(entity, ctx, api) {
4
- const { devMode } = api.getEntity("game")
5
-
6
- if (devMode) {
7
- // In dev mode, we want to see the camera's viewport.
8
- // We can reuse the rectangle renderer.
9
- renderRectangle(entity, ctx)
10
- }
11
- // In non-dev mode, the camera itself is not rendered;
12
- // its properties are used by the main CanvasRenderer to transform the scene.
13
- }
@@ -1,68 +0,0 @@
1
- import { track } from "@inglorious/engine/behaviors/input/mouse.js"
2
-
3
- import { createRenderingSystem } from "./rendering-system.js"
4
-
5
- export class CanvasRenderer {
6
- _onKeyPress = null
7
- _onMouseMove = null
8
- _onClick = null
9
- _onWheel = null
10
-
11
- constructor(canvas) {
12
- this.canvas = canvas
13
- this.ctx = canvas.getContext("2d")
14
- }
15
-
16
- getSystems() {
17
- return [createRenderingSystem(this.ctx)]
18
- }
19
-
20
- init(engine) {
21
- const game = engine._api.getEntity("game")
22
- const [, , width, height] = game.bounds
23
-
24
- this.canvas.style.width = `${width}px`
25
- this.canvas.style.height = `${height}px`
26
- const dpi = window.devicePixelRatio
27
- this.canvas.width = width * dpi
28
- this.canvas.height = height * dpi
29
- this.ctx.scale(dpi, dpi)
30
-
31
- if (game.pixelated) {
32
- this.canvas.style.imageRendering = "pixelated"
33
- this.ctx.textRendering = "geometricPrecision"
34
- this.ctx.imageSmoothingEnabled = false
35
- }
36
-
37
- this._onKeyPress = (event) => {
38
- if (event.key === "p") {
39
- engine.isRunning ? engine.stop() : engine.start()
40
- }
41
- }
42
- document.addEventListener("keypress", this._onKeyPress)
43
-
44
- const { onMouseMove, onClick, onWheel } = track(this.canvas, engine._api)
45
- this._onMouseMove = onMouseMove
46
- this._onClick = onClick
47
- this._onWheel = onWheel
48
-
49
- this.canvas.addEventListener("mousemove", this._onMouseMove)
50
- this.canvas.addEventListener("click", this._onClick)
51
- this.canvas.addEventListener("wheel", this._onWheel)
52
- }
53
-
54
- destroy() {
55
- if (this._onKeyPress) {
56
- document.removeEventListener("keypress", this._onKeyPress)
57
- }
58
- if (this._onMouseMove) {
59
- this.canvas.removeEventListener("mousemove", this._onMouseMove)
60
- }
61
- if (this._onClick) {
62
- this.canvas.removeEventListener("click", this._onClick)
63
- }
64
- if (this._onWheel) {
65
- this.canvas.removeEventListener("wheel", this._onWheel)
66
- }
67
- }
68
- }
@@ -1,38 +0,0 @@
1
- /* eslint-disable no-magic-numbers */
2
-
3
- import { renderCircle } from "./shapes/circle.js"
4
-
5
- export function renderCharacter(entity, ctx) {
6
- const { size = 24, orientation = 0 } = entity
7
-
8
- const radius = size * 0.5
9
-
10
- ctx.save()
11
-
12
- ctx.rotate(-orientation)
13
- ctx.translate(radius - 1, 0)
14
-
15
- ctx.fillStyle = "black"
16
-
17
- ctx.beginPath()
18
- ctx.moveTo(0, 6)
19
- ctx.lineTo(0, -6)
20
- ctx.lineTo(6, 0)
21
- ctx.fill()
22
- ctx.closePath()
23
- ctx.restore()
24
-
25
- ctx.save()
26
-
27
- renderCircle(
28
- {
29
- ...entity,
30
- radius,
31
- position: undefined,
32
- backgroundColor: "lightgrey",
33
- },
34
- ctx,
35
- )
36
-
37
- ctx.restore()
38
- }
@@ -1,25 +0,0 @@
1
- /* eslint-disable no-magic-numbers */
2
-
3
- export function renderButton(entity, ctx) {
4
- const { size, color = "black", thickness = 1 } = entity
5
- const [width = 100, height = 50] = size
6
-
7
- ctx.save()
8
-
9
- ctx.lineWidth = thickness
10
- ctx.strokeStyle = color
11
-
12
- if (entity.state === "pressed") {
13
- ctx.fillStyle = "white"
14
- } else {
15
- ctx.fillStyle = "black"
16
- }
17
-
18
- ctx.beginPath()
19
- ctx.fillRect(-width / 2, -height / 2, width, height)
20
- ctx.strokeRect(-width / 2, -height / 2, width, height)
21
- ctx.stroke()
22
- ctx.closePath()
23
-
24
- ctx.restore()
25
- }
@@ -1,18 +0,0 @@
1
- const DEFAULT_PADDING = 10
2
- const ONE_SECOND = 1
3
-
4
- export function renderFps(entity, ctx) {
5
- const { accuracy, size, value } = entity.dt
6
-
7
- ctx.save()
8
-
9
- ctx.font = `${size}px sans serif`
10
- ctx.fillStyle = "black"
11
- ctx.fillText(
12
- (ONE_SECOND / value).toFixed(accuracy),
13
- DEFAULT_PADDING,
14
- DEFAULT_PADDING + size,
15
- )
16
-
17
- ctx.restore()
18
- }
@@ -1,51 +0,0 @@
1
- import { renderRectangle } from "@inglorious/renderers/canvas/shapes/rectangle.js"
2
- import { max } from "@inglorious/utils/data-structures/array.js"
3
-
4
- const HALF = 2
5
- const NO_Y = 0
6
- const MAX_HUE = 255
7
-
8
- export function renderHitmask(entity, ctx) {
9
- const { tileSize, columns, heights } = entity
10
-
11
- const [tileWidth, tileHeight] = tileSize
12
- const halfTileWidth = tileWidth / HALF
13
- const halfTileHeight = tileHeight / HALF
14
-
15
- const dRows = Math.ceil(heights.length / columns)
16
- const tilemapWidth = columns * tileWidth
17
- const tilemapHeight = dRows * tileHeight
18
-
19
- const offsetX = -tilemapWidth / HALF
20
- const offsetY = tilemapHeight / HALF
21
-
22
- const minH = 0
23
- const maxH = max(heights)
24
-
25
- ctx.save()
26
-
27
- ctx.translate(offsetX, offsetY)
28
-
29
- heights.forEach((h, index) => {
30
- const dx = (index % columns) * tileWidth
31
- const dy = Math.floor(index / columns) * tileHeight - tilemapHeight
32
-
33
- const normalizedH = (h - minH) / (maxH - minH)
34
- const hue = MAX_HUE - normalizedH * MAX_HUE
35
-
36
- ctx.save()
37
-
38
- const entity = {
39
- offset: [dx + halfTileWidth, NO_Y, -dy - halfTileHeight],
40
- size: [tileWidth, NO_Y, tileHeight],
41
- color: "transparent",
42
- backgroundColor: `hsla(${hue}, 100%, 50%, 0.2)`,
43
- }
44
-
45
- renderRectangle(entity, ctx)
46
-
47
- ctx.restore()
48
- })
49
-
50
- ctx.restore()
51
- }