@inglorious/engine 1.0.0 → 2.0.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/package.json +8 -3
- package/src/behaviors/input/controls.js +10 -26
- package/src/behaviors/input/gamepad.js +20 -44
- package/src/behaviors/input/input.js +14 -31
- package/src/behaviors/input/keyboard.js +6 -18
- package/src/behaviors/input/mouse.js +1 -6
- package/src/core/middlewares/multiplayer-middleware.js +7 -3
- package/src/behaviors/physics/collidable.js +0 -20
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@inglorious/engine",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "A JavaScript game engine written with global state, immutability, and pure functions in mind. Have fun(ctional programming) with it!",
|
|
5
5
|
"author": "IceOnFire <antony.mistretta@gmail.com> (https://ingloriouscoderz.it)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -31,12 +31,17 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@inglorious/store": "4.0.0",
|
|
34
|
-
"@inglorious/utils": "3.
|
|
34
|
+
"@inglorious/utils": "3.5.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"@inglorious/store": "4.0.0",
|
|
38
|
+
"@inglorious/utils": "3.5.0"
|
|
35
39
|
},
|
|
36
40
|
"devDependencies": {
|
|
37
41
|
"prettier": "^3.5.3",
|
|
38
42
|
"vite": "^7.1.3",
|
|
39
|
-
"vitest": "^1.6.0"
|
|
43
|
+
"vitest": "^1.6.0",
|
|
44
|
+
"@inglorious/eslint-config": "1.0.0"
|
|
40
45
|
},
|
|
41
46
|
"engines": {
|
|
42
47
|
"node": ">= 22"
|
|
@@ -1,37 +1,21 @@
|
|
|
1
|
-
import { extend } from "@inglorious/utils/data-structures/objects.js"
|
|
2
|
-
|
|
3
1
|
import { createGamepad, gamepadListener, gamepadsPoller } from "./gamepad.js"
|
|
4
2
|
import { createInput, input } from "./input.js"
|
|
5
3
|
import { createKeyboard, keyboard } from "./keyboard.js"
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
name: "input0",
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function setupControls(params) {
|
|
12
|
-
params = extend(DEFAULT_PARAMS, params)
|
|
13
|
-
|
|
5
|
+
export function controls(...targetIds) {
|
|
14
6
|
return {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
input: [input(params)],
|
|
20
|
-
},
|
|
21
|
-
entities: {
|
|
22
|
-
gamepads: { type: "gamepads_poller" },
|
|
23
|
-
},
|
|
7
|
+
keyboard: [keyboard()],
|
|
8
|
+
gamepads_poller: [gamepadsPoller(targetIds)],
|
|
9
|
+
gamepad_listener: [gamepadListener()],
|
|
10
|
+
input: [input()],
|
|
24
11
|
}
|
|
25
12
|
}
|
|
26
13
|
|
|
27
|
-
export function
|
|
28
|
-
name = DEFAULT_PARAMS.name,
|
|
29
|
-
targetIds,
|
|
30
|
-
mapping = {},
|
|
31
|
-
) {
|
|
14
|
+
export function createControls(targetId, mapping = {}) {
|
|
32
15
|
return {
|
|
33
|
-
|
|
34
|
-
[`
|
|
35
|
-
[
|
|
16
|
+
gamepads: { type: "gamepads_poller" },
|
|
17
|
+
[`keyboard_${targetId}`]: createKeyboard(targetId, mapping),
|
|
18
|
+
[`gamepad_${targetId}`]: createGamepad(targetId, mapping),
|
|
19
|
+
[`input_${targetId}`]: createInput(targetId, mapping),
|
|
36
20
|
}
|
|
37
21
|
}
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
name: "gamepad_input0",
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
export function gamepadsPoller() {
|
|
1
|
+
export function gamepadsPoller(targetIds = []) {
|
|
6
2
|
return {
|
|
7
3
|
create(entity, entityId) {
|
|
8
4
|
if (entityId !== entity.id) return
|
|
@@ -12,9 +8,7 @@ export function gamepadsPoller() {
|
|
|
12
8
|
|
|
13
9
|
update(entity, dt, api) {
|
|
14
10
|
navigator.getGamepads().forEach((gamepad) => {
|
|
15
|
-
if (gamepad == null)
|
|
16
|
-
return
|
|
17
|
-
}
|
|
11
|
+
if (gamepad == null) return
|
|
18
12
|
|
|
19
13
|
const cache = (entity.gamepadStateCache[gamepad.index] ??= {
|
|
20
14
|
axes: [],
|
|
@@ -22,12 +16,10 @@ export function gamepadsPoller() {
|
|
|
22
16
|
})
|
|
23
17
|
|
|
24
18
|
gamepad.axes.forEach((axis, index) => {
|
|
25
|
-
if (axis === cache.axes[index])
|
|
26
|
-
return
|
|
27
|
-
}
|
|
19
|
+
if (axis === cache.axes[index]) return
|
|
28
20
|
|
|
29
21
|
api.notify("gamepadAxis", {
|
|
30
|
-
|
|
22
|
+
targetId: targetIds[gamepad.index],
|
|
31
23
|
axis: `Axis${index}`,
|
|
32
24
|
value: axis,
|
|
33
25
|
})
|
|
@@ -40,12 +32,12 @@ export function gamepadsPoller() {
|
|
|
40
32
|
|
|
41
33
|
if (isPressed && !wasPressed) {
|
|
42
34
|
api.notify("gamepadPress", {
|
|
43
|
-
|
|
35
|
+
targetId: targetIds[gamepad.index],
|
|
44
36
|
button: `Btn${index}`,
|
|
45
37
|
})
|
|
46
38
|
} else if (!isPressed && wasPressed) {
|
|
47
39
|
api.notify("gamepadRelease", {
|
|
48
|
-
|
|
40
|
+
targetId: targetIds[gamepad.index],
|
|
49
41
|
button: `Btn${index}`,
|
|
50
42
|
})
|
|
51
43
|
}
|
|
@@ -59,58 +51,42 @@ export function gamepadsPoller() {
|
|
|
59
51
|
|
|
60
52
|
export function gamepadListener() {
|
|
61
53
|
return {
|
|
62
|
-
gamepadAxis(entity, {
|
|
63
|
-
if (
|
|
64
|
-
return
|
|
65
|
-
}
|
|
54
|
+
gamepadAxis(entity, { targetId, axis, value }, api) {
|
|
55
|
+
if (targetId !== entity.targetId) return
|
|
66
56
|
|
|
67
57
|
const action = entity.mapping[axis]
|
|
68
|
-
if (!action)
|
|
69
|
-
return
|
|
70
|
-
}
|
|
58
|
+
if (!action) return
|
|
71
59
|
|
|
72
60
|
entity[action] = value
|
|
73
|
-
api.notify("inputAxis", {
|
|
61
|
+
api.notify("inputAxis", { targetId, action, value })
|
|
74
62
|
},
|
|
75
63
|
|
|
76
|
-
gamepadPress(entity, {
|
|
77
|
-
if (
|
|
78
|
-
return
|
|
79
|
-
}
|
|
64
|
+
gamepadPress(entity, { targetId, button }, api) {
|
|
65
|
+
if (targetId !== entity.targetId) return
|
|
80
66
|
|
|
81
67
|
const action = entity.mapping[button]
|
|
82
|
-
if (!action)
|
|
83
|
-
return
|
|
84
|
-
}
|
|
68
|
+
if (!action) return
|
|
85
69
|
|
|
86
70
|
if (!entity[action]) {
|
|
87
71
|
entity[action] = true
|
|
88
|
-
api.notify("inputPress", {
|
|
72
|
+
api.notify("inputPress", { targetId, action })
|
|
89
73
|
}
|
|
90
74
|
},
|
|
91
75
|
|
|
92
|
-
gamepadRelease(entity, {
|
|
93
|
-
if (
|
|
94
|
-
return
|
|
95
|
-
}
|
|
76
|
+
gamepadRelease(entity, { targetId, button }, api) {
|
|
77
|
+
if (targetId !== entity.targetId) return
|
|
96
78
|
|
|
97
79
|
const action = entity.mapping[button]
|
|
98
|
-
if (!action)
|
|
99
|
-
return
|
|
100
|
-
}
|
|
80
|
+
if (!action) return
|
|
101
81
|
|
|
102
82
|
if (entity[action]) {
|
|
103
83
|
entity[action] = false
|
|
104
|
-
api.notify("inputRelease", {
|
|
84
|
+
api.notify("inputRelease", { targetId, action })
|
|
105
85
|
}
|
|
106
86
|
},
|
|
107
87
|
}
|
|
108
88
|
}
|
|
109
89
|
|
|
110
|
-
export function createGamepad(
|
|
111
|
-
|
|
112
|
-
targetInput,
|
|
113
|
-
mapping = {},
|
|
114
|
-
) {
|
|
115
|
-
return { id: name, type: "gamepad_listener", targetInput, mapping }
|
|
90
|
+
export function createGamepad(targetId, mapping = {}) {
|
|
91
|
+
return { type: "gamepad_listener", targetId, mapping }
|
|
116
92
|
}
|
|
@@ -1,48 +1,31 @@
|
|
|
1
|
-
const DEFAULT_PARAMS = {
|
|
2
|
-
name: "input0",
|
|
3
|
-
}
|
|
4
|
-
|
|
5
1
|
export function input() {
|
|
6
2
|
return {
|
|
7
|
-
inputAxis(entity, {
|
|
8
|
-
if (
|
|
9
|
-
|
|
10
|
-
}
|
|
3
|
+
inputAxis(entity, { targetId, action, value }, api) {
|
|
4
|
+
if (targetId !== entity.targetId) return
|
|
5
|
+
|
|
11
6
|
entity[action] = value
|
|
12
7
|
|
|
13
|
-
entity.
|
|
14
|
-
api.notify(action, { entityId: targetId, value })
|
|
15
|
-
})
|
|
8
|
+
api.notify(action, { entityId: entity.targetId, value })
|
|
16
9
|
},
|
|
17
10
|
|
|
18
|
-
inputPress(entity, {
|
|
19
|
-
if (
|
|
20
|
-
|
|
21
|
-
}
|
|
11
|
+
inputPress(entity, { targetId, action }, api) {
|
|
12
|
+
if (targetId !== entity.targetId) return
|
|
13
|
+
|
|
22
14
|
entity[action] = true
|
|
23
15
|
|
|
24
|
-
entity.
|
|
25
|
-
api.notify(action, targetId)
|
|
26
|
-
})
|
|
16
|
+
api.notify(action, entity.targetId)
|
|
27
17
|
},
|
|
28
18
|
|
|
29
|
-
inputRelease(entity, {
|
|
30
|
-
if (
|
|
31
|
-
|
|
32
|
-
}
|
|
19
|
+
inputRelease(entity, { targetId, action }, api) {
|
|
20
|
+
if (targetId !== entity.targetId) return
|
|
21
|
+
|
|
33
22
|
entity[action] = false
|
|
34
23
|
|
|
35
|
-
entity.
|
|
36
|
-
api.notify(`${action}End`, targetId)
|
|
37
|
-
})
|
|
24
|
+
api.notify(`${action}End`, entity.targetId)
|
|
38
25
|
},
|
|
39
26
|
}
|
|
40
27
|
}
|
|
41
28
|
|
|
42
|
-
export function createInput(
|
|
43
|
-
|
|
44
|
-
targetIds = [],
|
|
45
|
-
mapping = {},
|
|
46
|
-
) {
|
|
47
|
-
return { id: name, type: "input", targetIds, mapping }
|
|
29
|
+
export function createInput(targetId, mapping = {}) {
|
|
30
|
+
return { type: "input", targetId, mapping }
|
|
48
31
|
}
|
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
const DEFAULT_PARAMS = {
|
|
2
|
-
name: "keyboard0",
|
|
3
|
-
}
|
|
4
|
-
|
|
5
1
|
export function keyboard() {
|
|
6
2
|
let handleKeyDown, handleKeyUp
|
|
7
3
|
let currentDocument = null
|
|
@@ -26,36 +22,28 @@ export function keyboard() {
|
|
|
26
22
|
|
|
27
23
|
keyboardKeyDown(entity, keyCode, api) {
|
|
28
24
|
const action = entity.mapping[keyCode]
|
|
29
|
-
if (!action)
|
|
30
|
-
return
|
|
31
|
-
}
|
|
25
|
+
if (!action) return
|
|
32
26
|
|
|
33
27
|
if (!entity[action]) {
|
|
34
28
|
entity[action] = true
|
|
35
|
-
api.notify("inputPress", {
|
|
29
|
+
api.notify("inputPress", { targetId: entity.targetId, action })
|
|
36
30
|
}
|
|
37
31
|
},
|
|
38
32
|
|
|
39
33
|
keyboardKeyUp(entity, keyCode, api) {
|
|
40
34
|
const action = entity.mapping[keyCode]
|
|
41
|
-
if (!action)
|
|
42
|
-
return
|
|
43
|
-
}
|
|
35
|
+
if (!action) return
|
|
44
36
|
|
|
45
37
|
if (entity[action]) {
|
|
46
38
|
entity[action] = false
|
|
47
|
-
api.notify("inputRelease", {
|
|
39
|
+
api.notify("inputRelease", { targetId: entity.targetId, action })
|
|
48
40
|
}
|
|
49
41
|
},
|
|
50
42
|
}
|
|
51
43
|
}
|
|
52
44
|
|
|
53
|
-
export function createKeyboard(
|
|
54
|
-
|
|
55
|
-
targetInput,
|
|
56
|
-
mapping = {},
|
|
57
|
-
) {
|
|
58
|
-
return { id: name, type: "keyboard", targetInput, mapping }
|
|
45
|
+
export function createKeyboard(targetId, mapping = {}) {
|
|
46
|
+
return { type: "keyboard", targetId, mapping }
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
function createKeyboardHandler(id, api) {
|
|
@@ -2,10 +2,6 @@ import { findCollision } from "@inglorious/engine/collision/detection.js"
|
|
|
2
2
|
import { clampToBounds } from "@inglorious/engine/physics/bounds.js"
|
|
3
3
|
import { zero } from "@inglorious/utils/math/vector.js"
|
|
4
4
|
|
|
5
|
-
const DEFAULT_PARAMS = {
|
|
6
|
-
name: "mouse",
|
|
7
|
-
}
|
|
8
|
-
|
|
9
5
|
const NO_Y = 0
|
|
10
6
|
|
|
11
7
|
export function mouse() {
|
|
@@ -49,9 +45,8 @@ export function track(parent, options) {
|
|
|
49
45
|
}
|
|
50
46
|
}
|
|
51
47
|
|
|
52
|
-
export function createMouse(
|
|
48
|
+
export function createMouse(overrides = {}) {
|
|
53
49
|
return {
|
|
54
|
-
id: name,
|
|
55
50
|
type: "mouse",
|
|
56
51
|
layer: 999, // A high layer value to ensure it's always rendered on top
|
|
57
52
|
position: zero(),
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import {
|
|
2
|
+
deserialize,
|
|
3
|
+
serialize,
|
|
4
|
+
} from "@inglorious/utils/data-structures/object.js"
|
|
1
5
|
import { extend } from "@inglorious/utils/data-structures/objects.js"
|
|
2
6
|
|
|
3
7
|
import { coreEvents } from "../core-events.js"
|
|
@@ -33,7 +37,7 @@ export function multiplayerMiddleware(config = {}) {
|
|
|
33
37
|
if (!event.fromServer) {
|
|
34
38
|
if (ws?.readyState === WebSocket.OPEN) {
|
|
35
39
|
// If the connection is open, send the event immediately.
|
|
36
|
-
ws.send(
|
|
40
|
+
ws.send(serialize(event))
|
|
37
41
|
} else {
|
|
38
42
|
// If the connection is not open, queue the event for later.
|
|
39
43
|
localQueue.push(event)
|
|
@@ -62,12 +66,12 @@ export function multiplayerMiddleware(config = {}) {
|
|
|
62
66
|
ws.onopen = () => {
|
|
63
67
|
// Send any queued events to the server.
|
|
64
68
|
while (localQueue.length) {
|
|
65
|
-
ws.send(
|
|
69
|
+
ws.send(serialize(localQueue.shift()))
|
|
66
70
|
}
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
ws.onmessage = (event) => {
|
|
70
|
-
const serverEvent =
|
|
74
|
+
const serverEvent = deserialize(event.data)
|
|
71
75
|
|
|
72
76
|
if (serverEvent.type === "initialState") {
|
|
73
77
|
// Merge the server's initial state with the client's local state.
|
|
@@ -1,20 +0,0 @@
|
|
|
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
|
-
}
|