@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,110 +1,110 @@
1
- import { filter } from "@inglorious/utils/data-structures/object.js"
2
- import * as circle from "@inglorious/utils/math/geometry/circle.js"
3
- import * as hitmask from "@inglorious/utils/math/geometry/hitmask.js"
4
- import * as line from "@inglorious/utils/math/geometry/line.js"
5
- import * as point from "@inglorious/utils/math/geometry/point.js"
6
- import * as rectangle from "@inglorious/utils/math/geometry/rectangle.js"
7
- import * as segment from "@inglorious/utils/math/geometry/segment.js"
8
- import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
9
- import { add } from "@inglorious/utils/math/linear-algebra/vectors.js"
10
-
11
- const Z = 2 // Z-axis index.
12
-
13
- const Shape = {
14
- circle,
15
- line,
16
- point,
17
- rectangle,
18
- segment,
19
- hitmask,
20
- }
21
-
22
- /**
23
- * Finds the first collision between a point and a list of entities.
24
- *
25
- * @param {Point} entity - The point to check for collisions.
26
- * @param {Options} options - Options for collision detection.
27
- * @returns {Entity | undefined} The first entity that collides with the point, or undefined if none are found.
28
- */
29
- export function findCollision(entity, entities, collisionGroup = "hitbox") {
30
- const otherEntities = filter(
31
- entities,
32
- (id, { collisions }) => id !== entity.id && collisions?.[collisionGroup],
33
- )
34
-
35
- return Object.values(otherEntities)
36
- .toSorted((a, b) => a.position[Z] - b.position[Z])
37
- .find((target) => collidesWith(entity, target, collisionGroup))
38
- }
39
-
40
- export function collidesWith(entity, target, collisionGroup = "hitbox") {
41
- const entityShape = getCollisionShape(entity, collisionGroup)
42
- const targetShape = getCollisionShape(target, collisionGroup)
43
-
44
- if (!entityShape || !targetShape) {
45
- return false
46
- }
47
-
48
- return shapeCollidesWith(entityShape, targetShape)
49
- }
50
-
51
- function shapeCollidesWith(entity, target) {
52
- const shapeFns = Shape[entity.shape]
53
-
54
- switch (target.shape) {
55
- case "circle":
56
- return shapeFns.intersectsCircle(entity, target)
57
-
58
- case "line":
59
- return shapeFns.intersectsLine(entity, target)
60
-
61
- case "point":
62
- return shapeFns.intersectsPoint(entity, target)
63
-
64
- case "rectangle":
65
- return shapeFns.intersectsRectangle(entity, target)
66
-
67
- case "segment":
68
- return shapeFns.intersectsSegment(entity, target)
69
- }
70
- }
71
-
72
- export function findCollisions(entity, target, collisionGroup = "hitbox") {
73
- const entityShape = getCollisionShape(entity, collisionGroup)
74
- const targetShape = getCollisionShape(target, collisionGroup)
75
-
76
- if (!entityShape || !targetShape) {
77
- return false
78
- }
79
-
80
- const shapeFns = Shape[entityShape.shape]
81
- if (!shapeFns || !shapeFns.findCollisions) {
82
- return false
83
- }
84
-
85
- return shapeFns.findCollisions(entityShape, targetShape)
86
- }
87
-
88
- /**
89
- * Correctly calculates the absolute position and size of an entity's
90
- * collision shape, including any offsets.
91
- */
92
- function getCollisionShape(entity, collisionGroup = "hitbox") {
93
- const collision = entity.collisions[collisionGroup]
94
- if (!collision) {
95
- return null
96
- }
97
-
98
- const position = add(
99
- entity.position,
100
- collision.offset ?? zero(),
101
- entity.offset ?? zero(),
102
- )
103
-
104
- return {
105
- ...collision,
106
- position,
107
- size: collision.size ?? entity.size,
108
- radius: collision.radius ?? entity.radius,
109
- }
110
- }
1
+ import { filter } from "@inglorious/utils/data-structures/object.js"
2
+ import * as circle from "@inglorious/utils/math/geometry/circle.js"
3
+ import * as hitmask from "@inglorious/utils/math/geometry/hitmask.js"
4
+ import * as line from "@inglorious/utils/math/geometry/line.js"
5
+ import * as point from "@inglorious/utils/math/geometry/point.js"
6
+ import * as rectangle from "@inglorious/utils/math/geometry/rectangle.js"
7
+ import * as segment from "@inglorious/utils/math/geometry/segment.js"
8
+ import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
9
+ import { add } from "@inglorious/utils/math/linear-algebra/vectors.js"
10
+
11
+ const Z = 2 // Z-axis index.
12
+
13
+ const Shape = {
14
+ circle,
15
+ line,
16
+ point,
17
+ rectangle,
18
+ segment,
19
+ hitmask,
20
+ }
21
+
22
+ /**
23
+ * Finds the first collision between a point and a list of entities.
24
+ *
25
+ * @param {Point} entity - The point to check for collisions.
26
+ * @param {Options} options - Options for collision detection.
27
+ * @returns {Entity | undefined} The first entity that collides with the point, or undefined if none are found.
28
+ */
29
+ export function findCollision(entity, entities, collisionGroup = "hitbox") {
30
+ const otherEntities = filter(
31
+ entities,
32
+ (id, { collisions }) => id !== entity.id && collisions?.[collisionGroup],
33
+ )
34
+
35
+ return Object.values(otherEntities)
36
+ .toSorted((a, b) => a.position[Z] - b.position[Z])
37
+ .find((target) => collidesWith(entity, target, collisionGroup))
38
+ }
39
+
40
+ export function collidesWith(entity, target, collisionGroup = "hitbox") {
41
+ const entityShape = getCollisionShape(entity, collisionGroup)
42
+ const targetShape = getCollisionShape(target, collisionGroup)
43
+
44
+ if (!entityShape || !targetShape) {
45
+ return false
46
+ }
47
+
48
+ return shapeCollidesWith(entityShape, targetShape)
49
+ }
50
+
51
+ function shapeCollidesWith(entity, target) {
52
+ const shapeFns = Shape[entity.shape]
53
+
54
+ switch (target.shape) {
55
+ case "circle":
56
+ return shapeFns.intersectsCircle(entity, target)
57
+
58
+ case "line":
59
+ return shapeFns.intersectsLine(entity, target)
60
+
61
+ case "point":
62
+ return shapeFns.intersectsPoint(entity, target)
63
+
64
+ case "rectangle":
65
+ return shapeFns.intersectsRectangle(entity, target)
66
+
67
+ case "segment":
68
+ return shapeFns.intersectsSegment(entity, target)
69
+ }
70
+ }
71
+
72
+ export function findCollisions(entity, target, collisionGroup = "hitbox") {
73
+ const entityShape = getCollisionShape(entity, collisionGroup)
74
+ const targetShape = getCollisionShape(target, collisionGroup)
75
+
76
+ if (!entityShape || !targetShape) {
77
+ return false
78
+ }
79
+
80
+ const shapeFns = Shape[entityShape.shape]
81
+ if (!shapeFns || !shapeFns.findCollisions) {
82
+ return false
83
+ }
84
+
85
+ return shapeFns.findCollisions(entityShape, targetShape)
86
+ }
87
+
88
+ /**
89
+ * Correctly calculates the absolute position and size of an entity's
90
+ * collision shape, including any offsets.
91
+ */
92
+ function getCollisionShape(entity, collisionGroup = "hitbox") {
93
+ const collision = entity.collisions[collisionGroup]
94
+ if (!collision) {
95
+ return null
96
+ }
97
+
98
+ const position = add(
99
+ entity.position,
100
+ collision.offset ?? zero(),
101
+ entity.offset ?? zero(),
102
+ )
103
+
104
+ return {
105
+ ...collision,
106
+ position,
107
+ size: collision.size ?? entity.size,
108
+ radius: collision.radius ?? entity.radius,
109
+ }
110
+ }
@@ -1,135 +1,135 @@
1
- export const ACTION_BLACKLIST = [
2
- "update",
3
- "gamepadAxis",
4
- "gamepadPress",
5
- "gamepadRelease",
6
- "keyboardKeyDown",
7
- "keyboardKeyUp",
8
- "inputAxis",
9
- "inputPress",
10
- "inputRelease",
11
- "mouseMove",
12
- "mouseClick",
13
- "spriteAnimationEnd",
14
- ]
15
-
16
- const LAST_STATE = 1
17
-
18
- let devToolsInstance = null
19
- let unsubscribe = null
20
-
21
- export function initDevTools(store) {
22
- // Prevent multiple connections
23
- if (devToolsInstance) {
24
- return
25
- }
26
-
27
- if (typeof window === "undefined" || !window.__REDUX_DEVTOOLS_EXTENSION__) {
28
- return
29
- }
30
-
31
- devToolsInstance = window.__REDUX_DEVTOOLS_EXTENSION__.connect({
32
- name: "Inglorious Engine",
33
- predicate: (state, action) => !ACTION_BLACKLIST.includes(action.type),
34
- actionCreators: {
35
- jump: () => ({ type: "jump", payload: { inputId: "input0" } }),
36
- },
37
- // @see https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#features
38
- features: {
39
- pause: true, // start/pause recording of dispatched actions
40
- lock: true, // lock/unlock dispatching actions and side effects
41
- persist: true, // persist states on page reloading
42
- export: true, // export history of actions in a file
43
- import: "custom", // import history of actions from a file
44
- jump: false, // jump back and forth (time travelling)
45
- skip: false, // skip (cancel) actions
46
- reorder: false, // drag and drop actions in the history list
47
- dispatch: true, // dispatch custom actions or action creators
48
- test: false, // generate tests for the selected actions
49
- },
50
- })
51
-
52
- unsubscribe = devToolsInstance.subscribe((message) => {
53
- switch (message.type) {
54
- case "DISPATCH":
55
- handleDispatch(message, store)
56
- break
57
-
58
- case "ACTION":
59
- handleAction(message, store)
60
- break
61
- }
62
- })
63
-
64
- devToolsInstance.init(store.getState())
65
- }
66
-
67
- export function disconnectDevTools() {
68
- // The `disconnect` method on the devToolsInstance is not available in all
69
- // environments or versions of the extension.
70
- // The safest way to "disconnect" is to unsubscribe from any listeners
71
- // and release our reference to the instance, which prevents any further
72
- // actions from being sent.
73
- if (unsubscribe) {
74
- unsubscribe()
75
- unsubscribe = null
76
- devToolsInstance = null
77
- }
78
- }
79
-
80
- export function sendAction(action, state) {
81
- if (devToolsInstance) {
82
- devToolsInstance.send(action, state)
83
- }
84
- }
85
-
86
- function handleDispatch(message, store) {
87
- switch (message.payload.type) {
88
- // reset button
89
- case "RESET": {
90
- store.reset()
91
- devToolsInstance.init(store.getState())
92
- break
93
- }
94
-
95
- // revert button
96
- case "ROLLBACK": {
97
- const newState = JSON.parse(message.state)
98
- store.setState(newState)
99
- break
100
- }
101
-
102
- // commit button
103
- case "COMMIT": {
104
- devToolsInstance.init(store.getState())
105
- break
106
- }
107
-
108
- // import from file button
109
- case "IMPORT_STATE": {
110
- const { computedStates, actionsById } = message.payload.nextLiftedState
111
-
112
- const [firstComputedState] = computedStates
113
- const lastComputedState =
114
- computedStates[computedStates.length - LAST_STATE]
115
- if (lastComputedState) {
116
- store.setState(lastComputedState.state)
117
- }
118
-
119
- const flattenedActions = Object.values(actionsById)
120
- .flatMap(({ action }) => action.payload ?? action)
121
- .map((action, index) => [index, action])
122
-
123
- devToolsInstance.init(
124
- firstComputedState.state,
125
- Object.fromEntries(flattenedActions),
126
- )
127
- break
128
- }
129
- }
130
- }
131
-
132
- function handleAction(message, store) {
133
- const action = JSON.parse(message.payload)
134
- store.dispatch(action)
135
- }
1
+ export const ACTION_BLACKLIST = [
2
+ "update",
3
+ "gamepadAxis",
4
+ "gamepadPress",
5
+ "gamepadRelease",
6
+ "keyboardKeyDown",
7
+ "keyboardKeyUp",
8
+ "inputAxis",
9
+ "inputPress",
10
+ "inputRelease",
11
+ "mouseMove",
12
+ "mouseClick",
13
+ "spriteAnimationEnd",
14
+ ]
15
+
16
+ const LAST_STATE = 1
17
+
18
+ let devToolsInstance = null
19
+ let unsubscribe = null
20
+
21
+ export function initDevTools(store) {
22
+ // Prevent multiple connections
23
+ if (devToolsInstance) {
24
+ return
25
+ }
26
+
27
+ if (typeof window === "undefined" || !window.__REDUX_DEVTOOLS_EXTENSION__) {
28
+ return
29
+ }
30
+
31
+ devToolsInstance = window.__REDUX_DEVTOOLS_EXTENSION__.connect({
32
+ name: "Inglorious Engine",
33
+ predicate: (state, action) => !ACTION_BLACKLIST.includes(action.type),
34
+ actionCreators: {
35
+ jump: () => ({ type: "jump", payload: { inputId: "input0" } }),
36
+ },
37
+ // @see https://github.com/reduxjs/redux-devtools/blob/main/extension/docs/API/Arguments.md#features
38
+ features: {
39
+ pause: true, // start/pause recording of dispatched actions
40
+ lock: true, // lock/unlock dispatching actions and side effects
41
+ persist: true, // persist states on page reloading
42
+ export: true, // export history of actions in a file
43
+ import: "custom", // import history of actions from a file
44
+ jump: false, // jump back and forth (time travelling)
45
+ skip: false, // skip (cancel) actions
46
+ reorder: false, // drag and drop actions in the history list
47
+ dispatch: true, // dispatch custom actions or action creators
48
+ test: false, // generate tests for the selected actions
49
+ },
50
+ })
51
+
52
+ unsubscribe = devToolsInstance.subscribe((message) => {
53
+ switch (message.type) {
54
+ case "DISPATCH":
55
+ handleDispatch(message, store)
56
+ break
57
+
58
+ case "ACTION":
59
+ handleAction(message, store)
60
+ break
61
+ }
62
+ })
63
+
64
+ devToolsInstance.init(store.getState())
65
+ }
66
+
67
+ export function disconnectDevTools() {
68
+ // The `disconnect` method on the devToolsInstance is not available in all
69
+ // environments or versions of the extension.
70
+ // The safest way to "disconnect" is to unsubscribe from any listeners
71
+ // and release our reference to the instance, which prevents any further
72
+ // actions from being sent.
73
+ if (unsubscribe) {
74
+ unsubscribe()
75
+ unsubscribe = null
76
+ devToolsInstance = null
77
+ }
78
+ }
79
+
80
+ export function sendAction(action, state) {
81
+ if (devToolsInstance) {
82
+ devToolsInstance.send(action, state)
83
+ }
84
+ }
85
+
86
+ function handleDispatch(message, store) {
87
+ switch (message.payload.type) {
88
+ // reset button
89
+ case "RESET": {
90
+ store.reset()
91
+ devToolsInstance.init(store.getState())
92
+ break
93
+ }
94
+
95
+ // revert button
96
+ case "ROLLBACK": {
97
+ const newState = JSON.parse(message.state)
98
+ store.setState(newState)
99
+ break
100
+ }
101
+
102
+ // commit button
103
+ case "COMMIT": {
104
+ devToolsInstance.init(store.getState())
105
+ break
106
+ }
107
+
108
+ // import from file button
109
+ case "IMPORT_STATE": {
110
+ const { computedStates, actionsById } = message.payload.nextLiftedState
111
+
112
+ const [firstComputedState] = computedStates
113
+ const lastComputedState =
114
+ computedStates[computedStates.length - LAST_STATE]
115
+ if (lastComputedState) {
116
+ store.setState(lastComputedState.state)
117
+ }
118
+
119
+ const flattenedActions = Object.values(actionsById)
120
+ .flatMap(({ action }) => action.payload ?? action)
121
+ .map((action, index) => [index, action])
122
+
123
+ devToolsInstance.init(
124
+ firstComputedState.state,
125
+ Object.fromEntries(flattenedActions),
126
+ )
127
+ break
128
+ }
129
+ }
130
+ }
131
+
132
+ function handleAction(message, store) {
133
+ const action = JSON.parse(message.payload)
134
+ store.dispatch(action)
135
+ }