@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.
- package/README.md +75 -75
- package/package.json +13 -23
- package/src/{engine/ai → ai}/movement/dynamic/align.js +63 -63
- package/src/{engine/ai → ai}/movement/dynamic/arrive.js +42 -42
- package/src/{engine/ai → ai}/movement/dynamic/evade.js +38 -38
- package/src/{engine/ai → ai}/movement/dynamic/face.js +19 -19
- package/src/{engine/ai → ai}/movement/dynamic/flee.js +45 -45
- package/src/{engine/ai → ai}/movement/dynamic/look-where-youre-going.js +16 -16
- package/src/{engine/ai → ai}/movement/dynamic/match-velocity.js +51 -51
- package/src/{engine/ai → ai}/movement/dynamic/pursue.js +38 -38
- package/src/{engine/ai → ai}/movement/dynamic/seek.js +44 -44
- package/src/{engine/ai → ai}/movement/dynamic/wander.js +31 -31
- package/src/{engine/ai → ai}/movement/kinematic/align.js +37 -37
- package/src/{engine/ai → ai}/movement/kinematic/arrive.js +42 -42
- package/src/{engine/ai → ai}/movement/kinematic/face.js +19 -19
- package/src/{engine/ai → ai}/movement/kinematic/flee.js +26 -26
- package/src/{engine/ai → ai}/movement/kinematic/seek.js +26 -26
- package/src/{engine/ai → ai}/movement/kinematic/seek.test.js +42 -42
- package/src/{engine/ai → ai}/movement/kinematic/wander-as-seek.js +31 -31
- package/src/{engine/ai → ai}/movement/kinematic/wander.js +27 -27
- package/src/{engine/animation → animation}/sprite.js +101 -101
- package/src/{engine/animation → animation}/ticker.js +38 -38
- package/src/{engine/behaviors → behaviors}/camera.js +68 -68
- package/src/{engine/behaviors → behaviors}/controls/dynamic/modern.js +76 -76
- package/src/{engine/behaviors → behaviors}/controls/dynamic/shooter.js +84 -84
- package/src/{engine/behaviors → behaviors}/controls/dynamic/tank.js +69 -69
- package/src/{engine/behaviors → behaviors}/controls/event-handlers.js +17 -17
- package/src/{engine/behaviors → behaviors}/controls/kinematic/modern.js +76 -76
- package/src/{engine/behaviors → behaviors}/controls/kinematic/shooter.js +82 -82
- package/src/{engine/behaviors → behaviors}/controls/kinematic/tank.js +67 -67
- package/src/behaviors/debug/collision.js +29 -0
- package/src/{engine/behaviors → behaviors}/fps.js +29 -29
- package/src/{engine/behaviors → behaviors}/fsm.js +33 -33
- package/src/{engine/behaviors → behaviors}/fsm.test.js +49 -49
- package/src/{engine/behaviors → behaviors}/game.js +15 -15
- package/src/{engine/behaviors → behaviors}/input/controls.js +37 -37
- package/src/{engine/behaviors → behaviors}/input/gamepad.js +114 -114
- package/src/{engine/behaviors → behaviors}/input/input.js +48 -48
- package/src/{engine/behaviors → behaviors}/input/keyboard.js +64 -64
- package/src/{engine/behaviors → behaviors}/input/mouse.js +91 -91
- package/src/{engine/behaviors → behaviors}/physics/bouncy.js +25 -25
- package/src/{engine/behaviors → behaviors}/physics/clamped.js +36 -36
- package/src/{engine/behaviors → behaviors}/physics/collidable.js +20 -20
- package/src/{engine/behaviors → behaviors}/physics/jumpable.js +145 -145
- package/src/{engine/behaviors → behaviors}/ui/button.js +17 -17
- package/src/{engine/collision → collision}/detection.js +110 -110
- package/src/{engine/core → core}/api.js +34 -34
- package/src/{engine/core → core}/dev-tools.js +135 -135
- package/src/{engine/core → core}/engine.js +119 -119
- package/src/{engine/core → core}/loop.js +15 -15
- package/src/{engine/core → core}/loops/animation-frame.js +25 -25
- package/src/{engine/core → core}/loops/elapsed.js +22 -22
- package/src/{engine/core → core}/loops/fixed.js +27 -27
- package/src/{engine/core → core}/loops/flash.js +13 -13
- package/src/{engine/core → core}/loops/lag.js +26 -26
- package/src/{engine/core → core}/select.js +26 -26
- package/src/{engine/core → core}/store.js +178 -178
- package/src/{engine/core → core}/store.test.js +110 -110
- package/src/main.js +10 -10
- package/src/{engine/movement → movement}/dynamic/modern.js +21 -21
- package/src/{engine/movement → movement}/dynamic/tank.js +43 -43
- package/src/{engine/movement → movement}/kinematic/modern.js +16 -16
- package/src/{engine/movement → movement}/kinematic/modern.test.js +27 -27
- package/src/{engine/movement → movement}/kinematic/tank.js +27 -27
- package/src/{engine/physics → physics}/bounds.js +138 -138
- package/src/{engine/physics → physics}/position.js +43 -43
- package/src/{engine/physics → physics}/position.test.js +80 -80
- package/src/{engine/systems → systems}/sprite-animation.js +27 -27
- package/src/engine/behaviors/debug/collision.js +0 -35
- package/src/renderers/canvas/absolute-position.js +0 -18
- package/src/renderers/canvas/camera.js +0 -13
- package/src/renderers/canvas/canvas-renderer.js +0 -68
- package/src/renderers/canvas/character.js +0 -38
- package/src/renderers/canvas/form/button.js +0 -25
- package/src/renderers/canvas/fps.js +0 -18
- package/src/renderers/canvas/image/hitmask.js +0 -51
- package/src/renderers/canvas/image/image.js +0 -34
- package/src/renderers/canvas/image/sprite.js +0 -49
- package/src/renderers/canvas/image/tilemap.js +0 -66
- package/src/renderers/canvas/mouse.js +0 -37
- package/src/renderers/canvas/rendering-system.js +0 -79
- package/src/renderers/canvas/shapes/circle.js +0 -29
- package/src/renderers/canvas/shapes/rectangle.js +0 -27
- package/src/renderers/react/game/character/character.module.scss +0 -17
- package/src/renderers/react/game/character/index.jsx +0 -20
- package/src/renderers/react/game/cursor/cursor.module.scss +0 -47
- package/src/renderers/react/game/cursor/index.jsx +0 -20
- package/src/renderers/react/game/form/fields/field/field.module.scss +0 -5
- package/src/renderers/react/game/form/fields/field/index.jsx +0 -56
- package/src/renderers/react/game/form/fields/fields.module.scss +0 -48
- package/src/renderers/react/game/form/fields/index.jsx +0 -12
- package/src/renderers/react/game/form/form.module.scss +0 -18
- package/src/renderers/react/game/form/index.jsx +0 -22
- package/src/renderers/react/game/fps/index.jsx +0 -16
- package/src/renderers/react/game/game.jsx +0 -72
- package/src/renderers/react/game/index.jsx +0 -29
- package/src/renderers/react/game/platform/index.jsx +0 -30
- package/src/renderers/react/game/platform/platform.module.scss +0 -7
- package/src/renderers/react/game/scene/index.jsx +0 -27
- package/src/renderers/react/game/scene/scene.module.scss +0 -9
- package/src/renderers/react/game/sprite/index.jsx +0 -60
- package/src/renderers/react/game/sprite/sprite.module.css +0 -3
- package/src/renderers/react/game/stats/index.jsx +0 -22
- package/src/renderers/react/hocs/with-absolute-position/index.jsx +0 -20
- package/src/renderers/react/hocs/with-absolute-position/with-absolute-position.module.scss +0 -5
- package/src/renderers/react/index.jsx +0 -9
- package/src/utils/algorithms/decision-tree.js +0 -24
- package/src/utils/algorithms/decision-tree.test.js +0 -153
- package/src/utils/algorithms/path-finding.js +0 -155
- package/src/utils/algorithms/path-finding.test.js +0 -151
- package/src/utils/algorithms/types.d.ts +0 -28
- package/src/utils/data-structures/array.js +0 -83
- package/src/utils/data-structures/array.test.js +0 -173
- package/src/utils/data-structures/board.js +0 -159
- package/src/utils/data-structures/board.test.js +0 -242
- package/src/utils/data-structures/boolean.js +0 -9
- package/src/utils/data-structures/heap.js +0 -164
- package/src/utils/data-structures/heap.test.js +0 -103
- package/src/utils/data-structures/object.js +0 -138
- package/src/utils/data-structures/object.test.js +0 -218
- package/src/utils/data-structures/objects.js +0 -66
- package/src/utils/data-structures/objects.test.js +0 -99
- package/src/utils/data-structures/tree.js +0 -36
- package/src/utils/data-structures/tree.test.js +0 -33
- package/src/utils/data-structures/types.d.ts +0 -4
- package/src/utils/functions/functions.js +0 -19
- package/src/utils/functions/functions.test.js +0 -23
- package/src/utils/math/geometry/circle.js +0 -70
- package/src/utils/math/geometry/circle.test.js +0 -97
- package/src/utils/math/geometry/hitmask.js +0 -70
- package/src/utils/math/geometry/hitmask.test.js +0 -155
- package/src/utils/math/geometry/line.js +0 -35
- package/src/utils/math/geometry/line.test.js +0 -49
- package/src/utils/math/geometry/point.js +0 -78
- package/src/utils/math/geometry/point.test.js +0 -81
- package/src/utils/math/geometry/rectangle.js +0 -76
- package/src/utils/math/geometry/rectangle.test.js +0 -42
- package/src/utils/math/geometry/segment.js +0 -80
- package/src/utils/math/geometry/segment.test.js +0 -183
- package/src/utils/math/geometry/triangle.js +0 -15
- package/src/utils/math/geometry/triangle.test.js +0 -11
- package/src/utils/math/geometry/types.d.ts +0 -23
- package/src/utils/math/linear-algebra/2d.js +0 -28
- package/src/utils/math/linear-algebra/2d.test.js +0 -17
- package/src/utils/math/linear-algebra/quaternion.js +0 -22
- package/src/utils/math/linear-algebra/quaternion.test.js +0 -25
- package/src/utils/math/linear-algebra/quaternions.js +0 -20
- package/src/utils/math/linear-algebra/quaternions.test.js +0 -29
- package/src/utils/math/linear-algebra/types.d.ts +0 -4
- package/src/utils/math/linear-algebra/vector.js +0 -327
- package/src/utils/math/linear-algebra/vector.test.js +0 -265
- package/src/utils/math/linear-algebra/vectors.js +0 -122
- package/src/utils/math/linear-algebra/vectors.test.js +0 -65
- package/src/utils/math/linear-interpolation.js +0 -9
- package/src/utils/math/numbers.js +0 -90
- package/src/utils/math/numbers.test.js +0 -137
- package/src/utils/math/rng.js +0 -44
- package/src/utils/math/rng.test.js +0 -39
- package/src/utils/math/statistics.js +0 -43
- package/src/utils/math/statistics.test.js +0 -47
- package/src/utils/math/trigonometry.js +0 -89
- package/src/utils/math/trigonometry.test.js +0 -52
- package/src/utils/physics/acceleration.js +0 -61
- package/src/utils/physics/friction.js +0 -28
- package/src/utils/physics/friction.test.js +0 -42
- package/src/utils/physics/gravity.js +0 -69
- package/src/utils/physics/gravity.test.js +0 -77
- package/src/utils/physics/jump.js +0 -31
- 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
|
+
}
|