@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,91 +1,91 @@
1
- import { findCollision } from "@inglorious/engine/collision/detection.js"
2
- import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
3
- import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
4
-
5
- const DEFAULT_PARAMS = {
6
- name: "mouse",
7
- }
8
-
9
- const NO_Y = 0
10
-
11
- export function mouse() {
12
- return {
13
- start(entity) {
14
- entity.collisions ??= {}
15
- entity.collisions.bounds ??= { shape: "point" }
16
- },
17
-
18
- mouseMove(entity, position, api) {
19
- const game = api.getEntity("game")
20
-
21
- entity.position = position
22
-
23
- clampToBounds(entity, game.bounds)
24
- },
25
-
26
- mouseClick(entity, position, api) {
27
- const entities = api.getEntities()
28
- const clickedEntity = findCollision(entity, entities)
29
- if (clickedEntity) {
30
- api.notify("entityClick", clickedEntity.id)
31
- } else {
32
- api.notify("sceneClick", position)
33
- }
34
- },
35
- }
36
- }
37
-
38
- export function track(parent, options) {
39
- const handleMouseMove = createHandler("mouseMove", parent, options)
40
- const handleClick = createHandler("mouseClick", parent, options)
41
- const handleWheel = createHandler("mouseWheel", parent, options)
42
-
43
- return {
44
- onMouseMove: handleMouseMove,
45
- onClick: handleClick,
46
- onWheel: handleWheel,
47
- }
48
- }
49
-
50
- export function createMouse(name = DEFAULT_PARAMS.name, overrides = {}) {
51
- return {
52
- id: name,
53
- type: "mouse",
54
- layer: 999, // A high layer value to ensure it's always rendered on top
55
- position: zero(),
56
- ...overrides,
57
- }
58
- }
59
-
60
- function createHandler(type, parent, api) {
61
- return (event) => {
62
- event.stopPropagation()
63
-
64
- if (parent == null) {
65
- return
66
- }
67
-
68
- // For wheel events, the payload is different from other mouse events.
69
- if (type === "mouseWheel") {
70
- api.notify(type, { deltaY: event.deltaY })
71
- return
72
- }
73
-
74
- // For move and click events, the payload is the calculated position.
75
- const payload = calculatePosition({
76
- clientX: event.clientX,
77
- clientY: event.clientY,
78
- parent,
79
- })
80
- api.notify(type, payload)
81
- }
82
- }
83
-
84
- function calculatePosition({ clientX, clientY, parent }) {
85
- const bounds = parent.getBoundingClientRect()
86
-
87
- const x = clientX - bounds.left
88
- const z = bounds.bottom - clientY
89
-
90
- return [x, NO_Y, z]
91
- }
1
+ import { findCollision } from "@inglorious/engine/collision/detection.js"
2
+ import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
3
+ import { zero } from "@inglorious/utils/math/linear-algebra/vector.js"
4
+
5
+ const DEFAULT_PARAMS = {
6
+ name: "mouse",
7
+ }
8
+
9
+ const NO_Y = 0
10
+
11
+ export function mouse() {
12
+ return {
13
+ start(entity) {
14
+ entity.collisions ??= {}
15
+ entity.collisions.bounds ??= { shape: "point" }
16
+ },
17
+
18
+ mouseMove(entity, position, api) {
19
+ const game = api.getEntity("game")
20
+
21
+ entity.position = position
22
+
23
+ clampToBounds(entity, game.bounds)
24
+ },
25
+
26
+ mouseClick(entity, position, api) {
27
+ const entities = api.getEntities()
28
+ const clickedEntity = findCollision(entity, entities)
29
+ if (clickedEntity) {
30
+ api.notify("entityClick", clickedEntity.id)
31
+ } else {
32
+ api.notify("sceneClick", position)
33
+ }
34
+ },
35
+ }
36
+ }
37
+
38
+ export function track(parent, options) {
39
+ const handleMouseMove = createHandler("mouseMove", parent, options)
40
+ const handleClick = createHandler("mouseClick", parent, options)
41
+ const handleWheel = createHandler("mouseWheel", parent, options)
42
+
43
+ return {
44
+ onMouseMove: handleMouseMove,
45
+ onClick: handleClick,
46
+ onWheel: handleWheel,
47
+ }
48
+ }
49
+
50
+ export function createMouse(name = DEFAULT_PARAMS.name, overrides = {}) {
51
+ return {
52
+ id: name,
53
+ type: "mouse",
54
+ layer: 999, // A high layer value to ensure it's always rendered on top
55
+ position: zero(),
56
+ ...overrides,
57
+ }
58
+ }
59
+
60
+ function createHandler(type, parent, api) {
61
+ return (event) => {
62
+ event.stopPropagation()
63
+
64
+ if (parent == null) {
65
+ return
66
+ }
67
+
68
+ // For wheel events, the payload is different from other mouse events.
69
+ if (type === "mouseWheel") {
70
+ api.notify(type, { deltaY: event.deltaY })
71
+ return
72
+ }
73
+
74
+ // For move and click events, the payload is the calculated position.
75
+ const payload = calculatePosition({
76
+ clientX: event.clientX,
77
+ clientY: event.clientY,
78
+ parent,
79
+ })
80
+ api.notify(type, payload)
81
+ }
82
+ }
83
+
84
+ function calculatePosition({ clientX, clientY, parent }) {
85
+ const bounds = parent.getBoundingClientRect()
86
+
87
+ const x = clientX - bounds.left
88
+ const z = bounds.bottom - clientY
89
+
90
+ return [x, NO_Y, z]
91
+ }
@@ -1,25 +1,25 @@
1
- import { defaults, extend } from "@inglorious/utils/data-structures/objects.js"
2
- import { jump } from "@inglorious/utils/physics/jump.js"
3
-
4
- const DEFAULT_PARAMS = {
5
- bounciness: 1,
6
- }
7
-
8
- export function bouncy(params) {
9
- params = extend(DEFAULT_PARAMS, params)
10
-
11
- return (type) =>
12
- extend(type, {
13
- start(entity) {
14
- type.start?.(entity)
15
- defaults(entity, params)
16
- },
17
-
18
- landed(entity, { entityId }) {
19
- if (entity.id === entityId) {
20
- entity.vy = jump(entity) * entity.bounciness
21
- entity.groundObject = undefined
22
- }
23
- },
24
- })
25
- }
1
+ import { defaults, extend } from "@inglorious/utils/data-structures/objects.js"
2
+ import { jump } from "@inglorious/utils/physics/jump.js"
3
+
4
+ const DEFAULT_PARAMS = {
5
+ bounciness: 1,
6
+ }
7
+
8
+ export function bouncy(params) {
9
+ params = extend(DEFAULT_PARAMS, params)
10
+
11
+ return (type) =>
12
+ extend(type, {
13
+ start(entity) {
14
+ type.start?.(entity)
15
+ defaults(entity, params)
16
+ },
17
+
18
+ landed(entity, { entityId }) {
19
+ if (entity.id === entityId) {
20
+ entity.vy = jump(entity) * entity.bounciness
21
+ entity.groundObject = undefined
22
+ }
23
+ },
24
+ })
25
+ }
@@ -1,36 +1,36 @@
1
- import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
2
- import { extend, merge } from "@inglorious/utils/data-structures/objects.js"
3
-
4
- const DEFAULT_PARAMS = {
5
- collisionGroup: "bounds",
6
- depthAxis: "y",
7
- }
8
-
9
- export function clamped(params) {
10
- params = extend(DEFAULT_PARAMS, params)
11
-
12
- return (type) =>
13
- extend(type, {
14
- start(entity, api) {
15
- type.start?.(entity, api)
16
-
17
- entity.collisions ??= {}
18
- entity.collisions[params.collisionGroup] ??= {}
19
- entity.collisions[params.collisionGroup].shape ??= "rectangle"
20
- },
21
-
22
- update(entity, dt, api) {
23
- type.update?.(entity, dt, api)
24
-
25
- const game = api.getEntity("game")
26
- merge(entity, {
27
- position: clampToBounds(
28
- entity,
29
- game.bounds,
30
- params.collisionGroup,
31
- params.depthAxis,
32
- ),
33
- })
34
- },
35
- })
36
- }
1
+ import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
2
+ import { extend, merge } from "@inglorious/utils/data-structures/objects.js"
3
+
4
+ const DEFAULT_PARAMS = {
5
+ collisionGroup: "bounds",
6
+ depthAxis: "y",
7
+ }
8
+
9
+ export function clamped(params) {
10
+ params = extend(DEFAULT_PARAMS, params)
11
+
12
+ return (type) =>
13
+ extend(type, {
14
+ start(entity, api) {
15
+ type.start?.(entity, api)
16
+
17
+ entity.collisions ??= {}
18
+ entity.collisions[params.collisionGroup] ??= {}
19
+ entity.collisions[params.collisionGroup].shape ??= "rectangle"
20
+ },
21
+
22
+ update(entity, dt, api) {
23
+ type.update?.(entity, dt, api)
24
+
25
+ const game = api.getEntity("game")
26
+ merge(entity, {
27
+ position: clampToBounds(
28
+ entity,
29
+ game.bounds,
30
+ params.collisionGroup,
31
+ params.depthAxis,
32
+ ),
33
+ })
34
+ },
35
+ })
36
+ }
@@ -1,20 +1,20 @@
1
- import { extend } from "@inglorious/utils/data-structures/objects.js"
2
-
3
- const DEFAULT_PARAMS = {
4
- onState: "default",
5
- }
6
-
7
- export function collidable(params) {
8
- params = extend(DEFAULT_PARAMS, params)
9
-
10
- return (type) =>
11
- extend(type, {
12
- states: {
13
- [params.onState]: {
14
- update(entity, dt, api) {
15
- type.states?.[params.onState].update?.(entity, dt, api)
16
- },
17
- },
18
- },
19
- })
20
- }
1
+ import { extend } from "@inglorious/utils/data-structures/objects.js"
2
+
3
+ const DEFAULT_PARAMS = {
4
+ onState: "default",
5
+ }
6
+
7
+ export function collidable(params) {
8
+ params = extend(DEFAULT_PARAMS, params)
9
+
10
+ return (type) =>
11
+ extend(type, {
12
+ states: {
13
+ [params.onState]: {
14
+ update(entity, dt, api) {
15
+ type.states?.[params.onState].update?.(entity, dt, api)
16
+ },
17
+ },
18
+ },
19
+ })
20
+ }
@@ -1,145 +1,145 @@
1
- import { findCollision } from "@inglorious/engine/collision/detection.js"
2
- import { defaults, extend } from "@inglorious/utils/data-structures/objects.js"
3
- import {
4
- angle,
5
- magnitude,
6
- zero,
7
- } from "@inglorious/utils/math/linear-algebra/vector.js"
8
- import { applyGravity } from "@inglorious/utils/physics/gravity.js"
9
- import { jump } from "@inglorious/utils/physics/jump.js"
10
-
11
- const DEFAULT_PARAMS = {
12
- maxSpeed: 250,
13
- maxJump: 100,
14
- maxLeap: 100,
15
- maxJumps: 1,
16
- }
17
-
18
- const DOUBLE = 2
19
- const HALF = 2
20
- const X = 0
21
- const Y = 1
22
- const NO_VELOCITY = 0
23
- const NO_PENETRAION = 0
24
-
25
- export function jumpable(params) {
26
- params = extend(DEFAULT_PARAMS, params)
27
-
28
- return (type) =>
29
- extend(type, {
30
- start(entity, api) {
31
- type.start?.(entity, api)
32
- defaults(entity, params)
33
- entity.jumpsLeft ??= entity.maxJumps
34
-
35
- entity.velocity ??= zero()
36
- entity.vy ??= 0
37
- },
38
-
39
- jump(entity, { entityId }) {
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 =
77
- collisionX.position[X] - collisionX.size[X] / HALF
78
- if (vx > NO_VELOCITY && prevRight <= platformLeft) {
79
- const penetration = entityRight - platformLeft
80
- if (penetration > NO_PENETRAION) entity.position[X] -= penetration
81
- entity.velocity[X] = 0
82
- }
83
-
84
- // Check if moving left and crossing the platform's right edge
85
- const prevLeft = prevX - width / HALF
86
- const platformRight =
87
- collisionX.position[X] + collisionX.size[X] / HALF
88
- if (vx < NO_VELOCITY && prevLeft >= platformRight) {
89
- const penetration = platformRight - entityLeft
90
- if (penetration > NO_PENETRAION) entity.position[X] += penetration
91
- entity.velocity[X] = 0
92
- }
93
- }
94
-
95
- // 2. VERTICAL MOVEMENT & RESOLUTION
96
- const wasOnGround = entity.groundObject
97
- entity.groundObject = undefined
98
-
99
- const { vy, position: nextGravityPosition } = applyGravity(entity, dt)
100
- entity.vy = vy
101
- entity.position[Y] = nextGravityPosition[Y]
102
-
103
- const collisionY = findCollision(entity, entities, "platform")
104
- if (collisionY) {
105
- const prevBottom = prevY - height / HALF
106
- const platformTop = collisionY.position[Y] + collisionY.size[Y] / HALF
107
-
108
- // Landing on top of a platform (one-way platform logic)
109
- if (entity.vy <= NO_VELOCITY && prevBottom >= platformTop) {
110
- const entityBottom = entity.position[Y] - height / HALF
111
- const penetration = platformTop - entityBottom
112
- if (penetration > NO_PENETRAION) entity.position[Y] += penetration
113
- entity.vy = 0
114
- entity.groundObject = collisionY
115
- entity.jumpsLeft = entity.maxJumps
116
-
117
- // Only notify on the frame we actually land, not every frame we're on the ground.
118
- if (!wasOnGround) {
119
- api.notify("landed", {
120
- entityId: entity.id,
121
- targetId: collisionY.id,
122
- })
123
- }
124
- }
125
-
126
- // Hitting head on bottom of a platform
127
- // else if (entity.vy > 0) {
128
- // const prevTop = prevY + height / HALF
129
- // const platformBottom =
130
- // collisionY.position[Y] - collisionY.size[Y] / HALF
131
- // if (prevTop <= platformBottom) {
132
- // const entityTop = entity.position[Y] + height / HALF
133
- // const penetration = entityTop - platformBottom
134
- // if (penetration > 0) entity.position[Y] -= penetration
135
- // entity.vy = 0
136
- // }
137
- // }
138
- }
139
-
140
- entity.orientation = magnitude(entity.velocity)
141
- ? angle(entity.velocity)
142
- : entity.orientation
143
- },
144
- })
145
- }
1
+ import { findCollision } from "@inglorious/engine/collision/detection.js"
2
+ import { defaults, extend } from "@inglorious/utils/data-structures/objects.js"
3
+ import {
4
+ angle,
5
+ magnitude,
6
+ zero,
7
+ } from "@inglorious/utils/math/linear-algebra/vector.js"
8
+ import { applyGravity } from "@inglorious/utils/physics/gravity.js"
9
+ import { jump } from "@inglorious/utils/physics/jump.js"
10
+
11
+ const DEFAULT_PARAMS = {
12
+ maxSpeed: 250,
13
+ maxJump: 100,
14
+ maxLeap: 100,
15
+ maxJumps: 1,
16
+ }
17
+
18
+ const DOUBLE = 2
19
+ const HALF = 2
20
+ const X = 0
21
+ const Y = 1
22
+ const NO_VELOCITY = 0
23
+ const NO_PENETRAION = 0
24
+
25
+ export function jumpable(params) {
26
+ params = extend(DEFAULT_PARAMS, params)
27
+
28
+ return (type) =>
29
+ extend(type, {
30
+ start(entity, api) {
31
+ type.start?.(entity, api)
32
+ defaults(entity, params)
33
+ entity.jumpsLeft ??= entity.maxJumps
34
+
35
+ entity.velocity ??= zero()
36
+ entity.vy ??= 0
37
+ },
38
+
39
+ jump(entity, { entityId }) {
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 =
77
+ collisionX.position[X] - collisionX.size[X] / HALF
78
+ if (vx > NO_VELOCITY && prevRight <= platformLeft) {
79
+ const penetration = entityRight - platformLeft
80
+ if (penetration > NO_PENETRAION) entity.position[X] -= penetration
81
+ entity.velocity[X] = 0
82
+ }
83
+
84
+ // Check if moving left and crossing the platform's right edge
85
+ const prevLeft = prevX - width / HALF
86
+ const platformRight =
87
+ collisionX.position[X] + collisionX.size[X] / HALF
88
+ if (vx < NO_VELOCITY && prevLeft >= platformRight) {
89
+ const penetration = platformRight - entityLeft
90
+ if (penetration > NO_PENETRAION) entity.position[X] += penetration
91
+ entity.velocity[X] = 0
92
+ }
93
+ }
94
+
95
+ // 2. VERTICAL MOVEMENT & RESOLUTION
96
+ const wasOnGround = entity.groundObject
97
+ entity.groundObject = undefined
98
+
99
+ const { vy, position: nextGravityPosition } = applyGravity(entity, dt)
100
+ entity.vy = vy
101
+ entity.position[Y] = nextGravityPosition[Y]
102
+
103
+ const collisionY = findCollision(entity, entities, "platform")
104
+ if (collisionY) {
105
+ const prevBottom = prevY - height / HALF
106
+ const platformTop = collisionY.position[Y] + collisionY.size[Y] / HALF
107
+
108
+ // Landing on top of a platform (one-way platform logic)
109
+ if (entity.vy <= NO_VELOCITY && prevBottom >= platformTop) {
110
+ const entityBottom = entity.position[Y] - height / HALF
111
+ const penetration = platformTop - entityBottom
112
+ if (penetration > NO_PENETRAION) entity.position[Y] += penetration
113
+ entity.vy = 0
114
+ entity.groundObject = collisionY
115
+ entity.jumpsLeft = entity.maxJumps
116
+
117
+ // Only notify on the frame we actually land, not every frame we're on the ground.
118
+ if (!wasOnGround) {
119
+ api.notify("landed", {
120
+ entityId: entity.id,
121
+ targetId: collisionY.id,
122
+ })
123
+ }
124
+ }
125
+
126
+ // Hitting head on bottom of a platform
127
+ // else if (entity.vy > 0) {
128
+ // const prevTop = prevY + height / HALF
129
+ // const platformBottom =
130
+ // collisionY.position[Y] - collisionY.size[Y] / HALF
131
+ // if (prevTop <= platformBottom) {
132
+ // const entityTop = entity.position[Y] + height / HALF
133
+ // const penetration = entityTop - platformBottom
134
+ // if (penetration > 0) entity.position[Y] -= penetration
135
+ // entity.vy = 0
136
+ // }
137
+ // }
138
+ }
139
+
140
+ entity.orientation = magnitude(entity.velocity)
141
+ ? angle(entity.velocity)
142
+ : entity.orientation
143
+ },
144
+ })
145
+ }
@@ -1,17 +1,17 @@
1
- export function button() {
2
- return {
3
- states: {
4
- default: {
5
- entityClick(entity) {
6
- entity.state = "pressed"
7
- },
8
- },
9
-
10
- pressed: {
11
- entityRelease(entity) {
12
- entity.state = "default"
13
- },
14
- },
15
- },
16
- }
17
- }
1
+ export function button() {
2
+ return {
3
+ states: {
4
+ default: {
5
+ entityClick(entity) {
6
+ entity.state = "pressed"
7
+ },
8
+ },
9
+
10
+ pressed: {
11
+ entityRelease(entity) {
12
+ entity.state = "default"
13
+ },
14
+ },
15
+ },
16
+ }
17
+ }