@inglorious/engine 0.1.0 → 0.2.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 (292) hide show
  1. package/README.md +39 -36
  2. package/package.json +20 -33
  3. package/src/engine/ai/movement/dynamic/align.js +63 -63
  4. package/src/engine/ai/movement/dynamic/arrive.js +42 -43
  5. package/src/engine/ai/movement/dynamic/evade.js +38 -38
  6. package/src/engine/ai/movement/dynamic/face.js +19 -20
  7. package/src/engine/ai/movement/dynamic/flee.js +45 -45
  8. package/src/engine/ai/movement/dynamic/look-where-youre-going.js +16 -17
  9. package/src/engine/ai/movement/dynamic/match-velocity.js +51 -50
  10. package/src/engine/ai/movement/dynamic/pursue.js +38 -38
  11. package/src/engine/ai/movement/dynamic/seek.js +44 -44
  12. package/src/engine/ai/movement/dynamic/wander.js +31 -32
  13. package/src/engine/ai/movement/kinematic/align.js +37 -37
  14. package/src/engine/ai/movement/kinematic/arrive.js +42 -42
  15. package/src/engine/ai/movement/kinematic/face.js +19 -20
  16. package/src/engine/ai/movement/kinematic/flee.js +26 -26
  17. package/src/engine/ai/movement/kinematic/seek.js +26 -26
  18. package/src/engine/ai/movement/kinematic/seek.test.js +42 -42
  19. package/src/engine/ai/movement/kinematic/wander-as-seek.js +31 -31
  20. package/src/engine/ai/movement/kinematic/wander.js +27 -27
  21. package/src/engine/animation/sprite.js +101 -0
  22. package/src/engine/animation/ticker.js +38 -0
  23. package/src/engine/behaviors/camera.js +68 -0
  24. package/src/engine/behaviors/controls/dynamic/modern.js +76 -0
  25. package/src/engine/behaviors/controls/dynamic/shooter.js +84 -0
  26. package/src/engine/behaviors/controls/dynamic/tank.js +69 -0
  27. package/src/engine/behaviors/controls/event-handlers.js +17 -0
  28. package/src/engine/behaviors/controls/kinematic/modern.js +76 -0
  29. package/src/engine/behaviors/controls/kinematic/shooter.js +82 -0
  30. package/src/engine/behaviors/controls/kinematic/tank.js +67 -0
  31. package/src/engine/behaviors/debug/collision.js +35 -0
  32. package/src/engine/behaviors/fps.js +29 -0
  33. package/src/engine/behaviors/fsm.js +33 -0
  34. package/src/{game/decorators → engine/behaviors}/fsm.test.js +49 -56
  35. package/src/engine/behaviors/game.js +15 -0
  36. package/src/engine/behaviors/input/controls.js +37 -0
  37. package/src/engine/behaviors/input/gamepad.js +114 -0
  38. package/src/engine/behaviors/input/input.js +48 -0
  39. package/src/engine/behaviors/input/keyboard.js +64 -0
  40. package/src/engine/behaviors/input/mouse.js +91 -0
  41. package/src/engine/behaviors/physics/bouncy.js +25 -0
  42. package/src/engine/behaviors/physics/clamped.js +36 -0
  43. package/src/{game/decorators/collisions.js → engine/behaviors/physics/collidable.js} +20 -24
  44. package/src/engine/behaviors/physics/jumpable.js +145 -0
  45. package/src/engine/behaviors/ui/button.js +17 -0
  46. package/src/engine/collision/detection.js +110 -115
  47. package/src/engine/core/api.js +34 -0
  48. package/src/engine/core/dev-tools.js +135 -0
  49. package/src/engine/core/engine.js +119 -0
  50. package/src/engine/core/loop.js +15 -0
  51. package/src/engine/{loop → core/loops}/animation-frame.js +25 -26
  52. package/src/engine/{loop → core/loops}/elapsed.js +22 -23
  53. package/src/engine/{loop → core/loops}/fixed.js +27 -28
  54. package/src/engine/{loop → core/loops}/flash.js +13 -14
  55. package/src/engine/{loop → core/loops}/lag.js +26 -27
  56. package/src/engine/core/select.js +26 -0
  57. package/src/engine/core/store.js +178 -0
  58. package/src/engine/core/store.test.js +110 -0
  59. package/src/engine/movement/dynamic/modern.js +21 -24
  60. package/src/engine/movement/dynamic/tank.js +43 -43
  61. package/src/engine/movement/kinematic/modern.js +16 -16
  62. package/src/engine/movement/kinematic/modern.test.js +27 -27
  63. package/src/engine/movement/kinematic/tank.js +27 -27
  64. package/src/engine/physics/bounds.js +138 -0
  65. package/src/engine/physics/position.js +43 -0
  66. package/src/engine/physics/position.test.js +80 -0
  67. package/src/engine/systems/sprite-animation.js +27 -0
  68. package/src/main.js +10 -5
  69. package/src/renderers/canvas/absolute-position.js +18 -0
  70. package/src/renderers/canvas/camera.js +13 -0
  71. package/src/renderers/canvas/canvas-renderer.js +68 -0
  72. package/src/{ui → renderers}/canvas/character.js +38 -35
  73. package/src/{ui → renderers}/canvas/form/button.js +25 -25
  74. package/src/{ui → renderers}/canvas/fps.js +18 -18
  75. package/src/renderers/canvas/image/hitmask.js +51 -0
  76. package/src/{ui → renderers}/canvas/image/image.js +34 -37
  77. package/src/{ui → renderers}/canvas/image/sprite.js +49 -49
  78. package/src/{ui → renderers}/canvas/image/tilemap.js +66 -64
  79. package/src/{ui → renderers}/canvas/mouse.js +37 -37
  80. package/src/renderers/canvas/rendering-system.js +79 -0
  81. package/src/{ui → renderers}/canvas/shapes/circle.js +29 -31
  82. package/src/{ui → renderers}/canvas/shapes/rectangle.js +27 -31
  83. package/src/renderers/react/game/character/index.jsx +20 -0
  84. package/src/{ui → renderers}/react/game/cursor/index.jsx +20 -20
  85. package/src/{ui → renderers}/react/game/form/fields/field/index.jsx +56 -56
  86. package/src/{ui → renderers}/react/game/form/fields/index.jsx +12 -12
  87. package/src/{ui → renderers}/react/game/form/index.jsx +22 -22
  88. package/src/{ui → renderers}/react/game/fps/index.jsx +16 -16
  89. package/src/{ui → renderers}/react/game/game.jsx +72 -71
  90. package/src/{ui → renderers}/react/game/index.jsx +29 -29
  91. package/src/{ui → renderers}/react/game/platform/index.jsx +30 -30
  92. package/src/{ui → renderers}/react/game/scene/index.jsx +27 -25
  93. package/src/{ui → renderers}/react/game/sprite/index.jsx +60 -58
  94. package/src/{ui → renderers}/react/game/stats/index.jsx +22 -22
  95. package/src/{ui → renderers}/react/hocs/with-absolute-position/index.jsx +20 -20
  96. package/src/{ui → renderers}/react/index.jsx +9 -9
  97. package/src/utils/algorithms/decision-tree.js +24 -24
  98. package/src/utils/algorithms/decision-tree.test.js +153 -102
  99. package/src/utils/algorithms/path-finding.js +155 -155
  100. package/src/utils/algorithms/path-finding.test.js +151 -151
  101. package/src/utils/data-structures/array.js +83 -83
  102. package/src/utils/data-structures/array.test.js +173 -173
  103. package/src/utils/data-structures/board.js +159 -159
  104. package/src/utils/data-structures/board.test.js +242 -242
  105. package/src/utils/data-structures/boolean.js +9 -9
  106. package/src/utils/data-structures/heap.js +164 -164
  107. package/src/utils/data-structures/heap.test.js +103 -103
  108. package/src/utils/data-structures/object.js +138 -102
  109. package/src/utils/data-structures/object.test.js +218 -121
  110. package/src/utils/data-structures/objects.js +66 -48
  111. package/src/utils/data-structures/objects.test.js +99 -99
  112. package/src/utils/data-structures/tree.js +36 -36
  113. package/src/utils/data-structures/tree.test.js +33 -33
  114. package/src/utils/functions/functions.js +19 -19
  115. package/src/utils/functions/functions.test.js +23 -23
  116. package/src/utils/math/geometry/circle.js +70 -117
  117. package/src/utils/math/geometry/circle.test.js +97 -97
  118. package/src/utils/math/geometry/hitmask.js +70 -39
  119. package/src/utils/math/geometry/hitmask.test.js +155 -84
  120. package/src/utils/math/geometry/line.js +35 -35
  121. package/src/utils/math/geometry/line.test.js +49 -49
  122. package/src/utils/math/geometry/point.js +78 -71
  123. package/src/utils/math/geometry/point.test.js +81 -81
  124. package/src/utils/math/geometry/rectangle.js +76 -45
  125. package/src/utils/math/geometry/rectangle.test.js +42 -42
  126. package/src/utils/math/geometry/segment.js +80 -80
  127. package/src/utils/math/geometry/segment.test.js +183 -183
  128. package/src/utils/math/geometry/triangle.js +15 -15
  129. package/src/utils/math/geometry/triangle.test.js +11 -11
  130. package/src/utils/math/linear-algebra/2d.js +28 -28
  131. package/src/utils/math/linear-algebra/2d.test.js +17 -17
  132. package/src/utils/math/linear-algebra/quaternion.js +22 -22
  133. package/src/utils/math/linear-algebra/quaternion.test.js +25 -25
  134. package/src/utils/math/linear-algebra/quaternions.js +20 -20
  135. package/src/utils/math/linear-algebra/quaternions.test.js +29 -29
  136. package/src/utils/math/linear-algebra/vector.js +327 -302
  137. package/src/utils/math/linear-algebra/vector.test.js +265 -257
  138. package/src/utils/math/linear-algebra/vectors.js +122 -122
  139. package/src/utils/math/linear-algebra/vectors.test.js +65 -65
  140. package/src/utils/math/linear-interpolation.js +9 -0
  141. package/src/utils/math/numbers.js +90 -90
  142. package/src/utils/math/numbers.test.js +137 -137
  143. package/src/utils/math/rng.js +44 -44
  144. package/src/utils/math/rng.test.js +39 -39
  145. package/src/utils/math/statistics.js +43 -43
  146. package/src/utils/math/statistics.test.js +47 -47
  147. package/src/utils/math/trigonometry.js +89 -89
  148. package/src/utils/math/trigonometry.test.js +52 -52
  149. package/src/utils/physics/acceleration.js +61 -63
  150. package/src/utils/physics/friction.js +28 -30
  151. package/src/utils/physics/friction.test.js +42 -44
  152. package/src/utils/physics/gravity.js +69 -71
  153. package/src/utils/physics/gravity.test.js +77 -80
  154. package/src/utils/physics/jump.js +31 -41
  155. package/src/utils/physics/velocity.js +36 -38
  156. package/src/docs/ai/movement/dynamic/align.js +0 -131
  157. package/src/docs/ai/movement/dynamic/arrive.js +0 -88
  158. package/src/docs/ai/movement/dynamic/dynamic.mdx +0 -99
  159. package/src/docs/ai/movement/dynamic/dynamic.stories.js +0 -58
  160. package/src/docs/ai/movement/dynamic/evade.js +0 -72
  161. package/src/docs/ai/movement/dynamic/face.js +0 -90
  162. package/src/docs/ai/movement/dynamic/flee.js +0 -38
  163. package/src/docs/ai/movement/dynamic/look-where-youre-going.js +0 -114
  164. package/src/docs/ai/movement/dynamic/match-velocity.js +0 -92
  165. package/src/docs/ai/movement/dynamic/pursue.js +0 -72
  166. package/src/docs/ai/movement/dynamic/seek.js +0 -37
  167. package/src/docs/ai/movement/dynamic/wander.js +0 -71
  168. package/src/docs/ai/movement/kinematic/align.js +0 -122
  169. package/src/docs/ai/movement/kinematic/arrive.js +0 -78
  170. package/src/docs/ai/movement/kinematic/face.js +0 -82
  171. package/src/docs/ai/movement/kinematic/flee.js +0 -36
  172. package/src/docs/ai/movement/kinematic/kinematic.mdx +0 -67
  173. package/src/docs/ai/movement/kinematic/kinematic.stories.js +0 -42
  174. package/src/docs/ai/movement/kinematic/seek.js +0 -34
  175. package/src/docs/ai/movement/kinematic/wander-as-seek.js +0 -62
  176. package/src/docs/ai/movement/kinematic/wander.js +0 -28
  177. package/src/docs/bounds.js +0 -7
  178. package/src/docs/code-reuse.js +0 -35
  179. package/src/docs/collision/circles.js +0 -58
  180. package/src/docs/collision/collision.mdx +0 -27
  181. package/src/docs/collision/collision.stories.js +0 -22
  182. package/src/docs/collision/platform.js +0 -76
  183. package/src/docs/collision/tilemap.js +0 -181
  184. package/src/docs/empty.js +0 -1
  185. package/src/docs/engine.mdx +0 -81
  186. package/src/docs/engine.stories.js +0 -37
  187. package/src/docs/event-handlers.js +0 -68
  188. package/src/docs/framerate.js +0 -37
  189. package/src/docs/game.jsx +0 -15
  190. package/src/docs/image/image.js +0 -19
  191. package/src/docs/image/image.stories.js +0 -22
  192. package/src/docs/image/sprite.js +0 -39
  193. package/src/docs/image/tilemap.js +0 -84
  194. package/src/docs/input/controls.js +0 -67
  195. package/src/docs/input/gamepad.js +0 -67
  196. package/src/docs/input/input.mdx +0 -55
  197. package/src/docs/input/input.stories.js +0 -27
  198. package/src/docs/input/keyboard.js +0 -58
  199. package/src/docs/input/mouse.js +0 -32
  200. package/src/docs/instances.js +0 -49
  201. package/src/docs/player/dynamic/double-jump.js +0 -90
  202. package/src/docs/player/dynamic/dynamic.stories.js +0 -32
  203. package/src/docs/player/dynamic/jump.js +0 -83
  204. package/src/docs/player/dynamic/modern-controls.js +0 -57
  205. package/src/docs/player/dynamic/shooter-controls.js +0 -51
  206. package/src/docs/player/dynamic/tank-controls.js +0 -44
  207. package/src/docs/player/kinematic/double-jump.js +0 -90
  208. package/src/docs/player/kinematic/jump.js +0 -82
  209. package/src/docs/player/kinematic/kinematic.stories.js +0 -32
  210. package/src/docs/player/kinematic/modern-controls.js +0 -56
  211. package/src/docs/player/kinematic/shooter-controls.js +0 -48
  212. package/src/docs/player/kinematic/tank-controls.js +0 -42
  213. package/src/docs/quick-start/first-game.js +0 -49
  214. package/src/docs/quick-start/hello-world.js +0 -1
  215. package/src/docs/quick-start.mdx +0 -127
  216. package/src/docs/quick-start.stories.js +0 -17
  217. package/src/docs/recipes/add-and-remove.js +0 -71
  218. package/src/docs/recipes/add-instance.js +0 -42
  219. package/src/docs/recipes/decision-tree.js +0 -169
  220. package/src/docs/recipes/random-instances.js +0 -25
  221. package/src/docs/recipes/recipes.mdx +0 -81
  222. package/src/docs/recipes/recipes.stories.js +0 -37
  223. package/src/docs/recipes/remove-instance.js +0 -52
  224. package/src/docs/recipes/states.js +0 -64
  225. package/src/docs/ui/button.js +0 -28
  226. package/src/docs/ui/form.stories.js +0 -55
  227. package/src/docs/ui-chooser.jsx +0 -6
  228. package/src/docs/utils/data-structures/object.mdx +0 -47
  229. package/src/docs/utils/data-structures/objects.mdx +0 -30
  230. package/src/docs/utils/functions/functions.mdx +0 -34
  231. package/src/docs/utils/math/geometry/circle.mdx +0 -55
  232. package/src/docs/utils/math/geometry/point.mdx +0 -38
  233. package/src/docs/utils/math/geometry/rectangle.mdx +0 -24
  234. package/src/docs/utils/math/geometry/segment.mdx +0 -55
  235. package/src/docs/utils/math/geometry/triangle.mdx +0 -22
  236. package/src/docs/utils/math/linear-algebra/2d.mdx +0 -22
  237. package/src/docs/utils/math/linear-algebra/quaternion.mdx +0 -21
  238. package/src/docs/utils/math/linear-algebra/quaternions.mdx +0 -22
  239. package/src/docs/utils/math/linear-algebra/vector.mdx +0 -177
  240. package/src/docs/utils/math/linear-algebra/vectors.mdx +0 -58
  241. package/src/docs/utils/math/numbers.mdx +0 -76
  242. package/src/docs/utils/math/random.mdx +0 -35
  243. package/src/docs/utils/math/statistics.mdx +0 -38
  244. package/src/docs/utils/math/trigonometry.mdx +0 -85
  245. package/src/docs/utils/physics/friction.mdx +0 -20
  246. package/src/docs/utils/physics/gravity.mdx +0 -28
  247. package/src/engine/loop.js +0 -15
  248. package/src/engine/store.js +0 -174
  249. package/src/engine/store.test.js +0 -256
  250. package/src/engine.js +0 -74
  251. package/src/game/animation.js +0 -26
  252. package/src/game/bounds.js +0 -66
  253. package/src/game/decorators/character.js +0 -5
  254. package/src/game/decorators/clamp-to-bounds.js +0 -15
  255. package/src/game/decorators/controls/dynamic/modern.js +0 -48
  256. package/src/game/decorators/controls/dynamic/shooter.js +0 -47
  257. package/src/game/decorators/controls/dynamic/tank.js +0 -55
  258. package/src/game/decorators/controls/kinematic/modern.js +0 -49
  259. package/src/game/decorators/controls/kinematic/shooter.js +0 -45
  260. package/src/game/decorators/controls/kinematic/tank.js +0 -52
  261. package/src/game/decorators/debug/collisions.js +0 -32
  262. package/src/game/decorators/double-jump.js +0 -70
  263. package/src/game/decorators/fps.js +0 -30
  264. package/src/game/decorators/fsm.js +0 -27
  265. package/src/game/decorators/game.js +0 -11
  266. package/src/game/decorators/image/image.js +0 -5
  267. package/src/game/decorators/image/sprite.js +0 -5
  268. package/src/game/decorators/image/tilemap.js +0 -5
  269. package/src/game/decorators/input/controls.js +0 -27
  270. package/src/game/decorators/input/gamepad.js +0 -74
  271. package/src/game/decorators/input/input.js +0 -41
  272. package/src/game/decorators/input/keyboard.js +0 -49
  273. package/src/game/decorators/input/mouse.js +0 -65
  274. package/src/game/decorators/jump.js +0 -72
  275. package/src/game/decorators/platform.js +0 -5
  276. package/src/game/decorators/ui/button.js +0 -21
  277. package/src/game/sprite.js +0 -119
  278. package/src/ui/canvas/absolute-position.js +0 -17
  279. package/src/ui/canvas/image/hitmask.js +0 -37
  280. package/src/ui/canvas.js +0 -81
  281. package/src/ui/react/game/character/index.jsx +0 -30
  282. package/src/utils/math/geometry/platform.js +0 -42
  283. package/src/utils/math/geometry/platform.test.js +0 -133
  284. /package/src/{ui → renderers}/react/game/character/character.module.scss +0 -0
  285. /package/src/{ui → renderers}/react/game/cursor/cursor.module.scss +0 -0
  286. /package/src/{ui → renderers}/react/game/form/fields/field/field.module.scss +0 -0
  287. /package/src/{ui → renderers}/react/game/form/fields/fields.module.scss +0 -0
  288. /package/src/{ui → renderers}/react/game/form/form.module.scss +0 -0
  289. /package/src/{ui → renderers}/react/game/platform/platform.module.scss +0 -0
  290. /package/src/{ui → renderers}/react/game/scene/scene.module.scss +0 -0
  291. /package/src/{ui → renderers}/react/game/sprite/sprite.module.css +0 -0
  292. /package/src/{ui → renderers}/react/hocs/with-absolute-position/with-absolute-position.module.scss +0 -0
@@ -0,0 +1,178 @@
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
+ }
@@ -0,0 +1,110 @@
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,24 +1,21 @@
1
- import {
2
- angle,
3
- magnitude,
4
- } from "@inglorious/utils/math/linear-algebra/vector.js"
5
- import { applyAcceleration } from "@inglorious/utils/physics/acceleration.js"
6
-
7
- const DEFAULT_ORIENTATION = 0
8
-
9
- const ORIENTATION_CHANGE_THRESHOLD = 4
10
-
11
- export default function modernMove(instance, options) {
12
- const { acceleration, velocity, position } = applyAcceleration(
13
- instance,
14
- options,
15
- )
16
-
17
- let orientation = instance.orientation ?? DEFAULT_ORIENTATION
18
- orientation =
19
- magnitude(velocity) > ORIENTATION_CHANGE_THRESHOLD
20
- ? angle(velocity)
21
- : orientation
22
-
23
- return { acceleration, velocity, position, orientation }
24
- }
1
+ import {
2
+ angle,
3
+ magnitude,
4
+ } from "@inglorious/utils/math/linear-algebra/vector.js"
5
+ import { applyAcceleration } from "@inglorious/utils/physics/acceleration.js"
6
+
7
+ const DEFAULT_ORIENTATION = 0
8
+
9
+ const ORIENTATION_CHANGE_THRESHOLD = 4
10
+
11
+ export function modernMove(entity, dt) {
12
+ const { acceleration, velocity, position } = applyAcceleration(entity, dt)
13
+
14
+ let orientation = entity.orientation ?? DEFAULT_ORIENTATION
15
+ orientation =
16
+ magnitude(velocity) > ORIENTATION_CHANGE_THRESHOLD
17
+ ? angle(velocity)
18
+ : orientation
19
+
20
+ return { acceleration, velocity, position, orientation }
21
+ }
@@ -1,43 +1,43 @@
1
- import {
2
- clamp,
3
- multiply,
4
- rotate,
5
- zero,
6
- } from "@inglorious/utils/math/linear-algebra/vector.js"
7
- import { sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
8
- import { toRange } from "@inglorious/utils/math/trigonometry.js"
9
- import { applyFriction } from "@inglorious/utils/physics/friction.js"
10
-
11
- const DEFAULT_MAX_ACCELERATION = 0
12
- const DEFAULT_MAX_SPEED = 0
13
- const DEFAULT_FRICTION = 0
14
-
15
- const DEFAULT_ORIENTATION = 0
16
-
17
- const HALF_ACCELERATION = 0.5
18
-
19
- export default function tankMove(instance, { dt }) {
20
- const maxAcceleration = instance.maxAcceleration ?? DEFAULT_MAX_ACCELERATION
21
- const maxSpeed = instance.maxSpeed ?? DEFAULT_MAX_SPEED
22
- const friction = instance.friction ?? DEFAULT_FRICTION
23
-
24
- let orientation = instance.orientation ?? DEFAULT_ORIENTATION
25
- orientation = toRange(orientation)
26
-
27
- let acceleration = instance.acceleration ?? zero()
28
- acceleration = rotate(acceleration, orientation)
29
- acceleration = clamp(acceleration, -maxAcceleration, maxAcceleration)
30
-
31
- let velocity = instance.velocity ?? zero()
32
- velocity = sum(velocity, multiply(acceleration, dt))
33
- velocity = clamp(velocity, -maxSpeed, maxSpeed)
34
- velocity = applyFriction({ velocity, friction }, { dt })
35
-
36
- const position = sum(
37
- instance.position,
38
- multiply(velocity, dt),
39
- multiply(acceleration, HALF_ACCELERATION * dt * dt),
40
- )
41
-
42
- return { velocity, position, orientation }
43
- }
1
+ import {
2
+ clamp,
3
+ multiply,
4
+ rotate,
5
+ zero,
6
+ } from "@inglorious/utils/math/linear-algebra/vector.js"
7
+ import { sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
8
+ import { toRange } from "@inglorious/utils/math/trigonometry.js"
9
+ import { applyFriction } from "@inglorious/utils/physics/friction.js"
10
+
11
+ const DEFAULT_MAX_ACCELERATION = 0
12
+ const DEFAULT_MAX_SPEED = 0
13
+ const DEFAULT_FRICTION = 0
14
+
15
+ const DEFAULT_ORIENTATION = 0
16
+
17
+ const HALF_ACCELERATION = 0.5
18
+
19
+ export function tankMove(entity, dt) {
20
+ const maxAcceleration = entity.maxAcceleration ?? DEFAULT_MAX_ACCELERATION
21
+ const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
22
+ const friction = entity.friction ?? DEFAULT_FRICTION
23
+
24
+ let orientation = entity.orientation ?? DEFAULT_ORIENTATION
25
+ orientation = toRange(orientation)
26
+
27
+ let acceleration = entity.acceleration ?? zero()
28
+ acceleration = rotate(acceleration, orientation)
29
+ acceleration = clamp(acceleration, -maxAcceleration, maxAcceleration)
30
+
31
+ let velocity = entity.velocity ?? zero()
32
+ velocity = sum(velocity, multiply(acceleration, dt))
33
+ velocity = clamp(velocity, -maxSpeed, maxSpeed)
34
+ velocity = applyFriction({ velocity, friction }, dt)
35
+
36
+ const position = sum(
37
+ entity.position,
38
+ multiply(velocity, dt),
39
+ multiply(acceleration, HALF_ACCELERATION * dt * dt),
40
+ )
41
+
42
+ return { velocity, position, orientation }
43
+ }
@@ -1,16 +1,16 @@
1
- import {
2
- angle,
3
- magnitude,
4
- } from "@inglorious/utils/math/linear-algebra/vector.js"
5
- import { applyVelocity } from "@inglorious/utils/physics/velocity.js"
6
-
7
- const DEFAULT_ORIENTATION = 0
8
-
9
- export default function modernMove(instance, options) {
10
- const { velocity, position } = applyVelocity(instance, options)
11
-
12
- let orientation = instance.orientation ?? DEFAULT_ORIENTATION
13
- orientation = magnitude(velocity) ? angle(velocity) : orientation
14
-
15
- return { velocity, position, orientation }
16
- }
1
+ import {
2
+ angle,
3
+ magnitude,
4
+ } from "@inglorious/utils/math/linear-algebra/vector.js"
5
+ import { applyVelocity } from "@inglorious/utils/physics/velocity.js"
6
+
7
+ const DEFAULT_ORIENTATION = 0
8
+
9
+ export function modernMove(entity, dt) {
10
+ const { velocity, position } = applyVelocity(entity, dt)
11
+
12
+ let orientation = entity.orientation ?? DEFAULT_ORIENTATION
13
+ orientation = magnitude(velocity) ? angle(velocity) : orientation
14
+
15
+ return { velocity, position, orientation }
16
+ }
@@ -1,27 +1,27 @@
1
- import { expect, test } from "vitest"
2
-
3
- import modernMove from "./modern.js"
4
-
5
- test("it should move following its velocity", () => {
6
- const instance = { maxSpeed: 1, velocity: [1, 0, 0], position: [0, 0, 0] }
7
- const options = { dt: 1 }
8
- const expectedResult = {
9
- velocity: [1, 0, 0],
10
- position: [1, 0, 0],
11
- orientation: 0,
12
- }
13
-
14
- expect(modernMove(instance, options)).toStrictEqual(expectedResult)
15
- })
16
-
17
- test("it should limit the velocity to the max speed", () => {
18
- const instance = { maxSpeed: 1, velocity: [10, 0, 0], position: [0, 0, 0] }
19
- const options = { dt: 1 }
20
- const expectedResult = {
21
- velocity: [1, 0, 0],
22
- position: [1, 0, 0],
23
- orientation: 0,
24
- }
25
-
26
- expect(modernMove(instance, options)).toStrictEqual(expectedResult)
27
- })
1
+ import { expect, test } from "vitest"
2
+
3
+ import { modernMove } from "./modern.js"
4
+
5
+ test("it should move following its velocity", () => {
6
+ const entity = { maxSpeed: 1, velocity: [1, 0, 0], position: [0, 0, 0] }
7
+ const dt = 1
8
+ const expectedResult = {
9
+ velocity: [1, 0, 0],
10
+ position: [1, 0, 0],
11
+ orientation: 0,
12
+ }
13
+
14
+ expect(modernMove(entity, dt)).toStrictEqual(expectedResult)
15
+ })
16
+
17
+ test("it should limit the velocity to the max speed", () => {
18
+ const entity = { maxSpeed: 1, velocity: [10, 0, 0], position: [0, 0, 0] }
19
+ const dt = 1
20
+ const expectedResult = {
21
+ velocity: [1, 0, 0],
22
+ position: [1, 0, 0],
23
+ orientation: 0,
24
+ }
25
+
26
+ expect(modernMove(entity, dt)).toStrictEqual(expectedResult)
27
+ })
@@ -1,27 +1,27 @@
1
- import {
2
- clamp,
3
- multiply,
4
- rotate,
5
- zero,
6
- } from "@inglorious/utils/math/linear-algebra/vector.js"
7
- import { sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
8
- import { toRange } from "@inglorious/utils/math/trigonometry.js"
9
-
10
- const DEFAULT_MAX_SPEED = 0
11
-
12
- const DEFAULT_ORIENTATION = 0
13
-
14
- export default function tankMove(instance, { dt }) {
15
- const maxSpeed = instance.maxSpeed ?? DEFAULT_MAX_SPEED
16
-
17
- let orientation = instance.orientation ?? DEFAULT_ORIENTATION
18
- orientation = toRange(orientation)
19
-
20
- let velocity = instance.velocity ?? zero()
21
- velocity = rotate(velocity, orientation)
22
- velocity = clamp(velocity, -maxSpeed, maxSpeed)
23
-
24
- const position = sum(instance.position, multiply(velocity, dt))
25
-
26
- return { velocity, position, orientation }
27
- }
1
+ import {
2
+ clamp,
3
+ multiply,
4
+ rotate,
5
+ zero,
6
+ } from "@inglorious/utils/math/linear-algebra/vector.js"
7
+ import { sum } from "@inglorious/utils/math/linear-algebra/vectors.js"
8
+ import { toRange } from "@inglorious/utils/math/trigonometry.js"
9
+
10
+ const DEFAULT_MAX_SPEED = 0
11
+
12
+ const DEFAULT_ORIENTATION = 0
13
+
14
+ export function tankMove(entity, dt) {
15
+ const maxSpeed = entity.maxSpeed ?? DEFAULT_MAX_SPEED
16
+
17
+ let orientation = entity.orientation ?? DEFAULT_ORIENTATION
18
+ orientation = toRange(orientation)
19
+
20
+ let velocity = entity.velocity ?? zero()
21
+ velocity = rotate(velocity, orientation)
22
+ velocity = clamp(velocity, -maxSpeed, maxSpeed)
23
+
24
+ const position = sum(entity.position, multiply(velocity, dt))
25
+
26
+ return { velocity, position, orientation }
27
+ }