@inglorious/engine 0.2.0 → 0.3.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 +75 -75
  2. package/package.json +13 -23
  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}/api.js +34 -34
  48. package/src/{engine/core → core}/dev-tools.js +135 -135
  49. package/src/{engine/core → core}/engine.js +119 -119
  50. package/src/{engine/core → core}/loop.js +15 -15
  51. package/src/{engine/core → core}/loops/animation-frame.js +25 -25
  52. package/src/{engine/core → core}/loops/elapsed.js +22 -22
  53. package/src/{engine/core → core}/loops/fixed.js +27 -27
  54. package/src/{engine/core → core}/loops/flash.js +13 -13
  55. package/src/{engine/core → core}/loops/lag.js +26 -26
  56. package/src/{engine/core → core}/select.js +26 -26
  57. package/src/{engine/core → core}/store.js +178 -178
  58. package/src/{engine/core → core}/store.test.js +110 -110
  59. package/src/main.js +10 -10
  60. package/src/{engine/movement → movement}/dynamic/modern.js +21 -21
  61. package/src/{engine/movement → movement}/dynamic/tank.js +43 -43
  62. package/src/{engine/movement → movement}/kinematic/modern.js +16 -16
  63. package/src/{engine/movement → movement}/kinematic/modern.test.js +27 -27
  64. package/src/{engine/movement → movement}/kinematic/tank.js +27 -27
  65. package/src/{engine/physics → physics}/bounds.js +138 -138
  66. package/src/{engine/physics → physics}/position.js +43 -43
  67. package/src/{engine/physics → physics}/position.test.js +80 -80
  68. package/src/{engine/systems → systems}/sprite-animation.js +27 -27
  69. package/src/engine/behaviors/debug/collision.js +0 -35
  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,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
+ }
@@ -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
+ }